Download ComfyUI-MiniMaxH3-Context-Loop/av_timing.py from dzmdd/Fast_deploy: direct link, hf CLI and curl.
- Browser
- Download file 3.62 kB
-
https://huggingface.co/dzmdd/Fast_deploy/resolve/main/ComfyUI-MiniMaxH3-Context-Loop/av_timing.py
- Command line
-
hf download hf://dzmdd/Fast_deploy/ComfyUI-MiniMaxH3-Context-Loop/av_timing.py
-
curl -L -o av_timing.py https://huggingface.co/dzmdd/Fast_deploy/resolve/main/ComfyUI-MiniMaxH3-Context-Loop/av_timing.py
3.62 kB
| """Exact MiniMax H3 picture/audio timeline helpers. | |
| H3 picture runs live on a 24 fps clock while generated audio latents live on | |
| a 40 Hz clock. A decoded audio stream can consequently land a few | |
| milliseconds either side of its exact frame-derived duration. Small grid | |
| mismatches are time-conformed here instead of being repaired with silence. | |
| """ | |
| from __future__ import annotations | |
| from fractions import Fraction | |
| import logging | |
| import torch | |
| try: | |
| import torchaudio | |
| except ImportError: | |
| torchaudio = None | |
| _LOG = logging.getLogger("minimax_h3_context_loop.av_timing") | |
| # Private AUDIO mapping fields passed only between Loop Trim and Segment Save. | |
| # Keeping this transport inside the existing AUDIO value avoids another public | |
| # socket and another copy of the same decoded stream in user workflows. | |
| AUDIO_WITH_OVERLAP_WAVEFORM_KEY = "_h3_audio_with_overlap_waveform" | |
| AUDIO_WITH_OVERLAP_FRAMES_KEY = "_h3_audio_with_overlap_frames" | |
| AUDIO_TRIM_FRAMES_KEY = "_h3_audio_trim_frames" | |
| def sample_boundary_from_seconds(seconds: float, sample_rate: int) -> int: | |
| """Return the nearest PCM sample on one absolute time boundary.""" | |
| return int(round(float(seconds) * int(sample_rate))) | |
| def sample_boundary_from_frames( | |
| frame_position: int, sample_rate: int, fps: int = 24) -> int: | |
| """Return the nearest PCM sample on one absolute video-frame boundary.""" | |
| return int(round(int(frame_position) / float(fps) * int(sample_rate))) | |
| def conform_waveform_length( | |
| waveform, samples: int, label: str, | |
| max_fractional_change: float = 0.005): | |
| """Time-conform a small decoder/grid mismatch to an exact sample span. | |
| A larger discrepancy is likely incorrect wiring or timing metadata and is | |
| rejected instead of being hidden by a large resample. | |
| """ | |
| target = int(samples) | |
| current = int(waveform.shape[-1]) | |
| if current == target: | |
| return waveform | |
| if current <= 0 or target <= 0: | |
| raise ValueError( | |
| "%s has an invalid audio sample length %d -> %d." % | |
| (label, current, target)) | |
| fractional_change = abs(target - current) / float(target) | |
| if fractional_change > float(max_fractional_change): | |
| raise ValueError( | |
| "%s differs from its exact frame timeline by %.3f%% " | |
| "(%d -> %d samples), too large for H3 audio time-conformance." % | |
| (label, fractional_change * 100.0, current, target)) | |
| ratio = Fraction(target, current).limit_denominator(10000) | |
| if torchaudio is not None: | |
| conformed = torchaudio.functional.resample( | |
| waveform, int(ratio.denominator), int(ratio.numerator)) | |
| else: | |
| shape = tuple(waveform.shape) | |
| conformed = torch.nn.functional.interpolate( | |
| waveform.reshape(-1, 1, current), | |
| size=target, | |
| mode="linear", | |
| align_corners=False, | |
| ).reshape(*shape[:-1], target) | |
| # A rational approximation or resampler backend may miss by one sample. | |
| # Correct the residual by interpolation so silence is never introduced. | |
| if int(conformed.shape[-1]) != target: | |
| shape = tuple(conformed.shape) | |
| conformed = torch.nn.functional.interpolate( | |
| conformed.reshape(-1, 1, int(conformed.shape[-1])), | |
| size=target, | |
| mode="linear", | |
| align_corners=False, | |
| ).reshape(*shape[:-1], target) | |
| _LOG.info( | |
| "%s time-conformed %d -> %d audio samples (%+.4f%%) to match " | |
| "the exact video timeline", | |
| label, current, target, | |
| (target - current) / float(current) * 100.0) | |
| return conformed | |