ResearchLiveGitHub stars: 1PCG Signal Analysis
Adaptive filtering for phonocardiogram signals.
Overview
Adaptive filtering for phonocardiogram (PCG) signal analysis. Heart-sound recordings carry critical diagnostic information about the heart's mechanical activity, but arrive heavily contaminated with noise. This was the precursor work to the RAEEUCCI publication.
PCG Signal Analysis was my final project in my last semester of college, built together with my batchmates. A phonocardiogram (PCG) is a recording of the sounds the heart makes, and recordings captured electronically arrive with noise mixed in. The project had one purpose: reduce that noise using adaptive filters.
The repository contains MATLAB implementations of two adaptive filters, a Python command-line tool that applies them to audio recordings, and a Next.js site that documents the project at pcga.kunalkeshan.dev.
Why I built it
It was my final college project, done with my batchmates, and its goal was narrow: reduce noise in electronically detected PCG signals. That single goal defined the scope. Take a noisy PCG recording, run it through an adaptive filter, and get a cleaner signal back.
An adaptive filter adjusts its own coefficients as it processes a signal instead of using fixed ones, which is why the technique suits noise whose character can change from one recording to the next.
How it works
The filtering follows the standard adaptive noise-cancellation setup. A noisy signal is the desired input, a separate noise reference is fed to the filter, and the filter learns to predict the noise so it can be subtracted. In the Python tool, the flow looks like this:
- Load a recording. The tool lists the MP3 files in the signals folder and asks which one to use, and which algorithm to run. Audio is read with audioread, with an FFmpeg fallback if no backend is found.
- Add noise. Gaussian noise with a scale of 0.033 is added to the recording to create the noisy signal.
- Build a noise reference. The reference is 0.95 times the same noise.
- Filter. For every sample, the filter takes the most recent samples of the reference, predicts the noise, and computes the error between the noisy signal and that prediction. The weights are then updated from the error.
- Output. The error signal is the filtered result. It is plotted next to the original and noisy signals, and saved as a timestamped WAV file.
The parameters used in the tool are a step size of 0.1, a filter length of 2 taps, and, for Leaky LMS, a leakage factor of 0.0001.
The two filters
Both filters share the same first steps at each sample: form the filter output from the weights and the latest reference samples, then subtract it from the noisy signal to get the error.
- LMS (Least Mean Squares) updates the weights by adding the error times the input samples, scaled by twice the step size.
- Leaky LMS does the same, but first scales the existing weights down by one minus the step size times the leakage factor. That slow decay stops the weights from growing without bound.
The same two update rules exist in both the MATLAB scripts and the Python tool.
Algorithm variants along the way
The commit history shows more than the two filters that survived.
- August 2024: the first Python prototype was a multi-stage cascade that alternated LMS and sign-LMS stages, with the error from one stage feeding the next. It ran on a synthetic signal at a 10 dB input SNR (32 taps, step size 0.01, two stages), and printed the SNR improvement.
- September 2024: a set of MATLAB scripts explored LMS, sign-error, sign-regressor and sign-sign variants. They came with synthetic test inputs: sine-wave mixtures and a generated spike-train waveform.
- October to December 2024: the MATLAB code was reduced to LMS and Leaky LMS, and the Python tool was rebuilt around the same two filters, working on real audio files instead of synthetic signals.
The documentation site
The Next.js site in the repository documents the project: background on PCG signals, objectives, methodology, project details, resources, and an interactive demo section. It is live at pcga.kunalkeshan.dev.
Challenges and insights
- Same noise level, different meaning. MATLAB's audioread returns samples scaled between -1 and 1, while the Python tool reads raw 16-bit integers. Both add noise at a scale of 0.033, so that noise is large in the MATLAB version and tiny in the Python one. Keeping two ports of one experiment comparable means matching the scale of the inputs, not only the algorithm.
- A controlled setup. The noise is generated in code, and the filter's reference is a scaled copy of it. That makes the experiment easy to reproduce, but it isn't the same as a real recording where the noise reference has to come from somewhere.
- Measurement. The August 2024 prototype printed an SNR improvement. The current command-line tool plots and saves the result but doesn't compute a metric.
- Decoding fallback. When the FFmpeg fallback is used, the tool assumes a 44,100 Hz sample rate instead of reading it from the file.
What I learned
- Small update-rule changes, such as a leakage term or taking the sign of the error, are easy to try in MATLAB, which is why several variants appeared in a few days.
- Porting the same algorithm between languages means checking the input scaling and the loop boundaries as well as the equations.
- Trimming the experiments down to two filters made the final tool simple enough to run from one prompt.
If I started today (suggestions)
- Add automated tests that run the filters on a signal with a known answer.
- Report the SNR improvement every run, so changes to the filters can be compared.
- Try normalised LMS, which adapts its step size to the input power.
- Evaluate on recordings with real, independently captured noise instead of simulated noise.
- Keep one shared set of test signals for MATLAB and Python, and keep the README in step with the code.
Timeline and outcomes
- August 2024: first commit and the early Python prototype.
- September 2024: MATLAB variants, and the documentation site's first pages.
- October to December 2024: LMS refactor, Leaky LMS, and the rebuilt Python tool with sample recordings.
- December 2025: documentation and site updates, including a dataset reference to the PhysioNet/CinC Challenge 2016 heart-sound recordings.
The outcome is a small public repository, a live documentation site, and two working adaptive filters in MATLAB and Python. This research preceded the RAEEUCCI publication.
Screenshots
Click any screenshot to view it full size.