import AVFoundation /// The AudioCodec translate audio data to another format. /// - seealso: https://developer.apple.com/library/ios/technotes/tn2236/_index.html final class AudioCodec { static let defaultFrameCapacity: UInt32 = 1024 static let defaultInputBuffersCursor = 0 var settings: AudioCodecSettings = .default { didSet { if settings.invalidateConverter(oldValue) { inputFormat = nil } else { settings.apply(audioConverter, oldValue: oldValue) } } } var outputFormat: AVAudioFormat? { return audioConverter?.outputFormat } @AsyncStreamedFlow var outputStream: AsyncStream<(AVAudioBuffer, AVAudioTime)> /// This instance is running to process(true) or not(false). private(set) var isRunning = false private(set) var inputFormat: AVAudioFormat? { didSet { guard inputFormat != oldValue else { return } inputBuffers.removeAll() inputBuffersCursor = Self.defaultInputBuffersCursor outputBuffers.removeAll() audioConverter = makeAudioConverter() for _ in 0.. AVAudioBuffer? { guard let inputFormat else { return nil } switch inputFormat.formatDescription.mediaSubType { case .linearPCM: let buffer = AVAudioPCMBuffer(pcmFormat: inputFormat, frameCapacity: Self.defaultFrameCapacity) buffer?.frameLength = Self.defaultFrameCapacity return buffer default: return AVAudioCompressedBuffer(format: inputFormat, packetCapacity: 1, maximumPacketSize: 1024) } } private func makeAudioConverter() -> AVAudioConverter? { guard let inputFormat, let outputFormat = settings.format.makeOutputAudioFormat(inputFormat, sampleRate: settings.sampleRate, channelMap: settings.channelMap) else { return nil } let converter = AVAudioConverter(from: inputFormat, to: outputFormat) settings.apply(converter, oldValue: nil) if inputFormat.formatDescription.mediaSubType == .linearPCM { ringBuffer = AudioRingBuffer(inputFormat) } if self.outputFormat?.sampleRate != outputFormat.sampleRate { audioTime.reset() } if logger.isEnabledFor(level: .info) { logger.info("converter:", converter ?? "nil", ",inputFormat:", inputFormat, ",outputFormat:", outputFormat) } return converter } } extension AudioCodec: Codec { // MARK: Codec typealias Buffer = AVAudioBuffer var outputBuffer: AVAudioBuffer { guard let outputFormat = audioConverter?.outputFormat else { return .init() } if outputBuffers.isEmpty { for _ in 0..