Skip to content

Commit 298bd68

Browse files
Copilotlmangani
andcommitted
Remove all audio decoding: generate becomes OUTPUT_NODE copying native MP3/WAV via shutil
Co-authored-by: lmangani <1423657+lmangani@users.noreply.github.com>
1 parent fc671ca commit 298bd68

7 files changed

Lines changed: 33 additions & 257 deletions

File tree

nodes.py

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,8 @@
55
import shutil
66
import subprocess
77
import tempfile
8-
import wave
98
from typing import Any, Dict, List, Optional, Tuple, Union
109

11-
import numpy as np
12-
import torch
13-
1410
import folder_paths
1511

1612
CONFIG_FILE = os.path.join(os.path.dirname(__file__), "config.json")
@@ -1128,8 +1124,8 @@ def INPUT_TYPES(cls):
11281124
},
11291125
}
11301126

1131-
RETURN_TYPES = ("AUDIO",)
1132-
RETURN_NAMES = ("audio",)
1127+
RETURN_TYPES = ()
1128+
OUTPUT_NODE = True
11331129
FUNCTION = "generate"
11341130
CATEGORY = "AcestepCPP"
11351131

@@ -1394,33 +1390,14 @@ def generate(
13941390
f"stdout: {dit_result.stdout}\nstderr: {dit_result.stderr}"
13951391
)
13961392

