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