From aabbf03f603605d7d868c47571b20e29a9bac1ca Mon Sep 17 00:00:00 2001 From: Beingpax Date: Wed, 9 Apr 2025 21:37:29 +0545 Subject: [PATCH] Add Core ML alert for Whisper models to inform users about performance improvements and first-run optimization time --- VoiceInk.xcodeproj/project.pbxproj | 37 ++++++++++++++----- .../Whisper/WhisperState+ModelManager.swift | 37 +++++++++++++++++++ 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/VoiceInk.xcodeproj/project.pbxproj b/VoiceInk.xcodeproj/project.pbxproj index 619c3d5..ec08e22 100644 --- a/VoiceInk.xcodeproj/project.pbxproj +++ b/VoiceInk.xcodeproj/project.pbxproj @@ -7,11 +7,12 @@ objects = { /* Begin PBXBuildFile section */ - E136D0112DA3EE57000E1E8A /* whisper.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = E136D0102DA3EE57000E1E8A /* whisper.xcframework */; }; - E136D0122DA3EE57000E1E8A /* whisper.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E136D0102DA3EE57000E1E8A /* whisper.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E1A261122CC143AC00B233D1 /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = E1A261112CC143AC00B233D1 /* KeyboardShortcuts */; }; E1ADD45A2CC5352A00303ECB /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = E1ADD4592CC5352A00303ECB /* LaunchAtLogin */; }; E1ADD45F2CC544F100303ECB /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = E1ADD45E2CC544F100303ECB /* Sparkle */; }; + E1F5FA752DA6CBD700B1FD8A /* whisper.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = E136D0102DA3EE57000E1E8A /* whisper.xcframework */; }; + E1F5FA762DA6CBD700B1FD8A /* whisper.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E136D0102DA3EE57000E1E8A /* whisper.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E1F5FA7A2DA6CBF900B1FD8A /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = E1F5FA792DA6CBF900B1FD8A /* Zip */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -32,13 +33,13 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - E136D00D2DA3EBE0000E1E8A /* Embed Frameworks */ = { + E1F5FA772DA6CBD700B1FD8A /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - E136D0122DA3EE57000E1E8A /* whisper.xcframework in Embed Frameworks */, + E1F5FA762DA6CBD700B1FD8A /* whisper.xcframework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -75,10 +76,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E136D0112DA3EE57000E1E8A /* whisper.xcframework in Frameworks */, + E1F5FA752DA6CBD700B1FD8A /* whisper.xcframework in Frameworks */, E1ADD45A2CC5352A00303ECB /* LaunchAtLogin in Frameworks */, E1ADD45F2CC544F100303ECB /* Sparkle in Frameworks */, E1A261122CC143AC00B233D1 /* KeyboardShortcuts in Frameworks */, + E1F5FA7A2DA6CBF900B1FD8A /* Zip in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -138,7 +140,7 @@ E11473AC2CBE0F0A00318EE4 /* Sources */, E11473AD2CBE0F0A00318EE4 /* Frameworks */, E11473AE2CBE0F0A00318EE4 /* Resources */, - E136D00D2DA3EBE0000E1E8A /* Embed Frameworks */, + E1F5FA772DA6CBD700B1FD8A /* Embed Frameworks */, ); buildRules = ( ); @@ -152,6 +154,7 @@ E1A261112CC143AC00B233D1 /* KeyboardShortcuts */, E1ADD4592CC5352A00303ECB /* LaunchAtLogin */, E1ADD45E2CC544F100303ECB /* Sparkle */, + E1F5FA792DA6CBF900B1FD8A /* Zip */, ); productName = VoiceInk; productReference = E11473B02CBE0F0A00318EE4 /* VoiceInk.app */; @@ -239,6 +242,7 @@ E1A261102CC143AC00B233D1 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */, E1ADD4582CC5352A00303ECB /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */, E1ADD45D2CC544F100303ECB /* XCRemoteSwiftPackageReference "Sparkle" */, + E1F5FA782DA6CBF900B1FD8A /* XCRemoteSwiftPackageReference "Zip" */, ); preferredProjectObjectVersion = 77; productRefGroup = E11473B12CBE0F0A00318EE4 /* Products */; @@ -442,7 +446,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 120; + CURRENT_PROJECT_VERSION = 121; DEVELOPMENT_ASSET_PATHS = "\"VoiceInk/Preview Content\""; DEVELOPMENT_TEAM = V6J6A3VWY2; ENABLE_HARDENED_RUNTIME = YES; @@ -457,7 +461,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 1.20; + MARKETING_VERSION = 1.21; PRODUCT_BUNDLE_IDENTIFIER = com.prakashjoshipax.VoiceInk; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -475,7 +479,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 120; + CURRENT_PROJECT_VERSION = 121; DEVELOPMENT_ASSET_PATHS = "\"VoiceInk/Preview Content\""; DEVELOPMENT_TEAM = V6J6A3VWY2; ENABLE_HARDENED_RUNTIME = YES; @@ -490,7 +494,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 1.20; + MARKETING_VERSION = 1.21; PRODUCT_BUNDLE_IDENTIFIER = com.prakashjoshipax.VoiceInk; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -632,6 +636,14 @@ minimumVersion = 2.6.4; }; }; + E1F5FA782DA6CBF900B1FD8A /* XCRemoteSwiftPackageReference "Zip" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/marmelroy/Zip?tab=readme-ov-file"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.1.2; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -650,6 +662,11 @@ package = E1ADD45D2CC544F100303ECB /* XCRemoteSwiftPackageReference "Sparkle" */; productName = Sparkle; }; + E1F5FA792DA6CBF900B1FD8A /* Zip */ = { + isa = XCSwiftPackageProductDependency; + package = E1F5FA782DA6CBF900B1FD8A /* XCRemoteSwiftPackageReference "Zip" */; + productName = Zip; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = E11473A82CBE0F0A00318EE4 /* Project object */; diff --git a/VoiceInk/Whisper/WhisperState+ModelManager.swift b/VoiceInk/Whisper/WhisperState+ModelManager.swift index 13dd92c..dc496e0 100644 --- a/VoiceInk/Whisper/WhisperState+ModelManager.swift +++ b/VoiceInk/Whisper/WhisperState+ModelManager.swift @@ -116,9 +116,46 @@ extension WhisperState { } } + // Shows an alert about Core ML support and first-run optimization + private func showCoreMLAlert(for model: PredefinedModel, completion: @escaping () -> Void) { + Task { @MainActor in + let alert = NSAlert() + alert.messageText = "Core ML Support for \(model.displayName) Model" + alert.informativeText = "This Whisper model supports Core ML, which can improve performance by 2-4x on Apple Silicon devices.\n\nDuring the first run, it can take several minutes to optimize the model for your system. Subsequent runs will be much faster." + alert.alertStyle = .informational + alert.addButton(withTitle: "Download") + alert.addButton(withTitle: "Cancel") + + let response = alert.runModal() + if response == .alertFirstButtonReturn { + completion() + } + } + } + func downloadModel(_ model: PredefinedModel) async { guard let url = URL(string: model.downloadURL) else { return } + // Check if model supports Core ML (non-quantized models) + let supportsCoreML = !model.name.contains("q5") && !model.name.contains("q8") + + if supportsCoreML { + // Show the CoreML alert for models that support it + await MainActor.run { + showCoreMLAlert(for: model) { + // This completion handler is called when user clicks "Download" + Task { + await self.performModelDownload(model, url) + } + } + } + } else { + // Directly download the model if it doesn't support Core ML + await performModelDownload(model, url) + } + } + + private func performModelDownload(_ model: PredefinedModel, _ url: URL) async { do { let whisperModel = try await downloadMainModel(model, from: url)