ref: e07962e7e8054594e3201b0c0c18c435c191af68
parent: 44f681529d002d64e6ab61d113b199313a0c1224
author: Doug Cook <idigdoug@users.sourceforge.net>
date: Tue Mar 27 21:46:57 EDT 2012
Minor tweaks to Windows WaveAudio device - Use bufsiz as byte count instead of sample count. - Exit more quickly after Ctrl+C (don't wait for audio to finish playing). - Better status and error messages. - If output device given by name, match on prefix. - Don't use 24-bit samples - use 32-bit samples and indicate that only 24 bits are valid.
--- a/src/waveaudio.c
+++ b/src/waveaudio.c
@@ -39,7 +39,7 @@
HANDLE block_finished_event;
/* Data transfer buffers. The lpData member of the first buffer points at
- * data[buf_len*sample_size*0], the second buffer's lpData points
+ * data[buf_len*sample_size*0], the second buffer's lpData points at
* data[buf_len*sample_size*1], etc. The dwUser field contains the number
* of samples of this buffer that have already been processed.
*/
@@ -58,17 +58,19 @@
*/
unsigned current;
- /* Width of a sample in bytes: 1, 2, 3, or 4. */
- unsigned sample_width;
+ /* Shift sample count by this many to get byte count:
+ * 0 for 8-bit samples, 1 for 16-bit samples, or 2 for 32-bit samples.
+ */
+ unsigned sample_shift;
/* If there has been an error, this has the Win32 error code. Otherwise, this is 0. */
- DWORD error;
+ unsigned error;
} priv_t;
-static void fail(sox_format_t* ft, DWORD code, const char* context)
+static void fail(sox_format_t* ft, unsigned code, const char* context)
{
char message[256];
- DWORD formatMessageOk = FormatMessageA(
+ unsigned formatMessageOk = FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
code,
@@ -79,7 +81,7 @@
if (formatMessageOk)
lsx_fail_errno(ft, SOX_EOF, "WaveAudio %s failed with code %d: %s", context, (int)code, message);
else
- lsx_fail_errno(ft, SOX_EOF, "WaveAudio %s failed with unrecognized MMSYSERR code %d.", context, (int)code);
+ lsx_fail_errno(ft, SOX_EOF, "WaveAudio %s failed with unrecognized MMSYSERR code: %d", context, (int)code);
}
static int stop(sox_format_t* ft)
@@ -93,15 +95,8 @@
priv->error = waveInClose(priv->hin);
}
- if (priv->hout && !priv->error)
+ if (priv->hout)
{
- while ((priv->error = waveOutClose(priv->hout)) == WAVERR_STILLPLAYING)
- {
- WaitForSingleObject(priv->block_finished_event, INFINITE);
- }
- }
- else if (priv->hout)
- {
priv->error = waveOutReset(priv->hout);
priv->error = waveOutClose(priv->hout);
}
@@ -115,7 +110,7 @@
return SOX_SUCCESS;
}
-static int check_format(
+static unsigned check_format(
WAVEFORMATEXTENSIBLE* pfmt,
int recording,
unsigned dev,
@@ -123,10 +118,14 @@
unsigned width,
unsigned hertz)
{
- static unsigned char const SubformatPcm[] = "\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71";
- const unsigned bytewidth = width > 24 ? 4 : width > 16 ? 3 : width > 8 ? 2 : 1;
- const int extend = channels > 2 || bytewidth > 2;
- DWORD error;
+ /* GUID: KSDATAFORMAT_SUBTYPE_PCM */
+ static unsigned char const SubformatPcm[] =
+ "\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71";
+
+ const unsigned bytewidth = width > 16 ? 4 : width > 8 ? 2 : 1;
+ const int extend = channels > 2 || width > 16;
+ unsigned error;
+
pfmt->Format.wFormatTag = extend ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
pfmt->Format.nChannels = channels;
pfmt->Format.nSamplesPerSec = hertz;
@@ -137,16 +136,28 @@
pfmt->Samples.wValidBitsPerSample = width;
pfmt->dwChannelMask = 0;
memcpy(&pfmt->SubFormat, SubformatPcm, 16);
+
if (recording)
error = waveInOpen(0, dev, &pfmt->Format, 0, 0, WAVE_FORMAT_QUERY);
else
error = waveOutOpen(0, dev, &pfmt->Format, 0, 0, WAVE_FORMAT_QUERY);
- return error == MMSYSERR_NOERROR;
+
+ lsx_debug(
+ "%s(QUERY: Dev %d %uHz %uCh %uPrec %uWide) returned %u",
+ recording ? "waveInOpen" : "waveOutOpen",
+ dev,
+ hertz,
+ channels,
+ bytewidth * 8,
+ width,
+ error);
+ return error;
}
-static int negotiate_format(sox_format_t* ft, WAVEFORMATEXTENSIBLE* pfmt, unsigned dev)
+static unsigned negotiate_format(sox_format_t* ft, WAVEFORMATEXTENSIBLE* pfmt, unsigned dev)
{
int recording = ft->mode == 'r';
+ unsigned error = 0;
unsigned precision = ft->encoding.bits_per_sample;
if (precision > 32)
@@ -156,18 +167,19 @@
while (precision > 0)
{
- if (check_format(pfmt, recording, dev, ft->signal.channels, precision, (unsigned)ft->signal.rate))
- return 1;
- precision = (precision - 1) & ~0x7;
+ error = check_format(pfmt, recording, dev, ft->signal.channels, precision, (unsigned)ft->signal.rate);
+ if (error == MMSYSERR_NOERROR)
+ return MMSYSERR_NOERROR;
+ precision = (precision - 1) & ~(7u);
}
- return 0;
+ return error;
}
static int start(sox_format_t* ft)
{
size_t i;
- UINT dev;
+ unsigned dev;
WAVEFORMATEXTENSIBLE fmt;
int recording = ft->mode == 'r';
priv_t *priv = (priv_t*)ft->priv;
@@ -199,24 +211,29 @@
if (priv->error)
{
- lsx_fail_errno(ft, ENODEV, "WaveAudio was unable to find the AUDIODEV %s device \"%s\".", recording ? "input" : "output", ft->filename);
+ lsx_fail_errno(ft, ENODEV, "WaveAudio device not found.");
return SOX_EOF;
}
}
else
{
- UINT dev_count = recording ? waveInGetNumDevs() : waveOutGetNumDevs();
- for (dev = (UINT)-1; dev == WAVE_MAPPER || dev < dev_count; dev++)
+ unsigned dev_count = recording ? waveInGetNumDevs() : waveOutGetNumDevs();
+ size_t name_len = strlen(ft->filename);
+ if (name_len > 31)
+ name_len = 31;
+ for (dev = WAVE_MAPPER; dev != dev_count; dev++)
{
if (recording)
{
priv->error = waveInGetDevCapsA(dev, &incaps, sizeof(incaps));
dev_name = incaps.szPname;
+ lsx_debug("Enumerating input device %2d: \"%s\"", dev, dev_name);
}
else
{
priv->error = waveOutGetDevCapsA(dev, &outcaps, sizeof(outcaps));
dev_name = outcaps.szPname;
+ lsx_debug("Enumerating output device %2d: \"%s\"", dev, dev_name);
}
if (priv->error)
@@ -225,38 +242,70 @@
return SOX_EOF;
}
- if (!strncasecmp(ft->filename, dev_name, 31))
+ if (!strncasecmp(ft->filename, dev_name, name_len))
+ {
+ lsx_report("Requested name \"%s\" matched device %d: \"%s\"", ft->filename, dev, dev_name);
break;
+ }
}
if (dev == dev_count)
{
- lsx_fail_errno(ft, ENODEV, "WaveAudio was unable to find the AUDIODEV %s device \"%s\".", recording ? "input" : "output", ft->filename);
+ lsx_fail_errno(ft, ENODEV, "The requested WaveAudio device was not found.");
return SOX_EOF;
}
}
}
- if (!negotiate_format(ft, &fmt, dev))
+ priv->error = negotiate_format(ft, &fmt, dev);
+ if (priv->error != MMSYSERR_NOERROR)
{
- lsx_fail_errno(ft, ENODEV, "WaveAudio was unable to negotiate a sample format.");
+ fail(ft, priv->error, "sample format negotiation");
return SOX_EOF;
}
- priv->sample_width = fmt.Format.wBitsPerSample / 8;
+ switch (fmt.Format.wBitsPerSample)
+ {
+ case 8:
+ priv->sample_shift = 0;
+ break;
+ case 16:
+ priv->sample_shift = 1;
+ break;
+ case 32:
+ priv->sample_shift = 2;
+ break;
+ default:
+ lsx_fail_errno(ft, E2BIG, "Unexpected value for WaveAudio wBitsPerSample: %u", fmt.Format.wBitsPerSample);
+ return SOX_EOF;
+ }
+
ft->signal.precision = fmt.Samples.wValidBitsPerSample;
ft->signal.channels = fmt.Format.nChannels;
- lsx_report(
- "WaveAudio negotiated %s device %d with %uHz %uCh %uprec %uwidth.",
- recording ? "input" : "output",
- (int)dev,
- (unsigned)fmt.Format.nSamplesPerSec,
- (unsigned)fmt.Format.nChannels,
- (unsigned)fmt.Samples.wValidBitsPerSample,
- (unsigned)fmt.Format.wBitsPerSample);
+ if (dev == WAVE_MAPPER)
+ {
+ lsx_report(
+ "Using default %s device at %uHz %uCh %uPrec %uWide.",
+ recording ? "input" : "output",
+ (unsigned)fmt.Format.nSamplesPerSec,
+ (unsigned)fmt.Format.nChannels,
+ (unsigned)fmt.Samples.wValidBitsPerSample,
+ (unsigned)fmt.Format.wBitsPerSample);
+ }
+ else
+ {
+ lsx_report(
+ "Using %s device #%d at %uHz %uCh %uPrec %uWide.",
+ recording ? "input" : "output",
+ (int)dev,
+ (unsigned)fmt.Format.nSamplesPerSec,
+ (unsigned)fmt.Format.nChannels,
+ (unsigned)fmt.Samples.wValidBitsPerSample,
+ (unsigned)fmt.Format.wBitsPerSample);
+ }
- priv->buf_len = sox_globals.bufsiz;
- priv->data = lsx_malloc(priv->buf_len * priv->sample_width * num_buffers);
+ priv->buf_len = ((sox_globals.bufsiz >> priv->sample_shift) + 31) & ~31u;
+ priv->data = lsx_malloc((priv->buf_len * num_buffers) << priv->sample_shift);
if (!priv->data)
{
lsx_fail_errno(ft, SOX_ENOMEM, "Out of memory.");
@@ -273,9 +322,25 @@
}
if (recording)
- priv->error = waveInOpen(&priv->hin, dev, &fmt.Format, (DWORD_PTR)priv->block_finished_event, 0, CALLBACK_EVENT);
+ {
+ priv->error = waveInOpen(
+ &priv->hin,
+ dev,
+ &fmt.Format,
+ (DWORD_PTR)priv->block_finished_event,
+ 0,
+ CALLBACK_EVENT);
+ }
else
- priv->error = waveOutOpen(&priv->hout, dev, &fmt.Format, (DWORD_PTR)priv->block_finished_event, 0, CALLBACK_EVENT);
+ {
+ priv->error = waveOutOpen(
+ &priv->hout,
+ dev,
+ &fmt.Format,
+ (DWORD_PTR)priv->block_finished_event,
+ 0,
+ CALLBACK_EVENT);
+ }
if (priv->error != MMSYSERR_NOERROR)
{
@@ -284,10 +349,10 @@
return SOX_EOF;
}
- for (i = 0; i < num_buffers; i++)
+ for (i = 0; i != num_buffers; i++)
{
- priv->headers[i].lpData = priv->data + priv->buf_len * priv->sample_width * i;
- priv->headers[i].dwBufferLength = priv->buf_len * priv->sample_width;
+ priv->headers[i].lpData = priv->data + ((priv->buf_len * i) << priv->sample_shift);
+ priv->headers[i].dwBufferLength = priv->buf_len << priv->sample_shift;
if (recording)
priv->error = waveInPrepareHeader(priv->hin, &priv->headers[i], sizeof(priv->headers[i]));
@@ -339,35 +404,27 @@
if (0 == (header->dwFlags & WHDR_INQUEUE) ||
0 != (header->dwFlags & WHDR_DONE))
{
- size_t length = header->dwBytesRecorded / priv->sample_width;
+ size_t length = header->dwBytesRecorded >> priv->sample_shift;
size_t ready = min(len - copied, length - header->dwUser);
size_t i;
- switch (priv->sample_width)
+ switch (priv->sample_shift)
{
- case 1:
+ case 0:
for (i = 0; i < ready; ++i)
{
buf[copied++] = SOX_UNSIGNED_8BIT_TO_SAMPLE(((uint8_t *)header->lpData)[header->dwUser++], dummy);
}
break;
- case 2:
+ case 1:
for (i = 0; i < ready; ++i)
{
buf[copied++] = SOX_SIGNED_16BIT_TO_SAMPLE(((int16_t *)header->lpData)[header->dwUser++], dummy);
}
break;
- case 3:
+ case 2:
for (i = 0; i < ready; ++i)
{
- sox_int24_t x = *(UNALIGNED sox_int24_t*)(header->lpData + header->dwUser * 3);
- buf[copied++] = SOX_SIGNED_24BIT_TO_SAMPLE(x, dummy);
- header->dwUser++;
- }
- break;
- case 4:
- for (i = 0; i < ready; ++i)
- {
buf[copied++] = SOX_SIGNED_32BIT_TO_SAMPLE(((int32_t *)header->lpData)[header->dwUser++], dummy);
}
break;
@@ -410,9 +467,9 @@
size_t ready = min(len - copied, priv->buf_len - header->dwUser);
size_t i;
- switch (priv->sample_width)
+ switch (priv->sample_shift)
{
- case 1:
+ case 0:
for (i = 0; i < ready; ++i)
{
SOX_SAMPLE_LOCALS;
@@ -419,7 +476,7 @@
((uint8_t *)header->lpData)[header->dwUser++] = SOX_SAMPLE_TO_UNSIGNED_8BIT(buf[copied++], clips);
}
break;
- case 2:
+ case 1:
for (i = 0; i < ready; ++i)
{
SOX_SAMPLE_LOCALS;
@@ -426,27 +483,15 @@
((int16_t *)header->lpData)[header->dwUser++] = SOX_SAMPLE_TO_SIGNED_16BIT(buf[copied++], clips);
}
break;
- case 3:
+ case 2:
for (i = 0; i < ready; ++i)
{
- SOX_SAMPLE_LOCALS;
- unsigned char* pdata = (unsigned char*)header->lpData + header->dwUser * 3;
- sox_int24_t x = SOX_SAMPLE_TO_SIGNED_24BIT(buf[copied++], clips);
- *pdata++ = x;
- *pdata++ = x >> 8;
- *pdata++ = x >> 16;
- header->dwUser++;
- }
- break;
- case 4:
- for (i = 0; i < ready; ++i)
- {
((int32_t *)header->lpData)[header->dwUser++] = SOX_SAMPLE_TO_SIGNED_32BIT(buf[copied++], clips);
}
break;
}
- header->dwBufferLength = header->dwUser * priv->sample_width;
+ header->dwBufferLength = header->dwUser << priv->sample_shift;
priv->error = waveOutWrite(priv->hout, header, sizeof(*header));
priv->current = (priv->current + 1) % num_buffers;
priv->headers[priv->current].dwUser = 0;
@@ -470,10 +515,10 @@
{
static const char * const names[] = {"waveaudio", NULL};
static unsigned const write_encodings[] = {
- SOX_ENCODING_SIGN2, 16, 24, 32, 8, 0,
+ SOX_ENCODING_SIGN2, 32, 24, 16, 8, 0,
0};
static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
- "Windows Multimedia Audio", names,
+ "Windows Multimedia Audio", names,
SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
start, read, stop,
start, write, stop,