Repository: librosa/librosa

Issue:
Fix strongly hardcoded BPM range for tempograms
**Is your feature request related to a problem? Please describe.**
Spend hours trying to produce proper tempograms with BPM ~ 550. The BPM-axis seems locked to around (16, 512).

Finally found [this](https://github.com/librosa/librosa/blob/main/librosa/display.py#L1368) being called by `specshow`:

    elif ax_type in ["tempo", "fourier_tempo"]:
        mode = "log"
        kwargs[base] = 2
        limit(16, 480)

**Describe the solution you'd like**
1. Document this hardcoded "feature" in `tempogram`/`tempogram_fourier` and/or `specshow`.
2. Make the `limit(...)` adjustable or add a note in the documentation on below workaround.

https://github.com/librosa/librosa/blob/main/librosa/display.py#L1368

**Describe alternatives you've considered**
Calling `ax[1].set_ylim(auto=True)` or `ax[1].set_ylim(8, 1024)` after `specshow` fixes the axis.

Fix strongly hardcoded BPM range for tempograms
**Is your feature request related to a problem? Please describe.**
Spend hours trying to produce proper tempograms with BPM ~ 550. The BPM-axis seems locked to around (16, 512).

Finally found [this](https://github.com/librosa/librosa/blob/main/librosa/display.py#L1368) being called by `specshow`:

    elif ax_type in ["tempo", "fourier_tempo"]:
        mode = "log"
        kwargs[base] = 2
        limit(16, 480)

**Describe the solution you'd like**
1. Document this hardcoded "feature" in `tempogram`/`tempogram_fourier` and/or `specshow`.
2. Make the `limit(...)` adjustable or add a note in the documentation on below workaround.

https://github.com/librosa/librosa/blob/main/librosa/display.py#L1368

**Describe alternatives you've considered**
Calling `ax[1].set_ylim(auto=True)` or `ax[1].set_ylim(8, 1024)` after `specshow` fixes the axis.



Discussion:
I believe this is only an issue for tempogram display, not the feature calculation. But yeah, we should expand the api to expose the min and max tempo parameters.
I believe this is only an issue for tempogram display, not the feature calculation. But yeah, we should expand the api to expose the min and max tempo parameters.

Resolving patch:
diff --git a/librosa/display.py b/librosa/display.py
--- a/librosa/display.py
+++ b/librosa/display.py
@@ -934,6 +934,8 @@ def specshow(
     win_length: Optional[int] = None,
     fmin: Optional[float] = None,
     fmax: Optional[float] = None,
+    tempo_min: Optional[float] = 16,
+    tempo_max: Optional[float] = 480,
     tuning: float = 0.0,
     bins_per_octave: int = 12,
     key: str = "C:maj",
@@ -1062,6 +1064,12 @@ def specshow(
     fmax : float > 0 [scalar] or None
         Used for setting the Mel frequency scales
 
+    tempo_min : float > 0 [scalar]
+        Lowest tempo (in beats per minute) for tempogram display.
+
+    tempo_max : float > 0 [scalar]
+        Highest tempo (in beats per minute) for tempogram display.
+
     tuning : float
         Tuning deviation from A440, in fractions of a bin.
 
@@ -1210,8 +1218,8 @@ def specshow(
     __set_current_image(ax, out)
 
     # Set up axis scaling
-    __scale_axes(axes, x_axis, "x")
-    __scale_axes(axes, y_axis, "y")
+    __scale_axes(axes, x_axis, "x", tempo_min=tempo_min, tempo_max=tempo_max)
+    __scale_axes(axes, y_axis, "y", tempo_min=tempo_min, tempo_max=tempo_max)
 
     # Construct tickers and locators
     __decorate_axis(
@@ -1324,7 +1332,7 @@ def __check_axes(axes: Optional[mplaxes.Axes]) -> mplaxes.Axes:
     return axes
 
 
-def __scale_axes(axes, ax_type, which):
+def __scale_axes(axes, ax_type, which, tempo_min, tempo_max):
     """Set the axis scaling"""
     kwargs = dict()
     thresh = "linthresh"
@@ -1365,7 +1373,7 @@ def __scale_axes(axes, ax_type, which):
     elif ax_type in ["tempo", "fourier_tempo"]:
         mode = "log"
         kwargs[base] = 2
-        limit(16, 480)
+        limit(tempo_min, tempo_max)
     else:
         return
 


Test patch: