// Multi-channel input → mono fold (SessionAudio.foldToMono): the fix for a mic on one channel of // a multi-channel interface. AVAudioConverter's default N→stereo downmix grabs channels 0/1 — dead // silence when the mic sits higher up — so we fold ourselves. This pins the fiddly bits (the // interleaved stride, channel pinning, the sum-clamp) against regressions without needing hardware. #if !os(tvOS) import XCTest @testable import PunktfunkKit final class AudioChannelFoldTests: XCTestCase { /// Drive `foldToMono` over channel data expressed as `[[Float]]`, mirroring the two /// `floatChannelData` layouts: /// - deinterleaved: each inner array is one channel (all `frames` long). /// - interleaved: a single inner array already interleaved (c0f0, c1f0, …), with the real /// channel count passed separately. private func fold( _ planes: [[Float]], frames: Int, channels: Int, interleaved: Bool, pinned: Int? ) -> [Float] { // One C buffer per plane + a table of pointers to them — the shape of floatChannelData. let buffers: [UnsafeMutablePointer] = planes.map { plane in let p = UnsafeMutablePointer.allocate(capacity: plane.count) for i in 0..>.allocate( capacity: buffers.count) for (i, b) in buffers.enumerated() { table[i] = b } let out = UnsafeMutablePointer.allocate(capacity: frames) defer { buffers.forEach { $0.deallocate() } table.deallocate() out.deallocate() } SessionAudio.foldToMono( input: table, frames: frames, channels: channels, interleaved: interleaved, pinned: pinned, out: out) return (0..