1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      DirectSound sound driver.
12  *
13  *      By Stefan Schimanski.
14  *
15  *      Various bugfixes by Patrick Hogan.
16  *
17  *      Custom loop points support added by Eric Botcazou.
18  *
19  *      Backward playing support, bidirectional looping support
20  *      and bugfixes by Javier Gonzalez.
21  *
22  *      See readme.txt for copyright information.
23  */
24 
25 
26 #define DIRECTSOUND_VERSION 0x0300
27 
28 #include "allegro.h"
29 #include "allegro/internal/aintern.h"
30 #include "allegro/platform/aintwin.h"
31 
32 #ifndef SCAN_DEPEND
33    #ifdef ALLEGRO_MINGW32
34       #undef MAKEFOURCC
35    #endif
36 
37    #include <mmsystem.h>
38    #include <dsound.h>
39    #include <math.h>
40 
41    #ifdef ALLEGRO_MSVC
42       #include <mmreg.h>
43    #endif
44 #endif
45 
46 #ifndef ALLEGRO_WINDOWS
47 #error something is wrong with the makefile
48 #endif
49 
50 #define PREFIX_I                "al-dsound INFO: "
51 #define PREFIX_W                "al-dsound WARNING: "
52 #define PREFIX_E                "al-dsound ERROR: "
53 
54 
55 static int digi_directsound_detect(int input);
56 static int digi_directsound_init(int input, int voices);
57 static void digi_directsound_exit(int input);
58 static int digi_directsound_set_mixer_volume(int volume);
59 static int digi_directsound_get_mixer_volume(void);
60 
61 static void digi_directsound_init_voice(int voice, AL_CONST SAMPLE * sample);
62 static void digi_directsound_release_voice(int voice);
63 static void digi_directsound_start_voice(int voice);
64 static void digi_directsound_stop_voice(int voice);
65 static void digi_directsound_loop_voice(int voice, int playmode);
66 
67 static void *digi_directsound_lock_voice(int voice, int start, int end);
68 static void digi_directsound_unlock_voice(int voice);
69 
70 static int digi_directsound_get_position(int voice);
71 static void digi_directsound_set_position(int voice, int position);
72 
73 static int digi_directsound_get_volume(int voice);
74 static void digi_directsound_set_volume(int voice, int volume);
75 
76 static int digi_directsound_get_frequency(int voice);
77 static void digi_directsound_set_frequency(int voice, int frequency);
78 
79 static int digi_directsound_get_pan(int voice);
80 static void digi_directsound_set_pan(int voice, int pan);
81 
82 
83 /* template driver: will be cloned for each device */
84 static DIGI_DRIVER digi_directsound =
85 {
86    0,
87    empty_string,
88    empty_string,
89    empty_string,
90    0,   // available voices
91    0,   // voice number offset
92    16,  // maximum voices we can support
93    4,   // default number of voices to use
94 
95    /* setup routines */
96    digi_directsound_detect,
97    digi_directsound_init,
98    digi_directsound_exit,
99    digi_directsound_set_mixer_volume,
100    digi_directsound_get_mixer_volume,
101 
102    /* audiostream locking functions */
103    digi_directsound_lock_voice,
104    digi_directsound_unlock_voice,
105    NULL,  // AL_METHOD(int,  buffer_size, (void));
106 
107    /* voice control functions */
108    digi_directsound_init_voice,
109    digi_directsound_release_voice,
110    digi_directsound_start_voice,
111    digi_directsound_stop_voice,
112    digi_directsound_loop_voice,
113 
114    /* position control functions */
115    digi_directsound_get_position,
116    digi_directsound_set_position,
117 
118    /* volume control functions */
119    digi_directsound_get_volume,
120    digi_directsound_set_volume,
121    NULL,  // AL_METHOD(void, ramp_volume, (int voice, int time, int endvol));
122    NULL,  // AL_METHOD(void, stop_volume_ramp, (int voice));
123 
124    /* pitch control functions */
125    digi_directsound_get_frequency,
126    digi_directsound_set_frequency,
127    NULL,  // AL_METHOD(void, sweep_frequency, (int voice, int time, int endfreq));
128    NULL,  // AL_METHOD(void, stop_frequency_sweep, (int voice));
129 
130    /* pan control functions */
131    digi_directsound_get_pan,
132    digi_directsound_set_pan,
133    NULL,  // AL_METHOD(void, sweep_pan, (int voice, int time, int endpan));
134    NULL,  // AL_METHOD(void, stop_pan_sweep, (int voice));
135 
136    /* effect control functions */
137    NULL,  // AL_METHOD(void, set_echo, (int voice, int strength, int delay));
138    NULL,  // AL_METHOD(void, set_tremolo, (int voice, int rate, int depth));
139    NULL,  // AL_METHOD(void, set_vibrato, (int voice, int rate, int depth));
140 
141    /* input functions */
142    0,                           /* int rec_cap_bits; */
143    0,                           /* int rec_cap_stereo; */
144    digi_directsound_rec_cap_rate,
145    digi_directsound_rec_cap_param,
146    digi_directsound_rec_source,
147    digi_directsound_rec_start,
148    digi_directsound_rec_stop,
149    digi_directsound_rec_read
150 };
151 
152 
153 /* sound driver globals */
154 static LPDIRECTSOUND directsound = NULL;
155 static LPDIRECTSOUNDBUFFER prim_buf = NULL;
156 static long int initial_volume;
157 static int _freq, _bits, _stereo;
158 static int alleg_to_dsound_volume[256];
159 static int alleg_to_dsound_pan[256];
160 
161 
162 /* internal driver representation of a voice */
163 struct DIRECTSOUND_VOICE {
164    int bits;
165    int bytes_per_sample;
166    int freq, pan, vol;
167    int stereo;
168    int reversed;
169    int bidir;
170    int len;
171    unsigned char *data;
172    int loop_offset;
173    int loop_len;
174    int looping;
175    void *lock_buf_a;
176    long lock_size_a;
177    LPDIRECTSOUNDBUFFER ds_buffer;
178    LPDIRECTSOUNDBUFFER ds_loop_buffer;
179    LPDIRECTSOUNDBUFFER ds_locked_buffer;
180 };
181 
182 static struct DIRECTSOUND_VOICE *ds_voices;
183 
184 
185 /* dynamically generated driver list */
186 static _DRIVER_INFO *driver_list = NULL;
187 
188 #define MAX_DRIVERS  16
189 static int num_drivers = 0;
190 static char *driver_names[MAX_DRIVERS];
191 static LPGUID driver_guids[MAX_DRIVERS];
192 
193 
194 
195 /* DSEnumCallback:
196  *  Callback function for enumerating the available DirectSound drivers.
197  */
DSEnumCallback(LPGUID lpGuid,LPCSTR lpcstrDescription,LPCSTR lpcstrModule,LPVOID lpContext)198 static BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
199 {
200    if (lpGuid) {
201       driver_guids[num_drivers] = lpGuid;
202       driver_names[num_drivers] = _AL_MALLOC_ATOMIC(strlen(lpcstrDescription)+1);
203       if(driver_names[num_drivers])
204       {
205          _al_sane_strncpy(driver_names[num_drivers], lpcstrDescription, strlen(lpcstrDescription)+1);
206          num_drivers++;
207       }
208    }
209 
210    return (num_drivers < MAX_DRIVERS);
211 }
212 
213 
214 
215 /* _get_win_digi_driver_list:
216  *  System driver hook for listing the available sound drivers. This
217  *  generates the device list at runtime, to match whatever DirectSound
218  *  devices are available.
219  */
_get_win_digi_driver_list(void)220 _DRIVER_INFO *_get_win_digi_driver_list(void)
221 {
222    DIGI_DRIVER *driver;
223    HRESULT hr;
224    int i;
225 
226    if (!driver_list) {
227       driver_list = _create_driver_list();
228 
229       /* enumerate the DirectSound drivers */
230       hr = DirectSoundEnumerate(DSEnumCallback, NULL);
231 
232       if (hr == DS_OK) {
233          /* Allegro mixer to DirectSound drivers */
234          for (i=0; i<num_drivers; i++) {
235             driver = _get_dsalmix_driver(driver_names[i], driver_guids[i], i);
236 
237             _driver_list_append_driver(&driver_list, driver->id, driver, TRUE);
238          }
239 
240          /* pure DirectSound drivers */
241          for (i=0; i<num_drivers; i++) {
242             driver = _AL_MALLOC(sizeof(DIGI_DRIVER));
243             memcpy(driver, &digi_directsound, sizeof(DIGI_DRIVER));
244             driver->id = DIGI_DIRECTX(i);
245             driver->ascii_name = driver_names[i];
246 
247             _driver_list_append_driver(&driver_list, driver->id, driver, TRUE);
248          }
249       }
250 
251       /* Allegro mixer to WaveOut drivers */
252       for (i=0; i<2; i++) {
253          driver = _get_woalmix_driver(i);
254 
255          _driver_list_append_driver(&driver_list, driver->id, driver, TRUE);
256       }
257    }
258 
259    return driver_list;
260 }
261 
262 
263 
264 /* _free_win_digi_driver_list:
265  *  Helper function for freeing the dynamically generated driver list.
266  */
_free_win_digi_driver_list(void)267 void _free_win_digi_driver_list(void)
268 {
269    int i = 0;
270 
271    if (driver_list) {
272       while (driver_list[i].driver) {
273          _AL_FREE(driver_list[i].driver);
274          i++;
275       }
276 
277       _destroy_driver_list(driver_list);
278       driver_list = NULL;
279    }
280 
281    for (i = 0; i < num_drivers; i++) {
282       _AL_FREE(driver_names[i]);
283    }
284 
285    _free_win_dsalmix_name_list();
286 }
287 
288 
289 
290 /* ds_err:
291  *  Returns a DirectSound error string.
292  */
293 #ifdef DEBUGMODE
ds_err(long err)294 static char *ds_err(long err)
295 {
296    static char err_str[64];
297 
298    switch (err) {
299 
300       case DS_OK:
301          _al_sane_strncpy(err_str, "DS_OK", sizeof(err_str));
302          break;
303 
304       case DSERR_ALLOCATED:
305          _al_sane_strncpy(err_str, "DSERR_ALLOCATED", sizeof(err_str));
306          break;
307 
308       case DSERR_BADFORMAT:
309          _al_sane_strncpy(err_str, "DSERR_BADFORMAT", sizeof(err_str));
310          break;
311 
312       case DSERR_INVALIDPARAM:
313          _al_sane_strncpy(err_str, "DSERR_INVALIDPARAM", sizeof(err_str));
314          break;
315 
316       case DSERR_NOAGGREGATION:
317          _al_sane_strncpy(err_str, "DSERR_NOAGGREGATION", sizeof(err_str));
318          break;
319 
320       case DSERR_OUTOFMEMORY:
321          _al_sane_strncpy(err_str, "DSERR_OUTOFMEMORY", sizeof(err_str));
322          break;
323 
324       case DSERR_UNINITIALIZED:
325          _al_sane_strncpy(err_str, "DSERR_UNINITIALIZED", sizeof(err_str));
326          break;
327 
328       case DSERR_UNSUPPORTED:
329          _al_sane_strncpy(err_str, "DSERR_UNSUPPORTED", sizeof(err_str));
330          break;
331 
332       default:
333          _al_sane_strncpy(err_str, "DSERR_UNKNOWN", sizeof(err_str));
334          break;
335    }
336 
337    return err_str;
338 }
339 #else
340 #define ds_err(hr) "\0"
341 #endif
342 
343 
344 
345 /* digi_directsound_detect:
346  */
digi_directsound_detect(int input)347 static int digi_directsound_detect(int input)
348 {
349    HRESULT hr;
350    int id;
351 
352    /* deduce our device number from the driver ID code */
353    id = ((digi_driver->id >> 8) & 0xFF) - 'A';
354 
355    if (input)
356       return digi_directsound_capture_detect(driver_guids[id]);
357 
358    if (!directsound) {
359       /* initialize DirectSound interface */
360       hr = DirectSoundCreate(driver_guids[id], &directsound, NULL);
361       if (FAILED(hr)) {
362          _TRACE(PREFIX_E "DirectSound interface creation failed during detect (%s).\n", ds_err(hr));
363          return 0;
364       }
365 
366       _TRACE(PREFIX_I "DirectSound interface successfully created.\n");
367 
368       /* release DirectSound interface */
369       IDirectSound_Release(directsound);
370       directsound = NULL;
371    }
372 
373    return 1;
374 }
375 
376 
377 
378 /* digi_directsound_init:
379  */
digi_directsound_init(int input,int voices)380 static int digi_directsound_init(int input, int voices)
381 {
382    HRESULT hr;
383    DSCAPS dscaps;
384    DSBUFFERDESC desc;
385    WAVEFORMATEX format;
386    int v, id;
387    HWND allegro_wnd = win_get_window();
388 
389    /* deduce our device number from the driver ID code */
390    id = ((digi_driver->id >> 8) & 0xFF) - 'A';
391 
392    if (input)
393       return digi_directsound_capture_init(driver_guids[id]);
394 
395    digi_driver->voices = voices;
396 
397    /* initialize DirectSound interface */
398    hr = DirectSoundCreate(driver_guids[id], &directsound, NULL);
399    if (FAILED(hr)) {
400       _TRACE(PREFIX_E "Can't create DirectSound interface (%s).\n", ds_err(hr));
401       goto Error;
402    }
403 
404    /* set cooperative level */
405    hr = IDirectSound_SetCooperativeLevel(directsound, allegro_wnd, DSSCL_PRIORITY);
406    if (FAILED(hr))
407       _TRACE(PREFIX_W "Can't set DirectSound cooperative level (%s).\n", ds_err(hr));
408    else
409       _TRACE(PREFIX_I "DirectSound cooperation level set to PRIORITY.\n");
410 
411    /* get hardware capabilities */
412    dscaps.dwSize = sizeof(DSCAPS);
413    hr = IDirectSound_GetCaps(directsound, &dscaps);
414    if (FAILED(hr)) {
415       _TRACE(PREFIX_E "Can't get DirectSound caps (%s).\n", ds_err(hr));
416       goto Error;
417    }
418 
419    /* For some reason the audio driver on my machine doesn't seem to set either
420     * PRIMARY16BIT or PRIMARY8BIT; of course it actually does support 16-bit
421     * sound.
422     */
423    if (((dscaps.dwFlags & DSCAPS_PRIMARY16BIT) || !(dscaps.dwFlags & DSCAPS_PRIMARY8BIT))
424    &&  ((_sound_bits >= 16) || (_sound_bits <= 0)))
425       _bits = 16;
426    else
427       _bits = 8;
428 
429    if ((dscaps.dwFlags & DSCAPS_PRIMARYSTEREO) && _sound_stereo)
430       _stereo = 1;
431    else
432       _stereo = 0;
433 
434    if ((dscaps.dwMaxSecondarySampleRate > (DWORD)_sound_freq) && (_sound_freq > 0))
435       _freq = _sound_freq;
436    else
437       _freq = dscaps.dwMaxSecondarySampleRate;
438 
439    _TRACE(PREFIX_I "DirectSound caps: %u bits, %s, %uHz\n", _bits, _stereo ? "stereo" : "mono", _freq);
440 
441    /* create primary buffer */
442    memset(&desc, 0, sizeof(DSBUFFERDESC));
443    desc.dwSize = sizeof(DSBUFFERDESC);
444    desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
445 
446    hr = IDirectSound_CreateSoundBuffer(directsound, &desc, &prim_buf, NULL);
447    if (hr != DS_OK) {
448       _TRACE(PREFIX_W "Can't create primary buffer (%s)\nGlobal volume control won't be available.\n", ds_err(hr));
449    }
450 
451    /* get current format */
452    if (prim_buf) {
453       hr = IDirectSoundBuffer_GetFormat(prim_buf, &format, sizeof(format), NULL);
454       if (FAILED(hr)) {
455          _TRACE(PREFIX_W "Can't get primary buffer format (%s).\n", ds_err(hr));
456       }
457       else {
458          format.nChannels = _stereo ? 2 : 1;
459          format.nSamplesPerSec = _freq;
460          format.wBitsPerSample = _bits;
461          format.nBlockAlign = (format.wBitsPerSample * format.nChannels) >> 3;
462          format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
463 
464          hr = IDirectSoundBuffer_SetFormat(prim_buf, &format);
465          if (FAILED(hr)) {
466             _TRACE(PREFIX_W "Can't set primary buffer format (%s).\n", ds_err(hr));
467          }
468 
469          /* output primary format */
470          hr = IDirectSoundBuffer_GetFormat(prim_buf, &format, sizeof(format), NULL);
471          if (FAILED(hr)) {
472             _TRACE(PREFIX_W "Can't get primary buffer format (%s).\n", ds_err(hr));
473          }
474          else {
475             _TRACE(PREFIX_I "primary format:\n");
476             _TRACE(PREFIX_I "  %u channels\n  %u Hz\n  %u AvgBytesPerSec\n  %u BlockAlign\n  %u bits\n  %u size\n",
477             format.nChannels, format.nSamplesPerSec, format.nAvgBytesPerSec,
478             format.nBlockAlign, format.wBitsPerSample, format.cbSize);
479          }
480       }
481 
482       /* get primary buffer (global) volume */
483       IDirectSoundBuffer_GetVolume(prim_buf, &initial_volume);
484    }
485 
486    /* initialize physical voices */
487    ds_voices = (struct DIRECTSOUND_VOICE *)_AL_MALLOC(sizeof(struct DIRECTSOUND_VOICE) * voices);
488    for (v = 0; v < digi_driver->voices; v++) {
489       ds_voices[v].ds_buffer = NULL;
490       ds_voices[v].ds_loop_buffer = NULL;
491       ds_voices[v].ds_locked_buffer = NULL;
492    }
493 
494    /* setup volume lookup table */
495    alleg_to_dsound_volume[0] = DSBVOLUME_MIN;
496    for (v = 1; v < 256; v++)
497       alleg_to_dsound_volume[v] = MAX(DSBVOLUME_MIN, DSBVOLUME_MAX + 2000.0*log10(v/255.0));
498 
499    /* setup pan lookup table */
500    alleg_to_dsound_pan[0] = DSBPAN_LEFT;
501    for (v = 1; v < 128; v++)
502       alleg_to_dsound_pan[v] = MAX(DSBPAN_LEFT, DSBPAN_CENTER + 2000.0*log10(v/127.0));
503 
504    alleg_to_dsound_pan[255] = DSBPAN_RIGHT;
505    for (v = 128; v < 255; v++)
506       alleg_to_dsound_pan[v] = MIN(DSBPAN_RIGHT, DSBPAN_CENTER - 2000.0*log10((255.0-v)/127.0));
507 
508    return 0;
509 
510  Error:
511    _TRACE(PREFIX_E "digi_directsound_init() failed.\n");
512    digi_directsound_exit(FALSE);
513    return -1;
514 }
515 
516 
517 
518 /* digi_directsound_exit:
519  */
digi_directsound_exit(int input)520 static void digi_directsound_exit(int input)
521 {
522    int v;
523 
524    if (input) {
525       digi_directsound_capture_exit();
526       return;
527    }
528 
529    /* destroy physical voices */
530    for (v = 0; v < digi_driver->voices; v++)
531       digi_directsound_release_voice(v);
532 
533    _AL_FREE(ds_voices);
534    ds_voices = NULL;
535 
536    /* destroy primary buffer */
537    if (prim_buf) {
538       /* restore primary buffer initial volume */
539       IDirectSoundBuffer_SetVolume(prim_buf, initial_volume);
540 
541       IDirectSoundBuffer_Release(prim_buf);
542       prim_buf = NULL;
543    }
544 
545    /* shutdown DirectSound */
546    if (directsound) {
547       IDirectSound_Release(directsound);
548       directsound = NULL;
549    }
550 }
551 
552 
553 
554 /* digi_directsound_set_mixer_volume:
555  */
digi_directsound_set_mixer_volume(int volume)556 static int digi_directsound_set_mixer_volume(int volume)
557 {
558    int ds_vol;
559 
560    if (prim_buf) {
561       ds_vol = alleg_to_dsound_volume[CLAMP(0, volume, 255)];
562       IDirectSoundBuffer_SetVolume(prim_buf, ds_vol);
563    }
564 
565    return 0;
566 }
567 
568 
569 
570 /* digi_directsound_get_mixer_volume:
571  */
digi_directsound_get_mixer_volume(void)572 static int digi_directsound_get_mixer_volume(void)
573 {
574    LONG vol;
575 
576    if (!prim_buf)
577       return -1;
578 
579    IDirectSoundBuffer_GetVolume(prim_buf, &vol);
580    vol = CLAMP(0, pow(10, (vol/2000.0))*255.0 - DSBVOLUME_MAX, 255);
581 
582    return vol;
583 }
584 
585 
586 
587 /********************************************************/
588 /*********** voice management functions *****************/
589 /********************************************************/
590 
591 
592 /* create_dsound_buffer:
593  *  Worker function for creating a DirectSound buffer.
594  */
create_dsound_buffer(int len,int freq,int bits,int stereo,int vol,int pan)595 static LPDIRECTSOUNDBUFFER create_dsound_buffer(int len, int freq, int bits, int stereo, int vol, int pan)
596 {
597    LPDIRECTSOUNDBUFFER snd_buf;
598    WAVEFORMATEX wf;
599    DSBUFFERDESC dsbdesc;
600    HRESULT hr;
601    int switch_mode;
602 
603    /* setup wave format structure */
604    memset(&wf, 0, sizeof(WAVEFORMATEX));
605    wf.wFormatTag = WAVE_FORMAT_PCM;
606    wf.nChannels = stereo ? 2 : 1;
607    wf.nSamplesPerSec = freq;
608    wf.wBitsPerSample = bits;
609    wf.nBlockAlign = bits * (stereo ? 2 : 1) / 8;
610    wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
611 
612    /* setup DSBUFFERDESC structure */
613    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
614    dsbdesc.dwSize = sizeof(DSBUFFERDESC);
615 
616    /* need default controls (pan, volume, frequency) */
617    dsbdesc.dwFlags = DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
618 
619    switch_mode = get_display_switch_mode();
620    if ((switch_mode == SWITCH_BACKAMNESIA) || (switch_mode == SWITCH_BACKGROUND))
621       dsbdesc.dwFlags |= DSBCAPS_GLOBALFOCUS;
622 
623    dsbdesc.dwBufferBytes = len * (bits / 8) * (stereo ? 2 : 1);
624    dsbdesc.lpwfxFormat = &wf;
625 
626    /* create buffer */
627    hr = IDirectSound_CreateSoundBuffer(directsound, &dsbdesc, &snd_buf, NULL);
628    if (FAILED(hr)) {
629       _TRACE(PREFIX_E "create_directsound_buffer() failed (%s).\n", ds_err(hr));
630       _TRACE(PREFIX_E " - %d Hz, %s, %d bits\n", freq, stereo ? "stereo" : "mono", bits);
631       return NULL;
632    }
633 
634    /* set volume */
635    IDirectSoundBuffer_SetVolume(snd_buf, alleg_to_dsound_volume[CLAMP(0, vol, 255)]);
636 
637    /* set pan */
638    IDirectSoundBuffer_SetPan(snd_buf, alleg_to_dsound_pan[CLAMP(0, pan, 255)]);
639 
640    return snd_buf;
641 }
642 
643 
644 
645 /* fill_dsound_buffer:
646  *  Worker function for copying data to a DirectSound buffer.
647  */
fill_dsound_buffer(LPDIRECTSOUNDBUFFER snd_buf,int offset,int len,int bits,int stereo,int reversed,unsigned char * data)648 static int fill_dsound_buffer(LPDIRECTSOUNDBUFFER snd_buf, int offset, int len, int bits, int stereo, int reversed, unsigned char *data)
649 {
650    void *buf_a;
651    unsigned long int size, size_a;
652    HRESULT hr;
653 
654    /* transform from samples to bytes */
655    size = len * (bits / 8) * (stereo ? 2 : 1);
656    offset = offset * (bits / 8) * (stereo ? 2 : 1);
657 
658    /* lock the buffer portion */
659    hr = IDirectSoundBuffer_Lock(snd_buf, offset, size, &buf_a, &size_a, NULL, NULL, 0);
660 
661    /* if buffer lost, restore and retry lock */
662    if (hr == DSERR_BUFFERLOST) {
663       IDirectSoundBuffer_Restore(snd_buf);
664 
665       hr = IDirectSoundBuffer_Lock(snd_buf, offset, size, &buf_a, &size_a, NULL, NULL, 0);
666       if (FAILED(hr)) {
667          _TRACE(PREFIX_E "fill_dsound_buffer() failed (%s).\n", ds_err(hr));
668          return -1;
669       }
670    }
671 
672    if (reversed) {
673       if (stereo) {
674          if (bits == 8) {
675             /* 8-bit stereo */
676             unsigned short *read_p =  (unsigned short *)data + len - 1;
677             unsigned short *write_p = (unsigned short *)buf_a;
678 
679             while (len--)
680                *write_p++ = *read_p--;
681          }
682          else if (bits == 16) {
683             /* 16-bit stereo (conversion needed) */
684             unsigned long *read_p =  (unsigned long *)data + len - 1;
685             unsigned long *write_p = (unsigned long *)buf_a;
686 
687             while (len--)
688                *write_p++ = *read_p-- ^ 0x80008000;
689          }
690       }
691       else {
692          if (bits == 8) {
693             /* 8-bit mono */
694             unsigned char *read_p =  (unsigned char *)data + len - 1;
695             unsigned char *write_p = (unsigned char *)buf_a;
696 
697             while (len--)
698                *write_p++ = *read_p--;
699          }
700          else {
701             /* 16-bit mono (conversion needed) */
702             unsigned short *read_p =  (unsigned short *)data + len - 1;
703             unsigned short *write_p = (unsigned short *)buf_a;
704 
705             while (len--)
706                *write_p++ = *read_p-- ^ 0x8000;
707          }
708       }
709    }
710    else {
711       if (bits == 8) {
712          /* 8-bit mono and stereo */
713          memcpy(buf_a, data, size);
714       }
715       else if (bits == 16) {
716          /* 16-bit mono and stereo (conversion needed) */
717          unsigned short *read_p =  (unsigned short *)data;
718          unsigned short *write_p = (unsigned short *)buf_a;
719 
720          size >>= 1;
721 
722          while (size--)
723             *write_p++ = *read_p++ ^ 0x8000;
724       }
725    }
726 
727    /* unlock the buffer */
728    IDirectSoundBuffer_Unlock(snd_buf, buf_a, size_a, NULL, 0);
729 
730    return 0;
731 }
732 
733 
734 
735 /* digi_directsound_init_voice:
736  */
digi_directsound_init_voice(int voice,AL_CONST SAMPLE * sample)737 static void digi_directsound_init_voice(int voice, AL_CONST SAMPLE *sample)
738 {
739    ds_voices[voice].bits = sample->bits;
740    ds_voices[voice].bytes_per_sample = (sample->bits/8) * (sample->stereo ? 2 : 1);
741    ds_voices[voice].freq = sample->freq;
742    ds_voices[voice].pan = 128;
743    ds_voices[voice].vol = 255;
744    ds_voices[voice].stereo = sample->stereo;
745    ds_voices[voice].reversed = FALSE;
746    ds_voices[voice].bidir = FALSE;
747    ds_voices[voice].len = sample->len;
748    ds_voices[voice].data = (unsigned char *)sample->data;
749    ds_voices[voice].loop_offset = sample->loop_start;
750    ds_voices[voice].loop_len = sample->loop_end - sample->loop_start;
751    ds_voices[voice].looping = FALSE;
752    ds_voices[voice].lock_buf_a = NULL;
753    ds_voices[voice].lock_size_a = 0;
754    ds_voices[voice].ds_locked_buffer = NULL;
755    ds_voices[voice].ds_loop_buffer = NULL;
756    ds_voices[voice].ds_buffer = create_dsound_buffer(ds_voices[voice].len,
757                                                      ds_voices[voice].freq,
758                                                      ds_voices[voice].bits,
759                                                      ds_voices[voice].stereo,
760                                                      ds_voices[voice].vol,
761                                                      ds_voices[voice].pan);
762    if (!ds_voices[voice].ds_buffer)
763       return;
764 
765    fill_dsound_buffer(ds_voices[voice].ds_buffer,
766                       0,  /* offset */
767                       ds_voices[voice].len,
768                       ds_voices[voice].bits,
769                       ds_voices[voice].stereo,
770                       ds_voices[voice].reversed,
771                       ds_voices[voice].data);
772 }
773 
774 
775 
776 /* digi_directsound_release_voice:
777  */
digi_directsound_release_voice(int voice)778 static void digi_directsound_release_voice(int voice)
779 {
780    /* just in case */
781    digi_directsound_unlock_voice(voice);
782 
783    if (ds_voices[voice].ds_buffer) {
784       IDirectSoundBuffer_Release(ds_voices[voice].ds_buffer);
785       ds_voices[voice].ds_buffer = NULL;
786    }
787 
788    if (ds_voices[voice].ds_loop_buffer) {
789       IDirectSoundBuffer_Release(ds_voices[voice].ds_loop_buffer);
790       ds_voices[voice].ds_loop_buffer = NULL;
791    }
792 }
793 
794 
795 
796 /* digi_directsound_start_voice:
797  */
digi_directsound_start_voice(int voice)798 static void digi_directsound_start_voice(int voice)
799 {
800    if (ds_voices[voice].looping && ds_voices[voice].ds_loop_buffer) {
801       IDirectSoundBuffer_Play(ds_voices[voice].ds_loop_buffer, 0, 0, DSBPLAY_LOOPING);
802    }
803    else if (ds_voices[voice].ds_buffer) {
804       IDirectSoundBuffer_Play(ds_voices[voice].ds_buffer, 0, 0,
805                               ds_voices[voice].looping ? DSBPLAY_LOOPING : 0);
806    }
807 }
808 
809 
810 
811 /* digi_directsound_stop_voice:
812  */
digi_directsound_stop_voice(int voice)813 static void digi_directsound_stop_voice(int voice)
814 {
815    if (ds_voices[voice].looping && ds_voices[voice].ds_loop_buffer) {
816       IDirectSoundBuffer_Stop(ds_voices[voice].ds_loop_buffer);
817    }
818    else if (ds_voices[voice].ds_buffer) {
819       IDirectSoundBuffer_Stop(ds_voices[voice].ds_buffer);
820    }
821 }
822 
823 
824 
825 /* update_voice_buffers:
826  *  Worker function for updating the voice buffers with the current
827  *  parameters.
828  */
update_voice_buffers(int voice,int reversed,int bidir,int loop)829 static void update_voice_buffers(int voice, int reversed, int bidir, int loop)
830 {
831    int update_main_buffer = FALSE;
832    int update_loop_buffer = FALSE;
833    int update_reversed = (ds_voices[voice].reversed != reversed);
834    int update_loop = (ds_voices[voice].looping != loop);
835    int update_bidir = (ds_voices[voice].bidir != bidir);
836 
837    /* no work to do? */
838    if (!update_reversed && !update_bidir && !update_loop)
839       return;
840 
841    /* we need to change looping or bidir? */
842    if (update_loop || update_bidir) {
843       ds_voices[voice].looping = loop;
844       ds_voices[voice].bidir = bidir;
845 
846       if (!loop && ds_voices[voice].ds_loop_buffer) {
847          /* we don't need a loop buffer anymore, destroy it */
848          if (ds_voices[voice].ds_locked_buffer == ds_voices[voice].ds_loop_buffer)
849             digi_directsound_unlock_voice(voice);
850          IDirectSoundBuffer_Release(ds_voices[voice].ds_loop_buffer);
851          ds_voices[voice].ds_loop_buffer = NULL;
852       }
853       else if (loop && !ds_voices[voice].ds_loop_buffer) {
854          /* we haven't got one, create it */
855          ds_voices[voice].ds_loop_buffer = create_dsound_buffer(ds_voices[voice].loop_len * (bidir ? 2 : 1),
856                                                                 ds_voices[voice].freq,
857                                                                 ds_voices[voice].bits,
858                                                                 ds_voices[voice].stereo,
859                                                                 ds_voices[voice].vol,
860                                                                 ds_voices[voice].pan);
861          update_loop_buffer = TRUE;
862       }
863       else if (update_bidir) {
864          /* we need to resize the loop buffer */
865          if (ds_voices[voice].ds_locked_buffer == ds_voices[voice].ds_loop_buffer)
866             digi_directsound_unlock_voice(voice);
867          IDirectSoundBuffer_Release(ds_voices[voice].ds_loop_buffer);
868          ds_voices[voice].ds_loop_buffer = create_dsound_buffer(ds_voices[voice].loop_len * (bidir ? 2 : 1),
869                                                                 ds_voices[voice].freq,
870                                                                 ds_voices[voice].bits,
871                                                                 ds_voices[voice].stereo,
872                                                                 ds_voices[voice].vol,
873                                                                 ds_voices[voice].pan);
874          update_loop_buffer = TRUE;
875       }
876    }
877 
878    /* we need to change the order? */
879    if (update_reversed) {
880       ds_voices[voice].reversed = reversed;
881 
882       update_main_buffer = TRUE;
883       update_loop_buffer = TRUE;
884    }
885 
886    /* we need to update the main buffer? */
887    if (update_main_buffer) {
888       fill_dsound_buffer(ds_voices[voice].ds_buffer,
889                          0,  /* offset */
890                          ds_voices[voice].len,
891                          ds_voices[voice].bits,
892                          ds_voices[voice].stereo,
893                          ds_voices[voice].reversed,
894                          ds_voices[voice].data);
895    }
896 
897    /* we need to update the loop buffer? */
898    if (update_loop_buffer && ds_voices[voice].ds_loop_buffer) {
899       fill_dsound_buffer(ds_voices[voice].ds_loop_buffer,
900                          0,  /* offset */
901                          ds_voices[voice].loop_len,
902                          ds_voices[voice].bits,
903                          ds_voices[voice].stereo,
904                          ds_voices[voice].reversed,
905                          ds_voices[voice].data + ds_voices[voice].loop_offset * ds_voices[voice].bytes_per_sample);
906 
907       if (ds_voices[voice].bidir)
908          fill_dsound_buffer(ds_voices[voice].ds_loop_buffer,
909                             ds_voices[voice].loop_len,
910                             ds_voices[voice].loop_len,
911                             ds_voices[voice].bits,
912                             ds_voices[voice].stereo,
913                             !ds_voices[voice].reversed,
914                             ds_voices[voice].data + ds_voices[voice].loop_offset * ds_voices[voice].bytes_per_sample);
915 
916       /* rewind the buffer */
917       IDirectSoundBuffer_SetCurrentPosition(ds_voices[voice].ds_loop_buffer, 0);
918    }
919 }
920 
921 
922 
923 /* digi_directsound_loop_voice:
924  */
digi_directsound_loop_voice(int voice,int playmode)925 static void digi_directsound_loop_voice(int voice, int playmode)
926 {
927    int reversed = (playmode & PLAYMODE_BACKWARD ? TRUE : FALSE);
928    int bidir = (playmode & PLAYMODE_BIDIR ? TRUE : FALSE);
929    int loop = (playmode & PLAYMODE_LOOP ? TRUE : FALSE);
930 
931    /* update the voice buffers */
932    update_voice_buffers(voice, reversed, bidir, loop);
933 }
934 
935 
936 
937 /* digi_directsound_lock_voice:
938  */
digi_directsound_lock_voice(int voice,int start,int end)939 static void *digi_directsound_lock_voice(int voice, int start, int end)
940 {
941    LPDIRECTSOUNDBUFFER ds_locked_buffer;
942    unsigned long size_a;
943    void *buf_a;
944    HRESULT hr;
945 
946    /* just in case */
947    digi_directsound_unlock_voice(voice);
948 
949    if (ds_voices[voice].looping && ds_voices[voice].ds_loop_buffer)
950       ds_locked_buffer = ds_voices[voice].ds_loop_buffer;
951    else
952       ds_locked_buffer = ds_voices[voice].ds_buffer;
953 
954    start = start * ds_voices[voice].bytes_per_sample;
955    end = end * ds_voices[voice].bytes_per_sample;
956 
957    hr = IDirectSoundBuffer_Lock(ds_locked_buffer, start, end - start, &buf_a, &size_a, NULL, NULL, 0);
958 
959    /* if buffer lost, restore and retry lock */
960    if (hr == DSERR_BUFFERLOST) {
961       IDirectSoundBuffer_Restore(ds_locked_buffer);
962 
963       hr = IDirectSoundBuffer_Lock(ds_locked_buffer, start, end - start, &buf_a, &size_a, NULL, NULL, 0);
964       if (FAILED(hr)) {
965          _TRACE(PREFIX_E "digi_directsound_lock_voice() failed (%s).\n", ds_err(hr));
966          return NULL;
967       }
968    }
969 
970    ds_voices[voice].ds_locked_buffer = ds_locked_buffer;
971    ds_voices[voice].lock_buf_a = buf_a;
972    ds_voices[voice].lock_size_a = size_a;
973 
974    return buf_a;
975 }
976 
977 
978 
979 /* digi_directsound_unlock_voice:
980  */
digi_directsound_unlock_voice(int voice)981 static void digi_directsound_unlock_voice(int voice)
982 {
983    HRESULT hr;
984    int lock_bytes;
985 
986    if (ds_voices[voice].ds_locked_buffer && ds_voices[voice].lock_buf_a) {
987 
988       if (ds_voices[voice].bits == 16) {
989          unsigned short *p = (unsigned short *)ds_voices[voice].lock_buf_a;
990 
991          lock_bytes = ds_voices[voice].lock_size_a >> 1;
992 
993          while (lock_bytes--)
994             *p++ ^= 0x8000;
995       }
996 
997       hr = IDirectSoundBuffer_Unlock(ds_voices[voice].ds_locked_buffer,
998                                      ds_voices[voice].lock_buf_a,
999                                      ds_voices[voice].lock_size_a,
1000                                      NULL,
1001                                      0);
1002 
1003       if (FAILED(hr)) {
1004          _TRACE(PREFIX_E "digi_directsound_unlock_voice() failed (%s).\n", ds_err(hr));
1005       }
1006 
1007       ds_voices[voice].ds_locked_buffer = NULL;
1008       ds_voices[voice].lock_buf_a = NULL;
1009       ds_voices[voice].lock_size_a = 0;
1010    }
1011 }
1012 
1013 
1014 
1015 /* digi_directsound_get_position:
1016  */
digi_directsound_get_position(int voice)1017 static int digi_directsound_get_position(int voice)
1018 {
1019    HRESULT hr;
1020    unsigned long int play_cursor;
1021    unsigned long int write_cursor;
1022    unsigned long int status;
1023    int pos;
1024 
1025    if (ds_voices[voice].looping && ds_voices[voice].ds_loop_buffer) {
1026       /* is buffer playing? */
1027       hr = IDirectSoundBuffer_GetStatus(ds_voices[voice].ds_loop_buffer, &status);
1028       if (FAILED(hr) || !(status & DSBSTATUS_PLAYING))
1029          return -1;
1030 
1031       hr = IDirectSoundBuffer_GetCurrentPosition(ds_voices[voice].ds_loop_buffer,
1032                                                  &play_cursor, &write_cursor);
1033       if (FAILED(hr)) {
1034          _TRACE(PREFIX_E "digi_directsound_get_position() failed (%s).\n", ds_err(hr));
1035          return -1;
1036       }
1037 
1038       pos = play_cursor / ds_voices[voice].bytes_per_sample;
1039 
1040       /* handle bidir data */
1041       if (ds_voices[voice].bidir && (pos >= ds_voices[voice].loop_len))
1042          pos = (ds_voices[voice].loop_len - 1) - (pos - ds_voices[voice].loop_len);
1043 
1044       /* handle reversed data */
1045       if (ds_voices[voice].reversed)
1046          pos = ds_voices[voice].loop_len - 1 - pos;
1047 
1048       return ds_voices[voice].loop_offset + pos;
1049    }
1050    else if (ds_voices[voice].ds_buffer) {
1051       /* is buffer playing? */
1052       hr = IDirectSoundBuffer_GetStatus(ds_voices[voice].ds_buffer, &status);
1053       if (FAILED(hr) || !(status & DSBSTATUS_PLAYING))
1054          return -1;
1055 
1056       hr = IDirectSoundBuffer_GetCurrentPosition(ds_voices[voice].ds_buffer,
1057                                                  &play_cursor, &write_cursor);
1058       if (FAILED(hr)) {
1059          _TRACE(PREFIX_E "digi_directsound_get_position() failed (%s).\n", ds_err(hr));
1060          return -1;
1061       }
1062 
1063       pos = play_cursor / ds_voices[voice].bytes_per_sample;
1064 
1065       /* handle reversed data */
1066       if (ds_voices[voice].reversed)
1067          pos = ds_voices[voice].len - 1 - pos;
1068 
1069       return pos;
1070    }
1071    else
1072       return -1;
1073 }
1074 
1075 
1076 
1077 /* digi_directsound_set_position:
1078  */
digi_directsound_set_position(int voice,int position)1079 static void digi_directsound_set_position(int voice, int position)
1080 {
1081    HRESULT hr;
1082    int pos;
1083 
1084    if (ds_voices[voice].ds_loop_buffer) {
1085       pos = CLAMP(0, position - ds_voices[voice].loop_offset, ds_voices[voice].loop_len-1);
1086 
1087       /* handle bidir data: todo? */
1088 
1089       /* handle reversed data */
1090       if (ds_voices[voice].reversed)
1091          pos = ds_voices[voice].loop_len - 1 - pos;
1092 
1093       hr = IDirectSoundBuffer_SetCurrentPosition(ds_voices[voice].ds_loop_buffer,
1094                                                  pos * ds_voices[voice].bytes_per_sample);
1095       if (FAILED(hr)) {
1096          _TRACE(PREFIX_E "digi_directsound_set_position() failed (%s).\n", ds_err(hr));
1097       }
1098    }
1099 
1100    if (ds_voices[voice].ds_buffer) {
1101       pos = position;
1102 
1103       /* handle reversed data */
1104       if (ds_voices[voice].reversed)
1105          pos = ds_voices[voice].len - 1 - pos;
1106 
1107       hr = IDirectSoundBuffer_SetCurrentPosition(ds_voices[voice].ds_buffer,
1108                                                  pos * ds_voices[voice].bytes_per_sample);
1109       if (FAILED(hr)) {
1110          _TRACE(PREFIX_E "digi_directsound_set_position() failed (%s).\n", ds_err(hr));
1111       }
1112    }
1113 }
1114 
1115 
1116 
1117 /* digi_directsound_get_volume:
1118  */
digi_directsound_get_volume(int voice)1119 static int digi_directsound_get_volume(int voice)
1120 {
1121    return ds_voices[voice].vol;
1122 }
1123 
1124 
1125 
1126 /* digi_directsound_set_volume:
1127  */
digi_directsound_set_volume(int voice,int volume)1128 static void digi_directsound_set_volume(int voice, int volume)
1129 {
1130    int ds_vol;
1131 
1132    ds_voices[voice].vol = volume;
1133 
1134    if (ds_voices[voice].ds_buffer) {
1135       ds_vol = alleg_to_dsound_volume[CLAMP(0, volume, 255)];
1136       IDirectSoundBuffer_SetVolume(ds_voices[voice].ds_buffer, ds_vol);
1137 
1138       if (ds_voices[voice].ds_loop_buffer)
1139          IDirectSoundBuffer_SetVolume(ds_voices[voice].ds_loop_buffer, ds_vol);
1140    }
1141 }
1142 
1143 
1144 
1145 /* digi_directsound_get_frequency:
1146  */
digi_directsound_get_frequency(int voice)1147 static int digi_directsound_get_frequency(int voice)
1148 {
1149    return ds_voices[voice].freq;
1150 }
1151 
1152 
1153 
1154 /* digi_directsound_set_frequency:
1155  */
digi_directsound_set_frequency(int voice,int frequency)1156 static void digi_directsound_set_frequency(int voice, int frequency)
1157 {
1158    ds_voices[voice].freq = frequency;
1159 
1160    if (ds_voices[voice].ds_buffer) {
1161       IDirectSoundBuffer_SetFrequency(ds_voices[voice].ds_buffer, frequency);
1162 
1163       if (ds_voices[voice].ds_loop_buffer)
1164          IDirectSoundBuffer_SetFrequency(ds_voices[voice].ds_loop_buffer, frequency);
1165    }
1166 }
1167 
1168 
1169 
1170 /* digi_directsound_get_pan:
1171  */
digi_directsound_get_pan(int voice)1172 static int digi_directsound_get_pan(int voice)
1173 {
1174    return ds_voices[voice].pan;
1175 }
1176 
1177 
1178 
1179 /* digi_directsound_set_pan:
1180  */
digi_directsound_set_pan(int voice,int pan)1181 static void digi_directsound_set_pan(int voice, int pan)
1182 {
1183    int ds_pan;
1184 
1185    ds_voices[voice].pan = pan;
1186 
1187    if (ds_voices[voice].ds_buffer) {
1188       ds_pan = alleg_to_dsound_pan[CLAMP(0, pan, 255)];
1189       IDirectSoundBuffer_SetPan(ds_voices[voice].ds_buffer, ds_pan);
1190 
1191       if (ds_voices[voice].ds_loop_buffer)
1192          IDirectSoundBuffer_SetPan(ds_voices[voice].ds_loop_buffer, ds_pan);
1193    }
1194 }
1195 
1196