Skip to content

Commit

Permalink
v3.0 react native (#607)
Browse files Browse the repository at this point in the history
  • Loading branch information
laves authored Oct 19, 2023
1 parent 1973ed0 commit 0e4b8f0
Show file tree
Hide file tree
Showing 24 changed files with 5,593 additions and 4,993 deletions.
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

0 comments on commit 0e4b8f0

Please sign in to comment.