diff options
Diffstat (limited to 'apps/codecs/aiff.c')
-rw-r--r-- | apps/codecs/aiff.c | 213 |
1 files changed, 120 insertions, 93 deletions
diff --git a/apps/codecs/aiff.c b/apps/codecs/aiff.c index 9a675415e2..1b8c2da920 100644 --- a/apps/codecs/aiff.c +++ b/apps/codecs/aiff.c | |||
@@ -8,6 +8,7 @@ | |||
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (c) 2005 Jvo Studer | 10 | * Copyright (c) 2005 Jvo Studer |
11 | * Copyright (c) 2009 Yoshihisa Uchida | ||
11 | * | 12 | * |
12 | * This program is free software; you can redistribute it and/or | 13 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 14 | * modify it under the terms of the GNU General Public License |
@@ -21,48 +22,62 @@ | |||
21 | 22 | ||
22 | #include "codeclib.h" | 23 | #include "codeclib.h" |
23 | #include <inttypes.h> | 24 | #include <inttypes.h> |
25 | #include "codecs/libpcm/support_formats.h" | ||
24 | 26 | ||
25 | CODEC_HEADER | 27 | CODEC_HEADER |
26 | 28 | ||
27 | /* Macro that sign extends an unsigned byte */ | 29 | #define FOURCC(c1, c2, c3, c4) \ |
28 | #define SE(x) ((int32_t)((int8_t)(x))) | 30 | ((((uint32_t)c1)<<24)|(((uint32_t)c2)<<16)|(((uint32_t)c3)<<8)|((uint32_t)c4)) |
29 | 31 | ||
30 | /* This codec supports AIFF files with the following formats: | 32 | /* This codec supports the following AIFC compressionType formats */ |
31 | * - PCM, 8, 16 and 24 bits, mono or stereo | 33 | enum { |
32 | */ | 34 | AIFC_FORMAT_PCM = FOURCC('N', 'O', 'N', 'E'), /* AIFC PCM Format (big endian) */ |
35 | AIFC_FORMAT_ALAW = FOURCC('a', 'l', 'a', 'w'), /* AIFC ALaw compressed */ | ||
36 | AIFC_FORMAT_MULAW = FOURCC('u', 'l', 'a', 'w'), /* AIFC uLaw compressed */ | ||
37 | }; | ||
33 | 38 | ||
34 | enum | 39 | static const struct pcm_entry pcm_codecs[] = { |
35 | { | 40 | { AIFC_FORMAT_PCM, get_linear_pcm_codec }, |
36 | AIFF_FORMAT_PCM = 0x0001, /* AIFF PCM Format (big endian) */ | 41 | { AIFC_FORMAT_ALAW, get_itut_g711_alaw_codec }, |
37 | IEEE_FORMAT_FLOAT = 0x0003, /* IEEE Float */ | 42 | { AIFC_FORMAT_MULAW, get_itut_g711_mulaw_codec }, |
38 | AIFF_FORMAT_ALAW = 0x0004, /* AIFC ALaw compressed */ | ||
39 | AIFF_FORMAT_ULAW = 0x0005 /* AIFC uLaw compressed */ | ||
40 | }; | 43 | }; |
41 | 44 | ||
42 | /* Maximum number of bytes to process in one iteration */ | 45 | #define NUM_FORMATS 3 |
43 | /* for 44.1kHz stereo 16bits, this represents 0.023s ~= 1/50s */ | ||
44 | #define AIF_CHUNK_SIZE (1024*2) | ||
45 | 46 | ||
46 | static int32_t samples[AIF_CHUNK_SIZE] IBSS_ATTR; | 47 | static int32_t samples[PCM_CHUNK_SIZE] IBSS_ATTR; |
48 | |||
49 | static const struct pcm_codec *get_codec(uint32_t formattag) | ||
50 | { | ||
51 | int i; | ||
52 | |||
53 | for (i = 0; i < NUM_FORMATS; i++) | ||
54 | { | ||
55 | if (pcm_codecs[i].format_tag == formattag) | ||
56 | { | ||
57 | if (pcm_codecs[i].get_codec) | ||
58 | return pcm_codecs[i].get_codec(); | ||
59 | return 0; | ||
60 | } | ||
61 | } | ||
62 | return 0; | ||
63 | } | ||
47 | 64 | ||
48 | enum codec_status codec_main(void) | 65 | enum codec_status codec_main(void) |
49 | { | 66 | { |
50 | uint32_t numbytes, bytesdone; | 67 | int status = CODEC_OK; |
51 | uint16_t num_channels = 0; | 68 | struct pcm_format format; |
69 | uint32_t bytesdone, decodedbytes; | ||
52 | uint32_t num_sample_frames = 0; | 70 | uint32_t num_sample_frames = 0; |
53 | uint16_t sample_size = 0; | 71 | uint32_t i = CODEC_OK; |
54 | uint32_t sample_rate = 0; | ||
55 | uint32_t i; | ||
56 | size_t n; | 72 | size_t n; |
57 | int bufcount; | 73 | int bufcount; |
58 | int endofstream; | 74 | int endofstream; |
59 | unsigned char *buf; | 75 | unsigned char *buf; |
60 | uint8_t *aifbuf; | 76 | uint8_t *aifbuf; |
61 | long chunksize; | ||
62 | uint32_t offset2snd = 0; | 77 | uint32_t offset2snd = 0; |
63 | uint16_t block_size = 0; | ||
64 | uint32_t avgbytespersec = 0; | ||
65 | off_t firstblockposn; /* position of the first block in file */ | 78 | off_t firstblockposn; /* position of the first block in file */ |
79 | bool is_aifc = false; | ||
80 | const struct pcm_codec *codec; | ||
66 | 81 | ||
67 | /* Generic codec initialisation */ | 82 | /* Generic codec initialisation */ |
68 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | 83 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); |
@@ -84,45 +99,80 @@ next_track: | |||
84 | i = CODEC_ERROR; | 99 | i = CODEC_ERROR; |
85 | goto done; | 100 | goto done; |
86 | } | 101 | } |
87 | if ((memcmp(buf, "FORM", 4) != 0) || (memcmp(&buf[8], "AIFF", 4) != 0)) { | 102 | |
103 | if (memcmp(buf, "FORM", 4) != 0) | ||
104 | { | ||
105 | DEBUGF("CODEC_ERROR: does not aiff format %c%c%c%c\n", buf[0], buf[1], buf[2], buf[3]); | ||
106 | i = CODEC_ERROR; | ||
107 | goto done; | ||
108 | } | ||
109 | if (memcmp(&buf[8], "AIFF", 4) == 0) | ||
110 | is_aifc = false; | ||
111 | else if (memcmp(&buf[8], "AIFC", 4) == 0) | ||
112 | is_aifc = true; | ||
113 | else | ||
114 | { | ||
115 | DEBUGF("CODEC_ERROR: does not aiff format %c%c%c%c\n", buf[8], buf[9], buf[10], buf[11]); | ||
88 | i = CODEC_ERROR; | 116 | i = CODEC_ERROR; |
89 | goto done; | 117 | goto done; |
90 | } | 118 | } |
91 | 119 | ||
92 | buf += 12; | 120 | buf += 12; |
93 | n -= 12; | 121 | n -= 12; |
94 | numbytes = 0; | 122 | |
123 | ci->memset(&format, 0, sizeof(struct pcm_format)); | ||
124 | format.is_signed = true; | ||
125 | format.is_little_endian = false; | ||
126 | |||
127 | decodedbytes = 0; | ||
128 | codec = 0; | ||
95 | 129 | ||
96 | /* read until 'SSND' chunk, which typically is last */ | 130 | /* read until 'SSND' chunk, which typically is last */ |
97 | while (numbytes == 0 && n >= 8) { | 131 | while (format.numbytes == 0 && n >= 8) |
132 | { | ||
98 | /* chunkSize */ | 133 | /* chunkSize */ |
99 | i = ((buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7]); | 134 | i = ((buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7]); |
100 | if (memcmp(buf, "COMM", 4) == 0) { | 135 | if (memcmp(buf, "COMM", 4) == 0) { |
101 | if (i < 18) { | 136 | if ((!is_aifc && i < 18) || (is_aifc && i < 22)) |
102 | DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu < 18\n", | 137 | { |
103 | (unsigned long)i); | 138 | DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu < %d\n", |
139 | (unsigned long)i, (is_aifc)?22:18); | ||
104 | i = CODEC_ERROR; | 140 | i = CODEC_ERROR; |
105 | goto done; | 141 | goto done; |
106 | } | 142 | } |
107 | /* num_channels */ | 143 | /* num_channels */ |
108 | num_channels = ((buf[8]<<8)|buf[9]); | 144 | format.channels = ((buf[8]<<8)|buf[9]); |
109 | /* num_sample_frames */ | 145 | /* num_sample_frames */ |
110 | num_sample_frames = ((buf[10]<<24)|(buf[11]<<16)|(buf[12]<<8) | 146 | num_sample_frames = ((buf[10]<<24)|(buf[11]<<16)|(buf[12]<<8) |
111 | |buf[13]); | 147 | |buf[13]); |
112 | /* sample_size */ | 148 | /* sample_size */ |
113 | sample_size = ((buf[14]<<8)|buf[15]); | 149 | format.bitspersample = ((buf[14]<<8)|buf[15]); |
114 | /* sample_rate (don't use last 4 bytes, only integer fs) */ | 150 | /* sample_rate (don't use last 4 bytes, only integer fs) */ |
115 | if (buf[16] != 0x40) { | 151 | if (buf[16] != 0x40) { |
116 | DEBUGF("CODEC_ERROR: weird sampling rate (no @)\n"); | 152 | DEBUGF("CODEC_ERROR: weird sampling rate (no @)\n"); |
117 | i = CODEC_ERROR; | 153 | i = CODEC_ERROR; |
118 | goto done; | 154 | goto done; |
119 | } | 155 | } |
120 | sample_rate = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1; | 156 | format.samplespersec = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1; |
121 | sample_rate = sample_rate >> (16 + 14 - buf[17]); | 157 | format.samplespersec >>= (16 + 14 - buf[17]); |
158 | /* compressionType (AIFC only) */ | ||
159 | if (is_aifc) | ||
160 | { | ||
161 | format.formattag = (buf[26]<<24)|(buf[27]<<16)|(buf[28]<<8)|buf[29]; | ||
162 | |||
163 | /* | ||
164 | * aiff's sample_size is uncompressed sound data size. | ||
165 | * But format.bitspersample is compressed sound data size. | ||
166 | */ | ||
167 | if (format.formattag == AIFC_FORMAT_ALAW || format.formattag == AIFC_FORMAT_MULAW) | ||
168 | format.bitspersample = 8; | ||
169 | } | ||
170 | else | ||
171 | format.formattag = AIFC_FORMAT_PCM; | ||
122 | /* calc average bytes per second */ | 172 | /* calc average bytes per second */ |
123 | avgbytespersec = sample_rate*num_channels*sample_size/8; | 173 | format.avgbytespersec = format.samplespersec*format.channels*format.bitspersample/8; |
124 | } else if (memcmp(buf, "SSND", 4)==0) { | 174 | } else if (memcmp(buf, "SSND", 4)==0) { |
125 | if (sample_size == 0) { | 175 | if (format.bitspersample == 0) { |
126 | DEBUGF("CODEC_ERROR: unsupported chunk order\n"); | 176 | DEBUGF("CODEC_ERROR: unsupported chunk order\n"); |
127 | i = CODEC_ERROR; | 177 | i = CODEC_ERROR; |
128 | goto done; | 178 | goto done; |
@@ -130,11 +180,14 @@ next_track: | |||
130 | /* offset2snd */ | 180 | /* offset2snd */ |
131 | offset2snd = (buf[8]<<24)|(buf[9]<<16)|(buf[10]<<8)|buf[11]; | 181 | offset2snd = (buf[8]<<24)|(buf[9]<<16)|(buf[10]<<8)|buf[11]; |
132 | /* block_size */ | 182 | /* block_size */ |
133 | block_size = (buf[12]<<24)|(buf[13]<<16)|(buf[14]<<8)|buf[15]; | 183 | format.blockalign = (buf[12]<<24)|(buf[13]<<16)|(buf[14]<<8)|buf[15]; |
134 | if (block_size == 0) | 184 | if (format.blockalign == 0) |
135 | block_size = num_channels*sample_size; | 185 | format.blockalign = format.channels*format.bitspersample; |
136 | numbytes = i - 8 - offset2snd; | 186 | format.numbytes = i - 8 - offset2snd; |
137 | i = 8 + offset2snd; /* advance to the beginning of data */ | 187 | i = 8 + offset2snd; /* advance to the beginning of data */ |
188 | } else if (is_aifc && (memcmp(buf, "FVER", 4)==0)) { | ||
189 | /* Format Version Chunk (AIFC only chunk) */ | ||
190 | /* skip this chunk */ | ||
138 | } else { | 191 | } else { |
139 | DEBUGF("unsupported AIFF chunk: '%c%c%c%c', size=%lu\n", | 192 | DEBUGF("unsupported AIFF chunk: '%c%c%c%c', size=%lu\n", |
140 | buf[0], buf[1], buf[2], buf[3], (unsigned long)i); | 193 | buf[0], buf[1], buf[2], buf[3], (unsigned long)i); |
@@ -151,28 +204,36 @@ next_track: | |||
151 | n -= i + 8; | 204 | n -= i + 8; |
152 | } /* while 'SSND' */ | 205 | } /* while 'SSND' */ |
153 | 206 | ||
154 | if (num_channels == 0) { | 207 | if (format.channels == 0) { |
155 | DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n"); | 208 | DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n"); |
156 | i = CODEC_ERROR; | 209 | i = CODEC_ERROR; |
157 | goto done; | 210 | goto done; |
158 | } | 211 | } |
159 | if (numbytes == 0) { | 212 | if (format.numbytes == 0) { |
160 | DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n"); | 213 | DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n"); |
161 | i = CODEC_ERROR; | 214 | i = CODEC_ERROR; |
162 | goto done; | 215 | goto done; |
163 | } | 216 | } |
164 | if (sample_size > 24) { | 217 | |
165 | DEBUGF("CODEC_ERROR: PCM with more than 24 bits per sample " | 218 | codec = get_codec(format.formattag); |
166 | "is unsupported\n"); | 219 | if (codec == 0) |
220 | { | ||
221 | DEBUGF("CODEC_ERROR: AIFC does not support compressionType: 0x%x\n", format.formattag); | ||
222 | i = CODEC_ERROR; | ||
223 | goto done; | ||
224 | } | ||
225 | |||
226 | if (!codec->set_format(&format, 0)) | ||
227 | { | ||
167 | i = CODEC_ERROR; | 228 | i = CODEC_ERROR; |
168 | goto done; | 229 | goto done; |
169 | } | 230 | } |
170 | 231 | ||
171 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 232 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
172 | 233 | ||
173 | if (num_channels == 2) { | 234 | if (format.channels == 2) { |
174 | ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); | 235 | ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); |
175 | } else if (num_channels == 1) { | 236 | } else if (format.channels == 1) { |
176 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | 237 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); |
177 | } else { | 238 | } else { |
178 | DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n"); | 239 | DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n"); |
@@ -187,18 +248,6 @@ next_track: | |||
187 | bytesdone = 0; | 248 | bytesdone = 0; |
188 | ci->set_elapsed(0); | 249 | ci->set_elapsed(0); |
189 | endofstream = 0; | 250 | endofstream = 0; |
190 | /* chunksize is computed so that one chunk is about 1/50s. | ||
191 | * this make 4096 for 44.1kHz 16bits stereo. | ||
192 | * It also has to be a multiple of blockalign */ | ||
193 | chunksize = (1 + avgbytespersec/(50*block_size))*block_size; | ||
194 | /* check that the output buffer is big enough (convert to samplespersec, | ||
195 | then round to the block_size multiple below) */ | ||
196 | if (((uint64_t)chunksize*ci->id3->frequency*num_channels*2) | ||
197 | /(uint64_t)avgbytespersec >= AIF_CHUNK_SIZE) { | ||
198 | chunksize = ((uint64_t)AIF_CHUNK_SIZE*avgbytespersec | ||
199 | /((uint64_t)ci->id3->frequency*num_channels*2 | ||
200 | *block_size))*block_size; | ||
201 | } | ||
202 | 251 | ||
203 | while (!endofstream) { | 252 | while (!endofstream) { |
204 | ci->yield(); | 253 | ci->yield(); |
@@ -206,61 +255,39 @@ next_track: | |||
206 | break; | 255 | break; |
207 | 256 | ||
208 | if (ci->seek_time) { | 257 | if (ci->seek_time) { |
209 | uint32_t newpos; | 258 | uint32_t newpos = codec->get_seek_pos(ci->seek_time); |
210 | 259 | if (newpos > format.numbytes) | |
211 | /* use avgbytespersec to round to the closest blockalign multiple, | ||
212 | add firstblockposn. 64-bit casts to avoid overflows. */ | ||
213 | newpos = (((uint64_t)avgbytespersec*(ci->seek_time - 1)) | ||
214 | /(1000LL*block_size))*block_size; | ||
215 | if (newpos > numbytes) | ||
216 | break; | 260 | break; |
217 | if (ci->seek_buffer(firstblockposn + newpos)) | 261 | if (ci->seek_buffer(firstblockposn + newpos)) |
218 | bytesdone = newpos; | 262 | bytesdone = newpos; |
219 | ci->seek_complete(); | 263 | ci->seek_complete(); |
220 | } | 264 | } |
221 | aifbuf = (uint8_t *)ci->request_buffer(&n, chunksize); | 265 | aifbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize); |
222 | 266 | ||
223 | if (n == 0) | 267 | if (n == 0) |
224 | break; /* End of stream */ | 268 | break; /* End of stream */ |
225 | 269 | ||
226 | if (bytesdone + n > numbytes) { | 270 | if (bytesdone + n > format.numbytes) { |
227 | n = numbytes - bytesdone; | 271 | n = format.numbytes - bytesdone; |
228 | endofstream = 1; | 272 | endofstream = 1; |
229 | } | 273 | } |
230 | 274 | ||
231 | if (sample_size > 24) { | 275 | status = codec->decode(aifbuf, n, samples, &bufcount); |
232 | for (i = 0; i < n; i += 4) { | 276 | if (status == CODEC_ERROR) |
233 | samples[i/4] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13) | 277 | { |
234 | |(aifbuf[i + 2]<<5)|(aifbuf[i + 3]>>3); | 278 | DEBUGF("codec error\n"); |
235 | } | 279 | goto done; |
236 | bufcount = n >> 2; | ||
237 | } else if (sample_size > 16) { | ||
238 | for (i = 0; i < n; i += 3) { | ||
239 | samples[i/3] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13) | ||
240 | |(aifbuf[i + 2]<<5); | ||
241 | } | ||
242 | bufcount = n/3; | ||
243 | } else if (sample_size > 8) { | ||
244 | for (i = 0; i < n; i += 2) | ||
245 | samples[i/2] = (SE(aifbuf[i])<<21)|(aifbuf[i + 1]<<13); | ||
246 | bufcount = n >> 1; | ||
247 | } else { | ||
248 | for (i = 0; i < n; i++) | ||
249 | samples[i] = SE(aifbuf[i]) << 21; | ||
250 | bufcount = n; | ||
251 | } | 280 | } |
252 | 281 | ||
253 | if (num_channels == 2) | ||
254 | bufcount >>= 1; | ||
255 | |||
256 | ci->pcmbuf_insert(samples, NULL, bufcount); | 282 | ci->pcmbuf_insert(samples, NULL, bufcount); |
257 | 283 | ||
258 | ci->advance_buffer(n); | 284 | ci->advance_buffer(n); |
259 | bytesdone += n; | 285 | bytesdone += n; |
260 | if (bytesdone >= numbytes) | 286 | decodedbytes += bufcount; |
287 | if (bytesdone >= format.numbytes) | ||
261 | endofstream = 1; | 288 | endofstream = 1; |
262 | 289 | ||
263 | ci->set_elapsed(bytesdone*1000LL/avgbytespersec); | 290 | ci->set_elapsed(decodedbytes*1000LL/ci->id3->frequency); |
264 | } | 291 | } |
265 | i = CODEC_OK; | 292 | i = CODEC_OK; |
266 | 293 | ||