Added support for Gemini TTS
This commit is contained in:
@@ -14,20 +14,6 @@ class RunwareAudioInferenceSpeech:
|
||||
"calm", "fluent", "whisper",
|
||||
]
|
||||
|
||||
SPEECH_LANGUAGES = [
|
||||
"Auto",
|
||||
"Chinese",
|
||||
"English",
|
||||
"Japanese",
|
||||
"Korean",
|
||||
"German",
|
||||
"French",
|
||||
"Russian",
|
||||
"Portuguese",
|
||||
"Spanish",
|
||||
"Italian",
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@@ -50,7 +36,7 @@ class RunwareAudioInferenceSpeech:
|
||||
"default": "",
|
||||
"tooltip": "Voice identifier.",
|
||||
}),
|
||||
"useSpeed": ("BOOLEAN", {"default": True, "tooltip": "Include speed (speech.speed)"}),
|
||||
"useSpeed": ("BOOLEAN", {"default": False, "tooltip": "Include speed (speech.speed)"}),
|
||||
"speed": ("FLOAT", {
|
||||
"default": 1.0,
|
||||
"min": 0.5,
|
||||
@@ -58,7 +44,7 @@ class RunwareAudioInferenceSpeech:
|
||||
"step": 0.01,
|
||||
"tooltip": "Speech rate multiplier (speech.speed)",
|
||||
}),
|
||||
"useVolume": ("BOOLEAN", {"default": True, "tooltip": "Include volume (speech.volume)"}),
|
||||
"useVolume": ("BOOLEAN", {"default": False, "tooltip": "Include volume (speech.volume)"}),
|
||||
"volume": ("FLOAT", {
|
||||
"default": 1.0,
|
||||
"min": 1.0,
|
||||
@@ -87,9 +73,12 @@ class RunwareAudioInferenceSpeech:
|
||||
"default": False,
|
||||
"tooltip": "Enable to include language (speech.language).",
|
||||
}),
|
||||
"language": (cls.SPEECH_LANGUAGES, {
|
||||
"language": ("STRING", {
|
||||
"default": "Auto",
|
||||
"tooltip": "Language of the input text. Options: Auto, Chinese, English, Japanese, Korean, German, French, Russian, Portuguese, Spanish, Italian. Applies to all variants. Only used when 'Use Language' is enabled.",
|
||||
"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.",
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -119,10 +108,13 @@ class RunwareAudioInferenceSpeech:
|
||||
use_tone = kwargs.get("useTone", False)
|
||||
tone_str = kwargs.get("tone", "") or ""
|
||||
use_language = kwargs.get("useLanguage", False)
|
||||
language = kwargs.get("language", "Auto")
|
||||
language = (kwargs.get("language") or "Auto").strip()
|
||||
voices = kwargs.get("voices")
|
||||
|
||||
speech: Dict[str, Any] = {"text": text}
|
||||
if use_voice:
|
||||
if isinstance(voices, list) and len(voices) > 0:
|
||||
speech["voices"] = voices
|
||||
elif use_voice:
|
||||
speech["voice"] = voice
|
||||
if use_speed:
|
||||
speech["speed"] = speed
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
Runware Audio Inference Speech Voices node:
|
||||
Builds speech.voices[] for multi-speaker audio inference speech payloads.
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import Any, Dict, List
|
||||
|
||||
|
||||
class RunwareAudioInferenceSpeechVoices:
|
||||
"""Build speech.voices[] list for Runware Audio Inference."""
|
||||
|
||||
MAX_VOICES = 4
|
||||
_SPEAKER_ALIAS_PATTERN = re.compile(r"^[A-Za-z0-9]+$")
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
optional_inputs = {}
|
||||
for i in range(1, cls.MAX_VOICES + 1):
|
||||
optional_inputs[f"useVoice{i}"] = ("BOOLEAN", {
|
||||
"default": i == 1,
|
||||
"tooltip": f"Enable to include speaker/voice pair #{i} in speech.voices.",
|
||||
})
|
||||
optional_inputs[f"speaker{i}"] = ("STRING", {
|
||||
"default": f"Speaker{i}",
|
||||
"tooltip": "Unique alphanumeric speaker alias (speech.voices[].speaker).",
|
||||
})
|
||||
optional_inputs[f"voice{i}"] = ("STRING", {
|
||||
"default": "",
|
||||
"tooltip": "Voice name for this speaker (speech.voices[].voice).",
|
||||
})
|
||||
|
||||
return {
|
||||
"required": {},
|
||||
"optional": optional_inputs,
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("RUNWAREAUDIOINFERENCESPEECHVOICES",)
|
||||
RETURN_NAMES = ("voices",)
|
||||
FUNCTION = "createVoices"
|
||||
CATEGORY = "Runware/Audio"
|
||||
DESCRIPTION = (
|
||||
"Build speech.voices[] for multi-speaker synthesis. "
|
||||
"Each item contains speaker (unique alphanumeric alias) and voice name."
|
||||
)
|
||||
|
||||
def createVoices(self, **kwargs) -> tuple[List[Dict[str, str]]]:
|
||||
voices: List[Dict[str, str]] = []
|
||||
seen_speakers = set()
|
||||
|
||||
for i in range(1, self.MAX_VOICES + 1):
|
||||
use_voice = bool(kwargs.get(f"useVoice{i}", i == 1))
|
||||
if not use_voice:
|
||||
continue
|
||||
|
||||
speaker = (kwargs.get(f"speaker{i}") or "").strip()
|
||||
voice = (kwargs.get(f"voice{i}") or "").strip()
|
||||
|
||||
if not speaker or not voice:
|
||||
raise Exception(
|
||||
f"Voice #{i} is enabled, but speaker{i} and voice{i} must both be provided."
|
||||
)
|
||||
if not self._SPEAKER_ALIAS_PATTERN.match(speaker):
|
||||
raise Exception(
|
||||
f"speaker{i} must be alphanumeric only (got: '{speaker}')."
|
||||
)
|
||||
if speaker in seen_speakers:
|
||||
raise Exception(
|
||||
f"Duplicate speaker alias '{speaker}'. Each speaker must be unique."
|
||||
)
|
||||
|
||||
seen_speakers.add(speaker)
|
||||
voices.append({
|
||||
"speaker": speaker,
|
||||
"voice": voice,
|
||||
})
|
||||
|
||||
return (voices,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"RunwareAudioInferenceSpeechVoices": RunwareAudioInferenceSpeechVoices,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"RunwareAudioInferenceSpeechVoices": "Runware Audio Inference Speech Voices",
|
||||
}
|
||||
@@ -40,6 +40,9 @@ class RunwareAudioModelSearch:
|
||||
"inworld:tts@1.5-mini (Inworld TTS-1.5 Mini)",
|
||||
"inworld:tts@1.5-max (Inworld TTS-1.5 Max)",
|
||||
],
|
||||
"Google": [
|
||||
"google:gemini@3.1-flash-tts (Gemini 3.1 Flash TTS)",
|
||||
],
|
||||
}
|
||||
|
||||
MODEL_PROVIDERS = [
|
||||
@@ -53,6 +56,7 @@ class RunwareAudioModelSearch:
|
||||
"xAI",
|
||||
"MiniMax",
|
||||
"Inworld",
|
||||
"Google",
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
||||
Reference in New Issue
Block a user