mirror of
https://github.com/make-42/xyosc
synced 2025-01-18 18:57:10 +01:00
223 lines
6.4 KiB
Go
223 lines
6.4 KiB
Go
package malgo
|
|
|
|
// #include "malgo.h"
|
|
import "C"
|
|
import (
|
|
"reflect"
|
|
"sync"
|
|
"unsafe"
|
|
)
|
|
|
|
// LogProc type.
|
|
type LogProc func(message string)
|
|
|
|
// AlsaContextConfig type.
|
|
type AlsaContextConfig struct {
|
|
UseVerboseDeviceEnumeration uint32
|
|
}
|
|
|
|
// PulseContextConfig type.
|
|
type PulseContextConfig struct {
|
|
PApplicationName *byte
|
|
PServerName *byte
|
|
// Enables autospawning of the PulseAudio daemon if necessary.
|
|
TryAutoSpawn uint32
|
|
}
|
|
|
|
// CoreAudioConfig type.
|
|
type CoreAudioConfig struct {
|
|
SessionCategory IOSSessionCategory
|
|
SessionCategoryOptions IOSSessionCategoryOptions
|
|
}
|
|
|
|
// JackContextConfig type.
|
|
type JackContextConfig struct {
|
|
PClientName *byte
|
|
TryStartServer uint32
|
|
}
|
|
|
|
// ContextConfig type.
|
|
type ContextConfig struct {
|
|
LogCallback *[0]byte
|
|
ThreadPriority ThreadPriority
|
|
PUserData *byte
|
|
AllocationCallbacks AllocationCallbacks
|
|
Alsa AlsaContextConfig
|
|
Pulse PulseContextConfig
|
|
CoreAudio CoreAudioConfig
|
|
Jack JackContextConfig
|
|
}
|
|
|
|
func (d *ContextConfig) toC() (C.ma_context_config, error) {
|
|
ctxConfig := C.ma_context_config_init()
|
|
ctxConfig.threadPriority = C.ma_thread_priority(d.ThreadPriority)
|
|
ctxConfig.pUserData = unsafe.Pointer(d.PUserData)
|
|
ctxConfig.allocationCallbacks.pUserData = unsafe.Pointer(d.AllocationCallbacks.PUserData)
|
|
ctxConfig.allocationCallbacks.onMalloc = d.AllocationCallbacks.OnMalloc
|
|
ctxConfig.allocationCallbacks.onRealloc = d.AllocationCallbacks.OnRealloc
|
|
ctxConfig.allocationCallbacks.onFree = d.AllocationCallbacks.OnFree
|
|
ctxConfig.alsa.useVerboseDeviceEnumeration = C.uint(d.Alsa.UseVerboseDeviceEnumeration)
|
|
ctxConfig.pulse.pApplicationName = (*C.char)(unsafe.Pointer((d.Pulse.PApplicationName)))
|
|
ctxConfig.pulse.pServerName = (*C.char)(unsafe.Pointer((d.Pulse.PServerName)))
|
|
ctxConfig.pulse.tryAutoSpawn = C.uint(d.Pulse.TryAutoSpawn)
|
|
ctxConfig.coreaudio.sessionCategory = C.ma_ios_session_category(d.CoreAudio.SessionCategory)
|
|
ctxConfig.coreaudio.sessionCategoryOptions = C.uint(d.CoreAudio.SessionCategoryOptions)
|
|
ctxConfig.jack.pClientName = (*C.char)(unsafe.Pointer((d.Jack.PClientName)))
|
|
ctxConfig.jack.tryStartServer = C.uint(d.Jack.TryStartServer)
|
|
|
|
return ctxConfig, nil
|
|
}
|
|
|
|
// AllocationCallbacks types.
|
|
type AllocationCallbacks struct {
|
|
PUserData *byte
|
|
OnMalloc *[0]byte
|
|
OnRealloc *[0]byte
|
|
OnFree *[0]byte
|
|
}
|
|
|
|
// Context is used for selecting and initializing the relevant backends.
|
|
type Context struct {
|
|
ptr *unsafe.Pointer
|
|
}
|
|
|
|
// DefaultContext is an unspecified context. It can be used to initialize a streaming
|
|
// function with implicit context defaults.
|
|
var DefaultContext Context = Context{}
|
|
|
|
func (ctx Context) cptr() *C.ma_context {
|
|
return (*C.ma_context)(*ctx.ptr)
|
|
}
|
|
|
|
// Uninit uninitializes a context.
|
|
// Results are undefined if you call this while any device created by this context is still active.
|
|
func (ctx Context) Uninit() error {
|
|
result := C.ma_context_uninit(ctx.cptr())
|
|
return errorFromResult(result)
|
|
}
|
|
|
|
// Devices retrieves basic information about every active playback or capture device.
|
|
func (ctx Context) Devices(kind DeviceType) ([]DeviceInfo, error) {
|
|
contextMutex.Lock()
|
|
defer contextMutex.Unlock()
|
|
|
|
var playbackDevices *C.ma_device_info
|
|
var playbackDeviceCount C.ma_uint32
|
|
var captureDevices *C.ma_device_info
|
|
var captureDeviceCount C.ma_uint32
|
|
|
|
result := C.ma_context_get_devices(ctx.cptr(),
|
|
&playbackDevices, &playbackDeviceCount,
|
|
&captureDevices, &captureDeviceCount)
|
|
err := errorFromResult(result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
devices := playbackDevices
|
|
deviceCount := int(playbackDeviceCount)
|
|
if kind == Capture {
|
|
devices = captureDevices
|
|
deviceCount = int(captureDeviceCount)
|
|
}
|
|
info := make([]DeviceInfo, deviceCount)
|
|
deviceInfoAddr := unsafe.Pointer(devices)
|
|
for i := 0; i < deviceCount; i++ {
|
|
info[i] = deviceInfoFromPointer(deviceInfoAddr)
|
|
deviceInfoAddr = unsafe.Add(deviceInfoAddr, rawDeviceInfoSize)
|
|
}
|
|
|
|
return info, nil
|
|
}
|
|
|
|
// DeviceInfo retrieves information about a device of the given type, with the specified ID and share mode.
|
|
func (ctx Context) DeviceInfo(kind DeviceType, id DeviceID, mode ShareMode) (DeviceInfo, error) {
|
|
var info C.ma_device_info
|
|
|
|
result := C.ma_context_get_device_info(ctx.cptr(), C.ma_device_type(kind), id.cptr(), &info)
|
|
err := errorFromResult(result)
|
|
if err != nil {
|
|
return DeviceInfo{}, err
|
|
}
|
|
|
|
return deviceInfoFromPointer(unsafe.Pointer(&info)), nil
|
|
}
|
|
|
|
var contextMutex sync.Mutex
|
|
var logProcMap = make(map[*C.ma_context]LogProc)
|
|
|
|
// SetLogProc sets the logging callback for the context.
|
|
func (ctx Context) SetLogProc(proc LogProc) {
|
|
contextMutex.Lock()
|
|
defer contextMutex.Unlock()
|
|
ptr := ctx.cptr()
|
|
if proc != nil {
|
|
logProcMap[ptr] = proc
|
|
} else {
|
|
delete(logProcMap, ptr)
|
|
}
|
|
}
|
|
|
|
//export goLogCallback
|
|
func goLogCallback(pContext *C.ma_context, message *C.char) {
|
|
contextMutex.Lock()
|
|
callback := logProcMap[pContext]
|
|
contextMutex.Unlock()
|
|
|
|
if callback != nil {
|
|
callback(C.GoString(message))
|
|
}
|
|
}
|
|
|
|
// AllocatedContext is a Context that has been created by the application.
|
|
// It must be freed after use in order to release resources.
|
|
type AllocatedContext struct {
|
|
Context
|
|
config *C.ma_context_config
|
|
}
|
|
|
|
// InitContext creates and initializes a context.
|
|
// When the application no longer needs the context instance, it needs to call Free() .
|
|
func InitContext(backends []Backend, config ContextConfig, logProc LogProc) (*AllocatedContext, error) {
|
|
configC, err := config.toC()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ptr := C.ma_malloc(C.sizeof_ma_context, nil)
|
|
ctx := AllocatedContext{
|
|
Context: Context{ptr: &ptr},
|
|
}
|
|
if uintptr(*ctx.Context.ptr) == 0 {
|
|
ctx.Free()
|
|
return nil, ErrOutOfMemory
|
|
}
|
|
|
|
C.goSetContextConfigCallbacks(&configC, ctx.cptr())
|
|
ctx.SetLogProc(logProc)
|
|
|
|
var backendsArg *C.ma_backend
|
|
backendCountArg := (C.ma_uint32)(len(backends))
|
|
if backendCountArg > 0 {
|
|
backendsArg = (*C.ma_backend)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&backends)).Data))
|
|
}
|
|
|
|
result := C.ma_context_init(backendsArg, backendCountArg, &configC, ctx.cptr())
|
|
if err := errorFromResult(result); err != nil {
|
|
ctx.SetLogProc(nil)
|
|
ctx.Free()
|
|
return nil, err
|
|
}
|
|
return &ctx, nil
|
|
}
|
|
|
|
// Free must be called when the allocated data is no longer used.
|
|
// This function must only be called for an uninitialized context.
|
|
func (ctx *AllocatedContext) Free() {
|
|
if ctx.Context.ptr == nil || uintptr(*ctx.Context.ptr) == 0 {
|
|
return
|
|
}
|
|
ctx.SetLogProc(nil)
|
|
C.ma_free(unsafe.Pointer(ctx.cptr()), nil)
|
|
ctx.Context.ptr = nil
|
|
}
|