chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:45:58 +08:00
commit 2dd9ea9aee
261 changed files with 32719 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
demo/model
demo/model-spk
demo/test.wav
+34
View File
@@ -0,0 +1,34 @@
This is an FFI-NAPI wrapper for the Vosk library.
## Usage
Bindings mostly follow Vosk interface, some methods are not yet fully implemented.
See [demo folder](https://github.com/alphacep/vosk-api/tree/master/nodejs/demo) for
details.
## About
Vosk is an offline open source speech recognition toolkit. It enables
speech recognition for 20+ languages and dialects - English, Indian
English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish,
Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi, Filipino,
Ukrainian, Kazakh, Swedish, Japanese, Esperanto, Hindi, Czech, Polish.
More to come.
Vosk models are small (50 Mb) but provide continuous large vocabulary
transcription, zero-latency response with streaming API, reconfigurable
vocabulary and speaker identification.
Vosk supplies speech recognition for chatbots, smart home appliances,
virtual assistants. It can also create subtitles for movies,
transcription for lectures and interviews.
Vosk scales from small devices like Raspberry Pi or Android smartphone to
big clusters.
# Documentation
For installation instructions, examples and documentation visit [Vosk
Website](https://alphacephei.com/vosk). See also our project on
[Github](https://github.com/alphacep/vosk-api).
+33
View File
@@ -0,0 +1,33 @@
var vosk = require('..')
const fs = require("fs");
const { spawn } = require("child_process");
MODEL_PATH = "model"
FILE_NAME = "test.wav"
SAMPLE_RATE = 16000
BUFFER_SIZE = 4000
if (!fs.existsSync(MODEL_PATH)) {
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
process.exit()
}
if (process.argv.length > 2)
FILE_NAME = process.argv[2]
vosk.setLogLevel(0);
const model = new vosk.Model(MODEL_PATH);
const rec = new vosk.Recognizer({model: model, sampleRate: SAMPLE_RATE});
const ffmpeg_run = spawn('ffmpeg', ['-loglevel', 'quiet', '-i', FILE_NAME,
'-ar', String(SAMPLE_RATE) , '-ac', '1',
'-f', 's16le', '-bufsize', String(BUFFER_SIZE) , '-']);
ffmpeg_run.stdout.on('data', (stdout) => {
if (rec.acceptWaveform(stdout))
console.log(rec.result());
else
console.log(rec.partialResult());
console.log(rec.finalResult());
});
+46
View File
@@ -0,0 +1,46 @@
var vosk = require('..')
const fs = require("fs");
var mic = require("mic");
MODEL_PATH = "model"
SAMPLE_RATE = 16000
if (!fs.existsSync(MODEL_PATH)) {
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
process.exit()
}
vosk.setLogLevel(0);
const model = new vosk.Model(MODEL_PATH);
const rec = new vosk.Recognizer({model: model, sampleRate: SAMPLE_RATE});
var micInstance = mic({
rate: String(SAMPLE_RATE),
channels: '1',
debug: false,
device: 'default',
});
var micInputStream = micInstance.getAudioStream();
micInputStream.on('data', data => {
if (rec.acceptWaveform(data))
console.log(rec.result());
else
console.log(rec.partialResult());
});
micInputStream.on('audioProcessExitComplete', function() {
console.log("Cleaning up");
console.log(rec.finalResult());
rec.free();
model.free();
});
process.on('SIGINT', function() {
console.log("\nStopping");
micInstance.stop();
});
micInstance.start();
+48
View File
@@ -0,0 +1,48 @@
var vosk = require('..')
const fs = require("fs");
const { Readable } = require("stream");
const wav = require("wav");
MODEL_PATH = "model"
FILE_NAME = "test.wav"
if (!fs.existsSync(MODEL_PATH)) {
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
process.exit()
}
if (process.argv.length > 2)
FILE_NAME = process.argv[2]
vosk.setLogLevel(0);
const model = new vosk.Model(MODEL_PATH);
const wfReader = new wav.Reader();
const wfReadable = new Readable().wrap(wfReader);
wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
if (audioFormat != 1 || channels != 1) {
console.error("Audio file must be WAV format mono PCM.");
process.exit(1);
}
const rec = new vosk.Recognizer({model: model, sampleRate: sampleRate});
rec.setMaxAlternatives(10);
rec.setWords(true);
rec.setPartialWords(true);
for await (const data of wfReadable) {
const end_of_speech = rec.acceptWaveform(data);
if (end_of_speech) {
console.log(JSON.stringify(rec.result(), null, 4));
} else {
console.log(JSON.stringify(rec.partialResult(), null, 4));
}
}
console.log(JSON.stringify(rec.finalResult(rec), null, 4));
rec.free();
});
fs.createReadStream(FILE_NAME, {'highWaterMark': 4096}).pipe(wfReader).on('finish',
function (err) {
model.free();
});
+46
View File
@@ -0,0 +1,46 @@
var vosk = require('..')
const async = require("async");
const fs = require("fs");
const { Readable } = require("stream");
const wav = require("wav");
MODEL_PATH = "model"
if (!fs.existsSync(MODEL_PATH)) {
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
process.exit()
}
// Process file 4 times in parallel with a single model
files = Array(10).fill("test.wav")
const model = new vosk.Model(MODEL_PATH)
async.filter(files, function(filePath, callback) {
const wfReader = new wav.Reader();
const wfReadable = new Readable().wrap(wfReader);
wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
const rec = new vosk.Recognizer({model: model, sampleRate: sampleRate});
if (audioFormat != 1 || channels != 1) {
console.error("Audio file must be WAV format mono PCM.");
process.exit(1);
}
for await (const data of wfReadable) {
const end_of_speech = await rec.acceptWaveformAsync(data);
if (end_of_speech) {
console.log(rec.result());
}
}
console.log(rec.finalResult(rec));
rec.free();
// Signal we are done without errors
callback(null, true);
});
fs.createReadStream(filePath, {'highWaterMark': 4096}).pipe(wfReader);
}, function(err, results) {
model.free();
console.log("Done!!!!!");
});
+53
View File
@@ -0,0 +1,53 @@
const vosk = require('..');
const fs = require("fs");
const { Readable } = require("stream");
const wav = require("wav");
MODEL_PATH = "model"
SPEAKER_MODEL_PATH = "model-spk"
FILE_NAME = "test.wav"
if (!fs.existsSync(MODEL_PATH)) {
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
process.exit()
}
if (!fs.existsSync(SPEAKER_MODEL_PATH)) {
console.log("Please download the speaker model from https://alphacephei.com/vosk/models and unpack as " + SPEAKER_MODEL_PATH + " in the current folder.")
process.exit()
}
if (process.argv.length > 2)
FILE_NAME = process.argv[2]
const model = new vosk.Model(MODEL_PATH);
const speakerModel = new vosk.SpeakerModel(SPEAKER_MODEL_PATH);
const wfReader = new wav.Reader();
const wfReadable = new Readable().wrap(wfReader);
wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
if (audioFormat != 1 || channels != 1) {
console.error('Audio file must be WAV format mono PCM.');
process.exit(1);
}
// const rec = new vosk.Recognizer({ model: model,
// speakerModel: speakerModel,
// sampleRate: sampleRate });
const rec = new vosk.Recognizer({model: model, sampleRate: sampleRate});
rec.setSpkModel(speakerModel);
for await (const data of wfReadable) {
const end_of_speech = rec.acceptWaveform(data);
if (end_of_speech) {
console.log(rec.finalResult());
}
}
console.log(rec.finalResult());
rec.free();
});
fs.createReadStream(FILE_NAME, { highWaterMark: 4096 }).pipe(wfReader).on('finish', function (err) {
model.free();
speakerModel.free();
});
+84
View File
@@ -0,0 +1,84 @@
var vosk = require('..')
const fs = require("fs");
const { spawn } = require("child_process");
const { stringifySync } = require('subtitle')
MODEL_PATH = "model"
FILE_NAME = "test.wav"
SAMPLE_RATE = 16000
BUFFER_SIZE = 4000
if (!fs.existsSync(MODEL_PATH)) {
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
process.exit()
}
if (process.argv.length > 2)
FILE_NAME = process.argv[2]
vosk.setLogLevel(-1);
const model = new vosk.Model(MODEL_PATH);
const rec = new vosk.Recognizer({model: model, sampleRate: SAMPLE_RATE});
rec.setWords(true);
const ffmpeg_run = spawn('ffmpeg', ['-loglevel', 'quiet', '-i', FILE_NAME,
'-ar', String(SAMPLE_RATE) , '-ac', '1',
'-f', 's16le', '-bufsize', String(BUFFER_SIZE), '-']);
WORDS_PER_LINE = 7
const subs = []
const results = []
ffmpeg_run.stdout.on('data', (stdout) => {
if (rec.acceptWaveform(stdout))
results.push(rec.result());
results.push(rec.finalResult());
});
ffmpeg_run.on('exit', code => {
rec.free();
model.free();
results.forEach(element =>{
if (!element.hasOwnProperty('result'))
return;
const words = element.result;
if (words.length == 1) {
subs.push({
type: 'cue',
data: {
start: words[0].start * 1000,
end: words[0].end * 1000,
text: words[0].word
}
});
return;
}
var start_index = 0;
var text = words[0].word + " ";
for (let i = 1; i < words.length; i++) {
text += words[i].word + " ";
if (i % WORDS_PER_LINE == 0) {
subs.push({
type: 'cue',
data: {
start: words[start_index].start * 1000,
end: words[i].end * 1000,
text: text.slice(0, text.length-1)
}
});
start_index = i;
text = "";
}
}
if (start_index != words.length - 1)
subs.push({
type: 'cue',
data: {
start: words[start_index].start * 1000,
end: words[words.length-1].end * 1000,
text: text
}
});
});
console.log(stringifySync(subs, {format: "SRT"}));
});
+445
View File
@@ -0,0 +1,445 @@
// @ts-check
'use strict'
/**
* @module vosk
*/
const os = require('os');
const path = require('path');
/** @type {import('ffi-napi')} */
const ffi = require('ffi-napi');
/** @type {import('ref-napi')} */
const ref = require('ref-napi');
const vosk_model = ref.types.void;
const vosk_model_ptr = ref.refType(vosk_model);
const vosk_spk_model = ref.types.void;
const vosk_spk_model_ptr = ref.refType(vosk_spk_model);
const vosk_recognizer = ref.types.void;
const vosk_recognizer_ptr = ref.refType(vosk_recognizer);
/**
* @typedef {Object} WordResult
* @property {number} conf The confidence rate in the detection. 0 For unlikely, and 1 for totally accurate.
* @property {number} start The start of the timeframe when the word is pronounced in seconds
* @property {number} end The end of the timeframe when the word is pronounced in seconds
* @property {string} word The word detected
*/
/**
* @typedef {Object} RecognitionResults
* @property {WordResult[]} result Details about the words that have been detected
* @property {string} text The complete sentence that have been detected
*/
/**
* @typedef {Object} SpeakerResults
* @property {number[]} spk A floating vector representing speaker identity. It is usually about 128 numbers which uniquely represent speaker voice.
* @property {number} spk_frames The number of frames used to extract speaker vector. The more frames you have the more reliable is speaker vector.
*/
/**
* @typedef {Object} BaseRecognizerParam
* @property {Model} model The language model to be used
* @property {number} sampleRate The sample rate. Most models are trained at 16kHz
*/
/**
* @typedef {Object} GrammarRecognizerParam
* @property {string[]} grammar The list of sentences to be recognized.
*/
/**
* @typedef {Object} SpeakerRecognizerParam
* @property {SpeakerModel} speakerModel The SpeakerModel that will enable speaker identification
*/
/**
* @template {SpeakerRecognizerParam | GrammarRecognizerParam} T
* @typedef {T extends SpeakerRecognizerParam ? SpeakerResults & RecognitionResults : RecognitionResults} Result
*/
/**
* @typedef {Object} PartialResults
* @property {string} partial The partial sentence that have been detected until now
*/
/** @typedef {string[]} Grammar The list of strings to be recognized */
let soname;
if (os.platform() == 'win32') {
// Update path to load dependent dlls
let currentPath = process.env.Path;
let dllDirectory = path.resolve(path.join(__dirname, 'lib', 'win-x86_64'));
process.env.Path = dllDirectory + path.delimiter + currentPath;
soname = path.join(__dirname, 'lib', 'win-x86_64', 'libvosk.dll');
} else if (os.platform() == 'darwin') {
soname = path.join(__dirname, 'lib', 'osx-universal', 'libvosk.dylib');
} else if (os.platform() == 'linux' && os.arch() == 'arm64') {
soname = path.join(__dirname, 'lib', 'linux-arm64', 'libvosk.so');
} else {
soname = path.join(__dirname, 'lib', 'linux-x86_64', 'libvosk.so');
}
const libvosk = ffi.Library(soname, {
'vosk_set_log_level': ['void', ['int']],
'vosk_model_new': [vosk_model_ptr, ['string']],
'vosk_model_free': ['void', [vosk_model_ptr]],
'vosk_spk_model_new': [vosk_spk_model_ptr, ['string']],
'vosk_spk_model_free': ['void', [vosk_spk_model_ptr]],
'vosk_recognizer_new': [vosk_recognizer_ptr, [vosk_model_ptr, 'float']],
'vosk_recognizer_new_spk': [vosk_recognizer_ptr, [vosk_model_ptr, 'float', vosk_spk_model_ptr]],
'vosk_recognizer_new_grm': [vosk_recognizer_ptr, [vosk_model_ptr, 'float', 'string']],
'vosk_recognizer_free': ['void', [vosk_recognizer_ptr]],
'vosk_recognizer_set_max_alternatives': ['void', [vosk_recognizer_ptr, 'int']],
'vosk_recognizer_set_words': ['void', [vosk_recognizer_ptr, 'bool']],
'vosk_recognizer_set_partial_words': ['void', [vosk_recognizer_ptr, 'bool']],
'vosk_recognizer_set_spk_model': ['void', [vosk_recognizer_ptr, vosk_spk_model_ptr]],
'vosk_recognizer_accept_waveform': ['bool', [vosk_recognizer_ptr, 'pointer', 'int']],
'vosk_recognizer_result': ['string', [vosk_recognizer_ptr]],
'vosk_recognizer_final_result': ['string', [vosk_recognizer_ptr]],
'vosk_recognizer_partial_result': ['string', [vosk_recognizer_ptr]],
'vosk_recognizer_reset': ['void', [vosk_recognizer_ptr]],
});
/**
* Set log level for Kaldi messages
* @param {number} level The higher, the more verbose. 0 for infos and errors. Less than 0 for silence.
*/
function setLogLevel(level) {
libvosk.vosk_set_log_level(level);
}
/**
* Build a Model from a model file.
* @see models [models](https://alphacephei.com/vosk/models)
*/
class Model {
/**
* Build a Model to be used with the voice recognition. Each language should have it's own Model
* for the speech recognition to work.
* @param {string} modelPath The abstract pathname to the model
* @see models [models](https://alphacephei.com/vosk/models)
*/
constructor(modelPath) {
/**
* Store the handle.
* For internal use only
* @type {unknown}
*/
this.handle = libvosk.vosk_model_new(modelPath);
if (!this.handle) {
throw new Error('Failed to create a model.');
}
}
/**
* Releases the model memory
*
* The model object is reference-counted so if some recognizer
* depends on this model, model might still stay alive. When
* last recognizer is released, model will be released too.
*/
free() {
libvosk.vosk_model_free(this.handle);
}
}
/**
* Build a Speaker Model from a speaker model file.
* The Speaker Model enables speaker identification.
* @see models [models](https://alphacephei.com/vosk/models)
*/
class SpeakerModel {
/**
* Loads speaker model data from the file and returns the model object
*
* @param {string} modelPath the path of the model on the filesystem
* @see models [models](https://alphacephei.com/vosk/models)
*/
constructor(modelPath) {
/**
* Store the handle.
* For internal use only
* @type {unknown}
*/
this.handle = libvosk.vosk_spk_model_new(modelPath);
if (!this.handle) {
throw new Error('Failed to create a speaker model.');
}
}
/**
* Releases the model memory
*
* The model object is reference-counted so if some recognizer
* depends on this model, model might still stay alive. When
* last recognizer is released, model will be released too.
*/
free() {
libvosk.vosk_spk_model_free(this.handle);
}
}
/**
* Helper to narrow down type while using `hasOwnProperty`.
* @see hasOwnProperty [typescript issue](https://fettblog.eu/typescript-hasownproperty/)
* @template {Object} Obj
* @template {PropertyKey} Key
* @param {Obj} obj
* @param {Key} prop
* @returns {obj is Obj & Record<Key, unknown>}
*/
function hasOwnProperty(obj, prop) {
return obj.hasOwnProperty(prop)
}
/**
* @template T
* @template U
* @typedef {{ [P in Exclude<keyof T, keyof U>]?: never }} Without
*/
/**
* @template T
* @template U
* @typedef {(T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U} XOR
*/
/**
* Create a Recognizer that will be able to transform audio streams into text using a Model.
* @template {XOR<SpeakerRecognizerParam, Partial<GrammarRecognizerParam>>} T extra parameter
* @see Model
*/
class Recognizer {
/**
* Create a Recognizer that will handle speech to text recognition.
* @constructor
* @param {T & BaseRecognizerParam} param The Recognizer parameters
*
* Sometimes when you want to improve recognition accuracy and when you don't need
* to recognize large vocabulary you can specify a list of phrases to recognize. This
* will improve recognizer speed and accuracy but might return [unk] if user said
* something different.
*
* Only recognizers with lookahead models support this type of quick configuration.
* Precompiled HCLG graph models are not supported.
*/
constructor(param) {
const { model, sampleRate } = param
// Prevent the user to receive unpredictable results
if (hasOwnProperty(param, 'speakerModel') && hasOwnProperty(param, 'grammar')) {
throw new Error('grammar and speakerModel cannot be used together for now.')
}
/**
* Store the handle.
* For internal use only
* @type {unknown}
*/
this.handle = hasOwnProperty(param, 'speakerModel')
? libvosk.vosk_recognizer_new_spk(model.handle, sampleRate, param.speakerModel.handle)
: hasOwnProperty(param, 'grammar')
? libvosk.vosk_recognizer_new_grm(model.handle, sampleRate, JSON.stringify(param.grammar))
: libvosk.vosk_recognizer_new(model.handle, sampleRate);
if (!this.handle) {
throw new Error('Failed to create a recognizer.');
}
}
/**
* Releases the model memory
*
* The model object is reference-counted so if some recognizer
* depends on this model, model might still stay alive. When
* last recognizer is released, model will be released too.
*/
free() {
libvosk.vosk_recognizer_free(this.handle);
}
/** Configures recognizer to output n-best results
*
* <pre>
* {
* "alternatives": [
* { "text": "one two three four five", "confidence": 0.97 },
* { "text": "one two three for five", "confidence": 0.03 },
* ]
* }
* </pre>
*
* @param max_alternatives - maximum alternatives to return from recognition results
*/
setMaxAlternatives(max_alternatives) {
libvosk.vosk_recognizer_set_max_alternatives(this.handle, max_alternatives);
}
/** Configures recognizer to output words with times
*
* <pre>
* "result" : [{
* "conf" : 1.000000,
* "end" : 1.110000,
* "start" : 0.870000,
* "word" : "what"
* }, {
* "conf" : 1.000000,
* "end" : 1.530000,
* "start" : 1.110000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 1.950000,
* "start" : 1.530000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.340000,
* "start" : 1.950000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.610000,
* "start" : 2.340000,
* "word" : "one"
* }],
* </pre>
*
* @param words - boolean value
*/
setWords(words) {
libvosk.vosk_recognizer_set_words(this.handle, words);
}
/** Same as above, but for partial results*/
setPartialWords(partial_words) {
libvosk.vosk_recognizer_set_partial_words(this.handle, partial_words);
}
/** Adds speaker recognition model to already created recognizer. Helps to initialize
* speaker recognition for grammar-based recognizer.
*
* @param spk_model Speaker recognition model
*/
setSpkModel(spk_model) {
libvosk.vosk_recognizer_set_spk_model(this.handle, spk_model.handle);
}
/**
* Accept voice data
*
* accept and process new chunk of voice data
*
* @param {Buffer} data audio data in PCM 16-bit mono format
* @returns true if silence is occured and you can retrieve a new utterance with result method
*/
acceptWaveform(data) {
return libvosk.vosk_recognizer_accept_waveform(this.handle, data, data.length);
};
/**
* Accept voice data
*
* accept and process new chunk of voice data
*
* @param {Buffer} data audio data in PCM 16-bit mono format
* @returns true if silence is occured and you can retrieve a new utterance with result method
*/
acceptWaveformAsync(data) {
return new Promise((resolve, reject) => {
libvosk.vosk_recognizer_accept_waveform.async(this.handle, data, data.length, function(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
};
/** Returns speech recognition result in a string
*
* @returns the result in JSON format which contains decoded line, decoded
* words, times in seconds and confidences. You can parse this result
* with any json parser
* <pre>
* {
* "result" : [{
* "conf" : 1.000000,
* "end" : 1.110000,
* "start" : 0.870000,
* "word" : "what"
* }, {
* "conf" : 1.000000,
* "end" : 1.530000,
* "start" : 1.110000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 1.950000,
* "start" : 1.530000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.340000,
* "start" : 1.950000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.610000,
* "start" : 2.340000,
* "word" : "one"
* }],
* "text" : "what zero zero zero one"
* }
* </pre>
*/
resultString() {
return libvosk.vosk_recognizer_result(this.handle);
};
/**
* Returns speech recognition results
* @returns {Result<T>} The results
*/
result() {
return JSON.parse(libvosk.vosk_recognizer_result(this.handle));
};
/**
* speech recognition text which is not yet finalized.
* result may change as recognizer process more data.
*
* @returns {PartialResults} The partial results
*/
partialResult() {
return JSON.parse(libvosk.vosk_recognizer_partial_result(this.handle));
};
/**
* Returns speech recognition result. Same as result, but doesn't wait for silence
* You usually call it in the end of the stream to get final bits of audio. It
* flushes the feature pipeline, so all remaining audio chunks got processed.
*
* @returns {Result<T>} speech result.
*/
finalResult() {
return JSON.parse(libvosk.vosk_recognizer_final_result(this.handle));
};
/**
*
* Resets current results so the recognition can continue from scratch
*/
reset() {
libvosk.vosk_recognizer_reset(this.handle);
}
}
exports.setLogLevel = setLogLevel
exports.Model = Model
exports.SpeakerModel = SpeakerModel
exports.Recognizer = Recognizer
+27
View File
@@ -0,0 +1,27 @@
{
"name": "vosk",
"version": "0.3.75",
"description": "Node binding for continuous offline voice recoginition with Vosk library.",
"repository": {
"type": "git",
"url": "git://github.com/alphacep/vosk-api.git"
},
"main": "index.js",
"keywords": [
"speech",
"speech recognition",
"voice"
],
"author": "Alpha Cephei Inc.",
"license": "Apache-2.0",
"engines": {
"node": ">= 12.x.x"
},
"dependencies": {
"async": "^3.2.0",
"ffi-napi": "^4.0.3",
"mic": "^2.1.2",
"ref-napi": ">=2.0.0",
"wav": "^1.0.2"
}
}