Adapting to surplus chunks and subchunks in .wav files
This commit is contained in:
parent
b5b5cc1fb2
commit
ef2fa1d99d
@ -1 +1 @@
|
|||||||
#define Cdrskin_timestamP "2017.09.16.105713"
|
#define Cdrskin_timestamP "2017.12.31.111507"
|
||||||
|
84
doc/waveformat.txt
Normal file
84
doc/waveformat.txt
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
Sound extraction for CD-DA burning from .WAV audio file format
|
||||||
|
|
||||||
|
Using information and text snippets
|
||||||
|
from https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
|
||||||
|
in may 2013. The link is now dead. An apparent copy of the page
|
||||||
|
is 2017 at: http://soundfile.sapp.org/doc/WaveFormat/
|
||||||
|
from https://en.wikipedia.org/wiki/WAV
|
||||||
|
|
||||||
|
For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net>
|
||||||
|
December 2017
|
||||||
|
|
||||||
|
|
||||||
|
The WAVE file format is an application of the Microsoft RIFF container format
|
||||||
|
for multimedia files. A RIFF file consists of Chunks which contain Subchunks.
|
||||||
|
The Chunks form a linked list within the file, the Subchunks form a linked
|
||||||
|
list inside their Chunk.
|
||||||
|
All numbers are stored in little-endian byte order.
|
||||||
|
|
||||||
|
A .WAV file consists at least of one Chunk with id "RIFF", which contains
|
||||||
|
one Subchunk with id "fmt " and one with id "data":
|
||||||
|
|
||||||
|
Offset Size Name Description
|
||||||
|
|
||||||
|
0 4 ChunkID Contains the letters "RIFF"
|
||||||
|
4 4 ChunkSize The size of the rest of the chunk following
|
||||||
|
this field. I.e. the two fields ChunkID and
|
||||||
|
ChunkSize are not included in this count.
|
||||||
|
8 4 Format Contains the letters "WAVE"
|
||||||
|
|
||||||
|
|
||||||
|
The "fmt " subchunk describes the sound data's format:
|
||||||
|
|
||||||
|
Offset Size Name Description
|
||||||
|
|
||||||
|
0 4 Subchunk1ID Contains the letters "fmt "
|
||||||
|
|
||||||
|
4 4 Subchunk1Size The size of the rest of the Subchunk following
|
||||||
|
this field. I.e. Subchunk1ID and Subchunk1Size
|
||||||
|
are not included in this count.
|
||||||
|
8 2 AudioFormat PCM = 1 (i.e. Linear quantization)
|
||||||
|
Values other than 1 indicate some
|
||||||
|
form of compression.
|
||||||
|
10 2 NumChannels Mono = 1, Stereo = 2, etc.
|
||||||
|
12 4 SampleRate 8000, 44100, etc.
|
||||||
|
16 4 ByteRate == SampleRate * NumChannels * BitsPerSample/8
|
||||||
|
20 2 BlockAlign == NumChannels * BitsPerSample/8
|
||||||
|
The number of bytes for one sample including
|
||||||
|
all channels.
|
||||||
|
22 2 BitsPerSample 8 bits = 8, 16 bits = 16, etc.
|
||||||
|
More data may follow in this Subchunk if AudioFormat is not PCM.
|
||||||
|
|
||||||
|
|
||||||
|
The "data" subchunk contains the size of the data and the actual sound:
|
||||||
|
|
||||||
|
Offset Size Name Description
|
||||||
|
|
||||||
|
0 4 Subchunk2ID Contains the letters "data"
|
||||||
|
|
||||||
|
4 4 Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8
|
||||||
|
The number of audio data bytes.
|
||||||
|
8 * Data The audio data bytes.
|
||||||
|
|
||||||
|
|
||||||
|
CD-DA prescribes these "fmt " parameters:
|
||||||
|
AudioFormat == 1
|
||||||
|
SampleRate == 44100
|
||||||
|
BitsPerSample == 16
|
||||||
|
NumChannels == 2 (stereo)
|
||||||
|
(little-endian byte order)
|
||||||
|
|
||||||
|
If matching parameters are given in the .WAV file, one can directly use the
|
||||||
|
data bytes of Subchunk "data" as payload for burning a CD-DA track.
|
||||||
|
|
||||||
|
|
||||||
|
Above simple form can be expanded by other Chunks or Subchunks of Chunk "RIFF".
|
||||||
|
A .wav file appeared which beared a Subchunk "LIST" inside Chunk "RIFF".
|
||||||
|
Wikipedia mentions Chunks "INFO", "CSET", "JUNK", "PAD ".
|
||||||
|
|
||||||
|
Therefore one should expect such Chunks before Chunk "RIFF" and Subchunks
|
||||||
|
other than "fmt " and "data" inside the "RIFF" Chunk.
|
||||||
|
Multiple Chunks "RIFF" and Subchunks "fmt " or "data" per file have not been
|
||||||
|
seen yet. They would make extraction more cumbersome.
|
||||||
|
|
@ -55,6 +55,7 @@ int libdax_audioxtr_new(struct libdax_audioxtr **xtr, char *path, int flag)
|
|||||||
o->bits_per_sample= 0;
|
o->bits_per_sample= 0;
|
||||||
o->msb_first= 0;
|
o->msb_first= 0;
|
||||||
|
|
||||||
|
o->wav_data_location= 44;
|
||||||
o->wav_subchunk2_size= 0;
|
o->wav_subchunk2_size= 0;
|
||||||
|
|
||||||
o->au_data_location= 0;
|
o->au_data_location= 0;
|
||||||
@ -121,47 +122,123 @@ static int libdax_audioxtr_open(struct libdax_audioxtr *o, int flag)
|
|||||||
return(1);
|
return(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* @param flag: bit0= sequential file, skip by reading data
|
||||||
|
*/
|
||||||
|
static int libdax_audioxtr_skip(struct libdax_audioxtr *o,
|
||||||
|
off_t *old_pos,
|
||||||
|
off_t pos, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
size_t to_read;
|
||||||
|
static char buf[256]; /* Thread safe because the content does not matter */
|
||||||
|
|
||||||
|
if((flag & 1) || o->fd == 0) { /* stdin */
|
||||||
|
while(pos - *old_pos > 0) {
|
||||||
|
to_read= pos - *old_pos;
|
||||||
|
if(to_read > sizeof(buf))
|
||||||
|
to_read= sizeof(buf);
|
||||||
|
ret= read(o->fd, buf, to_read);
|
||||||
|
if(ret < (int) to_read)
|
||||||
|
return(0);
|
||||||
|
*old_pos+= to_read;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret= lseek(o->fd, pos, SEEK_SET);
|
||||||
|
if(ret == -1)
|
||||||
|
return(0);
|
||||||
|
*old_pos= pos;
|
||||||
|
}
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
static int libdax_audioxtr_identify_wav(struct libdax_audioxtr *o, int flag)
|
static int libdax_audioxtr_identify_wav(struct libdax_audioxtr *o, int flag)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret, fmt_seen= 0, data_seen= 0;
|
||||||
char buf[45];
|
off_t pos= 0, old_pos= 0, riff_end= 0;
|
||||||
|
char buf[16];
|
||||||
|
unsigned char *ubuf;
|
||||||
|
|
||||||
/* check wether this is a MS WAVE file .wav */
|
/* check wether this is a MS WAVE file .wav */
|
||||||
/* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
|
/* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
|
||||||
|
https://en.wikipedia.org/wiki/WAV
|
||||||
|
see summary in: doc/waveformat.txt
|
||||||
|
*/
|
||||||
|
ubuf= (unsigned char *) buf;
|
||||||
|
|
||||||
if(o->fd!=0) {
|
/* Look for ChunkID "RIFF" , tolerate other known chunks */
|
||||||
ret= lseek(o->fd,0,SEEK_SET);
|
while(1) {
|
||||||
if(ret==-1)
|
ret= libdax_audioxtr_skip(o, &old_pos, pos, 0);
|
||||||
|
if(ret <= 0)
|
||||||
return(0);
|
return(0);
|
||||||
|
ret= read(o->fd, buf, 8);
|
||||||
|
if(ret < 8)
|
||||||
|
return(0);
|
||||||
|
old_pos+= 8;
|
||||||
|
pos= old_pos + libdax_audioxtr_to_int(o, ubuf + 4, 4, 0);
|
||||||
|
if(pos > 0xffffffff || pos - old_pos < 4) /* Too large or no Format word */
|
||||||
|
return(0);
|
||||||
|
if(strncmp(buf, "RIFF", 4) == 0)
|
||||||
|
break;
|
||||||
|
/* Wikipedia mentions these known ChunkId values */
|
||||||
|
if(strncmp(buf, "INFO", 4) == 0 ||
|
||||||
|
strncmp(buf, "CSET", 4) == 0 ||
|
||||||
|
strncmp(buf, "JUNK", 4) == 0 ||
|
||||||
|
strncmp(buf, "PAD ", 4) == 0)
|
||||||
|
continue;
|
||||||
|
return(0);
|
||||||
}
|
}
|
||||||
ret= read(o->fd, buf, 44);
|
|
||||||
if(ret<44)
|
|
||||||
return(0);
|
|
||||||
buf[44]= 0; /* as stopper for any string operations */
|
|
||||||
|
|
||||||
if(strncmp(buf,"RIFF",4)!=0) /* ChunkID */
|
/* Read RIFF Format header */
|
||||||
|
ret= read(o->fd, buf, 4);
|
||||||
|
if(ret < 4)
|
||||||
return(0);
|
return(0);
|
||||||
if(strncmp(buf+8,"WAVE",4)!=0) /* Format */
|
old_pos+= 4;
|
||||||
return(0);
|
if(strncmp(buf, "WAVE", 4) != 0) /* Format */
|
||||||
if(strncmp(buf+12,"fmt ",4)!=0) /* Subchunk1ID */
|
|
||||||
return(0);
|
|
||||||
if(buf[16]!=16 || buf[17]!=0 || buf[18]!=0 || buf[19]!=0) /* Subchunk1Size */
|
|
||||||
return(0);
|
|
||||||
if(buf[20]!=1 || buf[21]!=0) /* AudioFormat must be 1 (Linear quantization) */
|
|
||||||
return(0);
|
return(0);
|
||||||
|
riff_end= pos;
|
||||||
|
|
||||||
strcpy(o->fmt,".wav");
|
/* Look for SubchunkID "fmt " and "data" */
|
||||||
o->msb_first= 0;
|
pos= old_pos;
|
||||||
o->num_channels= libdax_audioxtr_to_int(o,(unsigned char *) buf+22,2,0);
|
while(old_pos < riff_end) {
|
||||||
o->sample_rate= libdax_audioxtr_to_int(o,(unsigned char *) buf+24,4,0);
|
ret= libdax_audioxtr_skip(o, &old_pos, pos, 0);
|
||||||
o->bits_per_sample= libdax_audioxtr_to_int(o,(unsigned char *)buf+34,2,0);
|
if(ret <= 0)
|
||||||
sprintf(o->fmt_info,
|
return(0);
|
||||||
".wav , num_channels=%d , sample_rate=%d , bits_per_sample=%d",
|
ret= read(o->fd, buf, 8);
|
||||||
o->num_channels,o->sample_rate,o->bits_per_sample);
|
if(ret < 8)
|
||||||
o->wav_subchunk2_size= libdax_audioxtr_to_int(o,(unsigned char *)buf+40,4,0);
|
return(0);
|
||||||
o->data_size= o->wav_subchunk2_size;
|
old_pos= pos + 8;
|
||||||
return(1);
|
pos= old_pos + libdax_audioxtr_to_int(o, ubuf + 4, 4, 0); /* SubchunkSize */
|
||||||
|
|
||||||
|
if(strncmp(buf,"fmt ", 4) == 0) {
|
||||||
|
if(pos - old_pos < 16)
|
||||||
|
return(0);
|
||||||
|
ret= read(o->fd, buf, 16);
|
||||||
|
if(ret < 16)
|
||||||
|
return(0);
|
||||||
|
old_pos+= 16;
|
||||||
|
if(buf[0]!=1 || buf[1]!=0) /* AudioFormat (1 = Linear quantization) */
|
||||||
|
return(0);
|
||||||
|
o->msb_first= 0;
|
||||||
|
o->num_channels= libdax_audioxtr_to_int(o, ubuf + 2 , 2, 0);
|
||||||
|
o->sample_rate= libdax_audioxtr_to_int(o, ubuf + 4, 4, 0);
|
||||||
|
o->bits_per_sample= libdax_audioxtr_to_int(o, ubuf + 14, 2, 0);
|
||||||
|
sprintf(o->fmt_info,
|
||||||
|
".wav , num_channels=%d , sample_rate=%d , bits_per_sample=%d",
|
||||||
|
o->num_channels, o->sample_rate, o->bits_per_sample);
|
||||||
|
fmt_seen= 1;
|
||||||
|
|
||||||
|
} else if(strncmp(buf,"data", 4) == 0) {
|
||||||
|
o->wav_data_location= old_pos;
|
||||||
|
o->wav_subchunk2_size= pos - old_pos;
|
||||||
|
o->data_size= o->wav_subchunk2_size;
|
||||||
|
data_seen= 1;
|
||||||
|
}
|
||||||
|
if(fmt_seen && data_seen) {
|
||||||
|
strcpy(o->fmt,".wav");
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -256,7 +333,7 @@ static int libdax_audioxtr_init_reading(struct libdax_audioxtr *o, int flag)
|
|||||||
|
|
||||||
o->extract_count= 0;
|
o->extract_count= 0;
|
||||||
if(strcmp(o->fmt,".wav")==0)
|
if(strcmp(o->fmt,".wav")==0)
|
||||||
ret= lseek(o->fd,44,SEEK_SET);
|
ret= lseek(o->fd, o->wav_data_location, SEEK_SET);
|
||||||
else if(strcmp(o->fmt,".au")==0)
|
else if(strcmp(o->fmt,".au")==0)
|
||||||
ret= lseek(o->fd,o->au_data_location,SEEK_SET);
|
ret= lseek(o->fd,o->au_data_location,SEEK_SET);
|
||||||
else
|
else
|
||||||
|
Loading…
Reference in New Issue
Block a user