Merge pull request #178 from Runware/feature-Seed-audio
Added support for Seed Audio 1
This commit is contained in:
@@ -3601,6 +3601,9 @@ function audioModelSearchFilterHandler(audioModelSearchNode) {
|
||||
"alibaba:qwen@3-tts-1.7b-customvoice (Qwen3 TTS 1.7B Custom Voice)",
|
||||
"alibaba:qwen@3-tts-1.7b-base (Qwen3 TTS 1.7B Base)",
|
||||
],
|
||||
"Bytedance": [
|
||||
"bytedance:seed-audio@1.0 (Seed Audio 1.0)",
|
||||
],
|
||||
"Dia": [
|
||||
"runware:dia@v1.0 (Dia 1.6B)",
|
||||
"runware:dia2@2b (Dia2 2B)",
|
||||
@@ -4975,6 +4978,7 @@ function audioInferenceInputsToggleHandler(audioInputsNode) {
|
||||
const videoWidget = audioInputsNode.widgets.find(w => w && w.name === "Video");
|
||||
const useVideosWidget = audioInputsNode.widgets.find(w => w && w.name === "useVideos");
|
||||
const useAudiosWidget = audioInputsNode.widgets.find(w => w && w.name === "useAudios");
|
||||
const useReferenceAudiosWidget = audioInputsNode.widgets.find(w => w && w.name === "useReferenceAudios");
|
||||
|
||||
// Helper function to toggle widget enabled state
|
||||
function toggleWidgetState(useWidget, paramWidget, paramName) {
|
||||
@@ -5136,6 +5140,59 @@ function audioInferenceInputsToggleHandler(audioInputsNode) {
|
||||
// Initial call to set initial state
|
||||
setTimeout(toggleVideosEnabled, 100);
|
||||
}
|
||||
|
||||
// Set up toggle handlers for reference audios (Reference Audio 1 … N; N from widgets, fallback to 4)
|
||||
if (useReferenceAudiosWidget) {
|
||||
function getAudioInferenceReferenceAudioSlotCount() {
|
||||
let max = 0;
|
||||
for (const w of audioInputsNode.widgets) {
|
||||
if (!w?.name) continue;
|
||||
const m = /^Reference Audio (\d+)$/.exec(w.name);
|
||||
if (m) max = Math.max(max, parseInt(m[1], 10));
|
||||
}
|
||||
return max > 0 ? max : 4;
|
||||
}
|
||||
|
||||
function toggleReferenceAudiosEnabled() {
|
||||
const enabled = useReferenceAudiosWidget.value === true;
|
||||
const slotCount = getAudioInferenceReferenceAudioSlotCount();
|
||||
|
||||
for (let i = 1; i <= slotCount; i++) {
|
||||
const refAudioWidget = audioInputsNode.widgets.find(w => w && w.name === `Reference Audio ${i}`);
|
||||
if (refAudioWidget) {
|
||||
if (refAudioWidget.inputEl) {
|
||||
refAudioWidget.inputEl.disabled = !enabled;
|
||||
refAudioWidget.inputEl.style.opacity = enabled ? "1" : "0.5";
|
||||
refAudioWidget.inputEl.style.cursor = enabled ? "text" : "not-allowed";
|
||||
refAudioWidget.inputEl.readOnly = !enabled;
|
||||
}
|
||||
|
||||
refAudioWidget.disabled = !enabled;
|
||||
|
||||
if (!refAudioWidget.inputEl) {
|
||||
const nodeElement = audioInputsNode.htmlElements?.widgetsContainer || audioInputsNode.htmlElements;
|
||||
if (nodeElement) {
|
||||
const input = nodeElement.querySelector(`input[name="Reference Audio ${i}"], textarea[name="Reference Audio ${i}"], select[name="Reference Audio ${i}"]`);
|
||||
if (input) {
|
||||
input.disabled = !enabled;
|
||||
input.style.opacity = enabled ? "1" : "0.5";
|
||||
input.style.cursor = enabled ? "text" : "not-allowed";
|
||||
input.readOnly = !enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
audioInputsNode.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
appendWidgetCB(useReferenceAudiosWidget, () => {
|
||||
setTimeout(toggleReferenceAudiosEnabled, 50);
|
||||
});
|
||||
|
||||
setTimeout(toggleReferenceAudiosEnabled, 100);
|
||||
}
|
||||
}
|
||||
|
||||
function audioInferenceSpeechVoicesToggleHandler(voicesNode) {
|
||||
|
||||
@@ -7,6 +7,8 @@ class audioInferenceInputs:
|
||||
|
||||
MAX_VIDEOS = 4
|
||||
MAX_AUDIOS = 4
|
||||
MAX_REFERENCE_IMAGES = 4
|
||||
MAX_REFERENCE_AUDIOS = 4
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
@@ -53,6 +55,23 @@ class audioInferenceInputs:
|
||||
"default": "",
|
||||
})
|
||||
|
||||
for i in range(1, cls.MAX_REFERENCE_IMAGES + 1):
|
||||
ordinal = rwUtils.getOrdinal(i)
|
||||
optionalInputs[f"Reference Image {i}"] = ("IMAGE", {
|
||||
"tooltip": f"Optional {ordinal} reference image for audio inference (inputs.referenceImages).",
|
||||
})
|
||||
|
||||
optionalInputs["useReferenceAudios"] = ("BOOLEAN", {
|
||||
"tooltip": "Enable to include reference audios in API request (inputs.referenceAudios array).",
|
||||
"default": False,
|
||||
})
|
||||
for i in range(1, cls.MAX_REFERENCE_AUDIOS + 1):
|
||||
ordinal = rwUtils.getOrdinal(i)
|
||||
optionalInputs[f"Reference Audio {i}"] = ("STRING", {
|
||||
"tooltip": f"Audio URL or mediaUUID for the {ordinal} reference audio (inputs.referenceAudios).",
|
||||
"default": "",
|
||||
})
|
||||
|
||||
optionalInputs["Reference Voice"] = ("RUNWAREAUDIOINFERENCEREFERENCEVOICES", {
|
||||
"tooltip": "Connect Runware Audio Inference Inputs Reference Audio for zero-shot voice cloning (inputs.referenceVoices).",
|
||||
})
|
||||
@@ -65,7 +84,8 @@ class audioInferenceInputs:
|
||||
DESCRIPTION = (
|
||||
"Configure custom inputs for Runware Audio Inference, including optional single or multiple audio URL/mediaUUID "
|
||||
"(inputs.audio or inputs.audios), reference voice for cloning (inputs.referenceVoices), "
|
||||
"and single or multiple video inputs for audio extraction or generation."
|
||||
"single or multiple video inputs for audio extraction or generation, up to 4 reference images "
|
||||
"(inputs.referenceImages), and up to 4 reference audios (inputs.referenceAudios)."
|
||||
)
|
||||
FUNCTION = "createInputs"
|
||||
RETURN_TYPES = ("RUNWAREAUDIOINFERENCEINPUTS",)
|
||||
@@ -80,6 +100,7 @@ class audioInferenceInputs:
|
||||
useVideo = kwargs.get("useVideo", False)
|
||||
video = kwargs.get("Video", None)
|
||||
useVideos = kwargs.get("useVideos", False)
|
||||
useReferenceAudios = kwargs.get("useReferenceAudios", False)
|
||||
referenceVoices = kwargs.get("Reference Voice", None)
|
||||
|
||||
inputs = {}
|
||||
@@ -116,6 +137,23 @@ class audioInferenceInputs:
|
||||
if len(videoList) > 0:
|
||||
inputs["videos"] = videoList
|
||||
|
||||
referenceImages = []
|
||||
for i in range(1, self.MAX_REFERENCE_IMAGES + 1):
|
||||
image = kwargs.get(f"Reference Image {i}", None)
|
||||
if image is not None:
|
||||
referenceImages.append(rwUtils.convertTensor2IMG(image))
|
||||
if len(referenceImages) > 0:
|
||||
inputs["referenceImages"] = referenceImages
|
||||
|
||||
if useReferenceAudios:
|
||||
referenceAudios = []
|
||||
for i in range(1, self.MAX_REFERENCE_AUDIOS + 1):
|
||||
audioUrl = kwargs.get(f"Reference Audio {i}", None)
|
||||
if audioUrl and isinstance(audioUrl, str) and audioUrl.strip() != "":
|
||||
referenceAudios.append(audioUrl.strip())
|
||||
if len(referenceAudios) > 0:
|
||||
inputs["referenceAudios"] = referenceAudios
|
||||
|
||||
if referenceVoices is not None and isinstance(referenceVoices, list) and len(referenceVoices) > 0:
|
||||
inputs["referenceVoices"] = referenceVoices
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ class RunwareAudioInferenceSpeech:
|
||||
"required": {
|
||||
"text": ("STRING", {
|
||||
"multiline": True,
|
||||
"default": "Hello, this is a test of the speech synthesis model.",
|
||||
"tooltip": "Text to synthesize as speech (speech.text)",
|
||||
"default": "",
|
||||
"tooltip": "Text to synthesize as speech (speech.text). Optional when positivePrompt on Runware Audio Inference carries the script (e.g. Seed Audio).",
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
@@ -38,19 +38,19 @@ class RunwareAudioInferenceSpeech:
|
||||
}),
|
||||
"useSpeed": ("BOOLEAN", {"default": False, "tooltip": "Include speed (speech.speed)"}),
|
||||
"speed": ("FLOAT", {
|
||||
"default": 1.0,
|
||||
"min": 0.5,
|
||||
"default": 0.0,
|
||||
"min": 0.0,
|
||||
"max": 2.0,
|
||||
"step": 0.01,
|
||||
"tooltip": "Speech rate multiplier (speech.speed)",
|
||||
"tooltip": "Speech rate adjustment (speech.speed). 0 = no change for Seed Audio.",
|
||||
}),
|
||||
"useVolume": ("BOOLEAN", {"default": False, "tooltip": "Include volume (speech.volume)"}),
|
||||
"volume": ("FLOAT", {
|
||||
"default": 1.0,
|
||||
"min": 1.0,
|
||||
"default": 0.0,
|
||||
"min": 0.0,
|
||||
"max": 10.0,
|
||||
"step": 0.1,
|
||||
"tooltip": "Volume level 1-10 (speech.volume)",
|
||||
"tooltip": "Volume adjustment (speech.volume). 0 = no change.",
|
||||
}),
|
||||
"usePitch": ("BOOLEAN", {"default": False, "tooltip": "Include pitch (speech.pitch)"}),
|
||||
"pitch": ("FLOAT", {
|
||||
@@ -98,9 +98,9 @@ class RunwareAudioInferenceSpeech:
|
||||
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))
|
||||
speed = float(kwargs.get("speed", 0.0))
|
||||
use_volume = kwargs.get("useVolume", False)
|
||||
volume = float(kwargs.get("volume", 1.0))
|
||||
volume = float(kwargs.get("volume", 0.0))
|
||||
use_pitch = kwargs.get("usePitch", False)
|
||||
pitch = float(kwargs.get("pitch", 0.0))
|
||||
use_emotion = kwargs.get("useEmotion", False)
|
||||
@@ -111,10 +111,12 @@ class RunwareAudioInferenceSpeech:
|
||||
language = (kwargs.get("language") or "Auto").strip()
|
||||
voices = kwargs.get("voices")
|
||||
|
||||
speech: Dict[str, Any] = {"text": text}
|
||||
speech: Dict[str, Any] = {}
|
||||
if text:
|
||||
speech["text"] = text
|
||||
if isinstance(voices, list) and len(voices) > 0:
|
||||
speech["voices"] = voices
|
||||
elif use_voice:
|
||||
elif use_voice and voice:
|
||||
speech["voice"] = voice
|
||||
if use_speed:
|
||||
speech["speed"] = speed
|
||||
@@ -131,8 +133,6 @@ class RunwareAudioInferenceSpeech:
|
||||
if use_language:
|
||||
speech["language"] = language
|
||||
|
||||
if not text:
|
||||
return ({},)
|
||||
return (speech,)
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ class RunwareAudioModelSearch:
|
||||
"alibaba:qwen@3-tts-1.7b-customvoice (Qwen3 TTS 1.7B Custom Voice)",
|
||||
"alibaba:qwen@3-tts-1.7b-base (Qwen3 TTS 1.7B Base)",
|
||||
],
|
||||
"Bytedance": [
|
||||
"bytedance:seed-audio@1.0 (Seed Audio 1.0)",
|
||||
],
|
||||
"Dia": [
|
||||
"runware:dia@v1.0 (Dia 1.6B)",
|
||||
"runware:dia2@2b (Dia2 2B)",
|
||||
@@ -59,6 +62,7 @@ class RunwareAudioModelSearch:
|
||||
"Mirelo",
|
||||
"Ace",
|
||||
"Alibaba",
|
||||
"Bytedance",
|
||||
"Dia",
|
||||
"xAI",
|
||||
"MiniMax",
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "1.9.1"
|
||||
__version__ = "1.9.2"
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user