From 4bc36019c2a4983a2f80b00762ff63ced1b36814 Mon Sep 17 00:00:00 2001 From: Nick Payne Date: Thu, 10 Jul 2025 20:43:17 +0100 Subject: [PATCH] get rid of O(n) ops on hot audio packet path --- Sources/Core/AudioBuffer.swift | 68 ++++++++++++++++---------------- Sources/Core/AudioRecorder.swift | 6 +-- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/Sources/Core/AudioBuffer.swift b/Sources/Core/AudioBuffer.swift index 5c09f23..4d4bfc7 100644 --- a/Sources/Core/AudioBuffer.swift +++ b/Sources/Core/AudioBuffer.swift @@ -8,7 +8,6 @@ public class AudioBuffer { private var availableBytes: Int = 0 private let maxBufferSize: Int - // Pre-calculated values for efficiency private let bytesPerChunk: Int private let chunkDuration: Double @@ -24,7 +23,7 @@ public class AudioBuffer { let bytesPerSecond = Int(format.mSampleRate) * bytesPerFrame self.maxBufferSize = bytesPerSecond * 10 - // Pre-allocate ring buffer + // Pre-allocated ring buffer self.buffer = Array(repeating: 0, count: maxBufferSize) } @@ -37,10 +36,25 @@ public class AudioBuffer { return } - // Simple, clean, fast enough - for byte in data { - buffer[writeIndex] = byte - writeIndex = (writeIndex + 1) % maxBufferSize + data.withUnsafeBytes { bytes in + let sourceBytes = bytes.bindMemory(to: UInt8.self) + let dataSize = sourceBytes.count + + // Check if we can copy in one block (no wrap-around) + if writeIndex + dataSize <= maxBufferSize { + // only one write needed + buffer.replaceSubrange(writeIndex.. AudioPacket? { - guard availableBytes > 0 else { return nil } - - // Create Data from remaining bytes - var remainingData = Data(capacity: availableBytes) - for _ in 0.. AudioPacket? { // Check if we have enough data for a complete chunk guard availableBytes >= bytesPerChunk else { return nil } - // Extract chunk data - bounds-checked but still efficient var chunkData = Data(capacity: bytesPerChunk) - for _ in 0..