Batch Processing
When processing multiple signals with the same parameters, the planner API provides significant performance benefits by reusing FFT plans.
Why Use Plans?
Creating an FFT plan involves:
Allocating buffers
Planning the FFT algorithm
Optimizing for your CPU
This setup cost is amortized over multiple signals when using plans.
Basic Usage
import spectrograms as sg
import numpy as np
# Generate test signals
signals = [np.random.randn(16000) for _ in range(100)]
# Set up parameters
stft = sg.StftParams(n_fft=512, hop_size=256, window=sg.WindowType.hanning)
params = sg.SpectrogramParams(stft, sample_rate=16000)
mel_params = sg.MelParams(n_mels=80, f_min=0.0, f_max=8000.0)
db_params = sg.LogParams(floor_db=-80.0)
# Create planner and plan
planner = sg.SpectrogramPlanner()
plan = planner.mel_db_plan(params, mel_params, db_params)
# Process all signals
results = [plan.compute(signal) for signal in signals]
Creating Plans
The SpectrogramPlanner creates reusable plans:
planner = sg.SpectrogramPlanner()
# Linear spectrograms
power_plan = planner.linear_power_plan(params)
mag_plan = planner.linear_magnitude_plan(params)
db_plan = planner.linear_db_plan(params, db_params)
# Mel spectrograms
mel_power = planner.mel_power_plan(params, mel_params)
mel_mag = planner.mel_magnitude_plan(params, mel_params)
mel_db = planner.mel_db_plan(params, mel_params, db_params)
# ERB spectrograms
erb_power = planner.erb_power_plan(params, erb_params)
erb_mag = planner.erb_magnitude_plan(params, erb_params)
erb_db = planner.erb_db_plan(params, erb_params, db_params)
Computing Spectrograms
Full Spectrogram
spec = plan.compute(samples)
Single Frame
For streaming or real-time processing:
# Compute only the 10th frame
frame = plan.compute_frame(samples, frame_idx=10)
Output Shape Prediction
Determine output dimensions before computation:
signal_length = 16000
n_bins, n_frames = plan.output_shape(signal_length)
Performance Comparison
Without plan reuse:
import time
start = time.time()
for signal in signals:
spec = sg.compute_mel_db_spectrogram(signal, params, mel_params, db_params)
elapsed_no_reuse = time.time() - start
With plan reuse:
planner = sg.SpectrogramPlanner()
plan = planner.mel_db_plan(params, mel_params, db_params)
start = time.time()
for signal in signals:
spec = plan.compute(signal)
elapsed_with_reuse = time.time() - start
speedup = elapsed_no_reuse / elapsed_with_reuse
print(f"Speedup: {speedup:.2f}x")
When to Use Plans
Use plans when:
Processing multiple signals with identical parameters
Building batch processing pipelines
Implementing real-time systems
Performance is critical
Use convenience functions when:
Processing a single signal
Prototyping or exploration
Parameters change frequently
Simplicity is preferred
Memory Considerations
Plans hold internal state and buffers. For many different parameter configurations:
# Create separate plans for different configurations
plans = {}
for n_fft in [512, 1024, 2048]:
stft = sg.StftParams(n_fft=n_fft, hop_size=n_fft//4, window=sg.WindowType.hanning)
params = sg.SpectrogramParams(stft, sample_rate=16000)
planner = sg.SpectrogramPlanner()
plans[n_fft] = planner.mel_db_plan(params, mel_params, db_params)
# Use appropriate plan
spec = plans[512].compute(signal)