Update the discussion of themeing in the manual, and put a note in the wps tags appen...
[kugel-rb.git] / apps / codecs / wav_enc.c
blob4cecb0b2b6337b34e7e94cf4287e025d7e59aa01
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 riff_header
31 uint8_t riff_id[4]; /* 00h - "RIFF" */
32 uint32_t riff_size; /* 04h - sz following headers + data_size */
33 /* format header */
34 uint8_t format[4]; /* 08h - "WAVE" */
35 uint8_t format_id[4]; /* 0Ch - "fmt " */
36 uint32_t format_size; /* 10h - 16 for PCM (sz format data) */
37 /* format data */
38 uint16_t audio_format; /* 14h - 1=PCM */
39 uint16_t num_channels; /* 16h - 1=M, 2=S, etc. */
40 uint32_t sample_rate; /* 18h - HZ */
41 uint32_t byte_rate; /* 1Ch - num_channels*sample_rate*bits_per_sample/8 */
42 uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */
43 uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */
44 /* Not for audio_format=1 (PCM) */
45 /* unsigned short extra_param_size; 24h - size of extra data */
46 /* unsigned char *extra_params; */
47 /* data header */
48 uint8_t data_id[4]; /* 24h - "data" */
49 uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */
50 /* unsigned char *data; 2ch - actual sound data */
51 } __attribute__((packed));
53 #define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */
54 #define RIFF_FMT_DATA_SIZE 16 /* audio_format -> bits_per_sample */
55 #define RIFF_DATA_HEADER_SIZE 8 /* data_id -> data_size */
57 #define PCM_DEPTH_BYTES 2
58 #define PCM_DEPTH_BITS 16
59 #define PCM_SAMP_PER_CHUNK 2048
60 #define PCM_CHUNK_SIZE (PCM_SAMP_PER_CHUNK*4)
62 static int num_channels IBSS_ATTR;
63 static int rec_mono_mode IBSS_ATTR;
64 static uint32_t sample_rate;
65 static uint32_t enc_size;
66 static int32_t err IBSS_ATTR;
68 static const struct riff_header riff_header =
70 /* "RIFF" header */
71 { 'R', 'I', 'F', 'F' }, /* riff_id */
72 0, /* riff_size (*) */
73 /* format header */
74 { 'W', 'A', 'V', 'E' }, /* format */
75 { 'f', 'm', 't', ' ' }, /* format_id */
76 H_TO_LE32(16), /* format_size */
77 /* format data */
78 H_TO_LE16(1), /* audio_format */
79 0, /* num_channels (*) */
80 0, /* sample_rate (*) */
81 0, /* byte_rate (*) */
82 0, /* block_align (*) */
83 H_TO_LE16(PCM_DEPTH_BITS), /* bits_per_sample */
84 /* data header */
85 { 'd', 'a', 't', 'a' }, /* data_id */
86 0 /* data_size (*) */
87 /* (*) updated during ENC_END_FILE event */
90 /* called version often - inline */
91 static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR;
92 static inline bool is_file_data_ok(struct enc_file_event_data *data)
94 return data->rec_file >= 0 && (long)data->chunk->flags >= 0;
95 } /* is_file_data_ok */
97 /* called version often - inline */
98 static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR;
99 static inline bool on_write_chunk(struct enc_file_event_data *data)
101 if (!is_file_data_ok(data))
102 return false;
104 if (data->chunk->enc_data == NULL)
106 #ifdef ROCKBOX_HAS_LOGF
107 ci->logf("wav enc: NULL data");
108 #endif
109 return true;
112 if (ci->write(data->rec_file, data->chunk->enc_data,
113 data->chunk->enc_size) != (ssize_t)data->chunk->enc_size)
114 return false;
116 data->num_pcm_samples += data->chunk->num_pcm;
117 return true;
118 } /* on_write_chunk */
120 static bool on_start_file(struct enc_file_event_data *data)
122 if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
123 return false;
125 data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
127 if (data->rec_file < 0)
128 return false;
130 /* reset sample count */
131 data->num_pcm_samples = 0;
133 /* write template header */
134 if (ci->write(data->rec_file, &riff_header, sizeof (riff_header))
135 != sizeof (riff_header))
137 return false;
140 data->new_enc_size += sizeof (riff_header);
141 return true;
142 } /* on_start_file */
144 static bool on_end_file(struct enc_file_event_data *data)
146 /* update template header */
147 struct riff_header hdr;
148 uint32_t data_size;
150 if (data->rec_file < 0)
151 return false; /* file already closed, nothing more we can do */
153 /* always _try_ to write the file header, even on error */
154 if ((ci->lseek(data->rec_file, 0, SEEK_SET)) ||
155 (ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)))
157 return false;
160 data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES;
162 /* "RIFF" header */
163 hdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE + RIFF_FMT_DATA_SIZE
164 + RIFF_DATA_HEADER_SIZE + data_size);
166 /* format data */
167 hdr.num_channels = htole16(num_channels);
168 hdr.sample_rate = htole32(sample_rate);
169 hdr.byte_rate = htole32(sample_rate*num_channels* PCM_DEPTH_BYTES);
170 hdr.block_align = htole16(num_channels*PCM_DEPTH_BYTES);
172 /* data header */
173 hdr.data_size = htole32(data_size);
175 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
176 ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr) ||
177 ci->close(data->rec_file) != 0)
179 return false;
182 data->rec_file = -1;
184 return true;
185 } /* on_end_file */
187 STATICIRAM void enc_events_callback(enum enc_events event, void *data)
188 ICODE_ATTR;
189 STATICIRAM void enc_events_callback(enum enc_events event, void *data)
191 switch (event)
193 case ENC_WRITE_CHUNK:
194 if (on_write_chunk((struct enc_file_event_data *)data))
195 return;
197 break;
199 case ENC_START_FILE:
200 if (on_start_file((struct enc_file_event_data *)data))
201 return;
203 break;
205 case ENC_END_FILE:
206 if (on_end_file((struct enc_file_event_data *)data))
207 return;
209 break;
211 default:
212 return;
215 /* Something failed above. Signal error back to core. */
216 ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
217 } /* enc_events_callback */
219 /* convert native pcm samples to wav format samples */
220 static inline void sample_to_mono(uint32_t **src, uint32_t **dst)
222 int32_t lr1, lr2;
224 switch(rec_mono_mode)
226 case 1:
227 /* mono = L */
228 lr1 = *(*src)++;
229 lr1 = lr1 >> 16;
230 lr2 = *(*src)++;
231 lr2 = lr2 >> 16;
232 break;
233 case 2:
234 /* mono = R */
235 lr1 = *(*src)++;
236 lr1 = (uint16_t)lr1;
237 lr2 = *(*src)++;
238 lr2 = (uint16_t)lr2;
239 break;
240 case 0:
241 default:
242 /* mono = (L+R)/2 */
243 lr1 = *(*src)++;
244 lr1 = (int16_t)lr1 + (lr1 >> 16) + err;
245 err = lr1 & 1;
246 lr1 >>= 1;
248 lr2 = *(*src)++;
249 lr2 = (int16_t)lr2 + (lr2 >> 16) + err;
250 err = lr2 & 1;
251 lr2 >>= 1;
252 break;
254 *(*dst)++ = htole32((lr2 << 16) | (uint16_t)lr1);
255 } /* sample_to_mono */
257 STATICIRAM void chunk_to_wav_format(uint32_t *src, uint32_t *dst) ICODE_ATTR;
258 STATICIRAM void chunk_to_wav_format(uint32_t *src, uint32_t *dst)
260 if (num_channels == 1)
262 /* On big endian:
263 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
264 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
265 * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM|
267 * On little endian:
268 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
269 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
270 * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM|
272 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
276 sample_to_mono(&src, &dst);
277 sample_to_mono(&src, &dst);
278 sample_to_mono(&src, &dst);
279 sample_to_mono(&src, &dst);
280 sample_to_mono(&src, &dst);
281 sample_to_mono(&src, &dst);
282 sample_to_mono(&src, &dst);
283 sample_to_mono(&src, &dst);
285 while (src < src_end);
287 else
289 #ifdef ROCKBOX_BIG_ENDIAN
290 /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
291 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
293 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
297 *dst++ = swap_odd_even32(*src++);
298 *dst++ = swap_odd_even32(*src++);
299 *dst++ = swap_odd_even32(*src++);
300 *dst++ = swap_odd_even32(*src++);
301 *dst++ = swap_odd_even32(*src++);
302 *dst++ = swap_odd_even32(*src++);
303 *dst++ = swap_odd_even32(*src++);
304 *dst++ = swap_odd_even32(*src++);
306 while (src < src_end);
307 #else
308 /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
309 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
311 ci->memcpy(dst, src, PCM_CHUNK_SIZE);
312 #endif
314 } /* chunk_to_wav_format */
316 static bool init_encoder(void)
318 struct enc_inputs inputs;
319 struct enc_parameters params;
321 if (ci->enc_get_inputs == NULL ||
322 ci->enc_set_parameters == NULL ||
323 ci->enc_get_chunk == NULL ||
324 ci->enc_finish_chunk == NULL ||
325 ci->enc_get_pcm_data == NULL )
326 return false;
328 ci->enc_get_inputs(&inputs);
330 if (inputs.config->afmt != AFMT_PCM_WAV)
331 return false;
333 sample_rate = inputs.sample_rate;
334 num_channels = inputs.num_channels;
335 rec_mono_mode = inputs.rec_mono_mode;
336 err = 0;
338 /* configure the buffer system */
339 params.afmt = AFMT_PCM_WAV;
340 enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2;
341 params.chunk_size = enc_size;
342 params.enc_sample_rate = sample_rate;
343 params.reserve_bytes = 0;
344 params.events_callback = enc_events_callback;
345 ci->enc_set_parameters(&params);
347 return true;
348 } /* init_encoder */
350 /* main codec entry point */
351 enum codec_status codec_main(void)
353 if (!init_encoder())
355 ci->enc_codec_loaded = -1;
356 return CODEC_ERROR;
359 /* main application waits for this flag during encoder loading */
360 ci->enc_codec_loaded = 1;
362 /* main encoding loop */
363 while(!ci->stop_encoder)
365 uint32_t *src;
367 while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
369 struct enc_chunk_hdr *chunk;
371 if (ci->stop_encoder)
372 break;
374 chunk = ci->enc_get_chunk();
375 chunk->enc_size = enc_size;
376 chunk->num_pcm = PCM_SAMP_PER_CHUNK;
377 chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
379 chunk_to_wav_format(src, (uint32_t *)chunk->enc_data);
381 ci->enc_finish_chunk();
382 ci->yield();
385 ci->yield();
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 */