~jderose/ubuntu/raring/qemu/vde-again

« back to all changes in this revision

Viewing changes to audio/dsoundaudio.c

  • Committer: Bazaar Package Importer
  • Author(s): Aurelien Jarno, Aurelien Jarno
  • Date: 2008-08-25 04:38:35 UTC
  • mfrom: (1.1.8 upstream)
  • Revision ID: james.westby@ubuntu.com-20080825043835-8e3tftavy8bujdch
Tags: 0.9.1-6
[ Aurelien Jarno ]
* debian/control: 
  - Update list of supported targets (Closes: bug#488339).
* debian/qemu-make-debian-root:
  - Use mktemp instead of $$ to create temporary directories (Closes: 
    bug#496394).
* debian/links:
  - Add missing links to manpages.

Show diffs side-by-side

added added

removed removed

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