non-streaming-asr.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /** @typedef {import('./types').OfflineStreamObject} OfflineStreamObject */
  2. /** @typedef {import('./types').OfflineStreamHandle} OfflineStreamHandle */
  3. /** @typedef {import('./types').OfflineRecognizerHandle} OfflineRecognizerHandle */
  4. /** @typedef {import('./types').Waveform} Waveform */
  5. /**
  6. * @typedef {import('./types').OfflineRecognizerConfig} OfflineRecognizerConfig
  7. */
  8. /**
  9. * @typedef {import('./types').OfflineRecognizerResult} OfflineRecognizerResult
  10. */
  11. const addon = require('./addon.js');
  12. /**
  13. * Internal symbol to mark async-created recognizers.
  14. * Not accessible unless someone has a reference to this Symbol.
  15. */
  16. const kFromAsyncFactory = Symbol('OfflineRecognizer.fromAsync');
  17. /**
  18. * OfflineStream represents a synchronous offline audio stream.
  19. */
  20. class OfflineStream {
  21. /**
  22. * @param {OfflineStreamObject|Object} handle
  23. */
  24. constructor(handle) {
  25. this.handle = handle;
  26. }
  27. /**
  28. * Accept a chunk of waveform samples.
  29. * @param {Waveform} obj - { samples: Float32Array, sampleRate: number }
  30. */
  31. acceptWaveform(obj) {
  32. addon.acceptWaveformOffline(this.handle, obj);
  33. }
  34. /**
  35. * Set a string option on the underlying offline stream.
  36. * @param {string} key
  37. * @param {string} value
  38. */
  39. setOption(key, value) {
  40. addon.offlineStreamSetOption(this.handle, key, value);
  41. }
  42. }
  43. /**
  44. * OfflineRecognizer wraps the native offline recognizer.
  45. */
  46. class OfflineRecognizer {
  47. /**
  48. * Constructor (SYNC path).
  49. *
  50. * Users call:
  51. * new OfflineRecognizer(config)
  52. *
  53. * Async factory calls this with an internal descriptor.
  54. *
  55. * @param {OfflineRecognizerConfig | Object} configOrInternal
  56. */
  57. constructor(configOrInternal) {
  58. // ----- async factory path -----
  59. if (configOrInternal && typeof configOrInternal === 'object' &&
  60. configOrInternal[kFromAsyncFactory]) {
  61. this.handle = configOrInternal.handle;
  62. this.config = configOrInternal.config;
  63. return;
  64. }
  65. // ----- sync constructor path -----
  66. this.config = configOrInternal;
  67. this.handle = addon.createOfflineRecognizer(this.config);
  68. }
  69. /**
  70. * Create an OfflineRecognizer asynchronously (non-blocking).
  71. *
  72. * @param {OfflineRecognizerConfig} config
  73. * @returns {Promise<OfflineRecognizer>}
  74. */
  75. static async createAsync(config) {
  76. const handle = await addon.createOfflineRecognizerAsync(config);
  77. return new OfflineRecognizer({
  78. [kFromAsyncFactory]: true,
  79. handle,
  80. config,
  81. });
  82. }
  83. /**
  84. * Create a new OfflineStream bound to this recognizer.
  85. *
  86. * The optional hotwords argument enables contextual biasing for this
  87. * stream only. Hotwords are supported only by transducer models decoded
  88. * with decodingMethod 'modified_beam_search'. Separate multiple phrases
  89. * with '/', and optionally append a per-phrase boosting score, e.g.
  90. * 'PHOEBE :2.0/DON QUIXOTE'. When modelConfig.modelingUnit and
  91. * modelConfig.bpeVocab are set, phrases are given as normal words;
  92. * otherwise each phrase must be a sequence of tokens from tokens.txt
  93. * separated by spaces. See also
  94. * https://k2-fsa.github.io/sherpa/onnx/hotwords/index.html
  95. *
  96. * @param {string} [hotwords] Optional hotwords for this stream.
  97. * @returns {OfflineStream}
  98. */
  99. createStream(hotwords) {
  100. const handle = hotwords === undefined ?
  101. addon.createOfflineStream(this.handle) :
  102. addon.createOfflineStream(this.handle, hotwords);
  103. return new OfflineStream(handle);
  104. }
  105. /**
  106. * Replace the recognizer config at runtime.
  107. * @param {OfflineRecognizerConfig} config
  108. */
  109. setConfig(config) {
  110. this.config = config;
  111. addon.offlineRecognizerSetConfig(this.handle, config);
  112. }
  113. /**
  114. * Decode an offline stream (synchronous).
  115. * @param {OfflineStream} stream
  116. */
  117. decode(stream) {
  118. addon.decodeOfflineStream(this.handle, stream.handle);
  119. }
  120. /**
  121. * Decode an offline stream asynchronously (non-blocking).
  122. * @param {OfflineStream} stream
  123. * @returns {Promise<OfflineRecognizerResult>}
  124. */
  125. async decodeAsync(stream) {
  126. const jsonStr =
  127. await addon.decodeOfflineStreamAsync(this.handle, stream.handle);
  128. return JSON.parse(jsonStr);
  129. }
  130. /**
  131. * Get recognition result for a stream.
  132. * @param {OfflineStream} stream
  133. * @returns {OfflineRecognizerResult}
  134. */
  135. getResult(stream) {
  136. const jsonStr = addon.getOfflineStreamResultAsJson(stream.handle);
  137. return JSON.parse(jsonStr);
  138. }
  139. }
  140. module.exports = {
  141. OfflineRecognizer,
  142. OfflineStream,
  143. };