diff options
Diffstat (limited to 'lib/rbcodec/codecs/wav_enc.c')
-rw-r--r-- | lib/rbcodec/codecs/wav_enc.c | 376 |
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 | ||
60 | static int num_channels IBSS_ATTR; | 60 | static int num_channels; |
61 | static int rec_mono_mode IBSS_ATTR; | ||
62 | static uint32_t sample_rate; | 61 | static uint32_t sample_rate; |
63 | static uint32_t enc_size; | 62 | static size_t frame_size; |
64 | static int32_t err IBSS_ATTR; | 63 | static size_t data_size; |
65 | 64 | ||
66 | static const struct riff_header riff_header = | 65 | static 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 */ | 87 | static inline void frame_htole(uint32_t *p, size_t size) |
89 | static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR; | ||
90 | static 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 */ | ||
96 | static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR; | ||
97 | static 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, | 108 | static 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 | |||
118 | static 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 | ||
120 | static 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 | ||
142 | static bool on_end_file(struct enc_file_event_data *data) | 133 | static 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 | ||
185 | static void enc_events_callback(enum enc_events event, void *data) | 173 | /* this is the codec entry point */ |
186 | ICODE_ATTR; | 174 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
187 | static 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 */ |
218 | static inline void sample_to_mono(uint32_t **src, uint32_t **dst) | 181 | enum 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 | ||
255 | static void chunk_to_wav_format(uint32_t *src, uint32_t *dst) ICODE_ATTR; | 194 | /* First obtain output buffer; when available, get PCM data */ |
256 | static 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 | ||
314 | static 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(¶ms); | ||
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); |
349 | enum 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 */ |
364 | enum codec_status codec_run(void) | 220 | int 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 | } |