0
$\begingroup$

I am quite new in DSP and generating the spectrogram of an audio file. My spectrogram is not smooth and it is showing the quite raw image with pixel values, something like this

enter image description here

While I am looking for a smooth spectrogram like this

enter image description here

Where I am doing mistake? Is it because of size of window size? My code to generate Mel spectrogram is

def readData(file):
    origData,origSampFreq = librosa.load(file, sr=None)
    return origData, origSampFreq


def resample(originalData, origSampFreq, targetSampFreq):
    resampledData = librosa.resample(originalData, origSampFreq, targetSampFreq)
    return resampledData


def normalizeSound(resampledData, axis):
    """ Axis is 0 for row-wise and 1 
    for column wise"""
    normalizedData = normalize(resampledData, axis)
    return normalizedData

def calculateMelSpectogram(normalizedData, hop_length, win_length, sr):
    #newSamplingFreq = 16000
    S=librosa.feature.melspectrogram(normalizedData, sr=sr, hop_length=hop_length, win_length=win_length)
    return S

#Plot melspectogram

def plotMelSpectogram(S, sr, name, ref=np.max):
    plt.figure(figsize=(10,3))
    S_dB = librosa.power_to_db(S, ref=np.max)
    librosa.display.specshow(S_dB, x_axis='time',y_axis='mel', sr=16000,)
    plt.colorbar(format='%+2.0f dB')
    plt.title('Mel-frequency spectrogram')
    plt.savefig('./chunk_images/' + name + "mel.png",dpi=(300), bbox_inches='tight')
    plt.tight_layout()
    plt.show()
def featureExtraction(audioFile, name, targetSampFreq = 16000, 
                      axis =0 , 
                      hop_length= 256,
                      win_length=512):
    
    y, y_sr = readData(file=audioFile)
    print(y, y_sr)
    resampledData = resample(originalData=y, origSampFreq=y_sr, targetSampFreq=targetSampFreq)
    normalizedData = normalizeSound(resampledData, axis=axis)
    S = calculateMelSpectogram(normalizedData=normalizedData, hop_length=hop_length, win_length=win_length, sr=targetSampFreq)
    plotSound(soundData=normalizedData, sr=targetSampFreq,x_axis_string='time' , name = name)
    plotMelSpectogram(S, sr=targetSampFreq, name = name, ref=np.max)
    return S

# plot orginal time domain data

def plotSound(soundData, sr, x_axis_string, name):
    plt.figure(figsize=(10,3))
    waveplot(soundData, sr, x_axis=x_axis_string)
    plt.savefig('./chunk_images/' + name + "sound.png",dpi=(300), bbox_inches='tight')
$\endgroup$

2 Answers 2

0
$\begingroup$

Creating a smooth looking spectrogram is not so much a DSP problem as it is a computer graphics thing. Of course, the choice of certain spectrogram parameters such as the frame/window length, the overlapping, the window function (Hann, Blackman etc) do play a role but they're not overly important.

By far , the 2 most important things when it comes to creating smooth looking spectrograms are

  1. having a gradient colour map(s)

  2. having a dense spectrogram grid, which corresponds to the spectrogram / fft frames (STFT frames)

Gradient colour maps can either be created in code or hardcoded. Creating/coding a colour map is simple but it takes some time to get it right.

The point #2 is actually a function of the track length TL (in number of samples ) and the frame length (number of samples) FL. The number of non-overlapping frames can be computed by simply dividing TL / FL. It should be evident that if this number exceeds the spectrogram width on the screen (the viewport width), you're going to have to average several frames for a single spectrogram column and no colour (pixel) averaging will be necessary (because we already have a dense spectrogram grid as in image 1).

If, on the other hand, your track is short (let's say only a few seconds) , you'll have only a few FFT/spectrogram frames and if you now draw these frames, you'll get a pretty grainy image. The solution is creating a smooth gradient based on the selected colour map between the adjacent frames ( between frame 1 and 2, 2 and 3, 3 and 4 and so on ).Linear interpolation is the easiest way to accomplish this (image 2).

enter image description here

I don't know exactly how you can do that in matlab (I only have c++ code for the examples above) but hopefully this will push in the right direction.

$\endgroup$
0
$\begingroup$

Try increasing the overlap in the spectrogram calculation, and using an interpolated array-to-image renderer (shading interp in matlabesque).

-k

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.