Procházet zdrojové kódy

Add CI test and refactor the code a bit. (#4)

* Refactoring.

* Add CI tests
Fangjun Kuang před 2 roky
rodič
revize
78ce6f6233

+ 9 - 0
.clang-format

@@ -0,0 +1,9 @@
+---
+BasedOnStyle: Google
+---
+Language:               Cpp
+Cpp11BracedListStyle:   true
+Standard:               Cpp11
+DerivePointerAlignment: false
+PointerAlignment:       Right
+---

+ 68 - 0
.github/workflows/run-greedy-search-test.yaml

@@ -0,0 +1,68 @@
+name: Run greedy search tests
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - master
+
+jobs:
+  run_greedy_search_tests:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest, macos-latest, windows-latest]
+
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+
+      # see https://github.com/microsoft/setup-msbuild
+      - name: Add msbuild to PATH
+        if: startsWith(matrix.os, 'windows')
+        uses: microsoft/setup-msbuild@v1.0.2
+
+      - name: Download pretrained model and test-data
+        shell: bash
+        run: |
+          git lfs install
+          git clone https://huggingface.co/csukuangfj/sherpa-ncnn-2022-09-05
+
+      - name: Configure Cmake
+        shell: bash
+        run: |
+          mkdir build
+          cd build
+          cmake -D CMAKE_BUILD_TYPE=Release ..
+
+      - name: Build sherpa for ubuntu/macos
+        if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos')
+        run: |
+          cd build
+          make -j2
+          ln -s $PWD/../sherpa-ncnn-2022-09-05/bar .
+          ln -s $PWD/../sherpa-ncnn-2022-09-05/test_wavs .
+          ln -s $PWD/../sherpa-ncnn-2022-09-05/tokens.txt .
+          time ./bin/sherpa-ncnn ./test_wavs/1089-134686-0001.wav 4
+          time ./bin/sherpa-ncnn ./test_wavs/1221-135766-0001.wav 4
+          time ./bin/sherpa-ncnn ./test_wavs/1221-135766-0002.wav 4
+
+      - name: Build sherpa for windows
+        if: startsWith(matrix.os, 'windows')
+        shell: bash
+        run: |
+          cd build
+          cmake --build . --target sherpa-ncnn -- -m:2
+
+          ln -s $PWD/../sherpa-ncnn-2022-09-05/bar .
+          ln -s $PWD/../sherpa-ncnn-2022-09-05/test_wavs .
+          ln -s $PWD/../sherpa-ncnn-2022-09-05/tokens.txt .
+
+          ./bin/Debug/sherpa-ncnn.exe ./test_wavs/1089-134686-0001.wav 2
+          ./bin/Debug/sherpa-ncnn.exe ./test_wavs/1221-135766-0001.wav 2
+          ./bin/Debug/sherpa-ncnn.exe ./test_wavs/1221-135766-0002.wav 2
+

+ 16 - 0
CPPLINT.cfg

@@ -0,0 +1,16 @@
+# Stop searching for additional config files.
+set noparent
+
+# There are lots of functions taking arguments like `Foo& foo`
+# in k2, but cpplint complains about this style.
+filter=-runtime/references
+
+# Suppress warnings for `// TODO` without a user name.
+# cpplint recommends `// TODO(username): `.
+filter=-readability/todo
+
+# cpplint does not support lambdas decorated with __host__ __device__
+# and issues a warning: You don't need a ; after a }
+filter=-readability/braces
+
+filter=-runtime/references

+ 2 - 2
cmake/ncnn.cmake

@@ -9,8 +9,8 @@ function(download_ncnn)
 
   include(FetchContent)
 
