[34] | 1 | /* |
---|
| 2 | * QEMU OS X CoreAudio audio driver |
---|
| 3 | * |
---|
| 4 | * Copyright (c) 2005 Mike Kronenberg |
---|
| 5 | * |
---|
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
---|
| 7 | * of this software and associated documentation files (the "Software"), to deal |
---|
| 8 | * in the Software without restriction, including without limitation the rights |
---|
| 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
---|
| 10 | * copies of the Software, and to permit persons to whom the Software is |
---|
| 11 | * furnished to do so, subject to the following conditions: |
---|
| 12 | * |
---|
| 13 | * The above copyright notice and this permission notice shall be included in |
---|
| 14 | * all copies or substantial portions of the Software. |
---|
| 15 | * |
---|
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
---|
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
---|
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
---|
| 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
---|
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
---|
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
---|
| 22 | * THE SOFTWARE. |
---|
| 23 | */ |
---|
| 24 | |
---|
| 25 | #include <CoreAudio/CoreAudio.h> |
---|
| 26 | #include <string.h> /* strerror */ |
---|
| 27 | #include <pthread.h> /* pthread_X */ |
---|
| 28 | |
---|
| 29 | #include "vl.h" |
---|
| 30 | |
---|
| 31 | #define AUDIO_CAP "coreaudio" |
---|
| 32 | #include "audio_int.h" |
---|
| 33 | |
---|
| 34 | struct { |
---|
| 35 | int buffer_frames; |
---|
| 36 | int nbuffers; |
---|
| 37 | int isAtexit; |
---|
| 38 | } conf = { |
---|
| 39 | .buffer_frames = 512, |
---|
| 40 | .nbuffers = 4, |
---|
| 41 | .isAtexit = 0 |
---|
| 42 | }; |
---|
| 43 | |
---|
| 44 | typedef struct coreaudioVoiceOut { |
---|
| 45 | HWVoiceOut hw; |
---|
| 46 | pthread_mutex_t mutex; |
---|
| 47 | int isAtexit; |
---|
| 48 | AudioDeviceID outputDeviceID; |
---|
| 49 | UInt32 audioDevicePropertyBufferFrameSize; |
---|
| 50 | AudioStreamBasicDescription outputStreamBasicDescription; |
---|
| 51 | int live; |
---|
| 52 | int decr; |
---|
| 53 | int rpos; |
---|
| 54 | } coreaudioVoiceOut; |
---|
| 55 | |
---|
| 56 | static void coreaudio_logstatus (OSStatus status) |
---|
| 57 | { |
---|
| 58 | char *str = "BUG"; |
---|
| 59 | |
---|
| 60 | switch(status) { |
---|
| 61 | case kAudioHardwareNoError: |
---|
| 62 | str = "kAudioHardwareNoError"; |
---|
| 63 | break; |
---|
| 64 | |
---|
| 65 | case kAudioHardwareNotRunningError: |
---|
| 66 | str = "kAudioHardwareNotRunningError"; |
---|
| 67 | break; |
---|
| 68 | |
---|
| 69 | case kAudioHardwareUnspecifiedError: |
---|
| 70 | str = "kAudioHardwareUnspecifiedError"; |
---|
| 71 | break; |
---|
| 72 | |
---|
| 73 | case kAudioHardwareUnknownPropertyError: |
---|
| 74 | str = "kAudioHardwareUnknownPropertyError"; |
---|
| 75 | break; |
---|
| 76 | |
---|
| 77 | case kAudioHardwareBadPropertySizeError: |
---|
| 78 | str = "kAudioHardwareBadPropertySizeError"; |
---|
| 79 | break; |
---|
| 80 | |
---|
| 81 | case kAudioHardwareIllegalOperationError: |
---|
| 82 | str = "kAudioHardwareIllegalOperationError"; |
---|
| 83 | break; |
---|
| 84 | |
---|
| 85 | case kAudioHardwareBadDeviceError: |
---|
| 86 | str = "kAudioHardwareBadDeviceError"; |
---|
| 87 | break; |
---|
| 88 | |
---|
| 89 | case kAudioHardwareBadStreamError: |
---|
| 90 | str = "kAudioHardwareBadStreamError"; |
---|
| 91 | break; |
---|
| 92 | |
---|
| 93 | case kAudioHardwareUnsupportedOperationError: |
---|
| 94 | str = "kAudioHardwareUnsupportedOperationError"; |
---|
| 95 | break; |
---|
| 96 | |
---|
| 97 | case kAudioDeviceUnsupportedFormatError: |
---|
| 98 | str = "kAudioDeviceUnsupportedFormatError"; |
---|
| 99 | break; |
---|
| 100 | |
---|
| 101 | case kAudioDevicePermissionsError: |
---|
| 102 | str = "kAudioDevicePermissionsError"; |
---|
| 103 | break; |
---|
| 104 | |
---|
| 105 | default: |
---|
| 106 | AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status); |
---|
| 107 | return; |
---|
| 108 | } |
---|
| 109 | |
---|
| 110 | AUD_log (AUDIO_CAP, "Reason: %s\n", str); |
---|
| 111 | } |
---|
| 112 | |
---|
| 113 | static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( |
---|
| 114 | OSStatus status, |
---|
| 115 | const char *fmt, |
---|
| 116 | ... |
---|
| 117 | ) |
---|
| 118 | { |
---|
| 119 | va_list ap; |
---|
| 120 | |
---|
| 121 | va_start (ap, fmt); |
---|
| 122 | AUD_log (AUDIO_CAP, fmt, ap); |
---|
| 123 | va_end (ap); |
---|
| 124 | |
---|
| 125 | coreaudio_logstatus (status); |
---|
| 126 | } |
---|
| 127 | |
---|
| 128 | static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( |
---|
| 129 | OSStatus status, |
---|
| 130 | const char *typ, |
---|
| 131 | const char *fmt, |
---|
| 132 | ... |
---|
| 133 | ) |
---|
| 134 | { |
---|
| 135 | va_list ap; |
---|
| 136 | |
---|
| 137 | AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); |
---|
| 138 | |
---|
| 139 | va_start (ap, fmt); |
---|
| 140 | AUD_vlog (AUDIO_CAP, fmt, ap); |
---|
| 141 | va_end (ap); |
---|
| 142 | |
---|
| 143 | coreaudio_logstatus (status); |
---|
| 144 | } |
---|
| 145 | |
---|
| 146 | static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) |
---|
| 147 | { |
---|
| 148 | OSStatus status; |
---|
| 149 | UInt32 result = 0; |
---|
| 150 | UInt32 propertySize = sizeof(outputDeviceID); |
---|
| 151 | status = AudioDeviceGetProperty( |
---|
| 152 | outputDeviceID, 0, 0, |
---|
| 153 | kAudioDevicePropertyDeviceIsRunning, &propertySize, &result); |
---|
| 154 | if (status != kAudioHardwareNoError) { |
---|
| 155 | coreaudio_logerr(status, |
---|
| 156 | "Could not determine whether Device is playing\n"); |
---|
| 157 | } |
---|
| 158 | return result; |
---|
| 159 | } |
---|
| 160 | |
---|
| 161 | static void coreaudio_atexit (void) |
---|
| 162 | { |
---|
| 163 | conf.isAtexit = 1; |
---|
| 164 | } |
---|
| 165 | |
---|
| 166 | static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) |
---|
| 167 | { |
---|
| 168 | int err; |
---|
| 169 | |
---|
| 170 | err = pthread_mutex_lock (&core->mutex); |
---|
| 171 | if (err) { |
---|
| 172 | dolog ("Could not lock voice for %s\nReason: %s\n", |
---|
| 173 | fn_name, strerror (err)); |
---|
| 174 | return -1; |
---|
| 175 | } |
---|
| 176 | return 0; |
---|
| 177 | } |
---|
| 178 | |
---|
| 179 | static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name) |
---|
| 180 | { |
---|
| 181 | int err; |
---|
| 182 | |
---|
| 183 | err = pthread_mutex_unlock (&core->mutex); |
---|
| 184 | if (err) { |
---|
| 185 | dolog ("Could not unlock voice for %s\nReason: %s\n", |
---|
| 186 | fn_name, strerror (err)); |
---|
| 187 | return -1; |
---|
| 188 | } |
---|
| 189 | return 0; |
---|
| 190 | } |
---|
| 191 | |
---|
| 192 | static int coreaudio_run_out (HWVoiceOut *hw) |
---|
| 193 | { |
---|
| 194 | int live, decr; |
---|
| 195 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |
---|
| 196 | |
---|
| 197 | if (coreaudio_lock (core, "coreaudio_run_out")) { |
---|
| 198 | return 0; |
---|
| 199 | } |
---|
| 200 | |
---|
| 201 | live = audio_pcm_hw_get_live_out (hw); |
---|
| 202 | |
---|
| 203 | if (core->decr > live) { |
---|
| 204 | ldebug ("core->decr %d live %d core->live %d\n", |
---|
| 205 | core->decr, |
---|
| 206 | live, |
---|
| 207 | core->live); |
---|
| 208 | } |
---|
| 209 | |
---|
| 210 | decr = audio_MIN (core->decr, live); |
---|
| 211 | core->decr -= decr; |
---|
| 212 | |
---|
| 213 | core->live = live - decr; |
---|
| 214 | hw->rpos = core->rpos; |
---|
| 215 | |
---|
| 216 | coreaudio_unlock (core, "coreaudio_run_out"); |
---|
| 217 | return decr; |
---|
| 218 | } |
---|
| 219 | |
---|
| 220 | /* callback to feed audiooutput buffer */ |
---|
| 221 | static OSStatus audioDeviceIOProc( |
---|
| 222 | AudioDeviceID inDevice, |
---|
| 223 | const AudioTimeStamp* inNow, |
---|
| 224 | const AudioBufferList* inInputData, |
---|
| 225 | const AudioTimeStamp* inInputTime, |
---|
| 226 | AudioBufferList* outOutputData, |
---|
| 227 | const AudioTimeStamp* inOutputTime, |
---|
| 228 | void* hwptr) |
---|
| 229 | { |
---|
| 230 | UInt32 frame, frameCount; |
---|
| 231 | float *out = outOutputData->mBuffers[0].mData; |
---|
| 232 | HWVoiceOut *hw = hwptr; |
---|
| 233 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; |
---|
| 234 | int rpos, live; |
---|
| 235 | st_sample_t *src; |
---|
| 236 | #ifndef FLOAT_MIXENG |
---|
| 237 | #ifdef RECIPROCAL |
---|
| 238 | const float scale = 1.f / UINT_MAX; |
---|
| 239 | #else |
---|
| 240 | const float scale = UINT_MAX; |
---|
| 241 | #endif |
---|
| 242 | #endif |
---|
| 243 | |
---|
| 244 | if (coreaudio_lock (core, "audioDeviceIOProc")) { |
---|
| 245 | inInputTime = 0; |
---|
| 246 | return 0; |
---|
| 247 | } |
---|
| 248 | |
---|
| 249 | frameCount = core->audioDevicePropertyBufferFrameSize; |
---|
| 250 | live = core->live; |
---|
| 251 | |
---|
| 252 | /* if there are not enough samples, set signal and return */ |
---|
| 253 | if (live < frameCount) { |
---|
| 254 | inInputTime = 0; |
---|
| 255 | coreaudio_unlock (core, "audioDeviceIOProc(empty)"); |
---|
| 256 | return 0; |
---|
| 257 | } |
---|
| 258 | |
---|
| 259 | rpos = core->rpos; |
---|
| 260 | src = hw->mix_buf + rpos; |
---|
| 261 | |
---|
| 262 | /* fill buffer */ |
---|
| 263 | for (frame = 0; frame < frameCount; frame++) { |
---|
| 264 | #ifdef FLOAT_MIXENG |
---|
| 265 | *out++ = src[frame].l; /* left channel */ |
---|
| 266 | *out++ = src[frame].r; /* right channel */ |
---|
| 267 | #else |
---|
| 268 | #ifdef RECIPROCAL |
---|
| 269 | *out++ = src[frame].l * scale; /* left channel */ |
---|
| 270 | *out++ = src[frame].r * scale; /* right channel */ |
---|
| 271 | #else |
---|
| 272 | *out++ = src[frame].l / scale; /* left channel */ |
---|
| 273 | *out++ = src[frame].r / scale; /* right channel */ |
---|
| 274 | #endif |
---|
| 275 | #endif |
---|
| 276 | } |
---|
| 277 | |
---|
| 278 | rpos = (rpos + frameCount) % hw->samples; |
---|
| 279 | core->decr += frameCount; |
---|
| 280 | core->rpos = rpos; |
---|
| 281 | |
---|
| 282 | coreaudio_unlock (core, "audioDeviceIOProc"); |
---|
| 283 | return 0; |
---|
| 284 | } |
---|
| 285 | |
---|
| 286 | static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) |
---|
| 287 | { |
---|
| 288 | return audio_pcm_sw_write (sw, buf, len); |
---|
| 289 | } |
---|
| 290 | |
---|
| 291 | static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as) |
---|
| 292 | { |
---|
| 293 | OSStatus status; |
---|
| 294 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |
---|
| 295 | UInt32 propertySize; |
---|
| 296 | int err; |
---|
| 297 | int bits = 8; |
---|
| 298 | const char *typ = "playback"; |
---|
| 299 | AudioValueRange frameRange; |
---|
| 300 | |
---|
| 301 | /* create mutex */ |
---|
| 302 | err = pthread_mutex_init(&core->mutex, NULL); |
---|
| 303 | if (err) { |
---|
| 304 | dolog("Could not create mutex\nReason: %s\n", strerror (err)); |
---|
| 305 | return -1; |
---|
| 306 | } |
---|
| 307 | |
---|
| 308 | if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) { |
---|
| 309 | bits = 16; |
---|
| 310 | } |
---|
| 311 | |
---|
| 312 | audio_pcm_init_info (&hw->info, as); |
---|
| 313 | |
---|
| 314 | /* open default output device */ |
---|
| 315 | propertySize = sizeof(core->outputDeviceID); |
---|
| 316 | status = AudioHardwareGetProperty( |
---|
| 317 | kAudioHardwarePropertyDefaultOutputDevice, |
---|
| 318 | &propertySize, |
---|
| 319 | &core->outputDeviceID); |
---|
| 320 | if (status != kAudioHardwareNoError) { |
---|
| 321 | coreaudio_logerr2 (status, typ, |
---|
| 322 | "Could not get default output Device\n"); |
---|
| 323 | return -1; |
---|
| 324 | } |
---|
| 325 | if (core->outputDeviceID == kAudioDeviceUnknown) { |
---|
| 326 | dolog ("Could not initialize %s - Unknown Audiodevice\n", typ); |
---|
| 327 | return -1; |
---|
| 328 | } |
---|
| 329 | |
---|
| 330 | /* get minimum and maximum buffer frame sizes */ |
---|
| 331 | propertySize = sizeof(frameRange); |
---|
| 332 | status = AudioDeviceGetProperty( |
---|
| 333 | core->outputDeviceID, |
---|
| 334 | 0, |
---|
| 335 | 0, |
---|
| 336 | kAudioDevicePropertyBufferFrameSizeRange, |
---|
| 337 | &propertySize, |
---|
| 338 | &frameRange); |
---|
| 339 | if (status != kAudioHardwareNoError) { |
---|
| 340 | coreaudio_logerr2 (status, typ, |
---|
| 341 | "Could not get device buffer frame range\n"); |
---|
| 342 | return -1; |
---|
| 343 | } |
---|
| 344 | |
---|
| 345 | if (frameRange.mMinimum > conf.buffer_frames) { |
---|
| 346 | core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; |
---|
| 347 | dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); |
---|
| 348 | } |
---|
| 349 | else if (frameRange.mMaximum < conf.buffer_frames) { |
---|
| 350 | core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; |
---|
| 351 | dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); |
---|
| 352 | } |
---|
| 353 | else { |
---|
| 354 | core->audioDevicePropertyBufferFrameSize = conf.buffer_frames; |
---|
| 355 | } |
---|
| 356 | |
---|
| 357 | /* set Buffer Frame Size */ |
---|
| 358 | propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); |
---|
| 359 | status = AudioDeviceSetProperty( |
---|
| 360 | core->outputDeviceID, |
---|
| 361 | NULL, |
---|
| 362 | 0, |
---|
| 363 | false, |
---|
| 364 | kAudioDevicePropertyBufferFrameSize, |
---|
| 365 | propertySize, |
---|
| 366 | &core->audioDevicePropertyBufferFrameSize); |
---|
| 367 | if (status != kAudioHardwareNoError) { |
---|
| 368 | coreaudio_logerr2 (status, typ, |
---|
| 369 | "Could not set device buffer frame size %ld\n", |
---|
| 370 | core->audioDevicePropertyBufferFrameSize); |
---|
| 371 | return -1; |
---|
| 372 | } |
---|
| 373 | |
---|
| 374 | /* get Buffer Frame Size */ |
---|
| 375 | propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); |
---|
| 376 | status = AudioDeviceGetProperty( |
---|
| 377 | core->outputDeviceID, |
---|
| 378 | 0, |
---|
| 379 | false, |
---|
| 380 | kAudioDevicePropertyBufferFrameSize, |
---|
| 381 | &propertySize, |
---|
| 382 | &core->audioDevicePropertyBufferFrameSize); |
---|
| 383 | if (status != kAudioHardwareNoError) { |
---|
| 384 | coreaudio_logerr2 (status, typ, |
---|
| 385 | "Could not get device buffer frame size\n"); |
---|
| 386 | return -1; |
---|
| 387 | } |
---|
| 388 | hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize; |
---|
| 389 | |
---|
| 390 | /* get StreamFormat */ |
---|
| 391 | propertySize = sizeof(core->outputStreamBasicDescription); |
---|
| 392 | status = AudioDeviceGetProperty( |
---|
| 393 | core->outputDeviceID, |
---|
| 394 | 0, |
---|
| 395 | false, |
---|
| 396 | kAudioDevicePropertyStreamFormat, |
---|
| 397 | &propertySize, |
---|
| 398 | &core->outputStreamBasicDescription); |
---|
| 399 | if (status != kAudioHardwareNoError) { |
---|
| 400 | coreaudio_logerr2 (status, typ, |
---|
| 401 | "Could not get Device Stream properties\n"); |
---|
| 402 | core->outputDeviceID = kAudioDeviceUnknown; |
---|
| 403 | return -1; |
---|
| 404 | } |
---|
| 405 | |
---|
| 406 | /* set Samplerate */ |
---|
| 407 | core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; |
---|
| 408 | propertySize = sizeof(core->outputStreamBasicDescription); |
---|
| 409 | status = AudioDeviceSetProperty( |
---|
| 410 | core->outputDeviceID, |
---|
| 411 | 0, |
---|
| 412 | 0, |
---|
| 413 | 0, |
---|
| 414 | kAudioDevicePropertyStreamFormat, |
---|
| 415 | propertySize, |
---|
| 416 | &core->outputStreamBasicDescription); |
---|
| 417 | if (status != kAudioHardwareNoError) { |
---|
| 418 | coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", |
---|
| 419 | as->freq); |
---|
| 420 | core->outputDeviceID = kAudioDeviceUnknown; |
---|
| 421 | return -1; |
---|
| 422 | } |
---|
| 423 | |
---|
| 424 | /* set Callback */ |
---|
| 425 | status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw); |
---|
| 426 | if (status != kAudioHardwareNoError) { |
---|
| 427 | coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); |
---|
| 428 | core->outputDeviceID = kAudioDeviceUnknown; |
---|
| 429 | return -1; |
---|
| 430 | } |
---|
| 431 | |
---|
| 432 | /* start Playback */ |
---|
| 433 | if (!isPlaying(core->outputDeviceID)) { |
---|
| 434 | status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); |
---|
| 435 | if (status != kAudioHardwareNoError) { |
---|
| 436 | coreaudio_logerr2 (status, typ, "Could not start playback\n"); |
---|
| 437 | AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); |
---|
| 438 | core->outputDeviceID = kAudioDeviceUnknown; |
---|
| 439 | return -1; |
---|
| 440 | } |
---|
| 441 | } |
---|
| 442 | |
---|
| 443 | return 0; |
---|
| 444 | } |
---|
| 445 | |
---|
| 446 | static void coreaudio_fini_out (HWVoiceOut *hw) |
---|
| 447 | { |
---|
| 448 | OSStatus status; |
---|
| 449 | int err; |
---|
| 450 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |
---|
| 451 | |
---|
| 452 | if (!conf.isAtexit) { |
---|
| 453 | /* stop playback */ |
---|
| 454 | if (isPlaying(core->outputDeviceID)) { |
---|
| 455 | status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); |
---|
| 456 | if (status != kAudioHardwareNoError) { |
---|
| 457 | coreaudio_logerr (status, "Could not stop playback\n"); |
---|
| 458 | } |
---|
| 459 | } |
---|
| 460 | |
---|
| 461 | /* remove callback */ |
---|
| 462 | status = AudioDeviceRemoveIOProc(core->outputDeviceID, |
---|
| 463 | audioDeviceIOProc); |
---|
| 464 | if (status != kAudioHardwareNoError) { |
---|
| 465 | coreaudio_logerr (status, "Could not remove IOProc\n"); |
---|
| 466 | } |
---|
| 467 | } |
---|
| 468 | core->outputDeviceID = kAudioDeviceUnknown; |
---|
| 469 | |
---|
| 470 | /* destroy mutex */ |
---|
| 471 | err = pthread_mutex_destroy(&core->mutex); |
---|
| 472 | if (err) { |
---|
| 473 | dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); |
---|
| 474 | } |
---|
| 475 | } |
---|
| 476 | |
---|
| 477 | static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) |
---|
| 478 | { |
---|
| 479 | OSStatus status; |
---|
| 480 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |
---|
| 481 | |
---|
| 482 | switch (cmd) { |
---|
| 483 | case VOICE_ENABLE: |
---|
| 484 | /* start playback */ |
---|
| 485 | if (!isPlaying(core->outputDeviceID)) { |
---|
| 486 | status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); |
---|
| 487 | if (status != kAudioHardwareNoError) { |
---|
| 488 | coreaudio_logerr (status, "Could not resume playback\n"); |
---|
| 489 | } |
---|
| 490 | } |
---|
| 491 | break; |
---|
| 492 | |
---|
| 493 | case VOICE_DISABLE: |
---|
| 494 | /* stop playback */ |
---|
| 495 | if (!conf.isAtexit) { |
---|
| 496 | if (isPlaying(core->outputDeviceID)) { |
---|
| 497 | status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); |
---|
| 498 | if (status != kAudioHardwareNoError) { |
---|
| 499 | coreaudio_logerr (status, "Could not pause playback\n"); |
---|
| 500 | } |
---|
| 501 | } |
---|
| 502 | } |
---|
| 503 | break; |
---|
| 504 | } |
---|
| 505 | return 0; |
---|
| 506 | } |
---|
| 507 | |
---|
| 508 | static void *coreaudio_audio_init (void) |
---|
| 509 | { |
---|
| 510 | atexit(coreaudio_atexit); |
---|
| 511 | return &coreaudio_audio_init; |
---|
| 512 | } |
---|
| 513 | |
---|
| 514 | static void coreaudio_audio_fini (void *opaque) |
---|
| 515 | { |
---|
| 516 | (void) opaque; |
---|
| 517 | } |
---|
| 518 | |
---|
| 519 | static struct audio_option coreaudio_options[] = { |
---|
| 520 | {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames, |
---|
| 521 | "Size of the buffer in frames", NULL, 0}, |
---|
| 522 | {"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers, |
---|
| 523 | "Number of buffers", NULL, 0}, |
---|
| 524 | {NULL, 0, NULL, NULL, NULL, 0} |
---|
| 525 | }; |
---|
| 526 | |
---|
| 527 | static struct audio_pcm_ops coreaudio_pcm_ops = { |
---|
| 528 | coreaudio_init_out, |
---|
| 529 | coreaudio_fini_out, |
---|
| 530 | coreaudio_run_out, |
---|
| 531 | coreaudio_write, |
---|
| 532 | coreaudio_ctl_out, |
---|
| 533 | |
---|
| 534 | NULL, |
---|
| 535 | NULL, |
---|
| 536 | NULL, |
---|
| 537 | NULL, |
---|
| 538 | NULL |
---|
| 539 | }; |
---|
| 540 | |
---|
| 541 | struct audio_driver coreaudio_audio_driver = { |
---|
| 542 | INIT_FIELD (name = ) "coreaudio", |
---|
| 543 | INIT_FIELD (descr = ) |
---|
| 544 | "CoreAudio http://developer.apple.com/audio/coreaudio.html", |
---|
| 545 | INIT_FIELD (options = ) coreaudio_options, |
---|
| 546 | INIT_FIELD (init = ) coreaudio_audio_init, |
---|
| 547 | INIT_FIELD (fini = ) coreaudio_audio_fini, |
---|
| 548 | INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops, |
---|
| 549 | INIT_FIELD (can_be_default = ) 1, |
---|
| 550 | INIT_FIELD (max_voices_out = ) 1, |
---|
| 551 | INIT_FIELD (max_voices_in = ) 0, |
---|
| 552 | INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut), |
---|
| 553 | INIT_FIELD (voice_size_in = ) 0 |
---|
| 554 | }; |
---|