Skip to content
Merged
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
9 changes: 7 additions & 2 deletions Sources/CSFBAudioEngine/Player/AudioPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,12 @@ class AudioPlayer final {
started = 1,
/// Decoding complete
complete = 2,
/// Seek
seek = 3,
/// Decoder canceled by user or aborted due to error
canceled = 3,
canceled = 4,
/// Decoding error
error = 4,
error = 5,
};

/// Render block events
Expand All @@ -288,6 +290,9 @@ class AudioPlayer final {
/// Reads and processes a decoding complete event from `decodingEvents_`
bool processDecodingCompleteEvent() noexcept;

/// Reads and processes a decoder seek event from `decodingEvents_`
bool processDecoderSeekEvent() noexcept;

/// Reads and processes a decoder canceled event from `decodingEvents_`
bool processDecoderCanceledEvent() noexcept;

Expand Down
42 changes: 42 additions & 0 deletions Sources/CSFBAudioEngine/Player/AudioPlayer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,15 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re
decoderState->setFlags(DecoderState::Flags::cancelRequested);
continue;
}

if (const auto frame = decoderState->framesDecoded_.load(std::memory_order_acquire);
decodingEvents_.writeAll(DecodingEventCommand::seek, nextEventIdentificationNumber(),
decoderState->sequenceNumber_, frame)) {
eventSemaphore_.signal();
} else {
os_log_fault(log_, "Error writing decoder seek event");
}

ringBufferStale = true;

if (bits::is_set(flags, DecoderState::Flags::decodingComplete)) {
Expand Down Expand Up @@ -1722,6 +1731,9 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re
case DecodingEventCommand::complete:
return processDecodingCompleteEvent();

case DecodingEventCommand::seek:
return processDecoderSeekEvent();

case DecodingEventCommand::canceled:
return processDecoderCanceledEvent();

Expand Down Expand Up @@ -1804,6 +1816,36 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re
return true;
}

bool sfb::AudioPlayer::processDecoderSeekEvent() noexcept {
uint64_t sequenceNumber;
int64_t frame;
if (!decodingEvents_.readAll(sequenceNumber, frame)) {
os_log_error(log_, "Missing decoder sequence number or frame position for decoder seek event");
return false;
}

Decoder decoder = nil;
{
std::lock_guard lock{activeDecodersMutex_};

if (const auto iter = std::ranges::find(activeDecoders_, sequenceNumber, &DecoderState::sequenceNumber_);
iter != activeDecoders_.cend()) {
decoder = (*iter)->decoder_;
} else {
os_log_error(log_, "Decoder state with sequence number %llu missing for decoder seek event",
sequenceNumber);
return false;
}
}

if (__strong id<SFBAudioPlayerDelegate> delegate = player_.delegate;
delegate != nil && [delegate respondsToSelector:@selector(audioPlayer:didSeek:toFrame:)]) {
[delegate audioPlayer:player_ didSeek:decoder toFrame:frame];
}

return true;
}

bool sfb::AudioPlayer::processDecoderCanceledEvent() noexcept {
uint64_t sequenceNumber;
if (!decodingEvents_.read(sequenceNumber)) {
Expand Down
21 changes: 15 additions & 6 deletions Sources/CSFBAudioEngine/include/SFBAudioEngine/SFBAudioPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,13 @@ typedef NS_ENUM(NSUInteger, SFBAudioPlayerPlaybackState) {
/// 7. Now playing changed
/// 8. Playback state changed
/// 9. End of audio
/// 10. Decoder canceled by user
/// 11. Decoding aborted due to error
/// 12. Asynchronous error encountered
/// 13. Processing graph format change with custom nodes present
/// 14. `AVAudioEngineConfigurationChange` notification received
/// 15. `AVAudioSessionInterruption` notification received
/// 10. Seek complete
/// 11. Decoder canceled by user
/// 12. Decoding aborted due to error
/// 13. Asynchronous error encountered
/// 14. Processing graph format change with custom nodes present
/// 15. `AVAudioEngineConfigurationChange` notification received
/// 16. `AVAudioSessionInterruption` notification received
///
/// The dispatch queue on which delegate messages are sent is not specified.
NS_SWIFT_NAME(AudioPlayer)
Expand Down Expand Up @@ -367,6 +368,14 @@ NS_SWIFT_NAME(AudioPlayer.Delegate)
/// Called to notify the delegate when rendering is complete for all available decoders
/// - parameter audioPlayer: The `SFBAudioPlayer` object
- (void)audioPlayerEndOfAudio:(SFBAudioPlayer *)audioPlayer NS_SWIFT_NAME(audioPlayerEndOfAudio(_:));
/// Called to notify the delegate after performing a user-initiated seek in a decoder
/// - warning: Do not change any properties of `decoder`
/// - parameter audioPlayer: The `SFBAudioPlayer` object processing `decoder`
/// - parameter decoder: The decoder that performed the seek
/// - parameter frame: The new frame position in `decoder`
- (void)audioPlayer:(SFBAudioPlayer *)audioPlayer
didSeek:(id<SFBPCMDecoding>)decoder
toFrame:(AVAudioFramePosition)frame;
/// Called to notify the delegate that the decoding and rendering processes for a decoder have been canceled by a
/// user-initiated request
/// - warning: Do not change any properties of `decoder`
Expand Down