@preconcurrency import AVFoundation import Foundation final class BufferConverter { private final class Box: @unchecked Sendable { var value: T; init(_ value: T) { self.value = value } } enum ConverterError: Swift.Error { case failedToCreateConverter case failedToCreateConversionBuffer case conversionFailed(NSError?) } private var converter: AVAudioConverter? func convert(_ buffer: AVAudioPCMBuffer, to format: AVAudioFormat) throws -> AVAudioPCMBuffer { let inputFormat = buffer.format if inputFormat == format { return buffer } if converter == nil || converter?.outputFormat != format { converter = AVAudioConverter(from: inputFormat, to: format) converter?.primeMethod = .none } guard let converter else { throw ConverterError.failedToCreateConverter } let sampleRateRatio = converter.outputFormat.sampleRate / converter.inputFormat.sampleRate let scaledInputFrameLength = Double(buffer.frameLength) * sampleRateRatio let frameCapacity = AVAudioFrameCount(scaledInputFrameLength.rounded(.up)) guard let conversionBuffer = AVAudioPCMBuffer(pcmFormat: converter.outputFormat, frameCapacity: frameCapacity) else { throw ConverterError.failedToCreateConversionBuffer } var nsError: NSError? let consumed = Box(false) let inputBuffer = buffer let status = converter.convert(to: conversionBuffer, error: &nsError) { _, statusPtr in if consumed.value { statusPtr.pointee = .noDataNow return nil } consumed.value = true statusPtr.pointee = .haveData return inputBuffer } if status == .error { throw ConverterError.conversionFailed(nsError) } return conversionBuffer } }