diff options
author | Brandon Low <lostlogic@rockbox.org> | 2006-04-25 12:34:28 +0000 |
---|---|---|
committer | Brandon Low <lostlogic@rockbox.org> | 2006-04-25 12:34:28 +0000 |
commit | 9602dd7e60e43bd327701a4e0231c60b797dfbd8 (patch) | |
tree | e7e45726bc5a700fa6369fcf2bbea7de039f0c03 /apps/pcmbuf.c | |
parent | 192476963129e50d1a064efec9053239593fbbbc (diff) | |
download | rockbox-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.c | 114 |
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)) { |