From e62f6c67a6ba9dda4ebb975ed25bc7753ab1cb9c Mon Sep 17 00:00:00 2001 From: billwuhao Date: Thu, 6 Mar 2025 21:35:11 +0800 Subject: [PATCH] add 'MW Audio Recorder' node --- MWAudioRecorder.py | 140 +++++++++++++++++++++++++++++++++ README-en.md | 4 + README.md | 4 + StepAudioTTS.py | 63 ++------------- __init__.py | 4 +- assets/2025-03-06_21-29-09.png | Bin 0 -> 27269 bytes pyproject.toml | 2 +- 7 files changed, 159 insertions(+), 58 deletions(-) create mode 100644 MWAudioRecorder.py create mode 100644 assets/2025-03-06_21-29-09.png diff --git a/MWAudioRecorder.py b/MWAudioRecorder.py new file mode 100644 index 0000000..ded4b9b --- /dev/null +++ b/MWAudioRecorder.py @@ -0,0 +1,140 @@ +import numpy as np +import torch +import time +import librosa +import sounddevice as sd +from scipy import ndimage +from comfy.utils import ProgressBar + +class AudioRecorder: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + # 触发控制 + "trigger": ("BOOLEAN", {"default": False}), + # 录音时长 + "record_sec": ("INT", { + "default": 5, + "min": 1, + "max": 60, + "step": 1 # 整数秒递增 + }), + "sample_rate": (["16000", "44100", "48000"], { # 限定标准采样率 + "default": "48000" + }), + "n_fft": ("INT", { # 限定为2的幂次方 + "default": 2048, + "min": 512, + "max": 4096, + "step": 512 # 只能选择512,1024,1536,2048,...4096 + }), + "sensitivity": ("FLOAT", { # 灵敏度精确控制 + "default": 1.2, + "min": 0.5, + "max": 3.0, + "step": 0.1 # 0.1步进 + }), + "smooth": ("INT", { # 确保为奇数 + "default": 5, + "min": 1, + "max": 11, + "step": 2 # 生成1,3,5,7,9,11 + }), + "seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF}), + } + } + + RETURN_TYPES = ("AUDIO",) + RETURN_NAMES = ("audio",) + FUNCTION = "record_and_clean" + CATEGORY = "MW-Step-Audio" + + def _stft(self, y, n_fft): + hop = n_fft // 4 + return librosa.stft(y, n_fft=n_fft, hop_length=hop, win_length=n_fft) + + def _istft(self, spec, n_fft): + hop = n_fft // 4 + return librosa.istft(spec, hop_length=hop, win_length=n_fft) + + def _calc_noise_profile(self, noise_clip, n_fft): + noise_spec = self._stft(noise_clip, n_fft) + return { + 'mean': np.mean(np.abs(noise_spec), axis=1, keepdims=True), + 'std': np.std(np.abs(noise_spec), axis=1, keepdims=True) + } + + def _spectral_gate(self, spec, noise_profile, sensitivity): + threshold = noise_profile['mean'] + sensitivity * noise_profile['std'] + return np.where(np.abs(spec) > threshold, spec, 0) + + def _smooth_mask(self, mask, kernel_size): + smoothed = ndimage.uniform_filter(mask, size=(kernel_size, kernel_size)) + return np.clip(smoothed * 1.2, 0, 1) # 增强边缘保留 + + def record_and_clean(self, trigger, record_sec, n_fft, sensitivity, smooth, sample_rate, seed): + if not trigger: + return (None,) + + sr = int(sample_rate) + final_audio = None + + try: + noise_clip = None + # 主录音 + # print(f"开始主录音 {record_sec}秒...") + main_rec = sd.rec(int(record_sec * sr), samplerate=sr, channels=1, dtype='float32') + pb = ProgressBar(record_sec) + for _ in range(record_sec * 2): + time.sleep(0.5) + pb.update(0.5) + sd.wait() + audio = main_rec.flatten() + + # 自动噪声检测 + if noise_clip is None: + # print("自动检测静默段作为噪声参考...") + energy = librosa.feature.rms(y=audio, frame_length=n_fft, hop_length=n_fft//4) + min_idx = np.argmin(energy) + start = min_idx * (n_fft//4) + noise_clip = audio[start:start + n_fft*2] + + # 降噪处理 + # print("进行频谱降噪...") + noise_profile = self._calc_noise_profile(noise_clip, n_fft) + spec = self._stft(audio, n_fft) + + # 多步骤处理 + mask = np.ones_like(spec) # 初始掩膜 + for _ in range(2): # 双重处理循环 + cleaned_spec = self._spectral_gate(spec, noise_profile, sensitivity) + mask = np.where(np.abs(cleaned_spec) > 0, 1, 0) + mask = self._smooth_mask(mask, smooth//2+1) + spec = spec * mask + + # 相位恢复重建 + processed = self._istft(spec * mask, n_fft) + + # 动态增益归一化 + peak = np.max(np.abs(processed)) + processed = processed * (0.99 / peak) if peak > 0 else processed + + # 格式转换 + waveform = torch.from_numpy(processed).float().unsqueeze(0).unsqueeze(0) + final_audio = {"waveform": waveform, "sample_rate": sr} + + except Exception as e: + print(f"Recording/processing failed: {str(e)}") + raise + + return (final_audio,) + +# 节点注册 +NODE_CLASS_MAPPINGS = { + "AudioRecorder": AudioRecorder +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "AudioRecorder": "MW Audio Recorder" +} \ No newline at end of file diff --git a/README-en.md b/README-en.md index 381209f..78247c7 100644 --- a/README-en.md +++ b/README-en.md @@ -6,6 +6,10 @@ ## Update +[2025-03-06]⚒️: New recording node `MW Audio Recorder` can be used to record audio with a microphone, and the progress bar displays the recording progress: + +![](https://github.com/billwuhao/ComfyUI_StepAudioTTS/blob/master/assets/2025-03-06_21-29-09.png) + [2025-03-02]⚒️: Add experimental `custom_mark`, surrounding with "()", for example `(温柔)(东北话)`, it may have an effect. [2025-02-25]⚒️: Support custom speaker `custom_stpeaker`. But please note that the three places in the figure must maintain consistent speaker name. When customizing, the `speaker` will automatically become invalid. diff --git a/README.md b/README.md index e424d09..6f23a0d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ ## 更新 +[2025-03-06]⚒️: 新增录音节点 `MW Audio Recorder` 可用麦克风录制音频, 进度条显示录制进度: + +![](https://github.com/billwuhao/ComfyUI_StepAudioTTS/blob/master/assets/2025-03-06_21-29-09.png) + [2025-03-02]⚒️: 增加实验性的 `custom_mark`, 用 "()" 包围例如 `(温柔)(东北话)`, 它可能会有效. [2025-02-25]⚒️: 支持自定义说话者 `custom_speaker`. 但注意下图三个地方必须保持一致的说话者名称. 自定义时 `speaker` 将自动无效. diff --git a/StepAudioTTS.py b/StepAudioTTS.py index a2a7e2c..f04aab3 100644 --- a/StepAudioTTS.py +++ b/StepAudioTTS.py @@ -85,61 +85,6 @@ class StepAudioTTS: return self._music_cosy_model def __call__(self, text: str, cosy_model, prompt_speaker_info, history): - # instruction_name = self.detect_instruction_name(text) - - # if "RAP" in instruction_name or "哼唱" in instruction_name: - # cosy_model = self.music_cosy_model - # else: - # cosy_model = self.common_cosy_model - - # prompt_speaker_info = {} - # if clone_dict: - # clone_prompt_code, clone_prompt_token, clone_prompt_token_len, clone_speech_feat, clone_speech_feat_len, clone_speech_embedding = ( - # self.preprocess_prompt_wav(clone_dict['audio'], cosy_model) - # ) - # prompt_speaker_info = { - # "prompt_text": clone_dict['prompt_text'], - # "prompt_code": clone_prompt_code, - # "cosy_speech_feat": clone_speech_feat.to(torch.bfloat16), - # "cosy_speech_feat_len": clone_speech_feat_len, - # "cosy_speech_embedding": clone_speech_embedding.to(torch.bfloat16), - # "cosy_prompt_token": clone_prompt_token, - # "cosy_prompt_token_len": clone_prompt_token_len, - # } - - # prompt_speaker = clone_dict['speaker'] - # # print(prompt_speaker, " 内置文本: ", prompt_speaker_info["prompt_text"], end="\n\n") - - # else: - # with open(f"{speaker_path}/speakers_info.json", "r") as f: - # speakers_info = json.load(f) - - # for speaker_id, prompt_text in speakers_info.items(): - # if speaker_id == prompt_speaker: - # prompt_wav_path = f"{speaker_path}/{speaker_id}_prompt.wav" - # waveform, sample_rate = torchaudio.load(prompt_wav_path) - # audio = {"waveform": waveform.unsqueeze(0), "sample_rate": sample_rate} - # prompt_code, prompt_token, prompt_token_len, speech_feat, speech_feat_len, speech_embedding = ( - # self.preprocess_prompt_wav(audio, cosy_model) - # ) - # prompt_speaker_info = { - # "prompt_text": prompt_text, - # "prompt_code": prompt_code, - # "cosy_speech_feat": speech_feat.to(torch.bfloat16), - # "cosy_speech_feat_len": speech_feat_len, - # "cosy_speech_embedding": speech_embedding.to(torch.bfloat16), - # "cosy_prompt_token": prompt_token, - # "cosy_prompt_token_len": prompt_token_len, - # } - # # print(prompt_speaker, " 内置文本: ", prompt_speaker_info["prompt_text"], end="\n\n") - # break - - # elif prompt_speaker not in speakers_info.keys(): - # raise ValueError("There is no such speaker") - - # print("指定文本: ", text, "的说话者是: ", prompt_speaker, end="\n\n") - - # cosy_model, prompt_speaker_info, history = self.data_preprocess() _prefix_tokens = self.autotokenizer.encode("\n") target_token_encode = self.autotokenizer.encode("\n" + text) @@ -482,8 +427,16 @@ class StepAudioClone: audio_tensor = torch.cat(audio_data, dim=1).unsqueeze(0).float() return ({"waveform": audio_tensor, "sample_rate": sr},) +from MWAudioRecorder import AudioRecorder NODE_CLASS_MAPPINGS = { "StepAudioRun": StepAudioRun, "StepAudioClone": StepAudioClone, + "AudioRecorder": AudioRecorder } + +NODE_DISPLAY_NAME_MAPPINGS = { + "StepAudioRun": "Step Audio Run", + "StepAudioClone": "Step Audio Clone", + "AudioRecorder": "MW Audio Recorder" +} \ No newline at end of file diff --git a/__init__.py b/__init__.py index 92e9f62..bfdf0d8 100644 --- a/__init__.py +++ b/__init__.py @@ -4,6 +4,6 @@ import os current_dir = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, current_dir) -from StepAudioTTS import NODE_CLASS_MAPPINGS +from StepAudioTTS import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS -__all__ = ["NODE_CLASS_MAPPINGS"] +__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] diff --git a/assets/2025-03-06_21-29-09.png b/assets/2025-03-06_21-29-09.png new file mode 100644 index 0000000000000000000000000000000000000000..7ddf97f8e166a8330c5be06f42a103b17fee3b62 GIT binary patch literal 27269 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�}T9m7piTQb_V~M7ge?W9` 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 S76QZ_mzKJoTAr#+;Qs*qTQ@@h literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml index 9e97cdd..3c3bc2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "stepaudiotts_mw" description = "A Text To Speech node using Step-Audio-TTS in ComfyUI. Can speak, rap, sing, or clone voice." -version = "1.1.2" +version = "1.1.3" license = {file = "LICENSE"} dependencies = ["transformers>=4.48.3", "openai-whisper>=20231117", "sox>=1.5.0", "hyperpyyaml", "conformer>=0.3.2", "funasr>=1.1.3"]