Statistics
| Branch: | Revision:

root / audio / dsoundaudio.c @ 109819e0

History | View | Annotate | Download (26.6 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
#include <windows.h>
36
#include <mmsystem.h>
37
#include <objbase.h>
38
#include <dsound.h>
39

    
40
/* #define DEBUG_DSOUND */
41

    
42
static struct {
43
    int lock_retries;
44
    int restore_retries;
45
    int getstatus_retries;
46
    int set_primary;
47
    int bufsize_in;
48
    int bufsize_out;
49
    struct audsettings settings;
50
    int latency_millis;
51
} conf = {
52
    1,
53
    1,
54
    1,
55
    0,
56
    16384,
57
    16384,
58
    {
59
        44100,
60
        2,
61
        AUD_FMT_S16
62
    },
63
    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
static int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
310
                                           struct audsettings *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,
350
                                         struct audsettings *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
    case 32:
391
        as->fmt = AUD_FMT_S32;
392
        break;
393

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

    
401
    return 0;
402
}
403

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

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

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

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

    
430
    return 0;
431
}
432

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

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

    
444
    return 0;
445
}
446

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
599
    return 0;
600

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

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

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

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

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

    
629
        dsound_clear_sample (hw, dsb);
630

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

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

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

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

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

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

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

    
685
    live = audio_pcm_hw_get_live_out (hw);
686

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

    
697
    len = live << hwshift;
698

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
819
        /* clear ?? */
820

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

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

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

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

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

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

    
872
    hwshift = hw->info.shift;
873

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1034
    return s;
1035
}
1036

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

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

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

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