From 9a053d9ed82b2b15006cc80cf9811791e66e0511 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Wed, 24 Sep 2025 22:43:14 +0800 Subject: [PATCH] fixed: mic redirection not working on iOS 12 --- VoidLink/Input/MicHandler.swift | 91 +++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/VoidLink/Input/MicHandler.swift b/VoidLink/Input/MicHandler.swift index 3d58d738..23db0953 100644 --- a/VoidLink/Input/MicHandler.swift +++ b/VoidLink/Input/MicHandler.swift @@ -23,7 +23,8 @@ public class MicHandler: NSObject { private var isRecording = false private var useBuiltinMic = false - private var pcm16Buffer = Deque() + private var pcm16BufferDeque = Deque() + private var pcm16BufferArray: [Int16] = [] private let bufferQueue = DispatchQueue(label: "pcm.buffer.queue") private var timer: SafeTimer? @@ -201,21 +202,38 @@ public class MicHandler: NSObject { } } - private func sendOpusFrame() { + private func sendOpusFrameFromDequeBuffer() { bufferQueue.sync { - if pcm16Buffer.count >= 960 { - let chunk = Array(pcm16Buffer.prefix(960)) + if pcm16BufferDeque.count >= 960 { + let chunk = Array(pcm16BufferDeque.prefix(960)) var packet = [UInt8](repeating: 0, count: 4000) guard let enc = self.opusEncoder else {return} let outBytes = opus_encode(enc, chunk, 960, &packet, Int32(packet.count)) sendMicrophoneData(packet, outBytes) - let removeCount = min(960, pcm16Buffer.count) + let removeCount = min(960, pcm16BufferDeque.count) if removeCount > 0 { - pcm16Buffer.removeFirst(removeCount) + pcm16BufferDeque.removeFirst(removeCount) } } } } + + private func sendOpusFrameFromArrayBuffer() { + bufferQueue.sync { + if pcm16BufferArray.count >= 960 { + let chunk = Array(pcm16BufferArray.prefix(960)) + var packet = [UInt8](repeating: 0, count: 4000) + guard let enc = self.opusEncoder else {return} + let outBytes = opus_encode(enc, chunk, 960, &packet, Int32(packet.count)) + sendMicrophoneData(packet, outBytes) + let removeCount = min(960, pcm16BufferArray.count) + if removeCount > 0 { + pcm16BufferArray.removeFirst(removeCount) + } + } + } + } + private func configureEngine() throws { let input = engine.inputNode @@ -232,9 +250,6 @@ public class MicHandler: NSObject { try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(0.02) let sinkNode = AVAudioSinkNode { timestamp, frameCount, audioBufferList -> OSStatus in - // 访问 AudioBufferList 数据 - // print("sink interval: \((CACurrentMediaTime()-self.globalTimestamp)*1000),frameCount: \(frameCount)") - // self.globalTimestamp = CACurrentMediaTime() guard self.isRecording else { return noErr} @@ -250,7 +265,7 @@ public class MicHandler: NSObject { } // 追加到缓冲区 - self.pcm16Buffer.append(contentsOf: chunk) + self.pcm16BufferDeque.append(contentsOf: chunk) return noErr } @@ -261,44 +276,54 @@ public class MicHandler: NSObject { // 开定时器,每 20ms 触发一次 self.timer = SafeTimer(interval:0.02, delay: 0.05) { - self.sendOpusFrame() + self.sendOpusFrameFromDequeBuffer() } } else{ engine.attach(playerNode) engine.connect(playerNode, to: engine.mainMixerNode, format: micInputFormat) - input.installTap(onBus: 0, bufferSize: 5760, format: micInputFormat) { [weak self] buffer, _ in guard let self = self, self.isRecording else { return } - DispatchQueue.global().async { - let pcm16Buffer = AVAudioPCMBuffer(pcmFormat: pcm16Format!, frameCapacity: buffer.frameCapacity) - try? converter?.convert(to: pcm16Buffer!, from: buffer) - // 处理转换后的 outputBuffer - - // =============================== - // 🔹 改动 1:把 buffer 转成 PCM16 并保存 - let frameLength = Int(buffer.frameLength) - let channels = Int(buffer.format.channelCount) - var pcm16InterleavedBuffer = [Int16](repeating: 0, count: frameLength * channels) - + let pcm16Buffer = AVAudioPCMBuffer(pcmFormat: pcm16Format!, frameCapacity: buffer.frameCapacity) + try? converter?.convert(to: pcm16Buffer!, from: buffer) + + // =============================== + // 把 buffer 转成 PCM16 并保存 + let frameLength = Int(buffer.frameLength) + let channels = Int(buffer.format.channelCount) + var pcm16InterleavedBuffer = [Int16](repeating: 0, count: frameLength * channels) + + if let floatPtrs = buffer.floatChannelData { for ch in 0.. Float(Int16.max) { + clipped = Float(Int16.max) + } else if scaled < Float(Int16.min) { + clipped = Float(Int16.min) + } else { + clipped = scaled } + pcm16InterleavedBuffer[i * channels + ch] = Int16(clipped) } } - // 🔹 新增:Opus 编码并存入缓存 - if let enc = self.opusEncoder { - var packet = [UInt8](repeating: 0, count: 8000) - let outBytes = opus_encode(enc, pcm16InterleavedBuffer, Int32(frameLength), &packet, Int32(packet.count)) - sendMicrophoneData(packet, outBytes) - } } + // 现在 pcm16InterleavedBuffer 里就是转换后的 Int16 数据 + self.pcm16BufferArray.append(contentsOf: pcm16InterleavedBuffer) + } + + // 开定时器,每 20ms 触发一次 + self.timer = SafeTimer(interval:0.02, delay: 0.05) { + self.sendOpusFrameFromArrayBuffer() } } - + engine.prepare() try engine.start() }