-  set(ncnn_URL  "https://github.com/csukuangfj/ncnn/archive/refs/tags/sherpa-0.1.tar.gz")
-  set(ncnn_HASH "SHA256=bd9669798846a2727eaa05c4ce156d19e8c729a0f6ee9277d5c4ded33fd38dff")
+  set(ncnn_URL  "https://github.com/csukuangfj/ncnn/archive/refs/tags/sherpa-0.2.tar.gz")
+  set(ncnn_HASH "SHA256=69a13424e69d4fe04c24557c51d1862f1a3242f60dc60c28e3fc6c168267642e")
 
   FetchContent_Declare(ncnn
     URL               ${ncnn_URL}

+ 126 - 0
scripts/check_style_cpplint.sh

@@ -0,0 +1,126 @@
+#!/bin/bash
+#
+# Copyright      2020  Mobvoi Inc. (authors: Fangjun Kuang)
+#
+# 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.
+#
+# Usage:
+#
+# (1) To check files of the last commit
+#  ./scripts/check_style_cpplint.sh
+#
+# (2) To check changed files not committed yet
+#  ./scripts/check_style_cpplint.sh 1
+#
+# (3) To check all files in the project
+#  ./scripts/check_style_cpplint.sh 2
+
+
+cpplint_version="1.5.4"
+cur_dir=$(cd $(dirname $BASH_SOURCE) && pwd)
+sherpa_ncnn_dir=$(cd $cur_dir/.. && pwd)
+
+build_dir=$sherpa_ncnn_dir/build
+mkdir -p $build_dir
+
+cpplint_src=$build_dir/cpplint-${cpplint_version}/cpplint.py
+
+if [ ! -d "$build_dir/cpplint-${cpplint_version}" ]; then
+  pushd $build_dir
+  if command -v wget &> /dev/null; then
+    wget https://github.com/cpplint/cpplint/archive/${cpplint_version}.tar.gz
+  elif command -v curl &> /dev/null; then
+    curl -O -SL https://github.com/cpplint/cpplint/archive/${cpplint_version}.tar.gz
+  else
+    echo "Please install wget or curl to download cpplint"
+    exit 1
+  fi
+  tar xf ${cpplint_version}.tar.gz
+  rm ${cpplint_version}.tar.gz
+
+  # cpplint will report the following error for: __host__ __device__ (
+  #
+  #     Extra space before ( in function call  [whitespace/parens] [4]
+  #
+  # the following patch disables the above error
+  sed -i "3490i\        not Search(r'__host__ __device__\\\s+\\\(', fncall) and" $cpplint_src
+  popd
+fi
+
+source $sherpa_ncnn_dir/scripts/utils.sh
+
+# return true if the given file is a c++ source file
+# return false otherwise
+function is_source_code_file() {
+  case "$1" in
+    *.cc|*.h|*.cu)
+      echo true;;
+    *)
+      echo false;;
+  esac
+}
+
+function check_style() {
+  python3 $cpplint_src $1 || abort $1
+}
+
+function check_last_commit() {
+  files=$(git diff HEAD^1 --name-only --diff-filter=ACDMRUXB)
+  echo $files
+}
+
+function check_current_dir() {
+  files=$(git status -s -uno --porcelain | awk '{
+  if (NF == 4) {
+    # a file has been renamed
+    print $NF
+  } else {
+    print $2
+  }}')
+
+  echo $files
+}
+
+function do_check() {
+  case "$1" in
+    1)
+      echo "Check changed files"
+      files=$(check_current_dir)
+      ;;
+    2)
+      echo "Check all files"
+      files=$(find $sherpa_ncnn_dir/sherpa-ncnn -name "*.h" -o -name "*.cc")
+      ;;
+    *)
+      echo "Check last commit"
+      files=$(check_last_commit)
+      ;;
+  esac
+
+  for f in $files; do
+    need_check=$(is_source_code_file $f)
+    if $need_check; then
+      [[ -f $f ]] && check_style $f
+    fi
+  done
+}
+
+function main() {
+  do_check $1
+
+  ok "Great! Style check passed!"
+}
+
+cd $sherpa_ncnn_dir
+
+main $1

+ 19 - 0
scripts/utils.sh

@@ -0,0 +1,19 @@
+#!/bin/bash
+
+default='\033[0m'
+bold='\033[1m'
+red='\033[31m'
+green='\033[32m'
+
+function ok() {
+  printf "${bold}${green}[OK]${default} $1\n"
+}
+
+function error() {
+  printf "${bold}${red}[FAILED]${default} $1\n"
+}
+
+function abort() {
+  printf "${bold}${red}[FAILED]${default} $1\n"
+  exit 1
+}

+ 178 - 76
sherpa-ncnn/csrc/sherpa-ncnn.cc

@@ -16,12 +16,54 @@
  * limitations under the License.
  */
 
