xref: /qemu/audio/dsoundaudio.c (revision 15c875a3cdfd968427a6631fb685c867a4230f82)
11d14ffa9Sbellard /*
21d14ffa9Sbellard  * QEMU DirectSound audio driver
31d14ffa9Sbellard  *
41d14ffa9Sbellard  * Copyright (c) 2005 Vassili Karpov (malc)
51d14ffa9Sbellard  *
61d14ffa9Sbellard  * Permission is hereby granted, free of charge, to any person obtaining a copy
71d14ffa9Sbellard  * of this software and associated documentation files (the "Software"), to deal
81d14ffa9Sbellard  * in the Software without restriction, including without limitation the rights
91d14ffa9Sbellard  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
101d14ffa9Sbellard  * copies of the Software, and to permit persons to whom the Software is
111d14ffa9Sbellard  * furnished to do so, subject to the following conditions:
121d14ffa9Sbellard  *
131d14ffa9Sbellard  * The above copyright notice and this permission notice shall be included in
141d14ffa9Sbellard  * all copies or substantial portions of the Software.
151d14ffa9Sbellard  *
161d14ffa9Sbellard  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
171d14ffa9Sbellard  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
181d14ffa9Sbellard  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
191d14ffa9Sbellard  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
201d14ffa9Sbellard  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
211d14ffa9Sbellard  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
221d14ffa9Sbellard  * THE SOFTWARE.
231d14ffa9Sbellard  */
241d14ffa9Sbellard 
251d14ffa9Sbellard /*
261d14ffa9Sbellard  * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
271d14ffa9Sbellard  */
281d14ffa9Sbellard 
29749bc4bfSpbrook #include "qemu-common.h"
30749bc4bfSpbrook #include "audio.h"
311d14ffa9Sbellard 
321d14ffa9Sbellard #define AUDIO_CAP "dsound"
331d14ffa9Sbellard #include "audio_int.h"
341d14ffa9Sbellard 
351d14ffa9Sbellard #include <windows.h>
364fddf62aSths #include <mmsystem.h>
371d14ffa9Sbellard #include <objbase.h>
381d14ffa9Sbellard #include <dsound.h>
391d14ffa9Sbellard 
401d14ffa9Sbellard /* #define DEBUG_DSOUND */
411d14ffa9Sbellard 
421d14ffa9Sbellard static struct {
431d14ffa9Sbellard     int lock_retries;
441d14ffa9Sbellard     int restore_retries;
451d14ffa9Sbellard     int getstatus_retries;
461d14ffa9Sbellard     int set_primary;
471d14ffa9Sbellard     int bufsize_in;
481d14ffa9Sbellard     int bufsize_out;
491ea879e5Smalc     struct audsettings settings;
501d14ffa9Sbellard     int latency_millis;
511d14ffa9Sbellard } conf = {
521a40d5e2SJuan Quintela     .lock_retries       = 1,
531a40d5e2SJuan Quintela     .restore_retries    = 1,
541a40d5e2SJuan Quintela     .getstatus_retries  = 1,
551a40d5e2SJuan Quintela     .set_primary        = 0,
561a40d5e2SJuan Quintela     .bufsize_in         = 16384,
571a40d5e2SJuan Quintela     .bufsize_out        = 16384,
581a40d5e2SJuan Quintela     .settings.freq      = 44100,
591a40d5e2SJuan Quintela     .settings.nchannels = 2,
60*15c875a3SConsul     .settings.fmt       = AUD_FMT_S16,
611a40d5e2SJuan Quintela     .latency_millis     = 10
621d14ffa9Sbellard };
631d14ffa9Sbellard 
641d14ffa9Sbellard typedef struct {
651d14ffa9Sbellard     LPDIRECTSOUND dsound;
661d14ffa9Sbellard     LPDIRECTSOUNDCAPTURE dsound_capture;
671d14ffa9Sbellard     LPDIRECTSOUNDBUFFER dsound_primary_buffer;
681ea879e5Smalc     struct audsettings settings;
691d14ffa9Sbellard } dsound;
701d14ffa9Sbellard 
711d14ffa9Sbellard static dsound glob_dsound;
721d14ffa9Sbellard 
731d14ffa9Sbellard typedef struct {
741d14ffa9Sbellard     HWVoiceOut hw;
751d14ffa9Sbellard     LPDIRECTSOUNDBUFFER dsound_buffer;
761d14ffa9Sbellard     DWORD old_pos;
771d14ffa9Sbellard     int first_time;
781d14ffa9Sbellard #ifdef DEBUG_DSOUND
791d14ffa9Sbellard     DWORD old_ppos;
801d14ffa9Sbellard     DWORD played;
811d14ffa9Sbellard     DWORD mixed;
821d14ffa9Sbellard #endif
831d14ffa9Sbellard } DSoundVoiceOut;
841d14ffa9Sbellard 
851d14ffa9Sbellard typedef struct {
861d14ffa9Sbellard     HWVoiceIn hw;
871d14ffa9Sbellard     int first_time;
881d14ffa9Sbellard     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
891d14ffa9Sbellard } DSoundVoiceIn;
901d14ffa9Sbellard 
911d14ffa9Sbellard static void dsound_log_hresult (HRESULT hr)
921d14ffa9Sbellard {
931d14ffa9Sbellard     const char *str = "BUG";
941d14ffa9Sbellard 
951d14ffa9Sbellard     switch (hr) {
961d14ffa9Sbellard     case DS_OK:
971d14ffa9Sbellard         str = "The method succeeded";
981d14ffa9Sbellard         break;
991d14ffa9Sbellard #ifdef DS_NO_VIRTUALIZATION
1001d14ffa9Sbellard     case DS_NO_VIRTUALIZATION:
1011d14ffa9Sbellard         str = "The buffer was created, but another 3D algorithm was substituted";
1021d14ffa9Sbellard         break;
1031d14ffa9Sbellard #endif
1041d14ffa9Sbellard #ifdef DS_INCOMPLETE
1051d14ffa9Sbellard     case DS_INCOMPLETE:
1061d14ffa9Sbellard         str = "The method succeeded, but not all the optional effects were obtained";
1071d14ffa9Sbellard         break;
1081d14ffa9Sbellard #endif
1091d14ffa9Sbellard #ifdef DSERR_ACCESSDENIED
1101d14ffa9Sbellard     case DSERR_ACCESSDENIED:
1111d14ffa9Sbellard         str = "The request failed because access was denied";
1121d14ffa9Sbellard         break;
1131d14ffa9Sbellard #endif
1141d14ffa9Sbellard #ifdef DSERR_ALLOCATED
1151d14ffa9Sbellard     case DSERR_ALLOCATED:
1161d14ffa9Sbellard         str = "The request failed because resources, such as a priority level, were already in use by another caller";
1171d14ffa9Sbellard         break;
1181d14ffa9Sbellard #endif
1191d14ffa9Sbellard #ifdef DSERR_ALREADYINITIALIZED
1201d14ffa9Sbellard     case DSERR_ALREADYINITIALIZED:
1211d14ffa9Sbellard         str = "The object is already initialized";
1221d14ffa9Sbellard         break;
1231d14ffa9Sbellard #endif
1241d14ffa9Sbellard #ifdef DSERR_BADFORMAT
1251d14ffa9Sbellard     case DSERR_BADFORMAT:
1261d14ffa9Sbellard         str = "The specified wave format is not supported";
1271d14ffa9Sbellard         break;
1281d14ffa9Sbellard #endif
1291d14ffa9Sbellard #ifdef DSERR_BADSENDBUFFERGUID
1301d14ffa9Sbellard     case DSERR_BADSENDBUFFERGUID:
1311d14ffa9Sbellard         str = "The GUID specified in an audiopath file does not match a valid mix-in buffer";
1321d14ffa9Sbellard         break;
1331d14ffa9Sbellard #endif
1341d14ffa9Sbellard #ifdef DSERR_BUFFERLOST
1351d14ffa9Sbellard     case DSERR_BUFFERLOST:
1361d14ffa9Sbellard         str = "The buffer memory has been lost and must be restored";
1371d14ffa9Sbellard         break;
1381d14ffa9Sbellard #endif
1391d14ffa9Sbellard #ifdef DSERR_BUFFERTOOSMALL
1401d14ffa9Sbellard     case DSERR_BUFFERTOOSMALL:
1411d14ffa9Sbellard         str = "The buffer size is not great enough to enable effects processing";
1421d14ffa9Sbellard         break;
1431d14ffa9Sbellard #endif
1441d14ffa9Sbellard #ifdef DSERR_CONTROLUNAVAIL
1451d14ffa9Sbellard     case DSERR_CONTROLUNAVAIL:
1461d14ffa9Sbellard         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";
1471d14ffa9Sbellard         break;
1481d14ffa9Sbellard #endif
1491d14ffa9Sbellard #ifdef DSERR_DS8_REQUIRED
1501d14ffa9Sbellard     case DSERR_DS8_REQUIRED:
1511d14ffa9Sbellard         str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface";
1521d14ffa9Sbellard         break;
1531d14ffa9Sbellard #endif
1541d14ffa9Sbellard #ifdef DSERR_FXUNAVAILABLE
1551d14ffa9Sbellard     case DSERR_FXUNAVAILABLE:
1561d14ffa9Sbellard         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";
1571d14ffa9Sbellard         break;
1581d14ffa9Sbellard #endif
1591d14ffa9Sbellard #ifdef DSERR_GENERIC
1601d14ffa9Sbellard     case DSERR_GENERIC :
1611d14ffa9Sbellard         str = "An undetermined error occurred inside the DirectSound subsystem";
1621d14ffa9Sbellard         break;
1631d14ffa9Sbellard #endif
1641d14ffa9Sbellard #ifdef DSERR_INVALIDCALL
1651d14ffa9Sbellard     case DSERR_INVALIDCALL:
1661d14ffa9Sbellard         str = "This function is not valid for the current state of this object";
1671d14ffa9Sbellard         break;
1681d14ffa9Sbellard #endif
1691d14ffa9Sbellard #ifdef DSERR_INVALIDPARAM
1701d14ffa9Sbellard     case DSERR_INVALIDPARAM:
1711d14ffa9Sbellard         str = "An invalid parameter was passed to the returning function";
1721d14ffa9Sbellard         break;
1731d14ffa9Sbellard #endif
1741d14ffa9Sbellard #ifdef DSERR_NOAGGREGATION
1751d14ffa9Sbellard     case DSERR_NOAGGREGATION:
1761d14ffa9Sbellard         str = "The object does not support aggregation";
1771d14ffa9Sbellard         break;
1781d14ffa9Sbellard #endif
1791d14ffa9Sbellard #ifdef DSERR_NODRIVER
1801d14ffa9Sbellard     case DSERR_NODRIVER:
1811d14ffa9Sbellard         str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID";
1821d14ffa9Sbellard         break;
1831d14ffa9Sbellard #endif
1841d14ffa9Sbellard #ifdef DSERR_NOINTERFACE
1851d14ffa9Sbellard     case DSERR_NOINTERFACE:
1861d14ffa9Sbellard         str = "The requested COM interface is not available";
1871d14ffa9Sbellard         break;
1881d14ffa9Sbellard #endif
1891d14ffa9Sbellard #ifdef DSERR_OBJECTNOTFOUND
1901d14ffa9Sbellard     case DSERR_OBJECTNOTFOUND:
1911d14ffa9Sbellard         str = "The requested object was not found";
1921d14ffa9Sbellard         break;
1931d14ffa9Sbellard #endif
1941d14ffa9Sbellard #ifdef DSERR_OTHERAPPHASPRIO
1951d14ffa9Sbellard     case DSERR_OTHERAPPHASPRIO:
1961d14ffa9Sbellard         str = "Another application has a higher priority level, preventing this call from succeeding";
1971d14ffa9Sbellard         break;
1981d14ffa9Sbellard #endif
1991d14ffa9Sbellard #ifdef DSERR_OUTOFMEMORY
2001d14ffa9Sbellard     case DSERR_OUTOFMEMORY:
2011d14ffa9Sbellard         str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request";
2021d14ffa9Sbellard         break;
2031d14ffa9Sbellard #endif
2041d14ffa9Sbellard #ifdef DSERR_PRIOLEVELNEEDED
2051d14ffa9Sbellard     case DSERR_PRIOLEVELNEEDED:
2061d14ffa9Sbellard         str = "A cooperative level of DSSCL_PRIORITY or higher is required";
2071d14ffa9Sbellard         break;
2081d14ffa9Sbellard #endif
2091d14ffa9Sbellard #ifdef DSERR_SENDLOOP
2101d14ffa9Sbellard     case DSERR_SENDLOOP:
2111d14ffa9Sbellard         str = "A circular loop of send effects was detected";
2121d14ffa9Sbellard         break;
2131d14ffa9Sbellard #endif
2141d14ffa9Sbellard #ifdef DSERR_UNINITIALIZED
2151d14ffa9Sbellard     case DSERR_UNINITIALIZED:
2161d14ffa9Sbellard         str = "The Initialize method has not been called or has not been called successfully before other methods were called";
2171d14ffa9Sbellard         break;
2181d14ffa9Sbellard #endif
2191d14ffa9Sbellard #ifdef DSERR_UNSUPPORTED
2201d14ffa9Sbellard     case DSERR_UNSUPPORTED:
2211d14ffa9Sbellard         str = "The function called is not supported at this time";
2221d14ffa9Sbellard         break;
2231d14ffa9Sbellard #endif
2241d14ffa9Sbellard     default:
2251d14ffa9Sbellard         AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr);
2261d14ffa9Sbellard         return;
2271d14ffa9Sbellard     }
2281d14ffa9Sbellard 
2291d14ffa9Sbellard     AUD_log (AUDIO_CAP, "Reason: %s\n", str);
2301d14ffa9Sbellard }
2311d14ffa9Sbellard 
2321d14ffa9Sbellard static void GCC_FMT_ATTR (2, 3) dsound_logerr (
2331d14ffa9Sbellard     HRESULT hr,
2341d14ffa9Sbellard     const char *fmt,
2351d14ffa9Sbellard     ...
2361d14ffa9Sbellard     )
2371d14ffa9Sbellard {
2381d14ffa9Sbellard     va_list ap;
2391d14ffa9Sbellard 
2401d14ffa9Sbellard     va_start (ap, fmt);
2411d14ffa9Sbellard     AUD_vlog (AUDIO_CAP, fmt, ap);
2421d14ffa9Sbellard     va_end (ap);
2431d14ffa9Sbellard 
2441d14ffa9Sbellard     dsound_log_hresult (hr);
2451d14ffa9Sbellard }
2461d14ffa9Sbellard 
2471d14ffa9Sbellard static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
2481d14ffa9Sbellard     HRESULT hr,
2491d14ffa9Sbellard     const char *typ,
2501d14ffa9Sbellard     const char *fmt,
2511d14ffa9Sbellard     ...
2521d14ffa9Sbellard     )
2531d14ffa9Sbellard {
2541d14ffa9Sbellard     va_list ap;
2551d14ffa9Sbellard 
256c0fe3827Sbellard     AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
2571d14ffa9Sbellard     va_start (ap, fmt);
2581d14ffa9Sbellard     AUD_vlog (AUDIO_CAP, fmt, ap);
2591d14ffa9Sbellard     va_end (ap);
2601d14ffa9Sbellard 
2611d14ffa9Sbellard     dsound_log_hresult (hr);
2621d14ffa9Sbellard }
2631d14ffa9Sbellard 
2641d14ffa9Sbellard static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
2651d14ffa9Sbellard {
2661d14ffa9Sbellard     return (millis * info->bytes_per_second) / 1000;
2671d14ffa9Sbellard }
2681d14ffa9Sbellard 
2691d14ffa9Sbellard #ifdef DEBUG_DSOUND
2701d14ffa9Sbellard static void print_wave_format (WAVEFORMATEX *wfx)
2711d14ffa9Sbellard {
2721d14ffa9Sbellard     dolog ("tag             = %d\n", wfx->wFormatTag);
2731d14ffa9Sbellard     dolog ("nChannels       = %d\n", wfx->nChannels);
2741d14ffa9Sbellard     dolog ("nSamplesPerSec  = %ld\n", wfx->nSamplesPerSec);
2751d14ffa9Sbellard     dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec);
2761d14ffa9Sbellard     dolog ("nBlockAlign     = %d\n", wfx->nBlockAlign);
2771d14ffa9Sbellard     dolog ("wBitsPerSample  = %d\n", wfx->wBitsPerSample);
2781d14ffa9Sbellard     dolog ("cbSize          = %d\n", wfx->cbSize);
2791d14ffa9Sbellard }
2801d14ffa9Sbellard #endif
2811d14ffa9Sbellard 
2821d14ffa9Sbellard static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
2831d14ffa9Sbellard {
2841d14ffa9Sbellard     HRESULT hr;
2851d14ffa9Sbellard     int i;
2861d14ffa9Sbellard 
2871d14ffa9Sbellard     for (i = 0; i < conf.restore_retries; ++i) {
2881d14ffa9Sbellard         hr = IDirectSoundBuffer_Restore (dsb);
2891d14ffa9Sbellard 
2901d14ffa9Sbellard         switch (hr) {
2911d14ffa9Sbellard         case DS_OK:
2921d14ffa9Sbellard             return 0;
2931d14ffa9Sbellard 
2941d14ffa9Sbellard         case DSERR_BUFFERLOST:
2951d14ffa9Sbellard             continue;
2961d14ffa9Sbellard 
2971d14ffa9Sbellard         default:
298c0fe3827Sbellard             dsound_logerr (hr, "Could not restore playback buffer\n");
2991d14ffa9Sbellard             return -1;
3001d14ffa9Sbellard         }
3011d14ffa9Sbellard     }
3021d14ffa9Sbellard 
3031d14ffa9Sbellard     dolog ("%d attempts to restore playback buffer failed\n", i);
3041d14ffa9Sbellard     return -1;
3051d14ffa9Sbellard }
3061d14ffa9Sbellard 
3071ea879e5Smalc static int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
3081ea879e5Smalc                                            struct audsettings *as)
3091d14ffa9Sbellard {
3101d14ffa9Sbellard     memset (wfx, 0, sizeof (*wfx));
3111d14ffa9Sbellard 
3121d14ffa9Sbellard     wfx->wFormatTag = WAVE_FORMAT_PCM;
313c0fe3827Sbellard     wfx->nChannels = as->nchannels;
314c0fe3827Sbellard     wfx->nSamplesPerSec = as->freq;
315c0fe3827Sbellard     wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
316c0fe3827Sbellard     wfx->nBlockAlign = 1 << (as->nchannels == 2);
3171d14ffa9Sbellard     wfx->cbSize = 0;
3181d14ffa9Sbellard 
319c0fe3827Sbellard     switch (as->fmt) {
3201d14ffa9Sbellard     case AUD_FMT_S8:
3211d14ffa9Sbellard     case AUD_FMT_U8:
3221d14ffa9Sbellard         wfx->wBitsPerSample = 8;
3231d14ffa9Sbellard         break;
3241d14ffa9Sbellard 
3251d14ffa9Sbellard     case AUD_FMT_S16:
326ca9cc28cSbalrog     case AUD_FMT_U16:
3271d14ffa9Sbellard         wfx->wBitsPerSample = 16;
3281d14ffa9Sbellard         wfx->nAvgBytesPerSec <<= 1;
3291d14ffa9Sbellard         wfx->nBlockAlign <<= 1;
3301d14ffa9Sbellard         break;
3311d14ffa9Sbellard 
332ca9cc28cSbalrog     case AUD_FMT_S32:
333ca9cc28cSbalrog     case AUD_FMT_U32:
334ca9cc28cSbalrog         wfx->wBitsPerSample = 32;
335ca9cc28cSbalrog         wfx->nAvgBytesPerSec <<= 2;
336ca9cc28cSbalrog         wfx->nBlockAlign <<= 2;
3371d14ffa9Sbellard         break;
3381d14ffa9Sbellard 
3391d14ffa9Sbellard     default:
340c0fe3827Sbellard         dolog ("Internal logic error: Bad audio format %d\n", as->freq);
3411d14ffa9Sbellard         return -1;
3421d14ffa9Sbellard     }
3431d14ffa9Sbellard 
3441d14ffa9Sbellard     return 0;
3451d14ffa9Sbellard }
3461d14ffa9Sbellard 
3471ea879e5Smalc static int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
3481ea879e5Smalc                                          struct audsettings *as)
3491d14ffa9Sbellard {
3501d14ffa9Sbellard     if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
3511d14ffa9Sbellard         dolog ("Invalid wave format, tag is not PCM, but %d\n",
3521d14ffa9Sbellard                wfx->wFormatTag);
3531d14ffa9Sbellard         return -1;
3541d14ffa9Sbellard     }
3551d14ffa9Sbellard 
3561d14ffa9Sbellard     if (!wfx->nSamplesPerSec) {
3571d14ffa9Sbellard         dolog ("Invalid wave format, frequency is zero\n");
3581d14ffa9Sbellard         return -1;
3591d14ffa9Sbellard     }
360c0fe3827Sbellard     as->freq = wfx->nSamplesPerSec;
3611d14ffa9Sbellard 
3621d14ffa9Sbellard     switch (wfx->nChannels) {
3631d14ffa9Sbellard     case 1:
364c0fe3827Sbellard         as->nchannels = 1;
3651d14ffa9Sbellard         break;
3661d14ffa9Sbellard 
3671d14ffa9Sbellard     case 2:
368c0fe3827Sbellard         as->nchannels = 2;
3691d14ffa9Sbellard         break;
3701d14ffa9Sbellard 
3711d14ffa9Sbellard     default:
3721d14ffa9Sbellard         dolog (
3731d14ffa9Sbellard             "Invalid wave format, number of channels is not 1 or 2, but %d\n",
3741d14ffa9Sbellard             wfx->nChannels
3751d14ffa9Sbellard             );
3761d14ffa9Sbellard         return -1;
3771d14ffa9Sbellard     }
3781d14ffa9Sbellard 
3791d14ffa9Sbellard     switch (wfx->wBitsPerSample) {
3801d14ffa9Sbellard     case 8:
381c0fe3827Sbellard         as->fmt = AUD_FMT_U8;
3821d14ffa9Sbellard         break;
3831d14ffa9Sbellard 
3841d14ffa9Sbellard     case 16:
385c0fe3827Sbellard         as->fmt = AUD_FMT_S16;
3861d14ffa9Sbellard         break;
3871d14ffa9Sbellard 
388ca9cc28cSbalrog     case 32:
389ca9cc28cSbalrog         as->fmt = AUD_FMT_S32;
390ca9cc28cSbalrog         break;
391ca9cc28cSbalrog 
3921d14ffa9Sbellard     default:
393ca9cc28cSbalrog         dolog ("Invalid wave format, bits per sample is not "
394ca9cc28cSbalrog                "8, 16 or 32, but %d\n",
3951d14ffa9Sbellard                wfx->wBitsPerSample);
3961d14ffa9Sbellard         return -1;
3971d14ffa9Sbellard     }
3981d14ffa9Sbellard 
3991d14ffa9Sbellard     return 0;
4001d14ffa9Sbellard }
4011d14ffa9Sbellard 
4021d14ffa9Sbellard #include "dsound_template.h"
4031d14ffa9Sbellard #define DSBTYPE_IN
4041d14ffa9Sbellard #include "dsound_template.h"
4051d14ffa9Sbellard #undef DSBTYPE_IN
4061d14ffa9Sbellard 
4071d14ffa9Sbellard static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
4081d14ffa9Sbellard {
4091d14ffa9Sbellard     HRESULT hr;
4101d14ffa9Sbellard     int i;
4111d14ffa9Sbellard 
4121d14ffa9Sbellard     for (i = 0; i < conf.getstatus_retries; ++i) {
4131d14ffa9Sbellard         hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
4141d14ffa9Sbellard         if (FAILED (hr)) {
415c0fe3827Sbellard             dsound_logerr (hr, "Could not get playback buffer status\n");
4161d14ffa9Sbellard             return -1;
4171d14ffa9Sbellard         }
4181d14ffa9Sbellard 
4191d14ffa9Sbellard         if (*statusp & DSERR_BUFFERLOST) {
4201d14ffa9Sbellard             if (dsound_restore_out (dsb)) {
4211d14ffa9Sbellard                 return -1;
4221d14ffa9Sbellard             }
4231d14ffa9Sbellard             continue;
4241d14ffa9Sbellard         }
4251d14ffa9Sbellard         break;
4261d14ffa9Sbellard     }
4271d14ffa9Sbellard 
4281d14ffa9Sbellard     return 0;
4291d14ffa9Sbellard }
4301d14ffa9Sbellard 
4311d14ffa9Sbellard static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
4321d14ffa9Sbellard                                  DWORD *statusp)
4331d14ffa9Sbellard {
4341d14ffa9Sbellard     HRESULT hr;
4351d14ffa9Sbellard 
4361d14ffa9Sbellard     hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
4371d14ffa9Sbellard     if (FAILED (hr)) {
438c0fe3827Sbellard         dsound_logerr (hr, "Could not get capture buffer status\n");
4391d14ffa9Sbellard         return -1;
4401d14ffa9Sbellard     }
4411d14ffa9Sbellard 
4421d14ffa9Sbellard     return 0;
4431d14ffa9Sbellard }
4441d14ffa9Sbellard 
4451d14ffa9Sbellard static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
4461d14ffa9Sbellard {
4471d14ffa9Sbellard     int src_len1 = dst_len;
4481d14ffa9Sbellard     int src_len2 = 0;
4491d14ffa9Sbellard     int pos = hw->rpos + dst_len;
4501ea879e5Smalc     struct st_sample *src1 = hw->mix_buf + hw->rpos;
4511ea879e5Smalc     struct st_sample *src2 = NULL;
4521d14ffa9Sbellard 
4531d14ffa9Sbellard     if (pos > hw->samples) {
4541d14ffa9Sbellard         src_len1 = hw->samples - hw->rpos;
4551d14ffa9Sbellard         src2 = hw->mix_buf;
4561d14ffa9Sbellard         src_len2 = dst_len - src_len1;
4571d14ffa9Sbellard         pos = src_len2;
4581d14ffa9Sbellard     }
4591d14ffa9Sbellard 
4601d14ffa9Sbellard     if (src_len1) {
4611d14ffa9Sbellard         hw->clip (dst, src1, src_len1);
4621d14ffa9Sbellard     }
4631d14ffa9Sbellard 
4641d14ffa9Sbellard     if (src_len2) {
4651d14ffa9Sbellard         dst = advance (dst, src_len1 << hw->info.shift);
4661d14ffa9Sbellard         hw->clip (dst, src2, src_len2);
4671d14ffa9Sbellard     }
4681d14ffa9Sbellard 
4691d14ffa9Sbellard     hw->rpos = pos % hw->samples;
4701d14ffa9Sbellard }
4711d14ffa9Sbellard 
4721d14ffa9Sbellard static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
4731d14ffa9Sbellard {
4741d14ffa9Sbellard     int err;
4751d14ffa9Sbellard     LPVOID p1, p2;
4761d14ffa9Sbellard     DWORD blen1, blen2, len1, len2;
4771d14ffa9Sbellard 
4781d14ffa9Sbellard     err = dsound_lock_out (
4791d14ffa9Sbellard         dsb,
4801d14ffa9Sbellard         &hw->info,
4811d14ffa9Sbellard         0,
4821d14ffa9Sbellard         hw->samples << hw->info.shift,
4831d14ffa9Sbellard         &p1, &p2,
4841d14ffa9Sbellard         &blen1, &blen2,
4851d14ffa9Sbellard         1
4861d14ffa9Sbellard         );
4871d14ffa9Sbellard     if (err) {
4881d14ffa9Sbellard         return;
4891d14ffa9Sbellard     }
4901d14ffa9Sbellard 
4911d14ffa9Sbellard     len1 = blen1 >> hw->info.shift;
4921d14ffa9Sbellard     len2 = blen2 >> hw->info.shift;
4931d14ffa9Sbellard 
4941d14ffa9Sbellard #ifdef DEBUG_DSOUND
4951d14ffa9Sbellard     dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
4961d14ffa9Sbellard            p1, blen1, len1,
4971d14ffa9Sbellard            p2, blen2, len2);
4981d14ffa9Sbellard #endif
4991d14ffa9Sbellard 
5001d14ffa9Sbellard     if (p1 && len1) {
5011d14ffa9Sbellard         audio_pcm_info_clear_buf (&hw->info, p1, len1);
5021d14ffa9Sbellard     }
5031d14ffa9Sbellard 
5041d14ffa9Sbellard     if (p2 && len2) {
5051d14ffa9Sbellard         audio_pcm_info_clear_buf (&hw->info, p2, len2);
5061d14ffa9Sbellard     }
5071d14ffa9Sbellard 
5081d14ffa9Sbellard     dsound_unlock_out (dsb, p1, p2, blen1, blen2);
5091d14ffa9Sbellard }
5101d14ffa9Sbellard 
5111d14ffa9Sbellard static void dsound_close (dsound *s)
5121d14ffa9Sbellard {
5131d14ffa9Sbellard     HRESULT hr;
5141d14ffa9Sbellard 
5151d14ffa9Sbellard     if (s->dsound_primary_buffer) {
5161d14ffa9Sbellard         hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
5171d14ffa9Sbellard         if (FAILED (hr)) {
518c0fe3827Sbellard             dsound_logerr (hr, "Could not release primary buffer\n");
5191d14ffa9Sbellard         }
5201d14ffa9Sbellard         s->dsound_primary_buffer = NULL;
5211d14ffa9Sbellard     }
5221d14ffa9Sbellard }
5231d14ffa9Sbellard 
5241d14ffa9Sbellard static int dsound_open (dsound *s)
5251d14ffa9Sbellard {
5261d14ffa9Sbellard     int err;
5271d14ffa9Sbellard     HRESULT hr;
5281d14ffa9Sbellard     WAVEFORMATEX wfx;
5291d14ffa9Sbellard     DSBUFFERDESC dsbd;
5301d14ffa9Sbellard     HWND hwnd;
5311d14ffa9Sbellard 
5321d14ffa9Sbellard     hwnd = GetForegroundWindow ();
5331d14ffa9Sbellard     hr = IDirectSound_SetCooperativeLevel (
5341d14ffa9Sbellard         s->dsound,
5351d14ffa9Sbellard         hwnd,
5361d14ffa9Sbellard         DSSCL_PRIORITY
5371d14ffa9Sbellard         );
5381d14ffa9Sbellard 
5391d14ffa9Sbellard     if (FAILED (hr)) {
540c0fe3827Sbellard         dsound_logerr (hr, "Could not set cooperative level for window %p\n",
5411d14ffa9Sbellard                        hwnd);
5421d14ffa9Sbellard         return -1;
5431d14ffa9Sbellard     }
5441d14ffa9Sbellard 
5451d14ffa9Sbellard     if (!conf.set_primary) {
5461d14ffa9Sbellard         return 0;
5471d14ffa9Sbellard     }
5481d14ffa9Sbellard 
549c0fe3827Sbellard     err = waveformat_from_audio_settings (&wfx, &conf.settings);
5501d14ffa9Sbellard     if (err) {
5511d14ffa9Sbellard         return -1;
5521d14ffa9Sbellard     }
5531d14ffa9Sbellard 
5541d14ffa9Sbellard     memset (&dsbd, 0, sizeof (dsbd));
5551d14ffa9Sbellard     dsbd.dwSize = sizeof (dsbd);
5561d14ffa9Sbellard     dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
5571d14ffa9Sbellard     dsbd.dwBufferBytes = 0;
5581d14ffa9Sbellard     dsbd.lpwfxFormat = NULL;
5591d14ffa9Sbellard 
5601d14ffa9Sbellard     hr = IDirectSound_CreateSoundBuffer (
5611d14ffa9Sbellard         s->dsound,
5621d14ffa9Sbellard         &dsbd,
5631d14ffa9Sbellard         &s->dsound_primary_buffer,
5641d14ffa9Sbellard         NULL
5651d14ffa9Sbellard         );
5661d14ffa9Sbellard     if (FAILED (hr)) {
567c0fe3827Sbellard         dsound_logerr (hr, "Could not create primary playback buffer\n");
5681d14ffa9Sbellard         return -1;
5691d14ffa9Sbellard     }
5701d14ffa9Sbellard 
5711d14ffa9Sbellard     hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
5721d14ffa9Sbellard     if (FAILED (hr)) {
573c0fe3827Sbellard         dsound_logerr (hr, "Could not set primary playback buffer format\n");
5741d14ffa9Sbellard     }
5751d14ffa9Sbellard 
5761d14ffa9Sbellard     hr = IDirectSoundBuffer_GetFormat (
5771d14ffa9Sbellard         s->dsound_primary_buffer,
5781d14ffa9Sbellard         &wfx,
5791d14ffa9Sbellard         sizeof (wfx),
5801d14ffa9Sbellard         NULL
5811d14ffa9Sbellard         );
5821d14ffa9Sbellard     if (FAILED (hr)) {
583c0fe3827Sbellard         dsound_logerr (hr, "Could not get primary playback buffer format\n");
5841d14ffa9Sbellard         goto fail0;
5851d14ffa9Sbellard     }
5861d14ffa9Sbellard 
5871d14ffa9Sbellard #ifdef DEBUG_DSOUND
5881d14ffa9Sbellard     dolog ("Primary\n");
5891d14ffa9Sbellard     print_wave_format (&wfx);
5901d14ffa9Sbellard #endif
5911d14ffa9Sbellard 
592c0fe3827Sbellard     err = waveformat_to_audio_settings (&wfx, &s->settings);
5931d14ffa9Sbellard     if (err) {
5941d14ffa9Sbellard         goto fail0;
5951d14ffa9Sbellard     }
5961d14ffa9Sbellard 
5971d14ffa9Sbellard     return 0;
5981d14ffa9Sbellard 
5991d14ffa9Sbellard  fail0:
6001d14ffa9Sbellard     dsound_close (s);
6011d14ffa9Sbellard     return -1;
6021d14ffa9Sbellard }
6031d14ffa9Sbellard 
6041d14ffa9Sbellard static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
6051d14ffa9Sbellard {
6061d14ffa9Sbellard     HRESULT hr;
6071d14ffa9Sbellard     DWORD status;
6081d14ffa9Sbellard     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
6091d14ffa9Sbellard     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
6101d14ffa9Sbellard 
6111d14ffa9Sbellard     if (!dsb) {
6121d14ffa9Sbellard         dolog ("Attempt to control voice without a buffer\n");
6131d14ffa9Sbellard         return 0;
6141d14ffa9Sbellard     }
6151d14ffa9Sbellard 
6161d14ffa9Sbellard     switch (cmd) {
6171d14ffa9Sbellard     case VOICE_ENABLE:
6181d14ffa9Sbellard         if (dsound_get_status_out (dsb, &status)) {
6191d14ffa9Sbellard             return -1;
6201d14ffa9Sbellard         }
6211d14ffa9Sbellard 
6221d14ffa9Sbellard         if (status & DSBSTATUS_PLAYING) {
623c0fe3827Sbellard             dolog ("warning: Voice is already playing\n");
6241d14ffa9Sbellard             return 0;
6251d14ffa9Sbellard         }
6261d14ffa9Sbellard 
6271d14ffa9Sbellard         dsound_clear_sample (hw, dsb);
6281d14ffa9Sbellard 
6291d14ffa9Sbellard         hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
6301d14ffa9Sbellard         if (FAILED (hr)) {
631c0fe3827Sbellard             dsound_logerr (hr, "Could not start playing buffer\n");
6321d14ffa9Sbellard             return -1;
6331d14ffa9Sbellard         }
6341d14ffa9Sbellard         break;
6351d14ffa9Sbellard 
6361d14ffa9Sbellard     case VOICE_DISABLE:
6371d14ffa9Sbellard         if (dsound_get_status_out (dsb, &status)) {
6381d14ffa9Sbellard             return -1;
6391d14ffa9Sbellard         }
6401d14ffa9Sbellard 
6411d14ffa9Sbellard         if (status & DSBSTATUS_PLAYING) {
6421d14ffa9Sbellard             hr = IDirectSoundBuffer_Stop (dsb);
6431d14ffa9Sbellard             if (FAILED (hr)) {
644c0fe3827Sbellard                 dsound_logerr (hr, "Could not stop playing buffer\n");
6451d14ffa9Sbellard                 return -1;
6461d14ffa9Sbellard             }
6471d14ffa9Sbellard         }
6481d14ffa9Sbellard         else {
649c0fe3827Sbellard             dolog ("warning: Voice is not playing\n");
6501d14ffa9Sbellard         }
6511d14ffa9Sbellard         break;
6521d14ffa9Sbellard     }
6531d14ffa9Sbellard     return 0;
6541d14ffa9Sbellard }
6551d14ffa9Sbellard 
6561d14ffa9Sbellard static int dsound_write (SWVoiceOut *sw, void *buf, int len)
6571d14ffa9Sbellard {
6581d14ffa9Sbellard     return audio_pcm_sw_write (sw, buf, len);
6591d14ffa9Sbellard }
6601d14ffa9Sbellard 
6611d14ffa9Sbellard static int dsound_run_out (HWVoiceOut *hw)
6621d14ffa9Sbellard {
6631d14ffa9Sbellard     int err;
6641d14ffa9Sbellard     HRESULT hr;
6651d14ffa9Sbellard     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
6661d14ffa9Sbellard     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
6671d14ffa9Sbellard     int live, len, hwshift;
6681d14ffa9Sbellard     DWORD blen1, blen2;
6691d14ffa9Sbellard     DWORD len1, len2;
6701d14ffa9Sbellard     DWORD decr;
6711d14ffa9Sbellard     DWORD wpos, ppos, old_pos;
6721d14ffa9Sbellard     LPVOID p1, p2;
673c0fe3827Sbellard     int bufsize;
6741d14ffa9Sbellard 
6751d14ffa9Sbellard     if (!dsb) {
6761d14ffa9Sbellard         dolog ("Attempt to run empty with playback buffer\n");
6771d14ffa9Sbellard         return 0;
6781d14ffa9Sbellard     }
6791d14ffa9Sbellard 
6801d14ffa9Sbellard     hwshift = hw->info.shift;
681c0fe3827Sbellard     bufsize = hw->samples << hwshift;
6821d14ffa9Sbellard 
6831d14ffa9Sbellard     live = audio_pcm_hw_get_live_out (hw);
6841d14ffa9Sbellard 
6851d14ffa9Sbellard     hr = IDirectSoundBuffer_GetCurrentPosition (
6861d14ffa9Sbellard         dsb,
6871d14ffa9Sbellard         &ppos,
6881d14ffa9Sbellard         ds->first_time ? &wpos : NULL
6891d14ffa9Sbellard         );
6901d14ffa9Sbellard     if (FAILED (hr)) {
691c0fe3827Sbellard         dsound_logerr (hr, "Could not get playback buffer position\n");
6921d14ffa9Sbellard         return 0;
6931d14ffa9Sbellard     }
6941d14ffa9Sbellard 
6951d14ffa9Sbellard     len = live << hwshift;
6961d14ffa9Sbellard 
6971d14ffa9Sbellard     if (ds->first_time) {
6981d14ffa9Sbellard         if (conf.latency_millis) {
699c0fe3827Sbellard             DWORD cur_blat;
7001d14ffa9Sbellard 
701c0fe3827Sbellard             cur_blat = audio_ring_dist (wpos, ppos, bufsize);
7021d14ffa9Sbellard             ds->first_time = 0;
7031d14ffa9Sbellard             old_pos = wpos;
7041d14ffa9Sbellard             old_pos +=
7051d14ffa9Sbellard                 millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
706c0fe3827Sbellard             old_pos %= bufsize;
7071d14ffa9Sbellard             old_pos &= ~hw->info.align;
7081d14ffa9Sbellard         }
7091d14ffa9Sbellard         else {
7101d14ffa9Sbellard             old_pos = wpos;
7111d14ffa9Sbellard         }
7121d14ffa9Sbellard #ifdef DEBUG_DSOUND
7131d14ffa9Sbellard         ds->played = 0;
7141d14ffa9Sbellard         ds->mixed = 0;
7151d14ffa9Sbellard #endif
7161d14ffa9Sbellard     }
7171d14ffa9Sbellard     else {
7181d14ffa9Sbellard         if (ds->old_pos == ppos) {
7191d14ffa9Sbellard #ifdef DEBUG_DSOUND
7201d14ffa9Sbellard             dolog ("old_pos == ppos\n");
7211d14ffa9Sbellard #endif
7221d14ffa9Sbellard             return 0;
7231d14ffa9Sbellard         }
7241d14ffa9Sbellard 
7251d14ffa9Sbellard #ifdef DEBUG_DSOUND
7261d14ffa9Sbellard         ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
7271d14ffa9Sbellard #endif
7281d14ffa9Sbellard         old_pos = ds->old_pos;
7291d14ffa9Sbellard     }
7301d14ffa9Sbellard 
7311d14ffa9Sbellard     if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
7321d14ffa9Sbellard         len = ppos - old_pos;
7331d14ffa9Sbellard     }
7341d14ffa9Sbellard     else {
735c0fe3827Sbellard         if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
736c0fe3827Sbellard             len = bufsize - old_pos + ppos;
7371d14ffa9Sbellard         }
7381d14ffa9Sbellard     }
7391d14ffa9Sbellard 
740c0fe3827Sbellard     if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
741c0fe3827Sbellard         dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
742c0fe3827Sbellard                len, bufsize, old_pos, ppos);
7431d14ffa9Sbellard         return 0;
7441d14ffa9Sbellard     }
7451d14ffa9Sbellard 
7461d14ffa9Sbellard     len &= ~hw->info.align;
7471d14ffa9Sbellard     if (!len) {
7481d14ffa9Sbellard         return 0;
7491d14ffa9Sbellard     }
7501d14ffa9Sbellard 
7511d14ffa9Sbellard #ifdef DEBUG_DSOUND
7521d14ffa9Sbellard     ds->old_ppos = ppos;
7531d14ffa9Sbellard #endif
7541d14ffa9Sbellard     err = dsound_lock_out (
7551d14ffa9Sbellard         dsb,
7561d14ffa9Sbellard         &hw->info,
7571d14ffa9Sbellard         old_pos,
7581d14ffa9Sbellard         len,
7591d14ffa9Sbellard         &p1, &p2,
7601d14ffa9Sbellard         &blen1, &blen2,
7611d14ffa9Sbellard         0
7621d14ffa9Sbellard         );
7631d14ffa9Sbellard     if (err) {
7641d14ffa9Sbellard         return 0;
7651d14ffa9Sbellard     }
7661d14ffa9Sbellard 
7671d14ffa9Sbellard     len1 = blen1 >> hwshift;
7681d14ffa9Sbellard     len2 = blen2 >> hwshift;
7691d14ffa9Sbellard     decr = len1 + len2;
7701d14ffa9Sbellard 
7711d14ffa9Sbellard     if (p1 && len1) {
7721d14ffa9Sbellard         dsound_write_sample (hw, p1, len1);
7731d14ffa9Sbellard     }
7741d14ffa9Sbellard 
7751d14ffa9Sbellard     if (p2 && len2) {
7761d14ffa9Sbellard         dsound_write_sample (hw, p2, len2);
7771d14ffa9Sbellard     }
7781d14ffa9Sbellard 
7791d14ffa9Sbellard     dsound_unlock_out (dsb, p1, p2, blen1, blen2);
780c0fe3827Sbellard     ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
7811d14ffa9Sbellard 
7821d14ffa9Sbellard #ifdef DEBUG_DSOUND
7831d14ffa9Sbellard     ds->mixed += decr << hwshift;
7841d14ffa9Sbellard 
7851d14ffa9Sbellard     dolog ("played %lu mixed %lu diff %ld sec %f\n",
7861d14ffa9Sbellard            ds->played,
7871d14ffa9Sbellard            ds->mixed,
7881d14ffa9Sbellard            ds->mixed - ds->played,
7891d14ffa9Sbellard            abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
7901d14ffa9Sbellard #endif
7911d14ffa9Sbellard     return decr;
7921d14ffa9Sbellard }
7931d14ffa9Sbellard 
7941d14ffa9Sbellard static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
7951d14ffa9Sbellard {
7961d14ffa9Sbellard     HRESULT hr;
7971d14ffa9Sbellard     DWORD status;
7981d14ffa9Sbellard     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
7991d14ffa9Sbellard     LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
8001d14ffa9Sbellard 
8011d14ffa9Sbellard     if (!dscb) {
8021d14ffa9Sbellard         dolog ("Attempt to control capture voice without a buffer\n");
8031d14ffa9Sbellard         return -1;
8041d14ffa9Sbellard     }
8051d14ffa9Sbellard 
8061d14ffa9Sbellard     switch (cmd) {
8071d14ffa9Sbellard     case VOICE_ENABLE:
8081d14ffa9Sbellard         if (dsound_get_status_in (dscb, &status)) {
8091d14ffa9Sbellard             return -1;
8101d14ffa9Sbellard         }
8111d14ffa9Sbellard 
8121d14ffa9Sbellard         if (status & DSCBSTATUS_CAPTURING) {
813c0fe3827Sbellard             dolog ("warning: Voice is already capturing\n");
8141d14ffa9Sbellard             return 0;
8151d14ffa9Sbellard         }
8161d14ffa9Sbellard 
8171d14ffa9Sbellard         /* clear ?? */
8181d14ffa9Sbellard 
8191d14ffa9Sbellard         hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
8201d14ffa9Sbellard         if (FAILED (hr)) {
821c0fe3827Sbellard             dsound_logerr (hr, "Could not start capturing\n");
8221d14ffa9Sbellard             return -1;
8231d14ffa9Sbellard         }
8241d14ffa9Sbellard         break;
8251d14ffa9Sbellard 
8261d14ffa9Sbellard     case VOICE_DISABLE:
8271d14ffa9Sbellard         if (dsound_get_status_in (dscb, &status)) {
8281d14ffa9Sbellard             return -1;
8291d14ffa9Sbellard         }
8301d14ffa9Sbellard 
8311d14ffa9Sbellard         if (status & DSCBSTATUS_CAPTURING) {
8321d14ffa9Sbellard             hr = IDirectSoundCaptureBuffer_Stop (dscb);
8331d14ffa9Sbellard             if (FAILED (hr)) {
834c0fe3827Sbellard                 dsound_logerr (hr, "Could not stop capturing\n");
8351d14ffa9Sbellard                 return -1;
8361d14ffa9Sbellard             }
8371d14ffa9Sbellard         }
8381d14ffa9Sbellard         else {
839c0fe3827Sbellard             dolog ("warning: Voice is not capturing\n");
8401d14ffa9Sbellard         }
8411d14ffa9Sbellard         break;
8421d14ffa9Sbellard     }
8431d14ffa9Sbellard     return 0;
8441d14ffa9Sbellard }
8451d14ffa9Sbellard 
8461d14ffa9Sbellard static int dsound_read (SWVoiceIn *sw, void *buf, int len)
8471d14ffa9Sbellard {
8481d14ffa9Sbellard     return audio_pcm_sw_read (sw, buf, len);
8491d14ffa9Sbellard }
8501d14ffa9Sbellard 
8511d14ffa9Sbellard static int dsound_run_in (HWVoiceIn *hw)
8521d14ffa9Sbellard {
8531d14ffa9Sbellard     int err;
8541d14ffa9Sbellard     HRESULT hr;
8551d14ffa9Sbellard     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
8561d14ffa9Sbellard     LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
8571d14ffa9Sbellard     int live, len, dead;
8581d14ffa9Sbellard     DWORD blen1, blen2;
8591d14ffa9Sbellard     DWORD len1, len2;
8601d14ffa9Sbellard     DWORD decr;
8611d14ffa9Sbellard     DWORD cpos, rpos;
8621d14ffa9Sbellard     LPVOID p1, p2;
8631d14ffa9Sbellard     int hwshift;
8641d14ffa9Sbellard 
8651d14ffa9Sbellard     if (!dscb) {
8661d14ffa9Sbellard         dolog ("Attempt to run without capture buffer\n");
8671d14ffa9Sbellard         return 0;
8681d14ffa9Sbellard     }
8691d14ffa9Sbellard 
8701d14ffa9Sbellard     hwshift = hw->info.shift;
8711d14ffa9Sbellard 
8721d14ffa9Sbellard     live = audio_pcm_hw_get_live_in (hw);
8731d14ffa9Sbellard     dead = hw->samples - live;
8741d14ffa9Sbellard     if (!dead) {
8751d14ffa9Sbellard         return 0;
8761d14ffa9Sbellard     }
8771d14ffa9Sbellard 
8781d14ffa9Sbellard     hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
8791d14ffa9Sbellard         dscb,
8801d14ffa9Sbellard         &cpos,
8811d14ffa9Sbellard         ds->first_time ? &rpos : NULL
8821d14ffa9Sbellard         );
8831d14ffa9Sbellard     if (FAILED (hr)) {
884c0fe3827Sbellard         dsound_logerr (hr, "Could not get capture buffer position\n");
8851d14ffa9Sbellard         return 0;
8861d14ffa9Sbellard     }
8871d14ffa9Sbellard 
8881d14ffa9Sbellard     if (ds->first_time) {
8891d14ffa9Sbellard         ds->first_time = 0;
8901d14ffa9Sbellard         if (rpos & hw->info.align) {
891c0fe3827Sbellard             ldebug ("warning: Misaligned capture read position %ld(%d)\n",
8921d14ffa9Sbellard                     rpos, hw->info.align);
8931d14ffa9Sbellard         }
8941d14ffa9Sbellard         hw->wpos = rpos >> hwshift;
8951d14ffa9Sbellard     }
8961d14ffa9Sbellard 
8971d14ffa9Sbellard     if (cpos & hw->info.align) {
898c0fe3827Sbellard         ldebug ("warning: Misaligned capture position %ld(%d)\n",
8991d14ffa9Sbellard                 cpos, hw->info.align);
9001d14ffa9Sbellard     }
9011d14ffa9Sbellard     cpos >>= hwshift;
9021d14ffa9Sbellard 
9031d14ffa9Sbellard     len = audio_ring_dist (cpos, hw->wpos, hw->samples);
9041d14ffa9Sbellard     if (!len) {
9051d14ffa9Sbellard         return 0;
9061d14ffa9Sbellard     }
9071d14ffa9Sbellard     len = audio_MIN (len, dead);
9081d14ffa9Sbellard 
9091d14ffa9Sbellard     err = dsound_lock_in (
9101d14ffa9Sbellard         dscb,
9111d14ffa9Sbellard         &hw->info,
9121d14ffa9Sbellard         hw->wpos << hwshift,
9131d14ffa9Sbellard         len << hwshift,
9141d14ffa9Sbellard         &p1,
9151d14ffa9Sbellard         &p2,
9161d14ffa9Sbellard         &blen1,
9171d14ffa9Sbellard         &blen2,
9181d14ffa9Sbellard         0
9191d14ffa9Sbellard         );
9201d14ffa9Sbellard     if (err) {
9211d14ffa9Sbellard         return 0;
9221d14ffa9Sbellard     }
9231d14ffa9Sbellard 
9241d14ffa9Sbellard     len1 = blen1 >> hwshift;
9251d14ffa9Sbellard     len2 = blen2 >> hwshift;
9261d14ffa9Sbellard     decr = len1 + len2;
9271d14ffa9Sbellard 
9281d14ffa9Sbellard     if (p1 && len1) {
9291d14ffa9Sbellard         hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
9301d14ffa9Sbellard     }
9311d14ffa9Sbellard 
9321d14ffa9Sbellard     if (p2 && len2) {
9331d14ffa9Sbellard         hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
9341d14ffa9Sbellard     }
9351d14ffa9Sbellard 
9361d14ffa9Sbellard     dsound_unlock_in (dscb, p1, p2, blen1, blen2);
9371d14ffa9Sbellard     hw->wpos = (hw->wpos + decr) % hw->samples;
9381d14ffa9Sbellard     return decr;
9391d14ffa9Sbellard }
9401d14ffa9Sbellard 
9411d14ffa9Sbellard static void dsound_audio_fini (void *opaque)
9421d14ffa9Sbellard {
9431d14ffa9Sbellard     HRESULT hr;
9441d14ffa9Sbellard     dsound *s = opaque;
9451d14ffa9Sbellard 
9461d14ffa9Sbellard     if (!s->dsound) {
9471d14ffa9Sbellard         return;
9481d14ffa9Sbellard     }
9491d14ffa9Sbellard 
9501d14ffa9Sbellard     hr = IDirectSound_Release (s->dsound);
9511d14ffa9Sbellard     if (FAILED (hr)) {
952c0fe3827Sbellard         dsound_logerr (hr, "Could not release DirectSound\n");
9531d14ffa9Sbellard     }
9541d14ffa9Sbellard     s->dsound = NULL;
9551d14ffa9Sbellard 
9561d14ffa9Sbellard     if (!s->dsound_capture) {
9571d14ffa9Sbellard         return;
9581d14ffa9Sbellard     }
9591d14ffa9Sbellard 
9601d14ffa9Sbellard     hr = IDirectSoundCapture_Release (s->dsound_capture);
9611d14ffa9Sbellard     if (FAILED (hr)) {
962c0fe3827Sbellard         dsound_logerr (hr, "Could not release DirectSoundCapture\n");
9631d14ffa9Sbellard     }
9641d14ffa9Sbellard     s->dsound_capture = NULL;
9651d14ffa9Sbellard }
9661d14ffa9Sbellard 
9671d14ffa9Sbellard static void *dsound_audio_init (void)
9681d14ffa9Sbellard {
9691d14ffa9Sbellard     int err;
9701d14ffa9Sbellard     HRESULT hr;
9711d14ffa9Sbellard     dsound *s = &glob_dsound;
9721d14ffa9Sbellard 
9731d14ffa9Sbellard     hr = CoInitialize (NULL);
9741d14ffa9Sbellard     if (FAILED (hr)) {
975c0fe3827Sbellard         dsound_logerr (hr, "Could not initialize COM\n");
9761d14ffa9Sbellard         return NULL;
9771d14ffa9Sbellard     }
9781d14ffa9Sbellard 
9791d14ffa9Sbellard     hr = CoCreateInstance (
9801d14ffa9Sbellard         &CLSID_DirectSound,
9811d14ffa9Sbellard         NULL,
9821d14ffa9Sbellard         CLSCTX_ALL,
9831d14ffa9Sbellard         &IID_IDirectSound,
9841d14ffa9Sbellard         (void **) &s->dsound
9851d14ffa9Sbellard         );
9861d14ffa9Sbellard     if (FAILED (hr)) {
987c0fe3827Sbellard         dsound_logerr (hr, "Could not create DirectSound instance\n");
9881d14ffa9Sbellard         return NULL;
9891d14ffa9Sbellard     }
9901d14ffa9Sbellard 
9911d14ffa9Sbellard     hr = IDirectSound_Initialize (s->dsound, NULL);
9921d14ffa9Sbellard     if (FAILED (hr)) {
993c0fe3827Sbellard         dsound_logerr (hr, "Could not initialize DirectSound\n");
9948ead62cfSbellard 
9958ead62cfSbellard         hr = IDirectSound_Release (s->dsound);
9968ead62cfSbellard         if (FAILED (hr)) {
9978ead62cfSbellard             dsound_logerr (hr, "Could not release DirectSound\n");
9988ead62cfSbellard         }
9998ead62cfSbellard         s->dsound = NULL;
10001d14ffa9Sbellard         return NULL;
10011d14ffa9Sbellard     }
10021d14ffa9Sbellard 
10031d14ffa9Sbellard     hr = CoCreateInstance (
10041d14ffa9Sbellard         &CLSID_DirectSoundCapture,
10051d14ffa9Sbellard         NULL,
10061d14ffa9Sbellard         CLSCTX_ALL,
10071d14ffa9Sbellard         &IID_IDirectSoundCapture,
10081d14ffa9Sbellard         (void **) &s->dsound_capture
10091d14ffa9Sbellard         );
10101d14ffa9Sbellard     if (FAILED (hr)) {
1011c0fe3827Sbellard         dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
10121d14ffa9Sbellard     }
10131d14ffa9Sbellard     else {
10141d14ffa9Sbellard         hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
10151d14ffa9Sbellard         if (FAILED (hr)) {
1016c0fe3827Sbellard             dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
10171d14ffa9Sbellard 
10181d14ffa9Sbellard             hr = IDirectSoundCapture_Release (s->dsound_capture);
10191d14ffa9Sbellard             if (FAILED (hr)) {
1020c0fe3827Sbellard                 dsound_logerr (hr, "Could not release DirectSoundCapture\n");
10211d14ffa9Sbellard             }
10221d14ffa9Sbellard             s->dsound_capture = NULL;
10231d14ffa9Sbellard         }
10241d14ffa9Sbellard     }
10251d14ffa9Sbellard 
10261d14ffa9Sbellard     err = dsound_open (s);
10271d14ffa9Sbellard     if (err) {
10281d14ffa9Sbellard         dsound_audio_fini (s);
10291d14ffa9Sbellard         return NULL;
10301d14ffa9Sbellard     }
10311d14ffa9Sbellard 
10321d14ffa9Sbellard     return s;
10331d14ffa9Sbellard }
10341d14ffa9Sbellard 
10351d14ffa9Sbellard static struct audio_option dsound_options[] = {
103698f9f48cSmalc     {
103798f9f48cSmalc         .name  = "LOCK_RETRIES",
10382700efa3SJuan Quintela         .tag   = AUD_OPT_INT,
10392700efa3SJuan Quintela         .valp  = &conf.lock_retries,
104098f9f48cSmalc         .descr = "Number of times to attempt locking the buffer"
104198f9f48cSmalc     },
104298f9f48cSmalc     {
104398f9f48cSmalc         .name  = "RESTOURE_RETRIES",
10442700efa3SJuan Quintela         .tag   = AUD_OPT_INT,
10452700efa3SJuan Quintela         .valp  = &conf.restore_retries,
104698f9f48cSmalc         .descr = "Number of times to attempt restoring the buffer"
104798f9f48cSmalc     },
104898f9f48cSmalc     {
104998f9f48cSmalc         .name  = "GETSTATUS_RETRIES",
10502700efa3SJuan Quintela         .tag   = AUD_OPT_INT,
10512700efa3SJuan Quintela         .valp  = &conf.getstatus_retries,
105298f9f48cSmalc         .descr = "Number of times to attempt getting status of the buffer"
105398f9f48cSmalc     },
105498f9f48cSmalc     {
105598f9f48cSmalc         .name  = "SET_PRIMARY",
10562700efa3SJuan Quintela         .tag   = AUD_OPT_BOOL,
1057*15c875a3SConsul         .valp  = &conf.set_primary,
105898f9f48cSmalc         .descr = "Set the parameters of primary buffer"
105998f9f48cSmalc     },
106098f9f48cSmalc     {
106198f9f48cSmalc         .name  = "LATENCY_MILLIS",
10622700efa3SJuan Quintela         .tag   = AUD_OPT_INT,
10632700efa3SJuan Quintela         .valp  = &conf.latency_millis,
106498f9f48cSmalc         .descr = "(undocumented)"
106598f9f48cSmalc     },
106698f9f48cSmalc     {
106798f9f48cSmalc         .name  = "PRIMARY_FREQ",
10682700efa3SJuan Quintela         .tag   = AUD_OPT_INT,
10692700efa3SJuan Quintela         .valp  = &conf.settings.freq,
107098f9f48cSmalc         .descr = "Primary buffer frequency"
107198f9f48cSmalc     },
107298f9f48cSmalc     {
107398f9f48cSmalc         .name  = "PRIMARY_CHANNELS",
10742700efa3SJuan Quintela         .tag   = AUD_OPT_INT,
10752700efa3SJuan Quintela         .valp  = &conf.settings.nchannels,
107698f9f48cSmalc         .descr = "Primary buffer number of channels (1 - mono, 2 - stereo)"
107798f9f48cSmalc     },
107898f9f48cSmalc     {
107998f9f48cSmalc         .name  = "PRIMARY_FMT",
10802700efa3SJuan Quintela         .tag   = AUD_OPT_FMT,
10812700efa3SJuan Quintela         .valp  = &conf.settings.fmt,
108298f9f48cSmalc         .descr = "Primary buffer format"
108398f9f48cSmalc     },
108498f9f48cSmalc     {
108598f9f48cSmalc         .name  = "BUFSIZE_OUT",
10862700efa3SJuan Quintela         .tag   = AUD_OPT_INT,
10872700efa3SJuan Quintela         .valp  = &conf.bufsize_out,
108898f9f48cSmalc         .descr = "(undocumented)"
108998f9f48cSmalc     },
109098f9f48cSmalc     {
109198f9f48cSmalc         .name  = "BUFSIZE_IN",
10922700efa3SJuan Quintela         .tag   = AUD_OPT_INT,
10932700efa3SJuan Quintela         .valp  = &conf.bufsize_in,
109498f9f48cSmalc         .descr = "(undocumented)"
109598f9f48cSmalc     },
10962700efa3SJuan Quintela     { /* End of list */ }
10971d14ffa9Sbellard };
10981d14ffa9Sbellard 
109935f4b58cSblueswir1 static struct audio_pcm_ops dsound_pcm_ops = {
11001dd3e4d1SJuan Quintela     .init_out = dsound_init_out,
11011dd3e4d1SJuan Quintela     .fini_out = dsound_fini_out,
11021dd3e4d1SJuan Quintela     .run_out  = dsound_run_out,
11031dd3e4d1SJuan Quintela     .write    = dsound_write,
11041dd3e4d1SJuan Quintela     .ctl_out  = dsound_ctl_out,
11051d14ffa9Sbellard 
11061dd3e4d1SJuan Quintela     .init_in  = dsound_init_in,
11071dd3e4d1SJuan Quintela     .fini_in  = dsound_fini_in,
11081dd3e4d1SJuan Quintela     .run_in   = dsound_run_in,
11091dd3e4d1SJuan Quintela     .read     = dsound_read,
11101dd3e4d1SJuan Quintela     .ctl_in   = dsound_ctl_in
11111d14ffa9Sbellard };
11121d14ffa9Sbellard 
11131d14ffa9Sbellard struct audio_driver dsound_audio_driver = {
1114bee37f32SJuan Quintela     .name           = "dsound",
1115bee37f32SJuan Quintela     .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound",
1116bee37f32SJuan Quintela     .options        = dsound_options,
1117bee37f32SJuan Quintela     .init           = dsound_audio_init,
1118bee37f32SJuan Quintela     .fini           = dsound_audio_fini,
1119bee37f32SJuan Quintela     .pcm_ops        = &dsound_pcm_ops,
1120bee37f32SJuan Quintela     .can_be_default = 1,
1121bee37f32SJuan Quintela     .max_voices_out = INT_MAX,
1122bee37f32SJuan Quintela     .max_voices_in  = 1,
1123bee37f32SJuan Quintela     .voice_size_out = sizeof (DSoundVoiceOut),
1124*15c875a3SConsul     .voice_size_in  = sizeof (DSoundVoiceIn)
11251d14ffa9Sbellard };
1126