Take the 2-minute tour ×

I'm adapting and extending the Matlab "Chroma Toolbox" by Meinard Müller and Sebastien Ewert to python. It aims to detect what musical pitches are present at each analysis frame for an audio recording.

The first step is to determine the tuning of the music, and the Chroma Toolbox tests whether the music is tuned at the standard A=440Hz, or down a quarter, third, half, two-thirds, or three-quarters of a semitone. That's ok, but in my application, I need more resolution in the tuning detection.

Once the tuning is selected from one of those choices, a corresponding filterbank is chosen, which is used to find how much energy there is at each musical pitch over the range of the piano. (Also, the waveform is resampled to 22050, 4410, and 882 Hz)

The coefficients for the filterbanks are stored in .mat files, given by the Chroma Toolbox. For example, the coefficients for detecting energy at standard-tuned middle C (261.63 Hz) are b = [ 1., -7.43749873, 24.72954997, -47.94740681, 59.25189976, -47.77885707, 24.55599193, -7.35933913, 0.98601284] and a = [0.00314443, -0.02341175, 0.07794208, -0.15134062, 0.18733283, -0.15134062, 0.07794208, -0.02341175, 0.00314443], and the sample rate for middle C is 4410 Hz.

These coefficients are used in a call to filtfilt: I use scipy.signal.filtfilt(b, a, x) where x is the waveform at an appropriate sampling frequency, low for the low notes, high for the higher ones. This step is done in the file "audio_to_pitch_via_FB.m".

the question:

Because I want to allow for different tuning levels than those designed into the Chroma Toolbox, I need to make my own filterbanks, and so need to know how to compute the filter coefficients. To do so, I need a function coeffs(freq, fs) that will find the right coefficients to find the energy at a given frequency freq, for a signal at sample frequency fs. How do I do it?

Here's the name of one of the .mat files, in case it contains a useful clue. "MIDI_FB_ellip_pitch_60_96_22050_Q25_minusQuarter.mat"

share|improve this question

1 Answer 1

The code that generates the filters is in generateMultiratePitchFilterbank.m file. The ellip function returns a and b the other way round, but otherwise it's more or less the same.

The following recipe reproduces the numbers you quoted:

import numpy as np
import scipy.signal as ss

def coeffs(pitch, fs, Q=25, stop=2, Rp=1, Rs=50):
    """Calculate filter coeffs for a given pitch and sampling rate, fs.
    See their source code for description of Q, stop, Rp, Rs"""
    nyq = fs/2.                       # Nyquist frequency
    pass_rel = 1/(2.*Q)             
    stop_rel = pass_rel * stop

    # The min-max edges of the pass band
    Wp = np.array([pitch - pass_rel*pitch, pitch+pass_rel*pitch])/nyq
    # And the stop band(s)
    Ws = np.array([pitch - stop_rel*pitch, pitch+stop_rel*pitch])/nyq

    # Get the order, natural freq
    n, Wn = ss.ellipord(Wp, Ws, Rp, Rs)

    # Get a and b:
    a, b = ss.ellip(n, Rp, Rs, Wn, btype="bandpass")

    return a, b
share|improve this answer

Your Answer


