Fix %Sp expansion in wps
[kugel-rb.git] / apps / codecs / aiff_enc.c
blob87358f3df8ab607207c0d0437eafd336fb09513e
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 Antonius Hellmann
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #ifndef SIMULATOR
24 #include <inttypes.h>
25 #include "codeclib.h"
27 CODEC_ENC_HEADER
29 struct aiff_header
31 uint8_t form_id[4]; /* 00h - 'FORM' */
32 uint32_t form_size; /* 04h - size of file - 8 */
33 uint8_t aiff_id[4]; /* 08h - 'AIFF' */
34 uint8_t comm_id[4]; /* 0Ch - 'COMM' */
35 int32_t comm_size; /* 10h - num_channels through sample_rate
36 (18) */
37 int16_t num_channels; /* 14h - 1=M, 2=S, etc. */
38 uint32_t num_sample_frames; /* 16h - num samples for each channel */
39 int16_t sample_size; /* 1ah - 1-32 bits per sample */
40 uint8_t sample_rate[10]; /* 1ch - IEEE 754 80-bit floating point */
41 uint8_t ssnd_id[4]; /* 26h - "SSND" */
42 int32_t ssnd_size; /* 2ah - size of chunk from offset to
43 end of pcm data */
44 uint32_t offset; /* 2eh - data offset from end of header */
45 uint32_t block_size; /* 32h - pcm data alignment */
46 /* 36h */
47 } __attribute__((packed));
49 #define PCM_DEPTH_BYTES 2
50 #define PCM_DEPTH_BITS 16
51 #define PCM_SAMP_PER_CHUNK 2048
52 #define PCM_CHUNK_SIZE (PCM_SAMP_PER_CHUNK*4)
54 /* Template headers */
55 struct aiff_header aiff_header =
57 { 'F', 'O', 'R', 'M' }, /* form_id */
58 0, /* form_size (*) */
59 { 'A', 'I', 'F', 'F' }, /* aiff_id */
60 { 'C', 'O', 'M', 'M' }, /* comm_id */
61 H_TO_BE32(18), /* comm_size */
62 0, /* num_channels (*) */
63 0, /* num_sample_frames (*) */
64 H_TO_BE16(PCM_DEPTH_BITS), /* sample_size */
65 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* sample_rate (*) */
66 { 'S', 'S', 'N', 'D' }, /* ssnd_id */
67 0, /* ssnd_size (*) */
68 H_TO_BE32(0), /* offset */
69 H_TO_BE32(0), /* block_size */
72 /* (*) updated when finalizing file */
74 static int num_channels IBSS_ATTR;
75 static int rec_mono_mode IBSS_ATTR;
76 static uint32_t sample_rate;
77 static uint32_t enc_size;
78 static int32_t err IBSS_ATTR;
80 /* convert unsigned 32 bit value to 80-bit floating point number */
81 STATICIRAM void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l)
82 ICODE_ATTR;
83 STATICIRAM void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l)
85 int32_t exp;
87 ci->memset(f, 0, 10);
89 if (l == 0)
90 return;
92 for (exp = 30; (l & (1ul << 31)) == 0; exp--)
93 l <<= 1;
95 /* sign always zero - bit 79 */
96 /* exponent is 0-31 (normalized: 31 - shift + 16383) - bits 64-78 */
97 f[0] = 0x40;
98 f[1] = (uint8_t)exp;
99 /* mantissa is value left justified with most significant non-zero
100 bit stored in bit 63 - bits 0-63 */
101 *(uint32_t *)&f[2] = htobe32(l);
102 } /* uint32_h_to_ieee754_extended_be */
104 /* called version often - inline */
105 static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR;
106 static inline bool is_file_data_ok(struct enc_file_event_data *data)
108 return data->rec_file >= 0 && (long)data->chunk->flags >= 0;
109 } /* is_file_data_ok */
111 /* called version often - inline */
112 static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR;
113 static inline bool on_write_chunk(struct enc_file_event_data *data)
115 if (!is_file_data_ok(data))
116 return false;
118 if (data->chunk->enc_data == NULL)
120 #ifdef ROCKBOX_HAS_LOGF
121 ci->logf("aiff enc: NULL data");
122 #endif
123 return true;
126 if (ci->write(data->rec_file, data->chunk->enc_data,
127 data->chunk->enc_size) != (ssize_t)data->chunk->enc_size)
128 return false;
130 data->num_pcm_samples += data->chunk->num_pcm;
131 return true;
132 } /* on_write_chunk */
134 static bool on_start_file(struct enc_file_event_data *data)
136 if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
137 return false;
139 data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC);
141 if (data->rec_file < 0)
142 return false;
144 /* reset sample count */
145 data->num_pcm_samples = 0;
147 /* write template headers */
148 if (ci->write(data->rec_file, &aiff_header, sizeof (aiff_header))
149 != sizeof (aiff_header))
151 return false;
154 data->new_enc_size += sizeof(aiff_header);
155 return true;
156 } /* on_start_file */
158 static bool on_end_file(struct enc_file_event_data *data)
160 /* update template headers */
161 struct aiff_header hdr;
162 uint32_t data_size;
164 if (!is_file_data_ok(data))
165 return false;
167 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
168 ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr))
170 return false;
173 data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES;
175 /* 'FORM' chunk */
176 hdr.form_size = htobe32(data_size + sizeof (hdr) - 8);
178 /* 'COMM' chunk */
179 hdr.num_channels = htobe16(num_channels);
180 hdr.num_sample_frames = htobe32(data->num_pcm_samples);
181 uint32_h_to_ieee754_extended_be(hdr.sample_rate, sample_rate);
183 /* 'SSND' chunk */
184 hdr.ssnd_size = htobe32(data_size + 8);
186 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
187 ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr) ||
188 ci->close(data->rec_file) != 0)
190 return false;
193 data->rec_file = -1;
195 return true;
196 } /* on_end_file */
198 STATICIRAM void enc_events_callback(enum enc_events event, void *data)
199 ICODE_ATTR;
200 STATICIRAM void enc_events_callback(enum enc_events event, void *data)
202 switch (event)
204 case ENC_WRITE_CHUNK:
205 if (on_write_chunk((struct enc_file_event_data *)data))
206 return;
208 break;
210 case ENC_START_FILE:
211 if (on_start_file((struct enc_file_event_data *)data))
212 return;
214 break;
216 case ENC_END_FILE:
217 if (on_end_file((struct enc_file_event_data *)data))
218 return;
220 break;
222 default:
223 return;
226 /* Something failed above. Signal error back to core. */
227 ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
228 } /* enc_events_callback */
230 /* convert native pcm samples to aiff format samples */
231 static inline void sample_to_mono(uint32_t **src, uint32_t **dst)
233 int32_t lr1, lr2;
235 switch(rec_mono_mode)
237 case 1:
238 /* mono = L */
239 lr1 = *(*src)++;
240 lr1 = lr1 >> 16;
241 lr2 = *(*src)++;
242 lr2 = lr2 >> 16;
243 break;
244 case 2:
245 /* mono = R */
246 lr1 = *(*src)++;
247 lr1 = (int16_t)lr1;
248 lr2 = *(*src)++;
249 lr2 = (int16_t)lr2;
250 break;
251 case 0:
252 default:
253 /* mono = (L+R)/2 */
254 lr1 = *(*src)++;
255 lr1 = (int16_t)lr1 + (lr1 >> 16) + err;
256 err = lr1 & 1;
257 lr1 >>= 1;
259 lr2 = *(*src)++;
260 lr2 = (int16_t)lr2 + (lr2 >> 16) + err;
261 err = lr2 & 1;
262 lr2 >>= 1;
263 break;
265 *(*dst)++ = htobe32((lr1 << 16) | (uint16_t)lr2);
266 } /* sample_to_mono */
268 STATICIRAM void chunk_to_aiff_format(uint32_t *src, uint32_t *dst) ICODE_ATTR;
269 STATICIRAM void chunk_to_aiff_format(uint32_t *src, uint32_t *dst)
271 if (num_channels == 1)
273 /* On big endian:
274 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
275 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
276 * |MMMMMMMMmmmmmmmm|MMMMMMMMmmmmmmmm|
278 * On little endian:
279 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
280 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
281 * |MMMMMMMMmmmmmmmm|MMMMMMMMmmmmmmmm|
283 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
287 sample_to_mono(&src, &dst);
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);
296 while (src < src_end);
298 else
300 #ifdef ROCKBOX_BIG_ENDIAN
301 /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
302 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
304 ci->memcpy(dst, src, PCM_CHUNK_SIZE);
305 #else
306 /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
307 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
309 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
313 *dst++ = swap_odd_even32(*src++);
314 *dst++ = swap_odd_even32(*src++);
315 *dst++ = swap_odd_even32(*src++);
316 *dst++ = swap_odd_even32(*src++);
317 *dst++ = swap_odd_even32(*src++);
318 *dst++ = swap_odd_even32(*src++);
319 *dst++ = swap_odd_even32(*src++);
320 *dst++ = swap_odd_even32(*src++);
322 while (src < src_end);
323 #endif
325 } /* chunk_to_aiff_format */
327 static bool init_encoder(void)
329 struct enc_inputs inputs;
330 struct enc_parameters params;
332 if (ci->enc_get_inputs == NULL ||
333 ci->enc_set_parameters == NULL ||
334 ci->enc_get_chunk == NULL ||
335 ci->enc_finish_chunk == NULL ||
336 ci->enc_get_pcm_data == NULL )
337 return false;
339 ci->enc_get_inputs(&inputs);
341 if (inputs.config->afmt != AFMT_AIFF)
342 return false;
344 sample_rate = inputs.sample_rate;
345 num_channels = inputs.num_channels;
346 rec_mono_mode = inputs.rec_mono_mode;
347 err = 0;
349 /* configure the buffer system */
350 params.afmt = AFMT_AIFF;
351 enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2;
352 params.chunk_size = enc_size;
353 params.enc_sample_rate = sample_rate;
354 params.reserve_bytes = 0;
355 params.events_callback = enc_events_callback;
356 ci->enc_set_parameters(&params);
358 return true;
359 } /* init_encoder */
361 /* main codec entry point */
362 enum codec_status codec_main(void)
364 if (!init_encoder())
366 ci->enc_codec_loaded = -1;
367 return CODEC_ERROR;
370 /* main application waits for this flag during encoder loading */
371 ci->enc_codec_loaded = 1;
373 /* main encoding loop */
374 while(!ci->stop_encoder)
376 uint32_t *src;
378 while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
380 struct enc_chunk_hdr *chunk;
382 if (ci->stop_encoder)
383 break;
385 chunk = ci->enc_get_chunk();
386 chunk->enc_size = enc_size;
387 chunk->num_pcm = PCM_SAMP_PER_CHUNK;
388 chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
390 chunk_to_aiff_format(src, (uint32_t *)chunk->enc_data);
392 ci->enc_finish_chunk();
393 ci->yield();
396 ci->yield();
399 /* reset parameters to initial state */
400 ci->enc_set_parameters(NULL);
402 /* main application waits for this flag during encoder removing */
403 ci->enc_codec_loaded = 0;
405 return CODEC_OK;
406 } /* codec_start */
408 #endif /* ndef SIMULATOR */