136 lines
3.5 KiB
Python
136 lines
3.5 KiB
Python
"""
|
|
Create synthetic test data for LoRA training.
|
|
|
|
This script generates a simple audio file with corresponding lyrics and tags
|
|
for testing the training pipeline.
|
|
"""
|
|
|
|
import torch
|
|
import numpy as np
|
|
from pathlib import Path
|
|
import wave
|
|
import struct
|
|
|
|
DATA_DIR = Path(__file__).parent / "data"
|
|
|
|
|
|
def create_test_audio():
|
|
"""Create a simple test audio file - a chord progression with harmonics."""
|
|
|
|
sample_rate = 48000
|
|
duration = 10 # 10 seconds
|
|
|
|
t = torch.linspace(0, duration, int(sample_rate * duration))
|
|
|
|
# Create a simple melody with multiple frequencies
|
|
frequencies = [261.63, 329.63, 392.00] # C4, E4, G4 (C major chord)
|
|
|
|
audio = torch.zeros_like(t)
|
|
for i, freq in enumerate(frequencies):
|
|
# Add fundamental and harmonics
|
|
audio += 0.3 * torch.sin(2 * np.pi * freq * t)
|
|
audio += 0.15 * torch.sin(2 * np.pi * freq * 2 * t) # 1st harmonic
|
|
audio += 0.08 * torch.sin(2 * np.pi * freq * 3 * t) # 2nd harmonic
|
|
|
|
# Add some variation over time (simple envelope)
|
|
envelope = torch.ones_like(t)
|
|
envelope[:int(0.1 * sample_rate)] = torch.linspace(0, 1, int(0.1 * sample_rate))
|
|
envelope[-int(0.5 * sample_rate):] = torch.linspace(1, 0, int(0.5 * sample_rate))
|
|
audio = audio * envelope
|
|
|
|
# Normalize
|
|
audio = audio / audio.abs().max() * 0.8
|
|
|
|
# Add slight noise for texture
|
|
audio += torch.randn_like(audio) * 0.01
|
|
|
|
# Ensure shape is [channels, samples]
|
|
audio = audio.unsqueeze(0)
|
|
|
|
return audio, sample_rate
|
|
|
|
|
|
def create_test_lyrics():
|
|
"""Create simple test lyrics with section markers."""
|
|
|
|
lyrics = """[Verse]
|
|
la la la
|
|
singing a song
|
|
la la la
|
|
all day long
|
|
|
|
[Chorus]
|
|
oh oh oh
|
|
here we go
|
|
oh oh oh
|
|
let it flow"""
|
|
|
|
return lyrics
|
|
|
|
|
|
def create_test_tags():
|
|
"""Create test style tags."""
|
|
|
|
tags = "pop, female vocal, energetic, happy, medium tempo"
|
|
|
|
return tags
|
|
|
|
|
|
def save_wav(filepath, audio_tensor, sample_rate):
|
|
"""Save audio tensor as WAV file using standard library."""
|
|
# Convert to numpy and ensure correct shape
|
|
if isinstance(audio_tensor, torch.Tensor):
|
|
audio = audio_tensor.numpy()
|
|
else:
|
|
audio = audio_tensor
|
|
|
|
# Ensure 1D or get first channel
|
|
if audio.ndim == 2:
|
|
audio = audio[0]
|
|
|
|
# Normalize to int16 range
|
|
audio = np.clip(audio, -1.0, 1.0)
|
|
audio_int16 = (audio * 32767).astype(np.int16)
|
|
|
|
# Write WAV file
|
|
with wave.open(str(filepath), 'w') as wav_file:
|
|
wav_file.setnchannels(1)
|
|
wav_file.setsampwidth(2) # 2 bytes = 16 bits
|
|
wav_file.setframerate(sample_rate)
|
|
wav_file.writeframes(audio_int16.tobytes())
|
|
|
|
|
|
def main():
|
|
"""Generate all test data."""
|
|
|
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
print("Creating synthetic test data...")
|
|
|
|
# Create audio
|
|
audio, sr = create_test_audio()
|
|
audio_path = DATA_DIR / "test_sample.wav"
|
|
save_wav(audio_path, audio, sr)
|
|
print(f"Created: {audio_path}")
|
|
print(f" Duration: {audio.shape[1] / sr:.2f} seconds")
|
|
print(f" Sample rate: {sr} Hz")
|
|
|
|
# Create lyrics
|
|
lyrics = create_test_lyrics()
|
|
lyrics_path = DATA_DIR / "test_sample.txt"
|
|
lyrics_path.write_text(lyrics, encoding='utf-8')
|
|
print(f"Created: {lyrics_path}")
|
|
|
|
# Create tags
|
|
tags = create_test_tags()
|
|
tags_path = DATA_DIR / "test_sample.tags"
|
|
tags_path.write_text(tags, encoding='utf-8')
|
|
print(f"Created: {tags_path}")
|
|
|
|
print("\nTest data created successfully!")
|
|
print(f"Directory: {DATA_DIR}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|