1 /* libSoX device driver: MS-Windows audio (c) 2009 SoX contributors
2 *
3 * This library is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as published by
5 * the Free Software Foundation; either version 2.1 of the License, or (at
6 * your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18 #include "sox_i.h"
19
20 #include <windows.h>
21 #include <mmsystem.h>
22 #include <mmreg.h>
23
24 /* Larger means more latency (difference between the status line and the audio you hear),
25 * but it means lower chance of stuttering/glitching. 2 buffers is usually enough. Use
26 * 4 if you want to be extra-safe.
27 */
28 #define num_buffers 4
29
30 typedef struct waveaudio_priv_t
31 {
32 /* Handle to the input device (microphone, line in, etc.). NULL if playing. */
33 HWAVEIN hin;
34
35 /* Handle to the output device (speakers, line out, etc.). NULL if recording. */
36 HWAVEOUT hout;
37
38 /* Event that becomes signaled when a the system has finished processing a buffer. */
39 HANDLE block_finished_event;
40
41 /* Data transfer buffers. The lpData member of the first buffer points at
42 * data[buf_len*sample_size*0], the second buffer's lpData points at
43 * data[buf_len*sample_size*1], etc. The dwUser field contains the number
44 * of samples of this buffer that have already been processed.
45 */
46 WAVEHDR headers[num_buffers];
47
48 /* The combined data area shared by all transfer buffers.
49 * size = (bufsiz rounded up to a multiple of 32 samples) * num_buffers.
50 */
51 char * data;
52
53 /* The number of samples that can fit into one transfer buffer. */
54 size_t buf_len;
55
56 /* Index of the buffer that we're currently processing. For playback, this is the buffer
57 * that will receive the next samples. For recording, this is the buffer from which we'll
58 * be getting the next samples. If no buffers are ready for processing, this is the buffer
59 * that will be the next to become ready.
60 */
61 unsigned current;
62
63 /* Shift sample count by this many to get byte count:
64 * 0 for 8-bit samples, 1 for 16-bit samples, or 2 for 32-bit samples.
65 */
66 unsigned char sample_shift;
67 } priv_t;
68
fail(sox_format_t * ft,unsigned code,const char * context)69 static void fail(sox_format_t* ft, unsigned code, const char* context)
70 {
71 char message[256];
72 unsigned formatMessageOk = FormatMessageA(
73 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
74 NULL,
75 code,
76 0,
77 message,
78 sizeof(message) / sizeof(message[0]),
79 NULL);
80 if (formatMessageOk)
81 lsx_fail_errno(ft, SOX_EOF, "WaveAudio %s failed with code %d: %s", context, (int)code, message);
82 else
83 lsx_fail_errno(ft, SOX_EOF, "WaveAudio %s failed with unrecognized MMSYSERR code: %d", context, (int)code);
84 }
85
stop(sox_format_t * ft)86 static int stop(sox_format_t* ft)
87 {
88 priv_t *priv = (priv_t*)ft->priv;
89 if (priv == NULL) return SOX_EOF;
90
91 if (priv->hin)
92 {
93 waveInReset(priv->hin);
94 waveInClose(priv->hin);
95 }
96
97 if (priv->hout)
98 {
99 waveOutReset(priv->hout);
100 waveOutClose(priv->hout);
101 }
102
103 if (priv->block_finished_event)
104 CloseHandle(priv->block_finished_event);
105
106 if (priv->data)
107 free(priv->data);
108
109 return SOX_SUCCESS;
110 }
111
check_format(WAVEFORMATEXTENSIBLE * pfmt,int recording,unsigned dev,unsigned channels,unsigned width,unsigned hertz)112 static unsigned check_format(
113 WAVEFORMATEXTENSIBLE* pfmt,
114 int recording,
115 unsigned dev,
116 unsigned channels,
117 unsigned width,
118 unsigned hertz)
119 {
120 /* GUID: KSDATAFORMAT_SUBTYPE_PCM */
121 static unsigned char const SubformatPcm[] =
122 "\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71";
123
124 const unsigned bytewidth = width > 16 ? 4 : width > 8 ? 2 : 1;
125 const int extend = channels > 2 || width > 16;
126 unsigned error;
127
128 pfmt->Format.wFormatTag = extend ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
129 pfmt->Format.nChannels = channels;
130 pfmt->Format.nSamplesPerSec = hertz;
131 pfmt->Format.nAvgBytesPerSec = channels * bytewidth * hertz;
132 pfmt->Format.nBlockAlign = channels * bytewidth;
133 pfmt->Format.wBitsPerSample = bytewidth * 8;
134 pfmt->Format.cbSize = extend ? 22 : 0;
135 pfmt->Samples.wValidBitsPerSample = width;
136 pfmt->dwChannelMask = 0;
137 memcpy(&pfmt->SubFormat, SubformatPcm, 16);
138
139 if (recording)
140 error = waveInOpen(0, dev, &pfmt->Format, 0, 0, WAVE_FORMAT_QUERY);
141 else
142 error = waveOutOpen(0, dev, &pfmt->Format, 0, 0, WAVE_FORMAT_QUERY);
143
144 lsx_debug(
145 "%s(QUERY: Dev %d %uHz %uCh %uPrec %uWide) returned %u",
146 recording ? "waveInOpen" : "waveOutOpen",
147 dev,
148 hertz,
149 channels,
150 bytewidth * 8,
151 width,
152 error);
153 return error;
154 }
155
negotiate_format(sox_format_t * ft,WAVEFORMATEXTENSIBLE * pfmt,unsigned dev)156 static unsigned negotiate_format(sox_format_t* ft, WAVEFORMATEXTENSIBLE* pfmt, unsigned dev)
157 {
158 int recording = ft->mode == 'r';
159 unsigned error = 0;
160
161 unsigned precision = ft->encoding.bits_per_sample;
162 if (precision > 32)
163 precision = 32;
164 else if (precision < 8)
165 precision = 8;
166
167 while (precision > 0)
168 {
169 error = check_format(pfmt, recording, dev, ft->signal.channels, precision, (unsigned)ft->signal.rate);
170 if (error == MMSYSERR_NOERROR)
171 return MMSYSERR_NOERROR;
172 precision = (precision - 1) & ~(7u);
173 }
174
175 return error;
176 }
177
start(sox_format_t * ft)178 static int start(sox_format_t* ft)
179 {
180 size_t i;
181 unsigned dev;
182 WAVEFORMATEXTENSIBLE fmt;
183 int recording = ft->mode == 'r';
184 unsigned error;
185 priv_t *priv = (priv_t*)ft->priv;
186 if (priv == NULL) return SOX_EOF;
187 memset(&fmt, 0, sizeof(fmt));
188
189 /* Handle AUDIODEV device selection:
190 * NULL, blank, or "default" gets you the default device (WAVE_MAPPER = -1).
191 * An integer value gets you the device with that device number, if it exists (counting starts at 0).
192 * A string gets you the device with that name, if it exists.
193 */
194 if (ft->filename == 0 || ft->filename[0] == 0 || !strcasecmp("default", ft->filename))
195 {
196 dev = WAVE_MAPPER;
197 }
198 else
199 {
200 WAVEINCAPSA incaps;
201 WAVEOUTCAPSA outcaps;
202 const char *dev_name;
203 char* dev_num_end;
204 dev = strtoul(ft->filename, &dev_num_end, 0);
205 if (dev_num_end[0] == 0)
206 {
207 if (recording)
208 error = waveInGetDevCapsA(dev, &incaps, sizeof(incaps));
209 else
210 error = waveOutGetDevCapsA(dev, &outcaps, sizeof(outcaps));
211
212 if (error)
213 {
214 lsx_fail_errno(ft, ENODEV, "WaveAudio device not found.");
215 return SOX_EOF;
216 }
217 }
218 else
219 {
220 unsigned dev_count = recording ? waveInGetNumDevs() : waveOutGetNumDevs();
221 size_t name_len = strlen(ft->filename);
222 if (name_len > 31)
223 name_len = 31;
224 for (dev = WAVE_MAPPER; dev != dev_count; dev++)
225 {
226 if (recording)
227 {
228 error = waveInGetDevCapsA(dev, &incaps, sizeof(incaps));
229 dev_name = incaps.szPname;
230 lsx_debug("Enumerating input device %2d: \"%s\"", dev, dev_name);
231 }
232 else
233 {
234 error = waveOutGetDevCapsA(dev, &outcaps, sizeof(outcaps));
235 dev_name = outcaps.szPname;
236 lsx_debug("Enumerating output device %2d: \"%s\"", dev, dev_name);
237 }
238
239 if (error)
240 {
241 fail(ft, error, recording ? "waveInGetDevCapsA" : "waveOutGetDevCapsA");
242 return SOX_EOF;
243 }
244
245 if (!strncasecmp(ft->filename, dev_name, name_len))
246 {
247 lsx_report("Requested name \"%s\" matched device %d: \"%s\"", ft->filename, dev, dev_name);
248 break;
249 }
250 }
251
252 if (dev == dev_count)
253 {
254 lsx_fail_errno(ft, ENODEV, "The requested WaveAudio device was not found.");
255 return SOX_EOF;
256 }
257 }
258 }
259
260 error = negotiate_format(ft, &fmt, dev);
261 if (error != MMSYSERR_NOERROR)
262 {
263 fail(ft, error, "sample format negotiation");
264 return SOX_EOF;
265 }
266
267 switch (fmt.Format.wBitsPerSample)
268 {
269 case 8:
270 priv->sample_shift = 0;
271 break;
272 case 16:
273 priv->sample_shift = 1;
274 break;
275 case 32:
276 priv->sample_shift = 2;
277 break;
278 default:
279 lsx_fail_errno(ft, E2BIG, "Unexpected value for WaveAudio wBitsPerSample: %u", fmt.Format.wBitsPerSample);
280 return SOX_EOF;
281 }
282
283 ft->signal.precision = fmt.Samples.wValidBitsPerSample;
284 ft->signal.channels = fmt.Format.nChannels;
285 if (dev == WAVE_MAPPER)
286 {
287 lsx_report(
288 "Using default %s device at %uHz %uCh %uPrec %uWide.",
289 recording ? "input" : "output",
290 (unsigned)fmt.Format.nSamplesPerSec,
291 (unsigned)fmt.Format.nChannels,
292 (unsigned)fmt.Samples.wValidBitsPerSample,
293 (unsigned)fmt.Format.wBitsPerSample);
294 }
295 else
296 {
297 lsx_report(
298 "Using %s device #%d at %uHz %uCh %uPrec %uWide.",
299 recording ? "input" : "output",
300 (int)dev,
301 (unsigned)fmt.Format.nSamplesPerSec,
302 (unsigned)fmt.Format.nChannels,
303 (unsigned)fmt.Samples.wValidBitsPerSample,
304 (unsigned)fmt.Format.wBitsPerSample);
305 }
306
307 priv->buf_len = ((sox_globals.bufsiz >> priv->sample_shift) + 31) & ~31u;
308 priv->data = lsx_malloc((priv->buf_len * num_buffers) << priv->sample_shift);
309 if (!priv->data)
310 {
311 lsx_fail_errno(ft, SOX_ENOMEM, "Out of memory.");
312 return SOX_EOF;
313 }
314
315 priv->block_finished_event = CreateEventA(NULL, FALSE, FALSE, NULL);
316 if (!priv->block_finished_event)
317 {
318 error = GetLastError();
319 fail(ft, error, "CreateEventA");
320 stop(ft);
321 return SOX_EOF;
322 }
323
324 if (recording)
325 {
326 error = waveInOpen(
327 &priv->hin,
328 dev,
329 &fmt.Format,
330 (DWORD_PTR)priv->block_finished_event,
331 0,
332 CALLBACK_EVENT);
333 }
334 else
335 {
336 error = waveOutOpen(
337 &priv->hout,
338 dev,
339 &fmt.Format,
340 (DWORD_PTR)priv->block_finished_event,
341 0,
342 CALLBACK_EVENT);
343 }
344
345 if (error != MMSYSERR_NOERROR)
346 {
347 fail(ft, error, recording ? "waveInOpen" : "waveOutOpen");
348 stop(ft);
349 return SOX_EOF;
350 }
351
352 for (i = 0; i != num_buffers; i++)
353 {
354 priv->headers[i].lpData = priv->data + ((priv->buf_len * i) << priv->sample_shift);
355 priv->headers[i].dwBufferLength = priv->buf_len << priv->sample_shift;
356
357 if (recording)
358 error = waveInPrepareHeader(priv->hin, &priv->headers[i], sizeof(priv->headers[i]));
359 else
360 error = waveOutPrepareHeader(priv->hout, &priv->headers[i], sizeof(priv->headers[i]));
361
362 if (error != MMSYSERR_NOERROR)
363 {
364 fail(ft, error, recording ? "waveInPrepareHeader" : "waveOutPrepareHeader");
365 stop(ft);
366 return SOX_EOF;
367 }
368
369 if (recording)
370 {
371 error = waveInAddBuffer(priv->hin, &priv->headers[i], sizeof(priv->headers[i]));
372 if (error != MMSYSERR_NOERROR)
373 {
374 fail(ft, error, "waveInAddBuffer");
375 stop(ft);
376 return SOX_EOF;
377 }
378 }
379 }
380
381 if (recording)
382 {
383 error = waveInStart(priv->hin);
384 if (error != MMSYSERR_NOERROR)
385 {
386 fail(ft, error, "waveInStart");
387 stop(ft);
388 return SOX_EOF;
389 }
390 }
391
392 return SOX_SUCCESS;
393 }
394
waveread(sox_format_t * ft,sox_sample_t * buf,size_t len)395 static size_t waveread(sox_format_t * ft, sox_sample_t* buf, size_t len)
396 {
397 size_t copied = 0;
398 priv_t *priv = (priv_t*)ft->priv;
399 unsigned error = 0;
400
401 if (priv == NULL)
402 return (size_t)SOX_EOF;
403
404 while (!error && copied < len)
405 {
406 LPWAVEHDR header = &priv->headers[priv->current];
407 if (0 == (header->dwFlags & WHDR_INQUEUE) ||
408 0 != (header->dwFlags & WHDR_DONE))
409 {
410 size_t length = header->dwBytesRecorded >> priv->sample_shift;
411 size_t ready = min(len - copied, length - header->dwUser);
412 size_t i;
413
414 switch (priv->sample_shift)
415 {
416 case 0:
417 for (i = 0; i < ready; ++i)
418 {
419 buf[copied++] = SOX_UNSIGNED_8BIT_TO_SAMPLE(((uint8_t *)header->lpData)[header->dwUser++], dummy);
420 }
421 break;
422 case 1:
423 for (i = 0; i < ready; ++i)
424 {
425 buf[copied++] = SOX_SIGNED_16BIT_TO_SAMPLE(((int16_t *)header->lpData)[header->dwUser++], dummy);
426 }
427 break;
428 case 2:
429 for (i = 0; i < ready; ++i)
430 {
431 buf[copied++] = SOX_SIGNED_32BIT_TO_SAMPLE(((int32_t *)header->lpData)[header->dwUser++], dummy);
432 }
433 break;
434 }
435
436 if (header->dwUser == length)
437 {
438 error = waveInAddBuffer(priv->hin, header, sizeof(*header));
439 priv->current = (priv->current + 1) % num_buffers;
440 priv->headers[priv->current].dwUser = 0;
441 if (error)
442 {
443 fail(ft, error, "waveInAddBuffer");
444 copied = 0;
445 }
446 }
447 }
448 else
449 {
450 WaitForSingleObject(priv->block_finished_event, INFINITE);
451 }
452 }
453
454 return copied;
455 }
456
wavewrite(sox_format_t * ft,const sox_sample_t * buf,size_t len)457 static size_t wavewrite(sox_format_t * ft, const sox_sample_t* buf, size_t len)
458 {
459 size_t copied = 0;
460 priv_t *priv = (priv_t*)ft->priv;
461 unsigned error = 0;
462 unsigned clips = 0;
463
464 if (priv == NULL)
465 return (size_t)SOX_EOF;
466
467 while (!error && copied < len)
468 {
469 LPWAVEHDR header = &priv->headers[priv->current];
470 if (0 == (header->dwFlags & WHDR_INQUEUE) ||
471 0 != (header->dwFlags & WHDR_DONE))
472 {
473 size_t ready = min(len - copied, priv->buf_len - header->dwUser);
474 size_t i;
475
476 switch (priv->sample_shift)
477 {
478 case 0:
479 for (i = 0; i < ready; ++i)
480 {
481 SOX_SAMPLE_LOCALS;
482 ((uint8_t *)header->lpData)[header->dwUser++] = SOX_SAMPLE_TO_UNSIGNED_8BIT(buf[copied++], clips);
483 }
484 break;
485 case 1:
486 for (i = 0; i < ready; ++i)
487 {
488 SOX_SAMPLE_LOCALS;
489 ((int16_t *)header->lpData)[header->dwUser++] = SOX_SAMPLE_TO_SIGNED_16BIT(buf[copied++], clips);
490 }
491 break;
492 case 2:
493 for (i = 0; i < ready; ++i)
494 {
495 ((int32_t *)header->lpData)[header->dwUser++] = SOX_SAMPLE_TO_SIGNED_32BIT(buf[copied++], clips);
496 }
497 break;
498 }
499
500 header->dwBufferLength = header->dwUser << priv->sample_shift;
501 error = waveOutWrite(priv->hout, header, sizeof(*header));
502 priv->current = (priv->current + 1) % num_buffers;
503 priv->headers[priv->current].dwUser = 0;
504
505 if (error)
506 {
507 fail(ft, error, "waveOutWrite");
508 copied = 0;
509 }
510 }
511 else
512 {
513 WaitForSingleObject(priv->block_finished_event, INFINITE);
514 }
515 }
516
517 return copied;
518 }
519
LSX_FORMAT_HANDLER(waveaudio)520 LSX_FORMAT_HANDLER(waveaudio)
521 {
522 static const char * const names[] = {"waveaudio", NULL};
523 static unsigned const write_encodings[] = {
524 SOX_ENCODING_SIGN2, 32, 24, 16, 8, 0,
525 0};
526 static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
527 "Windows Multimedia Audio", names,
528 SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
529 start, waveread, stop,
530 start, wavewrite, stop,
531 NULL, write_encodings, NULL, sizeof(priv_t)
532 };
533 return &handler;
534 }
535