summaryrefslogtreecommitdiffstats
path: root/apps/pcmbuf.c
diff options
context:
space:
mode:
authorBrandon Low <lostlogic@rockbox.org>2006-04-25 12:34:28 +0000
committerBrandon Low <lostlogic@rockbox.org>2006-04-25 12:34:28 +0000
commit9602dd7e60e43bd327701a4e0231c60b797dfbd8 (patch)
treee7e45726bc5a700fa6369fcf2bbea7de039f0c03 /apps/pcmbuf.c
parent192476963129e50d1a064efec9053239593fbbbc (diff)
downloadrockbox-9602dd7e60e43bd327701a4e0231c60b797dfbd8.tar.gz
rockbox-9602dd7e60e43bd327701a4e0231c60b797dfbd8.zip
Fix asymetric crossfade cases that were broken, and also a not yet reported bug where a buffer underrun during crossfade would take a long time to resume playing
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9798 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/pcmbuf.c')
-rw-r--r--apps/pcmbuf.c114
1 files changed, 72 insertions, 42 deletions
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index f95baf9c48..ec3965c113 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -65,7 +65,7 @@ static void (*position_callback)(size_t size) IDATA_ATTR;
/* Crossfade related state */
static bool crossfade_enabled;
-static bool crossfade_mix;
+static bool crossfade_mode;
static bool crossfade_active IDATA_ATTR;
static bool crossfade_init IDATA_ATTR;
@@ -100,6 +100,7 @@ static bool pcmbuf_flush;
#define LOW_DATA(quarter_secs) \
(pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs)
+static bool prepare_insert(size_t length);
static void pcmbuf_under_watermark(void);
static bool pcmbuf_flush_fillpos(void);
@@ -308,7 +309,7 @@ bool pcmbuf_crossfade_init(bool manual_skip)
pcmbuf_boost(true);
/* Don't enable mix mode when skipping tracks manually. */
- crossfade_mix = manual_skip && global_settings.crossfade_fade_out_mixmode;
+ crossfade_mode = manual_skip && global_settings.crossfade_fade_out_mixmode;
crossfade_init = true;
return true;
@@ -450,7 +451,7 @@ static bool pcmbuf_flush_fillpos(void)
static void crossfade_process_buffer(size_t fade_in_delay,
size_t fade_out_delay, size_t fade_out_rem)
{
- if (!crossfade_mix)
+ if (!crossfade_mode)
{
/* Fade out the specified amount of the already processed audio */
size_t total_fade_out = fade_out_rem;
@@ -577,33 +578,59 @@ static void crossfade_start(void)
crossfade_process_buffer(fade_in_delay, fade_out_delay, fade_out_rem);
}
-static size_t fade_mix(int factor, const char *buf, size_t fade_rem)
+/* Returns the number of bytes _NOT_ mixed */
+static size_t crossfade_fade_mix(int factor, const char *buf, size_t fade_rem)
{
const short *input_buf = (const short *)buf;
- size_t samples = 0;
short *output_buf = (short *)(crossfade_chunk->addr);
short *chunk_end = (short *)((size_t)output_buf + crossfade_chunk->size);
output_buf = &output_buf[crossfade_sample];
- fade_rem /= 2;
- while (samples < fade_rem)
+ while (fade_rem)
{
int sample = *input_buf++;
sample = ((sample * factor) >> 8) + *output_buf;
*output_buf++ = MIN(32767, MAX(-32768, sample));
- samples++;
+ fade_rem -= 2;
if (output_buf >= chunk_end)
{
crossfade_chunk = crossfade_chunk->link;
if (!crossfade_chunk)
- return samples * 2;
+ return fade_rem;
output_buf = (short *)(crossfade_chunk->addr);
chunk_end = (short *)((size_t)output_buf + crossfade_chunk->size);
}
}
crossfade_sample = (size_t)(output_buf - (short *)(crossfade_chunk->addr));
- return samples * 2;
+ return 0;
+}
+
+/* Returns the number of bytes _NOT_ mixed */
+static size_t crossfade_mix(const char *buf, size_t length)
+{
+ const short *input_buf = (const short *)buf;
+ short *output_buf = (short *)(crossfade_chunk->addr);
+ short *chunk_end = (short *)((size_t)output_buf + crossfade_chunk->size);
+ output_buf = &output_buf[crossfade_sample];
+
+ while (length)
+ {
+ int sample = *input_buf++ + *output_buf;
+ *output_buf++ = MIN(32767, MAX(-32768, sample));
+ length -= 2;
+
+ if (output_buf >= chunk_end)
+ {
+ crossfade_chunk = crossfade_chunk->link;
+ if (!crossfade_chunk)
+ return length;
+ output_buf = (short *)(crossfade_chunk->addr);
+ chunk_end = (short *)((size_t)output_buf + crossfade_chunk->size);
+ }
+ }
+ crossfade_sample = (size_t)(output_buf - (short *)(crossfade_chunk->addr));
+ return 0;
}
static void pcmbuf_flush_buffer(const char *buf, size_t length)
@@ -626,33 +653,35 @@ static void pcmbuf_flush_buffer(const char *buf, size_t length)
static void flush_crossfade(char *buf, size_t length)
{
- if (length && crossfade_fade_in_rem)
+ if (length)
{
- int factor = ((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) /
- crossfade_fade_in_total;
- /* Bytes to fade */
- size_t fade_rem = MIN(length, crossfade_fade_in_rem);
- crossfade_fade_in_rem -= fade_rem;
-
- if (crossfade_chunk)
- {
- /* Mix the data */
- size_t complete = fade_mix(factor, buf, fade_rem);
- length -= complete;
- buf += complete;
- fade_rem -= complete;
- /* If there is still fading to be done */
- if (fade_rem)
- goto mix_done;
- }
- else
+ if (crossfade_fade_in_rem)
{
size_t samples;
short *input_buf;
-mix_done:
- /* Fade samples in place */
+
+ /* Fade factor for this packet */
+ int factor =
+ ((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) /
+ crossfade_fade_in_total;
+ /* Bytes to fade */
+ size_t fade_rem = MIN(length, crossfade_fade_in_rem);
+
+ /* We _will_ fade this many bytes */
+ crossfade_fade_in_rem -= fade_rem;
+
+ if (crossfade_chunk)
+ {
+ /* Mix the data */
+ size_t fade_total = fade_rem;
+ fade_rem = crossfade_fade_mix(factor, buf, fade_rem);
+ length -= fade_total - fade_rem;
+ buf += fade_total - fade_rem;
+ }
+
samples = fade_rem / 2;
input_buf = (short *)buf;
+ /* Fade remaining samples in place */
while (samples)
{
int sample = *input_buf;
@@ -660,21 +689,23 @@ mix_done:
samples--;
}
}
- }
- if (length)
- {
- /* Flush samples to the buffer */
- while (pcmbuf_free() < length)
+ if (crossfade_chunk)
{
- pcmbuf_boost(false);
- sleep(1);
+ /* Mix the data */
+ size_t mix_total = length;
+ length = crossfade_mix(buf, length);
+ buf += mix_total - length;
}
+ else if (!crossfade_fade_in_rem)
+ crossfade_active = false;
+
+ /* Flush samples to the buffer */
+ while (!prepare_insert(length))
+ sleep(1);
pcmbuf_flush_buffer(buf, length);
}
- if (!crossfade_fade_in_rem)
- crossfade_active = false;
}
static bool prepare_insert(size_t length)
@@ -688,7 +719,7 @@ static bool prepare_insert(size_t length)
}
/* Need to save PCMBUF_MIN_CHUNK to prevent wrapping overwriting */
- if (pcmbuf_free() < length + PCMBUF_MIN_CHUNK && !crossfade_active)
+ if (pcmbuf_free() < length + PCMBUF_MIN_CHUNK)
{
pcmbuf_boost(false);
return false;
@@ -697,7 +728,6 @@ static bool prepare_insert(size_t length)
if (!pcm_is_playing())
{
pcmbuf_boost(true);
- crossfade_active = false;
/* Pre-buffer 1s. */
if (!LOW_DATA(4))
{