Skip to content

Commit e7d617d

Browse files
committed
Add pybind11 bindings for online websocket server
- Create OnlineWebsocketServerApp class with proper separation of library and executable code - Add online-websocket-server.h header file for public interface - Add online-websocket-server-app.cc with class implementation and StartServer() function for Python bindings - Update online-websocket-server.cc to only contain main() - Add websocket sources to sherpa-onnx-core library conditionally - Create Python pybind wrapper (online-websocket-server-app.cc/h)
1 parent c305335 commit e7d617d

9 files changed

Lines changed: 281 additions & 107 deletions

sherpa-onnx/csrc/CMakeLists.txt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,13 @@ if(SHERPA_ONNX_ENABLE_CHECK)
306306
list(APPEND sources log.cc)
307307
endif()
308308

309+
if(SHERPA_ONNX_ENABLE_WEBSOCKET)
310+
list(APPEND sources
311+
online-websocket-server-impl.cc
312+
online-websocket-server-app.cc
313+
)
314+
endif()
315+
309316
# Always static build
310317
add_library(sherpa-onnx-core STATIC ${sources})
311318

@@ -442,6 +449,15 @@ if(SHERPA_ONNX_ENABLE_CHECK)
442449
endif()
443450
endif()
444451

452+
if(SHERPA_ONNX_ENABLE_WEBSOCKET)
453+
target_compile_definitions(sherpa-onnx-core PRIVATE ASIO_STANDALONE)
454+
target_compile_definitions(sherpa-onnx-core PRIVATE _WEBSOCKETPP_CPP11_STL_)
455+
target_compile_definitions(sherpa-onnx-core PUBLIC SHERPA_ONNX_ENABLE_WEBSOCKET=1)
456+
if(NOT WIN32)
457+
target_compile_options(sherpa-onnx-core PRIVATE -Wno-deprecated-declarations)
458+
endif()
459+
endif()
460+
445461
if(NOT BUILD_SHARED_LIBS AND CMAKE_SYSTEM_NAME STREQUAL Linux)
446462
# This is for linux arm32 and arm64
447463
target_link_libraries(sherpa-onnx-core -ldl)
@@ -711,11 +727,7 @@ if(SHERPA_ONNX_ENABLE_PORTAUDIO AND SHERPA_ONNX_ENABLE_BINARY)
711727
endif()
712728

