go bindings: fix typo
[rtaudio.git] / rtaudio.go
1 package rtaudio
2
3 /*
4
5 #cgo CXXFLAGS: -g
6 #cgo LDFLAGS: -lstdc++ -g
7
8 #cgo linux CXXFLAGS: -D__LINUX_ALSA__
9 #cgo linux LDFLAGS: -lm -lasound -pthread
10
11 #cgo linux,pulseaudio CXXFLAGS: -D__LINUX_PULSE__
12 #cgo linux,pulseaudio LDFLAGS: -lpulse -lpulse-simple
13
14 #cgo jack CXXFLAGS: -D__UNIX_JACK__
15 #cgo jack LDFLAGS: -ljack
16
17 #cgo windows CXXFLAGS: -D__WINDOWS_WASAPI__
18 #cgo windows LDFLAGS: -lm -luuid -lksuser -lwinmm -lole32
19
20 #cgo darwin CXXFLAGS: -D__MACOSX_CORE__
21 #cgo darwin LDFLAGS: -framework CoreAudio -framework CoreFoundation
22
23 #include "rtaudio_c.h"
24
25 extern int goCallback(void *out, void *in, unsigned int nFrames,
26                             double stream_time, rtaudio_stream_status_t status,
27                             void *userdata);
28
29 */
30 import "C"
31 import (
32         "errors"
33         "sync"
34         "time"
35         "unsafe"
36 )
37
38 // API is an enumeration of available compiled APIs. Supported API include
39 // Alsa/PulseAudio/OSS, Jack, CoreAudio, WASAPI/ASIO/DS and dummy API.
40 type API C.rtaudio_api_t
41
42 const (
43         // APIUnspecified looks for a working compiled API.
44         APIUnspecified API = C.RTAUDIO_API_UNSPECIFIED
45         // APILinuxALSA uses the Advanced Linux Sound Architecture API.
46         APILinuxALSA = C.RTAUDIO_API_LINUX_ALSA
47         // APILinuxPulse uses the Linux PulseAudio API.
48         APILinuxPulse = C.RTAUDIO_API_LINUX_PULSE
49         // APILinuxOSS uses the Linux Open Sound System API.
50         APILinuxOSS = C.RTAUDIO_API_LINUX_OSS
51         // APIUnixJack uses the Jack Low-Latency Audio Server API.
52         APIUnixJack = C.RTAUDIO_API_UNIX_JACK
53         // APIMacOSXCore uses Macintosh OS-X Core Audio API.
54         APIMacOSXCore = C.RTAUDIO_API_MACOSX_CORE
55         // APIWindowsWASAPI uses the Microsoft WASAPI API.
56         APIWindowsWASAPI = C.RTAUDIO_API_WINDOWS_WASAPI
57         // APIWindowsASIO uses the Steinberg Audio Stream I/O API.
58         APIWindowsASIO = C.RTAUDIO_API_WINDOWS_ASIO
59         // APIWindowsDS uses the Microsoft Direct Sound API.
60         APIWindowsDS = C.RTAUDIO_API_WINDOWS_DS
61         // APIDummy is a compilable but non-functional API.
62         APIDummy = C.RTAUDIO_API_DUMMY
63 )
64
65 func (api API) String() string {
66         switch api {
67         case APIUnspecified:
68                 return "unspecified"
69         case APILinuxALSA:
70                 return "alsa"
71         case APILinuxPulse:
72                 return "pulse"
73         case APILinuxOSS:
74                 return "oss"
75         case APIUnixJack:
76                 return "jack"
77         case APIMacOSXCore:
78                 return "coreaudio"
79         case APIWindowsWASAPI:
80                 return "wasapi"
81         case APIWindowsASIO:
82                 return "asio"
83         case APIWindowsDS:
84                 return "directsound"
85         case APIDummy:
86                 return "dummy"
87         }
88         return "?"
89 }
90
91 // StreamStatus defines over- or underflow flags in the audio callback.
92 type StreamStatus C.rtaudio_stream_status_t
93
94 const (
95         // StatusInputOverflow indicates that data was discarded because of an
96         // overflow condition at the driver.
97         StatusInputOverflow StreamStatus = C.RTAUDIO_STATUS_INPUT_OVERFLOW
98         // StatusOutputUnderflow indicates that the output buffer ran low, likely
99         // producing a break in the output sound.
100         StatusOutputUnderflow StreamStatus = C.RTAUDIO_STATUS_OUTPUT_UNDERFLOW
101 )
102
103 // Version returns current RtAudio library version string.
104 func Version() string {
105         return C.GoString(C.rtaudio_version())
106 }
107
108 // CompiledAPI determines the available compiled audio APIs.
109 func CompiledAPI() (apis []API) {
110         capis := (*[1 << 30]C.rtaudio_api_t)(unsafe.Pointer(C.rtaudio_compiled_api()))
111         for i := 0; ; i++ {
112                 api := capis[i]
113                 if api == C.RTAUDIO_API_UNSPECIFIED {
114                         break
115                 }
116                 apis = append(apis, API(api))
117         }
118         return apis
119 }
120
121 // DeviceInfo is the public device information structure for returning queried values.
122 type DeviceInfo struct {
123         Name              string
124         Probed            bool
125         NumOutputChannels int
126         NumInputChannels  int
127         NumDuplexChannels int
128         IsDefaultOutput   bool
129         IsDefaultInput    bool
130
131         //rtaudio_format_t native_formats;
132
133         PreferredSampleRate uint
134         SampleRates         []int
135 }
136
137 // StreamParams is the structure for specifying input or output stream parameters.
138 type StreamParams struct {
139         DeviceID     uint
140         NumChannels  uint
141         FirstChannel uint
142 }
143
144 // StreamFlags is a set of RtAudio stream option flags.
145 type StreamFlags C.rtaudio_stream_flags_t
146
147 const (
148         // FlagsNoninterleaved is set to use non-interleaved buffers (default = interleaved).
149         FlagsNoninterleaved = C.RTAUDIO_FLAGS_NONINTERLEAVED
150         // FlagsMinimizeLatency when set attempts to configure stream parameters for lowest possible latency.
151         FlagsMinimizeLatency = C.RTAUDIO_FLAGS_MINIMIZE_LATENCY
152         // FlagsHogDevice when set attempts to grab device for exclusive use.
153         FlagsHogDevice = C.RTAUDIO_FLAGS_HOG_DEVICE
154         // FlagsScheduleRealtime is set in attempt to select realtime scheduling (round-robin) for the callback thread.
155         FlagsScheduleRealtime = C.RTAUDIO_FLAGS_SCHEDULE_REALTIME
156         // FlagsAlsaUseDefault is set to use the "default" PCM device (ALSA only).
157         FlagsAlsaUseDefault = C.RTAUDIO_FLAGS_ALSA_USE_DEFAULT
158 )
159
160 // StreamOptions is the structure for specifying stream options.
161 type StreamOptions struct {
162         Flags      StreamFlags
163         NumBuffers uint
164         Priotity   int
165         Name       string
166 }
167
168 // RtAudio is a "controller" used to select an available audio i/o interface.
169 type RtAudio interface {
170         Destroy()
171         CurrentAPI() API
172         Devices() ([]DeviceInfo, error)
173         DefaultOutputDevice() int
174         DefaultInputDevice() int
175
176         Open(out, in *StreamParams, format Format, sampleRate uint, frames uint, cb Callback, opts *StreamOptions) error
177         Close()
178         Start() error
179         Stop() error
180         Abort() error
181
182         IsOpen() bool
183         IsRunning() bool
184
185         Latency() (int, error)
186         SampleRate() (uint, error)
187         Time() (time.Duration, error)
188         SetTime(time.Duration) error
189
190         ShowWarnings(bool)
191 }
192
193 type rtaudio struct {
194         audio          C.rtaudio_t
195         cb             Callback
196         inputChannels  int
197         outputChannels int
198         format         Format
199 }
200
201 var _ RtAudio = &rtaudio{}
202
203 // Create a new RtAudio instance using the given API.
204 func Create(api API) (RtAudio, error) {
205         audio := C.rtaudio_create(C.rtaudio_api_t(api))
206         if C.rtaudio_error(audio) != nil {
207                 return nil, errors.New(C.GoString(C.rtaudio_error(audio)))
208         }
209         return &rtaudio{audio: audio}, nil
210 }
211
212 func (audio *rtaudio) Destroy() {
213         C.rtaudio_destroy(audio.audio)
214 }
215
216 func (audio *rtaudio) CurrentAPI() API {
217         return API(C.rtaudio_current_api(audio.audio))
218 }
219
220 func (audio *rtaudio) DefaultInputDevice() int {
221         return int(C.rtaudio_get_default_input_device(audio.audio))
222 }
223
224 func (audio *rtaudio) DefaultOutputDevice() int {
225         return int(C.rtaudio_get_default_output_device(audio.audio))
226 }
227
228 func (audio *rtaudio) Devices() ([]DeviceInfo, error) {
229         n := C.rtaudio_device_count(audio.audio)
230         devices := []DeviceInfo{}
231         for i := C.int(0); i < n; i++ {
232                 cinfo := C.rtaudio_get_device_info(audio.audio, i)
233                 if C.rtaudio_error(audio.audio) != nil {
234                         return nil, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
235                 }
236                 sr := []int{}
237                 for _, r := range cinfo.sample_rates {
238                         if r == 0 {
239                                 break
240                         }
241                         sr = append(sr, int(r))
242                 }
243                 devices = append(devices, DeviceInfo{
244                         Name:                C.GoString(&cinfo.name[0]),
245                         Probed:              cinfo.probed != 0,
246                         NumInputChannels:    int(cinfo.input_channels),
247                         NumOutputChannels:   int(cinfo.output_channels),
248                         NumDuplexChannels:   int(cinfo.duplex_channels),
249                         IsDefaultOutput:     cinfo.is_default_output != 0,
250                         IsDefaultInput:      cinfo.is_default_input != 0,
251                         PreferredSampleRate: uint(cinfo.preferred_sample_rate),
252                         SampleRates:         sr,
253                 })
254                 // TODO: formats
255         }
256         return devices, nil
257 }
258
259 // Format defines RtAudio data format type.
260 type Format int
261
262 const (
263         // FormatInt8 uses 8-bit signed integer.
264         FormatInt8 Format = C.RTAUDIO_FORMAT_SINT8
265         // FormatInt16 uses 16-bit signed integer.
266         FormatInt16 = C.RTAUDIO_FORMAT_SINT16
267         // FormatInt24 uses 24-bit signed integer.
268         FormatInt24 = C.RTAUDIO_FORMAT_SINT24
269         // FormatInt32 uses 32-bit signed integer.
270         FormatInt32 = C.RTAUDIO_FORMAT_SINT32
271         // FormatFloat32 uses 32-bit floating point values normalized between (-1..1).
272         FormatFloat32 = C.RTAUDIO_FORMAT_FLOAT32
273         // FormatFloat64 uses 64-bit floating point values normalized between (-1..1).
274         FormatFloat64 = C.RTAUDIO_FORMAT_FLOAT64
275 )
276
277 // Buffer is a common interface for audio buffers of various data format types.
278 type Buffer interface {
279         Len() int
280         Int8() []int8
281         Int16() []int16
282         Int24() []Int24
283         Int32() []int32
284         Float32() []float32
285         Float64() []float64
286 }
287
288 // Int24 is a helper type to convert int32 values to int24 and back.
289 type Int24 [3]byte
290
291 // Set Int24 value using the least significant bytes of the given number n.
292 func (i *Int24) Set(n int32) {
293         (*i)[0], (*i)[1], (*i)[2] = byte(n&0xff), byte((n&0xff00)>>8), byte((n&0xff0000)>>16)
294 }
295
296 // Get Int24 value as int32.
297 func (i Int24) Get() int32 {
298         n := int32(i[0]) | int32(i[1])<<8 | int32(i[2])<<16
299         if n&0x800000 != 0 {
300                 n |= ^0xffffff
301         }
302         return n
303 }
304
305 type buffer struct {
306         format      Format
307         length      int
308         numChannels int
309         ptr         unsafe.Pointer
310 }
311
312 func (b *buffer) Len() int {
313         if b.ptr == nil {
314                 return 0
315         }
316         return b.length
317 }
318
319 func (b *buffer) Int8() []int8 {
320         if b.format != FormatInt8 {
321                 return nil
322         }
323         if b.ptr == nil {
324                 return nil
325         }
326         return (*[1 << 30]int8)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
327 }
328
329 func (b *buffer) Int16() []int16 {
330         if b.format != FormatInt16 {
331                 return nil
332         }
333         if b.ptr == nil {
334                 return nil
335         }
336         return (*[1 << 30]int16)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
337 }
338
339 func (b *buffer) Int24() []Int24 {
340         if b.format != FormatInt24 {
341                 return nil
342         }
343         if b.ptr == nil {
344                 return nil
345         }
346         return (*[1 << 30]Int24)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
347 }
348
349 func (b *buffer) Int32() []int32 {
350         if b.format != FormatInt32 {
351                 return nil
352         }
353         if b.ptr == nil {
354                 return nil
355         }
356         return (*[1 << 30]int32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
357 }
358
359 func (b *buffer) Float32() []float32 {
360         if b.format != FormatFloat32 {
361                 return nil
362         }
363         if b.ptr == nil {
364                 return nil
365         }
366         return (*[1 << 30]float32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
367 }
368
369 func (b *buffer) Float64() []float64 {
370         if b.format != FormatFloat64 {
371                 return nil
372         }
373         if b.ptr == nil {
374                 return nil
375         }
376         return (*[1 << 30]float64)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
377 }
378
379 // Callback is a client-defined function that will be invoked when input data
380 // is available and/or output data is needed.
381 type Callback func(out Buffer, in Buffer, dur time.Duration, status StreamStatus) int
382
383 var (
384         mu     sync.Mutex
385         audios = map[int]*rtaudio{}
386 )
387
388 func registerAudio(a *rtaudio) int {
389         mu.Lock()
390         defer mu.Unlock()
391         for i := 0; ; i++ {
392                 if _, ok := audios[i]; !ok {
393                         audios[i] = a
394                         return i
395                 }
396         }
397 }
398
399 func unregisterAudio(a *rtaudio) {
400         mu.Lock()
401         defer mu.Unlock()
402         for i := 0; i < len(audios); i++ {
403                 if audios[i] == a {
404                         delete(audios, i)
405                         return
406                 }
407         }
408 }
409
410 func findAudio(k int) *rtaudio {
411         mu.Lock()
412         defer mu.Unlock()
413         return audios[k]
414 }
415
416 //export goCallback
417 func goCallback(out, in unsafe.Pointer, frames C.uint, sec C.double,
418         status C.rtaudio_stream_status_t, userdata unsafe.Pointer) C.int {
419
420         k := int(uintptr(userdata))
421         audio := findAudio(k)
422         dur := time.Duration(time.Microsecond * time.Duration(sec*1000000.0))
423         inbuf := &buffer{audio.format, int(frames), audio.inputChannels, in}
424         outbuf := &buffer{audio.format, int(frames), audio.outputChannels, out}
425         return C.int(audio.cb(outbuf, inbuf, dur, StreamStatus(status)))
426 }
427
428 func (audio *rtaudio) Open(out, in *StreamParams, format Format, sampleRate uint,
429         frames uint, cb Callback, opts *StreamOptions) error {
430         var (
431                 cInPtr   *C.rtaudio_stream_parameters_t
432                 cOutPtr  *C.rtaudio_stream_parameters_t
433                 cOptsPtr *C.rtaudio_stream_options_t
434                 cIn      C.rtaudio_stream_parameters_t
435                 cOut     C.rtaudio_stream_parameters_t
436                 cOpts    C.rtaudio_stream_options_t
437         )
438
439         audio.inputChannels = 0
440         audio.outputChannels = 0
441         if out != nil {
442                 audio.outputChannels = int(out.NumChannels)
443                 cOut.device_id = C.uint(out.DeviceID)
444                 cOut.num_channels = C.uint(out.NumChannels)
445                 cOut.first_channel = C.uint(out.FirstChannel)
446                 cOutPtr = &cOut
447         }
448         if in != nil {
449                 audio.inputChannels = int(in.NumChannels)
450                 cIn.device_id = C.uint(in.DeviceID)
451                 cIn.num_channels = C.uint(in.NumChannels)
452                 cIn.first_channel = C.uint(in.FirstChannel)
453                 cInPtr = &cIn
454         }
455         if opts != nil {
456                 cOpts.flags = C.rtaudio_stream_flags_t(opts.Flags)
457                 cOpts.num_buffers = C.uint(opts.NumBuffers)
458                 cOpts.priority = C.int(opts.Priotity)
459                 cOptsPtr = &cOpts
460         }
461         framesCount := C.uint(frames)
462         audio.format = format
463         audio.cb = cb
464
465         k := registerAudio(audio)
466         C.rtaudio_open_stream(audio.audio, cOutPtr, cInPtr,
467                 C.rtaudio_format_t(format), C.uint(sampleRate), &framesCount,
468                 C.rtaudio_cb_t(C.goCallback), unsafe.Pointer(uintptr(k)), cOptsPtr, nil)
469         if C.rtaudio_error(audio.audio) != nil {
470                 return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
471         }
472         return nil
473 }
474
475 func (audio *rtaudio) Close() {
476         unregisterAudio(audio)
477         C.rtaudio_close_stream(audio.audio)
478 }
479
480 func (audio *rtaudio) Start() error {
481         C.rtaudio_start_stream(audio.audio)
482         if C.rtaudio_error(audio.audio) != nil {
483                 return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
484         }
485         return nil
486 }
487
488 func (audio *rtaudio) Stop() error {
489         C.rtaudio_stop_stream(audio.audio)
490         if C.rtaudio_error(audio.audio) != nil {
491                 return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
492         }
493         return nil
494 }
495
496 func (audio *rtaudio) Abort() error {
497         C.rtaudio_abort_stream(audio.audio)
498         if C.rtaudio_error(audio.audio) != nil {
499                 return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
500         }
501         return nil
502 }
503
504 func (audio *rtaudio) IsOpen() bool {
505         return C.rtaudio_is_stream_open(audio.audio) != 0
506 }
507
508 func (audio *rtaudio) IsRunning() bool {
509         return C.rtaudio_is_stream_running(audio.audio) != 0
510 }
511
512 func (audio *rtaudio) Latency() (int, error) {
513         latency := C.rtaudio_get_stream_latency(audio.audio)
514         if C.rtaudio_error(audio.audio) != nil {
515                 return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
516         }
517         return int(latency), nil
518 }
519
520 func (audio *rtaudio) SampleRate() (uint, error) {
521         sampleRate := C.rtaudio_get_stream_sample_rate(audio.audio)
522         if C.rtaudio_error(audio.audio) != nil {
523                 return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
524         }
525         return uint(sampleRate), nil
526 }
527
528 func (audio *rtaudio) Time() (time.Duration, error) {
529         sec := C.rtaudio_get_stream_time(audio.audio)
530         if C.rtaudio_error(audio.audio) != nil {
531                 return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
532         }
533         return time.Duration(time.Microsecond * time.Duration(sec*1000000.0)), nil
534 }
535
536 func (audio *rtaudio) SetTime(t time.Duration) error {
537         sec := float64(t) * 1000000.0 / float64(time.Microsecond)
538         C.rtaudio_set_stream_time(audio.audio, C.double(sec))
539         if C.rtaudio_error(audio.audio) != nil {
540                 return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
541         }
542         return nil
543 }
544
545 func (audio *rtaudio) ShowWarnings(show bool) {
546         if show {
547                 C.rtaudio_show_warnings(audio.audio, 1)
548         } else {
549                 C.rtaudio_show_warnings(audio.audio, 0)
550         }
551 }