Repository: nussl/nussl (language: python)

Issue:
Mel-frequency visualization bug

<img width="719" alt="Screen Shot 2020-12-28 at 12 32 49 PM" src="https://user-images.githubusercontent.com/26678616/103241690-da8a6100-4908-11eb-9ad2-f5e55a3d1857.png">
**Describe the bug**
I tried the nussl tutorial and found the Mel-frequency spectrogram visualization is returning erroneous visualizations. I think the issue here is that the frequency bins are not being mapped to the right index numbers for Hz.  


**Steps To Reproduce**
Example:
```
## Example of mel display issue 

# Get the signal
input_file_path = nussl.efz_utils.download_audio_file('schoolboy_fascination_excerpt.wav')
signal1 = nussl.AudioSignal(input_file_path)

# calculate the stft
stft = signal1.stft()
psd = signal1.power_spectrogram_data
print(psd.shape)

# cut off the signal at 1kHz
import numpy as np

lp_stft = signal1.stft_data.copy()
lp_cutoff = 1000  # Hz
frequency_vector = signal1.freq_vector  # a vector of frequency values for each FFT bin
idx = (np.abs(frequency_vector - lp_cutoff)).argmin()  # trick to find the index of the closest value to lp_cutoff
lp_stft[idx:, :, :] = 0.0j  # every freq above lp_cutoff is 0 now
signal1_lp = signal1.make_copy_with_stft_data(lp_stft)
signal1_lp.istft()

# plot the output
#mysig = signal1
mysig = signal1_lp
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 9))
plt.subplot(311)
plt.title('Linear-frequency spectrogram')
nussl.utils.visualize_spectrogram(mysig)

plt.subplot(312)
plt.title('Log-frequency spectrogram')
nussl.utils.visualize_spectrogram(mysig, y_axis='log')

plt.subplot(313)
plt.title('Mel-frequency spectrogram')
nussl.utils.visualize_spectrogram(mysig, y_axis='mel')

plt.tight_layout()
plt.show()

```

**Expected behavior**
I expect it to show me the audio cut off at 1kHz for all the spectrograms.

**What did happen**
Every spectrogram except the Mel-frequency spectrogram showed me a 1 kHz cutoff. The Mel-frequency one showed me a cutoff around 100 Hz or so.

**Software versions***
I ran this in Google CoLab

Linux-4.19.112+-x86_64-with-Ubuntu-18.04-bionic
Python 3.6.9 (default, Oct  8 2020, 12:12:24) 
[GCC 8.4.0]
NumPy 1.19.4
SciPy 1.4.1
nussl 1.1.3rc5

**Additional context**
This code is a cut&paste of the nussl tutorial code.



Discussion / hints:
Hmm, I'll look into this more but the code for generating the spectrogram is just from librosa. Here is the relevant underlying code:

https://github.com/nussl/nussl/blob/af7d0c50e01d107f4ef3305b89eb130d95d0a7cd/nussl/core/utils.py#L397-L418

Fix patch:
diff --git a/nussl/__init__.py b/nussl/__init__.py
index 81f43067..e3ba8178 100644
--- a/nussl/__init__.py
+++ b/nussl/__init__.py
@@ -5,7 +5,7 @@
     vamp_imported = False
 
 # Current nussl version
-__version__ = '1.1.3'
+__version__ = '1.1.4'
 
 class ImportErrorClass(object):
     def __init__(self, lib, **kwargs):
diff --git a/nussl/core/utils.py b/nussl/core/utils.py
index 2e221336..a2901f8d 100644
--- a/nussl/core/utils.py
+++ b/nussl/core/utils.py
@@ -394,11 +394,12 @@ def visualize_gradient_flow(named_parameters, n_bins=50):
 
     plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3, ncol=2)
 
-def visualize_spectrogram(audio_signal, ch=0, do_mono=False, x_axis='time', 
-                          y_axis='linear',  **kwargs):
+
+def visualize_spectrogram(audio_signal, ch=0, do_mono=False, x_axis='time',
+                          y_axis='linear', **kwargs):
     """
     Wrapper around `librosa.display.specshow` for usage with AudioSignals.
-    
+
     Args:
         audio_signal (AudioSignal): AudioSignal to plot
         ch (int, optional): Which channel to plot. Defaults to 0.
@@ -411,11 +412,21 @@ def visualize_spectrogram(audio_signal, ch=0, do_mono=False, x_axis='time',
 
     if do_mono:
         audio_signal = audio_signal.to_mono(overwrite=False)
-    
-    data = librosa.amplitude_to_db(np.abs(audio_signal.stft()), ref=np.max)
-    librosa.display.specshow(data[..., ch], x_axis=x_axis, y_axis=y_axis, 
-        sr=audio_signal.sample_rate, hop_length=audio_signal.stft_params.hop_length,
-        **kwargs)
+
+    if y_axis == 'mel':
+        # Monkey patch for https://github.com/librosa/librosa/issues/1240
+        data = librosa.feature.melspectrogram(audio_signal.get_channel(ch),
+                                              sr=audio_signal.sample_rate)
+        kwargs.update({'fmax': audio_signal.sample_rate / 2.})
+    else:
+        data = np.abs(audio_signal.stft())[..., ch]
+
+    data = librosa.amplitude_to_db(data, ref=np.max)
+    librosa.display.specshow(data, x_axis=x_axis, y_axis=y_axis,
+                             sr=audio_signal.sample_rate,
+                             hop_length=audio_signal.stft_params.hop_length,
+                             **kwargs)
+
 
 def visualize_waveform(audio_signal, ch=0, do_mono=False, x_axis='time', **kwargs):
     """
diff --git a/setup.py b/setup.py
index c1c9e371..d8c187da 100644
--- a/setup.py
+++ b/setup.py
@@ -11,7 +11,7 @@
 
 setup(
     name='nussl',
-    version='1.1.3',
+    version='1.1.4',
     classifiers=[
         'Development Status :: 3 - Alpha',
         'Environment :: Console',


Test patch:
diff --git a/tests/core/test_utils.py b/tests/core/test_utils.py
index 2473dd9c..ebe1e720 100644
--- a/tests/core/test_utils.py
+++ b/tests/core/test_utils.py
@@ -237,13 +237,16 @@ def test_utils_slice_along_dim():
 def test_utils_visualize_spectrogram(music_mix_and_sources):
     mix, sources = music_mix_and_sources
 
-    plt.figure(figsize=(10, 6))
+    plt.figure(figsize=(10, 9))
     plt.subplot(211)
     nussl.utils.visualize_spectrogram(mix)
 
     plt.subplot(212)
     nussl.utils.visualize_spectrogram(mix, do_mono=True)
 
+    plt.subplot(313)
+    nussl.utils.visualize_spectrogram(mix, y_axis='mel')
+
     OUTPUT = os.path.join(PLOT_DIRECTORY, 'viz_spectrogram.png')
     plt.tight_layout()
     plt.savefig(OUTPUT)