diff options
Diffstat (limited to 'apps/codecs')
-rw-r--r-- | apps/codecs/aiff.c | 449 |
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 | ||
23 | CODEC_HEADER | 23 | CODEC_HEADER |
24 | 24 | ||
25 | struct codec_api* rb; | 25 | /* Macro that sign extends an unsigned byte */ |
26 | #define SE(x) ((int32_t)((int8_t)(x))) | ||
27 | |||
28 | struct 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 | ||
31 | enum | 34 | enum |
@@ -48,266 +51,238 @@ extern char iedata[]; | |||
48 | extern char iend[]; | 51 | extern char iend[]; |
49 | #endif | 52 | #endif |
50 | 53 | ||
51 | static int16_t int16_samples[AIF_CHUNK_SIZE] IBSS_ATTR; | 54 | static int32_t samples[AIF_CHUNK_SIZE] IBSS_ATTR; |
52 | |||
53 | 55 | ||
54 | /* this is the codec entry point */ | 56 | enum codec_status codec_start(struct codec_api *api) |
55 | enum 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: | 88 | next_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; |
311 | exit: | 285 | exit: |
312 | return i; | 286 | return i; |
313 | } | 287 | } |
288 | |||