1
0
Fork 0
forked from len0rd/rockbox

FS#5992 Reworked playback.c to fix voice bugs by Steve Bavin. Fixed to

work with the new scheduler and handled properly and fast the track
skipping.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10966 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Miika Pekkarinen 2006-09-17 08:34:42 +00:00
parent 4553069f9d
commit 815684aced
2 changed files with 173 additions and 109 deletions

View file

@ -232,7 +232,7 @@ static inline void pcmbuf_add_chunk(void)
pcmbuf_current->link = NULL; pcmbuf_current->link = NULL;
/* This is single use only */ /* This is single use only */
pcmbuf_event_handler = NULL; pcmbuf_event_handler = NULL;
if (pcmbuf_read) { if (pcmbuf_read != NULL) {
if (pcmbuf_flush) if (pcmbuf_flush)
{ {
pcmbuf_write_end->link = pcmbuf_read->link; pcmbuf_write_end->link = pcmbuf_read->link;
@ -305,14 +305,14 @@ bool pcmbuf_is_lowdata(void)
/* Amount of bytes left in the buffer. */ /* Amount of bytes left in the buffer. */
inline size_t pcmbuf_free(void) inline size_t pcmbuf_free(void)
{ {
if (pcmbuf_read) if (pcmbuf_read != NULL)
{ {
size_t read = (size_t)pcmbuf_read->addr; void *read = pcmbuf_read->addr;
size_t write = void *write = &audiobuffer[audiobuffer_pos + audiobuffer_fillpos];
(size_t)&audiobuffer[audiobuffer_pos + audiobuffer_fillpos];
if (read < write) if (read < write)
read += pcmbuf_size; return (size_t)(read - write) + pcmbuf_size;
return read - write; else
return (size_t) (read - write);
} }
return pcmbuf_size; return pcmbuf_size;
} }
@ -335,7 +335,6 @@ bool pcmbuf_crossfade_init(bool manual_skip)
return false; return false;
} }
logf("pcmbuf_crossfade_init");
pcmbuf_boost(true); pcmbuf_boost(true);
/* Don't enable mix mode when skipping tracks manually. */ /* Don't enable mix mode when skipping tracks manually. */
@ -444,10 +443,13 @@ void pcmbuf_play_start(void)
* until dma has been initialized. */ * until dma has been initialized. */
pcm_mute(true); pcm_mute(true);
last_chunksize = pcmbuf_read->size; if (pcmbuf_read != NULL)
pcmbuf_unplayed_bytes -= last_chunksize; {
pcm_play_data(pcmbuf_callback, last_chunksize = pcmbuf_read->size;
pcmbuf_unplayed_bytes -= last_chunksize;
pcm_play_data(pcmbuf_callback,
(unsigned char *)pcmbuf_read->addr, last_chunksize); (unsigned char *)pcmbuf_read->addr, last_chunksize);
}
/* Now unmute the audio. */ /* Now unmute the audio. */
pcm_mute(false); pcm_mute(false);
@ -462,7 +464,6 @@ static bool pcmbuf_flush_fillpos(void)
if (audiobuffer_fillpos) { if (audiobuffer_fillpos) {
/* Never use the last buffer descriptor */ /* Never use the last buffer descriptor */
while (pcmbuf_write == pcmbuf_write_end) { while (pcmbuf_write == pcmbuf_write_end) {
logf("pcmbuf_flush_fillpos no descriptors");
/* Deboost to let the playback catchup */ /* Deboost to let the playback catchup */
pcmbuf_boost(false); pcmbuf_boost(false);
/* If this happens, something is being stupid */ /* If this happens, something is being stupid */
@ -511,7 +512,7 @@ static void crossfade_process_buffer(size_t fade_in_delay,
fade_out_rem -= block_rem; fade_out_rem -= block_rem;
/* Fade this block */ /* Fade this block */
while (block_rem > 0) while (block_rem > 0 && fade_out_chunk != NULL)
{ {
/* Fade one sample */ /* Fade one sample */
short *buf = (short *)(fade_out_chunk->addr); short *buf = (short *)(fade_out_chunk->addr);
@ -777,7 +778,8 @@ static bool prepare_insert(size_t length)
logf("pcm starting"); logf("pcm starting");
pcmbuf_play_start(); pcmbuf_play_start();
} }
} else if (pcmbuf_unplayed_bytes <= pcmbuf_watermark) }
else if (pcmbuf_unplayed_bytes <= pcmbuf_watermark)
pcmbuf_under_watermark(); pcmbuf_under_watermark();
return true; return true;
@ -823,7 +825,12 @@ void* pcmbuf_request_voice_buffer(size_t length, size_t *realsize, bool mix)
{ {
if (mix) if (mix)
{ {
if (pcmbuf_mix_chunk || pcmbuf_read->link) if (pcmbuf_read == NULL)
{
*realsize = 0;
return NULL;
}
else if (pcmbuf_mix_chunk || pcmbuf_read->link)
{ {
*realsize = MIN(length, PCMBUF_MIX_CHUNK); *realsize = MIN(length, PCMBUF_MIX_CHUNK);
return voicebuf; return voicebuf;
@ -888,7 +895,7 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
short *pcmbuf_end = (short *)fadebuf; short *pcmbuf_end = (short *)fadebuf;
size_t samples = NATIVE_FREQUENCY / 1000 * duration; size_t samples = NATIVE_FREQUENCY / 1000 * duration;
if (pcm_is_playing()) if (pcm_is_playing() && pcmbuf_read != NULL)
{ {
if (pcmbuf_read->link) if (pcmbuf_read->link)
{ {
@ -971,7 +978,7 @@ void pcmbuf_mix_voice(size_t length)
short *obuf; short *obuf;
size_t chunk_samples; size_t chunk_samples;
if (!pcmbuf_mix_chunk && pcmbuf_read) if (pcmbuf_mix_chunk == NULL && pcmbuf_read != NULL)
{ {
pcmbuf_mix_chunk = pcmbuf_read->link; pcmbuf_mix_chunk = pcmbuf_read->link;
/* Start 1/8s into the next chunk */ /* Start 1/8s into the next chunk */

View file

@ -22,6 +22,8 @@
/* TODO: Also play, stop ^^ */ /* TODO: Also play, stop ^^ */
/* TODO: Can use the track changed callback to detect end of track and seek /* TODO: Can use the track changed callback to detect end of track and seek
* in the previous track until this happens */ * in the previous track until this happens */
/* TODO: Pause should be handled in here, rather than PCMBUF so that voice can
* play whilst audio is paused */
/* Design: we have prev_ti already, have a conditional for what type of seek /* Design: we have prev_ti already, have a conditional for what type of seek
* to do on a seek request, if it is a previous track seek, skip previous, * to do on a seek request, if it is a previous track seek, skip previous,
* and in the request_next_track callback set the offset up the same way that * and in the request_next_track callback set the offset up the same way that
@ -39,7 +41,6 @@
#include "file.h" #include "file.h"
#include "lcd.h" #include "lcd.h"
#include "font.h" #include "font.h"
#include "backlight.h"
#include "button.h" #include "button.h"
#include "kernel.h" #include "kernel.h"
#include "tree.h" #include "tree.h"
@ -81,10 +82,7 @@
#include "recording.h" #include "recording.h"
#endif #endif
static volatile bool audio_codec_loaded; #define PLAYBACK_VOICE
static volatile bool voice_codec_loaded;
static volatile bool playing;
static volatile bool paused;
/* default point to start buffer refill */ /* default point to start buffer refill */
@ -99,10 +97,10 @@ static volatile bool paused;
/* macros to enable logf for queues */ /* macros to enable logf for queues */
#ifdef SIMULATOR #ifdef SIMULATOR
#define LOGF_QUEUES /* Define this for logf output of all queuing */ #define PLAYBACK_LOGQUEUES /* Define this for logf output of all queuing */
#endif #endif
#ifdef LOGF_QUEUES #ifdef PLAYBACK_LOGQUEUES
#define LOGFQUEUE(s) logf("%s", s) #define LOGFQUEUE(s) logf("%s", s)
#else #else
#define LOGFQUEUE(s) #define LOGFQUEUE(s)
@ -159,6 +157,14 @@ enum {
#endif #endif
#define CODEC_IRAM_SIZE 0xc000 #define CODEC_IRAM_SIZE 0xc000
#ifdef PLAYBACK_VOICE
#ifdef SIMULATOR
static unsigned char sim_iram[CODEC_IRAM_SIZE];
#undef CODEC_IRAM_ORIGIN
#define CODEC_IRAM_ORIGIN sim_iram
#endif
#endif
#ifndef SIMULATOR #ifndef SIMULATOR
extern bool audio_is_initialized; extern bool audio_is_initialized;
#else #else
@ -170,13 +176,9 @@ static bool audio_is_initialized = false;
static struct mutex mutex_codecthread; static struct mutex mutex_codecthread;
static struct event_queue codec_callback_queue; static struct event_queue codec_callback_queue;
static struct mp3entry id3_voice; static volatile bool audio_codec_loaded;
static volatile bool playing;
static char *voicebuf; static volatile bool paused;
static size_t voice_remaining;
static bool voice_is_playing;
static void (*voice_getmore)(unsigned char** start, int* size);
static struct thread_entry *voice_thread_p = NULL;
/* Is file buffer currently being refilled? */ /* Is file buffer currently being refilled? */
static volatile bool filling IDATA_ATTR; static volatile bool filling IDATA_ATTR;
@ -197,9 +199,7 @@ size_t filebufused;
static volatile size_t buf_ridx IDATA_ATTR; static volatile size_t buf_ridx IDATA_ATTR;
static volatile size_t buf_widx IDATA_ATTR; static volatile size_t buf_widx IDATA_ATTR;
#ifndef SIMULATOR
static unsigned char *iram_buf[2]; static unsigned char *iram_buf[2];
#endif
static unsigned char *dram_buf[2]; static unsigned char *dram_buf[2];
/* Step count to the next unbuffered track. */ /* Step count to the next unbuffered track. */
@ -209,7 +209,7 @@ static int last_peek_offset;
track ring structure. */ track ring structure. */
static int track_ridx; static int track_ridx;
static int track_widx; static int track_widx;
static bool track_changed; static bool track_changed; /* Audio and codec threads */
/* Partially loaded song's file handle to continue buffering later. */ /* Partially loaded song's file handle to continue buffering later. */
static int current_fd; static int current_fd;
@ -218,23 +218,20 @@ static int current_fd;
static size_t fill_bytesleft; static size_t fill_bytesleft;
/* Track info structure about songs in the file buffer. */ /* Track info structure about songs in the file buffer. */
static struct track_info tracks[MAX_TRACK]; static struct track_info tracks[MAX_TRACK]; /* Audio thread */
/* Pointer to track info structure about current song playing. */ /* Pointer to track info structure about current song playing. */
static struct track_info *cur_ti; static struct track_info *cur_ti; /* Audio and codec threads */
static struct track_info *prev_ti; static struct track_info *prev_ti; /* Audio and codec threads */
/* Have we reached end of the current playlist. */ /* Have we reached end of the current playlist. */
static bool playlist_end = false; static bool playlist_end = false; /* Audio thread */
/* Codec API including function callbacks. */
extern struct codec_api ci;
extern struct codec_api ci_voice;
/* Was the skip being executed manual or automatic? */ /* Was the skip being executed manual or automatic? */
static bool automatic_skip; static bool automatic_skip = false; /* Audio and codec threads */
static bool dir_skip = false; static bool dir_skip = false; /* Audio thread */
static bool new_playlist = false; static bool new_playlist = false; /* Audio thread */
static int wps_offset = 0;
/* Callback function to call when current track has really changed. */ /* Callback function to call when current track has really changed. */
void (*track_changed_callback)(struct mp3entry *id3); void (*track_changed_callback)(struct mp3entry *id3);
@ -250,6 +247,7 @@ static bool v1first = false;
/* Multiple threads */ /* Multiple threads */
static const char * get_codec_filename(int enc_spec); static const char * get_codec_filename(int enc_spec);
static void set_filebuf_watermark(int seconds);
/* Audio thread */ /* Audio thread */
static struct event_queue audio_queue; static struct event_queue audio_queue;
@ -257,12 +255,12 @@ static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
static const char audio_thread_name[] = "audio"; static const char audio_thread_name[] = "audio";
static void audio_thread(void); static void audio_thread(void);
static void set_filebuf_watermark(int seconds);
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(void); static void audio_reset_buffer(void);
/* Codec thread */ /* Codec thread */
extern struct codec_api ci;
static struct event_queue codec_queue; static struct event_queue codec_queue;
static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
IBSS_ATTR; IBSS_ATTR;
@ -271,6 +269,17 @@ static const char codec_thread_name[] = "codec";
struct thread_entry *codec_thread_p; struct thread_entry *codec_thread_p;
/* Voice thread */ /* Voice thread */
#ifdef PLAYBACK_VOICE
extern struct codec_api ci_voice;
static volatile bool voice_thread_start;
static volatile bool voice_is_playing;
static volatile bool voice_codec_loaded;
static void (*voice_getmore)(unsigned char** start, int* size);
static char *voicebuf;
static size_t voice_remaining;
static struct thread_entry *voice_thread_p = NULL;
static struct event_queue voice_queue; static struct event_queue voice_queue;
static long voice_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] static long voice_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
IBSS_ATTR; IBSS_ATTR;
@ -286,30 +295,39 @@ static void voice_boost_cpu(bool state);
#else #else
#define voice_boost_cpu(state) do { } while(0) #define voice_boost_cpu(state) do { } while(0)
#endif #endif
static void voice_thread(void); static void voice_thread(void);
#endif /* PLAYBACK_VOICE */
/* --- External interfaces --- */ /* --- External interfaces --- */
void mp3_play_data(const unsigned char* start, int size, void mp3_play_data(const unsigned char* start, int size,
void (*get_more)(unsigned char** start, int* size)) void (*get_more)(unsigned char** start, int* size))
{ {
#ifdef PLAYBACK_VOICE
static struct voice_info voice_clip; static struct voice_info voice_clip;
voice_clip.callback = get_more; voice_clip.callback = get_more;
voice_clip.buf = (char *)start; voice_clip.buf = (char *)start;
voice_clip.size = size; voice_clip.size = size;
logf("mp3 > voice Q_VOICE_STOP"); LOGFQUEUE("mp3 > voice Q_VOICE_STOP");
queue_post(&voice_queue, Q_VOICE_STOP, 0); queue_post(&voice_queue, Q_VOICE_STOP, 0);
logf("mp3 > voice Q_VOICE_PLAY"); LOGFQUEUE("mp3 > voice Q_VOICE_PLAY");
queue_post(&voice_queue, Q_VOICE_PLAY, &voice_clip); queue_post(&voice_queue, Q_VOICE_PLAY, &voice_clip);
voice_is_playing = true; voice_thread_start = true;
voice_boost_cpu(true); voice_boost_cpu(true);
#else
(void) start;
(void) size;
(void) get_more;
#endif
} }
void mp3_play_stop(void) void mp3_play_stop(void)
{ {
logf("mp3 > voice Q_VOICE_STOP"); #ifdef PLAYBACK_VOICE
LOGFQUEUE("mp3 > voice Q_VOICE_STOP");
queue_post(&voice_queue, Q_VOICE_STOP, 0); queue_post(&voice_queue, Q_VOICE_STOP, 0);
#endif
} }
bool mp3_pause_done(void) bool mp3_pause_done(void)
@ -360,8 +378,9 @@ struct mp3entry* audio_current_track(void)
const char *p; const char *p;
static struct mp3entry temp_id3; static struct mp3entry temp_id3;
int cur_idx; int cur_idx;
int offset = ci.new_track + wps_offset;
cur_idx = track_ridx + ci.new_track; cur_idx = track_ridx + offset;
cur_idx &= MAX_TRACK_MASK; cur_idx &= MAX_TRACK_MASK;
if (tracks[cur_idx].taginfo_ready) if (tracks[cur_idx].taginfo_ready)
@ -369,7 +388,7 @@ struct mp3entry* audio_current_track(void)
memset(&temp_id3, 0, sizeof(struct mp3entry)); memset(&temp_id3, 0, sizeof(struct mp3entry));
filename = playlist_peek(ci.new_track); filename = playlist_peek(offset);
if (!filename) if (!filename)
filename = "No file!"; filename = "No file!";
@ -427,10 +446,7 @@ void audio_play(long offset)
} }
else else
{ {
if (playing) audio_stop();
audio_stop();
playing = true;
LOGFQUEUE("audio > audio Q_AUDIO_PLAY"); LOGFQUEUE("audio > audio Q_AUDIO_PLAY");
queue_post(&audio_queue, Q_AUDIO_PLAY, (void *)offset); queue_post(&audio_queue, Q_AUDIO_PLAY, (void *)offset);
} }
@ -440,8 +456,6 @@ void audio_stop(void)
{ {
LOGFQUEUE("audio > audio Q_AUDIO_STOP"); LOGFQUEUE("audio > audio Q_AUDIO_STOP");
queue_post(&audio_queue, Q_AUDIO_STOP, 0); queue_post(&audio_queue, Q_AUDIO_STOP, 0);
while (playing || audio_codec_loaded)
yield();
} }
void audio_pause(void) void audio_pause(void)
@ -461,10 +475,21 @@ void audio_next(void)
if (global_settings.beep) if (global_settings.beep)
pcmbuf_beep(5000, 100, 2500*global_settings.beep); pcmbuf_beep(5000, 100, 2500*global_settings.beep);
/* Should be safe to do outside of thread, that way we get if (playlist_check(ci.new_track + wps_offset + 1))
* the instant wps response at least. */ {
audio_initiate_track_change(1); LOGFQUEUE("audio > audio Q_AUDIO_SKIP 1");
// queue_post(&audio_queue, Q_AUDIO_SKIP, (void *)1); queue_post(&audio_queue, Q_AUDIO_SKIP, (void *)1);
/* Keep wps fast while our message travels inside deep playback queues. */
wps_offset++;
track_changed = true;
}
else
{
/* No more tracks. */
if (global_settings.beep)
pcmbuf_beep(3000, 300, 2500*global_settings.beep);
}
} }
void audio_prev(void) void audio_prev(void)
@ -472,8 +497,21 @@ void audio_prev(void)
if (global_settings.beep) if (global_settings.beep)
pcmbuf_beep(5000, 100, 2500*global_settings.beep); pcmbuf_beep(5000, 100, 2500*global_settings.beep);
audio_initiate_track_change(-1); if (playlist_check(ci.new_track + wps_offset - 1))
// queue_post(&audio_queue, Q_AUDIO_SKIP, (void *)-1); {
LOGFQUEUE("audio > audio Q_AUDIO_SKIP -1");
queue_post(&audio_queue, Q_AUDIO_SKIP, (void *)-1);
/* Keep wps fast while our message travels inside deep playback queues. */
wps_offset--;
track_changed = true;
}
else
{
/* No more tracks. */
if (global_settings.beep)
pcmbuf_beep(3000, 300, 2500*global_settings.beep);
}
} }
void audio_next_dir(void) void audio_next_dir(void)
@ -598,7 +636,6 @@ void audio_set_crossfade(int enable)
/* Restart playback. */ /* Restart playback. */
if (was_playing) { if (was_playing) {
playing = true;
LOGFQUEUE("audio > audio Q_AUDIO_PLAY"); LOGFQUEUE("audio > audio Q_AUDIO_PLAY");
queue_post(&audio_queue, Q_AUDIO_PLAY, (void *)offset); queue_post(&audio_queue, Q_AUDIO_PLAY, (void *)offset);
@ -619,7 +656,11 @@ void audio_preinit(void)
playing = false; playing = false;
paused = false; paused = false;
audio_codec_loaded = false; audio_codec_loaded = false;
#ifdef PLAYBACK_VOICE
voice_is_playing = false; voice_is_playing = false;
voice_thread_start = false;
voice_codec_loaded = false;
#endif
track_changed = false; track_changed = false;
current_fd = -1; current_fd = -1;
track_buffer_callback = NULL; track_buffer_callback = NULL;
@ -647,6 +688,7 @@ void audio_init(void)
void voice_init(void) void voice_init(void)
{ {
#ifdef PLAYBACK_VOICE
if (!filebuf) if (!filebuf)
return; /* Audio buffers not yet set up */ return; /* Audio buffers not yet set up */
@ -672,10 +714,12 @@ void voice_init(void)
while (!voice_codec_loaded) while (!voice_codec_loaded)
yield(); yield();
#endif
} /* voice_init */ } /* voice_init */
void voice_stop(void) void voice_stop(void)
{ {
#ifdef PLAYBACK_VOICE
/* Messages should not be posted to voice codec queue unless it is the /* Messages should not be posted to voice codec queue unless it is the
current codec or deadlocks happen. This will be addressed globally soon. current codec or deadlocks happen. This will be addressed globally soon.
-- jhMikeS */ -- jhMikeS */
@ -683,14 +727,14 @@ void voice_stop(void)
return; return;
mp3_play_stop(); mp3_play_stop();
while (voice_is_playing && !queue_empty(&voice_queue)) #endif
yield();
} /* voice_stop */ } /* voice_stop */
/* --- Routines called from multiple threads --- */ /* --- Routines called from multiple threads --- */
#ifdef PLAYBACK_VOICE
static void swap_codec(void) static void swap_codec(void)
{ {
int my_codec = current_codec; int my_codec = current_codec;
@ -698,31 +742,34 @@ static void swap_codec(void)
logf("swapping out codec:%d", my_codec); logf("swapping out codec:%d", my_codec);
/* Save our current IRAM and DRAM */ /* Save our current IRAM and DRAM */
#ifndef SIMULATOR
memcpy(iram_buf[my_codec], (unsigned char *)CODEC_IRAM_ORIGIN, memcpy(iram_buf[my_codec], (unsigned char *)CODEC_IRAM_ORIGIN,
CODEC_IRAM_SIZE); CODEC_IRAM_SIZE);
#endif
memcpy(dram_buf[my_codec], codecbuf, CODEC_SIZE); memcpy(dram_buf[my_codec], codecbuf, CODEC_SIZE);
/* Release my semaphore */
mutex_unlock(&mutex_codecthread);
/* Loop until the other codec has locked and run */
do { do {
/* Release my semaphore and force a task switch. */ /* Release my semaphore and force a task switch. */
mutex_unlock(&mutex_codecthread);
yield(); yield();
mutex_lock(&mutex_codecthread);
/* Loop until the other codec has locked and run */
} while (my_codec == current_codec); } while (my_codec == current_codec);
/* Wait for other codec to unlock */
mutex_lock(&mutex_codecthread);
/* Take control */
current_codec = my_codec; current_codec = my_codec;
/* Reload our IRAM and DRAM */ /* Reload our IRAM and DRAM */
#ifndef SIMULATOR
memcpy((unsigned char *)CODEC_IRAM_ORIGIN, iram_buf[my_codec], memcpy((unsigned char *)CODEC_IRAM_ORIGIN, iram_buf[my_codec],
CODEC_IRAM_SIZE); CODEC_IRAM_SIZE);
#endif
invalidate_icache(); invalidate_icache();
memcpy(codecbuf, dram_buf[my_codec], CODEC_SIZE); memcpy(codecbuf, dram_buf[my_codec], CODEC_SIZE);
logf("resuming codec:%d", my_codec); logf("resuming codec:%d", my_codec);
} }
#endif
static void set_filebuf_watermark(int seconds) static void set_filebuf_watermark(int seconds)
{ {
@ -761,6 +808,8 @@ static const char * get_codec_filename(int enc_spec)
/* --- Voice thread --- */ /* --- Voice thread --- */
#ifdef PLAYBACK_VOICE
#ifdef HAVE_ADJUSTABLE_CPU_FREQ #ifdef HAVE_ADJUSTABLE_CPU_FREQ
static void voice_boost_cpu(bool state) static void voice_boost_cpu(bool state)
{ {
@ -768,8 +817,8 @@ static void voice_boost_cpu(bool state)
if (state != voice_cpu_boosted) if (state != voice_cpu_boosted)
{ {
cpu_boost(state);
voice_cpu_boosted = state; voice_cpu_boosted = state;
cpu_boost(state);
} }
} }
#endif #endif
@ -795,7 +844,7 @@ static bool voice_pcmbuf_insert_split_callback(
while ((dest = pcmbuf_request_voice_buffer(est_output_size, while ((dest = pcmbuf_request_voice_buffer(est_output_size,
&output_size, playing)) == NULL) &output_size, playing)) == NULL)
{ {
if (playing) if (playing && audio_codec_loaded)
swap_codec(); swap_codec();
else else
yield(); yield();
@ -823,7 +872,7 @@ static bool voice_pcmbuf_insert_split_callback(
if (playing) if (playing)
{ {
pcmbuf_mix_voice(output_size); pcmbuf_mix_voice(output_size);
if (pcmbuf_usage() < 10 || pcmbuf_mix_free() < 30) if ((pcmbuf_usage() < 10 || pcmbuf_mix_free() < 30) && audio_codec_loaded)
swap_codec(); swap_codec();
} }
else else
@ -902,13 +951,19 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
case Q_AUDIO_PLAY: case Q_AUDIO_PLAY:
LOGFQUEUE("voice < Q_AUDIO_PLAY"); LOGFQUEUE("voice < Q_AUDIO_PLAY");
if (playing) if (playing)
swap_codec(); {
if (audio_codec_loaded)
swap_codec();
else
yield();
}
break; break;
#if defined(HAVE_RECORDING) && !defined(SIMULATOR) #if defined(HAVE_RECORDING) && !defined(SIMULATOR)
case Q_ENCODER_RECORD: case Q_ENCODER_RECORD:
LOGFQUEUE("voice < Q_ENCODER_RECORD"); LOGFQUEUE("voice < Q_ENCODER_RECORD");
swap_codec(); if (audio_codec_loaded)
swap_codec();
break; break;
#endif #endif
@ -940,6 +995,7 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
case Q_VOICE_PLAY: case Q_VOICE_PLAY:
LOGFQUEUE("voice < Q_VOICE_PLAY"); LOGFQUEUE("voice < Q_VOICE_PLAY");
if (!voice_is_playing)
{ {
struct voice_info *voice_data; struct voice_info *voice_data;
voice_is_playing = true; voice_is_playing = true;
@ -949,6 +1005,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;
case SYS_TIMEOUT: case SYS_TIMEOUT:
LOGFQUEUE("voice < SYS_TIMEOUT"); LOGFQUEUE("voice < SYS_TIMEOUT");
@ -967,7 +1024,7 @@ voice_play_clip:
voice_getmore((unsigned char **)&voicebuf, (int *)&voice_remaining); voice_getmore((unsigned char **)&voicebuf, (int *)&voice_remaining);
/* If this clip is done */ /* If this clip is done */
if (!voice_remaining) if (voice_remaining == 0)
{ {
LOGFQUEUE("voice > voice Q_VOICE_STOP"); LOGFQUEUE("voice > voice Q_VOICE_STOP");
queue_post(&voice_queue, Q_VOICE_STOP, 0); queue_post(&voice_queue, Q_VOICE_STOP, 0);
@ -1044,6 +1101,7 @@ static void voice_thread(void)
} }
} /* voice_thread */ } /* voice_thread */
#endif /* PLAYBACK_VOICE */
/* --- Codec thread --- */ /* --- Codec thread --- */
@ -1097,11 +1155,15 @@ static bool codec_pcmbuf_insert_split_callback(
pcmbuf_write_complete(output_size); pcmbuf_write_complete(output_size);
if (voice_is_playing && pcm_is_playing() && #ifdef PLAYBACK_VOICE
pcmbuf_usage() > 30 && pcmbuf_mix_free() > 80) if ((voice_is_playing || voice_thread_start)
&& pcm_is_playing() && voice_codec_loaded &&
pcmbuf_usage() > 30 && pcmbuf_mix_free() > 80)
{ {
voice_thread_start = false;
swap_codec(); swap_codec();
} }
#endif
length -= input_size; length -= input_size;
} }
@ -1684,11 +1746,13 @@ static void codec_thread(void)
case Q_CODEC_LOAD_DISK: case Q_CODEC_LOAD_DISK:
LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
audio_codec_loaded = true; audio_codec_loaded = true;
#ifdef PLAYBACK_VOICE
if (voice_codec_loaded) if (voice_codec_loaded)
{ {
LOGFQUEUE("codec > voice Q_AUDIO_PLAY"); LOGFQUEUE("codec > voice Q_AUDIO_PLAY");
queue_post(&voice_queue, Q_AUDIO_PLAY, 0); queue_post(&voice_queue, Q_AUDIO_PLAY, 0);
} }
#endif
mutex_lock(&mutex_codecthread); mutex_lock(&mutex_codecthread);
current_codec = CODEC_IDX_AUDIO; current_codec = CODEC_IDX_AUDIO;
ci.stop_codec = false; ci.stop_codec = false;
@ -1711,11 +1775,13 @@ static void codec_thread(void)
} }
audio_codec_loaded = true; audio_codec_loaded = true;
#ifdef PLAYBACK_VOICE
if (voice_codec_loaded) if (voice_codec_loaded)
{ {
LOGFQUEUE("codec > voice Q_AUDIO_PLAY"); LOGFQUEUE("codec > voice Q_AUDIO_PLAY");
queue_post(&voice_queue, Q_AUDIO_PLAY, 0); queue_post(&voice_queue, Q_AUDIO_PLAY, 0);
} }
#endif
mutex_lock(&mutex_codecthread); mutex_lock(&mutex_codecthread);
current_codec = CODEC_IDX_AUDIO; current_codec = CODEC_IDX_AUDIO;
ci.stop_codec = false; ci.stop_codec = false;
@ -1729,11 +1795,13 @@ static void codec_thread(void)
case Q_ENCODER_LOAD_DISK: case Q_ENCODER_LOAD_DISK:
LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK"); LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
audio_codec_loaded = false; audio_codec_loaded = false;
#ifdef PLAYBACK_VOICE
if (voice_codec_loaded && current_codec == CODEC_IDX_VOICE) if (voice_codec_loaded && current_codec == CODEC_IDX_VOICE)
{ {
LOGFQUEUE("codec > voice Q_ENCODER_RECORD"); LOGFQUEUE("codec > voice Q_ENCODER_RECORD");
queue_post(&voice_queue, Q_ENCODER_RECORD, NULL); queue_post(&voice_queue, Q_ENCODER_RECORD, NULL);
} }
#endif
mutex_lock(&mutex_codecthread); mutex_lock(&mutex_codecthread);
current_codec = CODEC_IDX_AUDIO; current_codec = CODEC_IDX_AUDIO;
ci.stop_codec = false; ci.stop_codec = false;
@ -1747,8 +1815,10 @@ static void codec_thread(void)
LOGFQUEUE("codec < SYS_USB_CONNECTED"); LOGFQUEUE("codec < SYS_USB_CONNECTED");
queue_clear(&codec_queue); queue_clear(&codec_queue);
usb_acknowledge(SYS_USB_CONNECTED_ACK); usb_acknowledge(SYS_USB_CONNECTED_ACK);
if (voice_codec_loaded) #ifdef PLAYBACK_VOICE
if(voice_codec_loaded)
swap_codec(); swap_codec();
#endif
usb_wait_for_disconnect(&codec_queue); usb_wait_for_disconnect(&codec_queue);
break; break;
#endif #endif
@ -2199,7 +2269,6 @@ static bool audio_loadcodec(bool start_play)
ci.id3 = &cur_ti->id3; ci.id3 = &cur_ti->id3;
ci.taginfo_ready = &cur_ti->taginfo_ready; ci.taginfo_ready = &cur_ti->taginfo_ready;
ci.curpos = 0; ci.curpos = 0;
playing = true;
LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK"); LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
queue_post(&codec_queue, Q_CODEC_LOAD_DISK, (void *)codec_fn); queue_post(&codec_queue, Q_CODEC_LOAD_DISK, (void *)codec_fn);
return true; return true;
@ -2261,8 +2330,6 @@ static bool audio_loadcodec(bool start_play)
buf_widx -= filebuflen; buf_widx -= filebuflen;
tracks[track_widx].codecsize += rc; tracks[track_widx].codecsize += rc;
audio_yield_codecs();
} }
tracks[track_widx].has_codec = true; tracks[track_widx].has_codec = true;
@ -2941,9 +3008,6 @@ static void audio_stop_playback(void)
(playlist_end && ci.stop_codec)?NULL:audio_current_track()); (playlist_end && ci.stop_codec)?NULL:audio_current_track());
} }
while (voice_is_playing && !queue_empty(&voice_queue))
yield();
filebufused = 0; filebufused = 0;
playing = false; playing = false;
filling = false; filling = false;
@ -2968,8 +3032,7 @@ static void audio_play_start(size_t offset)
#endif #endif
/* Wait for any previously playing audio to flush - TODO: Not necessary? */ /* Wait for any previously playing audio to flush - TODO: Not necessary? */
while (audio_codec_loaded) audio_stop_codec_flush();
audio_stop_codec_flush();
track_changed = true; track_changed = true;
playlist_end = false; playlist_end = false;
@ -2977,6 +3040,7 @@ static void audio_play_start(size_t offset)
playing = true; playing = true;
ci.new_track = 0; ci.new_track = 0;
ci.seek_time = 0; ci.seek_time = 0;
wps_offset = 0;
if (current_fd >= 0) if (current_fd >= 0)
{ {
@ -3057,15 +3121,9 @@ static void audio_new_playlist(void)
static void audio_initiate_track_change(long direction) static void audio_initiate_track_change(long direction)
{ {
if (playlist_check(direction)) playlist_end = false;
{ ci.new_track += direction;
playlist_end = false; wps_offset -= direction;
/* Flag track changed immediately so wps can update instantly.
* No need to wait for disk to spin up or message to travel
* through the deep queues as this info is only for the wps. */
track_changed = true;
ci.new_track += direction;
}
} }
static void audio_initiate_dir_change(long direction) static void audio_initiate_dir_change(long direction)
@ -3090,10 +3148,8 @@ static void audio_reset_buffer(void)
/* Allow 2 codecs at end of file buffer */ /* Allow 2 codecs at end of file buffer */
filebuflen -= 2 * (CODEC_IRAM_SIZE + CODEC_SIZE); filebuflen -= 2 * (CODEC_IRAM_SIZE + CODEC_SIZE);
#ifndef SIMULATOR
iram_buf[0] = &filebuf[filebuflen]; iram_buf[0] = &filebuf[filebuflen];
iram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE]; iram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE];
#endif
dram_buf[0] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2]; dram_buf[0] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2];
dram_buf[1] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2+CODEC_SIZE]; dram_buf[1] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2+CODEC_SIZE];
} }
@ -3102,10 +3158,8 @@ static void audio_reset_buffer(void)
/* Allow for 1 codec at end of file buffer */ /* Allow for 1 codec at end of file buffer */
filebuflen -= CODEC_IRAM_SIZE + CODEC_SIZE; filebuflen -= CODEC_IRAM_SIZE + CODEC_SIZE;
#ifndef SIMULATOR
iram_buf[0] = &filebuf[filebuflen]; iram_buf[0] = &filebuf[filebuflen];
iram_buf[1] = NULL; iram_buf[1] = NULL;
#endif
dram_buf[0] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE]; dram_buf[0] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE];
dram_buf[1] = NULL; dram_buf[1] = NULL;
} }
@ -3129,7 +3183,10 @@ static void audio_test_track_changed_event(struct mp3entry *id3)
static void audio_playback_init(void) static void audio_playback_init(void)
{ {
#ifdef PLAYBACK_VOICE
static bool voicetagtrue = true; static bool voicetagtrue = true;
static struct mp3entry id3_voice;
#endif
struct event ev; struct event ev;
logf("playback api init"); logf("playback api init");
@ -3162,6 +3219,7 @@ static void audio_playback_init(void)
ci.discard_codec = codec_discard_codec_callback; ci.discard_codec = codec_discard_codec_callback;
/* Initialize voice codec api. */ /* Initialize voice codec api. */
#ifdef PLAYBACK_VOICE
memcpy(&ci_voice, &ci, sizeof(struct codec_api)); memcpy(&ci_voice, &ci, sizeof(struct codec_api));
memset(&id3_voice, 0, sizeof(struct mp3entry)); memset(&id3_voice, 0, sizeof(struct mp3entry));
ci_voice.read_filebuf = voice_filebuf_callback; ci_voice.read_filebuf = voice_filebuf_callback;
@ -3182,6 +3240,7 @@ static void audio_playback_init(void)
ci_voice.id3 = &id3_voice; ci_voice.id3 = &id3_voice;
id3_voice.frequency = 11200; id3_voice.frequency = 11200;
id3_voice.length = 1000000L; id3_voice.length = 1000000L;
#endif
codec_thread_p = create_thread(codec_thread, codec_stack, codec_thread_p = create_thread(codec_thread, codec_stack,
sizeof(codec_stack), sizeof(codec_stack),
@ -3288,8 +3347,6 @@ static void audio_thread(void)
case Q_AUDIO_DIR_SKIP: case Q_AUDIO_DIR_SKIP:
LOGFQUEUE("audio < Q_AUDIO_DIR_SKIP"); LOGFQUEUE("audio < Q_AUDIO_DIR_SKIP");
playlist_end = false; playlist_end = false;
if (global_settings.beep)
pcmbuf_beep(5000, 100, 2500*global_settings.beep);
audio_initiate_dir_change((long)ev.data); audio_initiate_dir_change((long)ev.data);
break; break;