diff options
Diffstat (limited to 'src/buffer/audio.c')
| -rw-r--r-- | src/buffer/audio.c | 63 |
1 files changed, 40 insertions, 23 deletions
diff --git a/src/buffer/audio.c b/src/buffer/audio.c index f066f18..7c10dda 100644 --- a/src/buffer/audio.c +++ b/src/buffer/audio.c @@ -15,15 +15,21 @@ #define BUFFER_MARK_BUFFERED 0.35 #ifdef CAMU_AUDIO_BUFFER_FADE -#define FADE_STEP(fmt, down) ((down ? -2.5f : 2.5f) / (fmt)->sample_rate) -#define FADE_MIN 0.175f +#define FADE_STEP(fmt, down) ((down ? -4.75f : 2.25f) / (fmt)->sample_rate) +#define FADE_MIN 0.25f +#define FADE_TAIL 1 #endif enum { - PAUSE_PAUSED = 0, + // Resync (skip/delay) on read(), skip fade and signal BUFFER_PAUSED on pause. + PAUSE_SYNC = 0, + // Already signaled BUFFER_PAUSED, resync on resume. + PAUSE_PAUSED, #ifdef CAMU_AUDIO_BUFFER_FADE + // Fade out but don't consume any data from the buffer. PAUSE_FADING, - PAUSE_FADING_COMPLETE, + // Return silence fade.tail times then pause. + PAUSE_FADE_COMPLETE, #endif PAUSE_PLAYING }; @@ -31,7 +37,7 @@ enum { static void reset_buffer_state(struct camu_audio_buffer *buf) { atomic_store(f64)(&buf->pts, -1.0, AL_ATOMIC_RELAXED); - buf->pause = PAUSE_PAUSED; + buf->pause = PAUSE_SYNC; atomic_store(u32)(&buf->unpause, 0, AL_ATOMIC_RELAXED); buf->logged_delay = false; atomic_store(u32)(&buf->volume.set, 0, AL_ATOMIC_RELAXED); @@ -125,6 +131,7 @@ static bool push_internal(struct camu_audio_buffer *buf, f64 pts, u8 **data, s32 if (data && sample_count > 0) { f64 duration = camu_audio_format_samples_to_sec(&buf->fmt.in, sample_count); if (frame_is_late(buf->clock, base_pts, pts, duration)) { + log_trace("Discarding late frame with PTS %f.", pts); return true; } if (buf->fmt.resampler_needed) { @@ -281,20 +288,25 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif ptrdiff_t ret, signal = req; f64 base_pts = atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE); - bool set_clock = atomic_load(bool)(&buf->no_video, AL_ATOMIC_RELAXED); - f64 pts = camu_clock_get_pts(buf->clock, buf->latency, set_clock); + bool allow_set = atomic_load(bool)(&buf->no_video, AL_ATOMIC_RELAXED); + bool armed_for_pause = false; + f64 pts = camu_clock_get_pts(buf->clock, buf->latency, allow_set, &armed_for_pause); if (pts == CAMU_PTS_SIGNAL_PAUSE) { return 0; - } else if (pts == CAMU_PTS_PAUSED) { + } else if (pts == CAMU_PTS_PAUSED || armed_for_pause) { #ifdef CAMU_AUDIO_BUFFER_FADE + if (buf->pause == PAUSE_SYNC) { + buf->pause = PAUSE_FADE_COMPLETE; + buf->fade.tail = FADE_TAIL; + buf->logged_delay = false; + } if (buf->pause == PAUSE_PLAYING) { buf->pause = PAUSE_FADING; // fade offset should only ever be read when pause = FADING. buf->fade.offset = 0; - } else if (buf->pause == PAUSE_PAUSED || buf->pause == PAUSE_FADING_COMPLETE || - (buf->fade.volume == 0.f)) { + } else if (buf->pause == PAUSE_PAUSED || buf->pause == PAUSE_FADE_COMPLETE || buf->fade.volume == 0.f) { al_memset(data, 0, req); - if (buf->pause == PAUSE_FADING_COMPLETE) { + if (buf->pause == PAUSE_FADE_COMPLETE) { if (!--buf->fade.tail) { buf->callback(buf->userdata, CAMU_BUFFER_PAUSED); buf->pause = PAUSE_PAUSED; @@ -302,19 +314,24 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif } else if (buf->pause != PAUSE_PAUSED) { // Don't signal PAUSED until the next read to ensure at least 1 silent // frame is included in the fade out. - buf->pause = PAUSE_FADING_COMPLETE; + buf->pause = PAUSE_FADE_COMPLETE; // How many frames of silence to append after fading. - buf->fade.tail = 1; + buf->fade.tail = FADE_TAIL; } return req; } - } else if (buf->pause == PAUSE_FADING || buf->pause == PAUSE_FADING_COMPLETE) { + } else if (buf->pause == PAUSE_FADING || buf->pause == PAUSE_FADE_COMPLETE) { // If unpaused during a fade out, resume immediately to not break the stream. - buf->pause = PAUSE_PLAYING; - // Though, we will still jump back (possible pop) unless we are ignoring sync. + // If we're ignoring desync, we can move the buffer up the the current + // fade position so there's no break _or_ jump in the stream. if (buf->ignore_desync) { + buf->pause = PAUSE_PLAYING; ret = al_ring_buffer_discard(&buf->rb, buf->fade.offset); - base_pts += camu_audio_format_bytes_to_sec(fmt, ret); + f64 skip = camu_audio_format_bytes_to_sec(fmt, buf->fade.offset); + base_pts += skip; + log_info("Resuming at fade position, skipping %fs of audio (%zd bytes).", skip, ret); + } else { + buf->pause = PAUSE_SYNC; } #else // No fade and clock paused. al_memset(data, 0, req); @@ -336,8 +353,8 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif if (!buf->ignore_desync && atomic_load(u32)(&buf->unpause, AL_ATOMIC_ACQUIRE) > 0) { // Queuing multiple resyncs before resuming the stream will cause pops! - log_debug("Forcing resync."); - buf->pause = PAUSE_PAUSED; + log_info("Forcing resync."); + buf->pause = PAUSE_SYNC; atomic_sub(u32)(&buf->unpause, 1, AL_ATOMIC_RELEASE); } @@ -353,13 +370,13 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif // Unpause and possibly attempt syncing to the clock. // PAUSE_PAUSED signifies that the last read was silence, meaning we can skip around // in the buffer without worrying about pops. - if (UNLIKELY(buf->pause == PAUSE_PAUSED)) { + if (UNLIKELY(buf->pause == PAUSE_SYNC || buf->pause == PAUSE_PAUSED)) { if (!buf->ignore_desync) { pts -= base_pts; if (pts > 0.0) { // Skip. ret = MIN((ptrdiff_t)camu_audio_format_sec_to_bytes(fmt, pts), have); - log_info("Skipping %fs of audio (%zd bytes).", pts, ret); ret = al_ring_buffer_discard(&buf->rb, ret); + log_info("Skipping %fs of audio (%zd bytes).", pts, ret); have -= ret; base_pts += camu_audio_format_bytes_to_sec(fmt, ret); // Could go on to underrun. @@ -367,7 +384,7 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif pts = -pts; ret = MIN((ptrdiff_t)camu_audio_format_sec_to_bytes(fmt, pts), req); if (!buf->logged_delay) { - log_info("Delaying audio by %fs.", pts); + log_info("Delaying audio by %fs (%zd bytes).", pts, ret); buf->logged_delay = true; } al_memset(data, 0, ret); @@ -410,7 +427,7 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif // If we have data by the next read(), try to skip ahead to maintain sync. // This might exacerbate the underrun issue but an underrun is already // unexpected behavior, trying to stay in sync comes first. - buf->pause = PAUSE_PAUSED; + buf->pause = PAUSE_SYNC; } // If flow = SIGNALED, we are waiting to be removed or reset. } |