1package 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 -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid -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 27extern int goCallback(void *out, void *in, unsigned int nFrames, 28 double stream_time, rtaudio_stream_status_t status, void *userdata); 29 30static 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*/ 43import "C" 44import ( 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. 53type API C.rtaudio_api_t 54 55const ( 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 76) 77 78func (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. 105type StreamStatus C.rtaudio_stream_status_t 106 107const ( 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. 117func Version() string { 118 return C.GoString(C.rtaudio_version()) 119} 120 121// CompiledAPI determines the available compiled audio APIs. 122func 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. 135type 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. 151type StreamParams struct { 152 DeviceID uint 153 NumChannels uint 154 FirstChannel uint 155} 156 157// StreamFlags is a set of RtAudio stream option flags. 158type StreamFlags C.rtaudio_stream_flags_t 159 160const ( 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. 174type 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. 182type 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 206type rtaudio struct { 207 audio C.rtaudio_t 208 cb Callback 209 inputChannels int 210 outputChannels int 211 format Format 212} 213 214var _ RtAudio = &rtaudio{} 215 216// Create a new RtAudio instance using the given API. 217func 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 225func (audio *rtaudio) Destroy() { 226 C.rtaudio_destroy(audio.audio) 227} 228 229func (audio *rtaudio) CurrentAPI() API { 230 return API(C.rtaudio_current_api(audio.audio)) 231} 232 233func (audio *rtaudio) DefaultInputDevice() int { 234 return int(C.rtaudio_get_default_input_device(audio.audio)) 235} 236 237func (audio *rtaudio) DefaultOutputDevice() int { 238 return int(C.rtaudio_get_default_output_device(audio.audio)) 239} 240 241func (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. 273type Format int 274 275const ( 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. 291type 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. 302type Int24 [3]byte 303 304// Set Int24 value using the least significant bytes of the given number n. 305func (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. 310func (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 318type buffer struct { 319 format Format 320 length int 321 numChannels int 322 ptr unsafe.Pointer 323} 324 325func (b *buffer) Len() int { 326 if b.ptr == nil { 327 return 0 328 } 329 return b.length 330} 331 332func (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 342func (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 352func (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 362func (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 372func (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 382func (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. 394type Callback func(out Buffer, in Buffer, dur time.Duration, status StreamStatus) int 395 396var ( 397 mu sync.Mutex 398 audios = map[int]*rtaudio{} 399) 400 401func 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 412func 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 423func findAudio(k int) *rtaudio { 424 mu.Lock() 425 defer mu.Unlock() 426 return audios[k] 427} 428 429//export goCallback 430func 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 441func (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 487func (audio *rtaudio) Close() { 488 unregisterAudio(audio) 489 C.rtaudio_close_stream(audio.audio) 490} 491 492func (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 500func (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 508func (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 516func (audio *rtaudio) IsOpen() bool { 517 return C.rtaudio_is_stream_open(audio.audio) != 0 518} 519 520func (audio *rtaudio) IsRunning() bool { 521 return C.rtaudio_is_stream_running(audio.audio) != 0 522} 523 524func (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 532func (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 540func (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 548func (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 557func (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} 564