Fangjun Kuang vor 2 Jahren
Ursprung
Commit
9011180512

+ 1 - 1
.github/scripts/run-test.sh

@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 
-set -ex
+set -e
 
 log() {
   # This function is from espnet

+ 14 - 1
.github/workflows/aarch64-linux-gnu.yaml

@@ -113,7 +113,7 @@ jobs:
 
           file build-aarch64-linux-gnu/bin/sherpa-ncnn
 
-      - name: Run tests
+      - name: Test sherpa-ncnn
         shell: bash
         run: |
           export PATH=$GITHUB_WORKSPACE/toolchain/bin:$PATH
@@ -125,3 +125,16 @@ jobs:
           ls -lh ./build-aarch64-linux-gnu/bin
 
           .github/scripts/run-test.sh
+
+      - name: Test C API
+        shell: bash
+        run: |
+          export PATH=$GITHUB_WORKSPACE/toolchain/bin:$PATH
+          export PATH=$GITHUB_WORKSPACE/qemu-install/bin:$PATH
+          export QEMU_LD_PREFIX=$GITHUB_WORKSPACE/toolchain/aarch64-linux-gnu/libc
+
+          export EXE="qemu-aarch64 ./build-aarch64-linux-gnu/bin/decode-file-c-api"
+
+          ls -lh ./build-aarch64-linux-gnu/bin
+
+          .github/scripts/run-test.sh

+ 14 - 1
.github/workflows/arm-linux-gnueabihf.yaml

@@ -115,7 +115,7 @@ jobs:
 
           file build-arm-linux-gnueabihf/bin/sherpa-ncnn
 
-      - name: Run tests
+      - name: Test sherpa-ncnn
         shell: bash
         run: |
           export PATH=$GITHUB_WORKSPACE/toolchain/bin:$PATH
@@ -127,3 +127,16 @@ jobs:
           ls -lh ./build-arm-linux-gnueabihf/bin
 
           .github/scripts/run-test.sh
+
+      - name: Test C API
+        shell: bash
+        run: |
+          export PATH=$GITHUB_WORKSPACE/toolchain/bin:$PATH
+          export PATH=$GITHUB_WORKSPACE/qemu-install/bin:$PATH
+          export QEMU_LD_PREFIX=$GITHUB_WORKSPACE/toolchain/arm-linux-gnueabihf/libc
+
+          export EXE="qemu-arm ./build-arm-linux-gnueabihf/bin/decode-file-c-api"
+
+          ls -lh ./build-arm-linux-gnueabihf/bin
+
+          .github/scripts/run-test.sh

+ 11 - 1
.github/workflows/linux.yaml

@@ -61,15 +61,25 @@ jobs:
           ls -lh bin/sherpa-ncnn-microphone
           file bin/sherpa-ncnn-microphone
 
+          ls -lh bin/decode-file-c-api
+          file bin/decode-file-c-api
+
       - name: Upload binary sherpa-ncnn and sherpa-ncnn-microphone
         uses: actions/upload-artifact@v2
         with:
           name: sherpa-ncnn-pre-built-binaries-os-${{ matrix.os }}
           path: ./build/bin
 
-      - name: Run tests for ubuntu
+      - name: Test sherpa-ncnn
         run: |
           export PATH=$PWD/build/bin:$PATH
           export EXE=sherpa-ncnn
 
           .github/scripts/run-test.sh
+
+      - name: Test C API
+        run: |
+          export PATH=$PWD/build/bin:$PATH
+          export EXE=decode-file-c-api
+
+          .github/scripts/run-test.sh

+ 11 - 1
.github/workflows/macos.yaml

@@ -61,15 +61,25 @@ jobs:
           ls -lh bin/sherpa-ncnn-microphone
           file bin/sherpa-ncnn-microphone
 
+          ls -lh bin/decode-file-c-api
+          file bin/decode-file-c-api
+
       - name: Upload binary sherpa-ncnn and sherpa-ncnn-microphone
         uses: actions/upload-artifact@v2
         with:
           name: sherpa-ncnn-pre-built-binaries-os-${{ matrix.os }}
           path: ./build/bin
 
-      - name: Run tests for macos
+      - name: Test sherpa-ncnn
         run: |
           export PATH=$PWD/build/bin:$PATH
           export EXE=sherpa-ncnn
 
           .github/scripts/run-test.sh
+
+      - name: Test C API
+        run: |
+          export PATH=$PWD/build/bin:$PATH
+          export EXE=decode-file-c-api
+
+          .github/scripts/run-test.sh

+ 9 - 1
.github/workflows/windows-x64.yaml

@@ -72,10 +72,18 @@ jobs:
           ls -lh ./bin/Release/sherpa-ncnn.exe
           ls -lh ./bin/Release/sherpa-ncnn-microphone.exe
 
-      - name: Run tests for windows
+      - name: Test sherpa-ncnn
         shell: bash
         run: |
           export PATH=$PWD/build/bin/Release:$PATH
           export EXE=sherpa-ncnn.exe
 
           .github/scripts/run-test.sh
+
+      - name: Test C API
+        shell: bash
+        run: |
+          export PATH=$PWD/build/bin/Release:$PATH
+          export EXE=decode-file-c-api.exe
+
+          .github/scripts/run-test.sh

+ 10 - 1
.github/workflows/windows-x86.yaml

@@ -70,12 +70,21 @@ jobs:
           cmake --build . --config Release -- -m:2
 
           ls -lh ./bin/Release/sherpa-ncnn.exe
+          ls -lh ./bin/Release/decode-file-c-api.exe
           ls -lh ./bin/Release/sherpa-ncnn-microphone.exe
 
-      - name: Run tests for windows
+      - name: Test sherpa-ncnn
         shell: bash
         run: |
           export PATH=$PWD/build/bin/Release:$PATH
           export EXE=sherpa-ncnn.exe
 
           .github/scripts/run-test.sh
+
+      - name: Test C API
+        shell: bash
+        run: |
+          export PATH=$PWD/build/bin/Release:$PATH
+          export EXE=decode-file-c-api.exe
+
+          .github/scripts/run-test.sh

+ 6 - 1
CMakeLists.txt

@@ -35,6 +35,7 @@ option(SHERPA_NCNN_ENABLE_PORTAUDIO "Whether to build with portaudio" ON)
 option(SHERPA_NCNN_ENABLE_JNI "Whether to build JNI internface" OFF)
 option(SHERPA_NCNN_ENABLE_BINARY "Whether to build the binary sherpa-ncnn" ON)
 option(SHERPA_NCNN_ENABLE_TEST "Whether to build tests" OFF)
+option(SHERPA_NCNN_ENABLE_C_API "Whether to build C API" ON)
 
 if(DEFINED ANDROID_ABI)
   message(STATUS "Set SHERPA_NCNN_ENABLE_JNI to ON for Android")
@@ -47,7 +48,7 @@ message(STATUS "SHERPA_NCNN_ENABLE_PORTAUDIO ${SHERPA_NCNN_ENABLE_PORTAUDIO}")
 message(STATUS "SHERPA_NCNN_ENABLE_JNI ${SHERPA_NCNN_ENABLE_JNI}")
 message(STATUS "SHERPA_NCNN_ENABLE_BINARY ${SHERPA_NCNN_ENABLE_BINARY}")
 message(STATUS "SHERPA_NCNN_ENABLE_TEST ${SHERPA_NCNN_ENABLE_TEST}")
-
+message(STATUS "SHERPA_NCNN_ENABLE_C_API ${SHERPA_NCNN_ENABLE_C_API}")
 
 if(NOT CMAKE_BUILD_TYPE)
   message(STATUS "No CMAKE_BUILD_TYPE given, default to Release")
@@ -105,3 +106,7 @@ if(SHERPA_NCNN_ENABLE_PYTHON)
 endif()
 
 add_subdirectory(sherpa-ncnn)
+
+if(SHERPA_NCNN_ENABLE_C_API)
+  add_subdirectory(c-api-examples)
+endif()

+ 3 - 0
c-api-examples/CMakeLists.txt

@@ -0,0 +1,3 @@
+include_directories(${CMAKE_SOURCE_DIR})
+add_executable(decode-file-c-api decode-file-c-api.c)
+target_link_libraries(decode-file-c-api sherpa-ncnn-c-api)

+ 126 - 0
c-api-examples/decode-file-c-api.c

@@ -0,0 +1,126 @@
+/**
+ * Copyright (c)  2023  Xiaomi Corporation (authors: Fangjun Kuang)
+ *
+ * See LICENSE for clarification regarding multiple authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sherpa-ncnn/c-api/c-api.h"
+
+const char *kUsage =
+    "\n"
+    "Usage:\n"
+    "  ./bin/decode-file-c-api \\\n"
+    "    /path/to/tokens.txt \\\n"
+    "    /path/to/encoder.ncnn.param \\\n"
+    "    /path/to/encoder.ncnn.bin \\\n"
+    "    /path/to/decoder.ncnn.param \\\n"
+    "    /path/to/decoder.ncnn.bin \\\n"
+    "    /path/to/joiner.ncnn.param \\\n"
+    "    /path/to/joiner.ncnn.bin \\\n"
+    "    /path/to/foo.wav [<num_threads> [decode_method, can be "
+    "greedy_search/modified_beam_search]]"
+    "\n\n"
+    "Please refer to \n"
+    "https://k2-fsa.github.io/sherpa/ncnn/pretrained_models/index.html\n"
+    "for a list of pre-trained models to download.\n";
+
+int32_t main(int32_t argc, char *argv[]) {
+  if (argc < 9 || argc > 11) {
+    fprintf(stderr, "%s\n", kUsage);
+    return -1;
+  }
+  SherpaNcnnModelConfig model_config;
+  model_config.tokens = argv[1];
+  model_config.encoder_param = argv[2];
+  model_config.encoder_bin = argv[3];
+  model_config.decoder_param = argv[4];
+  model_config.decoder_bin = argv[5];
+  model_config.joiner_param = argv[6];
+  model_config.joiner_bin = argv[7];
+
+  int32_t num_threads = 4;
+  if (argc >= 10 && atoi(argv[9]) > 0) {
+    num_threads = atoi(argv[9]);
+  }
+  model_config.num_threads = num_threads;
+  model_config.use_vulkan_compute = 0;
+
+  SherpaNcnnDecoderConfig decoder_config;
+  decoder_config.decoding_method = "greedy_search";
+
+  if (argc == 11) {
+    decoder_config.decoding_method = argv[10];
+  }
+  decoder_config.num_active_paths = 4;
+  decoder_config.enable_endpoint = 0;
+  decoder_config.rule1_min_trailing_silence = 2.4;
+  decoder_config.rule2_min_trailing_silence = 1.2;
+  decoder_config.rule3_min_utterance_length = 300;
+
+  SherpaNcnnRecognizer *recognizer =
+      CreateRecognizer(&model_config, &decoder_config);
+
+  const char *wav_filename = argv[8];
+  FILE *fp = fopen(wav_filename, "rb");
+  if (!fp) {
+    fprintf(stderr, "Failed to open %s\n", wav_filename);
+    return -1;
+  }
+
+  // Assume the wave header occupies 44 bytes.
+  fseek(fp, 44, SEEK_SET);
+
+  // simulate streaming
+
+#define N 3200  // 0.2 s. Sample rate is fixed to 16 kHz
+
+  int16_t buffer[N];
+  float samples[N];
+
+  while (!feof(fp)) {
+    size_t n = fread((void *)buffer, sizeof(int16_t), N, fp);
+    if (n > 0) {
+      for (size_t i = 0; i != n; ++i) {
+        samples[i] = buffer[i] / 32768.;
+      }
+      AcceptWaveform(recognizer, 16000, samples, n);
+      Decode(recognizer);
+      SherpaNcnnResult *r = GetResult(recognizer);
+      if (strlen(r->text)) {
+        fprintf(stderr, "%s\n", r->text);
+      }
+      DestroyResult(r);
+    }
+  }
+  fclose(fp);
+
+  // add some tail padding
+  float tail_paddings[4800] = {0};  // 0.3 seconds at 16 kHz sample rate
+  AcceptWaveform(recognizer, 16000, tail_paddings, 4800);
+
+  InputFinished(recognizer);
+
+  Decode(recognizer);
+  SherpaNcnnResult *r = GetResult(recognizer);
+  fprintf(stderr, "%s\n", r->text);
+  DestroyResult(r);
+
+  DestroyRecognizer(recognizer);
+
+  return 0;
+}

+ 4 - 0
python-api-examples/README.md

@@ -1,5 +1,9 @@
 # Introduction
 
+Please refer to
+<https://k2-fsa.github.io/sherpa/ncnn/python/index.html>
+for how to build a Python package.
+
 ## decode-file.py
 
 This file shows how to recognize a file.

+ 4 - 0
sherpa-ncnn/CMakeLists.txt

@@ -1,5 +1,9 @@
 add_subdirectory(csrc)
 
+if(SHERPA_NCNN_ENABLE_C_API)
+  add_subdirectory(c-api)
+endif()
+
 if(SHERPA_NCNN_ENABLE_JNI)
   add_subdirectory(jni)
 endif()

+ 9 - 0
sherpa-ncnn/c-api/CMakeLists.txt

@@ -0,0 +1,9 @@
+include_directories(${CMAKE_SOURCE_DIR})
+add_library(sherpa-ncnn-c-api c-api.cc)
+target_link_libraries(sherpa-ncnn-c-api sherpa-ncnn-core)
+
+install(TARGETS sherpa-ncnn-c-api DESTINATION lib)
+
+install(FILES c-api.h
+  DESTINATION include/sherpa-ncnn/
+)

+ 124 - 0
sherpa-ncnn/c-api/c-api.cc

@@ -0,0 +1,124 @@
+/**
+ * Copyright (c)  2023  Xiaomi Corporation (authors: Fangjun Kuang)
+ *
+ * See LICENSE for clarification regarding multiple authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sherpa-ncnn/c-api/c-api.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "sherpa-ncnn/csrc/model.h"
+#include "sherpa-ncnn/csrc/recognizer.h"
+
+#ifdef __cplusplus
+#define SHERPA_NCNN_EXTERN_C extern "C"
+#endif
+
+SHERPA_NCNN_EXTERN_C
+struct SherpaNcnnRecognizer {
+  std::unique_ptr<sherpa_ncnn::Recognizer> recognizer;
+};
+
+SherpaNcnnRecognizer *CreateRecognizer(
+    const SherpaNcnnModelConfig *in_model_config,
+    const SherpaNcnnDecoderConfig *in_decoder_config) {
+  // model_config
+  sherpa_ncnn::ModelConfig model_config;
+  model_config.encoder_param = in_model_config->encoder_param;
+  model_config.encoder_bin = in_model_config->encoder_bin;
+
+  model_config.decoder_param = in_model_config->decoder_param;
+  model_config.decoder_bin = in_model_config->decoder_bin;
+
+  model_config.joiner_param = in_model_config->joiner_param;
+  model_config.joiner_bin = in_model_config->joiner_bin;
+
+  model_config.tokens = in_model_config->tokens;
+  model_config.use_vulkan_compute = in_model_config->use_vulkan_compute;
+
+  int32_t num_threads = in_model_config->num_threads;
+
+  model_config.encoder_opt.num_threads = num_threads;
+  model_config.decoder_opt.num_threads = num_threads;
+  model_config.joiner_opt.num_threads = num_threads;
+
+  // decoder_config
+  sherpa_ncnn::DecoderConfig decoder_config;
+  decoder_config.method = in_decoder_config->decoding_method;
+  decoder_config.num_active_paths = in_decoder_config->num_active_paths;
+
+  decoder_config.enable_endpoint = in_decoder_config->enable_endpoint;
+
+  sherpa_ncnn::EndpointConfig endpoint_config;
+
+  endpoint_config.rule1.min_trailing_silence =
+      in_decoder_config->rule1_min_trailing_silence;
+
+  endpoint_config.rule2.min_trailing_silence =
+      in_decoder_config->rule2_min_trailing_silence;
+
+  endpoint_config.rule3.min_utterance_length =
+      in_decoder_config->rule3_min_utterance_length;
+
+  decoder_config.endpoint_config = endpoint_config;
+
+  float expected_sampling_rate = 16000;
+  knf::FbankOptions fbank_opts;
+  fbank_opts.frame_opts.dither = 0;
+  fbank_opts.frame_opts.snip_edges = false;
+  fbank_opts.frame_opts.samp_freq = expected_sampling_rate;
+  fbank_opts.mel_opts.num_bins = 80;
+
+  auto ans = new SherpaNcnnRecognizer;
+  ans->recognizer = std::make_unique<sherpa_ncnn::Recognizer>(
+      decoder_config, model_config, fbank_opts);
+  return ans;
+}
+
+void DestroyRecognizer(SherpaNcnnRecognizer *p) { delete p; }
+
+void AcceptWaveform(SherpaNcnnRecognizer *p, float sample_rate,
+                    const float *samples, int32_t n) {
+  p->recognizer->AcceptWaveform(sample_rate, samples, n);
+}
+
+void Decode(SherpaNcnnRecognizer *p) { p->recognizer->Decode(); }
+
+SherpaNcnnResult *GetResult(SherpaNcnnRecognizer *p) {
+  std::string text = p->recognizer->GetResult().text;
+
+  auto r = new SherpaNcnnResult;
+  r->text = new char[text.size() + 1];
+  std::copy(text.begin(), text.end(), const_cast<char *>(r->text));
+  const_cast<char *>(r->text)[text.size()] = 0;
+
+  return r;
+}
+
+void DestroyResult(const SherpaNcnnResult *r) {
+  delete[] r->text;
+  delete r;
+}
+
+void Reset(SherpaNcnnRecognizer *p) { p->recognizer->Reset(); }
+
+void InputFinished(SherpaNcnnRecognizer *p) { p->recognizer->InputFinished(); }
+
+int32_t IsEndpoint(SherpaNcnnRecognizer *p) {
+  return p->recognizer->IsEndpoint();
+}

+ 172 - 0
sherpa-ncnn/c-api/c-api.h

@@ -0,0 +1,172 @@
+/**
+ * Copyright (c)  2023  Xiaomi Corporation (authors: Fangjun Kuang)
+ *
+ * See LICENSE for clarification regarding multiple authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SHERPA_NCNN_C_API_C_API_H_
+#define SHERPA_NCNN_C_API_C_API_H_
+
+// C API for sherpa-ncnn
+//
+// Please refer to
+// https://github.com/k2-fsa/sherpa-ncnn/blob/master/c-api-examples/decode-file-c-api.c
+// for usages.
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Please refer to
+/// https://k2-fsa.github.io/sherpa/ncnn/pretrained_models/index.html
+/// to download pre-trained models. That is, you can find .ncnn.param,
+/// .ncnn.bin, and tokens.txt for this struct from there.
+typedef struct SherpaNcnnModelConfig {
+  /// Path to encoder.ncnn.param
+  const char *encoder_param;
+
+  /// Path to encoder.ncnn.bin
+  const char *encoder_bin;
+
+  /// Path to decoder.ncnn.param
+  const char *decoder_param;
+
+  /// Path to decoder.ncnn.bin
+  const char *decoder_bin;
+
+  /// Path to joiner.ncnn.param
+  const char *joiner_param;
+
+  /// Path to joiner.ncnn.bin
+  const char *joiner_bin;
+
+  /// Path to tokens.txt
+  const char *tokens;
+
+  /// If it is non-zero, and it has GPU available, and ncnn is built
+  /// with vulkan, then it will use GPU for computation.
+  /// Otherwise, it uses CPU for computation.
+  int32_t use_vulkan_compute;
+
+  /// Number of threads for neural network computation.
+  int32_t num_threads;
+} SherpaNcnnModelConfig;
+
+typedef struct SherpaNcnnDecoderConfig {
+  /// Decoding method. Supported values are:
+  /// greedy_search, modified_beam_search
+  const char *decoding_method;
+
+  /// Number of active paths for modified_beam_search.
+  /// It is ignored when decoding_method is greedy_search.
+  int32_t num_active_paths;
+
+  /// 0 to disable endpoint detection.
+  /// A non-zero value to enable endpoint detection.
+  int32_t enable_endpoint;
+
+  /// An endpoint is detected if trailing silence in seconds is larger than
+  /// this value even if nothing has been decoded.
+  /// Used only when enable_endpoint is not 0.
+  float rule1_min_trailing_silence;
+
+  /// An endpoint is detected if trailing silence in seconds is larger than
+  /// this value after something that is not blank has been decoded.
+  /// Used only when enable_endpoint is not 0.
+  float rule2_min_trailing_silence;
+
+  /// An endpoint is detected if the utterance in seconds is larger than
+  /// this value.
+  /// Used only when enable_endpoint is not 0.
+  float rule3_min_utterance_length;
+} SherpaNcnnDecoderConfig;
+
+typedef struct SherpaNcnnResult {
+  const char *text;
+  // TODO: Add more fields
+} SherpaNcnnResult;
+
+typedef struct SherpaNcnnRecognizer SherpaNcnnRecognizer;
+
+/// Create a recognizer.
+///
+/// @param model_config  Config for the model.
+/// @param decoder_config Config for decoding.
+/// @return Return a pointer to the recognizer. The user has to invoke
+//          DestroyRecognizer() to free it to avoid memory leak.
+SherpaNcnnRecognizer *CreateRecognizer(
+    const SherpaNcnnModelConfig *model_config,
+    const SherpaNcnnDecoderConfig *decoder_config);
+
+/// Free a pointer returned by CreateRecognizer()
+///
+/// @param p A pointer returned by CreateRecognizer()
+void DestroyRecognizer(SherpaNcnnRecognizer *p);
+
+/// Accept input audio samples and compute the features.
+/// The user has to invoke Decode() to run the neural network and decoding.
+///
+/// @param p  A pointer returned by CreateRecognizer().
+/// @param sample_rate  Sampler rate of the input samples. It has to be 16 kHz
+///                     for models from icefall.
+/// @param samples A pointer to a 1-D array containing audio samples.
+///                The range of samples has to be normalized to [-1, 1].
+/// @param n  Number of elements in the samples array.
+void AcceptWaveform(SherpaNcnnRecognizer *p, float sample_rate,
+                    const float *samples, int32_t n);
+
+/// If there are enough number of feature frames, it invokes the neural network
+/// computation and decoding. Otherwise, it is a no-op.
+void Decode(SherpaNcnnRecognizer *p);
+
+/// Get the decoding results so far.
+///
+/// @param p A pointer returned by CreateRecognizer().
+/// @return A pointer containing the result. The user has to invoke
+///         DestroyResult() to free the returned pointer to avoid memory leak.
+SherpaNcnnResult *GetResult(SherpaNcnnRecognizer *p);
+
+/// Destroy the pointer returned by GetResult().
+///
+/// @param r A pointer returned by GetResult()
+void DestroyResult(const SherpaNcnnResult *r);
+
+/// Reset the recognizer, which clears the neural network model state
+/// and the state for decoding.
+///
+/// @param p A pointer returned by CreateRecognizer().
+void Reset(SherpaNcnnRecognizer *p);
+
+/// Signal that no more audio samples would be available.
+/// After this call, you cannot call AcceptWaveform() any more.
+///
+/// @param p A pointer returned by CreateRecognizer()
+void InputFinished(SherpaNcnnRecognizer *p);
+
+/// Return 1 is an endpoint has been detected.
+///
+/// Caution: You have to call this function before GetResult().
+///
+/// @param p A pointer returned by CreateRecognizer()
+/// @return Return 1 if an endpoint is detected. Return 0 otherwise.
+int32_t IsEndpoint(SherpaNcnnRecognizer *p);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif  // SHERPA_NCNN_C_API_C_API_H_

+ 1 - 1
sherpa-ncnn/csrc/sherpa-ncnn.cc

@@ -65,7 +65,7 @@ for a list of pre-trained models to download.
   model_conf.decoder_opt.num_threads = num_threads;
   model_conf.joiner_opt.num_threads = num_threads;
 
-  const float expected_sampling_rate = 16000;
+  float expected_sampling_rate = 16000;
   sherpa_ncnn::DecoderConfig decoder_conf;
   if (argc == 11) {
     std::string method = argv[10];