--- /dev/null
+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 -luuid -lksuser -lwinmm -lole32
+
+#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 Direct Sound 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 << 30]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 << 30]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 << 30]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 << 30]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 << 30]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 << 30]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)
+ }
+}
--- /dev/null
+#include "../../../RtAudio.h"
+
+#include "../../../RtAudio.cpp"
+#include "../../../rtaudio_c.cpp"
--- /dev/null
+#include "../../../rtaudio_c.h"
--- /dev/null
+package rtaudio
+
+import (
+ "log"
+ "math"
+ "time"
+)
+
+func ExampleCompiledAPI() {
+ log.Println("RtAudio version: ", Version())
+ for _, api := range CompiledAPI() {
+ log.Println("Compiled API: ", api)
+ }
+}
+
+func ExampleRtAudio_Devices() {
+ audio, err := Create(APIUnspecified)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer audio.Destroy()
+ devices, err := audio.Devices()
+ if err != nil {
+ log.Fatal(err)
+ }
+ for _, d := range devices {
+ log.Printf("Audio device: %#v\n", d)
+ }
+}
+
+func ExampleRtAudio_Open() {
+ const (
+ sampleRate = 44100
+ bufSz = 512
+ freq = 440.0
+ )
+ phase := 0.0
+ audio, err := Create(APIUnspecified)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer audio.Destroy()
+
+ params := StreamParams{
+ DeviceID: uint(audio.DefaultOutputDevice()),
+ NumChannels: 2,
+ FirstChannel: 0,
+ }
+ options := StreamOptions{
+ Flags: FlagsAlsaUseDefault,
+ }
+ cb := func(out, in Buffer, dur time.Duration, status StreamStatus) int {
+ samples := out.Float32()
+ for i := 0; i < len(samples)/2; i++ {
+ sample := float32(math.Sin(2 * math.Pi * phase))
+ phase += freq / sampleRate
+
+ samples[i*2] = sample
+ samples[i*2+1] = sample
+ }
+ return 0
+ }
+ err = audio.Open(¶ms, nil, FormatFloat32, sampleRate, bufSz, cb, &options)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer audio.Close()
+ audio.Start()
+ defer audio.Stop()
+ time.Sleep(3 * time.Second)
+}
+++ /dev/null
-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 -luuid -lksuser -lwinmm -lole32
-
-#cgo darwin CXXFLAGS: -D__MACOSX_CORE__
-#cgo darwin LDFLAGS: -framework CoreAudio -framework CoreFoundation
-
-#include <stdlib.h>
-#include <stdint.h>
-#include "rtaudio_c.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 Direct Sound 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 << 30]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 << 30]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 << 30]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 << 30]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 << 30]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 << 30]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)
- }
-}
+++ /dev/null
-package rtaudio
-
-import (
- "log"
- "math"
- "time"
-)
-
-func ExampleCompiledAPI() {
- log.Println("RtAudio version: ", Version())
- for _, api := range CompiledAPI() {
- log.Println("Compiled API: ", api)
- }
-}
-
-func ExampleRtAudio_Devices() {
- audio, err := Create(APIUnspecified)
- if err != nil {
- log.Fatal(err)
- }
- defer audio.Destroy()
- devices, err := audio.Devices()
- if err != nil {
- log.Fatal(err)
- }
- for _, d := range devices {
- log.Printf("Audio device: %#v\n", d)
- }
-}
-
-func ExampleRtAudio_Open() {
- const (
- sampleRate = 44100
- bufSz = 512
- freq = 440.0
- )
- phase := 0.0
- audio, err := Create(APIUnspecified)
- if err != nil {
- log.Fatal(err)
- }
- defer audio.Destroy()
-
- params := StreamParams{
- DeviceID: uint(audio.DefaultOutputDevice()),
- NumChannels: 2,
- FirstChannel: 0,
- }
- options := StreamOptions{
- Flags: FlagsAlsaUseDefault,
- }
- cb := func(out, in Buffer, dur time.Duration, status StreamStatus) int {
- samples := out.Float32()
- for i := 0; i < len(samples)/2; i++ {
- sample := float32(math.Sin(2 * math.Pi * phase))
- phase += freq / sampleRate
-
- samples[i*2] = sample
- samples[i*2+1] = sample
- }
- return 0
- }
- err = audio.Open(¶ms, nil, FormatFloat32, sampleRate, bufSz, cb, &options)
- if err != nil {
- log.Fatal(err)
- }
- defer audio.Close()
- audio.Start()
- defer audio.Stop()
- time.Sleep(3 * time.Second)
-}