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

v3.0 react native #607

Merged
merged 4 commits into from
Oct 19, 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
20 changes: 16 additions & 4 deletions .github/workflows/react-native-demos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@ jobs:
with:
node-version: ${{ matrix.node-version }}

- name: Pre-build dependencies
run: npm install yarn
# ************ REMOVE AFTER RELEASE *****************
- name: Build and package binding
working-directory: binding/react-native
run: yarn && yarn pkg

- name: Add to demo
run: yarn add ../../binding/react-native/pkg/picovoice-rhino-react-native-3.0.0.tgz
# ***************************************************

- name: Install dependencies
run: yarn android-install
Expand All @@ -63,8 +69,14 @@ jobs:
with:
node-version: ${{ matrix.node-version }}

- name: Pre-build dependencies
run: npm install yarn
# ************ REMOVE AFTER RELEASE *****************
- name: Build and package binding
working-directory: binding/react-native
run: yarn && yarn pkg

- name: Add to demo
run: yarn add ../../binding/react-native/pkg/picovoice-rhino-react-native-3.0.0.tgz
# ***************************************************

- name: Install dependencies
run: yarn ios-install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/react-native-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:

- name: Cocoapods install
working-directory: binding/react-native/test-app/RhinoTestApp/ios
run: pod install
run: pod install --repo-update

- name: Inject AppID
run: sed -i '.bak' 's:{TESTING_ACCESS_KEY_HERE}:${{secrets.PV_VALID_ACCESS_KEY}}:' Tests.ts
Expand Down
5 changes: 4 additions & 1 deletion binding/react-native/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ buildscript {
google()
jcenter()
mavenCentral()
maven {
url 'https://s01.oss.sonatype.org/content/repositories/aipicovoice-1267'
}
}

