From 66a2b136f636b9b6dd5328ad6ccdeb85a270bb2a Mon Sep 17 00:00:00 2001 From: Dita Aji Pratama Date: Tue, 15 Sep 2026 13:50:17 +0700 Subject: [PATCH] First commit --- .gitignore | 4 ++++ README.md | 22 +++++++++++++++++++ core/args_parser.py | 14 ++++++++++++ requirements.txt | 2 ++ stt_runner.py | 52 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 core/args_parser.py create mode 100644 requirements.txt create mode 100644 stt_runner.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e7ef4ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.env +.venv +**/__pycache__ +*.pyc \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e3e1728 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# STT Runner + +Speech-to-Text transcription using sherpa-onnx + Qwen3-ASR. + +## Installation + +```bash +python3 -m venv .venv +.venv/bin/pip install -r requirements.txt +``` + +## Usage + +```bash +python stt_runner.py \ + --conv-frontend=path/conv_frontend.onnx \ + --encoder=path/encoder.onnx \ + --decoder=path/decoder.onnx \ + --tokenizer=path/tokenizer \ + audio1.wav audio2.wav ... +``` + diff --git a/core/args_parser.py b/core/args_parser.py new file mode 100644 index 0000000..c7af05d --- /dev/null +++ b/core/args_parser.py @@ -0,0 +1,14 @@ +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("--conv-frontend", type=str, required=True) +parser.add_argument("--encoder", type=str, required=True) +parser.add_argument("--decoder", type=str, required=True) +parser.add_argument("--tokenizer", type=str, required=True) +parser.add_argument("--language", type=str, default="", help="Force language, e.g. Indonesian, English, Chinese") +parser.add_argument("--hotwords", type=str, default="", help="Comma-separated hotword phrases, e.g. 'foo,bar'") +parser.add_argument("--num-threads", type=int, default=2) +parser.add_argument("--provider", type=str, default="cpu", choices=["cpu", "cuda"]) +parser.add_argument("--max-total-len", type=int, default=2048) +parser.add_argument("--max-new-tokens", type=int, default=256) +parser.add_argument("sounds", nargs="+", help="Audio files to transcribe") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..016ccbc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +sherpa-onnx +soundfile \ No newline at end of file diff --git a/stt_runner.py b/stt_runner.py new file mode 100644 index 0000000..54d4247 --- /dev/null +++ b/stt_runner.py @@ -0,0 +1,52 @@ +import sys +from pathlib import Path +import sherpa_onnx, soundfile as sf +from core import args_parser as ap + +def stt_run(args): + + print("Recognize...") + recognizer = sherpa_onnx.OfflineRecognizer.from_qwen3_asr( # qwen3 asr + conv_frontend = args.conv_frontend, + encoder = args.encoder, + decoder = args.decoder, + tokenizer = args.tokenizer, + hotwords = args.hotwords, + num_threads = args.num_threads, + sample_rate = 16000, + feature_dim = 128, + provider = args.provider, + max_total_len = args.max_total_len, + max_new_tokens = args.max_new_tokens, + ) + print("Recognizer ready!") + + for f in args.sounds: # Multi-file + if not Path(f).is_file(): + print(f"Skip. file not found: {f}", file=sys.stderr) + continue + + audio, sr = sf.read(f, dtype="float32", always_2d=True) + audio = audio[:, 0] + + stream = recognizer.create_stream() + + if args.language: + stream.set_option("language", args.language) + stream.accept_waveform(sr, audio) + + recognizer.decode_stream(stream) # Inference execution for `stream.result` + + text = stream.result.text + + if "" in text: # qwen3 asr format + text = text.split("", 1)[1] + + print() + print(f"File : {f}") + print(f"Duration : {len(audio) / sr:.2f} s") + print(f"Result : {text}") + print() + +if __name__ == "__main__": + stt_run( ap.parser.parse_args() )