+#include <algorithm>
+#include <iostream>
+
 #include "kaldi-native-fbank/csrc/online-feature.h"
-#include "net.h"
+#include "net.h"  // NOLINT
 #include "sherpa-ncnn/csrc/symbol-table.h"
 #include "sherpa-ncnn/csrc/wave-reader.h"
-#include <algorithm>
-#include <iostream>
+
+/** Compute fbank features of the input wave filename.
+ *
+ * @param wav_filename. Path to a mono wave file.
+ * @param expected_sampling_rate  Expected sampling rate of the input wave file.
+ * @return Return a mat of shape (num_frames, feature_dim).
+ *         Note: ans.w == feature_dim; ans.h == num_frames
+ *
+ */
+static ncnn::Mat ComputeFeatures(const std::string &wav_filename,
+                                 float expected_sampling_rate) {
+  std::vector<float> samples =
+      sherpa_ncnn::ReadWave(wav_filename, expected_sampling_rate);
+
+  float duration = samples.size() / expected_sampling_rate;
+
+  std::cout << "wav filename: " << wav_filename << "\n";
+  std::cout << "wav duration (s): " << duration << "\n";
+
+  knf::FbankOptions opts;
+  opts.frame_opts.dither = 0;
+  opts.frame_opts.snip_edges = false;
+  opts.frame_opts.samp_freq = expected_sampling_rate;
+
+  opts.mel_opts.num_bins = 80;
+
+  knf::OnlineFbank fbank(opts);
+  fbank.AcceptWaveform(expected_sampling_rate, samples.data(), samples.size());
+  fbank.InputFinished();
+
+  int32_t feature_dim = 80;
+  ncnn::Mat features;
+  features.create(feature_dim, fbank.NumFramesReady());
+
+  for (int32_t i = 0; i != fbank.NumFramesReady(); ++i) {
+    const float *f = fbank.GetFrame(i);
+    std::copy(f, f + feature_dim, features.row(i));
+  }
+
+  return features;
+}
 
 static void InitNet(ncnn::Net &net, const std::string &param,
                     const std::string &model) {
@@ -36,7 +78,120 @@ static void InitNet(ncnn::Net &net, const std::string &param,
   }
 }
 