713729
if(SHERPA_ONNX_ENABLE_WEBSOCKET AND SHERPA_ONNX_ENABLE_BINARY)
714-
add_definitions(-DASIO_STANDALONE)
715-
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
716-
717730
add_executable(sherpa-onnx-online-websocket-server
718-
online-websocket-server-impl.cc
719731
online-websocket-server.cc
720732
)
721733
target_link_libraries(sherpa-onnx-online-websocket-server sherpa-onnx-core)
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// sherpa-onnx/csrc/online-websocket-server-app.cc
2+
//
3+
// Copyright (c) 2022-2023 Xiaomi Corporation
4+
// Copyright (c) 2025 Uniphore (Author: Manickavela A)
5+
6+
#include "sherpa-onnx/csrc/online-websocket-server.h"
7+
8+
#include <csignal>
9+
#include <vector>
10+
11+
#include "asio.hpp" // NOLINT
12+
#include "sherpa-onnx/csrc/macros.h"
13+
#include "sherpa-onnx/csrc/online-websocket-server-impl.h"
14+
#include "sherpa-onnx/csrc/parse-options.h"
15+
16+
static constexpr const char *kUsageMessage = R"(
17+
Automatic speech recognition with sherpa-onnx using websocket.
18+
19+
Usage:
20+
21+
./bin/sherpa-onnx-online-websocket-server --help
22+
23+
./bin/sherpa-onnx-online-websocket-server \
24+
--port=6006 \
25+
--num-work-threads=5 \
26+
--tokens=/path/to/tokens.txt \
27+
--encoder=/path/to/encoder.onnx \
28+
--decoder=/path/to/decoder.onnx \
29+
--joiner=/path/to/joiner.onnx \
30+
--log-file=./log.txt \
31+
--max-batch-size=5 \
32+
--loop-interval-ms=10
33+
34+
Please refer to
35+
https://k2-fsa.github.io/sherpa/onnx/pretrained_models/index.html
36+
for a list of pre-trained models to download.
37+
)";
38+
39+
// Global pointer for signal handling
40+
static OnlineWebsocketServerApp *g_server_app = nullptr;
41+
42+
static void SignalHandler(int32_t signal) {
43+
SHERPA_ONNX_LOGE("Caught signal %d, stopping server...", signal);
44+
if (g_server_app != nullptr) {
45+
g_server_app->Stop();
46+
}
47+
}
48+
49+
OnlineWebsocketServerApp::OnlineWebsocketServerApp(int32_t argc, char *argv[])
50+
: argc_(argc), argv_(argv) {
51+
g_server_app = this;
52+
}
53+
54+
void OnlineWebsocketServerApp::Run() {
55+
sherpa_onnx::ParseOptions po(kUsageMessage);
56+
57+
sherpa_onnx::OnlineWebsocketServerConfig config;
58+
59+
// the server will listen on this port
60+
int32_t port = 6006;
61+
62+
// size of the thread pool for handling network connections
63+
int32_t num_io_threads = 1;
64+
65+
// size of the thread pool for neural network computation and decoding
66+
int32_t num_work_threads = 3;
67+
68+
po.Register("num-io-threads", &num_io_threads,
69+
"Thread pool size for network connections.");
70+
71+
po.Register("num-work-threads", &num_work_threads,
72+
"Thread pool size for for neural network "
73+
"computation and decoding.");
74+
75+
po.Register("port", &port, "The port on which the server will listen.");
76+
77+
config.Register(&po);
78+
79+
if (argc_ == 1) {
80+
po.PrintUsage();
81+
return;
82+
}
83+
84+
po.Read(argc_, argv_);
85+
86+
if (po.NumArgs() != 0) {
87+
SHERPA_ONNX_LOGE("Unrecognized positional arguments!");
88+
po.PrintUsage();
89+
return;
90+
}
91+
92+
config.Validate();
93+
94+
// Register signal handlers
95+
std::signal(SIGINT, SignalHandler);
96+
std::signal(SIGTERM, SignalHandler);
97+
98+
sherpa_onnx::OnlineWebsocketServer server(io_conn_, io_work_, config);
99+
server.Run(port);
100+
101+
SHERPA_ONNX_LOGE("Started!");
102+
SHERPA_ONNX_LOGE("Listening on: %d", port);
103+
SHERPA_ONNX_LOGE("Number of work threads: %d", num_work_threads);
104+
105+
// give some work to do for the io_work pool
106+
auto work_guard = asio::make_work_guard(io_work_);
107+
108+
// decrement since the main thread is also used for network communications
109+
for (int32_t i = 0; i < num_io_threads - 1; ++i) {
110+
io_threads_.emplace_back([this]() { io_conn_.run(); });
111+
}
112+
113+
for (int32_t i = 0; i < num_work_threads; ++i) {
114+
work_threads_.emplace_back([this]() { io_work_.run(); });
115+
}
116+
117+
io_conn_.run();
118+
119+
for (auto &t : io_threads_) {
120+
t.join();
121+
}
122+
123+
for (auto &t : work_threads_) {
124+
t.join();
125+
}
126+
127+
g_server_app = nullptr;
128+
}
129+
130+
void OnlineWebsocketServerApp::Stop() {
131+
if (shutdown_requested_.exchange(true)) {
132+
return; // Already requested
133+
}
134+
135+
SHERPA_ONNX_LOGE("Stopping server...");
136+
io_conn_.stop();
137+
io_work_.stop();
138+
}
139+
140+
void StartServer(int32_t argc, char *argv[]) {
141+
OnlineWebsocketServerApp app(argc, argv);
142+
app.Run();
143+
}
Lines changed: 4 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,12 @@
11
// sherpa-onnx/csrc/online-websocket-server.cc
22
//
33
// Copyright (c) 2022-2023 Xiaomi Corporation
4+
// Copyright (c) 2025 Uniphore (Author: Manickavela A)
45

5-
#include <vector>
6-
7-
#include "asio.hpp" // NOLINT
8-
#include "sherpa-onnx/csrc/macros.h"
9-
#include "sherpa-onnx/csrc/online-websocket-server-impl.h"
10-
#include "sherpa-onnx/csrc/parse-options.h"
11-
12-
static constexpr const char *kUsageMessage = R"(
13-
Automatic speech recognition with sherpa-onnx using websocket.
14-
15-
Usage:
16-
17-
./bin/sherpa-onnx-online-websocket-server --help
18-
19-
./bin/sherpa-onnx-online-websocket-server \
20-
--port=6006 \
21-
--num-work-threads=5 \
22-
--tokens=/path/to/tokens.txt \
23-
--encoder=/path/to/encoder.onnx \
24-
--decoder=/path/to/decoder.onnx \
25-
--joiner=/path/to/joiner.onnx \
26-
--log-file=./log.txt \
27-
--max-batch-size=5 \
28-
--loop-interval-ms=10
29-
30-
Please refer to
31-
https://k2-fsa.github.io/sherpa/onnx/pretrained_models/index.html
32-
for a list of pre-trained models to download.
33-
)";
6+
#include "sherpa-onnx/csrc/online-websocket-server.h"
347

