Statistics
| Branch: | Revision:

root / audio / dsoundaudio.c @ da3d9c5b

History | View | Annotate | Download (26.5 kB)

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
    case AUD_FMT_U8:
324
        wfx->wBitsPerSample = 8;
325
        break;
326

    
327
    case AUD_FMT_S16:
328
    case AUD_FMT_U16:
329
        wfx->wBitsPerSample = 16;
330
        wfx->nAvgBytesPerSec <<= 1;
331
        wfx->nBlockAlign <<= 1;
332
        break;
333

    
334
    case AUD_FMT_S32:
335
    case AUD_FMT_U32:
336
        wfx->wBitsPerSample = 32;
337
        wfx->nAvgBytesPerSec <<= 2;
338
        wfx->nBlockAlign <<= 2;
339
        break;
340

    
341
    default:
342
        dolog ("Internal logic error: Bad audio format %d\n", as->freq);
343
        return -1;
344
    }
345

    
346
    return 0;
347
}
348

    
349
static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
350
{
351
    if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
352
        dolog ("Invalid wave format, tag is not PCM, but %d\n",
353
               wfx->wFormatTag);
354
        return -1;
355
    }
356

    
357
    if (!wfx->nSamplesPerSec) {
358
        dolog ("Invalid wave format, frequency is zero\n");
359
        return -1;
360
    }
361
    as->freq = wfx->nSamplesPerSec;
362

    
363
    switch (wfx->nChannels) {
364
    case 1:
365
        as->nchannels = 1;
366
        break;
367

    
368
    case 2:
369
        as->nchannels = 2;
370
        break;
371

    
372
    default:
373
        dolog (
374
            "Invalid wave format, number of channels is not 1 or 2, but %d\n",
375
            wfx->nChannels
376
            );
377
        return -1;
378
    }
379

    
380
    switch (wfx->wBitsPerSample) {
381
    case 8:
382
        as->fmt = AUD_FMT_U8;
383
        break;
384

    
385
    case 16:
386
        as->fmt = AUD_FMT_S16;
387
        break;
388

    
389
    case 32:
390
        as->fmt = AUD_FMT_S32;
391
        break;
392

    
393
    default:
394
        dolog ("Invalid wave format, bits per sample is not "
395
               "8, 16 or 32, but %d\n",
396
               wfx->wBitsPerSample);
397
        return -1;
398
    }
399

    
400
    return 0;
401
}
402

    
403
#include "dsound_template.h"
404
#define DSBTYPE_IN
405
#include "dsound_template.h"
406
#undef DSBTYPE_IN
407

    
408
static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
409
{
410
    HRESULT hr;
411
    int i;
412

    
413
    for (i = 0; i < conf.getstatus_retries; ++i) {
414
        hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
415
        if (FAILED (hr)) {
416
            dsound_logerr (hr, "Could not get playback buffer status\n");
417
            return -1;
418
        }
419

    
420
        if (*statusp & DSERR_BUFFERLOST) {
421
            if (dsound_restore_out (dsb)) {
422
                return -1;
423
            }
424
            continue;
425
        }
426
        break;
427
    }
428

    
429
    return 0;
430
}
431

    
432
static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
433
                                 DWORD *statusp)
434
{
435
    HRESULT hr;
436

    
437
    hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
438
    if (FAILED (hr)) {
439
        dsound_logerr (hr, "Could not get capture buffer status\n");
440
        return -1;
441
    }
442

    
443
    return 0;
444
}
445

    
446
static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
447
{
448
    int src_len1 = dst_len;
449
    int src_len2 = 0;
450
    int pos = hw->rpos + dst_len;
451
    st_sample_t *src1 = hw->mix_buf + hw->rpos;
452
    st_sample_t *src2 = NULL;
453

    
454
    if (pos > hw->samples) {
455
        src_len1 = hw->samples - hw->rpos;
456
        src2 = hw->mix_buf;
457
        src_len2 = dst_len - src_len1;
458
        pos = src_len2;
459
    }
460

    
461
    if (src_len1) {
462
        hw->clip (dst, src1, src_len1);
463
    }
464

    
465
    if (src_len2) {
466
        dst = advance (dst, src_len1 << hw->info.shift);
467
        hw->clip (dst, src2, src_len2);
468
    }
469

    
470
    hw->rpos = pos % hw->samples;
471
}
472

    
473
static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
474
{
475
    int err;
476
    LPVOID p1, p2;
477
    DWORD blen1, blen2, len1, len2;
478

    
479
    err = dsound_lock_out (
480
        dsb,
481
        &hw->info,
482
        0,
483
        hw->samples << hw->info.shift,
484
        &p1, &p2,
485
        &blen1, &blen2,
486
        1
487
        );
488
    if (err) {
489
        return;
490
    }
491

    
492
    len1 = blen1 >> hw->info.shift;
493
    len2 = blen2 >> hw->info.shift;
494

    
495
#ifdef DEBUG_DSOUND
496
    dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
497
           p1, blen1, len1,
498
           p2, blen2, len2);