1397-
# Decode the generated audio to raw PCM via ffmpeg, then read it
1398-
# with the built-in wave module. This avoids optional torchaudio
1399-
# backends such as torchcodec that may not be installed.
1400-
decoded_wav = os.path.join(tmpdir, "decoded.wav")
1401-
_decode = subprocess.run(
1402-
["ffmpeg", "-y", "-i", audio_path, "-acodec", "pcm_f32le", decoded_wav],
1403-
capture_output=True,
1393+
# Copy the native MP3/WAV to ComfyUI's output directory unchanged —
1394+
# no decoding, no re-encoding, no external tools.
1395+
out_dir = folder_paths.get_output_directory()
1396+
full_out_folder, base_name, counter, subfolder, _ = (
1397+
folder_paths.get_save_image_path("acestep", out_dir)
14041398
)
1405-
if _decode.returncode != 0:
1406-
raise RuntimeError(
1407-
f"ffmpeg could not decode {os.path.basename(audio_path)}:\n"
1408-
+ _decode.stderr.decode(errors="replace")
1409-
)
1410-
1411-
with wave.open(decoded_wav, "rb") as _wf:
1412-
n_ch = _wf.getnchannels()
1413-
sample_rate = _wf.getframerate()
1414-
raw = _wf.readframes(_wf.getnframes())
1415-
1416-
# pcm_f32le: 32-bit float, little-endian, interleaved channels
1417-
pcm = np.frombuffer(raw, dtype=np.float32)
1418-
if n_ch > 1:
1419-
pcm_2d = pcm.reshape(-1, n_ch).T.copy() # (channels, samples)
1420-
else:
1421-
pcm_2d = pcm.reshape(1, -1).copy() # (1, samples)
1422-
waveform = torch.from_numpy(pcm_2d)
1423-
# ComfyUI AUDIO format: waveform shape (batch, channels, samples)
1424-
audio = {"waveform": waveform.unsqueeze(0), "sample_rate": sample_rate}
1399+
save_filename = f"{base_name}_{counter:05}{ext}"
1400+
save_path = os.path.join(full_out_folder, save_filename)
1401+
shutil.copy2(audio_path, save_path)
14251402

1426-
return (audio,)
1403+
return {"ui": {"audio": [{"filename": save_filename, "subfolder": subfolder, "type": "output"}]}}

tests/test_nodes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,10 @@ def test_reference_audio_removed(self):
333333
assert "reference_audio_input" not in opt
334334

335335
def test_return_types(self):
336-
assert nodes.AcestepCPPGenerate.RETURN_TYPES == ("AUDIO",)
336+
assert nodes.AcestepCPPGenerate.RETURN_TYPES == ()
337+
338+
def test_is_output_node(self):
339+
assert nodes.AcestepCPPGenerate.OUTPUT_NODE is True
337340

338341
def test_lego_tracks_list(self):
339342
"""LEGO_TRACKS must include the track names documented in the README."""

tests/test_workflows.py

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -77,31 +77,24 @@ def test_has_generate_node(self, workflow_path):
7777
assert _nodes_by_type(wf, "AcestepCPPGenerate"), \
7878
"Workflow must include AcestepCPPGenerate"
7979

80-
# --- Output node: save-then-play --------------------------------------
80+
# --- Output: generate node is itself the output node ---------------------
8181

82-
def test_uses_save_audio_not_preview_audio(self, workflow_path):
82+
def test_has_no_audio_output_node(self, workflow_path):
83+
"""AcestepCPPGenerate is itself an OUTPUT_NODE; no SaveAudio or PreviewAudio needed."""
8384
wf = _load(workflow_path)
8485
node_types = [n["type"] for n in wf["nodes"]]
86+
assert "SaveAudio" not in node_types, \
87+
"SaveAudio is no longer needed; AcestepCPPGenerate copies the file and shows its own player"
8588
assert "PreviewAudio" not in node_types, \
86-
"PreviewAudio (play-only) should be replaced by SaveAudio (save-then-play)"
87-
assert "SaveAudio" in node_types, \
88-
"SaveAudio must be present so generated audio is persisted to disk"
89+
"PreviewAudio is no longer needed; AcestepCPPGenerate shows its own audio player"
8990

90-
def test_save_audio_has_filename_prefix(self, workflow_path):
91+
def test_generate_has_no_output_links(self, workflow_path):
92+
"""AcestepCPPGenerate is a terminal output node; its output slot must have no outgoing links."""
9193
wf = _load(workflow_path)
92-
for node in _nodes_by_type(wf, "SaveAudio"):
93-
wv = node.get("widgets_values", [])
94-
assert len(wv) >= 1 and isinstance(wv[0], str) and wv[0], \
95-
"SaveAudio must have a non-empty filename_prefix widget value"
96-
97-
def test_save_audio_format_is_mp3(self, workflow_path):
98-
"""SaveAudio must use mp3 format — not the ComfyUI default of flac."""
99-
wf = _load(workflow_path)
100-
for node in _nodes_by_type(wf, "SaveAudio"):
101-
wv = node.get("widgets_values", [])
102-
got = repr(wv[1]) if len(wv) > 1 else "(missing)"
103-
assert len(wv) >= 2 and wv[1] == "mp3", \
104-
f"SaveAudio format (index 1) must be 'mp3', got {got}"
94+
for node in _nodes_by_type(wf, "AcestepCPPGenerate"):
95+
for output in node.get("outputs", []):
96+
assert not output.get("links"), \
97+
f"AcestepCPPGenerate output '{output.get('name')}' should have no outgoing links"
10598

10699
# --- Model loader widget values ---------------------------------------
107100

@@ -310,12 +303,3 @@ def test_model_loader_output_connected(self, workflow_path):
310303
for node in _nodes_by_type(wf, "AcestepCPPModelLoader"):
311304
assert node["id"] in link_src_nodes, \
312305
"AcestepCPPModelLoader output is not connected to any node"
313-
314-
def test_generate_output_connected_to_save_audio(self, workflow_path):
315-
"""AcestepCPPGenerate audio output must feed into SaveAudio."""
316-
wf = _load(workflow_path)
317-
node_ids = {n["id"]: n for n in wf["nodes"]}
318-
save_audio_ids = {n["id"] for n in _nodes_by_type(wf, "SaveAudio")}
319-
# Walk links: find any link whose destination is a SaveAudio node
320-
connected = any(lnk[3] in save_audio_ids for lnk in wf.get("links", []))
321-
assert connected, "No link from any node to SaveAudio found"

workflow-examples/acestep-cpp-cover.json

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,7 @@
127127
"link": null
128128
}
129129
],
130-
"outputs": [
131-
{
132-
"name": "audio",
133-
"type": "AUDIO",
134-
"links": [
135-
2
136-
],
137-
"slot_index": 0
138-
}
139-
],
130+
"outputs": [],
140131
"properties": {
141132
"Node name for S&R": "AcestepCPPGenerate"
142133
},
@@ -167,36 +158,6 @@
167158
"",
168159
1.0
169160
]
170-
},
171-
{
172-
"id": 3,
173-
"type": "SaveAudio",
174-
"pos": [
175-
880,
176-
20
177-
],
178-
"size": {
179-
"0": 320,
180-
"1": 130
181-
},
182-
"flags": {},
183-
"order": 3,
184-
"mode": 0,
185-
"inputs": [
186-
{
187-
"name": "audio",
188-
"type": "AUDIO",
189-
"link": 2
190-
}
191-
],
192-
"outputs": [],
193-
"properties": {
194-
"Node name for S&R": "SaveAudio"
195-
},
196-
"widgets_values": [
197-
"acestep/output",
198-
"mp3"
199-
]
200161
}
201162
],
202163
"links": [
@@ -208,14 +169,6 @@
208169
0,
209170
"ACESTEP_MODELS"
210171
],
211-
[
212-
2,
213-
2,
214-
0,
215-
3,
216-
0,
217-
"AUDIO"
218-
],
219172
[
220173
3,
221174
4,

workflow-examples/acestep-cpp-lora.json

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,7 @@
128128
"link": null
129129
}
130130
],
131-
"outputs": [
132-
{
133-
"name": "audio",
134-
"type": "AUDIO",
135-
"links": [
136-
2
137-
],
138-
"slot_index": 0
139-
}
140-
],
131+
"outputs": [],
141132
"properties": {
142133
"Node name for S&R": "AcestepCPPGenerate"
143134
},
@@ -168,36 +159,6 @@
168159
"",
169160
1.0
170161
]
171-
},
172-
{
173-
"id": 3,
174-
"type": "SaveAudio",
175-
"pos": [
176-
880,
177-
20
178-
],
179-
"size": {
180-
"0": 320,
181-
"1": 130
182-
},
183-
"flags": {},
184-
"order": 3,
185-
"mode": 0,
186-
"inputs": [
187-
{
188-
"name": "audio",
189-
"type": "AUDIO",
190-
"link": 2
191-
}
192-
],
193-
"outputs": [],
194-
"properties": {
195-
"Node name for S&R": "SaveAudio"
196-
},
197-
"widgets_values": [
198-
"acestep/output",
199-
"mp3"
200-
]
201162
}
202163
],
203164
"links": [
@@ -209,14 +170,6 @@
209170
0,
210171
"ACESTEP_MODELS"
211172
],
212-
[
213-
2,
214-
2,
215-
0,
216-
3,
217-
0,
218-
"AUDIO"
219-
],
220173
[
221174
3,
222175
4,

workflow-examples/acestep-cpp-reference-audio.json

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,7 @@
127127
"link": null
128128
}
129129
],
130-
"outputs": [
131-
{
132-
"name": "audio",
133-
"type": "AUDIO",
134-
"links": [
135-
2
136-
],
137-
"slot_index": 0
138-
}
139-
],
130+
"outputs": [],
140131
"properties": {
141132
"Node name for S&R": "AcestepCPPGenerate"
142133
},
@@ -167,36 +158,6 @@
167158
"",
168159
1.0
169160
]
170-
},
171-
{
172-
"id": 3,
173-
"type": "SaveAudio",
174-
"pos": [
175-
880,
176-
20
177-
],
178-
"size": {
179-
"0": 320,
180-
"1": 130
181-
},
182-
"flags": {},
183-
"order": 3,
184-
"mode": 0,
185-
"inputs": [
186-
{
187-
"name": "audio",
188-
"type": "AUDIO",
189-
"link": 2
190-
}
191-
],
192-
"outputs": [],
193-
"properties": {
194-
"Node name for S&R": "SaveAudio"
195-
},
196-
"widgets_values": [
197-
"acestep/output",
198-
"mp3"
199-
]
200161
}
201162
],
202163
"links": [
@@ -208,14 +169,6 @@
208169
0,
209170
"ACESTEP_MODELS"
210171
],
211-
[
212-
2,
213-
2,
214-
0,
215-
3,
216-
0,
217-
"AUDIO"
218-
],
219172
[
220173
3,
221174
4,

0 commit comments

Comments
 (0)