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