1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      DirectSound input driver.
12  *
13  *      By Nick Kochakian.
14  *
15  *      API compliance improvements, enhanced format detection
16  *      and bugfixes by Javier Gonzalez.
17  *
18  *      See readme.txt for copyright information.
19  */
20 
21 
22 #define DIRECTSOUND_VERSION 0x0500
23 
24 #include "allegro.h"
25 #include "allegro/internal/aintern.h"
26 #include "allegro/platform/aintwin.h"
27 
28 #ifndef SCAN_DEPEND
29    #ifdef ALLEGRO_MINGW32
30       #undef MAKEFOURCC
31    #endif
32 
33    #include <mmsystem.h>
34    #include <dsound.h>
35    #include <math.h>
36 
37    #ifdef ALLEGRO_MSVC
38       #include <mmreg.h>
39    #endif
40 #endif
41 
42 #ifndef ALLEGRO_WINDOWS
43 #error something is wrong with the makefile
44 #endif
45 
46 #define PREFIX_I                "al-dsinput INFO: "
47 #define PREFIX_W                "al-dsinput WARNING: "
48 #define PREFIX_E                "al-dsinput ERROR: "
49 
50 
51 /* sound driver globals */
52 static LPDIRECTSOUNDCAPTURE ds_capture = NULL;
53 static LPDIRECTSOUNDCAPTUREBUFFER ds_capture_buf = NULL;
54 static WAVEFORMATEX dsc_buf_wfx;
55 static unsigned long int ds_capture_buffer_size;
56 static unsigned long int last_capture_pos, input_wave_bytes_read;
57 static unsigned char *input_wave_data = NULL;
58 
59 
60 
61 /* ds_err:
62  *  Returns a DirectSound error string.
63  */
64 #ifdef DEBUGMODE
ds_err(long err)65 static char *ds_err(long err)
66 {
67    static char err_str[64];
68 
69    switch (err) {
70 
71       case DS_OK:
72          _al_sane_strncpy(err_str, "DS_OK", sizeof(err_str));
73          break;
74 
75       case DSERR_ALLOCATED:
76          _al_sane_strncpy(err_str, "DSERR_ALLOCATED", sizeof(err_str));
77          break;
78 
79       case DSERR_BADFORMAT:
80          _al_sane_strncpy(err_str, "DSERR_BADFORMAT", sizeof(err_str));
81          break;
82 
83       case DSERR_INVALIDPARAM:
84          _al_sane_strncpy(err_str, "DSERR_INVALIDPARAM", sizeof(err_str));
85          break;
86 
87       case DSERR_NOAGGREGATION:
88          _al_sane_strncpy(err_str, "DSERR_NOAGGREGATION", sizeof(err_str));
89          break;
90 
91       case DSERR_OUTOFMEMORY:
92          _al_sane_strncpy(err_str, "DSERR_OUTOFMEMORY", sizeof(err_str));
93          break;
94 
95       case DSERR_UNINITIALIZED:
96          _al_sane_strncpy(err_str, "DSERR_UNINITIALIZED", sizeof(err_str));
97          break;
98 
99       case DSERR_UNSUPPORTED:
100          _al_sane_strncpy(err_str, "DSERR_UNSUPPORTED", sizeof(err_str));
101          break;
102 
103       default:
104          _al_sane_strncpy(err_str, "DSERR_UNKNOWN", sizeof(err_str));
105          break;
106    }
107 
108    return err_str;
109 }
110 #else
111 #define ds_err(hr) "\0"
112 #endif
113 
114 
115 
116 /* create_test_capture_buffer:
117  *  Helper function that tries to create a capture buffer with
118  *  the specified format and deletes it immediatly.
119  */
create_test_capture_buffer(WAVEFORMATEX * wfx)120 static int create_test_capture_buffer(WAVEFORMATEX *wfx)
121 {
122    LPDIRECTSOUNDCAPTUREBUFFER dsc_trybuf;
123    DSCBUFFERDESC dsc_trybuf_desc;
124    HRESULT hr;
125 
126    /* create the capture buffer */
127    ZeroMemory(&dsc_trybuf_desc, sizeof(DSCBUFFERDESC));
128    dsc_trybuf_desc.dwSize  = sizeof(DSCBUFFERDESC);
129    dsc_trybuf_desc.dwFlags = 0;
130    dsc_trybuf_desc.dwBufferBytes = 1024;
131    dsc_trybuf_desc.dwReserved  = 0;
132    dsc_trybuf_desc.lpwfxFormat = wfx;
133 
134    hr = IDirectSoundCapture_CreateCaptureBuffer(ds_capture, &dsc_trybuf_desc, &dsc_trybuf, NULL);
135 
136    if (FAILED(hr))
137       return -1;
138 
139    IDirectSoundCaptureBuffer_Release(dsc_trybuf);
140    return 0;
141 }
142 
143 
144 
145 /* get_capture_format_support:
146  *  Helper function to see if the specified input device
147  *  can support a combination of capture settings.
148  */
get_capture_format_support(int bits,int stereo,int rate,int autodetect,WAVEFORMATEX * wfx)149 static int get_capture_format_support(int bits, int stereo, int rate,
150                                       int autodetect, WAVEFORMATEX *wfx)
151 {
152    int i;
153    DSCCAPS dsCaps;
154    HRESULT hr;
155    WAVEFORMATEX *test_wfx;
156 
157    struct {
158       unsigned long int type;
159       unsigned long int freq;
160       unsigned char bits;
161       unsigned char channels;
162       BOOL stereo;
163    } ds_formats[] = {
164       { WAVE_FORMAT_4S16,   44100, 16, 2, TRUE  },
165       { WAVE_FORMAT_2S16,   22050, 16, 2, TRUE  },
166       { WAVE_FORMAT_1S16,   11025, 16, 2, TRUE  },
167 
168       { WAVE_FORMAT_4M16,   44100, 16, 1, FALSE },
169       { WAVE_FORMAT_2M16,   22050, 16, 1, FALSE },
170       { WAVE_FORMAT_1M16,   11025, 16, 1, FALSE },
171 
172       { WAVE_FORMAT_4S08,   44100, 8,  2, TRUE  },
173       { WAVE_FORMAT_2S08,   22050, 8,  2, TRUE  },
174       { WAVE_FORMAT_1S08,   11025, 8,  2, TRUE  },
175 
176       { WAVE_FORMAT_4M08,   44100, 8,  1, FALSE },
177       { WAVE_FORMAT_2M08,   22050, 8,  1, FALSE },
178       { WAVE_FORMAT_1M08,   11025, 8,  1, FALSE },
179 
180       { WAVE_INVALIDFORMAT,     0, 0,  0, FALSE }
181    };
182 
183    if (!ds_capture)
184      return -1;
185 
186    /* if we have already a capture buffer working
187     * we return its format as the only valid one
188     */
189    if (ds_capture_buf) {
190       if (!autodetect) {
191          /* we must check that everything that cares is
192           * the same as in the capture buffer settings
193           */
194          if (((bits > 0) && (dsc_buf_wfx.wBitsPerSample != bits)) ||
195              ( stereo && (dsc_buf_wfx.nChannels != 2)) ||
196              (!stereo && (dsc_buf_wfx.nChannels != 1)) ||
197              ((rate > 0) && (dsc_buf_wfx.nSamplesPerSec != (unsigned int) rate)))
198             return -1;
199       }
200 
201       /* return the actual capture buffer settings */
202       if (wfx)
203          memcpy(wfx, &dsc_buf_wfx, sizeof(WAVEFORMATEX));
204 
205       return 0;
206    }
207 
208    /* we use a two-level checking process:
209     *  - the normal check of exposed capabilities,
210     *  - the actual creation of the capture buffer,
211     * because of the single frequency limitation on some
212     * sound cards (e.g SB16 ISA) in full duplex mode.
213     */
214    dsCaps.dwSize = sizeof(DSCCAPS);
215    hr = IDirectSoundCapture_GetCaps(ds_capture, &dsCaps);
216    if (FAILED(hr)) {
217       _TRACE(PREFIX_E "Can't get input device caps (%s).\n", ds_err(hr));
218       return -1;
219    }
220 
221    if (wfx)
222       test_wfx = wfx;
223    else
224       test_wfx = _AL_MALLOC(sizeof(WAVEFORMATEX));
225 
226    for (i=0; ds_formats[i].type != WAVE_INVALIDFORMAT; i++)
227       /* if the current format is supported */
228       if (dsCaps.dwFormats & ds_formats[i].type) {
229          if (!autodetect) {
230             /* we must check that everything that cares is
231              * the same as in the capture buffer settings
232              */
233             if (((bits > 0) && (ds_formats[i].bits != bits)) ||
234                 (stereo && !ds_formats[i].stereo) ||
235                 (!stereo && ds_formats[i].stereo) ||
236                 ((rate > 0) && (ds_formats[i].freq != (unsigned int) rate)))
237                continue;  /* go to next format */
238          }
239 
240          test_wfx->wFormatTag = WAVE_FORMAT_PCM;
241          test_wfx->nChannels = ds_formats[i].channels;
242          test_wfx->nSamplesPerSec = ds_formats[i].freq;
243          test_wfx->wBitsPerSample = ds_formats[i].bits;
244          test_wfx->nBlockAlign = test_wfx->nChannels * (test_wfx->wBitsPerSample / 8);
245          test_wfx->nAvgBytesPerSec = test_wfx->nSamplesPerSec * test_wfx->nBlockAlign;
246          test_wfx->cbSize = 0;
247 
248          if (create_test_capture_buffer(test_wfx) == 0) {
249             if (!wfx)
250                _AL_FREE(test_wfx);
251             return 0;
252          }
253       }
254 
255    if (!wfx)
256       _AL_FREE(test_wfx);
257 
258    _TRACE(PREFIX_W "No valid recording formats found.\n");
259    return -1;
260 }
261 
262 
263 
264 /* digi_directsound_capture_init:
265  */
digi_directsound_capture_init(LPGUID guid)266 int digi_directsound_capture_init(LPGUID guid)
267 {
268    DSCCAPS dsCaps;
269    WAVEFORMATEX wfx;
270    HRESULT hr;
271    LPVOID temp;
272 
273    /* the DirectSoundCapture interface is not part of DirectX 3 */
274    if (_dx_ver < 0x0500)
275       return -1;
276 
277    /* create the device:
278     *  we use CoCreateInstance() instead of DirectSoundCaptureCreate() to avoid
279     *  the dll loader blocking the start of Allegro under DirectX 3.
280     */
281 
282    hr = CoCreateInstance(&CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
283                          &IID_IDirectSoundCapture, &temp);
284    if (FAILED(hr)) {
285       _TRACE(PREFIX_E "Can't create DirectSoundCapture interface (%s).\n", ds_err(hr));
286       goto Error;
287    }
288 
289    ds_capture = temp;
290 
291    /* initialize the device */
292    hr = IDirectSoundCapture_Initialize(ds_capture, guid);
293 
294    if (FAILED(hr)) {
295       hr = IDirectSoundCapture_Initialize(ds_capture, NULL);
296       if (FAILED(hr)) {
297          _TRACE(PREFIX_E "Can't initialize DirectSoundCapture interface (%s).\n", ds_err(hr));
298          goto Error;
299       }
300    }
301 
302    /* get the device caps */
303    dsCaps.dwSize = sizeof(DSCCAPS);
304    hr = IDirectSoundCapture_GetCaps(ds_capture, &dsCaps);
305    if (FAILED(hr)) {
306       _TRACE(PREFIX_E "Can't get input device caps (%s).\n", ds_err(hr));
307       goto Error;
308    }
309 
310    /* cool little 'autodetection' process :) */
311    if (get_capture_format_support(0, FALSE, 0, TRUE, &wfx) != 0) {
312       _TRACE(PREFIX_E "The DirectSound hardware doesn't support any capture types.\n");
313       goto Error;
314    }
315 
316    /* set capabilities */
317    digi_input_driver->rec_cap_bits = wfx.wBitsPerSample;
318    digi_input_driver->rec_cap_stereo = (wfx.nChannels == 2) ? 1 : 0;
319 
320    return 0;
321 
322  Error:
323    /* shutdown DirectSoundCapture */
324    digi_directsound_capture_exit();
325 
326    return -1;
327 }
328 
329 
330 
331 /* digi_directsound_capture_exit:
332  */
digi_directsound_capture_exit(void)333 void digi_directsound_capture_exit(void)
334 {
335    /* destroy capture buffer */
336    digi_directsound_rec_stop();
337 
338    /* shutdown DirectSoundCapture */
339    if (ds_capture) {
340       IDirectSoundCapture_Release(ds_capture);
341       ds_capture = NULL;
342    }
343 }
344 
345 
346 
347 /* digi_directsound_capture_detect:
348  */
digi_directsound_capture_detect(LPGUID guid)349 int digi_directsound_capture_detect(LPGUID guid)
350 {
351    HRESULT hr;
352    LPVOID temp;
353 
354    /* the DirectSoundCapture interface is not part of DirectX 3 */
355    if (_dx_ver < 0x500)
356       return 0;
357 
358    if (!ds_capture) {
359       /* create the device:
360        *  we use CoCreateInstance() instead of DirectSoundCaptureCreate() to avoid
361        *  the dll loader blocking the start of Allegro under DirectX 3.
362        */
363       hr = CoCreateInstance(&CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
364                             &IID_IDirectSoundCapture, &temp);
365 
366       if (FAILED(hr)) {
367          _TRACE(PREFIX_E "DirectSoundCapture interface creation failed during detect (%s).\n", ds_err(hr));
368          return 0;
369       }
370 
371       ds_capture = temp;
372 
373       /* initialize the device */
374       hr = IDirectSoundCapture_Initialize(ds_capture, guid);
375 
376       if (FAILED(hr)) {
377          hr = IDirectSoundCapture_Initialize(ds_capture, NULL);
378          if (FAILED(hr)) {
379             _TRACE(PREFIX_E "DirectSoundCapture interface initialization failed during detect (%s).\n", ds_err(hr));
380             return 0;
381          }
382       }
383 
384       _TRACE(PREFIX_I "DirectSoundCapture interface successfully created.\n");
385 
386       /* release DirectSoundCapture interface */
387       IDirectSoundCapture_Release(ds_capture);
388       ds_capture = NULL;
389    }
390 
391    return 1;
392 }
393 
394 
395 
396 /* digi_directsound_rec_cap_rate:
397  *  Gets the maximum input frequency for the specified parameters.
398  */
digi_directsound_rec_cap_rate(int bits,int stereo)399 int digi_directsound_rec_cap_rate(int bits, int stereo)
400 {
401    WAVEFORMATEX wfx;
402 
403    if (get_capture_format_support(bits, stereo, 0, FALSE, &wfx) != 0)
404       return 0;
405 
406    return wfx.nSamplesPerSec;
407 }
408 
409 
410 
411 /* digi_directsound_rec_cap_param:
412  *  Determines if the combination of provided parameters can be
413  *  used for recording.
414  */
digi_directsound_rec_cap_param(int rate,int bits,int stereo)415 int digi_directsound_rec_cap_param(int rate, int bits, int stereo)
416 {
417    if (get_capture_format_support(bits, stereo, rate, FALSE, NULL) == 0)
418       return 2;
419 
420    if (get_capture_format_support(bits, stereo, 44100, FALSE, NULL) == 0)
421       return -44100;
422 
423    if (get_capture_format_support(bits, stereo, 22050, FALSE, NULL) == 0)
424       return -22050;
425 
426    if (get_capture_format_support(bits, stereo, 11025, FALSE, NULL) == 0)
427       return -11025;
428 
429    return 0;
430 }
431 
432 
433 
434 /* digi_directsound_rec_source:
435  *  Sets the source for the audio recording.
436  */
digi_directsound_rec_source(int source)437 int digi_directsound_rec_source(int source)
438 {
439    /* since DirectSoundCapture doesn't allow us to
440     * select a input source manually, we return -1
441     */
442    return -1;
443 }
444 
445 
446 
447 /* digi_directsound_rec_start:
448  *  Start recording with the specified parameters.
449  */
digi_directsound_rec_start(int rate,int bits,int stereo)450 int digi_directsound_rec_start(int rate, int bits, int stereo)
451 {
452    DSCBUFFERDESC dscBufDesc;
453    HRESULT hr;
454 
455    if (!ds_capture || ds_capture_buf)
456       return 0;
457 
458    /* check if we support the desired format */
459    if ((bits <= 0) || (rate <= 0))
460       return 0;
461 
462    if (get_capture_format_support(bits, stereo, rate, FALSE, &dsc_buf_wfx) != 0)
463       return 0;
464 
465    digi_driver->rec_cap_bits = dsc_buf_wfx.wBitsPerSample;
466    digi_driver->rec_cap_stereo = (dsc_buf_wfx.nChannels == 2) ? 1 : 0;
467 
468    /* create the capture buffer */
469    ZeroMemory(&dscBufDesc, sizeof(DSCBUFFERDESC));
470    dscBufDesc.dwSize  = sizeof(DSCBUFFERDESC);
471    dscBufDesc.dwFlags = 0;
472    dscBufDesc.dwBufferBytes = dsc_buf_wfx.nAvgBytesPerSec;
473    dscBufDesc.dwReserved  = 0;
474    dscBufDesc.lpwfxFormat = &dsc_buf_wfx;
475    ds_capture_buffer_size = dscBufDesc.dwBufferBytes;
476 
477    hr = IDirectSoundCapture_CreateCaptureBuffer(ds_capture, &dscBufDesc, &ds_capture_buf, NULL);
478    if (FAILED(hr)) {
479       _TRACE(PREFIX_E "Can't create the DirectSound capture buffer (%s).\n", ds_err(hr));
480       return 0;
481    }
482 
483    hr = IDirectSoundCaptureBuffer_Start(ds_capture_buf, DSCBSTART_LOOPING);
484    if (FAILED(hr)) {
485       IDirectSoundCaptureBuffer_Release(ds_capture_buf);
486       ds_capture_buf = NULL;
487 
488       _TRACE(PREFIX_E "Can't start the DirectSound capture buffer (%s).\n", ds_err(hr));
489       return 0;
490    }
491 
492    last_capture_pos = 0;
493    input_wave_data = _AL_MALLOC(ds_capture_buffer_size);
494    input_wave_bytes_read = 0;
495 
496    return ds_capture_buffer_size;
497 }
498 
499 
500 
501 /* digi_directsound_rec_stop:
502  *  Stops recording.
503  */
digi_directsound_rec_stop(void)504 void digi_directsound_rec_stop(void)
505 {
506    if (ds_capture_buf) {
507       IDirectSoundCaptureBuffer_Stop(ds_capture_buf);
508       IDirectSoundCaptureBuffer_Release(ds_capture_buf);
509       ds_capture_buf = NULL;
510    }
511 
512    if (input_wave_data) {
513       _AL_FREE(input_wave_data);
514       input_wave_data = NULL;
515    }
516 }
517 
518 
519 
520 /* digi_directsound_rec_read:
521  *  Reads the input buffer.
522  */
digi_directsound_rec_read(void * buf)523 int digi_directsound_rec_read(void *buf)
524 {
525    unsigned char *input_ptr1, *input_ptr2, *linear_input_ptr;
526    unsigned long int input_bytes1, input_bytes2, bytes_to_lock;
527    unsigned long int capture_pos;
528    HRESULT hr;
529    BOOL buffer_filled = FALSE;
530    LPVOID temp1, temp2;
531 
532    if (!ds_capture || !ds_capture_buf || !input_wave_data)
533       return 0;
534 
535    IDirectSoundCaptureBuffer_GetCurrentPosition(ds_capture_buf, &capture_pos, NULL);
536 
537    /* check if we are not still in the same capture position */
538    if (last_capture_pos == capture_pos)
539       return 0;
540 
541    /* check how many bytes we need to lock since last check */
542    if (capture_pos > last_capture_pos) {
543       bytes_to_lock = capture_pos - last_capture_pos;
544    }
545    else {
546       bytes_to_lock = ds_capture_buffer_size - last_capture_pos;
547       bytes_to_lock += capture_pos;
548    }
549 
550    hr = IDirectSoundCaptureBuffer_Lock(ds_capture_buf, last_capture_pos,
551                                        bytes_to_lock, &temp1,
552                                        &input_bytes1, &temp2,
553                                        &input_bytes2, 0);
554    if (FAILED(hr))
555       return 0;
556 
557    input_ptr1 = temp1;
558    input_ptr2 = temp2;
559 
560    /* let's get the data aligned linearly */
561    linear_input_ptr = _AL_MALLOC(bytes_to_lock);
562    memcpy(linear_input_ptr, input_ptr1, input_bytes1);
563 
564    if (input_ptr2)
565       memcpy(linear_input_ptr + input_bytes1, input_ptr2, input_bytes2);
566 
567    IDirectSoundCaptureBuffer_Unlock(ds_capture_buf, input_ptr1,
568                                     input_bytes1, input_ptr2, input_bytes2);
569 
570    if ((input_wave_bytes_read + bytes_to_lock) >= ds_capture_buffer_size) {
571       /* we fill the buffer */
572       long int bytes_left_to_fill = ds_capture_buffer_size - input_wave_bytes_read;
573       long int bytes_to_internal = bytes_to_lock - bytes_left_to_fill;
574 
575       /* copy old buffer to user buffer */
576       memcpy((char*)buf, input_wave_data, input_wave_bytes_read);
577 
578       /* and the rest of bytes we would need to fill in the buffer */
579       memcpy((char*)buf + input_wave_bytes_read, linear_input_ptr, bytes_left_to_fill);
580 
581       /* and the rest of the data to the internal buffer */
582       input_wave_bytes_read = bytes_to_internal;
583       memcpy(input_wave_data, linear_input_ptr + bytes_left_to_fill, bytes_to_internal);
584 
585       buffer_filled = TRUE;
586 
587       /* if we are using 16-bit data, we need to convert it to unsigned format */
588       if (digi_driver->rec_cap_bits == 16) {
589          unsigned int i;
590          unsigned short *buf16 = (unsigned short *)buf;
591 
592          for (i = 0; i < ds_capture_buffer_size/2; i++)
593             buf16[i] ^= 0x8000;
594       }
595    }
596    else {
597       /* we won't fill the buffer */
598       memcpy(input_wave_data + input_wave_bytes_read, linear_input_ptr, bytes_to_lock);
599       input_wave_bytes_read += bytes_to_lock;
600    }
601 
602    _AL_FREE(linear_input_ptr);
603 
604    last_capture_pos = capture_pos;
605 
606    if (buffer_filled)
607       return 1;
608    else
609       return 0;
610 }
611