xyosc/vendor/github.com/gen2brain/malgo/device.go
2024-12-21 17:38:26 +01:00

189 lines
5.4 KiB
Go

package malgo
// #include "malgo.h"
import "C"
import (
"sync"
"unsafe"
)
// DataProc type.
type DataProc func(pOutputSample, pInputSamples []byte, framecount uint32)
// StopProc type.
type StopProc func()
// DeviceCallbacks contains callbacks for one initialized device.
type DeviceCallbacks struct {
// Data is called for the full duplex IO.
Data DataProc
// Stop is called when the device stopped.
Stop StopProc
}
// Device represents a streaming instance.
type Device struct {
ptr *unsafe.Pointer
}
// InitDevice initializes a device.
//
// The device ID can be nil, in which case the default device is used. Otherwise, you
// can retrieve the ID by calling Context.Devices() and use the ID from the returned data.
//
// Set device ID to nil to use the default device. Do _not_ rely on the first device ID returned
// by Context.Devices() to be the default device.
//
// The returned instance has to be cleaned up using Uninit().
func InitDevice(context Context, deviceConfig DeviceConfig, deviceCallbacks DeviceCallbacks) (*Device, error) {
ptr := C.ma_malloc(C.sizeof_ma_device, nil)
dev := Device{
ptr: &ptr,
}
if uintptr(*dev.ptr) == 0 {
return nil, ErrOutOfMemory
}
devConfigC, release := deviceConfig.toC()
defer release()
rawDevice := dev.cptr()
C.goSetDeviceConfigCallbacks(&devConfigC)
result := C.ma_device_init(context.cptr(), &devConfigC, rawDevice)
if result != 0 {
dev.free()
return nil, errorFromResult(result)
}
deviceMutex.Lock()
dataCallbacks[rawDevice] = deviceCallbacks.Data
stopCallbacks[rawDevice] = deviceCallbacks.Stop
deviceMutex.Unlock()
return &dev, nil
}
func (dev Device) cptr() *C.ma_device {
return (*C.ma_device)(*dev.ptr)
}
func (dev Device) free() {
if dev.ptr != nil {
C.ma_free(*dev.ptr, nil)
}
}
// Type returns device type.
func (dev *Device) Type() DeviceType {
return DeviceType(dev.cptr()._type)
}
// PlaybackFormat returns device playback format.
func (dev *Device) PlaybackFormat() FormatType {
return FormatType(dev.cptr().playback.format)
}
// CaptureFormat returns device capture format.
func (dev *Device) CaptureFormat() FormatType {
return FormatType(dev.cptr().capture.format)
}
// PlaybackChannels returns number of playback channels.
func (dev *Device) PlaybackChannels() uint32 {
return uint32(dev.cptr().playback.channels)
}
// CaptureChannels returns number of playback channels.
func (dev *Device) CaptureChannels() uint32 {
return uint32(dev.cptr().capture.channels)
}
// SampleRate returns sample rate.
func (dev *Device) SampleRate() uint32 {
return uint32(dev.cptr().sampleRate)
}
// Start activates the device.
// For playback devices this begins playback. For capture devices it begins recording.
//
// For a playback device, this will retrieve an initial chunk of audio data from the client before
// returning. The reason for this is to ensure there is valid audio data in the buffer, which needs
// to be done _before_ the device begins playback.
//
// This API waits until the backend device has been started for real by the worker thread. It also
// waits on a mutex for thread-safety.
func (dev *Device) Start() error {
result := C.ma_device_start(dev.cptr())
return errorFromResult(result)
}
// IsStarted determines whether or not the device is started.
func (dev *Device) IsStarted() bool {
result := C.ma_device_is_started(dev.cptr())
return result != 0
}
// Stop puts the device to sleep, but does not uninitialize it. Use Start() to start it up again.
//
// This API needs to wait on the worker thread to stop the backend device properly before returning. It
// also waits on a mutex for thread-safety. In addition, some backends need to wait for the device to
// finish playback/recording of the current fragment which can take some time (usually proportionate to
// the buffer size that was specified at initialization time).
func (dev *Device) Stop() error {
result := C.ma_device_stop(dev.cptr())
return errorFromResult(result)
}
// Uninit uninitializes a device.
//
// This will explicitly stop the device. You do not need to call Stop() beforehand, but it's
// harmless if you do.
func (dev *Device) Uninit() {
rawDevice := dev.cptr()
deviceMutex.Lock()
delete(dataCallbacks, rawDevice)
delete(stopCallbacks, rawDevice)
deviceMutex.Unlock()
C.ma_device_uninit(rawDevice)
dev.free()
}
var deviceMutex sync.Mutex
var dataCallbacks = make(map[*C.ma_device]DataProc)
var stopCallbacks = make(map[*C.ma_device]StopProc)
//export goDataCallback
func goDataCallback(pDevice *C.ma_device, pOutput, pInput unsafe.Pointer, frameCount C.ma_uint32) {
deviceMutex.Lock()
callback := dataCallbacks[pDevice]
deviceMutex.Unlock()
if callback != nil {
var inputSamples, outputSamples []byte
if pOutput != nil {
sampleCount := uint32(frameCount) * uint32(pDevice.playback.channels)
sizeInBytes := uint32(C.ma_get_bytes_per_sample(pDevice.playback.format))
outputSamples = unsafe.Slice((*byte)(pOutput), sampleCount*sizeInBytes)
}
if pInput != nil {
sampleCount := uint32(frameCount) * uint32(pDevice.capture.channels)
sizeInBytes := uint32(C.ma_get_bytes_per_sample(pDevice.capture.format))
inputSamples = unsafe.Slice((*byte)(pInput), sampleCount*sizeInBytes)
}
callback(outputSamples, inputSamples, uint32(frameCount))
}
}
//export goStopCallback
func goStopCallback(pDevice *C.ma_device) {
deviceMutex.Lock()
callback := stopCallbacks[pDevice]
deviceMutex.Unlock()
if callback != nil {
callback()
}
}