Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2.0 ios #265

Merged
merged 2 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ios-perf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions binding/ios/Cheetah-iOS.podspec
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -23,8 +23,8 @@ Pod::Spec.new do |s|
DESC
s.homepage = 'https://github.com/Picovoice/cheetah/tree/master/binding/ios'
s.author = { 'Picovoice' => '[email protected]' }
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}'
Expand Down
88 changes: 61 additions & 27 deletions binding/ios/Cheetah.swift
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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<Int8>?
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)
}
Expand All @@ -139,10 +153,13 @@ public class Cheetah {

var cFinalTranscript: UnsafeMutablePointer<Int8>?
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
}
Expand All @@ -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<UnsafeMutablePointer<Int8>?>?
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..<messageStackDepth {
messageStack.append(String(cString: messageStackRef!.advanced(by: Int(i)).pointee!))
}

pv_free_error_stack(messageStackRef)

return messageStack
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -601,7 +601,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
Expand All @@ -626,7 +626,7 @@
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -656,7 +656,7 @@
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
10 changes: 2 additions & 8 deletions binding/ios/CheetahAppTest/CheetahAppTest/ViewController.swift
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -9,10 +9,4 @@

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
}

}
class ViewController: UIViewController { }
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -19,14 +19,6 @@ class CheetahDemoUITests: XCTestCase {

let modelURL = Bundle(for: CheetahDemoUITests.self).url(forResource: "cheetah_params", withExtension: "pv")!

override func setUp() {
super.setUp()
}

override func tearDown() {
super.tearDown()
}

override func setUpWithError() throws {
continueAfterFailure = true
}
Expand Down Expand Up @@ -102,4 +94,37 @@ class CheetahDemoUITests: XCTestCase {
func testVersion() throws {
XCTAssertGreaterThan(Cheetah.version, "")
}

func testMessageStack() throws {
var first_error: String = ""
do {
let cheetah = try Cheetah.init(accessKey: "invalid", modelURL: modelURL)
XCTAssertNil(cheetah)
} catch {
first_error = "\(error.localizedDescription)"
XCTAssert(first_error.count < 1024)
}

do {
let cheetah = try Cheetah.init(accessKey: "invalid", modelURL: modelURL)
XCTAssertNil(cheetah)
} catch {
XCTAssert("\(error.localizedDescription)".count == first_error.count)
}
}

func testProcessMessageStack() throws {
let cheetah = try Cheetah(accessKey: accessKey, modelURL: modelURL)
cheetah.delete()

var testPcm: [Int16] = []
testPcm.reserveCapacity(Int(Cheetah.frameLength))

do {
let res = try cheetah.process(testPcm)
XCTAssertNil(res)
} catch {
XCTAssert("\(error.localizedDescription)".count > 0)
}
}
}
8 changes: 4 additions & 4 deletions binding/ios/CheetahAppTest/Podfile
Original file line number Diff line number Diff line change
@@ -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
16 changes: 8 additions & 8 deletions binding/ios/CheetahAppTest/Podfile.lock
Original file line number Diff line number Diff line change
@@ -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
15 changes: 12 additions & 3 deletions binding/ios/CheetahErrors.swift
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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..<messageStack.count {
messageString += "\n [\(i)] \(messageStack[i])"
}
}
return messageString
}

public var name: String {
Expand Down
2 changes: 1 addition & 1 deletion demo/ios/CheetahDemo/CheetahDemo/ViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class ViewModel: ObservableObject {

state = UIState.READY
} catch let error as CheetahInvalidArgumentError {
errorMessage = "\(error.localizedDescription)\nEnsure your AccessKey '\(ACCESS_KEY)' is valid."
errorMessage = "\(error.localizedDescription)"
} catch is CheetahActivationError {
errorMessage = "ACCESS_KEY activation error"
} catch is CheetahActivationRefusedError {
Expand Down
Loading