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