Skip to content
Closed
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
5 changes: 5 additions & 0 deletions AudioStreaming/OggVorbis/VorbisFileDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ final class VorbisFileDecoder {
switch channels {
case 1: layoutTag = kAudioChannelLayoutTag_Mono
case 2: layoutTag = kAudioChannelLayoutTag_Stereo
// Vorbis uses FL, FC, FR, RL, RR, LFE channel order. The MPEG
// 5.1 layout instead expects FL, FR, FC, LFE, RL, RR, so tagging
// Vorbis PCM as MPEG 5.1 misidentifies four of the six channels.
// Ogg_5_1 describes the channel order emitted by the Vorbis decoder.
case 6: layoutTag = kAudioChannelLayoutTag_Ogg_5_1
default: layoutTag = kAudioChannelLayoutTag_Unknown | UInt32(channels)
}

Expand Down
10 changes: 9 additions & 1 deletion AudioStreaming/Streaming/AudioPlayer/AudioPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,15 @@ open class AudioPlayer {
}

/// An `AVAudioFormat` object for the canonical audio stream
private var outputAudioFormat: AVAudioFormat = .init(commonFormat: .pcmFormatFloat32, sampleRate: 44100.0, channels: 2, interleaved: true)!
private let outputAudioFormat: AVAudioFormat = {
let channelLayout = AVAudioChannelLayout(layoutTag: kAudioChannelLayoutTag_MPEG_5_1_A)!
return AVAudioFormat(
commonFormat: .pcmFormatFloat32,
sampleRate: 44100.0,
interleaved: true,
channelLayout: channelLayout
)
}()

/// Keeps track of the player's state before being paused.
private var stateBeforePaused: InternalState = .initial
Expand Down
12 changes: 9 additions & 3 deletions AudioStreaming/Streaming/AudioPlayer/AudioRendererContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ final class AudioRendererContext {
framesRequiredForDataAfterSeekPlaying = Double(canonicalStream.mSampleRate) * Double(configuration.gracePeriodAfterSeekInSeconds)

let dataByteSize = Int(canonicalStream.mSampleRate * configuration.bufferSizeInSeconds) * Int(canonicalStream.mBytesPerFrame)
inOutAudioBufferList = allocateBufferList(dataByteSize: dataByteSize)
inOutAudioBufferList = allocateBufferList(
dataByteSize: dataByteSize,
channelCount: canonicalStream.mChannelsPerFrame
)

audioBuffer = inOutAudioBufferList[0].mBuffers

Expand Down Expand Up @@ -71,14 +74,17 @@ final class AudioRendererContext {
///
/// - parameter dataByteSize: An `Int` value indicating the size that the buffer will hold
/// - Returns: An `UnsafeMutablePointer<AudioBufferList>` object
private func allocateBufferList(dataByteSize: Int) -> UnsafeMutablePointer<AudioBufferList> {
private func allocateBufferList(
dataByteSize: Int,
channelCount: UInt32
) -> UnsafeMutablePointer<AudioBufferList> {
let _bufferList = AudioBufferList.allocate(maximumBuffers: 1)

_bufferList[0].mDataByteSize = UInt32(dataByteSize)
let alignment = MemoryLayout<UInt8>.alignment
let mData = UnsafeMutableRawPointer.allocate(byteCount: dataByteSize, alignment: alignment)
_bufferList[0].mData = mData
_bufferList[0].mNumberChannels = 2
_bufferList[0].mNumberChannels = channelCount

return _bufferList.unsafeMutablePointer
}
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,47 @@ final class AudioFileStreamProcessor {
return
}
}
configureChannelLayouts(for: audioConverter, inputChannels: inputFormat.mChannelsPerFrame)
self.inputFormat = inputFormat
assignMagicCookieToConverterIfNeeded()
}

private func configureChannelLayouts(
for converter: AudioConverterRef?,
inputChannels: UInt32
) {
guard let converter else { return }

if let inputLayoutTag = channelLayoutTag(for: inputChannels),
let inputLayout = AVAudioChannelLayout(layoutTag: inputLayoutTag) {
AudioConverterSetProperty(
converter,
kAudioConverterInputChannelLayout,
UInt32(MemoryLayout<AudioChannelLayout>.size),
inputLayout.layout
)
}

guard let outputLayout = AVAudioChannelLayout(
layoutTag: kAudioChannelLayoutTag_MPEG_5_1_A
) else { return }
AudioConverterSetProperty(
converter,
kAudioConverterOutputChannelLayout,
UInt32(MemoryLayout<AudioChannelLayout>.size),
outputLayout.layout
)
}

private func channelLayoutTag(for channelCount: UInt32) -> AudioChannelLayoutTag? {
return switch channelCount {
case 1: kAudioChannelLayoutTag_Mono
case 2: kAudioChannelLayoutTag_Stereo
case 6: kAudioChannelLayoutTag_MPEG_5_1_A
default: nil
}
}

private func assignMagicCookieToConverterIfNeeded() {
// magic cookie info
if let fileStream = audioFileStream {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ final class AudioPlayerRenderProcessor: NSObject {
if used > 0 && !waitForBuffer && playingEntry != nil && state.contains(.running) && state != .paused {
if end > start {
let framesToCopy = min(inNumberFrames, used)
bufferList.mBuffers.mNumberChannels = 2
bufferList.mBuffers.mNumberChannels = outputAudioFormat.mChannelsPerFrame
bufferList.mBuffers.mDataByteSize = frameSizeInBytes * framesToCopy

if isMuted {
Expand Down Expand Up @@ -155,7 +155,7 @@ final class AudioPlayerRenderProcessor: NSObject {
#endif
frameToCopy = 0
}
bufferList.mBuffers.mNumberChannels = 2
bufferList.mBuffers.mNumberChannels = outputAudioFormat.mChannelsPerFrame
bufferList.mBuffers.mDataByteSize = frameSizeInBytes * frameToCopy

if isMuted {
Expand All @@ -172,7 +172,7 @@ final class AudioPlayerRenderProcessor: NSObject {
let delta = inNumberFrames - frameToCopy
if delta > 0 {
moreFramesToCopy = min(delta, end)
bufferList.mBuffers.mNumberChannels = 2
bufferList.mBuffers.mNumberChannels = outputAudioFormat.mChannelsPerFrame
bufferList.mBuffers.mDataByteSize += frameSizeInBytes * moreFramesToCopy
if let ioBufferData = bufferList.mBuffers.mData {
if isMuted {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,12 +291,21 @@ final class OggVorbisStreamProcessor {
private func createAudioConverter(from sourceFormat: AVAudioFormat, to destFormat: AudioStreamBasicDescription) {
audioConverter = nil

var dest = destFormat
let dest = destFormat

guard let destAVFormat = AVAudioFormat(streamDescription: &dest) else {
Logger.error("Failed to create output AVAudioFormat", category: .audioRendering)
guard let channelLayout = AVAudioChannelLayout(
layoutTag: kAudioChannelLayoutTag_MPEG_5_1_A
) else {
Logger.error("Failed to create 5.1 output channel layout", category: .audioRendering)
return
}

let destAVFormat = AVAudioFormat(
commonFormat: .pcmFormatFloat32,
sampleRate: dest.mSampleRate,
interleaved: true,
channelLayout: channelLayout
)

guard let converter = AVAudioConverter(from: sourceFormat, to: destAVFormat) else {
Logger.error("Failed to create AVAudioConverter", category: .audioRendering)
Expand Down