From 5251bc6a12a40635999644ed5879238de0fcba74 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 Feb 2024 12:06:06 +0100 Subject: [PATCH 1/9] first cut worker reuse --- index.ts | 21 +++--- src/main.cc | 155 ++++++++++++++++++++++++++------------------- test/basic.test.ts | 1 + 3 files changed, 100 insertions(+), 77 deletions(-) diff --git a/index.ts b/index.ts index d0af7a0..ac2f3dc 100644 --- a/index.ts +++ b/index.ts @@ -11,6 +11,7 @@ interface SpeechLib { createTranscriber: (modelPath: string, modelName: string, modelKey: string, wavPath: string | undefined, logsPath: string | undefined, callback: (error: Error | undefined, result: ITranscriptionResult) => void) => number, startTranscriber: (id: number) => void, stopTranscriber: (id: number) => void, + disposeTranscriber: (id: number) => void, // Keyword Recognition recognize: (modelPath: string, callback: (error: Error | undefined, result: IKeywordRecognitionResult) => void) => number, @@ -29,7 +30,8 @@ export enum TranscriptionStatusCode { SPEECH_START_DETECTED = 7, SPEECH_END_DETECTED = 8, STOPPED = 9, - ERROR = 10 + DISPOSED = 10, + ERROR = 11 } export interface ITranscriptionResult { @@ -56,28 +58,21 @@ export interface ITranscriptionOptions { * Path to a file to store verbose logs from the Azure Speech SDK to. */ readonly logsPath?: string; - - readonly signal: AbortSignal; } export interface ITranscriber { start(): void; stop(): void; + dispose(): void; } -export function createTranscriber({ modelPath, modelName, modelKey, signal, wavPath, logsPath }: ITranscriptionOptions, callback: ITranscriptionCallback): ITranscriber { +export function createTranscriber({ modelPath, modelName, modelKey, wavPath, logsPath }: ITranscriptionOptions, callback: ITranscriptionCallback): ITranscriber { const id = speechapi.createTranscriber(modelPath, modelName, modelKey, wavPath ?? undefined, logsPath ?? undefined, callback); - const onAbort = () => { - speechapi.stopTranscriber(id); - signal.removeEventListener('abort', onAbort); - }; - - signal.addEventListener('abort', onAbort); - return { start: () => speechapi.startTranscriber(id), - stop: () => speechapi.stopTranscriber(id) + stop: () => speechapi.stopTranscriber(id), + dispose: () => speechapi.disposeTranscriber(id) }; } @@ -88,7 +83,7 @@ export function createTranscriber({ modelPath, modelName, modelKey, signal, wavP export enum KeywordRecognitionStatusCode { RECOGNIZED = 3, STOPPED = 9, - ERROR = 10 + ERROR = 11 } export interface IKeywordRecognitionResult { diff --git a/src/main.cc b/src/main.cc index 499e2fa..3644a50 100644 --- a/src/main.cc +++ b/src/main.cc @@ -23,35 +23,46 @@ enum StatusCode SPEECH_START_DETECTED = 7, SPEECH_END_DETECTED = 8, STOPPED = 9, - ERROR = 10 + DISPOSED = 10, + ERROR = 11 +}; + +enum RuntimeStatus +{ + START = 1, + STOP = 2, + DISPOSE = 3 }; #pragma region Transcription static int transcriptionWorkerIds = 0; -static std::unordered_map> waitingToStartTranscriptionWorkers; -static std::unordered_map> waitingToStopTranscriptionWorkers; +static std::unordered_map transcriptionWorkers; static std::mutex transcriptionWorkersMutex; -void StartTranscriptionWorker(int workerId) +void UpdateTranscriptionWorkerStatus(int workerId, RuntimeStatus status) { std::lock_guard lock(transcriptionWorkersMutex); - auto waitingToStartTranscriptionWorker = waitingToStartTranscriptionWorkers.find(workerId); - if (waitingToStartTranscriptionWorker != waitingToStartTranscriptionWorkers.end()) - { - waitingToStartTranscriptionWorker->second.set_value(); - waitingToStartTranscriptionWorkers.erase(waitingToStartTranscriptionWorker); - } + transcriptionWorkers[workerId] = status; +} + +void RemoveTranscriptionWorkerStatus(int workerId) +{ + std::lock_guard lock(transcriptionWorkersMutex); + transcriptionWorkers.erase(workerId); } -void StopTranscriptionWorker(int workerId) +RuntimeStatus GetTranscriptionWorkerStatus(int workerId) { std::lock_guard lock(transcriptionWorkersMutex); - auto waitingToStopTranscriptionWorker = waitingToStopTranscriptionWorkers.find(workerId); - if (waitingToStopTranscriptionWorker != waitingToStopTranscriptionWorkers.end()) + auto it = transcriptionWorkers.find(workerId); + if (it != transcriptionWorkers.end()) + { + return it->second; + } + else { - waitingToStopTranscriptionWorker->second.set_value(); - waitingToStopTranscriptionWorkers.erase(waitingToStopTranscriptionWorker); + return RuntimeStatus::DISPOSE; } } @@ -67,14 +78,9 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorker(callback), id(transcriptionWorkerIds++), path(path), key(key), model(model), wavPath(wavPath), logsPath(logsPath) + : Napi::AsyncProgressQueueWorker(callback), id(transcriptionWorkerIds++), path(path), key(key), model(model), wavPath(wavPath), logsPath(logsPath), started(false) { - std::lock_guard lock(transcriptionWorkersMutex); - waitingToStartTranscriptionWorkers[this->id] = std::promise(); - waitingToStopTranscriptionWorkers[this->id] = std::promise(); - - this->waitingToStart = waitingToStartTranscriptionWorkers[this->id].get_future(); - this->waitingToStop = waitingToStopTranscriptionWorkers[this->id].get_future(); + UpdateTranscriptionWorkerStatus(this->id, RuntimeStatus::START); } void Execute(const ExecutionProgress &progress) @@ -201,18 +207,46 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorkerSessionStopped += [this](const SessionEventArgs &e) + recognizer->SessionStopped += [progress](const SessionEventArgs &e) { UNUSED(e); - StopTranscriptionWorker(this->id); + auto result = TranscriptionWorkerCallbackResult{StatusCode::STOPPED}; + progress.Send(&result, 1); }; - // Start/stop of the worker is guarded with a barrier to allow - // that this can be called from the outside. - this->waitingToStart.get(); - recognizer->StartContinuousRecognitionAsync().get(); - this->waitingToStop.get(); - recognizer->StopContinuousRecognitionAsync().get(); + RuntimeStatus status; + while ((status = GetTranscriptionWorkerStatus(this->id)) != RuntimeStatus::DISPOSE) + { + switch (status) + { + case RuntimeStatus::START: + if (!this->started) + { + recognizer->StartContinuousRecognitionAsync().get(); + this->started = true; + } + break; + case RuntimeStatus::STOP: + if (this->started) + { + recognizer->StopContinuousRecognitionAsync().get(); + this->started = false; + } + break; + case RuntimeStatus::DISPOSE: + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + if (this->started) + { + recognizer->StopContinuousRecognitionAsync().get(); + this->started = false; + } + + RemoveTranscriptionWorkerStatus(this->id); } catch (const std::exception &e) { @@ -225,7 +259,7 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorkerstatus)); if (!result->data.empty()) { @@ -239,8 +273,8 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorker waitingToStart; - std::future waitingToStop; + bool started; }; Napi::Value CreateTranscriber(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); + auto env = info.Env(); // Validate args if (info.Length() != 6) @@ -295,7 +328,7 @@ Napi::Value CreateTranscriber(const Napi::CallbackInfo &info) try { - TranscriptionWorker *worker = new TranscriptionWorker(modelPath, modelKey, modelName, wavPath, logsPath, callback); + auto *worker = new TranscriptionWorker(modelPath, modelKey, modelName, wavPath, logsPath, callback); worker->Queue(); return Napi::Number::New(env, worker->id); @@ -307,9 +340,9 @@ Napi::Value CreateTranscriber(const Napi::CallbackInfo &info) } } -Napi::Value StartTranscriber(const Napi::CallbackInfo &info) +Napi::Value UpdateTranscriber(const Napi::CallbackInfo &info, RuntimeStatus status) { - Napi::Env env = info.Env(); + auto env = info.Env(); // Validate args if (info.Length() < 1) @@ -323,32 +356,25 @@ Napi::Value StartTranscriber(const Napi::CallbackInfo &info) return env.Undefined(); } - Napi::Number workerId = info[0].As(); - StartTranscriptionWorker(workerId.Int32Value()); + auto workerId = info[0].As(); + UpdateTranscriptionWorkerStatus(workerId.Int32Value(), status); return env.Undefined(); } -Napi::Value StopTranscriber(const Napi::CallbackInfo &info) +Napi::Value StartTranscriber(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - - // Validate args - if (info.Length() < 1) - { - Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException(); - return env.Undefined(); - } - else if (!info[0].IsNumber()) - { - Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException(); - return env.Undefined(); - } + return UpdateTranscriber(info, RuntimeStatus::START); +} - Napi::Number workerId = info[0].As(); - StopTranscriptionWorker(workerId.Int32Value()); +Napi::Value StopTranscriber(const Napi::CallbackInfo &info) +{ + return UpdateTranscriber(info, RuntimeStatus::STOP); +} - return env.Undefined(); +Napi::Value DisposeTranscriber(const Napi::CallbackInfo &info) +{ + return UpdateTranscriber(info, RuntimeStatus::DISPOSE); } #pragma endregion @@ -447,7 +473,7 @@ class KeywordWorker : public Napi::AsyncProgressQueueWorkerstatus)); if (!result->data.empty()) { @@ -461,7 +487,7 @@ class KeywordWorker : public Napi::AsyncProgressQueueWorkerQueue(); return Napi::Number::New(env, worker->id); @@ -514,7 +540,7 @@ Napi::Value Recognize(const Napi::CallbackInfo &info) Napi::Value Unrecognize(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); + auto env = info.Env(); // Validate args if (info.Length() < 1) @@ -528,7 +554,7 @@ Napi::Value Unrecognize(const Napi::CallbackInfo &info) return env.Undefined(); } - Napi::Number workerId = info[0].As(); + auto workerId = info[0].As(); StopKeywordWorker(workerId.Int32Value()); return env.Undefined(); @@ -541,6 +567,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) exports.Set(Napi::String::New(env, "createTranscriber"), Napi::Function::New(env, CreateTranscriber)); exports.Set(Napi::String::New(env, "startTranscriber"), Napi::Function::New(env, StartTranscriber)); exports.Set(Napi::String::New(env, "stopTranscriber"), Napi::Function::New(env, StopTranscriber)); + exports.Set(Napi::String::New(env, "disposeTranscriber"), Napi::Function::New(env, DisposeTranscriber)); exports.Set(Napi::String::New(env, "recognize"), Napi::Function::New(env, Recognize)); exports.Set(Napi::String::New(env, "unrecognize"), Napi::Function::New(env, Unrecognize)); diff --git a/test/basic.test.ts b/test/basic.test.ts index db74b6b..94ac21c 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -12,6 +12,7 @@ describe('Basics', () => { createTranscriber: expect.any(Function), startTranscriber: expect.any(Function), stopTranscriber: expect.any(Function), + disposeTranscriber: expect.any(Function), recognize: expect.any(Function), unrecognize: expect.any(Function) })); From cfa7dbdf2187e99511f8200018c0ace176f9fffc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 Feb 2024 15:27:27 +0100 Subject: [PATCH 2/9] . --- src/main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cc b/src/main.cc index 3644a50..7cce33f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -80,7 +80,7 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorker(callback), id(transcriptionWorkerIds++), path(path), key(key), model(model), wavPath(wavPath), logsPath(logsPath), started(false) { - UpdateTranscriptionWorkerStatus(this->id, RuntimeStatus::START); + UpdateTranscriptionWorkerStatus(this->id, RuntimeStatus::STOP); } void Execute(const ExecutionProgress &progress) From 1a7778e84d4e7ae9818bdd19d35e001cab3cfcaa Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 Feb 2024 15:42:10 +0100 Subject: [PATCH 3/9] remove `wavPath` --- README.md | 12 +++--------- index.ts | 12 +++--------- src/main.cc | 26 ++++++-------------------- 3 files changed, 12 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 2ee507a..0d5ec08 100644 --- a/README.md +++ b/README.md @@ -23,21 +23,15 @@ const modelKey = ""; // Live transcription from microphone let transcriber = speech.createTranscriber( - { modelName, modelPath, modelKey, signal, wavPath: undefined }, + { modelName, modelPath, modelKey }, (err, res) => console.log(err, res) ); transcriber.start(); -// later when done... +// you can stop/start later transcriber.stop(); - -// Transcription from *.wav file -transcriber = speech.createTranscriber( - { modelName, modelPath, modelKey, signal, wavPath: "path-to-wav-file" }, - (err, res) => console.log(err, res) -); transcriber.start(); // later when done... -transcriber.stop(); +transcriber.dispose(); ``` ## Usage: Keyword Recognition diff --git a/index.ts b/index.ts index ac2f3dc..c54ad08 100644 --- a/index.ts +++ b/index.ts @@ -8,7 +8,7 @@ export const speechapi = require('bindings')('speechapi.node') as SpeechLib; interface SpeechLib { // Transcription - createTranscriber: (modelPath: string, modelName: string, modelKey: string, wavPath: string | undefined, logsPath: string | undefined, callback: (error: Error | undefined, result: ITranscriptionResult) => void) => number, + createTranscriber: (modelPath: string, modelName: string, modelKey: string, logsPath: string | undefined, callback: (error: Error | undefined, result: ITranscriptionResult) => void) => number, startTranscriber: (id: number) => void, stopTranscriber: (id: number) => void, disposeTranscriber: (id: number) => void, @@ -48,12 +48,6 @@ export interface ITranscriptionOptions { readonly modelName: string; readonly modelKey: string; - /** - * Path to the wav file to transcribe. If not specified, the audio - * will be streamed from the microphone. - */ - readonly wavPath?: string; - /** * Path to a file to store verbose logs from the Azure Speech SDK to. */ @@ -66,8 +60,8 @@ export interface ITranscriber { dispose(): void; } -export function createTranscriber({ modelPath, modelName, modelKey, wavPath, logsPath }: ITranscriptionOptions, callback: ITranscriptionCallback): ITranscriber { - const id = speechapi.createTranscriber(modelPath, modelName, modelKey, wavPath ?? undefined, logsPath ?? undefined, callback); +export function createTranscriber({ modelPath, modelName, modelKey, logsPath }: ITranscriptionOptions, callback: ITranscriptionCallback): ITranscriber { + const id = speechapi.createTranscriber(modelPath, modelName, modelKey, logsPath ?? undefined, callback); return { start: () => speechapi.startTranscriber(id), diff --git a/src/main.cc b/src/main.cc index 7cce33f..d494706 100644 --- a/src/main.cc +++ b/src/main.cc @@ -77,8 +77,8 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorker(callback), id(transcriptionWorkerIds++), path(path), key(key), model(model), wavPath(wavPath), logsPath(logsPath), started(false) + TranscriptionWorker(std::string &path, std::string &key, std::string &model, std::string &logsPath, Napi::Function &callback) + : Napi::AsyncProgressQueueWorker(callback), id(transcriptionWorkerIds++), path(path), key(key), model(model), logsPath(logsPath), started(false) { UpdateTranscriptionWorkerStatus(this->id, RuntimeStatus::STOP); } @@ -94,15 +94,7 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorkerSetProperty(PropertyId::Speech_LogFilename, logsPath); } - std::shared_ptr audioConfig; - if (this->wavPath.empty()) - { - audioConfig = AudioConfig::FromDefaultMicrophoneInput(); - } - else - { - audioConfig = AudioConfig::FromWavFileInput(this->wavPath); - } + auto audioConfig = AudioConfig::FromDefaultMicrophoneInput(); auto recognizer = SpeechRecognizer::FromConfig(speechConfig, audioConfig); // Callback: intermediate transcription results @@ -290,7 +282,6 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorker().Utf8Value(); auto modelName = info[1].As().Utf8Value(); auto modelKey = info[2].As().Utf8Value(); - std::string wavPath; - if (!info[3].IsUndefined()) - { - wavPath = info[3].As().Utf8Value(); - } std::string logsPath; if (!info[4].IsUndefined()) { @@ -328,7 +314,7 @@ Napi::Value CreateTranscriber(const Napi::CallbackInfo &info) try { - auto *worker = new TranscriptionWorker(modelPath, modelKey, modelName, wavPath, logsPath, callback); + auto *worker = new TranscriptionWorker(modelPath, modelKey, modelName, logsPath, callback); worker->Queue(); return Napi::Number::New(env, worker->id); From 7e5eb98983e92ec4c1d4a530ff4a4fbf7ee67704 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 Feb 2024 15:48:38 +0100 Subject: [PATCH 4/9] fix --- src/main.cc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main.cc b/src/main.cc index d494706..2971b8f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -175,8 +175,10 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorkerSessionStarted += [progress](const SessionEventArgs &e) + recognizer->SessionStarted += [this, progress](const SessionEventArgs &e) { + this->started = true; + UNUSED(e); auto result = TranscriptionWorkerCallbackResult{StatusCode::STARTED}; progress.Send(&result, 1); @@ -199,8 +201,10 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorkerSessionStopped += [progress](const SessionEventArgs &e) + recognizer->SessionStopped += [this, progress](const SessionEventArgs &e) { + this->started = false; + UNUSED(e); auto result = TranscriptionWorkerCallbackResult{StatusCode::STOPPED}; progress.Send(&result, 1); @@ -215,14 +219,12 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorkerstarted) { recognizer->StartContinuousRecognitionAsync().get(); - this->started = true; } break; case RuntimeStatus::STOP: if (this->started) { recognizer->StopContinuousRecognitionAsync().get(); - this->started = false; } break; case RuntimeStatus::DISPOSE: @@ -235,7 +237,6 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorkerstarted) { recognizer->StopContinuousRecognitionAsync().get(); - this->started = false; } RemoveTranscriptionWorkerStatus(this->id); @@ -306,11 +307,11 @@ Napi::Value CreateTranscriber(const Napi::CallbackInfo &info) auto modelName = info[1].As().Utf8Value(); auto modelKey = info[2].As().Utf8Value(); std::string logsPath; - if (!info[4].IsUndefined()) + if (!info[3].IsUndefined()) { - logsPath = info[4].As().Utf8Value(); + logsPath = info[3].As().Utf8Value(); } - auto callback = info[5].As(); + auto callback = info[4].As(); try { From b9b224f2aeffb884f122ba20b4c1156b16f2ec20 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 Feb 2024 16:32:30 +0100 Subject: [PATCH 5/9] . --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 26ab686..7964f96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@vscode/node-speech", - "version": "1.2.3", + "version": "1.2.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@vscode/node-speech", - "version": "1.2.3", + "version": "1.2.4", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 7e8f07b..6cdc655 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vscode/node-speech", - "version": "1.2.3", + "version": "1.2.4", "description": "Native bindings for Micrsooft Speech SDK", "repository": { "type": "git", From 3bdf924988e41d3cd4520699da18c493fc46d366 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 Feb 2024 16:34:03 +0100 Subject: [PATCH 6/9] . --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7964f96..9def90f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@vscode/node-speech", - "version": "1.2.4", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@vscode/node-speech", - "version": "1.2.4", + "version": "1.3.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 6cdc655..35bbc55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vscode/node-speech", - "version": "1.2.4", + "version": "1.3.0", "description": "Native bindings for Micrsooft Speech SDK", "repository": { "type": "git", From 1cd622af875f5d07152879e21e0a0770defd480f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 Feb 2024 17:12:35 +0100 Subject: [PATCH 7/9] . --- src/main.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.cc b/src/main.cc index 2971b8f..67b5d62 100644 --- a/src/main.cc +++ b/src/main.cc @@ -8,6 +8,7 @@ #include #include +#include using namespace Microsoft::CognitiveServices::Speech; using namespace Microsoft::CognitiveServices::Speech::Audio; From db1204db17a833cf0590325829b4815716cb35e3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 Feb 2024 17:26:41 +0100 Subject: [PATCH 8/9] support phrases --- index.ts | 19 +++++++++++++++---- src/main.cc | 25 +++++++++++++++++++------ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/index.ts b/index.ts index c54ad08..768586c 100644 --- a/index.ts +++ b/index.ts @@ -8,7 +8,7 @@ export const speechapi = require('bindings')('speechapi.node') as SpeechLib; interface SpeechLib { // Transcription - createTranscriber: (modelPath: string, modelName: string, modelKey: string, logsPath: string | undefined, callback: (error: Error | undefined, result: ITranscriptionResult) => void) => number, + createTranscriber: (modelPath: string, modelName: string, modelKey: string, logsPath: string | undefined, phrases: string[], callback: (error: Error | undefined, result: ITranscriptionResult) => void) => number, startTranscriber: (id: number) => void, stopTranscriber: (id: number) => void, disposeTranscriber: (id: number) => void, @@ -49,9 +49,20 @@ export interface ITranscriptionOptions { readonly modelKey: string; /** - * Path to a file to store verbose logs from the Azure Speech SDK to. + * The path to a file to store verbose logs from the Azure Speech SDK to. + * + * @see https://learn.microsoft.com/en-us/azure/ai-services/speech-service/how-to-use-logging */ readonly logsPath?: string; + + /** + * A phrase list is a list of words or phrases provided ahead of time to help + * improve their recognition. Adding a phrase to a phrase list increases its + * importance, thus making it more likely to be recognized. + * + * @see https://learn.microsoft.com/en-us/azure/ai-services/speech-service/improve-accuracy-phrase-list + */ + readonly phrases?: string[]; } export interface ITranscriber { @@ -60,8 +71,8 @@ export interface ITranscriber { dispose(): void; } -export function createTranscriber({ modelPath, modelName, modelKey, logsPath }: ITranscriptionOptions, callback: ITranscriptionCallback): ITranscriber { - const id = speechapi.createTranscriber(modelPath, modelName, modelKey, logsPath ?? undefined, callback); +export function createTranscriber({ modelPath, modelName, modelKey, phrases, logsPath }: ITranscriptionOptions, callback: ITranscriptionCallback): ITranscriber { + const id = speechapi.createTranscriber(modelPath, modelName, modelKey, logsPath ?? undefined, phrases ?? [], callback); return { start: () => speechapi.startTranscriber(id), diff --git a/src/main.cc b/src/main.cc index 67b5d62..38d92cc 100644 --- a/src/main.cc +++ b/src/main.cc @@ -78,8 +78,8 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorker(callback), id(transcriptionWorkerIds++), path(path), key(key), model(model), logsPath(logsPath), started(false) + TranscriptionWorker(std::string &path, std::string &key, std::string &model, std::string &logsPath, std::vector &phrases, Napi::Function &callback) + : Napi::AsyncProgressQueueWorker(callback), id(transcriptionWorkerIds++), path(path), key(key), model(model), logsPath(logsPath), phrases(phrases), started(false) { UpdateTranscriptionWorkerStatus(this->id, RuntimeStatus::STOP); } @@ -98,6 +98,12 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorkerphrases) + { + phraseList->AddPhrase(phrase); + } + // Callback: intermediate transcription results recognizer->Recognizing += [progress](const SpeechRecognitionEventArgs &e) { @@ -285,6 +291,7 @@ class TranscriptionWorker : public Napi::AsyncProgressQueueWorker phrases; bool started; }; @@ -293,12 +300,12 @@ Napi::Value CreateTranscriber(const Napi::CallbackInfo &info) auto env = info.Env(); // Validate args - if (info.Length() != 5) + if (info.Length() != 6) { Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException(); return env.Undefined(); } - else if (!info[0].IsString() || !info[1].IsString() || !info[2].IsString() || (!info[3].IsUndefined() && !info[3].IsString()) || !info[4].IsFunction()) + else if (!info[0].IsString() || !info[1].IsString() || !info[2].IsString() || (!info[3].IsUndefined() && !info[3].IsString()) || !info[4].IsArray() || !info[5].IsFunction()) { Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException(); return env.Undefined(); @@ -312,11 +319,17 @@ Napi::Value CreateTranscriber(const Napi::CallbackInfo &info) { logsPath = info[3].As().Utf8Value(); } - auto callback = info[4].As(); + auto phrasesRaw = info[4].As(); + std::vector phrases; + for (size_t i = 0; i < phrasesRaw.Length(); i++) + { + phrases.push_back(phrasesRaw.Get(i).As().Utf8Value()); + } + auto callback = info[5].As(); try { - auto *worker = new TranscriptionWorker(modelPath, modelKey, modelName, logsPath, callback); + auto *worker = new TranscriptionWorker(modelPath, modelKey, modelName, logsPath, phrases, callback); worker->Queue(); return Napi::Number::New(env, worker->id); From 6d55edb891005247c562e882bf0cf7675e461001 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 Feb 2024 17:38:46 +0100 Subject: [PATCH 9/9] . --- src/main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cc b/src/main.cc index 38d92cc..c7b61ea 100644 --- a/src/main.cc +++ b/src/main.cc @@ -321,7 +321,7 @@ Napi::Value CreateTranscriber(const Napi::CallbackInfo &info) } auto phrasesRaw = info[4].As(); std::vector phrases; - for (size_t i = 0; i < phrasesRaw.Length(); i++) + for (uint32_t i = 0; i < static_cast(phrasesRaw.Length()); i++) { phrases.push_back(phrasesRaw.Get(i).As().Utf8Value()); }