diff --git a/.github/workflows/nodejs-perf.yml b/.github/workflows/nodejs-perf.yml index 9afb3cad..ea8c6c3a 100644 --- a/.github/workflows/nodejs-perf.yml +++ b/.github/workflows/nodejs-perf.yml @@ -63,7 +63,7 @@ jobs: - machine: rpi3-32 proc_performance_threshold_sec: 2.9 - machine: rpi3-64 - proc_performance_threshold_sec: 2.1 + proc_performance_threshold_sec: 2.5 - machine: rpi4-32 proc_performance_threshold_sec: 1.3 - machine: rpi4-64 diff --git a/.github/workflows/web-demos.yml b/.github/workflows/web-demos.yml index 894138a6..496f2ca9 100644 --- a/.github/workflows/web-demos.yml +++ b/.github/workflows/web-demos.yml @@ -25,7 +25,7 @@ jobs: strategy: matrix: - node-version: [ 16.x, 18.x, 20.x ] + node-version: [ 18.x, 20.x, 22.x ] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml index 0bf2c2a9..007beda6 100644 --- a/.github/workflows/web.yml +++ b/.github/workflows/web.yml @@ -29,7 +29,7 @@ jobs: strategy: matrix: - node-version: [ 16.x, 18.x, 20.x ] + node-version: [ 18.x, 20.x, 22.x ] steps: - uses: actions/checkout@v3 diff --git a/README.md b/README.md index 9d3a1350..f6243a44 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ voice assistants. Orca is: - [Orca streaming text synthesis](#orca-input-and-output-streaming-synthesis) - [Text input](#text-input) - [Custom pronunciations](#custom-pronunciations) - - [Voices](#voices) + - [Language and Voice](#language-and-voice) - [Speech control](#speech-control) - [Audio output](#audio-output) - [AccessKey](#accesskey) @@ -93,17 +93,11 @@ The following are examples of sentences using custom pronunciations: - "{read|R IY D} this as {read|R EH D}, please." - "I {live|L IH V} in {Sevilla|S EH V IY Y AH}. We have great {live|L AY V} sports!" -### Voices +### Language and Voice -Orca can synthesize speech with various voices, each of which is characterized by a model file located -in [lib/common](./lib/common). -To synthesize speech with a specific voice, provide the associated model file as an argument to the orca init function. -The following are the voices currently available: +Orca Streaming Text-to-Speech can synthesize speech in different languages and with a variety of voices, each of which is characterized by a model file (`.pv`) located in [lib/common](./lib/common). The language and gender of the speaker is indicated in the file name. -| Model name | Sample rate (Hz) | -|:---------------------------------------------------------:|:----------------:| -| [orca_params_female.pv](lib/common/orca_params_female.pv) | 22050 | -| [orca_params_male.pv](lib/common/orca_params_male.pv) | 22050 | +To synthesize speech with a specific language and voice, provide the associated model file as an argument to the Orca init function. ### Speech control @@ -779,7 +773,14 @@ For more details, see the [Node.js SDK](./binding/nodejs/). ## Releases -### v1.0.0 - Aug 20th, 2024 +### v1.1.0 - February 24th, 2025 + +- Added support for Spanish voices +- Improved English voices +- Added .NET SDK +- Improved text normalization + +### v1.0.0 - August 20th, 2024 - Improved voice quality - Significantly reduced latency in streaming synthesis diff --git a/binding/android/Orca/orca/build.gradle b/binding/android/Orca/orca/build.gradle index c885013c..810679f7 100644 --- a/binding/android/Orca/orca/build.gradle +++ b/binding/android/Orca/orca/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' ext { PUBLISH_GROUP_ID = 'ai.picovoice' - PUBLISH_VERSION = '1.0.0' + PUBLISH_VERSION = '1.1.0' PUBLISH_ARTIFACT_ID = 'orca-android' } diff --git a/binding/android/OrcaTestApp/orca-test-app/build.gradle b/binding/android/OrcaTestApp/orca-test-app/build.gradle index 0ba3340d..680bb43c 100644 --- a/binding/android/OrcaTestApp/orca-test-app/build.gradle +++ b/binding/android/OrcaTestApp/orca-test-app/build.gradle @@ -71,8 +71,8 @@ android { tasks.register('copyParams', Copy) { from("$projectDir/../../../../lib/common/") - include("orca_params_female.pv") - include("orca_params_male.pv") + include("orca_params_en_female.pv") + include("orca_params_en_male.pv") into("$projectDir/src/main/assets/models") } @@ -113,7 +113,7 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'com.google.code.gson:gson:2.10' implementation 'com.google.errorprone:error_prone_annotations:2.36.0' - implementation 'ai.picovoice:orca-android:1.0.0' + implementation 'ai.picovoice:orca-android:1.1.0' // Espresso UI Testing androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/binding/android/OrcaTestApp/orca-test-app/src/androidTest/java/ai/picovoice/orca/testapp/PerformanceTest.java b/binding/android/OrcaTestApp/orca-test-app/src/androidTest/java/ai/picovoice/orca/testapp/PerformanceTest.java index e7f3e44b..9fe6b1bf 100644 --- a/binding/android/OrcaTestApp/orca-test-app/src/androidTest/java/ai/picovoice/orca/testapp/PerformanceTest.java +++ b/binding/android/OrcaTestApp/orca-test-app/src/androidTest/java/ai/picovoice/orca/testapp/PerformanceTest.java @@ -38,6 +38,9 @@ public class PerformanceTest extends BaseTest { @Parameterized.Parameter(value = 0) public String modelFile; + @Parameterized.Parameter(value = 1) + public String procSentence; + @Parameterized.Parameters(name = "{0}") public static Collection initParameters() throws IOException { String testDataJsonString = getTestDataString(); @@ -48,10 +51,12 @@ public static Collection initParameters() throws IOException { final JsonArray testCases = testDataJson.getAsJsonObject("tests").get("sentence_tests").getAsJsonArray(); JsonObject testCase = testCases.get(0).getAsJsonObject(); + String text = testCase.get("text").getAsString(); + List parameters = new ArrayList<>(); for (JsonElement modelJson : testCase.get("models").getAsJsonArray()) { String model = modelJson.getAsString(); - parameters.add(new Object[]{model}); + parameters.add(new Object[]{model, text}); } return parameters; } @@ -76,13 +81,9 @@ public void testProcPerformance() throws Exception { Assume.assumeFalse(procThresholdString.equals("")); final double procPerformanceThresholdSec = Double.parseDouble(procThresholdString); - final String procSentence = testJson - .getAsJsonObject("test_sentences") - .get("text") - .getAsString(); final Orca orca = new Orca.Builder() .setAccessKey(accessKey) - .setModelPath(modelFile) + .setModelPath(getModelFilepath(modelFile)) .build(appContext); long totalNSec = 0; diff --git a/binding/android/OrcaTestApp/orca-test-app/src/main/java/ai/picovoice/orca/testapp/MainActivity.java b/binding/android/OrcaTestApp/orca-test-app/src/main/java/ai/picovoice/orca/testapp/MainActivity.java index d9349f5f..d2f0c75c 100644 --- a/binding/android/OrcaTestApp/orca-test-app/src/main/java/ai/picovoice/orca/testapp/MainActivity.java +++ b/binding/android/OrcaTestApp/orca-test-app/src/main/java/ai/picovoice/orca/testapp/MainActivity.java @@ -1,5 +1,5 @@ /* - Copyright 2024 Picovoice Inc. + Copyright 2024-2025 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. @@ -61,7 +61,7 @@ public void runTest() { ArrayList results = new ArrayList<>(); - final String modelFile = "models/orca_params_female.pv"; + final String modelFile = "models/orca_params_en_female.pv"; TestResult result = new TestResult(); result.testName = "Test Init"; diff --git a/binding/android/README.md b/binding/android/README.md index 49df5b7d..244e97e5 100644 --- a/binding/android/README.md +++ b/binding/android/README.md @@ -137,10 +137,11 @@ The pronunciation is expressed in [ARPAbet](https://en.wikipedia.org/wiki/ARPABE - "{read|R IY D} this as {read|R EH D}, please." - "I {live|L IH V} in {Sevilla|S EH V IY Y AH}. We have great {live|L AY V} sports!" -### Voices +### Language and Voice -Orca can synthesize speech with various voices, each of which is characterized by a model file located -in [lib/common](../../lib/common). +Orca Streaming Text-to-Speech can synthesize speech in different languages and with a variety of voices, +each of which is characterized by a model file (`.pv`) located in [lib/common](../../lib/common). +The language and gender of the speaker is indicated in the file name. To add the Orca model file to your Android application: diff --git a/binding/dotnet/Orca/Orca.cs b/binding/dotnet/Orca/Orca.cs index b3161bd5..a6e065a7 100644 --- a/binding/dotnet/Orca/Orca.cs +++ b/binding/dotnet/Orca/Orca.cs @@ -404,6 +404,7 @@ public short[] Synthesize(string text) pv_orca_pcm_delete(cPcm); } + return pcm; } @@ -437,9 +438,14 @@ public short[] Flush() HandlePvStatus(status, "Orca stream flush failed"); } - short[] pcm = new short[numSamples]; - Marshal.Copy(cPcm, pcm, 0, numSamples); - pv_orca_pcm_delete(cPcm); + short[] pcm = null; + if (numSamples > 0) + { + pcm = new short[numSamples]; + Marshal.Copy(cPcm, pcm, 0, numSamples); + pv_orca_pcm_delete(cPcm); + + } return pcm; } diff --git a/binding/dotnet/Orca/Orca.csproj b/binding/dotnet/Orca/Orca.csproj index 320d52a6..74b689f5 100644 --- a/binding/dotnet/Orca/Orca.csproj +++ b/binding/dotnet/Orca/Orca.csproj @@ -1,7 +1,7 @@  net8.0;net6.0;netcoreapp3.0;netstandard2.0 - 1.0.0 + 1.1.0 Picovoice Orca Streaming Text-to-Speech Engine @@ -100,12 +100,12 @@ - + - buildTransitive/common/orca_params_female.pv; + buildTransitive/common/orca_params_en_female.pv; PreserveNewest - lib\common\orca_params_female.pv + lib\common\orca_params_en_female.pv false diff --git a/binding/dotnet/Orca/Picovoice.Orca.netstandard2.0.targets b/binding/dotnet/Orca/Picovoice.Orca.netstandard2.0.targets index 256dafd2..004a77ed 100644 --- a/binding/dotnet/Orca/Picovoice.Orca.netstandard2.0.targets +++ b/binding/dotnet/Orca/Picovoice.Orca.netstandard2.0.targets @@ -21,9 +21,9 @@ PreserveNewest false - - lib/common/orca_params_female.pv - content/picovoice/common/orca_params_female.pv + + lib/common/orca_params_en_female.pv + content/picovoice/common/orca_params_en_female.pv PreserveNewest false diff --git a/binding/dotnet/Orca/Picovoice.Orca.targets b/binding/dotnet/Orca/Picovoice.Orca.targets index db4f4f0b..bf33d0e9 100644 --- a/binding/dotnet/Orca/Picovoice.Orca.targets +++ b/binding/dotnet/Orca/Picovoice.Orca.targets @@ -6,9 +6,9 @@ PreserveNewest false - - lib/common/orca_params_female.pv - content/picovoice/common/orca_params_female.pv + + lib/common/orca_params_en_female.pv + content/picovoice/common/orca_params_en_female.pv PreserveNewest false diff --git a/binding/dotnet/Orca/Utils.cs b/binding/dotnet/Orca/Utils.cs index 589dde07..852f11be 100644 --- a/binding/dotnet/Orca/Utils.cs +++ b/binding/dotnet/Orca/Utils.cs @@ -136,7 +136,7 @@ private static string GetCpuPart() public static string PvModelPath() { - return Path.Combine(AppContext.BaseDirectory, "lib/common/orca_params_female.pv"); + return Path.Combine(AppContext.BaseDirectory, "lib/common/orca_params_en_female.pv"); } } } \ No newline at end of file diff --git a/binding/dotnet/README.md b/binding/dotnet/README.md index 2046c679..679d1629 100644 --- a/binding/dotnet/README.md +++ b/binding/dotnet/README.md @@ -161,10 +161,12 @@ The pronunciation is expressed in [ARPAbet](https://en.wikipedia.org/wiki/ARPABE - "{read|R IY D} this as {read|R EH D}, please." - "I {live|L IH V} in {Sevilla|S EH V IY Y AH}. We have great {live|L AY V} sports!" -### Voices +### Language and Voice + +Orca Streaming Text-to-Speech can synthesize speech in different languages and with a variety of voices, +each of which is characterized by a model file (`.pv`) located in [lib/common](../../lib/common). +The language and gender of the speaker is indicated in the file name. -Orca can synthesize speech with various voices, each of which is characterized by a model file located -in [lib/common](https://github.com/Picovoice/orca/tree/main/lib/common). To create an instance of the engine with a specific voice, use: ```csharp diff --git a/binding/ios/Orca-iOS.podspec b/binding/ios/Orca-iOS.podspec index 24afce00..168e80e9 100644 --- a/binding/ios/Orca-iOS.podspec +++ b/binding/ios/Orca-iOS.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'Orca-iOS' s.module_name = 'Orca' - s.version = '1.0.1' + s.version = '1.1.0' s.license = {:type => 'Apache 2.0'} s.summary = 'iOS binding for Picovoice\'s Orca Text-to-Speech Engine.' s.description = @@ -11,7 +11,7 @@ Pod::Spec.new do |s| Orca is an on-device text-to-speech engine producing high-quality, realistic, spoken audio with zero latency. Orca is: - Private; All voice processing runs locally. - Cross-Platform: - - Linux (x86_64), macOS (x86_64, arm64), Windows (x86_64) + - Linux (x86_64), macOS (x86_64, arm64), Windows (x86_64, arm64) - Android and iOS - Chrome, Safari, Firefox, and Edge - Raspberry Pi (3, 4, 5) diff --git a/binding/ios/Orca.swift b/binding/ios/Orca.swift index ad8d5408..2f9d6e8c 100644 --- a/binding/ios/Orca.swift +++ b/binding/ios/Orca.swift @@ -235,7 +235,7 @@ public class Orca { } var cNumCharacters: Int32 = 0 - var cCharacters: UnsafeMutablePointer?>? + var cCharacters: UnsafePointer?>? let validCharactersStatus = pv_orca_valid_characters(handle, &cNumCharacters, &cCharacters) if validCharactersStatus != PV_STATUS_SUCCESS { let messageStack = try getMessageStack() diff --git a/binding/ios/OrcaAppTest/OrcaAppTest.xcodeproj/project.pbxproj b/binding/ios/OrcaAppTest/OrcaAppTest.xcodeproj/project.pbxproj index 35fff480..263fe7df 100644 --- a/binding/ios/OrcaAppTest/OrcaAppTest.xcodeproj/project.pbxproj +++ b/binding/ios/OrcaAppTest/OrcaAppTest.xcodeproj/project.pbxproj @@ -236,7 +236,7 @@ ); mainGroup = 1E00643F27CEDF9B006FF6E9; packageReferences = ( - E1F352FD2D00ECD60069B0E6 /* XCRemoteSwiftPackageReference "orca" */, + 07F723562D6D57E90002D88F /* XCRemoteSwiftPackageReference "orca" */, ); productRefGroup = 1E00644927CEDF9B006FF6E9 /* Products */; projectDirPath = ""; @@ -655,12 +655,12 @@ /* End XCLocalSwiftPackageReference section */ /* Begin XCRemoteSwiftPackageReference section */ - E1F352FD2D00ECD60069B0E6 /* XCRemoteSwiftPackageReference "orca" */ = { + 07F723562D6D57E90002D88F /* XCRemoteSwiftPackageReference "orca" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/Picovoice/orca"; requirement = { kind = exactVersion; - version = 1.0.1; + version = 1.1.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/binding/ios/OrcaAppTest/OrcaAppTestUITests/OrcaAppTestUITests.swift b/binding/ios/OrcaAppTest/OrcaAppTestUITests/OrcaAppTestUITests.swift index 21268f8e..7d932a05 100644 --- a/binding/ios/OrcaAppTest/OrcaAppTestUITests/OrcaAppTestUITests.swift +++ b/binding/ios/OrcaAppTest/OrcaAppTestUITests/OrcaAppTestUITests.swift @@ -412,7 +412,7 @@ class OrcaAppTestUITests: BaseTest { func testMessageStack() throws { let bundle = Bundle(for: type(of: self)) let modelPath: String = bundle.path( - forResource: "orca_params_female", + forResource: "orca_params_en_female", ofType: "pv", inDirectory: "test_resources/model_files")! @@ -436,7 +436,7 @@ class OrcaAppTestUITests: BaseTest { func testSynthesizeMessageStack() throws { let bundle = Bundle(for: type(of: self)) let modelPath: String = bundle.path( - forResource: "orca_params_female", + forResource: "orca_params_en_female", ofType: "pv", inDirectory: "test_resources/model_files")! diff --git a/binding/ios/README.md b/binding/ios/README.md index 46f8a204..ba57d0b1 100644 --- a/binding/ios/README.md +++ b/binding/ios/README.md @@ -130,11 +130,13 @@ The pronunciation is expressed in [ARPAbet](https://en.wikipedia.org/wiki/ARPABE - "{read|R IY D} this as {read|R EH D}, please." - "I {live|L IH V} in {Sevilla|S EH V IY Y AH}. We have great {live|L AY V} sports!" -### Voices +### Language and Voice -Orca can synthesize speech with various voices, each of which is characterized by a model file located -in [lib/common](https://github.com/Picovoice/orca/tree/main/lib/common). -To create an instance of the engine with a specific voice, use: +Orca Streaming Text-to-Speech can synthesize speech in different languages and with a variety of voices, +each of which is characterized by a model file (`.pv`) located in [lib/common](../../lib/common). +The language and gender of the speaker is indicated in the file name. + +To create an instance of the engine with a specific language and voice, use: ```swift import Orca @@ -146,7 +148,7 @@ do { } catch { } ``` -and replace `${MODEL_FILE_PATH}` or `${MODEL_FILE_URL}` with the path to the model file with the desired voice. +and replace `${MODEL_FILE_PATH}` or `${MODEL_FILE_URL}` with the path to the model file with the desired language/voice. ### Speech control diff --git a/binding/nodejs/README.md b/binding/nodejs/README.md index 7c68fba6..96287ae5 100644 --- a/binding/nodejs/README.md +++ b/binding/nodejs/README.md @@ -105,7 +105,7 @@ orca.release() ### Text input -Orca supports a wide range of English characters, including letters, numbers, symbols, and punctuation marks. +Orca supports a wide range of English characters, including letters, numbers, symbols, and punctuation marks. You can get a list of all supported characters by calling `validCharacters()`. Pronunciations of characters or words not supported by this list can be achieved with [custom pronunciations](#custom-pronunciations). @@ -119,10 +119,12 @@ The pronunciation is expressed in [ARPAbet](https://en.wikipedia.org/wiki/ARPABE - "{read|R IY D} this as {read|R EH D}, please." - "I {live|L IH V} in {Sevilla|S EH V IY Y AH}. We have great {live|L AY V} sports!" -### Voices +### Language and Voice + +Orca Streaming Text-to-Speech can synthesize speech in different languages and with a variety of voices, +each of which is characterized by a model file (`.pv`) located in [lib/common](../../lib/common). +The language and gender of the speaker is indicated in the file name. -Orca can synthesize speech with various voices, each of which is characterized by a model file located -in [lib/common](https://github.com/Picovoice/orca/tree/main/lib/common). To create an instance of the engine with a specific voice, use: ```typescript diff --git a/binding/nodejs/copy.js b/binding/nodejs/copy.js index 665ae6f6..c12500f0 100644 --- a/binding/nodejs/copy.js +++ b/binding/nodejs/copy.js @@ -1,5 +1,5 @@ // -// Copyright 2024 Picovoice Inc. +// Copyright 2024-2025 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. @@ -18,8 +18,8 @@ console.log('Copying library files...'); // Library & Model mkdirp.sync('./lib/common'); ncp( - '../../lib/common/orca_params_female.pv', - './lib/common/orca_params_female.pv', + '../../lib/common/orca_params_en_female.pv', + './lib/common/orca_params_en_female.pv', function(err) { if (err) { return console.error(err); @@ -28,17 +28,6 @@ ncp( }, ); -ncp( - '../../lib/common/orca_params_male.pv', - './lib/common/orca_params_male.pv', - function(err) { - if (err) { - return console.error(err); - } - console.log('../../lib/common male copied.'); - }, -); - ncp('../../lib/node', './lib', function(err) { if (err) { return console.error(err); diff --git a/binding/nodejs/package.json b/binding/nodejs/package.json index d4a8480d..914c54d7 100644 --- a/binding/nodejs/package.json +++ b/binding/nodejs/package.json @@ -1,6 +1,6 @@ { "name": "@picovoice/orca-node", - "version": "1.0.2", + "version": "1.1.0", "description": "Picovoice Orca Node.js binding", "main": "dist/index.js", "types": "dist/types/index.d.ts", diff --git a/binding/nodejs/src/orca.ts b/binding/nodejs/src/orca.ts index b0184857..7d0fc8a5 100644 --- a/binding/nodejs/src/orca.ts +++ b/binding/nodejs/src/orca.ts @@ -1,5 +1,5 @@ // -// Copyright 2024 Picovoice Inc. +// Copyright 2024-2025 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. @@ -29,7 +29,7 @@ import { import { getSystemLibraryPath } from './platforms'; -const DEFAULT_MODEL_PATH = '../lib/common/orca_params_female.pv'; +const DEFAULT_MODEL_PATH = '../lib/common/orca_params_en_female.pv'; type OrcaHandleAndStatus = { handle: any; status: PvStatus }; type OrcaResult = OrcaSynthesizeResult & { diff --git a/binding/nodejs/test/index.test.ts b/binding/nodejs/test/index.test.ts index 80181fe5..a7884b51 100644 --- a/binding/nodejs/test/index.test.ts +++ b/binding/nodejs/test/index.test.ts @@ -21,11 +21,9 @@ const ACCESS_KEY = process.argv .filter(x => x.startsWith('--access_key='))[0] .split('--access_key=')[1]; -const testData = getTestData() +const testData = getTestData(); -const getAudioFileName = (model: string, synthesis_type: string): string => { - return model.replace(".pv", `_${synthesis_type}.wav`) -} +const getAudioFileName = (model: string, synthesis_type: string): string => model.replace(".pv", `_${synthesis_type}.wav`); const loadPcm = (audioFile: string): any => { const waveFilePath = getAudioFile(audioFile); @@ -83,8 +81,8 @@ const validateAlignmentsExact = (alignments: OrcaAlignment[], expectedAlignments }; describe('properties', () => { - describe.each(testData.tests.sentence_tests)('$language', ({language, models}) => { - describe.each(models)('%s', (model) => { + describe.each(testData.tests.sentence_tests)('$language', ({ language, models }) => { + describe.each(models)('%s', model => { it('version', () => { const orcaEngine = new Orca(ACCESS_KEY, { modelPath: getModelPath(model) }); expect(typeof orcaEngine.version).toEqual('string'); @@ -111,13 +109,13 @@ describe('properties', () => { expect(orcaEngine.maxCharacterLimit).toBeGreaterThan(0); orcaEngine.release(); }); - }) - }) + }); + }); }); describe('sentences', () => { - describe.each(testData.tests.sentence_tests)('$language', ({language, models, random_state, text, text_no_punctuation, text_custom_pronunciation}) => { - describe.each(models)('%s', (model) => { + describe.each(testData.tests.sentence_tests)('$language', ({ language, models, random_state, text, text_no_punctuation, text_custom_pronunciation }) => { + describe.each(models)('%s', model => { it('synthesize', () => { const orcaEngine = new Orca(ACCESS_KEY, { modelPath: getModelPath(model) }); const { pcm, alignments } = orcaEngine.synthesize( @@ -197,12 +195,12 @@ describe('sentences', () => { expect(pcmSlow.length).toBeGreaterThan(pcmFast.length); orcaEngine.release(); }); - }) - }) + }); + }); }); describe('alignments', () => { - describe.each(testData.tests.alignment_tests)('$language $model', ({language, model, random_state, text_alignment, alignments: expectedAlignments}) => { + describe.each(testData.tests.alignment_tests)('$language $model', ({ language, model, random_state, text_alignment, alignments: expectedAlignments }) => { it('synthesize alignment', () => { const orcaEngine = new Orca(ACCESS_KEY, { modelPath: getModelPath(model) }); const { pcm, alignments } = orcaEngine.synthesize(text_alignment, { randomState: random_state }); @@ -210,12 +208,12 @@ describe('alignments', () => { validateAlignmentsExact(alignments, expectedAlignments); orcaEngine.release(); }); - }) + }); }); describe('invalids', () => { - describe.each(testData.tests.invalid_tests)('$language', ({language, models, text_invalid}) => { - describe.each(models)('%s', (model) => { + describe.each(testData.tests.invalid_tests)('$language', ({ language, models, text_invalid }) => { + describe.each(models)('%s', model => { it('invalid input', () => { text_invalid.forEach((sentence: string) => { const orcaEngine = new Orca(ACCESS_KEY, { modelPath: getModelPath(model) }); @@ -226,8 +224,8 @@ describe('invalids', () => { } }); }); - }) - }) + }); + }); }); diff --git a/binding/python/README.md b/binding/python/README.md index 1b00a45a..b7d57a52 100644 --- a/binding/python/README.md +++ b/binding/python/README.md @@ -106,7 +106,7 @@ orca.delete() ### Text input -Orca supports a wide range of English characters, including letters, numbers, symbols, and punctuation marks. +Orca supports a wide range of English characters, including letters, numbers, symbols, and punctuation marks. You can get a list of all supported characters with the `.valid_characters` property. Pronunciations of characters or words not supported by this list can be achieved with [custom pronunciations](#custom-pronunciations). @@ -120,17 +120,17 @@ The pronunciation is expressed in [ARPAbet](https://en.wikipedia.org/wiki/ARPABE - "{read|R IY D} this as {read|R EH D}, please." - "I {live|L IH V} in {Sevilla|S EH V IY Y AH}. We have great {live|L AY V} sports!" -### Voices +### Language and Voice -Orca can synthesize speech with various voices, each of which is characterized by a model file located -in [lib/common](https://github.com/Picovoice/orca/tree/main/lib/common). -To create an instance of the engine with a specific voice, use: +Orca Streaming Text-to-Speech can synthesize speech in different languages and with a variety of voices, each of which is characterized by a model file (`.pv`) located in [lib/common](../../lib/common). The language and gender of the speaker is indicated in the file name. + +To create an instance of the engine with a specific language and voice, use: ```python orca = pvorca.create(access_key='${ACCESS_KEY}', model_path='${MODEL_PATH}') ``` -and replace `${MODEL_PATH}` with the path to the model file with the desired voice. +and replace `${MODEL_PATH}` with the path to the model file with the desired language/voice. ### Speech control diff --git a/binding/python/_util.py b/binding/python/_util.py index bc768de5..39c12764 100644 --- a/binding/python/_util.py +++ b/binding/python/_util.py @@ -1,5 +1,5 @@ # -# Copyright 2024 Picovoice Inc. +# Copyright 2024-2025 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. @@ -78,7 +78,7 @@ def default_library_path(relative: str = "") -> str: def default_model_path(relative: str = "") -> str: - return os.path.join(os.path.dirname(__file__), relative, "lib", "common", "orca_params_female.pv") + return os.path.join(os.path.dirname(__file__), relative, "lib", "common", "orca_params_en_female.pv") __all__ = [ diff --git a/binding/python/setup.py b/binding/python/setup.py index 8ca18f1c..cddad524 100644 --- a/binding/python/setup.py +++ b/binding/python/setup.py @@ -1,5 +1,5 @@ # -# Copyright 2024 Picovoice Inc. +# Copyright 2024-2025 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. @@ -16,7 +16,7 @@ INCLUDE_FILES = ('../../LICENSE', '__init__.py', '_factory.py', '_orca.py', '_util.py') INCLUDE_LIBS = ('linux', 'mac', 'raspberry-pi', 'windows') -DEFAULT_MODEL_FILE = 'orca_params_female.pv' +DEFAULT_MODEL_FILE = 'orca_params_en_female.pv' os.system('git clean -dfx') @@ -49,7 +49,7 @@ setuptools.setup( name="pvorca", - version="1.0.2", + version="1.1.0", author="Picovoice", author_email="hello@picovoice.ai", description="Orca Streaming Text-to-Speech Engine", diff --git a/binding/web/README.md b/binding/web/README.md index ac486e7a..0497cf67 100644 --- a/binding/web/README.md +++ b/binding/web/README.md @@ -58,7 +58,7 @@ For the web packages, there are two methods to initialize Orca. server. This method fetches the [model file](https://github.com/Picovoice/orca/tree/main/lib/common) from the public directory -and feeds it to Orca. Copy a model file for the desired voice (male or female) into the public +and feeds it to Orca. Copy a model file for the desired language and voice into the public directory: ```console diff --git a/binding/web/cypress.config.ts b/binding/web/cypress.config.ts index ff2969e8..fe3f2682 100644 --- a/binding/web/cypress.config.ts +++ b/binding/web/cypress.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ }, e2e: { supportFile: 'cypress/support/index.ts', - defaultCommandTimeout: 30000, + defaultCommandTimeout: 60000, specPattern: 'test/*.test.{js,jsx,ts,tsx}', video: false, screenshotOnRunFailure: false, diff --git a/binding/web/package.json b/binding/web/package.json index 4f8f2abc..e2fd4d18 100644 --- a/binding/web/package.json +++ b/binding/web/package.json @@ -3,7 +3,7 @@ "description": "Orca Text-to-Speech engine for web browsers (via WebAssembly)", "author": "Picovoice Inc", "license": "Apache-2.0", - "version": "1.0.0", + "version": "1.1.0", "keywords": [ "orca", "web", @@ -28,12 +28,12 @@ "watch": "rollup --config --watch", "format": "prettier --write \"**/*.{js,ts,json}\"", "copywasm": "node scripts/copy_wasm.js", - "setup-test": "node scripts/setup_test.js && npx pvbase64 -i ./test/orca_params_male.pv -o ./test/orca_params_male.js && npx pvbase64 -i ./test/orca_params_female.pv -o ./test/orca_params_female.js", + "setup-test": "node scripts/setup_test.js && npx pvbase64 -i ./test/orca_params_en_male.pv -o ./test/orca_params_en_male.js && npx pvbase64 -i ./test/orca_params_en_female.pv -o ./test/orca_params_en_female.js", "test": "cypress run --spec test/orca.test.ts", "test-perf": "cypress run --spec test/orca_perf.test.ts" }, "dependencies": { - "@picovoice/web-utils": "=1.4.2" + "@picovoice/web-utils": "=1.4.3" }, "devDependencies": { "@babel/core": "^7.21.3", @@ -63,6 +63,6 @@ "wasm-feature-detect": "^1.5.0" }, "engines": { - "node": ">=16" + "node": ">=18" } } diff --git a/binding/web/src/orca.ts b/binding/web/src/orca.ts index 211b4323..989a26fd 100644 --- a/binding/web/src/orca.ts +++ b/binding/web/src/orca.ts @@ -932,7 +932,7 @@ export class Orca { private static async initWasm(accessKey: string, modelPath: string, wasmBase64: string): Promise { // A WebAssembly page has a constant size of 64KiB. -> 1MiB ~= 16 pages const memory = new WebAssembly.Memory({ initial: 1600 }); - const memoryBufferUint8 = new Uint8Array(memory.buffer); + let memoryBufferUint8 = new Uint8Array(memory.buffer); const pvError = new PvError(); const exports = await buildWasm(memory, wasmBase64, pvError); @@ -1020,8 +1020,6 @@ export class Orca { throw new OrcaErrors.OrcaOutOfMemoryError('malloc failed: Cannot allocate memory'); } - const memoryBufferView = new DataView(memory.buffer); - const initStatus = await pv_orca_init( accessKeyAddress, modelPathAddress, @@ -1029,6 +1027,9 @@ export class Orca { await pv_free(accessKeyAddress); await pv_free(modelPathAddress); + const memoryBufferView = new DataView(memory.buffer); + memoryBufferUint8 = new Uint8Array(memory.buffer); + if (initStatus !== PvStatus.SUCCESS) { const messageStack = await Orca.getMessageStack( pv_get_error_stack, diff --git a/binding/web/test/orca.test.ts b/binding/web/test/orca.test.ts index b64f2478..398ccdbc 100644 --- a/binding/web/test/orca.test.ts +++ b/binding/web/test/orca.test.ts @@ -15,10 +15,10 @@ import { OrcaError } from '../dist/types/orca_errors'; import { PvModel } from '@picovoice/web-utils'; // @ts-ignore -import orcaParamsMale from './orca_params_male'; +import orcaParamsMale from './orca_params_en_male'; // @ts-ignore -import orcaParamsFemale from './orca_params_female'; +import orcaParamsFemale from './orca_params_en_female'; /* eslint camelcase: 0 */ @@ -43,12 +43,13 @@ const EXPECTED_VALID_CHARACTERS = [ '&', '\n', '_', '(', ')', '°', 'º', '²', '³', '$', '€', '¥', '₪', '£', '₩', '₺', '₱', '₽', '฿', '₴', '₹', - '¢', '+', '=', + '¢', '+', '=', '#', '−', '–', '‒', + '—', '―', '’' ]; const getAudioFileName = (model: string, synthesis_type: string): string => { - return model.replace(".pv", `_${synthesis_type}.wav`) -} + return model.replace(".pv", `_${synthesis_type}.wav`); +}; const compareArrays = (arr1: Int16Array, arr2: Int16Array, step: number) => { expect(arr1.length).eq(arr2.length); @@ -67,7 +68,7 @@ const runInitTest = async ( ) => { const { accessKey = ACCESS_KEY, - model = { publicPath: `/test/orca_params_male.pv`, forceWrite: true }, + model = { publicPath: `/test/orca_params_en_male.pv`, forceWrite: true }, expectFailure = false, } = params; @@ -76,6 +77,7 @@ const runInitTest = async ( try { orca = await instance.create(accessKey, model); + console.log(orca.validCharacters) expect(typeof orca.version).eq('string'); expect(orca.version.length).gt(0); expect(orca.maxCharacterLimit).eq(EXPECTED_MAX_CHARACTER_LIMIT); @@ -106,122 +108,118 @@ const runInitTest = async ( }; describe('Orca Binding', function() { - for (const testCase of testData.tests.sentence_tests) { - for (const model of testCase.models) { - for (const instance of [Orca, OrcaWorker]) { - const instanceString = instance === Orca ? 'main' : 'worker'; - const testCaseString = `${testCase.language} | ${model} | ${instanceString}` + for (const instance of [Orca, OrcaWorker]) { + const instanceString = instance === Orca ? 'main' : 'worker'; + const testCaseString = `/test/orca_params_en_male.pv | ${instanceString}`; - const publicPath = `/test/${model}` + const publicPath = `/test/orca_params_en_male.pv`; - it(`should be able to handle invalid public path (${testCaseString})`, async () => { - await runInitTest(instance, { - model: { publicPath: 'invalid', forceWrite: true }, - expectFailure: true, - }); - }); + it(`should be able to handle invalid public path (${testCaseString})`, async () => { + await runInitTest(instance, { + model: { publicPath: 'invalid', forceWrite: true }, + expectFailure: true, + }); + }); - it(`should be able to handle invalid base64 (${testCaseString})`, async () => { - await runInitTest(instance, { - model: { base64: 'invalid', forceWrite: true }, - expectFailure: true, - }); - }); + it(`should be able to handle invalid base64 (${testCaseString})`, async () => { + await runInitTest(instance, { + model: { base64: 'invalid', forceWrite: true }, + expectFailure: true, + }); + }); - it(`should be able to handle invalid access key (${testCaseString})`, async () => { - await runInitTest(instance, { - accessKey: 'invalid', - expectFailure: true, - }); - }); + it(`should be able to handle invalid access key (${testCaseString})`, async () => { + await runInitTest(instance, { + accessKey: 'invalid', + expectFailure: true, + }); + }); - it(`should be able to init with public path (${testCaseString})`, async () => { - await runInitTest(instance, { - model: { publicPath, forceWrite: true }, - }); - }); + it(`should be able to init with public path (${testCaseString})`, async () => { + await runInitTest(instance, { + model: { publicPath, forceWrite: true }, + }); + }); - it(`should be able to init with base64 (${testCaseString})`, async () => { - await runInitTest(instance, { - model: { base64: orcaParamsMale, forceWrite: true }, - }); - }); + it(`should be able to init with base64 (${testCaseString})`, async () => { + await runInitTest(instance, { + model: { base64: orcaParamsMale, forceWrite: true }, + }); + }); - it(`should be able to handle UTF-8 public path (${testCaseString})`, async () => { - await runInitTest(instance, { - model: { publicPath, forceWrite: true, customWritePath: '테스트' }, - }); - }); + it(`should be able to handle UTF-8 public path (${testCaseString})`, async () => { + await runInitTest(instance, { + model: { publicPath, forceWrite: true, customWritePath: '테스트' }, + }); + }); - it(`should return process and flush error message stack (${testCaseString})`, async () => { - const orca = await Orca.create( - ACCESS_KEY, - { publicPath: publicPath, forceWrite: true }, - ); + it(`should return process and flush error message stack (${testCaseString})`, async () => { + const orca = await Orca.create( + ACCESS_KEY, + { publicPath: publicPath, forceWrite: true }, + ); - // @ts-ignore - const objectAddress = orca._objectAddress; + // @ts-ignore + const objectAddress = orca._objectAddress; - // @ts-ignore - orca._objectAddress = 0; + // @ts-ignore + orca._objectAddress = 0; - const errors: OrcaError[] = []; - try { - await orca.synthesize('test'); - } catch (e: any) { - errors.push(e); - } + const errors: OrcaError[] = []; + try { + await orca.synthesize('test'); + } catch (e: any) { + errors.push(e); + } - // @ts-ignore - orca._objectAddress = objectAddress; - await orca.release(); + // @ts-ignore + orca._objectAddress = objectAddress; + await orca.release(); - expect(errors.length).to.be.gte(0); + expect(errors.length).to.be.gte(0); - for (let i = 0; i < errors.length; i++) { - expect((errors[i] as OrcaError).messageStack.length).to.be.gt(0); - expect((errors[i] as OrcaError).messageStack.length).to.be.lte(8); - } - }); + for (let i = 0; i < errors.length; i++) { + expect((errors[i] as OrcaError).messageStack.length).to.be.gt(0); + expect((errors[i] as OrcaError).messageStack.length).to.be.lte(8); + } + }); - it(`should return correct error message stack (${testCaseString})`, async () => { - let messageStack = []; - try { - const orca = await instance.create('invalidAccessKey', { - publicPath, - forceWrite: true, - }); - expect(orca).to.be.undefined; - } catch (e: any) { - messageStack = e.messageStack; - } + it(`should return correct error message stack (${testCaseString})`, async () => { + let messageStack = []; + try { + const orca = await instance.create('invalidAccessKey', { + publicPath, + forceWrite: true, + }); + expect(orca).to.be.undefined; + } catch (e: any) { + messageStack = e.messageStack; + } - expect(messageStack.length).to.be.gt(0); - expect(messageStack.length).to.be.lte(8); + expect(messageStack.length).to.be.gt(0); + expect(messageStack.length).to.be.lte(8); - try { - const orca = await instance.create('invalidAccessKey', { - publicPath, - forceWrite: true, - }); - expect(orca).to.be.undefined; - } catch (e: any) { - expect(messageStack.length).to.be.eq(e.messageStack.length); - } + try { + const orca = await instance.create('invalidAccessKey', { + publicPath, + forceWrite: true, }); + expect(orca).to.be.undefined; + } catch (e: any) { + expect(messageStack.length).to.be.eq(e.messageStack.length); } - } + }); } -}) +}); describe('Sentence Tests', function() { for (const testCase of testData.tests.sentence_tests) { for (const model of testCase.models) { for (const instance of [Orca, OrcaWorker]) { const instanceString = instance === Orca ? 'main' : 'worker'; - const testCaseString = `${testCase.language} | ${model} | ${instanceString}` + const testCaseString = `${testCase.language} | ${model} | ${instanceString}`; - const publicPath = `/test/${model}` + const publicPath = `/test/${model}`; it(`should be able to process text streaming (${testCaseString})`, () => { try { @@ -377,16 +375,15 @@ describe('Sentence Tests', function() { } } } -}) +}); - -describe('Sentence Tests', function() { +describe('Alignment Tests', function() { for (const testCase of testData.tests.alignment_tests) { for (const instance of [Orca, OrcaWorker]) { const instanceString = instance === Orca ? 'main' : 'worker'; - const testCaseString = `${testCase.language} | ${testCase.model} | ${instanceString}` + const testCaseString = `${testCase.language} | ${testCase.model} | ${instanceString}`; - const publicPath = `/test/${testCase.model}` + const publicPath = `/test/${testCase.model}`; it(`should be able to process alignment exact (${testCaseString})`, async () => { try { @@ -425,16 +422,16 @@ describe('Sentence Tests', function() { }); } } -}) +}); -describe('Sentence Tests', function() { +describe('Invalid Tests', function() { for (const testCase of testData.tests.invalid_tests) { for (const model of testCase.models) { for (const instance of [Orca, OrcaWorker]) { const instanceString = instance === Orca ? 'main' : 'worker'; - const testCaseString = `${testCase.language} | ${model} | ${instanceString}` + const testCaseString = `${testCase.language} | ${model} | ${instanceString}`; - const publicPath = `/test/${model}` + const publicPath = `/test/${model}`; it(`should handle invalid input (${testCaseString})`, async () => { const orca = await instance.create( @@ -459,4 +456,4 @@ describe('Sentence Tests', function() { } } } -}) +}); diff --git a/binding/web/yarn.lock b/binding/web/yarn.lock index de861ec1..6a8b0c54 100644 --- a/binding/web/yarn.lock +++ b/binding/web/yarn.lock @@ -1122,10 +1122,10 @@ dependencies: commander "^9.2.0" -"@picovoice/web-utils@=1.4.2": - version "1.4.2" - resolved "https://registry.yarnpkg.com/@picovoice/web-utils/-/web-utils-1.4.2.tgz#2ddc44552d15fa1a4958e0c3384e58545255eea1" - integrity sha512-pF5Uw3Vm4mOWJ2H3Zc7E/nDr/O7OhbvgEK6W7cx9MNNK3qq51MqiGluPpZ8a2K61BuIzxcNMC1mXWpmIAWVolA== +"@picovoice/web-utils@=1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@picovoice/web-utils/-/web-utils-1.4.3.tgz#1de0b20d6080c18d295c6df37c09d88bf7c4f555" + integrity sha512-7JN3YYsSD9Gtce6YKG3XqpX49dkeu7jTdbox7rHQA/X/Q3zxopXA9zlCKSq6EIjFbiX2iuzDKUx1XrFa3d8c0w== dependencies: commander "^10.0.1" diff --git a/demo/android/OrcaDemo/orca-demo-app/build.gradle b/demo/android/OrcaDemo/orca-demo-app/build.gradle index df701367..1f1b929d 100644 --- a/demo/android/OrcaDemo/orca-demo-app/build.gradle +++ b/demo/android/OrcaDemo/orca-demo-app/build.gradle @@ -33,12 +33,12 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.8.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'ai.picovoice:orca-android:1.0.0' + implementation 'ai.picovoice:orca-android:1.1.0' } tasks.register('copyParams', Copy) { from("${rootDir}/../../../lib/common") - include('orca_params_female.pv') + include('orca_params_en_female.pv') into("${rootDir}/orca-demo-app/src/main/assets") } diff --git a/demo/android/OrcaDemo/orca-demo-app/src/main/java/ai/picovoice/orcademo/MainActivity.java b/demo/android/OrcaDemo/orca-demo-app/src/main/java/ai/picovoice/orcademo/MainActivity.java index 34a4ca12..8a0502c7 100644 --- a/demo/android/OrcaDemo/orca-demo-app/src/main/java/ai/picovoice/orcademo/MainActivity.java +++ b/demo/android/OrcaDemo/orca-demo-app/src/main/java/ai/picovoice/orcademo/MainActivity.java @@ -1,5 +1,5 @@ /* - Copyright 2024 Picovoice Inc. + Copyright 2024-2025 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. @@ -58,7 +58,7 @@ public class MainActivity extends AppCompatActivity { private static final String ACCESS_KEY = "${YOUR_ACCESS_KEY_HERE}"; - private static final String MODEL_FILE = "orca_params_female.pv"; + private static final String MODEL_FILE = "orca_params_en_female.pv"; private static final int STREAMING_NUM_AUDIO_WAIT_CHUNKS = 1; private final Handler mainHandler = new Handler(Looper.getMainLooper()); diff --git a/demo/c/test/test_orca_c.py b/demo/c/test/test_orca_c.py index c88728eb..ce95de4e 100644 --- a/demo/c/test/test_orca_c.py +++ b/demo/c/test/test_orca_c.py @@ -1,5 +1,5 @@ # -# Copyright 2024 Picovoice Inc. +# Copyright 2024-2025 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. diff --git a/demo/c/test/test_util.py b/demo/c/test/test_util.py index 21acd5de..6c677af2 100644 --- a/demo/c/test/test_util.py +++ b/demo/c/test/test_util.py @@ -1,5 +1,5 @@ # -# Copyright 2024 Picovoice Inc. +# Copyright 2024-2025 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. @@ -17,20 +17,22 @@ @dataclass -class TestSentences: +class SentenceTestData: + language: str + models: Sequence[str] + random_state: int text: str text_no_punctuation: str text_custom_pronunciation: str - text_invalid: Sequence[str] -def get_test_data() -> TestSentences: +def get_test_data() -> SentenceTestData: data_file_path = os.path.join(os.path.dirname(__file__), "../../../resources/.test/test_data.json") with open(data_file_path, encoding="utf8") as data_file: - json_test_data = data_file.read() - test_data = json.loads(json_test_data)['test_sentences'] - test_data.pop("text_alignment") - return TestSentences(**test_data) + test_data = json.loads(data_file.read()) + + sentence_tests = [SentenceTestData(**data) for data in test_data['tests']['sentence_tests']] + return sentence_tests[0] def get_model_paths() -> List[str]: diff --git a/demo/dotnet/OrcaDemo/OrcaDemo.csproj b/demo/dotnet/OrcaDemo/OrcaDemo.csproj index a03b3d57..15cc2e89 100644 --- a/demo/dotnet/OrcaDemo/OrcaDemo.csproj +++ b/demo/dotnet/OrcaDemo/OrcaDemo.csproj @@ -18,7 +18,7 @@ - + diff --git a/demo/ios/OrcaDemo/OrcaDemo.xcodeproj/project.pbxproj b/demo/ios/OrcaDemo/OrcaDemo.xcodeproj/project.pbxproj index 1dbe3963..8998670c 100644 --- a/demo/ios/OrcaDemo/OrcaDemo.xcodeproj/project.pbxproj +++ b/demo/ios/OrcaDemo/OrcaDemo.xcodeproj/project.pbxproj @@ -11,8 +11,10 @@ 02A1194B268D39A700A2AC99 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A1194A268D39A700A2AC99 /* ContentView.swift */; }; 02A1194D268D39AB00A2AC99 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 02A1194C268D39AB00A2AC99 /* Assets.xcassets */; }; 02A1195F268D3FD600A2AC99 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A1195E268D3FD600A2AC99 /* ViewModel.swift */; }; + 0751D6642D6D46AA001EB29D /* Orca in Frameworks */ = {isa = PBXBuildFile; productRef = 0751D6632D6D46AA001EB29D /* Orca */; }; + 07F723592D6D594B0002D88F /* Orca in Frameworks */ = {isa = PBXBuildFile; productRef = 07F723582D6D594B0002D88F /* Orca */; }; 1E001B682B76FFE700D8E72D /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E001B672B76FFE700D8E72D /* AudioPlayer.swift */; }; - 1E001B6A2B7D451200D8E72D /* orca_params_female.pv in Resources */ = {isa = PBXBuildFile; fileRef = 1E001B692B7D451200D8E72D /* orca_params_female.pv */; }; + 1E001B6A2B7D451200D8E72D /* orca_params_en_female.pv in Resources */ = {isa = PBXBuildFile; fileRef = 1E001B692B7D451200D8E72D /* orca_params_en_female.pv */; }; E109D1A32CFE4F380014F5AA /* Orca in Frameworks */ = {isa = PBXBuildFile; productRef = E109D1A22CFE4F380014F5AA /* Orca */; }; E125E1892BE99DCA008B6D56 /* AtomicBool.swift in Sources */ = {isa = PBXBuildFile; fileRef = E125E1882BE99DCA008B6D56 /* AtomicBool.swift */; }; E1C5A45F2BE587A2002C0C40 /* AudioPlayerStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1C5A45E2BE587A2002C0C40 /* AudioPlayerStream.swift */; }; @@ -27,7 +29,7 @@ 02A11951268D39AB00A2AC99 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 02A1195E268D3FD600A2AC99 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 1E001B672B76FFE700D8E72D /* AudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = ""; }; - 1E001B692B7D451200D8E72D /* orca_params_female.pv */ = {isa = PBXFileReference; lastKnownFileType = file; name = orca_params_female.pv; path = ../../../../lib/common/orca_params_female.pv; sourceTree = ""; }; + 1E001B692B7D451200D8E72D /* orca_params_en_female.pv */ = {isa = PBXFileReference; lastKnownFileType = file; name = orca_params_en_female.pv; path = ../../../../lib/common/orca_params_en_female.pv; sourceTree = ""; }; E125E1882BE99DCA008B6D56 /* AtomicBool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtomicBool.swift; sourceTree = ""; }; E1C5A45E2BE587A2002C0C40 /* AudioPlayerStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerStream.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -37,6 +39,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0751D6642D6D46AA001EB29D /* Orca in Frameworks */, + 07F723592D6D594B0002D88F /* Orca in Frameworks */, E109D1A32CFE4F380014F5AA /* Orca in Frameworks */, E1F353002D00ED5C0069B0E6 /* Orca in Frameworks */, ); @@ -64,7 +68,7 @@ 02A11947268D39A700A2AC99 /* OrcaDemo */ = { isa = PBXGroup; children = ( - 1E001B692B7D451200D8E72D /* orca_params_female.pv */, + 1E001B692B7D451200D8E72D /* orca_params_en_female.pv */, 02A11948268D39A700A2AC99 /* OrcaDemoApp.swift */, 02A1194A268D39A700A2AC99 /* ContentView.swift */, 02A1194C268D39AB00A2AC99 /* Assets.xcassets */, @@ -121,7 +125,7 @@ ); mainGroup = 02A1193C268D39A700A2AC99; packageReferences = ( - E1F352FE2D00ED5C0069B0E6 /* XCRemoteSwiftPackageReference "orca" */, + 07F723572D6D594B0002D88F /* XCRemoteSwiftPackageReference "orca" */, ); productRefGroup = 02A11946268D39A700A2AC99 /* Products */; projectDirPath = ""; @@ -137,7 +141,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1E001B6A2B7D451200D8E72D /* orca_params_female.pv in Resources */, + 1E001B6A2B7D451200D8E72D /* orca_params_en_female.pv in Resources */, 02A1194D268D39AB00A2AC99 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -345,6 +349,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 07F723572D6D594B0002D88F /* XCRemoteSwiftPackageReference "orca" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Picovoice/orca"; + requirement = { + kind = exactVersion; + version = 1.1.0; + }; + }; E1F352FE2D00ED5C0069B0E6 /* XCRemoteSwiftPackageReference "orca" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/Picovoice/orca"; @@ -356,6 +368,15 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 0751D6632D6D46AA001EB29D /* Orca */ = { + isa = XCSwiftPackageProductDependency; + productName = Orca; + }; + 07F723582D6D594B0002D88F /* Orca */ = { + isa = XCSwiftPackageProductDependency; + package = 07F723572D6D594B0002D88F /* XCRemoteSwiftPackageReference "orca" */; + productName = Orca; + }; E109D1A22CFE4F380014F5AA /* Orca */ = { isa = XCSwiftPackageProductDependency; productName = Orca; diff --git a/demo/ios/OrcaDemo/OrcaDemo/ViewModel.swift b/demo/ios/OrcaDemo/OrcaDemo/ViewModel.swift index 1b9ac382..cadbe8d4 100644 --- a/demo/ios/OrcaDemo/OrcaDemo/ViewModel.swift +++ b/demo/ios/OrcaDemo/OrcaDemo/ViewModel.swift @@ -1,5 +1,5 @@ // -// Copyright 2024 Picovoice Inc. +// Copyright 2024-2025 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 @@ -53,7 +53,7 @@ class ViewModel: ObservableObject { public func initialize() { state = UIState.INIT do { - try orca = Orca(accessKey: ACCESS_KEY, modelPath: "orca_params_female.pv") + try orca = Orca(accessKey: ACCESS_KEY, modelPath: "orca_params_en_female.pv") maxCharacterLimit = orca.maxCharacterLimit! sampleRate = orca.sampleRate! state = UIState.READY diff --git a/demo/llm_voice_assistant/requirements.txt b/demo/llm_voice_assistant/requirements.txt index 850d4722..1f70cd10 100644 --- a/demo/llm_voice_assistant/requirements.txt +++ b/demo/llm_voice_assistant/requirements.txt @@ -11,7 +11,7 @@ openai==1.17.0 pvcheetah==2.0.1 -pvorca==1.0.0 -pvrecorder==1.2.2 +pvorca==1.1.0 +pvrecorder==1.2.5 sounddevice==0.4.6 tiktoken==0.6.0 diff --git a/demo/nodejs/package.json b/demo/nodejs/package.json index c9b41e92..1385c8ca 100644 --- a/demo/nodejs/package.json +++ b/demo/nodejs/package.json @@ -1,6 +1,6 @@ { "name": "@picovoice/orca-node-demo", - "version": "1.0.3", + "version": "1.1.0", "description": "Picovoice Orca Node.js file-based and streaming demos", "scripts": { "file": "node file.js", @@ -21,7 +21,7 @@ "author": "Picovoice Inc.", "license": "Apache-2.0", "dependencies": { - "@picovoice/orca-node": "=1.0.2", + "@picovoice/orca-node": "=1.1.0", "@picovoice/pvspeaker-node": "^1.0.3", "commander": "^6.1.0", "prettier": "^2.6.2", diff --git a/demo/nodejs/yarn.lock b/demo/nodejs/yarn.lock index fb1837eb..7d8ab137 100644 --- a/demo/nodejs/yarn.lock +++ b/demo/nodejs/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@picovoice/orca-node@=1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@picovoice/orca-node/-/orca-node-1.0.2.tgz#9d2f5ee7b578b048037a4012469748d32e79bce2" - integrity sha512-6qOgJmgrfNi/AuJcCPXJHuWtG/3cAlJXayQfK+lmpJ3jXeOtxdHWfgYnUscyE7kYCYVFGX60wadiukuiwm3O7g== +"@picovoice/orca-node@=1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@picovoice/orca-node/-/orca-node-1.1.0.tgz#2077907e5aa10b9b283b11140d411c71e3860e92" + integrity sha512-kEcyaD+i5mT6wMqMJk8bcbLVlRKYCDyekwQ4QbHaKGr6MoegEOlVKuw3wwD3mdRyPzDxFjCRwpb//RvOiN82hg== "@picovoice/pvspeaker-node@^1.0.3": version "1.0.3" diff --git a/demo/python/requirements.txt b/demo/python/requirements.txt index de7639c3..db4cae34 100644 --- a/demo/python/requirements.txt +++ b/demo/python/requirements.txt @@ -1,4 +1,4 @@ numpy>=1.24.0; sys_platform != 'win32' or platform_machine != 'ARM64' -pvorca==1.0.2 -pvspeaker==1.0.4 +pvorca==1.1.0 +pvspeaker==1.0.5 tiktoken==0.8.0; sys_platform != 'win32' or platform_machine != 'ARM64' diff --git a/demo/python/setup.py b/demo/python/setup.py index ce0548e9..18b122ba 100644 --- a/demo/python/setup.py +++ b/demo/python/setup.py @@ -26,14 +26,14 @@ long_description = f.read() if platform.platform() != 'win32' or platform.machine() != 'ARM64': - dependencies = ["numpy>=1.24.0", "pvorca==1.0.2", "pvspeaker==1.0.4", "tiktoken==0.6.0"] + dependencies = ["numpy>=1.24.0", "pvorca==1.1.0", "pvspeaker==1.0.5", "tiktoken==0.6.0"] else: - dependencies = ["pvorca==1.0.2", "pvspeaker==1.0.4"] + dependencies = ["pvorca==1.1.0", "pvspeaker==1.0.5"] setuptools.setup( name="pvorcademo", - version="1.0.3", + version="1.1.0", author="Picovoice", author_email="hello@picovoice.ai", description="Orca Streaming Text-to-Speech Engine demos", diff --git a/demo/web/.gitignore b/demo/web/.gitignore index c5e10112..5f1644aa 100644 --- a/demo/web/.gitignore +++ b/demo/web/.gitignore @@ -4,5 +4,4 @@ node_modules dist/ *.log .DS_Store -orca_params_male.js -orca_params_female.js +models/ diff --git a/demo/web/README.md b/demo/web/README.md index 88164656..d6060bbb 100644 --- a/demo/web/README.md +++ b/demo/web/README.md @@ -65,5 +65,5 @@ voice: ```html - + ``` diff --git a/demo/web/index.html b/demo/web/index.html index 5a95a2cf..6d4d94eb 100644 --- a/demo/web/index.html +++ b/demo/web/index.html @@ -2,7 +2,7 @@ - +