Initial voice ui support for software codec platforms. Added also a
beep when changing tracks. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7360 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
329caa8ade
commit
159c52dd36
17 changed files with 774 additions and 230 deletions
|
@ -22,7 +22,7 @@ settings_menu.c
|
|||
sleeptimer.c
|
||||
sound_menu.c
|
||||
status.c
|
||||
#ifndef SIMULATOR
|
||||
#if !defined(SIMULATOR) || CONFIG_HWCODEC == MASNONE
|
||||
talk.c
|
||||
#endif
|
||||
tree.c
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
|
||||
#ifdef SIMULATOR
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
static unsigned char codecbuf[CODEC_SIZE];
|
||||
unsigned char codecbuf[CODEC_SIZE];
|
||||
#endif
|
||||
void *sim_codec_load_ram(char* codecptr, int size,
|
||||
void* ptr2, int bufwrap, int *pd);
|
||||
|
@ -68,6 +68,8 @@ extern void* plugin_get_audio_buffer(int *buffer_size);
|
|||
|
||||
static int codec_test(int api_version, int model, int memsize);
|
||||
|
||||
struct codec_api ci_voice;
|
||||
|
||||
struct codec_api ci = {
|
||||
CODEC_API_VERSION,
|
||||
codec_test,
|
||||
|
@ -247,7 +249,8 @@ struct codec_api ci = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap)
|
||||
int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap,
|
||||
struct codec_api *api)
|
||||
{
|
||||
enum codec_status (*codec_start)(const struct codec_api* api);
|
||||
int status;
|
||||
|
@ -277,7 +280,7 @@ int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap)
|
|||
#endif /* SIMULATOR */
|
||||
|
||||
invalidate_icache();
|
||||
status = codec_start(&ci);
|
||||
status = codec_start(api);
|
||||
#ifdef SIMULATOR
|
||||
sim_codec_close(pd);
|
||||
#endif
|
||||
|
@ -285,7 +288,7 @@ int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap)
|
|||
return status;
|
||||
}
|
||||
|
||||
int codec_load_file(const char *plugin)
|
||||
int codec_load_file(const char *plugin, struct codec_api *api)
|
||||
{
|
||||
char msgbuf[80];
|
||||
int fd;
|
||||
|
@ -309,7 +312,7 @@ int codec_load_file(const char *plugin)
|
|||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
return codec_load_ram(codecbuf, (size_t)rc, NULL, 0);
|
||||
return codec_load_ram(codecbuf, (size_t)rc, NULL, 0, api);
|
||||
}
|
||||
|
||||
static int codec_test(int api_version, int model, int memsize)
|
||||
|
|
|
@ -331,8 +331,9 @@ struct codec_api {
|
|||
|
||||
/* defined by the codec loader (codec.c) */
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap);
|
||||
int codec_load_file(const char* codec);
|
||||
int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap,
|
||||
struct codec_api *api);
|
||||
int codec_load_file(const char* codec, struct codec_api *api);
|
||||
#endif
|
||||
|
||||
/* defined by the codec */
|
||||
|
|
|
@ -71,15 +71,12 @@ enum codec_status codec_start(struct codec_api* api)
|
|||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
while (!rb->taginfo_ready)
|
||||
while (!*rb->taginfo_ready)
|
||||
rb->yield();
|
||||
|
||||
if (rb->id3->frequency != NATIVE_FREQUENCY) {
|
||||
rb->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency));
|
||||
rb->configure(CODEC_DSP_ENABLE, (bool *)true);
|
||||
} else {
|
||||
rb->configure(CODEC_DSP_ENABLE, (bool *)false);
|
||||
}
|
||||
|
||||
/* Always enable DSP to support voice ui. */
|
||||
rb->configure(CODEC_DSP_ENABLE, (bool *)true);
|
||||
rb->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency));
|
||||
|
||||
/* FIX: Correctly parse WAV header - we assume canonical 44-byte header */
|
||||
|
||||
|
|
|
@ -208,8 +208,8 @@ bool dbg_audio_thread(void)
|
|||
}
|
||||
#else
|
||||
extern size_t audiobuffer_free;
|
||||
extern int codecbuflen;
|
||||
extern int codecbufused;
|
||||
extern int filebuflen;
|
||||
extern int filebufused;
|
||||
extern int track_count;
|
||||
|
||||
static int ticks, boost_ticks;
|
||||
|
@ -260,12 +260,12 @@ bool dbg_audio_thread(void)
|
|||
bufsize-audiobuffer_free, HORIZONTAL);
|
||||
line++;
|
||||
|
||||
snprintf(buf, sizeof(buf), "codec: %d/%d", codecbufused, codecbuflen);
|
||||
snprintf(buf, sizeof(buf), "codec: %d/%d", filebufused, filebuflen);
|
||||
lcd_puts(0, line++, buf);
|
||||
|
||||
/* Playable space left */
|
||||
scrollbar(0, line*8, LCD_WIDTH, 6, codecbuflen, 0,
|
||||
codecbufused, HORIZONTAL);
|
||||
scrollbar(0, line*8, LCD_WIDTH, 6, filebuflen, 0,
|
||||
filebufused, HORIZONTAL);
|
||||
line++;
|
||||
|
||||
snprintf(buf, sizeof(buf), "track count: %d", track_count);
|
||||
|
|
195
apps/dsp.c
195
apps/dsp.c
|
@ -132,9 +132,12 @@ struct dither_data
|
|||
long random;
|
||||
};
|
||||
|
||||
static struct dsp_config dsp IDATA_ATTR;
|
||||
static struct dsp_config dsp_conf[2] IDATA_ATTR;
|
||||
static struct dither_data dither_data[2] IDATA_ATTR;
|
||||
static struct resample_data resample_data[2] IDATA_ATTR;
|
||||
static struct resample_data resample_data[2][2] IDATA_ATTR;
|
||||
|
||||
extern int current_codec;
|
||||
struct dsp_config *dsp;
|
||||
|
||||
/* The internal format is 32-bit samples, non-interleaved, stereo. This
|
||||
* format is similar to the raw output from several codecs, so the amount
|
||||
|
@ -155,20 +158,20 @@ static int convert_to_internal(char* src[], int count, long* dst[])
|
|||
{
|
||||
count = MIN(SAMPLE_BUF_SIZE / 2, count);
|
||||
|
||||
if ((dsp.sample_depth <= NATIVE_DEPTH)
|
||||
|| (dsp.stereo_mode == STEREO_INTERLEAVED))
|
||||
if ((dsp->sample_depth <= NATIVE_DEPTH)
|
||||
|| (dsp->stereo_mode == STEREO_INTERLEAVED))
|
||||
{
|
||||
dst[0] = &sample_buf[0];
|
||||
dst[1] = (dsp.stereo_mode == STEREO_MONO)
|
||||
dst[1] = (dsp->stereo_mode == STEREO_MONO)
|
||||
? dst[0] : &sample_buf[SAMPLE_BUF_SIZE / 2];
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[0] = (long*) src[0];
|
||||
dst[1] = (long*) ((dsp.stereo_mode == STEREO_MONO) ? src[0] : src[1]);
|
||||
dst[1] = (long*) ((dsp->stereo_mode == STEREO_MONO) ? src[0] : src[1]);
|
||||
}
|
||||
|
||||
if (dsp.sample_depth <= NATIVE_DEPTH)
|
||||
if (dsp->sample_depth <= NATIVE_DEPTH)
|
||||
{
|
||||
short* s0 = (short*) src[0];
|
||||
long* d0 = dst[0];
|
||||
|
@ -176,7 +179,7 @@ static int convert_to_internal(char* src[], int count, long* dst[])
|
|||
int scale = WORD_SHIFT;
|
||||
int i;
|
||||
|
||||
if (dsp.stereo_mode == STEREO_INTERLEAVED)
|
||||
if (dsp->stereo_mode == STEREO_INTERLEAVED)
|
||||
{
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
|
@ -184,7 +187,7 @@ static int convert_to_internal(char* src[], int count, long* dst[])
|
|||
*d1++ = *s0++ << scale;
|
||||
}
|
||||
}
|
||||
else if (dsp.stereo_mode == STEREO_NONINTERLEAVED)
|
||||
else if (dsp->stereo_mode == STEREO_NONINTERLEAVED)
|
||||
{
|
||||
short* s1 = (short*) src[1];
|
||||
|
||||
|
@ -202,7 +205,7 @@ static int convert_to_internal(char* src[], int count, long* dst[])
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (dsp.stereo_mode == STEREO_INTERLEAVED)
|
||||
else if (dsp->stereo_mode == STEREO_INTERLEAVED)
|
||||
{
|
||||
long* s0 = (long*) src[0];
|
||||
long* d0 = dst[0];
|
||||
|
@ -216,18 +219,18 @@ static int convert_to_internal(char* src[], int count, long* dst[])
|
|||
}
|
||||
}
|
||||
|
||||
if (dsp.stereo_mode == STEREO_NONINTERLEAVED)
|
||||
if (dsp->stereo_mode == STEREO_NONINTERLEAVED)
|
||||
{
|
||||
src[0] += count * dsp.sample_bytes;
|
||||
src[1] += count * dsp.sample_bytes;
|
||||
src[0] += count * dsp->sample_bytes;
|
||||
src[1] += count * dsp->sample_bytes;
|
||||
}
|
||||
else if (dsp.stereo_mode == STEREO_INTERLEAVED)
|
||||
else if (dsp->stereo_mode == STEREO_INTERLEAVED)
|
||||
{
|
||||
src[0] += count * dsp.sample_bytes * 2;
|
||||
src[0] += count * dsp->sample_bytes * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
src[0] += count * dsp.sample_bytes;
|
||||
src[0] += count * dsp->sample_bytes;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
@ -310,29 +313,33 @@ static inline int resample(long* src[], int count)
|
|||
{
|
||||
long new_count;
|
||||
|
||||
if (dsp.frequency != NATIVE_FREQUENCY)
|
||||
if (dsp->frequency != NATIVE_FREQUENCY)
|
||||
{
|
||||
long* d0 = &resample_buf[0];
|
||||
/* Only process the second channel if needed. */
|
||||
long* d1 = (src[0] == src[1]) ? d0
|
||||
: &resample_buf[RESAMPLE_BUF_SIZE / 2];
|
||||
|
||||
if (dsp.frequency < NATIVE_FREQUENCY)
|
||||
if (dsp->frequency < NATIVE_FREQUENCY)
|
||||
{
|
||||
new_count = upsample(d0, src[0], count, &resample_data[0]);
|
||||
new_count = upsample(d0, src[0], count,
|
||||
&resample_data[current_codec][0]);
|
||||
|
||||
if (d0 != d1)
|
||||
{
|
||||
upsample(d1, src[1], count, &resample_data[1]);
|
||||
upsample(d1, src[1], count,
|
||||
&resample_data[current_codec][1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new_count = downsample(d0, src[0], count, &resample_data[0]);
|
||||
new_count = downsample(d0, src[0], count,
|
||||
&resample_data[current_codec][0]);
|
||||
|
||||
if (d0 != d1)
|
||||
{
|
||||
downsample(d1, src[1], count, &resample_data[1]);
|
||||
downsample(d1, src[1], count,
|
||||
&resample_data[current_codec][1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -389,8 +396,8 @@ static long dither_sample(long sample, long bias, long mask,
|
|||
|
||||
/* Clip and quantize */
|
||||
|
||||
min = dsp.clip_min;
|
||||
max = dsp.clip_max;
|
||||
min = dsp->clip_min;
|
||||
max = dsp->clip_max;
|
||||
sample = clip_sample(sample, min, max);
|
||||
output = clip_sample(output, min, max) & ~mask;
|
||||
|
||||
|
@ -407,13 +414,13 @@ static long dither_sample(long sample, long bias, long mask,
|
|||
*/
|
||||
static void apply_gain(long* src[], int count)
|
||||
{
|
||||
if (dsp.replaygain)
|
||||
if (dsp->replaygain)
|
||||
{
|
||||
long* s0 = src[0];
|
||||
long* s1 = src[1];
|
||||
long* d0 = &sample_buf[0];
|
||||
long* d1 = (s0 == s1) ? d0 : &sample_buf[SAMPLE_BUF_SIZE / 2];
|
||||
long gain = dsp.replaygain;
|
||||
long gain = dsp->replaygain;
|
||||
long s;
|
||||
long i;
|
||||
|
||||
|
@ -442,11 +449,11 @@ static void write_samples(short* dst, long* src[], int count)
|
|||
{
|
||||
long* s0 = src[0];
|
||||
long* s1 = src[1];
|
||||
int scale = dsp.frac_bits + 1 - NATIVE_DEPTH;
|
||||
int scale = dsp->frac_bits + 1 - NATIVE_DEPTH;
|
||||
|
||||
if (dsp.dither_enabled)
|
||||
if (dsp->dither_enabled)
|
||||
{
|
||||
long bias = (1L << (dsp.frac_bits - NATIVE_DEPTH));
|
||||
long bias = (1L << (dsp->frac_bits - NATIVE_DEPTH));
|
||||
long mask = (1L << scale) - 1;
|
||||
|
||||
while (count-- > 0)
|
||||
|
@ -459,8 +466,8 @@ static void write_samples(short* dst, long* src[], int count)
|
|||
}
|
||||
else
|
||||
{
|
||||
long min = dsp.clip_min;
|
||||
long max = dsp.clip_max;
|
||||
long min = dsp->clip_min;
|
||||
long max = dsp->clip_max;
|
||||
|
||||
while (count-- > 0)
|
||||
{
|
||||
|
@ -482,10 +489,13 @@ long dsp_process(char* dst, char* src[], long size)
|
|||
{
|
||||
long* tmp[2];
|
||||
long written = 0;
|
||||
long factor = (dsp.stereo_mode != STEREO_MONO) ? 2 : 1;
|
||||
long factor;
|
||||
int samples;
|
||||
|
||||
size /= dsp.sample_bytes * factor;
|
||||
dsp = &dsp_conf[current_codec];
|
||||
|
||||
factor = (dsp->stereo_mode != STEREO_MONO) ? 2 : 1;
|
||||
size /= dsp->sample_bytes * factor;
|
||||
INIT();
|
||||
dsp_set_replaygain(false);
|
||||
|
||||
|
@ -513,21 +523,23 @@ long dsp_process(char* dst, char* src[], long size)
|
|||
/* dsp_input_size MUST be called afterwards */
|
||||
long dsp_output_size(long size)
|
||||
{
|
||||
if (dsp.sample_depth > NATIVE_DEPTH)
|
||||
dsp = &dsp_conf[current_codec];
|
||||
|
||||
if (dsp->sample_depth > NATIVE_DEPTH)
|
||||
{
|
||||
size /= 2;
|
||||
}
|
||||
|
||||
if (dsp.frequency != NATIVE_FREQUENCY)
|
||||
if (dsp->frequency != NATIVE_FREQUENCY)
|
||||
{
|
||||
size = (long) ((((unsigned long) size * NATIVE_FREQUENCY)
|
||||
+ (dsp.frequency - 1)) / dsp.frequency);
|
||||
+ (dsp->frequency - 1)) / dsp->frequency);
|
||||
}
|
||||
|
||||
/* round to the next multiple of 2 (these are shorts) */
|
||||
size = (size + 1) & ~1;
|
||||
|
||||
if (dsp.stereo_mode == STEREO_MONO)
|
||||
if (dsp->stereo_mode == STEREO_MONO)
|
||||
{
|
||||
size *= 2;
|
||||
}
|
||||
|
@ -547,25 +559,28 @@ long dsp_output_size(long size)
|
|||
*/
|
||||
long dsp_input_size(long size)
|
||||
{
|
||||
dsp = &dsp_conf[current_codec];
|
||||
|
||||
/* convert to number of output stereo samples. */
|
||||
size /= 2;
|
||||
|
||||
/* Mono means we need half input samples to fill the output buffer */
|
||||
if (dsp.stereo_mode == STEREO_MONO)
|
||||
if (dsp->stereo_mode == STEREO_MONO)
|
||||
size /= 2;
|
||||
|
||||
/* size is now the number of resampled input samples. Convert to
|
||||
original input samples. */
|
||||
if (dsp.frequency != NATIVE_FREQUENCY)
|
||||
if (dsp->frequency != NATIVE_FREQUENCY)
|
||||
{
|
||||
/* Use the real resampling delta =
|
||||
* (unsigned long) dsp.frequency * 65536 / NATIVE_FREQUENCY, and
|
||||
* (unsigned long) dsp->frequency * 65536 / NATIVE_FREQUENCY, and
|
||||
* round towards zero to avoid buffer overflows. */
|
||||
size = ((unsigned long)size * resample_data[0].delta) >> 16;
|
||||
size = ((unsigned long)size *
|
||||
resample_data[current_codec][0].delta) >> 16;
|
||||
}
|
||||
|
||||
/* Convert back to bytes. */
|
||||
if (dsp.sample_depth > NATIVE_DEPTH)
|
||||
if (dsp->sample_depth > NATIVE_DEPTH)
|
||||
size *= 4;
|
||||
else
|
||||
size *= 2;
|
||||
|
@ -575,90 +590,96 @@ long dsp_input_size(long size)
|
|||
|
||||
int dsp_stereo_mode(void)
|
||||
{
|
||||
return dsp.stereo_mode;
|
||||
dsp = &dsp_conf[current_codec];
|
||||
|
||||
return dsp->stereo_mode;
|
||||
}
|
||||
|
||||
bool dsp_configure(int setting, void *value)
|
||||
{
|
||||
dsp = &dsp_conf[current_codec];
|
||||
|
||||
switch (setting)
|
||||
{
|
||||
case DSP_SET_FREQUENCY:
|
||||
memset(resample_data, 0, sizeof(resample_data));
|
||||
memset(&resample_data[current_codec][0], 0,
|
||||
sizeof(struct resample_data) * 2);
|
||||
/* Fall through!!! */
|
||||
case DSP_SWITCH_FREQUENCY:
|
||||
dsp.frequency = ((int) value == 0) ? NATIVE_FREQUENCY : (int) value;
|
||||
resample_data[0].delta = resample_data[1].delta =
|
||||
(unsigned long) dsp.frequency * 65536 / NATIVE_FREQUENCY;
|
||||
dsp->frequency = ((int) value == 0) ? NATIVE_FREQUENCY : (int) value;
|
||||
resample_data[current_codec][0].delta =
|
||||
resample_data[current_codec][1].delta =
|
||||
(unsigned long) dsp->frequency * 65536 / NATIVE_FREQUENCY;
|
||||
break;
|
||||
|
||||
case DSP_SET_CLIP_MIN:
|
||||
dsp.clip_min = (long) value;
|
||||
dsp->clip_min = (long) value;
|
||||
break;
|
||||
|
||||
case DSP_SET_CLIP_MAX:
|
||||
dsp.clip_max = (long) value;
|
||||
dsp->clip_max = (long) value;
|
||||
break;
|
||||
|
||||
case DSP_SET_SAMPLE_DEPTH:
|
||||
dsp.sample_depth = (long) value;
|
||||
dsp->sample_depth = (long) value;
|
||||
|
||||
if (dsp.sample_depth <= NATIVE_DEPTH)
|
||||
if (dsp->sample_depth <= NATIVE_DEPTH)
|
||||
{
|
||||
dsp.frac_bits = WORD_FRACBITS;
|
||||
dsp.sample_bytes = sizeof(short);
|
||||
dsp.clip_max = ((1 << WORD_FRACBITS) - 1);
|
||||
dsp.clip_min = -((1 << WORD_FRACBITS));
|
||||
dsp->frac_bits = WORD_FRACBITS;
|
||||
dsp->sample_bytes = sizeof(short);
|
||||
dsp->clip_max = ((1 << WORD_FRACBITS) - 1);
|
||||
dsp->clip_min = -((1 << WORD_FRACBITS));
|
||||
}
|
||||
else
|
||||
{
|
||||
dsp.frac_bits = (long) value;
|
||||
dsp.sample_bytes = sizeof(long);
|
||||
dsp->frac_bits = (long) value;
|
||||
dsp->sample_bytes = sizeof(long);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DSP_SET_STEREO_MODE:
|
||||
dsp.stereo_mode = (long) value;
|
||||
dsp->stereo_mode = (long) value;
|
||||
break;
|
||||
|
||||
case DSP_RESET:
|
||||
dsp.dither_enabled = false;
|
||||
dsp.stereo_mode = STEREO_NONINTERLEAVED;
|
||||
dsp.clip_max = ((1 << WORD_FRACBITS) - 1);
|
||||
dsp.clip_min = -((1 << WORD_FRACBITS));
|
||||
dsp.track_gain = 0;
|
||||
dsp.album_gain = 0;
|
||||
dsp.track_peak = 0;
|
||||
dsp.album_peak = 0;
|
||||
dsp.frequency = NATIVE_FREQUENCY;
|
||||
dsp.sample_depth = NATIVE_DEPTH;
|
||||
dsp.frac_bits = WORD_FRACBITS;
|
||||
dsp.new_gain = true;
|
||||
dsp->dither_enabled = false;
|
||||
dsp->stereo_mode = STEREO_NONINTERLEAVED;
|
||||
dsp->clip_max = ((1 << WORD_FRACBITS) - 1);
|
||||
dsp->clip_min = -((1 << WORD_FRACBITS));
|
||||
dsp->track_gain = 0;
|
||||
dsp->album_gain = 0;
|
||||
dsp->track_peak = 0;
|
||||
dsp->album_peak = 0;
|
||||
dsp->frequency = NATIVE_FREQUENCY;
|
||||
dsp->sample_depth = NATIVE_DEPTH;
|
||||
dsp->frac_bits = WORD_FRACBITS;
|
||||
dsp->new_gain = true;
|
||||
break;
|
||||
|
||||
case DSP_DITHER:
|
||||
memset(dither_data, 0, sizeof(dither_data));
|
||||
dsp.dither_enabled = (bool) value;
|
||||
dsp->dither_enabled = (bool) value;
|
||||
break;
|
||||
|
||||
case DSP_SET_TRACK_GAIN:
|
||||
dsp.track_gain = (long) value;
|
||||
dsp.new_gain = true;
|
||||
dsp->track_gain = (long) value;
|
||||
dsp->new_gain = true;
|
||||
break;
|
||||
|
||||
case DSP_SET_ALBUM_GAIN:
|
||||
dsp.album_gain = (long) value;
|
||||
dsp.new_gain = true;
|
||||
dsp->album_gain = (long) value;
|
||||
dsp->new_gain = true;
|
||||
break;
|
||||
|
||||
case DSP_SET_TRACK_PEAK:
|
||||
dsp.track_peak = (long) value;
|
||||
dsp.new_gain = true;
|
||||
dsp->track_peak = (long) value;
|
||||
dsp->new_gain = true;
|
||||
break;
|
||||
|
||||
case DSP_SET_ALBUM_PEAK:
|
||||
dsp.album_peak = (long) value;
|
||||
dsp.new_gain = true;
|
||||
dsp->album_peak = (long) value;
|
||||
dsp->new_gain = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -670,11 +691,13 @@ bool dsp_configure(int setting, void *value)
|
|||
|
||||
void dsp_set_replaygain(bool always)
|
||||
{
|
||||
if (always || dsp.new_gain)
|
||||
dsp = &dsp_conf[current_codec];
|
||||
|
||||
if (always || dsp->new_gain)
|
||||
{
|
||||
long gain = 0;
|
||||
|
||||
dsp.new_gain = false;
|
||||
dsp->new_gain = false;
|
||||
|
||||
if (global_settings.replaygain || global_settings.replaygain_noclip)
|
||||
{
|
||||
|
@ -682,8 +705,8 @@ void dsp_set_replaygain(bool always)
|
|||
|
||||
if (global_settings.replaygain)
|
||||
{
|
||||
gain = (global_settings.replaygain_track || !dsp.album_gain)
|
||||
? dsp.track_gain : dsp.album_gain;
|
||||
gain = (global_settings.replaygain_track || !dsp->album_gain)
|
||||
? dsp->track_gain : dsp->album_gain;
|
||||
|
||||
if (global_settings.replaygain_preamp)
|
||||
{
|
||||
|
@ -694,8 +717,8 @@ void dsp_set_replaygain(bool always)
|
|||
}
|
||||
}
|
||||
|
||||
peak = (global_settings.replaygain_track || !dsp.album_peak)
|
||||
? dsp.track_peak : dsp.album_peak;
|
||||
peak = (global_settings.replaygain_track || !dsp->album_peak)
|
||||
? dsp->track_peak : dsp->album_peak;
|
||||
|
||||
if (gain == 0)
|
||||
{
|
||||
|
@ -718,6 +741,6 @@ void dsp_set_replaygain(bool always)
|
|||
}
|
||||
|
||||
/* Store in S8.23 format to simplify calculations. */
|
||||
dsp.replaygain = gain >> 1;
|
||||
dsp->replaygain = gain >> 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,6 +129,9 @@ void init(void)
|
|||
global_settings.mdb_enable,
|
||||
global_settings.superbass);
|
||||
button_clear_queue(); /* Empty the keyboard buffer */
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
talk_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
104
apps/pcmbuf.c
104
apps/pcmbuf.c
|
@ -34,6 +34,7 @@
|
|||
#include "buffer.h"
|
||||
#include "settings.h"
|
||||
#include "audio.h"
|
||||
#include "dsp.h"
|
||||
|
||||
#define CHUNK_SIZE PCMBUF_GUARD
|
||||
/* Must be a power of 2 */
|
||||
|
@ -86,9 +87,11 @@ struct pcmbufdesc
|
|||
volatile int pcmbuf_read_index;
|
||||
volatile int pcmbuf_write_index;
|
||||
int pcmbuf_unplayed_bytes;
|
||||
int pcmbuf_mix_used_bytes;
|
||||
int pcmbuf_watermark;
|
||||
void (*pcmbuf_watermark_event)(int bytes_left);
|
||||
static int last_chunksize;
|
||||
static long mixpos = 0;
|
||||
|
||||
static void pcmbuf_boost(bool state)
|
||||
{
|
||||
|
@ -173,6 +176,7 @@ bool pcmbuf_add_chunk(void *addr, int size, void (*callback)(void))
|
|||
pcmbuffers[pcmbuf_write_index].callback = callback;
|
||||
pcmbuf_write_index = (pcmbuf_write_index+1) & NUM_PCM_BUFFERS_MASK;
|
||||
pcmbuf_unplayed_bytes += size;
|
||||
pcmbuf_mix_used_bytes = MAX(0, pcmbuf_mix_used_bytes - size);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -254,6 +258,7 @@ void pcmbuf_play_stop(void)
|
|||
pcm_play_stop();
|
||||
last_chunksize = 0;
|
||||
pcmbuf_unplayed_bytes = 0;
|
||||
pcmbuf_mix_used_bytes = 0;
|
||||
pcmbuf_read_index = 0;
|
||||
pcmbuf_write_index = 0;
|
||||
audiobuffer_pos = 0;
|
||||
|
@ -297,6 +302,13 @@ void pcmbuf_flush_audio(void)
|
|||
crossfade_init = true;
|
||||
}
|
||||
|
||||
/* Force playback. */
|
||||
void pcmbuf_play_start(void)
|
||||
{
|
||||
if (!pcm_is_playing() && pcmbuf_unplayed_bytes)
|
||||
pcm_play_data(pcmbuf_callback);
|
||||
}
|
||||
|
||||
void pcmbuf_flush_fillpos(void)
|
||||
{
|
||||
int copy_n;
|
||||
|
@ -562,6 +574,98 @@ bool pcmbuf_insert_buffer(char *buf, long length)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Generates a constant square wave sound with a given frequency
|
||||
in Hertz for a duration in milliseconds. */
|
||||
void pcmbuf_beep(int frequency, int duration, int amplitude)
|
||||
{
|
||||
int state = 0, count = 0;
|
||||
int interval = NATIVE_FREQUENCY / frequency;
|
||||
int pos;
|
||||
short *buf = (short *)audiobuffer;
|
||||
int bufsize = pcmbuf_size / 2;
|
||||
|
||||
/* FIXME: Should start playback. */
|
||||
//if (pcmbuf_unplayed_bytes * 1000 < 4 * NATIVE_FREQUENCY * duration)
|
||||
// return ;
|
||||
|
||||
pos = (audiobuffer_pos - pcmbuf_unplayed_bytes) / 2;
|
||||
if (pos < 0)
|
||||
pos += bufsize;
|
||||
|
||||
duration = NATIVE_FREQUENCY / 1000 * duration;
|
||||
while (duration-- > 0)
|
||||
{
|
||||
if (state) {
|
||||
buf[pos] = MIN(MAX(buf[pos] + amplitude, -32768), 32767);
|
||||
if (++pos >= bufsize)
|
||||
pos = 0;
|
||||
buf[pos] = MIN(MAX(buf[pos] + amplitude, -32768), 32767);
|
||||
} else {
|
||||
buf[pos] = MIN(MAX(buf[pos] - amplitude, -32768), 32767);
|
||||
if (++pos >= bufsize)
|
||||
pos = 0;
|
||||
buf[pos] = MIN(MAX(buf[pos] - amplitude, -32768), 32767);
|
||||
}
|
||||
|
||||
if (++count >= interval)
|
||||
{
|
||||
count = 0;
|
||||
if (state)
|
||||
state = 0;
|
||||
else
|
||||
state = 1;
|
||||
}
|
||||
pos++;
|
||||
if (pos >= bufsize)
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns pcm buffer usage in percents (0 to 100). */
|
||||
int pcmbuf_usage(void)
|
||||
{
|
||||
return pcmbuf_unplayed_bytes * 100 / pcmbuf_size;
|
||||
}
|
||||
|
||||
int pcmbuf_mix_usage(void)
|
||||
{
|
||||
return pcmbuf_mix_used_bytes * 100 / pcmbuf_unplayed_bytes;
|
||||
}
|
||||
|
||||
void pcmbuf_reset_mixpos(void)
|
||||
{
|
||||
int bufsize = pcmbuf_size / 2;
|
||||
|
||||
pcmbuf_mix_used_bytes = 0;
|
||||
mixpos = (audiobuffer_pos - pcmbuf_unplayed_bytes) / 2;
|
||||
if (mixpos < 0)
|
||||
mixpos += bufsize;
|
||||
if (mixpos >= bufsize)
|
||||
mixpos -= bufsize;
|
||||
}
|
||||
|
||||
void pcmbuf_mix(char *buf, long length)
|
||||
{
|
||||
short *ibuf = (short *)buf;
|
||||
short *obuf = (short *)audiobuffer;
|
||||
int bufsize = pcmbuf_size / 2;
|
||||
|
||||
if (pcmbuf_mix_used_bytes == 0)
|
||||
pcmbuf_reset_mixpos();
|
||||
|
||||
pcmbuf_mix_used_bytes += length;
|
||||
length /= 2;
|
||||
|
||||
while (length-- > 0) {
|
||||
obuf[mixpos] = MIN(MAX(obuf[mixpos] + *ibuf*4, -32768), 32767);
|
||||
|
||||
ibuf++;
|
||||
mixpos++;
|
||||
if (mixpos >= bufsize)
|
||||
mixpos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void pcmbuf_crossfade_enable(bool on_off)
|
||||
{
|
||||
crossfade_enabled = on_off;
|
||||
|
|
|
@ -36,6 +36,7 @@ void pcmbuf_set_watermark(int numbytes, void (*callback)(int bytes_left));
|
|||
void pcmbuf_set_boost_mode(bool state);
|
||||
bool pcmbuf_is_lowdata(void);
|
||||
void pcmbuf_flush_audio(void);
|
||||
void pcmbuf_play_start(void);
|
||||
bool pcmbuf_crossfade_init(int type);
|
||||
void pcmbuf_add_event(void (*event_handler)(void));
|
||||
unsigned int pcmbuf_get_latency(void);
|
||||
|
@ -45,4 +46,10 @@ void* pcmbuf_request_buffer(long length, long *realsize);
|
|||
bool pcmbuf_is_crossfade_enabled(void);
|
||||
void pcmbuf_crossfade_enable(bool on_off);
|
||||
|
||||
int pcmbuf_usage(void);
|
||||
int pcmbuf_mix_usage(void);
|
||||
void pcmbuf_beep(int frequency, int duration, int amplitude);
|
||||
void pcmbuf_reset_mixpos(void);
|
||||
void pcmbuf_mix(char *buf, long length);
|
||||
|
||||
#endif
|
||||
|
|
507
apps/playback.c
507
apps/playback.c
|
@ -60,18 +60,20 @@
|
|||
#include "misc.h"
|
||||
#include "sound.h"
|
||||
#include "metadata.h"
|
||||
#include "talk.h"
|
||||
|
||||
static volatile bool codec_loaded;
|
||||
static volatile bool audio_codec_loaded;
|
||||
static volatile bool voice_codec_loaded;
|
||||
static volatile bool playing;
|
||||
static volatile bool paused;
|
||||
|
||||
#define CODEC_VORBIS "/.rockbox/codecs/vorbis.codec";
|
||||
#define CODEC_MPA_L3 "/.rockbox/codecs/mpa.codec";
|
||||
#define CODEC_FLAC "/.rockbox/codecs/flac.codec";
|
||||
#define CODEC_WAV "/.rockbox/codecs/wav.codec";
|
||||
#define CODEC_A52 "/.rockbox/codecs/a52.codec";
|
||||
#define CODEC_MPC "/.rockbox/codecs/mpc.codec";
|
||||
#define CODEC_WAVPACK "/.rockbox/codecs/wavpack.codec";
|
||||
#define CODEC_VORBIS "/.rockbox/codecs/vorbis.codec"
|
||||
#define CODEC_MPA_L3 "/.rockbox/codecs/mpa.codec"
|
||||
#define CODEC_FLAC "/.rockbox/codecs/flac.codec"
|
||||
#define CODEC_WAV "/.rockbox/codecs/wav.codec"
|
||||
#define CODEC_A52 "/.rockbox/codecs/a52.codec"
|
||||
#define CODEC_MPC "/.rockbox/codecs/mpc.codec"
|
||||
#define CODEC_WAVPACK "/.rockbox/codecs/wavpack.codec"
|
||||
|
||||
#define AUDIO_FILL_CYCLE (1024*256)
|
||||
#define AUDIO_DEFAULT_WATERMARK (1024*512)
|
||||
|
@ -96,6 +98,10 @@ static volatile bool paused;
|
|||
#define MALLOC_BUFSIZE (512*1024)
|
||||
#define GUARD_BUFSIZE (8*1024)
|
||||
|
||||
/* As defined in plugin.lds */
|
||||
#define CODEC_IRAM_ORIGIN 0x10010000
|
||||
#define CODEC_IRAM_SIZE 0x8000
|
||||
|
||||
extern bool audio_is_initialized;
|
||||
|
||||
/* Buffer control thread. */
|
||||
|
@ -108,19 +114,39 @@ static struct event_queue codec_queue;
|
|||
static long codec_stack[(DEFAULT_STACK_SIZE + 0x2500)/sizeof(long)] IDATA_ATTR;
|
||||
static const char codec_thread_name[] = "codec";
|
||||
|
||||
/* Voice codec thread. */
|
||||
static struct event_queue voice_codec_queue;
|
||||
/* Not enough IRAM for this. */
|
||||
static long voice_codec_stack[(DEFAULT_STACK_SIZE + 0x2500)/sizeof(long)];
|
||||
static const char voice_codec_thread_name[] = "voice codec";
|
||||
|
||||
static struct mutex mutex_bufferfill;
|
||||
static struct mutex mutex_codecthread;
|
||||
|
||||
static struct mp3entry id3_voice;
|
||||
|
||||
#define CODEC_IDX_AUDIO 0
|
||||
#define CODEC_IDX_VOICE 1
|
||||
|
||||
static char *voicebuf;
|
||||
static int voice_remaining;
|
||||
static bool voice_is_playing;
|
||||
static void (*voice_getmore)(unsigned char** start, int* size);
|
||||
|
||||
/* Is file buffer currently being refilled? */
|
||||
static volatile bool filling;
|
||||
|
||||
volatile int current_codec;
|
||||
extern unsigned char codecbuf[];
|
||||
|
||||
/* Ring buffer where tracks and codecs are loaded. */
|
||||
static char *codecbuf;
|
||||
static char *filebuf;
|
||||
|
||||
/* Total size of the ring buffer. */
|
||||
int codecbuflen;
|
||||
int filebuflen;
|
||||
|
||||
/* Bytes available in the buffer. */
|
||||
int codecbufused;
|
||||
int filebufused;
|
||||
|
||||
/* Ring buffer read and write indexes. */
|
||||
static volatile int buf_ridx;
|
||||
|
@ -153,6 +179,7 @@ static struct track_info *cur_ti;
|
|||
|
||||
/* Codec API including function callbacks. */
|
||||
extern struct codec_api ci;
|
||||
extern struct codec_api ci_voice;
|
||||
|
||||
/* When we change a song and buffer is not in filling state, this
|
||||
variable keeps information about whether to go a next/previous track. */
|
||||
|
@ -174,6 +201,59 @@ static bool v1first = false;
|
|||
static void mp3_set_elapsed(struct mp3entry* id3);
|
||||
int mp3_get_file_pos(void);
|
||||
|
||||
static void do_swap(int idx_old, int idx_new)
|
||||
{
|
||||
#ifndef SIMULATOR
|
||||
unsigned char *iram_p = (unsigned char *)(CODEC_IRAM_ORIGIN);
|
||||
unsigned char *iram_buf[2];
|
||||
#endif
|
||||
unsigned char *dram_buf[2];
|
||||
|
||||
|
||||
#ifndef SIMULATOR
|
||||
iram_buf[0] = &filebuf[filebuflen];
|
||||
iram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE];
|
||||
memcpy(iram_buf[idx_old], iram_p, CODEC_IRAM_SIZE);
|
||||
memcpy(iram_p, iram_buf[idx_new], CODEC_IRAM_SIZE);
|
||||
#endif
|
||||
|
||||
dram_buf[0] = &filebuf[filebuflen+CODEC_IRAM_SIZE*2];
|
||||
dram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE*2+CODEC_SIZE];
|
||||
memcpy(dram_buf[idx_old], codecbuf, CODEC_SIZE);
|
||||
memcpy(codecbuf, dram_buf[idx_new], CODEC_SIZE);
|
||||
}
|
||||
|
||||
static void swap_codec(void)
|
||||
{
|
||||
int last_codec;
|
||||
|
||||
logf("swapping codec:%d", current_codec);
|
||||
|
||||
/* We should swap codecs' IRAM contents and code space. */
|
||||
do_swap(current_codec, !current_codec);
|
||||
|
||||
last_codec = current_codec;
|
||||
current_codec = !current_codec;
|
||||
|
||||
/* Release the semaphore and force a task switch. */
|
||||
mutex_unlock(&mutex_codecthread);
|
||||
sleep(1);
|
||||
|
||||
/* Waiting until we are ready to run again. */
|
||||
mutex_lock(&mutex_codecthread);
|
||||
|
||||
/* Check if codec swap did not happen. */
|
||||
if (current_codec != last_codec)
|
||||
{
|
||||
logf("no codec switch happened!");
|
||||
do_swap(current_codec, !current_codec);
|
||||
current_codec = !current_codec;
|
||||
}
|
||||
|
||||
invalidate_icache();
|
||||
logf("codec resuming:%d", current_codec);
|
||||
}
|
||||
|
||||
bool codec_pcmbuf_insert_split_callback(void *ch1, void *ch2,
|
||||
long length)
|
||||
{
|
||||
|
@ -209,12 +289,41 @@ bool codec_pcmbuf_insert_split_callback(void *ch1, void *ch2,
|
|||
pcmbuf_flush_buffer(0);
|
||||
DEBUGF("Warning: dsp_input_size(%ld=dsp_output_size(%ld))=%ld <= 0\n",
|
||||
output_size, length, input_size);
|
||||
/* should we really continue, or should we break? */
|
||||
/* should we really continue, or should we break?
|
||||
* We should probably continue because calling pcmbuf_flush_buffer(0)
|
||||
* will wrap the buffer if it was fully filled and so next call to
|
||||
* pcmbuf_request_buffer should give the requested output_size. */
|
||||
continue;
|
||||
}
|
||||
|
||||
output_size = dsp_process(dest, src, input_size);
|
||||
pcmbuf_flush_buffer(output_size);
|
||||
|
||||
/* Hotswap between audio and voice codecs as necessary. */
|
||||
switch (current_codec)
|
||||
{
|
||||
case CODEC_IDX_AUDIO:
|
||||
pcmbuf_flush_buffer(output_size);
|
||||
if (voice_is_playing && pcmbuf_usage() > 30
|
||||
&& pcmbuf_mix_usage() < 20)
|
||||
{
|
||||
cpu_boost(true);
|
||||
swap_codec();
|
||||
cpu_boost(false);
|
||||
}
|
||||
break ;
|
||||
|
||||
case CODEC_IDX_VOICE:
|
||||
if (audio_codec_loaded) {
|
||||
pcmbuf_mix(dest, output_size);
|
||||
if ((pcmbuf_usage() < 10)
|
||||
|| pcmbuf_mix_usage() > 70)
|
||||
swap_codec();
|
||||
} else {
|
||||
pcmbuf_flush_buffer(output_size);
|
||||
}
|
||||
break ;
|
||||
}
|
||||
|
||||
length -= input_size;
|
||||
}
|
||||
|
||||
|
@ -241,6 +350,9 @@ bool codec_pcmbuf_insert_callback(char *buf, long length)
|
|||
void* get_codec_memory_callback(long *size)
|
||||
{
|
||||
*size = MALLOC_BUFSIZE;
|
||||
if (voice_codec_loaded)
|
||||
return &audiobuf[talk_get_bufsize()];
|
||||
|
||||
return &audiobuf[0];
|
||||
}
|
||||
|
||||
|
@ -248,7 +360,7 @@ void codec_set_elapsed_callback(unsigned int value)
|
|||
{
|
||||
unsigned int latency;
|
||||
|
||||
if (ci.stop_codec)
|
||||
if (ci.stop_codec || current_codec == CODEC_IDX_VOICE)
|
||||
return ;
|
||||
|
||||
latency = pcmbuf_get_latency();
|
||||
|
@ -265,7 +377,7 @@ void codec_set_offset_callback(unsigned int value)
|
|||
{
|
||||
unsigned int latency;
|
||||
|
||||
if (ci.stop_codec)
|
||||
if (ci.stop_codec || current_codec == CODEC_IDX_VOICE)
|
||||
return ;
|
||||
|
||||
latency = pcmbuf_get_latency() * cur_ti->id3.bitrate / 8;
|
||||
|
@ -283,7 +395,7 @@ long codec_filebuf_callback(void *ptr, long size)
|
|||
int copy_n;
|
||||
int part_n;
|
||||
|
||||
if (ci.stop_codec || !playing)
|
||||
if (ci.stop_codec || !playing || current_codec == CODEC_IDX_VOICE)
|
||||
return 0;
|
||||
|
||||
copy_n = MIN((off_t)size, (off_t)cur_ti->available + cur_ti->filerem);
|
||||
|
@ -297,26 +409,78 @@ long codec_filebuf_callback(void *ptr, long size)
|
|||
if (copy_n == 0)
|
||||
return 0;
|
||||
|
||||
part_n = MIN(copy_n, codecbuflen - buf_ridx);
|
||||
memcpy(buf, &codecbuf[buf_ridx], part_n);
|
||||
part_n = MIN(copy_n, filebuflen - buf_ridx);
|
||||
memcpy(buf, &filebuf[buf_ridx], part_n);
|
||||
if (part_n < copy_n) {
|
||||
memcpy(&buf[part_n], &codecbuf[0], copy_n - part_n);
|
||||
memcpy(&buf[part_n], &filebuf[0], copy_n - part_n);
|
||||
}
|
||||
|
||||
buf_ridx += copy_n;
|
||||
if (buf_ridx >= codecbuflen)
|
||||
buf_ridx -= codecbuflen;
|
||||
if (buf_ridx >= filebuflen)
|
||||
buf_ridx -= filebuflen;
|
||||
ci.curpos += copy_n;
|
||||
cur_ti->available -= copy_n;
|
||||
codecbufused -= copy_n;
|
||||
filebufused -= copy_n;
|
||||
|
||||
return copy_n;
|
||||
}
|
||||
|
||||
void* voice_request_data(long *realsize, long reqsize)
|
||||
{
|
||||
while (queue_empty(&voice_codec_queue) && (voice_remaining == 0
|
||||
|| voicebuf == NULL) && !ci_voice.stop_codec)
|
||||
{
|
||||
yield();
|
||||
if (audio_codec_loaded && (pcmbuf_usage() < 30
|
||||
|| !voice_is_playing || voicebuf == NULL))
|
||||
{
|
||||
swap_codec();
|
||||
}
|
||||
if (!voice_is_playing)
|
||||
sleep(HZ/16);
|
||||
|
||||
if (voice_remaining)
|
||||
{
|
||||
voice_is_playing = true;
|
||||
break ;
|
||||
}
|
||||
|
||||
if (voice_getmore != NULL)
|
||||
{
|
||||
voice_getmore((unsigned char **)&voicebuf, (int *)&voice_remaining);
|
||||
|
||||
if (!voice_remaining)
|
||||
{
|
||||
voice_is_playing = false;
|
||||
/* Force pcm playback. */
|
||||
pcmbuf_play_start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reqsize < 0)
|
||||
reqsize = 0;
|
||||
|
||||
voice_is_playing = true;
|
||||
*realsize = voice_remaining;
|
||||
if (*realsize > reqsize)
|
||||
*realsize = reqsize;
|
||||
|
||||
if (*realsize == 0)
|
||||
return NULL;
|
||||
|
||||
return voicebuf;
|
||||
}
|
||||
|
||||
void* codec_request_buffer_callback(long *realsize, long reqsize)
|
||||
{
|
||||
long part_n;
|
||||
|
||||
|
||||
/* Voice codec. */
|
||||
if (current_codec == CODEC_IDX_VOICE) {
|
||||
return voice_request_data(realsize, reqsize);
|
||||
}
|
||||
|
||||
if (ci.stop_codec || !playing) {
|
||||
*realsize = 0;
|
||||
return NULL;
|
||||
|
@ -335,22 +499,22 @@ void* codec_request_buffer_callback(long *realsize, long reqsize)
|
|||
}
|
||||
}
|
||||
|
||||
part_n = MIN((int)*realsize, codecbuflen - buf_ridx);
|
||||
part_n = MIN((int)*realsize, filebuflen - buf_ridx);
|
||||
if (part_n < *realsize) {
|
||||
part_n += GUARD_BUFSIZE;
|
||||
if (part_n < *realsize)
|
||||
*realsize = part_n;
|
||||
memcpy(&codecbuf[codecbuflen], &codecbuf[0], *realsize -
|
||||
(codecbuflen - buf_ridx));
|
||||
memcpy(&filebuf[filebuflen], &filebuf[0], *realsize -
|
||||
(filebuflen - buf_ridx));
|
||||
}
|
||||
|
||||
return (char *)&codecbuf[buf_ridx];
|
||||
return (char *)&filebuf[buf_ridx];
|
||||
}
|
||||
|
||||
static bool rebuffer_and_seek(int newpos)
|
||||
{
|
||||
int fd;
|
||||
|
||||
|
||||
logf("Re-buffering song");
|
||||
mutex_lock(&mutex_bufferfill);
|
||||
|
||||
|
@ -367,7 +531,7 @@ static bool rebuffer_and_seek(int newpos)
|
|||
|
||||
/* Clear codec buffer. */
|
||||
audio_invalidate_tracks();
|
||||
codecbufused = 0;
|
||||
filebufused = 0;
|
||||
buf_ridx = buf_widx = 0;
|
||||
cur_ti->filerem = cur_ti->filesize - newpos;
|
||||
cur_ti->filepos = newpos;
|
||||
|
@ -390,6 +554,15 @@ static bool rebuffer_and_seek(int newpos)
|
|||
|
||||
void codec_advance_buffer_callback(long amount)
|
||||
{
|
||||
if (current_codec == CODEC_IDX_VOICE) {
|
||||
//logf("voice ad.buf:%d", amount);
|
||||
amount = MAX(0, MIN(amount, voice_remaining));
|
||||
voicebuf += amount;
|
||||
voice_remaining -= amount;
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
if (amount > cur_ti->available + cur_ti->filerem)
|
||||
amount = cur_ti->available + cur_ti->filerem;
|
||||
|
||||
|
@ -400,10 +573,10 @@ void codec_advance_buffer_callback(long amount)
|
|||
}
|
||||
|
||||
buf_ridx += amount;
|
||||
if (buf_ridx >= codecbuflen)
|
||||
buf_ridx -= codecbuflen;
|
||||
if (buf_ridx >= filebuflen)
|
||||
buf_ridx -= filebuflen;
|
||||
cur_ti->available -= amount;
|
||||
codecbufused -= amount;
|
||||
filebufused -= amount;
|
||||
ci.curpos += amount;
|
||||
codec_set_offset_callback(ci.curpos);
|
||||
}
|
||||
|
@ -411,15 +584,18 @@ void codec_advance_buffer_callback(long amount)
|
|||
void codec_advance_buffer_loc_callback(void *ptr)
|
||||
{
|
||||
long amount;
|
||||
|
||||
amount = (int)ptr - (int)&codecbuf[buf_ridx];
|
||||
|
||||
if (current_codec == CODEC_IDX_VOICE)
|
||||
amount = (int)ptr - (int)voicebuf;
|
||||
else
|
||||
amount = (int)ptr - (int)&filebuf[buf_ridx];
|
||||
codec_advance_buffer_callback(amount);
|
||||
}
|
||||
|
||||
off_t codec_mp3_get_filepos_callback(int newtime)
|
||||
{
|
||||
off_t newpos;
|
||||
|
||||
|
||||
cur_ti->id3.elapsed = newtime;
|
||||
newpos = mp3_get_file_pos();
|
||||
|
||||
|
@ -429,7 +605,10 @@ off_t codec_mp3_get_filepos_callback(int newtime)
|
|||
bool codec_seek_buffer_callback(off_t newpos)
|
||||
{
|
||||
int difference;
|
||||
|
||||
|
||||
if (current_codec == CODEC_IDX_VOICE)
|
||||
return false;
|
||||
|
||||
if (newpos < 0)
|
||||
newpos = 0;
|
||||
|
||||
|
@ -457,11 +636,11 @@ bool codec_seek_buffer_callback(off_t newpos)
|
|||
|
||||
/* Seeking inside buffer space. */
|
||||
logf("seek: -%d", difference);
|
||||
codecbufused += difference;
|
||||
filebufused += difference;
|
||||
cur_ti->available += difference;
|
||||
buf_ridx -= difference;
|
||||
if (buf_ridx < 0)
|
||||
buf_ridx = codecbuflen + buf_ridx;
|
||||
buf_ridx = filebuflen + buf_ridx;
|
||||
ci.curpos -= difference;
|
||||
if (!pcmbuf_is_crossfade_active())
|
||||
pcmbuf_play_stop();
|
||||
|
@ -473,8 +652,11 @@ static void set_filebuf_watermark(int seconds)
|
|||
{
|
||||
long bytes;
|
||||
|
||||
if (current_codec == CODEC_IDX_VOICE)
|
||||
return ;
|
||||
|
||||
bytes = MAX((int)cur_ti->id3.bitrate * seconds * (1000/8), conf_watermark);
|
||||
bytes = MIN(bytes, codecbuflen / 2);
|
||||
bytes = MIN(bytes, filebuflen / 2);
|
||||
conf_watermark = bytes;
|
||||
}
|
||||
|
||||
|
@ -540,7 +722,7 @@ void yield_codecs(void)
|
|||
sleep(5);
|
||||
while ((pcmbuf_is_crossfade_active() || pcmbuf_is_lowdata())
|
||||
&& !ci.stop_codec && playing && queue_empty(&audio_queue)
|
||||
&& codecbufused > (128*1024))
|
||||
&& filebufused > (128*1024))
|
||||
yield();
|
||||
}
|
||||
|
||||
|
@ -552,18 +734,18 @@ void strip_id3v1_tag(void)
|
|||
int tagptr;
|
||||
bool found = true;
|
||||
|
||||
if (codecbufused >= 128)
|
||||
if (filebufused >= 128)
|
||||
{
|
||||
tagptr = buf_widx - 128;
|
||||
if (tagptr < 0)
|
||||
tagptr += codecbuflen;
|
||||
tagptr += filebuflen;
|
||||
|
||||
for(i = 0;i < 3;i++)
|
||||
{
|
||||
if(tagptr >= codecbuflen)
|
||||
tagptr -= codecbuflen;
|
||||
if(tagptr >= filebuflen)
|
||||
tagptr -= filebuflen;
|
||||
|
||||
if(codecbuf[tagptr] != tag[i])
|
||||
if(filebuf[tagptr] != tag[i])
|
||||
{
|
||||
found = false;
|
||||
break;
|
||||
|
@ -578,7 +760,7 @@ void strip_id3v1_tag(void)
|
|||
logf("Skipping ID3v1 tag\n");
|
||||
buf_widx -= 128;
|
||||
tracks[track_widx].available -= 128;
|
||||
codecbufused -= 128;
|
||||
filebufused -= 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -603,9 +785,9 @@ void audio_fill_file_buffer(void)
|
|||
|
||||
if (fill_bytesleft == 0)
|
||||
break ;
|
||||
rc = MIN(conf_filechunk, codecbuflen - buf_widx);
|
||||
rc = MIN(conf_filechunk, filebuflen - buf_widx);
|
||||
rc = MIN(rc, fill_bytesleft);
|
||||
rc = read(current_fd, &codecbuf[buf_widx], rc);
|
||||
rc = read(current_fd, &filebuf[buf_widx], rc);
|
||||
if (rc <= 0) {
|
||||
tracks[track_widx].filerem = 0;
|
||||
strip_id3v1_tag();
|
||||
|
@ -613,13 +795,13 @@ void audio_fill_file_buffer(void)
|
|||
}
|
||||
|
||||
buf_widx += rc;
|
||||
if (buf_widx >= codecbuflen)
|
||||
buf_widx -= codecbuflen;
|
||||
if (buf_widx >= filebuflen)
|
||||
buf_widx -= filebuflen;
|
||||
i += rc;
|
||||
tracks[track_widx].available += rc;
|
||||
tracks[track_widx].filerem -= rc;
|
||||
tracks[track_widx].filepos += rc;
|
||||
codecbufused += rc;
|
||||
filebufused += rc;
|
||||
fill_bytesleft -= rc;
|
||||
}
|
||||
|
||||
|
@ -725,15 +907,15 @@ bool loadcodec(const char *trackname, bool start_play)
|
|||
while (i < size) {
|
||||
yield_codecs();
|
||||
|
||||
copy_n = MIN(conf_filechunk, codecbuflen - buf_widx);
|
||||
rc = read(fd, &codecbuf[buf_widx], copy_n);
|
||||
copy_n = MIN(conf_filechunk, filebuflen - buf_widx);
|
||||
rc = read(fd, &filebuf[buf_widx], copy_n);
|
||||
if (rc < 0)
|
||||
return false;
|
||||
buf_widx += rc;
|
||||
codecbufused += rc;
|
||||
filebufused += rc;
|
||||
fill_bytesleft -= rc;
|
||||
if (buf_widx >= codecbuflen)
|
||||
buf_widx -= codecbuflen;
|
||||
if (buf_widx >= filebuflen)
|
||||
buf_widx -= filebuflen;
|
||||
i += rc;
|
||||
}
|
||||
close(fd);
|
||||
|
@ -840,20 +1022,23 @@ bool audio_load_track(int offset, bool start_play, int peek_offset)
|
|||
tracks[track_widx].playlist_offset = peek_offset;
|
||||
last_peek_offset = peek_offset;
|
||||
|
||||
if (buf_widx >= codecbuflen)
|
||||
buf_widx -= codecbuflen;
|
||||
if (buf_widx >= filebuflen)
|
||||
buf_widx -= filebuflen;
|
||||
|
||||
/* Set default values */
|
||||
if (start_play) {
|
||||
int last_codec = current_codec;
|
||||
current_codec = CODEC_IDX_AUDIO;
|
||||
conf_bufferlimit = 0;
|
||||
conf_watermark = AUDIO_DEFAULT_WATERMARK;
|
||||
conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
|
||||
dsp_configure(DSP_RESET, 0);
|
||||
ci.configure(CODEC_DSP_ENABLE, false);
|
||||
current_codec = last_codec;
|
||||
}
|
||||
|
||||
/* Load the codec. */
|
||||
tracks[track_widx].codecbuf = &codecbuf[buf_widx];
|
||||
tracks[track_widx].codecbuf = &filebuf[buf_widx];
|
||||
if (!loadcodec(trackname, start_play)) {
|
||||
close(fd);
|
||||
/* Stop buffer filling if codec load failed. */
|
||||
|
@ -870,7 +1055,7 @@ bool audio_load_track(int offset, bool start_play, int peek_offset)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
// tracks[track_widx].filebuf = &codecbuf[buf_widx];
|
||||
// tracks[track_widx].filebuf = &filebuf[buf_widx];
|
||||
tracks[track_widx].start_pos = 0;
|
||||
|
||||
/* Get track metadata if we don't already have it. */
|
||||
|
@ -933,10 +1118,10 @@ bool audio_load_track(int offset, bool start_play, int peek_offset)
|
|||
if (fill_bytesleft == 0)
|
||||
break ;
|
||||
|
||||
copy_n = MIN(conf_filechunk, codecbuflen - buf_widx);
|
||||
copy_n = MIN(conf_filechunk, filebuflen - buf_widx);
|
||||
copy_n = MIN(size - i, copy_n);
|
||||
copy_n = MIN((int)fill_bytesleft, copy_n);
|
||||
rc = read(fd, &codecbuf[buf_widx], copy_n);
|
||||
rc = read(fd, &filebuf[buf_widx], copy_n);
|
||||
if (rc < copy_n) {
|
||||
logf("File error!");
|
||||
tracks[track_widx].filesize = 0;
|
||||
|
@ -945,12 +1130,12 @@ bool audio_load_track(int offset, bool start_play, int peek_offset)
|
|||
return false;
|
||||
}
|
||||
buf_widx += rc;
|
||||
if (buf_widx >= codecbuflen)
|
||||
buf_widx -= codecbuflen;
|
||||
if (buf_widx >= filebuflen)
|
||||
buf_widx -= filebuflen;
|
||||
i += rc;
|
||||
tracks[track_widx].available += rc;
|
||||
tracks[track_widx].filerem -= rc;
|
||||
codecbufused += rc;
|
||||
filebufused += rc;
|
||||
fill_bytesleft -= rc;
|
||||
}
|
||||
|
||||
|
@ -997,10 +1182,10 @@ void audio_play_start(int offset)
|
|||
track_ridx = 0;
|
||||
buf_ridx = 0;
|
||||
buf_widx = 0;
|
||||
codecbufused = 0;
|
||||
filebufused = 0;
|
||||
pcmbuf_set_boost_mode(true);
|
||||
|
||||
fill_bytesleft = codecbuflen;
|
||||
fill_bytesleft = filebuflen;
|
||||
filling = true;
|
||||
last_peek_offset = -1;
|
||||
if (audio_load_track(offset, true, 0)) {
|
||||
|
@ -1089,7 +1274,7 @@ void initialize_buffer_fill(void)
|
|||
int cur_idx, i;
|
||||
|
||||
|
||||
fill_bytesleft = codecbuflen - codecbufused;
|
||||
fill_bytesleft = filebuflen - filebufused;
|
||||
cur_ti->start_pos = ci.curpos;
|
||||
|
||||
pcmbuf_set_boost_mode(true);
|
||||
|
@ -1124,7 +1309,7 @@ void initialize_buffer_fill(void)
|
|||
void audio_check_buffer(void)
|
||||
{
|
||||
/* Start buffer filling as necessary. */
|
||||
if ((codecbufused > conf_watermark || !queue_empty(&audio_queue)
|
||||
if ((filebufused > conf_watermark || !queue_empty(&audio_queue)
|
||||
|| !playing || ci.stop_codec || ci.reload_codec) && !filling)
|
||||
return ;
|
||||
|
||||
|
@ -1132,8 +1317,8 @@ void audio_check_buffer(void)
|
|||
|
||||
/* Limit buffering size at first run. */
|
||||
if (conf_bufferlimit && fill_bytesleft > conf_bufferlimit
|
||||
- codecbufused) {
|
||||
fill_bytesleft = MAX(0, conf_bufferlimit - codecbufused);
|
||||
- filebufused) {
|
||||
fill_bytesleft = MAX(0, conf_bufferlimit - filebufused);
|
||||
}
|
||||
|
||||
/* Try to load remainings of the file. */
|
||||
|
@ -1169,27 +1354,27 @@ void audio_update_trackinfo(void)
|
|||
{
|
||||
if (new_track >= 0) {
|
||||
buf_ridx += cur_ti->available;
|
||||
codecbufused -= cur_ti->available;
|
||||
filebufused -= cur_ti->available;
|
||||
|
||||
cur_ti = &tracks[track_ridx];
|
||||
buf_ridx += cur_ti->codecsize;
|
||||
codecbufused -= cur_ti->codecsize;
|
||||
if (buf_ridx >= codecbuflen)
|
||||
buf_ridx -= codecbuflen;
|
||||
filebufused -= cur_ti->codecsize;
|
||||
if (buf_ridx >= filebuflen)
|
||||
buf_ridx -= filebuflen;
|
||||
|
||||
if (!filling)
|
||||
pcmbuf_set_boost_mode(false);
|
||||
} else {
|
||||
buf_ridx -= ci.curpos + cur_ti->codecsize;
|
||||
codecbufused += ci.curpos + cur_ti->codecsize;
|
||||
filebufused += ci.curpos + cur_ti->codecsize;
|
||||
cur_ti->available = cur_ti->filesize;
|
||||
|
||||
cur_ti = &tracks[track_ridx];
|
||||
buf_ridx -= cur_ti->filesize;
|
||||
codecbufused += cur_ti->filesize;
|
||||
filebufused += cur_ti->filesize;
|
||||
cur_ti->available = cur_ti->filesize;
|
||||
if (buf_ridx < 0)
|
||||
buf_ridx = codecbuflen + buf_ridx;
|
||||
buf_ridx = filebuflen + buf_ridx;
|
||||
}
|
||||
|
||||
ci.filesize = cur_ti->filesize;
|
||||
|
@ -1220,7 +1405,7 @@ static void audio_stop_playback(void)
|
|||
current_fd = -1;
|
||||
}
|
||||
pcmbuf_play_stop();
|
||||
while (codec_loaded)
|
||||
while (audio_codec_loaded)
|
||||
yield();
|
||||
pcm_play_pause(true);
|
||||
track_count = 0;
|
||||
|
@ -1267,6 +1452,11 @@ static int get_codec_base_type(int type)
|
|||
|
||||
bool codec_request_next_track_callback(void)
|
||||
{
|
||||
if (current_codec == CODEC_IDX_VOICE) {
|
||||
voice_remaining = 0;
|
||||
return !ci_voice.stop_codec;
|
||||
}
|
||||
|
||||
if (ci.stop_codec || !playing)
|
||||
return false;
|
||||
|
||||
|
@ -1309,8 +1499,8 @@ bool codec_request_next_track_callback(void)
|
|||
if (--track_ridx < 0)
|
||||
track_ridx = MAX_TRACK-1;
|
||||
if (tracks[track_ridx].filesize == 0 ||
|
||||
codecbufused+ci.curpos+tracks[track_ridx].filesize
|
||||
/*+ (off_t)tracks[track_ridx].codecsize*/ > codecbuflen) {
|
||||
filebufused+ci.curpos+tracks[track_ridx].filesize
|
||||
/*+ (off_t)tracks[track_ridx].codecsize*/ > filebuflen) {
|
||||
logf("Loading from disk...");
|
||||
new_track = 0;
|
||||
last_index = -1;
|
||||
|
@ -1379,10 +1569,10 @@ void audio_invalidate_tracks(void)
|
|||
track_widx = track_ridx;
|
||||
/* Mark all other entries null (also buffered wrong metadata). */
|
||||
audio_clear_track_entries(false);
|
||||
codecbufused = cur_ti->available;
|
||||
filebufused = cur_ti->available;
|
||||
buf_widx = buf_ridx + cur_ti->available;
|
||||
if (buf_widx >= codecbuflen)
|
||||
buf_widx -= codecbuflen;
|
||||
if (buf_widx >= filebuflen)
|
||||
buf_widx -= filebuflen;
|
||||
read_next_metadata();
|
||||
}
|
||||
|
||||
|
@ -1436,7 +1626,7 @@ void audio_thread(void)
|
|||
ci.reload_codec = false;
|
||||
ci.seek_time = 0;
|
||||
pcmbuf_crossfade_init(CROSSFADE_MODE_CROSSFADE);
|
||||
while (codec_loaded)
|
||||
while (audio_codec_loaded)
|
||||
yield();
|
||||
audio_play_start((int)ev.data);
|
||||
playlist_update_resume_info(audio_current_track());
|
||||
|
@ -1462,11 +1652,13 @@ void audio_thread(void)
|
|||
|
||||
case AUDIO_NEXT:
|
||||
logf("audio_next");
|
||||
pcmbuf_beep(5000, 100, 5000);
|
||||
initiate_track_change(1);
|
||||
break ;
|
||||
|
||||
case AUDIO_PREV:
|
||||
logf("audio_prev");
|
||||
pcmbuf_beep(5000, 100, 5000);
|
||||
initiate_track_change(-1);
|
||||
break;
|
||||
|
||||
|
@ -1514,8 +1706,11 @@ void codec_thread(void)
|
|||
switch (ev.id) {
|
||||
case CODEC_LOAD_DISK:
|
||||
ci.stop_codec = false;
|
||||
codec_loaded = true;
|
||||
status = codec_load_file((char *)ev.data);
|
||||
audio_codec_loaded = true;
|
||||
mutex_lock(&mutex_codecthread);
|
||||
current_codec = CODEC_IDX_AUDIO;
|
||||
status = codec_load_file((char *)ev.data, &ci);
|
||||
mutex_unlock(&mutex_codecthread);
|
||||
break ;
|
||||
|
||||
case CODEC_LOAD:
|
||||
|
@ -1531,10 +1726,13 @@ void codec_thread(void)
|
|||
}
|
||||
|
||||
ci.stop_codec = false;
|
||||
wrap = (int)&codecbuf[codecbuflen] - (int)cur_ti->codecbuf;
|
||||
codec_loaded = true;
|
||||
status = codec_load_ram(cur_ti->codecbuf, codecsize,
|
||||
&codecbuf[0], wrap);
|
||||
wrap = (int)&filebuf[filebuflen] - (int)cur_ti->codecbuf;
|
||||
audio_codec_loaded = true;
|
||||
mutex_lock(&mutex_codecthread);
|
||||
current_codec = CODEC_IDX_AUDIO;
|
||||
status = codec_load_ram(cur_ti->codecbuf, codecsize,
|
||||
&filebuf[0], wrap, &ci);
|
||||
mutex_unlock(&mutex_codecthread);
|
||||
break ;
|
||||
|
||||
#ifndef SIMULATOR
|
||||
|
@ -1545,7 +1743,7 @@ void codec_thread(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
codec_loaded = false;
|
||||
audio_codec_loaded = false;
|
||||
|
||||
switch (ev.id) {
|
||||
case CODEC_LOAD_DISK:
|
||||
|
@ -1569,6 +1767,83 @@ void codec_thread(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void reset_buffer(void)
|
||||
{
|
||||
filebuf = &audiobuf[MALLOC_BUFSIZE];
|
||||
filebuflen = audiobufend - audiobuf - pcmbuf_get_bufsize()
|
||||
- PCMBUF_GUARD - MALLOC_BUFSIZE - GUARD_BUFSIZE;
|
||||
|
||||
if (talk_get_bufsize() && voice_codec_loaded)
|
||||
{
|
||||
filebuf = &filebuf[talk_get_bufsize()];
|
||||
filebuflen -= 2*CODEC_IRAM_SIZE + 2*CODEC_SIZE + talk_get_bufsize();
|
||||
}
|
||||
}
|
||||
|
||||
void voice_codec_thread(void)
|
||||
{
|
||||
struct event ev;
|
||||
int status;
|
||||
|
||||
current_codec = CODEC_IDX_AUDIO;
|
||||
voice_codec_loaded = false;
|
||||
while (1) {
|
||||
status = 0;
|
||||
queue_wait(&voice_codec_queue, &ev);
|
||||
switch (ev.id) {
|
||||
case CODEC_LOAD_DISK:
|
||||
logf("Loading voice codec");
|
||||
audio_stop_playback();
|
||||
mutex_lock(&mutex_codecthread);
|
||||
current_codec = CODEC_IDX_VOICE;
|
||||
dsp_configure(DSP_RESET, 0);
|
||||
ci.configure(CODEC_DSP_ENABLE, (bool *)true);
|
||||
voice_remaining = 0;
|
||||
voice_getmore = NULL;
|
||||
voice_codec_loaded = true;
|
||||
reset_buffer();
|
||||
ci_voice.stop_codec = false;
|
||||
|
||||
status = codec_load_file((char *)ev.data, &ci_voice);
|
||||
|
||||
logf("Voice codec finished");
|
||||
audio_stop_playback();
|
||||
mutex_unlock(&mutex_codecthread);
|
||||
current_codec = CODEC_IDX_AUDIO;
|
||||
voice_codec_loaded = false;
|
||||
reset_buffer();
|
||||
break ;
|
||||
|
||||
#ifndef SIMULATOR
|
||||
case SYS_USB_CONNECTED:
|
||||
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
||||
usb_wait_for_disconnect(&voice_codec_queue);
|
||||
break ;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void voice_init(void)
|
||||
{
|
||||
while (voice_codec_loaded)
|
||||
{
|
||||
logf("Terminating voice codec");
|
||||
ci_voice.stop_codec = true;
|
||||
if (current_codec != CODEC_IDX_VOICE)
|
||||
swap_codec();
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (!talk_get_bufsize())
|
||||
return ;
|
||||
|
||||
logf("Starting voice codec");
|
||||
queue_post(&voice_codec_queue, CODEC_LOAD_DISK, (void *)CODEC_MPA_L3);
|
||||
while (!voice_codec_loaded)
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
struct mp3entry* audio_current_track(void)
|
||||
{
|
||||
// logf("audio_current_track");
|
||||
|
@ -1620,7 +1895,7 @@ void audio_stop(void)
|
|||
{
|
||||
logf("audio_stop");
|
||||
queue_post(&audio_queue, AUDIO_STOP, 0);
|
||||
while (playing || codec_loaded)
|
||||
while (playing || audio_codec_loaded)
|
||||
yield();
|
||||
}
|
||||
|
||||
|
@ -1805,6 +2080,16 @@ int mp3_get_file_pos(void)
|
|||
return pos;
|
||||
}
|
||||
|
||||
void mp3_play_data(const unsigned char* start, int size,
|
||||
void (*get_more)(unsigned char** start, int* size))
|
||||
{
|
||||
voice_getmore = get_more;
|
||||
voicebuf = (unsigned char *)start;
|
||||
voice_remaining = size;
|
||||
voice_is_playing = true;
|
||||
pcmbuf_reset_mixpos();
|
||||
}
|
||||
|
||||
void audio_set_buffer_margin(int setting)
|
||||
{
|
||||
int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600};
|
||||
|
@ -1827,7 +2112,7 @@ void audio_set_crossfade(int type)
|
|||
offset = cur_ti->id3.offset;
|
||||
|
||||
if (type == CROSSFADE_MODE_OFF)
|
||||
seconds = 0;
|
||||
seconds = 1;
|
||||
|
||||
/* Buffer has to be at least 2s long. */
|
||||
seconds += 2;
|
||||
|
@ -1843,12 +2128,13 @@ void audio_set_crossfade(int type)
|
|||
if (was_playing)
|
||||
splash(0, true, str(LANG_RESTARTING_PLAYBACK));
|
||||
pcmbuf_init(size);
|
||||
pcmbuf_crossfade_enable(seconds > 2);
|
||||
codecbuflen = audiobufend - audiobuf - pcmbuf_get_bufsize()
|
||||
- PCMBUF_GUARD - MALLOC_BUFSIZE - GUARD_BUFSIZE;
|
||||
pcmbuf_crossfade_enable(type != CROSSFADE_MODE_OFF);
|
||||
reset_buffer();
|
||||
logf("abuf:%dB", pcmbuf_get_bufsize());
|
||||
logf("fbuf:%dB", codecbuflen);
|
||||
logf("fbuf:%dB", filebuflen);
|
||||
|
||||
voice_init();
|
||||
|
||||
/* Restart playback. */
|
||||
if (was_playing) {
|
||||
audio_play(offset);
|
||||
|
@ -1856,7 +2142,7 @@ void audio_set_crossfade(int type)
|
|||
/* Wait for the playback to start again (and display the splash
|
||||
screen during that period. */
|
||||
playing = true;
|
||||
while (playing && !codec_loaded)
|
||||
while (playing && !audio_codec_loaded)
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
@ -1884,13 +2170,17 @@ void test_unbuffer_event(struct mp3entry *id3, bool last_track)
|
|||
|
||||
void audio_init(void)
|
||||
{
|
||||
static bool voicetagtrue = true;
|
||||
|
||||
logf("audio api init");
|
||||
pcm_init();
|
||||
codecbufused = 0;
|
||||
filebufused = 0;
|
||||
filling = false;
|
||||
codecbuf = &audiobuf[MALLOC_BUFSIZE];
|
||||
current_codec = CODEC_IDX_AUDIO;
|
||||
filebuf = &audiobuf[MALLOC_BUFSIZE];
|
||||
playing = false;
|
||||
codec_loaded = false;
|
||||
audio_codec_loaded = false;
|
||||
voice_is_playing = false;
|
||||
paused = false;
|
||||
track_changed = false;
|
||||
current_fd = -1;
|
||||
|
@ -1918,12 +2208,25 @@ void audio_init(void)
|
|||
ci.set_offset = codec_set_offset_callback;
|
||||
ci.configure = codec_configure_callback;
|
||||
|
||||
memcpy(&ci_voice, &ci, sizeof(struct codec_api));
|
||||
memset(&id3_voice, 0, sizeof(struct mp3entry));
|
||||
ci_voice.taginfo_ready = &voicetagtrue;
|
||||
ci_voice.id3 = &id3_voice;
|
||||
ci_voice.pcmbuf_insert = codec_pcmbuf_insert_callback;
|
||||
id3_voice.frequency = 11200;
|
||||
id3_voice.length = 1000000L;
|
||||
|
||||
mutex_init(&mutex_bufferfill);
|
||||
mutex_init(&mutex_codecthread);
|
||||
|
||||
queue_init(&audio_queue);
|
||||
queue_init(&codec_queue);
|
||||
queue_init(&voice_codec_queue);
|
||||
|
||||
create_thread(codec_thread, codec_stack, sizeof(codec_stack),
|
||||
codec_thread_name);
|
||||
create_thread(voice_codec_thread, voice_codec_stack,
|
||||
sizeof(voice_codec_stack), voice_codec_thread_name);
|
||||
create_thread(audio_thread, audio_stack, sizeof(audio_stack),
|
||||
audio_thread_name);
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3,
|
|||
void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3,
|
||||
bool last_track));
|
||||
void audio_invalidate_tracks(void);
|
||||
void voice_init(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1413,9 +1413,14 @@ int playlist_resume(void)
|
|||
};
|
||||
|
||||
/* use mp3 buffer for maximum load speed */
|
||||
#if CONFIG_HWCODEC != MASNONE
|
||||
talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
|
||||
buflen = (audiobufend - audiobuf);
|
||||
buffer = audiobuf;
|
||||
#else
|
||||
buflen = (audiobufend - audiobuf - talk_get_bufsize());
|
||||
buffer = &audiobuf[talk_get_bufsize()];
|
||||
#endif
|
||||
|
||||
empty_playlist(playlist, true);
|
||||
|
||||
|
@ -1827,7 +1832,9 @@ int playlist_start(int start_index, int offset)
|
|||
struct playlist_info* playlist = ¤t_playlist;
|
||||
|
||||
playlist->index = start_index;
|
||||
#if CONFIG_HWCODEC != MASNONE
|
||||
talk_buffer_steal(); /* will use the mp3 buffer */
|
||||
#endif
|
||||
audio_play(offset);
|
||||
|
||||
return 0;
|
||||
|
|
111
apps/talk.c
111
apps/talk.c
|
@ -32,7 +32,11 @@
|
|||
#include "lang.h"
|
||||
#include "talk.h"
|
||||
#include "id3.h"
|
||||
#include "logf.h"
|
||||
#include "bitswap.h"
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
#include "playback.h"
|
||||
#endif
|
||||
|
||||
/***************** Constants *****************/
|
||||
|
||||
|
@ -88,6 +92,7 @@ static int filehandle; /* global, so the MMC variant can keep the file open */
|
|||
static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */
|
||||
static long silence_len; /* length of the VOICE_PAUSE clip */
|
||||
static unsigned char* p_lastclip; /* address of latest clip, for silence add */
|
||||
static unsigned long voicefile_size = 0; /* size of the loaded voice file */
|
||||
|
||||
|
||||
/***************** Private prototypes *****************/
|
||||
|
@ -114,10 +119,28 @@ static int open_voicefile(void)
|
|||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), ROCKBOX_DIR LANG_DIR "/%s.voice", p_lang);
|
||||
|
||||
|
||||
return open(buf, O_RDONLY);
|
||||
}
|
||||
|
||||
int talk_get_bufsize(void)
|
||||
{
|
||||
return voicefile_size;
|
||||
}
|
||||
|
||||
#ifdef SIMULATOR
|
||||
static unsigned short BSWAP16(unsigned short value)
|
||||
{
|
||||
return (value >> 8) | (value << 8);
|
||||
}
|
||||
|
||||
static unsigned long BSWAP32(unsigned long value)
|
||||
{
|
||||
unsigned long hi = BSWAP16(value >> 16);
|
||||
unsigned long lo = BSWAP16(value & 0xffff);
|
||||
return (lo << 16) | hi;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* load the voice file into the mp3 buffer */
|
||||
static void load_voicefile(void)
|
||||
|
@ -125,6 +148,10 @@ static void load_voicefile(void)
|
|||
int load_size;
|
||||
int got_size;
|
||||
int file_size;
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
int length, i;
|
||||
unsigned char *buf, temp;
|
||||
#endif
|
||||
|
||||
filehandle = open_voicefile();
|
||||
if (filehandle < 0) /* failed to open */
|
||||
|
@ -141,8 +168,20 @@ static void load_voicefile(void)
|
|||
#endif
|
||||
|
||||
got_size = read(filehandle, audiobuf, load_size);
|
||||
if (got_size == load_size /* success */
|
||||
&& ((struct voicefile*)audiobuf)->table /* format check */
|
||||
if (got_size != load_size /* failure */)
|
||||
goto load_err;
|
||||
|
||||
#ifdef SIMULATOR
|
||||
logf("Byte swapping voice file");
|
||||
p_voicefile = (struct voicefile*)audiobuf;
|
||||
p_voicefile->version = BSWAP32(p_voicefile->version);
|
||||
p_voicefile->table = BSWAP32(p_voicefile->table);
|
||||
p_voicefile->id1_max = BSWAP32(p_voicefile->id1_max);
|
||||
p_voicefile->id2_max = BSWAP32(p_voicefile->id2_max);
|
||||
p_voicefile = NULL;
|
||||
#endif
|
||||
|
||||
if (((struct voicefile*)audiobuf)->table /* format check */
|
||||
== offsetof(struct voicefile, index))
|
||||
{
|
||||
p_voicefile = (struct voicefile*)audiobuf;
|
||||
|
@ -155,7 +194,42 @@ static void load_voicefile(void)
|
|||
else
|
||||
goto load_err;
|
||||
|
||||
#ifdef HAVE_MMC
|
||||
#ifdef SIMULATOR
|
||||
for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++)
|
||||
{
|
||||
struct clip_entry *ce;
|
||||
ce = &p_voicefile->index[i];
|
||||
ce->offset = BSWAP32(ce->offset);
|
||||
ce->size = BSWAP32(ce->size);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Do a bitswap as necessary. */
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
logf("Bitswapping voice file.");
|
||||
cpu_boost(true);
|
||||
buf = (unsigned char *)(&p_voicefile->index) +
|
||||
(p_voicefile->id1_max + p_voicefile->id2_max) * sizeof(struct clip_entry);
|
||||
length = file_size - offsetof(struct voicefile, index) -
|
||||
(p_voicefile->id1_max - p_voicefile->id2_max) * sizeof(struct clip_entry);
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
temp = buf[i];
|
||||
buf[i] = ((temp >> 7) & 0x01)
|
||||
| ((temp >> 5) & 0x02)
|
||||
| ((temp >> 3) & 0x04)
|
||||
| ((temp >> 1) & 0x08)
|
||||
| ((temp << 1) & 0x10)
|
||||
| ((temp << 3) & 0x20)
|
||||
| ((temp << 5) & 0x40)
|
||||
| ((temp << 7) & 0x80);
|
||||
}
|
||||
cpu_boost(false);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MMC
|
||||
/* load the index table, now that we know its size from the header */
|
||||
load_size = (p_voicefile->id1_max + p_voicefile->id2_max)
|
||||
* sizeof(struct clip_entry);
|
||||
|
@ -193,7 +267,11 @@ static void mp3_callback(unsigned char** start, int* size)
|
|||
|
||||
if (queue[queue_read].len > 0) /* current clip not finished? */
|
||||
{ /* feed the next 64K-1 chunk */
|
||||
#if CONFIG_HWCODEC != MASNONE
|
||||
sent = MIN(queue[queue_read].len, 0xFFFF);
|
||||
#else
|
||||
sent = queue[queue_read].len;
|
||||
#endif
|
||||
*start = queue[queue_read].buf;
|
||||
*size = sent;
|
||||
return;
|
||||
|
@ -207,7 +285,11 @@ re_check:
|
|||
|
||||
if (QUEUE_LEVEL) /* queue is not empty? */
|
||||
{ /* start next clip */
|
||||
#if CONFIG_HWCODEC != MASNONE
|
||||
sent = MIN(queue[queue_read].len, 0xFFFF);
|
||||
#else
|
||||
sent = queue[queue_read].len;
|
||||
#endif
|
||||
*start = p_lastclip = queue[queue_read].buf;
|
||||
*size = sent;
|
||||
curr_hd[0] = p_lastclip[1];
|
||||
|
@ -286,7 +368,7 @@ static int shutup(void)
|
|||
/* nothing to do, was frame boundary or not our clip */
|
||||
mp3_play_stop();
|
||||
queue_write = queue_read = 0; /* reset the queue */
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -317,7 +399,11 @@ static int queue_clip(unsigned char* buf, long size, bool enqueue)
|
|||
if (queue_level == 0)
|
||||
{ /* queue was empty, we have to do the initial start */
|
||||
p_lastclip = buf;
|
||||
#if CONFIG_HWCODEC != MASNONE
|
||||
sent = MIN(size, 0xFFFF); /* DMA can do no more */
|
||||
#else
|
||||
sent = size;
|
||||
#endif
|
||||
mp3_play_data(buf, sent, mp3_callback);
|
||||
curr_hd[0] = buf[1];
|
||||
curr_hd[1] = buf[2];
|
||||
|
@ -400,12 +486,19 @@ void talk_init(void)
|
|||
#else
|
||||
filehandle = open_voicefile();
|
||||
has_voicefile = (filehandle >= 0); /* test if we can open it */
|
||||
voicefile_size = 0;
|
||||
|
||||
if (has_voicefile)
|
||||
{
|
||||
voicefile_size = filesize(filehandle);
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
voice_init();
|
||||
#endif
|
||||
close(filehandle); /* close again, this was just to detect presence */
|
||||
filehandle = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -432,8 +525,10 @@ int talk_id(long id, bool enqueue)
|
|||
unsigned char* clipbuf;
|
||||
int unit;
|
||||
|
||||
#if CONFIG_HWCODEC != MASNONE
|
||||
if (audio_status()) /* busy, buffer in use */
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
if (p_voicefile == NULL && has_voicefile)
|
||||
load_voicefile(); /* reload needed */
|
||||
|
@ -514,8 +609,10 @@ int talk_number(long n, bool enqueue)
|
|||
int level = 0; /* mille count */
|
||||
long mil = 1000000000; /* highest possible "-illion" */
|
||||
|
||||
#if CONFIG_HWCODEC != MASNONE
|
||||
if (audio_status()) /* busy, buffer in use */
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
if (!enqueue)
|
||||
shutup(); /* cut off all the pending stuff */
|
||||
|
@ -593,8 +690,10 @@ int talk_value(long n, int unit, bool enqueue)
|
|||
VOICE_HERTZ,
|
||||
};
|
||||
|
||||
#if CONFIG_HWCODEC != MASNONE
|
||||
if (audio_status()) /* busy, buffer in use */
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
if (unit < 0 || unit >= UNIT_LAST)
|
||||
unit_id = -1;
|
||||
|
@ -625,8 +724,10 @@ int talk_spell(const char* spell, bool enqueue)
|
|||
{
|
||||
char c; /* currently processed char */
|
||||
|
||||
#if CONFIG_HWCODEC != MASNONE
|
||||
if (audio_status()) /* busy, buffer in use */
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
if (!enqueue)
|
||||
shutup(); /* cut off all the pending stuff */
|
||||
|
|
|
@ -59,6 +59,7 @@ extern const char* const dir_thumbnail_name; /* "_dirname.talk" */
|
|||
extern const char* const file_thumbnail_ext; /* ".talk" for file voicing */
|
||||
|
||||
void talk_init(void);
|
||||
int talk_get_bufsize(void); /* get the loaded voice file size */
|
||||
int talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */
|
||||
int talk_id(long id, bool enqueue); /* play a voice ID from voicefont */
|
||||
int talk_file(const char* filename, bool enqueue); /* play a thumbnail from file */
|
||||
|
|
|
@ -576,7 +576,6 @@ void mp3_reset_playtime(void)
|
|||
playstart_tick = current_tick;
|
||||
}
|
||||
|
||||
|
||||
bool mp3_is_playing(void)
|
||||
{
|
||||
return playing;
|
||||
|
@ -624,18 +623,11 @@ void mp3_init(int volume, int bass, int treble, int balance, int loudness,
|
|||
audio_is_initialized = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void mp3_shutdown(void)
|
||||
{
|
||||
/* a dummy */
|
||||
}
|
||||
void mp3_play_data(const unsigned char* start, int size,
|
||||
void (*get_more)(unsigned char** start, int* size))
|
||||
{
|
||||
/* a dummy */
|
||||
(void)start;
|
||||
(void)size;
|
||||
(void)get_more;
|
||||
}
|
||||
|
||||
void mp3_play_stop(void)
|
||||
{
|
||||
|
@ -653,4 +645,10 @@ unsigned char* mp3_get_pos(void)
|
|||
/* a dummy */
|
||||
return (unsigned char *)0x1234;
|
||||
}
|
||||
|
||||
bool mp3_is_playing(void)
|
||||
{
|
||||
return playing;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_HWCODEC == MASNONE */
|
||||
|
|
|
@ -704,10 +704,3 @@ int sound_get_pitch(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_HWCODEC == MASNONE
|
||||
bool mp3_is_playing(void)
|
||||
{
|
||||
/* a dummy */
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -273,6 +273,7 @@ void button_set_flip(bool yesno)
|
|||
(void)yesno;
|
||||
}
|
||||
|
||||
#if CONFIG_HWCODEC != MASNONE
|
||||
void talk_init(void)
|
||||
{
|
||||
}
|
||||
|
@ -320,6 +321,7 @@ int talk_spell(char* spell, bool enqueue)
|
|||
|
||||
const char* const dir_thumbnail_name = "_dirname.talk";
|
||||
const char* const file_thumbnail_ext = ".talk";
|
||||
#endif
|
||||
|
||||
/* FIXME: this shoudn't be a stub, rather the real thing.
|
||||
I'm afraid on Win32/X11 it'll be hard to kill a thread from outside. */
|
||||
|
|
Loading…
Reference in a new issue