-int main() {
+/** Run the encoder network.
+ *
+ * @param encoder_net The encoder model.
+ * @param features  A 2-d mat of shape (num_frames, feature_dim).
+ *                  Note: features.w = feature_dim.
+ *                        features.h = num_frames.
+ * @param num_threads  Number of threads to use for computation.
+ *
+ * @return Return the output of the encoder. Its shape is
+ *  (num_frames, encoder_dim).
+ *  Note: ans.w == encoder_dim; ans.h == num_frames
+ */
+static ncnn::Mat RunEncoder(ncnn::Net &encoder_net, ncnn::Mat &features,
+                            int32_t num_threads) {
+  int32_t num_encoder_layers = 12;
+  int32_t batch_size = 1;
+  int32_t d_model = 512;
+  int32_t rnn_hidden_size = 1024;
+
+  ncnn::Mat h0;
+  h0.create(d_model, num_encoder_layers);
+  ncnn::Mat c0;
+  c0.create(rnn_hidden_size, num_encoder_layers);
+  h0.fill(0);
+  c0.fill(0);
+
+  ncnn::Mat feature_lengths(1);
+  feature_lengths[0] = features.h;
+
+  ncnn::Extractor encoder_ex = encoder_net.create_extractor();
+  encoder_ex.set_num_threads(num_threads);
+
+  encoder_ex.input("in0", features);
+  encoder_ex.input("in1", feature_lengths);
+  encoder_ex.input("in2", h0);
+  encoder_ex.input("in3", c0);
+
+  ncnn::Mat encoder_out;
+  encoder_ex.extract("out0", encoder_out);
+
+  return encoder_out;
+}
+
+/** Run the decoder network.
+ *
+ * @param decoder_net The decoder network.
+ * @param  decoder_input A mat of shape (context_size,). Note: Its underlying
+ *                       content consists of integers, though its type is float.
+ * @param num_threads  Number of threads to use for computation.
+ *
+ * @return Return a mat of shape (decoder_dim,)
+ */
+static ncnn::Mat RunDecoder(ncnn::Net &decoder_net, ncnn::Mat &decoder_input,
+                            int32_t num_threads) {
+  ncnn::Extractor decoder_ex = decoder_net.create_extractor();
+  decoder_ex.set_num_threads(num_threads);
+
+  ncnn::Mat decoder_out;
+  decoder_ex.input("in0", decoder_input);
+  decoder_ex.extract("out0", decoder_out);
+  decoder_out = decoder_out.reshape(decoder_out.w);
+
+  return decoder_out;
+}
+
+/** Run the joiner network.
+ *
+ * @param joiner_net The joiner network.
+ * @param encoder_out  A mat of shape (encoder_dim,)
+ * @param decoder_out  A mat of shape (decoder_dim,)
+ * @param num_threads  Number of threads to use for computation.
+ *
+ * @return Return the joiner output which is of shape (vocab_size,)
+ */
+static ncnn::Mat RunJoiner(ncnn::Net &joiner_net, ncnn::Mat &encoder_out,
+                           ncnn::Mat &decoder_out, int32_t num_threads) {
+  auto joiner_ex = joiner_net.create_extractor();
+  joiner_ex.set_num_threads(num_threads);
+  joiner_ex.input("in0", encoder_out);
+  joiner_ex.input("in1", decoder_out);
+
+  ncnn::Mat joiner_out;
+  joiner_ex.extract("out0", joiner_out);
+  return joiner_out;
+}
+
+int main(int argc, char *argv[]) {
+  if (argc == 1 || argc > 3) {
+    const char *usage = R"usage(
+Usage:
+  ./sherpa-ncnn /path/to/foo.wav [num_threads]
+
+We assume that you have placed the models files in
+the directory bar. That is, you should have the
+following files:
+  bar/encoder_jit_trace-iter-468000-avg-16-pnnx.ncnn.param
+  bar/encoder_jit_trace-iter-468000-avg-16-pnnx.ncnn.bin
+  bar/decoder_jit_trace-iter-468000-avg-16-pnnx.ncnn.param
+  bar/decoder_jit_trace-iter-468000-avg-16-pnnx.ncnn.bin
+  bar/joiner_jit_trace-iter-468000-avg-16-pnnx.ncnn.param
+  bar/joiner_jit_trace-iter-468000-avg-16-pnnx.ncnn.bin
+
+We also assume that you have ./tokens.txt in the current directory.
+
+You can find the above files in the following repository:
+
+https://huggingface.co/csukuangfj/sherpa-ncnn-2022-09-05
+)usage";
+    std::cerr << usage << "\n";
+
+    return 0;
+  }
+
+  sherpa_ncnn::SymbolTable sym("./tokens.txt");
 
   std::string encoder_param =
       "bar/encoder_jit_trace-iter-468000-avg-16-pnnx.ncnn.param";
@@ -56,9 +211,14 @@ int main() {
   std::string joiner_model =
       "bar/joiner_jit_trace-iter-468000-avg-16-pnnx.ncnn.bin";
 
-  std::string wav1 = "./test_wavs/1089-134686-0001.wav";
-  // wav1 = "./test_wavs/1221-135766-0001.wav";
-  wav1 = "./test_wavs/1221-135766-0002.wav";
+  std::string wav = argv[1];
+  int32_t num_threads = 5;
+  if (argc == 3) {
+    num_threads = atoi(argv[2]);
+  }
+  std::cout << "number of threads: " << num_threads << "\n";
+
+  ncnn::Mat features = ComputeFeatures(wav, 16000);
 
   ncnn::Net encoder_net;
   encoder_net.opt.use_packing_layout = false;
@@ -74,52 +234,7 @@ int main() {
   InitNet(decoder_net, decoder_param, decoder_model);
   InitNet(joiner_net, joiner_param, joiner_model);
 
-  std::vector<float> samples = sherpa_ncnn::ReadWave(wav1, 16000);
-
-  knf::FbankOptions opts;
-  opts.frame_opts.dither = 0;
-  opts.frame_opts.snip_edges = false;
-  opts.frame_opts.samp_freq = 16000;
-
-  opts.mel_opts.num_bins = 80;
-
-  knf::OnlineFbank fbank(opts);
-  fbank.AcceptWaveform(16000, samples.data(), samples.size());
-  fbank.InputFinished();
-
-  int32_t num_encoder_layers = 12;
-  int32_t batch_size = 1;
-  int32_t d_model = 512;
-  int32_t rnn_hidden_size = 1024;
-
-  ncnn::Mat h0;
-  h0.create(d_model, num_encoder_layers);
-  ncnn::Mat c0;
-  c0.create(rnn_hidden_size, num_encoder_layers);
-  h0.fill(0);
-  c0.fill(0);
-
-  int32_t feature_dim = 80;
-  ncnn::Mat features;
-  features.create(feature_dim, fbank.NumFramesReady());
-
-  for (int32_t i = 0; i != fbank.NumFramesReady(); ++i) {
-    const float *f = fbank.GetFrame(i);
-    std::copy(f, f + feature_dim, features.row(i));
-  }
-
-  ncnn::Mat feature_lengths(1);
-  feature_lengths[0] = features.h;
-
-  ncnn::Extractor encoder_ex = encoder_net.create_extractor();
-
-  encoder_ex.input("in0", features);
-  encoder_ex.input("in1", feature_lengths);
-  encoder_ex.input("in2", h0);
-  encoder_ex.input("in3", c0);
-
-  ncnn::Mat encoder_out;
-  encoder_ex.extract("out0", encoder_out);
+  ncnn::Mat encoder_out = RunEncoder(encoder_net, features, num_threads);
 
   int32_t context_size = 2;
   int32_t blank_id = 0;
@@ -130,45 +245,32 @@ int main() {
   static_cast<int32_t *>(decoder_input)[1] = blank_id + 2;
   decoder_input.fill(blank_id);
 
-  ncnn::Extractor decoder_ex = decoder_net.create_extractor();
-  ncnn::Mat decoder_out;
-  decoder_ex.input("in0", decoder_input);
-  decoder_ex.extract("out0", decoder_out);
-  decoder_out = decoder_out.reshape(decoder_out.w);
+  ncnn::Mat decoder_out = RunDecoder(decoder_net, decoder_input, num_threads);
 
-  ncnn::Mat joiner_out;
   for (int32_t t = 0; t != encoder_out.h; ++t) {
     ncnn::Mat encoder_out_t(512, encoder_out.row(t));
+    ncnn::Mat joiner_out =
+        RunJoiner(joiner_net, encoder_out_t, decoder_out, num_threads);
 
-    auto joiner_ex = joiner_net.create_extractor();
-    joiner_ex.input("in0", encoder_out_t);
-    joiner_ex.input("in1", decoder_out);
-
-    joiner_ex.extract("out0", joiner_out);
-
-    auto y = static_cast<int32_t>(
-        std::distance(static_cast<const float *>(joiner_out),
-                      std::max_element(static_cast<const float *>(joiner_out),
-                                       static_cast<const float *>(joiner_out) +
-                                           joiner_out.w)));
+    auto y = static_cast<int32_t>(std::distance(
+        static_cast<const float *>(joiner_out),
+        std::max_element(
+            static_cast<const float *>(joiner_out),
+            static_cast<const float *>(joiner_out) + joiner_out.w)));
 
     if (y != blank_id) {
       static_cast<int32_t *>(decoder_input)[0] = hyp.back();
       static_cast<int32_t *>(decoder_input)[1] = y;
       hyp.push_back(y);
 
-      decoder_ex = decoder_net.create_extractor();
-      decoder_ex.input("in0", decoder_input);
-      decoder_ex.extract("out0", decoder_out);
-      decoder_out = decoder_out.reshape(decoder_out.w);
+      decoder_out = RunDecoder(decoder_net, decoder_input, num_threads);
     }
   }
   std::string text;
-  sherpa_ncnn::SymbolTable sym("./tokens.txt");
   for (int32_t i = context_size; i != hyp.size(); ++i) {
     text += sym[hyp[i]];
   }
 
-  fprintf(stderr, "%s\n", text.c_str());
+  std::cout << "Recognition result for " << wav << "\n" << text << "\n";
   return 0;
 }

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

@@ -75,4 +75,4 @@ std::ostream &operator<<(std::ostream &os, const SymbolTable &symbol_table) {
   return os << symbol_table.ToString();
 }
 
-} // namespace sherpa_ncnn
+}  // namespace sherpa_ncnn

+ 4 - 4
sherpa-ncnn/csrc/symbol-table.h

@@ -26,7 +26,7 @@ namespace sherpa_ncnn {
 
 /// It manages mapping between symbols and integer IDs.
 class SymbolTable {
-public:
+ public:
   SymbolTable() = default;
   /// Construct a symbol table from a file.
   /// Each line in the file contains two fields:
@@ -50,13 +50,13 @@ public:
   /// Return true if there is a given symbol in the symbol table.
   bool contains(const std::string &sym) const;
 
-private:
+ private:
   std::unordered_map<std::string, int32_t> sym2id_;
   std::unordered_map<int32_t, std::string> id2sym_;
 };
 
 std::ostream &operator<<(std::ostream &os, const SymbolTable &symbol_table);
 
-} // namespace sherpa_ncnn
+}  // namespace sherpa_ncnn
 
