diff --git a/.github/workflows/ios-perf.yml b/.github/workflows/ios-perf.yml index 272a493e..2805e7ae 100644 --- a/.github/workflows/ios-perf.yml +++ b/.github/workflows/ios-perf.yml @@ -29,8 +29,8 @@ jobs: device: [ios-perf] include: - device: ios-perf - initPerformanceThresholdSec: 1.5 - procPerformanceThresholdSec: 0.2 + initPerformanceThresholdSec: 2.5 + procPerformanceThresholdSec: 0.5 steps: - name: Checkout diff --git a/binding/ios/Cheetah-iOS.podspec b/binding/ios/Cheetah-iOS.podspec index cf7fa14e..b5616768 100644 --- a/binding/ios/Cheetah-iOS.podspec +++ b/binding/ios/Cheetah-iOS.podspec @@ -1,13 +1,13 @@ Pod::Spec.new do |s| s.name = 'Cheetah-iOS' s.module_name = 'Cheetah' - s.version = '1.1.0' + s.version = '2.0.0' s.license = {:type => 'Apache 2.0'} s.summary = 'iOS SDK for Picovoice\'s Cheetah speech-to-text engine.' - s.description = + s.description = <<-DESC Cheetah is an on-device streaming speech-to-text engine. - + Cheetah is: - Private, all voice processing runs locally. - Accurate @@ -23,8 +23,8 @@ Pod::Spec.new do |s| DESC s.homepage = 'https://github.com/Picovoice/cheetah/tree/master/binding/ios' s.author = { 'Picovoice' => 'hello@picovoice.ai' } - s.source = { :git => "https://github.com/Picovoice/cheetah.git", :tag => "Cheetah-iOS-v1.1.0" } - s.ios.deployment_target = '9.0' + s.source = { :git => "https://github.com/Picovoice/cheetah.git", :tag => "Cheetah-iOS-v2.0.0" } + s.ios.deployment_target = '13.0' s.swift_version = '5.0' s.vendored_frameworks = 'lib/ios/PvCheetah.xcframework' s.source_files = 'binding/ios/*.{swift}' diff --git a/binding/ios/Cheetah.swift b/binding/ios/Cheetah.swift index e9a201dc..b71e02c7 100644 --- a/binding/ios/Cheetah.swift +++ b/binding/ios/Cheetah.swift @@ -1,5 +1,5 @@ // -// Copyright 2022 Picovoice Inc. +// Copyright 2022-2023 Picovoice Inc. // You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" // file accompanying this source. // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on @@ -23,6 +23,12 @@ public class Cheetah { /// Current Cheetah version. public static let version = String(cString: pv_cheetah_version()) + private static var sdk = "ios" + + public static func setSdk(sdk: String) { + self.sdk = sdk + } + /// Constructor. /// /// - Parameters: @@ -52,13 +58,18 @@ public class Cheetah { throw CheetahInvalidArgumentError("EndpointDuration must be a non-negative number.") } + pv_set_sdk(Cheetah.sdk) + let status = pv_cheetah_init( accessKey, modelPathArg, endpointDuration, enableAutomaticPunctuation, &handle) - try checkStatus(status, "Cheetah init failed") + if status != PV_STATUS_SUCCESS { + let messageStack = try getMessageStack() + throw pvStatusToCheetahError(status, "Cheetah init failed", messageStack) + } } deinit { @@ -113,17 +124,20 @@ public class Cheetah { if pcm.count != Cheetah.frameLength { throw CheetahInvalidArgumentError( - "Frame of audio data must contain \(Cheetah.frameLength) samples - - given frame contained \(pcm.count)") + "Frame of audio data must contain \(Cheetah.frameLength) samples" + + " - given frame contained \(pcm.count)") } var cPartialTranscript: UnsafeMutablePointer? var endPoint = false let status = pv_cheetah_process(self.handle, pcm, &cPartialTranscript, &endPoint) - try checkStatus(status, "Cheetah process failed") + if status != PV_STATUS_SUCCESS { + let messageStack = try getMessageStack() + throw pvStatusToCheetahError(status, "Cheetah process failed", messageStack) + } let transcript = String(cString: cPartialTranscript!) - cPartialTranscript?.deallocate() + pv_cheetah_transcript_delete(cPartialTranscript!) return (transcript, endPoint) } @@ -139,10 +153,13 @@ public class Cheetah { var cFinalTranscript: UnsafeMutablePointer? let status = pv_cheetah_flush(self.handle, &cFinalTranscript) - try checkStatus(status, "Cheetah flush failed") + if status != PV_STATUS_SUCCESS { + let messageStack = try getMessageStack() + throw pvStatusToCheetahError(status, "Cheetah flush failed", messageStack) + } let transcript = String(cString: cFinalTranscript!) - cFinalTranscript?.deallocate() + pv_cheetah_transcript_delete(cFinalTranscript!) return transcript } @@ -161,42 +178,59 @@ public class Cheetah { } throw CheetahIOError( - "Could not find file at path '\(filePath)'. If this is a packaged asset, - ensure you have added it to your xcode project." + "Could not find file at path '\(filePath)'. If this is a packaged asset, " + + "ensure you have added it to your xcode project." ) } - private func checkStatus(_ status: pv_status_t, _ message: String) throws { - if status == PV_STATUS_SUCCESS { - return - } - + private func pvStatusToCheetahError( + _ status: pv_status_t, + _ message: String, + _ messageStack: [String] = []) -> CheetahError { switch status { case PV_STATUS_OUT_OF_MEMORY: - throw CheetahMemoryError(message) + return CheetahMemoryError(message, messageStack) case PV_STATUS_IO_ERROR: - throw CheetahIOError(message) + return CheetahIOError(message, messageStack) case PV_STATUS_INVALID_ARGUMENT: - throw CheetahInvalidArgumentError(message) + return CheetahInvalidArgumentError(message, messageStack) case PV_STATUS_STOP_ITERATION: - throw CheetahStopIterationError(message) + return CheetahStopIterationError(message, messageStack) case PV_STATUS_KEY_ERROR: - throw CheetahKeyError(message) + return CheetahKeyError(message, messageStack) case PV_STATUS_INVALID_STATE: - throw CheetahInvalidStateError(message) + return CheetahInvalidStateError(message, messageStack) case PV_STATUS_RUNTIME_ERROR: - throw CheetahRuntimeError(message) + return CheetahRuntimeError(message, messageStack) case PV_STATUS_ACTIVATION_ERROR: - throw CheetahActivationError(message) + return CheetahActivationError(message, messageStack) case PV_STATUS_ACTIVATION_LIMIT_REACHED: - throw CheetahActivationLimitError(message) + return CheetahActivationLimitError(message, messageStack) case PV_STATUS_ACTIVATION_THROTTLED: - throw CheetahActivationThrottledError(message) + return CheetahActivationThrottledError(message, messageStack) case PV_STATUS_ACTIVATION_REFUSED: - throw CheetahActivationRefusedError(message) + return CheetahActivationRefusedError(message, messageStack) default: let pvStatusString = String(cString: pv_status_to_string(status)) - throw CheetahError("\(pvStatusString): \(message)") + return CheetahError("\(pvStatusString): \(message)", messageStack) + } + } + + private func getMessageStack() throws -> [String] { + var messageStackRef: UnsafeMutablePointer?>? + var messageStackDepth: Int32 = 0 + let status = pv_get_error_stack(&messageStackRef, &messageStackDepth) + if status != PV_STATUS_SUCCESS { + throw pvStatusToCheetahError(status, "Unable to get Cheetah error state") } + + var messageStack: [String] = [] + for i in 0.. 0) + } + } } diff --git a/binding/ios/CheetahAppTest/Podfile b/binding/ios/CheetahAppTest/Podfile index f097b7a7..c7aba4e5 100644 --- a/binding/ios/CheetahAppTest/Podfile +++ b/binding/ios/CheetahAppTest/Podfile @@ -1,14 +1,14 @@ source 'https://cdn.cocoapods.org/' -platform :ios, '9.0' +platform :ios, '13.0' target 'CheetahAppTest' do - pod 'Cheetah-iOS', '~> 1.1.0' + pod 'Cheetah-iOS', :podspec => 'https://raw.githubusercontent.com/Picovoice/cheetah/v2.0-ios/binding/ios/Cheetah-iOS.podspec' end target 'CheetahAppTestUITests' do - pod 'Cheetah-iOS', '~> 1.1.0' + pod 'Cheetah-iOS', :podspec => 'https://raw.githubusercontent.com/Picovoice/cheetah/v2.0-ios/binding/ios/Cheetah-iOS.podspec' end target 'PerformanceTest' do - pod 'Cheetah-iOS', '~> 1.1.0' + pod 'Cheetah-iOS', :podspec => 'https://raw.githubusercontent.com/Picovoice/cheetah/v2.0-ios/binding/ios/Cheetah-iOS.podspec' end diff --git a/binding/ios/CheetahAppTest/Podfile.lock b/binding/ios/CheetahAppTest/Podfile.lock index 4fbf2fdd..a16ca7ce 100644 --- a/binding/ios/CheetahAppTest/Podfile.lock +++ b/binding/ios/CheetahAppTest/Podfile.lock @@ -1,16 +1,16 @@ PODS: - - Cheetah-iOS (1.1.0) + - Cheetah-iOS (2.0.0) DEPENDENCIES: - - Cheetah-iOS (~> 1.1.0) + - Cheetah-iOS (from `https://raw.githubusercontent.com/Picovoice/cheetah/v2.0-ios/binding/ios/Cheetah-iOS.podspec`) -SPEC REPOS: - trunk: - - Cheetah-iOS +EXTERNAL SOURCES: + Cheetah-iOS: + :podspec: https://raw.githubusercontent.com/Picovoice/cheetah/v2.0-ios/binding/ios/Cheetah-iOS.podspec SPEC CHECKSUMS: - Cheetah-iOS: 6fb7be693878f5b1dec0ea5b6534fbba30954afc + Cheetah-iOS: d98a5edcbf3b74dda6027aeac6a8c0f5997a47a2 -PODFILE CHECKSUM: be70602bbfa54a30183877dfe83545b7f63d2887 +PODFILE CHECKSUM: 813d31bb6f15922d6ea3eef044c962b3c3c8dfbe -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3 diff --git a/binding/ios/CheetahErrors.swift b/binding/ios/CheetahErrors.swift index 9e530cca..da41d259 100644 --- a/binding/ios/CheetahErrors.swift +++ b/binding/ios/CheetahErrors.swift @@ -1,5 +1,5 @@ // -// Copyright 2022 Picovoice Inc. +// Copyright 2022-2023 Picovoice Inc. // You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" // file accompanying this source. // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on @@ -9,13 +9,22 @@ public class CheetahError: LocalizedError { private let message: String + private let messageStack: [String] - public init (_ message: String) { + public init (_ message: String, _ messageStack: [String] = []) { self.message = message + self.messageStack = messageStack } public var errorDescription: String? { - return message + var messageString = message + if messageStack.count > 0 { + messageString += ":" + for i in 0.. 1.1.0' + pod 'Cheetah-iOS', :podspec => 'https://raw.githubusercontent.com/Picovoice/cheetah/v2.0-ios/binding/ios/Cheetah-iOS.podspec' pod 'ios-voice-processor', '~> 1.1.0' end diff --git a/demo/ios/CheetahDemo/Podfile.lock b/demo/ios/CheetahDemo/Podfile.lock index 56786c9e..dbb1a48f 100644 --- a/demo/ios/CheetahDemo/Podfile.lock +++ b/demo/ios/CheetahDemo/Podfile.lock @@ -1,20 +1,23 @@ PODS: - - Cheetah-iOS (1.1.0) + - Cheetah-iOS (2.0.0) - ios-voice-processor (1.1.0) DEPENDENCIES: - - Cheetah-iOS (~> 1.1.0) + - Cheetah-iOS (from `https://raw.githubusercontent.com/Picovoice/cheetah/v2.0-ios/binding/ios/Cheetah-iOS.podspec`) - ios-voice-processor (~> 1.1.0) SPEC REPOS: trunk: - - Cheetah-iOS - ios-voice-processor +EXTERNAL SOURCES: + Cheetah-iOS: + :podspec: https://raw.githubusercontent.com/Picovoice/cheetah/v2.0-ios/binding/ios/Cheetah-iOS.podspec + SPEC CHECKSUMS: - Cheetah-iOS: 6fb7be693878f5b1dec0ea5b6534fbba30954afc + Cheetah-iOS: d98a5edcbf3b74dda6027aeac6a8c0f5997a47a2 ios-voice-processor: 8e32d7f980a06d392d128ef1cd19cf6ddcaca3c1 -PODFILE CHECKSUM: 9b9aa7cee64517773071f86ed45a3b16f8ffddad +PODFILE CHECKSUM: e162593f02a8dbab3949d69a3a9888de0e155a29 COCOAPODS: 1.11.3 diff --git a/lib/ios/PvCheetah.xcframework/ios-arm64/PvCheetah.framework/Headers/picovoice.h b/lib/ios/PvCheetah.xcframework/ios-arm64/PvCheetah.framework/Headers/picovoice.h index 886558d1..3f13d705 100644 --- a/lib/ios/PvCheetah.xcframework/ios-arm64/PvCheetah.framework/Headers/picovoice.h +++ b/lib/ios/PvCheetah.xcframework/ios-arm64/PvCheetah.framework/Headers/picovoice.h @@ -77,6 +77,8 @@ PV_API pv_status_t pv_get_error_stack( */ PV_API void pv_free_error_stack(char **message_stack); +PV_API void pv_set_sdk(const char *sdk); + #ifdef __cplusplus } diff --git a/lib/ios/PvCheetah.xcframework/ios-arm64_x86_64-simulator/PvCheetah.framework/Headers/picovoice.h b/lib/ios/PvCheetah.xcframework/ios-arm64_x86_64-simulator/PvCheetah.framework/Headers/picovoice.h index 886558d1..3f13d705 100644 --- a/lib/ios/PvCheetah.xcframework/ios-arm64_x86_64-simulator/PvCheetah.framework/Headers/picovoice.h +++ b/lib/ios/PvCheetah.xcframework/ios-arm64_x86_64-simulator/PvCheetah.framework/Headers/picovoice.h @@ -77,6 +77,8 @@ PV_API pv_status_t pv_get_error_stack( */ PV_API void pv_free_error_stack(char **message_stack); +PV_API void pv_set_sdk(const char *sdk); + #ifdef __cplusplus }