diff options
Diffstat (limited to 'apps/codecs')
-rw-r--r-- | apps/codecs/Makefile | 1 | ||||
-rw-r--r-- | apps/codecs/SOURCES | 1 | ||||
-rw-r--r-- | apps/codecs/aiff_enc.c | 408 |
3 files changed, 410 insertions, 0 deletions
diff --git a/apps/codecs/Makefile b/apps/codecs/Makefile index 12235b490c..38723b7940 100644 --- a/apps/codecs/Makefile +++ b/apps/codecs/Makefile | |||
@@ -61,6 +61,7 @@ $(OBJDIR)/wavpack.elf : $(OBJDIR)/wavpack.o $(BUILDDIR)/libwavpack.a | |||
61 | $(OBJDIR)/alac.elf : $(OBJDIR)/alac.o $(BUILDDIR)/libalac.a $(BUILDDIR)/libm4a.a | 61 | $(OBJDIR)/alac.elf : $(OBJDIR)/alac.o $(BUILDDIR)/libalac.a $(BUILDDIR)/libm4a.a |
62 | $(OBJDIR)/aac.elf : $(OBJDIR)/aac.o $(BUILDDIR)/libfaad.a $(BUILDDIR)/libm4a.a | 62 | $(OBJDIR)/aac.elf : $(OBJDIR)/aac.o $(BUILDDIR)/libfaad.a $(BUILDDIR)/libm4a.a |
63 | $(OBJDIR)/shorten.elf : $(OBJDIR)/shorten.o $(BUILDDIR)/libffmpegFLAC.a | 63 | $(OBJDIR)/shorten.elf : $(OBJDIR)/shorten.o $(BUILDDIR)/libffmpegFLAC.a |
64 | $(OBJDIR)/aiff_enc.elf: $(OBJDIR)/aiff_enc.o | ||
64 | $(OBJDIR)/mp3_enc.elf: $(OBJDIR)/mp3_enc.o | 65 | $(OBJDIR)/mp3_enc.elf: $(OBJDIR)/mp3_enc.o |
65 | $(OBJDIR)/wav_enc.elf: $(OBJDIR)/wav_enc.o | 66 | $(OBJDIR)/wav_enc.elf: $(OBJDIR)/wav_enc.o |
66 | $(OBJDIR)/wavpack_enc.elf: $(OBJDIR)/wavpack_enc.o $(BUILDDIR)/libwavpack.a | 67 | $(OBJDIR)/wavpack_enc.elf: $(OBJDIR)/wavpack_enc.o $(BUILDDIR)/libwavpack.a |
diff --git a/apps/codecs/SOURCES b/apps/codecs/SOURCES index feadcde0ac..68bb04f926 100644 --- a/apps/codecs/SOURCES +++ b/apps/codecs/SOURCES | |||
@@ -17,6 +17,7 @@ sid.c | |||
17 | adx.c | 17 | adx.c |
18 | #if defined(HAVE_RECORDING) && !defined(SIMULATOR) | 18 | #if defined(HAVE_RECORDING) && !defined(SIMULATOR) |
19 | /* encoders */ | 19 | /* encoders */ |
20 | aiff_enc.c | ||
20 | mp3_enc.c | 21 | mp3_enc.c |
21 | wav_enc.c | 22 | wav_enc.c |
22 | wavpack_enc.c | 23 | wavpack_enc.c |
diff --git a/apps/codecs/aiff_enc.c b/apps/codecs/aiff_enc.c new file mode 100644 index 0000000000..50c682fa31 --- /dev/null +++ b/apps/codecs/aiff_enc.c | |||
@@ -0,0 +1,408 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Antonius Hellmann | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #ifndef SIMULATOR | ||
21 | |||
22 | #include <inttypes.h> | ||
23 | #include "codeclib.h" | ||
24 | |||
25 | CODEC_ENC_HEADER | ||
26 | |||
27 | #ifdef USE_IRAM | ||
28 | extern char iramcopy[]; | ||
29 | extern char iramstart[]; | ||
30 | extern char iramend[]; | ||
31 | extern char iedata[]; | ||
32 | extern char iend[]; | ||
33 | #endif | ||
34 | |||
35 | struct aiff_header | ||
36 | { | ||
37 | uint8_t form_id[4]; /* 00h - 'FORM' */ | ||
38 | uint32_t form_size; /* 04h - size of file - 8 */ | ||
39 | uint8_t aiff_id[4]; /* 08h - 'AIFF' */ | ||
40 | uint8_t comm_id[4]; /* 0Ch - 'COMM' */ | ||
41 | int32_t comm_size; /* 10h - num_channels through sample_rate | ||
42 | (18) */ | ||
43 | int16_t num_channels; /* 14h - 1=M, 2=S, etc. */ | ||
44 | uint32_t num_sample_frames; /* 16h - num samples for each channel */ | ||
45 | int16_t sample_size; /* 1ah - 1-32 bits per sample */ | ||
46 | uint8_t sample_rate[10]; /* 1ch - IEEE 754 80-bit floating point */ | ||
47 | uint8_t ssnd_id[4]; /* 26h - "SSND" */ | ||
48 | int32_t ssnd_size; /* 2ah - size of chunk from offset to | ||
49 | end of pcm data */ | ||
50 | uint32_t offset; /* 2eh - data offset from end of header */ | ||
51 | uint32_t block_size; /* 32h - pcm data alignment */ | ||
52 | /* 36h */ | ||
53 | } __attribute__((packed)); | ||
54 | |||
55 | #define PCM_DEPTH_BYTES 2 | ||
56 | #define PCM_DEPTH_BITS 16 | ||
57 | #define PCM_SAMP_PER_CHUNK 2048 | ||
58 | #define PCM_CHUNK_SIZE (PCM_SAMP_PER_CHUNK*4) | ||
59 | |||
60 | /* Template headers */ | ||
61 | struct aiff_header aiff_header = | ||
62 | { | ||
63 | { 'F', 'O', 'R', 'M' }, /* form_id */ | ||
64 | 0, /* form_size (*) */ | ||
65 | { 'A', 'I', 'F', 'F' }, /* aiff_id */ | ||
66 | { 'C', 'O', 'M', 'M' }, /* comm_id */ | ||
67 | H_TO_BE32(18), /* comm_size */ | ||
68 | 0, /* num_channels (*) */ | ||
69 | 0, /* num_sample_frames (*) */ | ||
70 | H_TO_BE32(PCM_DEPTH_BITS), /* sample_size */ | ||
71 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* sample_rate (*) */ | ||
72 | { 'S', 'S', 'N', 'D' }, /* ssnd_id */ | ||
73 | 0, /* ssnd_size (*) */ | ||
74 | H_TO_BE32(0), /* offset */ | ||
75 | H_TO_BE32(0), /* block_size */ | ||
76 | }; | ||
77 | |||
78 | /* (*) updated when finalizing file */ | ||
79 | |||
80 | static struct codec_api *ci; | ||
81 | static int num_channels; | ||
82 | uint32_t sample_rate; | ||
83 | uint32_t enc_size; | ||
84 | |||
85 | /* convert unsigned 32 bit value to 80-bit floating point number */ | ||
86 | static void uint32_to_ieee754_extended(uint8_t f[10], uint32_t l) ICODE_ATTR; | ||
87 | static void uint32_to_ieee754_extended(uint8_t f[10], uint32_t l) | ||
88 | { | ||
89 | int32_t exp; | ||
90 | |||
91 | ci->memset(f, 0, 10); | ||
92 | |||
93 | if (l == 0) | ||
94 | return; | ||
95 | |||
96 | for (exp = 30; (l & (1ul << 31)) == 0; exp--) | ||
97 | l <<= 1; | ||
98 | |||
99 | /* sign always zero - bit 79 */ | ||
100 | /* exponent is 0-31 (normalized: 31 - shift + 16383) - bits 64-78 */ | ||
101 | f[0] = 0x40; | ||
102 | f[1] = (uint8_t)exp; | ||
103 | /* mantissa is value left justified with most significant non-zero | ||
104 | bit stored in bit 63 - bits 0-63 */ | ||
105 | *(uint32_t *)&f[2] = htobe32(l); | ||
106 | } /* long_to_ieee754_extended */ | ||
107 | |||
108 | /* called version often - inline */ | ||
109 | static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR; | ||
110 | static inline bool is_file_data_ok(struct enc_file_event_data *data) | ||
111 | { | ||
112 | return data->rec_file >= 0 && (long)data->chunk->flags >= 0; | ||
113 | } /* is_file_data_ok */ | ||
114 | |||
115 | /* called version often - inline */ | ||
116 | static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR; | ||
117 | static inline bool on_write_chunk(struct enc_file_event_data *data) | ||
118 | { | ||
119 | if (!is_file_data_ok(data)) | ||
120 | return false; | ||
121 | |||
122 | if (data->chunk->enc_data == NULL) | ||
123 | { | ||
124 | #ifdef ROCKBOX_HAS_LOGF | ||
125 | ci->logf("aiff enc: NULL data"); | ||
126 | #endif | ||
127 | return true; | ||
128 | } | ||
129 | |||
130 | if (ci->write(data->rec_file, data->chunk->enc_data, | ||
131 | data->chunk->enc_size) != (ssize_t)data->chunk->enc_size) | ||
132 | return false; | ||
133 | |||
134 | data->num_pcm_samples += data->chunk->num_pcm; | ||
135 | return true; | ||
136 | } /* on_write_chunk */ | ||
137 | |||
138 | static bool on_start_file(struct enc_file_event_data *data) | ||
139 | { | ||
140 | if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0') | ||
141 | return false; | ||
142 | |||
143 | data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC); | ||
144 | |||
145 | if (data->rec_file < 0) | ||
146 | return false; | ||
147 | |||
148 | /* reset sample count */ | ||
149 | data->num_pcm_samples = 0; | ||
150 | |||
151 | /* write template headers */ | ||
152 | if (ci->write(data->rec_file, &aiff_header, sizeof (aiff_header)) | ||
153 | != sizeof (aiff_header)) | ||
154 | { | ||
155 | return false; | ||
156 | } | ||
157 | |||
158 | data->new_enc_size += sizeof(aiff_header); | ||
159 | return true; | ||
160 | } /* on_start_file */ | ||
161 | |||
162 | static bool on_end_file(struct enc_file_event_data *data) | ||
163 | { | ||
164 | /* update template headers */ | ||
165 | struct aiff_header hdr; | ||
166 | uint32_t data_size; | ||
167 | |||
168 | if (!is_file_data_ok(data)) | ||
169 | return false; | ||
170 | |||
171 | if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || | ||
172 | ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)) | ||
173 | { | ||
174 | return false; | ||
175 | } | ||
176 | |||
177 | data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES; | ||
178 | |||
179 | /* 'FORM' chunk */ | ||
180 | hdr.form_size = data_size + sizeof (hdr) - 8; | ||
181 | |||
182 | /* 'COMM' chunk */ | ||
183 | hdr.num_channels = htobe16(num_channels); | ||
184 | hdr.num_sample_frames = htobe32(data->num_pcm_samples*num_channels/2); | ||
185 | uint32_to_ieee754_extended(hdr.sample_rate, sample_rate); | ||
186 | |||
187 | /* 'SSND' chunk */ | ||
188 | hdr.ssnd_size = htobe32(data_size + 8); | ||
189 | |||
190 | if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || | ||
191 | ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)) | ||
192 | { | ||
193 | return false; | ||
194 | } | ||
195 | |||
196 | ci->fsync(data->rec_file); | ||
197 | ci->close(data->rec_file); | ||
198 | data->rec_file = -1; | ||
199 | |||
200 | return true; | ||
201 | } /* on_end_file */ | ||
202 | |||
203 | static void enc_events_callback(enum enc_events event, void *data) ICODE_ATTR; | ||
204 | static void enc_events_callback(enum enc_events event, void *data) | ||
205 | { | ||
206 | if (event == ENC_WRITE_CHUNK) | ||
207 | { | ||
208 | if (on_write_chunk((struct enc_file_event_data *)data)) | ||
209 | return; | ||
210 | } | ||
211 | else if (event == ENC_START_FILE) | ||
212 | { | ||
213 | if (on_start_file((struct enc_file_event_data *)data)) | ||
214 | return; | ||
215 | } | ||
216 | else if (event == ENC_END_FILE) | ||
217 | { | ||
218 | if (on_end_file((struct enc_file_event_data *)data)) | ||
219 | return; | ||
220 | } | ||
221 | else | ||
222 | { | ||
223 | return; | ||
224 | } | ||
225 | |||
226 | ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR; | ||
227 | } /* enc_events_callback */ | ||
228 | |||
229 | /* convert native pcm samples to aiff format samples */ | ||
230 | static void chunk_to_aiff_format(uint32_t *src, uint32_t *dst) ICODE_ATTR; | ||
231 | static void chunk_to_aiff_format(uint32_t *src, uint32_t *dst) | ||
232 | { | ||
233 | if (num_channels == 1) | ||
234 | { | ||
235 | /* On big endian: | ||
236 | * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| | ||
237 | * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => | ||
238 | * |MMMMMMMMmmmmmmmm|MMMMMMMMmmmmmmmm| | ||
239 | * | ||
240 | * On little endian: | ||
241 | * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| | ||
242 | * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => | ||
243 | * |MMMMMMMMmmmmmmmm|MMMMMMMMmmmmmmmm| | ||
244 | */ | ||
245 | uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; | ||
246 | |||
247 | inline void to_mono(uint32_t **src, uint32_t **dst) | ||
248 | { | ||
249 | int32_t lr1, lr2; | ||
250 | |||
251 | lr1 = *(*src)++; | ||
252 | lr1 = ((int16_t)lr1 + (lr1 >> 16)) >> 1; | ||
253 | |||
254 | lr2 = *(*src)++; | ||
255 | lr2 = ((int16_t)lr2 + (lr2 >> 16)) >> 1; | ||
256 | *(*dst)++ = swap_odd_even_le32((lr1 << 16) | (uint16_t)lr2); | ||
257 | } /* to_mono */ | ||
258 | |||
259 | do | ||
260 | { | ||
261 | to_mono(&src, &dst); | ||
262 | to_mono(&src, &dst); | ||
263 | to_mono(&src, &dst); | ||
264 | to_mono(&src, &dst); | ||
265 | to_mono(&src, &dst); | ||
266 | to_mono(&src, &dst); | ||
267 | to_mono(&src, &dst); | ||
268 | to_mono(&src, &dst); | ||
269 | } | ||
270 | while (src < src_end); | ||
271 | } | ||
272 | else | ||
273 | { | ||
274 | #ifdef ROCKBOX_BIG_ENDIAN | ||
275 | /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => | ||
276 | * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| | ||
277 | */ | ||
278 | ci->memcpy(dst, src, PCM_CHUNK_SIZE); | ||
279 | #else | ||
280 | /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => | ||
281 | * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| | ||
282 | */ | ||
283 | uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; | ||
284 | |||
285 | do | ||
286 | { | ||
287 | *dst++ = swap_odd_even32(*src++); | ||
288 | *dst++ = swap_odd_even32(*src++); | ||
289 | *dst++ = swap_odd_even32(*src++); | ||
290 | *dst++ = swap_odd_even32(*src++); | ||
291 | *dst++ = swap_odd_even32(*src++); | ||
292 | *dst++ = swap_odd_even32(*src++); | ||
293 | *dst++ = swap_odd_even32(*src++); | ||
294 | *dst++ = swap_odd_even32(*src++); | ||
295 | } | ||
296 | while (src < src_end); | ||
297 | #endif | ||
298 | } | ||
299 | } /* chunk_to_aiff_format */ | ||
300 | |||
301 | static bool init_encoder(void) | ||
302 | { | ||
303 | struct enc_inputs inputs; | ||
304 | struct enc_parameters params; | ||
305 | |||
306 | if (ci->enc_get_inputs == NULL || | ||
307 | ci->enc_set_parameters == NULL || | ||
308 | ci->enc_get_chunk == NULL || | ||
309 | ci->enc_finish_chunk == NULL || | ||
310 | ci->enc_pcm_buf_near_empty == NULL || | ||
311 | ci->enc_get_pcm_data == NULL ) | ||
312 | return false; | ||
313 | |||
314 | ci->enc_get_inputs(&inputs); | ||
315 | |||
316 | if (inputs.config->afmt != AFMT_AIFF) | ||
317 | return false; | ||
318 | |||
319 | sample_rate = inputs.sample_rate; | ||
320 | num_channels = inputs.num_channels; | ||
321 | |||
322 | /* configure the buffer system */ | ||
323 | params.afmt = AFMT_AIFF; | ||
324 | enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2; | ||
325 | params.chunk_size = enc_size; | ||
326 | params.enc_sample_rate = sample_rate; | ||
327 | params.reserve_bytes = 0; | ||
328 | params.events_callback = enc_events_callback; | ||
329 | ci->enc_set_parameters(¶ms); | ||
330 | |||
331 | return true; | ||
332 | } /* init_encoder */ | ||
333 | |||
334 | /* main codec entry point */ | ||
335 | enum codec_status codec_start(struct codec_api* api) | ||
336 | { | ||
337 | bool cpu_boosted; | ||
338 | |||
339 | ci = api; /* copy to global api pointer */ | ||
340 | |||
341 | #ifdef USE_IRAM | ||
342 | ci->memcpy(iramstart, iramcopy, iramend - iramstart); | ||
343 | ci->memset(iedata, 0, iend - iedata); | ||
344 | #endif | ||
345 | |||
346 | if (!init_encoder()) | ||
347 | { | ||
348 | ci->enc_codec_loaded = -1; | ||
349 | return CODEC_ERROR; | ||
350 | } | ||
351 | |||
352 | /* main application waits for this flag during encoder loading */ | ||
353 | ci->enc_codec_loaded = 1; | ||
354 | |||
355 | ci->cpu_boost(true); | ||
356 | cpu_boosted = true; | ||
357 | |||
358 | /* main encoding loop */ | ||
359 | while(!ci->stop_codec) | ||
360 | { | ||
361 | uint32_t *src; | ||
362 | |||
363 | while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL) | ||
364 | { | ||
365 | struct enc_chunk_hdr *chunk; | ||
366 | |||
367 | if (ci->stop_codec) | ||
368 | break; | ||
369 | |||
370 | if (!cpu_boosted && ci->enc_pcm_buf_near_empty() == 0) | ||
371 | { | ||
372 | ci->cpu_boost(true); | ||
373 | cpu_boosted = true; | ||
374 | } | ||
375 | |||
376 | chunk = ci->enc_get_chunk(); | ||
377 | chunk->enc_size = enc_size; | ||
378 | chunk->num_pcm = PCM_SAMP_PER_CHUNK; | ||
379 | chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); | ||
380 | |||
381 | chunk_to_aiff_format(src, (uint32_t *)chunk->enc_data); | ||
382 | |||
383 | ci->enc_finish_chunk(); | ||
384 | ci->yield(); | ||
385 | } | ||
386 | |||
387 | if (cpu_boosted && ci->enc_pcm_buf_near_empty() != 0) | ||
388 | { | ||
389 | ci->cpu_boost(false); | ||
390 | cpu_boosted = false; | ||
391 | } | ||
392 | |||
393 | ci->yield(); | ||
394 | } | ||
395 | |||
396 | if (cpu_boosted) /* set initial boost state */ | ||
397 | ci->cpu_boost(false); | ||
398 | |||
399 | /* reset parameters to initial state */ | ||
400 | ci->enc_set_parameters(NULL); | ||
401 | |||
402 | /* main application waits for this flag during encoder removing */ | ||
403 | ci->enc_codec_loaded = 0; | ||
404 | |||
405 | return CODEC_OK; | ||
406 | } /* codec_start */ | ||
407 | |||
408 | #endif /* ndef SIMULATOR */ | ||