move go bindings into contrib/go/rtaudio subdirectory
authorSerge A. Zaitsev <zaitsev.serge@gmail.com>
Tue, 3 Oct 2017 15:40:01 +0000 (18:40 +0300)
committerSerge A. Zaitsev <zaitsev.serge@gmail.com>
Tue, 3 Oct 2017 15:40:01 +0000 (18:40 +0300)
contrib/go/rtaudio/rtaudio.go [new file with mode: 0644]
contrib/go/rtaudio/rtaudio_stub.cpp [new file with mode: 0644]
contrib/go/rtaudio/rtaudio_stub.h [new file with mode: 0644]
contrib/go/rtaudio/rtaudio_test.go [new file with mode: 0644]
rtaudio.go [deleted file]
rtaudio_test.go [deleted file]

diff --git a/contrib/go/rtaudio/rtaudio.go b/contrib/go/rtaudio/rtaudio.go
new file mode 100644 (file)
index 0000000..0289706
--- /dev/null
@@ -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 -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)
+       }
+}
diff --git a/contrib/go/rtaudio/rtaudio_stub.cpp b/contrib/go/rtaudio/rtaudio_stub.cpp
new file mode 100644 (file)
index 0000000..1db9da1
--- /dev/null
@@ -0,0 +1,4 @@
+#include "../../../RtAudio.h"
+
+#include "../../../RtAudio.cpp"
+#include "../../../rtaudio_c.cpp"
diff --git a/contrib/go/rtaudio/rtaudio_stub.h b/contrib/go/rtaudio/rtaudio_stub.h
new file mode 100644 (file)
index 0000000..7778f10
--- /dev/null
@@ -0,0 +1 @@
+#include "../../../rtaudio_c.h"
diff --git a/contrib/go/rtaudio/rtaudio_test.go b/contrib/go/rtaudio/rtaudio_test.go
new file mode 100644 (file)
index 0000000..32475cf
--- /dev/null
@@ -0,0 +1,71 @@
+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(&params, 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)
+}
diff --git a/rtaudio.go b/rtaudio.go
deleted file mode 100644 (file)
index 8b0f644..0000000
+++ /dev/null
@@ -1,563 +0,0 @@
-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)
-       }
-}
diff --git a/rtaudio_test.go b/rtaudio_test.go
deleted file mode 100644 (file)
index 32475cf..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-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(&params, 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)
-}