dependencies {
Expand Down Expand Up @@ -120,5 +123,5 @@ repositories {
dependencies {
// noinspection GradleDynamicVersion
api 'com.facebook.react:react-native:+'
implementation 'ai.picovoice:rhino-android:2.2.2'
implementation 'ai.picovoice:rhino-android:3.0.0'
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ public void delete(String handle) {
}
}

@ReactMethod
public void reset(String handle, Promise promise) {
try {
if (!rhinoPool.containsKey(handle)) {
promise.reject(
RhinoInvalidStateException.class.getSimpleName(),
"Invalid Rhino handle provided to native module.");
return;
}

Rhino rhino = rhinoPool.get(handle);
rhino.reset();
promise.resolve(null);
} catch (RhinoException e) {
promise.reject(e.getClass().getSimpleName(), e.getMessage());
}
}

@ReactMethod
public void process(String handle, ReadableArray pcmArray, Promise promise) {
try {
Expand Down
6 changes: 5 additions & 1 deletion binding/react-native/ios/Rhino.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2020 Picovoice Inc.
// Copyright 2020-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.
Expand All @@ -24,6 +24,10 @@ @interface RCT_EXTERN_MODULE(PvRhino, NSObject)

RCT_EXTERN_METHOD(delete: (NSString *)handle)

RCT_EXTERN_METHOD(reset: (NSString *)handle
resolver: (RCTPromiseResolveBlock)resolve
rejecter: (RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(process: (NSString *)handle
pcm:(NSArray<NSNumber>)pcm
resolver: (RCTPromiseResolveBlock)resolve
Expand Down
22 changes: 21 additions & 1 deletion binding/react-native/ios/Rhino.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2020-2022 Picovoice Inc.
// Copyright 2020-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.
Expand Down Expand Up @@ -63,6 +63,26 @@ class PvRhino: NSObject {
}
}

@objc(reset:resolver:rejecter:)
func reset(handle: String, resolver resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
do {
if let rhino = rhinoPool[handle] {
try rhino.reset()
resolve(nil)
} else {
let (code, message) = errorToCodeAndMessage(
RhinoInvalidStateError("Invalid handle provided to Rhino 'process'"))
reject(code, message, nil)
}
} catch let error as RhinoError {
let (code, message) = errorToCodeAndMessage(error)
reject(code, message, nil)
} catch {
let (code, message) = errorToCodeAndMessage(RhinoError(error.localizedDescription))
reject(code, message, nil)
}
}

@objc(process:pcm:resolver:rejecter:)
func process(handle: String, pcm: [Int16],
resolver resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
Expand Down
2 changes: 1 addition & 1 deletion binding/react-native/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@picovoice/rhino-react-native",
"version": "2.2.2",
"version": "3.0.0",
"description": "Picovoice Rhino React Native binding",
"main": "lib/commonjs/index",
"module": "lib/module/index",
Expand Down
2 changes: 1 addition & 1 deletion binding/react-native/rhino-react-native.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ Pod::Spec.new do |s|
s.source_files = "ios/*.{h,m,mm,swift}"

s.dependency "React"
s.dependency 'Rhino-iOS', '~> 2.2.2'
s.dependency 'Rhino-iOS', '~> 3.0.0'
end
43 changes: 27 additions & 16 deletions binding/react-native/src/rhino.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,30 @@ export class RhinoInference {
}

/**
* whether Rhino has made an inference
* Whether Rhino has made an inference
*/
get isFinalized() {
public get isFinalized(): boolean {
return this._isFinalized;
}

/**
* if isFinalized, whether Rhino understood what it heard based on the context
* If `isFinalized`, whether Rhino understood what it heard based on the context.
*/
get isUnderstood() {
public get isUnderstood(): boolean | undefined {
return this._isUnderstood;
}

/**
* if isUnderstood, name of intent that was inferred
* If `isUnderstood`, name of intent that was inferred.
*/
get intent() {
public get intent(): string | undefined {
return this._intent;
}

/**
* if isUnderstood, dictionary of slot keys and values that were inferred
* If `isUnderstood`, dictionary of slot keys and values that were inferred.
*/
get slots() {
public get slots(): { [key: string]: string } | undefined {
return this._slots;
}
}
Expand Down Expand Up @@ -143,7 +143,7 @@ class Rhino {
* - intent: if isUnderstood, name of intent that were inferred
* - slots: if isUnderstood, dictionary of slot keys and values that were inferred
*/
async process(frame: number[]): Promise<RhinoInference> {
public async process(frame: number[]): Promise<RhinoInference> {
if (frame === undefined || frame === null) {
throw new RhinoErrors.RhinoInvalidArgumentError(
`Frame array provided to process() is undefined or null`
Expand Down Expand Up @@ -172,9 +172,17 @@ class Rhino {
}

/**
* Frees memory that was allocated for Rhino
* Resets the internal state of Rhino. It should be called before the engine
* can be used to infer intent from a new stream of audio.
*/
async delete() {
public async reset(): Promise<void> {
return RCTRhino.reset(this._handle);
}

/**
* Frees memory that was allocated for Rhino.
*/
public async delete(): Promise<void> {
return RCTRhino.delete(this._handle);
}

Expand All @@ -183,31 +191,31 @@ class Rhino {
* which expressions map to those intents, as well as slots and their possible values.
* @returns The context YAML
*/
get contextInfo() {
public get contextInfo(): string {
return this._contextInfo;
}

/**
* Gets the required number of audio samples per frame.
* @returns Required frame length.
*/
get frameLength() {
public get frameLength(): number {
return this._frameLength;
}

/**
* Get the audio sample rate required by Rhino.
* @returns Required sample rate.
*/
get sampleRate() {
public get sampleRate(): number {
return this._sampleRate;
}

/**
* Gets the version number of the Rhino library.
* @returns Version of Rhino
*/
get version() {
public get version(): string {
return this._version;
}

Expand All @@ -216,7 +224,10 @@ class Rhino {
* @param code Code name of native Error.
* @param message Detailed message of the error.
*/
private static codeToError(code: string, message: string) {
private static codeToError(
code: string,
message: string
): RhinoErrors.RhinoError {
switch (code) {
case 'RhinoException':
return new RhinoErrors.RhinoError(message);
Expand Down
20 changes: 10 additions & 10 deletions binding/react-native/src/rhino_manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class RhinoManager {
sensitivity: number = 0.5,
endpointDurationSec: number = 1.0,
requireEndpoint: boolean = true
) {
): Promise<RhinoManager> {
let rhino = await Rhino.create(
accessKey,
contextPath,
Expand Down Expand Up @@ -113,7 +113,7 @@ class RhinoManager {
}

/**
* Opens audio input stream and sends audio frames to Rhino
* Opens audio input stream and sends audio frames to Rhino.
*/
public async process(): Promise<void> {
if (this._isListening) {
Expand Down Expand Up @@ -149,7 +149,7 @@ class RhinoManager {
}

/**
* Closes audio stream
* Closes audio stream.
*/
private async _stop(): Promise<void> {
if (!this._isListening) {
Expand All @@ -173,11 +173,11 @@ class RhinoManager {
}

/**
* Releases resources and listeners
* Releases resources and listeners.
*/
delete() {
public async delete(): Promise<void> {
if (this._rhino !== null) {
this._rhino.delete();
await this._rhino.delete();
this._rhino = null;
}
}
Expand All @@ -187,31 +187,31 @@ class RhinoManager {
* which expressions map to those intents, as well as slots and their possible values.
* @returns The context YAML
*/
get contextInfo() {
public get contextInfo(): string | undefined {
return this._rhino?.contextInfo;
}

/**
* Gets the required number of audio samples per frame.
* @returns Required frame length.
*/
get frameLength() {
public get frameLength(): number | undefined {
return this._rhino?.frameLength;
}

/**
* Get the audio sample rate required by Rhino.
* @returns Required sample rate.
*/
get sampleRate() {
public get sampleRate(): number | undefined {
return this._rhino?.sampleRate;
}

/**
* Gets the version number of the Rhino library.
* @returns Version of Rhino
*/
get version() {
public get version(): string | undefined {
return this._rhino?.version;
}
}
Expand Down
39 changes: 38 additions & 1 deletion binding/react-native/test-app/RhinoTestApp/Tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,49 @@ async function outOfContextTest(testcases: any): Promise<Result[]> {
return results;
}

async function resetTest(): Promise<Result> {
const audioFilePath = getPath('audio_samples/test_out_of_context.wav');
const contextPath = getPath(
`context_files/en/smart_lighting_${platform}.rhn`,
);
const modelPath = getPath('model_files/rhino_params.pv');

const result: Result = {testName: 'Reset test', success: false};
let rhino = null;
try {
rhino = await Rhino.create(accessKey, contextPath, modelPath);

const pcm = await getPcmFromFile(audioFilePath, rhino.sampleRate);
const frameLength = rhino.frameLength;
let isFinalized = false;
for (let i = 0; i < pcm.length - frameLength; i += frameLength) {
if (i === Math.floor((pcm.length - frameLength) / 2)) {
await rhino.reset();
}

const inference = await rhino.process(pcm.slice(i, i + frameLength));
isFinalized = inference.isFinalized;
if (isFinalized === true) {
break;
}
}
result.success = isFinalized === false;

await rhino.delete();
} catch (error) {
result.success = false;
result.errorString = `${error}`;
}
return result;
}

export async function runRhinoTests(): Promise<Result[]> {
const withinContextResults = await withinContextTest(
testData.tests.within_context,
);
const outOfContextResults = await outOfContextTest(
testData.tests.out_of_context,
);
return [...withinContextResults, ...outOfContextResults];
const resetResult = await resetTest();
return [...withinContextResults, ...outOfContextResults, resetResult];
}
Loading
Loading