Improve recorder device change handling

This commit is contained in:
Beingpax 2025-12-21 13:04:35 +05:45
parent 6ab3705123
commit 2944e4ce5c
3 changed files with 8 additions and 91 deletions

View File

@ -29,32 +29,6 @@ class AudioEngineRecorder: ObservableObject {
// Callback to notify parent class of runtime recording errors
var onRecordingError: ((Error) -> Void)?
init() {
setupNotifications()
}
private func setupNotifications() {
NotificationCenter.default.addObserver(
self,
selector: #selector(handleConfigurationChange),
name: .AVAudioEngineConfigurationChange,
object: nil
)
}
@objc private func handleConfigurationChange(notification: Notification) {
Task { @MainActor in
guard isRecording else { return }
logger.info("⚠️ AVAudioEngine configuration change detected (e.g. sample rate change). Restarting engine...")
do {
try restartRecordingPreservingFile()
} catch {
logger.error("Failed to recover from configuration change: \(error.localizedDescription)")
stopRecording()
}
}
}
func startRecording(toOutputFile url: URL) throws {
stopRecording()
@ -131,52 +105,6 @@ class AudioEngineRecorder: ObservableObject {
}
}
private func restartRecordingPreservingFile() throws {
if let input = inputNode {
input.removeTap(onBus: tapBusNumber)
}
audioEngine?.stop()
// Drain queue to prevent old-format buffers racing with new converter
audioProcessingQueue.sync { }
let engine = AVAudioEngine()
audioEngine = engine
let input = engine.inputNode
inputNode = input
let inputFormat = input.outputFormat(forBus: tapBusNumber)
logger.info("Restarting with new input format - Sample Rate: \(inputFormat.sampleRate)")
guard inputFormat.sampleRate > 0 else {
throw AudioEngineRecorderError.invalidInputFormat
}
guard let format = recordingFormat else {
throw AudioEngineRecorderError.invalidRecordingFormat
}
guard let newConverter = AVAudioConverter(from: inputFormat, to: format) else {
throw AudioEngineRecorderError.failedToCreateConverter
}
fileWriteLock.lock()
converter = newConverter
fileWriteLock.unlock()
input.installTap(onBus: tapBusNumber, bufferSize: tapBufferSize, format: inputFormat) { [weak self] (buffer, time) in
guard let self = self else { return }
self.audioProcessingQueue.async {
self.processAudioBuffer(buffer)
}
}
engine.prepare()
try engine.start()
logger.info("✅ Audio engine successfully restarted after configuration change")
}
func stopRecording() {
guard isRecording else {
return
@ -307,10 +235,6 @@ class AudioEngineRecorder: ObservableObject {
var currentRecordingURL: URL? {
return recordingURL
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
// MARK: - Error Types

View File

@ -36,20 +36,15 @@ class Recorder: NSObject, ObservableObject {
private func handleDeviceChange() async {
guard !isReconfiguring else { return }
isReconfiguring = true
if recorder != nil {
let currentURL = recorder?.currentRecordingURL
stopRecording()
if let url = currentURL {
do {
try await startRecording(toOutputFile: url)
} catch {
logger.error("❌ Failed to restart recording after device change: \(error.localizedDescription)")
}
}
try? await Task.sleep(nanoseconds: 200_000_000)
await MainActor.run {
NotificationCenter.default.post(name: .toggleMiniRecorder, object: nil)
}
isReconfiguring = false
}

View File

@ -526,8 +526,6 @@ class AudioDeviceManager: ObservableObject {
}
private func notifyDeviceChange() {
if !isRecordingActive {
NotificationCenter.default.post(name: NSNotification.Name("AudioDeviceChanged"), object: nil)
}
NotificationCenter.default.post(name: NSNotification.Name("AudioDeviceChanged"), object: nil)
}
}