summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/aiff_enc.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2013-06-22 16:41:16 -0400
committerMichael Sevakis <jethead71@rockbox.org>2013-06-30 00:40:27 +0200
commit488813197292bd1db8d533d7b42c38852971c2e8 (patch)
tree07ea7247799b1b6b487c5ca73311380fc947700e /lib/rbcodec/codecs/aiff_enc.c
parenta9ea1a42695401334717f2e497a7f5576d87691d (diff)
downloadrockbox-488813197292bd1db8d533d7b42c38852971c2e8.tar.gz
rockbox-488813197292bd1db8d533d7b42c38852971c2e8.zip
Update software recording engine to latest codec interface.
Basically, just give it a good rewrite. Software codec recording can be implemented in a more straightforward and simple manner and made more robust through the better codec control now available. Encoded audio buffer uses a packed format instead of fixed-size chunks and uses smaller data headers leading to more efficient usage. The greatest benefit is with a VBR format like wavpack which needs to request a maximum size but only actually ends up committing part of that request. No guard buffers are used for either PCM or encoded audio. PCM is read into the codec's provided buffer and mono conversion done at that time in the core if required. Any highly-specialized sample conversion is still done within the codec itself, such as 32-bit (wavpack) or interleaved mono (mp3). There is no longer a separate filename array. All metadata goes onto the main encoded audio buffer, eliminating any predermined file limit on the buffer as well as not wasting the space for unused path queue slots. The core and codec interface is less awkward and a bit more sensible. Some less useful interface features were removed. Threads are kept on narrow code paths ie. the audio thread never calls encoding functions and the codec thread never calls file functions as before. Codecs no longer call file functions directly. Writes are buffered in the core and data written to storage in larger chunks to speed up flushing of data. In fact, codecs are no longer aware of the stream being a file at all and have no access to the fd. SPDIF frequency detection no longer requires a restart of recording or plugging the source before entering the screen. It will poll for changes and update when stopped or prerecording (which does discard now-invalid prerecorded data). I've seen to it that writing a proper header on full disk works when the format makes it reasonably practical to do so. Other cases may have incorrect data sizes but sample info will be in tact. File left that way may play anyway. mp3_enc.codec acquires the ability to write 'Info' headers with LAME tags to make it gapless (bonus). Change-Id: I670685166d5eb32ef58ef317f50b8af766ceb653 Reviewed-on: http://gerrit.rockbox.org/493 Reviewed-by: Michael Sevakis <jethead71@rockbox.org> Tested-by: Michael Sevakis <jethead71@rockbox.org>
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}