#!/usr/bin/env python3 """ generate_sample_audio.py - Gera os 5 arquivos de áudio de exemplo para o pipeline RVC. Uso: python generate_sample_audio.py Os arquivos serão criados em: examples/sample_outputs/ - entrada.mp3 (áudio original sintético) - entrada_acapella.mp3 (vocal extraído – simulado) - entrada_instrumental.mp3 (instrumental simulado) - saida.mp3 (RVC simulado sobre o original) - saida_acapella.mp3 (RVC simulado sobre o acapella) Também gera um arquivo de entrada em examples/sample_inputs/sample_audio.wav e um vídeo opcional (se moviepy estiver instalado). """ import os import subprocess import sys import tempfile from pathlib import Path # Tenta importar numpy e scipy; instala automaticamente se faltar try: import numpy as np from scipy.io.wavfile import write as write_wav from scipy import signal except ImportError: print("Instalando dependências necessárias (numpy, scipy)...") subprocess.check_call([sys.executable, "-m", "pip", "install", "numpy", "scipy"]) import numpy as np from scipy.io.wavfile import write as write_wav from scipy import signal # Configurações SAMPLE_RATE = 22050 # Hz DURATION_SEC = 8 # segundos BASE_DIR = Path(__file__).parent INPUTS_DIR = BASE_DIR / "sample_inputs" OUTPUTS_DIR = BASE_DIR / "sample_outputs" def generate_voice(duration_sec: float, sample_rate: int) -> np.ndarray: """ Gera um sinal de voz sintética (formantes simples + vibrato). Retorna array float32 no intervalo [-1, 1]. """ t = np.linspace(0, duration_sec, int(sample_rate * duration_sec), endpoint=False) # Frequência fundamental (voz média) f0 = 180.0 # Vibrato (modulação de frequência) vibrato_freq = 5.0 freq_mod = f0 + 10 * np.sin(2 * np.pi * vibrato_freq * t) # Gera onda com fase acumulada phase = 2 * np.pi * np.cumsum(freq_mod) / sample_rate voice = 0.7 * np.sin(phase) # Adiciona um harmônico (formante) voice += 0.3 * np.sin(2 * np.pi * 2 * f0 * t) # Envelope de amplitude (ataque e decaimento suaves) envelope = np.ones_like(t) attack_samples = int(0.1 * sample_rate) decay_samples = int(0.2 * sample_rate) envelope[:attack_samples] = np.linspace(0, 1, attack_samples) envelope[-decay_samples:] = np.linspace(1, 0, decay_samples) voice *= envelope return voice.astype(np.float32) def generate_instrumental(duration_sec: float, sample_rate: int) -> np.ndarray: """ Gera um fundo instrumental simples (acorde + ruído leve). """ t = np.linspace(0, duration_sec, int(sample_rate * duration_sec), endpoint=False) # Acorde de Dó maior (262, 330, 392 Hz) chord = (0.2 * np.sin(2 * np.pi * 262 * t) + 0.2 * np.sin(2 * np.pi * 330 * t) + 0.2 * np.sin(2 * np.pi * 392 * t)) # Ruído branco suave (simula percussão) noise = np.random.normal(0, 0.05, len(t)) # Envelope comum envelope = np.ones_like(t) attack = int(0.05 * sample_rate) envelope[:attack] = np.linspace(0, 1, attack) chord *= envelope noise *= envelope instrumental = chord + noise return instrumental.astype(np.float32) def apply_pitch_shift(audio: np.ndarray, semitones: float, sample_rate: int) -> np.ndarray: """ Aplica um deslocamento de pitch (em semitons) usando remostragem. Positivo = mais agudo, negativo = mais grave. """ if semitones == 0: return audio # Fator de remostragem: 2^(semitones/12) factor = 2 ** (semitones / 12.0) new_length = int(len(audio) / factor) shifted = signal.resample(audio, new_length) # Ajusta para o comprimento original (corta ou completa com zeros) if len(shifted) > len(audio): shifted = shifted[:len(audio)] else: shifted = np.pad(shifted, (0, len(audio) - len(shifted)), mode='constant') return shifted.astype(audio.dtype) def save_audio(file_path: Path, audio_array: np.ndarray, sample_rate: int = SAMPLE_RATE) -> None: """ Salva um array como arquivo MP3 usando ffmpeg (via arquivo WAV temporário). """ # Salva WAV temporário with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp_wav: tmp_wav_path = tmp_wav.name write_wav(tmp_wav_path, sample_rate, audio_array) # Converte para MP3 com ffmpeg cmd = [ "ffmpeg", "-y", "-i", tmp_wav_path, "-acodec", "libmp3lame", "-b:a", "192k", str(file_path) ] subprocess.run(cmd, check=True, capture_output=True) # Remove o WAV temporário os.unlink(tmp_wav_path) print(f" ✅ {file_path.name}") def main(): print("=" * 60) print("🎵 Gerando arquivos de áudio de exemplo para o RVC Full Suite") print("=" * 60) # Criar diretórios INPUTS_DIR.mkdir(parents=True, exist_ok=True) OUTPUTS_DIR.mkdir(parents=True, exist_ok=True) print(f"📁 Diretório de entrada: {INPUTS_DIR}") print(f"📁 Diretório de saída: {OUTPUTS_DIR}") # 1. Gerar áudio sintético (voz + instrumental) print("\n🎤 Gerando áudio sintético...") voice = generate_voice(DURATION_SEC, SAMPLE_RATE) instrumental = generate_instrumental(DURATION_SEC, SAMPLE_RATE) # 2. Misturar para obter o áudio original (entrada) # Normaliza para evitar clipping original = (voice + instrumental) / 1.2 original = np.clip(original, -1.0, 1.0) # Salvar entrada original como WAV (para referência) e MP3 input_wav = INPUTS_DIR / "sample_audio.wav" write_wav(str(input_wav), SAMPLE_RATE, original) print(f"✅ Áudio original salvo em: {input_wav}") # 3. Gerar os 5 arquivos de saída print("\n📦 Gerando 5 arquivos de saída (sample_outputs)...") # entrada.mp3 (original) entrada_mp3 = OUTPUTS_DIR / "entrada.mp3" save_audio(entrada_mp3, original) # entrada_acapella.mp3 (apenas a voz, simulando extração do Demucs) entrada_acapella = OUTPUTS_DIR / "entrada_acapella.mp3" save_audio(entrada_acapella, voice) # entrada_instrumental.mp3 (apenas o instrumental) entrada_instrumental = OUTPUTS_DIR / "entrada_instrumental.mp3" save_audio(entrada_instrumental, instrumental) # saida.mp3 (RVC simulado: pitch shift + leve alteração tímbrica) # Aplica +3 semitons para simular conversão shifted_original = apply_pitch_shift(original, semitones=3, sample_rate=SAMPLE_RATE) saida_mp3 = OUTPUTS_DIR / "saida.mp3" save_audio(saida_mp3, shifted_original) # saida_acapella.mp3 (RVC simulado sobre o acapella) shifted_acapella = apply_pitch_shift(voice, semitones=3, sample_rate=SAMPLE_RATE) saida_acapella = OUTPUTS_DIR / "saida_acapella.mp3" save_audio(saida_acapella, shifted_acapella) # 4. (Opcional) Gerar vídeo de exemplo usando moviepy try: from moviepy.video.VideoClip import ColorClip from moviepy.audio.AudioClip import AudioArrayClip video_path = INPUTS_DIR / "sample_video.mp4" if not video_path.exists(): print("\n🎬 Criando vídeo de exemplo (sample_video.mp4)...") audio_clip = AudioArrayClip(original, fps=SAMPLE_RATE) video_clip = ColorClip(size=(640, 480), color=(30, 80, 120), duration=DURATION_SEC) video_clip = video_clip.with_audio(audio_clip) video_clip.write_videofile(str(video_path), fps=24, logger=None, verbose=False) print(f" ✅ {video_path.name}") except ImportError: print("\n⚠️ moviepy não instalado. Pulando criação de vídeo.") print(" Instale com: pip install moviepy") print("\n" + "=" * 60) print("✅ Geração concluída!") print("Arquivos disponíveis em:") print(f" - Entrada: {INPUTS_DIR}") print(f" - 5 saídas: {OUTPUTS_DIR}") print("\nAgora você pode usar esses arquivos para testar a interface Gradio.") print("=" * 60) if __name__ == "__main__": main()