summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/aiff_enc.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/aiff_enc.c')
-rw-r--r--lib/rbcodec/codecs/aiff_enc.c399
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
52static int num_channels;
53static uint32_t sample_rate;
54static size_t frame_size;
55static size_t pcm_size;
56static uint32_t num_sample_frames;
51 57
52/* Template headers */ 58/* Template headers */
53struct aiff_header aiff_header = 59static 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 */ 77static inline void frame_htobe(uint32_t *p, size_t size)
71 78{
72static int num_channels IBSS_ATTR; 79#ifdef ROCKBOX_LITTLE_ENDIAN
73static int rec_mono_mode IBSS_ATTR; 80 /* Byte-swap samples, stereo or mono */
74static uint32_t sample_rate; 81 do
75static uint32_t enc_size; 82 {
76static 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 */
79static void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l) 99static void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l)
80 ICODE_ATTR;
81static 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 */
106static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR;
107static 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 */ 121static int on_stream_data(struct enc_chunk_data *data)
113static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR;
114static 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
135static bool on_start_file(struct enc_file_event_data *data) 134static 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
159static bool on_end_file(struct enc_file_event_data *data) 149static 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
199static void enc_events_callback(enum enc_events event, void *data) 192/* this is the codec entry point */
200 ICODE_ATTR; 193enum codec_status codec_main(enum codec_entry_call_reason reason)
201static 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 */
232static inline void sample_to_mono(uint32_t **src, uint32_t **dst) 200enum 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
269static void chunk_to_aiff_format(uint32_t *src, uint32_t *dst) ICODE_ATTR; 213 /* First obtain output buffer; when available, get PCM data */
270static 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
328static 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(&params);
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);
363enum 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 */
378enum codec_status codec_run(void) 239int 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}