fix: own system audio buffers before conversion
This commit is contained in:
@@ -276,7 +276,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 20;
|
||||
CURRENT_PROJECT_VERSION = 21;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"meetingnotes/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = G9LVHZAJNX;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -290,7 +290,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||
MARKETING_VERSION = 1.1.8;
|
||||
MARKETING_VERSION = 1.1.9;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D ENABLE_TCC_SPI";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.jamesbone.meetingnotes;
|
||||
@@ -312,7 +312,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 20;
|
||||
CURRENT_PROJECT_VERSION = 21;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"meetingnotes/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = G9LVHZAJNX;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -326,7 +326,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||
MARKETING_VERSION = 1.1.8;
|
||||
MARKETING_VERSION = 1.1.9;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D ENABLE_TCC_SPI";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.jamesbone.meetingnotes;
|
||||
|
||||
@@ -309,7 +309,7 @@ final class AudioManager: NSObject, ObservableObject {
|
||||
try tap.run(on: tapQueue) { [weak self] _, inputData, _, _, _ in
|
||||
guard let self else { return }
|
||||
self.processAudioBuffer(
|
||||
{ AVAudioPCMBuffer(pcmFormat: inputFormat, bufferListNoCopy: inputData, deallocator: nil) },
|
||||
{ self.copyAudioBuffer(from: inputData, format: inputFormat) },
|
||||
converter: converter,
|
||||
targetFormat: targetFormat,
|
||||
source: .system
|
||||
@@ -319,6 +319,42 @@ final class AudioManager: NSObject, ObservableObject {
|
||||
Task { await self.restartSystemAudioTap() }
|
||||
}
|
||||
}
|
||||
|
||||
private func copyAudioBuffer(
|
||||
from inputData: UnsafePointer<AudioBufferList>,
|
||||
format: AVAudioFormat
|
||||
) -> AVAudioPCMBuffer? {
|
||||
guard let borrowedBuffer = AVAudioPCMBuffer(
|
||||
pcmFormat: format,
|
||||
bufferListNoCopy: inputData,
|
||||
deallocator: nil
|
||||
), borrowedBuffer.frameLength > 0,
|
||||
let ownedBuffer = AVAudioPCMBuffer(
|
||||
pcmFormat: format,
|
||||
frameCapacity: borrowedBuffer.frameLength
|
||||
) else { return nil }
|
||||
|
||||
ownedBuffer.frameLength = borrowedBuffer.frameLength
|
||||
let sourceBuffers = UnsafeMutableAudioBufferListPointer(
|
||||
UnsafeMutablePointer(mutating: inputData)
|
||||
)
|
||||
let destinationBuffers = UnsafeMutableAudioBufferListPointer(
|
||||
ownedBuffer.mutableAudioBufferList
|
||||
)
|
||||
guard sourceBuffers.count == destinationBuffers.count else { return nil }
|
||||
|
||||
for index in 0..<sourceBuffers.count {
|
||||
let source = sourceBuffers[index]
|
||||
let destination = destinationBuffers[index]
|
||||
let byteCount = Int(source.mDataByteSize)
|
||||
guard byteCount <= Int(destination.mDataByteSize),
|
||||
let sourceData = source.mData,
|
||||
let destinationData = destination.mData else { return nil }
|
||||
memcpy(destinationData, sourceData, byteCount)
|
||||
destinationBuffers[index].mDataByteSize = source.mDataByteSize
|
||||
}
|
||||
return ownedBuffer
|
||||
}
|
||||
|
||||
private func processAudioBuffer(
|
||||
_ inputBufferProvider: () -> AVAudioPCMBuffer?,
|
||||
@@ -326,8 +362,8 @@ final class AudioManager: NSObject, ObservableObject {
|
||||
targetFormat: AVAudioFormat,
|
||||
source: AudioSource
|
||||
) {
|
||||
// Keep callback-owned buffers alive until conversion finishes. Teardown
|
||||
// takes this same lock before invalidating the Core Audio process tap.
|
||||
// The system callback copies its borrowed Core Audio memory while this
|
||||
// lock prevents teardown, then conversion operates on the owned copy.
|
||||
audioFileLock.lock()
|
||||
defer { audioFileLock.unlock() }
|
||||
guard isAcceptingAudio,
|
||||
|
||||
Reference in New Issue
Block a user