Committed by
GitHub
Add speaker diarization demo for HarmonyOS (#1610)
正在显示
45 个修改的文件
包含
1074 行增加
和
17 行删除
| @@ -282,6 +282,170 @@ static Napi::Array OfflineSpeakerDiarizationProcessWrapper( | @@ -282,6 +282,170 @@ static Napi::Array OfflineSpeakerDiarizationProcessWrapper( | ||
| 282 | return ans; | 282 | return ans; |
| 283 | } | 283 | } |
| 284 | 284 | ||
| 285 | +struct SpeakerDiarizationCallbackData { | ||
| 286 | + int32_t num_processed_chunks; | ||
| 287 | + int32_t num_total_chunks; | ||
| 288 | +}; | ||
| 289 | + | ||
| 290 | +// see | ||
| 291 | +// https://github.com/nodejs/node-addon-examples/blob/main/src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/clock.cc | ||
| 292 | +static void InvokeJsCallback(Napi::Env env, Napi::Function callback, | ||
| 293 | + Napi::Reference<Napi::Value> *context, | ||
| 294 | + SpeakerDiarizationCallbackData *data) { | ||
| 295 | + if (env != nullptr) { | ||
| 296 | + if (callback != nullptr) { | ||
| 297 | + Napi::Number num_processed_chunks = | ||
| 298 | + Napi::Number::New(env, data->num_processed_chunks); | ||
| 299 | + Napi::Number num_total_chunks = | ||
| 300 | + Napi::Number::New(env, data->num_total_chunks); | ||
| 301 | + | ||
| 302 | + callback.Call(context->Value(), {num_processed_chunks, num_total_chunks}); | ||
| 303 | + } | ||
| 304 | + } | ||
| 305 | + delete data; | ||
| 306 | +} | ||
| 307 | + | ||
| 308 | +using TSFN = Napi::TypedThreadSafeFunction<Napi::Reference<Napi::Value>, | ||
| 309 | + SpeakerDiarizationCallbackData, | ||
| 310 | + InvokeJsCallback>; | ||
| 311 | + | ||
| 312 | +class SpeakerDiarizationProcessWorker : public Napi::AsyncWorker { | ||
| 313 | + public: | ||
| 314 | + SpeakerDiarizationProcessWorker(const Napi::Env &env, TSFN tsfn, | ||
| 315 | + const SherpaOnnxOfflineSpeakerDiarization *sd, | ||
| 316 | + std::vector<float> samples) | ||
| 317 | + : tsfn_(tsfn), | ||
| 318 | + Napi::AsyncWorker{env, "SpeakerDiarizationProcessAsyncWorker"}, | ||
| 319 | + deferred_(env), | ||
| 320 | + sd_(sd), | ||
| 321 | + samples_(std::move(samples)) {} | ||
| 322 | + | ||
| 323 | + Napi::Promise Promise() { return deferred_.Promise(); } | ||
| 324 | + | ||
| 325 | + protected: | ||
| 326 | + void Execute() override { | ||
| 327 | + auto callback = [](int32_t num_processed_chunks, int32_t num_total_chunks, | ||
| 328 | + void *arg) -> int32_t { | ||
| 329 | + auto _this = reinterpret_cast<SpeakerDiarizationProcessWorker *>(arg); | ||
| 330 | + | ||
| 331 | + auto data = new SpeakerDiarizationCallbackData; | ||
| 332 | + data->num_processed_chunks = num_processed_chunks; | ||
| 333 | + data->num_total_chunks = num_total_chunks; | ||
| 334 | + | ||
| 335 | + _this->tsfn_.NonBlockingCall(data); | ||
| 336 | + | ||
| 337 | + return 0; | ||
| 338 | + }; | ||
| 339 | + | ||
| 340 | + r_ = SherpaOnnxOfflineSpeakerDiarizationProcessWithCallback( | ||
| 341 | + sd_, samples_.data(), samples_.size(), callback, this); | ||
| 342 | + | ||
| 343 | + tsfn_.Release(); | ||
| 344 | + } | ||
| 345 | + | ||
| 346 | + void OnOK() override { | ||
| 347 | + Napi::Env env = deferred_.Env(); | ||
| 348 | + | ||
| 349 | + int32_t num_segments = | ||
| 350 | + SherpaOnnxOfflineSpeakerDiarizationResultGetNumSegments(r_); | ||
| 351 | + | ||
| 352 | + const SherpaOnnxOfflineSpeakerDiarizationSegment *segments = | ||
| 353 | + SherpaOnnxOfflineSpeakerDiarizationResultSortByStartTime(r_); | ||
| 354 | + | ||
| 355 | + Napi::Array ans = Napi::Array::New(env, num_segments); | ||
| 356 | + | ||
| 357 | + for (int32_t i = 0; i != num_segments; ++i) { | ||
| 358 | + Napi::Object obj = Napi::Object::New(env); | ||
| 359 | + | ||
| 360 | + obj.Set(Napi::String::New(env, "start"), segments[i].start); | ||
| 361 | + obj.Set(Napi::String::New(env, "end"), segments[i].end); | ||
| 362 | + obj.Set(Napi::String::New(env, "speaker"), segments[i].speaker); | ||
| 363 | + | ||
| 364 | + ans.Set(i, obj); | ||
| 365 | + } | ||
| 366 | + | ||
| 367 | + SherpaOnnxOfflineSpeakerDiarizationDestroySegment(segments); | ||
| 368 | + SherpaOnnxOfflineSpeakerDiarizationDestroyResult(r_); | ||
| 369 | + | ||
| 370 | + deferred_.Resolve(ans); | ||
| 371 | + } | ||
| 372 | + | ||
| 373 | + private: | ||
| 374 | + TSFN tsfn_; | ||
| 375 | + Napi::Promise::Deferred deferred_; | ||
| 376 | + const SherpaOnnxOfflineSpeakerDiarization *sd_; | ||
| 377 | + std::vector<float> samples_; | ||
| 378 | + const SherpaOnnxOfflineSpeakerDiarizationResult *r_; | ||
| 379 | +}; | ||
| 380 | + | ||
| 381 | +static Napi::Object OfflineSpeakerDiarizationProcessAsyncWrapper( | ||
| 382 | + const Napi::CallbackInfo &info) { | ||
| 383 | + Napi::Env env = info.Env(); | ||
| 384 | + | ||
| 385 | + if (info.Length() != 3) { | ||
| 386 | + std::ostringstream os; | ||
| 387 | + os << "Expect only 3 arguments. Given: " << info.Length(); | ||
| 388 | + | ||
| 389 | + Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException(); | ||
| 390 | + | ||
| 391 | + return {}; | ||
| 392 | + } | ||
| 393 | + | ||
| 394 | + if (!info[0].IsExternal()) { | ||
| 395 | + Napi::TypeError::New( | ||
| 396 | + env, "Argument 0 should be an offline speaker diarization pointer.") | ||
| 397 | + .ThrowAsJavaScriptException(); | ||
| 398 | + | ||
| 399 | + return {}; | ||
| 400 | + } | ||
| 401 | + | ||
| 402 | + const SherpaOnnxOfflineSpeakerDiarization *sd = | ||
| 403 | + info[0].As<Napi::External<SherpaOnnxOfflineSpeakerDiarization>>().Data(); | ||
| 404 | + | ||
| 405 | + if (!info[1].IsTypedArray()) { | ||
| 406 | + Napi::TypeError::New(env, "Argument 1 should be a typed array") | ||
| 407 | + .ThrowAsJavaScriptException(); | ||
| 408 | + | ||
| 409 | + return {}; | ||
| 410 | + } | ||
| 411 | + | ||
| 412 | + if (!info[2].IsFunction()) { | ||
| 413 | + Napi::TypeError::New(env, "Argument 2 should be a function") | ||
| 414 | + .ThrowAsJavaScriptException(); | ||
| 415 | + | ||
| 416 | + return {}; | ||
| 417 | + } | ||
| 418 | + | ||
| 419 | + Napi::Function cb = info[2].As<Napi::Function>(); | ||
| 420 | + | ||
| 421 | + auto context = | ||
| 422 | + new Napi::Reference<Napi::Value>(Napi::Persistent(info.This())); | ||
| 423 | + | ||
| 424 | + TSFN tsfn = TSFN::New( | ||
| 425 | + env, | ||
| 426 | + cb, // JavaScript function called asynchronously | ||
| 427 | + "SpeakerDiarizationProcessAsyncFunc", // Name | ||
| 428 | + 0, // Unlimited queue | ||
| 429 | + 1, // Only one thread will use this initially | ||
| 430 | + context, | ||
| 431 | + [](Napi::Env, void *, Napi::Reference<Napi::Value> *ctx) { delete ctx; }); | ||
| 432 | + | ||
| 433 | + Napi::Float32Array samples = info[1].As<Napi::Float32Array>(); | ||
| 434 | + | ||
| 435 | +#if __OHOS__ | ||
| 436 | + int32_t num_samples = samples.ElementLength() / sizeof(float); | ||
| 437 | +#else | ||
| 438 | + int32_t num_samples = samples.ElementLength(); | ||
| 439 | +#endif | ||
| 440 | + std::vector<float> v(num_samples); | ||
| 441 | + std::copy(samples.Data(), samples.Data() + num_samples, v.begin()); | ||
| 442 | + | ||
| 443 | + SpeakerDiarizationProcessWorker *worker = | ||
| 444 | + new SpeakerDiarizationProcessWorker(env, tsfn, sd, v); | ||
| 445 | + worker->Queue(); | ||
| 446 | + return worker->Promise(); | ||
| 447 | +} | ||
| 448 | + | ||
| 285 | static void OfflineSpeakerDiarizationSetConfigWrapper( | 449 | static void OfflineSpeakerDiarizationSetConfigWrapper( |
| 286 | const Napi::CallbackInfo &info) { | 450 | const Napi::CallbackInfo &info) { |
| 287 | Napi::Env env = info.Env(); | 451 | Napi::Env env = info.Env(); |
| @@ -313,7 +477,7 @@ static void OfflineSpeakerDiarizationSetConfigWrapper( | @@ -313,7 +477,7 @@ static void OfflineSpeakerDiarizationSetConfigWrapper( | ||
| 313 | return; | 477 | return; |
| 314 | } | 478 | } |
| 315 | 479 | ||
| 316 | - Napi::Object o = info[0].As<Napi::Object>(); | 480 | + Napi::Object o = info[1].As<Napi::Object>(); |
| 317 | 481 | ||
| 318 | SherpaOnnxOfflineSpeakerDiarizationConfig c; | 482 | SherpaOnnxOfflineSpeakerDiarizationConfig c; |
| 319 | memset(&c, 0, sizeof(c)); | 483 | memset(&c, 0, sizeof(c)); |
| @@ -335,6 +499,10 @@ void InitNonStreamingSpeakerDiarization(Napi::Env env, Napi::Object exports) { | @@ -335,6 +499,10 @@ void InitNonStreamingSpeakerDiarization(Napi::Env env, Napi::Object exports) { | ||
| 335 | Napi::Function::New(env, OfflineSpeakerDiarizationProcessWrapper)); | 499 | Napi::Function::New(env, OfflineSpeakerDiarizationProcessWrapper)); |
| 336 | 500 | ||
| 337 | exports.Set( | 501 | exports.Set( |
| 502 | + Napi::String::New(env, "offlineSpeakerDiarizationProcessAsync"), | ||
| 503 | + Napi::Function::New(env, OfflineSpeakerDiarizationProcessAsyncWrapper)); | ||
| 504 | + | ||
| 505 | + exports.Set( | ||
| 338 | Napi::String::New(env, "offlineSpeakerDiarizationSetConfig"), | 506 | Napi::String::New(env, "offlineSpeakerDiarizationSetConfig"), |
| 339 | Napi::Function::New(env, OfflineSpeakerDiarizationSetConfigWrapper)); | 507 | Napi::Function::New(env, OfflineSpeakerDiarizationSetConfigWrapper)); |
| 340 | } | 508 | } |
| @@ -344,9 +344,9 @@ struct TtsCallbackData { | @@ -344,9 +344,9 @@ struct TtsCallbackData { | ||
| 344 | 344 | ||
| 345 | // see | 345 | // see |
| 346 | // https://github.com/nodejs/node-addon-examples/blob/main/src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/clock.cc | 346 | // https://github.com/nodejs/node-addon-examples/blob/main/src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/clock.cc |
| 347 | -void InvokeJsCallback(Napi::Env env, Napi::Function callback, | ||
| 348 | - Napi::Reference<Napi::Value> *context, | ||
| 349 | - TtsCallbackData *data) { | 347 | +static void InvokeJsCallback(Napi::Env env, Napi::Function callback, |
| 348 | + Napi::Reference<Napi::Value> *context, | ||
| 349 | + TtsCallbackData *data) { | ||
| 350 | if (env != nullptr) { | 350 | if (env != nullptr) { |
| 351 | if (callback != nullptr) { | 351 | if (callback != nullptr) { |
| 352 | Napi::ArrayBuffer arrayBuffer = | 352 | Napi::ArrayBuffer arrayBuffer = |
| @@ -580,7 +580,6 @@ static Napi::Object OfflineTtsGenerateAsyncWrapper( | @@ -580,7 +580,6 @@ static Napi::Object OfflineTtsGenerateAsyncWrapper( | ||
| 580 | context, | 580 | context, |
| 581 | [](Napi::Env, void *, Napi::Reference<Napi::Value> *ctx) { delete ctx; }); | 581 | [](Napi::Env, void *, Napi::Reference<Napi::Value> *ctx) { delete ctx; }); |
| 582 | 582 | ||
| 583 | - const SherpaOnnxGeneratedAudio *audio; | ||
| 584 | TtsGenerateWorker *worker = new TtsGenerateWorker( | 583 | TtsGenerateWorker *worker = new TtsGenerateWorker( |
| 585 | env, tsfn, tts, text, speed, sid, enable_external_buffer); | 584 | env, tsfn, tts, text, speed, sid, enable_external_buffer); |
| 586 | worker->Queue(); | 585 | worker->Queue(); |
| @@ -65,5 +65,6 @@ export const speakerEmbeddingManagerGetAllSpeakers: (handle: object) => Array<st | @@ -65,5 +65,6 @@ export const speakerEmbeddingManagerGetAllSpeakers: (handle: object) => Array<st | ||
| 65 | 65 | ||
| 66 | export const createOfflineSpeakerDiarization: (config: object, mgr?: object) => object; | 66 | export const createOfflineSpeakerDiarization: (config: object, mgr?: object) => object; |
| 67 | export const getOfflineSpeakerDiarizationSampleRate: (handle: object) => number; | 67 | export const getOfflineSpeakerDiarizationSampleRate: (handle: object) => number; |
| 68 | -export const offlineSpeakerDiarizationProcess: (handle: object, samples: Float32Array) => object; | 68 | +export const offlineSpeakerDiarizationProcess: (handle: object, input: object) => object; |
| 69 | +export const offlineSpeakerDiarizationProcessAsync: (handle: object, input: object, callback: object) => object; | ||
| 69 | export const offlineSpeakerDiarizationSetConfig: (handle: object, config: object) => void; | 70 | export const offlineSpeakerDiarizationSetConfig: (handle: object, config: object) => void; |
| @@ -2,6 +2,7 @@ import { | @@ -2,6 +2,7 @@ import { | ||
| 2 | createOfflineSpeakerDiarization, | 2 | createOfflineSpeakerDiarization, |
| 3 | getOfflineSpeakerDiarizationSampleRate, | 3 | getOfflineSpeakerDiarizationSampleRate, |
| 4 | offlineSpeakerDiarizationProcess, | 4 | offlineSpeakerDiarizationProcess, |
| 5 | + offlineSpeakerDiarizationProcessAsync, | ||
| 5 | offlineSpeakerDiarizationSetConfig, | 6 | offlineSpeakerDiarizationSetConfig, |
| 6 | } from 'libsherpa_onnx.so'; | 7 | } from 'libsherpa_onnx.so'; |
| 7 | 8 | ||
| @@ -32,8 +33,12 @@ export class OfflineSpeakerDiarizationConfig { | @@ -32,8 +33,12 @@ export class OfflineSpeakerDiarizationConfig { | ||
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | export class OfflineSpeakerDiarizationSegment { | 35 | export class OfflineSpeakerDiarizationSegment { |
| 35 | - public start: number = 0; // in secondspublic end: number = 0; // in secondspublic speaker: number = | ||
| 36 | - 0; // ID of the speaker; count from 0 | 36 | + // in seconds |
| 37 | + public start: number = 0; | ||
| 38 | + // in seconds | ||
| 39 | + public end: number = 0; | ||
| 40 | + // ID of the speaker; count from 0 | ||
| 41 | + public speaker: number = 0; | ||
| 37 | } | 42 | } |
| 38 | 43 | ||
| 39 | export class OfflineSpeakerDiarization { | 44 | export class OfflineSpeakerDiarization { |
| @@ -62,8 +67,14 @@ export class OfflineSpeakerDiarization { | @@ -62,8 +67,14 @@ export class OfflineSpeakerDiarization { | ||
| 62 | * "speaker": an_integer, | 67 | * "speaker": an_integer, |
| 63 | * } | 68 | * } |
| 64 | */ | 69 | */ |
| 65 | - process(samples: Float32Array): OfflineSpeakerDiarizationSegment { | ||
| 66 | - return offlineSpeakerDiarizationProcess(this.handle, samples) as OfflineSpeakerDiarizationSegment; | 70 | + process(samples: Float32Array): OfflineSpeakerDiarizationSegment[] { |
| 71 | + return offlineSpeakerDiarizationProcess(this.handle, samples) as OfflineSpeakerDiarizationSegment[]; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + processAsync(samples: Float32Array, callback: (numProcessedChunks: number, | ||
| 75 | + numTotalChunks: number) => void): Promise<OfflineSpeakerDiarizationSegment[]> { | ||
| 76 | + return offlineSpeakerDiarizationProcessAsync(this.handle, samples, | ||
| 77 | + callback) as Promise<OfflineSpeakerDiarizationSegment[]>; | ||
| 67 | } | 78 | } |
| 68 | 79 | ||
| 69 | setConfig(config: OfflineSpeakerDiarizationConfig) { | 80 | setConfig(config: OfflineSpeakerDiarizationConfig) { |
2.7 KB
| 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/SpeakerDiarizationWorker.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 | +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/SherpaOnnxSpeakerDiarization/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, promptAction } from '@kit.ArkUI'; | ||
| 2 | +import worker, { MessageEvents } from '@ohos.worker'; | ||
| 3 | +import { BusinessError, pasteboard } from '@kit.BasicServicesKit'; | ||
| 4 | +import { picker } from '@kit.CoreFileKit'; | ||
| 5 | + | ||
| 6 | + | ||
| 7 | +@Entry | ||
| 8 | +@Component | ||
| 9 | +struct Index { | ||
| 10 | + @State title: string = 'Next-gen Kaldi: Speaker Diarization'; | ||
| 11 | + @State titleFontSize: number = 15; | ||
| 12 | + @State currentIndex: number = 0; | ||
| 13 | + @State resultForFile: string = ''; | ||
| 14 | + @State resultForMic: string = ''; | ||
| 15 | + @State progressForFile: number = 0; | ||
| 16 | + @State selectFileBtnEnabled: boolean = false; | ||
| 17 | + @State copyBtnForFileEnabled: boolean = false; | ||
| 18 | + private controller: TabsController = new TabsController(); | ||
| 19 | + private workerInstance?: worker.ThreadWorker | ||
| 20 | + private readonly scriptURL: string = 'entry/ets/workers/SpeakerDiarizationWorker.ets' | ||
| 21 | + private numSpeakers: string = '-1'; | ||
| 22 | + | ||
| 23 | + @Builder | ||
| 24 | + TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) { | ||
| 25 | + Column() { | ||
| 26 | + Image(this.currentIndex == targetIndex ? selectedImg : normalImg).size({ width: 25, height: 25 }) | ||
| 27 | + Text(title).fontColor(this.currentIndex == targetIndex ? '#28bff1' : '#8a8a8a') | ||
| 28 | + }.width('100%').height(50).justifyContent(FlexAlign.Center).onClick(() => { | ||
| 29 | + this.currentIndex = targetIndex; | ||
| 30 | + this.controller.changeIndex(this.currentIndex); | ||
| 31 | + }) | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + aboutToAppear(): void { | ||
| 35 | + this.workerInstance = new worker.ThreadWorker(this.scriptURL, { | ||
| 36 | + name: 'Streaming ASR worker' | ||
| 37 | + }); | ||
| 38 | + | ||
| 39 | + this.workerInstance.onmessage = (e: MessageEvents) => { | ||
| 40 | + const msgType = e.data['msgType'] as string; | ||
| 41 | + | ||
| 42 | + if (msgType != 'speaker-diarization-file-progress') { | ||
| 43 | + console.log(`received msg from worker: ${msgType}`); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + if (msgType == 'init-speaker-diarization-done') { | ||
| 47 | + console.log('Speaker diarization initialized successfully'); | ||
| 48 | + | ||
| 49 | + this.resultForFile = 'Initialization finished.\nPlease select a .wav file.'; | ||
| 50 | + this.resultForMic = 'Initialization finished.\nPlease click the button Start recording.'; | ||
| 51 | + | ||
| 52 | + this.selectFileBtnEnabled = true; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + if (msgType == 'speaker-diarization-file-progress') { | ||
| 56 | + this.progressForFile = e.data['progress'] as number; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + if (msgType == 'speaker-diarization-file-done') { | ||
| 60 | + const result = e.data['result'] as string; | ||
| 61 | + this.resultForFile = result; | ||
| 62 | + | ||
| 63 | + this.selectFileBtnEnabled = true; | ||
| 64 | + this.copyBtnForFileEnabled = true; | ||
| 65 | + } | ||
| 66 | + }; | ||
| 67 | + | ||
| 68 | + const context = getContext(); | ||
| 69 | + this.workerInstance.postMessage({ msgType: 'init-speaker-diarization', context }); | ||
| 70 | + console.log('initializing'); | ||
| 71 | + this.resultForFile = 'Initializing models. Please wait'; | ||
| 72 | + this.resultForMic = this.resultForFile; | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + build() { | ||
| 76 | + Column() { | ||
| 77 | + Tabs({ barPosition: BarPosition.End, controller: this.controller }) { | ||
| 78 | + TabContent() { | ||
| 79 | + Column({ space: 10 }) { | ||
| 80 | + Text(this.title).fontSize(this.titleFontSize).fontWeight(FontWeight.Bold); | ||
| 81 | + Row({ space: 10 }) { | ||
| 82 | + Text(`Number of speakers`).width('60%') | ||
| 83 | + | ||
| 84 | + TextInput({ text: this.numSpeakers }).onChange((text) => { | ||
| 85 | + this.numSpeakers = text.trim(); | ||
| 86 | + }).width('20%') | ||
| 87 | + }.justifyContent(FlexAlign.Center) | ||
| 88 | + | ||
| 89 | + Row({ space: 10 }) { | ||
| 90 | + Button('Select .wav file (16kHz) ').enabled(this.selectFileBtnEnabled).onClick(() => { | ||
| 91 | + this.resultForFile = ''; | ||
| 92 | + this.progressForFile = 0; | ||
| 93 | + this.copyBtnForFileEnabled = false; | ||
| 94 | + | ||
| 95 | + let numSpeakers = parseInt(this.numSpeakers); | ||
| 96 | + if (numSpeakers.toString() != this.numSpeakers) { | ||
| 97 | + this.resultForFile = | ||
| 98 | + 'Please input a valid value for the number of speakers in the .wav file you are going to select'; | ||
| 99 | + return; | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + if (numSpeakers < 1) { | ||
| 103 | + this.resultForFile = | ||
| 104 | + 'Please input a positive value for the number of speakers in the .wav file you are going to select'; | ||
| 105 | + return; | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + this.selectFileBtnEnabled = false; | ||
| 109 | + | ||
| 110 | + const documentSelectOptions = new picker.DocumentSelectOptions(); | ||
| 111 | + documentSelectOptions.maxSelectNumber = 1; | ||
| 112 | + documentSelectOptions.fileSuffixFilters = ['.wav']; | ||
| 113 | + const documentViewPicker = new picker.DocumentViewPicker(); | ||
| 114 | + | ||
| 115 | + documentViewPicker.select(documentSelectOptions).then((result: Array<string>) => { | ||
| 116 | + console.log(`select file result: ${result}`); | ||
| 117 | + | ||
| 118 | + if (!result[0]) { | ||
| 119 | + this.resultForFile = 'Please select a file to decode'; | ||
| 120 | + this.selectFileBtnEnabled = true; | ||
| 121 | + return; | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + if (this.workerInstance) { | ||
| 125 | + this.workerInstance.postMessage({ | ||
| 126 | + msgType: 'speaker-diarization-file', filename: result[0], numSpeakers, | ||
| 127 | + }); | ||
| 128 | + this.resultForFile = `Decoding ${result[0]} ... ...`; | ||
| 129 | + } else { | ||
| 130 | + console.log(`this worker instance is undefined ${this.workerInstance}`); | ||
| 131 | + } | ||
| 132 | + }).catch((err: BusinessError) => { | ||
| 133 | + console.error(`Failed to select file, code is ${err.code}, message is ${err.message}`); | ||
| 134 | + this.selectFileBtnEnabled = true; | ||
| 135 | + }) | ||
| 136 | + }) | ||
| 137 | + Button('Copy results') | ||
| 138 | + .enabled(this.copyBtnForFileEnabled) | ||
| 139 | + .onClick(() => { // See https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-308-V5 | ||
| 140 | + const pasteboardData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, this.resultForFile); | ||
| 141 | + const systemPasteboard = pasteboard.getSystemPasteboard(); | ||
| 142 | + systemPasteboard.setData(pasteboardData); | ||
| 143 | + systemPasteboard.getData().then((data) => { | ||
| 144 | + if (data) { | ||
| 145 | + promptAction.showToast({ message: 'Result copied.' }); | ||
| 146 | + } else { | ||
| 147 | + promptAction.showToast({ message: 'Failed to copy' }); | ||
| 148 | + } | ||
| 149 | + }) | ||
| 150 | + }) | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + if (this.progressForFile > 0) { | ||
| 154 | + Row() { | ||
| 155 | + Progress({ value: 0, total: 100, type: ProgressType.Capsule }) | ||
| 156 | + .width('80%') | ||
| 157 | + .height(20) | ||
| 158 | + .value(this.progressForFile); | ||
| 159 | + | ||
| 160 | + Text(`${this.progressForFile.toFixed(2)}%`).width('15%') | ||
| 161 | + }.width('100%').justifyContent(FlexAlign.Center) | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + TextArea({ text: this.resultForFile }) | ||
| 165 | + .lineSpacing({ value: 10, unit: LengthUnit.VP }) | ||
| 166 | + .width('100%') | ||
| 167 | + .height('100%') | ||
| 168 | + } | ||
| 169 | + }.tabBar(this.TabBuilder('From file', 0, $r('app.media.icon_doc'), $r('app.media.icon_doc'))) | ||
| 170 | + | ||
| 171 | + TabContent() { | ||
| 172 | + Column({ space: 10 }) { | ||
| 173 | + Text(this.title).fontSize(this.titleFontSize).fontWeight(FontWeight.Bold); | ||
| 174 | + TextArea({ | ||
| 175 | + text: ` | ||
| 176 | +Everyting is open-sourced. | ||
| 177 | + | ||
| 178 | +It runs locally, without accessing the network | ||
| 179 | + | ||
| 180 | +See also https://github.com/k2-fsa/sherpa-onnx | ||
| 181 | + | ||
| 182 | +新一代 Kaldi QQ 和微信交流群: 请看 | ||
| 183 | + | ||
| 184 | +https://k2-fsa.github.io/sherpa/social-groups.html | ||
| 185 | + | ||
| 186 | +微信公众号: 新一代 Kaldi | ||
| 187 | + ` | ||
| 188 | + }).width('100%').height('100%').focusable(false) | ||
| 189 | + }.justifyContent(FlexAlign.Start) | ||
| 190 | + }.tabBar(this.TabBuilder('Help', 1, $r('app.media.info'), $r('app.media.info'))) | ||
| 191 | + }.scrollable(false) | ||
| 192 | + } | ||
| 193 | + } | ||
| 194 | +} |
harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/ets/workers/SpeakerDiarizationWorker.ets
0 → 100644
| 1 | +import worker, { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope } from '@ohos.worker'; | ||
| 2 | +import { | ||
| 3 | + OfflineSpeakerDiarization, | ||
| 4 | + OfflineSpeakerDiarizationConfig, | ||
| 5 | + OfflineSpeakerDiarizationSegment, | ||
| 6 | + readWaveFromBinary, | ||
| 7 | + Samples | ||
| 8 | +} from 'sherpa_onnx'; | ||
| 9 | +import { fileIo } from '@kit.CoreFileKit'; | ||
| 10 | + | ||
| 11 | +const workerPort: ThreadWorkerGlobalScope = worker.workerPort; | ||
| 12 | + | ||
| 13 | +let sd: OfflineSpeakerDiarization; | ||
| 14 | +let useAsync: boolean = true; | ||
| 15 | + | ||
| 16 | +function readWave(filename: string): Samples { | ||
| 17 | + const fp = fileIo.openSync(filename); | ||
| 18 | + const stat = fileIo.statSync(fp.fd); | ||
| 19 | + const arrayBuffer = new ArrayBuffer(stat.size); | ||
| 20 | + fileIo.readSync(fp.fd, arrayBuffer); | ||
| 21 | + const data: Uint8Array = new Uint8Array(arrayBuffer); | ||
| 22 | + return readWaveFromBinary(data) as Samples; | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +function initOfflineSpeakerDiarization(context: Context): OfflineSpeakerDiarization { | ||
| 26 | + const config: OfflineSpeakerDiarizationConfig = new OfflineSpeakerDiarizationConfig(); | ||
| 27 | + | ||
| 28 | + // Please refer to https://github.com/k2-fsa/sherpa-onnx/releases/tag/speaker-segmentation-models | ||
| 29 | + // to download models. | ||
| 30 | + // Make sure you have placed it inside the directory | ||
| 31 | + // harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/rawfile | ||
| 32 | + // | ||
| 33 | + // Also, please delete unused files to reduce the size of the app | ||
| 34 | + config.segmentation.pyannote.model = 'sherpa-onnx-pyannote-segmentation-3-0/model.int8.onnx'; | ||
| 35 | + config.segmentation.numThreads = 2; | ||
| 36 | + config.segmentation.debug = true; | ||
| 37 | + | ||
| 38 | + // Please refer to https://github.com/k2-fsa/sherpa-onnx/releases/tag/speaker-recongition-models | ||
| 39 | + // to download models. | ||
| 40 | + // Make sure you have placed it inside the directory | ||
| 41 | + // harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/rawfile | ||
| 42 | + config.embedding.model = '3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx'; | ||
| 43 | + config.embedding.numThreads = 2; | ||
| 44 | + config.embedding.debug = true; | ||
| 45 | + | ||
| 46 | + config.minDurationOn = 0.2; | ||
| 47 | + config.minDurationOff = 0.5; | ||
| 48 | + return new OfflineSpeakerDiarization(config, context.resourceManager); | ||
| 49 | + | ||
| 50 | + // For the above two models files, you should have the following directory structure | ||
| 51 | + /* | ||
| 52 | + (py38) fangjuns-MacBook-Pro:rawfile fangjun$ pwd | ||
| 53 | + /Users/fangjun/open-source/sherpa-onnx/harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/rawfile | ||
| 54 | + (py38) fangjuns-MacBook-Pro:rawfile fangjun$ ls -lh | ||
| 55 | + total 77336 | ||
| 56 | + -rw-r--r-- 1 fangjun staff 38M Dec 10 16:28 3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx | ||
| 57 | + drwxr-xr-x 3 fangjun staff 96B Dec 10 19:36 sherpa-onnx-pyannote-segmentation-3-0 | ||
| 58 | + (py38) fangjuns-MacBook-Pro:rawfile fangjun$ tree . | ||
| 59 | + . | ||
| 60 | + ├── 3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx | ||
| 61 | + └── sherpa-onnx-pyannote-segmentation-3-0 | ||
| 62 | + └── model.int8.onnx | ||
| 63 | + | ||
| 64 | + 1 directory, 2 files | ||
| 65 | + | ||
| 66 | + (Note that we have kept only model.int8.onnx and removed all other files | ||
| 67 | + from sherpa-onnx-pyannote-segmentation-3-0 | ||
| 68 | + ) | ||
| 69 | + */ | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +/** | ||
| 73 | + * Defines the event handler to be called when the worker thread receives a message sent by the host thread. | ||
| 74 | + * The event handler is executed in the worker thread. | ||
| 75 | + * | ||
| 76 | + * @param e message data | ||
| 77 | + */ | ||
| 78 | +workerPort.onmessage = (e: MessageEvents) => { | ||
| 79 | + const msgType = e.data['msgType'] as string; | ||
| 80 | + | ||
| 81 | + console.log(`from the main thread, msg-type: ${msgType}`); | ||
| 82 | + if (msgType == 'init-speaker-diarization' && !sd) { | ||
| 83 | + const context: Context = e.data['context'] as Context; | ||
| 84 | + sd = initOfflineSpeakerDiarization(context); | ||
| 85 | + workerPort.postMessage({ msgType: 'init-speaker-diarization-done' }); | ||
| 86 | + console.log('Init sd done'); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + if (msgType == 'speaker-diarization-file') { | ||
| 90 | + const filename = e.data['filename'] as string; | ||
| 91 | + const numSpeakers = e.data['numSpeakers'] as number; | ||
| 92 | + const wave = readWave(filename); | ||
| 93 | + let result = ''; | ||
| 94 | + if (wave == undefined || wave == null) { | ||
| 95 | + result = `Failed to read ${filename}`; | ||
| 96 | + | ||
| 97 | + workerPort.postMessage({ | ||
| 98 | + msgType: 'speaker-diarization-file-done', result | ||
| 99 | + }); | ||
| 100 | + return; | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + if (wave.sampleRate != sd.sampleRate) { | ||
| 104 | + result = `Expected sample rate: ${sd.sampleRate}`; | ||
| 105 | + result += '\n'; | ||
| 106 | + result += `Sample rate in file ${filename} is ${wave.sampleRate}`; | ||
| 107 | + | ||
| 108 | + workerPort.postMessage({ | ||
| 109 | + msgType: 'speaker-diarization-file-done', result | ||
| 110 | + }); | ||
| 111 | + | ||
| 112 | + return; | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + const duration = wave.samples.length / wave.sampleRate; | ||
| 116 | + console.log(`Processing ${filename} of ${duration} seconds`); | ||
| 117 | + | ||
| 118 | + // You can remove this if statement if you want | ||
| 119 | + if (duration < 0.3) { | ||
| 120 | + result = `${filename} has only ${duration} seconds. Please use a longer file`; | ||
| 121 | + | ||
| 122 | + workerPort.postMessage({ | ||
| 123 | + msgType: 'speaker-diarization-file-done', result | ||
| 124 | + }); | ||
| 125 | + return; | ||
| 126 | + } | ||
| 127 | + sd.config.clustering.numClusters = numSpeakers; | ||
| 128 | + sd.setConfig(sd.config); | ||
| 129 | + | ||
| 130 | + if (useAsync) { | ||
| 131 | + sd.processAsync(wave.samples, (numProcessedChunks: number, numTotalChunks: number) => { | ||
| 132 | + const progress = numProcessedChunks / numTotalChunks * 100; | ||
| 133 | + workerPort.postMessage({ | ||
| 134 | + msgType: 'speaker-diarization-file-progress', progress | ||
| 135 | + }); | ||
| 136 | + }).then((r: OfflineSpeakerDiarizationSegment[]) => { | ||
| 137 | + console.log(`r is ${r.length}, ${r}`); | ||
| 138 | + | ||
| 139 | + for (const s of r) { | ||
| 140 | + const start: string = s.start.toFixed(3); | ||
| 141 | + const end: string = s.end.toFixed(3); | ||
| 142 | + result += `${start}\t--\t${end}\tspeaker_${s.speaker}\n`; | ||
| 143 | + console.log(`result: ${result}`); | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + if (r.length == 0) { | ||
| 147 | + result = 'The result is empty'; | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + workerPort.postMessage({ | ||
| 151 | + msgType: 'speaker-diarization-file-done', result | ||
| 152 | + }); | ||
| 153 | + }); | ||
| 154 | + } else { | ||
| 155 | + const r: OfflineSpeakerDiarizationSegment[] = sd.process(wave.samples) | ||
| 156 | + console.log(`r is ${r.length}, ${r}`); | ||
| 157 | + for (const s of r) { | ||
| 158 | + const start: string = s.start.toFixed(3); | ||
| 159 | + const end: string = s.end.toFixed(3); | ||
| 160 | + result += `${start}\t--\t${end}\tspeaker_${s.speaker}\n`; | ||
| 161 | + console.log(`result: ${result}`); | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + if (r.length == 0) { | ||
| 165 | + result = 'The result is empty'; | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + workerPort.postMessage({ | ||
| 169 | + msgType: 'speaker-diarization-file-done', result | ||
| 170 | + }); | ||
| 171 | + } | ||
| 172 | + } | ||
| 173 | +} /** | ||
| 174 | + * Defines the event handler to be called when the worker receives a message that cannot be deserialized. | ||
| 175 | + * The event handler is executed in the worker thread. | ||
| 176 | + * | ||
| 177 | + * @param e message data | ||
| 178 | + */ | ||
| 179 | +workerPort.onmessageerror = (e: MessageEvents) => { | ||
| 180 | +} | ||
| 181 | + | ||
| 182 | +/** | ||
| 183 | + * Defines the event handler to be called when an exception occurs during worker execution. | ||
| 184 | + * The event handler is executed in the worker thread. | ||
| 185 | + * | ||
| 186 | + * @param e error message | ||
| 187 | + */ | ||
| 188 | +workerPort.onerror = (e: ErrorEvent) => { | ||
| 189 | +} |
| 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 | + } | ||
| 52 | +} |
harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/element/string.json
0 → 100644
| 1 | +{ | ||
| 2 | + "string": [ | ||
| 3 | + { | ||
| 4 | + "name": "module_desc", | ||
| 5 | + "value": "On-device speaker diarization with Next-gen Kaldi" | ||
| 6 | + }, | ||
| 7 | + { | ||
| 8 | + "name": "EntryAbility_desc", | ||
| 9 | + "value": "On-device speaker diarization with Next-gen Kaldi" | ||
| 10 | + }, | ||
| 11 | + { | ||
| 12 | + "name": "EntryAbility_label", | ||
| 13 | + "value": "Speaker diarization" | ||
| 14 | + } | ||
| 15 | + ] | ||
| 16 | +} |
harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/background.png
0 → 100644
56.0 KB
harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/foreground.png
0 → 100644
12.1 KB
| 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> |
harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/base/media/startIcon.png
0 → 100644
19.6 KB
harmony-os/SherpaOnnxSpeakerDiarization/entry/src/main/resources/en_US/element/string.json
0 → 100644
| 1 | +{ | ||
| 2 | + "string": [ | ||
| 3 | + { | ||
| 4 | + "name": "module_desc", | ||
| 5 | + "value": "On-device speaker diarization with Next-gen Kaldi" | ||
| 6 | + }, | ||
| 7 | + { | ||
| 8 | + "name": "EntryAbility_desc", | ||
| 9 | + "value": "On-device speaker diarization with Next-gen Kaldi" | ||
| 10 | + }, | ||
| 11 | + { | ||
| 12 | + "name": "EntryAbility_label", | ||
| 13 | + "value": "Speaker diarization" | ||
| 14 | + } | ||
| 15 | + ] | ||
| 16 | +} |
| 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 | +} |
| @@ -2053,11 +2053,6 @@ SherpaOnnxCreateOfflineSpeakerDiarizationOHOS( | @@ -2053,11 +2053,6 @@ SherpaOnnxCreateOfflineSpeakerDiarizationOHOS( | ||
| 2053 | 2053 | ||
| 2054 | auto sd_config = GetOfflineSpeakerDiarizationConfig(config); | 2054 | auto sd_config = GetOfflineSpeakerDiarizationConfig(config); |
| 2055 | 2055 | ||
| 2056 | - if (!sd_config.Validate()) { | ||
| 2057 | - SHERPA_ONNX_LOGE("Errors in config"); | ||
| 2058 | - return nullptr; | ||
| 2059 | - } | ||
| 2060 | - | ||
| 2061 | SherpaOnnxOfflineSpeakerDiarization *sd = | 2056 | SherpaOnnxOfflineSpeakerDiarization *sd = |
| 2062 | new SherpaOnnxOfflineSpeakerDiarization; | 2057 | new SherpaOnnxOfflineSpeakerDiarization; |
| 2063 | 2058 |
| @@ -1512,10 +1512,10 @@ SHERPA_ONNX_API void SherpaOnnxOfflineSpeakerDiarizationDestroySegment( | @@ -1512,10 +1512,10 @@ SHERPA_ONNX_API void SherpaOnnxOfflineSpeakerDiarizationDestroySegment( | ||
| 1512 | const SherpaOnnxOfflineSpeakerDiarizationSegment *s); | 1512 | const SherpaOnnxOfflineSpeakerDiarizationSegment *s); |
| 1513 | 1513 | ||
| 1514 | typedef int32_t (*SherpaOnnxOfflineSpeakerDiarizationProgressCallback)( | 1514 | typedef int32_t (*SherpaOnnxOfflineSpeakerDiarizationProgressCallback)( |
| 1515 | - int32_t num_processed_chunk, int32_t num_total_chunks, void *arg); | 1515 | + int32_t num_processed_chunks, int32_t num_total_chunks, void *arg); |
| 1516 | 1516 | ||
| 1517 | typedef int32_t (*SherpaOnnxOfflineSpeakerDiarizationProgressCallbackNoArg)( | 1517 | typedef int32_t (*SherpaOnnxOfflineSpeakerDiarizationProgressCallbackNoArg)( |
| 1518 | - int32_t num_processed_chunk, int32_t num_total_chunks); | 1518 | + int32_t num_processed_chunks, int32_t num_total_chunks); |
| 1519 | 1519 | ||
| 1520 | // The user has to invoke SherpaOnnxOfflineSpeakerDiarizationDestroyResult() | 1520 | // The user has to invoke SherpaOnnxOfflineSpeakerDiarizationDestroyResult() |
| 1521 | // to free the returned pointer to avoid memory leak. | 1521 | // to free the returned pointer to avoid memory leak. |
-
请 注册 或 登录 后发表评论