Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Sources/CSFBAudioEngine/Player/AudioPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,10 @@ class AudioPlayer final {
isMuted = 1u << 2,
/// The ring buffer needs to be drained during the next render cycle
drainRequired = 1u << 3,
/// A ring buffer format change is pending
formatChangePending = 1u << 4,
/// The event message queue had insufficient space to record a render event
renderEventDropped = 1u << 4,
renderEventDropped = 1u << 5,
};

// Enable bitmask operations for `Flags`
Expand Down
21 changes: 16 additions & 5 deletions Sources/CSFBAudioEngine/Player/AudioPlayer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,7 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re
// Clear the format mismatch flag if any decoders were canceled
if (anyCanceled && formatMismatch) {
formatMismatch = false;
clearFlags(Flags::formatChangePending);
}

// Get the earliest decoder state that has not completed rendering
Expand Down Expand Up @@ -1250,6 +1251,7 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re
// match. Clear the format mismatch flag so rendering can continue; the flag will be set again when
// decoding completes.
formatMismatch = false;
clearFlags(Flags::formatChangePending);

fetchUpdate(
decoderState->flags_,
Expand Down Expand Up @@ -1418,6 +1420,10 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re
// decoding can't start until the processing graph is reconfigured which occurs after
// all active decoders complete
formatMismatch = true;

// Ring buffer underruns are expected while waiting for the format change to complete;
// suppress underrun notifications until the processing graph is reconfigured
setFlags(Flags::formatChangePending);
}
}

Expand All @@ -1430,13 +1436,16 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re
}();

if (okToReconfigure) {
clearFlags(Flags::drainRequired);
formatMismatch = false;

os_log_debug(log_, "Non-gapless join for %{public}@", decoderState->decoder_);

auto renderFormat = decoderState->converter_.outputFormat;
if (NSError *error = nil; !configureProcessingGraphAndRingBufferForFormat(renderFormat, &error)) {
NSError *error = nil;
const auto reconfigured = configureProcessingGraphAndRingBufferForFormat(renderFormat, &error);

formatMismatch = false;
Comment thread
sbooth marked this conversation as resolved.
clearFlags(Flags::drainRequired | Flags::formatChangePending);

if (!reconfigured) {
decoderState->error_ = error;
decoderState->setFlags(DecoderState::Flags::cancelRequested);
continue;
Expand Down Expand Up @@ -1613,7 +1622,9 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re
isSilence = YES;
}

if (framesRead != frameCount) {
// Suppress underrun notifications while a non-gapless format change is pending; the ring buffer
// is expected to run dry while the decoding thread waits to reconfigure the processing graph
if (framesRead != frameCount && bits::is_clear(flags, Flags::formatChangePending)) {
if (!events_.enqueue(EventCommand::renderBufferUnderrun, timestamp.mHostTime, static_cast<uint32_t>(framesRead),
static_cast<uint32_t>(frameCount))) {
setFlags(Flags::renderEventDropped);
Expand Down
Loading