summaryrefslogtreecommitdiff
path: root/apps/playback.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/playback.c')
-rw-r--r--apps/playback.c262
1 files changed, 197 insertions, 65 deletions
diff --git a/apps/playback.c b/apps/playback.c
index f837266..af6b573 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -54,8 +54,6 @@
#include "playlist.h"
#include "playback.h"
#include "pcmbuf.h"
-#include "pcm_playback.h"
-#include "pcm_record.h"
#include "buffer.h"
#include "dsp.h"
#include "abrepeat.h"
@@ -78,6 +76,7 @@
#ifdef HAVE_RECORDING
#include "recording.h"
+#include "talk.h"
#endif
#define PLAYBACK_VOICE
@@ -93,9 +92,13 @@
* for their correct seeek target, 32k seems a good size */
#define AUDIO_REBUFFER_GUESS_SIZE (1024*32)
-/* macros to enable logf for queues */
+/* macros to enable logf for queues
+ logging on SYS_TIMEOUT can be disabled */
#ifdef SIMULATOR
-#define PLAYBACK_LOGQUEUES /* Define this for logf output of all queuing */
+/* Define this for logf output of all queuing except SYS_TIMEOUT */
+#define PLAYBACK_LOGQUEUES
+/* Define this to logf SYS_TIMEOUT messages */
+#define PLAYBACK_LOGQUEUES_SYS_TIMEOUT
#endif
#ifdef PLAYBACK_LOGQUEUES
@@ -104,6 +107,18 @@
#define LOGFQUEUE(s)
#endif
+#ifdef PLAYBACK_LOGQUEUES_SYS_TIMEOUT
+#define LOGFQUEUE_SYS_TIMEOUT(s) logf("%s", s)
+#else
+#define LOGFQUEUE_SYS_TIMEOUT(s)
+#endif
+
+
+/* Define one constant that includes recording related functionality */
+#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
+#define AUDIO_HAVE_RECORDING
+#endif
+
enum {
Q_AUDIO_PLAY = 1,
Q_AUDIO_STOP,
@@ -122,6 +137,9 @@ enum {
#if MEM > 8
Q_AUDIO_FILL_BUFFER_IF_ACTIVE_ATA,
#endif
+#ifdef AUDIO_HAVE_RECORDING
+ Q_AUDIO_LOAD_ENCODER,
+#endif
Q_CODEC_REQUEST_PENDING,
Q_CODEC_REQUEST_COMPLETE,
@@ -133,7 +151,7 @@ enum {
Q_CODEC_LOAD,
Q_CODEC_LOAD_DISK,
-#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
+#ifdef AUDIO_HAVE_RECORDING
Q_ENCODER_LOAD_DISK,
Q_ENCODER_RECORD,
#endif
@@ -178,11 +196,16 @@ static volatile bool paused; /* Is audio paused? (A/C-) */
static volatile bool filling IDATA_ATTR; /* Is file buffer currently being refilled? (A/C-) */
/* Ring buffer where tracks and codecs are loaded */
-static char *filebuf; /* Pointer to start of ring buffer (A/C-) */
+static unsigned char *filebuf; /* Pointer to start of ring buffer (A/C-) */
size_t filebuflen; /* Total size of the ring buffer FIXME: make static (A/C-)*/
static volatile size_t buf_ridx IDATA_ATTR; /* Ring buffer read position (A/C) FIXME? should be (C/A-) */
static volatile size_t buf_widx IDATA_ATTR; /* Ring buffer read position (A/C-) */
+#define BUFFER_STATE_TRASHED -1 /* Buffer is in a trashed state and must be reset */
+#define BUFFER_STATE_NORMAL 0 /* Buffer is arranged for voice and audio */
+#define BUFFER_STATE_VOICED_ONLY 1 /* Buffer is arranged for voice-only use */
+static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */
+
#define RINGBUF_ADD(p,v) ((p+v)<filebuflen ? p+v : p+v-filebuflen)
#define RINGBUF_SUB(p,v) ((p>=v) ? p-v : p+filebuflen-v)
#define RINGBUF_ADD_CROSS(p1,v,p2) ((p1<p2)?(int)(p1+v)-(int)p2:(int)(p1+v-p2)-(int)filebuflen)
@@ -235,7 +258,7 @@ static const char audio_thread_name[] = "audio";
static void audio_thread(void);
static void audio_initiate_track_change(long direction);
static bool audio_have_tracks(void);
-static void audio_reset_buffer(void);
+static void audio_reset_buffer(size_t pcmbufsize);
/* Codec thread */
extern struct codec_api ci;
@@ -294,6 +317,10 @@ static void voice_thread(void);
void mp3_play_data(const unsigned char* start, int size,
void (*get_more)(unsigned char** start, int* size))
{
+ /* must reset the buffer before any playback begins if needed */
+ if (buffer_state == BUFFER_STATE_TRASHED)
+ audio_reset_buffer(pcmbuf_get_bufsize());
+
#ifdef PLAYBACK_VOICE
static struct voice_info voice_clip;
voice_clip.callback = get_more;
@@ -330,38 +357,95 @@ void mpeg_id3_options(bool _v1first)
v1first = _v1first;
}
-void audio_load_encoder(int enc_id)
+unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
{
-#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
- const char *enc_fn = get_codec_filename(enc_id | CODEC_TYPE_ENCODER);
+ unsigned char *buf = audiobuf;
+ unsigned char *end = audiobufend;
+
+ audio_stop();
+
+ if (talk_buf || !talk_voice_required()
+ || buffer_state == BUFFER_STATE_TRASHED)
+ {
+ logf("get buffer: talk_buf");
+ /* ok to use everything from audiobuf to audiobufend */
+ if (buffer_state != BUFFER_STATE_TRASHED)
+ talk_buffer_steal();
+ buffer_state = BUFFER_STATE_TRASHED;
+ }
+ else
+ {
+ /* skip talk buffer and move pcm buffer to end */
+ logf("get buffer: voice");
+ mp3_play_stop();
+ buf += talk_get_bufsize();
+ end -= pcmbuf_init(pcmbuf_get_bufsize(), audiobufend);
+ buffer_state = BUFFER_STATE_VOICED_ONLY;
+ }
+
+ *buffer_size = end - buf;
+
+ return buf;
+}
+
+#ifdef HAVE_RECORDING
+unsigned char *audio_get_recording_buffer(size_t *buffer_size)
+{
+ /* don't allow overwrite of voice swap area or we'll trash the
+ swapped-out voice codec but can use whole thing if none */
+ unsigned char *end = iram_buf[CODEC_IDX_VOICE] ?
+ iram_buf[CODEC_IDX_VOICE] : audiobufend;
+
+ audio_stop();
+ talk_buffer_steal();
+
+ buffer_state = BUFFER_STATE_TRASHED;
+
+ *buffer_size = end - audiobuf;
+
+ return (unsigned char *)audiobuf;
+}
+
+bool audio_load_encoder(int afmt)
+{
+#ifndef SIMULATOR
+ const char *enc_fn = get_codec_filename(afmt | CODEC_TYPE_ENCODER);
if (!enc_fn)
- return;
+ return false;
audio_remove_encoder();
+ ci.enc_codec_loaded = 0; /* clear any previous error condition */
- LOGFQUEUE("audio > codec Q_ENCODER_LOAD_DISK");
- queue_post(&codec_queue, Q_ENCODER_LOAD_DISK, (void *)enc_fn);
+ LOGFQUEUE("audio > Q_AUDIO_LOAD_ENCODER");
+ queue_post(&audio_queue, Q_AUDIO_LOAD_ENCODER, (void *)enc_fn);
- while (!ci.enc_codec_loaded)
+ while (ci.enc_codec_loaded == 0)
yield();
+
+ logf("codec loaded: %d", ci.enc_codec_loaded);
+
+ return ci.enc_codec_loaded > 0;
+#else
+ (void)afmt;
+ return true;
#endif
- return;
- (void)enc_id;
} /* audio_load_encoder */
void audio_remove_encoder(void)
{
-#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
- /* force encoder codec unload (if previously loaded) */
- if (!ci.enc_codec_loaded)
+#ifndef SIMULATOR
+ /* force encoder codec unload (if currently loaded) */
+ if (ci.enc_codec_loaded <= 0)
return;
ci.stop_codec = true;
- while (ci.enc_codec_loaded)
+ while (ci.enc_codec_loaded > 0)
yield();
#endif
} /* audio_remove_encoder */
+#endif /* HAVE_RECORDING */
+
struct mp3entry* audio_current_track(void)
{
const char *filename;
@@ -553,6 +637,9 @@ void audio_flush_and_reload_tracks(void)
void audio_error_clear(void)
{
+#ifdef AUDIO_HAVE_RECORDING
+ pcm_rec_error_clear();
+#endif
}
int audio_status(void)
@@ -573,11 +660,6 @@ int audio_status(void)
return ret;
}
-bool audio_query_poweroff(void)
-{
- return !(playing && paused);
-}
-
int audio_get_file_pos(void)
{
return 0;
@@ -617,7 +699,7 @@ void audio_set_crossfade(int enable)
enable = 0;
size = NATIVE_FREQUENCY*2;
#endif
- if (pcmbuf_get_bufsize() == size)
+ if (buffer_state == BUFFER_STATE_NORMAL && pcmbuf_is_same_size(size))
return ;
if (was_playing)
@@ -633,9 +715,8 @@ void audio_set_crossfade(int enable)
voice_stop();
/* Re-initialize audio system. */
- pcmbuf_init(size);
+ audio_reset_buffer(size);
pcmbuf_crossfade_enable(enable);
- audio_reset_buffer();
logf("abuf:%dB", pcmbuf_get_bufsize());
logf("fbuf:%dB", filebuflen);
@@ -714,8 +795,7 @@ 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.
- -- jhMikeS */
+ current codec or deadlocks happen. */
if (current_codec != CODEC_IDX_VOICE)
return;
@@ -784,21 +864,32 @@ static void set_filebuf_watermark(int seconds)
conf_watermark = bytes;
}
-static const char * get_codec_filename(int enc_spec)
+static const char * get_codec_filename(int cod_spec)
{
const char *fname;
- int type = enc_spec & CODEC_TYPE_MASK;
- int afmt = enc_spec & CODEC_AFMT_MASK;
+
+#ifdef HAVE_RECORDING
+ /* Can choose decoder or encoder if one available */
+ int type = cod_spec & CODEC_TYPE_MASK;
+ int afmt = cod_spec & CODEC_AFMT_MASK;
if ((unsigned)afmt >= AFMT_NUM_CODECS)
type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK);
- fname = (type == CODEC_TYPE_DECODER) ?
- audio_formats[afmt].codec_fn : audio_formats[afmt].codec_enc_fn;
+ fname = (type == CODEC_TYPE_ENCODER) ?
+ audio_formats[afmt].codec_enc_root_fn :
+ audio_formats[afmt].codec_root_fn;
logf("%s: %d - %s",
(type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder",
afmt, fname ? fname : "<unknown>");
+#else /* !HAVE_RECORDING */
+ /* Always decoder */
+ if ((unsigned)cod_spec >= AFMT_NUM_CODECS)
+ cod_spec = AFMT_UNKNOWN;
+ fname = audio_formats[cod_spec].codec_root_fn;
+ logf("Codec: %d - %s", cod_spec, fname ? fname : "<unknown>");
+#endif /* HAVE_RECORDING */
return fname;
} /* get_codec_filename */
@@ -940,7 +1031,7 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
}
break;
-#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
+#ifdef AUDIO_HAVE_RECORDING
case Q_ENCODER_RECORD:
LOGFQUEUE("voice < Q_ENCODER_RECORD");
swap_codec();
@@ -995,7 +1086,7 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
goto voice_play_clip;
case SYS_TIMEOUT:
- LOGFQUEUE("voice < SYS_TIMEOUT");
+ LOGFQUEUE_SYS_TIMEOUT("voice < SYS_TIMEOUT");
goto voice_play_clip;
default:
@@ -1773,7 +1864,7 @@ static void codec_thread(void)
#endif
break ;
-#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
+#ifdef AUDIO_HAVE_RECORDING
case Q_ENCODER_LOAD_DISK:
LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
audio_codec_loaded = false; /* Not audio codec! */
@@ -1785,12 +1876,14 @@ static void codec_thread(void)
}
#endif
mutex_lock(&mutex_codecthread);
+ logf("loading encoder");
current_codec = CODEC_IDX_AUDIO;
ci.stop_codec = false;
status = codec_load_file((const char *)ev.data, &ci);
mutex_unlock(&mutex_codecthread);
+ logf("encoder stopped");
break;
-#endif
+#endif /* AUDIO_HAVE_RECORDING */
#ifndef SIMULATOR
case SYS_USB_CONNECTED:
@@ -1872,6 +1965,24 @@ static void codec_thread(void)
}
break;
+#ifdef AUDIO_HAVE_RECORDING
+ case Q_ENCODER_LOAD_DISK:
+ LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
+
+ if (status == CODEC_OK)
+ break;
+
+ logf("Encoder failure");
+ gui_syncsplash(HZ*2, true, "Encoder failure");
+
+ if (ci.enc_codec_loaded < 0)
+ break;
+
+ logf("Encoder failed to load");
+ ci.enc_codec_loaded = -1;
+ break;
+#endif /* AUDIO_HAVE_RECORDING */
+
default:
LOGFQUEUE("codec < default");
@@ -2992,6 +3103,10 @@ static void audio_play_start(size_t offset)
/* Wait for any previously playing audio to flush - TODO: Not necessary? */
audio_stop_codec_flush();
+ /* must reset the buffer before any playback begins if needed */
+ if (buffer_state != BUFFER_STATE_NORMAL)
+ audio_reset_buffer(pcmbuf_get_bufsize());
+
track_changed = true;
playlist_end = false;
@@ -3084,51 +3199,60 @@ static void audio_initiate_dir_change(long direction)
ci.new_track = direction;
}
-static void audio_reset_buffer(void)
+/*
+ * Layout audio buffer as follows:
+ * [|TALK]|MALLOC|FILE|GUARD|PCM|AUDIOCODEC|[VOICECODEC|]
+ */
+static void audio_reset_buffer(size_t pcmbufsize)
{
+ /* see audio_get_recording_buffer if this is modified */
size_t offset;
- /* Set up file buffer as all space available */
- filebuf = (char *)&audiobuf[talk_get_bufsize()+MALLOC_BUFSIZE];
- filebuflen = audiobufend - (unsigned char *) filebuf - GUARD_BUFSIZE -
- (pcmbuf_get_bufsize() + get_pcmbuf_descsize() + PCMBUF_MIX_CHUNK * 2);
+ logf("audio_reset_buffer");
+ logf(" size:%08X", pcmbufsize);
+
+ /* Initially set up file buffer as all space available */
+ filebuf = audiobuf + MALLOC_BUFSIZE + talk_get_bufsize();
+ filebuflen = audiobufend - filebuf;
- /* Allow for codec(s) at end of file buffer */
+ /* Allow for codec(s) at end of audio buffer */
if (talk_voice_required())
{
- /* Allow 2 codecs at end of file buffer */
+#ifdef PLAYBACK_VOICE
+ /* Allow 2 codecs at end of audio buffer */
filebuflen -= 2 * (CODEC_IRAM_SIZE + CODEC_SIZE);
-#ifdef PLAYBACK_VOICE
- iram_buf[0] = &filebuf[filebuflen];
- iram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE];
- dram_buf[0] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2];
- dram_buf[1] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE*2+CODEC_SIZE];
+ 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] = dram_buf[CODEC_IDX_AUDIO] + CODEC_SIZE;
+ dram_buf[CODEC_IDX_VOICE] = iram_buf[CODEC_IDX_VOICE] + CODEC_IRAM_SIZE;
#endif
}
else
{
- /* Allow for 1 codec at end of file buffer */
+#ifdef PLAYBACK_VOICE
+ /* Allow for 1 codec at end of audio buffer */
filebuflen -= CODEC_IRAM_SIZE + CODEC_SIZE;
-#ifdef PLAYBACK_VOICE
- iram_buf[0] = &filebuf[filebuflen];
- iram_buf[1] = NULL;
- dram_buf[0] = (unsigned char *)&filebuf[filebuflen+CODEC_IRAM_SIZE];
- dram_buf[1] = 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
}
+ filebuflen -= pcmbuf_init(pcmbufsize, filebuf + filebuflen) + GUARD_BUFSIZE;
+
/* Ensure that file buffer is aligned */
- offset = (-(size_t)filebuf) & 3;
+ offset = -(size_t)filebuf & 3;
filebuf += offset;
filebuflen -= offset;
filebuflen &= ~3;
/* Clear any references to the file buffer */
+ buffer_state = BUFFER_STATE_NORMAL;
}
-
#ifdef ROCKBOX_HAS_LOGF
static void audio_test_track_changed_event(struct mp3entry *id3)
{
@@ -3149,9 +3273,8 @@ static void audio_playback_init(void)
logf("playback api init");
pcm_init();
-#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
- /* Set the input multiplexer to Line In */
- pcm_rec_mux(0);
+#ifdef AUDIO_HAVE_RECORDING
+ rec_set_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
#endif
#ifdef ROCKBOX_HAS_LOGF
@@ -3219,8 +3342,9 @@ static void audio_playback_init(void)
#endif
}
- filebuf = (char *)&audiobuf[MALLOC_BUFSIZE]; /* Will be reset by reset_buffer */
-
+ /* initialize the buffer */
+ filebuf = audiobuf; /* must be non-NULL for audio_set_crossfade */
+ buffer_state = BUFFER_STATE_TRASHED; /* force it */
audio_set_crossfade(global_settings.crossfade);
audio_is_initialized = true;
@@ -3358,6 +3482,14 @@ static void audio_thread(void)
playlist_update_resume_info(audio_current_track());
break ;
+#ifdef AUDIO_HAVE_RECORDING
+ case Q_AUDIO_LOAD_ENCODER:
+ LOGFQUEUE("audio < Q_AUDIO_LOAD_ENCODER");
+ LOGFQUEUE("audio > codec Q_ENCODER_LOAD_DISK");
+ queue_post(&codec_queue, Q_ENCODER_LOAD_DISK, ev.data);
+ break;
+#endif
+
#ifndef SIMULATOR
case SYS_USB_CONNECTED:
LOGFQUEUE("audio < SYS_USB_CONNECTED");
@@ -3368,7 +3500,7 @@ static void audio_thread(void)
#endif
case SYS_TIMEOUT:
- LOGFQUEUE("audio < SYS_TIMEOUT");
+ LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT");
break;
default: