Skip to content

roanye/audio_processing

Repository files navigation

Audio Processing - MIT Music Tech & Computation

Author

Roan Yeh

Overview

Goal: Remove interfering sound from audio file

Description

To accomplish this goal, I created an algorithm that:

  1. Reads in an audio file.
  2. Detects prominent interfering frequencies.
  3. Designs a set of biquad notch filters to suppress the detected fundamental frequencies and their harmonics, with varying Q factors, along with another wider notch to reduce fuzz around the fundamental frequencies.
  4. Applies those filters online to the audio signal.
  5. Outputs a cleaned .wav file with interfering frequencies supressed.
  6. Visualizes the results by plotting a spectrogram for the outputted signal and frequency response curves for the filters applied to the original audio file.

See the appendix (last section) for all formulas used in this project.

Specifications

  • Apply filter to remove sound online one sample at a time
  • Design filter on your own
  • Do not use existing routine for applying filters on sounds
  • Write output to "output.wav"

Design

audio_processing.py: Main file that runs the algorithm to remove interfering frequencies.

filters.py: Contains functions that create filters

  • notch_biquad_coefficients
    • Computes the coefficients for a notch filter using the biquad filter design method.
  • notch_filter_coefficients_with_harmonics
    • Computes the coefficients for a notch filter that removes multiple harmonics of a given fundamental frequency.

filter_proccessing.py: Contains functions that apply filters to an input signal

  • process_single_sample
    • Processes a single sample of the input signal through a biquad filter.
  • apply_notch_filters
    • Applies a series of notch filters to the input signal online, sample by sample.

frequency_detection.py: Contains functions that detect peak frequencies in a given frequency range.

  • detect_interfering_frequencies
    • Detects the fundamental frequencies of the interfering sound in the input signal.

visualization.py: Contains functions that plot spectrograms and filters

  • compute_spectrogram
    • Computes a spectrogram of an audio signal.
  • plot_spectrogram
    • Plots a spectrogram of an audio signal.
  • plot_filters
    • Plots the frequency response of multiple filters.

Approach

  1. Design filter

    • Implement a notch filter from scratch
  2. Visualization - testing aid (allows for concrete feedback from each test)

    • Display a spectrogram of the filtered audio.
    • Display filter response curve to see effect of filter
  3. Apply filter online

    • For each interfering frequency:
      • Loop through each audio sample.
      • Apply the filter recursively.
  4. Improve filter design (more notches, adjusted/scaled Q factors)

  5. Analyze audio for interfering frequencies

    • Take a short segment of the audio and compute its frequency spectrum.
    • Identify peaks corresponding to unwanted sounds.

Journal: Observations/Thoughts

1. My first step was the visualize the audio file by creating a spectrogram from the signal information. Upon opening the spectrogram, I noticed two prominent frequencies centered around 2k and 2.5k Hz.

Input Spectogram

2. Filter approach: Center a notch biquad filter on the fundamental interfering frequencies with a small bandwidth to also remove fuzz. Use a moderate Q factor. The noise is not a perfect sine wave so also cut harmonics (to a lesser degree than the fundamental of course). Choosing IIR over FIR as tonal interference is stationary and efficiency (FIR is overkill).

3. Created notch biquad filter and visualized it to make sure the output is correct. Below is the notch filter at a frequency of 2,000 and Q factor of 30.

Filter Reponse Fundamental

4. Now lets use my filter function to generate filter coefficients for a fundamental frequency AND its harmonics. New function made to produce multiple coefficients for fundamental and its harmonics. Updated the filter response plot function. Below is the notch filter at a frequency of 2,000 with a Q factor of 10 and a Q factor of 30 for the harmonics.

Filter Reponse with Harmonics

Return here to improve harmonic handling later (possibly modify the Q factor per harmonic)

6. Now I need to apply a filter to a single sample. This will be run in the main for loop and will apply the notch biquad filter to the audio wav online.

5. Now I need to focus on how to apply this filter online sample by sample. To do this, I need to look at the samples around any given sample. I will make a class for this.

7. Now I need to loop thru all samples and apply this function.

Return to this function later to add dynamic processing since the interfering noise is NOT CONSTANT

8. First test! Manually chose fundamental frequencies of 2075 and 2575 Hz (rough esimation). I probably don't need to cut the harmonics that much and need to focus on fuzz around the fundamental frequencies, but yay! It works! Now let's make it better!

First Test

9. I want to improve my filter so the harmonic cuts scale better and the fundamental frequency is cut wider. Scaled Q_factor for harmonics by n^2.

Notch filter adjusted

I might want to change the filter for the fundamental frequency. My method is sort of janky right now.

Here's the output spectrogram given the conditions and fundamental frequencies of the first test:

Output spectrogram second test

10. Now I want to detect the interfering frequencies. Created a new function and tested it in a full test going thru all steps (reference full_test_1).

Output spectrogram full test 1

Conclusion: Cutting way too much around 2.5k Hz. Need to modify the filters.

11. Reorganized my code into separate files. Tweaked some function inputs in audio_processing.py. See results in test/full_test_2

Output spectrogram full test 2

12. Need to fix frequency_detection.py. I am currently taking the top 2 of all frequencies EVEN IF they are right next to each other. I need to do clustering.

I've implemented clustering by adding a new arg to detect_interfering_frequencies. Also added a new arg bandwidth_harmonic to notch_filter_coefficients_with_harmonics to differ the Q factor between the fundamental frequencies and harmonics. See test results in tests/full_test_3

Output spectrogram full test 3

13. Let's focus on the fuzz around the fundamental interfering frequencies. I'm going to add a wider cut on the fundamental frequencies ontop of the deeper notch so I sharply remove the fundamental and reduce the fuzz around it.

Output spectrogram full test 4

14. Yay! While the algorithm is not perfect, I have removed a significant part of the interfering frequencies without overcutting and removing too much of the upper mid range. In the future, I would like to make this algorithm dynamic so the filter is adjusted based on the presence/magnitude of the interfering frequencies. This was a really fun project! Here are visuals for the final results.

Output spectrogram final result

Notch filters final result

Necessary installs

Run:

pip install -r requirements.txt

Appendix

Coefficient Formulas

Source: Audio EQ Cookbook by Robert Bristow-Johnson

$$ w_0 = 2 \pi \frac{f_0}{f_s}, \quad \alpha = \frac{\sin(w_0)}{2 Q} $$

Numerator coefficients

$$ b_0 = 1, \quad b_1 = -2 \cos(w_0), \quad b_2 = 1 $$

Denominator coefficients

$$ a_0 = 1 + \alpha, \quad a_1 = -2 \cos(w_0), \quad a_2 = 1 - \alpha $$

Biquad Filter Formula

$$ y[n] = \frac{1}{a_0} \Big( b_0 x[n] + b_1 x[n-1] + b_2 x[n-2] - a_1 y[n-1] - a_2 y[n-2] \Big) $$

Direct Form 1 Filter Equation

$$ y[n] = \frac{b_0 x[n] + b_1 x[n-1] + b_2 x[n-2] - a_1 y[n-1] - a_2 y[n-2]}{a_0} $$

Q_factor formula

Source: Wikipedia

$$ Q = \frac{f_r}{\Delta f} $$

Frequency Response Formulas

Source: UC Berkeley EECS

$$ H(e^{j\omega}) = \frac{\sum_{k=0}^{N} b_k , e^{-j k \omega}}{\sum_{k=0}^{M} a_k , e^{-j k \omega}} $$

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages