~ubuntu-branches/ubuntu/wily/qemu-kvm-spice/wily

« back to all changes in this revision

Viewing changes to audio/dsoundaudio.c

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-10-19 10:44:56 UTC
  • Revision ID: james.westby@ubuntu.com-20111019104456-xgvskumk3sxi97f4
Tags: upstream-0.15.0+noroms
ImportĀ upstreamĀ versionĀ 0.15.0+noroms

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