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