diff options
Diffstat (limited to 'lib/rbcodec/codecs/aiff_enc.c')
-rw-r--r-- | lib/rbcodec/codecs/aiff_enc.c | 399 |
1 files changed, 131 insertions, 268 deletions
diff --git a/lib/rbcodec/codecs/aiff_enc.c b/lib/rbcodec/codecs/aiff_enc.c index 8e9246d2bb..fb8db384a3 100644 --- a/lib/rbcodec/codecs/aiff_enc.c +++ b/lib/rbcodec/codecs/aiff_enc.c | |||
@@ -8,6 +8,7 @@ | |||
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2006 Antonius Hellmann | 10 | * Copyright (C) 2006 Antonius Hellmann |
11 | * Copyright (C) 2006-2013 Michael Sevakis | ||
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 |
@@ -47,10 +48,15 @@ struct aiff_header | |||
47 | #define PCM_DEPTH_BYTES 2 | 48 | #define PCM_DEPTH_BYTES 2 |
48 | #define PCM_DEPTH_BITS 16 | 49 | #define PCM_DEPTH_BITS 16 |
49 | #define PCM_SAMP_PER_CHUNK 2048 | 50 | #define PCM_SAMP_PER_CHUNK 2048 |
50 | #define PCM_CHUNK_SIZE (PCM_SAMP_PER_CHUNK*4) | 51 | |
52 | static int num_channels; | ||
53 | static uint32_t sample_rate; | ||
54 | static size_t frame_size; | ||
55 | static size_t pcm_size; | ||
56 | static uint32_t num_sample_frames; | ||
51 | 57 | ||
52 | /* Template headers */ | 58 | /* Template headers */ |
53 | struct aiff_header aiff_header = | 59 | static const struct aiff_header aiff_template_header = |
54 | { | 60 | { |
55 | { 'F', 'O', 'R', 'M' }, /* form_id */ | 61 | { 'F', 'O', 'R', 'M' }, /* form_id */ |
56 | 0, /* form_size (*) */ | 62 | 0, /* form_size (*) */ |
@@ -65,336 +71,193 @@ struct aiff_header aiff_header = | |||
65 | 0, /* ssnd_size (*) */ | 71 | 0, /* ssnd_size (*) */ |
66 | htobe32(0), /* offset */ | 72 | htobe32(0), /* offset */ |
67 | htobe32(0), /* block_size */ | 73 | htobe32(0), /* block_size */ |
74 | /* (*) updated when finalizing stream */ | ||
68 | }; | 75 | }; |
69 | 76 | ||
70 | /* (*) updated when finalizing file */ | 77 | static inline void frame_htobe(uint32_t *p, size_t size) |
71 | 78 | { | |
72 | static int num_channels IBSS_ATTR; | 79 | #ifdef ROCKBOX_LITTLE_ENDIAN |
73 | static int rec_mono_mode IBSS_ATTR; | 80 | /* Byte-swap samples, stereo or mono */ |
74 | static uint32_t sample_rate; | 81 | do |
75 | static uint32_t enc_size; | 82 | { |
76 | static int32_t err IBSS_ATTR; | 83 | uint32_t t; |
84 | t = swap_odd_even32(*p); *p++ = t; | ||
85 | t = swap_odd_even32(*p); *p++ = t; | ||
86 | t = swap_odd_even32(*p); *p++ = t; | ||
87 | t = swap_odd_even32(*p); *p++ = t; | ||
88 | t = swap_odd_even32(*p); *p++ = t; | ||
89 | t = swap_odd_even32(*p); *p++ = t; | ||
90 | t = swap_odd_even32(*p); *p++ = t; | ||
91 | t = swap_odd_even32(*p); *p++ = t; | ||
92 | } | ||
93 | while (size -= 8 * 2 * PCM_DEPTH_BYTES); | ||
94 | #endif /* ROCKBOX_LITTLE_ENDIAN */ | ||
95 | (void)p; (void)size; | ||
96 | } | ||
77 | 97 | ||
78 | /* convert unsigned 32 bit value to 80-bit floating point number */ | 98 | /* convert unsigned 32 bit value to 80-bit floating point number */ |
79 | static void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l) | 99 | static void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l) |
80 | ICODE_ATTR; | ||
81 | static void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l) | ||
82 | { | 100 | { |
83 | int32_t exp; | ||
84 | |||
85 | ci->memset(f, 0, 10); | 101 | ci->memset(f, 0, 10); |
86 | 102 | ||
87 | if (l == 0) | 103 | if (l == 0) |
88 | return; | 104 | return; |
89 | 105 | ||
90 | for (exp = 30; (l & (1ul << 31)) == 0; exp--) | 106 | int shift = __builtin_clz(l); |
91 | l <<= 1; | ||
92 | 107 | ||
93 | /* sign always zero - bit 79 */ | 108 | /* sign always zero - bit 79 */ |
94 | /* exponent is 0-31 (normalized: 31 - shift + 16383) - bits 64-78 */ | 109 | /* exponent is 0-31 (normalized: 30 - shift + 16383) - bits 64-78 */ |
95 | f[0] = 0x40; | 110 | f[0] = 0x40; |
96 | f[1] = (uint8_t)exp; | 111 | f[1] = (uint8_t)(30 - shift); |
97 | /* mantissa is value left justified with most significant non-zero | 112 | /* mantissa is value left justified with most significant non-zero |
98 | bit stored in bit 63 - bits 0-63 */ | 113 | bit stored in bit 63 - bits 0-63 */ |
114 | l <<= shift; | ||
99 | f[2] = (uint8_t)(l >> 24); | 115 | f[2] = (uint8_t)(l >> 24); |
100 | f[3] = (uint8_t)(l >> 16); | 116 | f[3] = (uint8_t)(l >> 16); |
101 | f[4] = (uint8_t)(l >> 8); | 117 | f[4] = (uint8_t)(l >> 8); |
102 | f[5] = (uint8_t)(l >> 0); | 118 | f[5] = (uint8_t)(l >> 0); |
103 | } /* uint32_h_to_ieee754_extended_be */ | 119 | } |
104 | |||
105 | /* called version often - inline */ | ||
106 | static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR; | ||
107 | static inline bool is_file_data_ok(struct enc_file_event_data *data) | ||
108 | { | ||
109 | return data->rec_file >= 0 && (long)data->chunk->flags >= 0; | ||
110 | } /* is_file_data_ok */ | ||
111 | 120 | ||
112 | /* called version often - inline */ | 121 | static int on_stream_data(struct enc_chunk_data *data) |
113 | static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR; | ||
114 | static inline bool on_write_chunk(struct enc_file_event_data *data) | ||
115 | { | 122 | { |
116 | if (!is_file_data_ok(data)) | 123 | size_t size = data->hdr.size; |
117 | return false; | ||
118 | 124 | ||
119 | if (data->chunk->enc_data == NULL) | 125 | if (ci->enc_stream_write(data->data, size) != (ssize_t)size) |
120 | { | 126 | return -1; |
121 | #ifdef ROCKBOX_HAS_LOGF | ||
122 | ci->logf("aiff enc: NULL data"); | ||
123 | #endif | ||
124 | return true; | ||
125 | } | ||
126 | 127 | ||
127 | if (ci->write(data->rec_file, data->chunk->enc_data, | 128 | pcm_size += size; |
128 | data->chunk->enc_size) != (ssize_t)data->chunk->enc_size) | 129 | num_sample_frames += data->pcm_count; |
129 | return false; | ||
130 | 130 | ||
131 | data->num_pcm_samples += data->chunk->num_pcm; | 131 | return 0; |
132 | return true; | 132 | } |
133 | } /* on_write_chunk */ | ||
134 | 133 | ||
135 | static bool on_start_file(struct enc_file_event_data *data) | 134 | static int on_stream_start(void) |
136 | { | 135 | { |
137 | if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0') | ||
138 | return false; | ||
139 | |||
140 | data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666); | ||
141 | |||
142 | if (data->rec_file < 0) | ||
143 | return false; | ||
144 | |||
145 | /* reset sample count */ | 136 | /* reset sample count */ |
146 | data->num_pcm_samples = 0; | 137 | pcm_size = 0; |
138 | num_sample_frames = 0; | ||
147 | 139 | ||
148 | /* write template headers */ | 140 | /* write template header */ |
149 | if (ci->write(data->rec_file, &aiff_header, sizeof (aiff_header)) | 141 | if (ci->enc_stream_write(&aiff_template_header, |
150 | != sizeof (aiff_header)) | 142 | sizeof (struct aiff_header)) |
151 | { | 143 | != sizeof (struct aiff_header)) |
152 | return false; | 144 | return -1; |
153 | } | ||
154 | 145 | ||
155 | data->new_enc_size += sizeof(aiff_header); | 146 | return 0; |
156 | return true; | 147 | } |
157 | } /* on_start_file */ | ||
158 | 148 | ||
159 | static bool on_end_file(struct enc_file_event_data *data) | 149 | static int on_stream_end(union enc_chunk_hdr *hdr) |
160 | { | 150 | { |
161 | /* update template headers */ | 151 | /* update template header */ |
162 | struct aiff_header hdr; | 152 | struct aiff_header aiff; |
163 | uint32_t data_size; | ||
164 | 153 | ||
165 | if (!is_file_data_ok(data)) | 154 | if (hdr->err) |
166 | return false; | ||
167 | |||
168 | if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || | ||
169 | ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)) | ||
170 | { | 155 | { |
171 | return false; | 156 | /* Called for stream error; get correct data size */ |
157 | ssize_t size = ci->enc_stream_lseek(0, SEEK_END); | ||
158 | |||
159 | if (size > (ssize_t)sizeof (aiff)) | ||
160 | { | ||
161 | pcm_size = size - sizeof (aiff); | ||
162 | num_sample_frames = pcm_size / (PCM_DEPTH_BYTES*num_channels); | ||
163 | } | ||
172 | } | 164 | } |
173 | 165 | ||
174 | data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES; | 166 | if (ci->enc_stream_lseek(0, SEEK_SET) != 0) |
167 | return -1; | ||
168 | |||
169 | if (ci->enc_stream_read(&aiff, sizeof (aiff)) != sizeof (aiff)) | ||
170 | return -2; | ||
175 | 171 | ||
176 | /* 'FORM' chunk */ | 172 | /* 'FORM' chunk */ |
177 | hdr.form_size = htobe32(data_size + sizeof (hdr) - 8); | 173 | aiff.form_size = htobe32(pcm_size + sizeof (aiff) - 8); |
178 | 174 | ||
179 | /* 'COMM' chunk */ | 175 | /* 'COMM' chunk */ |
180 | hdr.num_channels = htobe16(num_channels); | 176 | aiff.num_channels = htobe16(num_channels); |
181 | hdr.num_sample_frames = htobe32(data->num_pcm_samples); | 177 | aiff.num_sample_frames = htobe32(num_sample_frames); |
182 | uint32_h_to_ieee754_extended_be(hdr.sample_rate, sample_rate); | 178 | uint32_h_to_ieee754_extended_be(aiff.sample_rate, sample_rate); |
183 | 179 | ||
184 | /* 'SSND' chunk */ | 180 | /* 'SSND' chunk */ |
185 | hdr.ssnd_size = htobe32(data_size + 8); | 181 | aiff.ssnd_size = htobe32(pcm_size + 8); |
186 | 182 | ||
187 | if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || | 183 | if (ci->enc_stream_lseek(0, SEEK_SET) != 0) |
188 | ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr) || | 184 | return -3; |
189 | ci->close(data->rec_file) != 0) | ||
190 | { | ||
191 | return false; | ||
192 | } | ||
193 | 185 | ||
194 | data->rec_file = -1; | 186 | if (ci->enc_stream_write(&aiff, sizeof (aiff)) != sizeof (aiff)) |
187 | return -4; | ||
195 | 188 | ||
196 | return true; | 189 | return 0; |
197 | } /* on_end_file */ | 190 | } |
198 | 191 | ||
199 | static void enc_events_callback(enum enc_events event, void *data) | 192 | /* this is the codec entry point */ |
200 | ICODE_ATTR; | 193 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
201 | static void enc_events_callback(enum enc_events event, void *data) | ||
202 | { | 194 | { |
203 | switch (event) | 195 | return CODEC_OK; |
204 | { | 196 | (void)reason; |
205 | case ENC_WRITE_CHUNK: | 197 | } |
206 | if (on_write_chunk((struct enc_file_event_data *)data)) | ||
207 | return; | ||
208 | |||
209 | break; | ||
210 | |||
211 | case ENC_START_FILE: | ||
212 | if (on_start_file((struct enc_file_event_data *)data)) | ||
213 | return; | ||
214 | |||
215 | break; | ||
216 | |||
217 | case ENC_END_FILE: | ||
218 | if (on_end_file((struct enc_file_event_data *)data)) | ||
219 | return; | ||
220 | |||
221 | break; | ||
222 | |||
223 | default: | ||
224 | return; | ||
225 | } | ||
226 | |||
227 | /* Something failed above. Signal error back to core. */ | ||
228 | ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR; | ||
229 | } /* enc_events_callback */ | ||
230 | 198 | ||
231 | /* convert native pcm samples to aiff format samples */ | 199 | /* this is called for each file to process */ |
232 | static inline void sample_to_mono(uint32_t **src, uint32_t **dst) | 200 | enum codec_status ICODE_ATTR codec_run(void) |
233 | { | 201 | { |
234 | int32_t lr1, lr2; | 202 | enum { GETBUF_ENC, GETBUF_PCM } getbuf = GETBUF_ENC; |
203 | struct enc_chunk_data *data = NULL; | ||
235 | 204 | ||
236 | switch(rec_mono_mode) | 205 | /* main encoding loop */ |
206 | while (1) | ||
237 | { | 207 | { |
238 | case 1: | 208 | enum codec_command_action action = ci->get_command(NULL); |
239 | /* mono = L */ | 209 | |
240 | lr1 = *(*src)++; | 210 | if (action != CODEC_ACTION_NULL) |
241 | lr1 = lr1 >> 16; | ||
242 | lr2 = *(*src)++; | ||
243 | lr2 = lr2 >> 16; | ||
244 | break; | ||
245 | case 2: | ||
246 | /* mono = R */ | ||
247 | lr1 = *(*src)++; | ||
248 | lr1 = (int16_t)lr1; | ||
249 | lr2 = *(*src)++; | ||
250 | lr2 = (int16_t)lr2; | ||
251 | break; | ||
252 | case 0: | ||
253 | default: | ||
254 | /* mono = (L+R)/2 */ | ||
255 | lr1 = *(*src)++; | ||
256 | lr1 = (int16_t)lr1 + (lr1 >> 16) + err; | ||
257 | err = lr1 & 1; | ||
258 | lr1 >>= 1; | ||
259 | |||
260 | lr2 = *(*src)++; | ||
261 | lr2 = (int16_t)lr2 + (lr2 >> 16) + err; | ||
262 | err = lr2 & 1; | ||
263 | lr2 >>= 1; | ||
264 | break; | 211 | break; |
265 | } | ||
266 | *(*dst)++ = htobe32((lr1 << 16) | (uint16_t)lr2); | ||
267 | } /* sample_to_mono */ | ||
268 | 212 | ||
269 | static void chunk_to_aiff_format(uint32_t *src, uint32_t *dst) ICODE_ATTR; | 213 | /* First obtain output buffer; when available, get PCM data */ |
270 | static void chunk_to_aiff_format(uint32_t *src, uint32_t *dst) | 214 | switch (getbuf) |
271 | { | ||
272 | if (num_channels == 1) | ||
273 | { | ||
274 | /* On big endian: | ||
275 | * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| | ||
276 | * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => | ||
277 | * |MMMMMMMMmmmmmmmm|MMMMMMMMmmmmmmmm| | ||
278 | * | ||
279 | * On little endian: | ||
280 | * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| | ||
281 | * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => | ||
282 | * |MMMMMMMMmmmmmmmm|MMMMMMMMmmmmmmmm| | ||
283 | */ | ||
284 | uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; | ||
285 | |||
286 | do | ||
287 | { | ||
288 | sample_to_mono(&src, &dst); | ||
289 | sample_to_mono(&src, &dst); | ||
290 | sample_to_mono(&src, &dst); | ||
291 | sample_to_mono(&src, &dst); | ||
292 | sample_to_mono(&src, &dst); | ||
293 | sample_to_mono(&src, &dst); | ||
294 | sample_to_mono(&src, &dst); | ||
295 | sample_to_mono(&src, &dst); | ||
296 | } | ||
297 | while (src < src_end); | ||
298 | } | ||
299 | else | ||
300 | { | ||
301 | #ifdef ROCKBOX_BIG_ENDIAN | ||
302 | /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => | ||
303 | * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| | ||
304 | */ | ||
305 | ci->memcpy(dst, src, PCM_CHUNK_SIZE); | ||
306 | #else | ||
307 | /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => | ||
308 | * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| | ||
309 | */ | ||
310 | uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; | ||
311 | |||
312 | do | ||
313 | { | 215 | { |
314 | *dst++ = swap_odd_even32(*src++); | 216 | case GETBUF_ENC: |
315 | *dst++ = swap_odd_even32(*src++); | 217 | if (!(data = ci->enc_encbuf_get_buffer(frame_size))) |
316 | *dst++ = swap_odd_even32(*src++); | 218 | continue; |
317 | *dst++ = swap_odd_even32(*src++); | 219 | getbuf = GETBUF_PCM; |
318 | *dst++ = swap_odd_even32(*src++); | 220 | case GETBUF_PCM: |
319 | *dst++ = swap_odd_even32(*src++); | 221 | if (!ci->enc_pcmbuf_read(data->data, PCM_SAMP_PER_CHUNK)) |
320 | *dst++ = swap_odd_even32(*src++); | 222 | continue; |
321 | *dst++ = swap_odd_even32(*src++); | 223 | getbuf = GETBUF_ENC; |
322 | } | 224 | } |
323 | while (src < src_end); | ||
324 | #endif | ||
325 | } | ||
326 | } /* chunk_to_aiff_format */ | ||
327 | 225 | ||
328 | static bool init_encoder(void) | 226 | data->hdr.size = frame_size; |
329 | { | 227 | data->pcm_count = PCM_SAMP_PER_CHUNK; |
330 | struct enc_inputs inputs; | ||
331 | struct enc_parameters params; | ||
332 | |||
333 | if (ci->enc_get_inputs == NULL || | ||
334 | ci->enc_set_parameters == NULL || | ||
335 | ci->enc_get_chunk == NULL || | ||
336 | ci->enc_finish_chunk == NULL || | ||
337 | ci->enc_get_pcm_data == NULL ) | ||
338 | return false; | ||
339 | |||
340 | ci->enc_get_inputs(&inputs); | ||
341 | |||
342 | if (inputs.config->afmt != AFMT_AIFF) | ||
343 | return false; | ||
344 | |||
345 | sample_rate = inputs.sample_rate; | ||
346 | num_channels = inputs.num_channels; | ||
347 | rec_mono_mode = inputs.rec_mono_mode; | ||
348 | err = 0; | ||
349 | |||
350 | /* configure the buffer system */ | ||
351 | params.afmt = AFMT_AIFF; | ||
352 | enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2; | ||
353 | params.chunk_size = enc_size; | ||
354 | params.enc_sample_rate = sample_rate; | ||
355 | params.reserve_bytes = 0; | ||
356 | params.events_callback = enc_events_callback; | ||
357 | ci->enc_set_parameters(¶ms); | ||
358 | |||
359 | return true; | ||
360 | } /* init_encoder */ | ||
361 | 228 | ||
362 | /* this is the codec entry point */ | 229 | frame_htobe((uint32_t *)data->data, frame_size); |
363 | enum codec_status codec_main(enum codec_entry_call_reason reason) | 230 | |
364 | { | 231 | ci->enc_pcmbuf_advance(PCM_SAMP_PER_CHUNK); |
365 | if (reason == CODEC_LOAD) { | 232 | ci->enc_encbuf_finish_buffer(); |
366 | if (!init_encoder()) | ||
367 | return CODEC_ERROR; | ||
368 | } | ||
369 | else if (reason == CODEC_UNLOAD) { | ||
370 | /* reset parameters to initial state */ | ||
371 | ci->enc_set_parameters(NULL); | ||
372 | } | 233 | } |
373 | 234 | ||
374 | return CODEC_OK; | 235 | return CODEC_OK; |
375 | } | 236 | } |
376 | 237 | ||
377 | /* this is called for each file to process */ | 238 | /* this is called by recording system */ |
378 | enum codec_status codec_run(void) | 239 | int ICODE_ATTR enc_callback(enum enc_callback_reason reason, |
240 | void *params) | ||
379 | { | 241 | { |
380 | /* main encoding loop */ | 242 | if (LIKELY(reason == ENC_CB_STREAM)) |
381 | while (ci->get_command(NULL) != CODEC_ACTION_HALT) | ||
382 | { | 243 | { |
383 | uint32_t *src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE); | 244 | switch (((union enc_chunk_hdr *)params)->type) |
384 | struct enc_chunk_hdr *chunk; | 245 | { |
385 | 246 | case CHUNK_T_DATA: | |
386 | if (src == NULL) | 247 | return on_stream_data(params); |
387 | continue; | 248 | case CHUNK_T_STREAM_START: |
388 | 249 | return on_stream_start(); | |
389 | chunk = ci->enc_get_chunk(); | 250 | case CHUNK_T_STREAM_END: |
390 | chunk->enc_size = enc_size; | 251 | return on_stream_end(params); |
391 | chunk->num_pcm = PCM_SAMP_PER_CHUNK; | 252 | } |
392 | chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); | 253 | } |
393 | 254 | else if (reason == ENC_CB_INPUTS) | |
394 | chunk_to_aiff_format(src, (uint32_t *)chunk->enc_data); | 255 | { |
395 | 256 | struct enc_inputs *inputs = params; | |
396 | ci->enc_finish_chunk(); | 257 | sample_rate = inputs->sample_rate; |
258 | num_channels = inputs->num_channels; | ||
259 | frame_size = PCM_SAMP_PER_CHUNK*PCM_DEPTH_BYTES*num_channels; | ||
397 | } | 260 | } |
398 | 261 | ||
399 | return CODEC_OK; | 262 | return 0; |
400 | } | 263 | } |