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 @@ * $Id$ * * Copyright (C) 2006 Antonius Hellmann + * Copyright (C) 2006-2013 Michael Sevakis * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -40,12 +41,12 @@ struct riff_header uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */ uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */ /* Not for audio_format=1 (PCM) */ -/* unsigned short extra_param_size; 24h - size of extra data */ -/* unsigned char *extra_params; */ +/* uint16_t extra_param_size; 24h - size of extra data */ +/* uint8_t extra_params[extra_param_size]; */ /* data header */ uint8_t data_id[4]; /* 24h - "data" */ uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */ -/* unsigned char *data; 2ch - actual sound data */ +/* uint8_t data[data_size]; 2Ch - actual sound data */ } __attribute__((packed)); #define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */ @@ -55,19 +56,17 @@ struct riff_header #define PCM_DEPTH_BYTES 2 #define PCM_DEPTH_BITS 16 #define PCM_SAMP_PER_CHUNK 2048 -#define PCM_CHUNK_SIZE (PCM_SAMP_PER_CHUNK*4) -static int num_channels IBSS_ATTR; -static int rec_mono_mode IBSS_ATTR; +static int num_channels; static uint32_t sample_rate; -static uint32_t enc_size; -static int32_t err IBSS_ATTR; +static size_t frame_size; +static size_t data_size; -static const struct riff_header riff_header = +static const struct riff_header riff_template_header = { /* "RIFF" header */ { 'R', 'I', 'F', 'F' }, /* riff_id */ - 0, /* riff_size (*) */ + 0, /* riff_size (*) */ /* format header */ { 'W', 'A', 'V', 'E' }, /* format */ { 'f', 'm', 't', ' ' }, /* format_id */ @@ -82,305 +81,164 @@ static const struct riff_header riff_header = /* data header */ { 'd', 'a', 't', 'a' }, /* data_id */ 0 /* data_size (*) */ - /* (*) updated during ENC_END_FILE event */ + /* (*) updated when finalizing stream */ }; -/* called version often - inline */ -static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR; -static inline bool is_file_data_ok(struct enc_file_event_data *data) +static inline void frame_htole(uint32_t *p, size_t size) { - return data->rec_file >= 0 && (long)data->chunk->flags >= 0; -} /* is_file_data_ok */ - -/* called version often - inline */ -static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR; -static inline bool on_write_chunk(struct enc_file_event_data *data) -{ - if (!is_file_data_ok(data)) - return false; - - if (data->chunk->enc_data == NULL) +#ifdef ROCKBOX_BIG_ENDIAN + /* Byte-swap samples, stereo or mono */ + do { -#ifdef ROCKBOX_HAS_LOGF - ci->logf("wav enc: NULL data"); -#endif - return true; + uint32_t t; + t = swap_odd_even32(*p); *p++ = t; + t = swap_odd_even32(*p); *p++ = t; + t = swap_odd_even32(*p); *p++ = t; + t = swap_odd_even32(*p); *p++ = t; + t = swap_odd_even32(*p); *p++ = t; + t = swap_odd_even32(*p); *p++ = t; + t = swap_odd_even32(*p); *p++ = t; + t = swap_odd_even32(*p); *p++ = t; } + while (size -= 8 * 2 * PCM_DEPTH_BYTES); +#endif /* ROCKBOX_BIG_ENDIAN */ + (void)p; (void)size; +} - if (ci->write(data->rec_file, data->chunk->enc_data, - data->chunk->enc_size) != (ssize_t)data->chunk->enc_size) - return false; - - data->num_pcm_samples += data->chunk->num_pcm; - return true; -} /* on_write_chunk */ - -static bool on_start_file(struct enc_file_event_data *data) +static int on_stream_data(struct enc_chunk_data *data) { - if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0') - return false; + size_t size = data->hdr.size; + + if (ci->enc_stream_write(data->data, size) != (ssize_t)size) + return -1; - data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666); + data_size += size; - if (data->rec_file < 0) - return false; + return 0; +} +static int on_stream_start(void) +{ /* reset sample count */ - data->num_pcm_samples = 0; + data_size = 0; /* write template header */ - if (ci->write(data->rec_file, &riff_header, sizeof (riff_header)) - != sizeof (riff_header)) - { - return false; - } + if (ci->enc_stream_write(&riff_template_header, sizeof (struct riff_header)) + != sizeof (struct riff_header)) + return -1; - data->new_enc_size += sizeof (riff_header); - return true; -} /* on_start_file */ + return 0; +} -static bool on_end_file(struct enc_file_event_data *data) +static int on_stream_end(union enc_chunk_hdr *hdr) { /* update template header */ - struct riff_header hdr; - uint32_t data_size; - - if (data->rec_file < 0) - return false; /* file already closed, nothing more we can do */ + struct riff_header riff; - /* always _try_ to write the file header, even on error */ - if ((ci->lseek(data->rec_file, 0, SEEK_SET)) || - (ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr))) + if (hdr->err) { - return false; + /* Called for stream error; get correct data size */ + ssize_t size = ci->enc_stream_lseek(0, SEEK_END); + + if (size > (ssize_t)sizeof (riff)) + data_size = size - sizeof (riff); } - data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES; + if (ci->enc_stream_lseek(0, SEEK_SET) || + ci->enc_stream_read(&riff, sizeof (riff)) != sizeof (riff)) + return -1; /* "RIFF" header */ - hdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE + RIFF_FMT_DATA_SIZE - + RIFF_DATA_HEADER_SIZE + data_size); + riff.riff_size = htole32(RIFF_FMT_HEADER_SIZE + RIFF_FMT_DATA_SIZE + + RIFF_DATA_HEADER_SIZE + data_size); /* format data */ - hdr.num_channels = htole16(num_channels); - hdr.sample_rate = htole32(sample_rate); - hdr.byte_rate = htole32(sample_rate*num_channels* PCM_DEPTH_BYTES); - hdr.block_align = htole16(num_channels*PCM_DEPTH_BYTES); + riff.num_channels = htole16(num_channels); + riff.sample_rate = htole32(sample_rate); + riff.byte_rate = htole32(sample_rate*num_channels*PCM_DEPTH_BYTES); + riff.block_align = htole16(num_channels*PCM_DEPTH_BYTES); /* data header */ - hdr.data_size = htole32(data_size); + riff.data_size = htole32(data_size); - if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || - ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr) || - ci->close(data->rec_file) != 0) - { - return false; - } + if (ci->enc_stream_lseek(0, SEEK_SET) != 0) + return -2; - data->rec_file = -1; + if (ci->enc_stream_write(&riff, sizeof (riff)) != sizeof (riff)) + return -3; - return true; -} /* on_end_file */ + return 0; +} -static void enc_events_callback(enum enc_events event, void *data) - ICODE_ATTR; -static void enc_events_callback(enum enc_events event, void *data) +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) { - switch (event) - { - case ENC_WRITE_CHUNK: - if (on_write_chunk((struct enc_file_event_data *)data)) - return; - - break; - - case ENC_START_FILE: - if (on_start_file((struct enc_file_event_data *)data)) - return; - - break; - - case ENC_END_FILE: - if (on_end_file((struct enc_file_event_data *)data)) - return; - - break; - - default: - return; - } - - /* Something failed above. Signal error back to core. */ - ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR; -} /* enc_events_callback */ + return CODEC_OK; + (void)reason; +} -/* convert native pcm samples to wav format samples */ -static inline void sample_to_mono(uint32_t **src, uint32_t **dst) +/* this is called for each file to process */ +enum codec_status ICODE_ATTR codec_run(void) { - int32_t lr1, lr2; + enum { GETBUF_ENC, GETBUF_PCM } getbuf = GETBUF_ENC; + struct enc_chunk_data *data = NULL; - switch(rec_mono_mode) + /* main encoding loop */ + while (1) { - case 1: - /* mono = L */ - lr1 = *(*src)++; - lr1 = lr1 >> 16; - lr2 = *(*src)++; - lr2 = lr2 >> 16; - break; - case 2: - /* mono = R */ - lr1 = *(*src)++; - lr1 = (uint16_t)lr1; - lr2 = *(*src)++; - lr2 = (uint16_t)lr2; - break; - case 0: - default: - /* mono = (L+R)/2 */ - lr1 = *(*src)++; - lr1 = (int16_t)lr1 + (lr1 >> 16) + err; - err = lr1 & 1; - lr1 >>= 1; - - lr2 = *(*src)++; - lr2 = (int16_t)lr2 + (lr2 >> 16) + err; - err = lr2 & 1; - lr2 >>= 1; + enum codec_command_action action = ci->get_command(NULL); + + if (action != CODEC_ACTION_NULL) break; - } - *(*dst)++ = htole32((lr2 << 16) | (uint16_t)lr1); -} /* sample_to_mono */ -static void chunk_to_wav_format(uint32_t *src, uint32_t *dst) ICODE_ATTR; -static void chunk_to_wav_format(uint32_t *src, uint32_t *dst) -{ - if (num_channels == 1) - { - /* On big endian: - * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| - * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => - * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM| - * - * On little endian: - * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| - * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => - * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM| - */ - uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; - - do + /* First obtain output buffer; when available, get PCM data */ + switch (getbuf) { - sample_to_mono(&src, &dst); - sample_to_mono(&src, &dst); - sample_to_mono(&src, &dst); - sample_to_mono(&src, &dst); - sample_to_mono(&src, &dst); - sample_to_mono(&src, &dst); - sample_to_mono(&src, &dst); - sample_to_mono(&src, &dst); + case GETBUF_ENC: + if (!(data = ci->enc_encbuf_get_buffer(frame_size))) + continue; + getbuf = GETBUF_PCM; + case GETBUF_PCM: + if (!ci->enc_pcmbuf_read(data->data, PCM_SAMP_PER_CHUNK)) + continue; + getbuf = GETBUF_ENC; } - while (src < src_end); - } - else - { -#ifdef ROCKBOX_BIG_ENDIAN - /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => - * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| - */ - uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; - do - { - *dst++ = swap_odd_even32(*src++); - *dst++ = swap_odd_even32(*src++); - *dst++ = swap_odd_even32(*src++); - *dst++ = swap_odd_even32(*src++); - *dst++ = swap_odd_even32(*src++); - *dst++ = swap_odd_even32(*src++); - *dst++ = swap_odd_even32(*src++); - *dst++ = swap_odd_even32(*src++); - } - while (src < src_end); -#else - /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => - * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| - */ - ci->memcpy(dst, src, PCM_CHUNK_SIZE); -#endif - } -} /* chunk_to_wav_format */ + data->hdr.size = frame_size; + data->pcm_count = PCM_SAMP_PER_CHUNK; -static bool init_encoder(void) -{ - struct enc_inputs inputs; - struct enc_parameters params; - - if (ci->enc_get_inputs == NULL || - ci->enc_set_parameters == NULL || - ci->enc_get_chunk == NULL || - ci->enc_finish_chunk == NULL || - ci->enc_get_pcm_data == NULL ) - return false; - - ci->enc_get_inputs(&inputs); - - if (inputs.config->afmt != AFMT_PCM_WAV) - return false; - - sample_rate = inputs.sample_rate; - num_channels = inputs.num_channels; - rec_mono_mode = inputs.rec_mono_mode; - err = 0; - - /* configure the buffer system */ - params.afmt = AFMT_PCM_WAV; - enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2; - params.chunk_size = enc_size; - params.enc_sample_rate = sample_rate; - params.reserve_bytes = 0; - params.events_callback = enc_events_callback; - ci->enc_set_parameters(¶ms); - - return true; -} /* init_encoder */ + frame_htole((uint32_t *)data->data, frame_size); -/* this is the codec entry point */ -enum codec_status codec_main(enum codec_entry_call_reason reason) -{ - if (reason == CODEC_LOAD) { - if (!init_encoder()) - return CODEC_ERROR; - } - else if (reason == CODEC_UNLOAD) { - /* reset parameters to initial state */ - ci->enc_set_parameters(NULL); + ci->enc_pcmbuf_advance(PCM_SAMP_PER_CHUNK); + ci->enc_encbuf_finish_buffer(); } return CODEC_OK; } -/* this is called for each file to process */ -enum codec_status codec_run(void) +/* this is called by recording system */ +int ICODE_ATTR enc_callback(enum enc_callback_reason reason, + void *params) { - /* main encoding loop */ - while(ci->get_command(NULL) != CODEC_ACTION_HALT) + if (LIKELY(reason == ENC_CB_STREAM)) { - uint32_t *src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE); - struct enc_chunk_hdr *chunk; - - if(src == NULL) - continue; - - chunk = ci->enc_get_chunk(); - chunk->enc_size = enc_size; - chunk->num_pcm = PCM_SAMP_PER_CHUNK; - chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); - - chunk_to_wav_format(src, (uint32_t *)chunk->enc_data); - - ci->enc_finish_chunk(); + switch (((union enc_chunk_hdr *)params)->type) + { + case CHUNK_T_DATA: + return on_stream_data(params); + case CHUNK_T_STREAM_START: + return on_stream_start(); + case CHUNK_T_STREAM_END: + return on_stream_end(params); + } + } + else if (reason == ENC_CB_INPUTS) + { + struct enc_inputs *inputs = params; + sample_rate = inputs->sample_rate; + num_channels = inputs->num_channels; + frame_size = PCM_SAMP_PER_CHUNK*PCM_DEPTH_BYTES*num_channels; } - return CODEC_OK; + return 0; } |