--- 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 probably Well, 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 would who? 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 and you 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.