| from __future__ import annotations |
|
|
| import json |
| import sys |
| from dataclasses import asdict, dataclass |
| from pathlib import Path |
|
|
| REPO_ROOT = Path(__file__).resolve().parents[1] |
| SRC_DIR = REPO_ROOT / "src" |
| if str(SRC_DIR) not in sys.path: |
| sys.path.insert(0, str(SRC_DIR)) |
|
|
| from wfloat_tts import SPEAKER_IDS, load_generator, write_wave |
|
|
|
|
| @dataclass(frozen=True) |
| class SampleSpec: |
| voice: str |
| emotion: str |
| intensity: float |
| text: str |
|
|
|
|
| SAMPLES = ( |
| SampleSpec( |
| voice="skilled_hero_man", |
| emotion="joy", |
| intensity=0.7, |
| text="We did it! The bridge is secure and everyone made it across. Take a breath and enjoy this moment with me.", |
| ), |
| SampleSpec( |
| voice="skilled_hero_woman", |
| emotion="neutral", |
| intensity=0.5, |
| text="Stay close. The stones are loose near the edge. We will move carefully and get there together.", |
| ), |
| SampleSpec( |
| voice="fun_hero_man", |
| emotion="surprise", |
| intensity=0.6, |
| text="Wait, that worked? I pulled one lever, the whole wall spun around, and now there is treasure everywhere!", |
| ), |
| SampleSpec( |
| voice="fun_hero_woman", |
| emotion="joy", |
| intensity=0.7, |
| text="Come on, keep up! The crowd is cheering.", |
| ), |
| SampleSpec( |
| voice="strong_hero_man", |
| emotion="anger", |
| intensity=0.80, |
| text="Enough. You had your warning, and you kept pushing innocent people around. Take one more step, and I end this.", |
| ), |
| SampleSpec( |
| voice="strong_hero_woman", |
| emotion="anger", |
| intensity=0.8, |
| text="The chain is holding, and we are not letting this town fall today. Push harder.", |
| ), |
| SampleSpec( |
| voice="mad_scientist_man", |
| emotion="joy", |
| intensity=0.90, |
| text="Look at that reaction. The coils are singing, the chamber is stable, and the entire machine is finally alive. Oh, this is magnificent.", |
| ), |
| SampleSpec( |
| voice="mad_scientist_woman", |
| emotion="surprise", |
| intensity=0.80, |
| text="No, no, that's not possible. The formula should have crystallized, but it adapted instead. Do you realize what that means for the rest of my work?", |
| ), |
| SampleSpec( |
| voice="clever_villain_man", |
| emotion="dismissive", |
| intensity=0.60, |
| text="You came all this way for that little threat? I have heard sharper words from people with far less ambition. Sit down and let the adults finish the game.", |
| ), |
| SampleSpec( |
| voice="clever_villain_woman", |
| emotion="dismissive", |
| intensity=0.74, |
| text="Please. If that was your grand reveal, I expected more style and much better timing. You are already behind, and you still do not know the real plan.", |
| ), |
| SampleSpec( |
| voice="narrator_man", |
| emotion="neutral", |
| intensity=0.5, |
| text="The harbor was quiet again. The storm had passed in the night, leaving scattered crates, broken rope, and one unanswered question.", |
| ), |
| SampleSpec( |
| voice="narrator_woman", |
| emotion="sadness", |
| intensity=0.4, |
| text="When the letter finally arrived, no one rushed to open it. The room had grown too familiar with bad news, and hope had learned to speak softly.", |
| ), |
| SampleSpec( |
| voice="wise_elder_man", |
| emotion="anger", |
| intensity=0.4, |
| text="I have seen this mistake before. Pride speaks loudly in the beginning, but regret is the voice that stays with you. Listen while there is still time to change course.", |
| ), |
| SampleSpec( |
| voice="wise_elder_woman", |
| emotion="joy", |
| intensity=0.5, |
| text="There you are. The answer was never as far away as you feared, and your patience has finally borne fruit. Let yourself be proud of how far you have come.", |
| ), |
| SampleSpec( |
| voice="outgoing_anime_man", |
| emotion="joy", |
| intensity=0.8, |
| text="Yes. This is our chance, and I can feel the whole day lighting up around us. We are going in together, and we are coming out legends.", |
| ), |
| SampleSpec( |
| voice="outgoing_anime_woman", |
| emotion="surprise", |
| intensity=0.5, |
| text="Hold on, you are telling me that tiny mascot can drive the entire ship? That is completely ridiculous, and somehow it makes me want to see it even more.", |
| ), |
| SampleSpec( |
| voice="scary_villain_man", |
| emotion="anger", |
| intensity=0.5, |
| text="You should have stayed hidden. Now I know your face, your voice, and exactly how afraid you are trying not to sound. Run, if it helps you feel alive.", |
| ), |
| SampleSpec( |
| voice="scary_villain_woman", |
| emotion="fear", |
| intensity=0.80, |
| text="Did you hear that? The halls were silent a moment ago, and now something is moving behind the walls. Do not leave me alone with that sound.", |
| ), |
| SampleSpec( |
| voice="news_reporter_man", |
| emotion="neutral", |
| intensity=0.5, |
| text="We are live outside city hall, where officials say the meeting will begin within the hour. Security has tightened, and a large crowd is still gathering behind us.", |
| ), |
| SampleSpec( |
| voice="news_reporter_woman", |
| emotion="neutral", |
| intensity=0.6, |
| text="We are receiving conflicting reports from the scene. One agency says the system is back online, while another says key services are still down. We are working to confirm the facts.", |
| ), |
| ) |
|
|
|
|
| def intensity_slug(value: float) -> str: |
| return f"{int(round(value * 100)):03d}" |
|
|
|
|
| def sample_filename(index: int, sample: SampleSpec) -> str: |
| return f"{index:02d}_{sample.voice}_{sample.emotion}_{intensity_slug(sample.intensity)}.wav" |
|
|
|
|
| def main() -> None: |
| output_dir = REPO_ROOT / "samples" |
| output_dir.mkdir(parents=True, exist_ok=True) |
|
|
| generator = load_generator( |
| checkpoint_path=REPO_ROOT / "model.safetensors", |
| config_path=REPO_ROOT / "config.json", |
| ) |
|
|
| manifest: list[dict[str, object]] = [] |
|
|
| for index, sample in enumerate(SAMPLES, start=1): |
| sid = SPEAKER_IDS[sample.voice] |
| audio = generator.generate( |
| text=sample.text, |
| sid=sid, |
| emotion=sample.emotion, |
| intensity=sample.intensity, |
| ) |
| filename = sample_filename(index, sample) |
| output_path = output_dir / filename |
| write_wave(output_path, audio.samples, audio.sample_rate) |
| manifest.append( |
| { |
| "filename": filename, |
| "sid": sid, |
| **asdict(sample), |
| } |
| ) |
| print(output_path.relative_to(REPO_ROOT)) |
|
|
| manifest_path = output_dir / "manifest.json" |
| manifest_path.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8") |
| print(manifest_path.relative_to(REPO_ROOT)) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|