hanxxing's picture
Duplicate from rzgar/Bernini-R-S2V
832fee5
Raw
History Blame Contribute Delete
3.63 kB
import math
import torch
import node_helpers
from comfy_extras.nodes_wan import get_audio_embed_bucket_fps, linear_interpolation
WAN_AUDIO_INPUT_FPS = 50
WAN_AUDIO_VIDEO_RATE = 30
WAN_AUDIO_FPS = 16
WAN_AUDIO_SAMPLE_RATE = 16000
def _audio_feat(audio_encoder_output):
feat = torch.cat(audio_encoder_output["encoded_audio_all_layers"])
return linear_interpolation(feat, input_fps=WAN_AUDIO_INPUT_FPS, output_fps=WAN_AUDIO_VIDEO_RATE)
def audio_encoder_output_video_frames(audio_encoder_output, fps=WAN_AUDIO_FPS):
audio_samples = audio_encoder_output.get("audio_samples")
if audio_samples is not None:
return max(1, int(round(audio_samples / float(WAN_AUDIO_SAMPLE_RATE) * fps)))
feat = _audio_feat(audio_encoder_output)
return max(1, int(round(feat.shape[1] * fps / WAN_AUDIO_VIDEO_RATE)))
def _permute_audio_embed_bucket(audio_embed_bucket):
audio_embed_bucket = audio_embed_bucket.unsqueeze(0)
if len(audio_embed_bucket.shape) == 3:
return audio_embed_bucket.permute(0, 2, 1)
return audio_embed_bucket.permute(0, 2, 3, 1)
def build_timeline_audio_embed(length, segments):
latent_t = ((length - 1) // 4) + 1
batch_frames = latent_t * 4
total_feat_frames = int(math.ceil(batch_frames * WAN_AUDIO_VIDEO_RATE / WAN_AUDIO_FPS))
composite = None
cursor_auto = 0
for segment in segments:
feat = _audio_feat(segment["audio_encoder_output"])
if composite is None:
composite = torch.zeros(
feat.shape[0], total_feat_frames, feat.shape[2],
dtype=feat.dtype, device=feat.device)
start_frame = segment.get("start_frame", -1)
if start_frame is None or start_frame < 0:
start_frame = cursor_auto
else:
start_frame = int(start_frame)
start_feat = int(round(start_frame * WAN_AUDIO_VIDEO_RATE / WAN_AUDIO_FPS))
copy_len = min(feat.shape[1], total_feat_frames - start_feat)
if copy_len > 0 and start_feat < total_feat_frames:
composite[:, start_feat:start_feat + copy_len, :] = feat[:, :copy_len, :]
cursor_auto = start_frame + audio_encoder_output_video_frames(segment["audio_encoder_output"])
audio_embed_bucket, _ = get_audio_embed_bucket_fps(
composite, fps=WAN_AUDIO_FPS, batch_frames=batch_frames, m=0, video_rate=WAN_AUDIO_VIDEO_RATE)
audio_embed_bucket = _permute_audio_embed_bucket(audio_embed_bucket)
return audio_embed_bucket[:, :, :, :batch_frames]
def resolve_timeline_segment_ranges(length, segments):
batch_frames = (((length - 1) // 4) + 1) * 4
resolved = []
cursor_auto = 0
for segment in segments:
start_frame = segment.get("start_frame", -1)
if start_frame is None or start_frame < 0:
start_frame = cursor_auto
else:
start_frame = int(start_frame)
end_frame = min(batch_frames, start_frame + audio_encoder_output_video_frames(segment["audio_encoder_output"]))
resolved.append({**segment, "start_frame": start_frame, "end_frame": end_frame})
cursor_auto = end_frame
return resolved
def apply_timeline_audio_conditioning(positive, negative, length, segments):
audio_embed_bucket = build_timeline_audio_embed(length, segments)
if audio_embed_bucket is None or audio_embed_bucket.shape[3] <= 0:
return positive, negative
positive = node_helpers.conditioning_set_values(positive, {"audio_embed": audio_embed_bucket})
negative = node_helpers.conditioning_set_values(negative, {"audio_embed": audio_embed_bucket * 0.0})
return positive, negative