Fangjun Kuang
Committed by GitHub

Add on-device real-time ASR demo for HarmonyOS (#1606)

正在显示 55 个修改的文件 包含 1341 行增加4 行删除
... ... @@ -10,5 +10,8 @@
VAD + Non-streaming ASR for speech recognition.
Please see the doc at <https://k2-fsa.github.io/sherpa/onnx/harmony-os/vad-asr.html>
- [./SherpaOnnxStreamingAsr](./SherpaOnnxStreamingAsr) It shows how to use
streaming ASR models for real-time on-device speech recognition.
- [./SherpaOnnxTts](./SherpaOnnxTts) It shows how to run on-device text-to-speech.
Please see the doc at <https://k2-fsa.github.io/sherpa/onnx/harmony-os/tts.html>
... ...
/node_modules
/oh_modules
/local.properties
/.idea
**/build
/.hvigor
.cxx
/.clangd
/.clang-format
/.clang-tidy
**/.test
/.appanalyzer
\ No newline at end of file
... ...
{
"app": {
"bundleName": "com.k2fsa.sherpa.onnx.streaming.asr",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:app_icon",
"label": "$string:app_name"
}
}
... ...
{
"string": [
{
"name": "app_name",
"value": "SherpaOnnxStreamingAsr"
}
]
}
... ...
{
"app": {
"signingConfigs": [],
"products": [
{
"name": "default",
"signingConfig": "default",
"compatibleSdkVersion": "4.0.0(10)",
"runtimeOS": "HarmonyOS",
"buildOption": {
"strictMode": {
"caseSensitiveCheck": true,
}
}
}
],
"buildModeSet": [
{
"name": "debug",
},
{
"name": "release"
}
]
},
"modules": [
{
"name": "entry",
"srcPath": "./entry",
"targets": [
{
"name": "default",
"applyToProducts": [
"default"
]
}
]
}
]
}
\ No newline at end of file
... ...
{
"files": [
"**/*.ets"
],
"ignore": [
"**/src/ohosTest/**/*",
"**/src/test/**/*",
"**/src/mock/**/*",
"**/node_modules/**/*",
"**/oh_modules/**/*",
"**/build/**/*",
"**/.preview/**/*"
],
"ruleSet": [
"plugin:@performance/recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
}
}
\ No newline at end of file
... ...
/node_modules
/oh_modules
/.preview
/build
/.cxx
/.test
\ No newline at end of file
... ...
{
"apiType": "stageMode",
"buildOption": {
"sourceOption": {
"workers": [
'./src/main/ets/workers/StreamingAsrWorker.ets'
]
}
},
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": false,
"files": [
"./obfuscation-rules.txt"
]
}
}
}
},
],
"targets": [
{
"name": "default"
},
{
"name": "ohosTest",
}
]
}
\ No newline at end of file
... ...
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
export default {
system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
}
... ...
# Define project specific obfuscation rules here.
# You can include the obfuscation configuration files in the current module's build-profile.json5.
#
# For more details, see
# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5
# Obfuscation options:
# -disable-obfuscation: disable all obfuscations
# -enable-property-obfuscation: obfuscate the property names
# -enable-toplevel-obfuscation: obfuscate the names in the global scope
# -compact: remove unnecessary blank spaces and all line feeds
# -remove-log: remove all console.* statements
# -print-namecache: print the name cache that contains the mapping from the old names to new names
# -apply-namecache: reuse the given cache file
# Keep options:
# -keep-property-name: specifies property names that you want to keep
# -keep-global-name: specifies names that you want to keep in the global scope
-enable-property-obfuscation
-enable-toplevel-obfuscation
-enable-filename-obfuscation
-enable-export-obfuscation
\ No newline at end of file
... ...
{
"meta": {
"stableOrder": true
},
"lockfileVersion": 3,
"ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
"specifiers": {
"libsherpa_onnx.so@../oh_modules/.ohpm/sherpa_onnx@1.10.33/oh_modules/sherpa_onnx/src/main/cpp/types/libsherpa_onnx": "libsherpa_onnx.so@../oh_modules/.ohpm/sherpa_onnx@1.10.33/oh_modules/sherpa_onnx/src/main/cpp/types/libsherpa_onnx",
"sherpa_onnx@1.10.33": "sherpa_onnx@1.10.33"
},
"packages": {
"libsherpa_onnx.so@../oh_modules/.ohpm/sherpa_onnx@1.10.33/oh_modules/sherpa_onnx/src/main/cpp/types/libsherpa_onnx": {
"name": "libsherpa_onnx.so",
"version": "1.0.0",
"resolved": "../oh_modules/.ohpm/sherpa_onnx@1.10.33/oh_modules/sherpa_onnx/src/main/cpp/types/libsherpa_onnx",
"registryType": "local"
},
"sherpa_onnx@1.10.33": {
"name": "sherpa_onnx",
"version": "1.10.33",
"integrity": "sha512-cmZ8zwOMx4qmDvOjF1/PL6/suBgReanSf5XdQTuMWWZ6qN74rynODHrt4C+Qz754MTXg0q/phAKeVjGA4rHHSA==",
"resolved": "https://ohpm.openharmony.cn/ohpm/sherpa_onnx/-/sherpa_onnx-1.10.33.har",
"registryType": "ohpm",
"dependencies": {
"libsherpa_onnx.so": "file:./src/main/cpp/types/libsherpa_onnx"
}
}
}
}
\ No newline at end of file
... ...
{
"name": "entry",
"version": "1.0.0",
"description": "Please describe the basic information.",
"main": "",
"author": "",
"license": "",
"dependencies": {
"sherpa_onnx": "1.10.33",
}
}
... ...
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
}
... ...
import hilog from '@ohos.hilog';
import BackupExtensionAbility, { BundleVersion } from '@ohos.application.BackupExtensionAbility';
export default class EntryBackupAbility extends BackupExtensionAbility {
async onBackup() {
hilog.info(0x0000, 'testTag', 'onBackup ok');
}
async onRestore(bundleVersion: BundleVersion) {
hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion));
}
}
\ No newline at end of file
... ...
import { LengthUnit } from '@kit.ArkUI';
import worker, { MessageEvents } from '@ohos.worker';
import { BusinessError } from '@kit.BasicServicesKit';
import { picker } from '@kit.CoreFileKit';
import systemTime from '@ohos.systemTime';
import { Permissions } from '@kit.AbilityKit';
import { allAllowed, requestPermissions } from './Permission';
import { audio } from '@kit.AudioKit';
import fs from '@ohos.file.fs';
function savePcmToWav(filename: string, samples: Int16Array, sampleRate: number) {
const fp = fs.openSync(filename, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
const header = new ArrayBuffer(44);
const view = new DataView(header);
// http://soundfile.sapp.org/doc/WaveFormat/
// F F I R
view.setUint32(0, 0x46464952, true); // chunkID
view.setUint32(4, 36 + samples.length * 2, true); // chunkSize // E V A W
view.setUint32(8, 0x45564157, true); // format // // t m f
view.setUint32(12, 0x20746d66, true); // subchunk1ID
view.setUint32(16, 16, true); // subchunk1Size, 16 for PCM
view.setUint32(20, 1, true); // audioFormat, 1 for PCM
view.setUint16(22, 1, true); // numChannels: 1 channel
view.setUint32(24, sampleRate, true); // sampleRate
view.setUint32(28, sampleRate * 2, true); // byteRate
view.setUint16(32, 2, true); // blockAlign
view.setUint16(34, 16, true); // bitsPerSample
view.setUint32(36, 0x61746164, true); // Subchunk2ID
view.setUint32(40, samples.length * 2, true); // subchunk2Size
fs.writeSync(fp.fd, new Uint8Array(header).buffer, { length: header.byteLength });
fs.writeSync(fp.fd, samples.buffer, { length: samples.buffer.byteLength });
fs.closeSync(fp.fd);
}
function toInt16Samples(samples: Float32Array): Int16Array {
const int16Samples = new Int16Array(samples.length);
for (let i = 0; i < samples.length; ++i) {
let s = samples[i] * 32767;
s = s > 32767 ? 32767 : s;
s = s < -32768 ? -32768 : s;
int16Samples[i] = s;
}
return int16Samples;
}
@Entry
@Component
struct Index {
@State title: string = 'Next-gen Kaldi: Real-time speech recognition';
@State titleFontSize: number = 15;
@State currentIndex: number = 0;
@State lang: string = 'English';
@State resultForFile: string = ''
@State resultForMic: string = ''
@State selectFileBtnEnabled: boolean = false;
@State micBtnCaption: string = 'Start';
@State micStarted: boolean = false;
@State micAllowed: boolean = false;
@State micBtnEnabled: boolean = false;
@State micSaveBtnCaption: string = 'Save recorded audio';
@State micSaveBtnEnabled: boolean = false;
@State info: string = '';
@State micInfo: string = '';
@State micInitDone: boolean = false;
private resultListForMic: string[] = [];
private controller: TabsController = new TabsController();
private workerInstance?: worker.ThreadWorker
private readonly scriptURL: string = 'entry/ets/workers/StreamingAsrWorker.ets'
private startTime: number = 0;
private stopTime: number = 0;
private sampleRate: number = 48000;
private sampleList: Float32Array[] = []
private mic?: audio.AudioCapturer;
flatten(samples: Float32Array[]): Float32Array {
let n = 0;
for (let i = 0; i < samples.length; ++i) {
n += samples[i].length;
}
const ans: Float32Array = new Float32Array(n);
let offset: number = 0;
for (let i = 0; i < samples.length; ++i) {
ans.set(samples[i], offset);
offset += samples[i].length;
}
return ans;
}
async initMic() {
const permissions: Permissions[] = ["ohos.permission.MICROPHONE"];
let allowed: boolean = await allAllowed(permissions);
if (!allowed) {
console.log("request to access the microphone");
const status: boolean = await requestPermissions(permissions);
if (!status) {
console.error('access to microphone is denied')
this.resultForMic = "Failed to get microphone permission. Please retry";
return;
}
allowed = await allAllowed(permissions);
if (!allowed) {
console.error('failed to get microphone permission');
this.resultForMic = "Failed to get microphone permission. Please retry";
return;
}
this.micAllowed = true;
} else {
console.log("allowed to access microphone");
this.micAllowed = true;
}
const audioStreamInfo: audio.AudioStreamInfo = {
samplingRate: this.sampleRate,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW,
};
const audioCapturerInfo: audio.AudioCapturerInfo = {
source: audio.SourceType.SOURCE_TYPE_MIC, capturerFlags: 0
};
const audioCapturerOptions: audio.AudioCapturerOptions = {
streamInfo: audioStreamInfo, capturerInfo: audioCapturerInfo
};
audio.createAudioCapturer(audioCapturerOptions, (err, data) => {
if (err) {
console.error(`error code is ${err.code}, error message is ${err.message}`);
this.resultForMic = 'Failed to init microphone';
} else {
console.info(`init mic successfully`);
this.mic = data;
this.mic.on('readData', this.micCallback);
}
});
}
async aboutToAppear() {
this.workerInstance = new worker.ThreadWorker(this.scriptURL, {
name: 'Streaming ASR worker'
});
this.workerInstance.onmessage = (e: MessageEvents) => {
const msgType = e.data['msgType'] as string;
console.log(`received msg from worker: ${msgType}`);
if (msgType == 'init-streaming-asr-done') {
this.selectFileBtnEnabled = true;
this.micBtnEnabled = true;
this.info = `Initializing done.\n\nPlease select a wave file of 16kHz in language ${this.lang}`;
this.micInfo = `Initializing done.\n\nPlease click Start and speak`;
}
if (msgType == 'streaming-asr-decode-file-done') {
const text = e.data['text'] as string;
this.resultForFile = text;
this.selectFileBtnEnabled = true;
systemTime.getRealTime((err, data) => {
if (err) {
console.log('Failed to get stop time');
} else {
this.stopTime = data;
const audioDuration = e.data['duration'] as number;
const elapsedSeconds = (this.stopTime - this.startTime) / 1000;
const RTF = elapsedSeconds / audioDuration;
this.info = `Audio duration: ${audioDuration.toFixed(2)} s
Elapsed: ${elapsedSeconds.toFixed(2)} s
RTF = ${elapsedSeconds.toFixed(2)}/${audioDuration.toFixed(2)} = ${RTF.toFixed(3)}
`;
}
});
}
if (msgType == 'streaming-asr-decode-mic-result') {
const text = e.data['text'] as string;
if (text.trim() == '') {
return;
}
const isEndpoint = e.data['isEndpoint'] as boolean;
let s = '';
let i = 0;
for (; i < this.resultListForMic.length; ++i) {
s += `${i}: ${this.resultListForMic[i]}\n`
}
s += `${i}: ${text}`;
this.resultForMic = s;
if (isEndpoint) {
this.resultListForMic.push(text);
}
}
};
const context = getContext();
this.workerInstance.postMessage({ msgType: 'init-streaming-asr', context });
this.info = 'Initializing ASR model.\nPlease wait';
this.micInfo = 'Initializing ASR model.\nPlease wait';
await this.initMic();
}
@Builder
TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
Column() {
Image(this.currentIndex == targetIndex ? selectedImg : normalImg).size({ width: 25, height: 25 })
Text(title).fontColor(this.currentIndex == targetIndex ? '#28bff1' : '#8a8a8a')
}.width('100%').height(50).justifyContent(FlexAlign.Center).onClick(() => {
this.currentIndex = targetIndex;
this.controller.changeIndex(this.currentIndex);
})
}
build() {
Column() {
Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
TabContent() {
Column({ space: 10 }) {
Text(this.title).fontSize(this.titleFontSize).fontWeight(FontWeight.Bold);
Button('Select .wav file (16kHz) ')
.enabled(this.selectFileBtnEnabled)
.fontSize(13)
.width(296)
.height(60)
.onClick(() => {
this.resultForFile = '';
this.info = '';
this.selectFileBtnEnabled = false;
const documentSelectOptions = new picker.DocumentSelectOptions();
documentSelectOptions.maxSelectNumber = 1;
documentSelectOptions.fileSuffixFilters = ['.wav'];
const documentViewPicker = new picker.DocumentViewPicker();
documentViewPicker.select(documentSelectOptions).then((result: Array<string>) => {
console.log(`select file result: ${result}`);
if (!result[0]) {
this.resultForFile = 'Please select a file to decode';
this.selectFileBtnEnabled = true;
return;
}
if (this.workerInstance) {
systemTime.getRealTime((err, data) => {
if (err) {
console.log('Failed to get start time');
} else {
this.startTime = data;
}
});
this.workerInstance.postMessage({
msgType: 'streaming-asr-decode-file', filename: result[0],
});
this.info = `Decoding ${result[0]} ... ...`;
} else {
console.log(`this worker instance is undefined ${this.workerInstance}`);
}
}).catch((err: BusinessError) => {
console.error(`Failed to select file, code is ${err.code}, message is ${err.message}`);
this.selectFileBtnEnabled = true;
})
})
Text(`Supported languages: ${this.lang}`);
if (this.info != '') {
TextArea({ text: this.info }).focusable(false);
}
TextArea({ text: this.resultForFile })
.width('100%')
.lineSpacing({ value: 10, unit: LengthUnit.VP })
.height('100%');
}
}.tabBar(this.TabBuilder('From file', 0, $r('app.media.icon_doc'), $r('app.media.icon_doc')))
TabContent() {
Column({ space: 10 }) {
Text(this.title).fontSize(this.titleFontSize).fontWeight(FontWeight.Bold);
Button(this.micBtnCaption)
.enabled(this.micBtnEnabled)
.fontSize(13)
.width(296)
.height(60)
.onClick(() => {
this.micInfo = '';
if (this.mic) {
if (this.micStarted) {
this.micStarted = false;
this.micBtnCaption = 'Start';
this.mic.stop();
this.micSaveBtnEnabled = true;
if (this.workerInstance) {
this.workerInstance.postMessage({
msgType: 'streaming-asr-decode-mic-stop'
});
}
} else {
this.micStarted = true;
this.micSaveBtnEnabled = false;
this.micBtnCaption = 'Stop';
this.resultForMic = '';
this.resultListForMic = [];
if (this.workerInstance) {
this.workerInstance.postMessage({
msgType: 'streaming-asr-decode-mic-start'
});
}
this.sampleList = [];
this.mic.start();
}
}
});
Button(this.micSaveBtnCaption)
.enabled(this.micSaveBtnEnabled)
.fontSize(13)
.width(296)
.height(60)
.onClick(() => {
if (this.sampleList.length == 0) {
this.micSaveBtnEnabled = false;
return;
}
const samples = this.flatten(this.sampleList);
if (samples.length == 0) {
this.micSaveBtnEnabled = false;
return;
}
let uri: string = '';
const audioOptions = new picker.AudioSaveOptions(); // audioOptions.newFileNames = ['o.wav'];
const audioViewPicker = new picker.AudioViewPicker();
audioViewPicker.save(audioOptions).then((audioSelectResult: Array<string>) => {
uri = audioSelectResult[0];
savePcmToWav(uri, toInt16Samples(samples), this.sampleRate);
console.log(`Saved to ${uri}`);
this.micInfo += `\nSaved to ${uri}`;
});
})
Text(`Supported languages: ${this.lang}`)
if (this.micInfo != '') {
TextArea({ text: this.micInfo })
.focusable(false);
}
TextArea({ text: this.resultForMic })
.width('100%')
.lineSpacing({ value: 10, unit: LengthUnit.VP })
.width('100%')
.height('100%');
}
}.tabBar(this.TabBuilder('From mic', 1, $r('app.media.icon_mic'), $r('app.media.icon_mic')))
TabContent() {
Column({ space: 10 }) {
Text(this.title).fontSize(this.titleFontSize).fontWeight(FontWeight.Bold);
TextArea({
text: `
Everyting is open-sourced.
It runs locally, without accessing the network
See also https://github.com/k2-fsa/sherpa-onnx
新一代 Kaldi QQ 和微信交流群: 请看
https://k2-fsa.github.io/sherpa/social-groups.html
微信公众号: 新一代 Kaldi
`
}).width('100%').height('100%').focusable(false)
}.justifyContent(FlexAlign.Start)
}.tabBar(this.TabBuilder('Help', 2, $r('app.media.info'), $r('app.media.info')))
}.scrollable(false)
}.width('100%')
}
private micCallback = (buffer: ArrayBuffer) => {
const view: Int16Array = new Int16Array(buffer);
const samplesFloat: Float32Array = new Float32Array(view.length);
for (let i = 0; i < view.length; ++i) {
samplesFloat[i] = view[i] / 32768.0;
}
this.sampleList.push(samplesFloat);
if (this.workerInstance) {
this.workerInstance.postMessage({
msgType: 'streaming-asr-decode-mic-samples',
samples: samplesFloat,
sampleRate: this.sampleRate,
})
}
}
}
\ No newline at end of file
... ...
// This file is modified from
// https://gitee.com/ukSir/hmchat2/blob/master/entry/src/main/ets/utils/permissionMananger.ets
import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit';
export function allAllowed(permissions: Permissions[]): boolean {
if (permissions.length == 0) {
return false;
}
const mgr: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
let tokenID: number = bundleInfo.appInfo.accessTokenId;
return permissions.every(permission => abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED ==
mgr.checkAccessTokenSync(tokenID, permission));
}
export async function requestPermissions(permissions: Permissions[]): Promise<boolean> {
const mgr: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
const context: Context = getContext() as common.UIAbilityContext;
const result = await mgr.requestPermissionsFromUser(context, permissions);
return result.authResults.length > 0 && result.authResults.every(authResults => authResults == 0);
}
\ No newline at end of file
... ...
import worker, { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope } from '@ohos.worker';
import {
OnlineModelConfig,
OnlineRecognizer,
OnlineRecognizerConfig,
OnlineStream,
readWaveFromBinary,
Samples
} from 'sherpa_onnx';
import { fileIo } from '@kit.CoreFileKit';
const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
let recognizer: OnlineRecognizer;
let micStream: OnlineStream;
function getModelConfig(type: number): OnlineModelConfig {
const modelConfig = new OnlineModelConfig();
switch (type) {
case 0: {
const modelDir = 'sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20';
modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.onnx`;
modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.onnx`;
modelConfig.tokens = `${modelDir}/tokens.txt`;
modelConfig.modelType = 'zipformer';
break;
}
case 1: {
const modelDir = 'sherpa-onnx-lstm-zh-2023-02-20';
modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-11-avg-1.onnx`;
modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-11-avg-1.onnx`;
modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-11-avg-1.onnx`;
modelConfig.tokens = `${modelDir}/tokens.txt`;
modelConfig.modelType = 'lstm';
break;
}
case 2: {
const modelDir = 'sherpa-onnx-lstm-en-2023-02-17';
modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.onnx`;
modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.onnx`;
modelConfig.tokens = `${modelDir}/tokens.txt`;
modelConfig.modelType = 'lstm';
break;
}
case 3: {
const modelDir = 'icefall-asr-zipformer-streaming-wenetspeech-20230615';
modelConfig.transducer.encoder = `${modelDir}/exp/encoder-epoch-12-avg-4-chunk-16-left-128.int8.onnx`;
modelConfig.transducer.decoder = `${modelDir}/exp/decoder-epoch-12-avg-4-chunk-16-left-128.onnx`;
modelConfig.transducer.joiner = `${modelDir}/exp/joiner-epoch-12-avg-4-chunk-16-left-128.onnx`;
modelConfig.tokens = `${modelDir}/data/lang_char/tokens.txt`;
modelConfig.modelType = 'zipformer2';
break;
}
case 4: {
const modelDir = 'icefall-asr-zipformer-streaming-wenetspeech-20230615';
modelConfig.transducer.encoder = `${modelDir}/exp/encoder-epoch-12-avg-4-chunk-16-left-128.onnx`;
modelConfig.transducer.decoder = `${modelDir}/exp/decoder-epoch-12-avg-4-chunk-16-left-128.onnx`;
modelConfig.transducer.joiner = `${modelDir}/exp/joiner-epoch-12-avg-4-chunk-16-left-128.onnx`;
modelConfig.tokens = `${modelDir}/data/lang_char/tokens.txt`;
modelConfig.modelType = 'zipformer2';
break;
}
case 5: {
const modelDir = 'sherpa-onnx-streaming-paraformer-bilingual-zh-en';
modelConfig.paraformer.encoder = `${modelDir}/encoder.int8.onnx`;
modelConfig.paraformer.decoder = `${modelDir}/decoder.int8.onnx`;
modelConfig.tokens = `${modelDir}/tokens.txt`;
modelConfig.modelType = 'paraformer';
break;
}
case 6: {
const modelDir = 'sherpa-onnx-streaming-zipformer-en-2023-06-26';
modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1-chunk-16-left-128.int8.onnx`;
modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1-chunk-16-left-128.onnx`;
modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1-chunk-16-left-128.onnx`;
modelConfig.tokens = `${modelDir}/tokens.txt`;
modelConfig.modelType = 'zipformer2';
break;
}
case 7: {
const modelDir = 'sherpa-onnx-streaming-zipformer-fr-2023-04-14';
modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-29-avg-9-with-averaged-model.int8.onnx`;
modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-29-avg-9-with-averaged-model.onnx`;
modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-29-avg-9-with-averaged-model.onnx`;
modelConfig.tokens = `${modelDir}/tokens.txt`;
modelConfig.modelType = 'zipformer';
break;
}
case 8: {
const modelDir = 'sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20';
modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`;
modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`;
modelConfig.tokens = `${modelDir}/tokens.txt`;
modelConfig.modelType = 'zipformer';
break;
}
case 9: {
const modelDir = 'sherpa-onnx-streaming-zipformer-zh-14M-2023-02-23'
modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`;
modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`;
modelConfig.tokens = `${modelDir}/tokens.txt`;
modelConfig.modelType = 'zipformer';
break;
}
case 10: {
const modelDir = 'sherpa-onnx-streaming-zipformer-en-20M-2023-02-17';
modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`;
modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`;
modelConfig.tokens = `${modelDir}/tokens.txt`;
modelConfig.modelType = 'zipformer';
break;
}
case 14: {
const modelDir = 'sherpa-onnx-streaming-zipformer-korean-2024-06-16';
modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`;
modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`;
modelConfig.tokens = `${modelDir}/tokens.txt`;
modelConfig.modelType = 'zipformer';
break;
}
default: {
console.log(`Please specify a supported type. Given type ${type}`);
}
}
return modelConfig;
}
function initStreamingAsr(context: Context): OnlineRecognizer {
let type: number;
/*
If you use type = 8, then you should have the following directory structure in the rawfile directory
(py38) fangjuns-MacBook-Pro:rawfile fangjun$ pwd
/Users/fangjun/open-source/sherpa-onnx/harmony-os/SherpaOnnxStreamingAsr/entry/src/main/resources/rawfile
(py38) fangjuns-MacBook-Pro:rawfile fangjun$ ls
sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20
(py38) fangjuns-MacBook-Pro:rawfile fangjun$ tree .
.
└── sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20
├── decoder-epoch-99-avg-1.onnx
├── encoder-epoch-99-avg-1.int8.onnx
├── joiner-epoch-99-avg-1.int8.onnx
└── tokens.txt
1 directory, 4 files
You can download model files from
https://github.com/k2-fsa/sherpa-onnx/releases/tag/asr-models
Note that please delete files that are not used. Otherwise, you APP will be very large
due to containing unused large files.
*/
type = 8;
const config: OnlineRecognizerConfig = new OnlineRecognizerConfig();
config.modelConfig = getModelConfig(type);
config.modelConfig.debug = true;
config.modelConfig.numThreads = 2;
config.enableEndpoint = true;
return new OnlineRecognizer(config, context.resourceManager);
}
interface DecodeFileResult {
text: string;
duration: number;
}
function decodeFile(filename: string): DecodeFileResult {
const fp = fileIo.openSync(filename);
const stat = fileIo.statSync(fp.fd);
const arrayBuffer = new ArrayBuffer(stat.size);
fileIo.readSync(fp.fd, arrayBuffer);
const data: Uint8Array = new Uint8Array(arrayBuffer);
const wave: Samples = readWaveFromBinary(data) as Samples;
console.log(`Sample rate: ${wave.sampleRate}`);
const stream = recognizer.createStream();
stream.acceptWaveform(wave);
const tailPadding = new Float32Array(0.5 * wave.sampleRate);
tailPadding.fill(0);
stream.acceptWaveform({ samples: tailPadding, sampleRate: wave.sampleRate });
while (recognizer.isReady(stream)) {
recognizer.decode(stream);
}
const audioDuration = wave.samples.length / wave.sampleRate;
return { text: recognizer.getResult(stream).text, duration: audioDuration };
}
/**
* Defines the event handler to be called when the worker thread receives a message sent by the host thread.
* The event handler is executed in the worker thread.
*
* @param e message data
*/
workerPort.onmessage = (e: MessageEvents) => {
const msgType = e.data['msgType'] as string;
if (msgType != 'streaming-asr-decode-mic-samples') {
console.log(`from the main thread, msg-type: ${msgType}`);
}
if (msgType == 'init-streaming-asr' && !recognizer) {
console.log('initializing streaming ASR...');
const context = e.data['context'] as Context;
recognizer = initStreamingAsr(context);
console.log('streaming ASR is initialized. ');
workerPort.postMessage({ 'msgType': 'init-streaming-asr-done' });
}
if (msgType == 'streaming-asr-decode-file') {
const filename = e.data['filename'] as string;
console.log(`decoding ${filename}`);
const result = decodeFile(filename);
workerPort.postMessage({
'msgType': 'streaming-asr-decode-file-done', text: result.text, duration: result.duration
});
}
if (msgType == 'streaming-asr-decode-mic-start') {
micStream = recognizer.createStream();
}
if (msgType == 'streaming-asr-decode-mic-stop') { // nothing to do
}
if (msgType == 'streaming-asr-decode-mic-samples') {
const samples = e.data['samples'] as Float32Array;
const sampleRate = e.data['sampleRate'] as number;
micStream.acceptWaveform({ samples, sampleRate });
while (recognizer.isReady(micStream)) {
recognizer.decode(micStream);
let isEndpoint = false;
let text = recognizer.getResult(micStream).text;
if (recognizer.isEndpoint(micStream)) {
isEndpoint = true;
recognizer.reset(micStream);
}
if (text.trim() != '') {
workerPort.postMessage({
'msgType': 'streaming-asr-decode-mic-result', text: text, isEndpoint: isEndpoint,
});
}
}
}
}
/**
* Defines the event handler to be called when the worker receives a message that cannot be deserialized.
* The event handler is executed in the worker thread.
*
* @param e message data
*/
workerPort.onmessageerror = (e: MessageEvents) => {
}
/**
* Defines the event handler to be called when an exception occurs during worker execution.
* The event handler is executed in the worker thread.
*
* @param e error message
*/
workerPort.onerror = (e: ErrorEvent) => {
}
\ No newline at end of file
... ...
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet",
"2in1"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
],
"extensionAbilities": [
{
"name": "EntryBackupAbility",
"srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
"type": "backup",
"exported": false,
"metadata": [
{
"name": "ohos.extension.backup",
"resource": "$profile:backup_config"
}
],
}
],
"requestPermissions": [
{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:mic_reason",
"usedScene": {
"abilities": [
"EntryAbility",
],
"when": "inuse",
}
}
]
}
}
\ No newline at end of file
... ...
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
}
]
}
\ No newline at end of file
... ...
{
"string": [
{
"name": "module_desc",
"value": "On-device real-time speech recognition with Next-gen Kaldi"
},
{
"name": "EntryAbility_desc",
"value": "On-device real-time speech recognition with Next-gen Kaldi"
},
{
"name": "EntryAbility_label",
"value": "Real-time ASR"
},
{
"name": "mic_reason",
"value": "access the microhone for on-device real-time speech recognition with Next-gen Kaldi"
}
]
}
\ No newline at end of file
... ...
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -960 960 960"><path d="m480-840 440 330-48 64-72-54v380H160v-380l-72 54-48-64zM294-478q0 53 57 113t129 125q72-65 129-125t57-113q0-44-30-73t-72-29q-26 0-47.5 10.5T480-542q-15-17-37.5-27.5T396-580q-42 0-72 29t-30 73m426 278v-360L480-740 240-560v360zm0 0H240z"/></svg>
\ No newline at end of file
... ...
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><rect width="24" height="24" opacity="0"></rect><g><path d="M6.22 1.01Q5.35 1.01 4.61 1.45Q3.86 1.9 3.42 2.64Q2.98 3.38 2.98 4.25L2.98 19.75Q2.98 20.64 3.42 21.38Q3.86 22.13 4.61 22.56Q5.35 22.99 6.22 22.99L17.76 22.99Q18.65 22.99 19.39 22.56Q20.14 22.13 20.58 21.38Q21.02 20.64 21.02 19.75L21.02 7.25L14.76 1.01L6.22 1.01ZM15.48 7.25Q15.17 7.25 14.95 7.02Q14.74 6.79 14.74 6.48L14.74 3.1L18.89 7.25L15.48 7.25ZM6.22 21.5Q5.5 21.5 4.98 20.99Q4.46 20.47 4.46 19.75L4.46 4.25Q4.46 3.53 4.98 3.01Q5.5 2.5 6.22 2.5L13.22 2.5L13.22 6.48Q13.22 7.1 13.52 7.62Q13.82 8.14 14.34 8.44Q14.86 8.74 15.48 8.74L19.51 8.74L19.51 19.75Q19.51 20.47 19 20.99Q18.48 21.5 17.76 21.5L6.22 21.5Z" fill="rgba(0,0,0,0.9019607843137255)"></path></g></svg>
\ No newline at end of file
... ...
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><rect width="24" height="24" opacity="0"></rect><g><path d="M12 3.46Q13.06 3.46 13.78 4.18Q14.5 4.9 14.5 5.95L14.5 11.21Q14.5 12.24 13.78 12.97Q13.06 13.7 12 13.7Q10.97 13.7 10.24 12.97Q9.5 12.24 9.5 11.21L9.5 5.95Q9.5 4.9 10.24 4.18Q10.97 3.46 12 3.46ZM12 1.94Q10.92 1.94 10 2.48Q9.07 3.02 8.53 3.95Q7.99 4.87 7.99 5.95L7.99 11.21Q7.99 12.29 8.53 13.21Q9.07 14.14 10 14.68Q10.92 15.22 12 15.22Q13.08 15.22 14 14.68Q14.93 14.14 15.47 13.21Q16.01 12.29 16.01 11.21L16.01 5.95Q16.01 4.87 15.47 3.95Q14.93 3.02 14 2.48Q13.08 1.94 12 1.94ZM19.51 11.23Q19.51 10.92 19.28 10.69Q19.06 10.46 18.74 10.46Q18.43 10.46 18.22 10.69Q18 10.92 18 11.23Q18 12.84 17.2 14.22Q16.39 15.6 15.01 16.4Q13.63 17.21 12 17.21Q10.37 17.21 8.99 16.4Q7.61 15.6 6.8 14.22Q6 12.84 6 11.23Q6 10.92 5.78 10.69Q5.57 10.46 5.26 10.46Q4.94 10.46 4.73 10.69Q4.51 10.92 4.51 11.23Q4.51 13.13 5.4 14.76Q6.29 16.39 7.84 17.44Q9.38 18.48 11.26 18.67L11.26 21.29Q11.26 21.6 11.47 21.82Q11.69 22.03 12 22.03Q12.31 22.03 12.53 21.82Q12.74 21.6 12.74 21.29L12.74 18.67Q14.62 18.48 16.16 17.44Q17.71 16.39 18.61 14.76Q19.51 13.13 19.51 11.23Z" fill="rgba(0,0,0,0.9019607843137255)"></path></g></svg>
\ No newline at end of file
... ...
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -960 960 960"><path d="M440-280h80v-240h-80zm40-320q17 0 28.5-11.5T520-640t-11.5-28.5T480-680t-28.5 11.5T440-640t11.5 28.5T480-600m0 520q-83 0-156-31.5T197-197t-85.5-127T80-480t31.5-156T197-763t127-85.5T480-880t156 31.5T763-763t85.5 127T880-480t-31.5 156T763-197t-127 85.5T480-80m0-80q134 0 227-93t93-227-93-227-227-93-227 93-93 227 93 227 227 93m0-320"/></svg>
\ No newline at end of file
... ...
{
"layered-image":
{
"background" : "$media:background",
"foreground" : "$media:foreground"
}
}
\ No newline at end of file
... ...
{
"allowToBackupRestore": true
}
\ No newline at end of file
... ...
{
"string": [
{
"name": "module_desc",
"value": "On-device real-time speech recognition with Next-gen Kaldi"
},
{
"name": "EntryAbility_desc",
"value": "On-device real-time speech recognition with Next-gen Kaldi"
},
{
"name": "EntryAbility_label",
"value": "Real-time ASR"
},
{
"name": "mic_reason",
"value": "access the microhone for on-device real-time speech recognition with Next-gen Kaldi"
}
]
}
\ No newline at end of file
... ...
{
"string": [
{
"name": "module_desc",
"value": "新一代Kaldi: 本地实时语音识别"
},
{
"name": "EntryAbility_desc",
"value": "新一代Kaldi: 本地实时语音识别"
},
{
"name": "EntryAbility_label",
"value": "实时语音识别"
},
{
"name": "mic_reason",
"value": "使用新一代Kaldi, 访问麦克风进行本地实时语音识别 (不需要联网)"
}
]
}
\ No newline at end of file
... ...
import hilog from '@ohos.hilog';
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
export default function abilityTest() {
describe('ActsAbilityTest', () => {
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
beforeAll(() => {
// Presets an action, which is performed only once before all test cases of the test suite start.
// This API supports only one parameter: preset action function.
})
beforeEach(() => {
// Presets an action, which is performed before each unit test case starts.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: preset action function.
})
afterEach(() => {
// Presets a clear action, which is performed after each unit test case ends.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: clear action function.
})
afterAll(() => {
// Presets a clear action, which is performed after all test cases of the test suite end.
// This API supports only one parameter: clear action function.
})
it('assertContain', 0, () => {
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
let a = 'abc';
let b = 'b';
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
expect(a).assertContain(b);
expect(a).assertEqual(a);
})
})
}
\ No newline at end of file
... ...
import abilityTest from './Ability.test';
export default function testsuite() {
abilityTest();
}
\ No newline at end of file
... ...
{
"module": {
"name": "entry_test",
"type": "feature",
"deviceTypes": [
"phone",
"tablet",
"2in1"
],
"deliveryWithInstall": true,
"installationFree": false
}
}
... ...
import localUnitTest from './LocalUnit.test';
export default function testsuite() {
localUnitTest();
}
\ No newline at end of file
... ...
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
export default function localUnitTest() {
describe('localUnitTest', () => {
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
beforeAll(() => {
// Presets an action, which is performed only once before all test cases of the test suite start.
// This API supports only one parameter: preset action function.
});
beforeEach(() => {
// Presets an action, which is performed before each unit test case starts.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: preset action function.
});
afterEach(() => {
// Presets a clear action, which is performed after each unit test case ends.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: clear action function.
});
afterAll(() => {
// Presets a clear action, which is performed after all test cases of the test suite end.
// This API supports only one parameter: clear action function.
});
it('assertContain', 0, () => {
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
let a = 'abc';
let b = 'b';
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
expect(a).assertContain(b);
expect(a).assertEqual(a);
});
});
}
\ No newline at end of file
... ...
{
"modelVersion": "5.0.0",
"dependencies": {
},
"execution": {
// "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */
// "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */
// "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */
// "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */
// "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */
},
"logging": {
// "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */
},
"debugging": {
// "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */
},
"nodeOptions": {
// "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/
// "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/
}
}
... ...
import { appTasks } from '@ohos/hvigor-ohos-plugin';
export default {
system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
}
... ...
{
"meta": {
"stableOrder": true
},
"lockfileVersion": 3,
"ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
"specifiers": {
"@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19"
},
"packages": {
"@ohos/hypium@1.0.19": {
"name": "@ohos/hypium",
"version": "1.0.19",
"integrity": "sha512-cEjDgLFCm3cWZDeRXk7agBUkPqjWxUo6AQeiu0gEkb3J8ESqlduQLSIXeo3cCsm8U/asL7iKjF85ZyOuufAGSQ==",
"resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.19.har",
"registryType": "ohpm"
}
}
}
\ No newline at end of file
... ...
{
"modelVersion": "5.0.0",
"description": "Please describe the basic information.",
"dependencies": {
},
"devDependencies": {
"@ohos/hypium": "1.0.19"
}
}
... ...
... ... @@ -229,7 +229,7 @@ struct Index {
.lineSpacing({ value: 10, unit: LengthUnit.VP })
.height('100%');
}.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Start)
}.tabBar(this.TabBuilder('From file', 0, $r('app.media.icon_doc'), $r('app.media.icon_doc_default')))
}.tabBar(this.TabBuilder('From file', 0, $r('app.media.icon_doc'), $r('app.media.icon_doc')))
TabContent() {
Column({ space: 10 }) {
... ... @@ -278,8 +278,8 @@ struct Index {
.height('100%');
}.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Start)
}
.tabBar(this.TabBuilder('From mic', 1, $r('app.media.ic_public_input_voice'),
$r('app.media.ic_public_input_voice_default')))
.tabBar(this.TabBuilder('From mic', 1, $r('app.media.icon_mic'),
$r('app.media.icon_mic')))
TabContent() {
Column({ space: 10 }) {
... ... @@ -300,7 +300,7 @@ https://k2-fsa.github.io/sherpa/social-groups.html
`
}).width('100%').height('100%').focusable(false)
}.justifyContent(FlexAlign.Start)
}.tabBar(this.TabBuilder('Help', 2, $r('app.media.info_circle'), $r('app.media.info_circle_default')))
}.tabBar(this.TabBuilder('Help', 2, $r('app.media.info'), $r('app.media.info')))
}.scrollable(false)
}.width('100%').justifyContent(FlexAlign.Start)
... ...
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><rect width="24" height="24" opacity="0"></rect><g><path d="M6.22 1.01Q5.35 1.01 4.61 1.45Q3.86 1.9 3.42 2.64Q2.98 3.38 2.98 4.25L2.98 19.75Q2.98 20.64 3.42 21.38Q3.86 22.13 4.61 22.56Q5.35 22.99 6.22 22.99L17.76 22.99Q18.65 22.99 19.39 22.56Q20.14 22.13 20.58 21.38Q21.02 20.64 21.02 19.75L21.02 7.25L14.76 1.01L6.22 1.01ZM15.48 7.25Q15.17 7.25 14.95 7.02Q14.74 6.79 14.74 6.48L14.74 3.1L18.89 7.25L15.48 7.25ZM6.22 21.5Q5.5 21.5 4.98 20.99Q4.46 20.47 4.46 19.75L4.46 4.25Q4.46 3.53 4.98 3.01Q5.5 2.5 6.22 2.5L13.22 2.5L13.22 6.48Q13.22 7.1 13.52 7.62Q13.82 8.14 14.34 8.44Q14.86 8.74 15.48 8.74L19.51 8.74L19.51 19.75Q19.51 20.47 19 20.99Q18.48 21.5 17.76 21.5L6.22 21.5Z" fill="rgba(0,0,0,0.9019607843137255)"></path></g></svg>
\ No newline at end of file
... ...
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><rect width="24" height="24" opacity="0"></rect><g><path d="M12 3.46Q13.06 3.46 13.78 4.18Q14.5 4.9 14.5 5.95L14.5 11.21Q14.5 12.24 13.78 12.97Q13.06 13.7 12 13.7Q10.97 13.7 10.24 12.97Q9.5 12.24 9.5 11.21L9.5 5.95Q9.5 4.9 10.24 4.18Q10.97 3.46 12 3.46ZM12 1.94Q10.92 1.94 10 2.48Q9.07 3.02 8.53 3.95Q7.99 4.87 7.99 5.95L7.99 11.21Q7.99 12.29 8.53 13.21Q9.07 14.14 10 14.68Q10.92 15.22 12 15.22Q13.08 15.22 14 14.68Q14.93 14.14 15.47 13.21Q16.01 12.29 16.01 11.21L16.01 5.95Q16.01 4.87 15.47 3.95Q14.93 3.02 14 2.48Q13.08 1.94 12 1.94ZM19.51 11.23Q19.51 10.92 19.28 10.69Q19.06 10.46 18.74 10.46Q18.43 10.46 18.22 10.69Q18 10.92 18 11.23Q18 12.84 17.2 14.22Q16.39 15.6 15.01 16.4Q13.63 17.21 12 17.21Q10.37 17.21 8.99 16.4Q7.61 15.6 6.8 14.22Q6 12.84 6 11.23Q6 10.92 5.78 10.69Q5.57 10.46 5.26 10.46Q4.94 10.46 4.73 10.69Q4.51 10.92 4.51 11.23Q4.51 13.13 5.4 14.76Q6.29 16.39 7.84 17.44Q9.38 18.48 11.26 18.67L11.26 21.29Q11.26 21.6 11.47 21.82Q11.69 22.03 12 22.03Q12.31 22.03 12.53 21.82Q12.74 21.6 12.74 21.29L12.74 18.67Q14.62 18.48 16.16 17.44Q17.71 16.39 18.61 14.76Q19.51 13.13 19.51 11.23Z" fill="rgba(0,0,0,0.9019607843137255)"></path></g></svg>
\ No newline at end of file
... ...
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -960 960 960"><path d="M440-280h80v-240h-80zm40-320q17 0 28.5-11.5T520-640t-11.5-28.5T480-680t-28.5 11.5T440-640t11.5 28.5T480-600m0 520q-83 0-156-31.5T197-197t-85.5-127T80-480t31.5-156T197-763t127-85.5T480-880t156 31.5T763-763t85.5 127T880-480t-31.5 156T763-197t-127 85.5T480-80m0-80q134 0 227-93t93-227-93-227-227-93-227 93-93 227 93 227 227 93m0-320"/></svg>
\ No newline at end of file
... ...
... ... @@ -118,15 +118,24 @@ void OnlineZipformerTransducerModel::InitEncoder(void *model_data,
for (auto i : v) {
os << i << " ";
}
#if __OHOS__
SHERPA_ONNX_LOGE("%{public}s\n", os.str().c_str());
#else
SHERPA_ONNX_LOGE("%s\n", os.str().c_str());
#endif
};
print(encoder_dims_, "encoder_dims");
print(attention_dims_, "attention_dims");
print(num_encoder_layers_, "num_encoder_layers");
print(cnn_module_kernels_, "cnn_module_kernels");
print(left_context_len_, "left_context_len");
#if __OHOS__
SHERPA_ONNX_LOGE("T: %{public}d", T_);
SHERPA_ONNX_LOGE("decode_chunk_len_: %{public}d", decode_chunk_len_);
#else
SHERPA_ONNX_LOGE("T: %d", T_);
SHERPA_ONNX_LOGE("decode_chunk_len_: %d", decode_chunk_len_);
#endif
}
}
... ... @@ -147,7 +156,11 @@ void OnlineZipformerTransducerModel::InitDecoder(void *model_data,
std::ostringstream os;
os << "---decoder---\n";
PrintModelMetadata(os, meta_data);
#if __OHOS__
SHERPA_ONNX_LOGE("%{public}s", os.str().c_str());
#else
SHERPA_ONNX_LOGE("%s", os.str().c_str());
#endif
}
Ort::AllocatorWithDefaultOptions allocator; // used in the macro below
... ... @@ -172,7 +185,11 @@ void OnlineZipformerTransducerModel::InitJoiner(void *model_data,
std::ostringstream os;
os << "---joiner---\n";
PrintModelMetadata(os, meta_data);
#if __OHOS__
SHERPA_ONNX_LOGE("%{public}s", os.str().c_str());
#else
SHERPA_ONNX_LOGE("%s", os.str().c_str());
#endif
}
}
... ...
... ... @@ -126,7 +126,11 @@ void OnlineZipformer2TransducerModel::InitEncoder(void *model_data,
for (auto i : v) {
os << i << " ";
}
#if __OHOS__
SHERPA_ONNX_LOGE("%{public}s\n", os.str().c_str());
#else
SHERPA_ONNX_LOGE("%s\n", os.str().c_str());
#endif
};
print(encoder_dims_, "encoder_dims");
print(query_head_dims_, "query_head_dims");
... ... @@ -135,8 +139,14 @@ void OnlineZipformer2TransducerModel::InitEncoder(void *model_data,
print(num_encoder_layers_, "num_encoder_layers");
print(cnn_module_kernels_, "cnn_module_kernels");
print(left_context_len_, "left_context_len");
#if __OHOS__
SHERPA_ONNX_LOGE("T: %{public}d", T_);
SHERPA_ONNX_LOGE("decode_chunk_len_: %{public}d", decode_chunk_len_);
#else
SHERPA_ONNX_LOGE("T: %d", T_);
SHERPA_ONNX_LOGE("decode_chunk_len_: %d", decode_chunk_len_);
#endif
}
}
... ...