dsoundaudio.c revision 413f05aaf54fa08c0ae7e997327a4f4a473c0a8d
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 "vl.h" 30 31#define AUDIO_CAP "dsound" 32#include "audio_int.h" 33 34#include <windows.h> 35#include <objbase.h> 36#include <dsound.h> 37 38/* #define DEBUG_DSOUND */ 39 40static struct { 41 int lock_retries; 42 int restore_retries; 43 int getstatus_retries; 44 int set_primary; 45 int bufsize_in; 46 int bufsize_out; 47 audsettings_t settings; 48 int latency_millis; 49} conf = { 50 1, 51 1, 52 1, 53 0, 54 16384, 55 16384, 56 { 57 44100, 58 2, 59 AUD_FMT_S16 60 }, 61 10 62}; 63 64typedef struct { 65 LPDIRECTSOUND dsound; 66 LPDIRECTSOUNDCAPTURE dsound_capture; 67 LPDIRECTSOUNDBUFFER dsound_primary_buffer; 68 audsettings_t settings; 69} dsound; 70 71static dsound glob_dsound; 72 73typedef struct { 74 HWVoiceOut hw; 75 LPDIRECTSOUNDBUFFER dsound_buffer; 76 DWORD old_pos; 77 int first_time; 78#ifdef DEBUG_DSOUND 79 DWORD old_ppos; 80 DWORD played; 81 DWORD mixed; 82#endif 83} DSoundVoiceOut; 84 85typedef struct { 86 HWVoiceIn hw; 87 int first_time; 88 LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; 89} DSoundVoiceIn; 90 91static void dsound_log_hresult (HRESULT hr) 92{ 93 const char *str = "BUG"; 94 95 switch (hr) { 96 case DS_OK: 97 str = "The method succeeded"; 98 break; 99#ifdef DS_NO_VIRTUALIZATION 100 case DS_NO_VIRTUALIZATION: 101 str = "The buffer was created, but another 3D algorithm was substituted"; 102 break; 103#endif 104#ifdef DS_INCOMPLETE 105 case DS_INCOMPLETE: 106 str = "The method succeeded, but not all the optional effects were obtained"; 107 break; 108#endif 109#ifdef DSERR_ACCESSDENIED 110 case DSERR_ACCESSDENIED: 111 str = "The request failed because access was denied"; 112 break; 113#endif 114#ifdef DSERR_ALLOCATED 115 case DSERR_ALLOCATED: 116 str = "The request failed because resources, such as a priority level, were already in use by another caller"; 117 break; 118#endif 119#ifdef DSERR_ALREADYINITIALIZED 120 case DSERR_ALREADYINITIALIZED: 121 str = "The object is already initialized"; 122 break; 123#endif 124#ifdef DSERR_BADFORMAT 125 case DSERR_BADFORMAT: 126 str = "The specified wave format is not supported"; 127 break; 128#endif 129#ifdef DSERR_BADSENDBUFFERGUID 130 case DSERR_BADSENDBUFFERGUID: 131 str = "The GUID specified in an audiopath file does not match a valid mix-in buffer"; 132 break; 133#endif 134#ifdef DSERR_BUFFERLOST 135 case DSERR_BUFFERLOST: 136 str = "The buffer memory has been lost and must be restored"; 137 break; 138#endif 139#ifdef DSERR_BUFFERTOOSMALL 140 case DSERR_BUFFERTOOSMALL: 141 str = "The buffer size is not great enough to enable effects processing"; 142 break; 143#endif 144#ifdef DSERR_CONTROLUNAVAIL 145 case DSERR_CONTROLUNAVAIL: 146 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"; 147 break; 148#endif 149#ifdef DSERR_DS8_REQUIRED 150 case DSERR_DS8_REQUIRED: 151 str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface"; 152 break; 153#endif 154#ifdef DSERR_FXUNAVAILABLE 155 case DSERR_FXUNAVAILABLE: 156 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"; 157 break; 158#endif 159#ifdef DSERR_GENERIC 160 case DSERR_GENERIC : 161 str = "An undetermined error occurred inside the DirectSound subsystem"; 162 break; 163#endif 164#ifdef DSERR_INVALIDCALL 165 case DSERR_INVALIDCALL: 166 str = "This function is not valid for the current state of this object"; 167 break; 168#endif 169#ifdef DSERR_INVALIDPARAM 170 case DSERR_INVALIDPARAM: 171 str = "An invalid parameter was passed to the returning function"; 172 break; 173#endif 174#ifdef DSERR_NOAGGREGATION 175 case DSERR_NOAGGREGATION: 176 str = "The object does not support aggregation"; 177 break; 178#endif 179#ifdef DSERR_NODRIVER 180 case DSERR_NODRIVER: 181 str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID"; 182 break; 183#endif 184#ifdef DSERR_NOINTERFACE 185 case DSERR_NOINTERFACE: 186 str = "The requested COM interface is not available"; 187 break; 188#endif 189#ifdef DSERR_OBJECTNOTFOUND 190 case DSERR_OBJECTNOTFOUND: 191 str = "The requested object was not found"; 192 break; 193#endif 194#ifdef DSERR_OTHERAPPHASPRIO 195 case DSERR_OTHERAPPHASPRIO: 196 str = "Another application has a higher priority level, preventing this call from succeeding"; 197 break; 198#endif 199#ifdef DSERR_OUTOFMEMORY 200 case DSERR_OUTOFMEMORY: 201 str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request"; 202 break; 203#endif 204#ifdef DSERR_PRIOLEVELNEEDED 205 case DSERR_PRIOLEVELNEEDED: 206 str = "A cooperative level of DSSCL_PRIORITY or higher is required"; 207 break; 208#endif 209#ifdef DSERR_SENDLOOP 210 case DSERR_SENDLOOP: 211 str = "A circular loop of send effects was detected"; 212 break; 213#endif 214#ifdef DSERR_UNINITIALIZED 215 case DSERR_UNINITIALIZED: 216 str = "The Initialize method has not been called or has not been called successfully before other methods were called"; 217 break; 218#endif 219#ifdef DSERR_UNSUPPORTED 220 case DSERR_UNSUPPORTED: 221 str = "The function called is not supported at this time"; 222 break; 223#endif 224 default: 225 AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr); 226 return; 227 } 228 229 AUD_log (AUDIO_CAP, "Reason: %s\n", str); 230} 231 232static void GCC_FMT_ATTR (2, 3) dsound_logerr ( 233 HRESULT hr, 234 const char *fmt, 235 ... 236 ) 237{ 238 va_list ap; 239 240 va_start (ap, fmt); 241 AUD_vlog (AUDIO_CAP, fmt, ap); 242 va_end (ap); 243 244 dsound_log_hresult (hr); 245} 246 247static void GCC_FMT_ATTR (3, 4) dsound_logerr2 ( 248 HRESULT hr, 249 const char *typ, 250 const char *fmt, 251 ... 252 ) 253{ 254 va_list ap; 255 256 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); 257 va_start (ap, fmt); 258 AUD_vlog (AUDIO_CAP, fmt, ap); 259 va_end (ap); 260 261 dsound_log_hresult (hr); 262} 263 264static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis) 265{ 266 return (millis * info->bytes_per_second) / 1000; 267} 268 269#ifdef DEBUG_DSOUND 270static void print_wave_format (WAVEFORMATEX *wfx) 271{ 272 dolog ("tag = %d\n", wfx->wFormatTag); 273 dolog ("nChannels = %d\n", wfx->nChannels); 274 dolog ("nSamplesPerSec = %ld\n", wfx->nSamplesPerSec); 275 dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec); 276 dolog ("nBlockAlign = %d\n", wfx->nBlockAlign); 277 dolog ("wBitsPerSample = %d\n", wfx->wBitsPerSample); 278 dolog ("cbSize = %d\n", wfx->cbSize); 279} 280#endif 281 282static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb) 283{ 284 HRESULT hr; 285 int i; 286 287 for (i = 0; i < conf.restore_retries; ++i) { 288 hr = IDirectSoundBuffer_Restore (dsb); 289 290 switch (hr) { 291 case DS_OK: 292 return 0; 293 294 case DSERR_BUFFERLOST: 295 continue; 296 297 default: 298 dsound_logerr (hr, "Could not restore playback buffer\n"); 299 return -1; 300 } 301 } 302 303 dolog ("%d attempts to restore playback buffer failed\n", i); 304 return -1; 305} 306 307static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as) 308{ 309 memset (wfx, 0, sizeof (*wfx)); 310 311 wfx->wFormatTag = WAVE_FORMAT_PCM; 312 wfx->nChannels = as->nchannels; 313 wfx->nSamplesPerSec = as->freq; 314 wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2); 315 wfx->nBlockAlign = 1 << (as->nchannels == 2); 316 wfx->cbSize = 0; 317 318 switch (as->fmt) { 319 case AUD_FMT_S8: 320 wfx->wBitsPerSample = 8; 321 break; 322 323 case AUD_FMT_U8: 324 wfx->wBitsPerSample = 8; 325 break; 326 327 case AUD_FMT_S16: 328 wfx->wBitsPerSample = 16; 329 wfx->nAvgBytesPerSec <<= 1; 330 wfx->nBlockAlign <<= 1; 331 break; 332 333 case AUD_FMT_U16: 334 wfx->wBitsPerSample = 16; 335 wfx->nAvgBytesPerSec <<= 1; 336 wfx->nBlockAlign <<= 1; 337 break; 338 339 default: 340 dolog ("Internal logic error: Bad audio format %d\n", as->freq); 341 return -1; 342 } 343 344 return 0; 345} 346 347static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as) 348{ 349 if (wfx->wFormatTag != WAVE_FORMAT_PCM) { 350 dolog ("Invalid wave format, tag is not PCM, but %d\n", 351 wfx->wFormatTag); 352 return -1; 353 } 354 355 if (!wfx->nSamplesPerSec) { 356 dolog ("Invalid wave format, frequency is zero\n"); 357 return -1; 358 } 359 as->freq = wfx->nSamplesPerSec; 360 361 switch (wfx->nChannels) { 362 case 1: 363 as->nchannels = 1; 364 break; 365 366 case 2: 367 as->nchannels = 2; 368 break; 369 370 default: 371 dolog ( 372 "Invalid wave format, number of channels is not 1 or 2, but %d\n", 373 wfx->nChannels 374 ); 375 return -1; 376 } 377 378 switch (wfx->wBitsPerSample) { 379 case 8: 380 as->fmt = AUD_FMT_U8; 381 break; 382 383 case 16: 384 as->fmt = AUD_FMT_S16; 385 break; 386 387 default: 388 dolog ("Invalid wave format, bits per sample is not 8 or 16, but %d\n", 389 wfx->wBitsPerSample); 390 return -1; 391 } 392 393 return 0; 394} 395 396#include "dsound_template.h" 397#define DSBTYPE_IN 398#include "dsound_template.h" 399#undef DSBTYPE_IN 400 401static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp) 402{ 403 HRESULT hr; 404 int i; 405 406 for (i = 0; i < conf.getstatus_retries; ++i) { 407 hr = IDirectSoundBuffer_GetStatus (dsb, statusp); 408 if (FAILED (hr)) { 409 dsound_logerr (hr, "Could not get playback buffer status\n"); 410 return -1; 411 } 412 413 if (*statusp & DSERR_BUFFERLOST) { 414 if (dsound_restore_out (dsb)) { 415 return -1; 416 } 417 continue; 418 } 419 break; 420 } 421 422 return 0; 423} 424 425static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb, 426 DWORD *statusp) 427{ 428 HRESULT hr; 429 430 hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp); 431 if (FAILED (hr)) { 432 dsound_logerr (hr, "Could not get capture buffer status\n"); 433 return -1; 434 } 435 436 return 0; 437} 438 439static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) 440{ 441 int src_len1 = dst_len; 442 int src_len2 = 0; 443 int pos = hw->rpos + dst_len; 444 st_sample_t *src1 = hw->mix_buf + hw->rpos; 445 st_sample_t *src2 = NULL; 446 447 if (pos > hw->samples) { 448 src_len1 = hw->samples - hw->rpos; 449 src2 = hw->mix_buf; 450 src_len2 = dst_len - src_len1; 451 pos = src_len2; 452 } 453 454 if (src_len1) { 455 hw->clip (dst, src1, src_len1); 456 } 457 458 if (src_len2) { 459 dst = advance (dst, src_len1 << hw->info.shift); 460 hw->clip (dst, src2, src_len2); 461 } 462 463 hw->rpos = pos % hw->samples; 464} 465 466static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb) 467{ 468 int err; 469 LPVOID p1, p2; 470 DWORD blen1, blen2, len1, len2; 471 472 err = dsound_lock_out ( 473 dsb, 474 &hw->info, 475 0, 476 hw->samples << hw->info.shift, 477 &p1, &p2, 478 &blen1, &blen2, 479 1 480 ); 481 if (err) { 482 return; 483 } 484 485 len1 = blen1 >> hw->info.shift; 486 len2 = blen2 >> hw->info.shift; 487 488#ifdef DEBUG_DSOUND 489 dolog ("clear %p,%ld,%ld %p,%ld,%ld\n", 490 p1, blen1, len1, 491 p2, blen2, len2); 492#endif 493 494 if (p1 && len1) { 495 audio_pcm_info_clear_buf (&hw->info, p1, len1); 496 } 497 498 if (p2 && len2) { 499 audio_pcm_info_clear_buf (&hw->info, p2, len2); 500 } 501 502 dsound_unlock_out (dsb, p1, p2, blen1, blen2); 503} 504 505static void dsound_close (dsound *s) 506{ 507 HRESULT hr; 508 509 if (s->dsound_primary_buffer) { 510 hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer); 511 if (FAILED (hr)) { 512 dsound_logerr (hr, "Could not release primary buffer\n"); 513 } 514 s->dsound_primary_buffer = NULL; 515 } 516} 517 518static int dsound_open (dsound *s) 519{ 520 int err; 521 HRESULT hr; 522 WAVEFORMATEX wfx; 523 DSBUFFERDESC dsbd; 524 HWND hwnd; 525 526 hwnd = GetForegroundWindow (); 527 hr = IDirectSound_SetCooperativeLevel ( 528 s->dsound, 529 hwnd, 530 DSSCL_PRIORITY 531 ); 532 533 if (FAILED (hr)) { 534 dsound_logerr (hr, "Could not set cooperative level for window %p\n", 535 hwnd); 536 return -1; 537 } 538 539 if (!conf.set_primary) { 540 return 0; 541 } 542 543 err = waveformat_from_audio_settings (&wfx, &conf.settings); 544 if (err) { 545 return -1; 546 } 547 548 memset (&dsbd, 0, sizeof (dsbd)); 549 dsbd.dwSize = sizeof (dsbd); 550 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; 551 dsbd.dwBufferBytes = 0; 552 dsbd.lpwfxFormat = NULL; 553 554 hr = IDirectSound_CreateSoundBuffer ( 555 s->dsound, 556 &dsbd, 557 &s->dsound_primary_buffer, 558 NULL 559 ); 560 if (FAILED (hr)) { 561 dsound_logerr (hr, "Could not create primary playback buffer\n"); 562 return -1; 563 } 564 565 hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx); 566 if (FAILED (hr)) { 567 dsound_logerr (hr, "Could not set primary playback buffer format\n"); 568 } 569 570 hr = IDirectSoundBuffer_GetFormat ( 571 s->dsound_primary_buffer, 572 &wfx, 573 sizeof (wfx), 574 NULL 575 ); 576 if (FAILED (hr)) { 577 dsound_logerr (hr, "Could not get primary playback buffer format\n"); 578 goto fail0; 579 } 580 581#ifdef DEBUG_DSOUND 582 dolog ("Primary\n"); 583 print_wave_format (&wfx); 584#endif 585 586 err = waveformat_to_audio_settings (&wfx, &s->settings); 587 if (err) { 588 goto fail0; 589 } 590 591 return 0; 592 593 fail0: 594 dsound_close (s); 595 return -1; 596} 597 598static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) 599{ 600 HRESULT hr; 601 DWORD status; 602 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; 603 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; 604 605 if (!dsb) { 606 dolog ("Attempt to control voice without a buffer\n"); 607 return 0; 608 } 609 610 switch (cmd) { 611 case VOICE_ENABLE: 612 if (dsound_get_status_out (dsb, &status)) { 613 return -1; 614 } 615 616 if (status & DSBSTATUS_PLAYING) { 617 dolog ("warning: Voice is already playing\n"); 618 return 0; 619 } 620 621 dsound_clear_sample (hw, dsb); 622 623 hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); 624 if (FAILED (hr)) { 625 dsound_logerr (hr, "Could not start playing buffer\n"); 626 return -1; 627 } 628 break; 629 630 case VOICE_DISABLE: 631 if (dsound_get_status_out (dsb, &status)) { 632 return -1; 633 } 634 635 if (status & DSBSTATUS_PLAYING) { 636 hr = IDirectSoundBuffer_Stop (dsb); 637 if (FAILED (hr)) { 638 dsound_logerr (hr, "Could not stop playing buffer\n"); 639 return -1; 640 } 641 } 642 else { 643 dolog ("warning: Voice is not playing\n"); 644 } 645 break; 646 } 647 return 0; 648} 649 650static int dsound_write (SWVoiceOut *sw, void *buf, int len) 651{ 652 return audio_pcm_sw_write (sw, buf, len); 653} 654 655static int dsound_run_out (HWVoiceOut *hw) 656{ 657 int err; 658 HRESULT hr; 659 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; 660 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; 661 int live, len, hwshift; 662 DWORD blen1, blen2; 663 DWORD len1, len2; 664 DWORD decr; 665 DWORD wpos, ppos, old_pos; 666 LPVOID p1, p2; 667 int bufsize; 668 669 if (!dsb) { 670 dolog ("Attempt to run empty with playback buffer\n"); 671 return 0; 672 } 673 674 hwshift = hw->info.shift; 675 bufsize = hw->samples << hwshift; 676 677 live = audio_pcm_hw_get_live_out (hw); 678 679 hr = IDirectSoundBuffer_GetCurrentPosition ( 680 dsb, 681 &ppos, 682 ds->first_time ? &wpos : NULL 683 ); 684 if (FAILED (hr)) { 685 dsound_logerr (hr, "Could not get playback buffer position\n"); 686 return 0; 687 } 688 689 len = live << hwshift; 690 691 if (ds->first_time) { 692 if (conf.latency_millis) { 693 DWORD cur_blat; 694 695 cur_blat = audio_ring_dist (wpos, ppos, bufsize); 696 ds->first_time = 0; 697 old_pos = wpos; 698 old_pos += 699 millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat; 700 old_pos %= bufsize; 701 old_pos &= ~hw->info.align; 702 } 703 else { 704 old_pos = wpos; 705 } 706#ifdef DEBUG_DSOUND 707 ds->played = 0; 708 ds->mixed = 0; 709#endif 710 } 711 else { 712 if (ds->old_pos == ppos) { 713#ifdef DEBUG_DSOUND 714 dolog ("old_pos == ppos\n"); 715#endif 716 return 0; 717 } 718 719#ifdef DEBUG_DSOUND 720 ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize); 721#endif 722 old_pos = ds->old_pos; 723 } 724 725 if ((old_pos < ppos) && ((old_pos + len) > ppos)) { 726 len = ppos - old_pos; 727 } 728 else { 729 if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) { 730 len = bufsize - old_pos + ppos; 731 } 732 } 733 734 if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) { 735 dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n", 736 len, bufsize, old_pos, ppos); 737 return 0; 738 } 739 740 len &= ~hw->info.align; 741 if (!len) { 742 return 0; 743 } 744 745#ifdef DEBUG_DSOUND 746 ds->old_ppos = ppos; 747#endif 748 err = dsound_lock_out ( 749 dsb, 750 &hw->info, 751 old_pos, 752 len, 753 &p1, &p2, 754 &blen1, &blen2, 755 0 756 ); 757 if (err) { 758 return 0; 759 } 760 761 len1 = blen1 >> hwshift; 762 len2 = blen2 >> hwshift; 763 decr = len1 + len2; 764 765 if (p1 && len1) { 766 dsound_write_sample (hw, p1, len1); 767 } 768 769 if (p2 && len2) { 770 dsound_write_sample (hw, p2, len2); 771 } 772 773 dsound_unlock_out (dsb, p1, p2, blen1, blen2); 774 ds->old_pos = (old_pos + (decr << hwshift)) % bufsize; 775 776#ifdef DEBUG_DSOUND 777 ds->mixed += decr << hwshift; 778 779 dolog ("played %lu mixed %lu diff %ld sec %f\n", 780 ds->played, 781 ds->mixed, 782 ds->mixed - ds->played, 783 abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second); 784#endif 785 return decr; 786} 787 788static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...) 789{ 790 HRESULT hr; 791 DWORD status; 792 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; 793 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; 794 795 if (!dscb) { 796 dolog ("Attempt to control capture voice without a buffer\n"); 797 return -1; 798 } 799 800 switch (cmd) { 801 case VOICE_ENABLE: 802 if (dsound_get_status_in (dscb, &status)) { 803 return -1; 804 } 805 806 if (status & DSCBSTATUS_CAPTURING) { 807 dolog ("warning: Voice is already capturing\n"); 808 return 0; 809 } 810 811 /* clear ?? */ 812 813 hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING); 814 if (FAILED (hr)) { 815 dsound_logerr (hr, "Could not start capturing\n"); 816 return -1; 817 } 818 break; 819 820 case VOICE_DISABLE: 821 if (dsound_get_status_in (dscb, &status)) { 822 return -1; 823 } 824 825 if (status & DSCBSTATUS_CAPTURING) { 826 hr = IDirectSoundCaptureBuffer_Stop (dscb); 827 if (FAILED (hr)) { 828 dsound_logerr (hr, "Could not stop capturing\n"); 829 return -1; 830 } 831 } 832 else { 833 dolog ("warning: Voice is not capturing\n"); 834 } 835 break; 836 } 837 return 0; 838} 839 840static int dsound_read (SWVoiceIn *sw, void *buf, int len) 841{ 842 return audio_pcm_sw_read (sw, buf, len); 843} 844 845static int dsound_run_in (HWVoiceIn *hw) 846{ 847 int err; 848 HRESULT hr; 849 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; 850 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; 851 int live, len, dead; 852 DWORD blen1, blen2; 853 DWORD len1, len2; 854 DWORD decr; 855 DWORD cpos, rpos; 856 LPVOID p1, p2; 857 int hwshift; 858 859 if (!dscb) { 860 dolog ("Attempt to run without capture buffer\n"); 861 return 0; 862 } 863 864 hwshift = hw->info.shift; 865 866 live = audio_pcm_hw_get_live_in (hw); 867 dead = hw->samples - live; 868 if (!dead) { 869 return 0; 870 } 871 872 hr = IDirectSoundCaptureBuffer_GetCurrentPosition ( 873 dscb, 874 &cpos, 875 ds->first_time ? &rpos : NULL 876 ); 877 if (FAILED (hr)) { 878 dsound_logerr (hr, "Could not get capture buffer position\n"); 879 return 0; 880 } 881 882 if (ds->first_time) { 883 ds->first_time = 0; 884 if (rpos & hw->info.align) { 885 ldebug ("warning: Misaligned capture read position %ld(%d)\n", 886 rpos, hw->info.align); 887 } 888 hw->wpos = rpos >> hwshift; 889 } 890 891 if (cpos & hw->info.align) { 892 ldebug ("warning: Misaligned capture position %ld(%d)\n", 893 cpos, hw->info.align); 894 } 895 cpos >>= hwshift; 896 897 len = audio_ring_dist (cpos, hw->wpos, hw->samples); 898 if (!len) { 899 return 0; 900 } 901 len = audio_MIN (len, dead); 902 903 err = dsound_lock_in ( 904 dscb, 905 &hw->info, 906 hw->wpos << hwshift, 907 len << hwshift, 908 &p1, 909 &p2, 910 &blen1, 911 &blen2, 912 0 913 ); 914 if (err) { 915 return 0; 916 } 917 918 len1 = blen1 >> hwshift; 919 len2 = blen2 >> hwshift; 920 decr = len1 + len2; 921 922 if (p1 && len1) { 923 hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); 924 } 925 926 if (p2 && len2) { 927 hw->conv (hw->conv_buf, p2, len2, &nominal_volume); 928 } 929 930 dsound_unlock_in (dscb, p1, p2, blen1, blen2); 931 hw->wpos = (hw->wpos + decr) % hw->samples; 932 return decr; 933} 934 935static void dsound_audio_fini (void *opaque) 936{ 937 HRESULT hr; 938 dsound *s = opaque; 939 940 if (!s->dsound) { 941 return; 942 } 943 944 hr = IDirectSound_Release (s->dsound); 945 if (FAILED (hr)) { 946 dsound_logerr (hr, "Could not release DirectSound\n"); 947 } 948 s->dsound = NULL; 949 950 if (!s->dsound_capture) { 951 return; 952 } 953 954 hr = IDirectSoundCapture_Release (s->dsound_capture); 955 if (FAILED (hr)) { 956 dsound_logerr (hr, "Could not release DirectSoundCapture\n"); 957 } 958 s->dsound_capture = NULL; 959} 960 961static void *dsound_audio_init (void) 962{ 963 int err; 964 HRESULT hr; 965 dsound *s = &glob_dsound; 966 967 hr = CoInitialize (NULL); 968 if (FAILED (hr)) { 969 dsound_logerr (hr, "Could not initialize COM\n"); 970 return NULL; 971 } 972 973 hr = CoCreateInstance ( 974 &CLSID_DirectSound, 975 NULL, 976 CLSCTX_ALL, 977 &IID_IDirectSound, 978 (void **) &s->dsound 979 ); 980 if (FAILED (hr)) { 981 dsound_logerr (hr, "Could not create DirectSound instance\n"); 982 return NULL; 983 } 984 985 hr = IDirectSound_Initialize (s->dsound, NULL); 986 if (FAILED (hr)) { 987 dsound_logerr (hr, "Could not initialize DirectSound\n"); 988 989 hr = IDirectSound_Release (s->dsound); 990 if (FAILED (hr)) { 991 dsound_logerr (hr, "Could not release DirectSound\n"); 992 } 993 s->dsound = NULL; 994 return NULL; 995 } 996 997 hr = CoCreateInstance ( 998 &CLSID_DirectSoundCapture, 999 NULL, 1000 CLSCTX_ALL, 1001 &IID_IDirectSoundCapture, 1002 (void **) &s->dsound_capture 1003 ); 1004 if (FAILED (hr)) { 1005 dsound_logerr (hr, "Could not create DirectSoundCapture instance\n"); 1006 } 1007 else { 1008 hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL); 1009 if (FAILED (hr)) { 1010 dsound_logerr (hr, "Could not initialize DirectSoundCapture\n"); 1011 1012 hr = IDirectSoundCapture_Release (s->dsound_capture); 1013 if (FAILED (hr)) { 1014 dsound_logerr (hr, "Could not release DirectSoundCapture\n"); 1015 } 1016 s->dsound_capture = NULL; 1017 } 1018 } 1019 1020 err = dsound_open (s); 1021 if (err) { 1022 dsound_audio_fini (s); 1023 return NULL; 1024 } 1025 1026 return s; 1027} 1028 1029static struct audio_option dsound_options[] = { 1030 {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries, 1031 "Number of times to attempt locking the buffer", NULL, 0}, 1032 {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries, 1033 "Number of times to attempt restoring the buffer", NULL, 0}, 1034 {"GETSTATUS_RETRIES", AUD_OPT_INT, &conf.getstatus_retries, 1035 "Number of times to attempt getting status of the buffer", NULL, 0}, 1036 {"SET_PRIMARY", AUD_OPT_BOOL, &conf.set_primary, 1037 "Set the parameters of primary buffer", NULL, 0}, 1038 {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis, 1039 "(undocumented)", NULL, 0}, 1040 {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq, 1041 "Primary buffer frequency", NULL, 0}, 1042 {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels, 1043 "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0}, 1044 {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt, 1045 "Primary buffer format", NULL, 0}, 1046 {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out, 1047 "(undocumented)", NULL, 0}, 1048 {"BUFSIZE_IN", AUD_OPT_INT, &conf.bufsize_in, 1049 "(undocumented)", NULL, 0}, 1050 {NULL, 0, NULL, NULL, NULL, 0} 1051}; 1052 1053static struct audio_pcm_ops dsound_pcm_ops = { 1054 dsound_init_out, 1055 dsound_fini_out, 1056 dsound_run_out, 1057 dsound_write, 1058 dsound_ctl_out, 1059 1060 dsound_init_in, 1061 dsound_fini_in, 1062 dsound_run_in, 1063 dsound_read, 1064 dsound_ctl_in 1065}; 1066 1067struct audio_driver dsound_audio_driver = { 1068 INIT_FIELD (name = ) "dsound", 1069 INIT_FIELD (descr = ) 1070 "DirectSound http://wikipedia.org/wiki/DirectSound", 1071 INIT_FIELD (options = ) dsound_options, 1072 INIT_FIELD (init = ) dsound_audio_init, 1073 INIT_FIELD (fini = ) dsound_audio_fini, 1074 INIT_FIELD (pcm_ops = ) &dsound_pcm_ops, 1075 INIT_FIELD (can_be_default = ) 1, 1076 INIT_FIELD (max_voices_out = ) INT_MAX, 1077 INIT_FIELD (max_voices_in = ) 1, 1078 INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut), 1079 INIT_FIELD (voice_size_in = ) sizeof (DSoundVoiceIn) 1080}; 1081