summaryrefslogtreecommitdiff
path: root/apps/codecs/aiff.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codecs/aiff.c')
-rw-r--r--apps/codecs/aiff.c449
1 files changed, 212 insertions, 237 deletions
diff --git a/apps/codecs/aiff.c b/apps/codecs/aiff.c
index 764655f776..5549d63239 100644
--- a/apps/codecs/aiff.c
+++ b/apps/codecs/aiff.c
@@ -18,14 +18,17 @@
18 ****************************************************************************/ 18 ****************************************************************************/
19 19
20#include "codeclib.h" 20#include "codeclib.h"
21#include "inttypes.h" 21#include <inttypes.h>
22 22
23CODEC_HEADER 23CODEC_HEADER
24 24
25struct codec_api* rb; 25/* Macro that sign extends an unsigned byte */
26#define SE(x) ((int32_t)((int8_t)(x)))
27
28struct codec_api *rb;
26 29
27/* This codec supports AIFF files with the following formats: 30/* This codec supports AIFF files with the following formats:
28 * - PCM, 8 and 16 bits, mono or stereo 31 * - PCM, 8, 16 and 24 bits, mono or stereo
29 */ 32 */
30 33
31enum 34enum
@@ -48,266 +51,238 @@ extern char iedata[];
48extern char iend[]; 51extern char iend[];
49#endif 52#endif
50 53
51static int16_t int16_samples[AIF_CHUNK_SIZE] IBSS_ATTR; 54static int32_t samples[AIF_CHUNK_SIZE] IBSS_ATTR;
52
53 55
54/* this is the codec entry point */ 56enum codec_status codec_start(struct codec_api *api)
55enum codec_status codec_start(struct codec_api* api)
56{ 57{
57 struct codec_api* ci; 58 struct codec_api *ci;
58 uint32_t numbytes, bytesdone; 59 uint32_t numbytes, bytesdone;
59 uint16_t num_channels = 0; 60 uint16_t num_channels = 0;
60 uint32_t num_sample_frames = 0; 61 uint32_t num_sample_frames = 0;
61 uint16_t sample_size = 0; 62 uint16_t sample_size = 0;
62 uint32_t sample_rate = 0; 63 uint32_t sample_rate = 0;
63 uint32_t i; 64 uint32_t i;
64 size_t n, aifbufsize; 65 size_t n, bufsize;
65 int endofstream; 66 int endofstream;
66 unsigned char* buf; 67 unsigned char *buf;
67 uint16_t* aifbuf; 68 uint8_t *aifbuf;
68 long chunksize; 69 long chunksize;
69 uint32_t offset2snd = 0; 70 uint32_t offset2snd = 0;
70 uint16_t block_size = 0; 71 uint16_t block_size = 0;
71 uint32_t avgbytespersec = 0; 72 uint32_t avgbytespersec = 0;
72 off_t firstblockposn; /* position of the first block in file */ 73 off_t firstblockposn; /* position of the first block in file */
73 int shortorlong = 1; /* do we output shorts (1) or longs (2)? */ 74
74 int32_t * const int32_samples = (int32_t*)int16_samples; 75 /* Generic codec initialisation */
75 76 rb = api;
76 /* Generic codec initialisation */ 77 ci = api;
77 rb = api;
78 ci = api;
79 78
80#ifdef USE_IRAM 79#ifdef USE_IRAM
81 ci->memcpy(iramstart, iramcopy, iramend-iramstart); 80 ci->memcpy(iramstart, iramcopy, iramend - iramstart);
82 ci->memset(iedata, 0, iend - iedata); 81 ci->memset(iedata, 0, iend - iedata);
83#endif 82#endif
84 83
85 ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512)); 84 ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
86 ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256)); 85 ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256));
87 86 ci->configure(DSP_DITHER, (bool *)false);
88 ci->configure(DSP_DITHER, (bool *)false);
89 87
90 next_track: 88next_track:
91 89 if (codec_init(api)) {
92 if (codec_init(api)) { 90 i = CODEC_ERROR;
93 i = CODEC_ERROR; 91 goto exit;
94 goto exit; 92 }
95 }
96 93
97 while (!*ci->taginfo_ready) 94 while (!*ci->taginfo_ready)
98 ci->yield(); 95 ci->yield();
99 96
100 /* assume the AIFF header is less than 1024 bytes */ 97 /* assume the AIFF header is less than 1024 bytes */
101 buf=ci->request_buffer((long *)&n,1024); 98 buf = ci->request_buffer((long *)&n, 1024);
102 if (n<44) { 99 if (n < 44) {
103 i = CODEC_ERROR; 100 i = CODEC_ERROR;
104 goto exit; 101 goto exit;
105 } 102 }
106 if ((memcmp(buf,"FORM",4)!=0) || (memcmp(&buf[8],"AIFF",4)!=0)) { 103 if ((memcmp(buf, "FORM", 4) != 0) || (memcmp(&buf[8], "AIFF", 4) != 0)) {
107 i = CODEC_ERROR; 104 i = CODEC_ERROR;
108 goto exit; 105 goto exit;
109 }
110
111 buf += 12;
112 n -= 12;
113 numbytes = 0;
114
115 /* read until 'SSND' chunk, which typically is last */
116 while(numbytes == 0 && n >= 8) {
117 /* chunkSize */
118 i = ((buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7]);
119 if (memcmp(buf,"COMM",4)==0) {
120 if (i != 18) {
121 DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu != 18\n",i);
122 i = CODEC_ERROR;
123 goto exit;
124 }
125 /* num_channels */
126 num_channels = ((buf[8]<<8)|buf[9]);
127 /* num_sample_frames */
128 num_sample_frames = ((buf[10]<<24)|(buf[11]<<16)|(buf[12]<<8)|buf[13]);
129 /* sample_size */
130 sample_size = ((buf[14]<<8)|buf[15]);
131 /* sample_rate (don't use last 4 bytes, only integer fs) */
132 if (buf[16] != 0x40) {
133 DEBUGF("CODEC_ERROR: wierd sampling rate (no @)\n",i);
134 i = CODEC_ERROR;
135 goto exit;
136 }
137 sample_rate = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1;
138 sample_rate = sample_rate >> (16+14-buf[17]);
139 /* calc average bytes per second */
140 avgbytespersec = sample_rate*num_channels*sample_size/8;
141 }
142 else if (memcmp(buf,"SSND",4)==0) {
143 if (sample_size == 0) {
144 DEBUGF("CODEC_ERROR: unsupported chunk order\n");
145 i = CODEC_ERROR;
146 goto exit;
147 }
148 /* offset2snd */
149 offset2snd = ((buf[8]<<8)|buf[9]);
150 /* block_size */
151 block_size = ((buf[10]<<8)|buf[11]);
152 if (block_size == 0)
153 block_size = num_channels*sample_size;
154 numbytes = i-8-offset2snd;
155 i = 8+offset2snd; /* advance to the beginning of data */
156 }
157 else {
158 DEBUGF("unsupported AIFF chunk: '%c%c%c%c', size=%lu\n",
159 buf[0], buf[1], buf[2], buf[3], i);
160 }
161
162 if (i & 0x01) /* odd chunk sizes must be padded */
163 i++;
164 buf += i+8;
165 if (n < (i+8)) {
166 DEBUGF("CODEC_ERROR: AIFF header size > 1024\n");
167 i = CODEC_ERROR;
168 goto exit;
169 }
170 n -= i+8;
171 } /* while 'SSND' */
172
173 if (num_channels == 0) {
174 DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n");
175 i = CODEC_ERROR;
176 goto exit;
177 }
178 if (numbytes == 0) {
179 DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n");
180 i = CODEC_ERROR;
181 goto exit;
182 }
183 if (sample_size > 24) {
184 DEBUGF("CODEC_ERROR: PCM with more than 24 bits per sample "
185 "is unsupported\n");
186 i = CODEC_ERROR;
187 goto exit;
188 }
189
190 ci->configure(CODEC_DSP_ENABLE, (bool *)true);
191 ci->configure(DSP_SET_FREQUENCY, (long *)(ci->id3->frequency));
192
193 if (sample_size <= 16) {
194 ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16));
195 } else {
196 shortorlong = 2;
197 ci->configure(DSP_DITHER, (bool *)false);
198 ci->configure(DSP_SET_SAMPLE_DEPTH, (long *) (32));
199 ci->configure(DSP_SET_CLIP_MAX, (long *) (2147483647));
200 ci->configure(DSP_SET_CLIP_MIN, (long *) (-2147483647-1));
201 }
202
203 if (num_channels == 2) {
204 ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED);
205 } else if (num_channels == 1) {
206 ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_MONO);
207 } else {
208 DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n");
209 i = CODEC_ERROR;
210 goto exit;
211 }
212
213 firstblockposn = (1024-n);
214 ci->advance_buffer(firstblockposn);
215
216 /* The main decoder loop */
217
218 bytesdone=0;
219 ci->set_elapsed(0);
220 endofstream=0;
221 /* chunksize is computed so that one chunk is about 1/50s.
222 * this make 4096 for 44.1kHz 16bits stereo.
223 * It also has to be a multiple of blockalign */
224 chunksize = (1 + avgbytespersec / (50*block_size)) * block_size;
225 /* check that the output buffer is big enough (convert to samplespersec,
226 then round to the block_size multiple below) */
227 if (((uint64_t)chunksize*ci->id3->frequency*num_channels*shortorlong)
228 / (uint64_t)avgbytespersec >= AIF_CHUNK_SIZE) {
229 chunksize = ((uint64_t)AIF_CHUNK_SIZE * avgbytespersec
230 / ((uint64_t)ci->id3->frequency * num_channels * shortorlong
231 * block_size)) * block_size;
232 }
233
234 while (!endofstream) {
235 uint8_t *aifbuf8;
236
237 ci->yield();
238 if (ci->stop_codec || ci->reload_codec) {
239 break;
240 } 106 }
241 107
242 if (ci->seek_time) { 108 buf += 12;
243 uint32_t newpos; 109 n -= 12;
110 numbytes = 0;
111
112 /* read until 'SSND' chunk, which typically is last */
113 while (numbytes == 0 && n >= 8) {
114 /* chunkSize */
115 i = ((buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7]);
116 if (memcmp(buf, "COMM", 4) == 0) {
117 if (i != 18) {
118 DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu != 18\n", i);
119 i = CODEC_ERROR;
120 goto exit;
121 }
122 /* num_channels */
123 num_channels = ((buf[8]<<8)|buf[9]);
124 /* num_sample_frames */
125 num_sample_frames = ((buf[10]<<24)|(buf[11]<<16)|(buf[12]<<8)
126 |buf[13]);
127 /* sample_size */
128 sample_size = ((buf[14]<<8)|buf[15]);
129 /* sample_rate (don't use last 4 bytes, only integer fs) */
130 if (buf[16] != 0x40) {
131 DEBUGF("CODEC_ERROR: weird sampling rate (no @)\n", i);
132 i = CODEC_ERROR;
133 goto exit;
134 }
135 sample_rate = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1;
136 sample_rate = sample_rate >> (16 + 14 - buf[17]);
137 /* calc average bytes per second */
138 avgbytespersec = sample_rate*num_channels*sample_size/8;
139 } else if (memcmp(buf, "SSND", 4)==0) {
140 if (sample_size == 0) {
141 DEBUGF("CODEC_ERROR: unsupported chunk order\n");
142 i = CODEC_ERROR;
143 goto exit;
144 }
145 /* offset2snd */
146 offset2snd = ((buf[8]<<8)|buf[9]);
147 /* block_size */
148 block_size = ((buf[10]<<8)|buf[11]);
149 if (block_size == 0)
150 block_size = num_channels*sample_size;
151 numbytes = i - 8 - offset2snd;
152 i = 8 + offset2snd; /* advance to the beginning of data */
153 } else {
154 DEBUGF("unsupported AIFF chunk: '%c%c%c%c', size=%lu\n",
155 buf[0], buf[1], buf[2], buf[3], i);
156 }
244 157
245 /* use avgbytespersec to round to the closest blockalign multiple, 158 if (i & 0x01) /* odd chunk sizes must be padded */
246 add firstblockposn. 64-bit casts to avoid overflows. */ 159 i++;
247 newpos = (((uint64_t)avgbytespersec * (ci->seek_time - 1)) 160 buf += i + 8;
248 / (1000LL*block_size)) * block_size; 161 if (n < (i + 8)) {
249 if (newpos > numbytes) 162 DEBUGF("CODEC_ERROR: AIFF header size > 1024\n");
250 break; 163 i = CODEC_ERROR;
251 if (ci->seek_buffer(firstblockposn + newpos)) { 164 goto exit;
252 bytesdone = newpos;
253 } 165 }
254 ci->seek_complete(); 166 n -= i + 8;
167 } /* while 'SSND' */
168
169 if (num_channels == 0) {
170 DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n");
171 i = CODEC_ERROR;
172 goto exit;
255 } 173 }
256 aifbuf=ci->request_buffer((long *)&n,chunksize); 174 if (numbytes == 0) {
257 aifbuf8 = (uint8_t*)aifbuf; 175 DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n");
176 i = CODEC_ERROR;
177 goto exit;
178 }
179 if (sample_size > 24) {
180 DEBUGF("CODEC_ERROR: PCM with more than 24 bits per sample "
181 "is unsupported\n");
182 i = CODEC_ERROR;
183 goto exit;
184 }
185
186 ci->configure(CODEC_DSP_ENABLE, (bool *)true);
187 ci->configure(DSP_SET_FREQUENCY, (long *)(ci->id3->frequency));
188 ci->configure(DSP_SET_SAMPLE_DEPTH, (long *)28);
258 189
259 if (n==0) 190 if (num_channels == 2) {
260 break; /* End of stream */ 191 ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED);
192 } else if (num_channels == 1) {
193 ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_MONO);
194 } else {
195 DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n");
196 i = CODEC_ERROR;
197 goto exit;
198 }
261 199
262 if (bytesdone + n > numbytes) { 200 firstblockposn = 1024 - n;
263 n = numbytes - bytesdone; 201 ci->advance_buffer(firstblockposn);
264 endofstream = 1; 202
203 /* The main decoder loop */
204 bytesdone = 0;
205 ci->set_elapsed(0);
206 endofstream = 0;
207 /* chunksize is computed so that one chunk is about 1/50s.
208 * this make 4096 for 44.1kHz 16bits stereo.
209 * It also has to be a multiple of blockalign */
210 chunksize = (1 + avgbytespersec/(50*block_size))*block_size;
211 /* check that the output buffer is big enough (convert to samplespersec,
212 then round to the block_size multiple below) */
213 if (((uint64_t)chunksize*ci->id3->frequency*num_channels*2)
214 /(uint64_t)avgbytespersec >= AIF_CHUNK_SIZE) {
215 chunksize = ((uint64_t)AIF_CHUNK_SIZE*avgbytespersec
216 /((uint64_t)ci->id3->frequency*num_channels*2
217 *block_size))*block_size;
265 } 218 }
266 219
267 aifbufsize = sizeof(int16_samples); 220 while (!endofstream) {
221 ci->yield();
222 if (ci->stop_codec || ci->reload_codec)
223 break;
268 224
269 if (sample_size > 24) { 225 if (ci->seek_time) {
270 for (i=0;i<n;i+=4) { 226 uint32_t newpos;
271 int32_samples[i/4]=(int32_t)((aifbuf8[i]<<24)| 227
272 (aifbuf8[i+1]<<16)|(aifbuf8[i+2]<<8)|aifbuf8[i+3]); 228 /* use avgbytespersec to round to the closest blockalign multiple,
229 add firstblockposn. 64-bit casts to avoid overflows. */
230 newpos = (((uint64_t)avgbytespersec*(ci->seek_time - 1))
231 /(1000LL*block_size))*block_size;
232 if (newpos > numbytes)
233 break;
234 if (ci->seek_buffer(firstblockposn + newpos))
235 bytesdone = newpos;
236 ci->seek_complete();
273 } 237 }
274 aifbufsize = n; 238 aifbuf = (uint8_t *)ci->request_buffer((long *)&n, chunksize);
275 } else if (sample_size > 16) { 239
276 for (i=0;i<n;i+=3) { 240 if (n == 0)
277 int32_samples[i/3]=(int32_t)((aifbuf8[i]<<24)| 241 break; /* End of stream */
278 (aifbuf8[i+1]<<16)|(aifbuf8[i+2]<<8)); 242
279 } 243 if (bytesdone + n > numbytes) {
280 aifbufsize = n*4/3; 244 n = numbytes - bytesdone;
281 } else if (sample_size > 8) { 245 endofstream = 1;
282 /* copy data. */
283 for (i=0;i<n;i+=2) {
284 int16_samples[i/2]=(int16_t)((aifbuf8[i]<<8)|aifbuf8[i+1]);
285 } 246 }
286 aifbufsize = n; 247
287 } else { 248 if (sample_size > 24) {
288 for (i=0;i<n;i++) { 249 for (i = 0; i < n; i += 4) {
289 int16_samples[i] = (aifbuf8[i]<<8) - 0x8000; 250 samples[i/4] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13)
251 |(aifbuf[i + 2]<<5)|(aifbuf[i + 3]>>3);
252 }
253 bufsize = n;
254 } else if (sample_size > 16) {
255 for (i = 0; i < n; i += 3) {
256 samples[i/3] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13)
257 |(aifbuf[i + 2]<<5);
258 }
259 bufsize = n*4/3;
260 } else if (sample_size > 8) {
261 for (i = 0; i < n; i += 2)
262 samples[i/2] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13);
263 bufsize = n*2;
264 } else {
265 for (i = 0; i < n; i++)
266 samples[i] = SE(aifbuf[i]) << 21;
267 bufsize = n*4;
290 } 268 }
291 aifbufsize = n*2;
292 }
293 269
294 while (!ci->pcmbuf_insert((char*)int16_samples, aifbufsize)) { 270 while (!ci->pcmbuf_insert((char *)samples, bufsize))
295 ci->yield(); 271 ci->yield();
296 }
297 272
298 ci->advance_buffer(n); 273 ci->advance_buffer(n);
299 bytesdone += n; 274 bytesdone += n;
300 if (bytesdone >= numbytes) { 275 if (bytesdone >= numbytes)
301 endofstream=1; 276 endofstream = 1;
302 }
303 277
304 ci->set_elapsed(bytesdone*1000LL/avgbytespersec); 278 ci->set_elapsed(bytesdone*1000LL/avgbytespersec);
305 } 279 }
306 280
307 if (ci->request_next_track()) 281 if (ci->request_next_track())
308 goto next_track; 282 goto next_track;
309 283
310 i = CODEC_OK; 284 i = CODEC_OK;
311exit: 285exit:
312 return i; 286 return i;
313} 287}
288