Skip to content

shahd891/signal-equalizer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Home Page

🔬 EQ Studio

Intelligent Audio & Biomedical Signal Processing Platform

Digital Signal Processing Course - Task 2: Signal Equalizer
Semester: Spring 2026 | Team: 08


📋 Table of Contents


🔍 Project Overview

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.


✨ Key Features

🎛️ Advanced Equalization

  • 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)

EQ Sliders

🔄 Multiple Transform Domains

  • 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

Wavelet Selection

🤖 AI-Powered 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

AI Analysis Panel

📊 Professional Visualization

  • 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

Spectrum and Spectrogram

💾 Configuration Management

  • 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

Upload Zone


🏗️ System Architecture

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)

Browser Fallback System

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

🎮 Modes of Operation

1. Generic Mode

Purpose: General-purpose audio processing with customizable bands

Generic Mode

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

Generic Sliders


2. Musical Instruments Mode

Purpose: Instrument-specific equalization from mixed music signals

Musical Mode

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

Musical Sliders


3. Animal Sounds Mode

Purpose: Bioacoustic analysis and animal call classification

Animal Mode

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

Animal Sliders


4. Human Voices Mode

Purpose: Speech processing and multi-speaker separation

Voices Mode

Implementation Highlights:

  • 6 voice-type specific bands covering male/female/young/old/child categories
  • Multiple separation methods with graceful fallback chain:
    1. pyannote/speaker-diarization-3.1 (primary)
    2. SepFormer (speechbrain)
    3. F0-guided separation (YIN algorithm)
    4. 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

Voices Sliders


5. ECG Abnormalities Mode

Purpose: Cardiac signal analysis and arrhythmia detection

ECG Mode

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

ECG Signal


🤖 AI-Powered Analysis

Model Integration Details

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

Music Separation (Open-Unmix)

# 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

Animal Classification (YAMNet)

# 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

Speaker Diarization (pyannote)

# 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

ECG Classification (Random Forest)

# 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)

EQ vs AI Comparison

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"
}

🔬 Signal Processing Techniques

Fourier Transform (FFT)

# 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)

Wavelet Transforms

Haar Wavelet

Properties: 
- Discontinuous, piecewise constant
- Perfect reconstruction
- Fast O(n) lifting implementation
- Ideal for ECG QRS complex detection

Daubechies-4

Properties:
- Continuous, smooth basis functions
- 4 vanishing moments
- Good frequency localization
- Optimal for harmonic signals (music, voice)

Symlet-4

Properties:
- Near-symmetric (reduced phase distortion)
- 4 vanishing moments
- Good for transient signals (animal sounds)
- Minimal aliasing

Coiflet-3 (musical.json)

Properties:
- 3 vanishing moments for both scaling and wavelet functions
- Near-linear phase
- Used in musical mode for instrument separation
- Better symmetry than Daubechies

Multiple Frequency Ranges per Band

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
  ]
}

Filter Banks

  • 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

HRV Analysis (ECG)

  • 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

Audiogram Scale Implementation

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

Audiogram Scale


🌊 Wavelet & Fourier Transformer Analysis & Comparison

Wavelet & Fourier Transformer Suitability by Mode

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)

Comparison Tool

The application includes a built-in wavelet comparison feature that:

  1. Processes the signal with all available transforms (FFT, Haar, Db4, Sym4)
  2. Calculates energy difference (Δ dB) for each
  3. Highlights the best performer for the current signal
  4. Allows one-click application of any transform

Wavelet Comparison

Comparison Metrics:

  • Δ (dB): Energy difference from original (lower = better preservation)
  • Values displayed in real-time for each transform
  • Active transform highlighted

Transform-Specific EQ Implementation

FFT Domain Equalization

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)

Wavelet Domain Equalization

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)

ECG-Specific Reconstruction

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

📦 Installation Guide

Prerequisites

  • Python 3.8+ (backend)
  • Modern browser (Chrome/Firefox/Edge recommended)
  • FFmpeg (included in /bin folder)
  • 4GB+ RAM (for AI models)
  • Internet connection (for first-time model downloads)

Step 1: Clone Repository

git clone https://github.com/team8-dsp/eq-studio.git
cd eq-studio

Step 2: Install Python Dependencies

pip install -r requirements.txt

requirements.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

Step 3: Train ECG Model (Optional)

cd backend
python train_ecg.py

This downloads MIT-BIH database (~100 MB) and trains the Random Forest classifier.
Note: If wfdb is unavailable, synthetic ECG data will be generated automatically.

Step 4: Set Hugging Face Token (For pyannote)

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_here

Step 5: Generate ECG Mixed Signal (Optional)

cd backend
python ecg.py

This creates ecg_mixed.csv - a mixture of normal and arrhythmia signals from MIT-BIH.

Step 6: Run the Application

python backend/app.py

Access the application at: http://localhost:5000


🖥️ User Interface Guide

Header Controls

Header

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

Status Pills (Real-time Updates)

  • 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)

Signal Input Panel

Upload Zone

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)

Equalizer Panel

EQ Sliders

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

Linked Cine Viewers

Signal Viewers

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

Spectrum Analysis

Spectrum

  • 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

Spectrogram

Spectrogram

  • 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

AI Analysis Panel

AI Panel

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

📚 Technical Documentation

API Endpoints

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}

Signal Flow

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')

Data Encoding/Decoding

// 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))

State Management

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
};

Configuration File Format

{
  "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}]
    }
  ]
}

✅ Validation & Testing

Synthetic Signal Validation

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.py mixing 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 Cases Handled

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

Code Quality Practices

  1. No Code Repetition:

    • Shared rendering functions (drawWaveform, _paintSpectrum) in render.js
    • Mode-agnostic band handling via activeBands() in state.js
    • Common AI response renderers (_renderStems) in ai_panel.js
  2. 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
  3. Error Handling:

    • Try/catch in all async operations
    • User-friendly error messages in UI
    • Loading overlay for long operations
    • Graceful degradation when models unavailable
  4. Clean Code:

    • No unused variables or functions
    • Consistent naming conventions across files
    • Comprehensive comments for complex algorithms

❓ Troubleshooting

Common Issues and Solutions

1. FFmpeg Not Found

Error: FFmpeg Error: [WinError 2] The system cannot find the file specified

Solution:

  • Ensure /bin/ffmpeg.exe exists in project directory
  • Or add FFmpeg to system PATH
  • Or install FFmpeg globally via package manager

2. Hugging Face Token Required

Error: HF_TOKEN environment variable is not set

Solution:

# Get token from huggingface.co/settings/tokens
export HF_TOKEN=hf_your_token_here

3. ECG Model Not Found

Error: ecg_model.pkl not found

Solution:

cd backend
python train_ecg.py

Or the system will fall back to rule-based classification.

4. Audio Decoding Failed

Error: Audio decode failed: EncodingError

Solution:

  • Convert audio to WAV format using external tool
  • Ensure FFmpeg is properly installed
  • Check file permissions and path

5. CORS Errors

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

6. Model Download Failures

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

7. Spectrogram Not Showing

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)

🌟 Thank you for reviewing our project! 🌟

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

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors