pcmbuf: refactoring
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23592 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
8c15799e70
commit
874c911464
1 changed files with 121 additions and 132 deletions
253
apps/pcmbuf.c
253
apps/pcmbuf.c
|
@ -68,10 +68,8 @@ struct chunkdesc
|
||||||
bool end_of_track;
|
bool end_of_track;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CHUNK_DESCS(bufsize) \
|
#define NUM_CHUNK_DESCS(bufsize) \
|
||||||
((bufsize) / PCMBUF_MINAVG_CHUNK)
|
((bufsize) / PCMBUF_MINAVG_CHUNK)
|
||||||
#define CHUNK_DESCS_SIZE(bufsize) \
|
|
||||||
(CHUNK_DESCS(bufsize)*sizeof(struct chunkdesc))
|
|
||||||
|
|
||||||
/* 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;
|
||||||
|
@ -132,7 +130,6 @@ extern unsigned int codec_thread_id;
|
||||||
#define LOW_DATA(quarter_secs) \
|
#define LOW_DATA(quarter_secs) \
|
||||||
(pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs)
|
(pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs)
|
||||||
|
|
||||||
static void finish_gapless_track_change(void);
|
|
||||||
#ifdef HAVE_CROSSFADE
|
#ifdef HAVE_CROSSFADE
|
||||||
static void crossfade_start(void);
|
static void crossfade_start(void);
|
||||||
static void flush_crossfade(char *buf, size_t length);
|
static void flush_crossfade(char *buf, size_t length);
|
||||||
|
@ -432,22 +429,17 @@ static size_t get_next_required_pcmbuf_size(void)
|
||||||
return seconds * (NATIVE_FREQUENCY*4); /* 2 channels + 2 bytes/sample */
|
return seconds * (NATIVE_FREQUENCY*4); /* 2 channels + 2 bytes/sample */
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *pcmbuf_calc_pcmbuffer_ptr(size_t bufsize)
|
|
||||||
{
|
|
||||||
return pcmbuf_bufend - (bufsize + PCMBUF_MIX_CHUNK * 2 +
|
|
||||||
CHUNK_DESCS_SIZE(bufsize));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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(unsigned char *bufend)
|
size_t pcmbuf_init(unsigned char *bufend)
|
||||||
{
|
{
|
||||||
pcmbuf_bufend = bufend;
|
pcmbuf_bufend = bufend;
|
||||||
pcmbuf_size = get_next_required_pcmbuf_size();
|
pcmbuf_size = get_next_required_pcmbuf_size();
|
||||||
pcmbuffer = pcmbuf_calc_pcmbuffer_ptr(pcmbuf_size);
|
write_chunk = (struct chunkdesc *)pcmbuf_bufend -
|
||||||
fadebuf = &pcmbuffer[pcmbuf_size];
|
NUM_CHUNK_DESCS(pcmbuf_size);
|
||||||
voicebuf = &fadebuf[PCMBUF_MIX_CHUNK];
|
voicebuf = (char *)write_chunk - PCMBUF_MIX_CHUNK;
|
||||||
write_chunk = (struct chunkdesc *)&voicebuf[PCMBUF_MIX_CHUNK];
|
fadebuf = voicebuf - PCMBUF_MIX_CHUNK;
|
||||||
|
pcmbuffer = fadebuf - pcmbuf_size;
|
||||||
|
|
||||||
init_pcmbuffers();
|
init_pcmbuffers();
|
||||||
|
|
||||||
|
@ -463,123 +455,6 @@ size_t pcmbuf_init(unsigned char *bufend)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Playback */
|
|
||||||
|
|
||||||
/** PCM driver callback
|
|
||||||
* This function has 3 major logical parts (separated by brackets both for
|
|
||||||
* readability and variable scoping). The first part performs the
|
|
||||||
* operations related to finishing off the last buffer we fed to the DMA.
|
|
||||||
* The second part detects the end of playlist condition when the pcm
|
|
||||||
* buffer is empty except for uncommitted samples. Then they are committed.
|
|
||||||
* The third part performs the operations involved in sending a new buffer
|
|
||||||
* to the DMA. */
|
|
||||||
static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) ICODE_ATTR;
|
|
||||||
static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
struct chunkdesc *pcmbuf_current = read_chunk;
|
|
||||||
/* Take the finished buffer out of circulation */
|
|
||||||
read_chunk = pcmbuf_current->link;
|
|
||||||
|
|
||||||
/* if during a track transition, update the elapsed time */
|
|
||||||
if (track_transition)
|
|
||||||
audio_pcmbuf_position_callback(last_chunksize);
|
|
||||||
|
|
||||||
/* if last buffer in the track, let the audio thread know */
|
|
||||||
if (pcmbuf_current->end_of_track)
|
|
||||||
finish_gapless_track_change();
|
|
||||||
|
|
||||||
/* Put the finished buffer back into circulation */
|
|
||||||
write_end_chunk->link = pcmbuf_current;
|
|
||||||
write_end_chunk = pcmbuf_current;
|
|
||||||
|
|
||||||
/* If we've read over the mix chunk while it's still mixing there */
|
|
||||||
if (pcmbuf_current == mix_chunk)
|
|
||||||
mix_chunk = NULL;
|
|
||||||
/* If we've read over the crossfade chunk while it's still fading */
|
|
||||||
if (pcmbuf_current == crossfade_chunk)
|
|
||||||
crossfade_chunk = read_chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
/* Commit last samples at end of playlist */
|
|
||||||
if (pcmbuffer_fillpos && !read_chunk)
|
|
||||||
{
|
|
||||||
logf("pcmbuf_pcm_callback: commit last samples");
|
|
||||||
commit_chunk();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
/* Send the new buffer to the pcm */
|
|
||||||
struct chunkdesc *pcmbuf_new = read_chunk;
|
|
||||||
size_t *realsize = size;
|
|
||||||
unsigned char** realstart = start;
|
|
||||||
if(pcmbuf_new)
|
|
||||||
{
|
|
||||||
size_t current_size = pcmbuf_new->size;
|
|
||||||
|
|
||||||
pcmbuf_unplayed_bytes -= current_size;
|
|
||||||
last_chunksize = current_size;
|
|
||||||
*realsize = current_size;
|
|
||||||
*realstart = pcmbuf_new->addr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* No more buffers */
|
|
||||||
last_chunksize = 0;
|
|
||||||
*realsize = 0;
|
|
||||||
*realstart = NULL;
|
|
||||||
if (end_of_track)
|
|
||||||
finish_gapless_track_change();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DISPLAY_DESC("callback");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Force playback. */
|
|
||||||
void pcmbuf_play_start(void)
|
|
||||||
{
|
|
||||||
if (!pcm_is_playing() && pcmbuf_unplayed_bytes && read_chunk != NULL)
|
|
||||||
{
|
|
||||||
last_chunksize = read_chunk->size;
|
|
||||||
pcmbuf_unplayed_bytes -= last_chunksize;
|
|
||||||
pcm_play_data(pcmbuf_pcm_callback,
|
|
||||||
(unsigned char *)read_chunk->addr, last_chunksize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void pcmbuf_play_stop(void)
|
|
||||||
{
|
|
||||||
pcm_play_stop();
|
|
||||||
|
|
||||||
pcmbuf_unplayed_bytes = 0;
|
|
||||||
mix_chunk = NULL;
|
|
||||||
if (read_chunk) {
|
|
||||||
write_end_chunk->link = read_chunk;
|
|
||||||
write_end_chunk = read_end_chunk;
|
|
||||||
read_chunk = read_end_chunk = NULL;
|
|
||||||
}
|
|
||||||
pcmbuffer_pos = 0;
|
|
||||||
pcmbuffer_fillpos = 0;
|
|
||||||
crossfade_track_change_started = false;
|
|
||||||
crossfade_active = false;
|
|
||||||
flush_pcmbuf = false;
|
|
||||||
DISPLAY_DESC("play_stop");
|
|
||||||
|
|
||||||
/* Can unboost the codec thread here no matter who's calling */
|
|
||||||
boost_codec_thread(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pcmbuf_pause(bool pause)
|
|
||||||
{
|
|
||||||
if (pcm_is_playing())
|
|
||||||
pcm_play_pause(!pause);
|
|
||||||
else if (!pause)
|
|
||||||
pcmbuf_play_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Track change */
|
/* Track change */
|
||||||
|
|
||||||
/* The codec is moving on to the next track, but the current track is
|
/* The codec is moving on to the next track, but the current track is
|
||||||
|
@ -665,6 +540,120 @@ static void finish_gapless_track_change(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Playback */
|
||||||
|
|
||||||
|
/** PCM driver callback
|
||||||
|
* This function has 3 major logical parts (separated by brackets both for
|
||||||
|
* readability and variable scoping). The first part performs the
|
||||||
|
* operations related to finishing off the last buffer we fed to the DMA.
|
||||||
|
* The second part detects the end of playlist condition when the pcm
|
||||||
|
* buffer is empty except for uncommitted samples. Then they are committed.
|
||||||
|
* The third part performs the operations involved in sending a new buffer
|
||||||
|
* to the DMA. */
|
||||||
|
static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) ICODE_ATTR;
|
||||||
|
static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
struct chunkdesc *pcmbuf_current = read_chunk;
|
||||||
|
/* Take the finished buffer out of circulation */
|
||||||
|
read_chunk = pcmbuf_current->link;
|
||||||
|
|
||||||
|
/* if during a track transition, update the elapsed time */
|
||||||
|
if (track_transition)
|
||||||
|
audio_pcmbuf_position_callback(last_chunksize);
|
||||||
|
|
||||||
|
/* if last buffer in the track, let the audio thread know */
|
||||||
|
if (pcmbuf_current->end_of_track)
|
||||||
|
finish_gapless_track_change();
|
||||||
|
|
||||||
|
/* Put the finished buffer back into circulation */
|
||||||
|
write_end_chunk->link = pcmbuf_current;
|
||||||
|
write_end_chunk = pcmbuf_current;
|
||||||
|
|
||||||
|
/* If we've read over the mix chunk while it's still mixing there */
|
||||||
|
if (pcmbuf_current == mix_chunk)
|
||||||
|
mix_chunk = NULL;
|
||||||
|
/* If we've read over the crossfade chunk while it's still fading */
|
||||||
|
if (pcmbuf_current == crossfade_chunk)
|
||||||
|
crossfade_chunk = read_chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
/* Commit last samples at end of playlist */
|
||||||
|
if (pcmbuffer_fillpos && !read_chunk)
|
||||||
|
{
|
||||||
|
logf("pcmbuf_pcm_callback: commit last samples");
|
||||||
|
commit_chunk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
/* Send the new buffer to the pcm */
|
||||||
|
if(read_chunk)
|
||||||
|
{
|
||||||
|
size_t current_size = read_chunk->size;
|
||||||
|
|
||||||
|
pcmbuf_unplayed_bytes -= current_size;
|
||||||
|
last_chunksize = current_size;
|
||||||
|
*size = current_size;
|
||||||
|
*start = read_chunk->addr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* No more buffers */
|
||||||
|
last_chunksize = 0;
|
||||||
|
*size = 0;
|
||||||
|
*start = NULL;
|
||||||
|
if (end_of_track)
|
||||||
|
finish_gapless_track_change();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DISPLAY_DESC("callback");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force playback. */
|
||||||
|
void pcmbuf_play_start(void)
|
||||||
|
{
|
||||||
|
if (!pcm_is_playing() && pcmbuf_unplayed_bytes && read_chunk != NULL)
|
||||||
|
{
|
||||||
|
last_chunksize = read_chunk->size;
|
||||||
|
pcmbuf_unplayed_bytes -= last_chunksize;
|
||||||
|
pcm_play_data(pcmbuf_pcm_callback,
|
||||||
|
(unsigned char *)read_chunk->addr, last_chunksize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcmbuf_play_stop(void)
|
||||||
|
{
|
||||||
|
pcm_play_stop();
|
||||||
|
|
||||||
|
pcmbuf_unplayed_bytes = 0;
|
||||||
|
mix_chunk = NULL;
|
||||||
|
if (read_chunk) {
|
||||||
|
write_end_chunk->link = read_chunk;
|
||||||
|
write_end_chunk = read_end_chunk;
|
||||||
|
read_chunk = read_end_chunk = NULL;
|
||||||
|
}
|
||||||
|
pcmbuffer_pos = 0;
|
||||||
|
pcmbuffer_fillpos = 0;
|
||||||
|
crossfade_track_change_started = false;
|
||||||
|
crossfade_active = false;
|
||||||
|
flush_pcmbuf = false;
|
||||||
|
DISPLAY_DESC("play_stop");
|
||||||
|
|
||||||
|
/* Can unboost the codec thread here no matter who's calling */
|
||||||
|
boost_codec_thread(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcmbuf_pause(bool pause)
|
||||||
|
{
|
||||||
|
if (pcm_is_playing())
|
||||||
|
pcm_play_pause(!pause);
|
||||||
|
else if (!pause)
|
||||||
|
pcmbuf_play_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Crossfade */
|
/* Crossfade */
|
||||||
|
|
||||||
/* Clip sample to signed 16 bit range */
|
/* Clip sample to signed 16 bit range */
|
||||||
|
@ -1095,7 +1084,7 @@ int pcmbuf_used_descs(void)
|
||||||
|
|
||||||
int pcmbuf_descs(void)
|
int pcmbuf_descs(void)
|
||||||
{
|
{
|
||||||
return CHUNK_DESCS(pcmbuf_size);
|
return NUM_CHUNK_DESCS(pcmbuf_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ROCKBOX_HAS_LOGF
|
#ifdef ROCKBOX_HAS_LOGF
|
||||||
|
|
Loading…
Reference in a new issue