diff --git a/Sources/AudioSnapshotTesting/Internal/AudioDataComparator.swift b/Sources/AudioSnapshotTesting/Internal/AudioDataComparator.swift index 77a2e34..de49921 100644 --- a/Sources/AudioSnapshotTesting/Internal/AudioDataComparator.swift +++ b/Sources/AudioSnapshotTesting/Internal/AudioDataComparator.swift @@ -59,23 +59,25 @@ enum AudioDataComparator { /// This matches the quantization that ALAC applies during encoding. private static func quantize(_ buffer: AVAudioPCMBuffer, bitDepth: AudioBitDepth) throws -> AVAudioPCMBuffer { let intFormat: AVAudioFormat? - switch bitDepth { - case .bits16: + let commonFormat: AVAudioCommonFormat = bitDepth == .bits16 ? .pcmFormatInt16 : .pcmFormatInt32 + + // For multi-channel audio (>2 channels), we need to preserve the channel layout + if let channelLayout = buffer.format.channelLayout { intFormat = AVAudioFormat( - commonFormat: .pcmFormatInt16, + commonFormat: commonFormat, sampleRate: buffer.format.sampleRate, - channels: buffer.format.channelCount, - interleaved: false + interleaved: false, + channelLayout: channelLayout ) - case .bits32: + } else { intFormat = AVAudioFormat( - commonFormat: .pcmFormatInt32, + commonFormat: commonFormat, sampleRate: buffer.format.sampleRate, channels: buffer.format.channelCount, interleaved: false ) } - + guard let intFormat else { throw AudioComparisonError.formatCreationFailed } diff --git a/Tests/AudioSnapshotTestingTests/AudioSnapshotTestingTests.swift b/Tests/AudioSnapshotTestingTests/AudioSnapshotTestingTests.swift index 549e1af..23f3326 100644 --- a/Tests/AudioSnapshotTestingTests/AudioSnapshotTestingTests.swift +++ b/Tests/AudioSnapshotTestingTests/AudioSnapshotTestingTests.swift @@ -154,14 +154,45 @@ func audioSnapshotSynthesized() async throws { ) } -private func createBuffer(from samples: [Float]) -> AVAudioPCMBuffer { - let format = AVAudioFormat(standardFormatWithSampleRate: 32768, channels: 1)! - let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(samples.count))! - let channelData = buffer.floatChannelData![0] - samples.enumerated().forEach { index, sample in - channelData[index] = sample +@available(iOS 16, macOS 13, *) +@Test( + "Multi-channel (4ch) audio comparison", + .audioSnapshot(record: false, strategy: .spectrogram(hopSize: 4096, frequencyCount: 2048)) +) +func multiChannelComparison() async throws { + let frameCount = 44100 + let frequencies: [Float] = [440, 880, 1320, 1760] + let channels = frequencies.map { frequency in + synthesizeSignal(frequencyAmplitudePairs: [(frequency, 0.5)], count: frameCount) + } + let buffer = createBuffer(channels: channels, sampleRate: 44100) + await assertAudioSnapshot(of: buffer, named: "multiChannelComparison.4ch") +} + +private func createBuffer(from samples: [Float], sampleRate: Double = 32768) -> AVAudioPCMBuffer { + createBuffer(channels: [samples], sampleRate: sampleRate) +} + +private func createBuffer(channels: [[Float]], sampleRate: Double = 32768) -> AVAudioPCMBuffer { + let channelCount = channels.count + let frameCount = channels.first?.count ?? 0 + + let format: AVAudioFormat + if channelCount <= 2 { + format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: AVAudioChannelCount(channelCount))! + } else { + let layout = AVAudioChannelLayout(layoutTag: kAudioChannelLayoutTag_DiscreteInOrder | UInt32(channelCount))! + format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channelLayout: layout) + } + + let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(frameCount))! + for (channelIndex, samples) in channels.enumerated() { + let channelData = buffer.floatChannelData![channelIndex] + for (frameIndex, sample) in samples.enumerated() { + channelData[frameIndex] = sample + } } - buffer.frameLength = AVAudioFrameCount(samples.count) + buffer.frameLength = AVAudioFrameCount(frameCount) return buffer } diff --git a/Tests/AudioSnapshotTestingTests/__AudioSnapshots__/AudioSnapshotTestingTests/multiChannelComparison.4ch.caf b/Tests/AudioSnapshotTestingTests/__AudioSnapshots__/AudioSnapshotTestingTests/multiChannelComparison.4ch.caf new file mode 100644 index 0000000..a989229 Binary files /dev/null and b/Tests/AudioSnapshotTestingTests/__AudioSnapshots__/AudioSnapshotTestingTests/multiChannelComparison.4ch.caf differ