Skip to content
Open
9 changes: 9 additions & 0 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ let package = Package(
.package(url: "https://github.com/sbooth/CXXAudioRingBuffer", .upToNextMinor(from: "0.1.1")),
.package(url: "https://github.com/sbooth/CXXDispatchSemaphore", .upToNextMinor(from: "0.4.1")),
.package(url: "https://github.com/sbooth/CXXMessageQueue", .upToNextMinor(from: "0.2.0")),
.package(url: "https://github.com/sbooth/CXXRingBuffer", .upToNextMinor(from: "0.6.1")),
.package(url: "https://github.com/sbooth/CXXUnfairLock", .upToNextMinor(from: "0.3.1")),
Comment on lines 27 to 32

// Standalone dependencies from source
Expand Down Expand Up @@ -66,6 +67,7 @@ let package = Package(
.product(name: "CXXAudioRingBuffer", package: "CXXAudioRingBuffer"),
.product(name: "CXXDispatchSemaphore", package: "CXXDispatchSemaphore"),
.product(name: "CXXMessageQueue", package: "CXXMessageQueue"),
.product(name: "CXXRingBuffer", package: "CXXRingBuffer"),
.product(name: "CXXUnfairLock", package: "CXXUnfairLock"),
// Standalone dependencies
.product(name: "dumb", package: "CDUMB"),
Expand Down
42 changes: 41 additions & 1 deletion Sources/CSFBAudioEngine/Player/AudioPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#import <mpsc/MessageQueue.hpp>
#import <mtx/UnfairMutex.hpp>
#import <spsc/AudioRingBuffer.hpp>
#import <spsc/RingBuffer.hpp>

#import <AVFAudio/AVFAudio.h>

Expand All @@ -34,6 +35,35 @@

namespace sfb {

namespace detail {

/// A descriptor for a decoded chunk of audio.
struct DecodedChunkDescriptor final {
/// The playback generation at the time this chunk was decoded
uint64_t playbackGeneration_{0};
/// Decoder sequence number that produced the audio.
uint64_t sequenceNumber_{0};
/// Decoder frame position for the first audio frame in the chunk.
int64_t framePosition_{0};
/// Number of audio frames in the chunk.
uint32_t frameLength_{0};
};

/// A descriptor for a rendering chunk of audio.
struct RenderingChunkDescriptor final {
/// The decoded chunk descriptor.
DecodedChunkDescriptor descriptor_{};
/// The number of frames consumed from `descriptor_`
uint32_t framesConsumed_{0};

/// Returns the number of frames remaining in this chunk
[[nodiscard]] uint32_t framesRemaining() const noexcept { return descriptor_.frameLength_ - framesConsumed_; }
/// Returns the frame position of the next frame in this chunk
[[nodiscard]] int64_t framePosition() const noexcept { return descriptor_.framePosition_ + framesConsumed_; }
};

} /* namespace detail */

// MARK: - AudioPlayer

/// SFBAudioPlayer implementation
Expand All @@ -54,7 +84,12 @@ class AudioPlayer final {
using DecoderStateVector = std::vector<std::unique_ptr<DecoderState>>;

/// Ring buffer transferring audio between the decoding thread and the render block
spsc::AudioRingBuffer audioRingBuffer_;
spsc::AudioRingBuffer audioBuffer_;
/// Ring buffer transferring audio metadata between the decoding thread and the render block
spsc::RingBuffer audioMetadata_;
/// The current transport epoch
std::atomic<uint64_t> playbackGeneration_{1};
static_assert(std::atomic<uint64_t>::is_always_lock_free, "Lock-free std::atomic<uint64_t> required");

/// Active decoders and associated state
DecoderStateVector activeDecoders_;
Expand Down Expand Up @@ -249,6 +284,8 @@ class AudioPlayer final {
/// Render block implementation
OSStatus render(BOOL &isSilence, const AudioTimeStamp &timestamp, AVAudioFrameCount frameCount,
AudioBufferList *_Nonnull outputData) noexcept;
/// The current rendering chunk descriptor
detail::RenderingChunkDescriptor renderingChunk_{};

// MARK: - Events

Expand Down Expand Up @@ -311,6 +348,9 @@ class AudioPlayer final {
/// Returns the first decoder state in `activeDecoders_` that has not been canceled
DecoderState *_Nullable firstActiveDecoderState() const noexcept;

/// Returns the decoder state in `activeDecoders_` with the specified sequence number
DecoderState *_Nullable decoderStateWithSequenceNumber(uint64_t sequenceNumber) const noexcept;

public:
// MARK: - AVAudioEngine Notification Handling

Expand Down
Loading