diff --git a/sherpa-onnx/csrc/CMakeLists.txt b/sherpa-onnx/csrc/CMakeLists.txt index 46377c1bd..7fc07117a 100644 --- a/sherpa-onnx/csrc/CMakeLists.txt +++ b/sherpa-onnx/csrc/CMakeLists.txt @@ -176,32 +176,42 @@ if(SHERPA_ONNX_ENABLE_PORTAUDIO) microphone.cc ) + add_executable(sherpa-onnx-vad-microphone + sherpa-onnx-vad-microphone.cc + microphone.cc + ) + if(BUILD_SHARED_LIBS) set(PA_LIB portaudio) else() set(PA_LIB portaudio_static) endif() - target_link_libraries(sherpa-onnx-microphone ${PA_LIB} sherpa-onnx-core) - target_link_libraries(sherpa-onnx-microphone-offline ${PA_LIB} sherpa-onnx-core) + set(exes + sherpa-onnx-microphone + sherpa-onnx-microphone-offline + sherpa-onnx-vad-microphone + ) + foreach(exe IN LISTS exes) + target_link_libraries(${exe} ${PA_LIB} sherpa-onnx-core) + endforeach() if(NOT WIN32) - target_link_libraries(sherpa-onnx-microphone "-Wl,-rpath,${SHERPA_ONNX_RPATH_ORIGIN}/../lib") - target_link_libraries(sherpa-onnx-microphone "-Wl,-rpath,${SHERPA_ONNX_RPATH_ORIGIN}/../../../sherpa_onnx/lib") - - target_link_libraries(sherpa-onnx-microphone-offline "-Wl,-rpath,${SHERPA_ONNX_RPATH_ORIGIN}/../lib") - target_link_libraries(sherpa-onnx-microphone-offline "-Wl,-rpath,${SHERPA_ONNX_RPATH_ORIGIN}/../../../sherpa_onnx/lib") + foreach(exe IN LISTS exes) + target_link_libraries(${exe} "-Wl,-rpath,${SHERPA_ONNX_RPATH_ORIGIN}/../lib") + target_link_libraries(${exe} "-Wl,-rpath,${SHERPA_ONNX_RPATH_ORIGIN}/../../../sherpa_onnx/lib") + endforeach() if(SHERPA_ONNX_ENABLE_PYTHON) - target_link_libraries(sherpa-onnx-microphone "-Wl,-rpath,${SHERPA_ONNX_RPATH_ORIGIN}/../lib/python${PYTHON_VERSION}/site-packages/sherpa_onnx/lib") - target_link_libraries(sherpa-onnx-microphone-offline "-Wl,-rpath,${SHERPA_ONNX_RPATH_ORIGIN}/../lib/python${PYTHON_VERSION}/site-packages/sherpa_onnx/lib") + + foreach(exe IN LISTS exes) + target_link_libraries(${exe} "-Wl,-rpath,${SHERPA_ONNX_RPATH_ORIGIN}/../lib/python${PYTHON_VERSION}/site-packages/sherpa_onnx/lib") + endforeach() endif() endif() install( - TARGETS - sherpa-onnx-microphone - sherpa-onnx-microphone-offline + TARGETS ${exes} DESTINATION bin ) diff --git a/sherpa-onnx/csrc/sherpa-onnx-vad-microphone.cc b/sherpa-onnx/csrc/sherpa-onnx-vad-microphone.cc new file mode 100644 index 000000000..4e0fba704 --- /dev/null +++ b/sherpa-onnx/csrc/sherpa-onnx-vad-microphone.cc @@ -0,0 +1,145 @@ +// sherpa-onnx/csrc/sherpa-onnx-vad-microphone.cc +// +// Copyright (c) 2022-2023 Xiaomi Corporation + +#include +#include +#include + +#include +#include // std::tolower +#include +#include + +#include "portaudio.h" // NOLINT +#include "sherpa-onnx/csrc/display.h" +#include "sherpa-onnx/csrc/microphone.h" +#include "sherpa-onnx/csrc/parse-options.h" +#include "sherpa-onnx/csrc/vad-model-config.h" +#include "sherpa-onnx/csrc/vad-model.h" + +bool stop = false; +std::mutex mutex; +std::queue> queue; + +static int32_t RecordCallback(const void *input_buffer, + void * /*output_buffer*/, + unsigned long frames_per_buffer, // NOLINT + const PaStreamCallbackTimeInfo * /*time_info*/, + PaStreamCallbackFlags /*status_flags*/, + void *user_data) { + std::lock_guard lock(mutex); + + queue.emplace( + reinterpret_cast(input_buffer), + reinterpret_cast(input_buffer) + frames_per_buffer); + + return stop ? paComplete : paContinue; +} + +static void Handler(int32_t sig) { + stop = true; + fprintf(stderr, "\nCaught Ctrl + C. Exiting...\n"); +} + +int32_t main(int32_t argc, char *argv[]) { + signal(SIGINT, Handler); + + const char *kUsageMessage = R"usage( +This program shows how to use VAD in sherpa-onnx. + + ./bin/sherpa-onnx-vad-microphone \ + --silero-vad-model=/path/to/silero_vad.onnx \ + --provider=cpu \ + --num-threads=1 + +Please download silero_vad.onnx from +https://github.com/snakers4/silero-vad/blob/master/files/silero_vad.onnx + +For instance, use +wget https://github.com/snakers4/silero-vad/raw/master/files/silero_vad.onnx +)usage"; + + sherpa_onnx::ParseOptions po(kUsageMessage); + sherpa_onnx::VadModelConfig config; + + config.Register(&po); + po.Read(argc, argv); + if (po.NumArgs() != 0) { + po.PrintUsage(); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "%s\n", config.ToString().c_str()); + + if (!config.Validate()) { + fprintf(stderr, "Errors in config!\n"); + return -1; + } + + sherpa_onnx::Microphone mic; + + PaDeviceIndex num_devices = Pa_GetDeviceCount(); + fprintf(stderr, "Num devices: %d\n", num_devices); + + PaStreamParameters param; + + param.device = Pa_GetDefaultInputDevice(); + if (param.device == paNoDevice) { + fprintf(stderr, "No default input device found\n"); + exit(EXIT_FAILURE); + } + fprintf(stderr, "Use default device: %d\n", param.device); + + const PaDeviceInfo *info = Pa_GetDeviceInfo(param.device); + fprintf(stderr, " Name: %s\n", info->name); + fprintf(stderr, " Max input channels: %d\n", info->maxInputChannels); + + param.channelCount = 1; + param.sampleFormat = paFloat32; + + param.suggestedLatency = info->defaultLowInputLatency; + param.hostApiSpecificStreamInfo = nullptr; + float sample_rate = 16000; + + PaStream *stream; + PaError err = + Pa_OpenStream(&stream, ¶m, nullptr, /* &outputParameters, */ + sample_rate, + 0, // frames per buffer + paClipOff, // we won't output out of range samples + // so don't bother clipping them + RecordCallback, nullptr); + if (err != paNoError) { + fprintf(stderr, "portaudio error: %s\n", Pa_GetErrorText(err)); + exit(EXIT_FAILURE); + } + + err = Pa_StartStream(stream); + fprintf(stderr, "Started\n"); + + if (err != paNoError) { + fprintf(stderr, "portaudio error: %s\n", Pa_GetErrorText(err)); + exit(EXIT_FAILURE); + } + + while (!stop) { + { + std::lock_guard lock(mutex); + while (!queue.empty()) { + fprintf(stderr, "%d\n", (int)queue.size()); + queue.pop(); + } + } + Pa_Sleep(100); // sleep for 100ms + stop = true; + } + + err = Pa_CloseStream(stream); + if (err != paNoError) { + fprintf(stderr, "portaudio error: %s\n", Pa_GetErrorText(err)); + exit(EXIT_FAILURE); + } + + return 0; +} diff --git a/sherpa-onnx/csrc/silero-vad-model-config.cc b/sherpa-onnx/csrc/silero-vad-model-config.cc index 1cf5df747..7a5633836 100644 --- a/sherpa-onnx/csrc/silero-vad-model-config.cc +++ b/sherpa-onnx/csrc/silero-vad-model-config.cc @@ -33,6 +33,11 @@ void SileroVadModelConfig::Register(ParseOptions *po) { } bool SileroVadModelConfig::Validate() const { + if (model.empty()) { + SHERPA_ONNX_LOGE("Please provide --silero-vad-model"); + return false; + } + if (!FileExists(model)) { SHERPA_ONNX_LOGE("Silero vad model file %s does not exist", model.c_str()); return false;