Skip to content

Commit 1ce8989

Browse files
committed
init
1 parent f57893c commit 1ce8989

File tree

11 files changed

+180
-92
lines changed

11 files changed

+180
-92
lines changed

.github/workflows/ios-browserstack.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ jobs:
5050
run: sed -i '.bak' 's:{TESTING_ACCESS_KEY_HERE}:${{secrets.PV_VALID_ACCESS_KEY}}:'
5151
OrcaAppTestUITests/BaseTest.swift
5252

53+
- name: Inject Device
54+
run: sed -i '.bak' 's:{TESTING_DEVICE_HERE}:best:'
55+
OrcaAppTestUITests/BaseTest.swift
56+
5357
- name: XCode Build
5458
run: xcrun xcodebuild build-for-testing
5559
-configuration Debug

.github/workflows/ios-perf.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ jobs:
5252
run: sed -i '.bak' 's:{TESTING_ACCESS_KEY_HERE}:${{secrets.PV_VALID_ACCESS_KEY}}:'
5353
OrcaAppTestUITests/BaseTest.swift
5454

55+
- name: Inject Device
56+
run: sed -i '.bak' 's:{TESTING_DEVICE_HERE}:best:'
57+
OrcaAppTestUITests/BaseTest.swift
58+
5559
- name: Inject Number of Iterations
5660
run: sed -i '.bak' 's:{NUM_TEST_ITERATIONS}:30:'
5761
PerformanceTest/PerformanceTest.swift

Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
// swift-tools-version:5.3
1+
// swift-tools-version:5.7
22
import PackageDescription
33
let package = Package(
44
name: "Orca-iOS",
55
platforms: [
6-
.iOS(.v13)
6+
.iOS(.v16)
77
],
88
products: [
99
.library(

binding/ios/Orca-iOS.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22
s.name = 'Orca-iOS'
33
s.module_name = 'Orca'
4-
s.version = '1.2.0'
4+
s.version = '2.0.0'
55
s.license = {:type => 'Apache 2.0'}
66
s.summary = 'iOS binding for Picovoice\'s Orca Text-to-Speech Engine.'
77
s.description =
@@ -19,7 +19,7 @@ Pod::Spec.new do |s|
1919
s.homepage = 'https://github.com/Picovoice/orca/tree/main/binding/ios'
2020
s.author = { 'Picovoice' => 'hello@picovoice.ai' }
2121
s.source = { :git => "https://github.com/Picovoice/orca.git", :tag => s.version.to_s }
22-
s.ios.deployment_target = '13.0'
22+
s.ios.deployment_target = '16.0'
2323
s.swift_version = '5.0'
2424
s.vendored_frameworks = 'lib/ios/PvOrca.xcframework'
2525
s.source_files = 'binding/ios/*.{swift}'

binding/ios/Orca.swift

Lines changed: 75 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2024 Picovoice Inc.
2+
// Copyright 2024-2025 Picovoice Inc.
33
// You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE"
44
// file accompanying this source.
55
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
@@ -135,8 +135,8 @@ public class Orca {
135135
&cNumSamples,
136136
&cPcm)
137137
if status != PV_STATUS_SUCCESS {
138-
let messageStack = try orca.getMessageStack()
139-
throw orca.pvStatusToOrcaError(status, "Unable to synthesize streaming speech", messageStack)
138+
let messageStack = try Orca.getMessageStack()
139+
throw Orca.pvStatusToOrcaError(status, "Unable to synthesize streaming speech", messageStack)
140140
}
141141

142142
let buffer = UnsafeBufferPointer(start: cPcm, count: Int(cNumSamples))
@@ -166,8 +166,8 @@ public class Orca {
166166
&cNumSamples,
167167
&cPcm)
168168
if status != PV_STATUS_SUCCESS {
169-
let messageStack = try orca.getMessageStack()
170-
throw orca.pvStatusToOrcaError(status, "Unable to flush streaming speech", messageStack)
169+
let messageStack = try Orca.getMessageStack()
170+
throw Orca.pvStatusToOrcaError(status, "Unable to flush streaming speech", messageStack)
171171
}
172172

173173
let buffer = UnsafeBufferPointer(start: cPcm, count: Int(cNumSamples))
@@ -195,6 +195,30 @@ public class Orca {
195195
public static func setSdk(sdk: String) {
196196
self.sdk = sdk
197197
}
198+
199+
/// Lists all available devices that Orca can use for inference.
200+
/// Entries in the list can be used as the `device` argument when initializing Orca.
201+
///
202+
/// - Throws: OrcaError
203+
/// - Returns: Array of available devices that Orca can be used for inference.
204+
public static func getAvailableDevices() throws -> [String] {
205+
var cHardwareDevices: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?
206+
var numHardwareDevices: Int32 = 0
207+
let status = pv_orca_list_hardware_devices(&cHardwareDevices, &numHardwareDevices)
208+
if status != PV_STATUS_SUCCESS {
209+
let messageStack = try Orca.getMessageStack()
210+
throw Orca.pvStatusToOrcaError(status, "Orca getAvailableDevices failed", messageStack)
211+
}
212+
213+
var hardwareDevices: [String] = []
214+
for i in 0..<numHardwareDevices {
215+
hardwareDevices.append(String(cString: cHardwareDevices!.advanced(by: Int(i)).pointee!))
216+
}
217+
218+
pv_orca_free_hardware_devices(cHardwareDevices, numHardwareDevices)
219+
220+
return hardwareDevices
221+
}
198222

199223
/// Set of characters supported by Orca.
200224
public var validCharacters: Set<String>? {
@@ -216,30 +240,42 @@ public class Orca {
216240
/// - Parameters:
217241
/// - accessKey: AccessKey obtained from the Picovoice Console (https://console.picovoice.ai/)
218242
/// - modelPath: Absolute path to file containing model parameters.
243+
/// - device: String representation of the device (e.g., CPU or GPU) to use. If set to `best`, the most
244+
/// suitable device is selected automatically. If set to `gpu`, the engine uses the first available GPU
245+
/// device. To select a specific GPU device, set this argument to `gpu:${GPU_INDEX}`, where `${GPU_INDEX}`
246+
/// is the index of the target GPU. If set to `cpu`, the engine will run on the CPU with the default
247+
/// number of threads. To specify the number of threads, set this argument to `cpu:${NUM_THREADS}`,
248+
/// where `${NUM_THREADS}` is the desired number of threads.
219249
/// - Throws: OrcaError
220250
public init(
221251
accessKey: String,
222-
modelPath: String) throws {
252+
modelPath: String,
253+
device: String? = nil) throws {
223254

224255
var modelPathArg = modelPath
225256
if !FileManager().fileExists(atPath: modelPathArg) {
226257
modelPathArg = try getResourcePath(modelPathArg)
227258
}
228259

260+
var deviceArg = device
261+
if device == nil {
262+
deviceArg = "best"
263+
}
264+
229265
pv_set_sdk(Orca.sdk)
230266

231267
let initStatus = pv_orca_init(accessKey, modelPathArg, &handle)
232268
if initStatus != PV_STATUS_SUCCESS {
233-
let messageStack = try getMessageStack()
234-
throw pvStatusToOrcaError(initStatus, "Orca init failed", messageStack)
269+
let messageStack = try Orca.getMessageStack()
270+
throw Orca.pvStatusToOrcaError(initStatus, "Orca init failed", messageStack)
235271
}
236272

237273
var cNumCharacters: Int32 = 0
238274
var cCharacters: UnsafePointer<UnsafePointer<Int8>?>?
239275
let validCharactersStatus = pv_orca_valid_characters(handle, &cNumCharacters, &cCharacters)
240276
if validCharactersStatus != PV_STATUS_SUCCESS {
241-
let messageStack = try getMessageStack()
242-
throw pvStatusToOrcaError(validCharactersStatus, "Unable to get Orca valid characters", messageStack)
277+
let messageStack = try Orca.getMessageStack()
278+
throw Orca.pvStatusToOrcaError(validCharactersStatus, "Unable to get Orca valid characters", messageStack)
243279
}
244280
var validCharacters: Set<String> = ["", "", "", ""]
245281
for i in 0..<cNumCharacters {
@@ -254,16 +290,16 @@ public class Orca {
254290
var cSampleRate: Int32 = 0
255291
let sampleRateStatus = pv_orca_sample_rate(handle, &cSampleRate)
256292
if sampleRateStatus != PV_STATUS_SUCCESS {
257-
let messageStack = try getMessageStack()
258-
throw pvStatusToOrcaError(sampleRateStatus, "Orca failed to get sample rate", messageStack)
293+
let messageStack = try Orca.getMessageStack()
294+
throw Orca.pvStatusToOrcaError(sampleRateStatus, "Orca failed to get sample rate", messageStack)
259295
}
260296
self._sampleRate = cSampleRate
261297

262298
var cMaxCharacterLimit: Int32 = 0
263299
let maxCharacterLimitStatus = pv_orca_max_character_limit(handle, &cMaxCharacterLimit)
264300
if maxCharacterLimitStatus != PV_STATUS_SUCCESS {
265-
let messageStack = try getMessageStack()
266-
throw pvStatusToOrcaError(maxCharacterLimitStatus, "Orca failed to get max character limit", messageStack)
301+
let messageStack = try Orca.getMessageStack()
302+
throw Orca.pvStatusToOrcaError(maxCharacterLimitStatus, "Orca failed to get max character limit", messageStack)
267303
}
268304
self._maxCharacterLimit = cMaxCharacterLimit
269305
}
@@ -273,14 +309,22 @@ public class Orca {
273309
/// - Parameters:
274310
/// - accessKey: The AccessKey obtained from Picovoice Console (https://console.picovoice.ai).
275311
/// - modelURL: URL to file containing model parameters.
312+
/// - device: String representation of the device (e.g., CPU or GPU) to use. If set to `best`, the most
313+
/// suitable device is selected automatically. If set to `gpu`, the engine uses the first available GPU
314+
/// device. To select a specific GPU device, set this argument to `gpu:${GPU_INDEX}`, where `${GPU_INDEX}`
315+
/// is the index of the target GPU. If set to `cpu`, the engine will run on the CPU with the default
316+
/// number of threads. To specify the number of threads, set this argument to `cpu:${NUM_THREADS}`,
317+
/// where `${NUM_THREADS}` is the desired number of threads.
276318
/// - Throws: OrcaError
277319
public convenience init(
278320
accessKey: String,
279-
modelURL: URL) throws {
321+
modelURL: URL,
322+
device: String? = nil) throws {
280323

281324
try self.init(
282325
accessKey: accessKey,
283-
modelPath: modelURL.path)
326+
modelPath: modelURL.path,
327+
device: device)
284328
}
285329

286330
deinit {
@@ -340,8 +384,8 @@ public class Orca {
340384
&cNumAlignments,
341385
&cAlignments)
342386
if status != PV_STATUS_SUCCESS {
343-
let messageStack = try getMessageStack()
344-
throw pvStatusToOrcaError(status, "Unable to synthesize speech", messageStack)
387+
let messageStack = try Orca.getMessageStack()
388+
throw Orca.pvStatusToOrcaError(status, "Unable to synthesize speech", messageStack)
345389
}
346390

347391
let buffer = UnsafeBufferPointer(start: cPcm, count: Int(cNumSamples))
@@ -426,8 +470,8 @@ public class Orca {
426470
&cNumAlignments,
427471
&cAlignments)
428472
if status != PV_STATUS_SUCCESS {
429-
let messageStack = try getMessageStack()
430-
throw pvStatusToOrcaError(status, "Unable to synthesize speech to file", messageStack)
473+
let messageStack = try Orca.getMessageStack()
474+
throw Orca.pvStatusToOrcaError(status, "Unable to synthesize speech to file", messageStack)
431475
}
432476

433477
var wordArray = [OrcaWord]()
@@ -492,23 +536,23 @@ public class Orca {
492536

493537
var status = pv_orca_synthesize_params_init(&cParams)
494538
if status != PV_STATUS_SUCCESS {
495-
let messageStack = try getMessageStack()
496-
throw pvStatusToOrcaError(status, "Unable to create Orca synthesize params object", messageStack)
539+
let messageStack = try Orca.getMessageStack()
540+
throw Orca.pvStatusToOrcaError(status, "Unable to create Orca synthesize params object", messageStack)
497541
}
498542

499543
if speechRate != nil {
500544
status = pv_orca_synthesize_params_set_speech_rate(cParams, Float(speechRate!))
501545
if status != PV_STATUS_SUCCESS {
502-
let messageStack = try getMessageStack()
503-
throw pvStatusToOrcaError(status, "Unable to set Orca speech rate", messageStack)
546+
let messageStack = try Orca.getMessageStack()
547+
throw Orca.pvStatusToOrcaError(status, "Unable to set Orca speech rate", messageStack)
504548
}
505549
}
506550

507551
if randomState != nil {
508552
status = pv_orca_synthesize_params_set_random_state(cParams, randomState!)
509553
if status != PV_STATUS_SUCCESS {
510-
let messageStack = try getMessageStack()
511-
throw pvStatusToOrcaError(status, "Unable to set Orca random state", messageStack)
554+
let messageStack = try Orca.getMessageStack()
555+
throw Orca.pvStatusToOrcaError(status, "Unable to set Orca random state", messageStack)
512556
}
513557
}
514558

@@ -534,8 +578,8 @@ public class Orca {
534578
cSynthesizeParams,
535579
&stream)
536580
if status != PV_STATUS_SUCCESS {
537-
let messageStack = try getMessageStack()
538-
throw pvStatusToOrcaError(status, "Unable to open stream", messageStack)
581+
let messageStack = try Orca.getMessageStack()
582+
throw Orca.pvStatusToOrcaError(status, "Unable to open stream", messageStack)
539583
}
540584

541585
return OrcaStream(orca: self, stream: stream!)
@@ -558,7 +602,7 @@ public class Orca {
558602
"If this is a packaged asset, ensure you have added it to your xcode project.")
559603
}
560604

561-
private func pvStatusToOrcaError(
605+
private static func pvStatusToOrcaError(
562606
_ status: pv_status_t,
563607
_ message: String,
564608
_ messageStack: [String] = []) -> OrcaError {
@@ -591,12 +635,12 @@ public class Orca {
591635
}
592636
}
593637

594-
private func getMessageStack() throws -> [String] {
638+
private static func getMessageStack() throws -> [String] {
595639
var messageStackRef: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?
596640
var messageStackDepth: Int32 = 0
597641
let status = pv_get_error_stack(&messageStackRef, &messageStackDepth)
598642
if status != PV_STATUS_SUCCESS {
599-
throw pvStatusToOrcaError(status, "Unable to get Orca error state")
643+
throw Orca.pvStatusToOrcaError(status, "Unable to get Orca error state")
600644
}
601645

602646
var messageStack: [String] = []

binding/ios/OrcaAppTest/OrcaAppTest.xcodeproj/project.pbxproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@
238238
);
239239
mainGroup = 1E00643F27CEDF9B006FF6E9;
240240
packageReferences = (
241-
02B588CD2DFBA7E3007E7026 /* XCRemoteSwiftPackageReference "orca" */,
241+
E109D19B2CFE4DA30014F5AA /* XCLocalSwiftPackageReference "../../../../orca" */,
242242
);
243243
productRefGroup = 1E00644927CEDF9B006FF6E9 /* Products */;
244244
projectDirPath = "";
@@ -394,7 +394,7 @@
394394
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
395395
GCC_WARN_UNUSED_FUNCTION = YES;
396396
GCC_WARN_UNUSED_VARIABLE = YES;
397-
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
397+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
398398
MTL_ENABLE_DEBUG_INFO = YES;
399399
ONLY_ACTIVE_ARCH = YES;
400400
SDKROOT = iphoneos;
@@ -448,7 +448,7 @@
448448
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
449449
GCC_WARN_UNUSED_FUNCTION = YES;
450450
GCC_WARN_UNUSED_VARIABLE = YES;
451-
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
451+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
452452
MTL_ENABLE_DEBUG_INFO = NO;
453453
SDKROOT = iphoneos;
454454
SWIFT_COMPILATION_MODE = wholemodule;

binding/ios/OrcaAppTest/OrcaAppTestUITests/BaseTest.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ extension String {
6767

6868
class BaseTest: XCTestCase {
6969

70-
let accessKey = "{TESTING_ACCESS_KEY_HERE}"
70+
let accessKey: String = "{TESTING_ACCESS_KEY_HERE}"
71+
let device: String = "{TESTING_DEVICE_HERE}"
72+
7173
var testData: TestData?
7274

7375
override func setUp() async throws {

0 commit comments

Comments
 (0)