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