summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/wavpack_enc.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/wavpack_enc.c')
-rw-r--r--lib/rbcodec/codecs/wavpack_enc.c467
1 files changed, 467 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/wavpack_enc.c b/lib/rbcodec/codecs/wavpack_enc.c
new file mode 100644
index 0000000000..730cf0734b
--- /dev/null
+++ b/lib/rbcodec/codecs/wavpack_enc.c
@@ -0,0 +1,467 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Antonius Hellmann
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "codeclib.h"
23#include "libwavpack/wavpack.h"
24
25CODEC_ENC_HEADER
26
27/** Types **/
28typedef struct
29{
30 uint8_t type; /* Type of metadata */
31 uint8_t word_size; /* Size of metadata in words */
32} __attribute__((packed)) WavpackMetadataHeader;
33
34struct riff_header
35{
36 uint8_t riff_id[4]; /* 00h - "RIFF" */
37 uint32_t riff_size; /* 04h - sz following headers + data_size */
38 /* format header */
39 uint8_t format[4]; /* 08h - "WAVE" */
40 uint8_t format_id[4]; /* 0Ch - "fmt " */
41 uint32_t format_size; /* 10h - 16 for PCM (sz format data) */
42 /* format data */
43 uint16_t audio_format; /* 14h - 1=PCM */
44 uint16_t num_channels; /* 16h - 1=M, 2=S, etc. */
45 uint32_t sample_rate; /* 18h - HZ */
46 uint32_t byte_rate; /* 1Ch - num_channels*sample_rate*bits_per_sample/8 */
47 uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */
48 uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */
49 /* Not for audio_format=1 (PCM) */
50/* unsigned short extra_param_size; 24h - size of extra data */
51/* unsigned char *extra_params; */
52 /* data header */
53 uint8_t data_id[4]; /* 24h - "data" */
54 uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */
55/* unsigned char *data; 2ch - actual sound data */
56} __attribute__((packed));
57
58#define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */
59#define RIFF_FMT_DATA_SIZE 16 /* audio_format -> bits_per_sample */
60#define RIFF_DATA_HEADER_SIZE 8 /* data_id -> data_size */
61
62#define PCM_DEPTH_BITS 16
63#define PCM_DEPTH_BYTES 2
64#define PCM_SAMP_PER_CHUNK 5000
65#define PCM_CHUNK_SIZE (4*PCM_SAMP_PER_CHUNK)
66
67/** Data **/
68static int8_t input_buffer[PCM_CHUNK_SIZE*2] IBSS_ATTR;
69static WavpackConfig config IBSS_ATTR;
70static WavpackContext *wpc;
71static int32_t data_size, input_size, input_step IBSS_ATTR;
72static int32_t err IBSS_ATTR;
73
74static const WavpackMetadataHeader wvpk_mdh =
75{
76 ID_RIFF_HEADER,
77 sizeof (struct riff_header) / sizeof (uint16_t),
78};
79
80static const struct riff_header riff_header =
81{
82 /* "RIFF" header */
83 { 'R', 'I', 'F', 'F' }, /* riff_id */
84 0, /* riff_size (*) */
85 /* format header */
86 { 'W', 'A', 'V', 'E' }, /* format */
87 { 'f', 'm', 't', ' ' }, /* format_id */
88 htole32(16), /* format_size */
89 /* format data */
90 htole16(1), /* audio_format */
91 0, /* num_channels (*) */
92 0, /* sample_rate (*) */
93 0, /* byte_rate (*) */
94 0, /* block_align (*) */
95 htole16(PCM_DEPTH_BITS), /* bits_per_sample */
96 /* data header */
97 { 'd', 'a', 't', 'a' }, /* data_id */
98 0 /* data_size (*) */
99 /* (*) updated during ENC_END_FILE event */
100};
101
102static inline void sample_to_int32_mono(int32_t **src, int32_t **dst)
103{
104 int32_t t = *(*src)++;
105 /* endianness irrelevant */
106 t = (int16_t)t + (t >> 16) + err;
107 err = t & 1;
108 *(*dst)++ = t >> 1;
109} /* sample_to_int32_mono */
110
111static inline void sample_to_int32_stereo(int32_t **src, int32_t **dst)
112{
113 int32_t t = *(*src)++;
114#ifdef ROCKBOX_BIG_ENDIAN
115 *(*dst)++ = t >> 16, *(*dst)++ = (int16_t)t;
116#else
117 *(*dst)++ = (int16_t)t, *(*dst)++ = t >> 16;
118#endif
119} /* sample_to_int32_stereo */
120
121STATICIRAM void chunk_to_int32(int32_t *src) ICODE_ATTR;
122STATICIRAM void chunk_to_int32(int32_t *src)
123{
124 int32_t *src_end, *dst;
125#ifdef USE_IRAM
126 /* copy to IRAM before converting data */
127 dst = (int32_t *)input_buffer + PCM_SAMP_PER_CHUNK;
128 src_end = dst + PCM_SAMP_PER_CHUNK;
129
130 memcpy(dst, src, PCM_CHUNK_SIZE);
131
132 src = dst;
133#else
134 src_end = src + PCM_SAMP_PER_CHUNK;
135#endif
136
137 dst = (int32_t *)input_buffer;
138
139 if (config.num_channels == 1)
140 {
141 /*
142 * |llllllllllllllll|rrrrrrrrrrrrrrrr| =>
143 * |mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm|
144 */
145 do
146 {
147 /* read 10 longs and write 10 longs */
148 sample_to_int32_mono(&src, &dst);
149 sample_to_int32_mono(&src, &dst);
150 sample_to_int32_mono(&src, &dst);
151 sample_to_int32_mono(&src, &dst);
152 sample_to_int32_mono(&src, &dst);
153 sample_to_int32_mono(&src, &dst);
154 sample_to_int32_mono(&src, &dst);
155 sample_to_int32_mono(&src, &dst);
156 sample_to_int32_mono(&src, &dst);
157 sample_to_int32_mono(&src, &dst);
158 }
159 while(src < src_end);
160
161 return;
162 }
163 else
164 {
165 /*
166 * |llllllllllllllll|rrrrrrrrrrrrrrrr| =>
167 * |llllllllllllllllllllllllllllllll|rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr|
168 */
169 do
170 {
171 /* read 10 longs and write 20 longs */
172 sample_to_int32_stereo(&src, &dst);
173 sample_to_int32_stereo(&src, &dst);
174 sample_to_int32_stereo(&src, &dst);
175 sample_to_int32_stereo(&src, &dst);
176 sample_to_int32_stereo(&src, &dst);
177 sample_to_int32_stereo(&src, &dst);
178 sample_to_int32_stereo(&src, &dst);
179 sample_to_int32_stereo(&src, &dst);
180 sample_to_int32_stereo(&src, &dst);
181 sample_to_int32_stereo(&src, &dst);
182 }
183 while (src < src_end);
184
185 return;
186 }
187} /* chunk_to_int32 */
188
189/* called very often - inline */
190static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR;
191static inline bool is_file_data_ok(struct enc_file_event_data *data)
192{
193 return data->rec_file >= 0 && (long)data->chunk->flags >= 0;
194} /* is_file_data_ok */
195
196/* called very often - inline */
197static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR;
198static inline bool on_write_chunk(struct enc_file_event_data *data)
199{
200 if (!is_file_data_ok(data))
201 return false;
202
203 if (data->chunk->enc_data == NULL)
204 {
205#ifdef ROCKBOX_HAS_LOGF
206 ci->logf("wvpk enc: NULL data");
207#endif
208 return true;
209 }
210
211 /* update timestamp (block_index) */
212 ((WavpackHeader *)data->chunk->enc_data)->block_index =
213 htole32(data->num_pcm_samples);
214
215 if (ci->write(data->rec_file, data->chunk->enc_data,
216 data->chunk->enc_size) != (ssize_t)data->chunk->enc_size)
217 return false;
218
219 data->num_pcm_samples += data->chunk->num_pcm;
220 return true;
221} /* on_write_chunk */
222
223static bool on_start_file(struct enc_file_event_data *data)
224{
225 if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
226 return false;
227
228 data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
229
230 if (data->rec_file < 0)
231 return false;
232
233 /* reset sample count */
234 data->num_pcm_samples = 0;
235
236 /* write template headers */
237 if (ci->write(data->rec_file, &wvpk_mdh, sizeof (wvpk_mdh))
238 != sizeof (wvpk_mdh) ||
239 ci->write(data->rec_file, &riff_header, sizeof (riff_header))
240 != sizeof (riff_header))
241 {
242 return false;
243 }
244
245 data->new_enc_size += sizeof(wvpk_mdh) + sizeof(riff_header);
246 return true;
247} /* on_start_file */
248
249static bool on_end_file(struct enc_file_event_data *data)
250{
251 struct
252 {
253 WavpackMetadataHeader wpmdh;
254 struct riff_header rhdr;
255 WavpackHeader wph;
256 } __attribute__ ((packed)) h;
257
258 uint32_t data_size;
259
260 if (data->rec_file < 0)
261 return false; /* file already closed, nothing more we can do */
262
263 /* always _try_ to write the file header, even on error */
264
265 /* read template headers at start */
266 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
267 ci->read(data->rec_file, &h, sizeof (h)) != sizeof (h))
268 return false;
269
270 data_size = data->num_pcm_samples*config.num_channels*PCM_DEPTH_BYTES;
271
272 /** "RIFF" header **/
273 h.rhdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE +
274 RIFF_FMT_DATA_SIZE + RIFF_DATA_HEADER_SIZE + data_size);
275
276 /* format data */
277 h.rhdr.num_channels = htole16(config.num_channels);
278 h.rhdr.sample_rate = htole32(config.sample_rate);
279 h.rhdr.byte_rate = htole32(config.sample_rate*config.num_channels*
280 PCM_DEPTH_BYTES);
281 h.rhdr.block_align = htole16(config.num_channels*PCM_DEPTH_BYTES);
282
283 /* data header */
284 h.rhdr.data_size = htole32(data_size);
285
286 /** Wavpack header **/
287 h.wph.ckSize = htole32(letoh32(h.wph.ckSize) + sizeof (h.wpmdh)
288 + sizeof (h.rhdr));
289 h.wph.total_samples = htole32(data->num_pcm_samples);
290
291 /* MDH|RIFF|WVPK => WVPK|MDH|RIFF */
292 if (ci->lseek(data->rec_file, 0, SEEK_SET)
293 != 0 ||
294 ci->write(data->rec_file, &h.wph, sizeof (h.wph))
295 != sizeof (h.wph) ||
296 ci->write(data->rec_file, &h.wpmdh, sizeof (h.wpmdh))
297 != sizeof (h.wpmdh) ||
298 ci->write(data->rec_file, &h.rhdr, sizeof (h.rhdr))
299 != sizeof (h.rhdr) ||
300 ci->close(data->rec_file) != 0 )
301 {
302 return false;
303 }
304
305 data->rec_file = -1;
306
307 return true;
308} /* on_end_file */
309
310STATICIRAM void enc_events_callback(enum enc_events event, void *data)
311 ICODE_ATTR;
312STATICIRAM void enc_events_callback(enum enc_events event, void *data)
313{
314 switch (event)
315 {
316 case ENC_WRITE_CHUNK:
317 if (on_write_chunk((struct enc_file_event_data *)data))
318 return;
319
320 break;
321
322 case ENC_START_FILE:
323 /* write metadata header and RIFF header */
324 if (on_start_file((struct enc_file_event_data *)data))
325 return;
326
327 break;
328
329 case ENC_END_FILE:
330 if (on_end_file((struct enc_file_event_data *)data))
331 return;
332
333 break;
334
335 default:
336 return;
337 }
338
339 /* Something failed above. Signal error back to core. */
340 ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
341} /* enc_events_callback */
342
343static bool init_encoder(void)
344{
345 struct enc_inputs inputs;
346 struct enc_parameters params;
347
348 codec_init();
349
350 if (ci->enc_get_inputs == NULL ||
351 ci->enc_set_parameters == NULL ||
352 ci->enc_get_chunk == NULL ||
353 ci->enc_finish_chunk == NULL ||
354 ci->enc_get_pcm_data == NULL ||
355 ci->enc_unget_pcm_data == NULL )
356 return false;
357
358 ci->enc_get_inputs(&inputs);
359
360 if (inputs.config->afmt != AFMT_WAVPACK)
361 return false;
362
363 memset(&config, 0, sizeof (config));
364 config.bits_per_sample = PCM_DEPTH_BITS;
365 config.bytes_per_sample = PCM_DEPTH_BYTES;
366 config.sample_rate = inputs.sample_rate;
367 config.num_channels = inputs.num_channels;
368
369 wpc = WavpackOpenFileOutput ();
370
371 if (!WavpackSetConfiguration(wpc, &config, -1))
372 return false;
373
374 err = 0;
375
376 /* configure the buffer system */
377 params.afmt = AFMT_WAVPACK;
378 input_size = PCM_CHUNK_SIZE*inputs.num_channels / 2;
379 data_size = 105*input_size / 100;
380 input_size *= 2;
381 input_step = input_size / 4;
382 params.chunk_size = data_size;
383 params.enc_sample_rate = inputs.sample_rate;
384 params.reserve_bytes = 0;
385 params.events_callback = enc_events_callback;
386
387 ci->enc_set_parameters(&params);
388
389 return true;
390} /* init_encoder */
391
392/* this is the codec entry point */
393enum codec_status codec_main(enum codec_entry_call_reason reason)
394{
395 if (reason == CODEC_LOAD) {
396 /* initialize params and config */
397 if (!init_encoder())
398 return CODEC_ERROR;
399 }
400 else if (reason == CODEC_UNLOAD) {
401 /* reset parameters to initial state */
402 ci->enc_set_parameters(NULL);
403 }
404
405 return CODEC_OK;
406}
407
408/* this is called for each file to process */
409enum codec_status codec_run(void)
410{
411 /* main encoding loop */
412 while(ci->get_command(NULL) != CODEC_ACTION_HALT)
413 {
414 uint8_t *src = (uint8_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE);
415 struct enc_chunk_hdr *chunk;
416 bool abort_chunk;
417 uint8_t *dst;
418 uint8_t *src_end;
419
420 if(src == NULL)
421 continue;
422
423 chunk = ci->enc_get_chunk();
424
425 /* reset counts and pointer */
426 chunk->enc_size = 0;
427 chunk->num_pcm = 0;
428 chunk->enc_data = NULL;
429
430 dst = ENC_CHUNK_SKIP_HDR(dst, chunk);
431
432 WavpackStartBlock(wpc, dst, dst + data_size);
433
434 chunk_to_int32((uint32_t*)src);
435 src = input_buffer;
436 src_end = src + input_size;
437
438 /* encode chunk in four steps yielding between each */
439 do
440 {
441 abort_chunk = true;
442 if (WavpackPackSamples(wpc, (int32_t *)src,
443 PCM_SAMP_PER_CHUNK/4))
444 {
445 chunk->num_pcm += PCM_SAMP_PER_CHUNK/4;
446 ci->yield();
447 /* could've been stopped in some way */
448 abort_chunk = chunk->flags & CHUNKF_ABORT;
449 }
450
451 src += input_step;
452 }
453 while (!abort_chunk && src < src_end);
454
455 if (!abort_chunk)
456 {
457 chunk->enc_data = dst;
458 if (chunk->num_pcm < PCM_SAMP_PER_CHUNK)
459 ci->enc_unget_pcm_data(PCM_CHUNK_SIZE - chunk->num_pcm*4);
460 /* finish the chunk and store chunk size info */
461 chunk->enc_size = WavpackFinishBlock(wpc);
462 ci->enc_finish_chunk();
463 }
464 }
465
466 return CODEC_OK;
467}