aboutsummaryrefslogtreecommitdiffstats
path: root/src/outputBuffer_xfade.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/outputBuffer_xfade.h')
-rw-r--r--src/outputBuffer_xfade.h105
1 files changed, 105 insertions, 0 deletions
diff --git a/src/outputBuffer_xfade.h b/src/outputBuffer_xfade.h
new file mode 100644
index 000000000..0f3a4d5a7
--- /dev/null
+++ b/src/outputBuffer_xfade.h
@@ -0,0 +1,105 @@
+#ifndef OUTPUT_BUFFER_XFADE_H
+#define OUTPUT_BUFFER_XFADE_H
+
+#include "os_compat.h"
+#include "audio.h"
+#include "pcm_utils.h"
+
+static struct ob_chunk *get_chunk(struct iovec vec[2], size_t i);
+static size_t calculate_xfade_chunks(struct iovec vec[2])
+{
+ size_t chunks;
+ struct ob_chunk *c;
+ size_t nr;
+ AudioFormat *af = &ob.audio_format;
+
+ assert(pthread_equal(ob.thread, pthread_self()));
+
+ if (!ob.total_time ||
+ (ob.elapsed_time + ob.xfade_time) < ob.total_time ||
+ !isCurrentAudioFormat(af))
+ return ob.nr_bpp; /* too early, don't enable xfade yet */
+
+ assert(af->bits > 0);
+ assert(af->channels > 0);
+ assert(af->sampleRate > 0);
+
+ chunks = af->sampleRate * af->bits * af->channels / 8.0 / CHUNK_SIZE;
+ chunks = chunks * (ob.xfade_time + 0.5);
+
+ assert(ob.index->size >= ob.nr_bpp);
+ if (chunks > (ob.index->size - ob.nr_bpp))
+ chunks = ob.index->size - ob.nr_bpp;
+ DEBUG("calculated xfade chunks: %d\n", chunks);
+ nr = vec[0].iov_len + vec[1].iov_len;
+
+ if (chunks > nr)
+ return chunks; /* not enough work with */
+
+ c = get_chunk(vec, chunks);
+ assert(c);
+ if (c->seq == ob.seq_player) {
+ do {
+ if (!(c = get_chunk(vec, ++chunks)))
+ return chunks; /* not enough to work with */
+ } while (c->seq == ob.seq_player);
+ } else {
+ do {
+ c = get_chunk(vec, --chunks);
+ assert(c);
+ } while (c->seq == ob.seq_decoder);
+ assert((c = get_chunk(vec, chunks)));
+ assert(c->seq != ob.seq_decoder);
+ ++chunks;
+ assert((c = get_chunk(vec, chunks)));
+ assert(c->seq == ob.seq_decoder);
+ }
+ DEBUG("adjusted xfade chunks: %d\n", chunks);
+
+ ob.xfade_cur = chunks;
+ ob.xfade_max = chunks;
+ assert(ob.xfade_state == XFADE_DISABLED);
+ ob.xfade_state = XFADE_ENABLED;
+ return chunks;
+}
+
+static size_t xfade_chunks_needed(struct iovec vec[2])
+{
+ assert(pthread_equal(ob.thread, pthread_self()));
+
+ if (ob.xfade_state == XFADE_DISABLED)
+ return calculate_xfade_chunks(vec);
+ assert(ob.xfade_state == XFADE_ENABLED);
+ return ob.xfade_max;
+}
+
+static void xfade_mix(struct ob_chunk *a, struct ob_chunk *b)
+{
+ assert(pthread_equal(ob.thread, pthread_self()));
+ assert(ob.xfade_state == XFADE_ENABLED);
+ assert(ob.xfade_cur <= ob.xfade_max);
+ assert(b);
+ assert(a != b);
+ assert(a->len <= CHUNK_SIZE);
+ assert(b->len <= CHUNK_SIZE);
+ if (b->seq == a->seq) {
+ /* deal with small rounding errors */
+ DEBUG("seq_match: %d == %d\n", a->seq, b->seq);
+ return;
+ }
+
+ /* as xfade_cur increases, b is scaled more and a is scaled less */
+ pcm_mix(a->data, b->data, a->len, b->len,
+ &ob.audio_format, ((float)ob.xfade_cur) / ob.xfade_max);
+
+ /* next time we fade more until we have nothing to fade */
+ if (ob.xfade_cur)
+ --ob.xfade_cur;
+ if (b->len > a->len) {
+ DEBUG("reassign len: %d => %d\n", a->len, b->len);
+ a->len = b->len;
+ }
+ b->len = 0; /* invalidate the chunk we already mixed in */
+}
+
+#endif /* OUTPUT_BUFFER_XFADE_H */