aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-06-21 22:45:28 +0800
committer魏曹先生 <1992414357@qq.com>2026-06-21 22:45:28 +0800
commitc6885ae2312a1be589e945129bdd9d268f14c370 (patch)
tree919e0fe49331b533de312a39548f17b133ef4975
parent291c01fd98d44e42bb0a65468e3b6cee3dc25044 (diff)
feat: replace `--pinyin` flag with generic `--post="+pinyin"` option
-rw-r--r--Cargo.toml2
-rw-r--r--binding/unity/DMVOPListener.cs2
-rw-r--r--help.txt2
-rw-r--r--src/args.rs50
-rw-r--r--src/main.rs6
-rw-r--r--src/post_proc.rs25
-rw-r--r--src/post_proc/pinyin.rs31
-rw-r--r--src/post_proc/trait.rs12
8 files changed, 115 insertions, 15 deletions
diff --git a/Cargo.toml b/Cargo.toml
index bb101a2..69724a4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@ path = "src/main.rs"
clap = { version = "4.6.1", features = ["derive"] }
tokio = { version = "1.52.3", features = ["net", "rt", "rt-multi-thread", "macros"] }
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
-pinyin = "0.11.0"
+pinyin = { version = "0.11.0", features = ["with_tone", "with_tone_num", "with_tone_num_end"] }
[dependencies.vtx-engine]
git = "https://github.com/Weicao-CatilGrass/vtx-engine"
diff --git a/binding/unity/DMVOPListener.cs b/binding/unity/DMVOPListener.cs
index 7964756..eaf1db0 100644
--- a/binding/unity/DMVOPListener.cs
+++ b/binding/unity/DMVOPListener.cs
@@ -7,7 +7,7 @@ using UnityEngine;
using UnityEngine.Events;
// Chinese:
-// dmvop --output=tcp --port=5117 --model=small --lang=zh --instant --pinyin --device=YOUR_DEVICE
+// dmvop --output=tcp --port=5117 --model=small --lang=zh --instant --post="+pinyin" --device=YOUR_DEVICE
//
// English
// dmvop --output=tcp --port=5117 --model=small.en --lang=en --instant --device=YOUR_DEVICE
diff --git a/help.txt b/help.txt
index 649cb67..f4b5507 100644
--- a/help.txt
+++ b/help.txt
@@ -6,7 +6,7 @@ Options
stdout | stderr | tcp | udp | udp-broadcast | ipc
-m, --model=<model> Whisper model. [default: base_en]
--lang=<code> Language hint (zh, ja, en, fr...). Skips detection.
- --pinyin Convert Chinese output to pinyin format.
+ --post=<spec> Post-process transcribed text, e.g. +pinyin
-f, --format=<pattern> Output format with %{vol}, %{word}, %{confid}.
[default: %{vol},%{word}]
-S, --format-file=<path> Read format from file.
diff --git a/src/args.rs b/src/args.rs
index 11a1f11..7b472c4 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -118,9 +118,9 @@ pub struct DMVOPArguments {
)]
pub subnet_mask: String,
- // Convert text output to pinyin (Chinese romanization)
- #[arg(long = "pinyin")]
- pub use_pinyin: bool,
+ // Post-processing pipeline, e.g. --post="+pinyin()" or --post="+reverse"
+ #[arg(long = "post", require_equals = true)]
+ pub post: Option<String>,
}
#[derive(Clone, Debug)]
@@ -182,13 +182,43 @@ pub fn format_output(pattern: &str, word: &str, confidence: f32, volume: f32) ->
result
}
-/// Convert transcribed text to pinyin if the `use_pinyin` flag is set.
-/// Otherwise returns the original text as a String.
-pub fn maybe_to_pinyin(text: &str, use_pinyin: bool) -> String {
- if use_pinyin {
- pinyin::to_pinyin_vec(text, pinyin::Pinyin::plain).join(" ")
- } else {
- text.to_string()
+/// Parse a post-processor spec in the form `+function_name(arg1, arg2, ...)`.
+/// Returns `(name, args)`.
+pub fn parse_post_spec(spec: &str) -> (&str, Vec<&str>) {
+ let spec = spec.trim();
+ if !spec.starts_with('+') {
+ return (spec, vec![]);
+ }
+ let inner = &spec[1..];
+ if let Some(paren) = inner.find('(') {
+ if inner.ends_with(')') {
+ let name = inner[..paren].trim();
+ let args_str = inner[paren + 1..inner.len() - 1].trim();
+ let args: Vec<&str> = if args_str.is_empty() {
+ vec![]
+ } else {
+ // Simple comma split (no nested parens, no escape handling for now)
+ args_str.split(',').map(|a| a.trim()).collect()
+ };
+ return (name, args);
+ }
+ }
+ // No parens → no args
+ (inner.trim(), vec![])
+}
+
+/// Run the post-processing pipeline on the given text.
+pub fn run_post_process(text: &str, spec: Option<&str>) -> String {
+ let Some(spec) = spec else {
+ return text.to_string();
+ };
+ let (name, args) = parse_post_spec(spec);
+ match crate::post_proc::run(text, name, &args) {
+ Some(result) => result,
+ None => {
+ eprintln!("[dmvop] Unknown post-processor: '{}'", name);
+ text.to_string()
+ }
}
}
diff --git a/src/main.rs b/src/main.rs
index e3a5b84..73b4e06 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,5 @@
+pub mod post_proc;
+
mod args;
pub use args::*;
@@ -252,7 +254,7 @@ async fn main() {
match rx.recv().await {
Ok(event) => match event {
vtx_engine::EngineEvent::TranscriptionComplete(result) => {
- let raw = maybe_to_pinyin(&result.text, args.use_pinyin);
+ let raw = run_post_process(&result.text, args.post.as_deref());
let formatted = format_output(&pattern, &raw, 0.0, last_volume_db);
for ch in &channels {
@@ -260,7 +262,7 @@ async fn main() {
}
}
vtx_engine::EngineEvent::TranscriptionSegment(segment) => {
- let raw = maybe_to_pinyin(&segment.text, args.use_pinyin);
+ let raw = run_post_process(&segment.text, args.post.as_deref());
let formatted = format_output(&pattern, &raw, 0.0, last_volume_db);
for ch in &channels {
diff --git a/src/post_proc.rs b/src/post_proc.rs
new file mode 100644
index 0000000..d8dfa74
--- /dev/null
+++ b/src/post_proc.rs
@@ -0,0 +1,25 @@
+mod r#trait;
+pub use r#trait::*;
+
+pub mod pinyin;
+
+/// Look up a processor by name and run it.
+pub fn run(text: &str, name: &str, args: &[&str]) -> Option<String> {
+ macro_rules! match_processor {
+ ($($processor:ty),+ $(,)?) => {
+ match name {
+ $(
+ n if n == <$processor as TextPostProcesser>::processer_name() => {
+ Some(<$processor as TextPostProcesser>::process(args, text))
+ }
+ )+
+ _ => None,
+ }
+ };
+ }
+
+ match_processor!(
+ // +pinyin
+ crate::post_proc::pinyin::PinyinPostProcesser,
+ )
+}
diff --git a/src/post_proc/pinyin.rs b/src/post_proc/pinyin.rs
new file mode 100644
index 0000000..c06b040
--- /dev/null
+++ b/src/post_proc/pinyin.rs
@@ -0,0 +1,31 @@
+use crate::post_proc::TextPostProcesser;
+
+/// Converts Chinese characters to pinyin (romanized phonetic representation).
+///
+/// # Styles
+/// - `plain` — ni hao (default)
+/// - `tone` — nǐ hǎo (with tone marks, requires `with_tone` feature)
+/// - `tone-num` — ni3 hao3 (tone number at end)
+/// - `first-letter` — n h (first letter only)
+pub struct PinyinPostProcesser;
+
+impl TextPostProcesser for PinyinPostProcesser {
+ fn processer_name() -> &'static str {
+ "pinyin"
+ }
+
+ fn process(param: &[&str], text: &str) -> String {
+ let style = param.first().copied().unwrap_or("plain");
+ let result: Vec<&'static str> = match style {
+ "tone" => pinyin::to_pinyin_vec(text, pinyin::Pinyin::with_tone),
+ "tone-num" | "tone_num" => {
+ pinyin::to_pinyin_vec(text, pinyin::Pinyin::with_tone_num_end)
+ }
+ "first-letter" | "first_letter" => {
+ pinyin::to_pinyin_vec(text, pinyin::Pinyin::first_letter)
+ }
+ _ => pinyin::to_pinyin_vec(text, pinyin::Pinyin::plain),
+ };
+ result.join(" ")
+ }
+}
diff --git a/src/post_proc/trait.rs b/src/post_proc/trait.rs
new file mode 100644
index 0000000..7fced5b
--- /dev/null
+++ b/src/post_proc/trait.rs
@@ -0,0 +1,12 @@
+/// A text post-processor that transforms transcribed text.
+pub trait TextPostProcesser {
+ /// Name used in `--post="+name(args)"`.
+ fn processer_name() -> &'static str;
+
+ /// Execute the post-processing.
+ ///
+ /// # Arguments
+ /// * `param` — arguments parsed from `--post="+name(arg1, arg2)"`
+ /// * `text` — the transcribed text to process
+ fn process(param: &[&str], text: &str) -> String;
+}