audio: have a global audio device and context
On mobile devices, there is typically only a single audio device. We may open it once and reuse it to stream audio buffers from multiple sources. This assumption also applies to the context since OpenAL contextes are process-wide. A shared global pointer is reusable across multiple OpenAL sources. This CL also removes the Device and Context types and the unexpected garbage collection issue introduced by Context's finalizer. Fixes golang/go#10636 Change-Id: I82e6e6e6a1500ba91d66a7848cf37d1a5e01af9b Reviewed-on: https://go-review.googlesource.com/9782 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
parent
5cddc1460e
commit
a8aa7bbd72
|
@ -7,62 +7,66 @@
|
|||
package al
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"errors"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Device represents an audio device.
|
||||
type Device struct {
|
||||
ptr unsafe.Pointer
|
||||
var (
|
||||
mu sync.Mutex // mu protects Device and context
|
||||
|
||||
// device is the currently open audio device or nil.
|
||||
device unsafe.Pointer
|
||||
context unsafe.Pointer
|
||||
)
|
||||
|
||||
// DeviceError returns the last known error from the current device.
|
||||
func DeviceError() int32 {
|
||||
return alcGetError(device)
|
||||
}
|
||||
|
||||
// Error returns the last known error from the current device.
|
||||
func (d *Device) Error() int32 {
|
||||
return alcGetError(d.ptr)
|
||||
}
|
||||
// TODO(jbd): Investigate the cases where multiple audio output
|
||||
// devices might be needed.
|
||||
|
||||
// Context represents a context created in the OpenAL layer. A valid current
|
||||
// context is required to run OpenAL functions.
|
||||
// The returned context will be available process-wide if it's made the
|
||||
// current by calling MakeContextCurrent.
|
||||
type Context struct {
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
// OpenDevice opens the default audio device.
|
||||
func OpenDevice() error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
// Open opens a new device in the OpenAL layer.
|
||||
func Open(name string) *Device {
|
||||
ptr := alcOpenDevice(name)
|
||||
if ptr == nil {
|
||||
// already opened
|
||||
if device != nil {
|
||||
return nil
|
||||
}
|
||||
return &Device{ptr: ptr}
|
||||
}
|
||||
|
||||
// Close closes the device.
|
||||
func (d *Device) Close() bool {
|
||||
return alcCloseDevice(d.ptr)
|
||||
}
|
||||
|
||||
// CreateContext creates a new context.
|
||||
func (d *Device) CreateContext(attrs []int32) *Context {
|
||||
ptr := alcCreateContext(d.ptr, attrs)
|
||||
ptr := alcOpenDevice("")
|
||||
if ptr == nil {
|
||||
return nil
|
||||
return errors.New("al: cannot open the default audio device")
|
||||
}
|
||||
c := &Context{ptr: ptr}
|
||||
runtime.SetFinalizer(c, (*Context).Destroy)
|
||||
return c
|
||||
ctx := alcCreateContext(ptr, nil)
|
||||
if ctx == nil {
|
||||
alcCloseDevice(ptr)
|
||||
return errors.New("al: cannot create a new context")
|
||||
}
|
||||
alcMakeContextCurrent(ctx)
|
||||
device = ptr
|
||||
context = ctx
|
||||
return nil
|
||||
}
|
||||
|
||||
// MakeContextCurrent makes a context current. The context available
|
||||
// process-wide, you don't need to lock the current OS thread to
|
||||
// access the current context.
|
||||
func MakeContextCurrent(c *Context) bool {
|
||||
return alcMakeContextCurrent(c.ptr)
|
||||
}
|
||||
// CloseDevice closes the device and frees related resources.
|
||||
func CloseDevice() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
// Destroy destroys the current context and frees the related resources.
|
||||
func (c *Context) Destroy() {
|
||||
alcDestroyContext(c.ptr)
|
||||
runtime.SetFinalizer(c, nil)
|
||||
alcMakeContextCurrent(nil)
|
||||
|
||||
if context != nil {
|
||||
alcDestroyContext(context)
|
||||
context = nil
|
||||
}
|
||||
|
||||
if device != nil {
|
||||
alcCloseDevice(device)
|
||||
device = nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,11 +92,6 @@ var codeToState = map[int32]State{
|
|||
al.Stopped: Stopped,
|
||||
}
|
||||
|
||||
var device struct {
|
||||
sync.Mutex
|
||||
d *al.Device
|
||||
}
|
||||
|
||||
type track struct {
|
||||
format Format
|
||||
samplesPerSecond int64
|
||||
|
@ -119,15 +114,8 @@ type Player struct {
|
|||
// NewPlayer returns a new Player.
|
||||
// It initializes the underlying audio devices and the related resources.
|
||||
func NewPlayer(src io.ReadSeeker, format Format, samplesPerSecond int64) (*Player, error) {
|
||||
device.Lock()
|
||||
defer device.Unlock()
|
||||
|
||||
if device.d == nil {
|
||||
device.d = al.Open("")
|
||||
c := device.d.CreateContext(nil)
|
||||
if !al.MakeContextCurrent(c) {
|
||||
return nil, fmt.Errorf("audio: cannot initiate a new player")
|
||||
}
|
||||
if err := al.OpenDevice(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := al.GenSources(1)
|
||||
if code := al.Error(); code != 0 {
|
||||
|
|
Loading…
Reference in New Issue