summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/wav_enc.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/wav_enc.c')
-rw-r--r--lib/rbcodec/codecs/wav_enc.c376
1 files changed, 117 insertions, 259 deletions
diff --git a/lib/rbcodec/codecs/wav_enc.c b/lib/rbcodec/codecs/wav_enc.c
index 01d0f79bcf..71bb652374 100644
--- a/lib/rbcodec/codecs/wav_enc.c
+++ b/lib/rbcodec/codecs/wav_enc.c
@@ -8,6 +8,7 @@
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2006 Antonius Hellmann 10 * Copyright (C) 2006 Antonius Hellmann
11 * Copyright (C) 2006-2013 Michael Sevakis
11 * 12 *
12 * This program is free software; you can redistribute it and/or 13 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 14 * modify it under the terms of the GNU General Public License
@@ -40,12 +41,12 @@ struct riff_header
40 uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */ 41 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 uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */
42 /* Not for audio_format=1 (PCM) */ 43 /* Not for audio_format=1 (PCM) */
43/* unsigned short extra_param_size; 24h - size of extra data */ 44/* uint16_t extra_param_size; 24h - size of extra data */
44/* unsigned char *extra_params; */ 45/* uint8_t extra_params[extra_param_size]; */
45 /* data header */ 46 /* data header */
46 uint8_t data_id[4]; /* 24h - "data" */ 47 uint8_t data_id[4]; /* 24h - "data" */
47 uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */ 48 uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */
48/* unsigned char *data; 2ch - actual sound data */ 49/* uint8_t data[data_size]; 2Ch - actual sound data */
49} __attribute__((packed)); 50} __attribute__((packed));
50 51
51#define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */ 52#define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */
@@ -55,19 +56,17 @@ struct riff_header
55#define PCM_DEPTH_BYTES 2 56#define PCM_DEPTH_BYTES 2
56#define PCM_DEPTH_BITS 16 57#define PCM_DEPTH_BITS 16
57#define PCM_SAMP_PER_CHUNK 2048 58#define PCM_SAMP_PER_CHUNK 2048
58#define PCM_CHUNK_SIZE (PCM_SAMP_PER_CHUNK*4)
59 59
60static int num_channels IBSS_ATTR; 60static int num_channels;
61static int rec_mono_mode IBSS_ATTR;
62static uint32_t sample_rate; 61static uint32_t sample_rate;
63static uint32_t enc_size; 62static size_t frame_size;
64static int32_t err IBSS_ATTR; 63static size_t data_size;
65 64
66static const struct riff_header riff_header = 65static const struct riff_header riff_template_header =
67{ 66{
68 /* "RIFF" header */ 67 /* "RIFF" header */
69 { 'R', 'I', 'F', 'F' }, /* riff_id */ 68 { 'R', 'I', 'F', 'F' }, /* riff_id */
70 0, /* riff_size (*) */ 69 0, /* riff_size (*) */
71 /* format header */ 70 /* format header */
72 { 'W', 'A', 'V', 'E' }, /* format */ 71 { 'W', 'A', 'V', 'E' }, /* format */
73 { 'f', 'm', 't', ' ' }, /* format_id */ 72 { 'f', 'm', 't', ' ' }, /* format_id */
@@ -82,305 +81,164 @@ static const struct riff_header riff_header =
82 /* data header */ 81 /* data header */
83 { 'd', 'a', 't', 'a' }, /* data_id */ 82 { 'd', 'a', 't', 'a' }, /* data_id */
84 0 /* data_size (*) */ 83 0 /* data_size (*) */
85 /* (*) updated during ENC_END_FILE event */ 84 /* (*) updated when finalizing stream */
86}; 85};
87 86
88/* called version often - inline */ 87static inline void frame_htole(uint32_t *p, size_t size)
89static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR;
90static inline bool is_file_data_ok(struct enc_file_event_data *data)
91{ 88{
92 return data->rec_file >= 0 && (long)data->chunk->flags >= 0; 89#ifdef ROCKBOX_BIG_ENDIAN
93} /* is_file_data_ok */ 90 /* Byte-swap samples, stereo or mono */
94 91 do
95/* called version often - inline */
96static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR;
97static inline bool on_write_chunk(struct enc_file_event_data *data)
98{
99 if (!is_file_data_ok(data))
100 return false;
101
102 if (data->chunk->enc_data == NULL)
103 { 92 {
104#ifdef ROCKBOX_HAS_LOGF 93 uint32_t t;
105 ci->logf("wav enc: NULL data"); 94 t = swap_odd_even32(*p); *p++ = t;
106#endif 95 t = swap_odd_even32(*p); *p++ = t;
107 return true; 96 t = swap_odd_even32(*p); *p++ = t;
97 t = swap_odd_even32(*p); *p++ = t;
98 t = swap_odd_even32(*p); *p++ = t;
99 t = swap_odd_even32(*p); *p++ = t;
100 t = swap_odd_even32(*p); *p++ = t;
101 t = swap_odd_even32(*p); *p++ = t;
108 } 102 }
103 while (size -= 8 * 2 * PCM_DEPTH_BYTES);
104#endif /* ROCKBOX_BIG_ENDIAN */
105 (void)p; (void)size;
106}
109 107
110 if (ci->write(data->rec_file, data->chunk->enc_data, 108static int on_stream_data(struct enc_chunk_data *data)
111 data->chunk->enc_size) != (ssize_t)data->chunk->enc_size)
112 return false;
113
114 data->num_pcm_samples += data->chunk->num_pcm;
115 return true;
116} /* on_write_chunk */
117
118static bool on_start_file(struct enc_file_event_data *data)
119{ 109{
120 if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0') 110 size_t size = data->hdr.size;
121 return false; 111
112 if (ci->enc_stream_write(data->data, size) != (ssize_t)size)
113 return -1;
122 114
123 data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666); 115 data_size += size;
124 116
125 if (data->rec_file < 0) 117 return 0;
126 return false; 118}
127 119
120static int on_stream_start(void)
121{
128 /* reset sample count */ 122 /* reset sample count */
129 data->num_pcm_samples = 0; 123 data_size = 0;
130 124
131 /* write template header */ 125 /* write template header */
132 if (ci->write(data->rec_file, &riff_header, sizeof (riff_header)) 126 if (ci->enc_stream_write(&riff_template_header, sizeof (struct riff_header))
133 != sizeof (riff_header)) 127 != sizeof (struct riff_header))
134 { 128 return -1;
135 return false;
136 }
137 129
138 data->new_enc_size += sizeof (riff_header); 130 return 0;
139 return true; 131}
140} /* on_start_file */
141 132
142static bool on_end_file(struct enc_file_event_data *data) 133static int on_stream_end(union enc_chunk_hdr *hdr)
143{ 134{
144 /* update template header */ 135 /* update template header */
145 struct riff_header hdr; 136 struct riff_header riff;
146 uint32_t data_size;
147
148 if (data->rec_file < 0)
149 return false; /* file already closed, nothing more we can do */
150 137
151 /* always _try_ to write the file header, even on error */ 138 if (hdr->err)
152 if ((ci->lseek(data->rec_file, 0, SEEK_SET)) ||
153 (ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)))
154 { 139 {
155 return false; 140 /* Called for stream error; get correct data size */
141 ssize_t size = ci->enc_stream_lseek(0, SEEK_END);
142
143 if (size > (ssize_t)sizeof (riff))
144 data_size = size - sizeof (riff);
156 } 145 }
157 146
158 data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES; 147 if (ci->enc_stream_lseek(0, SEEK_SET) ||
148 ci->enc_stream_read(&riff, sizeof (riff)) != sizeof (riff))
149 return -1;
159 150
160 /* "RIFF" header */ 151 /* "RIFF" header */
161 hdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE + RIFF_FMT_DATA_SIZE 152 riff.riff_size = htole32(RIFF_FMT_HEADER_SIZE + RIFF_FMT_DATA_SIZE
162 + RIFF_DATA_HEADER_SIZE + data_size); 153 + RIFF_DATA_HEADER_SIZE + data_size);
163 154
164 /* format data */ 155 /* format data */
165 hdr.num_channels = htole16(num_channels); 156 riff.num_channels = htole16(num_channels);
166 hdr.sample_rate = htole32(sample_rate); 157 riff.sample_rate = htole32(sample_rate);
167 hdr.byte_rate = htole32(sample_rate*num_channels* PCM_DEPTH_BYTES); 158 riff.byte_rate = htole32(sample_rate*num_channels*PCM_DEPTH_BYTES);
168 hdr.block_align = htole16(num_channels*PCM_DEPTH_BYTES); 159 riff.block_align = htole16(num_channels*PCM_DEPTH_BYTES);
169 160
170 /* data header */ 161 /* data header */
171 hdr.data_size = htole32(data_size); 162 riff.data_size = htole32(data_size);
172 163
173 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || 164 if (ci->enc_stream_lseek(0, SEEK_SET) != 0)
174 ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr) || 165 return -2;
175 ci->close(data->rec_file) != 0)
176 {
177 return false;
178 }
179 166
180 data->rec_file = -1; 167 if (ci->enc_stream_write(&riff, sizeof (riff)) != sizeof (riff))
168 return -3;
181 169
182 return true; 170 return 0;
183} /* on_end_file */ 171}
184 172
185static void enc_events_callback(enum enc_events event, void *data) 173/* this is the codec entry point */
186 ICODE_ATTR; 174enum codec_status codec_main(enum codec_entry_call_reason reason)
187static void enc_events_callback(enum enc_events event, void *data)
188{ 175{
189 switch (event) 176 return CODEC_OK;
190 { 177 (void)reason;
191 case ENC_WRITE_CHUNK: 178}
192 if (on_write_chunk((struct enc_file_event_data *)data))
193 return;
194
195 break;
196
197 case ENC_START_FILE:
198 if (on_start_file((struct enc_file_event_data *)data))
199 return;
200
201 break;
202
203 case ENC_END_FILE:
204 if (on_end_file((struct enc_file_event_data *)data))
205 return;
206
207 break;
208
209 default:
210 return;
211 }
212
213 /* Something failed above. Signal error back to core. */
214 ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
215} /* enc_events_callback */
216 179
217/* convert native pcm samples to wav format samples */ 180/* this is called for each file to process */
218static inline void sample_to_mono(uint32_t **src, uint32_t **dst) 181enum codec_status ICODE_ATTR codec_run(void)
219{ 182{
220 int32_t lr1, lr2; 183 enum { GETBUF_ENC, GETBUF_PCM } getbuf = GETBUF_ENC;
184 struct enc_chunk_data *data = NULL;
221 185
222 switch(rec_mono_mode) 186 /* main encoding loop */
187 while (1)
223 { 188 {
224 case 1: 189 enum codec_command_action action = ci->get_command(NULL);
225 /* mono = L */ 190
226 lr1 = *(*src)++; 191 if (action != CODEC_ACTION_NULL)
227 lr1 = lr1 >> 16;
228 lr2 = *(*src)++;
229 lr2 = lr2 >> 16;
230 break;
231 case 2:
232 /* mono = R */
233 lr1 = *(*src)++;
234 lr1 = (uint16_t)lr1;
235 lr2 = *(*src)++;
236 lr2 = (uint16_t)lr2;
237 break;
238 case 0:
239 default:
240 /* mono = (L+R)/2 */
241 lr1 = *(*src)++;
242 lr1 = (int16_t)lr1 + (lr1 >> 16) + err;
243 err = lr1 & 1;
244 lr1 >>= 1;
245
246 lr2 = *(*src)++;
247 lr2 = (int16_t)lr2 + (lr2 >> 16) + err;
248 err = lr2 & 1;
249 lr2 >>= 1;
250 break; 192 break;
251 }
252 *(*dst)++ = htole32((lr2 << 16) | (uint16_t)lr1);
253} /* sample_to_mono */
254 193
255static void chunk_to_wav_format(uint32_t *src, uint32_t *dst) ICODE_ATTR; 194 /* First obtain output buffer; when available, get PCM data */
256static void chunk_to_wav_format(uint32_t *src, uint32_t *dst) 195 switch (getbuf)
257{
258 if (num_channels == 1)
259 {
260 /* On big endian:
261 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
262 * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
263 * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM|
264 *
265 * On little endian:
266 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
267 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
268 * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM|
269 */
270 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
271
272 do
273 { 196 {
274 sample_to_mono(&src, &dst); 197 case GETBUF_ENC:
275 sample_to_mono(&src, &dst); 198 if (!(data = ci->enc_encbuf_get_buffer(frame_size)))
276 sample_to_mono(&src, &dst); 199 continue;
277 sample_to_mono(&src, &dst); 200 getbuf = GETBUF_PCM;
278 sample_to_mono(&src, &dst); 201 case GETBUF_PCM:
279 sample_to_mono(&src, &dst); 202 if (!ci->enc_pcmbuf_read(data->data, PCM_SAMP_PER_CHUNK))
280 sample_to_mono(&src, &dst); 203 continue;
281 sample_to_mono(&src, &dst); 204 getbuf = GETBUF_ENC;
282 } 205 }
283 while (src < src_end);
284 }
285 else
286 {
287#ifdef ROCKBOX_BIG_ENDIAN
288 /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
289 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
290 */
291 uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
292 206
293 do 207 data->hdr.size = frame_size;
294 { 208 data->pcm_count = PCM_SAMP_PER_CHUNK;
295 *dst++ = swap_odd_even32(*src++);
296 *dst++ = swap_odd_even32(*src++);
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 }
304 while (src < src_end);
305#else
306 /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
307 * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR|
308 */
309 ci->memcpy(dst, src, PCM_CHUNK_SIZE);
310#endif
311 }
312} /* chunk_to_wav_format */
313 209
314static bool init_encoder(void) 210 frame_htole((uint32_t *)data->data, frame_size);
315{
316 struct enc_inputs inputs;
317 struct enc_parameters params;
318
319 if (ci->enc_get_inputs == NULL ||
320 ci->enc_set_parameters == NULL ||
321 ci->enc_get_chunk == NULL ||
322 ci->enc_finish_chunk == NULL ||
323 ci->enc_get_pcm_data == NULL )
324 return false;
325
326 ci->enc_get_inputs(&inputs);
327
328 if (inputs.config->afmt != AFMT_PCM_WAV)
329 return false;
330
331 sample_rate = inputs.sample_rate;
332 num_channels = inputs.num_channels;
333 rec_mono_mode = inputs.rec_mono_mode;
334 err = 0;
335
336 /* configure the buffer system */
337 params.afmt = AFMT_PCM_WAV;
338 enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2;
339 params.chunk_size = enc_size;
340 params.enc_sample_rate = sample_rate;
341 params.reserve_bytes = 0;
342 params.events_callback = enc_events_callback;
343 ci->enc_set_parameters(&params);
344
345 return true;
346} /* init_encoder */
347 211
348/* this is the codec entry point */ 212 ci->enc_pcmbuf_advance(PCM_SAMP_PER_CHUNK);
349enum codec_status codec_main(enum codec_entry_call_reason reason) 213 ci->enc_encbuf_finish_buffer();
350{
351 if (reason == CODEC_LOAD) {
352 if (!init_encoder())
353 return CODEC_ERROR;
354 }
355 else if (reason == CODEC_UNLOAD) {
356 /* reset parameters to initial state */
357 ci->enc_set_parameters(NULL);
358 } 214 }
359 215
360 return CODEC_OK; 216 return CODEC_OK;
361} 217}
362 218
363/* this is called for each file to process */ 219/* this is called by recording system */
364enum codec_status codec_run(void) 220int ICODE_ATTR enc_callback(enum enc_callback_reason reason,
221 void *params)
365{ 222{
366 /* main encoding loop */ 223 if (LIKELY(reason == ENC_CB_STREAM))
367 while(ci->get_command(NULL) != CODEC_ACTION_HALT)
368 { 224 {
369 uint32_t *src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE); 225 switch (((union enc_chunk_hdr *)params)->type)
370 struct enc_chunk_hdr *chunk; 226 {
371 227 case CHUNK_T_DATA:
372 if(src == NULL) 228 return on_stream_data(params);
373 continue; 229 case CHUNK_T_STREAM_START:
374 230 return on_stream_start();
375 chunk = ci->enc_get_chunk(); 231 case CHUNK_T_STREAM_END:
376 chunk->enc_size = enc_size; 232 return on_stream_end(params);
377 chunk->num_pcm = PCM_SAMP_PER_CHUNK; 233 }
378 chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); 234 }
379 235 else if (reason == ENC_CB_INPUTS)
380 chunk_to_wav_format(src, (uint32_t *)chunk->enc_data); 236 {
381 237 struct enc_inputs *inputs = params;
382 ci->enc_finish_chunk(); 238 sample_rate = inputs->sample_rate;
239 num_channels = inputs->num_channels;
240 frame_size = PCM_SAMP_PER_CHUNK*PCM_DEPTH_BYTES*num_channels;
383 } 241 }
384 242
385 return CODEC_OK; 243 return 0;
386} 244}