Redo my previous commit. Highscore is only shown if there is room on the top line...
[Rockbox.git] / apps / codecs / wav_enc.c
bloba11aaa07d69037d679abbeb7fb03c80655ec13f2
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 Antonius Hellmann
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #ifndef SIMULATOR
22 #include <inttypes.h>
23 #include "codeclib.h"
25 CODEC_ENC_HEADER
27 struct riff_header
29 uint8_t riff_id[4]; /* 00h - "RIFF" */
30 uint32_t riff_size; /* 04h - sz following headers + data_size */
31 /* format header */
32 uint8_t format[4]; /* 08h - "WAVE" */
33 uint8_t format_id[4]; /* 0Ch - "fmt " */
34 uint32_t format_size; /* 10h - 16 for PCM (sz format data) */
35 /* format data */
36 uint16_t audio_format; /* 14h - 1=PCM */
37 uint16_t num_channels; /* 16h - 1=M, 2=S, etc. */
38 uint32_t sample_rate; /* 18h - HZ */
39 uint32_t byte_rate; /* 1Ch - num_channels*sample_rate*bits_per_sample/8 */
40 uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */
41 uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */
42 /* Not for audio_format=1 (PCM) */
43 /* unsigned short extra_param_size; 24h - size of extra data */
44 /* unsigned char *extra_params; */
45 /* data header */
46 uint8_t data_id[4]; /* 24h - "data" */
47 uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */
48 /* unsigned char *data; 2ch - actual sound data */
49 } __attribute__((packed));
51 #define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */
52 #define RIFF_FMT_DATA_SIZE 16 /* audio_format -> bits_per_sample */
53 #define RIFF_DATA_HEADER_SIZE 8 /* data_id -> data_size */
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)
60 static int num_channels IBSS_ATTR;
61 static uint32_t sample_rate;
62 static uint32_t enc_size;
63 static int32_t err IBSS_ATTR;
65 static const struct riff_header riff_header =
67 /* "RIFF" header */
68 { 'R', 'I', 'F', 'F' }, /* riff_id */
69 0, /* riff_size (*) */
70 /* format header */
71 { 'W', 'A', 'V', 'E' }, /* format */
72 { 'f', 'm', 't', ' ' }, /* format_id */
73 H_TO_LE32(16), /* format_size */
74 /* format data */
75 H_TO_LE16(1), /* audio_format */
76 0, /* num_channels (*) */
77 0, /* sample_rate (*) */
78 0, /* byte_rate (*) */
79 0, /* block_align (*) */
80 H_TO_LE16(PCM_DEPTH_BITS), /* bits_per_sample */
81 /* data header */
82 { 'd', 'a', 't', 'a' }, /* data_id */
83 0 /* data_size (*) */
84 /* (*) updated during ENC_END_FILE event */
87 /* called version often - inline */
88 static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR;
89 static inline bool is_file_data_ok(struct enc_file_event_data *data)
91 return data->rec_file >= 0 && (long)data->chunk->flags >= 0;
92 } /* is_file_data_ok */
94 /* called version often - inline */
95 static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR;
96 static inline bool on_write_chunk(struct enc_file_event_data *data)
98 if (!is_file_data_ok(data))
99 return false;
101 if (data->chunk->enc_data == NULL)
103 #ifdef ROCKBOX_HAS_LOGF
104 ci->logf("wav enc: NULL data");
105 #endif
106 return true;
109 if (ci->write(data->rec_file, data->chunk->enc_data,
110 data->chunk->enc_size) != (ssize_t)data->chunk->enc_size)
111 return false;
113 data->num_pcm_samples += data->chunk->num_pcm;
114 return true;
115 } /* on_write_chunk */
117 static bool on_start_file(struct enc_file_event_data *data)
119 if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
120 return false;
122 data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC);
124 if (data->rec_file < 0)
125 return false;
127 /* reset sample count */
128 data->num_pcm_samples = 0;
130 /* write template header */
131 if (ci->write(data->rec_file, &riff_header, sizeof (riff_header))
132 != sizeof (riff_header))
134 return false;
137 data->new_enc_size += sizeof (riff_header);
138 return true;
139 } /* on_start_file */
141 static bool on_end_file(struct enc_file_event_data *data)
143 /* update template header */
144 struct riff_header hdr;
145 uint32_t data_size;
147 if (!is_file_data_ok(data))
148 return false;
150 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
151 ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr))
153 return false;
156 data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES;
158 /* "RIFF" header */
159 hdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE + RIFF_FMT_DATA_SIZE
160 + RIFF_DATA_HEADER_SIZE + data_size);
162 /* format data */
163 hdr.num_channels = htole16(num_channels);
164 hdr.sample_rate = htole32(sample_rate);
165 hdr.byte_rate = htole32(sample_rate*num_channels* PCM_DEPTH_BYTES);
166 hdr.block_align = htole16(num_channels*PCM_DEPTH_BYTES);
168 /* data header */
169 hdr.data_size = htole32(data_size);
171 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
172 ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr) ||
173 ci->close(data->rec_file) != 0)
175 return false;
178 data->rec_file = -1;
180 return true;
181 } /* on_end_file */
183 STATICIRAM void enc_events_callback(enum enc_events event, void *data)
184 ICODE_ATTR;
185 STATICIRAM void enc_events_callback(enum enc_events event, void *data)
187 if (event == ENC_WRITE_CHUNK)
189 if (on_write_chunk((struct enc_file_event_data *)data))
190 return;
192 else if (event == ENC_START_FILE)
194 if (on_start_file((struct enc_file_event_data *)data))
195 return;
197 else if (event == ENC_END_FILE)
199 if (on_end_file((struct enc_file_event_data *)data))
200 return;
202 else
204 return;
207 ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
208 } /* enc_events_callback */
210 /* convert native pcm samples to wav format samples */
211 static inline void sample_to_mono(uint32_t **src, uint32_t **dst)
213 int32_t lr1, lr2;
215 lr1 = *(*src)++;
216 lr1 = (int16_t)lr1 + (lr1 >> 16) + err;
217 err = lr1 & 1;
218 lr1 >>= 1;
220 lr2 = *(*src)++;
221 lr2 = (int16_t)lr2 + (lr2 >> 16) + err;
222 err = lr2 & 1;
223 lr2 >>= 1;
224 *(*dst)++ = swap_odd_even_be32((lr1 << 16) | (uint16_t)lr2);
225 } /* sample_to_mono */
227 STATICIRAM void chunk_to_wav_format(uint32_t *src, uint32_t *dst) ICODE_ATTR;
228 STATICIRAM void chunk_to_wav_format(uint32_t *src, uint32_t *dst)
230 if (num_channels == 1)
232 /* On big endian:
233 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
234 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
235 * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM|
237 * On little endian:
238 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
239 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
240 * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM|
242 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
246 sample_to_mono(&src, &dst);
247 sample_to_mono(&src, &dst);
248 sample_to_mono(&src, &dst);
249 sample_to_mono(&src, &dst);
250 sample_to_mono(&src, &dst);
251 sample_to_mono(&src, &dst);
252 sample_to_mono(&src, &dst);
253 sample_to_mono(&src, &dst);
255 while (src < src_end);
257 else
259 #ifdef ROCKBOX_BIG_ENDIAN
260 /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
261 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
263 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
267 *dst++ = swap_odd_even32(*src++);
268 *dst++ = swap_odd_even32(*src++);
269 *dst++ = swap_odd_even32(*src++);
270 *dst++ = swap_odd_even32(*src++);
271 *dst++ = swap_odd_even32(*src++);
272 *dst++ = swap_odd_even32(*src++);
273 *dst++ = swap_odd_even32(*src++);
274 *dst++ = swap_odd_even32(*src++);
276 while (src < src_end);
277 #else
278 /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
279 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
281 ci->memcpy(dst, src, PCM_CHUNK_SIZE);
282 #endif
284 } /* chunk_to_wav_format */
286 static bool init_encoder(void)
288 struct enc_inputs inputs;
289 struct enc_parameters params;
291 if (ci->enc_get_inputs == NULL ||
292 ci->enc_set_parameters == NULL ||
293 ci->enc_get_chunk == NULL ||
294 ci->enc_finish_chunk == NULL ||
295 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
296 ci->enc_pcm_buf_near_empty == NULL ||
297 #endif
298 ci->enc_get_pcm_data == NULL )
299 return false;
301 ci->enc_get_inputs(&inputs);
303 if (inputs.config->afmt != AFMT_PCM_WAV)
304 return false;
306 sample_rate = inputs.sample_rate;
307 num_channels = inputs.num_channels;
308 err = 0;
310 /* configure the buffer system */
311 params.afmt = AFMT_PCM_WAV;
312 enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2;
313 params.chunk_size = enc_size;
314 params.enc_sample_rate = sample_rate;
315 params.reserve_bytes = 0;
316 params.events_callback = enc_events_callback;
317 ci->enc_set_parameters(&params);
319 return true;
320 } /* init_encoder */
322 /* main codec entry point */
323 enum codec_status codec_main(void)
325 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
326 bool cpu_boosted;
327 #endif
329 if (!init_encoder())
331 ci->enc_codec_loaded = -1;
332 return CODEC_ERROR;
335 /* main application waits for this flag during encoder loading */
336 ci->enc_codec_loaded = 1;
338 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
339 ci->cpu_boost(true);
340 cpu_boosted = true;
341 #endif
343 /* main encoding loop */
344 while(!ci->stop_encoder)
346 uint32_t *src;
348 while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
350 struct enc_chunk_hdr *chunk;
352 if (ci->stop_encoder)
353 break;
355 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
356 if (!cpu_boosted && ci->enc_pcm_buf_near_empty() == 0)
358 ci->cpu_boost(true);
359 cpu_boosted = true;
361 #endif
362 chunk = ci->enc_get_chunk();
363 chunk->enc_size = enc_size;
364 chunk->num_pcm = PCM_SAMP_PER_CHUNK;
365 chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
367 chunk_to_wav_format(src, (uint32_t *)chunk->enc_data);
369 ci->enc_finish_chunk();
370 ci->yield();
373 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
374 if (cpu_boosted && ci->enc_pcm_buf_near_empty() != 0)
376 ci->cpu_boost(false);
377 cpu_boosted = false;
379 #endif
380 ci->yield();
383 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
384 if (cpu_boosted) /* set initial boost state */
385 ci->cpu_boost(false);
386 #endif
388 /* reset parameters to initial state */
389 ci->enc_set_parameters(NULL);
391 /* main application waits for this flag during encoder removing */
392 ci->enc_codec_loaded = 0;
394 return CODEC_OK;
395 } /* codec_start */
397 #endif /* ndef SIMULATOR */