""" Runware Audio Inference Speech node: builds the speech payload for Runware Audio Inference. Connect its output to the "speech" input of Runware Audio Inference for speech synthesis (e.g. Minimax). """ from typing import Dict, Any class RunwareAudioInferenceSpeech: """Builds speech payload (text, voice, speed, volume, pitch?, emotion?, tone?) for Runware Audio Inference.""" EMOTIONS = [ "happy", "sad", "angry", "fearful", "disgusted", "surprised", "calm", "fluent", "whisper", ] def __init__(self): pass @classmethod def INPUT_TYPES(cls): return { "required": { "text": ("STRING", { "multiline": True, "default": "Hello, this is a test of the speech synthesis model.", "tooltip": "Text to synthesize as speech (speech.text)", }), }, "optional": { "useVoice": ("BOOLEAN", { "default": False, "tooltip": "Enable to include voice in speech synthesis (speech.voice).", }), "voice": ("STRING", { "default": "", "tooltip": "Voice identifier.", }), "useSpeed": ("BOOLEAN", {"default": False, "tooltip": "Include speed (speech.speed)"}), "speed": ("FLOAT", { "default": 1.0, "min": 0.5, "max": 2.0, "step": 0.01, "tooltip": "Speech rate multiplier (speech.speed)", }), "useVolume": ("BOOLEAN", {"default": False, "tooltip": "Include volume (speech.volume)"}), "volume": ("FLOAT", { "default": 1.0, "min": 1.0, "max": 10.0, "step": 0.1, "tooltip": "Volume level 1-10 (speech.volume)", }), "usePitch": ("BOOLEAN", {"default": False, "tooltip": "Include pitch (speech.pitch)"}), "pitch": ("FLOAT", { "default": 0.0, "min": -12.0, "max": 12.0, "step": 0.1, "tooltip": "Pitch multiplier -12 to 12 (speech.pitch)", }), "useEmotion": ("BOOLEAN", {"default": False, "tooltip": "Include emotion (not available for speech.turbo)"}), "emotion": (cls.EMOTIONS, {"default": "calm", "tooltip": "Emotion for speech (speech.emotion)"}), "useTone": ("BOOLEAN", {"default": False, "tooltip": "Include pronunciation dictionary (speech.tone)"}), "tone": ("STRING", { "multiline": True, "default": "", "placeholder": "omg/oh my god\nother/other expansion", "tooltip": "Pronunciation dictionary, one mapping per line (e.g. omg/oh my god)", }), "useLanguage": ("BOOLEAN", { "default": False, "tooltip": "Enable to include language (speech.language).", }), "language": ("STRING", { "default": "Auto", "tooltip": "Language of the input text (for example: en-US). Applies to all variants. Only used when 'Use Language' is enabled.", }), "voices": ("RUNWAREAUDIOINFERENCESPEECHVOICES", { "tooltip": "Optional multi-speaker voices array (speech.voices[]). If provided, it takes precedence over speech.voice.", }), } } RETURN_TYPES = ("RUNWARESPEECH",) RETURN_NAMES = ("speech",) FUNCTION = "createSpeech" CATEGORY = "Runware/Audio" DESCRIPTION = ( "Build speech parameters for Runware Audio Inference (text, voice, speed, volume, pitch, emotion, tone, language, etc.). " "Connect output to the 'speech' input of Runware Audio Inference." ) def createSpeech(self, **kwargs) -> tuple: """Build the speech object for the API (connect to Runware Audio Inference 'speech' input).""" text = (kwargs.get("text") or "").strip() voice = (kwargs.get("voice") or "").strip() use_voice = kwargs.get("useVoice", False) use_speed = kwargs.get("useSpeed", False) speed = float(kwargs.get("speed", 1.0)) use_volume = kwargs.get("useVolume", False) volume = float(kwargs.get("volume", 1.0)) use_pitch = kwargs.get("usePitch", False) pitch = float(kwargs.get("pitch", 0.0)) use_emotion = kwargs.get("useEmotion", False) emotion = kwargs.get("emotion", "calm") use_tone = kwargs.get("useTone", False) tone_str = kwargs.get("tone", "") or "" use_language = kwargs.get("useLanguage", False) language = (kwargs.get("language") or "Auto").strip() voices = kwargs.get("voices") speech: Dict[str, Any] = {"text": text} if isinstance(voices, list) and len(voices) > 0: speech["voices"] = voices elif use_voice: speech["voice"] = voice if use_speed: speech["speed"] = speed if use_volume: speech["volume"] = volume if use_pitch: speech["pitch"] = pitch if use_emotion: speech["emotion"] = emotion if use_tone and tone_str.strip(): tone_lines = [s.strip() for s in tone_str.replace(",", "\n").split("\n") if s.strip()] if tone_lines: speech["tone"] = tone_lines if use_language: speech["language"] = language if not text: return ({},) return (speech,) NODE_CLASS_MAPPINGS = { "RunwareAudioInferenceSpeech": RunwareAudioInferenceSpeech, } NODE_DISPLAY_NAME_MAPPINGS = { "RunwareAudioInferenceSpeech": "Runware Audio Inference Speech", }