499
#endif
500

    
501
    if (p1 && len1) {
502
        audio_pcm_info_clear_buf (&hw->info, p1, len1);
503
    }
504

    
505
    if (p2 && len2) {
506
        audio_pcm_info_clear_buf (&hw->info, p2, len2);
507
    }
508

    
509
    dsound_unlock_out (dsb, p1, p2, blen1, blen2);
510
}
511

    
512
static void dsound_close (dsound *s)
513
{
514
    HRESULT hr;
515

    
516
    if (s->dsound_primary_buffer) {
517
        hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
518
        if (FAILED (hr)) {
519
            dsound_logerr (hr, "Could not release primary buffer\n");
520
        }
521
        s->dsound_primary_buffer = NULL;
522
    }
523
}
524

    
525
static int dsound_open (dsound *s)
526
{
527
    int err;
528
    HRESULT hr;
529
    WAVEFORMATEX wfx;
530
    DSBUFFERDESC dsbd;
531
    HWND hwnd;
532

    
533
    hwnd = GetForegroundWindow ();
534
    hr = IDirectSound_SetCooperativeLevel (
535
        s->dsound,
536
        hwnd,
537
        DSSCL_PRIORITY
538
        );
539

    
540
    if (FAILED (hr)) {
541
        dsound_logerr (hr, "Could not set cooperative level for window %p\n",
542
                       hwnd);
543
        return -1;
544
    }
545

    
546
    if (!conf.set_primary) {
547
        return 0;
548
    }
549

    
550
    err = waveformat_from_audio_settings (&wfx, &conf.settings);
551
    if (err) {
552
        return -1;
553
    }
554

    
555
    memset (&dsbd, 0, sizeof (dsbd));
556
    dsbd.dwSize = sizeof (dsbd);
557
    dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
558
    dsbd.dwBufferBytes = 0;
559
    dsbd.lpwfxFormat = NULL;
560

    
561
    hr = IDirectSound_CreateSoundBuffer (
562
        s->dsound,
563
        &dsbd,
564
        &s->dsound_primary_buffer,
565
        NULL
566
        );
567
    if (FAILED (hr)) {
568
        dsound_logerr (hr, "Could not create primary playback buffer\n");
569
        return -1;
570
    }
571

    
572
    hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
573
    if (FAILED (hr)) {
574
        dsound_logerr (hr, "Could not set primary playback buffer format\n");
575
    }
576

    
577
    hr = IDirectSoundBuffer_GetFormat (
578
        s->dsound_primary_buffer,
579
        &wfx,
580
        sizeof (wfx),
581
        NULL
582
        );
583
    if (FAILED (hr)) {
584
        dsound_logerr (hr, "Could not get primary playback buffer format\n");
585
        goto fail0;
586
    }
587

    
588
#ifdef DEBUG_DSOUND
589
    dolog ("Primary\n");
590
    print_wave_format (&wfx);
591
#endif
592

    
593
    err = waveformat_to_audio_settings (&wfx, &s->settings);
594
    if (err) {
595
        goto fail0;
596
    }
597

    
598
    return 0;
599

    
600
 fail0:
601
    dsound_close (s);
602
    return -1;
603
}
604

    
605
static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
606
{
607
    HRESULT hr;
608
    DWORD status;
609
    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
610
    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
611

    
612
    if (!dsb) {
613
        dolog ("Attempt to control voice without a buffer\n");
614
        return 0;
615
    }
616

    
617
    switch (cmd) {
618
    case VOICE_ENABLE:
619
        if (dsound_get_status_out (dsb, &status)) {
620
            return -1;
621
        }
622

    
623
        if (status & DSBSTATUS_PLAYING) {
624
            dolog ("warning: Voice is already playing\n");
625
            return 0;
626
        }
627

    
628
        dsound_clear_sample (hw, dsb);
629

    
630
        hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
631
        if (FAILED (hr)) {
632
            dsound_logerr (hr, "Could not start playing buffer\n");
633
            return -1;
634
        }
635
        break;
636

    
637
    case VOICE_DISABLE:
638
        if (dsound_get_status_out (dsb, &status)) {
639
            return -1;
640
        }
641

    
642
        if (status & DSBSTATUS_PLAYING) {
643
            hr = IDirectSoundBuffer_Stop (dsb);
644
            if (FAILED (hr)) {
645
                dsound_logerr (hr, "Could not stop playing buffer\n");
646
                return -1;
647
            }
648
        }
649
        else {
650
            dolog ("warning: Voice is not playing\n");
651
        }
652
        break;
653
    }
654
    return 0;
655
}
656

    
657
static int dsound_write (SWVoiceOut *sw, void *buf, int len)
658
{
659
    return audio_pcm_sw_write (sw, buf, len);
660
}
661

    
662
static int dsound_run_out (HWVoiceOut *hw)
663
{
664
    int err;
665
    HRESULT hr;
666
    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
667
    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
668
    int live, len, hwshift;
669
    DWORD blen1, blen2;
670
    DWORD len1, len2;
671
    DWORD decr;
672
    DWORD wpos, ppos, old_pos;
673
    LPVOID p1, p2;
674
    int bufsize;
675

    
676
    if (!dsb) {
677
        dolog ("Attempt to run empty with playback buffer\n");
678
        return 0;
679
    }
680

    
681
    hwshift = hw->info.shift;
682
    bufsize = hw->samples << hwshift;
683

    
684
    live = audio_pcm_hw_get_live_out (hw);
685

    
686
    hr = IDirectSoundBuffer_GetCurrentPosition (
687
        dsb,
688
        &ppos,
689
        ds->first_time ? &wpos : NULL
690
        );
691
    if (FAILED (hr)) {
692
        dsound_logerr (hr, "Could not get playback buffer position\n");
693
        return 0;
694
    }
695

    
696
    len = live << hwshift;
697

    
698
    if (ds->first_time) {
699
        if (conf.latency_millis) {
700
            DWORD cur_blat;
701

    
702
            cur_blat = audio_ring_dist (wpos, ppos, bufsize);
703
            ds->first_time = 0;
704
            old_pos = wpos;
705
            old_pos +=
706
                millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
707
            old_pos %= bufsize;
708
            old_pos &= ~hw->info.align;
709
        }
710
        else {
711
            old_pos = wpos;
712
        }
713
#ifdef DEBUG_DSOUND
714
        ds->played = 0;
715
        ds->mixed = 0;
716
#endif
717
    }
718
    else {
719
        if (ds->old_pos == ppos) {
720
#ifdef DEBUG_DSOUND
721
            dolog ("old_pos == ppos\n");
722
#endif
723
            return 0;
724
        }
725

    
726
#ifdef DEBUG_DSOUND
727
        ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
728
#endif
729
        old_pos = ds->old_pos;
730
    }
731

    
732
    if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
733
        len = ppos - old_pos;
734
    }
