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