forked from len0rd/rockbox
SWCODEC: Get rid of extra swap buffer and get back 512K of RAM or 100K if the players RAM is <= 1MB. Make any needed changes to things to stabilize and facilitate this including removing flattening out initialization. Comment some things heavily. Fix a few logfs I didn't want to see the compiler complaining about.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12843 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
fbf52ae8fe
commit
e1dd10ddfb
10 changed files with 578 additions and 241 deletions
|
@ -58,7 +58,10 @@ struct pcmbufdesc
|
||||||
void (*callback)(void);
|
void (*callback)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PCMBUF_DESCS(bufsize) ((bufsize) / PCMBUF_MINAVG_CHUNK)
|
#define PCMBUF_DESCS(bufsize) \
|
||||||
|
((bufsize) / PCMBUF_MINAVG_CHUNK)
|
||||||
|
#define PCMBUF_DESCS_SIZE(bufsize) \
|
||||||
|
(PCMBUF_DESCS(bufsize)*sizeof(struct pcmbufdesc))
|
||||||
|
|
||||||
/* Size of the PCM buffer. */
|
/* Size of the PCM buffer. */
|
||||||
static size_t pcmbuf_size IDATA_ATTR = 0;
|
static size_t pcmbuf_size IDATA_ATTR = 0;
|
||||||
|
@ -76,6 +79,7 @@ static void (*position_callback)(size_t size) IDATA_ATTR;
|
||||||
|
|
||||||
/* Crossfade related state */
|
/* Crossfade related state */
|
||||||
static bool crossfade_enabled;
|
static bool crossfade_enabled;
|
||||||
|
static bool crossfade_enabled_pending;
|
||||||
static bool crossfade_mixmode;
|
static bool crossfade_mixmode;
|
||||||
static bool crossfade_active IDATA_ATTR;
|
static bool crossfade_active IDATA_ATTR;
|
||||||
static bool crossfade_init IDATA_ATTR;
|
static bool crossfade_init IDATA_ATTR;
|
||||||
|
@ -187,9 +191,13 @@ void pcmbuf_set_position_callback(void (*callback)(size_t size))
|
||||||
position_callback = callback;
|
position_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pcmbuf_set_watermark_bytes(size_t numbytes)
|
static void pcmbuf_set_watermark_bytes(void)
|
||||||
{
|
{
|
||||||
pcmbuf_watermark = numbytes;
|
pcmbuf_watermark = (crossfade_enabled && pcmbuf_size) ?
|
||||||
|
/* If crossfading, try to keep the buffer full other than 1 second */
|
||||||
|
(pcmbuf_size - (NATIVE_FREQUENCY * 4 * 1)) :
|
||||||
|
/* Otherwise, just keep it above 2 second */
|
||||||
|
PCMBUF_WATERMARK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is really just part of pcmbuf_flush_fillpos, but is easier to keep
|
/* This is really just part of pcmbuf_flush_fillpos, but is easier to keep
|
||||||
|
@ -413,30 +421,60 @@ static void pcmbuf_init_pcmbuffers(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pcmbuf_is_same_size(size_t bufsize)
|
static size_t pcmbuf_get_next_required_pcmbuf_size(void)
|
||||||
{
|
{
|
||||||
/* keep calculations synced with pcmbuf_init */
|
#if MEM > 1
|
||||||
bufsize += PCMBUF_MIX_CHUNK * 2 +
|
size_t seconds = 1;
|
||||||
PCMBUF_DESCS(bufsize)*sizeof(struct pcmbufdesc);
|
|
||||||
return bufsize == (size_t)(pcmbuf_bufend - audiobuffer);
|
if (crossfade_enabled_pending)
|
||||||
|
seconds += global_settings.crossfade_fade_out_delay
|
||||||
|
+ global_settings.crossfade_fade_out_duration;
|
||||||
|
|
||||||
|
/* Buffer has to be at least 2s long. */
|
||||||
|
seconds += 2;
|
||||||
|
logf("pcmbuf len: %ld", seconds);
|
||||||
|
return seconds * (NATIVE_FREQUENCY*4);
|
||||||
|
#else
|
||||||
|
return NATIVE_FREQUENCY*2;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *pcmbuf_calc_audiobuffer_ptr(size_t bufsize)
|
||||||
|
{
|
||||||
|
return pcmbuf_bufend - (bufsize + PCMBUF_MIX_CHUNK * 2 +
|
||||||
|
PCMBUF_DESCS_SIZE(bufsize));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pcmbuf_is_same_size(void)
|
||||||
|
{
|
||||||
|
if (audiobuffer == NULL)
|
||||||
|
return true; /* Not set up yet even once so always */
|
||||||
|
|
||||||
|
size_t bufsize = pcmbuf_get_next_required_pcmbuf_size();
|
||||||
|
return pcmbuf_calc_audiobuffer_ptr(bufsize) == audiobuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the pcmbuffer the structure looks like this:
|
/* Initialize the pcmbuffer the structure looks like this:
|
||||||
* ...|---------PCMBUF---------|FADEBUF|VOICEBUF|DESCS|... */
|
* ...|---------PCMBUF---------|FADEBUF|VOICEBUF|DESCS|... */
|
||||||
size_t pcmbuf_init(size_t bufsize, char *bufend)
|
size_t pcmbuf_init(unsigned char *bufend)
|
||||||
{
|
{
|
||||||
pcmbuf_size = bufsize;
|
|
||||||
pcmbuf_bufend = bufend;
|
pcmbuf_bufend = bufend;
|
||||||
pcmbuf_descsize = pcmbuf_descs()*sizeof(struct pcmbufdesc);
|
pcmbuf_size = pcmbuf_get_next_required_pcmbuf_size();
|
||||||
audiobuffer = pcmbuf_bufend - (pcmbuf_size + PCMBUF_MIX_CHUNK * 2
|
audiobuffer = pcmbuf_calc_audiobuffer_ptr(pcmbuf_size);
|
||||||
+ pcmbuf_descsize);
|
|
||||||
fadebuf = &audiobuffer[pcmbuf_size];
|
fadebuf = &audiobuffer[pcmbuf_size];
|
||||||
voicebuf = &fadebuf[PCMBUF_MIX_CHUNK];
|
voicebuf = &fadebuf[PCMBUF_MIX_CHUNK];
|
||||||
pcmbuf_write = (struct pcmbufdesc *)&voicebuf[PCMBUF_MIX_CHUNK];
|
pcmbuf_write = (struct pcmbufdesc *)&voicebuf[PCMBUF_MIX_CHUNK];
|
||||||
|
|
||||||
|
pcmbuf_descsize = PCMBUF_DESCS_SIZE(pcmbuf_size);
|
||||||
pcmbuf_init_pcmbuffers();
|
pcmbuf_init_pcmbuffers();
|
||||||
|
|
||||||
position_callback = NULL;
|
position_callback = NULL;
|
||||||
pcmbuf_event_handler = NULL;
|
pcmbuf_event_handler = NULL;
|
||||||
|
|
||||||
|
pcmbuf_crossfade_enable_finished();
|
||||||
|
|
||||||
pcmbuf_play_stop();
|
pcmbuf_play_stop();
|
||||||
|
|
||||||
return pcmbuf_bufend - audiobuffer;
|
return pcmbuf_bufend - audiobuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,6 +483,14 @@ size_t pcmbuf_get_bufsize(void)
|
||||||
return pcmbuf_size;
|
return pcmbuf_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ROCKBOX_HAS_LOGF
|
||||||
|
unsigned char * pcmbuf_get_meminfo(size_t *length)
|
||||||
|
{
|
||||||
|
*length = pcmbuf_bufend - audiobuffer;
|
||||||
|
return audiobuffer;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void pcmbuf_pause(bool pause) {
|
void pcmbuf_pause(bool pause) {
|
||||||
#ifdef PCMBUF_MUTING
|
#ifdef PCMBUF_MUTING
|
||||||
if (pause)
|
if (pause)
|
||||||
|
@ -1036,15 +1082,18 @@ void pcmbuf_mix_voice(int count)
|
||||||
|
|
||||||
void pcmbuf_crossfade_enable(bool on_off)
|
void pcmbuf_crossfade_enable(bool on_off)
|
||||||
{
|
{
|
||||||
crossfade_enabled = on_off;
|
#if MEM > 1
|
||||||
|
/* Next setting to be used, not applied now */
|
||||||
|
crossfade_enabled_pending = on_off;
|
||||||
|
#endif
|
||||||
|
(void)on_off;
|
||||||
|
}
|
||||||
|
|
||||||
if (crossfade_enabled) {
|
void pcmbuf_crossfade_enable_finished(void)
|
||||||
/* If crossfading, try to keep the buffer full other than 1 second */
|
{
|
||||||
pcmbuf_set_watermark_bytes(pcmbuf_size - (NATIVE_FREQUENCY * 4 * 1));
|
/* Copy the pending setting over now */
|
||||||
} else {
|
crossfade_enabled = crossfade_enabled_pending;
|
||||||
/* Otherwise, just keep it above 2 second */
|
pcmbuf_set_watermark_bytes();
|
||||||
pcmbuf_set_watermark_bytes(PCMBUF_WATERMARK);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pcmbuf_is_crossfade_enabled(void)
|
bool pcmbuf_is_crossfade_enabled(void)
|
||||||
|
@ -1054,4 +1103,3 @@ bool pcmbuf_is_crossfade_enabled(void)
|
||||||
|
|
||||||
return crossfade_enabled;
|
return crossfade_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,10 +37,14 @@
|
||||||
for mixing (crossfade or voice) */
|
for mixing (crossfade or voice) */
|
||||||
|
|
||||||
/* Returns true if the buffer needs to change size */
|
/* Returns true if the buffer needs to change size */
|
||||||
bool pcmbuf_is_same_size(size_t bufsize);
|
bool pcmbuf_is_same_size(void);
|
||||||
size_t pcmbuf_init(size_t bufsize, char *bufend);
|
size_t pcmbuf_init(unsigned char *bufend);
|
||||||
/* Size in bytes used by the pcmbuffer */
|
/* Size in bytes used by the pcmbuffer */
|
||||||
size_t pcmbuf_get_bufsize(void);
|
size_t pcmbuf_get_bufsize(void);
|
||||||
|
#ifdef ROCKBOX_HAS_LOGF
|
||||||
|
/* just used for logging for now */
|
||||||
|
unsigned char * pcmbuf_get_meminfo(size_t *length);
|
||||||
|
#endif
|
||||||
size_t get_pcmbuf_descsize(void);
|
size_t get_pcmbuf_descsize(void);
|
||||||
|
|
||||||
void pcmbuf_pause(bool pause);
|
void pcmbuf_pause(bool pause);
|
||||||
|
@ -68,7 +72,7 @@ void* pcmbuf_request_buffer(int *count);
|
||||||
void* pcmbuf_request_voice_buffer(int *count, bool mix);
|
void* pcmbuf_request_voice_buffer(int *count, bool mix);
|
||||||
bool pcmbuf_is_crossfade_enabled(void);
|
bool pcmbuf_is_crossfade_enabled(void);
|
||||||
void pcmbuf_crossfade_enable(bool on_off);
|
void pcmbuf_crossfade_enable(bool on_off);
|
||||||
|
void pcmbuf_crossfade_enable_finished(void);
|
||||||
int pcmbuf_usage(void);
|
int pcmbuf_usage(void);
|
||||||
int pcmbuf_mix_free(void);
|
int pcmbuf_mix_free(void);
|
||||||
void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
|
void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
|
||||||
|
|
537
apps/playback.c
537
apps/playback.c
|
@ -34,6 +34,8 @@
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
#include "panic.h"
|
||||||
|
#include "memory.h"
|
||||||
#include "lcd.h"
|
#include "lcd.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "button.h"
|
#include "button.h"
|
||||||
|
@ -167,14 +169,14 @@ enum {
|
||||||
|
|
||||||
/* As defined in plugin.lds */
|
/* As defined in plugin.lds */
|
||||||
#if defined(CPU_PP)
|
#if defined(CPU_PP)
|
||||||
#define CODEC_IRAM_ORIGIN 0x4000c000
|
#define CODEC_IRAM_ORIGIN ((unsigned char *)0x4000c000)
|
||||||
#define CODEC_IRAM_SIZE 0xc000
|
#define CODEC_IRAM_SIZE ((size_t)0xc000)
|
||||||
#elif defined(IAUDIO_X5) || defined(IAUDIO_M5)
|
#elif defined(IAUDIO_X5) || defined(IAUDIO_M5)
|
||||||
#define CODEC_IRAM_ORIGIN 0x10010000
|
#define CODEC_IRAM_ORIGIN ((unsigned char *)0x10010000)
|
||||||
#define CODEC_IRAM_SIZE 0x10000
|
#define CODEC_IRAM_SIZE ((size_t)0x10000)
|
||||||
#else
|
#else
|
||||||
#define CODEC_IRAM_ORIGIN 0x1000c000
|
#define CODEC_IRAM_ORIGIN ((unsigned char *)0x1000c000)
|
||||||
#define CODEC_IRAM_SIZE 0xc000
|
#define CODEC_IRAM_SIZE ((size_t)0xc000)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef IBSS_ATTR_VOICE_STACK
|
#ifndef IBSS_ATTR_VOICE_STACK
|
||||||
|
@ -205,7 +207,7 @@ static volatile size_t buf_widx IDATA_ATTR = 0; /* Buffer write position (A/C-)
|
||||||
|
|
||||||
/* Possible arrangements of the buffer */
|
/* Possible arrangements of the buffer */
|
||||||
#define BUFFER_STATE_TRASHED -1 /* trashed; must be reset */
|
#define BUFFER_STATE_TRASHED -1 /* trashed; must be reset */
|
||||||
#define BUFFER_STATE_NORMAL 0 /* voice+audio OR audio-only */
|
#define BUFFER_STATE_INITIALIZED 0 /* voice+audio OR audio-only */
|
||||||
#define BUFFER_STATE_VOICED_ONLY 1 /* voice-only */
|
#define BUFFER_STATE_VOICED_ONLY 1 /* voice-only */
|
||||||
static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */
|
static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */
|
||||||
|
|
||||||
|
@ -282,7 +284,7 @@ static const char audio_thread_name[] = "audio";
|
||||||
static void audio_thread(void);
|
static void audio_thread(void);
|
||||||
static void audio_initiate_track_change(long direction);
|
static void audio_initiate_track_change(long direction);
|
||||||
static bool audio_have_tracks(void);
|
static bool audio_have_tracks(void);
|
||||||
static void audio_reset_buffer(size_t pcmbufsize);
|
static void audio_reset_buffer(void);
|
||||||
|
|
||||||
/* Codec thread */
|
/* Codec thread */
|
||||||
extern struct codec_api ci;
|
extern struct codec_api ci;
|
||||||
|
@ -315,10 +317,16 @@ static unsigned char sim_iram[CODEC_IRAM_SIZE];
|
||||||
#define CODEC_IRAM_ORIGIN sim_iram
|
#define CODEC_IRAM_ORIGIN sim_iram
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Pointer to IRAM buffers for normal/voice codecs */
|
/* iram_buf and dram_buf are either both NULL or both non-NULL */
|
||||||
static unsigned char *iram_buf[2] = { NULL, NULL };
|
/* Pointer to IRAM buffer for codec swapping */
|
||||||
/* Pointer to DRAM buffers for normal/voice codecs */
|
static unsigned char *iram_buf = NULL;
|
||||||
static unsigned char *dram_buf[2] = { NULL, NULL };
|
/* Pointer to DRAM buffer for codec swapping */
|
||||||
|
static unsigned char *dram_buf = NULL;
|
||||||
|
/* Parity of swap_codec calls - needed because one codec swapping itself in
|
||||||
|
automatically swaps in the other and the swap when unlocking should not
|
||||||
|
happen if the parity is even.
|
||||||
|
*/
|
||||||
|
static bool swap_codec_parity = false; /* true=odd, false=even */
|
||||||
/* Mutex to control which codec (normal/voice) is running */
|
/* Mutex to control which codec (normal/voice) is running */
|
||||||
static struct mutex mutex_codecthread NOCACHEBSS_ATTR;
|
static struct mutex mutex_codecthread NOCACHEBSS_ATTR;
|
||||||
|
|
||||||
|
@ -342,6 +350,7 @@ struct voice_info {
|
||||||
char *buf;
|
char *buf;
|
||||||
};
|
};
|
||||||
static void voice_thread(void);
|
static void voice_thread(void);
|
||||||
|
static void voice_stop(void);
|
||||||
|
|
||||||
#endif /* PLAYBACK_VOICE */
|
#endif /* PLAYBACK_VOICE */
|
||||||
|
|
||||||
|
@ -404,7 +413,7 @@ void mpeg_id3_options(bool _v1first)
|
||||||
static void wait_for_voice_swap_in(void)
|
static void wait_for_voice_swap_in(void)
|
||||||
{
|
{
|
||||||
#ifdef PLAYBACK_VOICE
|
#ifdef PLAYBACK_VOICE
|
||||||
if (NULL == iram_buf[CODEC_IDX_VOICE])
|
if (NULL == iram_buf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while (current_codec != CODEC_IDX_VOICE)
|
while (current_codec != CODEC_IDX_VOICE)
|
||||||
|
@ -426,7 +435,8 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
|
||||||
|
|
||||||
if (buffer_size == NULL)
|
if (buffer_size == NULL)
|
||||||
{
|
{
|
||||||
/* Special case for talk_init to use */
|
/* Special case for talk_init to use since it already knows it's
|
||||||
|
trashed */
|
||||||
buffer_state = BUFFER_STATE_TRASHED;
|
buffer_state = BUFFER_STATE_TRASHED;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -434,8 +444,15 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
|
||||||
if (talk_buf || buffer_state == BUFFER_STATE_TRASHED
|
if (talk_buf || buffer_state == BUFFER_STATE_TRASHED
|
||||||
|| !talk_voice_required())
|
|| !talk_voice_required())
|
||||||
{
|
{
|
||||||
logf("get buffer: talk_buf");
|
logf("get buffer: talk, audio");
|
||||||
/* ok to use everything from audiobuf to audiobufend */
|
/* Ok to use everything from audiobuf to audiobufend - voice is loaded,
|
||||||
|
the talk buffer is not needed because voice isn't being used, or
|
||||||
|
could be BUFFER_STATE_TRASHED already. If state is
|
||||||
|
BUFFER_STATE_VOICED_ONLY, no problem as long as memory isn't written
|
||||||
|
without the caller knowing what's going on. Changing certain settings
|
||||||
|
may move it to a worse condition but the memory in use by something
|
||||||
|
else will remain undisturbed.
|
||||||
|
*/
|
||||||
if (buffer_state != BUFFER_STATE_TRASHED)
|
if (buffer_state != BUFFER_STATE_TRASHED)
|
||||||
{
|
{
|
||||||
talk_buffer_steal();
|
talk_buffer_steal();
|
||||||
|
@ -447,10 +464,14 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* skip talk buffer and move pcm buffer to end */
|
/* Safe to just return this if already BUFFER_STATE_VOICED_ONLY or
|
||||||
logf("get buffer: voice");
|
still BUFFER_STATE_INITIALIZED */
|
||||||
|
/* Skip talk buffer and move pcm buffer to end to maximize available
|
||||||
|
contiguous memory - no audio running means voice will not need the
|
||||||
|
swap space */
|
||||||
|
logf("get buffer: audio");
|
||||||
buf = audiobuf + talk_get_bufsize();
|
buf = audiobuf + talk_get_bufsize();
|
||||||
end = audiobufend - pcmbuf_init(pcmbuf_get_bufsize(), audiobufend);
|
end = audiobufend - pcmbuf_init(audiobufend);
|
||||||
buffer_state = BUFFER_STATE_VOICED_ONLY;
|
buffer_state = BUFFER_STATE_VOICED_ONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,18 +487,22 @@ void audio_iram_steal(void)
|
||||||
audio_stop();
|
audio_stop();
|
||||||
|
|
||||||
#ifdef PLAYBACK_VOICE
|
#ifdef PLAYBACK_VOICE
|
||||||
if (NULL != iram_buf[CODEC_IDX_VOICE])
|
if (NULL != iram_buf)
|
||||||
{
|
{
|
||||||
/* Can't already be stolen */
|
/* Can't already be stolen */
|
||||||
if (voice_iram_stolen)
|
if (voice_iram_stolen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Must wait for voice to be current again if it is swapped which
|
||||||
|
would cause the caller's buffer to get clobbered when voice locks
|
||||||
|
and runs - we'll wait for it to lock and yield again then make sure
|
||||||
|
the ride has come to a complete stop */
|
||||||
wait_for_voice_swap_in();
|
wait_for_voice_swap_in();
|
||||||
voice_stop();
|
voice_stop();
|
||||||
|
|
||||||
/* Save voice IRAM - safe to do here since state is known */
|
/* Save voice IRAM but just memcpy - safe to do here since voice
|
||||||
memcpy(iram_buf[CODEC_IDX_VOICE], (void *)CODEC_IRAM_ORIGIN,
|
is current and no audio codec is loaded */
|
||||||
CODEC_IRAM_SIZE);
|
memcpy(iram_buf, CODEC_IRAM_ORIGIN, CODEC_IRAM_SIZE);
|
||||||
voice_iram_stolen = true;
|
voice_iram_stolen = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -492,21 +517,23 @@ void audio_iram_steal(void)
|
||||||
#ifdef HAVE_RECORDING
|
#ifdef HAVE_RECORDING
|
||||||
unsigned char *audio_get_recording_buffer(size_t *buffer_size)
|
unsigned char *audio_get_recording_buffer(size_t *buffer_size)
|
||||||
{
|
{
|
||||||
/* don't allow overwrite of voice swap area or we'll trash the
|
/* Don't allow overwrite of voice swap area or we'll trash the
|
||||||
swapped-out voice codec but can use whole thing if none */
|
swapped-out voice codec but can use whole thing if none */
|
||||||
unsigned char *end;
|
unsigned char *end;
|
||||||
|
|
||||||
|
/* Stop audio and voice. Wait for voice to swap in and be clear
|
||||||
|
of pending events to ensure trouble-free operation of encoders */
|
||||||
audio_stop();
|
audio_stop();
|
||||||
wait_for_voice_swap_in();
|
wait_for_voice_swap_in();
|
||||||
voice_stop();
|
voice_stop();
|
||||||
talk_buffer_steal();
|
talk_buffer_steal();
|
||||||
|
|
||||||
#ifdef PLAYBACK_VOICE
|
#ifdef PLAYBACK_VOICE
|
||||||
#ifdef IRAM_STEAL
|
/* If no dram_buf, swap space not used and recording gets more
|
||||||
end = dram_buf[CODEC_IDX_VOICE];
|
memory. Codec swap areas will remain unaffected by the next init
|
||||||
#else
|
since they're allocated at the end of the buffer and their sizes
|
||||||
end = iram_buf[CODEC_IDX_VOICE];
|
don't change between calls */
|
||||||
#endif /* IRAM_STEAL */
|
end = dram_buf;
|
||||||
if (NULL == end)
|
if (NULL == end)
|
||||||
#endif /* PLAYBACK_VOICE */
|
#endif /* PLAYBACK_VOICE */
|
||||||
end = audiobufend;
|
end = audiobufend;
|
||||||
|
@ -781,101 +808,51 @@ void audio_set_buffer_margin(int setting)
|
||||||
{
|
{
|
||||||
static const int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600};
|
static const int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600};
|
||||||
buffer_margin = lookup[setting];
|
buffer_margin = lookup[setting];
|
||||||
logf("buffer margin: %ds", buffer_margin);
|
logf("buffer margin: %ld", buffer_margin);
|
||||||
set_filebuf_watermark(buffer_margin);
|
set_filebuf_watermark(buffer_margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set crossfade & PCM buffer length. */
|
/* Take nescessary steps to enable or disable the crossfade setting */
|
||||||
void audio_set_crossfade(int enable)
|
void audio_set_crossfade(int enable)
|
||||||
{
|
{
|
||||||
|
size_t offset;
|
||||||
|
bool was_playing;
|
||||||
size_t size;
|
size_t size;
|
||||||
bool was_playing = (playing && audio_is_initialized);
|
|
||||||
size_t offset = 0;
|
|
||||||
#if MEM > 1
|
|
||||||
int seconds = 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!filebuf)
|
/* Tell it the next setting to use */
|
||||||
return; /* Audio buffers not yet set up */
|
pcmbuf_crossfade_enable(enable);
|
||||||
|
|
||||||
#if MEM > 1
|
/* Return if size hasn't changed or this is too early to determine
|
||||||
if (enable)
|
which in the second case there's no way we could be playing
|
||||||
seconds = global_settings.crossfade_fade_out_delay
|
anything at all */
|
||||||
+ global_settings.crossfade_fade_out_duration;
|
if (pcmbuf_is_same_size())
|
||||||
|
{
|
||||||
|
/* This function is a copout and just syncs some variables -
|
||||||
|
to be removed at a later date */
|
||||||
|
pcmbuf_crossfade_enable_finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Buffer has to be at least 2s long. */
|
offset = 0;
|
||||||
seconds += 2;
|
was_playing = playing;
|
||||||
logf("buf len: %d", seconds);
|
|
||||||
size = seconds * (NATIVE_FREQUENCY*4);
|
|
||||||
#else
|
|
||||||
enable = 0;
|
|
||||||
size = NATIVE_FREQUENCY*2;
|
|
||||||
#endif
|
|
||||||
if (buffer_state == BUFFER_STATE_NORMAL && pcmbuf_is_same_size(size))
|
|
||||||
return ;
|
|
||||||
|
|
||||||
|
/* Playback has to be stopped before changing the buffer size */
|
||||||
if (was_playing)
|
if (was_playing)
|
||||||
{
|
{
|
||||||
/* Store the track resume position */
|
/* Store the track resume position */
|
||||||
offset = CUR_TI->id3.offset;
|
offset = CUR_TI->id3.offset;
|
||||||
|
gui_syncsplash(0, str(LANG_RESTARTING_PLAYBACK));
|
||||||
/* Playback has to be stopped before changing the buffer size. */
|
|
||||||
gui_syncsplash(0, (char *)str(LANG_RESTARTING_PLAYBACK));
|
|
||||||
audio_stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
voice_stop();
|
/* Blast it - audio buffer will have to be setup again next time
|
||||||
|
something plays */
|
||||||
|
audio_get_buffer(true, &size);
|
||||||
|
|
||||||
/* Re-initialize audio system. */
|
/* Restart playback if audio was running previously */
|
||||||
audio_reset_buffer(size);
|
|
||||||
pcmbuf_crossfade_enable(enable);
|
|
||||||
logf("abuf:%dB", pcmbuf_get_bufsize());
|
|
||||||
logf("fbuf:%dB", filebuflen);
|
|
||||||
|
|
||||||
voice_init();
|
|
||||||
|
|
||||||
/* Restart playback. */
|
|
||||||
if (was_playing)
|
if (was_playing)
|
||||||
audio_play(offset);
|
audio_play(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void voice_init(void)
|
|
||||||
{
|
|
||||||
#ifdef PLAYBACK_VOICE
|
|
||||||
if (voice_thread_p || !filebuf || voice_codec_loaded ||
|
|
||||||
!talk_voice_required())
|
|
||||||
return;
|
|
||||||
|
|
||||||
logf("Starting voice codec");
|
|
||||||
queue_init(&voice_queue, true);
|
|
||||||
voice_thread_p = create_thread(voice_thread, voice_stack,
|
|
||||||
sizeof(voice_stack), voice_thread_name
|
|
||||||
IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU, false));
|
|
||||||
|
|
||||||
while (!voice_codec_loaded)
|
|
||||||
yield();
|
|
||||||
#endif
|
|
||||||
} /* voice_init */
|
|
||||||
|
|
||||||
void voice_stop(void)
|
|
||||||
{
|
|
||||||
#ifdef PLAYBACK_VOICE
|
|
||||||
/* Messages should not be posted to voice codec queue unless it is the
|
|
||||||
current codec or deadlocks happen. */
|
|
||||||
if (current_codec != CODEC_IDX_VOICE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
LOGFQUEUE("mp3 > voice Q_VOICE_STOP");
|
|
||||||
queue_post(&voice_queue, Q_VOICE_STOP, 0);
|
|
||||||
while (voice_is_playing || !queue_empty(&voice_queue))
|
|
||||||
yield();
|
|
||||||
if (!playing)
|
|
||||||
pcmbuf_play_stop();
|
|
||||||
#endif
|
|
||||||
} /* voice_stop */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* --- Routines called from multiple threads --- */
|
/* --- Routines called from multiple threads --- */
|
||||||
static void set_current_codec(int codec_idx)
|
static void set_current_codec(int codec_idx)
|
||||||
{
|
{
|
||||||
|
@ -886,35 +863,61 @@ static void set_current_codec(int codec_idx)
|
||||||
#ifdef PLAYBACK_VOICE
|
#ifdef PLAYBACK_VOICE
|
||||||
static void swap_codec(void)
|
static void swap_codec(void)
|
||||||
{
|
{
|
||||||
int my_codec = current_codec;
|
int my_codec;
|
||||||
|
|
||||||
logf("swapping out codec:%d", my_codec);
|
/* Swap nothing if no swap buffers exist */
|
||||||
|
if (dram_buf == NULL)
|
||||||
|
{
|
||||||
|
logf("swap: no swap buffers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_codec = current_codec;
|
||||||
|
|
||||||
|
logf("swapping out codec: %d", my_codec);
|
||||||
|
|
||||||
|
/* Invert this when a codec thread enters and leaves */
|
||||||
|
swap_codec_parity = !swap_codec_parity;
|
||||||
|
|
||||||
|
/* If this is true, an odd number of calls has occurred and there's
|
||||||
|
no codec thread waiting to swap us out when it locks and runs. This
|
||||||
|
occurs when playback is stopped or when just starting playback and
|
||||||
|
the audio thread is loading a codec; parities should always be even
|
||||||
|
on entry when a thread calls this during playback */
|
||||||
|
if (swap_codec_parity)
|
||||||
|
{
|
||||||
/* Save our current IRAM and DRAM */
|
/* Save our current IRAM and DRAM */
|
||||||
#ifdef IRAM_STEAL
|
#ifdef IRAM_STEAL
|
||||||
if (voice_iram_stolen)
|
if (voice_iram_stolen)
|
||||||
{
|
{
|
||||||
logf("swap: iram restore");
|
logf("swap: iram restore");
|
||||||
voice_iram_stolen = false;
|
voice_iram_stolen = false;
|
||||||
/* Don't swap trashed data into buffer - _should_ always be the case
|
/* Don't swap trashed data into buffer as the voice IRAM will
|
||||||
if voice_iram_stolen is true since the voice has been swapped in
|
already be swapped out - should _always_ be the case if
|
||||||
before hand */
|
voice_iram_stolen is true since the voice has been swapped
|
||||||
|
in beforehand */
|
||||||
if (my_codec == CODEC_IDX_VOICE)
|
if (my_codec == CODEC_IDX_VOICE)
|
||||||
|
{
|
||||||
|
logf("voice iram already swapped");
|
||||||
goto skip_iram_swap;
|
goto skip_iram_swap;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memcpy(iram_buf[my_codec], (unsigned char *)CODEC_IRAM_ORIGIN,
|
memswap128(iram_buf, CODEC_IRAM_ORIGIN, CODEC_IRAM_SIZE);
|
||||||
CODEC_IRAM_SIZE);
|
|
||||||
|
|
||||||
#ifdef IRAM_STEAL
|
#ifdef IRAM_STEAL
|
||||||
skip_iram_swap:
|
skip_iram_swap:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memcpy(dram_buf[my_codec], codecbuf, CODEC_SIZE);
|
memswap128(dram_buf, codecbuf, CODEC_SIZE);
|
||||||
|
/* No cache invalidation needed; it will be done in codec_load_ram
|
||||||
|
or we won't be here otherwise */
|
||||||
|
}
|
||||||
|
|
||||||
/* Release my semaphore */
|
/* Release my semaphore */
|
||||||
mutex_unlock(&mutex_codecthread);
|
mutex_unlock(&mutex_codecthread);
|
||||||
|
logf("unlocked: %d", my_codec);
|
||||||
|
|
||||||
/* Loop until the other codec has locked and run */
|
/* Loop until the other codec has locked and run */
|
||||||
do {
|
do {
|
||||||
|
@ -923,28 +926,57 @@ skip_iram_swap:
|
||||||
} while (my_codec == current_codec);
|
} while (my_codec == current_codec);
|
||||||
|
|
||||||
/* Wait for other codec to unlock */
|
/* Wait for other codec to unlock */
|
||||||
|
/* FIXME: We need some sort of timed boost cancellation here or the CPU
|
||||||
|
doesn't unboost during playback when the voice codec goes back to
|
||||||
|
waiting - recall that mutex_lock calls block_thread which is an
|
||||||
|
indefinite wait that doesn't cancel the thread's CPU boost */
|
||||||
mutex_lock(&mutex_codecthread);
|
mutex_lock(&mutex_codecthread);
|
||||||
|
|
||||||
/* Take control */
|
/* Take control */
|
||||||
|
logf("waiting for lock: %d", my_codec);
|
||||||
set_current_codec(my_codec);
|
set_current_codec(my_codec);
|
||||||
|
|
||||||
/* Reload our IRAM and DRAM */
|
/* Reload our IRAM and DRAM */
|
||||||
memcpy((unsigned char *)CODEC_IRAM_ORIGIN, iram_buf[my_codec],
|
memswap128(iram_buf, CODEC_IRAM_ORIGIN, CODEC_IRAM_SIZE);
|
||||||
CODEC_IRAM_SIZE);
|
memswap128(dram_buf, codecbuf, CODEC_SIZE);
|
||||||
invalidate_icache();
|
invalidate_icache();
|
||||||
memcpy(codecbuf, dram_buf[my_codec], CODEC_SIZE);
|
|
||||||
|
|
||||||
logf("resuming codec:%d", my_codec);
|
/* Flip parity again */
|
||||||
|
swap_codec_parity = !swap_codec_parity;
|
||||||
|
|
||||||
|
logf("resuming codec: %d", my_codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function is meant to be used by the buffer stealing functions to
|
||||||
|
ensure the codec is no longer active and so voice will be swapped-in
|
||||||
|
before it is called */
|
||||||
|
static void voice_stop(void)
|
||||||
|
{
|
||||||
|
#ifdef PLAYBACK_VOICE
|
||||||
|
/* Must have a voice codec loaded or we'll hang forever here */
|
||||||
|
if (!voice_codec_loaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOGFQUEUE("mp3 > voice Q_VOICE_STOP");
|
||||||
|
queue_post(&voice_queue, Q_VOICE_STOP, 0);
|
||||||
|
|
||||||
|
/* Loop until voice empties it's queue, stops and picks up on the new
|
||||||
|
track; the voice thread must be stopped and waiting for messages
|
||||||
|
outside the codec */
|
||||||
|
while (voice_is_playing || !queue_empty(&voice_queue) ||
|
||||||
|
ci_voice.new_track)
|
||||||
|
yield();
|
||||||
|
|
||||||
|
if (!playing)
|
||||||
|
pcmbuf_play_stop();
|
||||||
#endif
|
#endif
|
||||||
|
} /* voice_stop */
|
||||||
|
#endif /* PLAYBACK_VOICE */
|
||||||
|
|
||||||
static void set_filebuf_watermark(int seconds)
|
static void set_filebuf_watermark(int seconds)
|
||||||
{
|
{
|
||||||
size_t bytes;
|
size_t bytes;
|
||||||
|
|
||||||
if (current_codec == CODEC_IDX_VOICE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!filebuf)
|
if (!filebuf)
|
||||||
return; /* Audio buffers not yet set up */
|
return; /* Audio buffers not yet set up */
|
||||||
|
|
||||||
|
@ -1058,6 +1090,14 @@ static void voice_set_offset_callback(size_t value)
|
||||||
(void)value;
|
(void)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void voice_configure_callback(int setting, intptr_t value)
|
||||||
|
{
|
||||||
|
if (!dsp_configure(setting, value))
|
||||||
|
{
|
||||||
|
logf("Illegal key:%d", setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static size_t voice_filebuf_callback(void *ptr, size_t size)
|
static size_t voice_filebuf_callback(void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
(void)ptr;
|
(void)ptr;
|
||||||
|
@ -1066,6 +1106,34 @@ static size_t voice_filebuf_callback(void *ptr, size_t size)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle Q_VOICE_STOP and part of SYS_USB_CONNECTED */
|
||||||
|
static bool voice_on_voice_stop(bool aborting, size_t *realsize)
|
||||||
|
{
|
||||||
|
if (aborting && !playing && pcm_is_playing())
|
||||||
|
{
|
||||||
|
/* Aborting: Slight hack - flush PCM buffer if
|
||||||
|
only being used for voice */
|
||||||
|
pcmbuf_play_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (voice_is_playing)
|
||||||
|
{
|
||||||
|
/* Clear the current buffer */
|
||||||
|
voice_is_playing = false;
|
||||||
|
voice_getmore = NULL;
|
||||||
|
voice_remaining = 0;
|
||||||
|
voicebuf = NULL;
|
||||||
|
|
||||||
|
/* Force the codec to think it's changing tracks */
|
||||||
|
ci_voice.new_track = 1;
|
||||||
|
|
||||||
|
*realsize = 0;
|
||||||
|
return true; /* Yes, change tracks */
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
|
static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
|
||||||
{
|
{
|
||||||
struct event ev;
|
struct event ev;
|
||||||
|
@ -1079,15 +1147,21 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (voice_is_playing || playing)
|
if (voice_is_playing || playing)
|
||||||
|
{
|
||||||
queue_wait_w_tmo(&voice_queue, &ev, 0);
|
queue_wait_w_tmo(&voice_queue, &ev, 0);
|
||||||
|
if (!voice_is_playing && ev.id == SYS_TIMEOUT)
|
||||||
|
ev.id = Q_AUDIO_PLAY;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
/* We must use queue_wait_w_tmo() because queue_wait() doesn't
|
/* We must use queue_wait_w_tmo() because queue_wait() doesn't
|
||||||
unboost the CPU */
|
unboost the CPU */
|
||||||
queue_wait_w_tmo(&voice_queue, &ev, INT_MAX);
|
/* FIXME: when long timeouts work correctly max out the the timeout
|
||||||
if (!voice_is_playing)
|
(we'll still need the timeout guard here) or an infinite timeout
|
||||||
{
|
can unboost, use that */
|
||||||
if (ev.id == SYS_TIMEOUT)
|
do
|
||||||
ev.id = Q_AUDIO_PLAY;
|
queue_wait_w_tmo(&voice_queue, &ev, HZ*5);
|
||||||
|
while (ev.id == SYS_TIMEOUT); /* Fake infinite wait */
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ev.id) {
|
switch (ev.id) {
|
||||||
|
@ -1110,35 +1184,27 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
|
||||||
|
|
||||||
case Q_VOICE_STOP:
|
case Q_VOICE_STOP:
|
||||||
LOGFQUEUE("voice < Q_VOICE_STOP");
|
LOGFQUEUE("voice < Q_VOICE_STOP");
|
||||||
if (ev.data == 1 && !playing && pcm_is_playing())
|
if (voice_on_voice_stop(ev.data, realsize))
|
||||||
{
|
|
||||||
/* Aborting: Slight hack - flush PCM buffer if
|
|
||||||
only being used for voice */
|
|
||||||
pcmbuf_play_stop();
|
|
||||||
}
|
|
||||||
if (voice_is_playing)
|
|
||||||
{
|
|
||||||
/* Clear the current buffer */
|
|
||||||
voice_is_playing = false;
|
|
||||||
voice_getmore = NULL;
|
|
||||||
voice_remaining = 0;
|
|
||||||
voicebuf = NULL;
|
|
||||||
|
|
||||||
/* Force the codec to think it's changing tracks */
|
|
||||||
ci_voice.new_track = 1;
|
|
||||||
*realsize = 0;
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SYS_USB_CONNECTED:
|
case SYS_USB_CONNECTED:
|
||||||
|
{
|
||||||
LOGFQUEUE("voice < SYS_USB_CONNECTED");
|
LOGFQUEUE("voice < SYS_USB_CONNECTED");
|
||||||
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
bool change_tracks = voice_on_voice_stop(ev.data, realsize);
|
||||||
|
/* Voice is obviously current so let us swap ourselves away if
|
||||||
|
playing so audio may stop itself - audio_codec_loaded can
|
||||||
|
only be true in this case if we're here even if the codec
|
||||||
|
is only about to load */
|
||||||
if (audio_codec_loaded)
|
if (audio_codec_loaded)
|
||||||
swap_codec();
|
swap_codec();
|
||||||
|
/* Playback should be finished by now - ack and wait */
|
||||||
|
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
||||||
usb_wait_for_disconnect(&voice_queue);
|
usb_wait_for_disconnect(&voice_queue);
|
||||||
|
if (change_tracks)
|
||||||
|
return NULL;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Q_VOICE_PLAY:
|
case Q_VOICE_PLAY:
|
||||||
LOGFQUEUE("voice < Q_VOICE_PLAY");
|
LOGFQUEUE("voice < Q_VOICE_PLAY");
|
||||||
|
@ -1149,17 +1215,17 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
|
||||||
#ifdef IRAM_STEAL
|
#ifdef IRAM_STEAL
|
||||||
if (voice_iram_stolen)
|
if (voice_iram_stolen)
|
||||||
{
|
{
|
||||||
|
/* Voice is the first to run again and is currently
|
||||||
|
loaded */
|
||||||
logf("voice: iram restore");
|
logf("voice: iram restore");
|
||||||
memcpy((void*)CODEC_IRAM_ORIGIN,
|
memcpy(CODEC_IRAM_ORIGIN, iram_buf, CODEC_IRAM_SIZE);
|
||||||
iram_buf[CODEC_IDX_VOICE],
|
|
||||||
CODEC_IRAM_SIZE);
|
|
||||||
voice_iram_stolen = false;
|
voice_iram_stolen = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* must reset the buffer before any playback
|
/* Must reset the buffer before any playback begins if
|
||||||
begins if needed */
|
needed */
|
||||||
if (buffer_state == BUFFER_STATE_TRASHED)
|
if (buffer_state == BUFFER_STATE_TRASHED)
|
||||||
audio_reset_buffer(pcmbuf_get_bufsize());
|
audio_reset_buffer();
|
||||||
|
|
||||||
voice_is_playing = true;
|
voice_is_playing = true;
|
||||||
trigger_cpu_boost();
|
trigger_cpu_boost();
|
||||||
|
@ -1168,7 +1234,7 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
|
||||||
voicebuf = voice_data->buf;
|
voicebuf = voice_data->buf;
|
||||||
voice_getmore = voice_data->callback;
|
voice_getmore = voice_data->callback;
|
||||||
}
|
}
|
||||||
goto voice_play_clip;
|
goto voice_play_clip; /* To exit both switch and while */
|
||||||
|
|
||||||
case SYS_TIMEOUT:
|
case SYS_TIMEOUT:
|
||||||
LOGFQUEUE_SYS_TIMEOUT("voice < SYS_TIMEOUT");
|
LOGFQUEUE_SYS_TIMEOUT("voice < SYS_TIMEOUT");
|
||||||
|
@ -1254,11 +1320,14 @@ static void voice_thread(void)
|
||||||
voice_remaining = 0;
|
voice_remaining = 0;
|
||||||
voice_getmore = NULL;
|
voice_getmore = NULL;
|
||||||
|
|
||||||
|
/* FIXME: If we being starting the voice thread without reboot, the
|
||||||
|
voice_queue could be full of old stuff and we must flush it. */
|
||||||
codec_load_file(get_codec_filename(AFMT_MPA_L3), &ci_voice);
|
codec_load_file(get_codec_filename(AFMT_MPA_L3), &ci_voice);
|
||||||
|
|
||||||
logf("Voice codec finished");
|
logf("Voice codec finished");
|
||||||
voice_codec_loaded = false;
|
voice_codec_loaded = false;
|
||||||
mutex_unlock(&mutex_codecthread);
|
mutex_unlock(&mutex_codecthread);
|
||||||
|
voice_thread_p = NULL;
|
||||||
remove_thread(NULL);
|
remove_thread(NULL);
|
||||||
} /* voice_thread */
|
} /* voice_thread */
|
||||||
|
|
||||||
|
@ -1898,7 +1967,8 @@ static void codec_thread(void)
|
||||||
LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
|
LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
|
||||||
audio_codec_loaded = true;
|
audio_codec_loaded = true;
|
||||||
#ifdef PLAYBACK_VOICE
|
#ifdef PLAYBACK_VOICE
|
||||||
/* Don't sent messages to voice codec if it's not current */
|
/* Don't sent messages to voice codec if it's already swapped
|
||||||
|
out or it will never get this */
|
||||||
if (voice_codec_loaded && current_codec == CODEC_IDX_VOICE)
|
if (voice_codec_loaded && current_codec == CODEC_IDX_VOICE)
|
||||||
{
|
{
|
||||||
LOGFQUEUE("codec > voice Q_AUDIO_PLAY");
|
LOGFQUEUE("codec > voice Q_AUDIO_PLAY");
|
||||||
|
@ -2346,7 +2416,7 @@ strip_ape_tag:
|
||||||
/* Skip APE tag */
|
/* Skip APE tag */
|
||||||
if (FILEBUFUSED > len)
|
if (FILEBUFUSED > len)
|
||||||
{
|
{
|
||||||
logf("Skipping APE tag (%dB)", len);
|
logf("Skipping APE tag (%ldB)", len);
|
||||||
buf_widx = RINGBUF_SUB(buf_widx, len);
|
buf_widx = RINGBUF_SUB(buf_widx, len);
|
||||||
tracks[track_widx].available -= len;
|
tracks[track_widx].available -= len;
|
||||||
tracks[track_widx].filesize -= len;
|
tracks[track_widx].filesize -= len;
|
||||||
|
@ -2388,7 +2458,7 @@ static bool audio_read_file(size_t minimum)
|
||||||
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
{
|
{
|
||||||
logf("File ended %dB early", tracks[track_widx].filerem);
|
logf("File ended %ldB early", tracks[track_widx].filerem);
|
||||||
tracks[track_widx].filesize -= tracks[track_widx].filerem;
|
tracks[track_widx].filesize -= tracks[track_widx].filerem;
|
||||||
tracks[track_widx].filerem = 0;
|
tracks[track_widx].filerem = 0;
|
||||||
break;
|
break;
|
||||||
|
@ -2408,7 +2478,7 @@ static bool audio_read_file(size_t minimum)
|
||||||
|
|
||||||
if ((unsigned)rc > tracks[track_widx].filerem)
|
if ((unsigned)rc > tracks[track_widx].filerem)
|
||||||
{
|
{
|
||||||
logf("Bad: rc-filerem=%d, fixing", rc-tracks[track_widx].filerem);
|
logf("Bad: rc-filerem=%ld, fixing", rc-tracks[track_widx].filerem);
|
||||||
tracks[track_widx].filesize += rc - tracks[track_widx].filerem;
|
tracks[track_widx].filesize += rc - tracks[track_widx].filerem;
|
||||||
tracks[track_widx].filerem = rc;
|
tracks[track_widx].filerem = rc;
|
||||||
}
|
}
|
||||||
|
@ -2440,7 +2510,7 @@ static bool audio_read_file(size_t minimum)
|
||||||
|
|
||||||
if (tracks[track_widx].filerem == 0)
|
if (tracks[track_widx].filerem == 0)
|
||||||
{
|
{
|
||||||
logf("Finished buf:%dB", tracks[track_widx].filesize);
|
logf("Finished buf:%ldB", tracks[track_widx].filesize);
|
||||||
close(current_fd);
|
close(current_fd);
|
||||||
current_fd = -1;
|
current_fd = -1;
|
||||||
audio_strip_tags();
|
audio_strip_tags();
|
||||||
|
@ -2453,7 +2523,7 @@ static bool audio_read_file(size_t minimum)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logf("%s buf:%dB", ret_val?"Quick":"Partially",
|
logf("%s buf:%ldB", ret_val?"Quick":"Partially",
|
||||||
tracks[track_widx].filesize - tracks[track_widx].filerem);
|
tracks[track_widx].filesize - tracks[track_widx].filerem);
|
||||||
return ret_val;
|
return ret_val;
|
||||||
}
|
}
|
||||||
|
@ -2548,7 +2618,7 @@ static bool audio_loadcodec(bool start_play)
|
||||||
tracks[track_widx].has_codec = true;
|
tracks[track_widx].has_codec = true;
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
logf("Done: %dB", size);
|
logf("Done: %ldB", size);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2901,9 +2971,11 @@ static void audio_fill_file_buffer(
|
||||||
bool had_next_track = audio_next_track() != NULL;
|
bool had_next_track = audio_next_track() != NULL;
|
||||||
bool continue_buffering;
|
bool continue_buffering;
|
||||||
|
|
||||||
/* must reset the buffer before use if trashed */
|
/* Must reset the buffer before use if trashed or voice only - voice
|
||||||
if (buffer_state != BUFFER_STATE_NORMAL)
|
file size shouldn't have changed so we can go straight from
|
||||||
audio_reset_buffer(pcmbuf_get_bufsize());
|
BUFFER_STATE_VOICED_ONLY to BUFFER_STATE_INITIALIZED */
|
||||||
|
if (buffer_state != BUFFER_STATE_INITIALIZED)
|
||||||
|
audio_reset_buffer();
|
||||||
|
|
||||||
if (!audio_initialize_buffer_fill(!start_play))
|
if (!audio_initialize_buffer_fill(!start_play))
|
||||||
return ;
|
return ;
|
||||||
|
@ -3342,16 +3414,13 @@ static void audio_initiate_dir_change(long direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Layout audio buffer as follows:
|
* Layout audio buffer as follows - iram buffer depends on target:
|
||||||
* [|TALK]|MALLOC|FILE|GUARD|PCM|AUDIOCODEC|[VOICECODEC|]
|
* [|SWAP:iram][|TALK]|MALLOC|FILE|GUARD|PCM|[SWAP:dram[|iram]|]
|
||||||
*/
|
*/
|
||||||
static void audio_reset_buffer(size_t pcmbufsize)
|
static void audio_reset_buffer(void)
|
||||||
{
|
{
|
||||||
/* see audio_get_recording_buffer if this is modified */
|
/* see audio_get_recording_buffer if this is modified */
|
||||||
size_t offset;
|
|
||||||
|
|
||||||
logf("audio_reset_buffer");
|
logf("audio_reset_buffer");
|
||||||
logf(" size:%08X", pcmbufsize);
|
|
||||||
|
|
||||||
/* If the setup of anything allocated before the file buffer is
|
/* If the setup of anything allocated before the file buffer is
|
||||||
changed, do check the adjustments after the buffer_alloc call
|
changed, do check the adjustments after the buffer_alloc call
|
||||||
|
@ -3362,19 +3431,34 @@ static void audio_reset_buffer(size_t pcmbufsize)
|
||||||
/* Align the malloc buf to line size. Especially important to cf
|
/* Align the malloc buf to line size. Especially important to cf
|
||||||
targets that do line reads/writes. */
|
targets that do line reads/writes. */
|
||||||
malloc_buf = (unsigned char *)(((uintptr_t)malloc_buf + 15) & ~15);
|
malloc_buf = (unsigned char *)(((uintptr_t)malloc_buf + 15) & ~15);
|
||||||
filebuf = malloc_buf + MALLOC_BUFSIZE;
|
filebuf = malloc_buf + MALLOC_BUFSIZE; /* filebuf line align implied */
|
||||||
filebuflen = audiobufend - filebuf;
|
filebuflen = audiobufend - filebuf;
|
||||||
|
|
||||||
/* Allow for codec(s) at end of audio buffer */
|
/* Allow for codec swap space at end of audio buffer */
|
||||||
if (talk_voice_required())
|
if (talk_voice_required())
|
||||||
{
|
{
|
||||||
|
/* Layout of swap buffer:
|
||||||
|
* #ifdef IRAM_STEAL (dedicated iram_buf):
|
||||||
|
* |iram_buf|...audiobuf...|dram_buf|audiobufend
|
||||||
|
* #else:
|
||||||
|
* audiobuf...|dram_buf|iram_buf|audiobufend
|
||||||
|
*/
|
||||||
#ifdef PLAYBACK_VOICE
|
#ifdef PLAYBACK_VOICE
|
||||||
|
/* Check for an absolutely nasty situation which should never,
|
||||||
|
ever happen - frankly should just panic */
|
||||||
|
if (voice_codec_loaded && current_codec != CODEC_IDX_VOICE)
|
||||||
|
{
|
||||||
|
logf("buffer reset with voice swapped");
|
||||||
|
}
|
||||||
|
/* line align length which line aligns the calculations below since
|
||||||
|
all sizes are also at least line aligned - needed for memswap128 */
|
||||||
|
filebuflen &= ~15;
|
||||||
#ifdef IRAM_STEAL
|
#ifdef IRAM_STEAL
|
||||||
filebuflen -= CODEC_IRAM_SIZE + 2*CODEC_SIZE;
|
filebuflen -= CODEC_SIZE;
|
||||||
#else
|
#else
|
||||||
filebuflen -= 2*(CODEC_IRAM_SIZE + CODEC_SIZE);
|
filebuflen -= CODEC_SIZE + CODEC_IRAM_SIZE;
|
||||||
#endif
|
#endif
|
||||||
/* Allow 2 codecs at end of audio buffer */
|
/* Allocate buffers for swapping voice <=> audio */
|
||||||
/* If using IRAM for plugins voice IRAM swap buffer must be dedicated
|
/* If using IRAM for plugins voice IRAM swap buffer must be dedicated
|
||||||
and out of the way of buffer usage or else a call to audio_get_buffer
|
and out of the way of buffer usage or else a call to audio_get_buffer
|
||||||
and subsequent buffer use might trash the swap space. A plugin
|
and subsequent buffer use might trash the swap space. A plugin
|
||||||
|
@ -3383,14 +3467,13 @@ static void audio_reset_buffer(size_t pcmbufsize)
|
||||||
has been obtained already or never allowing use of the voice IRAM
|
has been obtained already or never allowing use of the voice IRAM
|
||||||
buffer within the audio buffer. Using buffer_alloc basically
|
buffer within the audio buffer. Using buffer_alloc basically
|
||||||
implements the second in a more convenient way. */
|
implements the second in a more convenient way. */
|
||||||
iram_buf[CODEC_IDX_AUDIO] = filebuf + filebuflen;
|
dram_buf = filebuf + filebuflen;
|
||||||
dram_buf[CODEC_IDX_AUDIO] = iram_buf[CODEC_IDX_AUDIO] + CODEC_IRAM_SIZE;
|
|
||||||
|
|
||||||
#ifdef IRAM_STEAL
|
#ifdef IRAM_STEAL
|
||||||
/* Allocate voice IRAM swap buffer once */
|
/* Allocate voice IRAM swap buffer once */
|
||||||
if (iram_buf[CODEC_IDX_VOICE] == NULL)
|
if (iram_buf == NULL)
|
||||||
{
|
{
|
||||||
iram_buf[CODEC_IDX_VOICE] = buffer_alloc(CODEC_IRAM_SIZE);
|
iram_buf = buffer_alloc(CODEC_IRAM_SIZE);
|
||||||
/* buffer_alloc moves audiobuf; this is safe because only the end
|
/* buffer_alloc moves audiobuf; this is safe because only the end
|
||||||
* has been touched so far in this function and the address of
|
* has been touched so far in this function and the address of
|
||||||
* filebuf + filebuflen is not changed */
|
* filebuf + filebuflen is not changed */
|
||||||
|
@ -3398,41 +3481,63 @@ static void audio_reset_buffer(size_t pcmbufsize)
|
||||||
filebuf += CODEC_IRAM_SIZE;
|
filebuf += CODEC_IRAM_SIZE;
|
||||||
filebuflen -= CODEC_IRAM_SIZE;
|
filebuflen -= CODEC_IRAM_SIZE;
|
||||||
}
|
}
|
||||||
dram_buf[CODEC_IDX_VOICE] = dram_buf[CODEC_IDX_AUDIO] + CODEC_SIZE;
|
|
||||||
#else
|
#else
|
||||||
iram_buf[CODEC_IDX_VOICE] = dram_buf[CODEC_IDX_AUDIO] + CODEC_SIZE;
|
/* Allocate iram_buf after dram_buf */
|
||||||
dram_buf[CODEC_IDX_VOICE] = iram_buf[CODEC_IDX_VOICE] + CODEC_IRAM_SIZE;
|
iram_buf = dram_buf + CODEC_SIZE;
|
||||||
#endif /* IRAM_STEAL */
|
#endif /* IRAM_STEAL */
|
||||||
|
|
||||||
#endif /* PLAYBACK_VOICE */
|
#endif /* PLAYBACK_VOICE */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef PLAYBACK_VOICE
|
#ifdef PLAYBACK_VOICE
|
||||||
/* Allow for 1 codec at end of audio buffer */
|
/* No swap buffers needed */
|
||||||
filebuflen -= CODEC_IRAM_SIZE + CODEC_SIZE;
|
iram_buf = NULL;
|
||||||
|
dram_buf = NULL;
|
||||||
iram_buf[CODEC_IDX_AUDIO] = filebuf + filebuflen;
|
|
||||||
dram_buf[CODEC_IDX_AUDIO] = iram_buf[CODEC_IDX_AUDIO] + CODEC_IRAM_SIZE;
|
|
||||||
iram_buf[CODEC_IDX_VOICE] = NULL;
|
|
||||||
dram_buf[CODEC_IDX_VOICE] = NULL;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
filebuflen -= pcmbuf_init(pcmbufsize, filebuf + filebuflen) + GUARD_BUFSIZE;
|
/* Subtract whatever the pcm buffer says it used plus the guard buffer */
|
||||||
|
filebuflen -= pcmbuf_init(filebuf + filebuflen) + GUARD_BUFSIZE;
|
||||||
|
|
||||||
/* Ensure that file buffer is aligned */
|
/* Make sure filebuflen is a longword multiple after adjustment - filebuf
|
||||||
offset = -(size_t)filebuf & 3;
|
will already be line aligned */
|
||||||
filebuf += offset;
|
|
||||||
filebuflen -= offset;
|
|
||||||
filebuflen &= ~3;
|
filebuflen &= ~3;
|
||||||
|
|
||||||
|
/* Set the high watermark as 75% full...or 25% empty :) */
|
||||||
#if MEM > 8
|
#if MEM > 8
|
||||||
high_watermark = (3*filebuflen)/4;
|
high_watermark = 3*filebuflen / 4;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Clear any references to the file buffer */
|
/* Clear any references to the file buffer */
|
||||||
buffer_state = BUFFER_STATE_NORMAL;
|
buffer_state = BUFFER_STATE_INITIALIZED;
|
||||||
|
|
||||||
|
#ifdef ROCKBOX_HAS_LOGF
|
||||||
|
/* Make sure everything adds up - yes, some info is a bit redundant but
|
||||||
|
aids viewing and the sumation of certain variables should add up to
|
||||||
|
the location of others. */
|
||||||
|
{
|
||||||
|
size_t pcmbufsize;
|
||||||
|
unsigned char * pcmbuf = pcmbuf_get_meminfo(&pcmbufsize);
|
||||||
|
logf("mabuf: %08X", (unsigned)malloc_buf);
|
||||||
|
logf("mabufe: %08X", (unsigned)(malloc_buf + MALLOC_BUFSIZE));
|
||||||
|
logf("fbuf: %08X", (unsigned)filebuf);
|
||||||
|
logf("fbufe: %08X", (unsigned)(filebuf + filebuflen));
|
||||||
|
logf("gbuf: %08X", (unsigned)(filebuf + filebuflen));
|
||||||
|
logf("gbufe: %08X", (unsigned)(filebuf + filebuflen + GUARD_BUFSIZE));
|
||||||
|
logf("pcmb: %08X", (unsigned)pcmbuf);
|
||||||
|
logf("pcmbe: %08X", (unsigned)(pcmbuf + pcmbufsize));
|
||||||
|
if (dram_buf)
|
||||||
|
{
|
||||||
|
logf("dramb: %08X", (unsigned)dram_buf);
|
||||||
|
logf("drambe: %08X", (unsigned)(dram_buf + CODEC_SIZE));
|
||||||
|
}
|
||||||
|
if (iram_buf)
|
||||||
|
{
|
||||||
|
logf("iramb: %08X", (unsigned)iram_buf);
|
||||||
|
logf("irambe: %08X", (unsigned)(iram_buf + CODEC_IRAM_SIZE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MEM > 8
|
#if MEM > 8
|
||||||
|
@ -3454,6 +3559,16 @@ static void audio_thread(void)
|
||||||
#ifdef PLAYBACK_VOICE
|
#ifdef PLAYBACK_VOICE
|
||||||
/* Unlock mutex that init stage locks before creating this thread */
|
/* Unlock mutex that init stage locks before creating this thread */
|
||||||
mutex_unlock(&mutex_codecthread);
|
mutex_unlock(&mutex_codecthread);
|
||||||
|
|
||||||
|
/* Buffers must be set up by now - should panic - really */
|
||||||
|
if (buffer_state != BUFFER_STATE_INITIALIZED)
|
||||||
|
{
|
||||||
|
logf("audio_thread start: no buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Have to wait for voice to load up or else the codec swap will be
|
||||||
|
invalid when an audio codec is loaded */
|
||||||
|
wait_for_voice_swap_in();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
|
@ -3604,12 +3719,14 @@ void audio_init(void)
|
||||||
static struct mp3entry id3_voice;
|
static struct mp3entry id3_voice;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
logf("audio: %s", audio_is_initialized ?
|
|
||||||
"initializing" : "already initialized");
|
|
||||||
|
|
||||||
/* Can never do this twice */
|
/* Can never do this twice */
|
||||||
if (audio_is_initialized)
|
if (audio_is_initialized)
|
||||||
|
{
|
||||||
|
logf("audio: already initialized");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logf("audio: initializing");
|
||||||
|
|
||||||
/* Initialize queues before giving control elsewhere in case it likes
|
/* Initialize queues before giving control elsewhere in case it likes
|
||||||
to send messages. Thread creation will be delayed however so nothing
|
to send messages. Thread creation will be delayed however so nothing
|
||||||
|
@ -3620,6 +3737,7 @@ void audio_init(void)
|
||||||
hardware is initialized - audio thread unlocks it after final init
|
hardware is initialized - audio thread unlocks it after final init
|
||||||
stage */
|
stage */
|
||||||
mutex_lock(&mutex_codecthread);
|
mutex_lock(&mutex_codecthread);
|
||||||
|
queue_init(&voice_queue, true);
|
||||||
#endif
|
#endif
|
||||||
queue_init(&audio_queue, true);
|
queue_init(&audio_queue, true);
|
||||||
queue_enable_queue_send(&audio_queue, &audio_queue_sender_list);
|
queue_enable_queue_send(&audio_queue, &audio_queue_sender_list);
|
||||||
|
@ -3663,6 +3781,7 @@ void audio_init(void)
|
||||||
ci_voice.seek_complete = voice_do_nothing;
|
ci_voice.seek_complete = voice_do_nothing;
|
||||||
ci_voice.set_elapsed = voice_set_elapsed_callback;
|
ci_voice.set_elapsed = voice_set_elapsed_callback;
|
||||||
ci_voice.set_offset = voice_set_offset_callback;
|
ci_voice.set_offset = voice_set_offset_callback;
|
||||||
|
ci_voice.configure = voice_configure_callback;
|
||||||
ci_voice.discard_codec = voice_do_nothing;
|
ci_voice.discard_codec = voice_do_nothing;
|
||||||
ci_voice.taginfo_ready = &voicetagtrue;
|
ci_voice.taginfo_ready = &voicetagtrue;
|
||||||
ci_voice.id3 = &id3_voice;
|
ci_voice.id3 = &id3_voice;
|
||||||
|
@ -3671,12 +3790,14 @@ void audio_init(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* initialize the buffer */
|
/* initialize the buffer */
|
||||||
filebuf = audiobuf; /* must be non-NULL for audio_set_crossfade */
|
filebuf = audiobuf;
|
||||||
|
|
||||||
/* audio_reset_buffer must to know the size of voice buffer so init
|
/* audio_reset_buffer must to know the size of voice buffer so init
|
||||||
voice first */
|
talk first */
|
||||||
talk_init();
|
talk_init();
|
||||||
|
|
||||||
|
/* Create the threads late now that we shouldn't be yielding again before
|
||||||
|
returning */
|
||||||
codec_thread_p = create_thread(
|
codec_thread_p = create_thread(
|
||||||
codec_thread, codec_stack, sizeof(codec_stack),
|
codec_thread, codec_stack, sizeof(codec_stack),
|
||||||
codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
|
codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
|
||||||
|
@ -3686,8 +3807,24 @@ void audio_init(void)
|
||||||
audio_thread_name IF_PRIO(, PRIORITY_BUFFERING)
|
audio_thread_name IF_PRIO(, PRIORITY_BUFFERING)
|
||||||
IF_COP(, CPU, false));
|
IF_COP(, CPU, false));
|
||||||
|
|
||||||
audio_set_crossfade(global_settings.crossfade);
|
#ifdef PLAYBACK_VOICE
|
||||||
|
/* TODO: Change this around when various speech codecs can be used */
|
||||||
|
if (talk_voice_required())
|
||||||
|
{
|
||||||
|
logf("Starting voice codec");
|
||||||
|
create_thread(voice_thread, voice_stack,
|
||||||
|
sizeof(voice_stack), voice_thread_name
|
||||||
|
IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU, false));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Set crossfade setting for next buffer init which should be about... */
|
||||||
|
pcmbuf_crossfade_enable(global_settings.crossfade);
|
||||||
|
|
||||||
|
/* ...now! Set up the buffers */
|
||||||
|
audio_reset_buffer();
|
||||||
|
|
||||||
|
/* Probably safe to say */
|
||||||
audio_is_initialized = true;
|
audio_is_initialized = true;
|
||||||
|
|
||||||
sound_settings_apply();
|
sound_settings_apply();
|
||||||
|
|
|
@ -64,8 +64,6 @@ void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3,
|
||||||
bool last_track));
|
bool last_track));
|
||||||
void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3,
|
void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3,
|
||||||
bool last_track));
|
bool last_track));
|
||||||
void voice_init(void);
|
|
||||||
void voice_stop(void);
|
|
||||||
|
|
||||||
#if CONFIG_CODEC == SWCODEC /* This #ifdef is better here than gui/gwps.c */
|
#if CONFIG_CODEC == SWCODEC /* This #ifdef is better here than gui/gwps.c */
|
||||||
extern void audio_next_dir(void);
|
extern void audio_next_dir(void);
|
||||||
|
|
|
@ -528,9 +528,6 @@ void talk_init(void)
|
||||||
if (has_voicefile)
|
if (has_voicefile)
|
||||||
{
|
{
|
||||||
voicefile_size = filesize(filehandle);
|
voicefile_size = filesize(filehandle);
|
||||||
#if CONFIG_CODEC == SWCODEC
|
|
||||||
voice_init();
|
|
||||||
#endif
|
|
||||||
close(filehandle); /* close again, this was just to detect presence */
|
close(filehandle); /* close again, this was just to detect presence */
|
||||||
filehandle = -1;
|
filehandle = -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,6 +248,8 @@ target/coldfire/crt0.S
|
||||||
target/coldfire/memcpy-coldfire.S
|
target/coldfire/memcpy-coldfire.S
|
||||||
target/coldfire/memmove-coldfire.S
|
target/coldfire/memmove-coldfire.S
|
||||||
target/coldfire/memset-coldfire.S
|
target/coldfire/memset-coldfire.S
|
||||||
|
target/coldfire/memswap128-coldfire.S
|
||||||
|
common/memswap128.c
|
||||||
#if defined(HAVE_LCD_COLOR) \
|
#if defined(HAVE_LCD_COLOR) \
|
||||||
|| defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED)
|
|| defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED)
|
||||||
target/coldfire/memset16-coldfire.S
|
target/coldfire/memset16-coldfire.S
|
||||||
|
@ -269,6 +271,7 @@ common/strlen.c
|
||||||
#ifndef SIMULATOR
|
#ifndef SIMULATOR
|
||||||
target/arm/memset-arm.S
|
target/arm/memset-arm.S
|
||||||
target/arm/memset16-arm.S
|
target/arm/memset16-arm.S
|
||||||
|
target/arm/memswap128-arm.S
|
||||||
#if CONFIG_I2C == I2C_PP5020 || CONFIG_I2C == I2C_PP5002
|
#if CONFIG_I2C == I2C_PP5020 || CONFIG_I2C == I2C_PP5002
|
||||||
target/arm/i2c-pp.c
|
target/arm/i2c-pp.c
|
||||||
#elif CONFIG_I2C == I2C_PNX0101
|
#elif CONFIG_I2C == I2C_PNX0101
|
||||||
|
@ -295,6 +298,7 @@ common/memcpy.c
|
||||||
common/memmove.c
|
common/memmove.c
|
||||||
common/memset.c
|
common/memset.c
|
||||||
common/memset16.c
|
common/memset16.c
|
||||||
|
common/memswap128.c
|
||||||
common/strlen.c
|
common/strlen.c
|
||||||
#ifndef SIMULATOR
|
#ifndef SIMULATOR
|
||||||
crt0.S
|
crt0.S
|
||||||
|
|
44
firmware/common/memswap128.c
Normal file
44
firmware/common/memswap128.c
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
* \/ \/ \/ \/ \/
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2007 by Michael Sevakis
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
#include "config.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
void memswap128(void *a, void *b, size_t len)
|
||||||
|
{
|
||||||
|
for (len >>= 4; len > 0; len--, a += 16, b += 16)
|
||||||
|
{
|
||||||
|
int32_t a0 = *((int32_t *)a + 0);
|
||||||
|
int32_t a1 = *((int32_t *)a + 1);
|
||||||
|
int32_t a2 = *((int32_t *)a + 2);
|
||||||
|
int32_t a3 = *((int32_t *)a + 3);
|
||||||
|
int32_t b0 = *((int32_t *)b + 0);
|
||||||
|
int32_t b1 = *((int32_t *)b + 1);
|
||||||
|
int32_t b2 = *((int32_t *)b + 2);
|
||||||
|
int32_t b3 = *((int32_t *)b + 3);
|
||||||
|
*((int32_t *)b + 0) = a0;
|
||||||
|
*((int32_t *)b + 1) = a1;
|
||||||
|
*((int32_t *)b + 2) = a2;
|
||||||
|
*((int32_t *)b + 3) = a3;
|
||||||
|
*((int32_t *)a + 0) = b0;
|
||||||
|
*((int32_t *)a + 1) = b1;
|
||||||
|
*((int32_t *)a + 2) = b2;
|
||||||
|
*((int32_t *)a + 3) = b3;
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,4 +24,15 @@
|
||||||
|
|
||||||
void memset16(void *dst, int val, size_t len);
|
void memset16(void *dst, int val, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memswap128
|
||||||
|
*
|
||||||
|
* Exchanges the contents of two buffers.
|
||||||
|
* For maximum efficiency, this function performs no aligning of addresses
|
||||||
|
* and buf1, buf2 and len should be 16 byte (128 bit) aligned. Not being at
|
||||||
|
* least longword aligned will fail on some architechtures. Any len mod 16
|
||||||
|
* at the end is not swapped.
|
||||||
|
*/
|
||||||
|
void memswap128(void *buf1, void *buf2, size_t len);
|
||||||
|
|
||||||
#endif /* _MEMORY_H_ */
|
#endif /* _MEMORY_H_ */
|
||||||
|
|
44
firmware/target/arm/memswap128-arm.S
Normal file
44
firmware/target/arm/memswap128-arm.S
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
* \/ \/ \/ \/ \/
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2007 by Michael Sevakis
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* void memswap128(void *buf1, void *buf2, size_t len)
|
||||||
|
*/
|
||||||
|
.section .icode, "ax", %progbits
|
||||||
|
.align 2
|
||||||
|
.global memswap128
|
||||||
|
.type memswap128, %function
|
||||||
|
memswap128:
|
||||||
|
@ r0 = buf1
|
||||||
|
@ r1 = buf2
|
||||||
|
@ r2 = len
|
||||||
|
movs r2, r2, lsr #4 @ bytes => lines, len == 0?
|
||||||
|
moveq pc, lr @ not at least a line? leave
|
||||||
|
stmdb sp!, { r4-r10, lr } @ save registers and return address
|
||||||
|
.loop: @
|
||||||
|
ldmia r0, { r3-r6 } @ read four longwords from buf1
|
||||||
|
ldmia r1, { r7-r10 } @ read four longwords from buf2
|
||||||
|
stmia r0!, { r7-r10 } @ write buf2 data to buf1, buf1 += 16
|
||||||
|
stmia r1!, { r3-r6 } @ write buf1 data to buf2, buf2 += 16
|
||||||
|
subs r2, r2, #1 @ len -= 1, len > 0 ?
|
||||||
|
bhi .loop @ yes? keep exchanging
|
||||||
|
ldmia sp!, { r4-r10, pc } @ restore registers and return
|
||||||
|
.end:
|
||||||
|
.size memswap128, .end-memswap128
|
||||||
|
|
50
firmware/target/coldfire/memswap128-coldfire.S
Normal file
50
firmware/target/coldfire/memswap128-coldfire.S
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
* \/ \/ \/ \/ \/
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2007 by Michael Sevakis
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* void memswap128(void *buf1, void *buf2, size_t len)
|
||||||
|
*/
|
||||||
|
.section .icode, "ax", @progbits
|
||||||
|
.align 2
|
||||||
|
.global memswap128
|
||||||
|
.type memswap128, @function
|
||||||
|
memswap128:
|
||||||
|
lea.l -28(%sp), %sp | save registers
|
||||||
|
movem.l %d2-%d7/%a2, (%sp) |
|
||||||
|
movem.l 32(%sp), %a0-%a2 | %a0 = buf1
|
||||||
|
| %a1 = buf2
|
||||||
|
| %a2 = len
|
||||||
|
lea.l -15(%a0, %a2.l), %a2 | %a2 = end address - 15
|
||||||
|
cmp.l %a0, %a2 | end address <= buf1?
|
||||||
|
bls.b .no_lines | not at least a line? leave
|
||||||
|
.loop: |
|
||||||
|
movem.l (%a0), %d0-%d3 | read four longwords from buf1
|
||||||
|
movem.l (%a1), %d4-%d7 | read four longwords from buf2
|
||||||
|
movem.l %d4-%d7, (%a0) | write buf2 data to buf1
|
||||||
|
movem.l %d0-%d3, (%a1) | write buf1 data to buf2
|
||||||
|
lea.l 16(%a0), %a0 | buf1 += 16
|
||||||
|
lea.l 16(%a1), %a1 | buf2 += 16
|
||||||
|
cmp.l %a0, %a2 | %a0 < %d0?
|
||||||
|
bhi.b .loop | yes? keep exchanging
|
||||||
|
.no_lines: |
|
||||||
|
movem.l (%sp), %d2-%d7/%a2 | restore registers
|
||||||
|
lea.l 28(%sp), %sp |
|
||||||
|
rts |
|
||||||
|
.end:
|
||||||
|
.size memswap128, .end-memswap128
|
Loading…
Add table
Add a link
Reference in a new issue