1/*
2 * QEMU DirectSound audio driver header
3 *
4 * Copyright (c) 2005 Vassili Karpov (malc)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#ifdef DSBTYPE_IN
25#define NAME "capture buffer"
26#define NAME2 "DirectSoundCapture"
27#define TYPE in
28#define IFACE IDirectSoundCaptureBuffer
29#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
30#define FIELD dsound_capture_buffer
31#define FIELD2 dsound_capture
32#else
33#define NAME "playback buffer"
34#define NAME2 "DirectSound"
35#define TYPE out
36#define IFACE IDirectSoundBuffer
37#define BUFPTR LPDIRECTSOUNDBUFFER
38#define FIELD dsound_buffer
39#define FIELD2 dsound
40#endif
41
42static int glue (dsound_unlock_, TYPE) (
43    BUFPTR buf,
44    LPVOID p1,
45    LPVOID p2,
46    DWORD blen1,
47    DWORD blen2
48    )
49{
50    HRESULT hr;
51
52    hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2);
53    if (FAILED (hr)) {
54        dsound_logerr (hr, "Could not unlock " NAME "\n");
55        return -1;
56    }
57
58    return 0;
59}
60
61static int glue (dsound_lock_, TYPE) (
62    BUFPTR buf,
63    struct audio_pcm_info *info,
64    DWORD pos,
65    DWORD len,
66    LPVOID *p1p,
67    LPVOID *p2p,
68    DWORD *blen1p,
69    DWORD *blen2p,
70    int entire
71    )
72{
73    HRESULT hr;
74    int i;
75    LPVOID p1 = NULL, p2 = NULL;
76    DWORD blen1 = 0, blen2 = 0;
77    DWORD flag;
78
79#ifdef DSBTYPE_IN
80    flag = entire ? DSCBLOCK_ENTIREBUFFER : 0;
81#else
82    flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
83#endif
84    for (i = 0; i < conf.lock_retries; ++i) {
85        hr = glue (IFACE, _Lock) (
86            buf,
87            pos,
88            len,
89            &p1,
90            &blen1,
91            &p2,
92            &blen2,
93            flag
94            );
95
96        if (FAILED (hr)) {
97#ifndef DSBTYPE_IN
98            if (hr == DSERR_BUFFERLOST) {
99                if (glue (dsound_restore_, TYPE) (buf)) {
100                    dsound_logerr (hr, "Could not lock " NAME "\n");
101                    goto fail;
102                }
103                continue;
104            }
105#endif
106            dsound_logerr (hr, "Could not lock " NAME "\n");
107            goto fail;
108        }
109
110        break;
111    }
112
113    if (i == conf.lock_retries) {
114        dolog ("%d attempts to lock " NAME " failed\n", i);
115        goto fail;
116    }
117
118    if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
119        dolog ("DirectSound returned misaligned buffer %ld %ld\n",
120               blen1, blen2);
121        glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
122        goto fail;
123    }
124
125    if (!p1 && blen1) {
126        dolog ("warning: !p1 && blen1=%ld\n", blen1);
127        blen1 = 0;
128    }
129
130    if (!p2 && blen2) {
131        dolog ("warning: !p2 && blen2=%ld\n", blen2);
132        blen2 = 0;
133    }
134
135    *p1p = p1;
136    *p2p = p2;
137    *blen1p = blen1;
138    *blen2p = blen2;
139    return 0;
140
141 fail:
142    *p1p = NULL - 1;
143    *p2p = NULL - 1;
144    *blen1p = -1;
145    *blen2p = -1;
146    return -1;
147}
148
149#ifdef DSBTYPE_IN
150static void dsound_fini_in (HWVoiceIn *hw)
151#else
152static void dsound_fini_out (HWVoiceOut *hw)
153#endif
154{
155    HRESULT hr;
156#ifdef DSBTYPE_IN
157    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
158#else
159    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
160#endif
161
162    if (ds->FIELD) {
163        hr = glue (IFACE, _Stop) (ds->FIELD);
164        if (FAILED (hr)) {
165            dsound_logerr (hr, "Could not stop " NAME "\n");
166        }
167
168        hr = glue (IFACE, _Release) (ds->FIELD);
169        if (FAILED (hr)) {
170            dsound_logerr (hr, "Could not release " NAME "\n");
171        }
172        ds->FIELD = NULL;
173    }
174}
175
176#ifdef DSBTYPE_IN
177static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as)
178#else
179static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
180#endif
181{
182    int err;
183    HRESULT hr;
184    dsound *s = &glob_dsound;
185    WAVEFORMATEX wfx;
186    struct audsettings obt_as;
187#ifdef DSBTYPE_IN
188    const char *typ = "ADC";
189    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
190    DSCBUFFERDESC bd;
191    DSCBCAPS bc;
192#else
193    const char *typ = "DAC";
194    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
195    DSBUFFERDESC bd;
196    DSBCAPS bc;
197#endif
198
199    if (!s->FIELD2) {
200        dolog ("Attempt to initialize voice without " NAME2 " object\n");
201        return -1;
202    }
203
204    err = waveformat_from_audio_settings (&wfx, as);
205    if (err) {
206        return -1;
207    }
208
209    memset (&bd, 0, sizeof (bd));
210    bd.dwSize = sizeof (bd);
211    bd.lpwfxFormat = &wfx;
212#ifdef DSBTYPE_IN
213    bd.dwBufferBytes = conf.bufsize_in;
214    hr = IDirectSoundCapture_CreateCaptureBuffer (
215        s->dsound_capture,
216        &bd,
217        &ds->dsound_capture_buffer,
218        NULL
219        );
220#else
221    bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
222    bd.dwBufferBytes = conf.bufsize_out;
223    hr = IDirectSound_CreateSoundBuffer (
224        s->dsound,
225        &bd,
226        &ds->dsound_buffer,
227        NULL
228        );
229#endif
230
231    if (FAILED (hr)) {
232        dsound_logerr2 (hr, typ, "Could not create " NAME "\n");
233        return -1;
234    }
235
236    hr = glue (IFACE, _GetFormat) (ds->FIELD, &wfx, sizeof (wfx), NULL);
237    if (FAILED (hr)) {
238        dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
239        goto fail0;
240    }
241
242#ifdef DEBUG_DSOUND
243    dolog (NAME "\n");
244    print_wave_format (&wfx);
245#endif
246
247    memset (&bc, 0, sizeof (bc));
248    bc.dwSize = sizeof (bc);
249
250    hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc);
251    if (FAILED (hr)) {
252        dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
253        goto fail0;
254    }
255
256    err = waveformat_to_audio_settings (&wfx, &obt_as);
257    if (err) {
258        goto fail0;
259    }
260
261    ds->first_time = 1;
262    obt_as.endianness = 0;
263    audio_pcm_init_info (&hw->info, &obt_as);
264
265    if (bc.dwBufferBytes & hw->info.align) {
266        dolog (
267            "GetCaps returned misaligned buffer size %ld, alignment %d\n",
268            bc.dwBufferBytes, hw->info.align + 1
269            );
270    }
271    hw->samples = bc.dwBufferBytes >> hw->info.shift;
272
273#ifdef DEBUG_DSOUND
274    dolog ("caps %ld, desc %ld\n",
275           bc.dwBufferBytes, bd.dwBufferBytes);
276
277    dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
278           hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
279#endif
280    return 0;
281
282 fail0:
283    glue (dsound_fini_, TYPE) (hw);
284    return -1;
285}
286
287#undef NAME
288#undef NAME2
289#undef TYPE
290#undef IFACE
291#undef BUFPTR
292#undef FIELD
293#undef FIELD2
294