6 #cgo LDFLAGS: -lstdc++ -g
8 #cgo linux CXXFLAGS: -D__LINUX_ALSA__
9 #cgo linux LDFLAGS: -lm -lasound -pthread
11 #cgo linux,pulseaudio CXXFLAGS: -D__LINUX_PULSE__
12 #cgo linux,pulseaudio LDFLAGS: -lpulse -lpulse-simple
14 #cgo jack CXXFLAGS: -D__UNIX_JACK__
15 #cgo jack LDFLAGS: -ljack
17 #cgo windows CXXFLAGS: -D__WINDOWS_WASAPI__
18 #cgo windows LDFLAGS: -lm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid -lwinmm -lole32 -static
20 #cgo darwin CXXFLAGS: -D__MACOSX_CORE__
21 #cgo darwin LDFLAGS: -framework CoreAudio -framework CoreFoundation
25 #include "rtaudio_stub.h"
27 extern int goCallback(void *out, void *in, unsigned int nFrames,
28 double stream_time, rtaudio_stream_status_t status, void *userdata);
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,
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);
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
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 DirectSound API.
73 APIWindowsDS = C.RTAUDIO_API_WINDOWS_DS
74 // APIDummy is a compilable but non-functional API.
75 APIDummy = C.RTAUDIO_API_DUMMY
78 func (api API) String() string {
92 case APIWindowsWASAPI:
104 // StreamStatus defines over- or underflow flags in the audio callback.
105 type StreamStatus C.rtaudio_stream_status_t
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
116 // Version returns current RtAudio library version string.
117 func Version() string {
118 return C.GoString(C.rtaudio_version())
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()))
126 if api == C.RTAUDIO_API_UNSPECIFIED {
129 apis = append(apis, API(api))
134 // DeviceInfo is the public device information structure for returning queried values.
135 type DeviceInfo struct {
138 NumOutputChannels int
140 NumDuplexChannels int
144 //rtaudio_format_t native_formats;
146 PreferredSampleRate uint
150 // StreamParams is the structure for specifying input or output stream parameters.
151 type StreamParams struct {
157 // StreamFlags is a set of RtAudio stream option flags.
158 type StreamFlags C.rtaudio_stream_flags_t
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
173 // StreamOptions is the structure for specifying stream options.
174 type StreamOptions struct {
181 // RtAudio is a "controller" used to select an available audio i/o interface.
182 type RtAudio interface {
185 Devices() ([]DeviceInfo, error)
186 DefaultOutputDevice() int
187 DefaultInputDevice() int
189 Open(out, in *StreamParams, format Format, sampleRate uint, frames uint, cb Callback, opts *StreamOptions) error
198 Latency() (int, error)
199 SampleRate() (uint, error)
200 Time() (time.Duration, error)
201 SetTime(time.Duration) error
206 type rtaudio struct {
214 var _ RtAudio = &rtaudio{}
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)))
222 return &rtaudio{audio: audio}, nil
225 func (audio *rtaudio) Destroy() {
226 C.rtaudio_destroy(audio.audio)
229 func (audio *rtaudio) CurrentAPI() API {
230 return API(C.rtaudio_current_api(audio.audio))
233 func (audio *rtaudio) DefaultInputDevice() int {
234 return int(C.rtaudio_get_default_input_device(audio.audio))
237 func (audio *rtaudio) DefaultOutputDevice() int {
238 return int(C.rtaudio_get_default_output_device(audio.audio))
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)))
250 for _, r := range cinfo.sample_rates {
254 sr = append(sr, int(r))
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),
272 // Format defines RtAudio data format type.
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
290 // Buffer is a common interface for audio buffers of various data format types.
291 type Buffer interface {
301 // Int24 is a helper type to convert int32 values to int24 and back.
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)
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
325 func (b *buffer) Len() int {
332 func (b *buffer) Int8() []int8 {
333 if b.format != FormatInt8 {
339 return (*[1 << 30]int8)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
342 func (b *buffer) Int16() []int16 {
343 if b.format != FormatInt16 {
349 return (*[1 << 29]int16)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
352 func (b *buffer) Int24() []Int24 {
353 if b.format != FormatInt24 {
359 return (*[1 << 28]Int24)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
362 func (b *buffer) Int32() []int32 {
363 if b.format != FormatInt32 {
369 return (*[1 << 27]int32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
372 func (b *buffer) Float32() []float32 {
373 if b.format != FormatFloat32 {
379 return (*[1 << 27]float32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
382 func (b *buffer) Float64() []float64 {
383 if b.format != FormatFloat64 {
389 return (*[1 << 23]float64)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
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
398 audios = map[int]*rtaudio{}
401 func registerAudio(a *rtaudio) int {
405 if _, ok := audios[i]; !ok {
412 func unregisterAudio(a *rtaudio) {
415 for i := 0; i < len(audios); i++ {
423 func findAudio(k int) *rtaudio {
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 {
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)))
441 func (audio *rtaudio) Open(out, in *StreamParams, format Format, sampleRate uint,
442 frames uint, cb Callback, opts *StreamOptions) error {
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
452 audio.inputChannels = 0
453 audio.outputChannels = 0
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)
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)
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)
474 framesCount := C.uint(frames)
475 audio.format = format
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)))
487 func (audio *rtaudio) Close() {
488 unregisterAudio(audio)
489 C.rtaudio_close_stream(audio.audio)
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)))
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)))
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)))
516 func (audio *rtaudio) IsOpen() bool {
517 return C.rtaudio_is_stream_open(audio.audio) != 0
520 func (audio *rtaudio) IsRunning() bool {
521 return C.rtaudio_is_stream_running(audio.audio) != 0
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)))
529 return int(latency), nil
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)))
537 return uint(sampleRate), nil
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)))
545 return time.Duration(time.Microsecond * time.Duration(sec*1000000.0)), nil
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)))
557 func (audio *rtaudio) ShowWarnings(show bool) {
559 C.rtaudio_show_warnings(audio.audio, 1)
561 C.rtaudio_show_warnings(audio.audio, 0)