blob: a59eff75819cc5b2e8e5627e35a2df476414ad94 [file] [log] [blame]
Michael Sevakis4fc717a2006-08-28 22:38:41 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Antonius Hellmann
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * 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.
Michael Sevakis4fc717a2006-08-28 22:38:41 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef SIMULATOR
23
Michael Sevakis0f5cb942006-11-06 18:07:30 +000024#include <inttypes.h>
Michael Sevakis4fc717a2006-08-28 22:38:41 +000025#include "codeclib.h"
26
Michael Sevakis0f5cb942006-11-06 18:07:30 +000027CODEC_ENC_HEADER
28
Michael Sevakis0f5cb942006-11-06 18:07:30 +000029struct riff_header
30{
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 */
Michael Sevakis6ae46062006-11-29 17:52:30 +000051} __attribute__((packed));
Michael Sevakis0f5cb942006-11-06 18:07:30 +000052
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 */
56
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)
Michael Sevakis4fc717a2006-08-28 22:38:41 +000061
Michael Sevakisd52f2e42007-02-09 18:11:11 +000062static int num_channels IBSS_ATTR;
63static uint32_t sample_rate;
64static uint32_t enc_size;
65static int32_t err IBSS_ATTR;
Michael Sevakis4fc717a2006-08-28 22:38:41 +000066
Michael Sevakis0f5cb942006-11-06 18:07:30 +000067static const struct riff_header riff_header =
Michael Sevakis4fc717a2006-08-28 22:38:41 +000068{
Michael Sevakis0f5cb942006-11-06 18:07:30 +000069 /* "RIFF" header */
70 { 'R', 'I', 'F', 'F' }, /* riff_id */
71 0, /* riff_size (*) */
72 /* format header */
73 { 'W', 'A', 'V', 'E' }, /* format */
74 { 'f', 'm', 't', ' ' }, /* format_id */
75 H_TO_LE32(16), /* format_size */
76 /* format data */
77 H_TO_LE16(1), /* audio_format */
78 0, /* num_channels (*) */
79 0, /* sample_rate (*) */
80 0, /* byte_rate (*) */
81 0, /* block_align (*) */
82 H_TO_LE16(PCM_DEPTH_BITS), /* bits_per_sample */
83 /* data header */
84 { 'd', 'a', 't', 'a' }, /* data_id */
85 0 /* data_size (*) */
86 /* (*) updated during ENC_END_FILE event */
87};
Michael Sevakis4fc717a2006-08-28 22:38:41 +000088
Michael Sevakis0f5cb942006-11-06 18:07:30 +000089/* called version often - inline */
90static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR;
91static inline bool is_file_data_ok(struct enc_file_event_data *data)
92{
93 return data->rec_file >= 0 && (long)data->chunk->flags >= 0;
94} /* is_file_data_ok */
95
96/* called version often - inline */
97static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR;
98static inline bool on_write_chunk(struct enc_file_event_data *data)
99{
100 if (!is_file_data_ok(data))
101 return false;
102
103 if (data->chunk->enc_data == NULL)
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000104 {
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000105#ifdef ROCKBOX_HAS_LOGF
106 ci->logf("wav enc: NULL data");
107#endif
108 return true;
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000109 }
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000110
111 if (ci->write(data->rec_file, data->chunk->enc_data,
112 data->chunk->enc_size) != (ssize_t)data->chunk->enc_size)
113 return false;
114
115 data->num_pcm_samples += data->chunk->num_pcm;
116 return true;
117} /* on_write_chunk */
118
119static bool on_start_file(struct enc_file_event_data *data)
120{
121 if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
122 return false;
123
124 data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC);
125
126 if (data->rec_file < 0)
127 return false;
128
129 /* reset sample count */
130 data->num_pcm_samples = 0;
131
132 /* write template header */
133 if (ci->write(data->rec_file, &riff_header, sizeof (riff_header))
134 != sizeof (riff_header))
135 {
136 return false;
137 }
138
139 data->new_enc_size += sizeof (riff_header);
140 return true;
141} /* on_start_file */
142
143static bool on_end_file(struct enc_file_event_data *data)
144{
145 /* update template header */
146 struct riff_header hdr;
147 uint32_t data_size;
148
Peter D'Hoyeeb947cd2007-12-13 23:36:22 +0000149 if (data->rec_file < 0)
150 return false; /* file already closed, nothing more we can do */
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000151
Peter D'Hoyeeb947cd2007-12-13 23:36:22 +0000152 /* always _try_ to write the file header, even on error */
153 if ((ci->lseek(data->rec_file, 0, SEEK_SET)) ||
154 (ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)))
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000155 {
156 return false;
157 }
158
159 data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES;
160
161 /* "RIFF" header */
162 hdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE + RIFF_FMT_DATA_SIZE
163 + RIFF_DATA_HEADER_SIZE + data_size);
164
165 /* format data */
166 hdr.num_channels = htole16(num_channels);
167 hdr.sample_rate = htole32(sample_rate);
168 hdr.byte_rate = htole32(sample_rate*num_channels* PCM_DEPTH_BYTES);
169 hdr.block_align = htole16(num_channels*PCM_DEPTH_BYTES);
170
171 /* data header */
172 hdr.data_size = htole32(data_size);
173
174 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
Michael Sevakis205ec322007-04-25 19:53:34 +0000175 ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr) ||
176 ci->close(data->rec_file) != 0)
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000177 {
178 return false;
179 }
180
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000181 data->rec_file = -1;
182
183 return true;
184} /* on_end_file */
185
Jens Arnold471d8812007-02-17 11:19:14 +0000186STATICIRAM void enc_events_callback(enum enc_events event, void *data)
187 ICODE_ATTR;
188STATICIRAM void enc_events_callback(enum enc_events event, void *data)
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000189{
190 if (event == ENC_WRITE_CHUNK)
191 {
192 if (on_write_chunk((struct enc_file_event_data *)data))
193 return;
194 }
195 else if (event == ENC_START_FILE)
196 {
197 if (on_start_file((struct enc_file_event_data *)data))
198 return;
199 }
200 else if (event == ENC_END_FILE)
201 {
202 if (on_end_file((struct enc_file_event_data *)data))
203 return;
204 }
205 else
206 {
207 return;
208 }
209
210 ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
211} /* enc_events_callback */
212
213/* convert native pcm samples to wav format samples */
Michael Sevakis62e0a512007-03-29 01:55:47 +0000214static inline void sample_to_mono(uint32_t **src, uint32_t **dst)
215{
216 int32_t lr1, lr2;
217
218 lr1 = *(*src)++;
219 lr1 = (int16_t)lr1 + (lr1 >> 16) + err;
220 err = lr1 & 1;
221 lr1 >>= 1;
222
223 lr2 = *(*src)++;
224 lr2 = (int16_t)lr2 + (lr2 >> 16) + err;
225 err = lr2 & 1;
226 lr2 >>= 1;
227 *(*dst)++ = swap_odd_even_be32((lr1 << 16) | (uint16_t)lr2);
228} /* sample_to_mono */
229
Jens Arnold471d8812007-02-17 11:19:14 +0000230STATICIRAM void chunk_to_wav_format(uint32_t *src, uint32_t *dst) ICODE_ATTR;
231STATICIRAM void chunk_to_wav_format(uint32_t *src, uint32_t *dst)
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000232{
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
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000247 do
248 {
Michael Sevakis62e0a512007-03-29 01:55:47 +0000249 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);
254 sample_to_mono(&src, &dst);
255 sample_to_mono(&src, &dst);
256 sample_to_mono(&src, &dst);
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000257 }
258 while (src < src_end);
259 }
260 else
261 {
262#ifdef ROCKBOX_BIG_ENDIAN
263 /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
264 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
265 */
266 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
267
268 do
269 {
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++);
275 *dst++ = swap_odd_even32(*src++);
276 *dst++ = swap_odd_even32(*src++);
277 *dst++ = swap_odd_even32(*src++);
278 }
279 while (src < src_end);
280#else
281 /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
282 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
283 */
284 ci->memcpy(dst, src, PCM_CHUNK_SIZE);
285#endif
286 }
287} /* chunk_to_wav_format */
288
289static bool init_encoder(void)
290{
291 struct enc_inputs inputs;
292 struct enc_parameters params;
293
294 if (ci->enc_get_inputs == NULL ||
295 ci->enc_set_parameters == NULL ||
296 ci->enc_get_chunk == NULL ||
297 ci->enc_finish_chunk == NULL ||
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000298 ci->enc_get_pcm_data == NULL )
299 return false;
300
301 ci->enc_get_inputs(&inputs);
302
303 if (inputs.config->afmt != AFMT_PCM_WAV)
304 return false;
305
306 sample_rate = inputs.sample_rate;
307 num_channels = inputs.num_channels;
Michael Sevakisd52f2e42007-02-09 18:11:11 +0000308 err = 0;
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000309
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);
318
319 return true;
320} /* init_encoder */
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000321
322/* main codec entry point */
Tomasz Malesinski80da8b12006-11-26 18:31:41 +0000323enum codec_status codec_main(void)
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000324{
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000325 if (!init_encoder())
326 {
327 ci->enc_codec_loaded = -1;
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000328 return CODEC_ERROR;
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000329 }
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000330
331 /* main application waits for this flag during encoder loading */
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000332 ci->enc_codec_loaded = 1;
333
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000334 /* main encoding loop */
Michael Sevakis598629c2007-03-04 04:16:53 +0000335 while(!ci->stop_encoder)
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000336 {
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000337 uint32_t *src;
338
339 while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000340 {
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000341 struct enc_chunk_hdr *chunk;
342
Michael Sevakis598629c2007-03-04 04:16:53 +0000343 if (ci->stop_encoder)
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000344 break;
345
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000346 chunk = ci->enc_get_chunk();
347 chunk->enc_size = enc_size;
348 chunk->num_pcm = PCM_SAMP_PER_CHUNK;
349 chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000350
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000351 chunk_to_wav_format(src, (uint32_t *)chunk->enc_data);
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000352
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000353 ci->enc_finish_chunk();
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000354 ci->yield();
355 }
356
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000357 ci->yield();
358 }
359
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000360 /* reset parameters to initial state */
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000361 ci->enc_set_parameters(NULL);
Peter D'Hoye85058f52007-10-10 23:26:17 +0000362
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000363 /* main application waits for this flag during encoder removing */
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000364 ci->enc_codec_loaded = 0;
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000365
366 return CODEC_OK;
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000367} /* codec_start */
368
369#endif /* ndef SIMULATOR */