| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- """
- Generate synthetic test audio for CI testing.
- Creates deterministic test signals without requiring any external audio files,
- ensuring reproducibility and avoiding copyright concerns.
- Usage:
- python generate_test_audio.py --output test_audio.wav
- """
- import argparse
- import numpy as np
- try:
- import soundfile as sf
- except ImportError:
- sf = None
- def generate_test_audio(
- output_path: str,
- duration: float = 5.0,
- sample_rate: int = 44100,
- ) -> None:
- """
- Generate deterministic test audio (sine wave synthesis).
- Creates a mixture of "vocal-like" and "accompaniment-like" sine waves
- that covers a reasonable frequency range for testing audio separation.
- Args:
- output_path: Path to save the output WAV file
- duration: Duration in seconds
- sample_rate: Sample rate in Hz
- """
- if sf is None:
- raise ImportError(
- "soundfile is required for audio generation. "
- "Install with: pip install soundfile"
- )
- t = np.linspace(0, duration, int(sample_rate * duration), dtype=np.float32)
- # Simulate vocals: multiple sine waves (fundamental + harmonics)
- # Using A3 (220 Hz) as base frequency
- vocals = (
- 0.50 * np.sin(2 * np.pi * 220 * t) # A3 fundamental
- + 0.30 * np.sin(2 * np.pi * 440 * t) # A4 harmonic
- + 0.15 * np.sin(2 * np.pi * 880 * t) # A5 harmonic
- + 0.05 * np.sin(2 * np.pi * 1760 * t) # A6 harmonic
- )
- # Add slight vibrato to vocals (more realistic)
- vibrato = 0.02 * np.sin(2 * np.pi * 5 * t) # 5 Hz vibrato
- vocals = vocals * (1 + vibrato)
- # Simulate accompaniment: different frequency content
- accompaniment = (
- 0.40 * np.sin(2 * np.pi * 110 * t) # A2 bass
- + 0.30 * np.sin(2 * np.pi * 330 * t) # E4
- + 0.20 * np.sin(2 * np.pi * 660 * t) # E5
- + 0.10 * np.sin(2 * np.pi * 82.41 * t) # E2 sub-bass
- )
- # Add Gaussian noise to prevent zero-signal bands (crucial for RMSNorm stability)
- noise = np.random.normal(0, 0.001, t.shape).astype(np.float32)
- # Add slight amplitude envelope to make it more interesting
- envelope = np.ones_like(t)
- fade_samples = int(0.1 * sample_rate) # 100ms fade
- envelope[:fade_samples] = np.linspace(0, 1, fade_samples)
- envelope[-fade_samples:] = np.linspace(1, 0, fade_samples)
- # Mix vocals, accompaniment, and noise
- mix = (vocals + accompaniment) * envelope + noise
- # Normalize to prevent clipping
- max_val = np.max(np.abs(mix))
- if max_val > 0:
- mix = mix / max_val * 0.9 # Leave some headroom
- # Create stereo (identical channels for simplicity)
- stereo = np.stack([mix, mix], axis=-1)
- # Save as WAV
- sf.write(output_path, stereo, sample_rate, subtype="PCM_16")
- print(f"Generated: {output_path}")
- print(f" Duration: {duration}s")
- print(f" Sample rate: {sample_rate} Hz")
- print(" Channels: 2 (stereo)")
- print(" Format: PCM_16")
- def main():
- parser = argparse.ArgumentParser(
- description="Generate synthetic test audio for CI testing",
- formatter_class=argparse.RawDescriptionHelpFormatter,
- epilog="""
- This script generates deterministic test audio using sine wave synthesis.
- The output is suitable for testing audio processing pipelines without
- requiring real audio files.
- Example:
- python generate_test_audio.py --output test.wav
- python generate_test_audio.py --output test.wav --duration 10 --sample-rate 48000
- """,
- )
- parser.add_argument("--output", "-o", required=True, help="Output WAV file path")
- parser.add_argument(
- "--duration",
- "-d",
- type=float,
- default=5.0,
- help="Duration in seconds (default: 5.0)",
- )
- parser.add_argument(
- "--sample-rate",
- "-sr",
- type=int,
- default=44100,
- help="Sample rate in Hz (default: 44100)",
- )
- args = parser.parse_args()
- generate_test_audio(
- output_path=args.output,
- duration=args.duration,
- sample_rate=args.sample_rate,
- )
- if __name__ == "__main__":
- main()
|