ref: 0fc1ff31d5dafcdc0c580d43c4d275c976f469a0
parent: 78bf52d2b3a063fc1f4d7f712492f33ba5480790
author: Chris Bagwell <chris@cnpbagwell.com>
date: Sun Oct 26 17:26:40 EDT 2014
Add reading support for RF64 WAV files. Based on specs from http://tech.ebu.ch/docs/tech/tech3306-2009.pdf. Patch written by Dave Lambley
--- a/ChangeLog
+++ b/ChangeLog
@@ -24,6 +24,7 @@
o Add optional support for reading Ogg Opus files. (John Stumpo)
o Fix for max size text chunks in aiff files. (cbagwell)
+ o Add reading support for RF64 WAV files. (Dave Lambley)
Effects:
--- a/src/formats.c
+++ b/src/formats.c
@@ -62,6 +62,7 @@
CHECK(hcom ,65, 4, "FSSD" , 128,4, "HCOM")
CHECK(wav , 0, 4, "RIFF" , 8, 4, "WAVE")
CHECK(wav , 0, 4, "RIFX" , 8, 4, "WAVE")
+ CHECK(wav , 0, 4, "RF64" , 8, 4, "WAVE")
CHECK(aiff , 0, 4, "FORM" , 8, 4, "AIFF")
CHECK(aifc , 0, 4, "FORM" , 8, 4, "AIFC")
CHECK(8svx , 0, 4, "FORM" , 8, 4, "8SVX")
--- a/src/wav.c
+++ b/src/wav.c
@@ -94,6 +94,8 @@
gsm_signal *gsmsample;
int gsmindex;
size_t gsmbytecount; /* counts bytes written to data block */
+ sox_bool isRF64; /* True if file being read is a RF64 */
+ uint64_t ds64_dataSize; /* Size of data chunk from ds64 header */
} priv_t;
static char *wav_format_str(unsigned wFormatTag);
@@ -370,9 +372,13 @@
/****************************************************************************/
/* General Sox WAV file code */
/****************************************************************************/
-static int findChunk(sox_format_t * ft, const char *Label, uint32_t *len)
+static int findChunk(sox_format_t * ft, const char *Label, uint64_t *len)
{
char magic[5];
+ priv_t *wav = (priv_t *) ft->priv;
+ uint32_t len_tmp;
+
+ lsx_debug("Searching for %2x %2x %2x %2x", Label[0], Label[1], Label[2], Label[3]);
for (;;)
{
if (lsx_reads(ft, magic, (size_t)4) == SOX_EOF)
@@ -382,7 +388,7 @@
return SOX_EOF;
}
lsx_debug("WAV Chunk %s", magic);
- if (lsx_readdw(ft, len) == SOX_EOF)
+ if (lsx_readdw(ft, &len_tmp) == SOX_EOF)
{
lsx_fail_errno(ft, SOX_EHDR, "WAVE file %s chunk is too short",
magic);
@@ -389,17 +395,34 @@
return SOX_EOF;
}
- if (strncmp(Label, magic, (size_t)4) == 0)
+ if (len_tmp == 0xffffffff && wav->isRF64==sox_true)
+ {
+ /* Chunk length should come from ds64 header */
+ if (memcmp(magic, "data", (size_t)4)==0)
+ {
+ *len = wav->ds64_dataSize;
+ }
+ else
+ {
+ lsx_fail_errno(ft, SOX_EHDR, "Cannot yet read block sizes of arbitary RF64 chunks, cannot find chunk '%s'", Label);
+ return SOX_EOF;
+ }
+ }
+ else {
+ *len = len_tmp;
+ }
+
+ if (memcmp(Label, magic, (size_t)4) == 0)
break; /* Found the given chunk */
- /* Chunks are required to be word aligned. */
- if ((*len) % 2) (*len)++;
+ /* Chunks are required to be word aligned. */
+ if ((*len) % 2) (*len)++;
/* skip to next chunk */
if (*len > 0 && lsx_seeki(ft, (off_t)(*len), SEEK_CUR) != SOX_SUCCESS)
{
lsx_fail_errno(ft,SOX_EHDR,
- "WAV chunk appears to have invalid size %d.", *len);
+ "WAV chunk appears to have invalid size %ld.", *len);
return SOX_EOF;
}
}
@@ -424,10 +447,11 @@
{
priv_t * wav = (priv_t *) ft->priv;
char magic[5];
- uint32_t len;
+ uint64_t len;
/* wave file characteristics */
- uint32_t dwRiffLength;
+ uint64_t qwRiffLength;
+ uint32_t dwRiffLength_tmp;
unsigned short wChannels; /* number of channels */
uint32_t dwSamplesPerSecond; /* samples per second per channel */
uint32_t dwAvgBytesPerSec;/* estimate of bytes per second needed */
@@ -435,7 +459,7 @@
uint32_t wFmtSize;
uint16_t wExtSize = 0; /* extended field for non-PCM */
- uint32_t dwDataLength; /* length of sound data in bytes */
+ uint64_t qwDataLength; /* length of sound data in bytes */
size_t bytesPerBlock = 0;
int bytespersample; /* bytes per sample (per channel */
char text[256];
@@ -445,7 +469,7 @@
wav->ignoreSize = ft->signal.length == SOX_IGNORE_LENGTH;
if (lsx_reads(ft, magic, (size_t)4) == SOX_EOF || (strncmp("RIFF", magic, (size_t)4) != 0 &&
- strncmp("RIFX", magic, (size_t)4) != 0))
+ strncmp("RIFX", magic, (size_t)4) != 0 && strncmp("RF64", magic, (size_t)4)!=0 ))
{
lsx_fail_errno(ft,SOX_EHDR,"WAVE: RIFF header not found");
return SOX_EOF;
@@ -459,8 +483,18 @@
}
else ft->encoding.reverse_bytes = MACHINE_IS_BIGENDIAN;
- lsx_readdw(ft, &dwRiffLength);
+ if (strncmp("RF64", magic, (size_t)4) == 0)
+ {
+ wav->isRF64 = sox_true;
+ }
+ else
+ {
+ wav->isRF64 = sox_false;
+ }
+ lsx_readdw(ft, &dwRiffLength_tmp);
+ qwRiffLength = dwRiffLength_tmp;
+
if (lsx_reads(ft, magic, (size_t)4) == SOX_EOF || strncmp("WAVE", magic, (size_t)4))
{
lsx_fail_errno(ft,SOX_EHDR,"WAVE header not found");
@@ -467,6 +501,21 @@
return SOX_EOF;
}
+ if (wav->isRF64 && findChunk(ft, "ds64", &len) != SOX_EOF) {
+ lsx_debug("Found ds64 header");
+
+ if (dwRiffLength_tmp==0xffffffff)
+ {
+ lsx_readqw(ft, &qwRiffLength);
+ }
+ else
+ {
+ lsx_skipbytes(ft, (size_t)8);
+ }
+ lsx_readqw(ft, &wav->ds64_dataSize);
+ lsx_skipbytes(ft, (size_t)len-16);
+ }
+
/* Now look for the format chunk */
if (findChunk(ft, "fmt ", &len) == SOX_EOF)
{
@@ -804,8 +853,11 @@
lsx_fail_errno(ft, SOX_EOF, "Could not find data chunk.");
return SOX_EOF;
}
- dwDataLength = len;
- if (dwDataLength == MS_UNSPEC) {
+
+ /* ds64 size will have been applied in findChunk */
+ qwDataLength = len;
+ /* XXX - does MS_UNSPEC apply to RF64 files? */
+ if (qwDataLength == MS_UNSPEC) {
wav->ignoreSize = 1;
lsx_debug("WAV Chunk data's length is value often used in pipes or 4G files. Ignoring length.");
}
@@ -819,9 +871,9 @@
case WAVE_FORMAT_ADPCM:
wav->numSamples =
- lsx_ms_adpcm_samples_in((size_t)dwDataLength, (size_t)ft->signal.channels,
+ lsx_ms_adpcm_samples_in((size_t)qwDataLength, (size_t)ft->signal.channels,
(size_t)wav->blockAlign, (size_t)wav->samplesPerBlock);
- lsx_debug_more("datalen %d, numSamples %lu",dwDataLength, (unsigned long)wav->numSamples);
+ lsx_debug_more("datalen %ld, numSamples %lu",qwDataLength, (unsigned long)wav->numSamples);
wav->blockSamplesRemaining = 0; /* Samples left in buffer */
ft->signal.length = wav->numSamples*ft->signal.channels;
break;
@@ -830,9 +882,9 @@
/* Compute easiest part of number of samples. For every block, there
are samplesPerBlock samples to read. */
wav->numSamples =
- lsx_ima_samples_in((size_t)dwDataLength, (size_t)ft->signal.channels,
+ lsx_ima_samples_in((size_t)qwDataLength, (size_t)ft->signal.channels,
(size_t)wav->blockAlign, (size_t)wav->samplesPerBlock);
- lsx_debug_more("datalen %d, numSamples %lu",dwDataLength, (unsigned long)wav->numSamples);
+ lsx_debug_more("datalen %ld, numSamples %lu",qwDataLength, (unsigned long)wav->numSamples);
wav->blockSamplesRemaining = 0; /* Samples left in buffer */
lsx_ima_init_table();
ft->signal.length = wav->numSamples*ft->signal.channels;
@@ -839,13 +891,13 @@
break;
case WAVE_FORMAT_GSM610:
- wav->numSamples = ((dwDataLength / wav->blockAlign) * wav->samplesPerBlock);
+ wav->numSamples = ((qwDataLength / wav->blockAlign) * wav->samplesPerBlock);
wavgsminit(ft);
ft->signal.length = wav->numSamples*ft->signal.channels;
break;
default:
- wav->numSamples = div_bits(dwDataLength, ft->encoding.bits_per_sample) / ft->signal.channels;
+ wav->numSamples = div_bits(qwDataLength, ft->encoding.bits_per_sample) / ft->signal.channels;
ft->signal.length = wav->numSamples * ft->signal.channels;
}
@@ -858,8 +910,8 @@
lsx_debug("Reading Wave file: %s format, %d channel%s, %d samp/sec",
wav_format_str(wav->formatTag), ft->signal.channels,
wChannels == 1 ? "" : "s", dwSamplesPerSecond);
- lsx_debug(" %d byte/sec, %d block align, %d bits/samp, %u data bytes",
- dwAvgBytesPerSec, wav->blockAlign, wBitsPerSample, dwDataLength);
+ lsx_debug(" %d byte/sec, %d block align, %d bits/samp, %lu data bytes",
+ dwAvgBytesPerSec, wav->blockAlign, wBitsPerSample, qwDataLength);
/* Can also report extended fmt information */
switch (wav->formatTag)
@@ -924,8 +976,10 @@
}
else
{
- if (lsx_readdw(ft,&len) == SOX_EOF)
+ uint32_t len_tmp;
+ if (lsx_readdw(ft,&len_tmp) == SOX_EOF)
break;
+ len = len_tmp;
if (strncmp(magic,"ICRD",(size_t)4) == 0)
{
lsx_debug("Chunk ICRD");
@@ -981,7 +1035,7 @@
}
else
{
- lsx_debug("Attempting to seek beyond unsupported chunk `%c%c%c%c' of length %d bytes", magic[0], magic[1], magic[2], magic[3], len);
+ lsx_debug("Attempting to seek beyond unsupported chunk `%c%c%c%c' of length %ld bytes", magic[0], magic[1], magic[2], magic[3], len);
len = (len + 1) & ~1u;
lsx_seeki(ft, (off_t)len, SEEK_CUR);
}