1
0
Fork 0
forked from len0rd/rockbox

Big Patch adds primarily: Samplerate and format selection to recording for SWCODEC. Supprort for samplerates changing in playback (just goes with the recording part inseparably). Samplerates to all encoders. Encoders can be configured individually on a menu specific to the encoder in the recording menu. File creation is delayed until flush time to reduce spinups when splitting. Misc: statusbar icons for numbers are individual digits to display any number. Audio buffer was rearranged to maximize memory available to recording and properly reinitialized when trashed. ColdFire PCM stuff moved to target tree to avoid a complicated mess when adding samplerate switching. Some needed API changes and to neaten up growing gap between hardware and software codecs.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11452 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2006-11-06 18:07:30 +00:00
parent 0b22795e26
commit 0f5cb94aa4
58 changed files with 4769 additions and 2781 deletions

View file

@ -19,140 +19,364 @@
#ifndef SIMULATOR
#include <inttypes.h>
#include "codeclib.h"
CODEC_HEADER
CODEC_ENC_HEADER
#ifdef USE_IRAM
extern char iramcopy[];
extern char iramstart[];
extern char iramend[];
extern char iedata[];
extern char iend[];
#endif
struct riff_header
{
uint8_t riff_id[4]; /* 00h - "RIFF" */
uint32_t riff_size; /* 04h - sz following headers + data_size */
/* format header */
uint8_t format[4]; /* 08h - "WAVE" */
uint8_t format_id[4]; /* 0Ch - "fmt " */
uint32_t format_size; /* 10h - 16 for PCM (sz format data) */
/* format data */
uint16_t audio_format; /* 14h - 1=PCM */
uint16_t num_channels; /* 16h - 1=M, 2=S, etc. */
uint32_t sample_rate; /* 18h - HZ */
uint32_t byte_rate; /* 1Ch - num_channels*sample_rate*bits_per_sample/8 */
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; */
/* 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 */
};
#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 */
#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 struct codec_api *ci;
static int enc_channels;
static int num_channels;
uint32_t sample_rate;
uint32_t enc_size;
#define CHUNK_SIZE 8192
static unsigned char wav_header[44] =
{'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',16,
0,0,0,1,0,2,0,0x44,0xac,0,0,0x10,0xb1,2,0,4,0,16,0,'d','a','t','a',0,0,0,0};
static unsigned char wav_header_mono[44] =
{'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',16,
0,0,0,1,0,1,0,0x44,0xac,0,0,0x88,0x58,1,0,2,0,16,0,'d','a','t','a',0,0,0,0};
/* update file header info callback function (called by main application) */
void enc_set_header(void *head_buffer, /* ptr to the file header data */
int head_size, /* size of this header data */
int num_pcm_samples, /* amount of processed pcm samples */
bool is_file_header)
static const struct riff_header riff_header =
{
int num_file_bytes = num_pcm_samples * 2 * enc_channels;
/* "RIFF" header */
{ 'R', 'I', 'F', 'F' }, /* riff_id */
0, /* riff_size (*) */
/* format header */
{ 'W', 'A', 'V', 'E' }, /* format */
{ 'f', 'm', 't', ' ' }, /* format_id */
H_TO_LE32(16), /* format_size */
/* format data */
H_TO_LE16(1), /* audio_format */
0, /* num_channels (*) */
0, /* sample_rate (*) */
0, /* byte_rate (*) */
0, /* block_align (*) */
H_TO_LE16(PCM_DEPTH_BITS), /* bits_per_sample */
/* data header */
{ 'd', 'a', 't', 'a' }, /* data_id */
0 /* data_size (*) */
/* (*) updated during ENC_END_FILE event */
};
if(is_file_header)
/* 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)
{
/* update file header before file closing */
if((int)sizeof(wav_header) < head_size)
{
/* update wave header size entries: special to WAV format */
*(long*)(head_buffer+ 4) = htole32(num_file_bytes + 36);
*(long*)(head_buffer+40) = htole32(num_file_bytes);
}
#ifdef ROCKBOX_HAS_LOGF
ci->logf("wav 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 header */
if (ci->write(data->rec_file, &riff_header, sizeof (riff_header))
!= sizeof (riff_header))
{
return false;
}
data->new_enc_size += sizeof (riff_header);
return true;
} /* on_start_file */
static bool on_end_file(struct enc_file_event_data *data)
{
/* update template header */
struct riff_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;
/* "RIFF" header */
hdr.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);
/* data header */
hdr.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))
{
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 wav format samples */
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;
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_be32((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|
*/
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 */
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_PCM_WAV)
return false;
sample_rate = inputs.sample_rate;
num_channels = inputs.num_channels;
/* 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 */
/* main codec entry point */
enum codec_status codec_start(struct codec_api* api)
{
int i;
long lr;
unsigned long t;
unsigned long *src;
unsigned long *dst;
int chunk_size, num_chunks, samp_per_chunk;
int enc_buffer_size;
int enc_quality;
bool cpu_boosted = true; /* start boosted */
bool cpu_boosted;
ci = api; // copy to global api pointer
if(ci->enc_get_inputs == NULL ||
ci->enc_set_parameters == NULL ||
ci->enc_alloc_chunk == NULL ||
ci->enc_free_chunk == NULL ||
ci->enc_wavbuf_near_empty == NULL ||
ci->enc_get_wav_data == NULL ||
ci->enc_set_header_callback == NULL )
#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;
ci->cpu_boost(true);
*ci->enc_set_header_callback = enc_set_header;
ci->enc_get_inputs(&enc_buffer_size, &enc_channels, &enc_quality);
/* configure the buffer system */
chunk_size = sizeof(long) + CHUNK_SIZE * enc_channels / 2;
num_chunks = enc_buffer_size / chunk_size;
samp_per_chunk = CHUNK_SIZE / 4;
/* inform the main program about buffer dimensions and other params */
ci->enc_set_parameters(chunk_size, num_chunks, samp_per_chunk,
(enc_channels == 2) ? wav_header : wav_header_mono,
sizeof(wav_header), AFMT_PCM_WAV);
}
/* main application waits for this flag during encoder loading */
ci->enc_codec_loaded = true;
ci->enc_codec_loaded = 1;
ci->cpu_boost(true);
cpu_boosted = true;
/* main encoding loop */
while(!ci->stop_codec)
{
while((src = (unsigned long*)ci->enc_get_wav_data(CHUNK_SIZE)) != NULL)
uint32_t *src;
while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
{
if(ci->stop_codec)
struct enc_chunk_hdr *chunk;
if (ci->stop_codec)
break;
if(ci->enc_wavbuf_near_empty() == 0)
if (!cpu_boosted && ci->enc_pcm_buf_near_empty() == 0)
{
if(!cpu_boosted)
{
ci->cpu_boost(true);
cpu_boosted = true;
}
ci->cpu_boost(true);
cpu_boosted = true;
}
dst = (unsigned long*)ci->enc_alloc_chunk();
*dst++ = CHUNK_SIZE * enc_channels / 2; /* set size info */
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);
if(enc_channels == 2)
{
/* swap byte order & copy to destination */
for (i=0; i<CHUNK_SIZE/4; i++)
{
t = *src++;
*dst++ = ((t >> 8) & 0xff00ff) | ((t << 8) & 0xff00ff00);
}
}
else
{
/* mix left/right, swap byte order & copy to destination */
for (i=0; i<CHUNK_SIZE/8; i++)
{
lr = (long)*src++;
lr = (((lr<<16)>>16) + (lr>>16)) >> 1; /* left+right */
t = (lr << 16);
lr = (long)*src++;
lr = (((lr<<16)>>16) + (lr>>16)) >> 1; /* left+right */
t |= lr & 0xffff;
*dst++ = ((t >> 8) & 0xff00ff) | ((t << 8) & 0xff00ff00);
}
}
chunk_to_wav_format(src, (uint32_t *)chunk->enc_data);
ci->enc_free_chunk();
ci->enc_finish_chunk();
ci->yield();
}
if(ci->enc_wavbuf_near_empty())
if (cpu_boosted && ci->enc_pcm_buf_near_empty() != 0)
{
if(cpu_boosted)
{
ci->cpu_boost(false);
cpu_boosted = false;
}
ci->cpu_boost(false);
cpu_boosted = false;
}
ci->yield();
@ -162,11 +386,12 @@ enum codec_status codec_start(struct codec_api* api)
ci->cpu_boost(false);
/* reset parameters to initial state */
ci->enc_set_parameters(0, 0, 0, 0, 0, 0);
ci->enc_set_parameters(NULL);
/* main application waits for this flag during encoder removing */
ci->enc_codec_loaded = false;
ci->enc_codec_loaded = 0;
return CODEC_OK;
}
#endif
} /* codec_start */
#endif /* ndef SIMULATOR */