forked from len0rd/rockbox
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
This commit is contained in:
parent
1924769631
commit
9602dd7e60
1 changed files with 72 additions and 42 deletions
102
apps/pcmbuf.c
102
apps/pcmbuf.c
|
@ -65,7 +65,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_mix;
|
static bool crossfade_mode;
|
||||||
static bool crossfade_active IDATA_ATTR;
|
static bool crossfade_active IDATA_ATTR;
|
||||||
static bool crossfade_init IDATA_ATTR;
|
static bool crossfade_init IDATA_ATTR;
|
||||||
|
|
||||||
|
@ -100,6 +100,7 @@ static bool pcmbuf_flush;
|
||||||
#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 bool prepare_insert(size_t length);
|
||||||
static void pcmbuf_under_watermark(void);
|
static void pcmbuf_under_watermark(void);
|
||||||
static bool pcmbuf_flush_fillpos(void);
|
static bool pcmbuf_flush_fillpos(void);
|
||||||
|
|
||||||
|
@ -308,7 +309,7 @@ bool pcmbuf_crossfade_init(bool manual_skip)
|
||||||
pcmbuf_boost(true);
|
pcmbuf_boost(true);
|
||||||
|
|
||||||
/* Don't enable mix mode when skipping tracks manually. */
|
/* 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;
|
crossfade_init = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -450,7 +451,7 @@ static bool pcmbuf_flush_fillpos(void)
|
||||||
static void crossfade_process_buffer(size_t fade_in_delay,
|
static void crossfade_process_buffer(size_t fade_in_delay,
|
||||||
size_t fade_out_delay, size_t fade_out_rem)
|
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 */
|
/* Fade out the specified amount of the already processed audio */
|
||||||
size_t total_fade_out = fade_out_rem;
|
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);
|
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;
|
const short *input_buf = (const short *)buf;
|
||||||
size_t samples = 0;
|
|
||||||
short *output_buf = (short *)(crossfade_chunk->addr);
|
short *output_buf = (short *)(crossfade_chunk->addr);
|
||||||
short *chunk_end = (short *)((size_t)output_buf + crossfade_chunk->size);
|
short *chunk_end = (short *)((size_t)output_buf + crossfade_chunk->size);
|
||||||
output_buf = &output_buf[crossfade_sample];
|
output_buf = &output_buf[crossfade_sample];
|
||||||
|
|
||||||
fade_rem /= 2;
|
while (fade_rem)
|
||||||
while (samples < fade_rem)
|
|
||||||
{
|
{
|
||||||
int sample = *input_buf++;
|
int sample = *input_buf++;
|
||||||
sample = ((sample * factor) >> 8) + *output_buf;
|
sample = ((sample * factor) >> 8) + *output_buf;
|
||||||
*output_buf++ = MIN(32767, MAX(-32768, sample));
|
*output_buf++ = MIN(32767, MAX(-32768, sample));
|
||||||
samples++;
|
fade_rem -= 2;
|
||||||
|
|
||||||
if (output_buf >= chunk_end)
|
if (output_buf >= chunk_end)
|
||||||
{
|
{
|
||||||
crossfade_chunk = crossfade_chunk->link;
|
crossfade_chunk = crossfade_chunk->link;
|
||||||
if (!crossfade_chunk)
|
if (!crossfade_chunk)
|
||||||
return samples * 2;
|
return fade_rem;
|
||||||
output_buf = (short *)(crossfade_chunk->addr);
|
output_buf = (short *)(crossfade_chunk->addr);
|
||||||
chunk_end = (short *)((size_t)output_buf + crossfade_chunk->size);
|
chunk_end = (short *)((size_t)output_buf + crossfade_chunk->size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
crossfade_sample = (size_t)(output_buf - (short *)(crossfade_chunk->addr));
|
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)
|
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)
|
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) /
|
if (crossfade_fade_in_rem)
|
||||||
|
{
|
||||||
|
size_t samples;
|
||||||
|
short *input_buf;
|
||||||
|
|
||||||
|
/* Fade factor for this packet */
|
||||||
|
int factor =
|
||||||
|
((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) /
|
||||||
crossfade_fade_in_total;
|
crossfade_fade_in_total;
|
||||||
/* Bytes to fade */
|
/* Bytes to fade */
|
||||||
size_t fade_rem = MIN(length, crossfade_fade_in_rem);
|
size_t fade_rem = MIN(length, crossfade_fade_in_rem);
|
||||||
|
|
||||||
|
/* We _will_ fade this many bytes */
|
||||||
crossfade_fade_in_rem -= fade_rem;
|
crossfade_fade_in_rem -= fade_rem;
|
||||||
|
|
||||||
if (crossfade_chunk)
|
if (crossfade_chunk)
|
||||||
{
|
{
|
||||||
/* Mix the data */
|
/* Mix the data */
|
||||||
size_t complete = fade_mix(factor, buf, fade_rem);
|
size_t fade_total = fade_rem;
|
||||||
length -= complete;
|
fade_rem = crossfade_fade_mix(factor, buf, fade_rem);
|
||||||
buf += complete;
|
length -= fade_total - fade_rem;
|
||||||
fade_rem -= complete;
|
buf += fade_total - fade_rem;
|
||||||
/* If there is still fading to be done */
|
|
||||||
if (fade_rem)
|
|
||||||
goto mix_done;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t samples;
|
|
||||||
short *input_buf;
|
|
||||||
mix_done:
|
|
||||||
/* Fade samples in place */
|
|
||||||
samples = fade_rem / 2;
|
samples = fade_rem / 2;
|
||||||
input_buf = (short *)buf;
|
input_buf = (short *)buf;
|
||||||
|
/* Fade remaining samples in place */
|
||||||
while (samples)
|
while (samples)
|
||||||
{
|
{
|
||||||
int sample = *input_buf;
|
int sample = *input_buf;
|
||||||
|
@ -660,21 +689,23 @@ mix_done:
|
||||||
samples--;
|
samples--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (length)
|
if (crossfade_chunk)
|
||||||
{
|
{
|
||||||
/* Flush samples to the buffer */
|
/* Mix the data */
|
||||||
while (pcmbuf_free() < length)
|
size_t mix_total = length;
|
||||||
{
|
length = crossfade_mix(buf, length);
|
||||||
pcmbuf_boost(false);
|
buf += mix_total - length;
|
||||||
sleep(1);
|
|
||||||
}
|
}
|
||||||
|
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);
|
pcmbuf_flush_buffer(buf, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!crossfade_fade_in_rem)
|
|
||||||
crossfade_active = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool prepare_insert(size_t length)
|
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 */
|
/* 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);
|
pcmbuf_boost(false);
|
||||||
return false;
|
return false;
|
||||||
|
@ -697,7 +728,6 @@ static bool prepare_insert(size_t length)
|
||||||
if (!pcm_is_playing())
|
if (!pcm_is_playing())
|
||||||
{
|
{
|
||||||
pcmbuf_boost(true);
|
pcmbuf_boost(true);
|
||||||
crossfade_active = false;
|
|
||||||
/* Pre-buffer 1s. */
|
/* Pre-buffer 1s. */
|
||||||
if (!LOW_DATA(4))
|
if (!LOW_DATA(4))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue