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

v0.2 ios #31

Merged
merged 7 commits into from
Nov 6, 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
52 changes: 52 additions & 0 deletions .github/workflows/ios-demos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: iOS Demos

on:
workflow_dispatch:
push:
branches: [ main ]
paths:
- 'demo/ios/EagleDemo/**'
- '.github/workflows/ios-demos.yml'
pull_request:
branches: [ main, 'v[0-9]+.[0-9]+' ]
paths:
- 'demo/ios/EagleDemo/**'
- '.github/workflows/ios-demos.yml'

defaults:
run:
working-directory: demo/ios/EagleDemo

jobs:
build:
runs-on: macos-latest

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Set up Node.js LTS
uses: actions/setup-node@v3
with:
node-version: lts/*

- name: Install Cocoapods
run: gem install cocoapods

- name: Install AppCenter CLI
run: npm install -g appcenter-cli

- name: Make build dir
run: mkdir ddp

- name: Run Cocoapods
run: pod install

- name: Build
run: xcrun xcodebuild build
-configuration Debug
-workspace EagleDemo.xcworkspace
-sdk iphoneos
-scheme EagleDemo
-derivedDataPath ddp
CODE_SIGNING_ALLOWED=NO
6 changes: 3 additions & 3 deletions binding/ios/Eagle-iOS.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = 'Eagle-iOS'
s.module_name = 'Eagle'
s.version = '0.1.0'
s.version = '0.2.0'
s.license = {:type => 'Apache 2.0'}
s.summary = 'iOS binding for Picovoice\'s Eagle speaker recognition engine'
s.description =
Expand All @@ -10,8 +10,8 @@ Pod::Spec.new do |s|
DESC
s.homepage = 'https://github.com/Picovoice/eagle/tree/master/binding/ios'
s.author = { 'Picovoice' => '[email protected]' }
s.source = { :git => "https://github.com/Picovoice/eagle.git", :tag => "Eagle-iOS-v0.1.0" }
s.ios.deployment_target = '11.0'
s.source = { :git => "https://github.com/Picovoice/eagle.git", :tag => "Eagle-iOS-v0.2.0" }
s.ios.deployment_target = '13.0'
s.swift_version = '5.0'
s.vendored_frameworks = 'lib/ios/PvEagle.xcframework'
s.resources = 'lib/common/eagle_params.pv'
Expand Down
11 changes: 8 additions & 3 deletions binding/ios/Eagle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,17 @@ public class Eagle: EagleBase {

speakerCount = speakerProfiles.count

pv_set_sdk(Eagle.sdk)

let status = pv_eagle_init(
accessKey,
modelPathArg,
Int32(speakerCount),
speakerHandles,
&handle)
if status != PV_STATUS_SUCCESS {
throw pvStatusToEagleError(status, "Eagle init failed")
let messageStack = try getMessageStack()
throw pvStatusToEagleError(status, "Eagle init failed", messageStack)
}
}

Expand Down Expand Up @@ -116,7 +119,8 @@ public class Eagle: EagleBase {
scores.baseAddress)

if status != PV_STATUS_SUCCESS {
throw pvStatusToEagleError(status, "Eagle process failed")
let messageStack = try getMessageStack()
throw pvStatusToEagleError(status, "Eagle process failed", messageStack)
}

return Array(scores)
Expand All @@ -137,7 +141,8 @@ public class Eagle: EagleBase {
let status = pv_eagle_reset(handle)

if status != PV_STATUS_SUCCESS {
throw pvStatusToEagleError(status, "Eagle reset failed")
let messageStack = try getMessageStack()
throw pvStatusToEagleError(status, "Eagle reset failed", messageStack)
}
}
}
12 changes: 6 additions & 6 deletions binding/ios/EagleAppTest/EagleAppTest.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -704,7 +704,7 @@
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -726,7 +726,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 65723695GD;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -751,7 +751,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 65723695GD;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -776,7 +776,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 65723695GD;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -803,7 +803,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 65723695GD;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
8 changes: 1 addition & 7 deletions binding/ios/EagleAppTest/EagleAppTest/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -123,4 +123,84 @@ class EagleAppTestUITests: BaseTest {
XCTAssertLessThan(scores.max()!, 0.5)
eagle.delete()
}

func testMessageStack() throws {
let enrollUrls = enrollUrls()

let eagleProfiler = try EagleProfiler(accessKey: accessKey)
for url in enrollUrls {
let pcm = try readPcmFromFile(testAudioURL: url)
(_, _) = try eagleProfiler.enroll(pcm: pcm)
}

let profile = try eagleProfiler.export()
eagleProfiler.delete()

var first_error: String = ""
do {
let eagle = try Eagle(accessKey: "invalid", speakerProfiles: [profile])
XCTAssertNil(eagle)
} catch {
first_error = "\(error.localizedDescription)"
XCTAssert(first_error.count < 1024)
}

do {
let eagle = try Eagle(accessKey: "invalid", speakerProfiles: [profile])
XCTAssertNil(eagle)
} catch {
XCTAssert("\(error.localizedDescription)".count == first_error.count)
}
}

func testEnrollExportMessageStack() throws {
let e = try EagleProfiler.init(accessKey: accessKey)
e.delete()

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

do {
let (res, _) = try e.enroll(pcm: testPcm)
XCTAssert(res == -1)
} catch {
XCTAssert("\(error.localizedDescription)".count > 0)
XCTAssert("\(error.localizedDescription)".count < 1024)
}

do {
let res = try e.export()
XCTAssertNil(res)
} catch {
XCTAssert("\(error.localizedDescription)".count > 0)
XCTAssert("\(error.localizedDescription)".count < 1024)
}
}

func testProcessMessageStack() throws {
let enrollUrls = enrollUrls()

let eagleProfiler = try EagleProfiler(accessKey: accessKey)
for url in enrollUrls {
let pcm = try readPcmFromFile(testAudioURL: url)
(_, _) = try eagleProfiler.enroll(pcm: pcm)
}

let profile = try eagleProfiler.export()
eagleProfiler.delete()

let e = try Eagle.init(accessKey: accessKey, speakerProfiles: [profile])
e.delete()

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

do {
let res = try e.process(pcm: testPcm)
XCTAssert(res.count == -1)
} catch {
XCTAssert("\(error.localizedDescription)".count > 0)
XCTAssert("\(error.localizedDescription)".count < 1024)
}
}
}
8 changes: 4 additions & 4 deletions binding/ios/EagleAppTest/Podfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
source 'https://cdn.cocoapods.org/'
platform :ios, '11.0'
platform :ios, '13.0'

target 'EagleAppTest' do
pod 'Eagle-iOS', '~> 0.1.0'
pod 'Eagle-iOS', :podspec => 'https://raw.githubusercontent.com/Picovoice/eagle/v0.2-ios/binding/ios/Eagle-iOS.podspec'
end

target 'EagleAppTestUITests' do
pod 'Eagle-iOS', '~> 0.1.0'
pod 'Eagle-iOS', :podspec => 'https://raw.githubusercontent.com/Picovoice/eagle/v0.2-ios/binding/ios/Eagle-iOS.podspec'
end

target 'PerformanceTest' do
pod 'Eagle-iOS', '~> 0.1.0'
pod 'Eagle-iOS', :podspec => 'https://raw.githubusercontent.com/Picovoice/eagle/v0.2-ios/binding/ios/Eagle-iOS.podspec'
end
16 changes: 8 additions & 8 deletions binding/ios/EagleAppTest/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
PODS:
- Eagle-iOS (0.1.0)
- Eagle-iOS (0.2.0)

DEPENDENCIES:
- Eagle-iOS (~> 0.1.0)
- Eagle-iOS (from `https://raw.githubusercontent.com/Picovoice/eagle/v0.2-ios/binding/ios/Eagle-iOS.podspec`)

SPEC REPOS:
trunk:
- Eagle-iOS
EXTERNAL SOURCES:
Eagle-iOS:
:podspec: https://raw.githubusercontent.com/Picovoice/eagle/v0.2-ios/binding/ios/Eagle-iOS.podspec

SPEC CHECKSUMS:
Eagle-iOS: 2d510466a68b22ba137dd2b39086268c4130c321
Eagle-iOS: 155eb54e73e37533a0accf3ea5fa1a37d8c999ad

PODFILE CHECKSUM: a3a1c94c7c11252c2152ed23f2cead73c1cf3762
PODFILE CHECKSUM: 6ecb4b66367607455bddf4dcad4ec1f8c70935a7

COCOAPODS: 1.12.1
COCOAPODS: 1.11.3
54 changes: 41 additions & 13 deletions binding/ios/EagleBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ public class EagleBase {
/// Eagle/EagleProfiler version
public static let version = String(cString: pv_eagle_version())

internal static var sdk = "ios"

public static func setSdk(sdk: String) {
self.sdk = sdk
}

/// Given a path, return the full path to the resource.
///
/// - Parameters:
Expand All @@ -31,34 +37,38 @@ public class EagleBase {
/// - Parameters:
/// - status: C enum value.
/// - message: message to include with the EagleError.
/// - messageStack: Error stack returned from Eagle.
/// - Returns: An EagleError.
internal func pvStatusToEagleError(_ status: pv_status_t, _ message: String) -> EagleError {
internal func pvStatusToEagleError(
_ status: pv_status_t,
_ message: String,
_ messageStack: [String] = []) -> EagleError {
switch status {
case PV_STATUS_OUT_OF_MEMORY:
return EagleMemoryError(message)
return EagleMemoryError(message, messageStack)
case PV_STATUS_IO_ERROR:
return EagleIOError(message)
return EagleIOError(message, messageStack)
case PV_STATUS_INVALID_ARGUMENT:
return EagleInvalidArgumentError(message)
return EagleInvalidArgumentError(message, messageStack)
case PV_STATUS_STOP_ITERATION:
return EagleStopIterationError(message)
return EagleStopIterationError(message, messageStack)
case PV_STATUS_KEY_ERROR:
return EagleKeyError(message)
return EagleKeyError(message, messageStack)
case PV_STATUS_INVALID_STATE:
return EagleInvalidStateError(message)
return EagleInvalidStateError(message, messageStack)
case PV_STATUS_RUNTIME_ERROR:
return EagleRuntimeError(message)
return EagleRuntimeError(message, messageStack)
case PV_STATUS_ACTIVATION_ERROR:
return EagleActivationError(message)
return EagleActivationError(message, messageStack)
case PV_STATUS_ACTIVATION_LIMIT_REACHED:
return EagleActivationLimitError(message)
return EagleActivationLimitError(message, messageStack)
case PV_STATUS_ACTIVATION_THROTTLED:
return EagleActivationThrottledError(message)
return EagleActivationThrottledError(message, messageStack)
case PV_STATUS_ACTIVATION_REFUSED:
return EagleActivationRefusedError(message)
return EagleActivationRefusedError(message, messageStack)
default:
let pvStatusString = String(cString: pv_status_to_string(status))
return EagleError("\(pvStatusString): \(message)")
return EagleError("\(pvStatusString): \(message)", messageStack)
}
}

Expand All @@ -84,4 +94,22 @@ public class EagleBase {
return EagleProfilerEnrollFeedback.AUDIO_OK
}
}

internal 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 pvStatusToEagleError(status, "Unable to get Eagle 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
}
}
Loading