summaryrefslogtreecommitdiff
path: root/src/buffer/audio.c
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2026-04-13 17:36:54 -0400
committerAndrew Opalach <andrew@akon.city> 2026-04-13 17:36:54 -0400
commit4ceadc74f0086168fbc576032ba3e6f23af16e39 (patch)
treea542703d78f2b29ac1a756fdceec78e40860bab0 /src/buffer/audio.c
parent77b54c35bf9587450cd636e0d7df37e190e28bfb (diff)
downloadcamu-4ceadc74f0086168fbc576032ba3e6f23af16e39.tar.gz
camu-4ceadc74f0086168fbc576032ba3e6f23af16e39.tar.bz2
camu-4ceadc74f0086168fbc576032ba3e6f23af16e39.zip
Changes that went uncommitted for too long
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/buffer/audio.c')
-rw-r--r--src/buffer/audio.c63
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.
}