updates the README document.
[kugel-rb.git] / apps / codecs / wavpack_enc.c
blob7a5f35a53a379483591ddd75f0cb6cf2c73be8d4
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 "codeclib.h"
25 #include "libwavpack/wavpack.h"
27 CODEC_ENC_HEADER
29 /** Types **/
30 typedef struct
32 uint8_t type; /* Type of metadata */
33 uint8_t word_size; /* Size of metadata in words */
34 } __attribute__((packed)) WavpackMetadataHeader;
36 struct riff_header
38 uint8_t riff_id[4]; /* 00h - "RIFF" */
39 uint32_t riff_size; /* 04h - sz following headers + data_size */
40 /* format header */
41 uint8_t format[4]; /* 08h - "WAVE" */
42 uint8_t format_id[4]; /* 0Ch - "fmt " */
43 uint32_t format_size; /* 10h - 16 for PCM (sz format data) */
44 /* format data */
45 uint16_t audio_format; /* 14h - 1=PCM */
46 uint16_t num_channels; /* 16h - 1=M, 2=S, etc. */
47 uint32_t sample_rate; /* 18h - HZ */
48 uint32_t byte_rate; /* 1Ch - num_channels*sample_rate*bits_per_sample/8 */
49 uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */
50 uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */
51 /* Not for audio_format=1 (PCM) */
52 /* unsigned short extra_param_size; 24h - size of extra data */
53 /* unsigned char *extra_params; */
54 /* data header */
55 uint8_t data_id[4]; /* 24h - "data" */
56 uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */
57 /* unsigned char *data; 2ch - actual sound data */
58 } __attribute__((packed));
60 #define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */
61 #define RIFF_FMT_DATA_SIZE 16 /* audio_format -> bits_per_sample */
62 #define RIFF_DATA_HEADER_SIZE 8 /* data_id -> data_size */
64 #define PCM_DEPTH_BITS 16
65 #define PCM_DEPTH_BYTES 2
66 #define PCM_SAMP_PER_CHUNK 5000
67 #define PCM_CHUNK_SIZE (4*PCM_SAMP_PER_CHUNK)
69 /** Data **/
70 static int8_t input_buffer[PCM_CHUNK_SIZE*2] IBSS_ATTR;
71 static WavpackConfig config IBSS_ATTR;
72 static WavpackContext *wpc;
73 static int32_t data_size, input_size, input_step IBSS_ATTR;
74 static int32_t err IBSS_ATTR;
76 static const WavpackMetadataHeader wvpk_mdh =
78 ID_RIFF_HEADER,
79 sizeof (struct riff_header) / sizeof (uint16_t),
82 static const struct riff_header riff_header =
84 /* "RIFF" header */
85 { 'R', 'I', 'F', 'F' }, /* riff_id */
86 0, /* riff_size (*) */
87 /* format header */
88 { 'W', 'A', 'V', 'E' }, /* format */
89 { 'f', 'm', 't', ' ' }, /* format_id */
90 H_TO_LE32(16), /* format_size */
91 /* format data */
92 H_TO_LE16(1), /* audio_format */
93 0, /* num_channels (*) */
94 0, /* sample_rate (*) */
95 0, /* byte_rate (*) */
96 0, /* block_align (*) */
97 H_TO_LE16(PCM_DEPTH_BITS), /* bits_per_sample */
98 /* data header */
99 { 'd', 'a', 't', 'a' }, /* data_id */
100 0 /* data_size (*) */
101 /* (*) updated during ENC_END_FILE event */
104 static inline void sample_to_int32_mono(int32_t **src, int32_t **dst)
106 int32_t t = *(*src)++;
107 /* endianness irrelevant */
108 t = (int16_t)t + (t >> 16) + err;
109 err = t & 1;
110 *(*dst)++ = t >> 1;
111 } /* sample_to_int32_mono */
113 static inline void sample_to_int32_stereo(int32_t **src, int32_t **dst)
115 int32_t t = *(*src)++;
116 #ifdef ROCKBOX_BIG_ENDIAN
117 *(*dst)++ = t >> 16, *(*dst)++ = (int16_t)t;
118 #else
119 *(*dst)++ = (int16_t)t, *(*dst)++ = t >> 16;
120 #endif
121 } /* sample_to_int32_stereo */
123 STATICIRAM void chunk_to_int32(int32_t *src) ICODE_ATTR;
124 STATICIRAM void chunk_to_int32(int32_t *src)
126 int32_t *src_end, *dst;
127 #ifdef USE_IRAM
128 /* copy to IRAM before converting data */
129 dst = (int32_t *)input_buffer + PCM_SAMP_PER_CHUNK;
130 src_end = dst + PCM_SAMP_PER_CHUNK;
132 memcpy(dst, src, PCM_CHUNK_SIZE);
134 src = dst;
135 #else
136 src_end = src + PCM_SAMP_PER_CHUNK;
137 #endif
139 dst = (int32_t *)input_buffer;
141 if (config.num_channels == 1)
144 * |llllllllllllllll|rrrrrrrrrrrrrrrr| =>
145 * |mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm|
149 /* read 10 longs and write 10 longs */
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 sample_to_int32_mono(&src, &dst);
159 sample_to_int32_mono(&src, &dst);
161 while(src < src_end);
163 return;
165 else
168 * |llllllllllllllll|rrrrrrrrrrrrrrrr| =>
169 * |llllllllllllllllllllllllllllllll|rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr|
173 /* read 10 longs and write 20 longs */
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 sample_to_int32_stereo(&src, &dst);
183 sample_to_int32_stereo(&src, &dst);
185 while (src < src_end);
187 return;
189 } /* chunk_to_int32 */
191 /* called very often - inline */
192 static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR;
193 static inline bool is_file_data_ok(struct enc_file_event_data *data)
195 return data->rec_file >= 0 && (long)data->chunk->flags >= 0;
196 } /* is_file_data_ok */
198 /* called very often - inline */
199 static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR;
200 static inline bool on_write_chunk(struct enc_file_event_data *data)
202 if (!is_file_data_ok(data))
203 return false;
205 if (data->chunk->enc_data == NULL)
207 #ifdef ROCKBOX_HAS_LOGF
208 ci->logf("wvpk enc: NULL data");
209 #endif
210 return true;
213 /* update timestamp (block_index) */
214 ((WavpackHeader *)data->chunk->enc_data)->block_index =
215 htole32(data->num_pcm_samples);
217 if (ci->write(data->rec_file, data->chunk->enc_data,
218 data->chunk->enc_size) != (ssize_t)data->chunk->enc_size)
219 return false;
221 data->num_pcm_samples += data->chunk->num_pcm;
222 return true;
223 } /* on_write_chunk */
225 static bool on_start_file(struct enc_file_event_data *data)
227 if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
228 return false;
230 data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
232 if (data->rec_file < 0)
233 return false;
235 /* reset sample count */
236 data->num_pcm_samples = 0;
238 /* write template headers */
239 if (ci->write(data->rec_file, &wvpk_mdh, sizeof (wvpk_mdh))
240 != sizeof (wvpk_mdh) ||
241 ci->write(data->rec_file, &riff_header, sizeof (riff_header))
242 != sizeof (riff_header))
244 return false;
247 data->new_enc_size += sizeof(wvpk_mdh) + sizeof(riff_header);
248 return true;
249 } /* on_start_file */
251 static bool on_end_file(struct enc_file_event_data *data)
253 struct
255 WavpackMetadataHeader wpmdh;
256 struct riff_header rhdr;
257 WavpackHeader wph;
258 } __attribute__ ((packed)) h;
260 uint32_t data_size;
262 if (data->rec_file < 0)
263 return false; /* file already closed, nothing more we can do */
265 /* always _try_ to write the file header, even on error */
267 /* read template headers at start */
268 if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
269 ci->read(data->rec_file, &h, sizeof (h)) != sizeof (h))
270 return false;
272 data_size = data->num_pcm_samples*config.num_channels*PCM_DEPTH_BYTES;
274 /** "RIFF" header **/
275 h.rhdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE +
276 RIFF_FMT_DATA_SIZE + RIFF_DATA_HEADER_SIZE + data_size);
278 /* format data */
279 h.rhdr.num_channels = htole16(config.num_channels);
280 h.rhdr.sample_rate = htole32(config.sample_rate);
281 h.rhdr.byte_rate = htole32(config.sample_rate*config.num_channels*
282 PCM_DEPTH_BYTES);
283 h.rhdr.block_align = htole16(config.num_channels*PCM_DEPTH_BYTES);
285 /* data header */
286 h.rhdr.data_size = htole32(data_size);
288 /** Wavpack header **/
289 h.wph.ckSize = htole32(letoh32(h.wph.ckSize) + sizeof (h.wpmdh)
290 + sizeof (h.rhdr));
291 h.wph.total_samples = htole32(data->num_pcm_samples);
293 /* MDH|RIFF|WVPK => WVPK|MDH|RIFF */
294 if (ci->lseek(data->rec_file, 0, SEEK_SET)
295 != 0 ||
296 ci->write(data->rec_file, &h.wph, sizeof (h.wph))
297 != sizeof (h.wph) ||
298 ci->write(data->rec_file, &h.wpmdh, sizeof (h.wpmdh))
299 != sizeof (h.wpmdh) ||
300 ci->write(data->rec_file, &h.rhdr, sizeof (h.rhdr))
301 != sizeof (h.rhdr) ||
302 ci->close(data->rec_file) != 0 )
304 return false;
307 data->rec_file = -1;
309 return true;
310 } /* on_end_file */
312 STATICIRAM void enc_events_callback(enum enc_events event, void *data)
313 ICODE_ATTR;
314 STATICIRAM void enc_events_callback(enum enc_events event, void *data)
316 switch (event)
318 case ENC_WRITE_CHUNK:
319 if (on_write_chunk((struct enc_file_event_data *)data))
320 return;
322 break;
324 case ENC_START_FILE:
325 /* write metadata header and RIFF header */
326 if (on_start_file((struct enc_file_event_data *)data))
327 return;
329 break;
331 case ENC_END_FILE:
332 if (on_end_file((struct enc_file_event_data *)data))
333 return;
335 break;
337 default:
338 return;
341 /* Something failed above. Signal error back to core. */
342 ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
343 } /* enc_events_callback */
345 static bool init_encoder(void)
347 struct enc_inputs inputs;
348 struct enc_parameters params;
350 codec_init();
352 if (ci->enc_get_inputs == NULL ||
353 ci->enc_set_parameters == NULL ||
354 ci->enc_get_chunk == NULL ||
355 ci->enc_finish_chunk == NULL ||
356 ci->enc_get_pcm_data == NULL ||
357 ci->enc_unget_pcm_data == NULL )
358 return false;
360 ci->enc_get_inputs(&inputs);
362 if (inputs.config->afmt != AFMT_WAVPACK)
363 return false;
365 memset(&config, 0, sizeof (config));
366 config.bits_per_sample = PCM_DEPTH_BITS;
367 config.bytes_per_sample = PCM_DEPTH_BYTES;
368 config.sample_rate = inputs.sample_rate;
369 config.num_channels = inputs.num_channels;
371 wpc = WavpackOpenFileOutput ();
373 if (!WavpackSetConfiguration(wpc, &config, -1))
374 return false;
376 err = 0;
378 /* configure the buffer system */
379 params.afmt = AFMT_WAVPACK;
380 input_size = PCM_CHUNK_SIZE*inputs.num_channels / 2;
381 data_size = 105*input_size / 100;
382 input_size *= 2;
383 input_step = input_size / 4;
384 params.chunk_size = data_size;
385 params.enc_sample_rate = inputs.sample_rate;
386 params.reserve_bytes = 0;
387 params.events_callback = enc_events_callback;
389 ci->enc_set_parameters(&params);
391 return true;
392 } /* init_encoder */
394 enum codec_status codec_main(void)
396 /* initialize params and config */
397 if (!init_encoder())
399 ci->enc_codec_loaded = -1;
400 return CODEC_ERROR;
403 /* main application waits for this flag during encoder loading */
404 ci->enc_codec_loaded = 1;
406 /* main encoding loop */
407 while(!ci->stop_encoder)
409 uint8_t *src;
411 while ((src = ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
413 struct enc_chunk_hdr *chunk;
414 bool abort_chunk;
415 uint8_t *dst;
416 uint8_t *src_end;
418 if(ci->stop_encoder)
419 break;
421 abort_chunk = true;
423 chunk = ci->enc_get_chunk();
425 /* reset counts and pointer */
426 chunk->enc_size = 0;
427 chunk->num_pcm = 0;
428 chunk->enc_data = NULL;
430 dst = ENC_CHUNK_SKIP_HDR(dst, chunk);
432 WavpackStartBlock(wpc, dst, dst + data_size);
434 chunk_to_int32((uint32_t*)src);
435 src = input_buffer;
436 src_end = src + input_size;
438 /* encode chunk in four steps yielding between each */
441 if (WavpackPackSamples(wpc, (int32_t *)src,
442 PCM_SAMP_PER_CHUNK/4))
444 chunk->num_pcm += PCM_SAMP_PER_CHUNK/4;
445 ci->yield();
446 /* could've been stopped in some way */
447 abort_chunk = ci->stop_encoder ||
448 (chunk->flags & CHUNKF_ABORT);
451 src += input_step;
453 while (!abort_chunk && src < src_end);
455 if (!abort_chunk)
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();
466 ci->yield();
469 /* reset parameters to initial state */
470 ci->enc_set_parameters(NULL);
472 /* main application waits for this flag during encoder removing */
473 ci->enc_codec_loaded = 0;
475 return CODEC_OK;
476 } /* codec_start */
478 #endif /* ndef SIMULATOR */