{"record":{"id":"001751c8d001c812","repo":"SubtitleEdit/subtitleedit","slug":"moss-tts-crispasr-requires-a-reference-voice-wav","errorCode":null,"errorMessage":"MOSS-TTS (CrispASR) requires a reference voice WAV. Import one via the voice settings, then pick it in the voice combo. Reference WAV should be mono (3-10 s of clean speech); an adjacent .txt file with the spoken transcription is optional but improves cloning quality.","messagePattern":"MOSS-TTS \\(CrispASR\\) requires a reference voice WAV\\. Import one via the voice settings, then pick it in the voice combo\\. Reference WAV should be mono \\(3-10 s of clean speech\\); an adjacent \\.txt file with the spoken transcription is optional but improves cloning quality\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/MossTtsCrispAsr.cs","lineNumber":434,"sourceCode":"        GetVoices(language);\n\n    public async Task<TtsResult> Speak(\n        string text,\n        string outputFolder,\n        Voice voice,\n        TtsLanguage? language,\n        string? region,\n        string? model,\n        CancellationToken cancellationToken)\n    {\n        if (voice.EngineVoice is not MossTtsVoice mossVoice)\n        {\n            throw new ArgumentException(\"Voice is not a MossTtsVoice\");\n        }\n\n        if (string.IsNullOrEmpty(mossVoice.FilePath))\n        {\n            throw new InvalidOperationException(\n                \"MOSS-TTS (CrispASR) requires a reference voice WAV. \"\n                + \"Import one via the voice settings, then pick it in the voice combo. \"\n                + \"Reference WAV should be mono (3-10 s of clean speech); an adjacent .txt file \"\n                + \"with the spoken transcription is optional but improves cloning quality.\");\n        }\n\n        var refText = TryReadRefText(mossVoice.FilePath);\n        var modelKey = ResolveModelKey(model);\n        var languageArg = MossTtsLanguages.ResolveLanguageArg(language);\n        await EnsureServerRunningAsync(modelKey, mossVoice.FilePath, refText, languageArg, cancellationToken);\n\n        var outputFileName = Path.Combine(TtsOutputFolder.Resolve(outputFolder, GetSetFolder), Guid.NewGuid() + \".wav\");\n\n        var speed = Math.Clamp(Se.Settings.Video.TextToSpeech.MossTtsCrispAsrSpeed, 0.25, 4.0);\n        var payload = BuildSpeakPayload(text, speed);\n\n        var body = JsonSerializer.Serialize(payload);\n        using var content = new StringContent(body, Encoding.UTF8, \"application/json\");","sourceCodeStart":416,"sourceCodeEnd":452,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/MossTtsCrispAsr.cs#L416-L452","documentation":"InvalidOperationException from MossTtsCrispAsr.Speak when the MossTtsVoice is the correct type but its `FilePath` is null or empty. MOSS-TTS is a cloning engine that bakes the reference WAV into the server at startup via `--voice` (and an optional adjacent `.txt` for ref-text). An empty path is unrecoverable; the message directs the user to import a reference WAV (mono, 3–10 s) and notes the optional transcription `.txt` improves cloning.","triggerScenarios":"Selecting a MOSS-TTS voice entry created before a reference WAV was attached; deleting/moving the referenced WAV after import; a settings migration that lost FilePath.","commonSituations":"User picked the engine before importing a clip; reference file on a detached removable drive; older settings format that didn't persist FilePath.","solutions":["Open voice settings and import a clean 3–10 s mono WAV as the MOSS-TTS reference.","Optionally place a `.txt` with the transcription next to the WAV (TryReadRefText picks it up).","Re-select the voice in the MOSS-TTS combo so the entry is bound to the file.","If the WAV moved, re-import from its new location."],"exampleFix":"// before\nvar voice = new Voice { EngineVoice = new MossTtsVoice { FilePath = \"\" } };\n\n// after\nvar voice = new Voice { EngineVoice = new MossTtsVoice { FilePath = @\"C:\\Voices\\ref.wav\" } };","handlingStrategy":"validation","validationCode":"if (voice.EngineVoice is MossTtsVoice mv && string.IsNullOrEmpty(mv.FilePath))\n{\n    PromptImportReferenceWav();\n    return;\n}","typeGuard":null,"tryCatchPattern":"try { await mossEngine.Speak(...); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"requires a reference voice WAV\"))\n{\n    PromptImportReferenceWav();\n}","preventionTips":["Disable Speak until the selected MOSS-TTS voice has a non-empty FilePath.","Validate the WAV exists before each batch (files can be moved/deleted).","On import, verify mono PCM format and reject otherwise.","Optionally provide an adjacent `.txt` transcription for better cloning."],"tags":["moss-tts","crispasr","voice-cloning","reference-audio","tts","validation"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}