Caution
You're reading an old version of this documentation. If you want up-to-date information, please have a look at 0.9.1.
Source code for librosa.display
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Display
=======
.. autosummary::
:toctree: generated/
specshow
waveplot
cmap
TimeFormatter
NoteFormatter
LogHzFormatter
ChromaFormatter
TonnetzFormatter
"""
import warnings
import numpy as np
from matplotlib.cm import get_cmap
from matplotlib.axes import Axes
from matplotlib.ticker import Formatter, ScalarFormatter
from matplotlib.ticker import LogLocator, FixedLocator, MaxNLocator
from matplotlib.ticker import SymmetricalLogLocator
from . import core
from . import util
from .util.exceptions import ParameterError
__all__ = ['specshow',
'waveplot',
'cmap',
'TimeFormatter',
'NoteFormatter',
'LogHzFormatter',
'ChromaFormatter',
'TonnetzFormatter']
[docs]class TimeFormatter(Formatter):
'''A tick formatter for time axes.
Automatically switches between seconds, minutes:seconds,
or hours:minutes:seconds.
Parameters
----------
lag : bool
If `True`, then the time axis is interpreted in lag coordinates.
Anything past the midpoint will be converted to negative time.
unit : str or None
Abbreviation of the physical unit for axis labels and ticks.
Either equal to `s` (seconds) or `ms` (milliseconds) or None (default).
If set to None, the resulting TimeFormatter object adapts its string
representation to the duration of the underlying time range:
`hh:mm:ss` above 3600 seconds; `mm:ss` between 60 and 3600 seconds;
and `ss` below 60 seconds.
See also
--------
matplotlib.ticker.Formatter
Examples
--------
For normal time
>>> import matplotlib.pyplot as plt
>>> times = np.arange(30)
>>> values = np.random.randn(len(times))
>>> plt.figure()
>>> ax = plt.gca()
>>> ax.plot(times, values)
>>> ax.xaxis.set_major_formatter(librosa.display.TimeFormatter())
>>> ax.set_xlabel('Time')
>>> plt.show()
Manually set the physical time unit of the x-axis to milliseconds
>>> times = np.arange(100)
>>> values = np.random.randn(len(times))
>>> plt.figure()
>>> ax = plt.gca()
>>> ax.plot(times, values)
>>> ax.xaxis.set_major_formatter(librosa.display.TimeFormatter(unit='ms'))
>>> ax.set_xlabel('Time (ms)')
For lag plots
>>> times = np.arange(60)
>>> values = np.random.randn(len(times))
>>> plt.figure()
>>> ax = plt.gca()
>>> ax.plot(times, values)
>>> ax.xaxis.set_major_formatter(librosa.display.TimeFormatter(lag=True))
>>> ax.set_xlabel('Lag')
'''
[docs] def __init__(self, lag=False, unit=None):
if unit not in ['s', 'ms', None]:
raise ParameterError('Unknown time unit: {}'.format(unit))
self.unit = unit
self.lag = lag
def __call__(self, x, pos=None):
'''Return the time format as pos'''
_, dmax = self.axis.get_data_interval()
vmin, vmax = self.axis.get_view_interval()
# In lag-time axes, anything greater than dmax / 2 is negative time
if self.lag and x >= dmax * 0.5:
# In lag mode, don't tick past the limits of the data
if x > dmax:
return ''
value = np.abs(x - dmax)
# Do we need to tweak vmin/vmax here?
sign = '-'
else:
value = x
sign = ''
if self.unit == 's':
s = '{:.3g}'.format(value)
elif self.unit == 'ms':
s = '{:.3g}'.format(value * 1000)
else:
if vmax - vmin > 3600:
s = '{:d}:{:02d}:{:02d}'.format(int(value / 3600.0),
int(np.mod(value / 60.0, 60)),
int(np.mod(value, 60)))
elif vmax - vmin > 60:
s = '{:d}:{:02d}'.format(int(value / 60.0),
int(np.mod(value, 60)))
else:
s = '{:.2g}'.format(value)
return '{:s}{:s}'.format(sign, s)
[docs]class NoteFormatter(Formatter):
'''Ticker formatter for Notes
Parameters
----------
octave : bool
If `True`, display the octave number along with the note name.
Otherwise, only show the note name (and cent deviation)
major : bool
If `True`, ticks are always labeled.
If `False`, ticks are only labeled if the span is less than 2 octaves
See also
--------
LogHzFormatter
matplotlib.ticker.Formatter
Examples
--------
>>> import matplotlib.pyplot as plt
>>> values = librosa.midi_to_hz(np.arange(48, 72))
>>> plt.figure()
>>> ax1 = plt.subplot(2,1,1)
>>> ax1.bar(np.arange(len(values)), values)
>>> ax1.set_ylabel('Hz')
>>> ax2 = plt.subplot(2,1,2)
>>> ax2.bar(np.arange(len(values)), values)
>>> ax2.yaxis.set_major_formatter(librosa.display.NoteFormatter())
>>> ax2.set_ylabel('Note')
>>> plt.show()
'''
def __call__(self, x, pos=None):
if x <= 0:
return ''
# Only use cent precision if our vspan is less than an octave
vmin, vmax = self.axis.get_view_interval()
if not self.major and vmax > 4 * max(1, vmin):
return ''
cents = vmax <= 2 * max(1, vmin)
return core.hz_to_note(int(x), octave=self.octave, cents=cents)
[docs]class LogHzFormatter(Formatter):
'''Ticker formatter for logarithmic frequency
Parameters
----------
major : bool
If `True`, ticks are always labeled.
If `False`, ticks are only labeled if the span is less than 2 octaves
See also
--------
NoteFormatter
matplotlib.ticker.Formatter
Examples
--------
>>> import matplotlib.pyplot as plt
>>> values = librosa.midi_to_hz(np.arange(48, 72))
>>> plt.figure()
>>> ax1 = plt.subplot(2,1,1)
>>> ax1.bar(np.arange(len(values)), values)
>>> ax1.yaxis.set_major_formatter(librosa.display.LogHzFormatter())
>>> ax1.set_ylabel('Hz')
>>> ax2 = plt.subplot(2,1,2)
>>> ax2.bar(np.arange(len(values)), values)
>>> ax2.yaxis.set_major_formatter(librosa.display.NoteFormatter())
>>> ax2.set_ylabel('Note')
>>> plt.show()
'''
def __call__(self, x, pos=None):
if x <= 0:
return ''
vmin, vmax = self.axis.get_view_interval()
if not self.major and vmax > 4 * max(1, vmin):
return ''
return '{:g}'.format(x)
[docs]class ChromaFormatter(Formatter):
'''A formatter for chroma axes
See also
--------
matplotlib.ticker.Formatter
Examples
--------
>>> import matplotlib.pyplot as plt
>>> values = np.arange(12)
>>> plt.figure()
>>> ax = plt.gca()
>>> ax.plot(values)
>>> ax.yaxis.set_major_formatter(librosa.display.ChromaFormatter())
>>> ax.set_ylabel('Pitch class')
>>> plt.show()
'''
def __call__(self, x, pos=None):
'''Format for chroma positions'''
return core.midi_to_note(int(x), octave=False, cents=False)
[docs]class TonnetzFormatter(Formatter):
'''A formatter for tonnetz axes
See also
--------
matplotlib.ticker.Formatter
Examples
--------
>>> import matplotlib.pyplot as plt
>>> values = np.arange(6)
>>> plt.figure()
>>> ax = plt.gca()
>>> ax.plot(values)
>>> ax.yaxis.set_major_formatter(librosa.display.TonnetzFormatter())
>>> ax.set_ylabel('Tonnetz')
>>> plt.show()
'''
def __call__(self, x, pos=None):
'''Format for tonnetz positions'''
return [r'5$_x$', r'5$_y$', r'm3$_x$',
r'm3$_y$', r'M3$_x$', r'M3$_y$'][int(x)]
[docs]def cmap(data, robust=True, cmap_seq='magma', cmap_bool='gray_r', cmap_div='coolwarm'):
'''Get a default colormap from the given data.
If the data is boolean, use a black and white colormap.
If the data has both positive and negative values,
use a diverging colormap.
Otherwise, use a sequential colormap.
Parameters
----------
data : np.ndarray
Input data
robust : bool
If True, discard the top and bottom 2% of data when calculating
range.
cmap_seq : str
The sequential colormap name
cmap_bool : str
The boolean colormap name
cmap_div : str
The diverging colormap name
Returns
-------
cmap : matplotlib.colors.Colormap
The colormap to use for `data`
See Also
--------
matplotlib.pyplot.colormaps
'''
data = np.atleast_1d(data)
if data.dtype == 'bool':
return get_cmap(cmap_bool)
data = data[np.isfinite(data)]
if robust:
min_p, max_p = 2, 98
else:
min_p, max_p = 0, 100
max_val = np.percentile(data, max_p)
min_val = np.percentile(data, min_p)
if min_val >= 0 or max_val <= 0:
return get_cmap(cmap_seq)
return get_cmap(cmap_div)
def __envelope(x, hop):
'''Compute the max-envelope of non-overlapping frames of x at length hop
x is assumed to be multi-channel, of shape (n_channels, n_samples).
'''
x_frame = np.abs(util.frame(x, frame_length=hop, hop_length=hop))
return x_frame.max(axis=1)
[docs]def waveplot(y, sr=22050, max_points=5e4, x_axis='time', offset=0.0,
max_sr=1000, ax=None, **kwargs):
'''Plot the amplitude envelope of a waveform.
If `y` is monophonic, a filled curve is drawn between `[-abs(y), abs(y)]`.
If `y` is stereo, the curve is drawn between `[-abs(y[1]), abs(y[0])]`,
so that the left and right channels are drawn above and below the axis,
respectively.
Long signals (`duration >= max_points`) are down-sampled to at
most `max_sr` before plotting.
Parameters
----------
y : np.ndarray [shape=(n,) or (2,n)]
audio time series (mono or stereo)
sr : number > 0 [scalar]
sampling rate of `y`
max_points : postive number or None
Maximum number of time-points to plot: if `max_points` exceeds
the duration of `y`, then `y` is downsampled.
If `None`, no downsampling is performed.
x_axis : str or None
Display of the x-axis ticks and tick markers. Accepted values are:
- 'time' : markers are shown as milliseconds, seconds, minutes, or hours.
Values are plotted in units of seconds.
- 's' : markers are shown as seconds.
- 'ms' : markers are shown as milliseconds.
- 'lag' : like time, but past the halfway point counts as negative values.
- 'lag_s' : same as lag, but in seconds.
- 'lag_ms' : same as lag, but in milliseconds.
- `None`, 'none', or 'off': ticks and tick markers are hidden.
ax : matplotlib.axes.Axes or None
Axes to plot on instead of the default `plt.gca()`.
offset : float
Horizontal offset (in seconds) to start the waveform plot
max_sr : number > 0 [scalar]
Maximum sampling rate for the visualization
kwargs
Additional keyword arguments to `matplotlib.pyplot.fill_between`
Returns
-------
pc : matplotlib.collections.PolyCollection
The PolyCollection created by `fill_between`.
See also
--------
librosa.core.resample
matplotlib.pyplot.fill_between
Examples
--------
Plot a monophonic waveform
>>> import matplotlib.pyplot as plt
>>> y, sr = librosa.load(librosa.util.example_audio_file(), duration=10)
>>> plt.figure()
>>> plt.subplot(3, 1, 1)
>>> librosa.display.waveplot(y, sr=sr)
>>> plt.title('Monophonic')
Or a stereo waveform
>>> y, sr = librosa.load(librosa.util.example_audio_file(),
... mono=False, duration=10)
>>> plt.subplot(3, 1, 2)
>>> librosa.display.waveplot(y, sr=sr)
>>> plt.title('Stereo')
Or harmonic and percussive components with transparency
>>> y, sr = librosa.load(librosa.util.example_audio_file(), duration=10)
>>> y_harm, y_perc = librosa.effects.hpss(y)
>>> plt.subplot(3, 1, 3)
>>> librosa.display.waveplot(y_harm, sr=sr, alpha=0.25)
>>> librosa.display.waveplot(y_perc, sr=sr, color='r', alpha=0.5)
>>> plt.title('Harmonic + Percussive')
>>> plt.tight_layout()
>>> plt.show()
'''
util.valid_audio(y, mono=False)
if not (isinstance(max_sr, int) and max_sr > 0):
raise ParameterError('max_sr must be a non-negative integer')
target_sr = sr
hop_length = 1
# Pad an extra channel dimension, if necessary
if y.ndim == 1:
y = y[np.newaxis, :]
if max_points is not None:
if max_points <= 0:
raise ParameterError('max_points must be strictly positive')
if max_points < y.shape[-1]:
target_sr = min(max_sr, (sr * y.shape[-1]) // max_points)
hop_length = sr // target_sr
# Reduce by envelope calculation
y = __envelope(y, hop_length)
y_top = y[0]
y_bottom = -y[-1]
axes = __check_axes(ax)
kwargs.setdefault('color', next(axes._get_lines.prop_cycler)['color'])
locs = offset + core.times_like(y_top, sr=sr, hop_length=hop_length)
out = axes.fill_between(locs, y_bottom, y_top, **kwargs)
axes.set_xlim([locs.min(), locs.max()])
# Construct tickers and locators
__decorate_axis(axes.xaxis, x_axis)
return out
[docs]def specshow(data, x_coords=None, y_coords=None,
x_axis=None, y_axis=None,
sr=22050, hop_length=512,
fmin=None, fmax=None,
tuning=0.0,
bins_per_octave=12,
ax=None,
**kwargs):
'''Display a spectrogram/chromagram/cqt/etc.
Parameters
----------
data : np.ndarray [shape=(d, n)]
Matrix to display (e.g., spectrogram)
sr : number > 0 [scalar]
Sample rate used to determine time scale in x-axis.
hop_length : int > 0 [scalar]
Hop length, also used to determine time scale in x-axis
x_axis : None or str
y_axis : None or str
Range for the x- and y-axes.
Valid types are:
- None, 'none', or 'off' : no axis decoration is displayed.
Frequency types:
- 'linear', 'fft', 'hz' : frequency range is determined by
the FFT window and sampling rate.
- 'log' : the spectrum is displayed on a log scale.
- 'mel' : frequencies are determined by the mel scale.
- 'cqt_hz' : frequencies are determined by the CQT scale.
- 'cqt_note' : pitches are determined by the CQT scale.
All frequency types are plotted in units of Hz.
Categorical types:
- 'chroma' : pitches are determined by the chroma filters.
Pitch classes are arranged at integer locations (0-11).
- 'tonnetz' : axes are labeled by Tonnetz dimensions (0-5)
- 'frames' : markers are shown as frame counts.
Time types:
- 'time' : markers are shown as milliseconds, seconds, minutes, or hours.
Values are plotted in units of seconds.
- 's' : markers are shown as seconds.
- 'ms' : markers are shown as milliseconds.
- 'lag' : like time, but past the halfway point counts as negative values.
- 'lag_s' : same as lag, but in seconds.
- 'lag_ms' : same as lag, but in milliseconds.
Rhythm:
- 'tempo' : markers are shown as beats-per-minute (BPM)
using a logarithmic scale. This is useful for
visualizing the outputs of `feature.tempogram`.
- 'fourier_tempo' : same as `'tempo'`, but used when
tempograms are calculated in the Frequency domain
using `feature.fourier_tempogram`.
x_coords : np.ndarray [shape=data.shape[1]+1]
y_coords : np.ndarray [shape=data.shape[0]+1]
Optional positioning coordinates of the input data.
These can be use to explicitly set the location of each
element `data[i, j]`, e.g., for displaying beat-synchronous
features in natural time coordinates.
If not provided, they are inferred from `x_axis` and `y_axis`.
fmin : float > 0 [scalar] or None
Frequency of the lowest spectrogram bin. Used for Mel and CQT
scales.
If `y_axis` is `cqt_hz` or `cqt_note` and `fmin` is not given,
it is set by default to `note_to_hz('C1')`.
fmax : float > 0 [scalar] or None
Used for setting the Mel frequency scales
tuning : float
Tuning deviation from A440, in fractions of a bin.
This is used for CQT frequency scales, so that `fmin` is adjusted
to `fmin * 2**(tuning / bins_per_octave)`.
bins_per_octave : int > 0 [scalar]
Number of bins per octave. Used for CQT frequency scale.
ax : matplotlib.axes.Axes or None
Axes to plot on instead of the default `plt.gca()`.
kwargs : additional keyword arguments
Arguments passed through to `matplotlib.pyplot.pcolormesh`.
By default, the following options are set:
- `rasterized=True`
- `shading='flat'`
- `edgecolors='None'`
Returns
-------
axes
The axis handle for the figure.
See Also
--------
cmap : Automatic colormap detection
matplotlib.pyplot.pcolormesh
Examples
--------
Visualize an STFT power spectrum
>>> import matplotlib.pyplot as plt
>>> y, sr = librosa.load(librosa.util.example_audio_file())
>>> plt.figure(figsize=(12, 8))
>>> D = librosa.amplitude_to_db(np.abs(librosa.stft(y)), ref=np.max)
>>> plt.subplot(4, 2, 1)
>>> librosa.display.specshow(D, y_axis='linear')
>>> plt.colorbar(format='%+2.0f dB')
>>> plt.title('Linear-frequency power spectrogram')
Or on a logarithmic scale
>>> plt.subplot(4, 2, 2)
>>> librosa.display.specshow(D, y_axis='log')
>>> plt.colorbar(format='%+2.0f dB')
>>> plt.title('Log-frequency power spectrogram')
Or use a CQT scale
>>> CQT = librosa.amplitude_to_db(np.abs(librosa.cqt(y, sr=sr)), ref=np.max)
>>> plt.subplot(4, 2, 3)
>>> librosa.display.specshow(CQT, y_axis='cqt_note')
>>> plt.colorbar(format='%+2.0f dB')
>>> plt.title('Constant-Q power spectrogram (note)')
>>> plt.subplot(4, 2, 4)
>>> librosa.display.specshow(CQT, y_axis='cqt_hz')
>>> plt.colorbar(format='%+2.0f dB')
>>> plt.title('Constant-Q power spectrogram (Hz)')
Draw a chromagram with pitch classes
>>> C = librosa.feature.chroma_cqt(y=y, sr=sr)
>>> plt.subplot(4, 2, 5)
>>> librosa.display.specshow(C, y_axis='chroma')
>>> plt.colorbar()
>>> plt.title('Chromagram')
Force a grayscale colormap (white -> black)
>>> plt.subplot(4, 2, 6)
>>> librosa.display.specshow(D, cmap='gray_r', y_axis='linear')
>>> plt.colorbar(format='%+2.0f dB')
>>> plt.title('Linear power spectrogram (grayscale)')
Draw time markers automatically
>>> plt.subplot(4, 2, 7)
>>> librosa.display.specshow(D, x_axis='time', y_axis='log')
>>> plt.colorbar(format='%+2.0f dB')
>>> plt.title('Log power spectrogram')
Draw a tempogram with BPM markers
>>> plt.subplot(4, 2, 8)
>>> Tgram = librosa.feature.tempogram(y=y, sr=sr)
>>> librosa.display.specshow(Tgram, x_axis='time', y_axis='tempo')
>>> plt.colorbar()
>>> plt.title('Tempogram')
>>> plt.tight_layout()
>>> plt.show()
Draw beat-synchronous chroma in natural time
>>> plt.figure()
>>> tempo, beat_f = librosa.beat.beat_track(y=y, sr=sr, trim=False)
>>> beat_f = librosa.util.fix_frames(beat_f, x_max=C.shape[1])
>>> Csync = librosa.util.sync(C, beat_f, aggregate=np.median)
>>> beat_t = librosa.frames_to_time(beat_f, sr=sr)
>>> ax1 = plt.subplot(2,1,1)
>>> librosa.display.specshow(C, y_axis='chroma', x_axis='time')
>>> plt.title('Chroma (linear time)')
>>> ax2 = plt.subplot(2,1,2, sharex=ax1)
>>> librosa.display.specshow(Csync, y_axis='chroma', x_axis='time',
... x_coords=beat_t)
>>> plt.title('Chroma (beat time)')
>>> plt.tight_layout()
>>> plt.show()
'''
if np.issubdtype(data.dtype, np.complexfloating):
warnings.warn('Trying to display complex-valued input. '
'Showing magnitude instead.')
data = np.abs(data)
kwargs.setdefault('cmap', cmap(data))
kwargs.setdefault('rasterized', True)
kwargs.setdefault('edgecolors', 'None')
kwargs.setdefault('shading', 'flat')
all_params = dict(kwargs=kwargs,
sr=sr,
fmin=fmin,
fmax=fmax,
tuning=tuning,
bins_per_octave=bins_per_octave,
hop_length=hop_length)
# Get the x and y coordinates
y_coords = __mesh_coords(y_axis, y_coords, data.shape[0], **all_params)
x_coords = __mesh_coords(x_axis, x_coords, data.shape[1], **all_params)
axes = __check_axes(ax)
out = axes.pcolormesh(x_coords, y_coords, data, **kwargs)
__set_current_image(ax, out)
axes.set_xlim(x_coords.min(), x_coords.max())
axes.set_ylim(y_coords.min(), y_coords.max())
# Set up axis scaling
__scale_axes(axes, x_axis, 'x')
__scale_axes(axes, y_axis, 'y')
# Construct tickers and locators
__decorate_axis(axes.xaxis, x_axis)
__decorate_axis(axes.yaxis, y_axis)
return axes
def __set_current_image(ax, img):
'''Helper to set the current image in pyplot mode.
If the provided `ax` is not `None`, then we assume that the user is using the object API.
In this case, the pyplot current image is not set.
'''
if ax is None:
import matplotlib.pyplot as plt
plt.sci(img)
def __mesh_coords(ax_type, coords, n, **kwargs):
'''Compute axis coordinates'''
if coords is not None:
if len(coords) < n:
raise ParameterError('Coordinate shape mismatch: '
'{}<{}'.format(len(coords), n))
return coords
coord_map = {'linear': __coord_fft_hz,
'hz': __coord_fft_hz,
'log': __coord_fft_hz,
'mel': __coord_mel_hz,
'cqt': __coord_cqt_hz,
'cqt_hz': __coord_cqt_hz,
'cqt_note': __coord_cqt_hz,
'chroma': __coord_chroma,
'time': __coord_time,
's': __coord_time,
'ms': __coord_time,
'lag': __coord_time,
'lag_s': __coord_time,
'lag_ms': __coord_time,
'tonnetz': __coord_n,
'off': __coord_n,
'tempo': __coord_tempo,
'fourier_tempo': __coord_fourier_tempo,
'frames': __coord_n,
None: __coord_n}
if ax_type not in coord_map:
raise ParameterError('Unknown axis type: {}'.format(ax_type))
return coord_map[ax_type](n, **kwargs)
def __check_axes(axes):
'''Check if "axes" is an instance of an axis object. If not, use `gca`.'''
if axes is None:
import matplotlib.pyplot as plt
axes = plt.gca()
elif not isinstance(axes, Axes):
raise ValueError("`axes` must be an instance of matplotlib.axes.Axes. "
"Found type(axes)={}".format(type(axes)))
return axes
def __scale_axes(axes, ax_type, which):
'''Set the axis scaling'''
kwargs = dict()
if which == 'x':
thresh = 'linthreshx'
base = 'basex'
scale = 'linscalex'
scaler = axes.set_xscale
limit = axes.set_xlim
else:
thresh = 'linthreshy'
base = 'basey'
scale = 'linscaley'
scaler = axes.set_yscale
limit = axes.set_ylim
# Map ticker scales
if ax_type == 'mel':
mode = 'symlog'
kwargs[thresh] = 1000.0
kwargs[base] = 2
elif ax_type == 'log':
mode = 'symlog'
kwargs[base] = 2
kwargs[thresh] = core.note_to_hz('C2')
kwargs[scale] = 0.5
elif ax_type in ['cqt', 'cqt_hz', 'cqt_note']:
mode = 'log'
kwargs[base] = 2
elif ax_type in ['tempo', 'fourier_tempo']:
mode = 'log'
kwargs[base] = 2
limit(16, 480)
else:
return
scaler(mode, **kwargs)
def __decorate_axis(axis, ax_type):
'''Configure axis tickers, locators, and labels'''
if ax_type == 'tonnetz':
axis.set_major_formatter(TonnetzFormatter())
axis.set_major_locator(FixedLocator(0.5 + np.arange(6)))
axis.set_label_text('Tonnetz')
elif ax_type == 'chroma':
axis.set_major_formatter(ChromaFormatter())
axis.set_major_locator(FixedLocator(0.5 +
np.add.outer(12 * np.arange(10),
[0, 2, 4, 5, 7, 9, 11]).ravel()))
axis.set_label_text('Pitch class')
elif ax_type in ['tempo', 'fourier_tempo']:
axis.set_major_formatter(ScalarFormatter())
axis.set_major_locator(LogLocator(base=2.0))
axis.set_label_text('BPM')
elif ax_type == 'time':
axis.set_major_formatter(TimeFormatter(unit=None, lag=False))
axis.set_major_locator(MaxNLocator(prune=None,
steps=[1, 1.5, 5, 6, 10]))
axis.set_label_text('Time')
elif ax_type == 's':
axis.set_major_formatter(TimeFormatter(unit='s', lag=False))
axis.set_major_locator(MaxNLocator(prune=None,
steps=[1, 1.5, 5, 6, 10]))
axis.set_label_text('Time (s)')
elif ax_type == 'ms':
axis.set_major_formatter(TimeFormatter(unit='ms', lag=False))
axis.set_major_locator(MaxNLocator(prune=None,
steps=[1, 1.5, 5, 6, 10]))
axis.set_label_text('Time (ms)')
elif ax_type == 'lag':
axis.set_major_formatter(TimeFormatter(unit=None, lag=True))
axis.set_major_locator(MaxNLocator(prune=None,
steps=[1, 1.5, 5, 6, 10]))
axis.set_label_text('Lag')
elif ax_type == 'lag_s':
axis.set_major_formatter(TimeFormatter(unit='s', lag=True))
axis.set_major_locator(MaxNLocator(prune=None,
steps=[1, 1.5, 5, 6, 10]))
axis.set_label_text('Lag (s)')
elif ax_type == 'lag_ms':
axis.set_major_formatter(TimeFormatter(unit='ms', lag=True))
axis.set_major_locator(MaxNLocator(prune=None,
steps=[1, 1.5, 5, 6, 10]))
axis.set_label_text('Lag (ms)')
elif ax_type == 'cqt_note':
axis.set_major_formatter(NoteFormatter())
axis.set_major_locator(LogLocator(base=2.0))
axis.set_minor_formatter(NoteFormatter(major=False))
axis.set_minor_locator(LogLocator(base=2.0,
subs=2.0**(np.arange(1, 12)/12.0)))
axis.set_label_text('Note')
elif ax_type in ['cqt_hz']:
axis.set_major_formatter(LogHzFormatter())
axis.set_major_locator(LogLocator(base=2.0))
axis.set_minor_formatter(LogHzFormatter(major=False))
axis.set_minor_locator(LogLocator(base=2.0,
subs=2.0**(np.arange(1, 12)/12.0)))
axis.set_label_text('Hz')
elif ax_type in ['mel', 'log']:
axis.set_major_formatter(ScalarFormatter())
axis.set_major_locator(SymmetricalLogLocator(axis.get_transform()))
axis.set_label_text('Hz')
elif ax_type in ['linear', 'hz']:
axis.set_major_formatter(ScalarFormatter())
axis.set_label_text('Hz')
elif ax_type in ['frames']:
axis.set_label_text('Frames')
elif ax_type in ['off', 'none', None]:
axis.set_label_text('')
axis.set_ticks([])
def __coord_fft_hz(n, sr=22050, **_kwargs):
'''Get the frequencies for FFT bins'''
n_fft = 2 * (n - 1)
# The following code centers the FFT bins at their frequencies
# and clips to the non-negative frequency range [0, nyquist]
basis = core.fft_frequencies(sr=sr, n_fft=n_fft)
fmax = basis[-1]
basis -= 0.5 * (basis[1] - basis[0])
basis = np.append(np.maximum(0, basis), [fmax])
return basis
def __coord_mel_hz(n, fmin=0, fmax=11025.0, **_kwargs):
'''Get the frequencies for Mel bins'''
if fmin is None:
fmin = 0
if fmax is None:
fmax = 11025.0
basis = core.mel_frequencies(n, fmin=fmin, fmax=fmax)
basis[1:] -= 0.5 * np.diff(basis)
basis = np.append(np.maximum(0, basis), [fmax])
return basis
def __coord_cqt_hz(n, fmin=None, bins_per_octave=12, **_kwargs):
'''Get CQT bin frequencies'''
if fmin is None:
fmin = core.note_to_hz('C1')
# Apply tuning correction
fmin = fmin * 2.0**(_kwargs.get('tuning', 0.0) / bins_per_octave)
# we drop by half a bin so that CQT bins are centered vertically
return core.cqt_frequencies(n+1,
fmin=fmin / 2.0**(0.5/bins_per_octave),
bins_per_octave=bins_per_octave)
def __coord_chroma(n, bins_per_octave=12, **_kwargs):
'''Get chroma bin numbers'''
return np.linspace(0, (12.0 * n) / bins_per_octave, num=n+1, endpoint=True)
def __coord_tempo(n, sr=22050, hop_length=512, **_kwargs):
'''Tempo coordinates'''
basis = core.tempo_frequencies(n+2, sr=sr, hop_length=hop_length)[1:]
edges = np.arange(1, n+2)
return basis * (edges + 0.5) / edges
def __coord_fourier_tempo(n, sr=22050, hop_length=512, **_kwargs):
'''Fourier tempogram coordinates'''
n_fft = 2 * (n - 1)
# The following code centers the FFT bins at their frequencies
# and clips to the non-negative frequency range [0, nyquist]
basis = core.fourier_tempo_frequencies(sr=sr,
hop_length=hop_length,
win_length=n_fft)
fmax = basis[-1]
basis -= 0.5 * (basis[1] - basis[0])
basis = np.append(np.maximum(0, basis), [fmax])
return basis
def __coord_n(n, **_kwargs):
'''Get bare positions'''
return np.arange(n+1)
def __coord_time(n, sr=22050, hop_length=512, **_kwargs):
'''Get time coordinates from frames'''
return core.frames_to_time(np.arange(n+1), sr=sr, hop_length=hop_length)