Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
ComfyUI
Commits
4650e7d6
Commit
4650e7d6
authored
Jun 27, 2024
by
comfyanonymous
Browse files
Save and load workflow from the flac files output by SaveAudio.
parent
3b423afc
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
164 additions
and
4 deletions
+164
-4
comfy_extras/nodes_audio.py
comfy_extras/nodes_audio.py
+77
-2
web/scripts/app.js
web/scripts/app.js
+14
-1
web/scripts/pnginfo.js
web/scripts/pnginfo.js
+72
-0
web/scripts/ui.js
web/scripts/ui.js
+1
-1
No files found.
comfy_extras/nodes_audio.py
View file @
4650e7d6
...
...
@@ -3,6 +3,10 @@ import torch
import
comfy.model_management
import
folder_paths
import
os
import
io
import
json
import
struct
from
comfy.cli_args
import
args
class
EmptyLatentAudio
:
def
__init__
(
self
):
...
...
@@ -53,6 +57,61 @@ class VAEDecodeAudio:
audio
=
vae
.
decode
(
samples
[
"samples"
]).
movedim
(
-
1
,
1
)
return
({
"waveform"
:
audio
,
"sample_rate"
:
44100
},
)
def
create_vorbis_comment_block
(
comment_dict
,
last_block
):
vendor_string
=
b
'ComfyUI'
vendor_length
=
len
(
vendor_string
)
comments
=
[]
for
key
,
value
in
comment_dict
.
items
():
comment
=
f
"
{
key
}
=
{
value
}
"
.
encode
(
'utf-8'
)
comments
.
append
(
struct
.
pack
(
'<I'
,
len
(
comment
))
+
comment
)
user_comment_list_length
=
len
(
comments
)
user_comments
=
b
''
.
join
(
comments
)
comment_data
=
struct
.
pack
(
'<I'
,
vendor_length
)
+
vendor_string
+
struct
.
pack
(
'<I'
,
user_comment_list_length
)
+
user_comments
if
last_block
:
id
=
b
'
\x84
'
else
:
id
=
b
'
\x04
'
comment_block
=
id
+
struct
.
pack
(
'>I'
,
len
(
comment_data
))[
1
:]
+
comment_data
return
comment_block
def
insert_or_replace_vorbis_comment
(
flac_io
,
comment_dict
):
if
len
(
comment_dict
)
==
0
:
return
flac_io
flac_io
.
seek
(
4
)
blocks
=
[]
last_block
=
False
while
not
last_block
:
header
=
flac_io
.
read
(
4
)
last_block
=
(
header
[
0
]
&
0x80
)
!=
0
block_type
=
header
[
0
]
&
0x7F
block_length
=
struct
.
unpack
(
'>I'
,
b
'
\x00
'
+
header
[
1
:])[
0
]
block_data
=
flac_io
.
read
(
block_length
)
if
block_type
==
4
or
block_type
==
1
:
pass
else
:
header
=
bytes
([(
header
[
0
]
&
(
~
0x80
))])
+
header
[
1
:]
blocks
.
append
(
header
+
block_data
)
blocks
.
append
(
create_vorbis_comment_block
(
comment_dict
,
last_block
=
True
))
new_flac_io
=
io
.
BytesIO
()
new_flac_io
.
write
(
b
'fLaC'
)
for
block
in
blocks
:
new_flac_io
.
write
(
block
)
new_flac_io
.
write
(
flac_io
.
read
())
return
new_flac_io
class
SaveAudio
:
def
__init__
(
self
):
self
.
output_dir
=
folder_paths
.
get_output_directory
()
...
...
@@ -78,11 +137,27 @@ class SaveAudio:
filename_prefix
+=
self
.
prefix_append
full_output_folder
,
filename
,
counter
,
subfolder
,
filename_prefix
=
folder_paths
.
get_save_image_path
(
filename_prefix
,
self
.
output_dir
)
results
=
list
()
metadata
=
{}
if
not
args
.
disable_metadata
:
if
prompt
is
not
None
:
metadata
[
"prompt"
]
=
json
.
dumps
(
prompt
)
if
extra_pnginfo
is
not
None
:
for
x
in
extra_pnginfo
:
metadata
[
x
]
=
json
.
dumps
(
extra_pnginfo
[
x
])
for
(
batch_number
,
waveform
)
in
enumerate
(
audio
[
"waveform"
]):
#TODO: metadata
filename_with_batch_num
=
filename
.
replace
(
"%batch_num%"
,
str
(
batch_number
))
file
=
f
"
{
filename_with_batch_num
}
_
{
counter
:
05
}
_.flac"
torchaudio
.
save
(
os
.
path
.
join
(
full_output_folder
,
file
),
waveform
,
audio
[
"sample_rate"
],
format
=
"FLAC"
)
buff
=
io
.
BytesIO
()
torchaudio
.
save
(
buff
,
waveform
,
audio
[
"sample_rate"
],
format
=
"FLAC"
)
buff
=
insert_or_replace_vorbis_comment
(
buff
,
metadata
)
with
open
(
os
.
path
.
join
(
full_output_folder
,
file
),
'wb'
)
as
f
:
f
.
write
(
buff
.
getbuffer
())
results
.
append
({
"filename"
:
file
,
"subfolder"
:
subfolder
,
...
...
web/scripts/app.js
View file @
4650e7d6
...
...
@@ -3,7 +3,7 @@ import { ComfyWidgets, initWidgets } from "./widgets.js";
import
{
ComfyUI
,
$el
}
from
"
./ui.js
"
;
import
{
api
}
from
"
./api.js
"
;
import
{
defaultGraph
}
from
"
./defaultGraph.js
"
;
import
{
getPngMetadata
,
getWebpMetadata
,
importA1111
,
getLatentMetadata
}
from
"
./pnginfo.js
"
;
import
{
getPngMetadata
,
getWebpMetadata
,
getFlacMetadata
,
importA1111
,
getLatentMetadata
}
from
"
./pnginfo.js
"
;
import
{
addDomClippingSetting
}
from
"
./domWidget.js
"
;
import
{
createImageHost
,
calculateImageGrid
}
from
"
./ui/imagePreview.js
"
;
import
{
ComfyAppMenu
}
from
"
./ui/menu/index.js
"
;
...
...
@@ -2277,6 +2277,19 @@ export class ComfyApp {
const
workflow
=
pngInfo
?.
workflow
||
pngInfo
?.
Workflow
;
const
prompt
=
pngInfo
?.
prompt
||
pngInfo
?.
Prompt
;
if
(
workflow
)
{
this
.
loadGraphData
(
JSON
.
parse
(
workflow
),
true
,
true
,
fileName
);
}
else
if
(
prompt
)
{
this
.
loadApiJson
(
JSON
.
parse
(
prompt
),
fileName
);
}
else
{
this
.
showErrorOnFileLoad
(
file
);
}
}
else
if
(
file
.
type
===
"
audio/flac
"
)
{
const
pngInfo
=
await
getFlacMetadata
(
file
);
// Support loading workflows from that webp custom node.
const
workflow
=
pngInfo
?.
workflow
;
const
prompt
=
pngInfo
?.
prompt
;
if
(
workflow
)
{
this
.
loadGraphData
(
JSON
.
parse
(
workflow
),
true
,
true
,
fileName
);
}
else
if
(
prompt
)
{
...
...
web/scripts/pnginfo.js
View file @
4650e7d6
...
...
@@ -163,6 +163,78 @@ export function getLatentMetadata(file) {
});
}
function
getString
(
dataView
,
offset
,
length
)
{
let
string
=
''
;
for
(
let
i
=
0
;
i
<
length
;
i
++
)
{
string
+=
String
.
fromCharCode
(
dataView
.
getUint8
(
offset
+
i
));
}
return
string
;
}
// Function to parse the Vorbis Comment block
function
parseVorbisComment
(
dataView
)
{
let
offset
=
0
;
const
vendorLength
=
dataView
.
getUint32
(
offset
,
true
);
offset
+=
4
;
const
vendorString
=
getString
(
dataView
,
offset
,
vendorLength
);
offset
+=
vendorLength
;
const
userCommentListLength
=
dataView
.
getUint32
(
offset
,
true
);
offset
+=
4
;
const
comments
=
{};
for
(
let
i
=
0
;
i
<
userCommentListLength
;
i
++
)
{
const
commentLength
=
dataView
.
getUint32
(
offset
,
true
);
offset
+=
4
;
const
comment
=
getString
(
dataView
,
offset
,
commentLength
);
offset
+=
commentLength
;
const
[
key
,
value
]
=
comment
.
split
(
'
=
'
);
comments
[
key
]
=
value
;
}
return
comments
;
}
// Function to read a FLAC file and parse Vorbis comments
export
function
getFlacMetadata
(
file
)
{
return
new
Promise
((
r
)
=>
{
const
reader
=
new
FileReader
();
reader
.
onload
=
function
(
event
)
{
const
arrayBuffer
=
event
.
target
.
result
;
const
dataView
=
new
DataView
(
arrayBuffer
);
// Verify the FLAC signature
const
signature
=
String
.
fromCharCode
(...
new
Uint8Array
(
arrayBuffer
,
0
,
4
));
if
(
signature
!==
'
fLaC
'
)
{
console
.
error
(
'
Not a valid FLAC file
'
);
return
;
}
// Parse metadata blocks
let
offset
=
4
;
let
vorbisComment
=
null
;
while
(
offset
<
dataView
.
byteLength
)
{
const
isLastBlock
=
dataView
.
getUint8
(
offset
)
&
0x80
;
const
blockType
=
dataView
.
getUint8
(
offset
)
&
0x7F
;
const
blockSize
=
dataView
.
getUint32
(
offset
,
false
)
&
0xFFFFFF
;
offset
+=
4
;
if
(
blockType
===
4
)
{
// Vorbis Comment block type
vorbisComment
=
parseVorbisComment
(
new
DataView
(
arrayBuffer
,
offset
,
blockSize
));
}
offset
+=
blockSize
;
if
(
isLastBlock
)
break
;
}
r
(
vorbisComment
);
};
reader
.
readAsArrayBuffer
(
file
);
});
}
export
async
function
importA1111
(
graph
,
parameters
)
{
const
p
=
parameters
.
lastIndexOf
(
"
\n
Steps:
"
);
if
(
p
>
-
1
)
{
...
...
web/scripts/ui.js
View file @
4650e7d6
...
...
@@ -373,7 +373,7 @@ export class ComfyUI {
const
fileInput
=
$el
(
"
input
"
,
{
id
:
"
comfy-file-input
"
,
type
:
"
file
"
,
accept
:
"
.json,image/png,.latent,.safetensors,image/webp
"
,
accept
:
"
.json,image/png,.latent,.safetensors,image/webp
,audio/flac
"
,
style
:
{
display
:
"
none
"
},
parent
:
document
.
body
,
onchange
:
()
=>
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment