@@ -2,6 +2,77 @@ import AVFoundation
22import Accelerate
33import Foundation
44import OSLog
5+ import os
6+
7+ private struct PCMBufferSnapshot : Sendable {
8+ struct Plane : Sendable {
9+ let data : Data
10+ let byteSize : UInt32
11+ }
12+
13+ let streamDescription : AudioStreamBasicDescription
14+ let planes : [ Plane ]
15+ let frameLength : AVAudioFrameCount
16+
17+ init ? ( source buffer: AVAudioPCMBuffer ) {
18+ streamDescription = buffer. format. streamDescription. pointee
19+ frameLength = buffer. frameLength
20+
21+ let audioBuffers = UnsafeMutableAudioBufferListPointer ( buffer. mutableAudioBufferList)
22+ var collected : [ Plane ] = [ ]
23+ collected. reserveCapacity ( audioBuffers. count)
24+
25+ for audioBuffer in audioBuffers {
26+ let byteCount = Int ( audioBuffer. mDataByteSize)
27+ let planeData : Data
28+ if let baseAddress = audioBuffer. mData, byteCount > 0 {
29+ planeData = Data ( bytes: baseAddress, count: byteCount)
30+ } else {
31+ planeData = Data ( )
32+ }
33+ collected. append ( Plane ( data: planeData, byteSize: audioBuffer. mDataByteSize) )
34+ }
35+
36+ if collected. isEmpty && frameLength > 0 {
37+ return nil
38+ }
39+
40+ planes = collected
41+ }
42+
43+ func makeBuffer( ) -> AVAudioPCMBuffer ? {
44+ var descriptionCopy = streamDescription
45+ guard let format = AVAudioFormat ( streamDescription: & descriptionCopy) else {
46+ return nil
47+ }
48+
49+ let capacity = max ( frameLength, 1 )
50+ guard let buffer = AVAudioPCMBuffer ( pcmFormat: format, frameCapacity: capacity) else {
51+ return nil
52+ }
53+
54+ buffer. frameLength = frameLength
55+
56+ let audioBuffers = UnsafeMutableAudioBufferListPointer ( buffer. mutableAudioBufferList)
57+ guard audioBuffers. count == planes. count else {
58+ return nil
59+ }
60+
61+ for (index, plane) in planes. enumerated ( ) {
62+ audioBuffers [ index] . mDataByteSize = plane. byteSize
63+ guard plane. byteSize > 0 else { continue }
64+ plane. data. withUnsafeBytes { rawBytes in
65+ guard
66+ let srcBase = rawBytes. baseAddress,
67+ let dstBase = audioBuffers [ index] . mData
68+ else { return }
69+ memcpy ( dstBase, srcBase, rawBytes. count)
70+ }
71+ }
72+
73+ return buffer
74+ }
75+ }
576
677/// Converts audio buffers to the format required by ASR (16kHz, mono, Float32).
778///
@@ -92,17 +163,30 @@ final public class AudioConverter {
92163 var aggregated : [ Float ] = [ ]
93164 aggregated. reserveCapacity ( Int ( estimatedOutputFrames) )
94165
95- // Provide input once, then signal end-of-stream
96- var provided = false
166+ guard let snapshot = PCMBufferSnapshot ( source: buffer) else {
167+ throw AudioConverterError . failedToCreateBuffer
168+ }
169+
170+ // Provide input once, then signal end-of-stream (guarded for strict concurrency)
171+ let inputProvidedFlag = OSAllocatedUnfairLock ( initialState: false )
97172 let inputBlock : AVAudioConverterInputBlock = { _, status in
98- if !provided {
99- provided = true
100- status. pointee = . haveData
101- return buffer
102- } else {
173+ let shouldProvide = inputProvidedFlag. withLock { state -> Bool in
174+ if state {
175+ return false
176+ }
177+ state = true
178+ return true
179+ }
180+ guard shouldProvide else {
181+ status. pointee = . endOfStream
182+ return nil
183+ }
184+ guard let nextBuffer = snapshot. makeBuffer ( ) else {
103185 status. pointee = . endOfStream
104186 return nil
105187 }
188+ status. pointee = . haveData
189+ return nextBuffer
106190 }
107191
108192 var error : NSError ?
0 commit comments