| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- """
- Signal Generator — Generate test signals for audio calibration.
- Supports:
- - Logarithmic sine sweep (chirp)
- - Pink noise (1/f)
- - White noise
- - Sine tone
- """
- import numpy as np
- from scipy.signal import chirp
- def generate_log_sweep(
- start_freq: float = 20.0,
- end_freq: float = 20000.0,
- duration: float = 10.0,
- sample_rate: int = 44100,
- amplitude: float = 0.8
- ) -> np.ndarray:
- """
- Generate a logarithmic sine sweep (chirp).
-
- This is the preferred method for measuring frequency response
- as it provides a high signal-to-noise ratio across the spectrum.
- """
- t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
- # Generate the sweep
- sweep = chirp(t, f0=start_freq, f1=end_freq, t1=duration, method='logarithmic')
-
- # Apply a short fade-in and fade-out to prevent clicks
- fade_len = int(0.05 * sample_rate) # 50ms fade
- window = np.ones_like(sweep)
- if len(window) > 2 * fade_len:
- fade_in = np.linspace(0, 1, fade_len)
- fade_out = np.linspace(1, 0, fade_len)
- window[:fade_len] = fade_in
- window[-fade_len:] = fade_out
-
- return (sweep * window * amplitude).astype(np.float32)
- def generate_white_noise(
- duration: float = 10.0,
- sample_rate: int = 44100,
- amplitude: float = 0.8
- ) -> np.ndarray:
- """Generate white noise (equal energy per Hz)."""
- num_samples = int(sample_rate * duration)
- # np.random.randn generates Gaussian white noise (mean 0, variance 1)
- noise = np.random.randn(num_samples)
- # Normalize and scale
- noise = (noise / np.max(np.abs(noise))) * amplitude
- return noise.astype(np.float32)
- def generate_pink_noise(
- duration: float = 10.0,
- sample_rate: int = 44100,
- amplitude: float = 0.8
- ) -> np.ndarray:
- """
- Generate pink noise (equal energy per octave, 1/f spectrum).
-
- Uses a standard IIR filter approximation (Voss-McCartney algorithm or similar filter bank)
- applied to white noise.
- """
- import scipy.signal as signal
-
- white = np.random.randn(int(sample_rate * duration))
-
- # Filter coefficients for a rough pinking filter
- # (Paul Kellet's refined filter)
- b = [0.049922035, -0.095993537, 0.050612699, -0.004408786]
- a = [1.000000000, -2.494956002, 2.017265875, -0.522189400]
-
- pink = signal.lfilter(b, a, white)
-
- # Normalize and scale
- pink = (pink / np.max(np.abs(pink))) * amplitude
- return pink.astype(np.float32)
- def generate_sine_tone(
- frequency: float = 1000.0,
- duration: float = 5.0,
- sample_rate: int = 44100,
- amplitude: float = 0.8
- ) -> np.ndarray:
- """Generate a pure sine wave tone."""
- t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
- tone = np.sin(2 * np.pi * frequency * t)
-
- # Apply fade in/out
- fade_len = int(0.05 * sample_rate)
- window = np.ones_like(tone)
- if len(window) > 2 * fade_len:
- window[:fade_len] = np.linspace(0, 1, fade_len)
- window[-fade_len:] = np.linspace(1, 0, fade_len)
-
- return (tone * window * amplitude).astype(np.float32)
|