Repository navigation
Add Cybernetic Commander voice, timing, and mastering workflows #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b9b2562
c9e07b5
746d577
cfe484a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,3 +8,4 @@ dist/ | |
| vo/ | ||
| *.wav | ||
| !examples/**/*.wav | ||
| examples/voice-profiles/*/out/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # Cybernetic Commander authorization record | ||
|
|
||
| - Recorded: 2026-08-02 | ||
| - Provider: repository owner/user | ||
| - Profile: cybernetic-commander | ||
| - Evidence source: direct user confirmation in the Codex task that supplied the | ||
| four recordings | ||
| - Scope asserted by provider: use the supplied recordings to construct a | ||
| separate voice profile and add that profile to Protonmatter/voforge | ||
| - Repository policy scope: evaluation and non-commercial use only | ||
| - Independent verification: not performed | ||
|
|
||
| The provider affirmed that they have permission from the relevant speaker or | ||
| rightsholder to clone and redistribute the supplied voice recordings in this | ||
| repository. This file records that assertion; it is not independent proof of | ||
| identity, ownership, performer consent, or downstream commercial rights. | ||
|
|
||
| The profile and its reference assets must not be presented as a natural person's | ||
| actual speech, endorsement, or participation. Commercial use, identity claims, | ||
| or redistribution outside this repository require a separate rights review. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| # Cybernetic Commander voice profile | ||
|
|
||
| This is a separately licensed, reference-conditioned F5 profile built from four | ||
| user-supplied recordings. It is intentionally isolated from VOForge's anonymous | ||
| audio examples and is not covered by the repository's MIT source-code license. | ||
|
|
||
| The profile is called Cybernetic Commander. Do not represent it as the voice of, | ||
| or an endorsement by, a natural person. The recorded authorization boundary is | ||
| in docs/consents/cybernetic-commander.md. Commercial use requires a separate | ||
| rights review. | ||
|
|
||
| ## Rebuild | ||
|
|
||
| Use the existing pinned F5 environment from the repository root: | ||
|
|
||
| ..\f5tts-venv\Scripts\python.exe examples\voice-profiles\cybernetic-commander\build_reference.py | ||
|
|
||
| The build fails on decoder-version drift. It preserves and analyzes all four | ||
| source MP3s, selects the user-preferred v2 generated performance for F5 | ||
| conditioning, and retains the other clips as hash-bound comparison evidence. | ||
| It writes a normalized 24 kHz mono PCM16 reference and reference-manifest.json. | ||
| Accent-specific rhotic syllables are supplied through per-line spokenText while | ||
| the normal text remains available for subtitles and dialogue records. | ||
|
|
||
| profile-spec.json records the acoustic, pacing, pause, and pronunciation | ||
| contract. calibration-lines.json provides consonant, vowel, number, and tactical | ||
| coverage without making those long calibration passages part of the default | ||
| render. timed-dialogue.json preserves complete-sentence reference passages, | ||
| proper-noun QA terms, and source-scene provenance using VOForge's strict, local | ||
| timed-text parser. Each video reference has a bounded QA purpose; metadata is | ||
| recorded without downloading or redistributing source audio. The active | ||
| artifact is the exact user-preferred v2 systems-ready take, | ||
| hash-pinned in profile-spec.json. Its original inference speed was not preserved, | ||
| so forced rerendering is not reproducible and must not be treated as equivalent. | ||
| The numeric F0, formant, perturbation, VOT, PVI, syllable, and pause values in | ||
| profile-spec.json are evaluation targets, not post-render DSP instructions. | ||
|
|
||
| ## Validate and plan | ||
|
|
||
| project-her validate --config examples\voice-profiles\cybernetic-commander\project-her.json | ||
| project-her plan --config examples\voice-profiles\cybernetic-commander\project-her.json | ||
|
|
||
| Rendering remains disabled through MCP by default. The reference transcript was | ||
| ASR-assisted and manually normalized; a human listening review is still required | ||
| before promoting generated dialogue. | ||
|
|
||
| Render one isolated timed-dialogue candidate with the shipped, hash-pinned | ||
| composite reference: | ||
|
|
||
| python render_timed_dialogue.py dyson-responsibility | ||
|
|
||
| Phrase parts are resumable. The renderer preserves 80 ms of quiet boundary | ||
| padding, applies a 20 ms terminal release when a model part ends active, and | ||
| accounts for retained quiet samples when inserting each declared break. It does | ||
| not resample or pitch-shift voiced audio. Timed candidates remain under ignored | ||
| out/ until manually approved. | ||
|
|
||
| ## Experimental mastering A/B | ||
|
|
||
| `master_voice.py` applies the profile's measured post-processing preset to an | ||
| existing dry WAV without replacing that source. It requires an FFmpeg build | ||
| with the Rubber Band, equalizer, compressor, soft-clip, resampler, and EBU R128 | ||
| `loudnorm` filters. Pass an executable explicitly when FFmpeg is not on `PATH`: | ||
|
|
||
| python examples\voice-profiles\cybernetic-commander\master_voice.py ^ | ||
| examples\voice-profiles\cybernetic-commander\out\dry.wav ^ | ||
| examples\voice-profiles\cybernetic-commander\out\dry-mastered.wav ^ | ||
| --ffmpeg C:\path\to\ffmpeg.exe | ||
|
|
||
| The default preset slows cadence by 7%, lowers pitch by 0.75 semitone with | ||
| Rubber Band formant preservation, applies documented 115 Hz/310 Hz/presence EQ, | ||
| 3:1 RMS compression, and oversampled tanh saturation, then iteratively | ||
| calibrates a 24 kHz mono PCM24 result to -16.5 LUFS with a -1.0 dBTP ceiling. | ||
| The normalizer reserves 0.1 dB of true-peak margin. The script refuses to | ||
| replace an existing WAV or manifest unless `--force` is supplied and records | ||
| input/output hashes, exact filters, calibration attempts, format, and measured | ||
| loudness in the adjacent JSON manifest. | ||
|
|
||
| Passing mastering metrics is not a promotion gate. In the local acceptance | ||
| trial, formant-preserving pitch/time processing substantially reduced the | ||
| cached Whisper tiny.en transcription accuracy even though format, loudness, | ||
| true peak, and clipping checks passed. Every candidate therefore requires a | ||
| dry/mastered A/B, an intelligibility comparison, and human listening review. | ||
| Generated masters and manifests remain ignored under `out/`. | ||
|
|
||
| ## Checked-in name-free samples | ||
|
|
||
| The separately licensed [`samples/`](samples/) directory contains seven WAV | ||
| examples whose intended scripts and cached offline transcriptions contain no | ||
| personal, speaker, or character names. The directory includes exact/partial ASR | ||
| examples and the rejected mastering A/B so limitations are visible rather than | ||
| silently discarded. Each file is hash-bound in `samples/manifest.json`; human | ||
| listening and the profile's non-commercial rights boundary still apply. | ||
|
|
||
| ## Named dialogue evaluations | ||
|
|
||
| [`dialogue/`](dialogue/) is deliberately separate from the name-free sample | ||
| set. It contains long-form, named evaluation dialogue with dry/processed A/B | ||
| evidence, explicit text provenance, model and reference hashes, measurements, | ||
| and conservative QA dispositions. These files are not promotion-approved and | ||
| require human listening and rights review before use beyond repository-local, | ||
| non-commercial evaluation. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| r"""Rebuild the Cybernetic Commander F5 reference from its authorized sources. | ||
|
|
||
| Run from the repository root with the pinned F5 environment: | ||
|
|
||
| ..\f5tts-venv\Scripts\python.exe \ | ||
| examples\voice-profiles\cybernetic-commander\build_reference.py | ||
|
|
||
| The source clips are preserved byte-for-byte. This script decodes each one, | ||
| applies conservative peak-safe mastering, selects the preferred coherent | ||
| performance, and writes a mono 24 kHz PCM16 reference plus a hash-bound manifest. | ||
| """ | ||
| from __future__ import annotations | ||
|
|
||
| import json | ||
| import math | ||
| from pathlib import Path | ||
| from typing import Dict, List, Sequence, Tuple | ||
|
|
||
| import soundfile as sf | ||
|
|
||
| from voforge import __version__ as voforge_version | ||
| from voforge.audio import ( | ||
| MasteringSettings, | ||
| artifact_record, | ||
| master_samples, | ||
| sha256_file, | ||
| write_pcm16_mono, | ||
| ) | ||
|
|
||
|
|
||
| ROOT = Path(__file__).resolve().parent | ||
| REFERENCE = ROOT / "reference-cybernetic-commander.wav" | ||
| MANIFEST = ROOT / "reference-manifest.json" | ||
| EXPECTED_SOUNDFILE = "0.14.0" | ||
| EXPECTED_LIBSNDFILE = "1.2.2" | ||
| SETTINGS = MasteringSettings( | ||
| input_gain_db=-2.5, | ||
| peak_limit_dbfs=-3.0, | ||
| lookahead_ms=5.0, | ||
| release_ms=80.0, | ||
| sample_rate=24000, | ||
| ) | ||
| SOURCES: Sequence[Tuple[str, str, str]] = ( | ||
| ( | ||
| "generated-directive.mp3", | ||
| "generated-primary", | ||
| "Min Kang, come with me if you want to live. You have been targeted for " | ||
| "termination. Skynet is looking to destroy you. I was sent by you from " | ||
| "the future to protect you.", | ||
| ), | ||
| ( | ||
| "generated-night-walk.mp3", | ||
| "generated-comparison", | ||
| "Min Kang, nice night for a walk. Your clothes. Give them to me.", | ||
| ), | ||
| ( | ||
| "source-clothes-request.mp3", | ||
| "source-comparison", | ||
| "Your clothes. Give them to me. Now.", | ||
| ), | ||
| ( | ||
| "source-night-walk.mp3", | ||
| "source-comparison", | ||
| "Nice night for a walk.", | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| def _decode(path: Path) -> Tuple[List[float], int, Dict[str, object]]: | ||
| waveform, sample_rate = sf.read(path, dtype="float32", always_2d=True) | ||
| mono = waveform.mean(axis=1) | ||
| samples = [float(value) for value in mono] | ||
| peak = max((abs(value) for value in samples), default=0.0) | ||
| rms = math.sqrt(sum(value * value for value in samples) / max(1, len(samples))) | ||
| return samples, int(sample_rate), { | ||
| "channels": int(waveform.shape[1]), | ||
| "sampleRate": int(sample_rate), | ||
| "frames": int(waveform.shape[0]), | ||
| "durationSeconds": round(len(samples) / sample_rate, 3), | ||
| "peakDbfs": round(20 * math.log10(max(peak, 1e-12)), 4), | ||
| "rmsDbfs": round(20 * math.log10(max(rms, 1e-12)), 3), | ||
| "exactClipSamples": sum(abs(value) >= 1.0 for value in samples), | ||
| } | ||
|
|
||
|
|
||
| def build() -> Dict[str, object]: | ||
| soundfile_version = getattr(sf, "__version__", "") | ||
| libsndfile_version = getattr(sf, "__libsndfile_version__", "") | ||
| if soundfile_version != EXPECTED_SOUNDFILE: | ||
| raise RuntimeError( | ||
| f"soundfile version drift: expected {EXPECTED_SOUNDFILE}, got {soundfile_version}" | ||
| ) | ||
| if libsndfile_version != EXPECTED_LIBSNDFILE: | ||
| raise RuntimeError( | ||
| f"libsndfile version drift: expected {EXPECTED_LIBSNDFILE}, got {libsndfile_version}" | ||
| ) | ||
|
|
||
| reference_samples: List[float] = [] | ||
| reference_transcripts: List[str] = [] | ||
| source_records: List[Dict[str, object]] = [] | ||
| for filename, role, transcript in SOURCES: | ||
| source = ROOT / "sources" / filename | ||
| if not source.is_file(): | ||
| raise FileNotFoundError(source) | ||
| decoded, sample_rate, metrics = _decode(source) | ||
| delivery, mastering = master_samples(decoded, sample_rate, SETTINGS) | ||
| if role == "generated-primary": | ||
| reference_samples.extend(delivery) | ||
| reference_transcripts.append(transcript) | ||
| source_records.append( | ||
| { | ||
| "path": source.relative_to(ROOT).as_posix(), | ||
| "role": role, | ||
| "bytes": source.stat().st_size, | ||
| "sha256": sha256_file(source), | ||
| "transcript": transcript, | ||
| "decoded": metrics, | ||
| "mastering": mastering, | ||
| } | ||
| ) | ||
|
|
||
| if not reference_samples or not reference_transcripts: | ||
| raise RuntimeError("at least one generated-primary source must be selected") | ||
| write_pcm16_mono(REFERENCE, reference_samples, SETTINGS.sample_rate) | ||
| reference_text = " ".join(reference_transcripts) | ||
| manifest: Dict[str, object] = { | ||
| "schemaVersion": 1, | ||
| "profile": "cybernetic-commander", | ||
| "referenceText": reference_text, | ||
| "transcriptMethod": { | ||
| "engine": "openai/whisper-tiny.en", | ||
| "reviewStatus": "ASR-assisted and manually normalized", | ||
| "releaseReviewRequired": True, | ||
| }, | ||
| "build": { | ||
| "voforgeVersion": voforge_version, | ||
| "soundfileVersion": soundfile_version, | ||
| "libsndfileVersion": libsndfile_version, | ||
| "mastering": SETTINGS.record(), | ||
| }, | ||
| "selectionPolicy": { | ||
| "selectedRole": "generated-primary", | ||
| "reason": "user-selected v2 performance with per-line rhotic spelling controls", | ||
| "otherSources": "remaining clips retained as hash-bound comparison evidence", | ||
| }, | ||
| "sources": source_records, | ||
| "reference": artifact_record(REFERENCE, ROOT), | ||
| } | ||
| MANIFEST.write_text( | ||
| json.dumps(manifest, indent=2, sort_keys=True) + "\n", | ||
| encoding="utf-8", | ||
| newline="\n", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the documented rebuild is run on Python 3.9, which the package and CI matrix explicitly support, Useful? React with 👍 / 👎. |
||
| ) | ||
| return manifest | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| result = build() | ||
| print(json.dumps(result["reference"], indent=2, sort_keys=True)) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These explicit includes place the user-supplied source MP3s and cloned reference WAV into the source distribution that the workflow builds and uploads, even though
docs/consents/cybernetic-commander.mdlimits the recorded authorization to adding the recordings to this repository and requires a separate rights review for redistribution outside it. Publishing or sharing the built archive therefore redistributes the raw recordings without the required recorded review; keep them out of distributions or document that additional authorization before packaging them.Useful? React with 👍 / 👎.