summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2006-11-24 19:49:04 +0000
committerMichael Sevakis <jethead71@rockbox.org>2006-11-24 19:49:04 +0000
commit0729b928ce14eafc09efe484bf19df2346880159 (patch)
treea8561f21607bab954580fb8a6b3fafbb33e691c9 /apps
parent516d277958019ba27362d5f9fcc5ee70acc87c45 (diff)
downloadrockbox-0729b928ce14eafc09efe484bf19df2346880159.tar.gz
rockbox-0729b928ce14eafc09efe484bf19df2346880159.zip
Recording: Add AIFF recording to SWCODEC. Note: AIFF playback chokes on sample rates other than 44.1kHz whether recorded or created and saved with an external program. Recorded files will still open in an external editor however.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11583 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r--apps/codecs/Makefile1
-rw-r--r--apps/codecs/SOURCES1
-rw-r--r--apps/codecs/aiff_enc.c408
-rw-r--r--apps/enc_config.c9
-rw-r--r--apps/lang/english.lang15
-rw-r--r--apps/recorder/icons.c3
-rw-r--r--apps/recorder/icons.h1
-rw-r--r--apps/settings.c2
-rw-r--r--apps/settings.h1
-rw-r--r--apps/sound_menu.c1
10 files changed, 442 insertions, 0 deletions
diff --git a/apps/codecs/Makefile b/apps/codecs/Makefile
index 12235b490c..38723b7940 100644
--- a/apps/codecs/Makefile
+++ b/apps/codecs/Makefile
@@ -61,6 +61,7 @@ $(OBJDIR)/wavpack.elf : $(OBJDIR)/wavpack.o $(BUILDDIR)/libwavpack.a
$(OBJDIR)/alac.elf : $(OBJDIR)/alac.o $(BUILDDIR)/libalac.a $(BUILDDIR)/libm4a.a
$(OBJDIR)/aac.elf : $(OBJDIR)/aac.o $(BUILDDIR)/libfaad.a $(BUILDDIR)/libm4a.a
$(OBJDIR)/shorten.elf : $(OBJDIR)/shorten.o $(BUILDDIR)/libffmpegFLAC.a
+$(OBJDIR)/aiff_enc.elf: $(OBJDIR)/aiff_enc.o
$(OBJDIR)/mp3_enc.elf: $(OBJDIR)/mp3_enc.o
$(OBJDIR)/wav_enc.elf: $(OBJDIR)/wav_enc.o
$(OBJDIR)/wavpack_enc.elf: $(OBJDIR)/wavpack_enc.o $(BUILDDIR)/libwavpack.a
diff --git a/apps/codecs/SOURCES b/apps/codecs/SOURCES
index feadcde0ac..68bb04f926 100644
--- a/apps/codecs/SOURCES
+++ b/apps/codecs/SOURCES
@@ -17,6 +17,7 @@ sid.c
adx.c
#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
/* encoders */
+aiff_enc.c
mp3_enc.c
wav_enc.c
wavpack_enc.c
diff --git a/apps/codecs/aiff_enc.c b/apps/codecs/aiff_enc.c
new file mode 100644
index 0000000000..50c682fa31
--- /dev/null
+++ b/apps/codecs/aiff_enc.c
@@ -0,0 +1,408 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Antonius Hellmann
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef SIMULATOR
+
+#include <inttypes.h>
+#include "codeclib.h"
+
+CODEC_ENC_HEADER
+
+#ifdef USE_IRAM
+extern char iramcopy[];
+extern char iramstart[];
+extern char iramend[];
+extern char iedata[];
+extern char iend[];
+#endif
+
+struct aiff_header
+{
+ uint8_t form_id[4]; /* 00h - 'FORM' */
+ uint32_t form_size; /* 04h - size of file - 8 */
+ uint8_t aiff_id[4]; /* 08h - 'AIFF' */
+ uint8_t comm_id[4]; /* 0Ch - 'COMM' */
+ int32_t comm_size; /* 10h - num_channels through sample_rate
+ (18) */
+ int16_t num_channels; /* 14h - 1=M, 2=S, etc. */
+ uint32_t num_sample_frames; /* 16h - num samples for each channel */
+ int16_t sample_size; /* 1ah - 1-32 bits per sample */
+ uint8_t sample_rate[10]; /* 1ch - IEEE 754 80-bit floating point */
+ uint8_t ssnd_id[4]; /* 26h - "SSND" */
+ int32_t ssnd_size; /* 2ah - size of chunk from offset to
+ end of pcm data */
+ uint32_t offset; /* 2eh - data offset from end of header */
+ uint32_t block_size; /* 32h - pcm data alignment */
+ /* 36h */
+} __attribute__((packed));
+
+#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)
+
+/* Template headers */
+struct aiff_header aiff_header =
+{
+ { 'F', 'O', 'R', 'M' }, /* form_id */
+ 0, /* form_size (*) */
+ { 'A', 'I', 'F', 'F' }, /* aiff_id */
+ { 'C', 'O', 'M', 'M' }, /* comm_id */
+ H_TO_BE32(18), /* comm_size */
+ 0, /* num_channels (*) */
+ 0, /* num_sample_frames (*) */
+ H_TO_BE32(PCM_DEPTH_BITS), /* sample_size */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* sample_rate (*) */
+ { 'S', 'S', 'N', 'D' }, /* ssnd_id */
+ 0, /* ssnd_size (*) */
+ H_TO_BE32(0), /* offset */
+ H_TO_BE32(0), /* block_size */
+};
+
+/* (*) updated when finalizing file */
+
+static struct codec_api *ci;
+static int num_channels;
+uint32_t sample_rate;
+uint32_t enc_size;
+
+/* convert unsigned 32 bit value to 80-bit floating point number */
+static void uint32_to_ieee754_extended(uint8_t f[10], uint32_t l) ICODE_ATTR;
+static void uint32_to_ieee754_extended(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;
+
+ /* sign always zero - bit 79 */
+ /* exponent is 0-31 (normalized: 31 - shift + 16383) - bits 64-78 */
+ f[0] = 0x40;
+ f[1] = (uint8_t)exp;
+ /* mantissa is value left justified with most significant non-zero
+ bit stored in bit 63 - bits 0-63 */
+ *(uint32_t *)&f[2] = htobe32(l);
+} /* long_to_ieee754_extended */
+
+/* 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)
+{
+ if (!is_file_data_ok(data))
+ return false;
+
+ if (data->chunk->enc_data == NULL)
+ {
+#ifdef ROCKBOX_HAS_LOGF
+ ci->logf("aiff enc: NULL data");
+#endif
+ return true;
+ }
+
+ 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)
+{
+ if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0')
+ return false;
+
+ data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC);
+
+ if (data->rec_file < 0)
+ return false;
+
+ /* reset sample count */
+ data->num_pcm_samples = 0;
+
+ /* write template headers */
+ if (ci->write(data->rec_file, &aiff_header, sizeof (aiff_header))
+ != sizeof (aiff_header))
+ {
+ return false;
+ }
+
+ data->new_enc_size += sizeof(aiff_header);
+ return true;
+} /* on_start_file */
+
+static bool on_end_file(struct enc_file_event_data *data)
+{
+ /* update template headers */
+ struct aiff_header hdr;
+ uint32_t data_size;
+
+ 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))
+ {
+ return false;
+ }
+
+ data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES;
+
+ /* 'FORM' chunk */
+ hdr.form_size = data_size + sizeof (hdr) - 8;
+
+ /* 'COMM' chunk */
+ hdr.num_channels = htobe16(num_channels);
+ hdr.num_sample_frames = htobe32(data->num_pcm_samples*num_channels/2);
+ uint32_to_ieee754_extended(hdr.sample_rate, sample_rate);
+
+ /* 'SSND' chunk */
+ hdr.ssnd_size = htobe32(data_size + 8);
+
+ if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 ||
+ ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr))
+ {
+ return false;
+ }
+
+ ci->fsync(data->rec_file);
+ ci->close(data->rec_file);
+ data->rec_file = -1;
+
+ return true;
+} /* on_end_file */
+
+static void enc_events_callback(enum enc_events event, void *data) ICODE_ATTR;
+static void enc_events_callback(enum enc_events event, void *data)
+{
+ if (event == ENC_WRITE_CHUNK)
+ {
+ if (on_write_chunk((struct enc_file_event_data *)data))
+ return;
+ }
+ else if (event == ENC_START_FILE)
+ {
+ if (on_start_file((struct enc_file_event_data *)data))
+ return;
+ }
+ else if (event == ENC_END_FILE)
+ {
+ if (on_end_file((struct enc_file_event_data *)data))
+ return;
+ }
+ else
+ {
+ return;
+ }
+
+ ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR;
+} /* enc_events_callback */
+
+/* convert native pcm samples to aiff format samples */
+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;
+
+ inline void to_mono(uint32_t **src, uint32_t **dst)
+ {
+ int32_t lr1, lr2;
+
+ lr1 = *(*src)++;
+ lr1 = ((int16_t)lr1 + (lr1 >> 16)) >> 1;
+
+ lr2 = *(*src)++;
+ lr2 = ((int16_t)lr2 + (lr2 >> 16)) >> 1;
+ *(*dst)++ = swap_odd_even_le32((lr1 << 16) | (uint16_t)lr2);
+ } /* to_mono */
+
+ do
+ {
+ to_mono(&src, &dst);
+ to_mono(&src, &dst);
+ to_mono(&src, &dst);
+ to_mono(&src, &dst);
+ to_mono(&src, &dst);
+ to_mono(&src, &dst);
+ to_mono(&src, &dst);
+ 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
+ {
+ *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);
+#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_pcm_buf_near_empty == 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;
+
+ /* 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 */
+
+/* main codec entry point */
+enum codec_status codec_start(struct codec_api* api)
+{
+ bool cpu_boosted;
+
+ ci = api; /* copy to global api pointer */
+
+#ifdef USE_IRAM
+ ci->memcpy(iramstart, iramcopy, iramend - iramstart);
+ ci->memset(iedata, 0, iend - iedata);
+#endif
+
+ if (!init_encoder())
+ {
+ ci->enc_codec_loaded = -1;
+ return CODEC_ERROR;
+ }
+
+ /* main application waits for this flag during encoder loading */
+ ci->enc_codec_loaded = 1;
+
+ ci->cpu_boost(true);
+ cpu_boosted = true;
+
+ /* main encoding loop */
+ while(!ci->stop_codec)
+ {
+ uint32_t *src;
+
+ while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
+ {
+ struct enc_chunk_hdr *chunk;
+
+ if (ci->stop_codec)
+ break;
+
+ if (!cpu_boosted && ci->enc_pcm_buf_near_empty() == 0)
+ {
+ ci->cpu_boost(true);
+ cpu_boosted = true;
+ }
+
+ 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();
+ ci->yield();
+ }
+
+ if (cpu_boosted && ci->enc_pcm_buf_near_empty() != 0)
+ {
+ ci->cpu_boost(false);
+ cpu_boosted = false;
+ }
+
+ ci->yield();
+ }
+
+ if (cpu_boosted) /* set initial boost state */
+ ci->cpu_boost(false);
+
+ /* reset parameters to initial state */
+ ci->enc_set_parameters(NULL);
+
+ /* main application waits for this flag during encoder removing */
+ ci->enc_codec_loaded = 0;
+
+ return CODEC_OK;
+} /* codec_start */
+
+#endif /* ndef SIMULATOR */
diff --git a/apps/enc_config.c b/apps/enc_config.c
index 384e679c42..2d2abae61a 100644
--- a/apps/enc_config.c
+++ b/apps/enc_config.c
@@ -49,6 +49,8 @@ static bool enc_no_config_menu(struct encoder_config *cfg);
/** Function definitions for each codec - add these to enc_data
list following the definitions **/
+/** aiff_enc.codec **/
+
/** mp3_enc.codec **/
/* mp3_enc: return encoder capabilities */
static void mp3_enc_get_caps(const struct encoder_config *cfg,
@@ -209,6 +211,13 @@ static const struct encoder_data
bool (*menu)(struct encoder_config *);
} enc_data[REC_NUM_FORMATS] =
{
+ /* aiff_enc.codec */
+ [REC_FORMAT_AIFF] = {
+ NULL,
+ NULL,
+ NULL,
+ enc_no_config_menu,
+ },
/* mp3_enc.codec */
[REC_FORMAT_MPA_L3] = {
mp3_enc_get_caps,
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index f3603e951b..a306138356 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -10266,3 +10266,18 @@
*: ""
</voice>
</phrase>
+<phrase>
+ id: LANG_AFMT_AIFF
+ desc: audio format description
+ user:
+ <source>
+ *: "AIFF"
+ </source>
+ <dest>
+ *: "AIFF"
+ </dest>
+ <voice>
+ *: "AIFF"
+ </voice>
+</phrase>
+
diff --git a/apps/recorder/icons.c b/apps/recorder/icons.c
index ba22bb5a2c..4e1496e721 100644
--- a/apps/recorder/icons.c
+++ b/apps/recorder/icons.c
@@ -121,6 +121,9 @@ const unsigned char bitmap_formats_18x8[Format_18x8Last][18]=
[0 ... Format_18x8Last-1] = /* auto-blank */
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ___ */
+ [Format_18x8_AIFF] =
+ {0x00, 0x3c, 0x0a, 0x0a, 0x0a, 0x3c, 0x00, 0x3e, 0x00,
+ 0x3e, 0x0a, 0x02, 0x02, 0x00, 0x3e, 0x0a, 0x02, 0x02}, /* AIFF */
[Format_18x8_MPA_L3] =
{0x00, 0x3e, 0x04, 0x08, 0x04, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* M__ */
diff --git a/apps/recorder/icons.h b/apps/recorder/icons.h
index 1e7b8dba1e..5aec0730c6 100644
--- a/apps/recorder/icons.h
+++ b/apps/recorder/icons.h
@@ -111,6 +111,7 @@ extern const unsigned char bitmap_glyphs_4x8[Glyph_4x8Last][4];
#ifdef ID3_H
/* This enum is redundant but sort of in keeping with the style */
enum rec_format_18x8 {
+ Format_18x8_AIFF = REC_FORMAT_AIFF,
Format_18x8_MPA_L3 = REC_FORMAT_MPA_L3,
Format_18x8_WAVPACK = REC_FORMAT_WAVPACK,
Format_18x8_PCM_WAV = REC_FORMAT_PCM_WAV,
diff --git a/apps/settings.c b/apps/settings.c
index fc0e5248a3..94d456fcf5 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -533,6 +533,8 @@ static const struct bit_entry hd_bits[] =
{REC_FORMAT_CFG_NUM_BITS ,S_O(rec_format), REC_FORMAT_DEFAULT,
"rec format", REC_FORMAT_CFG_VAL_LIST },
/** Encoder settings start - keep these together **/
+ /* aiff_enc */
+ /* (no settings yet) */
/* mp3_enc */
{5,S_O(mp3_enc_config.bitrate), MP3_ENC_BITRATE_CFG_DEFAULT,
"mp3_enc bitrate", MP3_ENC_BITRATE_CFG_VALUE_LIST },
diff --git a/apps/settings.h b/apps/settings.h
index c0c6a943ce..cb57c1ac77 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -528,6 +528,7 @@ struct user_settings
struct mp3_enc_config mp3_enc_config;
#if 0 /* These currently contain no members but their places in line
should be held */
+ struct aiff_enc_config aiff_enc_config;
struct wav_enc_config wav_enc_config;
struct wavpack_enc_config wavpack_enc_config;
#endif
diff --git a/apps/sound_menu.c b/apps/sound_menu.c
index bcc9163893..52a6c3b34c 100644
--- a/apps/sound_menu.c
+++ b/apps/sound_menu.c
@@ -363,6 +363,7 @@ void make_options_from_indexes(const struct opt_items *src_names,
static bool recformat(void)
{
static const struct opt_items names[REC_NUM_FORMATS] = {
+ [REC_FORMAT_AIFF] = { STR(LANG_AFMT_AIFF) },
[REC_FORMAT_MPA_L3] = { STR(LANG_AFMT_MPA_L3) },
[REC_FORMAT_WAVPACK] = { STR(LANG_AFMT_WAVPACK) },
[REC_FORMAT_PCM_WAV] = { STR(LANG_AFMT_PCM_WAV) },