diff --git a/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/non-streaming-speaker-diarization.cc b/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/non-streaming-speaker-diarization.cc index 2cda40e76..a6b1f302e 100644 --- a/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/non-streaming-speaker-diarization.cc +++ b/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/non-streaming-speaker-diarization.cc @@ -282,6 +282,170 @@ static Napi::Array OfflineSpeakerDiarizationProcessWrapper( return ans; } +struct SpeakerDiarizationCallbackData { + int32_t num_processed_chunks; + int32_t num_total_chunks; +}; + +// see +// https://github.com/nodejs/node-addon-examples/blob/main/src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/clock.cc +static void InvokeJsCallback(Napi::Env env, Napi::Function callback, + Napi::Reference *context, + SpeakerDiarizationCallbackData *data) { + if (env != nullptr) { + if (callback != nullptr) { + Napi::Number num_processed_chunks = + Napi::Number::New(env, data->num_processed_chunks); + Napi::Number num_total_chunks = + Napi::Number::New(env, data->num_total_chunks); + + callback.Call(context->Value(), {num_processed_chunks, num_total_chunks}); + } + } + delete data; +} + +using TSFN = Napi::TypedThreadSafeFunction, + SpeakerDiarizationCallbackData, + InvokeJsCallback>; + +class SpeakerDiarizationProcessWorker : public Napi::AsyncWorker { + public: + SpeakerDiarizationProcessWorker(const Napi::Env &env, TSFN tsfn, + const SherpaOnnxOfflineSpeakerDiarization *sd, + std::vector samples) + : tsfn_(tsfn), + Napi::AsyncWorker{env, "SpeakerDiarizationProcessAsyncWorker"}, + deferred_(env), + sd_(sd), + samples_(std::move(samples)) {} + + Napi::Promise Promise() { return deferred_.Promise(); } + + protected: + void Execute() override { + auto callback = [](int32_t num_processed_chunks, int32_t num_total_chunks, + void *arg) -> int32_t { + auto _this = reinterpret_cast(arg); + + auto data = new SpeakerDiarizationCallbackData; + data->num_processed_chunks = num_processed_chunks; + data->num_total_chunks = num_total_chunks; + + _this->tsfn_.NonBlockingCall(data); + + return 0; + }; + + r_ = SherpaOnnxOfflineSpeakerDiarizationProcessWithCallback( + sd_, samples_.data(), samples_.size(), callback, this); + + tsfn_.Release(); + } + + void OnOK() override { + Napi::Env env = deferred_.Env(); + + int32_t num_segments = + SherpaOnnxOfflineSpeakerDiarizationResultGetNumSegments(r_); + + const SherpaOnnxOfflineSpeakerDiarizationSegment *segments = + SherpaOnnxOfflineSpeakerDiarizationResultSortByStartTime(r_); + + Napi::Array ans = Napi::Array::New(env, num_segments); + + for (int32_t i = 0; i != num_segments; ++i) { + Napi::Object obj = Napi::Object::New(env); + + obj.Set(Napi::String::New(env, "start"), segments[i].start); + obj.Set(Napi::String::New(env, "end"), segments[i].end); + obj.Set(Napi::String::New(env, "speaker"), segments[i].speaker); + + ans.Set(i, obj); + } + + SherpaOnnxOfflineSpeakerDiarizationDestroySegment(segments); + SherpaOnnxOfflineSpeakerDiarizationDestroyResult(r_); + + deferred_.Resolve(ans); + } + + private: + TSFN tsfn_; + Napi::Promise::Deferred deferred_; + const SherpaOnnxOfflineSpeakerDiarization *sd_; + std::vector samples_; + const SherpaOnnxOfflineSpeakerDiarizationResult *r_; +}; + +static Napi::Object OfflineSpeakerDiarizationProcessAsyncWrapper( + const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + + if (info.Length() != 3) { + std::ostringstream os; + os << "Expect only 3 arguments. Given: " << info.Length(); + + Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException(); + + return {}; + } + + if (!info[0].IsExternal()) { + Napi::TypeError::New( + env, "Argument 0 should be an offline speaker diarization pointer.") + .ThrowAsJavaScriptException(); + + return {}; + } + + const SherpaOnnxOfflineSpeakerDiarization *sd = + info[0].As>().Data(); + + if (!info[1].IsTypedArray()) { + Napi::TypeError::New(env, "Argument 1 should be a typed array") + .ThrowAsJavaScriptException(); + + return {}; + } + + if (!info[2].IsFunction()) { + Napi::TypeError::New(env, "Argument 2 should be a function") + .ThrowAsJavaScriptException(); + + return {}; + } + + Napi::Function cb = info[2].As(); + + auto context = + new Napi::Reference(Napi::Persistent(info.This())); + + TSFN tsfn = TSFN::New( + env, + cb, // JavaScript function called asynchronously + "SpeakerDiarizationProcessAsyncFunc", // Name + 0, // Unlimited queue + 1, // Only one thread will use this initially + context, + [](Napi::Env, void *, Napi::Reference *ctx) { delete ctx; }); + + Napi::Float32Array samples = info[1].As(); + +#if __OHOS__ + int32_t num_samples = samples.ElementLength() / sizeof(float); +#else + int32_t num_samples = samples.ElementLength(); +#endif + std::vector v(num_samples); + std::copy(samples.Data(), samples.Data() + num_samples, v.begin()); + + SpeakerDiarizationProcessWorker *worker = + new SpeakerDiarizationProcessWorker(env, tsfn, sd, v); + worker->Queue(); + return worker->Promise(); +} + static void OfflineSpeakerDiarizationSetConfigWrapper( const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); @@ -313,7 +477,7 @@ static void OfflineSpeakerDiarizationSetConfigWrapper( return; } - Napi::Object o = info[0].As(); + Napi::Object o = info[1].As(); SherpaOnnxOfflineSpeakerDiarizationConfig c; memset(&c, 0, sizeof(c)); @@ -334,6 +498,10 @@ void InitNonStreamingSpeakerDiarization(Napi::Env env, Napi::Object exports) { Napi::String::New(env, "offlineSpeakerDiarizationProcess"), Napi::Function::New(env, OfflineSpeakerDiarizationProcessWrapper)); + exports.Set( + Napi::String::New(env, "offlineSpeakerDiarizationProcessAsync"), + Napi::Function::New(env, OfflineSpeakerDiarizationProcessAsyncWrapper)); + exports.Set( Napi::String::New(env, "offlineSpeakerDiarizationSetConfig"), Napi::Function::New(env, OfflineSpeakerDiarizationSetConfigWrapper)); diff --git a/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/non-streaming-tts.cc b/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/non-streaming-tts.cc index ed1f3afb3..05e27846d 100644 --- a/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/non-streaming-tts.cc +++ b/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/non-streaming-tts.cc @@ -344,9 +344,9 @@ struct TtsCallbackData { // see // https://github.com/nodejs/node-addon-examples/blob/main/src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/clock.cc -void InvokeJsCallback(Napi::Env env, Napi::Function callback, - Napi::Reference *context, - TtsCallbackData *data) { +static void InvokeJsCallback(Napi::Env env, Napi::Function callback, + Napi::Reference *context, + TtsCallbackData *data) { if (env != nullptr) { if (callback != nullptr) { Napi::ArrayBuffer arrayBuffer = @@ -580,7 +580,6 @@ static Napi::Object OfflineTtsGenerateAsyncWrapper( context, [](Napi::Env, void *, Napi::Reference *ctx) { delete ctx; }); - const SherpaOnnxGeneratedAudio *audio; TtsGenerateWorker *worker = new TtsGenerateWorker( env, tsfn, tts, text, speed, sid, enable_external_buffer); worker->Queue(); diff --git a/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/types/libsherpa_onnx/Index.d.ts b/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/types/libsherpa_onnx/Index.d.ts index f71e2f6ee..7db410a4c 100644 --- a/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/types/libsherpa_onnx/Index.d.ts +++ b/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/types/libsherpa_onnx/Index.d.ts @@ -65,5 +65,6 @@ export const speakerEmbeddingManagerGetAllSpeakers: (handle: object) => Array object; export const getOfflineSpeakerDiarizationSampleRate: (handle: object) => number; -export const offlineSpeakerDiarizationProcess: (handle: object, samples: Float32Array) => object; +export const offlineSpeakerDiarizationProcess: (handle: object, input: object) => object; +export const offlineSpeakerDiarizationProcessAsync: (handle: object, input: object, callback: object) => object; export const offlineSpeakerDiarizationSetConfig: (handle: object, config: object) => void; diff --git a/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/ets/components/NonStreamingSpeakerDiarization.ets b/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/ets/components/NonStreamingSpeakerDiarization.ets index c0dcd3af1..176da87a5 100644 --- a/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/ets/components/NonStreamingSpeakerDiarization.ets +++ b/harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/ets/components/NonStreamingSpeakerDiarization.ets @@ -2,6 +2,7 @@ import { createOfflineSpeakerDiarization, getOfflineSpeakerDiarizationSampleRate, offlineSpeakerDiarizationProcess, + offlineSpeakerDiarizationProcessAsync, offlineSpeakerDiarizationSetConfig, } from 'libsherpa_onnx.so'; @@ -32,8 +33,12 @@ export class OfflineSpeakerDiarizationConfig { } export class OfflineSpeakerDiarizationSegment { - public start: number = 0; // in secondspublic end: number = 0; // in secondspublic speaker: number = - 0; // ID of the speaker; count from 0 + // in seconds + public start: number = 0; + // in seconds + public end: number = 0; + // ID of the speaker; count from 0 + public speaker: number = 0; } export class OfflineSpeakerDiarization { @@ -62,8 +67,14 @@ export class OfflineSpeakerDiarization { * "speaker": an_integer, * } */ - process(samples: Float32Array): OfflineSpeakerDiarizationSegment { - return offlineSpeakerDiarizationProcess(this.handle, samples) as OfflineSpeakerDiarizationSegment; + process(samples: Float32Array): OfflineSpeakerDiarizationSegment[] { + return offlineSpeakerDiarizationProcess(this.handle, samples) as OfflineSpeakerDiarizationSegment[]; + } + + processAsync(samples: Float32Array, callback: (numProcessedChunks: number, + numTotalChunks: number) => void): Promise { + return offlineSpeakerDiarizationProcessAsync(this.handle, samples, + callback) as Promise; } setConfig(config: OfflineSpeakerDiarizationConfig) { diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/.gitignore b/harmony-os/SherpaOnnxSpeakerDiarization/.gitignore new file mode 100644 index 000000000..d2ff20141 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/AppScope/app.json5 b/harmony-os/SherpaOnnxSpeakerDiarization/AppScope/app.json5 new file mode 100644 index 000000000..93816310b --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.k2fsa.sherpa.onnx.speaker.diarization", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/AppScope/resources/base/element/string.json b/harmony-os/SherpaOnnxSpeakerDiarization/AppScope/resources/base/element/string.json new file mode 100644 index 000000000..a9d8f0625 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "SherpaOnnxSpeakerDiarization" + } + ] +} diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/AppScope/resources/base/media/app_icon.png b/harmony-os/SherpaOnnxSpeakerDiarization/AppScope/resources/base/media/app_icon.png new file mode 100644 index 000000000..a39445dc8 Binary files /dev/null and b/harmony-os/SherpaOnnxSpeakerDiarization/AppScope/resources/base/media/app_icon.png differ diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/build-profile.json5 b/harmony-os/SherpaOnnxSpeakerDiarization/build-profile.json5 new file mode 100644 index 000000000..8e63d9768 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/build-profile.json5 @@ -0,0 +1,40 @@ +{ + "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 diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/code-linter.json5 b/harmony-os/SherpaOnnxSpeakerDiarization/code-linter.json5 new file mode 100644 index 000000000..77b31b517 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/code-linter.json5 @@ -0,0 +1,20 @@ +{ + "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 diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/.gitignore b/harmony-os/SherpaOnnxSpeakerDiarization/entry/.gitignore new file mode 100644 index 000000000..e2713a277 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/build-profile.json5 b/harmony-os/SherpaOnnxSpeakerDiarization/entry/build-profile.json5 new file mode 100644 index 000000000..cb3e674c9 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/build-profile.json5 @@ -0,0 +1,33 @@ +{ + "apiType": "stageMode", + "buildOption": { + "sourceOption": { + "workers": [ + './src/main/ets/workers/SpeakerDiarizationWorker.ets' + ] + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/hvigorfile.ts b/harmony-os/SherpaOnnxSpeakerDiarization/entry/hvigorfile.ts new file mode 100644 index 000000000..c6edcd904 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/hvigorfile.ts @@ -0,0 +1,6 @@ +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. */ +} diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/obfuscation-rules.txt b/harmony-os/SherpaOnnxSpeakerDiarization/entry/obfuscation-rules.txt new file mode 100644 index 000000000..272efb6ca --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# 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 diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/oh-package.json5 b/harmony-os/SherpaOnnxSpeakerDiarization/entry/oh-package.json5 new file mode 100644 index 000000000..97448bbf3 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "sherpa_onnx": "1.10.33" + } +} + diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/entryability/EntryAbility.ets b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 000000000..679d91453 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,43 @@ +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'); + } +} diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 000000000..d2c48b421 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,12 @@ +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 diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/pages/Index.ets b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/pages/Index.ets new file mode 100644 index 000000000..f6fc537b4 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,194 @@ +import { LengthUnit, promptAction } from '@kit.ArkUI'; +import worker, { MessageEvents } from '@ohos.worker'; +import { BusinessError, pasteboard } from '@kit.BasicServicesKit'; +import { picker } from '@kit.CoreFileKit'; + + +@Entry +@Component +struct Index { + @State title: string = 'Next-gen Kaldi: Speaker Diarization'; + @State titleFontSize: number = 15; + @State currentIndex: number = 0; + @State resultForFile: string = ''; + @State resultForMic: string = ''; + @State progressForFile: number = 0; + @State selectFileBtnEnabled: boolean = false; + @State copyBtnForFileEnabled: boolean = false; + private controller: TabsController = new TabsController(); + private workerInstance?: worker.ThreadWorker + private readonly scriptURL: string = 'entry/ets/workers/SpeakerDiarizationWorker.ets' + private numSpeakers: string = '-1'; + + @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); + }) + } + + aboutToAppear(): void { + this.workerInstance = new worker.ThreadWorker(this.scriptURL, { + name: 'Streaming ASR worker' + }); + + this.workerInstance.onmessage = (e: MessageEvents) => { + const msgType = e.data['msgType'] as string; + + if (msgType != 'speaker-diarization-file-progress') { + console.log(`received msg from worker: ${msgType}`); + } + + if (msgType == 'init-speaker-diarization-done') { + console.log('Speaker diarization initialized successfully'); + + this.resultForFile = 'Initialization finished.\nPlease select a .wav file.'; + this.resultForMic = 'Initialization finished.\nPlease click the button Start recording.'; + + this.selectFileBtnEnabled = true; + } + + if (msgType == 'speaker-diarization-file-progress') { + this.progressForFile = e.data['progress'] as number; + } + + if (msgType == 'speaker-diarization-file-done') { + const result = e.data['result'] as string; + this.resultForFile = result; + + this.selectFileBtnEnabled = true; + this.copyBtnForFileEnabled = true; + } + }; + + const context = getContext(); + this.workerInstance.postMessage({ msgType: 'init-speaker-diarization', context }); + console.log('initializing'); + this.resultForFile = 'Initializing models. Please wait'; + this.resultForMic = this.resultForFile; + } + + build() { + Column() { + Tabs({ barPosition: BarPosition.End, controller: this.controller }) { + TabContent() { + Column({ space: 10 }) { + Text(this.title).fontSize(this.titleFontSize).fontWeight(FontWeight.Bold); + Row({ space: 10 }) { + Text(`Number of speakers`).width('60%') + + TextInput({ text: this.numSpeakers }).onChange((text) => { + this.numSpeakers = text.trim(); + }).width('20%') + }.justifyContent(FlexAlign.Center) + + Row({ space: 10 }) { + Button('Select .wav file (16kHz) ').enabled(this.selectFileBtnEnabled).onClick(() => { + this.resultForFile = ''; + this.progressForFile = 0; + this.copyBtnForFileEnabled = false; + + let numSpeakers = parseInt(this.numSpeakers); + if (numSpeakers.toString() != this.numSpeakers) { + this.resultForFile = + 'Please input a valid value for the number of speakers in the .wav file you are going to select'; + return; + } + + if (numSpeakers < 1) { + this.resultForFile = + 'Please input a positive value for the number of speakers in the .wav file you are going to select'; + return; + } + + 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) => { + console.log(`select file result: ${result}`); + + if (!result[0]) { + this.resultForFile = 'Please select a file to decode'; + this.selectFileBtnEnabled = true; + return; + } + + if (this.workerInstance) { + this.workerInstance.postMessage({ + msgType: 'speaker-diarization-file', filename: result[0], numSpeakers, + }); + this.resultForFile = `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; + }) + }) + Button('Copy results') + .enabled(this.copyBtnForFileEnabled) + .onClick(() => { // See https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-308-V5 + const pasteboardData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, this.resultForFile); + const systemPasteboard = pasteboard.getSystemPasteboard(); + systemPasteboard.setData(pasteboardData); + systemPasteboard.getData().then((data) => { + if (data) { + promptAction.showToast({ message: 'Result copied.' }); + } else { + promptAction.showToast({ message: 'Failed to copy' }); + } + }) + }) + } + + if (this.progressForFile > 0) { + Row() { + Progress({ value: 0, total: 100, type: ProgressType.Capsule }) + .width('80%') + .height(20) + .value(this.progressForFile); + + Text(`${this.progressForFile.toFixed(2)}%`).width('15%') + }.width('100%').justifyContent(FlexAlign.Center) + } + + TextArea({ text: this.resultForFile }) + .lineSpacing({ value: 10, unit: LengthUnit.VP }) + .width('100%') + .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); + 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', 1, $r('app.media.info'), $r('app.media.info'))) + }.scrollable(false) + } + } +} \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/workers/SpeakerDiarizationWorker.ets b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/workers/SpeakerDiarizationWorker.ets new file mode 100644 index 000000000..4a297ec87 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/workers/SpeakerDiarizationWorker.ets @@ -0,0 +1,189 @@ +import worker, { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope } from '@ohos.worker'; +import { + OfflineSpeakerDiarization, + OfflineSpeakerDiarizationConfig, + OfflineSpeakerDiarizationSegment, + readWaveFromBinary, + Samples +} from 'sherpa_onnx'; +import { fileIo } from '@kit.CoreFileKit'; + +const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + +let sd: OfflineSpeakerDiarization; +let useAsync: boolean = true; + +function readWave(filename: string): Samples { + 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); + return readWaveFromBinary(data) as Samples; +} + +function initOfflineSpeakerDiarization(context: Context): OfflineSpeakerDiarization { + const config: OfflineSpeakerDiarizationConfig = new OfflineSpeakerDiarizationConfig(); + + // Please refer to https://github.com/k2-fsa/sherpa-onnx/releases/tag/speaker-segmentation-models + // to download models. + // Make sure you have placed it inside the directory + // harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/rawfile + // + // Also, please delete unused files to reduce the size of the app + config.segmentation.pyannote.model = 'sherpa-onnx-pyannote-segmentation-3-0/model.int8.onnx'; + config.segmentation.numThreads = 2; + config.segmentation.debug = true; + + // Please refer to https://github.com/k2-fsa/sherpa-onnx/releases/tag/speaker-recongition-models + // to download models. + // Make sure you have placed it inside the directory + // harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/rawfile + config.embedding.model = '3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx'; + config.embedding.numThreads = 2; + config.embedding.debug = true; + + config.minDurationOn = 0.2; + config.minDurationOff = 0.5; + return new OfflineSpeakerDiarization(config, context.resourceManager); + + // For the above two models files, you should have the following directory structure + /* + (py38) fangjuns-MacBook-Pro:rawfile fangjun$ pwd + /Users/fangjun/open-source/sherpa-onnx/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/rawfile + (py38) fangjuns-MacBook-Pro:rawfile fangjun$ ls -lh + total 77336 + -rw-r--r-- 1 fangjun staff 38M Dec 10 16:28 3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx + drwxr-xr-x 3 fangjun staff 96B Dec 10 19:36 sherpa-onnx-pyannote-segmentation-3-0 + (py38) fangjuns-MacBook-Pro:rawfile fangjun$ tree . + . + ├── 3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx + └── sherpa-onnx-pyannote-segmentation-3-0 + └── model.int8.onnx + + 1 directory, 2 files + + (Note that we have kept only model.int8.onnx and removed all other files + from sherpa-onnx-pyannote-segmentation-3-0 + ) + */ +} + +/** + * 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; + + console.log(`from the main thread, msg-type: ${msgType}`); + if (msgType == 'init-speaker-diarization' && !sd) { + const context: Context = e.data['context'] as Context; + sd = initOfflineSpeakerDiarization(context); + workerPort.postMessage({ msgType: 'init-speaker-diarization-done' }); + console.log('Init sd done'); + } + + if (msgType == 'speaker-diarization-file') { + const filename = e.data['filename'] as string; + const numSpeakers = e.data['numSpeakers'] as number; + const wave = readWave(filename); + let result = ''; + if (wave == undefined || wave == null) { + result = `Failed to read ${filename}`; + + workerPort.postMessage({ + msgType: 'speaker-diarization-file-done', result + }); + return; + } + + if (wave.sampleRate != sd.sampleRate) { + result = `Expected sample rate: ${sd.sampleRate}`; + result += '\n'; + result += `Sample rate in file ${filename} is ${wave.sampleRate}`; + + workerPort.postMessage({ + msgType: 'speaker-diarization-file-done', result + }); + + return; + } + + const duration = wave.samples.length / wave.sampleRate; + console.log(`Processing ${filename} of ${duration} seconds`); + + // You can remove this if statement if you want + if (duration < 0.3) { + result = `${filename} has only ${duration} seconds. Please use a longer file`; + + workerPort.postMessage({ + msgType: 'speaker-diarization-file-done', result + }); + return; + } + sd.config.clustering.numClusters = numSpeakers; + sd.setConfig(sd.config); + + if (useAsync) { + sd.processAsync(wave.samples, (numProcessedChunks: number, numTotalChunks: number) => { + const progress = numProcessedChunks / numTotalChunks * 100; + workerPort.postMessage({ + msgType: 'speaker-diarization-file-progress', progress + }); + }).then((r: OfflineSpeakerDiarizationSegment[]) => { + console.log(`r is ${r.length}, ${r}`); + + for (const s of r) { + const start: string = s.start.toFixed(3); + const end: string = s.end.toFixed(3); + result += `${start}\t--\t${end}\tspeaker_${s.speaker}\n`; + console.log(`result: ${result}`); + } + + if (r.length == 0) { + result = 'The result is empty'; + } + + workerPort.postMessage({ + msgType: 'speaker-diarization-file-done', result + }); + }); + } else { + const r: OfflineSpeakerDiarizationSegment[] = sd.process(wave.samples) + console.log(`r is ${r.length}, ${r}`); + for (const s of r) { + const start: string = s.start.toFixed(3); + const end: string = s.end.toFixed(3); + result += `${start}\t--\t${end}\tspeaker_${s.speaker}\n`; + console.log(`result: ${result}`); + } + + if (r.length == 0) { + result = 'The result is empty'; + } + + workerPort.postMessage({ + msgType: 'speaker-diarization-file-done', result + }); + } + } +} /** + * 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 diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/module.json5 b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/module.json5 new file mode 100644 index 000000000..a1cea8b6a --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/module.json5 @@ -0,0 +1,52 @@ +{ + "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" + } + ], + } + ] + } +} \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/element/color.json b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/element/color.json new file mode 100644 index 000000000..3c712962d --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/element/string.json b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/element/string.json new file mode 100644 index 000000000..55c8939f3 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "On-device speaker diarization with Next-gen Kaldi" + }, + { + "name": "EntryAbility_desc", + "value": "On-device speaker diarization with Next-gen Kaldi" + }, + { + "name": "EntryAbility_label", + "value": "Speaker diarization" + } + ] +} \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/background.png b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/background.png new file mode 100644 index 000000000..f939c9fa8 Binary files /dev/null and b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/background.png differ diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/foreground.png b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 000000000..4483ddad1 Binary files /dev/null and b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/foreground.png differ diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/icon_doc.svg b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/icon_doc.svg new file mode 100644 index 000000000..ab6b1fd76 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/icon_doc.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/icon_mic.svg b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/icon_mic.svg new file mode 100644 index 000000000..0aeb30d63 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/icon_mic.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/info.svg b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/info.svg new file mode 100644 index 000000000..2210223f4 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/layered_image.json b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 000000000..fb4992044 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/startIcon.png b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 000000000..205ad8b5a Binary files /dev/null and b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/startIcon.png differ diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/profile/backup_config.json b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 000000000..78f40ae7c --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/profile/main_pages.json b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 000000000..1898d94f5 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/en_US/element/string.json b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 000000000..55c8939f3 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "On-device speaker diarization with Next-gen Kaldi" + }, + { + "name": "EntryAbility_desc", + "value": "On-device speaker diarization with Next-gen Kaldi" + }, + { + "name": "EntryAbility_label", + "value": "Speaker diarization" + } + ] +} \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/rawfile/.gitkeep b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/rawfile/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/zh_CN/element/string.json b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 000000000..d9180dfd1 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "新一代Kaldi: 本地说话人日志" + }, + { + "name": "EntryAbility_desc", + "value": "新一代Kaldi: 本地说话人日志" + }, + { + "name": "EntryAbility_label", + "value": "说话人日志" + } + ] +} \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/ohosTest/ets/test/Ability.test.ets b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 000000000..8aa374977 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,35 @@ +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 diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/ohosTest/ets/test/List.test.ets b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 000000000..794c7dc4e --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,5 @@ +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/ohosTest/module.json5 b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/ohosTest/module.json5 new file mode 100644 index 000000000..55725a929 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/ohosTest/module.json5 @@ -0,0 +1,13 @@ +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/test/List.test.ets b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/test/List.test.ets new file mode 100644 index 000000000..bb5b5c373 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/test/List.test.ets @@ -0,0 +1,5 @@ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/test/LocalUnit.test.ets b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/test/LocalUnit.test.ets new file mode 100644 index 000000000..165fc1615 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/test/LocalUnit.test.ets @@ -0,0 +1,33 @@ +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 diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/hvigor/hvigor-config.json5 b/harmony-os/SherpaOnnxSpeakerDiarization/hvigor/hvigor-config.json5 new file mode 100644 index 000000000..06b278367 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/hvigor/hvigor-config.json5 @@ -0,0 +1,22 @@ +{ + "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*/ + } +} diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/hvigorfile.ts b/harmony-os/SherpaOnnxSpeakerDiarization/hvigorfile.ts new file mode 100644 index 000000000..f3cb9f1a8 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/hvigorfile.ts @@ -0,0 +1,6 @@ +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. */ +} diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/oh-package-lock.json5 b/harmony-os/SherpaOnnxSpeakerDiarization/oh-package-lock.json5 new file mode 100644 index 000000000..f538ae290 --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/oh-package-lock.json5 @@ -0,0 +1,19 @@ +{ + "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 diff --git a/harmony-os/SherpaOnnxSpeakerDiarization/oh-package.json5 b/harmony-os/SherpaOnnxSpeakerDiarization/oh-package.json5 new file mode 100644 index 000000000..a79d5300e --- /dev/null +++ b/harmony-os/SherpaOnnxSpeakerDiarization/oh-package.json5 @@ -0,0 +1,9 @@ +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.19" + } +} diff --git a/sherpa-onnx/c-api/c-api.cc b/sherpa-onnx/c-api/c-api.cc index 84b33eb0d..4a11cae29 100644 --- a/sherpa-onnx/c-api/c-api.cc +++ b/sherpa-onnx/c-api/c-api.cc @@ -2053,11 +2053,6 @@ SherpaOnnxCreateOfflineSpeakerDiarizationOHOS( auto sd_config = GetOfflineSpeakerDiarizationConfig(config); - if (!sd_config.Validate()) { - SHERPA_ONNX_LOGE("Errors in config"); - return nullptr; - } - SherpaOnnxOfflineSpeakerDiarization *sd = new SherpaOnnxOfflineSpeakerDiarization; diff --git a/sherpa-onnx/c-api/c-api.h b/sherpa-onnx/c-api/c-api.h index a781520ff..4d4a2c4fc 100644 --- a/sherpa-onnx/c-api/c-api.h +++ b/sherpa-onnx/c-api/c-api.h @@ -1512,10 +1512,10 @@ SHERPA_ONNX_API void SherpaOnnxOfflineSpeakerDiarizationDestroySegment( const SherpaOnnxOfflineSpeakerDiarizationSegment *s); typedef int32_t (*SherpaOnnxOfflineSpeakerDiarizationProgressCallback)( - int32_t num_processed_chunk, int32_t num_total_chunks, void *arg); + int32_t num_processed_chunks, int32_t num_total_chunks, void *arg); typedef int32_t (*SherpaOnnxOfflineSpeakerDiarizationProgressCallbackNoArg)( - int32_t num_processed_chunk, int32_t num_total_chunks); + int32_t num_processed_chunks, int32_t num_total_chunks); // The user has to invoke SherpaOnnxOfflineSpeakerDiarizationDestroyResult() // to free the returned pointer to avoid memory leak.