735
    else {
736
        if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
737
            len = bufsize - old_pos + ppos;
738
        }
739
    }
740

    
741
    if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
742
        dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
743
               len, bufsize, old_pos, ppos);
744
        return 0;
745
    }
746

    
747
    len &= ~hw->info.align;
748
    if (!len) {
749
        return 0;
750
    }
751

    
752
#ifdef DEBUG_DSOUND
753
    ds->old_ppos = ppos;
754
#endif
755
    err = dsound_lock_out (
756
        dsb,
757
        &hw->info,
758
        old_pos,
759
        len,
760
        &p1, &p2,
761
        &blen1, &blen2,
762
        0
763
        );
764
    if (err) {
765
        return 0;
766
    }
767

    
768
    len1 = blen1 >> hwshift;
769
    len2 = blen2 >> hwshift;
770
    decr = len1 + len2;
771

    
772
    if (p1 && len1) {
773
        dsound_write_sample (hw, p1, len1);
774
    }
775

    
776
    if (p2 && len2) {
777
        dsound_write_sample (hw, p2, len2);
778
    }
779

    
780
    dsound_unlock_out (dsb, p1, p2, blen1, blen2);
781
    ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
782

    
783
#ifdef DEBUG_DSOUND
784
    ds->mixed += decr << hwshift;
