# qwenlm/qwen3-asr-toolkit architecture

> Architecture diagram of the GitHub repository qwenlm/qwen3-asr-toolkit, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-05-13). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/qwenlm/qwen3-asr-toolkit
- Repository: https://github.com/qwenlm/qwen3-asr-toolkit

## Overview

1. This repository is a small multi-language toolkit centered on one pipeline: take long audio/video, split it into transcribable chunks, send those chunks to the Qwen-ASR service, then stitch the results back together. The Python package is the primary product; the Java directory is a standalone reference implementation of the same idea.

2. The Python code is organized into three main subsystems:
   - `qwen3asr.py`: the CLI/orchestration layer
   - `audio_tools.py`: media loading, conversion, chunking, and subtitle-related helpers
   - `call_api.py`: the DashScope/Qwen-ASR request boundary
   This separation maps cleanly to the architecture: input preparation, remote inference, and result handling.

3. The main data flow starts with an input media source that can be either a local file or a remote URL. The tool normalizes that source into audio suitable for the API, then uses voice activity detection to find natural breakpoints instead of cutting blindly by time.

4. Media preprocessing is a key boundary in this repo. The README indicates FFmpeg is used to accept many input formats and resample them into the 16 kHz mono format expected by the ASR API. Temporary chunk files are stored under a configurable cache directory, which makes the preprocessing stage explicit and separable from the transcription stage.

5. Chunking is controlled by VAD and a target segment duration. The design goal is to keep chunks under the API’s 3-minute limit while avoiding mid-sentence cuts. This means the splitter is not just a file splitter; it is a semantic boundary detector that tries to preserve speech continuity.

6. API interaction is isolated behind the `call_api.py` layer. That layer is the external-service boundary to Alibaba Cloud DashScope, authenticated via `DASHSCOPE_API_KEY` or a CLI argument. The repository does not embed a model locally in Python; it depends on the hosted Qwen-ASR service for transcription.

7. Long-form throughput is improved by parallelism. The toolkit submits chunk requests concurrently using multiple threads, then reorders and aggregates the returned text segments. In architectural terms, transcription is a fan-out/fan-in stage: split into chunks, dispatch in parallel, collect in order.

8. After transcription, the tool performs cleanup/post-processing on the assembled text. The README calls out removal of hallucinations and repetitive artifacts, so the final output stage is not a raw API passthrough; it is a normalization step that improves readability before writing the transcript.

9. Output generation is file-oriented. The final transcript is printed to the terminal and saved as a `.txt` file next to the source media. If enabled, an `.srt` subtitle file is generated from the same segmented timing information, so subtitle creation is a secondary output derived from the VAD/chunk timeline.

10. The package boundary is straightforward: install via `pip`, then invoke the `qwen3-asr` command. Python 3.8+, FFmpeg, and the DashScope API key are the main runtime dependencies that materially affect architecture.

11. The Java example in `examples/java-example` mirrors the same pipeline in another runtime. `App.java` appears to be the entry point, `AudioSplitter.java` handles chunking, `SlieroVadDetector.java` and `SlieroVadOnnxModel.java` wrap VAD inference, and `utils/Result.java` / `utils/Util.java` support orchestration. This example includes an embedded `silero_vad.onnx`, so unlike the Python path it demonstrates local ONNX-based VAD integration.

12. From an architecture-diagram perspective, the important boundaries are: media input, audio normalization/VAD splitting, parallel remote ASR calls, post-processing/ordering, and output files. The Python package is the production flow; the Java example is a parallel reference implementation of the same control flow.

Main entry points: [`qwen3_asr_toolkit/audio_tools.py`](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/qwen3_asr_toolkit/audio_tools.py) (Audio tools), [`examples/java-example/src/main/java/org/example/App.java`](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/App.java) (App), [`qwen3_asr_toolkit/qwen3asr.py`](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/qwen3_asr_toolkit/qwen3asr.py) (qwen3asr), [`examples/java-example/src/main/java/org/example/AudioSplitter.java`](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/AudioSplitter.java) (Audio splitter)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_python["Python toolkit"]
  node_src["Media source<br/>input"]
  node_cli["qwen3asr<br/>cli orchestrator<br/>[qwen3asr.py]"]
  node_audio["Audio tools<br/>media pipeline<br/>[audio_tools.py]"]
  node_api["ASR client<br/>dashscope client<br/>[call_api.py]"]
  node_vad{{"VAD splitter<br/>speech segmentation"}}
  node_ffmpeg["FFmpeg<br/>media runtime"]
  node_cache[("Chunk cache<br/>temp storage")]
  node_dispatch["Parallel jobs<br/>threaded fan-out"]
  node_aggregate["Merge results<br/>ordering stage"]
  node_normalize["Cleanup text<br/>post-process"]
  node_txt["Transcript file<br/>output"]
  node_srt["Subtitle file<br/>output"]
end

subgraph group_java["Java example"]
  node_app["App<br/>java entrypoint<br/>[App.java]"]
  node_splitter["Audio splitter<br/>java chunker<br/>[AudioSplitter.java]"]
  node_vad_java{{"VAD detector<br/>java speech VAD"}}
  node_onnx["ONNX model<br/>vad model<br/>[silero_vad.onnx]"]
  node_result["Result utils<br/>java helpers<br/>[Result.java]"]
  node_util["Util helpers<br/>java helpers<br/>[Util.java]"]
end