-#endif // SHERPA_NCNN_CSRC_SYMBOL_TABLE_H_
+#endif  // SHERPA_NCNN_CSRC_SYMBOL_TABLE_H_

+ 10 - 10
sherpa-ncnn/csrc/wave-reader.cc

@@ -16,14 +16,14 @@
  * limitations under the License.
  */
 
+#include "sherpa-ncnn/csrc/wave-reader.h"
+
 #include <cassert>
 #include <fstream>
 #include <iostream>
 #include <utility>
 #include <vector>
 
-#include "sherpa-ncnn/csrc/wave-reader.h"
-
 namespace sherpa_ncnn {
 namespace {
 // see http://soundfile.sapp.org/doc/WaveFormat/
@@ -38,12 +38,12 @@ struct WaveHeader {
     //                     E V A W
     assert(format == 0x45564157);
     assert(subchunk1_id == 0x20746d66);
-    assert(subchunk1_size == 16); // 16 for PCM
-    assert(audio_format == 1);    // 1 for PCM
-    assert(num_channels == 1);    // we support only single channel for now
+    assert(subchunk1_size == 16);  // 16 for PCM
+    assert(audio_format == 1);     // 1 for PCM
+    assert(num_channels == 1);     // we support only single channel for now
     assert(byte_rate == sample_rate * num_channels * bits_per_sample / 8);
     assert(block_align == num_channels * bits_per_sample / 8);
-    assert(bits_per_sample == 16); // we support only 16 bits per sample
+    assert(bits_per_sample == 16);  // we support only 16 bits per sample
   }
 
   int32_t chunk_id;
@@ -67,7 +67,7 @@ static_assert(sizeof(WaveHeader) == 44, "");
 std::vector<float> ReadWaveImpl(std::istream &is, float *sample_rate) {
   WaveHeader header;
   is.read(reinterpret_cast<char *>(&header), sizeof(header));
-  assert((bool)is);
+  assert(static_cast<bool>(is));
 
   header.Validate();
 
@@ -79,7 +79,7 @@ std::vector<float> ReadWaveImpl(std::istream &is, float *sample_rate) {
 
   is.read(reinterpret_cast<char *>(samples.data()), header.subchunk2_size);
 
-  assert((bool)is);
+  assert(static_cast<bool>(is));
 
   std::vector<float> ans(samples.size());
   for (int32_t i = 0; i != ans.size(); ++i) {
@@ -89,7 +89,7 @@ std::vector<float> ReadWaveImpl(std::istream &is, float *sample_rate) {
   return ans;
 }
 
-} // namespace
+}  // namespace
 
 std::vector<float> ReadWave(const std::string &filename,
                             float expected_sample_rate) {
@@ -104,4 +104,4 @@ std::vector<float> ReadWave(const std::string &filename,
   return samples;
 }
 
-} // namespace sherpa_ncnn
+}  // namespace sherpa_ncnn

+ 2 - 2
sherpa-ncnn/csrc/wave-reader.h

@@ -36,6 +36,6 @@ namespace sherpa_ncnn {
 std::vector<float> ReadWave(const std::string &filename,
                             float expected_sample_rate);
 
-} // namespace sherpa_ncnn
+}  // namespace sherpa_ncnn
 
-#endif // SHERPA_NCNN_CSRC_WAVE_READER_H_
+#endif  // SHERPA_NCNN_CSRC_WAVE_READER_H_