Skip to content

Commit bde2983

Browse files
committed
warpper for snapshot converter
1 parent 5799571 commit bde2983

1 file changed

Lines changed: 91 additions & 7 deletions

File tree

Sources/FluidAudio/Shared/AudioConverter.swift

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,77 @@ import AVFoundation
22
import Accelerate
33
import Foundation
44
import 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

Comments
 (0)