node_src -->|"start"| node_cli
node_cli -->|"prepare"| node_audio
node_audio -->|"convert"| node_ffmpeg
node_audio -->|"split"| node_vad
node_vad -->|"store chunks"| node_cache
node_cli -->|"transcribe"| node_api
node_api -->|"fan-out"| node_dispatch
node_dispatch -->|"fan-in"| node_aggregate
node_aggregate -->|"clean"| node_normalize
node_normalize -->|"write"| node_txt
node_aggregate -->|"subtitle"| node_srt
node_app -->|"orchestrate"| node_splitter
node_splitter -->|"detect speech"| node_vad_java
node_vad_java -->|"run model"| node_onnx
node_app -->|"collect"| node_result
node_app -->|"support"| node_util
node_splitter -->|"emit chunks"| node_result
node_audio -->|"timing data"| node_srt

click node_cli "https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/qwen3_asr_toolkit/qwen3asr.py"
click node_audio "https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/qwen3_asr_toolkit/audio_tools.py"
click node_api "https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/qwen3_asr_toolkit/call_api.py"
click node_app "https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/App.java"
click node_splitter "https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/AudioSplitter.java"
click node_vad_java "https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/SlieroVadDetector.java"
click node_onnx "https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/resources/silero_vad.onnx"
click node_result "https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/utils/Result.java"
click node_util "https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/utils/Util.java"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_src,node_cli,node_audio,node_api,node_vad,node_ffmpeg,node_cache,node_dispatch,node_aggregate,node_normalize,node_txt,node_srt toneBlue
class node_app,node_splitter,node_vad_java,node_onnx,node_result,node_util toneAmber
```

## Components

### Python toolkit

Primary production pipeline exposed as a CLI package.

- **Media source**: input · Local file or remote URL provided by the user.
- [qwen3asr](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/qwen3_asr_toolkit/qwen3asr.py): cli orchestrator · Command entrypoint that drives the end-to-end workflow. · `qwen3_asr_toolkit/qwen3asr.py`
- [Audio tools](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/qwen3_asr_toolkit/audio_tools.py): media pipeline · Load, convert, split, and prepare audio plus subtitle timing. · `qwen3_asr_toolkit/audio_tools.py`
- [ASR client](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/qwen3_asr_toolkit/call_api.py): dashscope client · Boundary for authenticated Qwen-ASR requests. · `qwen3_asr_toolkit/call_api.py`
- **VAD splitter**: speech segmentation · Finds speech-aware breakpoints and keeps chunks within API limits.
- **FFmpeg**: media runtime · Converts diverse inputs into 16 kHz mono audio.
- **Chunk cache**: temp storage · Configurable directory for temporary processed chunks.
- **Parallel jobs**: threaded fan-out · Submits chunk requests concurrently.
- **Merge results**: ordering stage · Reorders returned segments and stitches them into one transcript.
- **Cleanup text**: post-process · Removes repetitive artifacts and improves readability.
- **Transcript file**: output · Final text written next to the source media.
- **Subtitle file**: output · Optional subtitle output derived from segment timing.

### Java example

Standalone reference implementation of the same transcription flow.

- [App](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/App.java): java entrypoint · Reference entrypoint for the Java pipeline. · `examples/java-example/src/main/java/org/example/App.java`
- [Audio splitter](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/AudioSplitter.java): java chunker · Splits media into transcription-sized chunks. · `examples/java-example/src/main/java/org/example/AudioSplitter.java`
- [VAD detector](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/SlieroVadDetector.java): java speech VAD · Runs speech activity detection for natural chunk boundaries. · `examples/java-example/src/main/java/org/example/SlieroVadDetector.java`
- [ONNX model](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/resources/silero_vad.onnx): vad model · Embedded Silero VAD model used by the Java example. · `examples/java-example/src/main/resources/silero_vad.onnx`
- [Result utils](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/utils/Result.java): java helpers · Supports orchestration and transcript aggregation. · `examples/java-example/src/main/java/org/example/utils/Result.java`
- [Util helpers](https://github.com/qwenlm/qwen3-asr-toolkit/blob/main/examples/java-example/src/main/java/org/example/utils/Util.java): java helpers · Shared helper functions for the Java example. · `examples/java-example/src/main/java/org/example/utils/Util.java`

## Connections

- Media source → qwen3asr: start · User provides media to the CLI.
- qwen3asr → Audio tools: prepare · Coordinates media normalization and chunk preparation.
- Audio tools → FFmpeg: convert · Uses FFmpeg to decode and resample input media.
- Audio tools → VAD splitter: split · Delegates speech-aware chunking to VAD-based segmentation.
- VAD splitter → Chunk cache: store chunks · Writes temporary chunk files for later transcription.
- qwen3asr → ASR client: transcribe · Sends prepared chunks to the remote ASR boundary.
- ASR client → Parallel jobs: fan-out · Chunk requests are issued concurrently.
- Parallel jobs → Merge results: fan-in · Collected responses are reordered and merged.
- Merge results → Cleanup text: clean · Assembled transcript is post-processed for readability.
- Cleanup text → Transcript file: write · Final transcript is printed and saved as text.
- Merge results → Subtitle file: subtitle · Segment timing can be rendered into subtitles.
- App → Audio splitter: orchestrate · Java entrypoint drives the same chunking flow.
- Audio splitter → VAD detector: detect speech · Chunking is guided by VAD output.
- VAD detector → ONNX model: run model · Loads the embedded Silero VAD ONNX model.
- App → Result utils: collect · Uses helper types for aggregation and control flow.
- App → Util helpers: support · Uses shared Java utility functions.
- Audio splitter → Result utils: emit chunks · Produces chunk metadata for downstream handling.
- Audio tools → Subtitle file: timing data · Provides segment timing used for subtitle generation.
