|
|
|
@@ -0,0 +1,563 @@
|
|
|
|
|
package rtaudio
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
#cgo CXXFLAGS: -g
|
|
|
|
|
#cgo LDFLAGS: -lstdc++ -g
|
|
|
|
|
|
|
|
|
|
#cgo linux CXXFLAGS: -D__LINUX_ALSA__
|
|
|
|
|
#cgo linux LDFLAGS: -lm -lasound -pthread
|
|
|
|
|
|
|
|
|
|
#cgo linux,pulseaudio CXXFLAGS: -D__LINUX_PULSE__
|
|
|
|
|
#cgo linux,pulseaudio LDFLAGS: -lpulse -lpulse-simple
|
|
|
|
|
|
|
|
|
|
#cgo jack CXXFLAGS: -D__UNIX_JACK__
|
|
|
|
|
#cgo jack LDFLAGS: -ljack
|
|
|
|
|
|
|
|
|
|
#cgo windows CXXFLAGS: -D__WINDOWS_WASAPI__
|
|
|
|
|
#cgo windows LDFLAGS: -lm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid -lwinmm -lole32 -static
|
|
|
|
|
|
|
|
|
|
#cgo darwin CXXFLAGS: -D__MACOSX_CORE__
|
|
|
|
|
#cgo darwin LDFLAGS: -framework CoreAudio -framework CoreFoundation
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include "rtaudio_stub.h"
|
|
|
|
|
|
|
|
|
|
extern int goCallback(void *out, void *in, unsigned int nFrames,
|
|
|
|
|
double stream_time, rtaudio_stream_status_t status, void *userdata);
|
|
|
|
|
|
|
|
|
|
static inline void cgoRtAudioOpenStream(rtaudio_t audio,
|
|
|
|
|
rtaudio_stream_parameters_t *output_params,
|
|
|
|
|
rtaudio_stream_parameters_t *input_params,
|
|
|
|
|
rtaudio_format_t format,
|
|
|
|
|
unsigned int sample_rate,
|
|
|
|
|
unsigned int *buffer_frames,
|
|
|
|
|
int cb_id,
|
|
|
|
|
rtaudio_stream_options_t *options) {
|
|
|
|
|
rtaudio_open_stream(audio, output_params, input_params,
|
|
|
|
|
format, sample_rate, buffer_frames,
|
|
|
|
|
goCallback, (void *)(uintptr_t)cb_id, options, NULL);
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
import "C"
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
|
|
|
|
"unsafe"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// API is an enumeration of available compiled APIs. Supported API include
|
|
|
|
|
// Alsa/PulseAudio/OSS, Jack, CoreAudio, WASAPI/ASIO/DS and dummy API.
|
|
|
|
|
type API C.rtaudio_api_t
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// APIUnspecified looks for a working compiled API.
|
|
|
|
|
APIUnspecified API = C.RTAUDIO_API_UNSPECIFIED
|
|
|
|
|
// APILinuxALSA uses the Advanced Linux Sound Architecture API.
|
|
|
|
|
APILinuxALSA = C.RTAUDIO_API_LINUX_ALSA
|
|
|
|
|
// APILinuxPulse uses the Linux PulseAudio API.
|
|
|
|
|
APILinuxPulse = C.RTAUDIO_API_LINUX_PULSE
|
|
|
|
|
// APILinuxOSS uses the Linux Open Sound System API.
|
|
|
|
|
APILinuxOSS = C.RTAUDIO_API_LINUX_OSS
|
|
|
|
|
// APIUnixJack uses the Jack Low-Latency Audio Server API.
|
|
|
|
|
APIUnixJack = C.RTAUDIO_API_UNIX_JACK
|
|
|
|
|
// APIMacOSXCore uses Macintosh OS-X Core Audio API.
|
|
|
|
|
APIMacOSXCore = C.RTAUDIO_API_MACOSX_CORE
|
|
|
|
|
// APIWindowsWASAPI uses the Microsoft WASAPI API.
|
|
|
|
|
APIWindowsWASAPI = C.RTAUDIO_API_WINDOWS_WASAPI
|
|
|
|
|
// APIWindowsASIO uses the Steinberg Audio Stream I/O API.
|
|
|
|
|
APIWindowsASIO = C.RTAUDIO_API_WINDOWS_ASIO
|
|
|
|
|
// APIWindowsDS uses the Microsoft DirectSound API.
|
|
|
|
|
APIWindowsDS = C.RTAUDIO_API_WINDOWS_DS
|
|
|
|
|
// APIDummy is a compilable but non-functional API.
|
|
|
|
|
APIDummy = C.RTAUDIO_API_DUMMY
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func (api API) String() string {
|
|
|
|
|
switch api {
|
|
|
|
|
case APIUnspecified:
|
|
|
|
|
return "unspecified"
|
|
|
|
|
case APILinuxALSA:
|
|
|
|
|
return "alsa"
|
|
|
|
|
case APILinuxPulse:
|
|
|
|
|
return "pulse"
|
|
|
|
|
case APILinuxOSS:
|
|
|
|
|
return "oss"
|
|
|
|
|
case APIUnixJack:
|
|
|
|
|
return "jack"
|
|
|
|
|
case APIMacOSXCore:
|
|
|
|
|
return "coreaudio"
|
|
|
|
|
case APIWindowsWASAPI:
|
|
|
|
|
return "wasapi"
|
|
|
|
|
case APIWindowsASIO:
|
|
|
|
|
return "asio"
|
|
|
|
|
case APIWindowsDS:
|
|
|
|
|
return "directsound"
|
|
|
|
|
case APIDummy:
|
|
|
|
|
return "dummy"
|
|
|
|
|
}
|
|
|
|
|
return "?"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StreamStatus defines over- or underflow flags in the audio callback.
|
|
|
|
|
type StreamStatus C.rtaudio_stream_status_t
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// StatusInputOverflow indicates that data was discarded because of an
|
|
|
|
|
// overflow condition at the driver.
|
|
|
|
|
StatusInputOverflow StreamStatus = C.RTAUDIO_STATUS_INPUT_OVERFLOW
|
|
|
|
|
// StatusOutputUnderflow indicates that the output buffer ran low, likely
|
|
|
|
|
// producing a break in the output sound.
|
|
|
|
|
StatusOutputUnderflow StreamStatus = C.RTAUDIO_STATUS_OUTPUT_UNDERFLOW
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Version returns current RtAudio library version string.
|
|
|
|
|
func Version() string {
|
|
|
|
|
return C.GoString(C.rtaudio_version())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CompiledAPI determines the available compiled audio APIs.
|
|
|
|
|
func CompiledAPI() (apis []API) {
|
|
|
|
|
capis := (*[1 << 27]C.rtaudio_api_t)(unsafe.Pointer(C.rtaudio_compiled_api()))
|
|
|
|
|
for i := 0; ; i++ {
|
|
|
|
|
api := capis[i]
|
|
|
|
|
if api == C.RTAUDIO_API_UNSPECIFIED {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
apis = append(apis, API(api))
|
|
|
|
|
}
|
|
|
|
|
return apis
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DeviceInfo is the public device information structure for returning queried values.
|
|
|
|
|
type DeviceInfo struct {
|
|
|
|
|
Name string
|
|
|
|
|
Probed bool
|
|
|
|
|
NumOutputChannels int
|
|
|
|
|
NumInputChannels int
|
|
|
|
|
NumDuplexChannels int
|
|
|
|
|
IsDefaultOutput bool
|
|
|
|
|
IsDefaultInput bool
|
|
|
|
|
|
|
|
|
|
//rtaudio_format_t native_formats;
|
|
|
|
|
|
|
|
|
|
PreferredSampleRate uint
|
|
|
|
|
SampleRates []int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StreamParams is the structure for specifying input or output stream parameters.
|
|
|
|
|
type StreamParams struct {
|
|
|
|
|
DeviceID uint
|
|
|
|
|
NumChannels uint
|
|
|
|
|
FirstChannel uint
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StreamFlags is a set of RtAudio stream option flags.
|
|
|
|
|
type StreamFlags C.rtaudio_stream_flags_t
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// FlagsNoninterleaved is set to use non-interleaved buffers (default = interleaved).
|
|
|
|
|
FlagsNoninterleaved = C.RTAUDIO_FLAGS_NONINTERLEAVED
|
|
|
|
|
// FlagsMinimizeLatency when set attempts to configure stream parameters for lowest possible latency.
|
|
|
|
|
FlagsMinimizeLatency = C.RTAUDIO_FLAGS_MINIMIZE_LATENCY
|
|
|
|
|
// FlagsHogDevice when set attempts to grab device for exclusive use.
|
|
|
|
|
FlagsHogDevice = C.RTAUDIO_FLAGS_HOG_DEVICE
|
|
|
|
|
// FlagsScheduleRealtime is set in attempt to select realtime scheduling (round-robin) for the callback thread.
|
|
|
|
|
FlagsScheduleRealtime = C.RTAUDIO_FLAGS_SCHEDULE_REALTIME
|
|
|
|
|
// FlagsAlsaUseDefault is set to use the "default" PCM device (ALSA only).
|
|
|
|
|
FlagsAlsaUseDefault = C.RTAUDIO_FLAGS_ALSA_USE_DEFAULT
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// StreamOptions is the structure for specifying stream options.
|
|
|
|
|
type StreamOptions struct {
|
|
|
|
|
Flags StreamFlags
|
|
|
|
|
NumBuffers uint
|
|
|
|
|
Priotity int
|
|
|
|
|
Name string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RtAudio is a "controller" used to select an available audio i/o interface.
|
|
|
|
|
type RtAudio interface {
|
|
|
|
|
Destroy()
|
|
|
|
|
CurrentAPI() API
|
|
|
|
|
Devices() ([]DeviceInfo, error)
|
|
|
|
|
DefaultOutputDevice() int
|
|
|
|
|
DefaultInputDevice() int
|
|
|
|
|
|
|
|
|
|
Open(out, in *StreamParams, format Format, sampleRate uint, frames uint, cb Callback, opts *StreamOptions) error
|
|
|
|
|
Close()
|
|
|
|
|
Start() error
|
|
|
|
|
Stop() error
|
|
|
|
|
Abort() error
|
|
|
|
|
|
|
|
|
|
IsOpen() bool
|
|
|
|
|
IsRunning() bool
|
|
|
|
|
|
|
|
|
|
Latency() (int, error)
|
|
|
|
|
SampleRate() (uint, error)
|
|
|
|
|
Time() (time.Duration, error)
|
|
|
|
|
SetTime(time.Duration) error
|
|
|
|
|
|
|
|
|
|
ShowWarnings(bool)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type rtaudio struct {
|
|
|
|
|
audio C.rtaudio_t
|
|
|
|
|
cb Callback
|
|
|
|
|
inputChannels int
|
|
|
|
|
outputChannels int
|
|
|
|
|
format Format
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _ RtAudio = &rtaudio{}
|
|
|
|
|
|
|
|
|
|
// Create a new RtAudio instance using the given API.
|
|
|
|
|
func Create(api API) (RtAudio, error) {
|
|
|
|
|
audio := C.rtaudio_create(C.rtaudio_api_t(api))
|
|
|
|
|
if C.rtaudio_error(audio) != nil {
|
|
|
|
|
return nil, errors.New(C.GoString(C.rtaudio_error(audio)))
|
|
|
|
|
}
|
|
|
|
|
return &rtaudio{audio: audio}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) Destroy() {
|
|
|
|
|
C.rtaudio_destroy(audio.audio)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) CurrentAPI() API {
|
|
|
|
|
return API(C.rtaudio_current_api(audio.audio))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) DefaultInputDevice() int {
|
|
|
|
|
return int(C.rtaudio_get_default_input_device(audio.audio))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) DefaultOutputDevice() int {
|
|
|
|
|
return int(C.rtaudio_get_default_output_device(audio.audio))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) Devices() ([]DeviceInfo, error) {
|
|
|
|
|
n := C.rtaudio_device_count(audio.audio)
|
|
|
|
|
devices := []DeviceInfo{}
|
|
|
|
|
for i := C.int(0); i < n; i++ {
|
|
|
|
|
cinfo := C.rtaudio_get_device_info(audio.audio, i)
|
|
|
|
|
if C.rtaudio_error(audio.audio) != nil {
|
|
|
|
|
return nil, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
|
|
|
|
}
|
|
|
|
|
sr := []int{}
|
|
|
|
|
for _, r := range cinfo.sample_rates {
|
|
|
|
|
if r == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
sr = append(sr, int(r))
|
|
|
|
|
}
|
|
|
|
|
devices = append(devices, DeviceInfo{
|
|
|
|
|
Name: C.GoString(&cinfo.name[0]),
|
|
|
|
|
Probed: cinfo.probed != 0,
|
|
|
|
|
NumInputChannels: int(cinfo.input_channels),
|
|
|
|
|
NumOutputChannels: int(cinfo.output_channels),
|
|
|
|
|
NumDuplexChannels: int(cinfo.duplex_channels),
|
|
|
|
|
IsDefaultOutput: cinfo.is_default_output != 0,
|
|
|
|
|
IsDefaultInput: cinfo.is_default_input != 0,
|
|
|
|
|
PreferredSampleRate: uint(cinfo.preferred_sample_rate),
|
|
|
|
|
SampleRates: sr,
|
|
|
|
|
})
|
|
|
|
|
// TODO: formats
|
|
|
|
|
}
|
|
|
|
|
return devices, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Format defines RtAudio data format type.
|
|
|
|
|
type Format int
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// FormatInt8 uses 8-bit signed integer.
|
|
|
|
|
FormatInt8 Format = C.RTAUDIO_FORMAT_SINT8
|
|
|
|
|
// FormatInt16 uses 16-bit signed integer.
|
|
|
|
|
FormatInt16 = C.RTAUDIO_FORMAT_SINT16
|
|
|
|
|
// FormatInt24 uses 24-bit signed integer.
|
|
|
|
|
FormatInt24 = C.RTAUDIO_FORMAT_SINT24
|
|
|
|
|
// FormatInt32 uses 32-bit signed integer.
|
|
|
|
|
FormatInt32 = C.RTAUDIO_FORMAT_SINT32
|
|
|
|
|
// FormatFloat32 uses 32-bit floating point values normalized between (-1..1).
|
|
|
|
|
FormatFloat32 = C.RTAUDIO_FORMAT_FLOAT32
|
|
|
|
|
// FormatFloat64 uses 64-bit floating point values normalized between (-1..1).
|
|
|
|
|
FormatFloat64 = C.RTAUDIO_FORMAT_FLOAT64
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Buffer is a common interface for audio buffers of various data format types.
|
|
|
|
|
type Buffer interface {
|
|
|
|
|
Len() int
|
|
|
|
|
Int8() []int8
|
|
|
|
|
Int16() []int16
|
|
|
|
|
Int24() []Int24
|
|
|
|
|
Int32() []int32
|
|
|
|
|
Float32() []float32
|
|
|
|
|
Float64() []float64
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Int24 is a helper type to convert int32 values to int24 and back.
|
|
|
|
|
type Int24 [3]byte
|
|
|
|
|
|
|
|
|
|
// Set Int24 value using the least significant bytes of the given number n.
|
|
|
|
|
func (i *Int24) Set(n int32) {
|
|
|
|
|
(*i)[0], (*i)[1], (*i)[2] = byte(n&0xff), byte((n&0xff00)>>8), byte((n&0xff0000)>>16)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get Int24 value as int32.
|
|
|
|
|
func (i Int24) Get() int32 {
|
|
|
|
|
n := int32(i[0]) | int32(i[1])<<8 | int32(i[2])<<16
|
|
|
|
|
if n&0x800000 != 0 {
|
|
|
|
|
n |= ^0xffffff
|
|
|
|
|
}
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type buffer struct {
|
|
|
|
|
format Format
|
|
|
|
|
length int
|
|
|
|
|
numChannels int
|
|
|
|
|
ptr unsafe.Pointer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *buffer) Len() int {
|
|
|
|
|
if b.ptr == nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
return b.length
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *buffer) Int8() []int8 {
|
|
|
|
|
if b.format != FormatInt8 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if b.ptr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return (*[1 << 30]int8)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *buffer) Int16() []int16 {
|
|
|
|
|
if b.format != FormatInt16 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if b.ptr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return (*[1 << 29]int16)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *buffer) Int24() []Int24 {
|
|
|
|
|
if b.format != FormatInt24 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if b.ptr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return (*[1 << 28]Int24)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *buffer) Int32() []int32 {
|
|
|
|
|
if b.format != FormatInt32 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if b.ptr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return (*[1 << 27]int32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *buffer) Float32() []float32 {
|
|
|
|
|
if b.format != FormatFloat32 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if b.ptr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return (*[1 << 27]float32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *buffer) Float64() []float64 {
|
|
|
|
|
if b.format != FormatFloat64 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if b.ptr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return (*[1 << 23]float64)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Callback is a client-defined function that will be invoked when input data
|
|
|
|
|
// is available and/or output data is needed.
|
|
|
|
|
type Callback func(out Buffer, in Buffer, dur time.Duration, status StreamStatus) int
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
audios = map[int]*rtaudio{}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func registerAudio(a *rtaudio) int {
|
|
|
|
|
mu.Lock()
|
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
for i := 0; ; i++ {
|
|
|
|
|
if _, ok := audios[i]; !ok {
|
|
|
|
|
audios[i] = a
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func unregisterAudio(a *rtaudio) {
|
|
|
|
|
mu.Lock()
|
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
for i := 0; i < len(audios); i++ {
|
|
|
|
|
if audios[i] == a {
|
|
|
|
|
delete(audios, i)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func findAudio(k int) *rtaudio {
|
|
|
|
|
mu.Lock()
|
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
return audios[k]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//export goCallback
|
|
|
|
|
func goCallback(out, in unsafe.Pointer, frames C.uint, sec C.double,
|
|
|
|
|
status C.rtaudio_stream_status_t, userdata unsafe.Pointer) C.int {
|
|
|
|
|
|
|
|
|
|
k := int(uintptr(userdata))
|
|
|
|
|
audio := findAudio(k)
|
|
|
|
|
dur := time.Duration(time.Microsecond * time.Duration(sec*1000000.0))
|
|
|
|
|
inbuf := &buffer{audio.format, int(frames), audio.inputChannels, in}
|
|
|
|
|
outbuf := &buffer{audio.format, int(frames), audio.outputChannels, out}
|
|
|
|
|
return C.int(audio.cb(outbuf, inbuf, dur, StreamStatus(status)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) Open(out, in *StreamParams, format Format, sampleRate uint,
|
|
|
|
|
frames uint, cb Callback, opts *StreamOptions) error {
|
|
|
|
|
var (
|
|
|
|
|
cInPtr *C.rtaudio_stream_parameters_t
|
|
|
|
|
cOutPtr *C.rtaudio_stream_parameters_t
|
|
|
|
|
cOptsPtr *C.rtaudio_stream_options_t
|
|
|
|
|
cIn C.rtaudio_stream_parameters_t
|
|
|
|
|
cOut C.rtaudio_stream_parameters_t
|
|
|
|
|
cOpts C.rtaudio_stream_options_t
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
audio.inputChannels = 0
|
|
|
|
|
audio.outputChannels = 0
|
|
|
|
|
if out != nil {
|
|
|
|
|
audio.outputChannels = int(out.NumChannels)
|
|
|
|
|
cOut.device_id = C.uint(out.DeviceID)
|
|
|
|
|
cOut.num_channels = C.uint(out.NumChannels)
|
|
|
|
|
cOut.first_channel = C.uint(out.FirstChannel)
|
|
|
|
|
cOutPtr = &cOut
|
|
|
|
|
}
|
|
|
|
|
if in != nil {
|
|
|
|
|
audio.inputChannels = int(in.NumChannels)
|
|
|
|
|
cIn.device_id = C.uint(in.DeviceID)
|
|
|
|
|
cIn.num_channels = C.uint(in.NumChannels)
|
|
|
|
|
cIn.first_channel = C.uint(in.FirstChannel)
|
|
|
|
|
cInPtr = &cIn
|
|
|
|
|
}
|
|
|
|
|
if opts != nil {
|
|
|
|
|
cOpts.flags = C.rtaudio_stream_flags_t(opts.Flags)
|
|
|
|
|
cOpts.num_buffers = C.uint(opts.NumBuffers)
|
|
|
|
|
cOpts.priority = C.int(opts.Priotity)
|
|
|
|
|
cOptsPtr = &cOpts
|
|
|
|
|
}
|
|
|
|
|
framesCount := C.uint(frames)
|
|
|
|
|
audio.format = format
|
|
|
|
|
audio.cb = cb
|
|
|
|
|
|
|
|
|
|
k := registerAudio(audio)
|
|
|
|
|
C.cgoRtAudioOpenStream(audio.audio, cOutPtr, cInPtr,
|
|
|
|
|
C.rtaudio_format_t(format), C.uint(sampleRate), &framesCount, C.int(k), cOptsPtr)
|
|
|
|
|
if C.rtaudio_error(audio.audio) != nil {
|
|
|
|
|
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) Close() {
|
|
|
|
|
unregisterAudio(audio)
|
|
|
|
|
C.rtaudio_close_stream(audio.audio)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) Start() error {
|
|
|
|
|
C.rtaudio_start_stream(audio.audio)
|
|
|
|
|
if C.rtaudio_error(audio.audio) != nil {
|
|
|
|
|
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) Stop() error {
|
|
|
|
|
C.rtaudio_stop_stream(audio.audio)
|
|
|
|
|
if C.rtaudio_error(audio.audio) != nil {
|
|
|
|
|
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) Abort() error {
|
|
|
|
|
C.rtaudio_abort_stream(audio.audio)
|
|
|
|
|
if C.rtaudio_error(audio.audio) != nil {
|
|
|
|
|
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) IsOpen() bool {
|
|
|
|
|
return C.rtaudio_is_stream_open(audio.audio) != 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) IsRunning() bool {
|
|
|
|
|
return C.rtaudio_is_stream_running(audio.audio) != 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) Latency() (int, error) {
|
|
|
|
|
latency := C.rtaudio_get_stream_latency(audio.audio)
|
|
|
|
|
if C.rtaudio_error(audio.audio) != nil {
|
|
|
|
|
return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
|
|
|
|
}
|
|
|
|
|
return int(latency), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) SampleRate() (uint, error) {
|
|
|
|
|
sampleRate := C.rtaudio_get_stream_sample_rate(audio.audio)
|
|
|
|
|
if C.rtaudio_error(audio.audio) != nil {
|
|
|
|
|
return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
|
|
|
|
}
|
|
|
|
|
return uint(sampleRate), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) Time() (time.Duration, error) {
|
|
|
|
|
sec := C.rtaudio_get_stream_time(audio.audio)
|
|
|
|
|
if C.rtaudio_error(audio.audio) != nil {
|
|
|
|
|
return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
|
|
|
|
}
|
|
|
|
|
return time.Duration(time.Microsecond * time.Duration(sec*1000000.0)), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) SetTime(t time.Duration) error {
|
|
|
|
|
sec := float64(t) * 1000000.0 / float64(time.Microsecond)
|
|
|
|
|
C.rtaudio_set_stream_time(audio.audio, C.double(sec))
|
|
|
|
|
if C.rtaudio_error(audio.audio) != nil {
|
|
|
|
|
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (audio *rtaudio) ShowWarnings(show bool) {
|
|
|
|
|
if show {
|
|
|
|
|
C.rtaudio_show_warnings(audio.audio, 1)
|
|
|
|
|
} else {
|
|
|
|
|
C.rtaudio_show_warnings(audio.audio, 0)
|
|
|
|
|
}
|
|
|
|
|
}
|