1 /**
2  * Aften: A/52 audio encoder
3  * Copyright (c) 2006 Justin Ruggles
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 /**
21  * @file wav.c
22  * WAV file format
23  */
24 
25 #include "common.h"
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 
31 #include "pcm.h"
32 
33 /* chunk id's */
34 #define RIFF_ID     0x46464952
35 #define WAVE_ID     0x45564157
36 #define FMT__ID     0x20746D66
37 #define DATA_ID     0x61746164
38 
39 /**
40  * Reads a 4-byte little-endian word from the input stream
41  */
42 static inline uint32_t
read4le(ByteIOContext * io)43 read4le(ByteIOContext *io)
44 {
45     uint32_t x;
46     if(byteio_read(&x, 4, io) != 4)
47         return 0;
48     return le2me_32(x);
49 }
50 
51 /**
52  * Reads a 2-byte little-endian word from the input stream
53  */
54 static uint16_t
read2le(ByteIOContext * io)55 read2le(ByteIOContext *io)
56 {
57     uint16_t x;
58     if(byteio_read(&x, 2, io) != 2)
59         return 0;
60     return le2me_16(x);
61 }
62 
63 int
pcmfile_probe_wave(uint8_t * data,int size)64 pcmfile_probe_wave(uint8_t *data, int size)
65 {
66     int id;
67 
68     if(!data || size < 12)
69         return 0;
70     id = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
71     if(id != RIFF_ID) {
72         return 0;
73     }
74     id = data[8] | (data[9] << 8) | (data[10] << 16) | (data[11] << 24);
75     if(id != WAVE_ID) {
76         return 0;
77     }
78     return 100;
79 }
80 
81 int
pcmfile_init_wave(PcmFile * pf)82 pcmfile_init_wave(PcmFile *pf)
83 {
84     int id, found_data, found_fmt, chunksize;
85 
86     // read RIFF id. ignore size.
87     id = read4le(&pf->io);
88     pf->filepos += 4;
89     if(id != RIFF_ID) {
90         fprintf(stderr, "invalid RIFF id in wav header\n");
91         return -1;
92     }
93     read4le(&pf->io);
94     pf->filepos += 4;
95 
96     // read WAVE id. ignore size.
97     id = read4le(&pf->io);
98     pf->filepos += 4;
99     if(id != WAVE_ID) {
100         fprintf(stderr, "invalid WAVE id in wav header\n");
101         return -1;
102     }
103 
104     // read all header chunks. skip unknown chunks.
105     found_data = found_fmt = 0;
106     while(!found_data) {
107         id = read4le(&pf->io);
108         pf->filepos += 4;
109         chunksize = read4le(&pf->io);
110         pf->filepos += 4;
111         switch(id) {
112             case FMT__ID:
113                 if(chunksize < 16) {
114                     fprintf(stderr, "invalid fmt chunk in wav header\n");
115                     return -1;
116                 }
117                 pf->wav_format = read2le(&pf->io);
118                 pf->filepos += 2;
119                 if(pf->wav_format == WAVE_FORMAT_IEEEFLOAT) {
120                     pf->sample_type = PCM_SAMPLE_TYPE_FLOAT;
121                 } else {
122                     pf->sample_type = PCM_SAMPLE_TYPE_INT;
123                 }
124                 pf->channels = read2le(&pf->io);
125                 pf->filepos += 2;
126                 if(pf->channels == 0) {
127                     fprintf(stderr, "invalid number of channels in wav header\n");
128                     return -1;
129                 }
130                 pf->sample_rate = read4le(&pf->io);
131                 pf->filepos += 4;
132                 if(pf->sample_rate == 0) {
133                     fprintf(stderr, "invalid sample rate in wav header\n");
134                     return -1;
135                 }
136                 read4le(&pf->io);
137                 pf->filepos += 4;
138                 pf->block_align = read2le(&pf->io);
139                 pf->filepos += 2;
140                 pf->bit_width = read2le(&pf->io);
141                 pf->filepos += 2;
142                 if(pf->bit_width == 0) {
143                     fprintf(stderr, "invalid sample bit width in wav header\n");
144                     return -1;
145                 }
146                 chunksize -= 16;
147 
148                 // WAVE_FORMAT_EXTENSIBLE data
149                 pf->ch_mask = 0;
150                 if(pf->wav_format == WAVE_FORMAT_EXTENSIBLE && chunksize >= 10) {
151                     read4le(&pf->io);    // skip CbSize and ValidBitsPerSample
152                     pf->filepos += 4;
153                     pf->ch_mask = read4le(&pf->io);
154                     pf->filepos += 4;
155                     pf->wav_format = read2le(&pf->io);
156                     if(pf->wav_format == WAVE_FORMAT_IEEEFLOAT) {
157                         pf->sample_type = PCM_SAMPLE_TYPE_FLOAT;
158                     } else {
159                         pf->sample_type = PCM_SAMPLE_TYPE_INT;
160                     }
161                     pf->filepos += 2;
162                     chunksize -= 10;
163                 }
164 
165                 // override block alignment in header
166                 if(pf->wav_format == WAVE_FORMAT_IEEEFLOAT ||
167                         pf->wav_format == WAVE_FORMAT_PCM) {
168                     pf->block_align = MAX(1, ((pf->bit_width + 7) >> 3) * pf->channels);
169                 }
170 
171                 // make up channel mask if not using WAVE_FORMAT_EXTENSIBLE
172                 // or if ch_mask is set to zero (unspecified configuration)
173                 // TODO: select default configurations for >6 channels
174                 if(pf->ch_mask == 0) {
175                     switch(pf->channels) {
176                         case 1: pf->ch_mask = 0x04;  break;
177                         case 2: pf->ch_mask = 0x03;  break;
178                         case 3: pf->ch_mask = 0x07;  break;
179                         case 4: pf->ch_mask = 0x107; break;
180                         case 5: pf->ch_mask = 0x37;  break;
181                         case 6: pf->ch_mask = 0x3F;  break;
182                     }
183                 }
184 
185                 // skip any leftover bytes in fmt chunk
186                 if(pcmfile_seek_set(pf, pf->filepos + chunksize)) {
187                     fprintf(stderr, "error seeking in wav file\n");
188                     return -1;
189                 }
190                 found_fmt = 1;
191                 break;
192             case DATA_ID:
193                 if(!found_fmt) return -1;
194                 if(chunksize == 0)
195                     pf->read_to_eof = 1;
196                 pf->data_size = chunksize;
197                 pf->data_start = pf->filepos;
198                 if(pf->seekable && pf->file_size > 0) {
199                     // limit data size to end-of-file
200                     if(pf->data_size > 0)
201                         pf->data_size = MIN(pf->data_size, pf->file_size - pf->data_start);
202                     else
203                         pf->data_size = pf->file_size - pf->data_start;
204                 }
205                 pf->samples = (pf->data_size / pf->block_align);
206                 found_data = 1;
207                 break;
208             default:
209                 // skip unknown chunk
210                 if(chunksize > 0 && pcmfile_seek_set(pf, pf->filepos + chunksize)) {
211                     fprintf(stderr, "error seeking in wav file\n");
212                     return -1;
213                 }
214         }
215     }
216 
217     // set audio data format based on bit depth and sample type
218     pf->source_format = PCM_SAMPLE_FMT_UNKNOWN;
219     switch(pf->bit_width) {
220         case 8:  pf->source_format = PCM_SAMPLE_FMT_U8;  break;
221         case 16: pf->source_format = PCM_SAMPLE_FMT_S16; break;
222         case 20: pf->source_format = PCM_SAMPLE_FMT_S20; break;
223         case 24: pf->source_format = PCM_SAMPLE_FMT_S24; break;
224         case 32:
225             if(pf->sample_type == PCM_SAMPLE_TYPE_FLOAT)
226                 pf->source_format = PCM_SAMPLE_FMT_FLT;
227             else if(pf->sample_type == PCM_SAMPLE_TYPE_INT)
228                 pf->source_format = PCM_SAMPLE_FMT_S32;
229             break;
230         case 64:
231             if(pf->sample_type == PCM_SAMPLE_TYPE_FLOAT) {
232                 pf->source_format = PCM_SAMPLE_FMT_DBL;
233             } else {
234                 fprintf(stderr, "64-bit integer samples not supported\n");
235                 return -1;
236             }
237             break;
238     }
239     pcmfile_set_source(pf, pf->source_format, PCM_BYTE_ORDER_LE);
240 
241     return 0;
242 }
243