Fix recording state and cleanup flow
This commit is contained in:
parent
f02344e5d6
commit
4decbdc018
@ -150,11 +150,17 @@ extension WhisperState {
|
||||
// MARK: - Resource Management
|
||||
|
||||
func cleanupModelResources() async {
|
||||
if !isRecording && !isProcessing {
|
||||
// Only cleanup resources if we're not actively using them
|
||||
let canCleanup = !isRecording && !isProcessing
|
||||
|
||||
if canCleanup {
|
||||
logger.notice("🧹 Cleaning up Whisper resources")
|
||||
// Release any resources held by the model
|
||||
await whisperContext?.releaseResources()
|
||||
whisperContext = nil
|
||||
isModelLoaded = false
|
||||
} else {
|
||||
logger.info("Skipping cleanup while recording or processing is active")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,10 +25,10 @@ extension WhisperState {
|
||||
}
|
||||
|
||||
func hideRecorderPanel() {
|
||||
if isRecording {
|
||||
Task {
|
||||
await toggleRecord()
|
||||
}
|
||||
if recorderType == "notch" {
|
||||
notchWindowManager?.hide()
|
||||
} else {
|
||||
miniWindowManager?.hide()
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,33 +36,31 @@ extension WhisperState {
|
||||
|
||||
func toggleMiniRecorder() async {
|
||||
if isMiniRecorderVisible {
|
||||
await dismissMiniRecorder()
|
||||
} else {
|
||||
Task {
|
||||
if isRecording {
|
||||
await toggleRecord()
|
||||
|
||||
SoundManager.shared.playStartSound()
|
||||
|
||||
await MainActor.run {
|
||||
showRecorderPanel()
|
||||
isMiniRecorderVisible = true
|
||||
}
|
||||
} else {
|
||||
await cancelRecording()
|
||||
}
|
||||
} else {
|
||||
SoundManager.shared.playStartSound()
|
||||
|
||||
await MainActor.run {
|
||||
isMiniRecorderVisible = true
|
||||
}
|
||||
|
||||
await toggleRecord()
|
||||
}
|
||||
}
|
||||
|
||||
func dismissMiniRecorder() async {
|
||||
logger.notice("📱 Dismissing \(self.recorderType) recorder")
|
||||
shouldCancelRecording = true
|
||||
|
||||
if isRecording {
|
||||
await recorder.stopRecording()
|
||||
}
|
||||
|
||||
if recorderType == "notch" {
|
||||
notchWindowManager?.hide()
|
||||
} else {
|
||||
miniWindowManager?.hide()
|
||||
}
|
||||
hideRecorderPanel()
|
||||
|
||||
await MainActor.run {
|
||||
isRecording = false
|
||||
@ -79,11 +77,8 @@ extension WhisperState {
|
||||
}
|
||||
|
||||
func cancelRecording() async {
|
||||
shouldCancelRecording = true
|
||||
SoundManager.shared.playEscSound()
|
||||
if isRecording {
|
||||
await recorder.stopRecording()
|
||||
}
|
||||
shouldCancelRecording = true
|
||||
await dismissMiniRecorder()
|
||||
}
|
||||
|
||||
@ -95,26 +90,12 @@ extension WhisperState {
|
||||
}
|
||||
|
||||
@objc public func handleToggleMiniRecorder() {
|
||||
if isMiniRecorderVisible {
|
||||
Task {
|
||||
await toggleRecord()
|
||||
}
|
||||
} else {
|
||||
Task {
|
||||
await toggleRecord()
|
||||
|
||||
SoundManager.shared.playStartSound()
|
||||
|
||||
await MainActor.run {
|
||||
showRecorderPanel()
|
||||
isMiniRecorderVisible = true
|
||||
}
|
||||
}
|
||||
Task {
|
||||
await toggleMiniRecorder()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleLicenseStatusChanged() {
|
||||
// This will refresh the license state when it changes elsewhere in the app
|
||||
self.licenseViewModel = LicenseViewModel()
|
||||
}
|
||||
}
|
||||
@ -117,19 +117,24 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
func toggleRecord() async {
|
||||
if isRecording {
|
||||
logger.notice("🛑 Stopping recording")
|
||||
|
||||
await MainActor.run {
|
||||
isRecording = false
|
||||
isVisualizerActive = false
|
||||
}
|
||||
|
||||
await recorder.stopRecording()
|
||||
isRecording = false
|
||||
isVisualizerActive = false
|
||||
|
||||
if let recordedFile {
|
||||
let duration = Date().timeIntervalSince(transcriptionStartTime ?? Date())
|
||||
await transcribeAudio(recordedFile, duration: duration)
|
||||
if !shouldCancelRecording {
|
||||
await transcribeAudio(recordedFile, duration: duration)
|
||||
}
|
||||
} else {
|
||||
logger.error("❌ No recorded file found after stopping recording")
|
||||
}
|
||||
} else {
|
||||
// Validate that a model is selected before allowing recording to start
|
||||
guard currentModel != nil else {
|
||||
// Show an alert to the user
|
||||
await MainActor.run {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "No Whisper Model Selected"
|
||||
@ -141,6 +146,8 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
shouldCancelRecording = false
|
||||
|
||||
logger.notice("🎙️ Starting recording")
|
||||
requestRecordPermission { [self] granted in
|
||||
if granted {
|
||||
@ -152,11 +159,17 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
create: true)
|
||||
.appending(path: "output.wav")
|
||||
|
||||
// Start recording setup and window configuration in parallel
|
||||
self.recordedFile = file
|
||||
self.transcriptionStartTime = Date()
|
||||
|
||||
await MainActor.run {
|
||||
self.isRecording = true
|
||||
self.isVisualizerActive = true
|
||||
}
|
||||
|
||||
async let recordingTask = self.recorder.startRecording(toOutputFile: file, delegate: self)
|
||||
async let windowConfigTask = ActiveWindowService.shared.applyConfigurationForCurrentApp()
|
||||
|
||||
// Start model loading in parallel if needed
|
||||
async let modelLoadingTask: Void = {
|
||||
if let currentModel = await self.currentModel, await self.whisperContext == nil {
|
||||
logger.notice("🔄 Loading model in parallel with recording: \(currentModel.name)")
|
||||
@ -171,29 +184,23 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for recording and window configuration
|
||||
try await recordingTask
|
||||
await windowConfigTask
|
||||
|
||||
self.isRecording = true
|
||||
self.isVisualizerActive = true
|
||||
self.recordedFile = file
|
||||
self.transcriptionStartTime = Date()
|
||||
|
||||
// After recording and window config are done, handle enhancement service
|
||||
if let enhancementService = self.enhancementService,
|
||||
enhancementService.isEnhancementEnabled &&
|
||||
enhancementService.useScreenCaptureContext {
|
||||
await enhancementService.captureScreenContext()
|
||||
}
|
||||
|
||||
// Wait for model loading to complete (this won't block the UI)
|
||||
await modelLoadingTask
|
||||
|
||||
} catch {
|
||||
self.messageLog += "\(error.localizedDescription)\n"
|
||||
self.isRecording = false
|
||||
self.isVisualizerActive = false
|
||||
await MainActor.run {
|
||||
self.messageLog += "\(error.localizedDescription)\n"
|
||||
self.isRecording = false
|
||||
self.isVisualizerActive = false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -235,12 +242,28 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
}
|
||||
|
||||
private func onDidFinishRecording(success: Bool) {
|
||||
isRecording = false
|
||||
if !success {
|
||||
messageLog += "Recording did not finish successfully\n"
|
||||
}
|
||||
}
|
||||
|
||||
private func transcribeAudio(_ url: URL, duration: TimeInterval) async {
|
||||
if shouldCancelRecording { return }
|
||||
|
||||
await MainActor.run {
|
||||
isProcessing = true
|
||||
isTranscribing = true
|
||||
canTranscribe = false
|
||||
}
|
||||
|
||||
defer {
|
||||
if shouldCancelRecording {
|
||||
Task {
|
||||
await cleanupModelResources()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard let currentModel = currentModel else {
|
||||
logger.error("❌ Cannot transcribe: No model selected")
|
||||
messageLog += "Cannot transcribe: No model selected.\n"
|
||||
@ -256,7 +279,6 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
logger.error("❌ Failed to load model: \(currentModel.name) - \(error.localizedDescription)")
|
||||
messageLog += "Failed to load transcription model. Please try again.\n"
|
||||
currentError = .modelLoadFailed
|
||||
await cleanupModelResources()
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -270,47 +292,30 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
|
||||
logger.notice("🔄 Starting transcription with model: \(currentModel.name)")
|
||||
do {
|
||||
isProcessing = true
|
||||
isTranscribing = true
|
||||
canTranscribe = false
|
||||
|
||||
let permanentURL = try saveRecordingPermanently(url)
|
||||
let permanentURLString = permanentURL.absoluteString
|
||||
|
||||
if shouldCancelRecording {
|
||||
await cleanupModelResources()
|
||||
return
|
||||
}
|
||||
if shouldCancelRecording { return }
|
||||
|
||||
messageLog += "Reading wave samples...\n"
|
||||
let data = try readAudioSamples(url)
|
||||
|
||||
if shouldCancelRecording {
|
||||
await cleanupModelResources()
|
||||
return
|
||||
}
|
||||
if shouldCancelRecording { return }
|
||||
|
||||
messageLog += "Transcribing data using \(currentModel.name) model...\n"
|
||||
messageLog += "Setting prompt: \(whisperPrompt.transcriptionPrompt)\n"
|
||||
await whisperContext.setPrompt(whisperPrompt.transcriptionPrompt)
|
||||
|
||||
if shouldCancelRecording {
|
||||
await cleanupModelResources()
|
||||
return
|
||||
}
|
||||
if shouldCancelRecording { return }
|
||||
|
||||
await whisperContext.fullTranscribe(samples: data)
|
||||
|
||||
if shouldCancelRecording {
|
||||
await cleanupModelResources()
|
||||
return
|
||||
}
|
||||
if shouldCancelRecording { return }
|
||||
|
||||
var text = await whisperContext.getTranscription()
|
||||
text = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
logger.notice("✅ Transcription completed successfully, length: \(text.count) characters")
|
||||
|
||||
// Apply word replacements if enabled
|
||||
if UserDefaults.standard.bool(forKey: "IsWordReplacementEnabled") {
|
||||
text = WordReplacementService.shared.applyReplacements(to: text)
|
||||
logger.notice("✅ Word replacements applied")
|
||||
@ -320,10 +325,7 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
enhancementService.isEnhancementEnabled,
|
||||
enhancementService.isConfigured {
|
||||
do {
|
||||
if shouldCancelRecording {
|
||||
await cleanupModelResources()
|
||||
return
|
||||
}
|
||||
if shouldCancelRecording { return }
|
||||
|
||||
messageLog += "Enhancing transcription with AI...\n"
|
||||
let enhancedText = try await enhancementService.enhance(text)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user