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 pcm.c
22 * raw PCM decoder
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 int
pcmfile_seek_set(PcmFile * pf,uint64_t dest)34 pcmfile_seek_set(PcmFile *pf, uint64_t dest)
35 {
36 FILE *fp = pf->io.fp;
37 int slow_seek = !(pf->seekable);
38
39 if(pf->seekable) {
40 if(dest <= INT32_MAX) {
41 // destination is within first 2GB
42 if(fseek(fp, (long)dest, SEEK_SET)) return -1;
43 } else {
44 int64_t offset = (int64_t)dest - (int64_t)pf->filepos;
45 if(offset >= INT32_MIN && offset <= INT32_MAX) {
46 // offset is within +/- 2GB of file start
47 if(fseek(fp, (long)offset, SEEK_CUR)) return -1;
48 } else {
49 // absolute offset is more than 2GB
50 if(offset < 0) {
51 fprintf(stderr, "error: backward seeking is limited to 2GB\n");
52 return -1;
53 } else {
54 fprintf(stderr, "warning: forward seeking more than 2GB will be slow.\n");
55 }
56 slow_seek = 1;
57 }
58 }
59 byteio_flush(&pf->io);
60 }
61 if(slow_seek) {
62 // do forward-only seek by reading data to temp buffer
63 uint64_t offset;
64 uint8_t buf[1024];
65
66 if(dest < pf->filepos)
67 return -1;
68
69 for(offset = dest - pf->filepos; offset > 1024; offset -= 1024)
70 byteio_read(buf, 1024, &pf->io);
71
72 byteio_read(buf, offset, &pf->io);
73 }
74 pf->filepos = dest;
75
76 return 0;
77 }
78
79 int
pcmfile_init(PcmFile * pf,FILE * fp,enum PcmSampleFormat read_format,int file_format)80 pcmfile_init(PcmFile *pf, FILE *fp, enum PcmSampleFormat read_format,
81 int file_format)
82 {
83 int i;
84
85 if(pf == NULL || fp == NULL) {
86 fprintf(stderr, "null input to pcmfile_init()\n");
87 return -1;
88 }
89
90 pf->read_to_eof = 0;
91 pf->file_format = file_format;
92 pf->read_format = read_format;
93
94 // attempt to get file size
95 pf->file_size = 0;
96 pf->seekable = 0;
97 #ifdef _WIN32
98 // in Windows, don't try to detect seeking support for stdin
99 if(fp != stdin) {
100 pf->seekable = !fseek(fp, 0, SEEK_END);
101 }
102 #else
103 pf->seekable = !fseek(fp, 0, SEEK_END);
104 #endif
105 if(pf->seekable) {
106 // TODO: portable 64-bit ftell
107 long fs = ftell(fp);
108 // ftell should return an error if value cannot fit in return type
109 if(fs < 0) {
110 fprintf(stderr, "Warning, unsupported file size.\n");
111 pf->file_size = 0;
112 } else {
113 pf->file_size = (uint64_t)fs;
114 }
115 fseek(fp, 0, SEEK_SET);
116 }
117 pf->filepos = 0;
118 if(byteio_init(&pf->io, fp)) {
119 fprintf(stderr, "error initializing byte buffer\n");
120 return -1;
121 }
122
123 // detect file format if not specified by the user
124 if(pf->file_format == PCM_FORMAT_UNKNOWN) {
125 uint8_t probe_data[12];
126 int probe_scores[2], probe_max;
127 byteio_peek(probe_data, 12, &pf->io);
128 probe_max = 0;
129 for(i=0; i<2; i++) {
130 switch(i) {
131 case PCM_FORMAT_RAW:
132 probe_scores[i] = pcmfile_probe_raw(probe_data, 12);
133 break;
134 case PCM_FORMAT_WAVE:
135 probe_scores[i] = pcmfile_probe_wave(probe_data, 12);
136 break;
137 }
138 if(probe_scores[i] > probe_scores[probe_max])
139 probe_max = i;
140 }
141 pf->file_format = probe_max;
142 }
143
144 // initialize format
145 switch(pf->file_format) {
146 case PCM_FORMAT_RAW:
147 if(pcmfile_init_raw(pf))
148 return -1;
149 break;
150 case PCM_FORMAT_WAVE:
151 if(pcmfile_init_wave(pf))
152 return -1;
153 break;
154 default:
155 fprintf(stderr, "unknown file format\n");
156 return -1;
157 }
158
159 return 0;
160 }
161
162 void
pcmfile_close(PcmFile * pf)163 pcmfile_close(PcmFile *pf)
164 {
165 byteio_close(&pf->io);
166 }
167
168 int
pcmfile_read_samples(PcmFile * pf,void * output,int num_samples)169 pcmfile_read_samples(PcmFile *pf, void *output, int num_samples)
170 {
171 uint8_t *buffer;
172 uint8_t *read_buffer;
173 uint32_t bytes_needed, buffer_size;
174 int nr, i, j, bps, nsmp;
175
176 // check input and limit number of samples
177 if(pf == NULL || pf->io.fp == NULL || output == NULL || pf->fmt_convert == NULL) {
178 fprintf(stderr, "null input to pcmfile_read_samples()\n");
179 return -1;
180 }
181 if(pf->block_align <= 0) {
182 fprintf(stderr, "invalid block_align\n");
183 return -1;
184 }
185 num_samples = MIN(num_samples, PCM_MAX_READ);
186
187 // calculate number of bytes to read, being careful not to read past
188 // the end of the data chunk
189 bytes_needed = pf->block_align * num_samples;
190 if(!pf->read_to_eof) {
191 if((pf->filepos + bytes_needed) >= (pf->data_start + pf->data_size)) {
192 bytes_needed = (uint32_t)((pf->data_start + pf->data_size) - pf->filepos);
193 num_samples = bytes_needed / pf->block_align;
194 }
195 }
196 if(num_samples <= 0) return 0;
197
198 // allocate temporary buffer for raw input data
199 bps = pf->block_align / pf->channels;
200 buffer_size = (bps != 3) ? bytes_needed : num_samples * sizeof(int32_t) * pf->channels;
201 buffer = calloc(buffer_size, 1);
202 if(!buffer) {
203 fprintf(stderr, "error allocating read buffer\n");
204 return -1;
205 }
206 read_buffer = buffer + (buffer_size - bytes_needed);
207
208 // read raw audio samples from input stream into temporary buffer
209 nr = byteio_read(read_buffer, bytes_needed, &pf->io);
210 if (nr <= 0) {
211 free(buffer);
212 return nr;
213 }
214 pf->filepos += nr;
215 nr /= pf->block_align;
216 nsmp = nr * pf->channels;
217
218 // do any necessary conversion based on source_format and read_format.
219 // also do byte swapping when necessary based on source audio and system
220 // byte orders.
221 switch (bps) {
222 case 2:
223 #ifdef WORDS_BIGENDIAN
224 if(pf->order == PCM_BYTE_ORDER_LE)
225 #else
226 if(pf->order == PCM_BYTE_ORDER_BE)
227 #endif
228 {
229 uint16_t *buf16 = (uint16_t *)buffer;
230 for(i=0; i<nsmp; i++) {
231 buf16[i] = bswap_16(buf16[i]);
232 }
233 }
234 break;
235 case 3:
236 {
237 int32_t *input = (int32_t*)buffer;
238 int unused_bits = 32 - pf->bit_width;
239 int32_t v;
240 // last sample could cause invalid mem access for little endians
241 // but instead of complex logic use simple solution...
242 for(i=0,j=0; i<(nsmp-1)*bps; i+=bps,j++) {
243 #ifdef WORDS_BIGENDIAN
244 if(pf->order == PCM_BYTE_ORDER_LE)
245 #else
246 if(pf->order == PCM_BYTE_ORDER_BE)
247 #endif
248 {
249 v = read_buffer[i] | (read_buffer[i+1] << 8) | (read_buffer[i+2] << 16);
250 } else {
251 v = *(int32_t*)(read_buffer + i);
252 }
253 v <<= unused_bits; // clear unused high bits
254 v >>= unused_bits; // sign extend
255 input[j] = v;
256 }
257 v = read_buffer[i] | (read_buffer[i+1] << 8) | (read_buffer[i+2] << 16);
258 v <<= unused_bits; // clear unused high bits
259 v >>= unused_bits; // sign extend
260 input[j] = v;
261 }
262 break;
263 case 4:
264 #ifdef WORDS_BIGENDIAN
265 if(pf->order == PCM_BYTE_ORDER_LE)
266 #else
267 if(pf->order == PCM_BYTE_ORDER_BE)
268 #endif
269 {
270 uint32_t *buf32 = (uint32_t *)buffer;
271 for(i=0; i<nsmp; i++) {
272 buf32[i] = bswap_32(buf32[i]);
273 }
274 }
275 break;
276 default:
277 #ifdef WORDS_BIGENDIAN
278 if(pf->order == PCM_BYTE_ORDER_LE)
279 #else
280 if(pf->order == PCM_BYTE_ORDER_BE)
281 #endif
282 {
283 uint64_t *buf64 = (uint64_t *)buffer;
284 for(i=0; i<nsmp; i++) {
285 buf64[i] = bswap_64(buf64[i]);
286 }
287 }
288 break;
289 }
290 pf->fmt_convert(output, buffer, nsmp);
291
292 // free temporary buffer
293 free(buffer);
294
295 return nr;
296 }
297
298 int
pcmfile_seek_samples(PcmFile * pf,int64_t offset,int whence)299 pcmfile_seek_samples(PcmFile *pf, int64_t offset, int whence)
300 {
301 int64_t byte_offset;
302 uint64_t newpos, fpos, dst, dsz;
303
304 if(pf == NULL || pf->io.fp == NULL) return -1;
305 if(pf->block_align <= 0) return -1;
306 if(pf->filepos < pf->data_start) return -1;
307 if(pf->data_size == 0) return 0;
308
309 fpos = pf->filepos;
310 dst = pf->data_start;
311 dsz = pf->data_size;
312 byte_offset = offset;
313 byte_offset *= pf->block_align;
314
315 // calculate new destination within file
316 switch(whence) {
317 case PCM_SEEK_SET:
318 newpos = dst + CLIP(byte_offset, 0, (int64_t)dsz);
319 break;
320 case PCM_SEEK_CUR:
321 newpos = fpos - MIN(-byte_offset, (int64_t)(fpos - dst));
322 newpos = MIN(newpos, dst + dsz);
323 break;
324 case PCM_SEEK_END:
325 newpos = dst + dsz - CLIP(byte_offset, 0, (int64_t)dsz);
326 break;
327 default: return -1;
328 }
329
330 // seek to the destination point
331 if(pcmfile_seek_set(pf, newpos)) return -1;
332
333 return 0;
334 }
335
336 int
pcmfile_seek_time_ms(PcmFile * pf,int64_t offset,int whence)337 pcmfile_seek_time_ms(PcmFile *pf, int64_t offset, int whence)
338 {
339 int64_t samples;
340 if(pf == NULL) return -1;
341 samples = offset * pf->sample_rate / 1000;
342 return pcmfile_seek_samples(pf, samples, whence);
343 }
344
345 uint64_t
pcmfile_position(PcmFile * pf)346 pcmfile_position(PcmFile *pf)
347 {
348 uint64_t cur;
349
350 if(pf == NULL) return -1;
351 if(pf->block_align <= 0) return -1;
352 if(pf->data_start == 0 || pf->data_size == 0) return 0;
353
354 cur = (pf->filepos - pf->data_start) / pf->block_align;
355 return cur;
356 }
357
358 uint64_t
pcmfile_position_time_ms(PcmFile * pf)359 pcmfile_position_time_ms(PcmFile *pf)
360 {
361 return (pcmfile_position(pf) * 1000 / pf->sample_rate);
362 }
363
364 void
pcmfile_print(PcmFile * pf,FILE * st)365 pcmfile_print(PcmFile *pf, FILE *st)
366 {
367 char *type, *chan, *fmt, *order;
368 if(st == NULL || pf == NULL) return;
369 type = "?";
370 chan = "?-channel";
371 fmt = "unknown";
372 order = "?-endian";
373 if(pf->sample_type == PCM_SAMPLE_TYPE_INT) {
374 if(pf->bit_width > 8) type = "Signed";
375 else type = "Unsigned";
376 } else if(pf->sample_type == PCM_SAMPLE_TYPE_FLOAT) {
377 type = "Floating-point";
378 } else {
379 type = "[unsupported type]";
380 }
381 if(pf->ch_mask & 0x08) {
382 switch(pf->channels-1) {
383 case 1: chan = "1.1-channel"; break;
384 case 2: chan = "2.1-channel"; break;
385 case 3: chan = "3.1-channel"; break;
386 case 4: chan = "4.1-channel"; break;
387 case 5: chan = "5.1-channel"; break;
388 default: chan = "multi-channel with LFE"; break;
389 }
390 } else {
391 switch(pf->channels) {
392 case 1: chan = "mono"; break;
393 case 2: chan = "stereo"; break;
394 case 3: chan = "3-channel"; break;
395 case 4: chan = "4-channel"; break;
396 case 5: chan = "5-channel"; break;
397 case 6: chan = "6-channel"; break;
398 default: chan = "multi-channel"; break;
399 }
400 }
401 switch(pf->file_format) {
402 case PCM_FORMAT_RAW: fmt = "RAW"; break;
403 case PCM_FORMAT_WAVE: fmt = "WAVE"; break;
404 }
405 switch(pf->order) {
406 case PCM_BYTE_ORDER_LE: order = "little-endian"; break;
407 case PCM_BYTE_ORDER_BE: order = "big-endian"; break;
408 }
409 fprintf(st, "%s %s %d-bit %s %d Hz %s\n", fmt, type, pf->bit_width, order,
410 pf->sample_rate, chan);
411 }
412