dsoundaudio.c (19231B)
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 46typedef struct { 47 LPDIRECTSOUND dsound; 48 LPDIRECTSOUNDCAPTURE dsound_capture; 49 struct audsettings settings; 50 Audiodev *dev; 51} dsound; 52 53typedef struct { 54 HWVoiceOut hw; 55 LPDIRECTSOUNDBUFFER dsound_buffer; 56 bool first_time; 57 dsound *s; 58} DSoundVoiceOut; 59 60typedef struct { 61 HWVoiceIn hw; 62 LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; 63 bool first_time; 64 dsound *s; 65} DSoundVoiceIn; 66 67static 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 225static 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 240static 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 258static 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 270static 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 288static 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 307static 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 321static 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 362static 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 383static 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 430static 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 473static 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 488static 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 534static 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 cpos, rpos, act_size; 540 size_t req_size; 541 int err; 542 void *ret; 543 544 hr = IDirectSoundCaptureBuffer_GetCurrentPosition( 545 dscb, &cpos, ds->first_time ? &rpos : NULL); 546 if (FAILED(hr)) { 547 dsound_logerr(hr, "Could not get capture buffer position\n"); 548 *size = 0; 549 return NULL; 550 } 551 552 if (ds->first_time) { 553 hw->pos_emul = rpos; 554 ds->first_time = false; 555 } 556 557 req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul); 558 req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul)); 559 560 if (req_size == 0) { 561 *size = 0; 562 return NULL; 563 } 564 565 err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL, 566 &act_size, NULL, false, ds->s); 567 if (err) { 568 dolog("Failed to lock buffer\n"); 569 *size = 0; 570 return NULL; 571 } 572 573 *size = act_size; 574 return ret; 575} 576 577static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len) 578{ 579 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; 580 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; 581 int err = dsound_unlock_in(dscb, buf, NULL, len, 0); 582 583 if (err) { 584 dolog("Failed to unlock buffer!!\n"); 585 return; 586 } 587 hw->pos_emul = (hw->pos_emul + len) % hw->size_emul; 588} 589 590static void dsound_audio_fini (void *opaque) 591{ 592 HRESULT hr; 593 dsound *s = opaque; 594 595 if (!s->dsound) { 596 g_free(s); 597 return; 598 } 599 600 hr = IDirectSound_Release (s->dsound); 601 if (FAILED (hr)) { 602 dsound_logerr (hr, "Could not release DirectSound\n"); 603 } 604 s->dsound = NULL; 605 606 if (!s->dsound_capture) { 607 g_free(s); 608 return; 609 } 610 611 hr = IDirectSoundCapture_Release (s->dsound_capture); 612 if (FAILED (hr)) { 613 dsound_logerr (hr, "Could not release DirectSoundCapture\n"); 614 } 615 s->dsound_capture = NULL; 616 617 g_free(s); 618} 619 620static void *dsound_audio_init(Audiodev *dev) 621{ 622 int err; 623 HRESULT hr; 624 dsound *s = g_malloc0(sizeof(dsound)); 625 AudiodevDsoundOptions *dso; 626 627 assert(dev->driver == AUDIODEV_DRIVER_DSOUND); 628 s->dev = dev; 629 dso = &dev->u.dsound; 630 631 if (!dso->has_latency) { 632 dso->has_latency = true; 633 dso->latency = 10000; /* 10 ms */ 634 } 635 636 hr = CoInitialize (NULL); 637 if (FAILED (hr)) { 638 dsound_logerr (hr, "Could not initialize COM\n"); 639 g_free(s); 640 return NULL; 641 } 642 643 hr = CoCreateInstance ( 644 &CLSID_DirectSound, 645 NULL, 646 CLSCTX_ALL, 647 &IID_IDirectSound, 648 (void **) &s->dsound 649 ); 650 if (FAILED (hr)) { 651 dsound_logerr (hr, "Could not create DirectSound instance\n"); 652 g_free(s); 653 return NULL; 654 } 655 656 hr = IDirectSound_Initialize (s->dsound, NULL); 657 if (FAILED (hr)) { 658 dsound_logerr (hr, "Could not initialize DirectSound\n"); 659 660 hr = IDirectSound_Release (s->dsound); 661 if (FAILED (hr)) { 662 dsound_logerr (hr, "Could not release DirectSound\n"); 663 } 664 g_free(s); 665 return NULL; 666 } 667 668 hr = CoCreateInstance ( 669 &CLSID_DirectSoundCapture, 670 NULL, 671 CLSCTX_ALL, 672 &IID_IDirectSoundCapture, 673 (void **) &s->dsound_capture 674 ); 675 if (FAILED (hr)) { 676 dsound_logerr (hr, "Could not create DirectSoundCapture instance\n"); 677 } else { 678 hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL); 679 if (FAILED (hr)) { 680 dsound_logerr (hr, "Could not initialize DirectSoundCapture\n"); 681 682 hr = IDirectSoundCapture_Release (s->dsound_capture); 683 if (FAILED (hr)) { 684 dsound_logerr (hr, "Could not release DirectSoundCapture\n"); 685 } 686 s->dsound_capture = NULL; 687 } 688 } 689 690 err = dsound_set_cooperative_level(s); 691 if (err) { 692 dsound_audio_fini (s); 693 return NULL; 694 } 695 696 return s; 697} 698 699static struct audio_pcm_ops dsound_pcm_ops = { 700 .init_out = dsound_init_out, 701 .fini_out = dsound_fini_out, 702 .write = audio_generic_write, 703 .get_buffer_out = dsound_get_buffer_out, 704 .put_buffer_out = dsound_put_buffer_out, 705 .enable_out = dsound_enable_out, 706 707 .init_in = dsound_init_in, 708 .fini_in = dsound_fini_in, 709 .read = audio_generic_read, 710 .get_buffer_in = dsound_get_buffer_in, 711 .put_buffer_in = dsound_put_buffer_in, 712 .enable_in = dsound_enable_in, 713}; 714 715static struct audio_driver dsound_audio_driver = { 716 .name = "dsound", 717 .descr = "DirectSound http://wikipedia.org/wiki/DirectSound", 718 .init = dsound_audio_init, 719 .fini = dsound_audio_fini, 720 .pcm_ops = &dsound_pcm_ops, 721 .can_be_default = 1, 722 .max_voices_out = INT_MAX, 723 .max_voices_in = 1, 724 .voice_size_out = sizeof (DSoundVoiceOut), 725 .voice_size_in = sizeof (DSoundVoiceIn) 726}; 727 728static void register_audio_dsound(void) 729{ 730 audio_driver_register(&dsound_audio_driver); 731} 732type_init(register_audio_dsound);