358
int32_t main(int32_t argc, char *argv[]) {
36-
sherpa_onnx::ParseOptions po(kUsageMessage);
37-
38-
sherpa_onnx::OnlineWebsocketServerConfig config;
39-
40-
// the server will listen on this port
41-
int32_t port = 6006;
42-
43-
// size of the thread pool for handling network connections
44-
int32_t num_io_threads = 1;
45-
46-
// size of the thread pool for neural network computation and decoding
47-
int32_t num_work_threads = 3;
48-
49-
po.Register("num-io-threads", &num_io_threads,
50-
"Thread pool size for network connections.");
51-
52-
po.Register("num-work-threads", &num_work_threads,
53-
"Thread pool size for for neural network "
54-
"computation and decoding.");
55-
56-
po.Register("port", &port, "The port on which the server will listen.");
57-
58-
config.Register(&po);
59-
60-
if (argc == 1) {
61-
po.PrintUsage();
62-
exit(EXIT_FAILURE);
63-
}
64-
65-
po.Read(argc, argv);
66-
67-
if (po.NumArgs() != 0) {
68-
SHERPA_ONNX_LOGE("Unrecognized positional arguments!");
69-
po.PrintUsage();
70-
exit(EXIT_FAILURE);
71-
}
72-
73-
config.Validate();
74-
75-
asio::io_context io_conn; // for network connections
76-
asio::io_context io_work; // for neural network and decoding
77-
78-
sherpa_onnx::OnlineWebsocketServer server(io_conn, io_work, config);
79-
server.Run(port);
80-
81-
SHERPA_ONNX_LOGE("Started!");
82-
SHERPA_ONNX_LOGE("Listening on: %d", port);
83-
SHERPA_ONNX_LOGE("Number of work threads: %d", num_work_threads);
84-
85-
// give some work to do for the io_work pool
86-
auto work_guard = asio::make_work_guard(io_work);
87-
88-
std::vector<std::thread> io_threads;
89-
90-
// decrement since the main thread is also used for network communications
91-
for (int32_t i = 0; i < num_io_threads - 1; ++i) {
92-
io_threads.emplace_back([&io_conn]() { io_conn.run(); });
93-
}
94-
95-
std::vector<std::thread> work_threads;
96-
for (int32_t i = 0; i < num_work_threads; ++i) {
97-
work_threads.emplace_back([&io_work]() { io_work.run(); });
98-
}
99-
100-
io_conn.run();
101-
102-
for (auto &t : io_threads) {
103-
t.join();
104-
}
105-
106-
for (auto &t : work_threads) {
107-
t.join();
108-
}
109-
9+
OnlineWebsocketServerApp app(argc, argv);
10+
app.Run();
11011
return 0;
11112
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// sherpa-onnx/csrc/online-websocket-server.h
2+
//
3+
// Copyright (c) 2025 Uniphore (Author: Manickavela A)
4+
5+
#ifndef SHERPA_ONNX_ONLINE_WEBSOCKET_SERVER_H
6+
#define SHERPA_ONNX_ONLINE_WEBSOCKET_SERVER_H
7+
8+
#include <atomic>
9+
#include <thread>
10+
#include <vector>
11+
12+
#include "asio.hpp" // NOLINT
13+
#include "sherpa-onnx/csrc/macros.h"
14+
#include "sherpa-onnx/csrc/online-websocket-server-impl.h"
15+
#include "sherpa-onnx/csrc/parse-options.h"
16+
17+
class OnlineWebsocketServerApp {
18+
public:
19+
OnlineWebsocketServerApp(int32_t argc, char *argv[]);
20+
void Run();
21+
void Stop();
22+
23+
private:
24+
int32_t argc_;
25+
char **argv_;
26+
asio::io_context io_conn_; // ASIO context for connections
27+
asio::io_context io_work_; // ASIO context for work
28+
std::atomic<bool> shutdown_requested_{false};
29+
std::vector<std::thread> io_threads_;
30+
std::vector<std::thread> work_threads_;
31+
};
32+
33+
// Declare StartServer so it's accessible for Pybind
34+
void StartServer(int32_t argc, char *argv[]);
35+
36+
#endif // SHERPA_ONNX_ONLINE_WEBSOCKET_SERVER_H

sherpa-onnx/python/csrc/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,21 @@ if(SHERPA_ONNX_ENABLE_SPEAKER_DIARIZATION)
8989
)
9090
endif()
9191

92+
if(SHERPA_ONNX_ENABLE_WEBSOCKET)
93+
list(APPEND srcs online-websocket-server-app.cc)
94+
endif()
95+
9296
pybind11_add_module(_sherpa_onnx ${srcs})
9397

98+
if(SHERPA_ONNX_ENABLE_WEBSOCKET)
99+
target_compile_definitions(_sherpa_onnx PRIVATE ASIO_STANDALONE)
100+
target_compile_definitions(_sherpa_onnx PRIVATE _WEBSOCKETPP_CPP11_STL_)
101+
target_compile_definitions(_sherpa_onnx PRIVATE SHERPA_ONNX_ENABLE_WEBSOCKET=1)
102+
if(NOT WIN32)
103+
target_compile_options(_sherpa_onnx PRIVATE -Wno-deprecated-declarations)
104+
endif()
105+
endif()
106+
94107
if(APPLE)
95108
execute_process(
96109
COMMAND "${PYTHON_EXECUTABLE}" -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"

0 commit comments

Comments
 (0)