Committed by
GitHub
Add on-device real-time ASR demo for HarmonyOS (#1606)
正在显示
55 个修改的文件
包含
1341 行增加
和
4 行删除
| @@ -10,5 +10,8 @@ | @@ -10,5 +10,8 @@ | ||
| 10 | VAD + Non-streaming ASR for speech recognition. | 10 | VAD + Non-streaming ASR for speech recognition. |
| 11 | Please see the doc at <https://k2-fsa.github.io/sherpa/onnx/harmony-os/vad-asr.html> | 11 | Please see the doc at <https://k2-fsa.github.io/sherpa/onnx/harmony-os/vad-asr.html> |
| 12 | 12 | ||
| 13 | +- [./SherpaOnnxStreamingAsr](./SherpaOnnxStreamingAsr) It shows how to use | ||
| 14 | + streaming ASR models for real-time on-device speech recognition. | ||
| 15 | + | ||
| 13 | - [./SherpaOnnxTts](./SherpaOnnxTts) It shows how to run on-device text-to-speech. | 16 | - [./SherpaOnnxTts](./SherpaOnnxTts) It shows how to run on-device text-to-speech. |
| 14 | Please see the doc at <https://k2-fsa.github.io/sherpa/onnx/harmony-os/tts.html> | 17 | Please see the doc at <https://k2-fsa.github.io/sherpa/onnx/harmony-os/tts.html> |
harmony-os/SherpaOnnxStreamingAsr/.gitignore
0 → 100644
| 1 | +{ | ||
| 2 | + "app": { | ||
| 3 | + "signingConfigs": [], | ||
| 4 | + "products": [ | ||
| 5 | + { | ||
| 6 | + "name": "default", | ||
| 7 | + "signingConfig": "default", | ||
| 8 | + "compatibleSdkVersion": "4.0.0(10)", | ||
| 9 | + "runtimeOS": "HarmonyOS", | ||
| 10 | + "buildOption": { | ||
| 11 | + "strictMode": { | ||
| 12 | + "caseSensitiveCheck": true, | ||
| 13 | + } | ||
| 14 | + } | ||
| 15 | + } | ||
| 16 | + ], | ||
| 17 | + "buildModeSet": [ | ||
| 18 | + { | ||
| 19 | + "name": "debug", | ||
| 20 | + }, | ||
| 21 | + { | ||
| 22 | + "name": "release" | ||
| 23 | + } | ||
| 24 | + ] | ||
| 25 | + }, | ||
| 26 | + "modules": [ | ||
| 27 | + { | ||
| 28 | + "name": "entry", | ||
| 29 | + "srcPath": "./entry", | ||
| 30 | + "targets": [ | ||
| 31 | + { | ||
| 32 | + "name": "default", | ||
| 33 | + "applyToProducts": [ | ||
| 34 | + "default" | ||
| 35 | + ] | ||
| 36 | + } | ||
| 37 | + ] | ||
| 38 | + } | ||
| 39 | + ] | ||
| 40 | +} |
| 1 | +{ | ||
| 2 | + "files": [ | ||
| 3 | + "**/*.ets" | ||
| 4 | + ], | ||
| 5 | + "ignore": [ | ||
| 6 | + "**/src/ohosTest/**/*", | ||
| 7 | + "**/src/test/**/*", | ||
| 8 | + "**/src/mock/**/*", | ||
| 9 | + "**/node_modules/**/*", | ||
| 10 | + "**/oh_modules/**/*", | ||
| 11 | + "**/build/**/*", | ||
| 12 | + "**/.preview/**/*" | ||
| 13 | + ], | ||
| 14 | + "ruleSet": [ | ||
| 15 | + "plugin:@performance/recommended", | ||
| 16 | + "plugin:@typescript-eslint/recommended" | ||
| 17 | + ], | ||
| 18 | + "rules": { | ||
| 19 | + } | ||
| 20 | +} |
| 1 | +{ | ||
| 2 | + "apiType": "stageMode", | ||
| 3 | + "buildOption": { | ||
| 4 | + "sourceOption": { | ||
| 5 | + "workers": [ | ||
| 6 | + './src/main/ets/workers/StreamingAsrWorker.ets' | ||
| 7 | + ] | ||
| 8 | + } | ||
| 9 | + }, | ||
| 10 | + "buildOptionSet": [ | ||
| 11 | + { | ||
| 12 | + "name": "release", | ||
| 13 | + "arkOptions": { | ||
| 14 | + "obfuscation": { | ||
| 15 | + "ruleOptions": { | ||
| 16 | + "enable": false, | ||
| 17 | + "files": [ | ||
| 18 | + "./obfuscation-rules.txt" | ||
| 19 | + ] | ||
| 20 | + } | ||
| 21 | + } | ||
| 22 | + } | ||
| 23 | + }, | ||
| 24 | + ], | ||
| 25 | + "targets": [ | ||
| 26 | + { | ||
| 27 | + "name": "default" | ||
| 28 | + }, | ||
| 29 | + { | ||
| 30 | + "name": "ohosTest", | ||
| 31 | + } | ||
| 32 | + ] | ||
| 33 | +} |
| 1 | +# Define project specific obfuscation rules here. | ||
| 2 | +# You can include the obfuscation configuration files in the current module's build-profile.json5. | ||
| 3 | +# | ||
| 4 | +# For more details, see | ||
| 5 | +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 | ||
| 6 | + | ||
| 7 | +# Obfuscation options: | ||
| 8 | +# -disable-obfuscation: disable all obfuscations | ||
| 9 | +# -enable-property-obfuscation: obfuscate the property names | ||
| 10 | +# -enable-toplevel-obfuscation: obfuscate the names in the global scope | ||
| 11 | +# -compact: remove unnecessary blank spaces and all line feeds | ||
| 12 | +# -remove-log: remove all console.* statements | ||
| 13 | +# -print-namecache: print the name cache that contains the mapping from the old names to new names | ||
| 14 | +# -apply-namecache: reuse the given cache file | ||
| 15 | + | ||
| 16 | +# Keep options: | ||
| 17 | +# -keep-property-name: specifies property names that you want to keep | ||
| 18 | +# -keep-global-name: specifies names that you want to keep in the global scope | ||
| 19 | + | ||
| 20 | +-enable-property-obfuscation | ||
| 21 | +-enable-toplevel-obfuscation | ||
| 22 | +-enable-filename-obfuscation | ||
| 23 | +-enable-export-obfuscation |
| 1 | +{ | ||
| 2 | + "meta": { | ||
| 3 | + "stableOrder": true | ||
| 4 | + }, | ||
| 5 | + "lockfileVersion": 3, | ||
| 6 | + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", | ||
| 7 | + "specifiers": { | ||
| 8 | + "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", | ||
| 9 | + "sherpa_onnx@1.10.33": "sherpa_onnx@1.10.33" | ||
| 10 | + }, | ||
| 11 | + "packages": { | ||
| 12 | + "libsherpa_onnx.so@../oh_modules/.ohpm/sherpa_onnx@1.10.33/oh_modules/sherpa_onnx/src/main/cpp/types/libsherpa_onnx": { | ||
| 13 | + "name": "libsherpa_onnx.so", | ||
| 14 | + "version": "1.0.0", | ||
| 15 | + "resolved": "../oh_modules/.ohpm/sherpa_onnx@1.10.33/oh_modules/sherpa_onnx/src/main/cpp/types/libsherpa_onnx", | ||
| 16 | + "registryType": "local" | ||
| 17 | + }, | ||
| 18 | + "sherpa_onnx@1.10.33": { | ||
| 19 | + "name": "sherpa_onnx", | ||
| 20 | + "version": "1.10.33", | ||
| 21 | + "integrity": "sha512-cmZ8zwOMx4qmDvOjF1/PL6/suBgReanSf5XdQTuMWWZ6qN74rynODHrt4C+Qz754MTXg0q/phAKeVjGA4rHHSA==", | ||
| 22 | + "resolved": "https://ohpm.openharmony.cn/ohpm/sherpa_onnx/-/sherpa_onnx-1.10.33.har", | ||
| 23 | + "registryType": "ohpm", | ||
| 24 | + "dependencies": { | ||
| 25 | + "libsherpa_onnx.so": "file:./src/main/cpp/types/libsherpa_onnx" | ||
| 26 | + } | ||
| 27 | + } | ||
| 28 | + } | ||
| 29 | +} |
| 1 | +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; | ||
| 2 | +import hilog from '@ohos.hilog'; | ||
| 3 | +import UIAbility from '@ohos.app.ability.UIAbility'; | ||
| 4 | +import Want from '@ohos.app.ability.Want'; | ||
| 5 | +import window from '@ohos.window'; | ||
| 6 | + | ||
| 7 | +export default class EntryAbility extends UIAbility { | ||
| 8 | + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { | ||
| 9 | + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); | ||
| 10 | + } | ||
| 11 | + | ||
| 12 | + onDestroy(): void { | ||
| 13 | + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + onWindowStageCreate(windowStage: window.WindowStage): void { | ||
| 17 | + // Main window is created, set main page for this ability | ||
| 18 | + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); | ||
| 19 | + | ||
| 20 | + windowStage.loadContent('pages/Index', (err) => { | ||
| 21 | + if (err.code) { | ||
| 22 | + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); | ||
| 23 | + return; | ||
| 24 | + } | ||
| 25 | + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); | ||
| 26 | + }); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + onWindowStageDestroy(): void { | ||
| 30 | + // Main window is destroyed, release UI related resources | ||
| 31 | + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + onForeground(): void { | ||
| 35 | + // Ability has brought to foreground | ||
| 36 | + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + onBackground(): void { | ||
| 40 | + // Ability has back to background | ||
| 41 | + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); | ||
| 42 | + } | ||
| 43 | +} |
harmony-os/SherpaOnnxStreamingAsr/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets
0 → 100644
| 1 | +import hilog from '@ohos.hilog'; | ||
| 2 | +import BackupExtensionAbility, { BundleVersion } from '@ohos.application.BackupExtensionAbility'; | ||
| 3 | + | ||
| 4 | +export default class EntryBackupAbility extends BackupExtensionAbility { | ||
| 5 | + async onBackup() { | ||
| 6 | + hilog.info(0x0000, 'testTag', 'onBackup ok'); | ||
| 7 | + } | ||
| 8 | + | ||
| 9 | + async onRestore(bundleVersion: BundleVersion) { | ||
| 10 | + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); | ||
| 11 | + } | ||
| 12 | +} |
| 1 | +import { LengthUnit } from '@kit.ArkUI'; | ||
| 2 | +import worker, { MessageEvents } from '@ohos.worker'; | ||
| 3 | +import { BusinessError } from '@kit.BasicServicesKit'; | ||
| 4 | +import { picker } from '@kit.CoreFileKit'; | ||
| 5 | +import systemTime from '@ohos.systemTime'; | ||
| 6 | +import { Permissions } from '@kit.AbilityKit'; | ||
| 7 | +import { allAllowed, requestPermissions } from './Permission'; | ||
| 8 | +import { audio } from '@kit.AudioKit'; | ||
| 9 | +import fs from '@ohos.file.fs'; | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +function savePcmToWav(filename: string, samples: Int16Array, sampleRate: number) { | ||
| 13 | + const fp = fs.openSync(filename, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); | ||
| 14 | + | ||
| 15 | + const header = new ArrayBuffer(44); | ||
| 16 | + const view = new DataView(header); | ||
| 17 | + | ||
| 18 | + // http://soundfile.sapp.org/doc/WaveFormat/ | ||
| 19 | + // F F I R | ||
| 20 | + view.setUint32(0, 0x46464952, true); // chunkID | ||
| 21 | + view.setUint32(4, 36 + samples.length * 2, true); // chunkSize // E V A W | ||
| 22 | + view.setUint32(8, 0x45564157, true); // format // // t m f | ||
| 23 | + view.setUint32(12, 0x20746d66, true); // subchunk1ID | ||
| 24 | + view.setUint32(16, 16, true); // subchunk1Size, 16 for PCM | ||
| 25 | + view.setUint32(20, 1, true); // audioFormat, 1 for PCM | ||
| 26 | + view.setUint16(22, 1, true); // numChannels: 1 channel | ||
| 27 | + view.setUint32(24, sampleRate, true); // sampleRate | ||
| 28 | + view.setUint32(28, sampleRate * 2, true); // byteRate | ||
| 29 | + view.setUint16(32, 2, true); // blockAlign | ||
| 30 | + view.setUint16(34, 16, true); // bitsPerSample | ||
| 31 | + view.setUint32(36, 0x61746164, true); // Subchunk2ID | ||
| 32 | + view.setUint32(40, samples.length * 2, true); // subchunk2Size | ||
| 33 | + | ||
| 34 | + fs.writeSync(fp.fd, new Uint8Array(header).buffer, { length: header.byteLength }); | ||
| 35 | + fs.writeSync(fp.fd, samples.buffer, { length: samples.buffer.byteLength }); | ||
| 36 | + | ||
| 37 | + fs.closeSync(fp.fd); | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +function toInt16Samples(samples: Float32Array): Int16Array { | ||
| 41 | + const int16Samples = new Int16Array(samples.length); | ||
| 42 | + for (let i = 0; i < samples.length; ++i) { | ||
| 43 | + let s = samples[i] * 32767; | ||
| 44 | + s = s > 32767 ? 32767 : s; | ||
| 45 | + s = s < -32768 ? -32768 : s; | ||
| 46 | + int16Samples[i] = s; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + return int16Samples; | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | + | ||
| 53 | +@Entry | ||
| 54 | +@Component | ||
| 55 | +struct Index { | ||
| 56 | + @State title: string = 'Next-gen Kaldi: Real-time speech recognition'; | ||
| 57 | + @State titleFontSize: number = 15; | ||
| 58 | + @State currentIndex: number = 0; | ||
| 59 | + @State lang: string = 'English'; | ||
| 60 | + @State resultForFile: string = '' | ||
| 61 | + @State resultForMic: string = '' | ||
| 62 | + @State selectFileBtnEnabled: boolean = false; | ||
| 63 | + @State micBtnCaption: string = 'Start'; | ||
| 64 | + @State micStarted: boolean = false; | ||
| 65 | + @State micAllowed: boolean = false; | ||
| 66 | + @State micBtnEnabled: boolean = false; | ||
| 67 | + @State micSaveBtnCaption: string = 'Save recorded audio'; | ||
| 68 | + @State micSaveBtnEnabled: boolean = false; | ||
| 69 | + @State info: string = ''; | ||
| 70 | + @State micInfo: string = ''; | ||
| 71 | + @State micInitDone: boolean = false; | ||
| 72 | + private resultListForMic: string[] = []; | ||
| 73 | + private controller: TabsController = new TabsController(); | ||
| 74 | + private workerInstance?: worker.ThreadWorker | ||
| 75 | + private readonly scriptURL: string = 'entry/ets/workers/StreamingAsrWorker.ets' | ||
| 76 | + private startTime: number = 0; | ||
| 77 | + private stopTime: number = 0; | ||
| 78 | + private sampleRate: number = 48000; | ||
| 79 | + private sampleList: Float32Array[] = [] | ||
| 80 | + private mic?: audio.AudioCapturer; | ||
| 81 | + | ||
| 82 | + flatten(samples: Float32Array[]): Float32Array { | ||
| 83 | + let n = 0; | ||
| 84 | + for (let i = 0; i < samples.length; ++i) { | ||
| 85 | + n += samples[i].length; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + const ans: Float32Array = new Float32Array(n); | ||
| 89 | + let offset: number = 0; | ||
| 90 | + for (let i = 0; i < samples.length; ++i) { | ||
| 91 | + ans.set(samples[i], offset); | ||
| 92 | + offset += samples[i].length; | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + return ans; | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + async initMic() { | ||
| 99 | + const permissions: Permissions[] = ["ohos.permission.MICROPHONE"]; | ||
| 100 | + let allowed: boolean = await allAllowed(permissions); | ||
| 101 | + if (!allowed) { | ||
| 102 | + console.log("request to access the microphone"); | ||
| 103 | + const status: boolean = await requestPermissions(permissions); | ||
| 104 | + | ||
| 105 | + if (!status) { | ||
| 106 | + console.error('access to microphone is denied') | ||
| 107 | + this.resultForMic = "Failed to get microphone permission. Please retry"; | ||
| 108 | + return; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + allowed = await allAllowed(permissions); | ||
| 112 | + if (!allowed) { | ||
| 113 | + console.error('failed to get microphone permission'); | ||
| 114 | + this.resultForMic = "Failed to get microphone permission. Please retry"; | ||
| 115 | + return; | ||
| 116 | + } | ||
| 117 | + this.micAllowed = true; | ||
| 118 | + } else { | ||
| 119 | + console.log("allowed to access microphone"); | ||
| 120 | + this.micAllowed = true; | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + const audioStreamInfo: audio.AudioStreamInfo = { | ||
| 124 | + samplingRate: this.sampleRate, | ||
| 125 | + channels: audio.AudioChannel.CHANNEL_1, | ||
| 126 | + sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, | ||
| 127 | + encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW, | ||
| 128 | + }; | ||
| 129 | + | ||
| 130 | + const audioCapturerInfo: audio.AudioCapturerInfo = { | ||
| 131 | + source: audio.SourceType.SOURCE_TYPE_MIC, capturerFlags: 0 | ||
| 132 | + }; | ||
| 133 | + | ||
| 134 | + const audioCapturerOptions: audio.AudioCapturerOptions = { | ||
| 135 | + streamInfo: audioStreamInfo, capturerInfo: audioCapturerInfo | ||
| 136 | + | ||
| 137 | + }; | ||
| 138 | + audio.createAudioCapturer(audioCapturerOptions, (err, data) => { | ||
| 139 | + if (err) { | ||
| 140 | + console.error(`error code is ${err.code}, error message is ${err.message}`); | ||
| 141 | + this.resultForMic = 'Failed to init microphone'; | ||
| 142 | + } else { | ||
| 143 | + console.info(`init mic successfully`); | ||
| 144 | + this.mic = data; | ||
| 145 | + this.mic.on('readData', this.micCallback); | ||
| 146 | + } | ||
| 147 | + }); | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + async aboutToAppear() { | ||
| 151 | + this.workerInstance = new worker.ThreadWorker(this.scriptURL, { | ||
| 152 | + name: 'Streaming ASR worker' | ||
| 153 | + }); | ||
| 154 | + | ||
| 155 | + this.workerInstance.onmessage = (e: MessageEvents) => { | ||
| 156 | + const msgType = e.data['msgType'] as string; | ||
| 157 | + console.log(`received msg from worker: ${msgType}`); | ||
| 158 | + | ||
| 159 | + if (msgType == 'init-streaming-asr-done') { | ||
| 160 | + this.selectFileBtnEnabled = true; | ||
| 161 | + this.micBtnEnabled = true; | ||
| 162 | + this.info = `Initializing done.\n\nPlease select a wave file of 16kHz in language ${this.lang}`; | ||
| 163 | + this.micInfo = `Initializing done.\n\nPlease click Start and speak`; | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + if (msgType == 'streaming-asr-decode-file-done') { | ||
| 167 | + const text = e.data['text'] as string; | ||
| 168 | + this.resultForFile = text; | ||
| 169 | + this.selectFileBtnEnabled = true; | ||
| 170 | + | ||
| 171 | + systemTime.getRealTime((err, data) => { | ||
| 172 | + if (err) { | ||
| 173 | + console.log('Failed to get stop time'); | ||
| 174 | + } else { | ||
| 175 | + this.stopTime = data; | ||
| 176 | + | ||
| 177 | + const audioDuration = e.data['duration'] as number; | ||
| 178 | + const elapsedSeconds = (this.stopTime - this.startTime) / 1000; | ||
| 179 | + const RTF = elapsedSeconds / audioDuration; | ||
| 180 | + this.info = `Audio duration: ${audioDuration.toFixed(2)} s | ||
| 181 | +Elapsed: ${elapsedSeconds.toFixed(2)} s | ||
| 182 | +RTF = ${elapsedSeconds.toFixed(2)}/${audioDuration.toFixed(2)} = ${RTF.toFixed(3)} | ||
| 183 | +`; | ||
| 184 | + } | ||
| 185 | + }); | ||
| 186 | + } | ||
| 187 | + | ||
| 188 | + if (msgType == 'streaming-asr-decode-mic-result') { | ||
| 189 | + const text = e.data['text'] as string; | ||
| 190 | + if (text.trim() == '') { | ||
| 191 | + return; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + const isEndpoint = e.data['isEndpoint'] as boolean; | ||
| 195 | + | ||
| 196 | + let s = ''; | ||
| 197 | + let i = 0; | ||
| 198 | + for (; i < this.resultListForMic.length; ++i) { | ||
| 199 | + s += `${i}: ${this.resultListForMic[i]}\n` | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | + s += `${i}: ${text}`; | ||
| 203 | + this.resultForMic = s; | ||
| 204 | + | ||
| 205 | + if (isEndpoint) { | ||
| 206 | + this.resultListForMic.push(text); | ||
| 207 | + } | ||
| 208 | + } | ||
| 209 | + }; | ||
| 210 | + | ||
| 211 | + const context = getContext(); | ||
| 212 | + this.workerInstance.postMessage({ msgType: 'init-streaming-asr', context }); | ||
| 213 | + this.info = 'Initializing ASR model.\nPlease wait'; | ||
| 214 | + this.micInfo = 'Initializing ASR model.\nPlease wait'; | ||
| 215 | + | ||
| 216 | + await this.initMic(); | ||
| 217 | + } | ||
| 218 | + | ||
| 219 | + @Builder | ||
| 220 | + TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) { | ||
| 221 | + Column() { | ||
| 222 | + Image(this.currentIndex == targetIndex ? selectedImg : normalImg).size({ width: 25, height: 25 }) | ||
| 223 | + Text(title).fontColor(this.currentIndex == targetIndex ? '#28bff1' : '#8a8a8a') | ||
| 224 | + }.width('100%').height(50).justifyContent(FlexAlign.Center).onClick(() => { | ||
| 225 | + this.currentIndex = targetIndex; | ||
| 226 | + this.controller.changeIndex(this.currentIndex); | ||
| 227 | + }) | ||
| 228 | + } | ||
| 229 | + | ||
| 230 | + build() { | ||
| 231 | + Column() { | ||
| 232 | + Tabs({ barPosition: BarPosition.End, controller: this.controller }) { | ||
| 233 | + TabContent() { | ||
| 234 | + Column({ space: 10 }) { | ||
| 235 | + Text(this.title).fontSize(this.titleFontSize).fontWeight(FontWeight.Bold); | ||
| 236 | + Button('Select .wav file (16kHz) ') | ||
| 237 | + .enabled(this.selectFileBtnEnabled) | ||
| 238 | + .fontSize(13) | ||
| 239 | + .width(296) | ||
| 240 | + .height(60) | ||
| 241 | + .onClick(() => { | ||
| 242 | + this.resultForFile = ''; | ||
| 243 | + this.info = ''; | ||
| 244 | + this.selectFileBtnEnabled = false; | ||
| 245 | + | ||
| 246 | + const documentSelectOptions = new picker.DocumentSelectOptions(); | ||
| 247 | + documentSelectOptions.maxSelectNumber = 1; | ||
| 248 | + documentSelectOptions.fileSuffixFilters = ['.wav']; | ||
| 249 | + const documentViewPicker = new picker.DocumentViewPicker(); | ||
| 250 | + | ||
| 251 | + documentViewPicker.select(documentSelectOptions).then((result: Array<string>) => { | ||
| 252 | + console.log(`select file result: ${result}`); | ||
| 253 | + | ||
| 254 | + if (!result[0]) { | ||
| 255 | + this.resultForFile = 'Please select a file to decode'; | ||
| 256 | + this.selectFileBtnEnabled = true; | ||
| 257 | + return; | ||
| 258 | + } | ||
| 259 | + | ||
| 260 | + if (this.workerInstance) { | ||
| 261 | + systemTime.getRealTime((err, data) => { | ||
| 262 | + if (err) { | ||
| 263 | + console.log('Failed to get start time'); | ||
| 264 | + } else { | ||
| 265 | + this.startTime = data; | ||
| 266 | + } | ||
| 267 | + }); | ||
| 268 | + | ||
| 269 | + this.workerInstance.postMessage({ | ||
| 270 | + msgType: 'streaming-asr-decode-file', filename: result[0], | ||
| 271 | + }); | ||
| 272 | + this.info = `Decoding ${result[0]} ... ...`; | ||
| 273 | + } else { | ||
| 274 | + console.log(`this worker instance is undefined ${this.workerInstance}`); | ||
| 275 | + } | ||
| 276 | + | ||
| 277 | + }).catch((err: BusinessError) => { | ||
| 278 | + console.error(`Failed to select file, code is ${err.code}, message is ${err.message}`); | ||
| 279 | + this.selectFileBtnEnabled = true; | ||
| 280 | + }) | ||
| 281 | + }) | ||
| 282 | + | ||
| 283 | + Text(`Supported languages: ${this.lang}`); | ||
| 284 | + if (this.info != '') { | ||
| 285 | + TextArea({ text: this.info }).focusable(false); | ||
| 286 | + } | ||
| 287 | + TextArea({ text: this.resultForFile }) | ||
| 288 | + .width('100%') | ||
| 289 | + .lineSpacing({ value: 10, unit: LengthUnit.VP }) | ||
| 290 | + .height('100%'); | ||
| 291 | + } | ||
| 292 | + }.tabBar(this.TabBuilder('From file', 0, $r('app.media.icon_doc'), $r('app.media.icon_doc'))) | ||
| 293 | + | ||
| 294 | + TabContent() { | ||
| 295 | + Column({ space: 10 }) { | ||
| 296 | + Text(this.title).fontSize(this.titleFontSize).fontWeight(FontWeight.Bold); | ||
| 297 | + Button(this.micBtnCaption) | ||
| 298 | + .enabled(this.micBtnEnabled) | ||
| 299 | + .fontSize(13) | ||
| 300 | + .width(296) | ||
| 301 | + .height(60) | ||
| 302 | + .onClick(() => { | ||
| 303 | + this.micInfo = ''; | ||
| 304 | + if (this.mic) { | ||
| 305 | + if (this.micStarted) { | ||
| 306 | + this.micStarted = false; | ||
| 307 | + this.micBtnCaption = 'Start'; | ||
| 308 | + this.mic.stop(); | ||
| 309 | + this.micSaveBtnEnabled = true; | ||
| 310 | + | ||
| 311 | + if (this.workerInstance) { | ||
| 312 | + this.workerInstance.postMessage({ | ||
| 313 | + msgType: 'streaming-asr-decode-mic-stop' | ||
| 314 | + }); | ||
| 315 | + } | ||
| 316 | + } else { | ||
| 317 | + this.micStarted = true; | ||
| 318 | + this.micSaveBtnEnabled = false; | ||
| 319 | + this.micBtnCaption = 'Stop'; | ||
| 320 | + this.resultForMic = ''; | ||
| 321 | + this.resultListForMic = []; | ||
| 322 | + | ||
| 323 | + if (this.workerInstance) { | ||
| 324 | + this.workerInstance.postMessage({ | ||
| 325 | + msgType: 'streaming-asr-decode-mic-start' | ||
| 326 | + }); | ||
| 327 | + } | ||
| 328 | + | ||
| 329 | + this.sampleList = []; | ||
| 330 | + this.mic.start(); | ||
| 331 | + } | ||
| 332 | + } | ||
| 333 | + }); | ||
| 334 | + Button(this.micSaveBtnCaption) | ||
| 335 | + .enabled(this.micSaveBtnEnabled) | ||
| 336 | + .fontSize(13) | ||
| 337 | + .width(296) | ||
| 338 | + .height(60) | ||
| 339 | + .onClick(() => { | ||
| 340 | + if (this.sampleList.length == 0) { | ||
| 341 | + this.micSaveBtnEnabled = false; | ||
| 342 | + return; | ||
| 343 | + } | ||
| 344 | + | ||
| 345 | + const samples = this.flatten(this.sampleList); | ||
| 346 | + | ||
| 347 | + if (samples.length == 0) { | ||
| 348 | + this.micSaveBtnEnabled = false; | ||
| 349 | + return; | ||
| 350 | + } | ||
| 351 | + | ||
| 352 | + | ||
| 353 | + let uri: string = ''; | ||
| 354 | + | ||
| 355 | + | ||
| 356 | + const audioOptions = new picker.AudioSaveOptions(); // audioOptions.newFileNames = ['o.wav']; | ||
| 357 | + | ||
| 358 | + const audioViewPicker = new picker.AudioViewPicker(); | ||
| 359 | + | ||
| 360 | + audioViewPicker.save(audioOptions).then((audioSelectResult: Array<string>) => { | ||
| 361 | + uri = audioSelectResult[0]; | ||
| 362 | + savePcmToWav(uri, toInt16Samples(samples), this.sampleRate); | ||
| 363 | + console.log(`Saved to ${uri}`); | ||
| 364 | + this.micInfo += `\nSaved to ${uri}`; | ||
| 365 | + }); | ||
| 366 | + | ||
| 367 | + }) | ||
| 368 | + | ||
| 369 | + | ||
| 370 | + Text(`Supported languages: ${this.lang}`) | ||
| 371 | + | ||
| 372 | + if (this.micInfo != '') { | ||
| 373 | + TextArea({ text: this.micInfo }) | ||
| 374 | + .focusable(false); | ||
| 375 | + } | ||
| 376 | + | ||
| 377 | + TextArea({ text: this.resultForMic }) | ||
| 378 | + .width('100%') | ||
| 379 | + .lineSpacing({ value: 10, unit: LengthUnit.VP }) | ||
| 380 | + .width('100%') | ||
| 381 | + .height('100%'); | ||
| 382 | + } | ||
| 383 | + }.tabBar(this.TabBuilder('From mic', 1, $r('app.media.icon_mic'), $r('app.media.icon_mic'))) | ||
| 384 | + | ||
| 385 | + | ||
| 386 | + TabContent() { | ||
| 387 | + Column({ space: 10 }) { | ||
| 388 | + Text(this.title).fontSize(this.titleFontSize).fontWeight(FontWeight.Bold); | ||
| 389 | + TextArea({ | ||
| 390 | + text: ` | ||
| 391 | +Everyting is open-sourced. | ||
| 392 | + | ||
| 393 | +It runs locally, without accessing the network | ||
| 394 | + | ||
| 395 | +See also https://github.com/k2-fsa/sherpa-onnx | ||
| 396 | + | ||
| 397 | +新一代 Kaldi QQ 和微信交流群: 请看 | ||
| 398 | + | ||
| 399 | +https://k2-fsa.github.io/sherpa/social-groups.html | ||
| 400 | + | ||
| 401 | +微信公众号: 新一代 Kaldi | ||
| 402 | + ` | ||
| 403 | + }).width('100%').height('100%').focusable(false) | ||
| 404 | + }.justifyContent(FlexAlign.Start) | ||
| 405 | + }.tabBar(this.TabBuilder('Help', 2, $r('app.media.info'), $r('app.media.info'))) | ||
| 406 | + }.scrollable(false) | ||
| 407 | + }.width('100%') | ||
| 408 | + } | ||
| 409 | + | ||
| 410 | + private micCallback = (buffer: ArrayBuffer) => { | ||
| 411 | + const view: Int16Array = new Int16Array(buffer); | ||
| 412 | + | ||
| 413 | + const samplesFloat: Float32Array = new Float32Array(view.length); | ||
| 414 | + for (let i = 0; i < view.length; ++i) { | ||
| 415 | + samplesFloat[i] = view[i] / 32768.0; | ||
| 416 | + } | ||
| 417 | + | ||
| 418 | + this.sampleList.push(samplesFloat); | ||
| 419 | + | ||
| 420 | + if (this.workerInstance) { | ||
| 421 | + this.workerInstance.postMessage({ | ||
| 422 | + msgType: 'streaming-asr-decode-mic-samples', | ||
| 423 | + samples: samplesFloat, | ||
| 424 | + sampleRate: this.sampleRate, | ||
| 425 | + }) | ||
| 426 | + } | ||
| 427 | + } | ||
| 428 | +} |
| 1 | +// This file is modified from | ||
| 2 | +// https://gitee.com/ukSir/hmchat2/blob/master/entry/src/main/ets/utils/permissionMananger.ets | ||
| 3 | +import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit'; | ||
| 4 | + | ||
| 5 | +export function allAllowed(permissions: Permissions[]): boolean { | ||
| 6 | + if (permissions.length == 0) { | ||
| 7 | + return false; | ||
| 8 | + } | ||
| 9 | + | ||
| 10 | + const mgr: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); | ||
| 11 | + | ||
| 12 | + const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); | ||
| 13 | + | ||
| 14 | + let tokenID: number = bundleInfo.appInfo.accessTokenId; | ||
| 15 | + | ||
| 16 | + return permissions.every(permission => abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED == | ||
| 17 | + mgr.checkAccessTokenSync(tokenID, permission)); | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +export async function requestPermissions(permissions: Permissions[]): Promise<boolean> { | ||
| 21 | + const mgr: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); | ||
| 22 | + const context: Context = getContext() as common.UIAbilityContext; | ||
| 23 | + | ||
| 24 | + const result = await mgr.requestPermissionsFromUser(context, permissions); | ||
| 25 | + return result.authResults.length > 0 && result.authResults.every(authResults => authResults == 0); | ||
| 26 | +} |
| 1 | +import worker, { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope } from '@ohos.worker'; | ||
| 2 | +import { | ||
| 3 | + OnlineModelConfig, | ||
| 4 | + OnlineRecognizer, | ||
| 5 | + OnlineRecognizerConfig, | ||
| 6 | + OnlineStream, | ||
| 7 | + readWaveFromBinary, | ||
| 8 | + Samples | ||
| 9 | +} from 'sherpa_onnx'; | ||
| 10 | +import { fileIo } from '@kit.CoreFileKit'; | ||
| 11 | + | ||
| 12 | +const workerPort: ThreadWorkerGlobalScope = worker.workerPort; | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +let recognizer: OnlineRecognizer; | ||
| 16 | +let micStream: OnlineStream; | ||
| 17 | + | ||
| 18 | +function getModelConfig(type: number): OnlineModelConfig { | ||
| 19 | + const modelConfig = new OnlineModelConfig(); | ||
| 20 | + switch (type) { | ||
| 21 | + case 0: { | ||
| 22 | + const modelDir = 'sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20'; | ||
| 23 | + modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.onnx`; | ||
| 24 | + modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`; | ||
| 25 | + modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.onnx`; | ||
| 26 | + modelConfig.tokens = `${modelDir}/tokens.txt`; | ||
| 27 | + modelConfig.modelType = 'zipformer'; | ||
| 28 | + break; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + case 1: { | ||
| 32 | + const modelDir = 'sherpa-onnx-lstm-zh-2023-02-20'; | ||
| 33 | + modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-11-avg-1.onnx`; | ||
| 34 | + modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-11-avg-1.onnx`; | ||
| 35 | + modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-11-avg-1.onnx`; | ||
| 36 | + modelConfig.tokens = `${modelDir}/tokens.txt`; | ||
| 37 | + modelConfig.modelType = 'lstm'; | ||
| 38 | + break; | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + case 2: { | ||
| 42 | + const modelDir = 'sherpa-onnx-lstm-en-2023-02-17'; | ||
| 43 | + modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.onnx`; | ||
| 44 | + modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`; | ||
| 45 | + modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.onnx`; | ||
| 46 | + modelConfig.tokens = `${modelDir}/tokens.txt`; | ||
| 47 | + modelConfig.modelType = 'lstm'; | ||
| 48 | + break; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + case 3: { | ||
| 52 | + const modelDir = 'icefall-asr-zipformer-streaming-wenetspeech-20230615'; | ||
| 53 | + modelConfig.transducer.encoder = `${modelDir}/exp/encoder-epoch-12-avg-4-chunk-16-left-128.int8.onnx`; | ||
| 54 | + modelConfig.transducer.decoder = `${modelDir}/exp/decoder-epoch-12-avg-4-chunk-16-left-128.onnx`; | ||
| 55 | + modelConfig.transducer.joiner = `${modelDir}/exp/joiner-epoch-12-avg-4-chunk-16-left-128.onnx`; | ||
| 56 | + modelConfig.tokens = `${modelDir}/data/lang_char/tokens.txt`; | ||
| 57 | + modelConfig.modelType = 'zipformer2'; | ||
| 58 | + break; | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + case 4: { | ||
| 62 | + const modelDir = 'icefall-asr-zipformer-streaming-wenetspeech-20230615'; | ||
| 63 | + modelConfig.transducer.encoder = `${modelDir}/exp/encoder-epoch-12-avg-4-chunk-16-left-128.onnx`; | ||
| 64 | + modelConfig.transducer.decoder = `${modelDir}/exp/decoder-epoch-12-avg-4-chunk-16-left-128.onnx`; | ||
| 65 | + modelConfig.transducer.joiner = `${modelDir}/exp/joiner-epoch-12-avg-4-chunk-16-left-128.onnx`; | ||
| 66 | + modelConfig.tokens = `${modelDir}/data/lang_char/tokens.txt`; | ||
| 67 | + modelConfig.modelType = 'zipformer2'; | ||
| 68 | + break; | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + case 5: { | ||
| 72 | + const modelDir = 'sherpa-onnx-streaming-paraformer-bilingual-zh-en'; | ||
| 73 | + modelConfig.paraformer.encoder = `${modelDir}/encoder.int8.onnx`; | ||
| 74 | + modelConfig.paraformer.decoder = `${modelDir}/decoder.int8.onnx`; | ||
| 75 | + modelConfig.tokens = `${modelDir}/tokens.txt`; | ||
| 76 | + modelConfig.modelType = 'paraformer'; | ||
| 77 | + break; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + case 6: { | ||
| 81 | + const modelDir = 'sherpa-onnx-streaming-zipformer-en-2023-06-26'; | ||
| 82 | + modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1-chunk-16-left-128.int8.onnx`; | ||
| 83 | + modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1-chunk-16-left-128.onnx`; | ||
| 84 | + modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1-chunk-16-left-128.onnx`; | ||
| 85 | + modelConfig.tokens = `${modelDir}/tokens.txt`; | ||
| 86 | + modelConfig.modelType = 'zipformer2'; | ||
| 87 | + break; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + case 7: { | ||
| 91 | + const modelDir = 'sherpa-onnx-streaming-zipformer-fr-2023-04-14'; | ||
| 92 | + modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-29-avg-9-with-averaged-model.int8.onnx`; | ||
| 93 | + modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-29-avg-9-with-averaged-model.onnx`; | ||
| 94 | + modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-29-avg-9-with-averaged-model.onnx`; | ||
| 95 | + modelConfig.tokens = `${modelDir}/tokens.txt`; | ||
| 96 | + modelConfig.modelType = 'zipformer'; | ||
| 97 | + break; | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + case 8: { | ||
| 101 | + const modelDir = 'sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20'; | ||
| 102 | + modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`; | ||
| 103 | + modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`; | ||
| 104 | + modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`; | ||
| 105 | + modelConfig.tokens = `${modelDir}/tokens.txt`; | ||
| 106 | + modelConfig.modelType = 'zipformer'; | ||
| 107 | + break; | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + case 9: { | ||
| 111 | + const modelDir = 'sherpa-onnx-streaming-zipformer-zh-14M-2023-02-23' | ||
| 112 | + modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`; | ||
| 113 | + modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`; | ||
| 114 | + modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`; | ||
| 115 | + modelConfig.tokens = `${modelDir}/tokens.txt`; | ||
| 116 | + modelConfig.modelType = 'zipformer'; | ||
| 117 | + break; | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + case 10: { | ||
| 121 | + const modelDir = 'sherpa-onnx-streaming-zipformer-en-20M-2023-02-17'; | ||
| 122 | + modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`; | ||
| 123 | + modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`; | ||
| 124 | + modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`; | ||
| 125 | + modelConfig.tokens = `${modelDir}/tokens.txt`; | ||
| 126 | + modelConfig.modelType = 'zipformer'; | ||
| 127 | + break; | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + case 14: { | ||
| 131 | + const modelDir = 'sherpa-onnx-streaming-zipformer-korean-2024-06-16'; | ||
| 132 | + modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`; | ||
| 133 | + modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`; | ||
| 134 | + modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`; | ||
| 135 | + modelConfig.tokens = `${modelDir}/tokens.txt`; | ||
| 136 | + modelConfig.modelType = 'zipformer'; | ||
| 137 | + break; | ||
| 138 | + } | ||
| 139 | + default: { | ||
| 140 | + console.log(`Please specify a supported type. Given type ${type}`); | ||
| 141 | + } | ||
| 142 | + } | ||
| 143 | + return modelConfig; | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +function initStreamingAsr(context: Context): OnlineRecognizer { | ||
| 147 | + let type: number; | ||
| 148 | + | ||
| 149 | + /* | ||
| 150 | + | ||
| 151 | +If you use type = 8, then you should have the following directory structure in the rawfile directory | ||
| 152 | + | ||
| 153 | +(py38) fangjuns-MacBook-Pro:rawfile fangjun$ pwd | ||
| 154 | +/Users/fangjun/open-source/sherpa-onnx/harmony-os/SherpaOnnxStreamingAsr/entry/src/main/resources/rawfile | ||
| 155 | +(py38) fangjuns-MacBook-Pro:rawfile fangjun$ ls | ||
| 156 | +sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20 | ||
| 157 | +(py38) fangjuns-MacBook-Pro:rawfile fangjun$ tree . | ||
| 158 | +. | ||
| 159 | +└── sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20 | ||
| 160 | + ├── decoder-epoch-99-avg-1.onnx | ||
| 161 | + ├── encoder-epoch-99-avg-1.int8.onnx | ||
| 162 | + ├── joiner-epoch-99-avg-1.int8.onnx | ||
| 163 | + └── tokens.txt | ||
| 164 | + | ||
| 165 | +1 directory, 4 files | ||
| 166 | + | ||
| 167 | +You can download model files from | ||
| 168 | +https://github.com/k2-fsa/sherpa-onnx/releases/tag/asr-models | ||
| 169 | + | ||
| 170 | +Note that please delete files that are not used. Otherwise, you APP will be very large | ||
| 171 | +due to containing unused large files. | ||
| 172 | + | ||
| 173 | + */ | ||
| 174 | + type = 8; | ||
| 175 | + | ||
| 176 | + const config: OnlineRecognizerConfig = new OnlineRecognizerConfig(); | ||
| 177 | + config.modelConfig = getModelConfig(type); | ||
| 178 | + config.modelConfig.debug = true; | ||
| 179 | + config.modelConfig.numThreads = 2; | ||
| 180 | + config.enableEndpoint = true; | ||
| 181 | + | ||
| 182 | + return new OnlineRecognizer(config, context.resourceManager); | ||
| 183 | +} | ||
| 184 | + | ||
| 185 | +interface DecodeFileResult { | ||
| 186 | + text: string; | ||
| 187 | + duration: number; | ||
| 188 | +} | ||
| 189 | + | ||
| 190 | +function decodeFile(filename: string): DecodeFileResult { | ||
| 191 | + const fp = fileIo.openSync(filename); | ||
| 192 | + const stat = fileIo.statSync(fp.fd); | ||
| 193 | + const arrayBuffer = new ArrayBuffer(stat.size); | ||
| 194 | + fileIo.readSync(fp.fd, arrayBuffer); | ||
| 195 | + const data: Uint8Array = new Uint8Array(arrayBuffer); | ||
| 196 | + const wave: Samples = readWaveFromBinary(data) as Samples; | ||
| 197 | + console.log(`Sample rate: ${wave.sampleRate}`); | ||
| 198 | + | ||
| 199 | + const stream = recognizer.createStream(); | ||
| 200 | + stream.acceptWaveform(wave); | ||
| 201 | + const tailPadding = new Float32Array(0.5 * wave.sampleRate); | ||
| 202 | + tailPadding.fill(0); | ||
| 203 | + | ||
| 204 | + stream.acceptWaveform({ samples: tailPadding, sampleRate: wave.sampleRate }); | ||
| 205 | + | ||
| 206 | + while (recognizer.isReady(stream)) { | ||
| 207 | + recognizer.decode(stream); | ||
| 208 | + } | ||
| 209 | + | ||
| 210 | + const audioDuration = wave.samples.length / wave.sampleRate; | ||
| 211 | + | ||
| 212 | + return { text: recognizer.getResult(stream).text, duration: audioDuration }; | ||
| 213 | +} | ||
| 214 | + | ||
| 215 | +/** | ||
| 216 | + * Defines the event handler to be called when the worker thread receives a message sent by the host thread. | ||
| 217 | + * The event handler is executed in the worker thread. | ||
| 218 | + * | ||
| 219 | + * @param e message data | ||
| 220 | + */ | ||
| 221 | +workerPort.onmessage = (e: MessageEvents) => { | ||
| 222 | + const msgType = e.data['msgType'] as string; | ||
| 223 | + | ||
| 224 | + if (msgType != 'streaming-asr-decode-mic-samples') { | ||
| 225 | + console.log(`from the main thread, msg-type: ${msgType}`); | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + if (msgType == 'init-streaming-asr' && !recognizer) { | ||
| 229 | + console.log('initializing streaming ASR...'); | ||
| 230 | + const context = e.data['context'] as Context; | ||
| 231 | + recognizer = initStreamingAsr(context); | ||
| 232 | + console.log('streaming ASR is initialized. '); | ||
| 233 | + workerPort.postMessage({ 'msgType': 'init-streaming-asr-done' }); | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + if (msgType == 'streaming-asr-decode-file') { | ||
| 237 | + const filename = e.data['filename'] as string; | ||
| 238 | + console.log(`decoding ${filename}`); | ||
| 239 | + const result = decodeFile(filename); | ||
| 240 | + workerPort.postMessage({ | ||
| 241 | + 'msgType': 'streaming-asr-decode-file-done', text: result.text, duration: result.duration | ||
| 242 | + }); | ||
| 243 | + } | ||
| 244 | + | ||
| 245 | + if (msgType == 'streaming-asr-decode-mic-start') { | ||
| 246 | + micStream = recognizer.createStream(); | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + if (msgType == 'streaming-asr-decode-mic-stop') { // nothing to do | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + if (msgType == 'streaming-asr-decode-mic-samples') { | ||
| 253 | + const samples = e.data['samples'] as Float32Array; | ||
| 254 | + const sampleRate = e.data['sampleRate'] as number; | ||
| 255 | + | ||
| 256 | + micStream.acceptWaveform({ samples, sampleRate }); | ||
| 257 | + while (recognizer.isReady(micStream)) { | ||
| 258 | + recognizer.decode(micStream); | ||
| 259 | + | ||
| 260 | + let isEndpoint = false; | ||
| 261 | + let text = recognizer.getResult(micStream).text; | ||
| 262 | + | ||
| 263 | + if (recognizer.isEndpoint(micStream)) { | ||
| 264 | + isEndpoint = true; | ||
| 265 | + recognizer.reset(micStream); | ||
| 266 | + } | ||
| 267 | + | ||
| 268 | + if (text.trim() != '') { | ||
| 269 | + workerPort.postMessage({ | ||
| 270 | + 'msgType': 'streaming-asr-decode-mic-result', text: text, isEndpoint: isEndpoint, | ||
| 271 | + }); | ||
| 272 | + } | ||
| 273 | + } | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | +} | ||
| 277 | + | ||
| 278 | +/** | ||
| 279 | + * Defines the event handler to be called when the worker receives a message that cannot be deserialized. | ||
| 280 | + * The event handler is executed in the worker thread. | ||
| 281 | + * | ||
| 282 | + * @param e message data | ||
| 283 | + */ | ||
| 284 | +workerPort.onmessageerror = (e: MessageEvents) => { | ||
| 285 | +} | ||
| 286 | + | ||
| 287 | +/** | ||
| 288 | + * Defines the event handler to be called when an exception occurs during worker execution. | ||
| 289 | + * The event handler is executed in the worker thread. | ||
| 290 | + * | ||
| 291 | + * @param e error message | ||
| 292 | + */ | ||
| 293 | +workerPort.onerror = (e: ErrorEvent) => { | ||
| 294 | +} |
| 1 | +{ | ||
| 2 | + "module": { | ||
| 3 | + "name": "entry", | ||
| 4 | + "type": "entry", | ||
| 5 | + "description": "$string:module_desc", | ||
| 6 | + "mainElement": "EntryAbility", | ||
| 7 | + "deviceTypes": [ | ||
| 8 | + "phone", | ||
| 9 | + "tablet", | ||
| 10 | + "2in1" | ||
| 11 | + ], | ||
| 12 | + "deliveryWithInstall": true, | ||
| 13 | + "installationFree": false, | ||
| 14 | + "pages": "$profile:main_pages", | ||
| 15 | + "abilities": [ | ||
| 16 | + { | ||
| 17 | + "name": "EntryAbility", | ||
| 18 | + "srcEntry": "./ets/entryability/EntryAbility.ets", | ||
| 19 | + "description": "$string:EntryAbility_desc", | ||
| 20 | + "icon": "$media:layered_image", | ||
| 21 | + "label": "$string:EntryAbility_label", | ||
| 22 | + "startWindowIcon": "$media:startIcon", | ||
| 23 | + "startWindowBackground": "$color:start_window_background", | ||
| 24 | + "exported": true, | ||
| 25 | + "skills": [ | ||
| 26 | + { | ||
| 27 | + "entities": [ | ||
| 28 | + "entity.system.home" | ||
| 29 | + ], | ||
| 30 | + "actions": [ | ||
| 31 | + "action.system.home" | ||
| 32 | + ] | ||
| 33 | + } | ||
| 34 | + ] | ||
| 35 | + } | ||
| 36 | + ], | ||
| 37 | + "extensionAbilities": [ | ||
| 38 | + { | ||
| 39 | + "name": "EntryBackupAbility", | ||
| 40 | + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", | ||
| 41 | + "type": "backup", | ||
| 42 | + "exported": false, | ||
| 43 | + "metadata": [ | ||
| 44 | + { | ||
| 45 | + "name": "ohos.extension.backup", | ||
| 46 | + "resource": "$profile:backup_config" | ||
| 47 | + } | ||
| 48 | + ], | ||
| 49 | + } | ||
| 50 | + ], | ||
| 51 | + "requestPermissions": [ | ||
| 52 | + { | ||
| 53 | + "name": "ohos.permission.MICROPHONE", | ||
| 54 | + "reason": "$string:mic_reason", | ||
| 55 | + "usedScene": { | ||
| 56 | + "abilities": [ | ||
| 57 | + "EntryAbility", | ||
| 58 | + ], | ||
| 59 | + "when": "inuse", | ||
| 60 | + } | ||
| 61 | + } | ||
| 62 | + ] | ||
| 63 | + } | ||
| 64 | +} |
| 1 | +{ | ||
| 2 | + "string": [ | ||
| 3 | + { | ||
| 4 | + "name": "module_desc", | ||
| 5 | + "value": "On-device real-time speech recognition with Next-gen Kaldi" | ||
| 6 | + }, | ||
| 7 | + { | ||
| 8 | + "name": "EntryAbility_desc", | ||
| 9 | + "value": "On-device real-time speech recognition with Next-gen Kaldi" | ||
| 10 | + }, | ||
| 11 | + { | ||
| 12 | + "name": "EntryAbility_label", | ||
| 13 | + "value": "Real-time ASR" | ||
| 14 | + }, | ||
| 15 | + { | ||
| 16 | + "name": "mic_reason", | ||
| 17 | + "value": "access the microhone for on-device real-time speech recognition with Next-gen Kaldi" | ||
| 18 | + } | ||
| 19 | + ] | ||
| 20 | +} |
56.0 KB
12.1 KB
| 1 | +<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> |
| 1 | +<?xml version="1.0" standalone="no"?> | ||
| 2 | +<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> |
| 1 | +<?xml version="1.0" standalone="no"?> | ||
| 2 | +<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> |
| 1 | +<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> |
19.6 KB
| 1 | +{ | ||
| 2 | + "string": [ | ||
| 3 | + { | ||
| 4 | + "name": "module_desc", | ||
| 5 | + "value": "On-device real-time speech recognition with Next-gen Kaldi" | ||
| 6 | + }, | ||
| 7 | + { | ||
| 8 | + "name": "EntryAbility_desc", | ||
| 9 | + "value": "On-device real-time speech recognition with Next-gen Kaldi" | ||
| 10 | + }, | ||
| 11 | + { | ||
| 12 | + "name": "EntryAbility_label", | ||
| 13 | + "value": "Real-time ASR" | ||
| 14 | + }, | ||
| 15 | + { | ||
| 16 | + "name": "mic_reason", | ||
| 17 | + "value": "access the microhone for on-device real-time speech recognition with Next-gen Kaldi" | ||
| 18 | + } | ||
| 19 | + ] | ||
| 20 | +} |
| 1 | +{ | ||
| 2 | + "string": [ | ||
| 3 | + { | ||
| 4 | + "name": "module_desc", | ||
| 5 | + "value": "新一代Kaldi: 本地实时语音识别" | ||
| 6 | + }, | ||
| 7 | + { | ||
| 8 | + "name": "EntryAbility_desc", | ||
| 9 | + "value": "新一代Kaldi: 本地实时语音识别" | ||
| 10 | + }, | ||
| 11 | + { | ||
| 12 | + "name": "EntryAbility_label", | ||
| 13 | + "value": "实时语音识别" | ||
| 14 | + }, | ||
| 15 | + { | ||
| 16 | + "name": "mic_reason", | ||
| 17 | + "value": "使用新一代Kaldi, 访问麦克风进行本地实时语音识别 (不需要联网)" | ||
| 18 | + } | ||
| 19 | + ] | ||
| 20 | +} |
| 1 | +import hilog from '@ohos.hilog'; | ||
| 2 | +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; | ||
| 3 | + | ||
| 4 | +export default function abilityTest() { | ||
| 5 | + describe('ActsAbilityTest', () => { | ||
| 6 | + // Defines a test suite. Two parameters are supported: test suite name and test suite function. | ||
| 7 | + beforeAll(() => { | ||
| 8 | + // Presets an action, which is performed only once before all test cases of the test suite start. | ||
| 9 | + // This API supports only one parameter: preset action function. | ||
| 10 | + }) | ||
| 11 | + beforeEach(() => { | ||
| 12 | + // Presets an action, which is performed before each unit test case starts. | ||
| 13 | + // The number of execution times is the same as the number of test cases defined by **it**. | ||
| 14 | + // This API supports only one parameter: preset action function. | ||
| 15 | + }) | ||
| 16 | + afterEach(() => { | ||
| 17 | + // Presets a clear action, which is performed after each unit test case ends. | ||
| 18 | + // The number of execution times is the same as the number of test cases defined by **it**. | ||
| 19 | + // This API supports only one parameter: clear action function. | ||
| 20 | + }) | ||
| 21 | + afterAll(() => { | ||
| 22 | + // Presets a clear action, which is performed after all test cases of the test suite end. | ||
| 23 | + // This API supports only one parameter: clear action function. | ||
| 24 | + }) | ||
| 25 | + it('assertContain', 0, () => { | ||
| 26 | + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. | ||
| 27 | + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); | ||
| 28 | + let a = 'abc'; | ||
| 29 | + let b = 'b'; | ||
| 30 | + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. | ||
| 31 | + expect(a).assertContain(b); | ||
| 32 | + expect(a).assertEqual(a); | ||
| 33 | + }) | ||
| 34 | + }) | ||
| 35 | +} |
| 1 | +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; | ||
| 2 | + | ||
| 3 | +export default function localUnitTest() { | ||
| 4 | + describe('localUnitTest', () => { | ||
| 5 | + // Defines a test suite. Two parameters are supported: test suite name and test suite function. | ||
| 6 | + beforeAll(() => { | ||
| 7 | + // Presets an action, which is performed only once before all test cases of the test suite start. | ||
| 8 | + // This API supports only one parameter: preset action function. | ||
| 9 | + }); | ||
| 10 | + beforeEach(() => { | ||
| 11 | + // Presets an action, which is performed before each unit test case starts. | ||
| 12 | + // The number of execution times is the same as the number of test cases defined by **it**. | ||
| 13 | + // This API supports only one parameter: preset action function. | ||
| 14 | + }); | ||
| 15 | + afterEach(() => { | ||
| 16 | + // Presets a clear action, which is performed after each unit test case ends. | ||
| 17 | + // The number of execution times is the same as the number of test cases defined by **it**. | ||
| 18 | + // This API supports only one parameter: clear action function. | ||
| 19 | + }); | ||
| 20 | + afterAll(() => { | ||
| 21 | + // Presets a clear action, which is performed after all test cases of the test suite end. | ||
| 22 | + // This API supports only one parameter: clear action function. | ||
| 23 | + }); | ||
| 24 | + it('assertContain', 0, () => { | ||
| 25 | + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. | ||
| 26 | + let a = 'abc'; | ||
| 27 | + let b = 'b'; | ||
| 28 | + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. | ||
| 29 | + expect(a).assertContain(b); | ||
| 30 | + expect(a).assertEqual(a); | ||
| 31 | + }); | ||
| 32 | + }); | ||
| 33 | +} |
| 1 | +{ | ||
| 2 | + "modelVersion": "5.0.0", | ||
| 3 | + "dependencies": { | ||
| 4 | + }, | ||
| 5 | + "execution": { | ||
| 6 | + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ | ||
| 7 | + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ | ||
| 8 | + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ | ||
| 9 | + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ | ||
| 10 | + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ | ||
| 11 | + }, | ||
| 12 | + "logging": { | ||
| 13 | + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ | ||
| 14 | + }, | ||
| 15 | + "debugging": { | ||
| 16 | + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ | ||
| 17 | + }, | ||
| 18 | + "nodeOptions": { | ||
| 19 | + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ | ||
| 20 | + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ | ||
| 21 | + } | ||
| 22 | +} |
| 1 | +{ | ||
| 2 | + "meta": { | ||
| 3 | + "stableOrder": true | ||
| 4 | + }, | ||
| 5 | + "lockfileVersion": 3, | ||
| 6 | + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", | ||
| 7 | + "specifiers": { | ||
| 8 | + "@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19" | ||
| 9 | + }, | ||
| 10 | + "packages": { | ||
| 11 | + "@ohos/hypium@1.0.19": { | ||
| 12 | + "name": "@ohos/hypium", | ||
| 13 | + "version": "1.0.19", | ||
| 14 | + "integrity": "sha512-cEjDgLFCm3cWZDeRXk7agBUkPqjWxUo6AQeiu0gEkb3J8ESqlduQLSIXeo3cCsm8U/asL7iKjF85ZyOuufAGSQ==", | ||
| 15 | + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.19.har", | ||
| 16 | + "registryType": "ohpm" | ||
| 17 | + } | ||
| 18 | + } | ||
| 19 | +} |
| @@ -229,7 +229,7 @@ struct Index { | @@ -229,7 +229,7 @@ struct Index { | ||
| 229 | .lineSpacing({ value: 10, unit: LengthUnit.VP }) | 229 | .lineSpacing({ value: 10, unit: LengthUnit.VP }) |
| 230 | .height('100%'); | 230 | .height('100%'); |
| 231 | }.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Start) | 231 | }.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Start) |
| 232 | - }.tabBar(this.TabBuilder('From file', 0, $r('app.media.icon_doc'), $r('app.media.icon_doc_default'))) | 232 | + }.tabBar(this.TabBuilder('From file', 0, $r('app.media.icon_doc'), $r('app.media.icon_doc'))) |
| 233 | 233 | ||
| 234 | TabContent() { | 234 | TabContent() { |
| 235 | Column({ space: 10 }) { | 235 | Column({ space: 10 }) { |
| @@ -278,8 +278,8 @@ struct Index { | @@ -278,8 +278,8 @@ struct Index { | ||
| 278 | .height('100%'); | 278 | .height('100%'); |
| 279 | }.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Start) | 279 | }.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Start) |
| 280 | } | 280 | } |
| 281 | - .tabBar(this.TabBuilder('From mic', 1, $r('app.media.ic_public_input_voice'), | ||
| 282 | - $r('app.media.ic_public_input_voice_default'))) | 281 | + .tabBar(this.TabBuilder('From mic', 1, $r('app.media.icon_mic'), |
| 282 | + $r('app.media.icon_mic'))) | ||
| 283 | 283 | ||
| 284 | TabContent() { | 284 | TabContent() { |
| 285 | Column({ space: 10 }) { | 285 | Column({ space: 10 }) { |
| @@ -300,7 +300,7 @@ https://k2-fsa.github.io/sherpa/social-groups.html | @@ -300,7 +300,7 @@ https://k2-fsa.github.io/sherpa/social-groups.html | ||
| 300 | ` | 300 | ` |
| 301 | }).width('100%').height('100%').focusable(false) | 301 | }).width('100%').height('100%').focusable(false) |
| 302 | }.justifyContent(FlexAlign.Start) | 302 | }.justifyContent(FlexAlign.Start) |
| 303 | - }.tabBar(this.TabBuilder('Help', 2, $r('app.media.info_circle'), $r('app.media.info_circle_default'))) | 303 | + }.tabBar(this.TabBuilder('Help', 2, $r('app.media.info'), $r('app.media.info'))) |
| 304 | 304 | ||
| 305 | }.scrollable(false) | 305 | }.scrollable(false) |
| 306 | }.width('100%').justifyContent(FlexAlign.Start) | 306 | }.width('100%').justifyContent(FlexAlign.Start) |
harmony-os/SherpaOnnxVadAsr/entry/src/main/resources/base/media/ic_public_input_voice.png
已删除
100644 → 0
474 字节
438 字节
| 1 | +<?xml version="1.0" standalone="no"?> | ||
| 2 | +<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> |
295 字节
| 1 | +<?xml version="1.0" standalone="no"?> | ||
| 2 | +<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> |
| 1 | +<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> |
636 字节
harmony-os/SherpaOnnxVadAsr/entry/src/main/resources/base/media/info_circle_default.png
已删除
100644 → 0
636 字节
| @@ -118,15 +118,24 @@ void OnlineZipformerTransducerModel::InitEncoder(void *model_data, | @@ -118,15 +118,24 @@ void OnlineZipformerTransducerModel::InitEncoder(void *model_data, | ||
| 118 | for (auto i : v) { | 118 | for (auto i : v) { |
| 119 | os << i << " "; | 119 | os << i << " "; |
| 120 | } | 120 | } |
| 121 | +#if __OHOS__ | ||
| 122 | + SHERPA_ONNX_LOGE("%{public}s\n", os.str().c_str()); | ||
| 123 | +#else | ||
| 121 | SHERPA_ONNX_LOGE("%s\n", os.str().c_str()); | 124 | SHERPA_ONNX_LOGE("%s\n", os.str().c_str()); |
| 125 | +#endif | ||
| 122 | }; | 126 | }; |
| 123 | print(encoder_dims_, "encoder_dims"); | 127 | print(encoder_dims_, "encoder_dims"); |
| 124 | print(attention_dims_, "attention_dims"); | 128 | print(attention_dims_, "attention_dims"); |
| 125 | print(num_encoder_layers_, "num_encoder_layers"); | 129 | print(num_encoder_layers_, "num_encoder_layers"); |
| 126 | print(cnn_module_kernels_, "cnn_module_kernels"); | 130 | print(cnn_module_kernels_, "cnn_module_kernels"); |
| 127 | print(left_context_len_, "left_context_len"); | 131 | print(left_context_len_, "left_context_len"); |
| 132 | +#if __OHOS__ | ||
| 133 | + SHERPA_ONNX_LOGE("T: %{public}d", T_); | ||
| 134 | + SHERPA_ONNX_LOGE("decode_chunk_len_: %{public}d", decode_chunk_len_); | ||
| 135 | +#else | ||
| 128 | SHERPA_ONNX_LOGE("T: %d", T_); | 136 | SHERPA_ONNX_LOGE("T: %d", T_); |
| 129 | SHERPA_ONNX_LOGE("decode_chunk_len_: %d", decode_chunk_len_); | 137 | SHERPA_ONNX_LOGE("decode_chunk_len_: %d", decode_chunk_len_); |
| 138 | +#endif | ||
| 130 | } | 139 | } |
| 131 | } | 140 | } |
| 132 | 141 | ||
| @@ -147,7 +156,11 @@ void OnlineZipformerTransducerModel::InitDecoder(void *model_data, | @@ -147,7 +156,11 @@ void OnlineZipformerTransducerModel::InitDecoder(void *model_data, | ||
| 147 | std::ostringstream os; | 156 | std::ostringstream os; |
| 148 | os << "---decoder---\n"; | 157 | os << "---decoder---\n"; |
| 149 | PrintModelMetadata(os, meta_data); | 158 | PrintModelMetadata(os, meta_data); |
| 159 | +#if __OHOS__ | ||
| 160 | + SHERPA_ONNX_LOGE("%{public}s", os.str().c_str()); | ||
| 161 | +#else | ||
| 150 | SHERPA_ONNX_LOGE("%s", os.str().c_str()); | 162 | SHERPA_ONNX_LOGE("%s", os.str().c_str()); |
| 163 | +#endif | ||
| 151 | } | 164 | } |
| 152 | 165 | ||
| 153 | Ort::AllocatorWithDefaultOptions allocator; // used in the macro below | 166 | Ort::AllocatorWithDefaultOptions allocator; // used in the macro below |
| @@ -172,7 +185,11 @@ void OnlineZipformerTransducerModel::InitJoiner(void *model_data, | @@ -172,7 +185,11 @@ void OnlineZipformerTransducerModel::InitJoiner(void *model_data, | ||
| 172 | std::ostringstream os; | 185 | std::ostringstream os; |
| 173 | os << "---joiner---\n"; | 186 | os << "---joiner---\n"; |
| 174 | PrintModelMetadata(os, meta_data); | 187 | PrintModelMetadata(os, meta_data); |
| 188 | +#if __OHOS__ | ||
| 189 | + SHERPA_ONNX_LOGE("%{public}s", os.str().c_str()); | ||
| 190 | +#else | ||
| 175 | SHERPA_ONNX_LOGE("%s", os.str().c_str()); | 191 | SHERPA_ONNX_LOGE("%s", os.str().c_str()); |
| 192 | +#endif | ||
| 176 | } | 193 | } |
| 177 | } | 194 | } |
| 178 | 195 |
| @@ -126,7 +126,11 @@ void OnlineZipformer2TransducerModel::InitEncoder(void *model_data, | @@ -126,7 +126,11 @@ void OnlineZipformer2TransducerModel::InitEncoder(void *model_data, | ||
| 126 | for (auto i : v) { | 126 | for (auto i : v) { |
| 127 | os << i << " "; | 127 | os << i << " "; |
| 128 | } | 128 | } |
| 129 | +#if __OHOS__ | ||
| 130 | + SHERPA_ONNX_LOGE("%{public}s\n", os.str().c_str()); | ||
| 131 | +#else | ||
| 129 | SHERPA_ONNX_LOGE("%s\n", os.str().c_str()); | 132 | SHERPA_ONNX_LOGE("%s\n", os.str().c_str()); |
| 133 | +#endif | ||
| 130 | }; | 134 | }; |
| 131 | print(encoder_dims_, "encoder_dims"); | 135 | print(encoder_dims_, "encoder_dims"); |
| 132 | print(query_head_dims_, "query_head_dims"); | 136 | print(query_head_dims_, "query_head_dims"); |
| @@ -135,8 +139,14 @@ void OnlineZipformer2TransducerModel::InitEncoder(void *model_data, | @@ -135,8 +139,14 @@ void OnlineZipformer2TransducerModel::InitEncoder(void *model_data, | ||
| 135 | print(num_encoder_layers_, "num_encoder_layers"); | 139 | print(num_encoder_layers_, "num_encoder_layers"); |
| 136 | print(cnn_module_kernels_, "cnn_module_kernels"); | 140 | print(cnn_module_kernels_, "cnn_module_kernels"); |
| 137 | print(left_context_len_, "left_context_len"); | 141 | print(left_context_len_, "left_context_len"); |
| 142 | + | ||
| 143 | +#if __OHOS__ | ||
| 144 | + SHERPA_ONNX_LOGE("T: %{public}d", T_); | ||
| 145 | + SHERPA_ONNX_LOGE("decode_chunk_len_: %{public}d", decode_chunk_len_); | ||
| 146 | +#else | ||
| 138 | SHERPA_ONNX_LOGE("T: %d", T_); | 147 | SHERPA_ONNX_LOGE("T: %d", T_); |
| 139 | SHERPA_ONNX_LOGE("decode_chunk_len_: %d", decode_chunk_len_); | 148 | SHERPA_ONNX_LOGE("decode_chunk_len_: %d", decode_chunk_len_); |
| 149 | +#endif | ||
| 140 | } | 150 | } |
| 141 | } | 151 | } |
| 142 | 152 |
-
请 注册 或 登录 后发表评论