summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/rbcodec/codecs/aiff_enc.c399
-rw-r--r--lib/rbcodec/codecs/codecs.h45
-rw-r--r--lib/rbcodec/codecs/mp3_enc.c813
-rw-r--r--lib/rbcodec/codecs/wav_enc.c376
-rw-r--r--lib/rbcodec/codecs/wavpack_enc.c453
5 files changed, 862 insertions, 1224 deletions
diff --git a/lib/rbcodec/codecs/aiff_enc.c b/lib/rbcodec/codecs/aiff_enc.c
index 8e9246d..fb8db38 100644
--- a/lib/rbcodec/codecs/aiff_enc.c
+++ b/lib/rbcodec/codecs/aiff_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
@@ -47,10 +48,15 @@ struct aiff_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;
+static uint32_t sample_rate;
+static size_t frame_size;
+static size_t pcm_size;
+static uint32_t num_sample_frames;
/* Template headers */
-struct aiff_header aiff_header =
+static const struct aiff_header aiff_template_header =
{
{ 'F', 'O', 'R', 'M' }, /* form_id */
0, /* form_size (*) */
@@ -65,336 +71,193 @@ struct aiff_header aiff_header =
0, /* ssnd_size (*) */
htobe32(0), /* offset */
htobe32(0), /* block_size */
+ /* (*) updated when finalizing stream */
};
-/* (*) updated when finalizing file */
-
-static int num_channels IBSS_ATTR;
-static int rec_mono_mode IBSS_ATTR;
-static uint32_t sample_rate;
-static uint32_t enc_size;
-static int32_t err IBSS_ATTR;
+static inline void frame_htobe(uint32_t *p, size_t size)
+{
+#ifdef ROCKBOX_LITTLE_ENDIAN
+ /* Byte-swap samples, stereo or mono */
+ do
+ {
+ 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_LITTLE_ENDIAN */
+ (void)p; (void)size;
+}
/* convert unsigned 32 bit value to 80-bit floating point number */
static void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l)
- ICODE_ATTR;
-static void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l)
{
- int32_t exp;
-
ci->memset(f, 0, 10);
if (l == 0)
return;
- for (exp = 30; (l & (1ul << 31)) == 0; exp--)
- l <<= 1;
+ int shift = __builtin_clz(l);
/* sign always zero - bit 79 */
- /* exponent is 0-31 (normalized: 31 - shift + 16383) - bits 64-78 */
+ /* exponent is 0-31 (normalized: 30 - shift + 16383) - bits 64-78 */
f[0] = 0x40;
- f[1] = (uint8_t)exp;
+ f[1] = (uint8_t)(30 - shift);
/* mantissa is value left justified with most significant non-zero
bit stored in bit 63 - bits 0-63 */
+ l <<= shift;
f[2] = (uint8_t)(l >> 24);
f[3] = (uint8_t)(l >> 16);
f[4] = (uint8_t)(l >> 8);
f[5] = (uint8_t)(l >> 0);
-} /* uint32_h_to_ieee754_extended_be */
-
-/* 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)
-{
- 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)
+static int on_stream_data(struct enc_chunk_data *data)
{
- if (!is_file_data_ok(data))
- return false;
+ size_t size = data->hdr.size;
- if (data->chunk->enc_data == NULL)
- {
-#ifdef ROCKBOX_HAS_LOGF
- ci->logf("aiff enc: NULL data");
-#endif
- return true;
- }
+ if (ci->enc_stream_write(data->data, size) != (ssize_t)size)
+ return -1;
- if (ci->write(data->rec_file, data->chunk->enc_data,
- data->chunk->enc_size) != (ssize_t)data->chunk->enc_size)
- return false;
+ pcm_size += size;
+ num_sample_frames += data->pcm_count;
- data->num_pcm_samples += data->chunk->num_pcm;
- return true;
-} /* on_write_chunk */
+ return 0;
+}
-static bool on_start_file(struct enc_file_event_data *data)
+static int on_stream_start(void)
{
- if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
- return false;
-
- data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
-
- if (data->rec_file < 0)
- return false;
-
/* reset sample count */
- data->num_pcm_samples = 0;
+ pcm_size = 0;
+ num_sample_frames = 0;
- /* write template headers */
- if (ci->write(data->rec_file, &aiff_header, sizeof (aiff_header))
- != sizeof (aiff_header))
- {
- return false;
- }
+ /* write template header */
+ if (ci->enc_stream_write(&aiff_template_header,
+ sizeof (struct aiff_header))
+ != sizeof (struct aiff_header))
+ return -1;
- data->new_enc_size += sizeof(aiff_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 headers */
- struct aiff_header hdr;
- uint32_t data_size;
+ /* update template header */
+ struct aiff_header aiff;
- if (!is_file_data_ok(data))
- return false;
-
- if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
- 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 (aiff))
+ {
+ pcm_size = size - sizeof (aiff);
+ num_sample_frames = pcm_size / (PCM_DEPTH_BYTES*num_channels);
+ }
}
- data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES;
+ if (ci->enc_stream_lseek(0, SEEK_SET) != 0)
+ return -1;
+
+ if (ci->enc_stream_read(&aiff, sizeof (aiff)) != sizeof (aiff))
+ return -2;
/* 'FORM' chunk */
- hdr.form_size = htobe32(data_size + sizeof (hdr) - 8);
+ aiff.form_size = htobe32(pcm_size + sizeof (aiff) - 8);
/* 'COMM' chunk */
- hdr.num_channels = htobe16(num_channels);
- hdr.num_sample_frames = htobe32(data->num_pcm_samples);
- uint32_h_to_ieee754_extended_be(hdr.sample_rate, sample_rate);
+ aiff.num_channels = htobe16(num_channels);
+ aiff.num_sample_frames = htobe32(num_sample_frames);
+ uint32_h_to_ieee754_extended_be(aiff.sample_rate, sample_rate);
/* 'SSND' chunk */
- hdr.ssnd_size = htobe32(data_size + 8);
+ aiff.ssnd_size = htobe32(pcm_size + 8);
- 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 -3;
- data->rec_file = -1;
+ if (ci->enc_stream_write(&aiff, sizeof (aiff)) != sizeof (aiff))
+ return -4;
- 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 aiff 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 = (int16_t)lr1;
- lr2 = *(*src)++;
- lr2 = (int16_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)++ = htobe32((lr1 << 16) | (uint16_t)lr2);
-} /* sample_to_mono */
-static void chunk_to_aiff_format(uint32_t *src, uint32_t *dst) ICODE_ATTR;
-static void chunk_to_aiff_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
- {
- 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);
- }
- while (src < src_end);
- }
- else
- {
-#ifdef ROCKBOX_BIG_ENDIAN
- /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| =>
- * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
- */
- ci->memcpy(dst, src, PCM_CHUNK_SIZE);
-#else
- /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| =>
- * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr|
- */
- uint32_t *src_end = src + PCM_SAMP_PER_CHUNK;
-
- do
+ /* First obtain output buffer; when available, get PCM data */
+ switch (getbuf)
{
- *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++);
+ 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);
-#endif
- }
-} /* chunk_to_aiff_format */
-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_AIFF)
- 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_AIFF;
- 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(&params);
-
- return true;
-} /* init_encoder */
+ data->hdr.size = frame_size;
+ data->pcm_count = PCM_SAMP_PER_CHUNK;
-/* 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);
+ frame_htobe((uint32_t *)data->data, frame_size);
+
+ 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_aiff_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;
}
diff --git a/lib/rbcodec/codecs/codecs.h b/lib/rbcodec/codecs/codecs.h
index ae4233b..672b1de 100644
--- a/lib/rbcodec/codecs/codecs.h
+++ b/lib/rbcodec/codecs/codecs.h
@@ -36,7 +36,7 @@
#endif
#if (CONFIG_CODEC == SWCODEC)
#ifdef HAVE_RECORDING
-#include "pcm_record.h"
+#include "enc_base.h"
#endif
#include "dsp_core.h"
#include "dsp_misc.h"
@@ -72,12 +72,12 @@
#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */
/* increase this every time the api struct changes */
-#define CODEC_API_VERSION 45
+#define CODEC_API_VERSION 46
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
new function which are "waiting" at the end of the function table) */
-#define CODEC_MIN_API_VERSION 45
+#define CODEC_MIN_API_VERSION 46
/* reasons for calling codec main entrypoint */
enum codec_entry_call_reason {
@@ -96,6 +96,9 @@ enum codec_command_action {
CODEC_ACTION_HALT = -1,
CODEC_ACTION_NULL = 0,
CODEC_ACTION_SEEK_TIME = 1,
+#ifdef HAVE_RECORDING
+ CODEC_ACTION_STREAM_FINISH = 2,
+#endif
};
/* NOTE: To support backwards compatibility, only add new functions at
@@ -200,24 +203,18 @@ struct codec_api {
#endif
#ifdef HAVE_RECORDING
- void (*enc_get_inputs)(struct enc_inputs *inputs);
- void (*enc_set_parameters)(struct enc_parameters *params);
- struct enc_chunk_hdr * (*enc_get_chunk)(void);
- void (*enc_finish_chunk)(void);
- unsigned char * (*enc_get_pcm_data)(size_t size);
- size_t (*enc_unget_pcm_data)(size_t size);
-
- /* file */
- int (*open)(const char* pathname, int flags, ...);
- int (*close)(int fd);
- ssize_t (*read)(int fd, void* buf, size_t count);
- off_t (*lseek)(int fd, off_t offset, int whence);
- ssize_t (*write)(int fd, const void* buf, size_t count);
+ int (*enc_pcmbuf_read)(void *buf, int count);
+ int (*enc_pcmbuf_advance)(int count);
+ struct enc_chunk_data * (*enc_encbuf_get_buffer)(size_t need);
+ void (*enc_encbuf_finish_buffer)(void);
+ ssize_t (*enc_stream_read)(void *buf, size_t count);
+ off_t (*enc_stream_lseek)(off_t offset, int whence);
+ ssize_t (*enc_stream_write)(const void *buf, size_t count);
int (*round_value_to_list32)(unsigned long value,
const unsigned long list[],
int count,
bool signd);
-#endif
+#endif /* HAVE_RECORDING */
/* new stuff at the end, sort into place next time
the API gets incompatible */
@@ -229,6 +226,7 @@ struct codec_header {
enum codec_status(*entry_point)(enum codec_entry_call_reason reason);
enum codec_status(*run_proc)(void);
struct codec_api **api;
+ void * rec_extension[]; /* extension for encoders */
};
#ifdef CODEC
@@ -249,7 +247,7 @@ extern unsigned char plugin_end_addr[];
__attribute__ ((section (".header")))= { \
{ CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
plugin_start_addr, plugin_end_addr }, codec_start, \
- codec_run, &ci };
+ codec_run, &ci, { enc_callback } };
#else /* def SIMULATOR */
/* decoders */
@@ -262,7 +260,7 @@ extern unsigned char plugin_end_addr[];
#define CODEC_ENC_HEADER \
const struct codec_header __header = { \
{ CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \
- codec_start, codec_run, &ci };
+ codec_start, codec_run, &ci, { enc_callback } };
#endif /* SIMULATOR */
#endif /* CODEC */
@@ -277,12 +275,19 @@ void *codec_get_buffer_callback(size_t *size);
int codec_load_buf(int hid, struct codec_api *api);
int codec_load_file(const char* codec, struct codec_api *api);
int codec_run_proc(void);
-int codec_halt(void);
int codec_close(void);
+#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING)
+enc_callback_t codec_get_enc_callback(void);
+#else
+#define codec_get_enc_callback() NULL
+#endif
/* defined by the codec */
enum codec_status codec_start(enum codec_entry_call_reason reason);
enum codec_status codec_main(enum codec_entry_call_reason reason);
enum codec_status codec_run(void);
+#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING)
+int enc_callback(enum enc_callback_reason reason, void *params);
+#endif
#endif /* _CODECS_H_ */
diff --git a/lib/rbcodec/codecs/mp3_enc.c b/lib/rbcodec/codecs/mp3_enc.c
index 000eedd..a349f99 100644
--- a/lib/rbcodec/codecs/mp3_enc.c
+++ b/lib/rbcodec/codecs/mp3_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
@@ -37,17 +38,9 @@
CODEC_ENC_HEADER
-#define ENC_PADDING_FRAMES1 2
-#define ENC_PADDING_FRAMES2 4
-#define ENC_DELAY_SAMP 576
-#define ENC_DELAY_SIZE (ENC_DELAY_SAMP*4)
-#define SAMP_PER_FRAME1 1152
-#define SAMP_PER_FRAME2 576
-#define PCM_CHUNK_SIZE1 (SAMP_PER_FRAME1*4)
-#define PCM_CHUNK_SIZE2 (SAMP_PER_FRAME2*4)
-#define SAMPL2 576
-#define SBLIMIT 32
-#define HTN 16
+#define SAMPL2 576
+#define SBLIMIT 32
+#define HTN 16
#define memcpy ci->memcpy
#define memset ci->memset
#define putlong(c, s) if(s+sz <= 32) { cc = (cc << s) | c; sz+= s; } \
@@ -79,18 +72,24 @@ typedef struct {
} side_info_t;
typedef struct {
- side_info_t cod_info[2][2];
- mpeg_t mpg;
- long frac_per_frame;
- long byte_per_frame;
- long slot_lag;
- int sideinfo_len;
- int mean_bits;
- int ResvSize;
- int channels;
- int rec_mono_mode;
- int granules;
- long samplerate;
+ side_info_t cod_info[2][2];
+ mpeg_t mpg;
+ long frac_per_frame;
+ long byte_per_frame;
+ long req_byte_per_frame;
+ long slot_lag;
+ int sideinfo_len;
+ int mean_bits;
+ int ResvSize;
+ int channels;
+ int granules;
+ long src_samplerate;
+ long samplerate;
+ short *samp_buffer;
+ unsigned samp_per_frame;
+ int flush_frames;
+ int delay;
+ int padding;
} config_t;
typedef struct {
@@ -118,7 +117,8 @@ struct huffcodebig {
#define shft_n(x,n) ((x) >> n)
#define SQRT 724 /* sqrt(2) * 512 */
-static short mfbuf [2*(1152+512)] IBSS_ATTR; /* 3328 Bytes */
+static short mfbuf [2*(1152+512)] IBSS_ATTR
+ /* for memcpy and 32-bit access */ MEM_ALIGN_ATTR; /* 3328 Bytes */
static int sb_data [2][2][18][SBLIMIT] IBSS_ATTR; /* 13824 Bytes */
static int mdct_freq [SAMPL2] IBSS_ATTR; /* 2304 Bytes */
static char mdct_sign [SAMPL2] IBSS_ATTR; /* 576 Bytes */
@@ -171,12 +171,7 @@ static uint8_t t16l [256] IBSS_ATTR;
static uint8_t t24l [256] IBSS_ATTR;
static struct huffcodetab ht [HTN] IBSS_ATTR;
-static unsigned pcm_chunk_size IBSS_ATTR;
-static unsigned samp_per_frame IBSS_ATTR;
-
static config_t cfg IBSS_ATTR;
-static char *res_buffer;
-static int32_t err IBSS_ATTR;
static uint8_t band_scale_f[22];
static const uint8_t ht_count_const[2][2][16] =
@@ -848,42 +843,56 @@ static int count_bit1 ( short *ix, uint32_t start, uint32_t end, int *bits );
static int count_bigv ( short *ix, uint32_t start, uint32_t end, int table0, int table1,
int *bits);
+static inline uint32_t encodeHeader( int padding, long bitr_id )
+{
+ /*
+ * MPEG header layout:
+ * AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
+ * A (31-21) = frame sync
+ * B (20-19) = MPEG type
+ * C (18-17) = MPEG layer
+ * D (16) = protection bit
+ * E (15-12) = bitrate index
+ * F (11-10) = samplerate index
+ * G (9) = padding bit
+ * H (8) = private bit
+ * I (7-6) = channel mode
+ * J (5-4) = mode extension (jstereo only)
+ * K (3) = copyright bit
+ * L (2) = original
+ * M (1-0) = emphasis
+ */
+ return (0xffe00000 ) /* frame sync (AAAAAAAAA AAA) */
+ | (0x2 << 19) /* mp3 type (upper): 1 (BB) */
+ | (cfg.mpg.type << 19)
+ | (0x1 << 17) /* mp3 layer: 01 (CC) */
+ | (0x1 << 16) /* mp3 crc: 1 (D) */
+ | (bitr_id << 12)
+ | (cfg.mpg.smpl_id << 10)
+ | (padding << 9)
+ | (cfg.mpg.mode << 6)
+ | (0x1 << 2); /* mp3 org: 1 (L) */
+ /* no emphasis (bits 0-1) */
+}
+
+static long calcFrameSize(int bitr_id, long *frac)
+{
+ unsigned long v = bitr_index[cfg.mpg.type][bitr_id];
+ v = SAMPL2 * 16000 * v / (2 - cfg.mpg.type);
+ v /= cfg.samplerate;
+
+ if (frac)
+ *frac = v % 64;
+
+ return v / 64;
+}
static void encodeSideInfo( side_info_t si[2][2] )
{
- int gr, ch, header;
+ int gr, ch;
uint32_t cc=0, sz=0;
- /*
- * MPEG header layout:
- * AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
- * A (31-21) = frame sync
- * B (20-19) = MPEG type
- * C (18-17) = MPEG layer
- * D (16) = protection bit
- * E (15-12) = bitrate index
- * F (11-10) = samplerate index
- * G (9) = padding bit
- * H (8) = private bit
- * I (7-6) = channel mode
- * J (5-4) = mode extension (jstereo only)
- * K (3) = copyright bit
- * L (2) = original
- * M (1-0) = emphasis
- */
-
- header = (0xfff00000) | /* frame sync (AAAAAAAAA AAA)
- mp3 type (upper): 1 (B) */
- (0x01 << 17) | /* mp3 layer: 01 (CC) */
- ( 0x1 << 16) | /* mp3 crc: 1 (D) */
- ( 0x1 << 2); /* mp3 org: 1 (L) */
- header |= cfg.mpg.type << 19;
- header |= cfg.mpg.bitr_id << 12;
- header |= cfg.mpg.smpl_id << 10;
- header |= cfg.mpg.padding << 9;
- header |= cfg.mpg.mode << 6;
- /* no emphasis (bits 0-1) */
- putbits( header, 32 );
+ putbits( encodeHeader( cfg.mpg.padding, cfg.mpg.bitr_id ), 32 );
if(cfg.mpg.type == 1)
{ /* MPEG1 */
@@ -1501,8 +1510,8 @@ static void iteration_loop(int *xr, side_info_t *si, int gr_cnt)
/* returns sum_j=0^31 a[j]*cos(PI*j*(k+1/2)/32), 0<=k<32 */
-void window_subband1(short *wk, int sb0[SBLIMIT], int sb1[SBLIMIT]) ICODE_ATTR;
-void window_subband1(short *wk, int sb0[SBLIMIT], int sb1[SBLIMIT])
+static void ICODE_ATTR window_subband1(short *wk, int sb0[SBLIMIT],
+ int sb1[SBLIMIT])
{
int k, i, u, v;
short *wp, *x1, *x2;
@@ -1761,8 +1770,7 @@ void window_subband1(short *wk, int sb0[SBLIMIT], int sb1[SBLIMIT])
#endif
}
-void window_subband2(short *x1, int a[SBLIMIT]) ICODE_ATTR;
-void window_subband2(short *x1, int a[SBLIMIT])
+static void ICODE_ATTR window_subband2(short *x1, int a[SBLIMIT])
{
int xr;
short *wp = enwindow;
@@ -1879,8 +1887,7 @@ void window_subband2(short *x1, int a[SBLIMIT])
xr = a[29]; a[29] += a[ 2]; a[ 2] -= xr;
}
-void mdct_long(int *out, int *in) ICODE_ATTR;
-void mdct_long(int *out, int *in)
+static void ICODE_ATTR mdct_long(int *out, int *in)
{
int ct,st;
int tc1, tc2, tc3, tc4, ts5, ts6, ts7, ts8;
@@ -1969,44 +1976,51 @@ static int find_samplerate_index(long freq, int *mp3_type)
return i;
}
-static bool init_mp3_encoder_engine(int sample_rate,
- int num_channels,
- int rec_mono_mode,
- struct encoder_config *enc_cfg)
+static void mp3_encoder_reset(void)
+{
+ memset(&cfg.cod_info, 0, sizeof(cfg.cod_info));
+ memset(mfbuf , 0, sizeof(mfbuf ));
+ memset(mdct_freq , 0, sizeof(mdct_freq ));
+ memset(enc_data , 0, sizeof(enc_data ));
+ memset(sb_data , 0, sizeof(sb_data ));
+ memset(&CodedData , 0, sizeof(CodedData ));
+ cfg.slot_lag = 0;
+}
+
+static void mp3_encoder_init(unsigned long sample_rate, int num_channels,
+ unsigned long bitrate)
{
- const bool stereo = num_channels > 1;
- uint32_t avg_byte_per_frame;
-
- cfg.channels = stereo ? 2 : 1;
- cfg.rec_mono_mode = rec_mono_mode;
- cfg.mpg.mode = stereo ? 0 : 3; /* 0=stereo, 3=mono */
- cfg.mpg.smpl_id = find_samplerate_index(sample_rate, &cfg.mpg.type);
- cfg.samplerate = sampr_index[cfg.mpg.type][cfg.mpg.smpl_id];
- cfg.mpg.bitr_id = find_bitrate_index(cfg.mpg.type,
- enc_cfg->mp3_enc.bitrate,
- stereo);
- cfg.mpg.bitrate = bitr_index[cfg.mpg.type][cfg.mpg.bitr_id];
- cfg.mpg.num_bands = num_bands[stereo ? cfg.mpg.type : 2][cfg.mpg.bitr_id];
+ mp3_encoder_reset();
+
+ const bool stereo = num_channels > 1;
+ cfg.channels = stereo ? 2 : 1;
+ cfg.mpg.mode = stereo ? 0 : 3; /* 0=stereo, 3=mono */
+ cfg.mpg.smpl_id = find_samplerate_index(sample_rate, &cfg.mpg.type);
+ cfg.samplerate = sampr_index[cfg.mpg.type][cfg.mpg.smpl_id];
+ cfg.src_samplerate = sample_rate;
+ cfg.mpg.bitr_id = find_bitrate_index(cfg.mpg.type, bitrate, stereo);
+ cfg.mpg.bitrate = bitr_index[cfg.mpg.type][cfg.mpg.bitr_id];
+ cfg.mpg.num_bands = num_bands[stereo ? cfg.mpg.type : 2][cfg.mpg.bitr_id];
if (cfg.mpg.type == 1)
{
- cfg.granules = 2;
- pcm_chunk_size = PCM_CHUNK_SIZE1;
- samp_per_frame = SAMP_PER_FRAME1;
+ cfg.granules = 2;
+ cfg.samp_per_frame = 1152;
+ cfg.flush_frames = 2;
}
else
{
- cfg.granules = 1;
- pcm_chunk_size = PCM_CHUNK_SIZE2;
- samp_per_frame = SAMP_PER_FRAME2;
+ cfg.granules = 1;
+ cfg.samp_per_frame = 576;
+ cfg.flush_frames = 3;
}
+ cfg.delay = 576-16;
+ cfg.padding = 3*576+16;
+
+ cfg.samp_buffer = mfbuf + 2*512;
+
memcpy(scalefac, sfBand[cfg.mpg.smpl_id + 3*cfg.mpg.type], sizeof(scalefac));
- memset(mfbuf , 0 , sizeof(mfbuf ));
- memset(mdct_freq , 0 , sizeof(mdct_freq ));
- memset(enc_data , 0 , sizeof(enc_data ));
- memset(sb_data , 0 , sizeof(sb_data ));
- memset(&CodedData, 0 , sizeof(CodedData ));
memcpy(ca , ca_const , sizeof(ca ));
memcpy(cs , cs_const , sizeof(cs ));
memcpy(cx , cx_const , sizeof(cx ));
@@ -2052,6 +2066,7 @@ static bool init_mp3_encoder_engine(int sample_rate,
memcpy(t16l , t16l_const , sizeof(t16l ));
memcpy(t24l , t24l_const , sizeof(t24l ));
memcpy(ht , ht_const , sizeof(ht ));
+ memset(band_scale_f, 0 , sizeof(band_scale_f));
ht[ 0].table = NULL; ht[ 0].hlen = NULL; /* Apparently not used */
ht[ 1].table = t1HB; ht[ 1].hlen = t1l;
@@ -2071,90 +2086,14 @@ static bool init_mp3_encoder_engine(int sample_rate,
ht[15].table = t15HB; ht[15].hlen = t15l;
/* Figure average number of 'bytes' per frame */
- avg_byte_per_frame = SAMPL2 * 16000 * cfg.mpg.bitrate / (2 - cfg.mpg.type);
- avg_byte_per_frame = avg_byte_per_frame / cfg.samplerate;
- cfg.byte_per_frame = avg_byte_per_frame / 64;
- cfg.frac_per_frame = avg_byte_per_frame & 63;
- cfg.slot_lag = 0;
+ cfg.byte_per_frame = calcFrameSize(cfg.mpg.bitr_id, &cfg.frac_per_frame);
cfg.sideinfo_len = 32 + (cfg.mpg.type ? (cfg.channels == 1 ? 136 : 256)
: (cfg.channels == 1 ? 72 : 136));
- return true;
+ cfg.req_byte_per_frame = ALIGN_UP(cfg.byte_per_frame + 1,
+ sizeof (uint32_t));
}
-static inline void to_mono(uint16_t **samp)
-{
- int16_t l = **samp;
- int16_t r = *(*samp+1);
- int32_t m;
-
- switch(cfg.rec_mono_mode)
- {
- case 1:
- /* mono = L */
- m = l;
- break;
- case 2:
- /* mono = R */
- m = r;
- break;
- case 0:
- default:
- /* mono = (L+R)/2 */
- m = l + r + err;
- err = m & 1;
- m >>= 1;
- break;
- }
- *(*samp)++ = (uint16_t)m;
- *(*samp)++ = (uint16_t)m;
-} /* to_mono */
-
-static void to_mono_mm(void) ICODE_ATTR;
-static void to_mono_mm(void)
-{
- /* |llllllllllllllll|rrrrrrrrrrrrrrrr| =>
- * |mmmmmmmmmmmmmmmm|mmmmmmmmmmmmmmmm|
- */
- uint16_t *samp = &mfbuf[2*512];
- uint16_t *samp_end = samp + 2*samp_per_frame;
-
- do
- {
- to_mono(&samp);
- to_mono(&samp);
- to_mono(&samp);
- to_mono(&samp);
- to_mono(&samp);
- to_mono(&samp);
- to_mono(&samp);
- to_mono(&samp);
- }
- while (samp < samp_end);
-} /* to_mono_mm */
-
-#ifdef ROCKBOX_LITTLE_ENDIAN
-/* Swaps a frame to big endian */
-static inline void byte_swap_frame32(uint32_t *dst, uint32_t *src,
- size_t size)
-{
- uint32_t *src_end = SKIPBYTES(src, size);
-
- do
- {
- *dst++ = swap32(*src++);
- *dst++ = swap32(*src++);
- *dst++ = swap32(*src++);
- *dst++ = swap32(*src++);
- *dst++ = swap32(*src++);
- *dst++ = swap32(*src++);
- *dst++ = swap32(*src++);
- *dst++ = swap32(*src++);
- }
- while(src < src_end);
-} /* byte_swap_frame32 */
-#endif /* ROCKBOX_LITTLE_ENDIAN */
-
static void set_scale_facs(int *mdct_freq)
{
unsigned int i, is, ie, k, s;
@@ -2188,12 +2127,10 @@ static void set_scale_facs(int *mdct_freq)
}
}
-static void encode_frame(char *buffer, struct enc_chunk_hdr *chunk)
- ICODE_ATTR;
-static void encode_frame(char *buffer, struct enc_chunk_hdr *chunk)
+static size_t ICODE_ATTR mp3_encoder_encode_frame(uint8_t *outbuf)
{
- int gr, gr_cnt;
- uint32_t max;
+ int gr, gr_cnt;
+ uint32_t max;
/* encode one mp3 frame in this loop */
CodedData.bitpos = 0;
@@ -2211,28 +2148,6 @@ static void encode_frame(char *buffer, struct enc_chunk_hdr *chunk)
- cfg.sideinfo_len) / cfg.granules / cfg.channels
- 42; // reserved for scale_facs
- /* shift out old samples */
- memcpy(mfbuf, mfbuf + 2*cfg.granules*576, 4*512);
-
- if (chunk->flags & CHUNKF_START_FILE)
- {
- /* prefix silent samples for encoder delay */
- memset(mfbuf + 2*512, 0, ENC_DELAY_SIZE);
- /* read new samples to iram for further processing */
- memcpy(mfbuf + 2*512 + ENC_DELAY_SIZE/2,
- buffer, pcm_chunk_size - ENC_DELAY_SIZE);
- chunk->num_pcm = samp_per_frame - ENC_DELAY_SAMP;
- }
- else
- {
- /* read new samples to iram for further processing */
- memcpy(mfbuf + 2*512, buffer, pcm_chunk_size);
- chunk->num_pcm = samp_per_frame;
- }
-
- if (cfg.channels == 1)
- to_mono_mm();
-
cfg.ResvSize = 0;
gr_cnt = cfg.granules * cfg.channels;
CodedData.bitpos = cfg.sideinfo_len; /* leave space for mp3 header */
@@ -2366,264 +2281,398 @@ static void encode_frame(char *buffer, struct enc_chunk_hdr *chunk)
}
}
- chunk->enc_size = cfg.byte_per_frame + cfg.mpg.padding;
+ /* shift out old samples */
+ memmove(mfbuf, mfbuf + 2*cfg.granules*576, 4*512);
/* finish this chunk by adding sideinfo header data */
CodedData.bitpos = 0;
encodeSideInfo( cfg.cod_info );
-#ifdef ROCKBOX_BIG_ENDIAN
- /* copy chunk to enc_buffer */
- memcpy(chunk->enc_data, CodedData.bbuf, chunk->enc_size);
+ long size = cfg.byte_per_frame + cfg.mpg.padding;
+
+#ifdef ROCKBOX_LITTLE_ENDIAN
+ /* convert frame to big endian */
+ const uint32_t *src = CodedData.bbuf;
+ uint32_t *dst = (uint32_t *)outbuf;
+
+ for(long i = 0; i < size; i += sizeof(uint32_t))
+ *dst++ = swap32(*src++);
#else
- /* swap frame to big endian */
- byte_swap_frame32((uint32_t *)chunk->enc_data, CodedData.bbuf, chunk->enc_size);
-#endif
-} /* encode_frame */
+ memcpy(outbuf, CodedData.bbuf, size);
+#endif /* ROCKBOX_LITTLE_ENDIAN */
-/* called very often - inline */
-static inline bool is_file_data_ok(struct enc_file_event_data *filed)
-{
- return filed->rec_file >= 0 && (long)filed->chunk->flags >= 0;
-} /* is_event_ok */
+ return size;
+}
-static unsigned char mp3_data[16384] __attribute__((aligned(4)));
-static unsigned int mp3_data_len; /* current data size in buffer */
-/* called very often - inline */
-static inline bool on_write_chunk(struct enc_file_event_data *data)
-{
- if (!is_file_data_ok(data))
- return false;
+/*======== Codec section ========*/
- if (data->chunk->enc_data == NULL)
- {
-#ifdef ROCKBOX_HAS_LOGF
- ci->logf("mp3 enc: NULL data");
-#endif
- return true;
- }
+/* CRC code lovingly ripped from:
+ * github.com/CFR-maniac/lame/blob/master/libmp3lame/VbrTag.c */
- /* if current chunk doesn't fit => write collected data */
- if (mp3_data_len + data->chunk->enc_size > sizeof(mp3_data))
- {
- if (ci->write(data->rec_file, mp3_data,
- mp3_data_len) != (ssize_t)mp3_data_len)
- return false;
+/* Lookup table for fast CRC computation
+ * See 'crc_update_lookup'
+ * Uses the polynomial x^16+x^15+x^2+1 */
+static const uint16_t crc16_lookup[256] ICONST_ATTR =
+{
+ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
+};
- mp3_data_len = 0;
- }
+static ssize_t header_size;
+static unsigned int mp3_crc16;
- memcpy(mp3_data+mp3_data_len, data->chunk->enc_data,
- data->chunk->enc_size);
+/* fast CRC-16 computation - uses table crc16_lookup 8*/
+static inline unsigned int crc_update_lookup(unsigned int value,
+ unsigned int crc)
+{
+ unsigned int tmp = crc ^ value;
+ crc = (crc >> 8) ^ crc16_lookup[tmp & 0xff];
+ return crc & 0xffff;
+}
- mp3_data_len += data->chunk->enc_size;
+/* Calculate position of 'Info' header */
+static int get_info_offset(uint32_t header)
+{
+ uint32_t type = (header & (0x3 << 19)) >> 19;
+ uint32_t mode = (header & (0x3 << 6)) >> 6;
- data->num_pcm_samples += data->chunk->num_pcm;
- return true;
-} /* on_write_chunk */
+ return type == 3 ? (mode == 3 ? 21 : 36) : (mode == 3 ? 13 : 21);
+}
-static bool on_start_file(struct enc_file_event_data *data)
+/* Write very basic 'Info' header with delay, padding and a bit of
+ * miscellaneous info. */
+static bool write_info_header(bool first_encode)
{
- if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
- return false;
+ ssize_t size = cfg.byte_per_frame;
- data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ /* By default the MP3 frame header for the info frame is the same as
+ unpadded audio frames */
+ uint32_t header = encodeHeader(0, cfg.mpg.bitr_id);
- if (data->rec_file < 0)
- return false;
+ int i = get_info_offset(header);
- /* reset sample count */
- data->num_pcm_samples = 0;
+ if (i + 8 + 36 > size)
+ {
+ /* The default frame size too small so find the smallest one that
+ may accomodate it by increasing the bit rate for this empty
+ MP3 frame */
+ int j;
+ for (j = cfg.mpg.bitr_id + 1; j < 15; j++)
+ {
+ size = calcFrameSize(j, NULL);
- /* reset buffer write position */
- mp3_data_len = 0;
+ if (size >= i + 8 + 36)
+ break;
+ }
- return true;
-} /* on_start_file */
+ if (j >= 15)
+ {
+ /* Shouldn't really happen but... */
+ header_size = -1;
+ return true;
+ }
-static bool on_end_file(struct enc_file_event_data *data)
-{
- if (data->rec_file < 0)
- return false; /* file already closed, nothing more we can do */
+ header = encodeHeader(0, j);
+ /* Info offset won't change */
+ }
- /* write the remaining mp3_data */
- if (ci->write(data->rec_file, mp3_data, mp3_data_len)
- != (ssize_t)mp3_data_len)
- return false;
+ uint8_t frame[size];
+ memset(frame, 0, size);
- /* reset buffer write position */
- mp3_data_len = 0;
+ frame[0] = header >> 24;
+ frame[1] = header >> 16;
+ frame[2] = header >> 8;
+ frame[3] = header >> 0;
- /* always _try_ to write the file header, even on error */
- if (ci->close(data->rec_file) != 0)
- return false;
+ /* 'Info' header (CBR 'Xing') */
+ memcpy(&frame[i], "Info", 4);
- data->rec_file = -1;
+ /* flags = 0; Info contains no other sections and is 8 bytes */
- return true;
-} /* on_end_file */
+ /* Just mark the LAMEness to indicate header presence; we're not
+ actually _the_ LAME so 'rbshn' is the version we give */
+ memcpy(&frame[i + 8], "LAMErbshn", 9);
-static void on_rec_new_stream(struct enc_buffer_event_data *data)
-{
- int num_frames = cfg.mpg.type == 1 ?
- ENC_PADDING_FRAMES1 : ENC_PADDING_FRAMES2;
+ /* Fill-in some info about us
+ * reference: http://gabriel.mp3-tech.org/mp3infotag.html
+ */
- if (data->flags & CHUNKF_END_FILE)
- {
- /* add silent frames to end - encoder will also be flushed for start
- of next file if any */
- memset(res_buffer, 0, pcm_chunk_size);
+ /* Revision + VBR method:
+ * [7:4] = Revision (0 ??)
+ * [3:0] = VBR method (CBR)
+ */
+ frame[i + 17] = (0 << 4) | (1 << 0);
- /* the initial chunk given for the end is at enc_wr_index */
- while (num_frames-- > 0)
- {
- data->chunk->enc_data = ENC_CHUNK_SKIP_HDR(data->chunk->enc_data,
- data->chunk);
+ /* If first frame since encoder reset is long gone (not unlikely in
+ prerecording), then the delay is long passed and no trimming done
+ at the start */
+ unsigned int delay = first_encode ? cfg.delay : 0;
+ unsigned int padding = cfg.padding;
- encode_frame(res_buffer, data->chunk);
- data->chunk->num_pcm = samp_per_frame;
+ /* Delay and padding:
+ * [23:12] = delay
+ * [11: 0] = padding
+ */
+ frame[i + 29] = delay >> 4;
+ frame[i + 30] = (delay << 4) | (padding >> 8);
+ frame[i + 31] = padding;
+
+ /* Misc:
+ * [7:6] = source frequency
+ * [ 5] = unwise settings (of course not :)
+ * [4:2] = stereo mode (mono or stereo)
+ * [1:0] = noise shaping (who knows, 0)
+ */
+ uint8_t misc;
- ci->enc_finish_chunk();
- data->chunk = ci->enc_get_chunk();
- }
- }
- else if (data->flags & CHUNKF_PRERECORD)
- {
- /* nothing to add and we cannot change prerecorded data */
- }
- else if (data->flags & CHUNKF_START_FILE)
- {
- /* starting fresh ... be sure to flush encoder first */
- struct enc_chunk_hdr *chunk = ENC_CHUNK_HDR(res_buffer);
+ if (cfg.src_samplerate <= 32000)
+ misc = (0 << 6);
+ else if (cfg.src_samplerate <= 44100)
+ misc = (1 << 6);
+ else if (cfg.src_samplerate <= 48000)
+ misc = (2 << 6);
+ else /* > 48000 */
+ misc = (3 << 6);
- chunk->flags = 0;
- chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
+ if (cfg.channels > 1)
+ misc |= (1 << 2); /* Stereo */
- while (num_frames-- > 0)
- {
- memset(chunk->enc_data, 0, pcm_chunk_size);
- encode_frame(chunk->enc_data, chunk);
- }
+ frame[i + 32] = misc;
+
+ if (ci->enc_stream_write(frame, size) != size)
+ {
+ ci->enc_stream_lseek(0, SEEK_SET);
+ header_size = -1;
+ return false;
}
-} /* on_rec_new_stream */
-static void enc_events_callback(enum enc_events event, void *data)
+ header_size = size;
+ return true;
+}
+
+static inline int on_stream_data(struct enc_chunk_data *data)
{
- switch (event)
+ ssize_t size = data->hdr.size;
+
+ if (header_size > 0)
{
- case ENC_WRITE_CHUNK:
- if (on_write_chunk((struct enc_file_event_data *)data))
- return;
+ /* Header is layed-down; keep running CRC of audio data */
+ uint8_t *p = data->data;
+ uint8_t *p_end = p + size;
- break;
+ while (p < p_end)
+ mp3_crc16 = crc_update_lookup(*p++, mp3_crc16);
+ }
- case ENC_START_FILE:
- if (on_start_file((struct enc_file_event_data *)data))
- return;
+ if (ci->enc_stream_write(data->data, size) != size)
+ return -1;
- break;
+ return 0;
+}
- case ENC_END_FILE:
- if (on_end_file((struct enc_file_event_data *)data))
- return;
+static int on_stream_start(struct enc_chunk_file *file)
+{
+ mp3_crc16 = 0x0000;
- break;
+ if (!write_info_header(file->hdr.aux0))
+ return -1;
- case ENC_REC_NEW_STREAM:
- on_rec_new_stream((struct enc_buffer_event_data *)data);
- return;
+ return 0;
+}
- default:
- return;
- }
+static int on_stream_end(union enc_chunk_hdr *hdr)
+{
+ ssize_t size = header_size;
- /* Something failed above. Signal error back to core. */
- ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
-} /* enc_events_callback */
+ if (size <= 0)
+ return 0; /* No header possible/none yet written */
-static bool enc_init(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 ||
- ci->enc_unget_pcm_data == NULL )
- return false;
+ /* Update audio CRC and header CRC */
+ uint8_t frame[size];
- ci->enc_get_inputs(&inputs);
+ /* Won't fail this since it could still be useable if some decoder
+ plays loose with the CRC info (like Rockbox :) */
+ if (ci->enc_stream_lseek(0, SEEK_SET) != 0 ||
+ ci->enc_stream_read(frame, size) != size)
+ return 0;
- if (inputs.config->afmt != AFMT_MPA_L3)
- return false;
+ uint32_t header = (frame[0] << 24) | (frame[1] << 16) |
+ (frame[2] << 8) | (frame[3] << 0);
+ int i = get_info_offset(header); /* Get 'Info' header */
- init_mp3_encoder_engine(inputs.sample_rate, inputs.num_channels,
- inputs.rec_mono_mode, inputs.config);
+ /* 'Info' header = 8 bytes */
- err = 0;
+ /* Fill-in audio data CRC16 */
- /* configure the buffer system */
- params.afmt = AFMT_MPA_L3;
- params.chunk_size = cfg.byte_per_frame + 1;
- params.enc_sample_rate = cfg.samplerate;
- /* need enough reserved bytes to hold one frame of pcm samples + hdr
- for padding and flushing */
- params.reserve_bytes = ENC_CHUNK_HDR_SIZE + pcm_chunk_size;
- params.events_callback = enc_events_callback;
- ci->enc_set_parameters(&params);
+ /* On error, fixing data CRC would require scanning file since it
+ has probably dropped something we tried to write and the likely
+ reason is that the disk filled; just leave it 0 in that case. */
+ if (!hdr->err)
+ {
+ frame[i + 40] = mp3_crc16 >> 8;
+ frame[i + 41] = mp3_crc16;
+ }
- res_buffer = params.reserve_buffer;
+ /* Fill-in header CRC16 */
+ unsigned int hdr_crc16 = 0x0000;
+ for (int j = 0; j < i + 42; j++)
+ hdr_crc16 = crc_update_lookup(frame[j], hdr_crc16);
-#ifdef CPU_COLDFIRE
- asm volatile ("move.l #0, %macsr"); /* integer mode */
-#endif
+ frame[i + 42] = hdr_crc16 >> 8;
+ frame[i + 43] = hdr_crc16;
- return true;
-} /* enc_init */
+ /* Update file */
+ if (ci->enc_stream_lseek(0, SEEK_SET) == 0)
+ ci->enc_stream_write(frame, size);
+
+ return 0;
+}
/* this is the codec entry point */
enum codec_status codec_main(enum codec_entry_call_reason reason)
{
- if (reason == CODEC_LOAD) {
- if (!enc_init())
- return CODEC_ERROR;
- }
- else if (reason == CODEC_UNLOAD) {
- /* reset parameters to initial state */
- ci->enc_set_parameters(NULL);
- }
-
+#ifdef CPU_COLDFIRE
+ if (reason == CODEC_LOAD)
+ asm volatile ("move.l #0, %macsr"); /* integer mode */
+#endif
return CODEC_OK;
+ (void)reason;
}
/* this is called for each file to process */
enum codec_status codec_run(void)
{
+ mp3_encoder_reset();
+ uint32_t first = 1;
+
+ /* Needs to do stream finishing steps to flush-out all samples */
+ int frames_rem = -1; /* -1 = indeterminate */
+
+ enum { GETBUF_ENC, GETBUF_PCM } getbuf = GETBUF_ENC;
+ struct enc_chunk_data *data = NULL;
+
/* main encoding loop */
- while(ci->get_command(NULL) != CODEC_ACTION_HALT)
+ while (frames_rem)
{
- char *buffer = buffer = ci->enc_get_pcm_data(pcm_chunk_size);
- struct enc_chunk_hdr *chunk;
+ intptr_t param;
+ enum codec_command_action action = ci->get_command(&param);
- if(buffer == NULL)
- continue;
+ if (action != CODEC_ACTION_NULL)
+ {
+ if (action != CODEC_ACTION_STREAM_FINISH)
+ break;
- chunk = ci->enc_get_chunk();
- chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
+ if (frames_rem < 0)
+ frames_rem = cfg.flush_frames;
- encode_frame(buffer, chunk);
+ /* Reply with required space */
+ *(size_t *)param = cfg.req_byte_per_frame*frames_rem;
+ }
- if (chunk->num_pcm < samp_per_frame)
+ /* First obtain output buffer; when available, get PCM data */
+ switch (getbuf)
{
- ci->enc_unget_pcm_data(pcm_chunk_size - chunk->num_pcm*4);
- chunk->num_pcm = samp_per_frame;
+ case GETBUF_ENC:
+ if (!(data = ci->enc_encbuf_get_buffer(cfg.req_byte_per_frame)))
+ continue;
+ getbuf = GETBUF_PCM;
+ case GETBUF_PCM:
+ if (LIKELY(frames_rem < 0))
+ {
+ /* Encoding audio */
+ int count = cfg.samp_per_frame;
+ if (!ci->enc_pcmbuf_read(cfg.samp_buffer, count))
+ continue;
+
+ ci->enc_pcmbuf_advance(cfg.samp_per_frame);
+
+ if (cfg.channels == 1)
+ {
+ /* Interleave the mono samples to stereo as required by
+ encoder */
+ uint16_t *src = cfg.samp_buffer + count;
+ uint32_t *dst = (uint32_t *)(src + count);
+
+ for (int i = count; i > 0; i--)
+ { uint32_t s = *--src; *--dst = s | (s << 16); }
+ }
+ }
+ else
+ {
+ /* Flushing encoder */
+ memset(cfg.samp_buffer, 0, cfg.samp_per_frame*4);
+ frames_rem--;
+ }
+ getbuf = GETBUF_ENC;
}
- ci->enc_finish_chunk();
+ data->hdr.aux0 = first;
+ first = 0;
+ data->hdr.size = mp3_encoder_encode_frame(data->data);
+ data->pcm_count = cfg.samp_per_frame;
+ ci->enc_encbuf_finish_buffer();
}
return CODEC_OK;
}
+
+/* this is called by recording system */
+int ICODE_ATTR enc_callback(enum enc_callback_reason reason,
+ void *params)
+{
+ if (LIKELY(reason == ENC_CB_STREAM))
+ {
+ 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(params);
+ case CHUNK_T_STREAM_END:
+ return on_stream_end(params);
+ }
+ }
+ else if (reason == ENC_CB_INPUTS)
+ {
+ struct enc_inputs *inputs = params;
+
+ mp3_encoder_init(inputs->sample_rate, inputs->num_channels,
+ inputs->config->mp3_enc.bitrate);
+
+ /* Return the actual configuration */
+ inputs->enc_sample_rate = cfg.samplerate;
+ }
+
+ return 0;
+}
diff --git a/lib/rbcodec/codecs/wav_enc.c b/lib/rbcodec/codecs/wav_enc.c
index 01d0f79..71bb652 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(&params);
-
- 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;
}
diff --git a/lib/rbcodec/codecs/wavpack_enc.c b/lib/rbcodec/codecs/wavpack_enc.c
index 1fae2d4..864012b 100644
--- a/lib/rbcodec/codecs/wavpack_enc.c
+++ b/lib/rbcodec/codecs/wavpack_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
@@ -47,29 +48,39 @@ 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 */
#define RIFF_FMT_DATA_SIZE 16 /* audio_format -> bits_per_sample */
#define RIFF_DATA_HEADER_SIZE 8 /* data_id -> data_size */
+struct wvpk_chunk_data
+{
+ struct enc_chunk_data ckhdr; /* The base data chunk header */
+ WavpackHeader wphdr; /* The block wavpack info */
+ uint8_t data[]; /* Encoded audio data */
+};
+
#define PCM_DEPTH_BITS 16
#define PCM_DEPTH_BYTES 2
#define PCM_SAMP_PER_CHUNK 5000
-#define PCM_CHUNK_SIZE (4*PCM_SAMP_PER_CHUNK)
/** Data **/
-static int8_t input_buffer[PCM_CHUNK_SIZE*2] IBSS_ATTR;
-static WavpackConfig config IBSS_ATTR;
+static int32_t input_buffer[PCM_SAMP_PER_CHUNK*2] IBSS_ATTR;
+
+static WavpackConfig config IBSS_ATTR;
static WavpackContext *wpc;
-static int32_t data_size, input_size, input_step IBSS_ATTR;
-static int32_t err IBSS_ATTR;
+static uint32_t sample_rate;
+static int num_channels;
+static uint32_t total_samples;
+static size_t out_reqsize;
+static size_t frame_size;
static const WavpackMetadataHeader wvpk_mdh =
{
@@ -77,7 +88,7 @@ static const WavpackMetadataHeader wvpk_mdh =
sizeof (struct riff_header) / sizeof (uint16_t),
};
-static const struct riff_header riff_header =
+static const struct riff_header riff_template_header =
{
/* "RIFF" header */
{ 'R', 'I', 'F', 'F' }, /* riff_id */
@@ -96,157 +107,75 @@ 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 */
};
-static inline void sample_to_int32_mono(int32_t **src, int32_t **dst)
-{
- int32_t t = *(*src)++;
- /* endianness irrelevant */
- t = (int16_t)t + (t >> 16) + err;
- err = t & 1;
- *(*dst)++ = t >> 1;
-} /* sample_to_int32_mono */
-
-static inline void sample_to_int32_stereo(int32_t **src, int32_t **dst)
+static inline void sample_to_int32(int32_t **dst, int32_t **src)
{
- int32_t t = *(*src)++;
+ uint32_t t = *(*src)++;
#ifdef ROCKBOX_BIG_ENDIAN
- *(*dst)++ = t >> 16, *(*dst)++ = (int16_t)t;
+ *(*dst)++ = (int32_t)t >> 16;
+ *(*dst)++ = (int16_t)t;
#else
- *(*dst)++ = (int16_t)t, *(*dst)++ = t >> 16;
+ *(*dst)++ = (int16_t)t;
+ *(*dst)++ = (int32_t)t >> 16;
#endif
-} /* sample_to_int32_stereo */
+}
-static void chunk_to_int32(int32_t *src) ICODE_ATTR;
-static void chunk_to_int32(int32_t *src)
+static void ICODE_ATTR input_buffer_to_int32(size_t size)
{
- int32_t *src_end, *dst;
-#ifdef USE_IRAM
- /* copy to IRAM before converting data */
- dst = (int32_t *)input_buffer + PCM_SAMP_PER_CHUNK;
- src_end = dst + PCM_SAMP_PER_CHUNK;
-
- memcpy(dst, src, PCM_CHUNK_SIZE);
-
- src = dst;
-#else
- src_end = src + PCM_SAMP_PER_CHUNK;
-#endif
-
- dst = (int32_t *)input_buffer;
+ int32_t *dst = input_buffer;
+ int32_t *src = input_buffer + PCM_SAMP_PER_CHUNK;
- if (config.num_channels == 1)
+ do
{
- /*
- * |llllllllllllllll|rrrrrrrrrrrrrrrr| =>
- * |mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm|
- */
- do
- {
- /* read 10 longs and write 10 longs */
- sample_to_int32_mono(&src, &dst);
- sample_to_int32_mono(&src, &dst);
- sample_to_int32_mono(&src, &dst);
- sample_to_int32_mono(&src, &dst);
- sample_to_int32_mono(&src, &dst);
- sample_to_int32_mono(&src, &dst);
- sample_to_int32_mono(&src, &dst);
- sample_to_int32_mono(&src, &dst);
- sample_to_int32_mono(&src, &dst);
- sample_to_int32_mono(&src, &dst);
- }
- while(src < src_end);
-
- return;
+ sample_to_int32(&dst, &src);
+ sample_to_int32(&dst, &src);
+ sample_to_int32(&dst, &src);
+ sample_to_int32(&dst, &src);
+ sample_to_int32(&dst, &src);
+ sample_to_int32(&dst, &src);
+ sample_to_int32(&dst, &src);
+ sample_to_int32(&dst, &src);
+ sample_to_int32(&dst, &src);
+ sample_to_int32(&dst, &src);
}
- else
- {
- /*
- * |llllllllllllllll|rrrrrrrrrrrrrrrr| =>
- * |llllllllllllllllllllllllllllllll|rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr|
- */
- do
- {
- /* read 10 longs and write 20 longs */
- sample_to_int32_stereo(&src, &dst);
- sample_to_int32_stereo(&src, &dst);
- sample_to_int32_stereo(&src, &dst);
- sample_to_int32_stereo(&src, &dst);
- sample_to_int32_stereo(&src, &dst);
- sample_to_int32_stereo(&src, &dst);
- sample_to_int32_stereo(&src, &dst);
- sample_to_int32_stereo(&src, &dst);
- sample_to_int32_stereo(&src, &dst);
- sample_to_int32_stereo(&src, &dst);
- }
- while (src < src_end);
-
- return;
- }
-} /* chunk_to_int32 */
-
-/* called very 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)
-{
- return data->rec_file >= 0 && (long)data->chunk->flags >= 0;
-} /* is_file_data_ok */
+ while (size -= 10 * 2 * PCM_DEPTH_BYTES);
+}
-/* called very 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)
+static int on_stream_data(struct wvpk_chunk_data *wpdata)
{
- if (!is_file_data_ok(data))
- return false;
-
- if (data->chunk->enc_data == NULL)
- {
-#ifdef ROCKBOX_HAS_LOGF
- ci->logf("wvpk enc: NULL data");
-#endif
- return true;
- }
-
/* update timestamp (block_index) */
- ((WavpackHeader *)data->chunk->enc_data)->block_index =
- htole32(data->num_pcm_samples);
+ wpdata->wphdr.block_index = htole32(total_samples);
- if (ci->write(data->rec_file, data->chunk->enc_data,
- data->chunk->enc_size) != (ssize_t)data->chunk->enc_size)
- return false;
+ size_t size = wpdata->ckhdr.hdr.size;
+ if (ci->enc_stream_write(wpdata->ckhdr.data, size) != (ssize_t)size)
+ return -1;
- data->num_pcm_samples += data->chunk->num_pcm;
- return true;
-} /* on_write_chunk */
+ total_samples += wpdata->ckhdr.pcm_count;
-static bool on_start_file(struct enc_file_event_data *data)
-{
- if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
- return false;
-
- data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
-
- if (data->rec_file < 0)
- return false;
+ return 0;
+}
+static int on_stream_start(void)
+{
/* reset sample count */
- data->num_pcm_samples = 0;
+ total_samples = 0;
/* write template headers */
- if (ci->write(data->rec_file, &wvpk_mdh, sizeof (wvpk_mdh))
- != sizeof (wvpk_mdh) ||
- ci->write(data->rec_file, &riff_header, sizeof (riff_header))
- != sizeof (riff_header))
- {
- return false;
- }
+ if (ci->enc_stream_write(&wvpk_mdh, sizeof (wvpk_mdh))
+ != sizeof (wvpk_mdh))
+ return -1;
+
+ if (ci->enc_stream_write(&riff_template_header,
+ sizeof (riff_template_header))
+ != sizeof (riff_template_header))
+ return -2;
- data->new_enc_size += sizeof(wvpk_mdh) + 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(void)
{
struct
{
@@ -255,19 +184,16 @@ static bool on_end_file(struct enc_file_event_data *data)
WavpackHeader wph;
} __attribute__ ((packed)) h;
- uint32_t data_size;
-
- if (data->rec_file < 0)
- return false; /* file already closed, nothing more we can do */
-
- /* always _try_ to write the file header, even on error */
+ /* Correcting sizes on error is a bit of a pain */
/* read template headers at start */
- if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
- ci->read(data->rec_file, &h, sizeof (h)) != sizeof (h))
- return false;
+ if (ci->enc_stream_lseek(0, SEEK_SET) != 0)
+ return -1;
- data_size = data->num_pcm_samples*config.num_channels*PCM_DEPTH_BYTES;
+ if (ci->enc_stream_read(&h, sizeof (h)) != sizeof (h))
+ return -2;
+
+ size_t data_size = total_samples*config.num_channels*PCM_DEPTH_BYTES;
/** "RIFF" header **/
h.rhdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE +
@@ -286,121 +212,29 @@ static bool on_end_file(struct enc_file_event_data *data)
/** Wavpack header **/
h.wph.ckSize = htole32(letoh32(h.wph.ckSize) + sizeof (h.wpmdh)
+ sizeof (h.rhdr));
- h.wph.total_samples = htole32(data->num_pcm_samples);
+ h.wph.total_samples = htole32(total_samples);
/* MDH|RIFF|WVPK => WVPK|MDH|RIFF */
- if (ci->lseek(data->rec_file, 0, SEEK_SET)
- != 0 ||
- ci->write(data->rec_file, &h.wph, sizeof (h.wph))
- != sizeof (h.wph) ||
- ci->write(data->rec_file, &h.wpmdh, sizeof (h.wpmdh))
- != sizeof (h.wpmdh) ||
- ci->write(data->rec_file, &h.rhdr, sizeof (h.rhdr))
- != sizeof (h.rhdr) ||
- ci->close(data->rec_file) != 0 )
- {
- return false;
- }
-
- data->rec_file = -1;
-
- return true;
-} /* on_end_file */
+ if (ci->enc_stream_lseek(0, SEEK_SET) != 0)
+ return -3;
-static void enc_events_callback(enum enc_events event, void *data)
- ICODE_ATTR;
-static void enc_events_callback(enum enc_events event, void *data)
-{
- switch (event)
- {
- case ENC_WRITE_CHUNK:
- if (on_write_chunk((struct enc_file_event_data *)data))
- return;
-
- break;
-
- case ENC_START_FILE:
- /* write metadata header and RIFF header */
- 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 */
-
-static bool init_encoder(void)
-{
- struct enc_inputs inputs;
- struct enc_parameters params;
-
- codec_init();
-
- 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 ||
- ci->enc_unget_pcm_data == NULL )
- return false;
-
- ci->enc_get_inputs(&inputs);
-
- if (inputs.config->afmt != AFMT_WAVPACK)
- return false;
-
- memset(&config, 0, sizeof (config));
- config.bits_per_sample = PCM_DEPTH_BITS;
- config.bytes_per_sample = PCM_DEPTH_BYTES;
- config.sample_rate = inputs.sample_rate;
- config.num_channels = inputs.num_channels;
+ if (ci->enc_stream_write(&h.wph, sizeof (h.wph)) != sizeof (h.wph))
+ return -4;
- wpc = WavpackOpenFileOutput ();
+ if (ci->enc_stream_write(&h.wpmdh, sizeof (h.wpmdh)) != sizeof (h.wpmdh))
+ return -5;
- if (!WavpackSetConfiguration(wpc, &config, -1))
- return false;
+ if (ci->enc_stream_write(&h.rhdr, sizeof (h.rhdr)) != sizeof (h.rhdr))
+ return -6;
- err = 0;
-
- /* configure the buffer system */
- params.afmt = AFMT_WAVPACK;
- input_size = PCM_CHUNK_SIZE*inputs.num_channels / 2;
- data_size = 105*input_size / 100;
- input_size *= 2;
- input_step = input_size / 4;
- params.chunk_size = data_size;
- params.enc_sample_rate = inputs.sample_rate;
- params.reserve_bytes = 0;
- params.events_callback = enc_events_callback;
-
- ci->enc_set_parameters(&params);
-
- return true;
-} /* init_encoder */
+ return 0;
+}
/* this is the codec entry point */
enum codec_status codec_main(enum codec_entry_call_reason reason)
{
- if (reason == CODEC_LOAD) {
- /* initialize params and config */
- if (!init_encoder())
- return CODEC_ERROR;
- }
- else if (reason == CODEC_UNLOAD) {
- /* reset parameters to initial state */
- ci->enc_set_parameters(NULL);
- }
+ if (reason == CODEC_LOAD)
+ codec_init();
return CODEC_OK;
}
@@ -408,60 +242,89 @@ enum codec_status codec_main(enum codec_entry_call_reason reason)
/* this is called for each file to process */
enum codec_status codec_run(void)
{
+ enum { GETBUF_ENC, GETBUF_PCM } getbuf = GETBUF_ENC;
+ struct enc_chunk_data *data = NULL;
+
/* main encoding loop */
- while(ci->get_command(NULL) != CODEC_ACTION_HALT)
+ while (1)
{
- uint8_t *src = (uint8_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE);
- struct enc_chunk_hdr *chunk;
- bool abort_chunk;
- uint8_t *dst;
- uint8_t *src_end;
-
- if(src == NULL)
- continue;
-
- chunk = ci->enc_get_chunk();
-
- /* reset counts and pointer */
- chunk->enc_size = 0;
- chunk->num_pcm = 0;
- chunk->enc_data = NULL;
-
- dst = ENC_CHUNK_SKIP_HDR(dst, chunk);
+ enum codec_command_action action = ci->get_command(NULL);
- WavpackStartBlock(wpc, dst, dst + data_size);
+ if (action != CODEC_ACTION_NULL)
+ break;
- chunk_to_int32((uint32_t*)src);
- src = input_buffer;
- src_end = src + input_size;
-
- /* encode chunk in four steps yielding between each */
- do
+ /* First obtain output buffer; when available, get PCM data */
+ switch (getbuf)
{
- abort_chunk = true;
- if (WavpackPackSamples(wpc, (int32_t *)src,
- PCM_SAMP_PER_CHUNK/4))
- {
- chunk->num_pcm += PCM_SAMP_PER_CHUNK/4;
- ci->yield();
- /* could've been stopped in some way */
- abort_chunk = chunk->flags & CHUNKF_ABORT;
- }
-
- src += input_step;
+ case GETBUF_ENC:
+ if (!(data = ci->enc_encbuf_get_buffer(out_reqsize)))
+ continue;
+ getbuf = GETBUF_PCM;
+ case GETBUF_PCM:
+ if (!ci->enc_pcmbuf_read(input_buffer + PCM_SAMP_PER_CHUNK,
+ PCM_SAMP_PER_CHUNK))
+ continue;
+ getbuf = GETBUF_ENC;
}
- while (!abort_chunk && src < src_end);
- if (!abort_chunk)
+ input_buffer_to_int32(frame_size);
+
+ if (WavpackStartBlock(wpc, data->data, data->data + out_reqsize) &&
+ WavpackPackSamples(wpc, input_buffer, PCM_SAMP_PER_CHUNK))
{
- chunk->enc_data = dst;
- if (chunk->num_pcm < PCM_SAMP_PER_CHUNK)
- ci->enc_unget_pcm_data(PCM_CHUNK_SIZE - chunk->num_pcm*4);
/* finish the chunk and store chunk size info */
- chunk->enc_size = WavpackFinishBlock(wpc);
- ci->enc_finish_chunk();
+ data->hdr.size = WavpackFinishBlock(wpc);
+ data->pcm_count = PCM_SAMP_PER_CHUNK;
}
+ else
+ {
+ data->hdr.err = 1;
+ }
+
+ ci->enc_pcmbuf_advance(PCM_SAMP_PER_CHUNK);
+ ci->enc_encbuf_finish_buffer();
}
return CODEC_OK;
}
+
+/* this is called by recording system */
+int ICODE_ATTR enc_callback(enum enc_callback_reason reason,
+ void *params)
+{
+ if (LIKELY(reason == ENC_CB_STREAM))
+ {
+ 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();
+ }
+ }
+ else if (reason == ENC_CB_INPUTS)
+ {
+ /* Save parameters */
+ 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;
+ out_reqsize = frame_size*110 / 100; /* Add 10% */
+
+ /* Setup Wavpack encoder */
+ memset(&config, 0, sizeof (config));
+ config.bits_per_sample = PCM_DEPTH_BITS;
+ config.bytes_per_sample = PCM_DEPTH_BYTES;
+ config.sample_rate = sample_rate;
+ config.num_channels = num_channels;
+
+ wpc = WavpackOpenFileOutput();
+
+ if (!WavpackSetConfiguration(wpc, &config, -1))
+ return -1;
+ }
+
+ return 0;
+}