pythonmatplotliblibrosaspectrogramacoustics

librosa y-axis spectrogram does not align properly


How to align axis of spectrogram visualisations in Librosa or Matplotlib ?

Consider this example, from Librosa's documentation: enter image description here

as you can see, the rolloff are aligned with the spectrogram. I can't replicate the figure with my own audio.

The y-axis is never aligned.

Try:

sr = 250000
n_fft = 2048
hop_length=256
win_length = 1024
fmin = 220


S, phase = librosa.magphase(librosa.stft(filtered_audio))

sftf_spec = librosa.stft(filtered_audio, n_fft=n_fft, hop_length=hop_length)

S = np.abs(sftf_spec)

rolloff = librosa.feature.spectral_rolloff(S=S, 
                                           sr=sr, 
                                           n_fft=n_fft, 
                                           hop_length=hop_length, 
                                           win_length = win_length 
                                          )

amplitude_spec = librosa.amplitude_to_db(S, 
                                        
                                     
                                        ref=np.max)

rolloff_min = librosa.feature.spectral_rolloff(S=S, sr=sr, roll_percent=0.15)

fig, ax = plt.subplots()

librosa.display.specshow(amplitude_spec,

                         y_axis='log', x_axis='time', ax=ax)

ax.plot(librosa.times_like(rolloff), rolloff[0], label='Roll-off frequency (0.85)')

ax.plot(librosa.times_like(rolloff), rolloff_min[0], color='w',

        label='Roll-off frequency (0.15)')

ax.legend(loc='lower right')

ax.set(title='log Power spectrogram')

If you need to replicate, you can try download the audio wav :

https://drive.google.com/file/d/1UCUWAaczzejTN9m_y-usjPbG8__1mWI1/view?usp=sharing
filtered_audio  = np.array([[  #copy ]])

I got this:

enter image description here

and if I set the rate in specshow, I got this:

librosa.display.specshow(amplitude_spec,
                         sr=sr,

                         y_axis='log', x_axis='time', ax=ax)

enter image description here

I want to have the bandwidth following the same scale of the spectrogram they were build from...


Solution

  • One has to be diligent in passing all the relevant parameters. In your code, both the call to specshow, times_like, and spectral_rolloff were missing key arguments like sr, hop_length et.c. Without these, both the X and Y axis will typically be off.

    When ensuring this, the results look to be correct. See complete code below.

    enter image description here

    import librosa
    import numpy as np
    import pandas
    
    from matplotlib import pyplot as plt
    import librosa.display
    
    
    
    def plot_spectral(audio, sr, hop_length=256, win_length=1024, n_fft=2048):
    
        # shared parameters
        spec_params = dict(n_fft=n_fft, hop_length=hop_length, win_length=win_length)
    
        # compute
        sftf_spec = librosa.stft(audio, **spec_params)
        S = np.abs(sftf_spec)
        amplitude_spec = librosa.amplitude_to_db(S, ref=np.max)
    
        up = librosa.feature.spectral_rolloff(S=S, sr=sr, **spec_params, roll_percent=0.85)
        
        rolloff = pandas.DataFrame({
            'upper': librosa.feature.spectral_rolloff(S=S, sr=sr, **spec_params, roll_percent=0.85)[0, :],
            'lower': librosa.feature.spectral_rolloff(S=S, sr=sr, **spec_params, roll_percent=0.15)[0, :],
        })
        rolloff['time'] = librosa.times_like(rolloff['lower'], sr=sr, hop_length=hop_length)
    
        fig, ax = plt.subplots()
    
        librosa.display.specshow(amplitude_spec, sr=sr, **spec_params,
                                 y_axis='log', x_axis='time', ax=ax, )
    
        ax.plot(rolloff['time'], rolloff['upper'], color='blue', label='Roll-off frequency (0.85)')
    
        ax.plot(rolloff['time'], rolloff['lower'], color='white', label='Roll-off frequency (0.15)')
    
        ax.legend(loc='lower right')
    
        ax.set(title='log Power spectrogram')
    
        fig.savefig('spectral-rolloffs.png')
    
    
    def load_data():
        p = 'test_wav_segment.wav'
        audio, sr = librosa.load(p, sr=None)
        return audio, sr
    
    audio, sr = load_data()
    
    
    plot_spectral(audio, sr=sr)
    
    

    The use of Pandas is not critical. However it keeps the related data together, and ensures that the times array is equal length to the rolloffs which they are fo.