785

    
786
    dolog ("played %lu mixed %lu diff %ld sec %f\n",
787
           ds->played,
788
           ds->mixed,
789
           ds->mixed - ds->played,
790
           abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
791
#endif
792
    return decr;
793
}
794

    
795
static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
796
{
797
    HRESULT hr;
798
    DWORD status;
799
    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
800
    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
801

    
802
    if (!dscb) {
803
        dolog ("Attempt to control capture voice without a buffer\n");
804
        return -1;
805
    }
806

    
807
    switch (cmd) {
808
    case VOICE_ENABLE:
809
        if (dsound_get_status_in (dscb, &status)) {
810
            return -1;
811
        }
812

    
813
        if (status & DSCBSTATUS_CAPTURING) {
814
            dolog ("warning: Voice is already capturing\n");
815
            return 0;
816
        }
817

    
818
        /* clear ?? */
819

    
820
        hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
821
        if (FAILED (hr)) {
822
            dsound_logerr (hr, "Could not start capturing\n");
823
            return -1;
824
        }
825
        break;
826

    
827
    case VOICE_DISABLE:
828
        if (dsound_get_status_in (dscb, &status)) {
829
            return -1;
830
        }
831

    
832
        if (status & DSCBSTATUS_CAPTURING) {
833
            hr = IDirectSoundCaptureBuffer_Stop (dscb);
834
            if (FAILED (hr)) {
835
                dsound_logerr (hr, "Could not stop capturing\n");
836
                return -1;
837
            }
838
        }
839
        else {
840
            dolog ("warning: Voice is not capturing\n");
841
        }
842
        break;
843
    }
844
    return 0;
845
}
846

    
847
static int dsound_read (SWVoiceIn *sw, void *buf, int len)
848
{
849
    return audio_pcm_sw_read (sw, buf, len);
850
}
851

    
852
static int dsound_run_in (HWVoiceIn *hw)
853
{
854
    int err;
855
    HRESULT hr;
856
    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
857
    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
858
    int live, len, dead;
859
    DWORD blen1, blen2;
860
    DWORD len1, len2;
861
    DWORD decr;
862
    DWORD cpos, rpos;
863
    LPVOID p1, p2;
864
    int hwshift;
865

    
866
    if (!dscb) {
867
        dolog ("Attempt to run without capture buffer\n");
868
        return 0;
869
    }
870

    
871
    hwshift = hw->info.shift;
872

    
873
    live = audio_pcm_hw_get_live_in (hw);
874
    dead = hw->samples - live;
875
    if (!dead) {
876
        return 0;
877
    }
878

    
879
    hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
880
        dscb,
881
        &cpos,
882
        ds->first_time ? &rpos : NULL
883
        );
884
    if (FAILED (hr)) {
885
        dsound_logerr (hr, "Could not get capture buffer position\n");
886
        return 0;
887
    }
888

    
889
    if (ds->first_time) {
890
        ds->first_time = 0;
891
        if (rpos & hw->info.align) {
892
            ldebug ("warning: Misaligned capture read position %ld(%d)\n",
893
                    rpos, hw->info.align);
894
        }
895
        hw->wpos = rpos >> hwshift;
896
    }
897

    
898
    if (cpos & hw->info.align) {
899
        ldebug ("warning: Misaligned capture position %ld(%d)\n",
900
                cpos, hw->info.align);
901
    }
902
    cpos >>= hwshift;
903

    
904
    len = audio_ring_dist (cpos, hw->wpos, hw->samples);
905
    if (!len) {
906
        return 0;
907
    }
908
    len = audio_MIN (len, dead);
909

    
910
    err = dsound_lock_in (
911
        dscb,
912
        &hw->info,
913
        hw->wpos << hwshift,
914
        len << hwshift,
915
        &p1,
916
        &p2,
917
        &blen1,
918
        &blen2,
919
        0
920
        );
921
    if (err) {
922
        return 0;
923
    }
924

    
925
    len1 = blen1 >> hwshift;
926
    len2 = blen2 >> hwshift;
927
    decr = len1 + len2;
928

    
929
    if (p1 && len1) {
930
        hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
931
    }
932

    
933
    if (p2 && len2) {
934
        hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
935
    }
936

    
937
    dsound_unlock_in (dscb, p1, p2, blen1, blen2);
938
    hw->wpos = (hw->wpos + decr) % hw->samples;
939
    return decr;
