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 & DSERR_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(req_size, hw->size_emul - hw->pos_emul); 544 545 err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL, 546 &act_size, NULL, false, ds->s); 547 if (err) { 548 dolog("Failed to lock buffer\n"); 549 *size = 0; 550 return NULL; 551 } 552 553 *size = act_size; 554 return ret; 555 } 556 557 static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len) 558 { 559 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; 560 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; 561 int err = dsound_unlock_in(dscb, buf, NULL, len, 0); 562 563 if (err) { 564 dolog("Failed to unlock buffer!!\n"); 565 return; 566 } 567 hw->pos_emul = (hw->pos_emul + len) % hw->size_emul; 568 } 569 570 static void dsound_audio_fini (void *opaque) 571 { 572 HRESULT hr; 573 dsound *s = opaque; 574 575 if (!s->dsound) { 576 g_free(s); 577 return; 578 } 579 580 hr = IDirectSound_Release (s->dsound); 581 if (FAILED (hr)) { 582 dsound_logerr (hr, "Could not release DirectSound\n"); 583 } 584 s->dsound = NULL; 585 586 if (!s->dsound_capture) { 587 g_free(s); 588 return; 589 } 590 591 hr = IDirectSoundCapture_Release (s->dsound_capture); 592 if (FAILED (hr)) { 593 dsound_logerr (hr, "Could not release DirectSoundCapture\n"); 594 } 595 s->dsound_capture = NULL; 596 597 g_free(s); 598 } 599 600 static void *dsound_audio_init(Audiodev *dev) 601 { 602 int err; 603 HRESULT hr; 604 dsound *s = g_malloc0(sizeof(dsound)); 605 AudiodevDsoundOptions *dso; 606 607 assert(dev->driver == AUDIODEV_DRIVER_DSOUND); 608 s->dev = dev; 609 dso = &dev->u.dsound; 610 611 if (!dso->has_latency) { 612 dso->has_latency = true; 613 dso->latency = 10000; /* 10 ms */ 614 } 615 616 hr = CoInitialize (NULL); 617 if (FAILED (hr)) { 618 dsound_logerr (hr, "Could not initialize COM\n"); 619 g_free(s); 620 return NULL; 621 } 622 623 hr = CoCreateInstance ( 624 &CLSID_DirectSound, 625 NULL, 626 CLSCTX_ALL, 627 &IID_IDirectSound, 628 (void **) &s->dsound 629 ); 630 if (FAILED (hr)) { 631 dsound_logerr (hr, "Could not create DirectSound instance\n"); 632 g_free(s); 633 return NULL; 634 } 635 636 hr = IDirectSound_Initialize (s->dsound, NULL); 637 if (FAILED (hr)) { 638 dsound_logerr (hr, "Could not initialize DirectSound\n"); 639 640 hr = IDirectSound_Release (s->dsound); 641 if (FAILED (hr)) { 642 dsound_logerr (hr, "Could not release DirectSound\n"); 643 } 644 g_free(s); 645 return NULL; 646 } 647 648 hr = CoCreateInstance ( 649 &CLSID_DirectSoundCapture, 650 NULL, 651 CLSCTX_ALL, 652 &IID_IDirectSoundCapture, 653 (void **) &s->dsound_capture 654 ); 655 if (FAILED (hr)) { 656 dsound_logerr (hr, "Could not create DirectSoundCapture instance\n"); 657 } 658 else { 659 hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL); 660 if (FAILED (hr)) { 661 dsound_logerr (hr, "Could not initialize DirectSoundCapture\n"); 662 663 hr = IDirectSoundCapture_Release (s->dsound_capture); 664 if (FAILED (hr)) { 665 dsound_logerr (hr, "Could not release DirectSoundCapture\n"); 666 } 667 s->dsound_capture = NULL; 668 } 669 } 670 671 err = dsound_open (s); 672 if (err) { 673 dsound_audio_fini (s); 674 return NULL; 675 } 676 677 return s; 678 } 679 680 static struct audio_pcm_ops dsound_pcm_ops = { 681 .init_out = dsound_init_out, 682 .fini_out = dsound_fini_out, 683 .write = audio_generic_write, 684 .get_buffer_out = dsound_get_buffer_out, 685 .put_buffer_out = dsound_put_buffer_out, 686 .enable_out = dsound_enable_out, 687 688 .init_in = dsound_init_in, 689 .fini_in = dsound_fini_in, 690 .read = audio_generic_read, 691 .get_buffer_in = dsound_get_buffer_in, 692 .put_buffer_in = dsound_put_buffer_in, 693 .enable_in = dsound_enable_in, 694 }; 695 696 static struct audio_driver dsound_audio_driver = { 697 .name = "dsound", 698 .descr = "DirectSound http://wikipedia.org/wiki/DirectSound", 699 .init = dsound_audio_init, 700 .fini = dsound_audio_fini, 701 .pcm_ops = &dsound_pcm_ops, 702 .can_be_default = 1, 703 .max_voices_out = INT_MAX, 704 .max_voices_in = 1, 705 .voice_size_out = sizeof (DSoundVoiceOut), 706 .voice_size_in = sizeof (DSoundVoiceIn) 707 }; 708 709 static void register_audio_dsound(void) 710 { 711 audio_driver_register(&dsound_audio_driver); 712 } 713 type_init(register_audio_dsound); 714