Statistics
| Branch: | Revision:

root / audio / dsoundaudio.c @ 1ea879e5

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
#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
    struct audsettings 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
    struct audsettings 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,
311
                                           struct audsettings *as)
312
{
313
    memset (wfx, 0, sizeof (*wfx));
314

    
315
    wfx->wFormatTag = WAVE_FORMAT_PCM;
316
    wfx->nChannels = as->nchannels;
317
    wfx->nSamplesPerSec = as->freq;
318
    wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
319
    wfx->nBlockAlign = 1 << (as->nchannels == 2);
320
    wfx->cbSize = 0;
321

    
322
    switch (as->fmt) {
323
    case AUD_FMT_S8:
324
    case AUD_FMT_U8:
325
        wfx->wBitsPerSample = 8;
326
        break;
327

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

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

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

    
347
    return 0;
348
}
349

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

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

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

    
370
    case 2:
371
        as->nchannels = 2;
372
        break;
373

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

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

    
387
    case 16:
388
        as->fmt = AUD_FMT_S16;
389
        break;
390

    
391
    case 32:
392
        as->fmt = AUD_FMT_S32;
393
        break;
394

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

    
402
    return 0;
403
}
404

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

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

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

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

    
431
    return 0;
432
}
433

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

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

    
445
    return 0;
446
}
447

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
600
    return 0;
601

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

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

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

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

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

    
630
        dsound_clear_sample (hw, dsb);
631

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

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

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

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

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

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

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

    
686
    live = audio_pcm_hw_get_live_out (hw);
687

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

    
698
    len = live << hwshift;
699

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
820
        /* clear ?? */
821

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

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

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

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

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

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

    
873
    hwshift = hw->info.shift;
874

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1035
    return s;
1036
}
1037

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

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

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

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