940
}
941

    
942
static void dsound_audio_fini (void *opaque)
943
{
944
    HRESULT hr;
945
    dsound *s = opaque;
946

    
947
    if (!s->dsound) {
948
        return;
949
    }
950

    
951
    hr = IDirectSound_Release (s->dsound);
952
    if (FAILED (hr)) {
953
        dsound_logerr (hr, "Could not release DirectSound\n");
954
    }
955
    s->dsound = NULL;
956

    
957
    if (!s->dsound_capture) {
958
        return;
959
    }
960

    
961
    hr = IDirectSoundCapture_Release (s->dsound_capture);
962
    if (FAILED (hr)) {
963
        dsound_logerr (hr, "Could not release DirectSoundCapture\n");
964
    }
965
    s->dsound_capture = NULL;
966
}
967

    
968
static void *dsound_audio_init (void)
969
{
970
    int err;
971
    HRESULT hr;
972
    dsound *s = &glob_dsound;
973

    
974
    hr = CoInitialize (NULL);
975
    if (FAILED (hr)) {
976
        dsound_logerr (hr, "Could not initialize COM\n");
977
        return NULL;
978
    }
979

    
980
    hr = CoCreateInstance (
981
        &CLSID_DirectSound,
982
        NULL,
983
        CLSCTX_ALL,
984
        &IID_IDirectSound,
985
        (void **) &s->dsound
986
        );
987
    if (FAILED (hr)) {
988
        dsound_logerr (hr, "Could not create DirectSound instance\n");
989
        return NULL;
990
    }
991

    
992
    hr = IDirectSound_Initialize (s->dsound, NULL);
993
    if (FAILED (hr)) {
994
        dsound_logerr (hr, "Could not initialize DirectSound\n");
995

    
996
        hr = IDirectSound_Release (s->dsound);
997
        if (FAILED (hr)) {
998
            dsound_logerr (hr, "Could not release DirectSound\n");
999
        }
1000
        s->dsound = NULL;
1001
        return NULL;
1002
    }
1003

    
1004
    hr = CoCreateInstance (
1005
        &CLSID_DirectSoundCapture,
1006
        NULL,
1007
        CLSCTX_ALL,
1008
        &IID_IDirectSoundCapture,
1009
        (void **) &s->dsound_capture
1010
        );
1011
    if (FAILED (hr)) {
1012
        dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
1013
    }
1014
    else {
1015
        hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
1016
        if (FAILED (hr)) {
1017
            dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
1018

    
1019
            hr = IDirectSoundCapture_Release (s->dsound_capture);
1020
            if (FAILED (hr)) {
1021
                dsound_logerr (hr, "Could not release DirectSoundCapture\n");
1022
            }
1023
            s->dsound_capture = NULL;
1024
        }
1025
    }
1026

    
1027
    err = dsound_open (s);
1028
    if (err) {
1029
        dsound_audio_fini (s);
1030
        return NULL;
1031
    }
1032

    
1033
    return s;
1034
}
1035

    
1036
static struct audio_option dsound_options[] = {
1037
    {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries,
1038
     "Number of times to attempt locking the buffer", NULL, 0},
1039
    {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries,
1040
     "Number of times to attempt restoring the buffer", NULL, 0},
1041
    {"GETSTATUS_RETRIES", AUD_OPT_INT, &conf.getstatus_retries,
1042
     "Number of times to attempt getting status of the buffer", NULL, 0},
1043
    {"SET_PRIMARY", AUD_OPT_BOOL, &conf.set_primary,
1044
     "Set the parameters of primary buffer", NULL, 0},
1045
    {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis,
1046
     "(undocumented)", NULL, 0},
1047
    {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq,
1048
     "Primary buffer frequency", NULL, 0},
1049
    {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
1050
     "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0},
1051
    {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt,
1052
     "Primary buffer format", NULL, 0},
1053
    {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out,
1054
     "(undocumented)", NULL, 0},
1055
    {"BUFSIZE_IN", AUD_OPT_INT, &conf.bufsize_in,
1056
     "(undocumented)", NULL, 0},
1057
    {NULL, 0, NULL, NULL, NULL, 0}
1058
};
1059

    
1060
static struct audio_pcm_ops dsound_pcm_ops = {
1061
    dsound_init_out,
1062
    dsound_fini_out,
1063
    dsound_run_out,
1064
    dsound_write,
1065
    dsound_ctl_out,
1066

    
1067
    dsound_init_in,
1068
    dsound_fini_in,
1069
    dsound_run_in,
1070
    dsound_read,
1071
    dsound_ctl_in
1072
};
1073

    
1074
struct audio_driver dsound_audio_driver = {
1075
    INIT_FIELD (name           = ) "dsound",
1076
    INIT_FIELD (descr          = )
1077
    "DirectSound http://wikipedia.org/wiki/DirectSound",
1078
    INIT_FIELD (options        = ) dsound_options,
1079
    INIT_FIELD (init           = ) dsound_audio_init,
1080
    INIT_FIELD (fini           = ) dsound_audio_fini,
1081
    INIT_FIELD (pcm_ops        = ) &dsound_pcm_ops,
1082
    INIT_FIELD (can_be_default = ) 1,
1083
    INIT_FIELD (max_voices_out = ) INT_MAX,
1084
    INIT_FIELD (max_voices_in  = ) 1,
1085
    INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut),
1086
    INIT_FIELD (voice_size_in  = ) sizeof (DSoundVoiceIn)
1087
};