index.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Copyright (c) 2023 Xiaomi Corporation (authors: Fangjun Kuang)
  2. //
  3. // Please use
  4. //
  5. // npm install ffi-napi ref-struct-napi
  6. //
  7. // before you use this file
  8. //
  9. //
  10. // Please use node 13. node 16, 18, 20, and 21 are known not working.
  11. // See also
  12. // https://github.com/node-ffi-napi/node-ffi-napi/issues/244
  13. // and
  14. // https://github.com/node-ffi-napi/node-ffi-napi/issues/97
  15. 'use strict'
  16. const debug = require('debug')('sherpa-ncnn');
  17. const os = require('os');
  18. const path = require('path');
  19. const ffi = require('ffi-napi');
  20. const ref = require('ref-napi');
  21. const fs = require('fs');
  22. const StructType = require('ref-struct-napi');
  23. const cstring = ref.types.CString;
  24. const int32_t = ref.types.int32;
  25. const float = ref.types.float;
  26. const floatPtr = ref.refType(float);
  27. const RecognizerPtr = ref.refType(ref.types.void);
  28. const StreamPtr = ref.refType(ref.types.void);
  29. const SherpaNcnnModelConfig = StructType({
  30. 'encoderParam': cstring,
  31. 'encoderBin': cstring,
  32. 'decoderParam': cstring,
  33. 'decoderBin': cstring,
  34. 'joinerParam': cstring,
  35. 'joinerBin': cstring,
  36. 'tokens': cstring,
  37. 'useVulkanCompute': int32_t,
  38. 'numThreads': int32_t,
  39. });
  40. const SherpaNcnnDecoderConfig = StructType({
  41. 'decodingMethod': cstring,
  42. 'numActivePaths': int32_t,
  43. });
  44. const SherpaNcnnFeatureExtractorConfig = StructType({
  45. 'sampleRate': float,
  46. 'featureDim': int32_t,
  47. });
  48. const SherpaNcnnRecognizerConfig = StructType({
  49. 'featConfig': SherpaNcnnFeatureExtractorConfig,
  50. 'modelConfig': SherpaNcnnModelConfig,
  51. 'decoderConfig': SherpaNcnnDecoderConfig,
  52. 'enableEndpoint': int32_t,
  53. 'rule1MinTrailingSilence': float,
  54. 'rule2MinTrailingSilence': float,
  55. 'rule3MinUtteranceLength': float,
  56. 'hotwordsFile': cstring,
  57. 'hotwordsScore': cstring,
  58. });
  59. const SherpaNcnnResult = StructType({
  60. 'text': cstring,
  61. 'tokens': cstring,
  62. 'timestamps': floatPtr,
  63. 'count': int32_t,
  64. });
  65. const ResultPtr = ref.refType(SherpaNcnnResult);
  66. const RecognizerConfigPtr = ref.refType(SherpaNcnnRecognizerConfig)
  67. let soname;
  68. if (os.platform() == 'win32') {
  69. soname = path.join(__dirname, 'install', 'lib', 'sherpa-ncnn-c-api.dll');
  70. } else if (os.platform() == 'darwin') {
  71. soname = path.join(__dirname, 'install', 'lib', 'libsherpa-ncnn-c-api.dylib');
  72. } else if (os.platform() == 'linux') {
  73. soname = path.join(__dirname, 'install', 'lib', 'libsherpa-ncnn-c-api.so');
  74. } else {
  75. throw new Error(`Unsupported platform ${os.platform()}`);
  76. }
  77. if (!fs.existsSync(soname)) {
  78. throw new Error(`Cannot find file ${soname}. Please make sure you have run
  79. ./build.sh`);
  80. }
  81. debug('soname ', soname)
  82. const libsherpa_ncnn = ffi.Library(soname, {
  83. 'CreateRecognizer': [RecognizerPtr, [RecognizerConfigPtr]],
  84. 'DestroyRecognizer': ['void', [RecognizerPtr]],
  85. 'CreateStream': [StreamPtr, [RecognizerPtr]],
  86. 'DestroyStream': ['void', [StreamPtr]],
  87. 'AcceptWaveform': ['void', [StreamPtr, float, floatPtr, int32_t]],
  88. 'IsReady': [int32_t, [RecognizerPtr, StreamPtr]],
  89. 'Decode': ['void', [RecognizerPtr, StreamPtr]],
  90. 'GetResult': [ResultPtr, [RecognizerPtr, StreamPtr]],
  91. 'DestroyResult': ['void', [ResultPtr]],
  92. 'Reset': ['void', [RecognizerPtr, StreamPtr]],
  93. 'InputFinished': ['void', [StreamPtr]],
  94. 'IsEndpoint': [int32_t, [RecognizerPtr, StreamPtr]],
  95. });
  96. class Recognizer {
  97. /**
  98. * @param {SherpaNcnnRecognizerConfig} config Configuration for the recognizer
  99. *
  100. * The user has to invoke this.free() at the end to avoid memory leak.
  101. */
  102. constructor(config) {
  103. this.recognizer_handle = libsherpa_ncnn.CreateRecognizer(config.ref());
  104. this.stream_handle = libsherpa_ncnn.CreateStream(this.recognizer_handle);
  105. }
  106. free() {
  107. if (this.stream_handle) {
  108. libsherpa_ncnn.DestroyStream(this.stream_handle);
  109. this.stream_handle = null;
  110. }
  111. libsherpa_ncnn.DestroyRecognizer(this.recognizer_handle);
  112. this.handle = null;
  113. }
  114. /**
  115. * @param {bool} true to create a new stream
  116. */
  117. reset(recreate) {
  118. if (recreate) {
  119. libsherpa_ncnn.DestroyStream(this.stream_handle);
  120. this.stream_handle = libsherpa_ncnn.CreateStream(this.recognizer_handle);
  121. return;
  122. }
  123. libsherpa_ncnn.Reset(this.recognizer_handle, this.stream_handle)
  124. }
  125. /**
  126. * @param {float} Sample rate of the input data
  127. * @param {float[]} A 1-d float array containing audio samples. It should be
  128. * in the range [-1, 1].
  129. */
  130. acceptWaveform(sampleRate, samples) {
  131. libsherpa_ncnn.AcceptWaveform(
  132. this.stream_handle, sampleRate, samples, samples.length);
  133. }
  134. isReady() {
  135. return libsherpa_ncnn.IsReady(this.recognizer_handle, this.stream_handle);
  136. }
  137. decode() {
  138. libsherpa_ncnn.Decode(this.recognizer_handle, this.stream_handle);
  139. }
  140. getResult() {
  141. const h =
  142. libsherpa_ncnn.GetResult(this.recognizer_handle, this.stream_handle);
  143. const text = Buffer.from(h.deref().text, 'utf-8').toString();
  144. libsherpa_ncnn.DestroyResult(h);
  145. return text;
  146. }
  147. };
  148. // alias
  149. const ModelConfig = SherpaNcnnModelConfig;
  150. const DecoderConfig = SherpaNcnnDecoderConfig;
  151. const FeatureConfig = SherpaNcnnFeatureExtractorConfig;
  152. const RecognizerConfig = SherpaNcnnRecognizerConfig;
  153. module.exports = {
  154. FeatureConfig,
  155. ModelConfig,
  156. DecoderConfig,
  157. Recognizer,
  158. RecognizerConfig,
  159. };