---
language:
- en
license: cc-by-nc-4.0
pipeline_tag: text-to-speech
tags:
- full-duplex
- conversation-tts
- dialogue
- stereo
- xml
- qwen3-tts
base_model: Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice
datasets:
- facebook/seamless-interaction
---
# DuDE: Full-Duplex Conversation TTS
DuDE is a full-duplex conversation TTS (text-to-speech) model that generates
two speakers together from an XML dialogue. It supports turn-taking,
backchannels, and overlapping speech, with a separate voice for each speaker.
The output is a 24 kHz stereo WAV: speaker A on the left, speaker B on the right.
**Author:** Cheng-Kuang Chang
## Training details
DuDE first self-distills Qwen3-TTS on independent utterances to adapt a shared
backbone to two interleaved channels. It is then fine-tuned on approximately
1,922 hours of synchronized Seamless Interaction conversations to learn
turn-taking, backchannels, and overlapping speech.
The XML frontend resolves turn dependencies into interleaved text inputs.
Reference-speaker embeddings condition the two voices, and each audio channel
has its own end-of-speech decision while sharing the same timeline.
## Audio samples
The samples use XML transcripts of conversations from
[Meta's Seamless Interaction dataset](https://huggingface.co/datasets/facebook/seamless-interaction),
produced using ASR and vocal-event annotation. The audio is generated by DuDE.
### Sample 1
[Download stereo audio](examples/sample_1.wav) · [Input XML](examples/sample_1.xml) · Voices: `voice_1` / `voice_2`
Show complete input XML
```xml
So, if you had a chance to send a message back to the future, or the past, or reverse, or whatever, however, what would you do? Well, if I had a chance, I probablyWell, if I had a chance, I'd probably. Where can I do both, or I have to do one or the other? would do both. Probably,no, you probably will do both regardless. Well, most likely, I'd send a message back to the past and be like, "Yo, watch out! Don't go around that corner." I most likely wouldwho? I would send a message to myself. Oh, cool. I mean, who else would I contact in the past? Would you like yourself back then? I don't listen. I have no fucking clue. Okay. That far. Oh, great. great! I don't have a clue. Bye.
```
### Sample 2
[Download stereo audio](examples/sample_2.wav) · [Input XML](examples/sample_2.xml) · Voices: `voice_3` / `voice_4`
Show complete input XML
```xml
If we're to organize a fitness challenge, which I know you love, what would we include for activities? How would we make it engaging? What do you think?The activities number one has got to be yoga, probably.Yoga. I'm on board. Stretching andyou know, relaxing and feeling good. I I think weights are good too. I mean, just a little everything. I think one extreme to the other, maybe. Yeah, I mean, I'm definitely on board with yoga. I like mobility exercises. I Yeah, I think if you do,don't know if I like the word extreme, but you know.yeah. So the only thing missing would be so you have yoga, weights, and we need some kind of cardio. Any suggestions? Yeah.Like speed walking, I guess. Maybe a treadmill. Yeah. Yeah, I like the treadmill.Yeah, I think that would cover everything.
```
## Download and run
Use Python 3.12 and a CUDA-capable NVIDIA GPU. The download includes the model,
audio codec, XML frontend, four voice templates, and inference code. Requirements
pin PyTorch and torchaudio 2.8.0; use the corresponding CUDA builds for your system.
```bash
pip install "huggingface_hub>=0.34,<1"
hf download penguinfish1688/duplexdataengine --local-dir DuDE
cd DuDE
pip install -r requirements.txt
python infer.py \
--model . --xml examples/sample_1.xml \
--voice-a voice_1 --voice-b voice_2 \
--seed 20261060 --max-seconds 118 \
--output dialogue.wav
```
Or use the Python API from the downloaded directory:
```python
from pathlib import Path
from dude_tts import DuDE
model = DuDE.from_pretrained(".", device="cuda")
audio = model.generate(
Path("examples/sample_1.xml").read_text(),
voice_a="voice_1", voice_b="voice_2",
seed=20261060, max_seconds=118,
)
audio.save("dialogue.wav")
```
`max_seconds` is an output limit, not a target speaking duration. Generation
finishes when both speakers emit an end-of-speech token; `audio.eos` reports
whether each speaker finished. Leading silence and pauses are preserved, with
silence padding after the shorter channel ends. CPU inference is available
with `--device cpu`, but is slow.
Preset inference on an RTX PRO 6000 Blackwell used 11.6 GB of allocated GPU
memory and took about 1.1 seconds per second of stereo output. Weights are
stored in FP32; CUDA inference uses BF16 autocast.
## Writing a dialogue
Use `` and `` for the speakers. Exactly one initial turn has no `start`
attribute. Every later turn references an inline anchor with `start="e1"`,
`start="e2"`, and so on. Define each anchor once. The `` wrapper is optional.
For turn-taking, put the anchor at the preceding turn's end:
```xml
Hello! How was your day?Pretty good. I finished the project.That's great!
```
For overlap, put the anchor where the other speaker should enter:
```xml
I walked to the park, and then stopped for coffee.Mm-hmm.That sounds nice.
```
Anchors express turn relationships; the model predicts the audio timing.
Exact entry times are not guaranteed. Colliding turns for the same speaker
follow their XML order. All references must exist, and turns must not form a cycle.
Vocal events: ``, ``, ``, ``, ``,
``, and ``. Write spoken backchannels as ordinary text and
escape literal ampersands as `&`. Attributes such as `type`, timestamps,
and duration are not supported.
## Voice templates
Use your own reference audio for either speaker by passing a file path to
`--voice-a` and `--voice-b`, or to `voice_a` and `voice_b` in Python:
```bash
python infer.py --model . --xml dialogue.xml \
--voice-a speaker_a.wav --voice-b speaker_b.wav --output dialogue.wav
```
Use a clear, single-speaker WAV or FLAC clip between 0.5 and 60 seconds long.
No transcript is needed. Audio is converted to mono and resampled automatically.
You can mix a custom reference with an included voice, such as
`--voice-a speaker_a.wav --voice-b voice_2`.
If you do not have reference audio, four example voices are included for convenience:
| Template | Reference audio |
| --- | --- |
| `voice_1` | [Sample 1, speaker A](voices/voice_1.wav) |
| `voice_2` | [Sample 1, speaker B](voices/voice_2.wav) |
| `voice_3` | [Sample 2, speaker A](voices/voice_3.wav) |
| `voice_4` | [Sample 2, speaker B](voices/voice_4.wav) |
## License
Model weights, voice references, and conversation examples are available for
noncommercial use under [CC BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/).
DuDE inference code is Apache-2.0; see [LICENSE-CODE](LICENSE-CODE).
Qwen components retain their Apache-2.0 notices in [LICENSE-QWEN](LICENSE-QWEN).
Conversation material and reference voices derive from Meta's
[Seamless Interaction dataset](https://huggingface.co/datasets/facebook/seamless-interaction),
also licensed CC BY-NC 4.0. This release includes derived transcripts, XML,
speaker embeddings, and synthesized recordings. Preserve the dataset attribution
when redistributing these assets.