Lightweight React/Vite app that captures or ingests stethoscope audio, denoises it, visualizes the signal, and extracts clinical-style metrics (S1/S2 durations, systolic/diastolic intervals, heart rate, respiration proxy). Data can be exported for further analysis.
- Install deps:
npm install - Run dev server:
npm run dev - Production build:
npm run build
Use a Chromium-based browser for best WebAudio support.
Core workflow:
- Capture: microphone (Tone.js) or upload WAV/MP3; audio decoded to Float32 PCM.
- Preprocess (see
src/utils/signalProcessing.js):- Bandpass: 2nd-order Butterworth biquad, 20–180 Hz.
- Despike: median absolute deviation (MAD) clamp to remove clicks.
- Smooth: moving average (default 5 ms) to tame residual noise.
- Normalize: z-score before envelope detection.
- Envelope: rectification + smoothing.
- Peak picking: MAD-adaptive threshold with ~0.28 s refractory to isolate heart sounds.
- Respiration proxy: downsampled envelope, autocorrelation to dominant 0.1–0.5 Hz component.
- Metrics: heart rate (BPM), average/median S1/S2 durations, systolic/diastolic intervals (median), respiration BPM if detectable.
- Outputs: time-domain stages for visualization (
Visualizer), metrics inMetricsPanel, exportable JSON report from the UI.
- Preprocessing
- Bandpass 20–180 Hz (2nd-order Butterworth biquad) → clamp outliers with MAD → ~10 ms moving-average smooth → z-score normalize.
- Envelope: full-wave rectification → ~20 ms moving RMS → 8 Hz low-pass smooth.
- Beat (S1) detection
- Adaptive threshold = median + 2.2×MAD of the envelope; refractory ≈0.25 s; local maxima above threshold become S1 peaks.
- S2 detection and timings
- For each S1, search 0.12–0.6 s after it (before the next S1). The tallest local peak is S2 if it exceeds 25% of the S1 peak.
- Widths: span where envelope stays above 35% of the peak (capped to ±220 ms search).
- Systole: S2 start − S1 start. Diastole: next S1 start − S2 end (when next S1 exists and starts after S2 ends).
- Heart rate
- Compute intervals between successive S1 peaks (s); heartRateBpm = 60 / mean(intervals). Needs ≥2 beats; otherwise null.
- Durations (ms)
- S1 duration = mean(S1 end − S1 start) × 1000.
- S2 duration = mean(S2 end − S2 start) × 1000.
- Systole = mean(systole) × 1000; Diastole = mean(diastole) × 1000.
- If no samples for a metric, it is null.
- Respiration
- Downsample envelope to ~10 Hz, remove mean, compute autocorrelation lags from ~2–12 s; best lag → freq; accept only 0.1–0.5 Hz; respirationRateBpm = freq × 60 else null.
- Counts
- beatCount = number of detected S1 peaks.
If metrics display “—”, it means insufficient detectable beats or low SNR; try ≥10 s of cleaner audio and tighter mic placement.
- Scroll/zoom horizontally on the waveform; use the overview panel for navigation.
- Metrics update after processing finishes. Export the JSON report from the button in the main view.
- Use the JSON export to pull metrics and raw envelope/peaks into MATLAB or Python.
- For raw audio: capture/upload → save from browser devtools (decoded PCM in memory), or extend
useAudioEngineto POST the Float32Array to your backend. The arrays insignalProcessing.jsare plain JS arrays, easily serializable.
src/utils/signalProcessing.js: DSP pipeline and metrics.src/hooks/useAudioEngine.js: mic/recording, decoding, and processing orchestration.src/components/Visualizer.jsx: waveform + overview with scroll/zoom.src/components/MetricsPanel.jsx: metrics display.src/components/ControlPanel.jsx: capture/upload controls.App.jsx/App.css: layout and export wiring.
- Bandpass range:
lowCut = 20,highCut = 180, Q = 0.707 (Butterworth). - Smoothing:
smoothingMs = 5default. - Peak refractory:
minDistance≈ 0.28 s. - Respiration search: 0.1–0.5 Hz via autocorrelation.
- Quality depends on microphone placement and ambient noise; headset-style or chest-worn mics work best.
- Browser audio APIs can vary; prefer Chrome/Edge. Safari mobile is less consistent.
- Metrics are approximate and not for clinical decision-making.