Files
c0ffymachyne-ComfyUI_Signal…/core/io.py
T

335 lines
10 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Author: C0ffymachyne
License: GPLv3
Version: 1.0.0
Description:
io for audio files and comfy
"""
import torch, torchaudio
from pathlib import Path
from typing import Dict, Tuple, Any
import numpy as np
# this mess needs some cleanup !!
# when we return audio to comfy, turn it to cpu
def audio_to_comfy_3d(
waveform: torch.Tensor, sample_rate: int, cpu=True, clip: bool = False
) -> Tuple[Dict]:
# try to fix wrong shape
if waveform.ndim == 2:
waveform = waveform.unsqueeze(0)
if waveform.ndim != 3:
raise RuntimeError(
f"returning to_comfy failed {waveform.shape}@{waveform.device}[{waveform.ndim}] has incorrect dimentions, it should be of dimension [batch,channels,audio]"
)
if cpu:
if waveform.device != "cpu":
waveform = waveform.cpu()
if waveform.dtype != torch.float64:
waveform = waveform.to(dtype=torch.float64)
if clip:
waveform = np.clip(waveform, -1.0, 1.0)
return_value = ({"waveform": waveform, "sample_rate": sample_rate},)
print(
f"audio_to_comfy_3d '{return_value}' with shape {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate {sample_rate} Hz."
)
return return_value
# when we return audio to comfy, turn it to cpu
def audio_to_comfy_3dp1(
waveform: torch.Tensor, sample_rate: int, value0: Any, cpu=True, clip: bool = False
) -> Tuple[Dict]:
# try to fix wrong shape
if waveform.ndim == 2:
waveform = waveform.unsqueeze(0)
if waveform.ndim != 3:
raise RuntimeError(
f"returning to_comfy failed {waveform.shape}@{waveform.device}dim[{waveform.ndim}] has incorrect dimentions, it should be of dimension [batch,channels,audio]"
)
if cpu:
if waveform.device != "cpu":
waveform = waveform.cpu()
if waveform.dtype != torch.float64:
waveform = waveform.to(dtype=torch.float64)
if clip:
waveform = np.clip(waveform, -1.0, 1.0)
return_value = (
{"waveform": waveform, "sample_rate": sample_rate},
value0,
)
print(
f"audio_to_comfy_3d '{return_value}' with shape {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate {sample_rate} Hz."
)
return return_value
# when we return audio to comfy, turn it to cpu
def audio_to_comfy_3dp2(
waveform: torch.Tensor,
sample_rate: int,
value0: Any,
value1: Any,
cpu=True,
clip: bool = False,
) -> Tuple[Dict]:
# try to fix wrong shape
if waveform.ndim == 2:
waveform = waveform.unsqueeze(0)
if waveform.ndim != 3:
raise RuntimeError(
f"returning to_comfy failed {waveform.shape}@{waveform.device}dim[{waveform.ndim}] has incorrect dimentions, it should be of dimension [batch,channels,audio]"
)
if cpu:
if waveform.device != "cpu":
waveform = waveform.cpu()
if waveform.dtype != torch.float64:
waveform = waveform.to(dtype=torch.float64)
if clip:
waveform = np.clip(waveform, -1.0, 1.0)
return_value = (
{"waveform": waveform, "sample_rate": sample_rate},
value0,
value1,
)
print(
f"audio_to_comfy_3d '{return_value}' with shape {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate {sample_rate} Hz."
)
return return_value
# when we get audio from comfy, load it to gpu
def audio_from_comfy_2d(
audio: Tuple[Dict], repeat=True, try_gpu=True
) -> Tuple[torch.Tensor, int]:
if audio is None:
raise ValueError("Input audio must be provided.")
waveform = audio.get("waveform") # [batch, channels, samples]
sample_rate = audio.get("sample_rate")
if waveform.dtype != torch.float64:
waveform = waveform.to(dtype=torch.float64)
if waveform is None or sample_rate is None:
raise ValueError("Input audio must contain 'waveform' and 'sample_rate'.")
if not isinstance(waveform, torch.Tensor):
raise TypeError("Waveform must be a torch.Tensor.")
print(
f"audio_from_comfy_2d with shape {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate {sample_rate} Hz."
)
if waveform.ndim != 3:
raise ValueError(
f"Waveform must be a 3D tensor with shape (batch, channels, samples) not {waveform.shape}@{waveform.device}"
)
if repeat:
if waveform.ndim == 1: # add extra channel in case of mono
print(f"audio_from_comfy_2d repeat")
waveform = waveform.unsqueeze(0) # Add channel dimension
waveform = waveform.repeat(2, 1) # copy mono to the new channel
waveform = waveform.squeeze(0).contiguous()
if try_gpu:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
waveform = waveform.to(device)
print(
f"audio_from_comfy_2d with shape {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate {sample_rate} Hz."
)
return waveform, sample_rate
def audio_from_comfy_3d(
audio: Tuple[Dict], repeat=True, try_gpu=True
) -> Tuple[torch.Tensor, int]:
if audio is None:
raise ValueError("Input audio must be provided.")
waveform = audio.get("waveform") # [batch, channels, samples]
sample_rate = audio.get("sample_rate")
if waveform.dtype != torch.float64:
waveform = waveform.to(dtype=torch.float64)
if waveform is None or sample_rate is None:
raise ValueError("Input audio must contain 'waveform' and 'sample_rate'.")
if not isinstance(waveform, torch.Tensor):
raise TypeError("Waveform must be a torch.Tensor.")
print(
f"audio_from_comfy_2d with shape {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate {sample_rate} Hz."
)
if waveform.ndim != 3:
raise ValueError(
f"Waveform must be a 3D tensor with shape (batch, channels, samples) not {waveform.shape}@{waveform.device}"
)
if repeat:
if waveform.ndim == 1: # add extra channel in case of mono
print(f"audio_from_comfy_2d repeat")
waveform = waveform.unsqueeze(0) # Add channel dimension
waveform = waveform.repeat(2, 1) # copy mono to the new channel
# waveform = waveform.squeeze(0).contiguous()
if try_gpu:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
waveform = waveform.to(device)
print(
f"audio_from_comfy_3d with shape {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate {sample_rate} Hz."
)
return waveform, sample_rate
def from_disk_as_dict_3d(
audio_file: str,
gain: float = 1.0,
repeat=True,
start_time: float = 0.0,
end_time: float = 0.0,
) -> Dict:
# all loaded audio comes in 3 dimensions
# [batch,channels,audio] 3 dimensions
if not Path(audio_file).exists():
raise ValueError(f"File {audio_file} Does Not Exist")
info = torchaudio.info(audio_file)
sample_rate = info.sample_rate
# cut sudio segment
if (start_time > 0.0) and (end_time > start_time):
frame_offset = int(start_time * sample_rate) # Start frame
num_frames = int((end_time - start_time) * sample_rate) # Total frames to read
waveform, sample_rate = torchaudio.load(
audio_file, frame_offset=frame_offset, num_frames=num_frames
)
else:
waveform, sample_rate = torchaudio.load(audio_file)
if waveform.dtype != torch.float64:
waveform = waveform.to(dtype=torch.float64)
waveform = waveform * gain
if repeat:
if waveform.ndim == 1: # add extra channel in case of mono
waveform = waveform.repeat(2, 1) # copy mono to the new channel
# add batch dimension at the front
waveform = waveform.unsqueeze(0).contiguous()
# waveform = waveform * gain
print(
f"from_disk_as_dict_3d loaded audio file: '{audio_file}' with shape: {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate: {sample_rate} Hz."
)
return audio_to_comfy_3d(waveform, sample_rate)
def from_disk_as_raw_3d(
audio_file: str, repeat: bool = True, try_gpu: bool = False
) -> Tuple[torch.Tensor, int]:
# all loaded audio comes in 3 dimensions
# [batch,channels,audio] 3 dimensions
waveform, sample_rate = torchaudio.load(audio_file)
info = torchaudio.info(audio_file)
print(
f"from_disk_as_raw_3d loaded audio file: '{audio_file}' with shape {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate {sample_rate} Hz."
)
if waveform.dtype != torch.float64:
waveform = waveform.to(dtype=torch.float64)
if repeat:
if info.num_channels == 1: # add extra channel in case of mono
waveform = waveform.repeat(2, 1) # copy mono to the new channel
# add batch dimension at the front
waveform = waveform.unsqueeze(0).contiguous()
if try_gpu:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
waveform = waveform.to(device)
print(
f"from_disk_as_raw_3d loaded audio file: '{audio_file}' with shape {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate {sample_rate} Hz."
)
return waveform, sample_rate
def from_disk_as_raw_2d(
audio_file: str, repeat: bool = True, try_gpu: bool = False
) -> Tuple[torch.Tensor, int]:
# all loaded audio comes in 3 dimensions
# [batch,channels,audio] 3 dimensions
waveform, sample_rate = torchaudio.load(audio_file)
info = torchaudio.info(audio_file)
print(
f"from_disk_as_raw_2d loaded audio file: '{audio_file}' with shape {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate {sample_rate} Hz."
)
if waveform.dtype != torch.float64:
waveform = waveform.to(dtype=torch.float64)
if repeat:
if info.num_channels == 1: # add extra channel in case of mono
waveform = waveform.repeat(2, 1) # copy mono to the new channel
# waveform = waveform.unsqueeze(0) # Add channel dimension
if try_gpu:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
waveform = waveform.to(device)
print(
f"from_disk_as_raw_2d loaded audio file: '{audio_file}' with shape {waveform.shape}@{waveform.device}dim[{waveform.ndim}] and sample rate {sample_rate} Hz."
)
return waveform, sample_rate