Intelligent Audio & Biomedical Signal Processing Platform
Digital Signal Processing Course - Task 2: Signal Equalizer
Semester: Spring 2026 | Team: 08
- Project Overview
- Key Features
- System Architecture
- Modes of Operation
- AI-Powered Analysis
- Signal Processing Techniques
- Wavelet Analysis & Comparison
- Installation Guide
- User Interface Guide
- Technical Documentation
- Validation & Testing
- Troubleshooting
EQ Studio is a comprehensive, multi-modal signal processing platform designed for both audio engineering and biomedical signal analysis. The application provides professional-grade tools for signal visualization, equalization, and AI-powered analysis across five distinct domains as required in Task 2 - Signal Equalizer.
The platform seamlessly integrates real-time signal processing, multiple transform domains (FFT, Wavelet), and state-of-the-art AI models to provide intelligent insights and recommendations.
- Multi-band parametric EQ with frequency range specification
- Multiple ranges per band (non-continuous frequency selection)
- Real-time gain adjustment (0 to 2x) with visual feedback
- Automatic wavelet recommendation per mode with reasoning
- Linear and audiogram frequency scales (user-toggleable)
- Fourier FFT - Standard frequency analysis
- Haar Wavelet - Edge detection, ideal for ECG
- Daubechies-4 - Harmonic analysis for music and voice
- Symlet-4 - Near-symmetric, good for transient signals
- Coiflet-3 - Additional wavelet for musical mode (as shown in musical.json)
- Wavelet comparison tool - Side-by-side analysis
- Open-Unmix - Stem separation for music (vocals, drums, bass, other)
- YAMNet - Animal sound classification (521 classes)
- pyannote/SepFormer - Speaker diarization and separation
- Random Forest - ECG arrhythmia classification (MIT-BIH trained)
- pyloudnorm - EBU R128 loudness normalization
- EQ vs AI comparison - SNR and spectral distance metrics
- Linked cine viewers with synchronized playback and cursors
- Real-time spectrum analysis (linear/audiogram scales)
- High-resolution spectrograms with show/hide toggle
- Frequency response curves with gradient fill
- Time-domain waveform with zoom, pan, and speed control
- Save settings as JSON files with complete mode configuration
- Load settings from previously saved configurations
- External editing support - JSON files can be modified outside the application
- Automatic UI reconstruction based on loaded settings
EQ Studio
├── Frontend (Browser-based)
│ ├── HTML5/CSS3 (Glassmorphism UI)
│ ├── JavaScript (Vanilla, no frameworks)
│ ├── Canvas API (Visualization)
│ └── Web Audio API (Playback)
│
├── Backend (Flask Server)
│ ├── RESTful API endpoints
│ ├── Signal processing modules
│ ├── AI model integration
│ └── FFmpeg audio conversion
│
├── Signal Processing Core
│ ├── FFT/IFFT with overlap-add
│ ├── Wavelet transforms (pywt)
│ ├── Filter banks (Butterworth)
│ └── Spectral analysis
│
└── AI Models
├── Open-Unmix (Music separation)
├── YAMNet (Animal classification)
├── pyannote/SepFormer (Voice diarization)
├── Random Forest (ECG classification)
└── Spectral templates (Fallback methods)
The application includes a complete browser-based DSP fallback when the backend is unavailable:
- FFT: Custom O(n log n) implementation
- Haar Wavelet: Fast lifting scheme
- Spectrogram: STFT with canvas rendering
- Audio Decoding: Web Audio API
Purpose: General-purpose audio processing with customizable bands
Implementation Highlights:
- Arbitrary subdivisions - Add unlimited bands dynamically via UI
- Multiple frequency ranges per band - Each band can control non-continuous frequency regions
- Real-time spectral analysis - Centroid, bandwidth, rolloff extracted via Librosa
- Loudness measurement - EBU R128 integrated loudness (LUFS) via pyloudnorm
Default Configuration:
| Band | Frequency Ranges | Application |
|---|---|---|
| Low | 20-300 Hz | Bass, rumble |
| Mid | 300-4000 Hz | Fundamental frequencies |
| High | 4000-20000 Hz | Harmonics, air |
Validation Signal: Sum of pure tones at [100, 440, 880, 1500, 3000, 6000, 10000] Hz
Purpose: Instrument-specific equalization from mixed music signals
Implementation Highlights:
- 6 predefined instrument bands with harmonic modeling
- Multiple frequency ranges per instrument - Each instrument can control multiple spectral regions
- Open-Unmix integration - Neural stem separation (vocals, drums, bass, other)
- Wavelet selection: Coiflet-3 (as specified in musical.json) for optimal harmonic preservation
Instrument Bands (as defined in musical.json):
{
"mode": "musical",
"wavelet": "coif3",
"bands": [
{ "label": "Bass Guitar", "gain": 1, "ranges": [{"lo": 30, "hi": 120}] },
{ "label": "Drums", "gain": 1, "ranges": [{"lo": 120, "hi": 300}] },
{ "label": "Piano", "gain": 1, "ranges": [{"lo": 300, "hi": 800}] },
{ "label": "Violin", "gain": 1, "ranges": [{"lo": 800, "hi": 3000}] },
{ "label": "Vocals", "gain": 1, "ranges": [{"lo": 3000, "hi": 6000}] },
{ "label": "Hi-Hat", "gain": 1, "ranges": [{"lo": 6000, "hi": 15000}] }
]
}Validation Signal: Mixture of all 6 instrument harmonics with controlled amplitudes
Purpose: Bioacoustic analysis and animal call classification
Implementation Highlights:
- 8 animal-specific frequency bands with multiple ranges
- YAMNet integration - Google's pretrained model (521 animal sound classes)
- Spectral template matching fallback when TensorFlow unavailable
- Bandpass separation per animal type using Butterworth filters
Animal Bands (as defined in animals.json):
{
"mode": "animals",
"wavelet": "haar",
"bands": [
{ "label": "Elephant", "gain": 1, "ranges": [{"lo": 10, "hi": 80}] },
{ "label": "Frog Croak", "gain": 1, "ranges": [{"lo": 150, "hi": 350}] },
{ "label": "Lion Roar", "gain": 1, "ranges": [{"lo": 50, "hi": 150}] },
{ "label": "Wolf Howl", "gain": 1, "ranges": [{"lo": 300, "hi": 600}] },
{ "label": "Dog Bark", "gain": 1, "ranges": [{"lo": 500, "hi": 1000}] },
{ "label": "Sheep Baa", "gain": 1, "ranges": [{"lo": 400, "hi": 700}] },
{ "label": "Cat Meow", "gain": 1, "ranges": [{"lo": 800, "hi": 1500}] },
{ "label": "Bird Chirp", "gain": 1, "ranges": [{"lo": 3000, "hi": 7000}] }
]
}Validation Signal: Weighted mixture of all animal templates
Purpose: Speech processing and multi-speaker separation
Implementation Highlights:
- 6 voice-type specific bands covering male/female/young/old/child categories
- Multiple separation methods with graceful fallback chain:
- pyannote/speaker-diarization-3.1 (primary)
- SepFormer (speechbrain)
- F0-guided separation (YIN algorithm)
- NMF source separation (final fallback)
- YIN pitch detection for fundamental frequency analysis
- Speaker diarization with segment timestamps
Voice Bands (as defined in voices.json):
{
"mode": "voices",
"wavelet": "db4",
"bands": [
{ "label": "Male Deep", "gain": 1, "ranges": [{"lo": 80, "hi": 140}] },
{ "label": "Male Mid", "gain": 1, "ranges": [{"lo": 140, "hi": 220}] },
{ "label": "Female Old", "gain": 1, "ranges": [{"lo": 220, "hi": 350}] },
{ "label": "Female Young", "gain": 1, "ranges": [{"lo": 350, "hi": 700}] },
{ "label": "Child Voice", "gain": 1, "ranges": [{"lo": 700, "hi": 2000}] },
{ "label": "Sibilance", "gain": 1, "ranges": [{"lo": 5000, "hi": 8500}] }
]
}Validation Signal: Mixture of all 6 voice types with different languages and pitches
Purpose: Cardiac signal analysis and arrhythmia detection
Implementation Highlights:
- 4 arrhythmia-specific frequency bands (normal + 3 abnormalities)
- Random Forest classifier trained on MIT-BIH database via
train_ecg.py - HRV feature extraction (SDNN, RMSSD, pNN50, LF/HF)
- R-peak detection using biosppy with scipy fallback
- Real-time arrhythmia component isolation via FFT band separation
ECG Bands (as defined in ecg.json):
{
"mode": "ecg",
"wavelet": "sym4",
"bands": [
{ "label": "Normal Rhythm", "gain": 1, "ranges": [{"lo": 0.8, "hi": 1.8}] },
{ "label": "AFib", "gain": 1, "ranges": [{"lo": 4.5, "hi": 8.5}] },
{ "label": "VTach", "gain": 1, "ranges": [{"lo": 2.5, "hi": 6.5}] },
{ "label": "Bradycardia", "gain": 1, "ranges": [{"lo": 0.3, "hi": 0.9}] }
]
}Validation Signal: Generated by ecg.py mixing real MIT-BIH components with controlled gains
| Mode | Primary Model | Implementation | Fallback |
|---|---|---|---|
| Musical | Open-Unmix | openunmix.predict.separate() |
Spectral templates |
| Animals | YAMNet | tensorflow_hub.load('yamnet/1') |
Template matching |
| Voices | pyannote 3.1 | pyannote.audio.Pipeline |
SepFormer/NMF/F0 |
| ECG | Random Forest | sklearn.ensemble |
Rule-based |
| Generic | pyloudnorm | pyloudnorm.normalize.loudness() |
RMS normalization |
# From ai_models.py - Musical mode
def _run_umx(signal, sr):
import torch
from openunmix.predict import separate
# Resample to 44.1kHz
y = _resample(signal, sr, 44100)
audio_tensor = torch.tensor(y).unsqueeze(0).unsqueeze(0)
estimates = separate(audio_tensor, rate=44100,
model_str_or_path='umxhq',
targets=['vocals', 'drums', 'bass', 'other'])
return estimates# From ai_models.py - Animals mode
def _run_yamnet(signal, sr):
import tensorflow as tf
import tensorflow_hub as hub
model = hub.load('https://tfhub.dev/google/yamnet/1')
# Resample to 16kHz
y16 = _resample(signal, sr, 16000)
scores, embeddings, spectrogram = model(y16)
# Map scores to animal classes
animal_scores = {animal: np.max([scores[0][i] for i in indices])
for animal, indices in YAMNET_ANIMAL_MAP.items()}
return animal_scores# From ai_models.py - Voices mode
def _load_pyannote():
from pyannote.audio import Pipeline
pipeline = Pipeline.from_pretrained(
'pyannote/speaker-diarization-3.1',
use_auth_token=os.environ.get('HUGGINGFACE_TOKEN')
)
return pipeline
def _parse_diarization(diarization):
# Extract speaker segments with timestamps
segments = []
for turn, _, speaker in diarization.itertracks(yield_label=True):
segments.append((speaker, turn.start, turn.end))
return segments# From ai_models.py - ECG mode
def _extract_ecg_features(signal, sr):
import biosppy.signals.ecg as bsp
# R-peak detection
rpeaks = bsp.ecg(signal, sampling_rate=sr)['rpeaks']
# RR intervals in ms
rr = np.diff(rpeaks) / sr * 1000
# Extract features
features = [
np.mean(rr), # mean_rr
np.std(rr), # sdnn
np.sqrt(np.mean(np.diff(rr)**2)), # rmssd
len(rpeaks)/(len(signal)/sr)*60, # bpm
np.max(rr)-np.min(rr), # rr_range
np.sum(np.abs(np.diff(rr))>50)/len(rr), # pnn50
np.std(rr)/(np.mean(rr)+1e-9), # cv
# ... spectral features
]
return np.array(features)For each mode, the application provides quantitative comparison:
- SNR (dB): Signal-to-noise ratio between processed and original
- Spectral Distance: Mean absolute difference in normalized spectra
- Winner Declaration: Which method performs better
// Comparison result example from /api/ai/compare
{
eq_snr_db: 12.4,
ai_snr_db: 15.8,
eq_spectral_dist: 0.23,
ai_spectral_dist: 0.18,
winner_snr: "ai",
winner_dist: "ai"
}# From app.py - apply_fft_eq
def apply_fft_eq(signal, sr, bands):
n = len(signal)
N = int(2 ** np.ceil(np.log2(n)))
padded = np.zeros(N, dtype=np.float64)
padded[:n] = signal
spectrum = np.fft.rfft(padded)
freqs = np.fft.rfftfreq(N, d=1.0 / sr)
curve = build_gain_curve(freqs, bands)
spectrum *= curve
result = np.fft.irfft(spectrum)[:n]
return result.astype(np.float32)Properties:
- Discontinuous, piecewise constant
- Perfect reconstruction
- Fast O(n) lifting implementation
- Ideal for ECG QRS complex detection
Properties:
- Continuous, smooth basis functions
- 4 vanishing moments
- Good frequency localization
- Optimal for harmonic signals (music, voice)
Properties:
- Near-symmetric (reduced phase distortion)
- 4 vanishing moments
- Good for transient signals (animal sounds)
- Minimal aliasing
Properties:
- 3 vanishing moments for both scaling and wavelet functions
- Near-linear phase
- Used in musical mode for instrument separation
- Better symmetry than Daubechies
Each band can control multiple non-continuous frequency regions:
// Example band with multiple ranges
{
"label": "Drums",
"gain": 1,
"ranges": [
{"lo": 120, "hi": 300}, // Kick and snare fundamentals
{"lo": 7000, "hi": 9000} // Cymbal wash
]
}- Bandpass filters: 4th-order Butterworth with zero-phase filtering (
scipy.signal.filtfilt) - Multiple ranges: Each band can have any number of [lo, hi] pairs
- Frequency masking: Spectral multiplication in FFT domain
- Time domain: SDNN, RMSSD, pNN50, RR range, CV
- Frequency domain: LF (0.04-0.15 Hz), HF (0.15-0.4 Hz), LF/HF ratio
- Implementation: Biosppy + custom R-peak detection with scipy fallback
Research on audiogram (hearing test graph) led to:
- X-axis: Logarithmic frequency scale matching human hearing perception
- Standard points: [125, 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 6000, 8000] Hz
- Implementation: Nearest-neighbor interpolation to target frequencies
- Use case: Better representation of low-frequency details where human hearing is more sensitive
| Mode | Options | Recommended | Reasoning |
|---|---|---|---|
| Generic | FFT, Haar, Db4, Sym4 | Fourier FFT | Stationary signals, well-understood frequency representation |
| Musical | FFT, Haar, Db4, Sym4, Coif3 | Coiflet-3 | As specified in musical.json; good for harmonic instrument separation |
| Animals | FFT, Haar, Db4, Sym4 | Symlet-4 | Near-symmetric, handles short impulsive bursts (animal calls) |
| Voices | FFT, Haar, Db4, Sym4 | Daubechies-4 | Tracks speech formants and pitch harmonics |
| ECG | FFT, Haar, Db4, Sym4 | Haar | Matches sharp QRS complex edges (piecewise-constant basis) |
The application includes a built-in wavelet comparison feature that:
- Processes the signal with all available transforms (FFT, Haar, Db4, Sym4)
- Calculates energy difference (Δ dB) for each
- Highlights the best performer for the current signal
- Allows one-click application of any transform
Comparison Metrics:
- Δ (dB): Energy difference from original (lower = better preservation)
- Values displayed in real-time for each transform
- Active transform highlighted
def apply_fft_eq(signal, sr, bands):
spectrum = np.fft.rfft(signal)
freqs = np.fft.rfftfreq(len(signal), 1/sr)
curve = build_gain_curve(freqs, bands)
spectrum *= curve
return np.fft.irfft(spectrum)def apply_wavelet_eq(signal, sr, bands, wavelet_name):
# Handle 'fourier' case separately
if wavelet_name == 'fourier':
return apply_fft_eq(signal, sr, bands)
# Wavelet decomposition
max_level = pywt.dwt_max_level(len(signal), wavelet_name)
level = min(8, max_level)
coeffs = pywt.wavedec(signal, wavelet_name, level=level)
# Apply gains based on approximate center frequencies
for i, c in enumerate(coeffs):
freq_center = sr / (2 ** (level - i + 1)) if i > 0 else sr / (2 ** level)
gain = get_gain_for_freq(freq_center, bands)
coeffs[i] = c * gain
# Reconstruction
result = pywt.waverec(coeffs, wavelet_name)
return result[:len(signal)].astype(np.float32)def reconstruct_ecg_from_gains(signal, sr, bands):
# Isolate each arrhythmia band via FFT masking
components = decompose_ecg_components(signal, sr)
# Scale by gains and recombine
output = np.zeros_like(signal)
for label, gain in label_to_gain.items():
if label in components:
output += gain * components[label]
return output- Python 3.8+ (backend)
- Modern browser (Chrome/Firefox/Edge recommended)
- FFmpeg (included in
/binfolder) - 4GB+ RAM (for AI models)
- Internet connection (for first-time model downloads)
git clone https://github.com/team8-dsp/eq-studio.git
cd eq-studiopip install -r requirements.txtrequirements.txt includes:
biosppy==2.2.4 # ECG processing
Flask==3.1.3 # Web server
Flask_Cors==4.0.1 # CORS support
joblib==1.5.3 # Model serialization
librosa==0.11.0 # Audio analysis
numpy==2.4.3 # Numerical computing
openunmix==1.3.0 # Music separation
pandas==3.0.1 # Data handling
pyloudnorm==0.2.0 # Loudness normalization
scikit_learn==1.8.0 # ML models
scipy==1.17.1 # Signal processing
speechbrain==1.0.3 # Voice separation
tensorflow==2.20.0 # Deep learning
tensorflow_hub==0.16.1 # Model hub
torch==2.10.0 # PyTorch
wfdb==4.3.1 # ECG database
cd backend
python train_ecg.pyThis downloads MIT-BIH database (~100 MB) and trains the Random Forest classifier.
Note: If wfdb is unavailable, synthetic ECG data will be generated automatically.
Get your token from huggingface.co/settings/tokens
# Windows (Command Prompt)
set HF_TOKEN=hf_your_token_here
# Windows (PowerShell)
$env:HF_TOKEN="hf_your_token_here"
# Linux/Mac
export HF_TOKEN=hf_your_token_herecd backend
python ecg.pyThis creates ecg_mixed.csv - a mixture of normal and arrhythmia signals from MIT-BIH.
python backend/app.pyAccess the application at: http://localhost:5000
| Control | Function | Implementation |
|---|---|---|
| Mode Selector | Switch between 5 processing modes | Dynamic slider regeneration from mode configs |
| Wavelet Selector | Choose transform (Fourier/Haar/Db4/Sym4) | Real-time reprocessing on change |
| Wavelet Reason | Shows recommended wavelet with explanation | Mode-specific lookup table |
| Compare Wavelets | Compare all transforms side-by-side | Parallel processing with SNR calculation |
| Scale Toggle | Switch between Linear and Audiogram scales | Frequency axis remapping with standard audiogram points |
- SR: Sample rate (Hz) - from loaded file
- DUR: Signal duration (seconds) - calculated from length/sampleRate
- N: Number of samples - formatted with commas
- Mode: Current processing mode - from selection
- Wav: Active wavelet - from selector (shows COIF3 when applicable)
Supported Formats:
- Audio: .wav, .mp3, .m4a, .ogg, .flac (via FFmpeg)
- Data: .csv, .txt (ECG numeric data, multi-column averaging for ECG mode)
Controls:
- Drag & drop upload with visual feedback
- Synthetic signal generation per mode (multi-frequency test signals)
- Save settings as JSON (complete mode/bands configuration)
- Load settings from JSON (auto-reconstruct UI with all bands)
Features:
- Vertical sliders for gain adjustment (0-2x)
- Real-time frequency response update (no button needed - debounced 100ms)
- Band labels with frequency ranges
- Add/remove bands in generic mode (unlimited)
- Reset all gains to 1.0
- Multiple ranges per band in generic mode editor
Playback Controls:
- Play/Pause - Linked cursors in both viewers
- Stop - Reset to beginning
- Speed control - 0.25x to 4x
- Zoom in/out - 1x to 32x
- Pan slider - Navigate through signal
- Reset view - Full signal, zoom=1, pan=0
- Audio playback - Input or output signal via Web Audio API
Linked Behavior:
- Both viewers show exact same time window
- Zoom/pan affects both simultaneously
- Play cursor moves synchronously
- Dark overlay to right of cursor indicates "future" samples
- Linear scale: Even frequency spacing
- Audiogram scale: Logarithmic with standard audiogram points
- Normalized magnitude (0-1) with dB conversion
- Frequency tick labels (Hz/kHz) with smart formatting
- Gradient fill under curve matching theme color
- Grid lines at 20% intervals
- Time-frequency representation via STFT
- Color intensity = normalized magnitude
- Collapsible panel (Show/Hide toggle)
- Shared time axis with cine viewers
- Real-time updates on EQ change
Features:
- Mode-specific AI model display with name
- Real-time status (idle/running/done/error)
- Interactive stem controls with gain sliders (0-2x)
- Stem playback buttons for individual listening
- SNR comparison with EQ output
- Confidence scores and probabilities for classification
- Segments timeline for voice diarization
| Endpoint | Method | Description | Request Body |
|---|---|---|---|
/api/equalize |
POST | Apply EQ with selected wavelet | {signal, sampleRate, bands, wavelet, mode} |
/api/spectrum |
POST | Compute frequency spectrum | {signal, sampleRate, audiogram} |
/api/spectrogram |
POST | Generate spectrogram | {signal, sampleRate} |
/api/synthetic |
POST | Generate synthetic signal | {sampleRate, duration, freqs?} |
/api/synthetic_mode |
POST | Mode-specific synthetic | {mode, sampleRate, duration} |
/api/upload |
POST | Upload and decode file | multipart/form-data |
/api/ai/{mode} |
POST | Run AI analysis | {signal, sampleRate} |
/api/ai/compare |
POST | Compare EQ vs AI | {original, eq_output, ai_output, sampleRate} |
User Action (Slider Move)
↓
debounce(100ms) - scheduleEQ()
↓
/api/equalize (POST)
↓
[Backend Processing] - app.py
├── mode == 'ecg'? → reconstruct_ecg_from_gains()
├── wavelet == 'fourier'? → apply_fft_eq()
└── Else → apply_wavelet_eq() with named wavelet
↓
Output Signal (Float32Array)
↓
renderOutputOnly() - render.js
├── drawWaveform('outputCanvas')
├── drawSpectrum('outputSpectrum')
└── drawSpectrogram('outputSpectrogram')
// Frontend encoding (api.js)
function encodeSignal(f32) {
const bytes = new Uint8Array(f32.buffer);
let bin = '';
for (let i = 0; i < bytes.byteLength; i++)
bin += String.fromCharCode(bytes[i]);
return btoa(bin);
}
// Backend decoding (app.py)
def decode_signal(data):
raw = base64.b64decode(data['signal'])
signal = np.frombuffer(raw, dtype=np.float32).copy()
return signal, int(data.get('sampleRate', 44100))The application uses a single global state object S defined in state.js:
const S = {
mode: 'generic', // Current mode
inputSignal: null, // Input waveform
outputSignal: null, // Processed waveform
originalSignal: null, // Copy for AI processing
sampleRate: 44100, // Sample rate in Hz
wavelet: 'fourier', // Active transform
audiogram: false, // Scale toggle
genericBands: [], // Generic mode bands
zoom: 1, // View zoom level
panOffset: 0, // View pan (0-1)
playPos: 0, // Playhead position (0-1)
isPlaying: false, // Playback state
speed: 1, // Playback speed
spectrogramOn: true, // Spectrogram visibility
// ... audio context and animation handles
};{
"mode": "ecg",
"wavelet": "sym4",
"bands": [
{
"label": "Normal Rhythm",
"gain": 1,
"ranges": [{"lo": 0.8, "hi": 1.8}]
},
{
"label": "AFib",
"gain": 1,
"ranges": [{"lo": 4.5, "hi": 8.5}]
}
]
}Each mode includes a purpose-built synthetic signal to verify correct equalizer behavior:
Generic Mode Signal:
# From app.py - /api/synthetic
freqs = [100, 440, 880, 1500, 3000, 6000, 10000] # Hz
signal = sum(np.sin(2 * np.pi * f * t) for f in freqs) / len(freqs)Validation: Each band's gain change should affect only its specified frequency ranges.
Musical Mode Signal:
- Each instrument has multiple harmonics (2-5 harmonics per instrument)
- Bands are designed to be non-overlapping where possible
- Validation: Boosting "Bass Guitar" (30-120 Hz) should not affect "Hi-Hat" (6000-15000 Hz)
ECG Mode Signal:
- Generated from
ecg.pymixing real MIT-BIH components - Normal sinus always present with gain=1
- Arrhythmias added with controlled gains (0.5 each in test mix)
- Validation: Setting AFib gain=0 should remove fibrillatory waves from output
| Edge Case | Handling Mechanism |
|---|---|
| Empty signal | Show placeholder, disable controls |
| Single sample | Prevent division by zero in normalization |
| DC offset | Handle in FFT, ignore in wavelet (mean removal) |
| NaN/Inf values | Replace with 0 via np.nan_to_num() |
| Backend offline | Transparent fallback to browser DSP (api.js) |
| Missing AI models | Graceful fallback to spectral methods |
| Very long signals | Truncate for spectrogram (10s max in browser) |
| Sample rate mismatch | Resample automatically via librosa |
| Multi-channel audio | Convert to mono (average channels) |
| CSV parsing errors | Skip invalid lines, continue with valid data |
-
No Code Repetition:
- Shared rendering functions (
drawWaveform,_paintSpectrum) inrender.js - Mode-agnostic band handling via
activeBands()instate.js - Common AI response renderers (
_renderStems) inai_panel.js
- Shared rendering functions (
-
Real-time Updates:
- No "Apply" buttons needed - changes take effect immediately
- Debounced EQ processing (100ms) to avoid overwhelming backend
- Immediate slider value display in DOM
-
Error Handling:
- Try/catch in all async operations
- User-friendly error messages in UI
- Loading overlay for long operations
- Graceful degradation when models unavailable
-
Clean Code:
- No unused variables or functions
- Consistent naming conventions across files
- Comprehensive comments for complex algorithms
Error: FFmpeg Error: [WinError 2] The system cannot find the file specified
Solution:
- Ensure
/bin/ffmpeg.exeexists in project directory - Or add FFmpeg to system PATH
- Or install FFmpeg globally via package manager
Error: HF_TOKEN environment variable is not set
Solution:
# Get token from huggingface.co/settings/tokens
export HF_TOKEN=hf_your_token_hereError: ecg_model.pkl not found
Solution:
cd backend
python train_ecg.pyOr the system will fall back to rule-based classification.
Error: Audio decode failed: EncodingError
Solution:
- Convert audio to WAV format using external tool
- Ensure FFmpeg is properly installed
- Check file permissions and path
Error: Access to fetch at 'http://localhost:5000/...' from origin 'null'
Solution:
- Access via
http://localhost:5000(not file:// protocol) - Flask-CORS is enabled by default in app.py
Error: Connection timeout downloading pretrained models
Solution:
- Check internet connection
- Models will retry on next application run
- Use cached versions in
pretrained_models/folder if available
Issue: Spectrogram canvas remains blank after loading signal
Solution:
- Toggle spectrogram off/on using the button
- Check browser console for JavaScript errors
- Reduce signal length (< 60 seconds recommended)
For questions or support, please contact Team 08
EQ Studio © 2026 Team 8 DSP. All rights reserved.
Built for Digital Signal Processing Course - Task 2: Signal Equalizer
🔗 GitHub Repository



















