xref: /qemu/audio/dsoundaudio.c (revision 1d14ffa97eacd3cb722271eaf6f093038396eac4)
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