1
0
mirror of https://github.com/Picovoice/porcupine.git synced 2022-01-28 03:27:53 +03:00

Merge branch 'master' into updating_packages

This commit is contained in:
Iliad Ramezani
2021-12-14 14:27:07 -08:00
committed by GitHub
17 changed files with 3033 additions and 3039 deletions

View File

@@ -1384,60 +1384,60 @@ npm install @picovoice/porcupine-web-vue
```
```html
<template>
<div class="voice-widget">
<Porcupine
v-bind:porcupineFactoryArgs="{
accessKey: '${ACCESS_KEY}' // AccessKey obtained from [Picovoice Console](https://picovoice.ai/console/),
<script lang="ts">
import porcupineMixin from "@picovoice/porcupine-web-vue";
import { PorcupineWorkerFactoryEn } from "@picovoice/porcupine-web-en-worker";
export default {
name: "App",
mixins: [porcupineMixin],
data: function() {
return {
detections: [] as string[],
isError: false,
isLoaded: false,
factory: PorcupineWorkerFactoryEn,
factoryArgs: {
accessKey: '${ACCESS_KEY}', // AccessKey obtained from Picovoice Console(https://picovoice.ai/console/)
keywords: [
{ builtin: 'Grasshopper', sensitivity: 0.5 },
{ builtin: 'Grapefruit', sensitivity: 0.6 },
],
}"
v-bind:porcupineFactory="factory"
v-on:ppn-ready="ppnReadyFn"
v-on:ppn-keyword="ppnKeywordFn"
v-on:ppn-error="ppnErrorFn"
/>
<h3>Keyword Detections:</h3>
<ul v-if="detections.length > 0">
<li v-for="(item, index) in detections" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script>
import Porcupine from "@picovoice/porcupine-web-vue";
import { PorcupineWorkerFactoryEn } from "@picovoice/porcupine-web-en-worker";
export default {
name: "VoiceWidget",
components: {
Porcupine,
}
};
},
async created() {
await this.$porcupine.init(
this.factoryArgs, // Porcupine factory arguments
this.factory, // Porcupine Web Worker component
this.ppnKeywordFn, // Callback invoked after detection of keyword
this.ppnReadyFn, // Callback invoked after loading Porcupine
this.ppnErrorFn // Callback invoked in an error occurs while initializing Porcupine
);
},
methods: {
start: function () {
if (this.$porcupine.start()) {
this.isListening = !this.isListening;
}
},
data: function () {
return {
detections: [],
isError: null,
isLoaded: false,
factory: PorcupineWorkerFactoryEn,
};
pause: function () {
if (this.$porcupine.pause()) {
this.isListening = !this.isListening;
}
},
methods: {
ppnReadyFn: function () {
this.isLoaded = true;
},
ppnKeywordFn: function (data) {
this.detections = [...this.detections, data.keywordLabel];
},
ppnErrorFn: function (data) {
this.isError = true;
this.errorMessage = data.toString();
},
initEngine: function () {
this.$refs.porcupine.initEngine();
},
ppnReadyFn: function() {
this.isLoaded = true;
},
};
ppnKeywordFn: function(data: string) {
this.detections = [...this.detections, data.keywordLabel];
},
ppnErrorFn: function(error: Error) {
this.isError = true;
this.errorMessage = error.toString();
},
},
};
</script>
```

View File

@@ -1,6 +1,6 @@
# porcupine-web-vue
Renderless Vue component for Porcupine for Web.
Vue mixin for Porcupine Web.
## Porcupine
@@ -12,7 +12,9 @@ applications.
## Compatibility
This library is compatible with Vue 3.
This library is compatible with Vue:
- Vue.js 2.6.11+
- Vue.js 3.0.0+
The Picovoice SDKs for Web are powered by WebAssembly (WASM), the Web Audio API, and Web Workers.
@@ -41,79 +43,73 @@ To obtain your `AccessKey`:
## Usage
Import the Porcupine component and the Porcupine Web Worker component. Bind the worker to Porcupine like the demo `.vue` file below.
Import the Porcupine mixin and the Porcupine Web Worker component. When a component adds the `porcupineMixin`, the variable `$porcupine` is computed with the following functions:
In this example we're passing in two keywords: "Grasshopper" and "Grapefruit" with sensitivities 0.65 and 0.4, respectively. The demo maintains an array of detections which is updated every time the Porcupine `ppn-keyword` event is fired.
- `init`: initializes Porcupine.
- `start`: starts processing audio and detecting keywords.
- `pause`: stops processing audio.
- `delete`: cleans up used resources.
In this example we're passing in two keywords: "Grasshopper" and "Grapefruit" with sensitivities 0.65 and 0.4, respectively. The demo maintains an array of detections which is updated every time the Porcupine detects a keyword.
```html
<template>
<div class="voice-widget">
<Porcupine
v-bind:porcupineFactoryArgs="{
accessKey: 'AccessKey obtained from Picovoice Console(https://picovoice.ai/console/)',
keywords: [
{ builtin: 'Grasshopper', sensitivity: 0.5 },
{ builtin: 'Grapefruit', sensitivity: 0.6 },
],
}"
v-bind:porcupineFactory="factory"
v-on:ppn-ready="ppnReadyFn"
v-on:ppn-keyword="ppnKeywordFn"
v-on:ppn-error="ppnErrorFn"
/>
<h3>Keyword Detections:</h3>
<ul v-if="detections.length > 0">
<li v-for="(item, index) in detections" :key="index">{{ item }}</li>
</ul>
</div>
</template>
```
```javascript
<script>
import Porcupine from "@picovoice/porcupine-web-vue";
<script lang="ts">
import porcupineMixin from "@picovoice/porcupine-web-vue";
import { PorcupineWorkerFactoryEn } from "@picovoice/porcupine-web-en-worker";
export default {
name: "VoiceWidget",
components: {
Porcupine,
},
mixins: [porcupineMixin],
data: function() {
return {
detections: [],
isError: null,
detections: [] as string[],
isError: false,
isLoaded: false,
factory: PorcupineWorkerFactoryEn,
factoryArgs: {
accessKey: '${ACCESS_KEY}', // AccessKey obtained from Picovoice Console(https://picovoice.ai/console/)
keywords: [
{ builtin: 'Grasshopper', sensitivity: 0.5 },
{ builtin: 'Grapefruit', sensitivity: 0.6 },
],
}
};
},
async created() {
await this.$porcupine.init(
this.factoryArgs, // Porcupine factory arguments
this.factory, // Porcupine Web Worker component
this.ppnKeywordFn, // Callback invoked after detection of keyword
this.ppnReadyFn, // Callback invoked after loading Porcupine
this.ppnErrorFn // Callback invoked in an error occurs while initializing Porcupine
);
},
methods: {
start: function () {
if (this.$porcupine.start()) {
this.isListening = !this.isListening;
}
},
pause: function () {
if (this.$porcupine.pause()) {
this.isListening = !this.isListening;
}
},
ppnReadyFn: function() {
this.isLoaded = true;
},
ppnKeywordFn: function(data) {
ppnKeywordFn: function(data: string) {
this.detections = [...this.detections, data.keywordLabel];
},
ppnErrorFn: function(data) {
ppnErrorFn: function(error: Error) {
this.isError = true;
this.errorMessage = data.toString();
this.errorMessage = error.toString();
},
},
};
</script>
```
## Events
The Porcupine component will emit the following events:
| Event | Data | Description |
| ------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| "ppn-loading" | | Porcupine has begun loading |
| "ppn-ready" | | Porcupine has finished loading & the user has granted microphone permission: ready to process voice |
| "ppn-keyword" | The label of the keyword (e.g. "Grasshopper") | Porcupine has detected a keyword |
| "ppn-error" | The error that was caught (e.g. "NotAllowedError: Permission denied") | An error occurred while Porcupine or the WebVoiceProcessor was loading |
### Custom wake words
Each language includes a set of built-in keywords. The quickest way to get started is to use one of those. The builtin keywords are licensed under Apache-2.0 and are completely free to use.
@@ -122,27 +118,13 @@ Custom wake words are generated using [Picovoice Console](https://picovoice.ai/c
The `.zip` file contains a `.ppn` file and a `_b64.txt` file which contains the binary model encoded with Base64. Copy the base64 and provide it as an argument to Porcupine as below. You will need to also provide a label so that Porcupine can tell you which keyword occurred ("Deep Sky Blue", in this case):
```html
<template>
<div class="voice-widget">
<Porcupine
v-bind:porcupineFactoryArgs="{
accessKey: 'AccessKey obtained from Picovoice Console(https://picovoice.ai/console/)',
keywords: [
{ base64: '/* Base64 representation of deep_sky_blue.ppn */', custom: 'Deep Sky Blue', sensitivity: 0.65 },
],
}"
v-bind:porcupineFactory="factory"
v-on:ppn-ready="ppnReadyFn"
v-on:ppn-keyword="ppnKeywordFn"
v-on:ppn-error="ppnErrorFn"
/>
<h3>Keyword Detections:</h3>
<ul v-if="detections.length > 0">
<li v-for="(item, index) in detections" :key="index">{{ item }}</li>
</ul>
</div>
</template>
```typescript
factoryArgs: {
accessKey: 'AccessKey obtained from Picovoice Console(https://picovoice.ai/console/)',
keywords: [
{ custom: base64: '/* Base64 representation of deep_sky_blue.ppn */', custom: 'Deep Sky Blue', sensitivity: 0.65 },
],
}
```
You may wish to store the base64 string in a separate JavaScript file and export it to keep your application code separate.

View File

@@ -1,11 +1,45 @@
{
"name": "@picovoice/porcupine-web-vue",
"version": "2.0.0",
"description": "Vue component (renderless) for Porcupine Web SDK",
"entry": "src/index.js",
"module": "dist/esm/index.js",
"version": "2.0.1",
"description": "Vue mixin for Porcupine Web SDK",
"author": "Picovoice Inc",
"entry": "src/index.ts",
"iife": "dist/iife/index.js",
"license": "Apache-2.0",
"module": "dist/esm/index.js",
"types": "dist/types/index.d.ts",
"scripts": {
"build": "npm-run-all --parallel build:**",
"lint": "eslint . --ext .js,.ts",
"build:all": "rollup --config",
"build:types": "tsc --declaration --declarationMap --emitDeclarationOnly --outDir ./dist/types",
"format": "prettier --write \"**/*.{js,ts,json}\"",
"prepack": "npm-run-all build",
"start": "cross-env TARGET='debug' rollup --config --watch",
"watch": "rollup --config --watch"
},
"devDependencies": {
"@babel/core": "^7.12.13",
"@babel/plugin-transform-runtime": "^7.12.15",
"@babel/preset-env": "^7.12.13",
"@babel/runtime": "^7.12.13",
"@picovoice/web-voice-processor": "^2.1.2",
"@rollup/plugin-babel": "^5.2.3",
"@rollup/plugin-commonjs": "^17.1.0",
"@rollup/plugin-node-resolve": "^11.1.1",
"cross-env": "^7.0.3",
"eslint": "^7.19.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.2.1",
"rollup": "^2.38.5",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.29.0",
"rollup-plugin-web-worker-loader": "^1.6.0",
"typescript": "~4.1.5"
},
"peerDependencies": {
"@picovoice/web-voice-processor": "^2.1.2",
"vue": "^2.6.11 || ^3.0.0"
},
"keywords": [
"porcupine",
"web",
@@ -21,36 +55,5 @@
"vue",
"renderless"
],
"author": "Picovoice Inc",
"scripts": {
"build:all": "rollup --config",
"build": "npm-run-all --parallel build:**",
"lint": "eslint . --ext .js,.ts",
"prepack": "npm-run-all build",
"start": "cross-env TARGET='debug' rollup --config --watch",
"watch": "rollup --config --watch",
"format": "prettier --write \"**/*.{js,ts,json}\""
},
"devDependencies": {
"@babel/core": "^7.12.13",
"@babel/plugin-transform-runtime": "^7.12.15",
"@babel/preset-env": "^7.12.13",
"@babel/runtime": "^7.12.13",
"@picovoice/web-voice-processor": "^2.1.2",
"@rollup/plugin-babel": "^5.2.3",
"@rollup/plugin-commonjs": "^17.1.0",
"@rollup/plugin-node-resolve": "^11.1.1",
"@vue/compiler-sfc": "^3.0.7",
"cross-env": "^7.0.3",
"eslint": "^7.19.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.2.1",
"rollup": "^2.38.5",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-vue": "^6.0.0"
},
"peerDependencies": {
"vue": "^3.0.0",
"@picovoice/web-voice-processor": "^2.1.2"
}
"license": "Apache-2.0"
}

View File

@@ -2,13 +2,14 @@
const path = require('path');
const { nodeResolve } = require('@rollup/plugin-node-resolve');
const commonjs = require('@rollup/plugin-commonjs');
const typescript = require('rollup-plugin-typescript2');
const workerLoader = require('rollup-plugin-web-worker-loader');
const pkg = require('./package.json');
const { babel } = require('@rollup/plugin-babel');
const terser = require('rollup-plugin-terser').terser;
const vue = require('rollup-plugin-vue');
const { DEFAULT_EXTENSIONS } = require('@babel/core');
const extensions = DEFAULT_EXTENSIONS;
const extensions = [...DEFAULT_EXTENSIONS, '.ts'];
console.log(process.env.TARGET);
console.log(extensions);
@@ -26,7 +27,6 @@ console.log(iifeBundleName);
export default {
input: [path.resolve(__dirname, pkg.entry)],
external: ['vue'],
output: [
{
file: path.resolve(__dirname, pkg['module']),
@@ -60,9 +60,14 @@ export default {
},
],
plugins: [
vue(),
nodeResolve({ extensions }),
commonjs(),
workerLoader({ targetPlatform: 'browser', sourcemap: false }),
typescript({
typescript: require('typescript'),
cacheRoot: path.resolve(__dirname, '.rts2_cache'),
clean: true,
}),
babel({
extensions: extensions,
babelHelpers: 'runtime',

View File

@@ -1,88 +0,0 @@
<template>
<div>
<slot />
</div>
</template>
<script>
import { WebVoiceProcessor } from '@picovoice/web-voice-processor';
/**
* Porcupine Vue Component.
*
* Props
* - porcupineFactoryArgs: Arguments for PorcupineWorkerFactory.
* - PorcupineWorkerFactory: The language-specific worker factory.
*
* Events
* - ppn-ready: A method invoked after component has initialized.
* - ppn-keyword: A method invoked upon detection of the keywords.
* - ppn-error: A method invoked if an error occurs within `PorcupineWorkerFactory`.
*/
export default {
name: 'Porcupine',
props: {
porcupineFactoryArgs: [Object],
porcupineFactory: [Function],
},
data: function () {
return { webVp: null, ppnWorker: null };
},
methods: {
/**
* Method to start processing audio.
*/
start() {
if (this.webVp !== null) {
this.webVp.start();
return true;
}
return false;
},
/**
* Method to stop processing audio.
*/
pause() {
if (this.webVp !== null) {
this.webVp.pause();
return true;
}
return false;
},
keywordCallback(label) {
this.$emit('ppn-keyword', label);
},
errorCallback(error) {
this.$emit('ppn-error', error);
},
/**
* Method to initialize PorcupineWorker.
*/
async initEngine() {
try {
const { accessKey, keywords } = this.porcupineFactoryArgs;
this.ppnWorker = await this.porcupineFactory.create(
accessKey,
JSON.parse(JSON.stringify(keywords)),
this.keywordCallback,
this.errorCallback
);
this.webVp = await WebVoiceProcessor.init({
engines: [this.ppnWorker],
});
this.$emit('ppn-ready');
} catch (error) {
this.$emit('ppn-error', error);
}
},
},
beforeUnmount: function () {
if (this.webVp !== null) {
this.webVp.release();
}
if (this.ppnWorker !== null) {
this.ppnWorker.postMessage({ command: 'release' });
}
},
};
</script>

View File

@@ -1,28 +0,0 @@
// Import vue component
import component from './Porcupine.vue';
// Declare install function executed by Vue.use()
export function install(Vue) {
if (install.installed) return;
install.installed = true;
Vue.component('Porcupine', component);
}
// Create module definition for Vue.use()
const plugin = {
install,
};
// Auto-install when vue is found (eg. in browser via <script> tag)
let GlobalVue = null;
if (typeof window !== 'undefined') {
GlobalVue = window.Vue;
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue;
}
if (GlobalVue) {
GlobalVue.use(plugin);
}
// To allow use as module (npm/webpack/etc.) export component
export default component;

44
binding/vue/src/index.ts Normal file
View File

@@ -0,0 +1,44 @@
import porcupineMixin from './porcupine';
import {
PorcupineKeyword,
PorcupineKeywordCustom,
PorcupineKeywordBuiltin,
PorcupineWorkerFactoryArgs,
PorcupineWorkerFactory,
PorcupineVue
} from './porcupine_types';
// Create module definition for Vue.use()
const plugin = {
install: function(Vue: any) {
Vue.mixin(porcupineMixin);
}
};
// // Auto-install when vue is found (eg. in browser via <script> tag)
let GlobalVue = null;
if (typeof window !== 'undefined') {
// @ts-ignore
GlobalVue = window.Vue;
// @ts-ignore
} else if (typeof global !== 'undefined') {
// @ts-ignore
GlobalVue = global.Vue;
}
if (GlobalVue) {
GlobalVue.use(plugin);
}
// To allow use as module (npm/webpack/etc.) export component
export default porcupineMixin;
// export types
export {
PorcupineKeyword,
PorcupineKeywordCustom,
PorcupineKeywordBuiltin,
PorcupineWorkerFactoryArgs,
PorcupineWorkerFactory,
PorcupineVue
};

View File

@@ -0,0 +1,83 @@
import { WebVoiceProcessor } from '@picovoice/web-voice-processor';
import { PorcupineVue } from './porcupine_types';
export default {
computed: {
/**
* Porcupine Vue Mixin.
*/
$porcupine(): PorcupineVue {
return {
$_ppnWorker_: null as Worker | null,
$_webVp_: null as WebVoiceProcessor | null,
/**
* Init function for Porcupine.
*
* @param porcupineFactoryArgs Arguments for PorcupineWorkerFactory.
* @param porcupineFactory The language-specific worker factory
* @param keywordCallback A method invoked upon detection of the keywords.
* @param readyCallback A method invoked after component has initialized.
* @param errorCallback A method invoked if an error occurs within `PorcupineWorkerFactory`.
*/
async init(
porcupineFactoryArgs,
porcupineFactory,
keywordCallback = (_: string) => {},
readyCallback = () => {},
errorCallback = (error: Error) => {console.error(error)}
) {
try {
const { accessKey, keywords } = porcupineFactoryArgs;
this.$_ppnWorker_ = await porcupineFactory.create(
accessKey,
JSON.parse(JSON.stringify(keywords)),
keywordCallback,
errorCallback
);
this.$_webVp_ = await WebVoiceProcessor.init({
engines: [this.$_ppnWorker_!],
});
readyCallback();
} catch (error) {
errorCallback(error as Error);
}
},
/**
* Start processing audio.
*/
start() {
if (this.$_webVp_ !== null) {
this.$_webVp_.start();
return true;
}
return false;
},
/**
* Stop processing audio.
*/
pause() {
if (this.$_webVp_ !== null) {
this.$_webVp_.pause();
return true;
}
return false;
},
/**
* Delete used resources.
*/
delete() {
this.$_webVp_?.release();
this.$_ppnWorker_?.postMessage({ command: 'release' });
}
}
}
},
// Vue 3 method to clean resources.
beforeUnmount(this: any) {
this.$porcupine.delete();
},
// Vue 2 method to clean resources.
beforeDestory(this: any) {
this.$porcupine.delete();
}
};

View File

@@ -0,0 +1,64 @@
import { WebVoiceProcessor } from '@picovoice/web-voice-processor';
/**
* Type alias for the Porcupine keywords.
*/
export type PorcupineKeyword = PorcupineKeywordCustom | PorcupineKeywordBuiltin;
/**
* Type alias for builtin keywords.
*/
export type PorcupineKeywordBuiltin = {
builtin: string;
sensitivity?: number;
};
/**
* Type alias for custom keywords.
*/
export type PorcupineKeywordCustom = {
base64: string;
custom: string;
sensitivity?: number;
};
/**
* Type alias for PorcupineWorkerFactory arguments.
*/
export type PorcupineWorkerFactoryArgs = {
accessKey: string;
keywords: Array<PorcupineKeyword | string> | PorcupineKeyword | string;
start?: boolean;
};
/**
* The language-specific worker factory, imported as { PorcupineWorkerFactory } from the
* @picovoice/porcupine-web-xx-worker series of packages, where xx is the two-letter language code.
*/
export interface PorcupineWorkerFactory extends Object {
create: (
accessKey: string,
keywords: Array<PorcupineKeyword | string> | PorcupineKeyword | string,
keywordDetectionCallback?: CallableFunction,
processErrorCallback?: CallableFunction,
start?: boolean) => Promise<Worker>,
};
/**
* Type alias for Porcupine Vue Mixin.
* Use with `Vue as VueConstructor extends {$porcupine: PorcupineVue}` to get types in typescript.
*/
export interface PorcupineVue {
$_ppnWorker_: Worker | null;
$_webVp_: WebVoiceProcessor | null;
init: (
porcupineFactoryArgs: PorcupineWorkerFactoryArgs,
porcupineFactory: PorcupineWorkerFactory,
keywordCallback: (label: string) => void,
readyCallback: () => void,
errorCallback: (error: Error) => void) => void;
start: () => boolean;
pause: () => boolean;
delete: () => void;
}

37
binding/vue/tsconfig.json Normal file
View File

@@ -0,0 +1,37 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules",
"dist"
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# porcupine-web-vue-demo
This demo application includes a sample `VoiceWidget` Vue component which uses the `Porcupine` renderless Vue component service to allow listening for keywords. Porcupine keyword detections are handled via the `ppn-keyword` event. Our VoiceWidget subscribes to this event and displays the results.
This demo application includes a sample `VoiceWidget` Vue component which uses the `porcupineMixin` Vue mixin which integrates components to allow listening for keywords. Porcupine keyword detections are handled via the `keywordCallback` function.
The demo uses dynamic imports to split the VoiceWidget away from the main application bundle. This means that the initial download size of the Vue app will not be impacted by the ~1-2 MB requirement of Porcupine. While small for all-in-one offline Voice AI, the size is large for an initial web app load.

View File

@@ -15,7 +15,8 @@
"@picovoice/porcupine-web-vue": "^2.0.1",
"@picovoice/web-voice-processor": "^2.1.2",
"core-js": "^3.6.5",
"vue": "^3.0.0"
"vue": "^2.6.11",
"vue-template-compiler": "^2.6.11"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.18.0",
@@ -24,7 +25,6 @@
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^7.0.0",
"eslint": "^6.7.2",

View File

@@ -1,22 +1,24 @@
<template>
<h1>Porcupine Web + Vue ("Porcupine" Renderless Component)</h1>
<button v-on:click="toggle">
Toggle VoiceWidget <span v-if="show">"OFF"</span><span v-else>"ON"</span>
</button>
<br />
<br />
<VoiceWidget v-if="show" />
<div>
<h1>Porcupine Web + Vue ("Porcupine" Renderless Component)</h1>
<button v-on:click="toggle">
Toggle VoiceWidget <span v-if="show">"OFF"</span><span v-else>"ON"</span>
</button>
<br />
<br />
<VoiceWidget v-if="show" />
</div>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent } from "vue";
import VoiceWidget from "./components/VoiceWidget.vue";
import Vue from "vue";
export default defineComponent({
export default Vue.extend({
name: "App",
components: {
VoiceWidget: defineAsyncComponent(
() => import("./components/VoiceWidget.vue")
VoiceWidget: Vue.component(
'VoiceWidget',
async () => await import("./components/VoiceWidget.vue")
),
},
data: function () {

View File

@@ -1,13 +1,5 @@
<template>
<div class="voice-widget">
<Porcupine
ref="porcupine"
v-bind:porcupineFactoryArgs="factoryArgs"
v-bind:porcupineFactory="factory"
v-on:ppn-ready="ppnReadyFn"
v-on:ppn-keyword="ppnKeywordFn"
v-on:ppn-error="ppnErrorFn"
/>
<h2>VoiceWidget</h2>
<h3>
<label>
@@ -42,19 +34,20 @@
</div>
</template>
<script>
import Porcupine from "@picovoice/porcupine-web-vue";
import { PorcupineWorkerFactory as PorcupineWorkerFactoryEn } from "@picovoice/porcupine-web-en-worker";
<script lang="ts">
import Vue, { VueConstructor } from 'vue';
export default {
import { PorcupineWorkerFactory as PorcupineWorkerFactoryEn } from "@picovoice/porcupine-web-en-worker";
import porcupineMixin, { PorcupineVue } from "@picovoice/porcupine-web-vue";
const VoiceWidget = (Vue as VueConstructor<Vue & {$porcupine: PorcupineVue}>).extend({
name: "VoiceWidget",
components: {
Porcupine,
},
data: function () {
mixins: [porcupineMixin],
data() {
return {
detections: [],
detections: [] as string[],
isError: false,
errorMessage: null as string | null,
isLoaded: false,
isListening: false,
factory: PorcupineWorkerFactoryEn,
@@ -69,36 +62,44 @@ export default {
},
methods: {
start: function () {
if (this.$refs.porcupine.start()) {
if (this.$porcupine.start()) {
this.isListening = !this.isListening;
}
},
pause: function () {
if (this.$refs.porcupine.pause()) {
if (this.$porcupine.pause()) {
this.isListening = !this.isListening;
}
},
initEngine: function (event) {
initEngine: function (event: any) {
this.factoryArgs.accessKey = event.target.value;
this.isError = false;
this.isLoaded = false;
this.isListening = false;
this.$refs.porcupine.initEngine();
},
this.$porcupine.init(
this.factoryArgs,
this.factory,
this.ppnKeywordFn,
this.ppnReadyFn,
this.ppnErrorFn
);
},
ppnReadyFn: function () {
this.isLoaded = true;
this.isListening = true;
},
ppnKeywordFn: function (keywordLabel) {
ppnKeywordFn: function (keywordLabel: string) {
console.log(keywordLabel);
this.detections = [...this.detections, keywordLabel];
},
ppnErrorFn: function (error) {
ppnErrorFn: function (error: Error) {
this.isError = true;
this.errorMessage = error.toString();
},
},
};
});
export default VoiceWidget;
</script>
<style scoped>

View File

@@ -1,8 +1,6 @@
import { createApp } from "vue";
import Vue from "vue";
import App from "./App.vue";
const porcupineDemoApp = createApp(App)
porcupineDemoApp.component('VoiceWidget',
() => import('./components/VoiceWidget.vue')
)
porcupineDemoApp.mount("#app");
new Vue({
render: h => h(App),
}).$mount('#app');

File diff suppressed because it is too large Load Diff