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
This commit is contained in:
Michael Sevakis 2006-11-24 19:49:04 +00:00
parent 516d277958
commit 0729b928ce
14 changed files with 457 additions and 2 deletions

View File

@ -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

View File

@ -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

408
apps/codecs/aiff_enc.c Normal file
View File

@ -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 */

View File

@ -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,

View File

@ -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>

View File

@ -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__ */

View File

@ -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,

View File

@ -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 },

View File

@ -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

View File

@ -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) },

View File

@ -19,6 +19,8 @@
#include "config.h"
#include "audio.h"
/** aiff_enc.codec **/
/** mp3_enc.codec **/
/* These are in descending order rather than in MPEG frequency index

View File

@ -24,6 +24,14 @@
/** encoder config structures **/
/** aiff_enc.codec **/
struct aiff_enc_config
{
#if 0
unsigned long sample_depth;
#endif
};
/** mp3_enc.codec **/
#define MP3_BITR_CAP_8 (1 << 0)
#define MP3_BITR_CAP_16 (1 << 1)

View File

@ -78,6 +78,7 @@ enum rec_format_indexes
/* start formats */
REC_FORMAT_PCM_WAV,
REC_FORMAT_AIFF,
REC_FORMAT_WAVPACK,
REC_FORMAT_MPA_L3,
@ -93,7 +94,7 @@ enum rec_format_indexes
REC_FORMAT_CFG_NUM_BITS = 2
};
#define REC_FORMAT_CFG_VAL_LIST "wave,wvpk,mpa3"
#define REC_FORMAT_CFG_VAL_LIST "aiff,wave,wvpk,mpa3"
/* get REC_FORMAT_* corresponding AFMT_* */
extern const int rec_format_afmt[REC_NUM_FORMATS];

View File

@ -64,7 +64,7 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] =
#if CONFIG_CODEC == SWCODEC
/* Audio Interchange File Format */
[AFMT_AIFF] =
AFMT_ENTRY("AIFF", "aiff", NULL, "aiff\0aif\0"),
AFMT_ENTRY("AIFF", "aiff", "aiff_enc", "aiff\0aif\0"),
/* Uncompressed PCM in a WAV file */
[AFMT_PCM_WAV] =
AFMT_ENTRY("WAV", "wav", "wav_enc", "wav\0" ),
@ -108,6 +108,7 @@ const int rec_format_afmt[REC_NUM_FORMATS] =
/* give AFMT_UNKNOWN by default */
[0 ... REC_NUM_FORMATS-1] = AFMT_UNKNOWN,
/* add new entries below this line */
[REC_FORMAT_AIFF] = AFMT_AIFF,
[REC_FORMAT_MPA_L3] = AFMT_MPA_L3,
[REC_FORMAT_WAVPACK] = AFMT_WAVPACK,
[REC_FORMAT_PCM_WAV] = AFMT_PCM_WAV,
@ -119,6 +120,7 @@ const int afmt_rec_format[AFMT_NUM_CODECS] =
/* give -1 by default */
[0 ... AFMT_NUM_CODECS-1] = -1,
/* add new entries below this line */
[AFMT_AIFF] = REC_FORMAT_AIFF,
[AFMT_MPA_L3] = REC_FORMAT_MPA_L3,
[AFMT_WAVPACK] = REC_FORMAT_WAVPACK,
[AFMT_PCM_WAV] = REC_FORMAT_PCM_WAV,