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 GCC_FMT_ATTR (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 GCC_FMT_ATTR (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 void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size) 431 { 432 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; 433 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; 434 HRESULT hr; 435 DWORD ppos, wpos, act_size; 436 size_t req_size; 437 int err; 438 void *ret; 439 440 hr = IDirectSoundBuffer_GetCurrentPosition( 441 dsb, &ppos, ds->first_time ? &wpos : NULL); 442 if (FAILED(hr)) { 443 dsound_logerr(hr, "Could not get playback buffer position\n"); 444 *size = 0; 445 return NULL; 446 } 447 448 if (ds->first_time) { 449 hw->pos_emul = wpos; 450 ds->first_time = false; 451 } 452 453 req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul); 454 req_size = MIN(req_size, hw->size_emul - hw->pos_emul); 455 456 if (req_size == 0) { 457 *size = 0; 458 return NULL; 459 } 460 461 err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL, 462 &act_size, NULL, false, ds->s); 463 if (err) { 464 dolog("Failed to lock buffer\n"); 465 *size = 0; 466 return NULL; 467 } 468 469 *size = act_size; 470 return ret; 471 } 472 473 static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len) 474 { 475 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; 476 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; 477 int err = dsound_unlock_out(dsb, buf, NULL, len, 0); 478 479 if (err) { 480 dolog("Failed to unlock buffer!!\n"); 481 return 0; 482 } 483 hw->pos_emul = (hw->pos_emul + len) % hw->size_emul; 484 485 return len; 486 } 487 488 static void dsound_enable_in(HWVoiceIn *hw, bool enable) 489 { 490 HRESULT hr; 491 DWORD status; 492 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; 493 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; 494 495 if (!dscb) { 496 dolog ("Attempt to control capture voice without a buffer\n"); 497 return; 498 } 499 500 if (enable) { 501 if (dsound_get_status_in (dscb, &status)) { 502 return; 503 } 504 505 if (status & DSCBSTATUS_CAPTURING) { 506 dolog ("warning: Voice is already capturing\n"); 507 return; 508 } 509 510 /* clear ?? */ 511 512 hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING); 513 if (FAILED (hr)) { 514 dsound_logerr (hr, "Could not start capturing\n"); 515 return; 516 } 517 } else { 518 if (dsound_get_status_in (dscb, &status)) { 519 return; 520 } 521 522 if (status & DSCBSTATUS_CAPTURING) { 523 hr = IDirectSoundCaptureBuffer_Stop (dscb); 524 if (FAILED (hr)) { 525 dsound_logerr (hr, "Could not stop capturing\n"); 526 return; 527 } 528 } else { 529 dolog ("warning: Voice is not capturing\n"); 530 } 531 } 532 } 533 534 static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size) 535 { 536 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; 537 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; 538 HRESULT hr; 539 DWORD rpos, act_size; 540 size_t req_size; 541 int err; 542 void *ret; 543 544 hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, NULL, &rpos); 545 if (FAILED(hr)) { 546 dsound_logerr(hr, "Could not get capture buffer position\n"); 547 *size = 0; 548 return NULL; 549 } 550 551 if (ds->first_time) { 552 hw->pos_emul = rpos; 553 ds->first_time = false; 554 } 555 556 req_size = audio_ring_dist(rpos, hw->pos_emul, hw->size_emul); 557 req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul)); 558 559 if (req_size == 0) { 560 *size = 0; 561 return NULL; 562 } 563 564 err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL, 565 &act_size, NULL, false, ds->s); 566 if (err) { 567 dolog("Failed to lock buffer\n"); 568 *size = 0; 569 return NULL; 570 } 571 572 *size = act_size; 573 return ret; 574 } 575 576 static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len) 577 { 578 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; 579 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; 580 int err = dsound_unlock_in(dscb, buf, NULL, len, 0); 581 582 if (err) { 583 dolog("Failed to unlock buffer!!\n"); 584 return; 585 } 586 hw->pos_emul = (hw->pos_emul + len) % hw->size_emul; 587 } 588 589 static void dsound_audio_fini (void *opaque) 590 { 591 HRESULT hr; 592 dsound *s = opaque; 593 594 if (!s->dsound) { 595 g_free(s); 596 return; 597 } 598 599 hr = IDirectSound_Release (s->dsound); 600 if (FAILED (hr)) { 601 dsound_logerr (hr, "Could not release DirectSound\n"); 602 } 603 s->dsound = NULL; 604 605 if (!s->dsound_capture) { 606 g_free(s); 607 return; 608 } 609 610 hr = IDirectSoundCapture_Release (s->dsound_capture); 611 if (FAILED (hr)) { 612 dsound_logerr (hr, "Could not release DirectSoundCapture\n"); 613 } 614 s->dsound_capture = NULL; 615 616 g_free(s); 617 } 618 619 static void *dsound_audio_init(Audiodev *dev) 620 { 621 int err; 622 HRESULT hr; 623 dsound *s = g_malloc0(sizeof(dsound)); 624 AudiodevDsoundOptions *dso; 625 626 assert(dev->driver == AUDIODEV_DRIVER_DSOUND); 627 s->dev = dev; 628 dso = &dev->u.dsound; 629 630 if (!dso->has_latency) { 631 dso->has_latency = true; 632 dso->latency = 10000; /* 10 ms */ 633 } 634 635 hr = CoInitialize (NULL); 636 if (FAILED (hr)) { 637 dsound_logerr (hr, "Could not initialize COM\n"); 638 g_free(s); 639 return NULL; 640 } 641 642 hr = CoCreateInstance ( 643 &CLSID_DirectSound, 644 NULL, 645 CLSCTX_ALL, 646 &IID_IDirectSound, 647 (void **) &s->dsound 648 ); 649 if (FAILED (hr)) { 650 dsound_logerr (hr, "Could not create DirectSound instance\n"); 651 g_free(s); 652 return NULL; 653 } 654 655 hr = IDirectSound_Initialize (s->dsound, NULL); 656 if (FAILED (hr)) { 657 dsound_logerr (hr, "Could not initialize DirectSound\n"); 658 659 hr = IDirectSound_Release (s->dsound); 660 if (FAILED (hr)) { 661 dsound_logerr (hr, "Could not release DirectSound\n"); 662 } 663 g_free(s); 664 return NULL; 665 } 666 667 hr = CoCreateInstance ( 668 &CLSID_DirectSoundCapture, 669 NULL, 670 CLSCTX_ALL, 671 &IID_IDirectSoundCapture, 672 (void **) &s->dsound_capture 673 ); 674 if (FAILED (hr)) { 675 dsound_logerr (hr, "Could not create DirectSoundCapture instance\n"); 676 } else { 677 hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL); 678 if (FAILED (hr)) { 679 dsound_logerr (hr, "Could not initialize DirectSoundCapture\n"); 680 681 hr = IDirectSoundCapture_Release (s->dsound_capture); 682 if (FAILED (hr)) { 683 dsound_logerr (hr, "Could not release DirectSoundCapture\n"); 684 } 685 s->dsound_capture = NULL; 686 } 687 } 688 689 err = dsound_set_cooperative_level(s); 690 if (err) { 691 dsound_audio_fini (s); 692 return NULL; 693 } 694 695 return s; 696 } 697 698 static struct audio_pcm_ops dsound_pcm_ops = { 699 .init_out = dsound_init_out, 700 .fini_out = dsound_fini_out, 701 .write = audio_generic_write, 702 .get_buffer_out = dsound_get_buffer_out, 703 .put_buffer_out = dsound_put_buffer_out, 704 .enable_out = dsound_enable_out, 705 706 .init_in = dsound_init_in, 707 .fini_in = dsound_fini_in, 708 .read = audio_generic_read, 709 .get_buffer_in = dsound_get_buffer_in, 710 .put_buffer_in = dsound_put_buffer_in, 711 .enable_in = dsound_enable_in, 712 }; 713 714 static struct audio_driver dsound_audio_driver = { 715 .name = "dsound", 716 .descr = "DirectSound http://wikipedia.org/wiki/DirectSound", 717 .init = dsound_audio_init, 718 .fini = dsound_audio_fini, 719 .pcm_ops = &dsound_pcm_ops, 720 .can_be_default = 1, 721 .max_voices_out = INT_MAX, 722 .max_voices_in = 1, 723 .voice_size_out = sizeof (DSoundVoiceOut), 724 .voice_size_in = sizeof (DSoundVoiceIn) 725 }; 726 727 static void register_audio_dsound(void) 728 { 729 audio_driver_register(&dsound_audio_driver); 730 } 731 type_init(register_audio_dsound); 732