1 /* 2 * QEMU DirectSound audio driver 3 * 4 * Copyright (c) 2005 Vassili Karpov (malc) 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 /* 26 * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation 27 */ 28 29 #include "qemu/osdep.h" 30 #include "audio.h" 31 32 #define AUDIO_CAP "dsound" 33 #include "audio_int.h" 34 #include "qemu/host-utils.h" 35 #include "qemu/module.h" 36 37 #include <windows.h> 38 #include <mmsystem.h> 39 #include <objbase.h> 40 #include <dsound.h> 41 42 #include "audio_win_int.h" 43 44 /* #define DEBUG_DSOUND */ 45 46 typedef struct { 47 LPDIRECTSOUND dsound; 48 LPDIRECTSOUNDCAPTURE dsound_capture; 49 struct audsettings settings; 50 Audiodev *dev; 51 } dsound; 52 53 typedef struct { 54 HWVoiceOut hw; 55 LPDIRECTSOUNDBUFFER dsound_buffer; 56 bool first_time; 57 dsound *s; 58 } DSoundVoiceOut; 59 60 typedef struct { 61 HWVoiceIn hw; 62 LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; 63 bool first_time; 64 dsound *s; 65 } DSoundVoiceIn; 66 67 static void dsound_log_hresult (HRESULT hr) 68 { 69 const char *str = "BUG"; 70 71 switch (hr) { 72 case DS_OK: 73 str = "The method succeeded"; 74 break; 75 #ifdef DS_NO_VIRTUALIZATION 76 case DS_NO_VIRTUALIZATION: 77 str = "The buffer was created, but another 3D algorithm was substituted"; 78 break; 79 #endif 80 #ifdef DS_INCOMPLETE 81 case DS_INCOMPLETE: 82 str = "The method succeeded, but not all the optional effects were obtained"; 83 break; 84 #endif 85 #ifdef DSERR_ACCESSDENIED 86 case DSERR_ACCESSDENIED: 87 str = "The request failed because access was denied"; 88 break; 89 #endif 90 #ifdef DSERR_ALLOCATED 91 case DSERR_ALLOCATED: 92 str = "The request failed because resources, such as a priority level, were already in use by another caller"; 93 break; 94 #endif 95 #ifdef DSERR_ALREADYINITIALIZED 96 case DSERR_ALREADYINITIALIZED: 97 str = "The object is already initialized"; 98 break; 99 #endif 100 #ifdef DSERR_BADFORMAT 101 case DSERR_BADFORMAT: 102 str = "The specified wave format is not supported"; 103 break; 104 #endif 105 #ifdef DSERR_BADSENDBUFFERGUID 106 case DSERR_BADSENDBUFFERGUID: 107 str = "The GUID specified in an audiopath file does not match a valid mix-in buffer"; 108 break; 109 #endif 110 #ifdef DSERR_BUFFERLOST 111 case DSERR_BUFFERLOST: 112 str = "The buffer memory has been lost and must be restored"; 113 break; 114 #endif 115 #ifdef DSERR_BUFFERTOOSMALL 116 case DSERR_BUFFERTOOSMALL: 117 str = "The buffer size is not great enough to enable effects processing"; 118 break; 119 #endif 120 #ifdef DSERR_CONTROLUNAVAIL 121 case DSERR_CONTROLUNAVAIL: 122 str = "The buffer control (volume, pan, and so on) requested by the caller is not available. Controls must be specified when the buffer is created, using the dwFlags member of DSBUFFERDESC"; 123 break; 124 #endif 125 #ifdef DSERR_DS8_REQUIRED 126 case DSERR_DS8_REQUIRED: 127 str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface"; 128 break; 129 #endif 130 #ifdef DSERR_FXUNAVAILABLE 131 case DSERR_FXUNAVAILABLE: 132 str = "The effects requested could not be found on the system, or they are in the wrong order or in the wrong location; for example, an effect expected in hardware was found in software"; 133 break; 134 #endif 135 #ifdef DSERR_GENERIC 136 case DSERR_GENERIC : 137 str = "An undetermined error occurred inside the DirectSound subsystem"; 138 break; 139 #endif 140 #ifdef DSERR_INVALIDCALL 141 case DSERR_INVALIDCALL: 142 str = "This function is not valid for the current state of this object"; 143 break; 144 #endif 145 #ifdef DSERR_INVALIDPARAM 146 case DSERR_INVALIDPARAM: 147 str = "An invalid parameter was passed to the returning function"; 148 break; 149 #endif 150 #ifdef DSERR_NOAGGREGATION 151 case DSERR_NOAGGREGATION: 152 str = "The object does not support aggregation"; 153 break; 154 #endif 155 #ifdef DSERR_NODRIVER 156 case DSERR_NODRIVER: 157 str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID"; 158 break; 159 #endif 160 #ifdef DSERR_NOINTERFACE 161 case DSERR_NOINTERFACE: 162 str = "The requested COM interface is not available"; 163 break; 164 #endif 165 #ifdef DSERR_OBJECTNOTFOUND 166 case DSERR_OBJECTNOTFOUND: 167 str = "The requested object was not found"; 168 break; 169 #endif 170 #ifdef DSERR_OTHERAPPHASPRIO 171 case DSERR_OTHERAPPHASPRIO: 172 str = "Another application has a higher priority level, preventing this call from succeeding"; 173 break; 174 #endif 175 #ifdef DSERR_OUTOFMEMORY 176 case DSERR_OUTOFMEMORY: 177 str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request"; 178 break; 179 #endif 180 #ifdef DSERR_PRIOLEVELNEEDED 181 case DSERR_PRIOLEVELNEEDED: 182 str = "A cooperative level of DSSCL_PRIORITY or higher is required"; 183 break; 184 #endif 185 #ifdef DSERR_SENDLOOP 186 case DSERR_SENDLOOP: 187 str = "A circular loop of send effects was detected"; 188 break; 189 #endif 190 #ifdef DSERR_UNINITIALIZED 191 case DSERR_UNINITIALIZED: 192 str = "The Initialize method has not been called or has not been called successfully before other methods were called"; 193 break; 194 #endif 195 #ifdef DSERR_UNSUPPORTED 196 case DSERR_UNSUPPORTED: 197 str = "The function called is not supported at this time"; 198 break; 199 #endif 200 default: 201 AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr); 202 return; 203 } 204 205 AUD_log (AUDIO_CAP, "Reason: %s\n", str); 206 } 207 208 static void GCC_FMT_ATTR (2, 3) dsound_logerr ( 209 HRESULT hr, 210 const char *fmt, 211 ... 212 ) 213 { 214 va_list ap; 215 216 va_start (ap, fmt); 217 AUD_vlog (AUDIO_CAP, fmt, ap); 218 va_end (ap); 219 220 dsound_log_hresult (hr); 221 } 222 223 static void GCC_FMT_ATTR (3, 4) dsound_logerr2 ( 224 HRESULT hr, 225 const char *typ, 226 const char *fmt, 227 ... 228 ) 229 { 230 va_list ap; 231 232 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); 233 va_start (ap, fmt); 234 AUD_vlog (AUDIO_CAP, fmt, ap); 235 va_end (ap); 236 237 dsound_log_hresult (hr); 238 } 239 240 #ifdef DEBUG_DSOUND 241 static void print_wave_format (WAVEFORMATEX *wfx) 242 { 243 dolog ("tag = %d\n", wfx->wFormatTag); 244 dolog ("nChannels = %d\n", wfx->nChannels); 245 dolog ("nSamplesPerSec = %ld\n", wfx->nSamplesPerSec); 246 dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec); 247 dolog ("nBlockAlign = %d\n", wfx->nBlockAlign); 248 dolog ("wBitsPerSample = %d\n", wfx->wBitsPerSample); 249 dolog ("cbSize = %d\n", wfx->cbSize); 250 } 251 #endif 252 253 static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s) 254 { 255 HRESULT hr; 256 257 hr = IDirectSoundBuffer_Restore (dsb); 258 259 if (hr != DS_OK) { 260 dsound_logerr (hr, "Could not restore playback buffer\n"); 261 return -1; 262 } 263 return 0; 264 } 265 266 #include "dsound_template.h" 267 #define DSBTYPE_IN 268 #include "dsound_template.h" 269 #undef DSBTYPE_IN 270 271 static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp, 272 dsound *s) 273 { 274 HRESULT hr; 275 276 hr = IDirectSoundBuffer_GetStatus (dsb, statusp); 277 if (FAILED (hr)) { 278 dsound_logerr (hr, "Could not get playback buffer status\n"); 279 return -1; 280 } 281 282 if (*statusp & DSBSTATUS_BUFFERLOST) { 283 dsound_restore_out(dsb, s); 284 return -1; 285 } 286 287 return 0; 288 } 289 290 static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb, 291 DWORD *statusp) 292 { 293 HRESULT hr; 294 295 hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp); 296 if (FAILED (hr)) { 297 dsound_logerr (hr, "Could not get capture buffer status\n"); 298 return -1; 299 } 300 301 return 0; 302 } 303 304 static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, 305 dsound *s) 306 { 307 int err; 308 LPVOID p1, p2; 309 DWORD blen1, blen2, len1, len2; 310 311 err = dsound_lock_out ( 312 dsb, 313 &hw->info, 314 0, 315 hw->size_emul, 316 &p1, &p2, 317 &blen1, &blen2, 318 1, 319 s 320 ); 321 if (err) { 322 return; 323 } 324 325 len1 = blen1 / hw->info.bytes_per_frame; 326 len2 = blen2 / hw->info.bytes_per_frame; 327 328 #ifdef DEBUG_DSOUND 329 dolog ("clear %p,%ld,%ld %p,%ld,%ld\n", 330 p1, blen1, len1, 331 p2, blen2, len2); 332 #endif 333 334 if (p1 && len1) { 335 audio_pcm_info_clear_buf (&hw->info, p1, len1); 336 } 337 338 if (p2 && len2) { 339 audio_pcm_info_clear_buf (&hw->info, p2, len2); 340 } 341 342 dsound_unlock_out (dsb, p1, p2, blen1, blen2); 343 } 344 345 static int dsound_open (dsound *s) 346 { 347 HRESULT hr; 348 HWND hwnd; 349 350 hwnd = GetForegroundWindow (); 351 hr = IDirectSound_SetCooperativeLevel ( 352 s->dsound, 353 hwnd, 354 DSSCL_PRIORITY 355 ); 356 357 if (FAILED (hr)) { 358 dsound_logerr (hr, "Could not set cooperative level for window %p\n", 359 hwnd); 360 return -1; 361 } 362 363 return 0; 364 } 365 366 static void dsound_enable_out(HWVoiceOut *hw, bool enable) 367 { 368 HRESULT hr; 369 DWORD status; 370 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; 371 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; 372 dsound *s = ds->s; 373 374 if (!dsb) { 375 dolog ("Attempt to control voice without a buffer\n"); 376 return; 377 } 378 379 if (enable) { 380 if (dsound_get_status_out (dsb, &status, s)) { 381 return; 382 } 383 384 if (status & DSBSTATUS_PLAYING) { 385 dolog ("warning: Voice is already playing\n"); 386 return; 387 } 388 389 dsound_clear_sample (hw, dsb, s); 390 391 hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); 392 if (FAILED (hr)) { 393 dsound_logerr (hr, "Could not start playing buffer\n"); 394 return; 395 } 396 } else { 397 if (dsound_get_status_out (dsb, &status, s)) { 398 return; 399 } 400 401 if (status & DSBSTATUS_PLAYING) { 402 hr = IDirectSoundBuffer_Stop (dsb); 403 if (FAILED (hr)) { 404 dsound_logerr (hr, "Could not stop playing buffer\n"); 405 return; 406 } 407 } 408 else { 409 dolog ("warning: Voice is not playing\n"); 410 } 411 } 412 } 413 414 static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size) 415 { 416 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; 417 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; 418 HRESULT hr; 419 DWORD ppos, wpos, act_size; 420 size_t req_size; 421 int err; 422 void *ret; 423 424 hr = IDirectSoundBuffer_GetCurrentPosition( 425 dsb, &ppos, ds->first_time ? &wpos : NULL); 426 if (FAILED(hr)) { 427 dsound_logerr(hr, "Could not get playback buffer position\n"); 428 *size = 0; 429 return NULL; 430 } 431 432 if (ds->first_time) { 433 hw->pos_emul = wpos; 434 ds->first_time = false; 435 } 436 437 req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul); 438 req_size = MIN(req_size, hw->size_emul - hw->pos_emul); 439 440 if (req_size == 0) { 441 *size = 0; 442 return NULL; 443 } 444 445 err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL, 446 &act_size, NULL, false, ds->s); 447 if (err) { 448 dolog("Failed to lock buffer\n"); 449 *size = 0; 450 return NULL; 451 } 452 453 *size = act_size; 454 return ret; 455 } 456 457 static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len) 458 { 459 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; 460 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; 461 int err = dsound_unlock_out(dsb, buf, NULL, len, 0); 462 463 if (err) { 464 dolog("Failed to unlock buffer!!\n"); 465 return 0; 466 } 467 hw->pos_emul = (hw->pos_emul + len) % hw->size_emul; 468 469 return len; 470 } 471 472 static void dsound_enable_in(HWVoiceIn *hw, bool enable) 473 { 474 HRESULT hr; 475 DWORD status; 476 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; 477 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; 478 479 if (!dscb) { 480 dolog ("Attempt to control capture voice without a buffer\n"); 481 return; 482 } 483 484 if (enable) { 485 if (dsound_get_status_in (dscb, &status)) { 486 return; 487 } 488 489 if (status & DSCBSTATUS_CAPTURING) { 490 dolog ("warning: Voice is already capturing\n"); 491 return; 492 } 493 494 /* clear ?? */ 495 496 hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING); 497 if (FAILED (hr)) { 498 dsound_logerr (hr, "Could not start capturing\n"); 499 return; 500 } 501 } else { 502 if (dsound_get_status_in (dscb, &status)) { 503 return; 504 } 505 506 if (status & DSCBSTATUS_CAPTURING) { 507 hr = IDirectSoundCaptureBuffer_Stop (dscb); 508 if (FAILED (hr)) { 509 dsound_logerr (hr, "Could not stop capturing\n"); 510 return; 511 } 512 } 513 else { 514 dolog ("warning: Voice is not capturing\n"); 515 } 516 } 517 } 518 519 static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size) 520 { 521 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; 522 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; 523 HRESULT hr; 524 DWORD cpos, rpos, act_size; 525 size_t req_size; 526 int err; 527 void *ret; 528 529 hr = IDirectSoundCaptureBuffer_GetCurrentPosition( 530 dscb, &cpos, ds->first_time ? &rpos : NULL); 531 if (FAILED(hr)) { 532 dsound_logerr(hr, "Could not get capture buffer position\n"); 533 *size = 0; 534 return NULL; 535 } 536 537 if (ds->first_time) { 538 hw->pos_emul = rpos; 539 ds->first_time = false; 540 } 541 542 req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul); 543 req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul)); 544 545 if (req_size == 0) { 546 *size = 0; 547 return NULL; 548 } 549 550 err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL, 551 &act_size, NULL, false, ds->s); 552 if (err) { 553 dolog("Failed to lock buffer\n"); 554 *size = 0; 555 return NULL; 556 } 557 558 *size = act_size; 559 return ret; 560 } 561 562 static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len) 563 { 564 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; 565 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; 566 int err = dsound_unlock_in(dscb, buf, NULL, len, 0); 567 568 if (err) { 569 dolog("Failed to unlock buffer!!\n"); 570 return; 571 } 572 hw->pos_emul = (hw->pos_emul + len) % hw->size_emul; 573 } 574 575 static void dsound_audio_fini (void *opaque) 576 { 577 HRESULT hr; 578 dsound *s = opaque; 579 580 if (!s->dsound) { 581 g_free(s); 582 return; 583 } 584 585 hr = IDirectSound_Release (s->dsound); 586 if (FAILED (hr)) { 587 dsound_logerr (hr, "Could not release DirectSound\n"); 588 } 589 s->dsound = NULL; 590 591 if (!s->dsound_capture) { 592 g_free(s); 593 return; 594 } 595 596 hr = IDirectSoundCapture_Release (s->dsound_capture); 597 if (FAILED (hr)) { 598 dsound_logerr (hr, "Could not release DirectSoundCapture\n"); 599 } 600 s->dsound_capture = NULL; 601 602 g_free(s); 603 } 604 605 static void *dsound_audio_init(Audiodev *dev) 606 { 607 int err; 608 HRESULT hr; 609 dsound *s = g_malloc0(sizeof(dsound)); 610 AudiodevDsoundOptions *dso; 611 612 assert(dev->driver == AUDIODEV_DRIVER_DSOUND); 613 s->dev = dev; 614 dso = &dev->u.dsound; 615 616 if (!dso->has_latency) { 617 dso->has_latency = true; 618 dso->latency = 10000; /* 10 ms */ 619 } 620 621 hr = CoInitialize (NULL); 622 if (FAILED (hr)) { 623 dsound_logerr (hr, "Could not initialize COM\n"); 624 g_free(s); 625 return NULL; 626 } 627 628 hr = CoCreateInstance ( 629 &CLSID_DirectSound, 630 NULL, 631 CLSCTX_ALL, 632 &IID_IDirectSound, 633 (void **) &s->dsound 634 ); 635 if (FAILED (hr)) { 636 dsound_logerr (hr, "Could not create DirectSound instance\n"); 637 g_free(s); 638 return NULL; 639 } 640 641 hr = IDirectSound_Initialize (s->dsound, NULL); 642 if (FAILED (hr)) { 643 dsound_logerr (hr, "Could not initialize DirectSound\n"); 644 645 hr = IDirectSound_Release (s->dsound); 646 if (FAILED (hr)) { 647 dsound_logerr (hr, "Could not release DirectSound\n"); 648 } 649 g_free(s); 650 return NULL; 651 } 652 653 hr = CoCreateInstance ( 654 &CLSID_DirectSoundCapture, 655 NULL, 656 CLSCTX_ALL, 657 &IID_IDirectSoundCapture, 658 (void **) &s->dsound_capture 659 ); 660 if (FAILED (hr)) { 661 dsound_logerr (hr, "Could not create DirectSoundCapture instance\n"); 662 } 663 else { 664 hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL); 665 if (FAILED (hr)) { 666 dsound_logerr (hr, "Could not initialize DirectSoundCapture\n"); 667 668 hr = IDirectSoundCapture_Release (s->dsound_capture); 669 if (FAILED (hr)) { 670 dsound_logerr (hr, "Could not release DirectSoundCapture\n"); 671 } 672 s->dsound_capture = NULL; 673 } 674 } 675 676 err = dsound_open (s); 677 if (err) { 678 dsound_audio_fini (s); 679 return NULL; 680 } 681 682 return s; 683 } 684 685 static struct audio_pcm_ops dsound_pcm_ops = { 686 .init_out = dsound_init_out, 687 .fini_out = dsound_fini_out, 688 .write = audio_generic_write, 689 .get_buffer_out = dsound_get_buffer_out, 690 .put_buffer_out = dsound_put_buffer_out, 691 .enable_out = dsound_enable_out, 692 693 .init_in = dsound_init_in, 694 .fini_in = dsound_fini_in, 695 .read = audio_generic_read, 696 .get_buffer_in = dsound_get_buffer_in, 697 .put_buffer_in = dsound_put_buffer_in, 698 .enable_in = dsound_enable_in, 699 }; 700 701 static struct audio_driver dsound_audio_driver = { 702 .name = "dsound", 703 .descr = "DirectSound http://wikipedia.org/wiki/DirectSound", 704 .init = dsound_audio_init, 705 .fini = dsound_audio_fini, 706 .pcm_ops = &dsound_pcm_ops, 707 .can_be_default = 1, 708 .max_voices_out = INT_MAX, 709 .max_voices_in = 1, 710 .voice_size_out = sizeof (DSoundVoiceOut), 711 .voice_size_in = sizeof (DSoundVoiceIn) 712 }; 713 714 static void register_audio_dsound(void) 715 { 716 audio_driver_register(&dsound_audio_driver); 717 } 718 type_init(register_audio_dsound); 719