#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 rbvec vec[2], size_t i); static size_t calculate_xfade_chunks(struct rbvec vec[2]) { float xfade_time = ob.xfade_time; /* prevent race conditions */ size_t chunks; struct ob_chunk *c; size_t nr; AudioFormat *af = &ob.audio_format; assert(pthread_equal(ob.thread, pthread_self())); if (xfade_time <= 0) return ob.bpp_cur; if (!isCurrentAudioFormat(af)) return 0; if (!ob.total_time || (ob.elapsed_time + xfade_time) < ob.total_time) return ob.bpp_cur; /* too early, don't enable xfade yet */ assert(af->bits > 0); assert(af->channels > 0); assert(af->sampleRate > 0); chunks = audio_format_time_to_size(af) / CHUNK_SIZE; chunks = chunks * (xfade_time + 0.5); assert(chunks); assert(ob.index->size >= ob.bpp_cur); if (chunks > (ob.index->size - ob.bpp_cur)) chunks = ob.index->size - ob.bpp_cur; DEBUG("calculated xfade chunks: %d\n", chunks); nr = vec[0].len + vec[1].len; if (chunks <= nr) { c = get_chunk(vec, chunks); assert(c); if (c->seq != ob.seq_player) { do { c = get_chunk(vec, --chunks); assert(c); } while (chunks && c->seq == ob.seq_decoder); assert((c = get_chunk(vec, chunks))); if (!chunks && c->seq != ob.seq_decoder) return 0; /* nothing to xfade */ ++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 rbvec 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 */