|
|
|
|
|
|
|
|
"""Unit conversion utilities""" |
|
|
|
|
|
import re |
|
|
import numpy as np |
|
|
from . import notation |
|
|
from ..util.exceptions import ParameterError |
|
|
from ..util.decorators import deprecate_positional_args |
|
|
|
|
|
__all__ = [ |
|
|
"frames_to_samples", |
|
|
"frames_to_time", |
|
|
"samples_to_frames", |
|
|
"samples_to_time", |
|
|
"time_to_samples", |
|
|
"time_to_frames", |
|
|
"blocks_to_samples", |
|
|
"blocks_to_frames", |
|
|
"blocks_to_time", |
|
|
"note_to_hz", |
|
|
"note_to_midi", |
|
|
"midi_to_hz", |
|
|
"midi_to_note", |
|
|
"hz_to_note", |
|
|
"hz_to_midi", |
|
|
"hz_to_mel", |
|
|
"hz_to_octs", |
|
|
"mel_to_hz", |
|
|
"octs_to_hz", |
|
|
"A4_to_tuning", |
|
|
"tuning_to_A4", |
|
|
"fft_frequencies", |
|
|
"cqt_frequencies", |
|
|
"mel_frequencies", |
|
|
"tempo_frequencies", |
|
|
"fourier_tempo_frequencies", |
|
|
"A_weighting", |
|
|
"B_weighting", |
|
|
"C_weighting", |
|
|
"D_weighting", |
|
|
"Z_weighting", |
|
|
"frequency_weighting", |
|
|
"multi_frequency_weighting", |
|
|
"samples_like", |
|
|
"times_like", |
|
|
"midi_to_svara_h", |
|
|
"midi_to_svara_c", |
|
|
"note_to_svara_h", |
|
|
"note_to_svara_c", |
|
|
"hz_to_svara_h", |
|
|
"hz_to_svara_c", |
|
|
] |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def frames_to_samples(frames, *, hop_length=512, n_fft=None): |
|
|
"""Converts frame indices to audio sample indices. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frames : number or np.ndarray [shape=(n,)] |
|
|
frame index or vector of frame indices |
|
|
hop_length : int > 0 [scalar] |
|
|
number of samples between successive frames |
|
|
n_fft : None or int > 0 [scalar] |
|
|
Optional: length of the FFT window. |
|
|
If given, time conversion will include an offset of ``n_fft // 2`` |
|
|
to counteract windowing effects when using a non-centered STFT. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
times : number or np.ndarray |
|
|
time (in samples) of each given frame number:: |
|
|
|
|
|
times[i] = frames[i] * hop_length |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
frames_to_time : convert frame indices to time values |
|
|
samples_to_frames : convert sample indices to frame indices |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> y, sr = librosa.load(librosa.ex('choice')) |
|
|
>>> tempo, beats = librosa.beat.beat_track(y=y, sr=sr) |
|
|
>>> beat_samples = librosa.frames_to_samples(beats) |
|
|
""" |
|
|
|
|
|
offset = 0 |
|
|
if n_fft is not None: |
|
|
offset = int(n_fft // 2) |
|
|
|
|
|
return (np.asanyarray(frames) * hop_length + offset).astype(int) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def samples_to_frames(samples, *, hop_length=512, n_fft=None): |
|
|
"""Converts sample indices into STFT frames. |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> # Get the frame numbers for every 256 samples |
|
|
>>> librosa.samples_to_frames(np.arange(0, 22050, 256)) |
|
|
array([ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, |
|
|
7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, |
|
|
14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, |
|
|
21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, |
|
|
28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, |
|
|
35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, |
|
|
42, 42, 43]) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
samples : int or np.ndarray [shape=(n,)] |
|
|
sample index or vector of sample indices |
|
|
|
|
|
hop_length : int > 0 [scalar] |
|
|
number of samples between successive frames |
|
|
|
|
|
n_fft : None or int > 0 [scalar] |
|
|
Optional: length of the FFT window. |
|
|
If given, time conversion will include an offset of ``- n_fft // 2`` |
|
|
to counteract windowing effects in STFT. |
|
|
|
|
|
.. note:: This may result in negative frame indices. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
frames : int or np.ndarray [shape=(n,), dtype=int] |
|
|
Frame numbers corresponding to the given times:: |
|
|
|
|
|
frames[i] = floor( samples[i] / hop_length ) |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
samples_to_time : convert sample indices to time values |
|
|
frames_to_samples : convert frame indices to sample indices |
|
|
""" |
|
|
|
|
|
offset = 0 |
|
|
if n_fft is not None: |
|
|
offset = int(n_fft // 2) |
|
|
|
|
|
samples = np.asanyarray(samples) |
|
|
return np.floor((samples - offset) // hop_length).astype(int) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def frames_to_time(frames, *, sr=22050, hop_length=512, n_fft=None): |
|
|
"""Converts frame counts to time (seconds). |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frames : np.ndarray [shape=(n,)] |
|
|
frame index or vector of frame indices |
|
|
sr : number > 0 [scalar] |
|
|
audio sampling rate |
|
|
hop_length : int > 0 [scalar] |
|
|
number of samples between successive frames |
|
|
n_fft : None or int > 0 [scalar] |
|
|
Optional: length of the FFT window. |
|
|
If given, time conversion will include an offset of ``n_fft // 2`` |
|
|
to counteract windowing effects when using a non-centered STFT. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
times : np.ndarray [shape=(n,)] |
|
|
time (in seconds) of each given frame number:: |
|
|
|
|
|
times[i] = frames[i] * hop_length / sr |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
time_to_frames : convert time values to frame indices |
|
|
frames_to_samples : convert frame indices to sample indices |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> y, sr = librosa.load(librosa.ex('choice')) |
|
|
>>> tempo, beats = librosa.beat.beat_track(y=y, sr=sr) |
|
|
>>> beat_times = librosa.frames_to_time(beats, sr=sr) |
|
|
""" |
|
|
|
|
|
samples = frames_to_samples(frames, hop_length=hop_length, n_fft=n_fft) |
|
|
|
|
|
return samples_to_time(samples, sr=sr) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def time_to_frames(times, *, sr=22050, hop_length=512, n_fft=None): |
|
|
"""Converts time stamps into STFT frames. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
times : np.ndarray [shape=(n,)] |
|
|
time (in seconds) or vector of time values |
|
|
|
|
|
sr : number > 0 [scalar] |
|
|
audio sampling rate |
|
|
|
|
|
hop_length : int > 0 [scalar] |
|
|
number of samples between successive frames |
|
|
|
|
|
n_fft : None or int > 0 [scalar] |
|
|
Optional: length of the FFT window. |
|
|
If given, time conversion will include an offset of ``- n_fft // 2`` |
|
|
to counteract windowing effects in STFT. |
|
|
|
|
|
.. note:: This may result in negative frame indices. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
frames : np.ndarray [shape=(n,), dtype=int] |
|
|
Frame numbers corresponding to the given times:: |
|
|
|
|
|
frames[i] = floor( times[i] * sr / hop_length ) |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
frames_to_time : convert frame indices to time values |
|
|
time_to_samples : convert time values to sample indices |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get the frame numbers for every 100ms |
|
|
|
|
|
>>> librosa.time_to_frames(np.arange(0, 1, 0.1), |
|
|
... sr=22050, hop_length=512) |
|
|
array([ 0, 4, 8, 12, 17, 21, 25, 30, 34, 38]) |
|
|
|
|
|
""" |
|
|
|
|
|
samples = time_to_samples(times, sr=sr) |
|
|
|
|
|
return samples_to_frames(samples, hop_length=hop_length, n_fft=n_fft) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def time_to_samples(times, *, sr=22050): |
|
|
"""Convert timestamps (in seconds) to sample indices. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
times : number or np.ndarray |
|
|
Time value or array of time values (in seconds) |
|
|
sr : number > 0 |
|
|
Sampling rate |
|
|
|
|
|
Returns |
|
|
------- |
|
|
samples : int or np.ndarray [shape=times.shape, dtype=int] |
|
|
Sample indices corresponding to values in ``times`` |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
time_to_frames : convert time values to frame indices |
|
|
samples_to_time : convert sample indices to time values |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.time_to_samples(np.arange(0, 1, 0.1), sr=22050) |
|
|
array([ 0, 2205, 4410, 6615, 8820, 11025, 13230, 15435, |
|
|
17640, 19845]) |
|
|
|
|
|
""" |
|
|
|
|
|
return (np.asanyarray(times) * sr).astype(int) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def samples_to_time(samples, *, sr=22050): |
|
|
"""Convert sample indices to time (in seconds). |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
samples : np.ndarray |
|
|
Sample index or array of sample indices |
|
|
sr : number > 0 |
|
|
Sampling rate |
|
|
|
|
|
Returns |
|
|
------- |
|
|
times : np.ndarray [shape=samples.shape] |
|
|
Time values corresponding to ``samples`` (in seconds) |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
samples_to_frames : convert sample indices to frame indices |
|
|
time_to_samples : convert time values to sample indices |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get timestamps corresponding to every 512 samples |
|
|
|
|
|
>>> librosa.samples_to_time(np.arange(0, 22050, 512)) |
|
|
array([ 0. , 0.023, 0.046, 0.07 , 0.093, 0.116, 0.139, |
|
|
0.163, 0.186, 0.209, 0.232, 0.255, 0.279, 0.302, |
|
|
0.325, 0.348, 0.372, 0.395, 0.418, 0.441, 0.464, |
|
|
0.488, 0.511, 0.534, 0.557, 0.58 , 0.604, 0.627, |
|
|
0.65 , 0.673, 0.697, 0.72 , 0.743, 0.766, 0.789, |
|
|
0.813, 0.836, 0.859, 0.882, 0.906, 0.929, 0.952, |
|
|
0.975, 0.998]) |
|
|
""" |
|
|
|
|
|
return np.asanyarray(samples) / float(sr) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def blocks_to_frames(blocks, *, block_length): |
|
|
"""Convert block indices to frame indices |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
blocks : np.ndarray |
|
|
Block index or array of block indices |
|
|
block_length : int > 0 |
|
|
The number of frames per block |
|
|
|
|
|
Returns |
|
|
------- |
|
|
frames : np.ndarray [shape=samples.shape, dtype=int] |
|
|
The index or indices of frames corresponding to the beginning |
|
|
of each provided block. |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
blocks_to_samples |
|
|
blocks_to_time |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get frame indices for each block in a stream |
|
|
|
|
|
>>> filename = librosa.ex('brahms') |
|
|
>>> sr = librosa.get_samplerate(filename) |
|
|
>>> stream = librosa.stream(filename, block_length=16, |
|
|
... frame_length=2048, hop_length=512) |
|
|
>>> for n, y in enumerate(stream): |
|
|
... n_frame = librosa.blocks_to_frames(n, block_length=16) |
|
|
|
|
|
""" |
|
|
return block_length * np.asanyarray(blocks) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def blocks_to_samples(blocks, *, block_length, hop_length): |
|
|
"""Convert block indices to sample indices |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
blocks : np.ndarray |
|
|
Block index or array of block indices |
|
|
block_length : int > 0 |
|
|
The number of frames per block |
|
|
hop_length : int > 0 |
|
|
The number of samples to advance between frames |
|
|
|
|
|
Returns |
|
|
------- |
|
|
samples : np.ndarray [shape=samples.shape, dtype=int] |
|
|
The index or indices of samples corresponding to the beginning |
|
|
of each provided block. |
|
|
|
|
|
Note that these correspond to the *first* sample index in |
|
|
each block, and are not frame-centered. |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
blocks_to_frames |
|
|
blocks_to_time |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get sample indices for each block in a stream |
|
|
|
|
|
>>> filename = librosa.ex('brahms') |
|
|
>>> sr = librosa.get_samplerate(filename) |
|
|
>>> stream = librosa.stream(filename, block_length=16, |
|
|
... frame_length=2048, hop_length=512) |
|
|
>>> for n, y in enumerate(stream): |
|
|
... n_sample = librosa.blocks_to_samples(n, block_length=16, |
|
|
... hop_length=512) |
|
|
|
|
|
""" |
|
|
frames = blocks_to_frames(blocks, block_length=block_length) |
|
|
return frames_to_samples(frames, hop_length=hop_length) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def blocks_to_time(blocks, *, block_length, hop_length, sr): |
|
|
"""Convert block indices to time (in seconds) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
blocks : np.ndarray |
|
|
Block index or array of block indices |
|
|
block_length : int > 0 |
|
|
The number of frames per block |
|
|
hop_length : int > 0 |
|
|
The number of samples to advance between frames |
|
|
sr : int > 0 |
|
|
The sampling rate (samples per second) |
|
|
|
|
|
Returns |
|
|
------- |
|
|
times : np.ndarray [shape=samples.shape] |
|
|
The time index or indices (in seconds) corresponding to the |
|
|
beginning of each provided block. |
|
|
|
|
|
Note that these correspond to the time of the *first* sample |
|
|
in each block, and are not frame-centered. |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
blocks_to_frames |
|
|
blocks_to_samples |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get time indices for each block in a stream |
|
|
|
|
|
>>> filename = librosa.ex('brahms') |
|
|
>>> sr = librosa.get_samplerate(filename) |
|
|
>>> stream = librosa.stream(filename, block_length=16, |
|
|
... frame_length=2048, hop_length=512) |
|
|
>>> for n, y in enumerate(stream): |
|
|
... n_time = librosa.blocks_to_time(n, block_length=16, |
|
|
... hop_length=512, sr=sr) |
|
|
|
|
|
""" |
|
|
samples = blocks_to_samples( |
|
|
blocks, block_length=block_length, hop_length=hop_length |
|
|
) |
|
|
return samples_to_time(samples, sr=sr) |
|
|
|
|
|
|
|
|
def note_to_hz(note, **kwargs): |
|
|
"""Convert one or more note names to frequency (Hz) |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> # Get the frequency of a note |
|
|
>>> librosa.note_to_hz('C') |
|
|
array([ 16.352]) |
|
|
>>> # Or multiple notes |
|
|
>>> librosa.note_to_hz(['A3', 'A4', 'A5']) |
|
|
array([ 220., 440., 880.]) |
|
|
>>> # Or notes with tuning deviations |
|
|
>>> librosa.note_to_hz('C2-32', round_midi=False) |
|
|
array([ 64.209]) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
note : str or iterable of str |
|
|
One or more note names to convert |
|
|
**kwargs : additional keyword arguments |
|
|
Additional parameters to `note_to_midi` |
|
|
|
|
|
Returns |
|
|
------- |
|
|
frequencies : number or np.ndarray [shape=(len(note),)] |
|
|
Array of frequencies (in Hz) corresponding to ``note`` |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
midi_to_hz |
|
|
note_to_midi |
|
|
hz_to_note |
|
|
""" |
|
|
return midi_to_hz(note_to_midi(note, **kwargs)) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def note_to_midi(note, *, round_midi=True): |
|
|
"""Convert one or more spelled notes to MIDI number(s). |
|
|
|
|
|
Notes may be spelled out with optional accidentals or octave numbers. |
|
|
|
|
|
The leading note name is case-insensitive. |
|
|
|
|
|
Sharps are indicated with ``#``, flats may be indicated with ``!`` or ``b``. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
note : str or iterable of str |
|
|
One or more note names. |
|
|
round_midi : bool |
|
|
- If ``True``, allow for fractional midi notes |
|
|
- Otherwise, round cent deviations to the nearest note |
|
|
|
|
|
Returns |
|
|
------- |
|
|
midi : float or np.array |
|
|
Midi note numbers corresponding to inputs. |
|
|
|
|
|
Raises |
|
|
------ |
|
|
ParameterError |
|
|
If the input is not in valid note format |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
midi_to_note |
|
|
note_to_hz |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.note_to_midi('C') |
|
|
12 |
|
|
>>> librosa.note_to_midi('C#3') |
|
|
49 |
|
|
>>> librosa.note_to_midi('C♯3') # Using Unicode sharp |
|
|
49 |
|
|
>>> librosa.note_to_midi('C♭3') # Using Unicode flat |
|
|
47 |
|
|
>>> librosa.note_to_midi('f4') |
|
|
65 |
|
|
>>> librosa.note_to_midi('Bb-1') |
|
|
10 |
|
|
>>> librosa.note_to_midi('A!8') |
|
|
116 |
|
|
>>> librosa.note_to_midi('G𝄪6') # Double-sharp |
|
|
93 |
|
|
>>> librosa.note_to_midi('B𝄫6') # Double-flat |
|
|
93 |
|
|
>>> librosa.note_to_midi('C♭𝄫5') # Triple-flats also work |
|
|
69 |
|
|
>>> # Lists of notes also work |
|
|
>>> librosa.note_to_midi(['C', 'E', 'G']) |
|
|
array([12, 16, 19]) |
|
|
""" |
|
|
|
|
|
if not isinstance(note, str): |
|
|
return np.array([note_to_midi(n, round_midi=round_midi) for n in note]) |
|
|
|
|
|
pitch_map = {"C": 0, "D": 2, "E": 4, "F": 5, "G": 7, "A": 9, "B": 11} |
|
|
acc_map = { |
|
|
"#": 1, |
|
|
"": 0, |
|
|
"b": -1, |
|
|
"!": -1, |
|
|
"♯": 1, |
|
|
"𝄪": 2, |
|
|
"♭": -1, |
|
|
"𝄫": -2, |
|
|
"♮": 0, |
|
|
} |
|
|
|
|
|
match = re.match( |
|
|
r"^(?P<note>[A-Ga-g])" |
|
|
r"(?P<accidental>[#♯𝄪b!♭𝄫♮]*)" |
|
|
r"(?P<octave>[+-]?\d+)?" |
|
|
r"(?P<cents>[+-]\d+)?$", |
|
|
note, |
|
|
) |
|
|
if not match: |
|
|
raise ParameterError("Improper note format: {:s}".format(note)) |
|
|
|
|
|
pitch = match.group("note").upper() |
|
|
offset = np.sum([acc_map[o] for o in match.group("accidental")]) |
|
|
octave = match.group("octave") |
|
|
cents = match.group("cents") |
|
|
|
|
|
if not octave: |
|
|
octave = 0 |
|
|
else: |
|
|
octave = int(octave) |
|
|
|
|
|
if not cents: |
|
|
cents = 0 |
|
|
else: |
|
|
cents = int(cents) * 1e-2 |
|
|
|
|
|
note_value = 12 * (octave + 1) + pitch_map[pitch] + offset + cents |
|
|
|
|
|
if round_midi: |
|
|
note_value = int(np.round(note_value)) |
|
|
|
|
|
return note_value |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def midi_to_note(midi, *, octave=True, cents=False, key="C:maj", unicode=True): |
|
|
"""Convert one or more MIDI numbers to note strings. |
|
|
|
|
|
MIDI numbers will be rounded to the nearest integer. |
|
|
|
|
|
Notes will be of the format 'C0', 'C♯0', 'D0', ... |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.midi_to_note(0) |
|
|
'C-1' |
|
|
|
|
|
>>> librosa.midi_to_note(37) |
|
|
'C♯2' |
|
|
|
|
|
>>> librosa.midi_to_note(37, unicode=False) |
|
|
'C#2' |
|
|
|
|
|
>>> librosa.midi_to_note(-2) |
|
|
'A♯-2' |
|
|
|
|
|
>>> librosa.midi_to_note(104.7) |
|
|
'A7' |
|
|
|
|
|
>>> librosa.midi_to_note(104.7, cents=True) |
|
|
'A7-30' |
|
|
|
|
|
>>> librosa.midi_to_note(list(range(12, 24))) |
|
|
['C0', 'C♯0', 'D0', 'D♯0', 'E0', 'F0', 'F♯0', 'G0', 'G♯0', 'A0', 'A♯0', 'B0'] |
|
|
|
|
|
Use a key signature to resolve enharmonic equivalences |
|
|
|
|
|
>>> librosa.midi_to_note(range(12, 24), key='F:min') |
|
|
['C0', 'D♭0', 'D0', 'E♭0', 'E0', 'F0', 'G♭0', 'G0', 'A♭0', 'A0', 'B♭0', 'B0'] |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
midi : int or iterable of int |
|
|
Midi numbers to convert. |
|
|
|
|
|
octave : bool |
|
|
If True, include the octave number |
|
|
|
|
|
cents : bool |
|
|
If true, cent markers will be appended for fractional notes. |
|
|
Eg, ``midi_to_note(69.3, cents=True) == 'A4+03'`` |
|
|
|
|
|
key : str |
|
|
A key signature to use when resolving enharmonic equivalences. |
|
|
|
|
|
unicode : bool |
|
|
If ``True`` (default), accidentals will use Unicode notation: ♭ or ♯ |
|
|
|
|
|
If ``False``, accidentals will use ASCII-compatible notation: b or # |
|
|
|
|
|
Returns |
|
|
------- |
|
|
notes : str or iterable of str |
|
|
Strings describing each midi note. |
|
|
|
|
|
Raises |
|
|
------ |
|
|
ParameterError |
|
|
if ``cents`` is True and ``octave`` is False |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
midi_to_hz |
|
|
note_to_midi |
|
|
hz_to_note |
|
|
key_to_notes |
|
|
""" |
|
|
|
|
|
if cents and not octave: |
|
|
raise ParameterError("Cannot encode cents without octave information.") |
|
|
|
|
|
if not np.isscalar(midi): |
|
|
return [ |
|
|
midi_to_note(x, octave=octave, cents=cents, key=key, unicode=unicode) |
|
|
for x in midi |
|
|
] |
|
|
|
|
|
note_map = notation.key_to_notes(key=key, unicode=unicode) |
|
|
|
|
|
note_num = int(np.round(midi)) |
|
|
note_cents = int(100 * np.around(midi - note_num, 2)) |
|
|
|
|
|
note = note_map[note_num % 12] |
|
|
|
|
|
if octave: |
|
|
note = "{:s}{:0d}".format(note, int(note_num / 12) - 1) |
|
|
if cents: |
|
|
note = "{:s}{:+02d}".format(note, note_cents) |
|
|
|
|
|
return note |
|
|
|
|
|
|
|
|
def midi_to_hz(notes): |
|
|
"""Get the frequency (Hz) of MIDI note(s) |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.midi_to_hz(36) |
|
|
65.406 |
|
|
|
|
|
>>> librosa.midi_to_hz(np.arange(36, 48)) |
|
|
array([ 65.406, 69.296, 73.416, 77.782, 82.407, |
|
|
87.307, 92.499, 97.999, 103.826, 110. , |
|
|
116.541, 123.471]) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
notes : int or np.ndarray [shape=(n,), dtype=int] |
|
|
midi number(s) of the note(s) |
|
|
|
|
|
Returns |
|
|
------- |
|
|
frequency : number or np.ndarray [shape=(n,), dtype=float] |
|
|
frequency (frequencies) of ``notes`` in Hz |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
hz_to_midi |
|
|
note_to_hz |
|
|
""" |
|
|
|
|
|
return 440.0 * (2.0 ** ((np.asanyarray(notes) - 69.0) / 12.0)) |
|
|
|
|
|
|
|
|
def hz_to_midi(frequencies): |
|
|
"""Get MIDI note number(s) for given frequencies |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.hz_to_midi(60) |
|
|
34.506 |
|
|
>>> librosa.hz_to_midi([110, 220, 440]) |
|
|
array([ 45., 57., 69.]) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : float or np.ndarray [shape=(n,), dtype=float] |
|
|
frequencies to convert |
|
|
|
|
|
Returns |
|
|
------- |
|
|
note_nums : number or np.ndarray [shape=(n,), dtype=float] |
|
|
MIDI notes to ``frequencies`` |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
midi_to_hz |
|
|
note_to_midi |
|
|
hz_to_note |
|
|
""" |
|
|
|
|
|
return 12 * (np.log2(np.asanyarray(frequencies)) - np.log2(440.0)) + 69 |
|
|
|
|
|
|
|
|
def hz_to_note(frequencies, **kwargs): |
|
|
"""Convert one or more frequencies (in Hz) to the nearest note names. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : float or iterable of float |
|
|
Input frequencies, specified in Hz |
|
|
**kwargs : additional keyword arguments |
|
|
Arguments passed through to `midi_to_note` |
|
|
|
|
|
Returns |
|
|
------- |
|
|
notes : list of str |
|
|
``notes[i]`` is the closest note name to ``frequency[i]`` |
|
|
(or ``frequency`` if the input is scalar) |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
hz_to_midi |
|
|
midi_to_note |
|
|
note_to_hz |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get a single note name for a frequency |
|
|
|
|
|
>>> librosa.hz_to_note(440.0) |
|
|
['A5'] |
|
|
|
|
|
Get multiple notes with cent deviation |
|
|
|
|
|
>>> librosa.hz_to_note([32, 64], cents=True) |
|
|
['C1-38', 'C2-38'] |
|
|
|
|
|
Get multiple notes, but suppress octave labels |
|
|
|
|
|
>>> librosa.hz_to_note(440.0 * (2.0 ** np.linspace(0, 1, 12)), |
|
|
... octave=False) |
|
|
['A', 'A#', 'B', 'C', 'C#', 'D', 'E', 'F', 'F#', 'G', 'G#', 'A'] |
|
|
|
|
|
""" |
|
|
return midi_to_note(hz_to_midi(frequencies), **kwargs) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def hz_to_mel(frequencies, *, htk=False): |
|
|
"""Convert Hz to Mels |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.hz_to_mel(60) |
|
|
0.9 |
|
|
>>> librosa.hz_to_mel([110, 220, 440]) |
|
|
array([ 1.65, 3.3 , 6.6 ]) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : number or np.ndarray [shape=(n,)] , float |
|
|
scalar or array of frequencies |
|
|
htk : bool |
|
|
use HTK formula instead of Slaney |
|
|
|
|
|
Returns |
|
|
------- |
|
|
mels : number or np.ndarray [shape=(n,)] |
|
|
input frequencies in Mels |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
mel_to_hz |
|
|
""" |
|
|
|
|
|
frequencies = np.asanyarray(frequencies) |
|
|
|
|
|
if htk: |
|
|
return 2595.0 * np.log10(1.0 + frequencies / 700.0) |
|
|
|
|
|
|
|
|
f_min = 0.0 |
|
|
f_sp = 200.0 / 3 |
|
|
|
|
|
mels = (frequencies - f_min) / f_sp |
|
|
|
|
|
|
|
|
|
|
|
min_log_hz = 1000.0 |
|
|
min_log_mel = (min_log_hz - f_min) / f_sp |
|
|
logstep = np.log(6.4) / 27.0 |
|
|
|
|
|
if frequencies.ndim: |
|
|
|
|
|
log_t = frequencies >= min_log_hz |
|
|
mels[log_t] = min_log_mel + np.log(frequencies[log_t] / min_log_hz) / logstep |
|
|
elif frequencies >= min_log_hz: |
|
|
|
|
|
mels = min_log_mel + np.log(frequencies / min_log_hz) / logstep |
|
|
|
|
|
return mels |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def mel_to_hz(mels, *, htk=False): |
|
|
"""Convert mel bin numbers to frequencies |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.mel_to_hz(3) |
|
|
200. |
|
|
|
|
|
>>> librosa.mel_to_hz([1,2,3,4,5]) |
|
|
array([ 66.667, 133.333, 200. , 266.667, 333.333]) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
mels : np.ndarray [shape=(n,)], float |
|
|
mel bins to convert |
|
|
htk : bool |
|
|
use HTK formula instead of Slaney |
|
|
|
|
|
Returns |
|
|
------- |
|
|
frequencies : np.ndarray [shape=(n,)] |
|
|
input mels in Hz |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
hz_to_mel |
|
|
""" |
|
|
|
|
|
mels = np.asanyarray(mels) |
|
|
|
|
|
if htk: |
|
|
return 700.0 * (10.0 ** (mels / 2595.0) - 1.0) |
|
|
|
|
|
|
|
|
f_min = 0.0 |
|
|
f_sp = 200.0 / 3 |
|
|
freqs = f_min + f_sp * mels |
|
|
|
|
|
|
|
|
min_log_hz = 1000.0 |
|
|
min_log_mel = (min_log_hz - f_min) / f_sp |
|
|
logstep = np.log(6.4) / 27.0 |
|
|
|
|
|
if mels.ndim: |
|
|
|
|
|
log_t = mels >= min_log_mel |
|
|
freqs[log_t] = min_log_hz * np.exp(logstep * (mels[log_t] - min_log_mel)) |
|
|
elif mels >= min_log_mel: |
|
|
|
|
|
freqs = min_log_hz * np.exp(logstep * (mels - min_log_mel)) |
|
|
|
|
|
return freqs |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def hz_to_octs(frequencies, *, tuning=0.0, bins_per_octave=12): |
|
|
"""Convert frequencies (Hz) to (fractional) octave numbers. |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.hz_to_octs(440.0) |
|
|
4. |
|
|
>>> librosa.hz_to_octs([32, 64, 128, 256]) |
|
|
array([ 0.219, 1.219, 2.219, 3.219]) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : number >0 or np.ndarray [shape=(n,)] or float |
|
|
scalar or vector of frequencies |
|
|
tuning : float |
|
|
Tuning deviation from A440 in (fractional) bins per octave. |
|
|
bins_per_octave : int > 0 |
|
|
Number of bins per octave. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
octaves : number or np.ndarray [shape=(n,)] |
|
|
octave number for each frequency |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
octs_to_hz |
|
|
""" |
|
|
|
|
|
A440 = 440.0 * 2.0 ** (tuning / bins_per_octave) |
|
|
|
|
|
return np.log2(np.asanyarray(frequencies) / (float(A440) / 16)) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def octs_to_hz(octs, *, tuning=0.0, bins_per_octave=12): |
|
|
"""Convert octaves numbers to frequencies. |
|
|
|
|
|
Octaves are counted relative to A. |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.octs_to_hz(1) |
|
|
55. |
|
|
>>> librosa.octs_to_hz([-2, -1, 0, 1, 2]) |
|
|
array([ 6.875, 13.75 , 27.5 , 55. , 110. ]) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
octs : np.ndarray [shape=(n,)] or float |
|
|
octave number for each frequency |
|
|
tuning : float |
|
|
Tuning deviation from A440 in (fractional) bins per octave. |
|
|
bins_per_octave : int > 0 |
|
|
Number of bins per octave. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
frequencies : number or np.ndarray [shape=(n,)] |
|
|
scalar or vector of frequencies |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
hz_to_octs |
|
|
""" |
|
|
A440 = 440.0 * 2.0 ** (tuning / bins_per_octave) |
|
|
|
|
|
return (float(A440) / 16) * (2.0 ** np.asanyarray(octs)) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def A4_to_tuning(A4, *, bins_per_octave=12): |
|
|
"""Convert a reference pitch frequency (e.g., ``A4=435``) to a tuning |
|
|
estimation, in fractions of a bin per octave. |
|
|
|
|
|
This is useful for determining the tuning deviation relative to |
|
|
A440 of a given frequency, assuming equal temperament. By default, |
|
|
12 bins per octave are used. |
|
|
|
|
|
This method is the inverse of `tuning_to_A4`. |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
The base case of this method in which A440 yields 0 tuning offset |
|
|
from itself. |
|
|
|
|
|
>>> librosa.A4_to_tuning(440.0) |
|
|
0. |
|
|
|
|
|
Convert a non-A440 frequency to a tuning offset relative |
|
|
to A440 using the default of 12 bins per octave. |
|
|
|
|
|
>>> librosa.A4_to_tuning(432.0) |
|
|
-0.318 |
|
|
|
|
|
Convert two reference pitch frequencies to corresponding |
|
|
tuning estimations at once, but using 24 bins per octave. |
|
|
|
|
|
>>> librosa.A4_to_tuning([440.0, 444.0], bins_per_octave=24) |
|
|
array([ 0., 0.313 ]) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
A4 : float or np.ndarray [shape=(n,), dtype=float] |
|
|
Reference frequency(s) corresponding to A4. |
|
|
bins_per_octave : int > 0 |
|
|
Number of bins per octave. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
tuning : float or np.ndarray [shape=(n,), dtype=float] |
|
|
Tuning deviation from A440 in (fractional) bins per octave. |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
tuning_to_A4 |
|
|
""" |
|
|
return bins_per_octave * (np.log2(np.asanyarray(A4)) - np.log2(440.0)) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def tuning_to_A4(tuning, *, bins_per_octave=12): |
|
|
"""Convert a tuning deviation (from 0) in fractions of a bin per |
|
|
octave (e.g., ``tuning=-0.1``) to a reference pitch frequency |
|
|
relative to A440. |
|
|
|
|
|
This is useful if you are working in a non-A440 tuning system |
|
|
to determine the reference pitch frequency given a tuning |
|
|
offset and assuming equal temperament. By default, 12 bins per |
|
|
octave are used. |
|
|
|
|
|
This method is the inverse of `A4_to_tuning`. |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
The base case of this method in which a tuning deviation of 0 |
|
|
gets us to our A440 reference pitch. |
|
|
|
|
|
>>> librosa.tuning_to_A4(0.0) |
|
|
440. |
|
|
|
|
|
Convert a nonzero tuning offset to its reference pitch frequency. |
|
|
|
|
|
>>> librosa.tuning_to_A4(-0.318) |
|
|
431.992 |
|
|
|
|
|
Convert 3 tuning deviations at once to respective reference |
|
|
pitch frequencies, using 36 bins per octave. |
|
|
|
|
|
>>> librosa.tuning_to_A4([0.1, 0.2, -0.1], bins_per_octave=36) |
|
|
array([ 440.848, 441.698 439.154]) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
tuning : float or np.ndarray [shape=(n,), dtype=float] |
|
|
Tuning deviation from A440 in fractional bins per octave. |
|
|
bins_per_octave : int > 0 |
|
|
Number of bins per octave. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
A4 : float or np.ndarray [shape=(n,), dtype=float] |
|
|
Reference frequency corresponding to A4. |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
A4_to_tuning |
|
|
""" |
|
|
return 440.0 * 2.0 ** (np.asanyarray(tuning) / bins_per_octave) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def fft_frequencies(*, sr=22050, n_fft=2048): |
|
|
"""Alternative implementation of `np.fft.fftfreq` |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
sr : number > 0 [scalar] |
|
|
Audio sampling rate |
|
|
n_fft : int > 0 [scalar] |
|
|
FFT window size |
|
|
|
|
|
Returns |
|
|
------- |
|
|
freqs : np.ndarray [shape=(1 + n_fft/2,)] |
|
|
Frequencies ``(0, sr/n_fft, 2*sr/n_fft, ..., sr/2)`` |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.fft_frequencies(sr=22050, n_fft=16) |
|
|
array([ 0. , 1378.125, 2756.25 , 4134.375, |
|
|
5512.5 , 6890.625, 8268.75 , 9646.875, 11025. ]) |
|
|
|
|
|
""" |
|
|
|
|
|
return np.fft.rfftfreq(n=n_fft, d=1.0 / sr) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def cqt_frequencies(n_bins, *, fmin, bins_per_octave=12, tuning=0.0): |
|
|
"""Compute the center frequencies of Constant-Q bins. |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> # Get the CQT frequencies for 24 notes, starting at C2 |
|
|
>>> librosa.cqt_frequencies(24, fmin=librosa.note_to_hz('C2')) |
|
|
array([ 65.406, 69.296, 73.416, 77.782, 82.407, 87.307, |
|
|
92.499, 97.999, 103.826, 110. , 116.541, 123.471, |
|
|
130.813, 138.591, 146.832, 155.563, 164.814, 174.614, |
|
|
184.997, 195.998, 207.652, 220. , 233.082, 246.942]) |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
n_bins : int > 0 [scalar] |
|
|
Number of constant-Q bins |
|
|
fmin : float > 0 [scalar] |
|
|
Minimum frequency |
|
|
bins_per_octave : int > 0 [scalar] |
|
|
Number of bins per octave |
|
|
tuning : float |
|
|
Deviation from A440 tuning in fractional bins |
|
|
|
|
|
Returns |
|
|
------- |
|
|
frequencies : np.ndarray [shape=(n_bins,)] |
|
|
Center frequency for each CQT bin |
|
|
""" |
|
|
|
|
|
correction = 2.0 ** (float(tuning) / bins_per_octave) |
|
|
frequencies = 2.0 ** (np.arange(0, n_bins, dtype=float) / bins_per_octave) |
|
|
|
|
|
return correction * fmin * frequencies |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def mel_frequencies(n_mels=128, *, fmin=0.0, fmax=11025.0, htk=False): |
|
|
"""Compute an array of acoustic frequencies tuned to the mel scale. |
|
|
|
|
|
The mel scale is a quasi-logarithmic function of acoustic frequency |
|
|
designed such that perceptually similar pitch intervals (e.g. octaves) |
|
|
appear equal in width over the full hearing range. |
|
|
|
|
|
Because the definition of the mel scale is conditioned by a finite number |
|
|
of subjective psychoaoustical experiments, several implementations coexist |
|
|
in the audio signal processing literature [#]_. By default, librosa replicates |
|
|
the behavior of the well-established MATLAB Auditory Toolbox of Slaney [#]_. |
|
|
According to this default implementation, the conversion from Hertz to mel is |
|
|
linear below 1 kHz and logarithmic above 1 kHz. Another available implementation |
|
|
replicates the Hidden Markov Toolkit [#]_ (HTK) according to the following formula:: |
|
|
|
|
|
mel = 2595.0 * np.log10(1.0 + f / 700.0). |
|
|
|
|
|
The choice of implementation is determined by the ``htk`` keyword argument: setting |
|
|
``htk=False`` leads to the Auditory toolbox implementation, whereas setting it ``htk=True`` |
|
|
leads to the HTK implementation. |
|
|
|
|
|
.. [#] Umesh, S., Cohen, L., & Nelson, D. Fitting the mel scale. |
|
|
In Proc. International Conference on Acoustics, Speech, and Signal Processing |
|
|
(ICASSP), vol. 1, pp. 217-220, 1998. |
|
|
|
|
|
.. [#] Slaney, M. Auditory Toolbox: A MATLAB Toolbox for Auditory |
|
|
Modeling Work. Technical Report, version 2, Interval Research Corporation, 1998. |
|
|
|
|
|
.. [#] Young, S., Evermann, G., Gales, M., Hain, T., Kershaw, D., Liu, X., |
|
|
Moore, G., Odell, J., Ollason, D., Povey, D., Valtchev, V., & Woodland, P. |
|
|
The HTK book, version 3.4. Cambridge University, March 2009. |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
hz_to_mel |
|
|
mel_to_hz |
|
|
librosa.feature.melspectrogram |
|
|
librosa.feature.mfcc |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
n_mels : int > 0 [scalar] |
|
|
Number of mel bins. |
|
|
fmin : float >= 0 [scalar] |
|
|
Minimum frequency (Hz). |
|
|
fmax : float >= 0 [scalar] |
|
|
Maximum frequency (Hz). |
|
|
htk : bool |
|
|
If True, use HTK formula to convert Hz to mel. |
|
|
Otherwise (False), use Slaney's Auditory Toolbox. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
bin_frequencies : ndarray [shape=(n_mels,)] |
|
|
Vector of ``n_mels`` frequencies in Hz which are uniformly spaced on the Mel |
|
|
axis. |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.mel_frequencies(n_mels=40) |
|
|
array([ 0. , 85.317, 170.635, 255.952, |
|
|
341.269, 426.586, 511.904, 597.221, |
|
|
682.538, 767.855, 853.173, 938.49 , |
|
|
1024.856, 1119.114, 1222.042, 1334.436, |
|
|
1457.167, 1591.187, 1737.532, 1897.337, |
|
|
2071.84 , 2262.393, 2470.47 , 2697.686, |
|
|
2945.799, 3216.731, 3512.582, 3835.643, |
|
|
4188.417, 4573.636, 4994.285, 5453.621, |
|
|
5955.205, 6502.92 , 7101.009, 7754.107, |
|
|
8467.272, 9246.028, 10096.408, 11025. ]) |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
min_mel = hz_to_mel(fmin, htk=htk) |
|
|
max_mel = hz_to_mel(fmax, htk=htk) |
|
|
|
|
|
mels = np.linspace(min_mel, max_mel, n_mels) |
|
|
|
|
|
return mel_to_hz(mels, htk=htk) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def tempo_frequencies(n_bins, *, hop_length=512, sr=22050): |
|
|
"""Compute the frequencies (in beats per minute) corresponding |
|
|
to an onset auto-correlation or tempogram matrix. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
n_bins : int > 0 |
|
|
The number of lag bins |
|
|
hop_length : int > 0 |
|
|
The number of samples between each bin |
|
|
sr : number > 0 |
|
|
The audio sampling rate |
|
|
|
|
|
Returns |
|
|
------- |
|
|
bin_frequencies : ndarray [shape=(n_bins,)] |
|
|
vector of bin frequencies measured in BPM. |
|
|
|
|
|
.. note:: ``bin_frequencies[0] = +np.inf`` corresponds to 0-lag |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get the tempo frequencies corresponding to a 384-bin (8-second) tempogram |
|
|
|
|
|
>>> librosa.tempo_frequencies(384) |
|
|
array([ inf, 2583.984, 1291.992, ..., 6.782, |
|
|
6.764, 6.747]) |
|
|
""" |
|
|
|
|
|
bin_frequencies = np.zeros(int(n_bins), dtype=np.float64) |
|
|
|
|
|
bin_frequencies[0] = np.inf |
|
|
bin_frequencies[1:] = 60.0 * sr / (hop_length * np.arange(1.0, n_bins)) |
|
|
|
|
|
return bin_frequencies |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def fourier_tempo_frequencies(*, sr=22050, win_length=384, hop_length=512): |
|
|
"""Compute the frequencies (in beats per minute) corresponding |
|
|
to a Fourier tempogram matrix. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
sr : number > 0 |
|
|
The audio sampling rate |
|
|
win_length : int > 0 |
|
|
The number of frames per analysis window |
|
|
hop_length : int > 0 |
|
|
The number of samples between each bin |
|
|
|
|
|
Returns |
|
|
------- |
|
|
bin_frequencies : ndarray [shape=(win_length // 2 + 1 ,)] |
|
|
vector of bin frequencies measured in BPM. |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get the tempo frequencies corresponding to a 384-bin (8-second) tempogram |
|
|
|
|
|
>>> librosa.fourier_tempo_frequencies(win_length=384) |
|
|
array([ 0. , 0.117, 0.234, ..., 22.266, 22.383, 22.5 ]) |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
return fft_frequencies(sr=sr * 60 / float(hop_length), n_fft=win_length) |
|
|
|
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def A_weighting(frequencies, *, min_db=-80.0): |
|
|
"""Compute the A-weighting of a set of frequencies. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : scalar or np.ndarray [shape=(n,)] |
|
|
One or more frequencies (in Hz) |
|
|
min_db : float [scalar] or None |
|
|
Clip weights below this threshold. |
|
|
If `None`, no clipping is performed. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
A_weighting : scalar or np.ndarray [shape=(n,)] |
|
|
``A_weighting[i]`` is the A-weighting of ``frequencies[i]`` |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
perceptual_weighting |
|
|
frequency_weighting |
|
|
multi_frequency_weighting |
|
|
B_weighting |
|
|
C_weighting |
|
|
D_weighting |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get the A-weighting for CQT frequencies |
|
|
|
|
|
>>> import matplotlib.pyplot as plt |
|
|
>>> freqs = librosa.cqt_frequencies(n_bins=108, fmin=librosa.note_to_hz('C1')) |
|
|
>>> weights = librosa.A_weighting(freqs) |
|
|
>>> fig, ax = plt.subplots() |
|
|
>>> ax.plot(freqs, weights) |
|
|
>>> ax.set(xlabel='Frequency (Hz)', |
|
|
... ylabel='Weighting (log10)', |
|
|
... title='A-Weighting of CQT frequencies') |
|
|
""" |
|
|
f_sq = np.asanyarray(frequencies) ** 2.0 |
|
|
|
|
|
const = np.array([12194.217, 20.598997, 107.65265, 737.86223]) ** 2.0 |
|
|
weights = 2.0 + 20.0 * ( |
|
|
np.log10(const[0]) |
|
|
+ 2 * np.log10(f_sq) |
|
|
- np.log10(f_sq + const[0]) |
|
|
- np.log10(f_sq + const[1]) |
|
|
- 0.5 * np.log10(f_sq + const[2]) |
|
|
- 0.5 * np.log10(f_sq + const[3]) |
|
|
) |
|
|
|
|
|
return weights if min_db is None else np.maximum(min_db, weights) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def B_weighting(frequencies, *, min_db=-80.0): |
|
|
"""Compute the B-weighting of a set of frequencies. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : scalar or np.ndarray [shape=(n,)] |
|
|
One or more frequencies (in Hz) |
|
|
min_db : float [scalar] or None |
|
|
Clip weights below this threshold. |
|
|
If `None`, no clipping is performed. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
B_weighting : scalar or np.ndarray [shape=(n,)] |
|
|
``B_weighting[i]`` is the B-weighting of ``frequencies[i]`` |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
perceptual_weighting |
|
|
frequency_weighting |
|
|
multi_frequency_weighting |
|
|
A_weighting |
|
|
C_weighting |
|
|
D_weighting |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get the B-weighting for CQT frequencies |
|
|
|
|
|
>>> import matplotlib.pyplot as plt |
|
|
>>> freqs = librosa.cqt_frequencies(n_bins=108, fmin=librosa.note_to_hz('C1')) |
|
|
>>> weights = librosa.B_weighting(freqs) |
|
|
>>> fig, ax = plt.subplots() |
|
|
>>> ax.plot(freqs, weights) |
|
|
>>> ax.set(xlabel='Frequency (Hz)', |
|
|
... ylabel='Weighting (log10)', |
|
|
... title='B-Weighting of CQT frequencies') |
|
|
""" |
|
|
f_sq = np.asanyarray(frequencies) ** 2.0 |
|
|
|
|
|
const = np.array([12194.217, 20.598997, 158.48932]) ** 2.0 |
|
|
weights = 0.17 + 20.0 * ( |
|
|
np.log10(const[0]) |
|
|
+ 1.5 * np.log10(f_sq) |
|
|
- np.log10(f_sq + const[0]) |
|
|
- np.log10(f_sq + const[1]) |
|
|
- 0.5 * np.log10(f_sq + const[2]) |
|
|
) |
|
|
|
|
|
return weights if min_db is None else np.maximum(min_db, weights) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def C_weighting(frequencies, *, min_db=-80.0): |
|
|
"""Compute the C-weighting of a set of frequencies. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : scalar or np.ndarray [shape=(n,)] |
|
|
One or more frequencies (in Hz) |
|
|
min_db : float [scalar] or None |
|
|
Clip weights below this threshold. |
|
|
If `None`, no clipping is performed. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
C_weighting : scalar or np.ndarray [shape=(n,)] |
|
|
``C_weighting[i]`` is the C-weighting of ``frequencies[i]`` |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
perceptual_weighting |
|
|
frequency_weighting |
|
|
multi_frequency_weighting |
|
|
A_weighting |
|
|
B_weighting |
|
|
D_weighting |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get the C-weighting for CQT frequencies |
|
|
|
|
|
>>> import matplotlib.pyplot as plt |
|
|
>>> freqs = librosa.cqt_frequencies(n_bins=108, fmin=librosa.note_to_hz('C1')) |
|
|
>>> weights = librosa.C_weighting(freqs) |
|
|
>>> fig, ax = plt.subplots() |
|
|
>>> ax.plot(freqs, weights) |
|
|
>>> ax.set(xlabel='Frequency (Hz)', ylabel='Weighting (log10)', |
|
|
... title='C-Weighting of CQT frequencies') |
|
|
""" |
|
|
f_sq = np.asanyarray(frequencies) ** 2.0 |
|
|
|
|
|
const = np.array([12194.217, 20.598997]) ** 2.0 |
|
|
weights = 0.062 + 20.0 * ( |
|
|
np.log10(const[0]) |
|
|
+ np.log10(f_sq) |
|
|
- np.log10(f_sq + const[0]) |
|
|
- np.log10(f_sq + const[1]) |
|
|
) |
|
|
|
|
|
return weights if min_db is None else np.maximum(min_db, weights) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def D_weighting(frequencies, *, min_db=-80.0): |
|
|
"""Compute the D-weighting of a set of frequencies. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : scalar or np.ndarray [shape=(n,)] |
|
|
One or more frequencies (in Hz) |
|
|
min_db : float [scalar] or None |
|
|
Clip weights below this threshold. |
|
|
If `None`, no clipping is performed. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
D_weighting : scalar or np.ndarray [shape=(n,)] |
|
|
``D_weighting[i]`` is the D-weighting of ``frequencies[i]`` |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
perceptual_weighting |
|
|
frequency_weighting |
|
|
multi_frequency_weighting |
|
|
A_weighting |
|
|
B_weighting |
|
|
C_weighting |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get the D-weighting for CQT frequencies |
|
|
|
|
|
>>> import matplotlib.pyplot as plt |
|
|
>>> freqs = librosa.cqt_frequencies(n_bins=108, fmin=librosa.note_to_hz('C1')) |
|
|
>>> weights = librosa.D_weighting(freqs) |
|
|
>>> fig, ax = plt.subplots() |
|
|
>>> ax.plot(freqs, weights) |
|
|
>>> ax.set(xlabel='Frequency (Hz)', ylabel='Weighting (log10)', |
|
|
... title='D-Weighting of CQT frequencies') |
|
|
""" |
|
|
f_sq = np.asanyarray(frequencies) ** 2.0 |
|
|
|
|
|
const = np.array([8.3046305e-3, 1018.7, 1039.6, 3136.5, 3424, 282.7, 1160]) ** 2.0 |
|
|
weights = 20.0 * ( |
|
|
0.5 * np.log10(f_sq) |
|
|
- np.log10(const[0]) |
|
|
+ 0.5 |
|
|
* ( |
|
|
+np.log10((const[1] - f_sq) ** 2 + const[2] * f_sq) |
|
|
- np.log10((const[3] - f_sq) ** 2 + const[4] * f_sq) |
|
|
- np.log10(const[5] + f_sq) |
|
|
- np.log10(const[6] + f_sq) |
|
|
) |
|
|
) |
|
|
|
|
|
return weights if min_db is None else np.maximum(min_db, weights) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def Z_weighting(frequencies, *, min_db=None): |
|
|
weights = np.zeros(len(frequencies)) |
|
|
return weights if min_db is None else np.maximum(min_db, weights) |
|
|
|
|
|
|
|
|
WEIGHTING_FUNCTIONS = { |
|
|
"A": A_weighting, |
|
|
"B": B_weighting, |
|
|
"C": C_weighting, |
|
|
"D": D_weighting, |
|
|
"Z": Z_weighting, |
|
|
None: Z_weighting, |
|
|
} |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def frequency_weighting(frequencies, *, kind="A", **kwargs): |
|
|
"""Compute the weighting of a set of frequencies. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : scalar or np.ndarray [shape=(n,)] |
|
|
One or more frequencies (in Hz) |
|
|
kind : str in |
|
|
The weighting kind. e.g. `'A'`, `'B'`, `'C'`, `'D'`, `'Z'` |
|
|
**kwargs |
|
|
Additional keyword arguments to A_weighting, B_weighting, etc. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
weighting : scalar or np.ndarray [shape=(n,)] |
|
|
``weighting[i]`` is the weighting of ``frequencies[i]`` |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
perceptual_weighting |
|
|
multi_frequency_weighting |
|
|
A_weighting |
|
|
B_weighting |
|
|
C_weighting |
|
|
D_weighting |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get the A-weighting for CQT frequencies |
|
|
|
|
|
>>> import matplotlib.pyplot as plt |
|
|
>>> freqs = librosa.cqt_frequencies(n_bins=108, fmin=librosa.note_to_hz('C1')) |
|
|
>>> weights = librosa.frequency_weighting(freqs, kind='A') |
|
|
>>> fig, ax = plt.subplots() |
|
|
>>> ax.plot(freqs, weights) |
|
|
>>> ax.set(xlabel='Frequency (Hz)', ylabel='Weighting (log10)', |
|
|
... title='A-Weighting of CQT frequencies') |
|
|
""" |
|
|
if isinstance(kind, str): |
|
|
kind = kind.upper() |
|
|
return WEIGHTING_FUNCTIONS[kind](frequencies, **kwargs) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def multi_frequency_weighting(frequencies, *, kinds="ZAC", **kwargs): |
|
|
"""Compute multiple weightings of a set of frequencies. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : scalar or np.ndarray [shape=(n,)] |
|
|
One or more frequencies (in Hz) |
|
|
kinds : list or tuple or str |
|
|
An iterable of weighting kinds. e.g. `('Z', 'B')`, `'ZAD'`, `'C'` |
|
|
**kwargs : keywords to pass to the weighting function. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
weighting : scalar or np.ndarray [shape=(len(kinds), n)] |
|
|
``weighting[i, j]`` is the weighting of ``frequencies[j]`` |
|
|
using the curve determined by ``kinds[i]``. |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
perceptual_weighting |
|
|
frequency_weighting |
|
|
A_weighting |
|
|
B_weighting |
|
|
C_weighting |
|
|
D_weighting |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Get the A, B, C, D, and Z weightings for CQT frequencies |
|
|
|
|
|
>>> import matplotlib.pyplot as plt |
|
|
>>> freqs = librosa.cqt_frequencies(n_bins=108, fmin=librosa.note_to_hz('C1')) |
|
|
>>> weightings = 'ABCDZ' |
|
|
>>> weights = librosa.multi_frequency_weighting(freqs, kinds=weightings) |
|
|
>>> fig, ax = plt.subplots() |
|
|
>>> for label, w in zip(weightings, weights): |
|
|
... ax.plot(freqs, w, label=label) |
|
|
>>> ax.set(xlabel='Frequency (Hz)', ylabel='Weighting (log10)', |
|
|
... title='Weightings of CQT frequencies') |
|
|
>>> ax.legend() |
|
|
""" |
|
|
return np.stack( |
|
|
[frequency_weighting(frequencies, kind=k, **kwargs) for k in kinds], axis=0 |
|
|
) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def times_like(X, *, sr=22050, hop_length=512, n_fft=None, axis=-1): |
|
|
"""Return an array of time values to match the time axis from a feature matrix. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
X : np.ndarray or scalar |
|
|
- If ndarray, X is a feature matrix, e.g. STFT, chromagram, or mel spectrogram. |
|
|
- If scalar, X represents the number of frames. |
|
|
sr : number > 0 [scalar] |
|
|
audio sampling rate |
|
|
hop_length : int > 0 [scalar] |
|
|
number of samples between successive frames |
|
|
n_fft : None or int > 0 [scalar] |
|
|
Optional: length of the FFT window. |
|
|
If given, time conversion will include an offset of ``n_fft // 2`` |
|
|
to counteract windowing effects when using a non-centered STFT. |
|
|
axis : int [scalar] |
|
|
The axis representing the time axis of X. |
|
|
By default, the last axis (-1) is taken. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
times : np.ndarray [shape=(n,)] |
|
|
ndarray of times (in seconds) corresponding to each frame of X. |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
samples_like : |
|
|
Return an array of sample indices to match the time axis from a feature matrix. |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Provide a feature matrix input: |
|
|
|
|
|
>>> y, sr = librosa.load(librosa.ex('trumpet')) |
|
|
>>> D = librosa.stft(y) |
|
|
>>> times = librosa.times_like(D) |
|
|
>>> times |
|
|
array([0. , 0.023, ..., 5.294, 5.317]) |
|
|
|
|
|
Provide a scalar input: |
|
|
|
|
|
>>> n_frames = 2647 |
|
|
>>> times = librosa.times_like(n_frames) |
|
|
>>> times |
|
|
array([ 0.00000000e+00, 2.32199546e-02, 4.64399093e-02, ..., |
|
|
6.13935601e+01, 6.14167800e+01, 6.14400000e+01]) |
|
|
""" |
|
|
samples = samples_like(X, hop_length=hop_length, n_fft=n_fft, axis=axis) |
|
|
return samples_to_time(samples, sr=sr) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def samples_like(X, *, hop_length=512, n_fft=None, axis=-1): |
|
|
"""Return an array of sample indices to match the time axis from a feature matrix. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
X : np.ndarray or scalar |
|
|
- If ndarray, X is a feature matrix, e.g. STFT, chromagram, or mel spectrogram. |
|
|
- If scalar, X represents the number of frames. |
|
|
hop_length : int > 0 [scalar] |
|
|
number of samples between successive frames |
|
|
n_fft : None or int > 0 [scalar] |
|
|
Optional: length of the FFT window. |
|
|
If given, time conversion will include an offset of ``n_fft // 2`` |
|
|
to counteract windowing effects when using a non-centered STFT. |
|
|
axis : int [scalar] |
|
|
The axis representing the time axis of ``X``. |
|
|
By default, the last axis (-1) is taken. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
samples : np.ndarray [shape=(n,)] |
|
|
ndarray of sample indices corresponding to each frame of ``X``. |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
times_like : |
|
|
Return an array of time values to match the time axis from a feature matrix. |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Provide a feature matrix input: |
|
|
|
|
|
>>> y, sr = librosa.load(librosa.ex('trumpet')) |
|
|
>>> X = librosa.stft(y) |
|
|
>>> samples = librosa.samples_like(X) |
|
|
>>> samples |
|
|
array([ 0, 512, ..., 116736, 117248]) |
|
|
|
|
|
Provide a scalar input: |
|
|
|
|
|
>>> n_frames = 2647 |
|
|
>>> samples = librosa.samples_like(n_frames) |
|
|
>>> samples |
|
|
array([ 0, 512, 1024, ..., 1353728, 1354240, 1354752]) |
|
|
""" |
|
|
if np.isscalar(X): |
|
|
frames = np.arange(X) |
|
|
else: |
|
|
frames = np.arange(X.shape[axis]) |
|
|
return frames_to_samples(frames, hop_length=hop_length, n_fft=n_fft) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def midi_to_svara_h(midi, *, Sa, abbr=True, octave=True, unicode=True): |
|
|
"""Convert MIDI numbers to Hindustani svara |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
midi : numeric or np.ndarray |
|
|
The MIDI number or numbers to convert |
|
|
|
|
|
Sa : number > 0 |
|
|
MIDI number of the reference Sa. |
|
|
|
|
|
abbr : bool |
|
|
If `True` (default) return abbreviated names ('S', 'r', 'R', 'g', 'G', ...) |
|
|
|
|
|
If `False`, return long-form names ('Sa', 're', 'Re', 'ga', 'Ga', ...) |
|
|
|
|
|
octave : bool |
|
|
If `True`, decorate svara in neighboring octaves with over- or under-dots. |
|
|
|
|
|
If `False`, ignore octave height information. |
|
|
|
|
|
unicode : bool |
|
|
If `True`, use unicode symbols to decorate octave information. |
|
|
|
|
|
If `False`, use low-order ASCII (' and ,) for octave decorations. |
|
|
|
|
|
This only takes effect if `octave=True`. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
svara : str or list of str |
|
|
The svara corresponding to the given MIDI number(s) |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
hz_to_svara_h |
|
|
note_to_svara_h |
|
|
midi_to_svara_c |
|
|
midi_to_note |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
The first three svara with Sa at midi number 60: |
|
|
|
|
|
>>> librosa.midi_svara_h([60, 61, 62], Sa=60) |
|
|
['S', 'r', 'R'] |
|
|
|
|
|
With Sa=67, midi 60-62 are in the octave below: |
|
|
|
|
|
>>> librosa.midi_to_svara_h([60, 61, 62], Sa=67) |
|
|
['ṃ', 'Ṃ', 'P̣'] |
|
|
|
|
|
Or without unicode decoration: |
|
|
|
|
|
>>> librosa.midi_to_svara_h([60, 61, 62], Sa=67, unicode=False) |
|
|
['m,', 'M,', 'P,'] |
|
|
|
|
|
Or going up an octave, with Sa=60, and using unabbreviated notes |
|
|
|
|
|
>>> librosa.midi_to_svara_h([72, 73, 74], Sa=60, abbr=False) |
|
|
['Ṡa', 'ṙe', 'Ṙe'] |
|
|
""" |
|
|
|
|
|
SVARA_MAP = [ |
|
|
"Sa", |
|
|
"re", |
|
|
"Re", |
|
|
"ga", |
|
|
"Ga", |
|
|
"ma", |
|
|
"Ma", |
|
|
"Pa", |
|
|
"dha", |
|
|
"Dha", |
|
|
"ni", |
|
|
"Ni", |
|
|
] |
|
|
|
|
|
SVARA_MAP_SHORT = list(s[0] for s in SVARA_MAP) |
|
|
|
|
|
if not np.isscalar(midi): |
|
|
return [ |
|
|
midi_to_svara_h(m, Sa=Sa, abbr=abbr, octave=octave, unicode=unicode) |
|
|
for m in midi |
|
|
] |
|
|
|
|
|
svara_num = int(np.round(midi - Sa)) |
|
|
|
|
|
if abbr: |
|
|
svara = SVARA_MAP_SHORT[svara_num % 12] |
|
|
else: |
|
|
svara = SVARA_MAP[svara_num % 12] |
|
|
|
|
|
if octave: |
|
|
if 24 > svara_num >= 12: |
|
|
if unicode: |
|
|
svara = svara[0] + "\u0307" + svara[1:] |
|
|
else: |
|
|
svara += "'" |
|
|
elif -12 <= svara_num < 0: |
|
|
if unicode: |
|
|
svara = svara[0] + "\u0323" + svara[1:] |
|
|
else: |
|
|
svara += "," |
|
|
|
|
|
return svara |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def hz_to_svara_h(frequencies, *, Sa, abbr=True, octave=True, unicode=True): |
|
|
"""Convert frequencies (in Hz) to Hindustani svara |
|
|
|
|
|
Note that this conversion assumes 12-tone equal temperament. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : positive number or np.ndarray |
|
|
The frequencies (in Hz) to convert |
|
|
|
|
|
Sa : positive number |
|
|
Frequency (in Hz) of the reference Sa. |
|
|
|
|
|
abbr : bool |
|
|
If `True` (default) return abbreviated names ('S', 'r', 'R', 'g', 'G', ...) |
|
|
|
|
|
If `False`, return long-form names ('Sa', 're', 'Re', 'ga', 'Ga', ...) |
|
|
|
|
|
octave : bool |
|
|
If `True`, decorate svara in neighboring octaves with over- or under-dots. |
|
|
|
|
|
If `False`, ignore octave height information. |
|
|
|
|
|
unicode : bool |
|
|
If `True`, use unicode symbols to decorate octave information. |
|
|
|
|
|
If `False`, use low-order ASCII (' and ,) for octave decorations. |
|
|
|
|
|
This only takes effect if `octave=True`. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
svara : str or list of str |
|
|
The svara corresponding to the given frequency/frequencies |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
midi_to_svara_h |
|
|
note_to_svara_h |
|
|
hz_to_svara_c |
|
|
hz_to_note |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Convert Sa in three octaves: |
|
|
|
|
|
>>> librosa.hz_to_svara_h([261/2, 261, 261*2], Sa=261) |
|
|
['Ṣ', 'S', 'Ṡ'] |
|
|
|
|
|
Convert one octave worth of frequencies with full names: |
|
|
|
|
|
>>> freqs = librosa.cqt_frequencies(n_bins=12, fmin=261) |
|
|
>>> librosa.hz_to_svara_h(freqs, Sa=freqs[0], abbr=False) |
|
|
['Sa', 're', 'Re', 'ga', 'Ga', 'ma', 'Ma', 'Pa', 'dha', 'Dha', 'ni', 'Ni'] |
|
|
""" |
|
|
|
|
|
midis = hz_to_midi(frequencies) |
|
|
return midi_to_svara_h( |
|
|
midis, Sa=hz_to_midi(Sa), abbr=abbr, octave=octave, unicode=unicode |
|
|
) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def note_to_svara_h(notes, *, Sa, abbr=True, octave=True, unicode=True): |
|
|
"""Convert western notes to Hindustani svara |
|
|
|
|
|
Note that this conversion assumes 12-tone equal temperament. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
notes : str or list of str |
|
|
Notes to convert (e.g., `'C#'` or `['C4', 'Db4', 'D4']` |
|
|
|
|
|
Sa : str |
|
|
Note corresponding to Sa (e.g., `'C'` or `'C5'`). |
|
|
|
|
|
If no octave information is provided, it will default to octave 0 |
|
|
(``C0`` ~= 16 Hz) |
|
|
|
|
|
abbr : bool |
|
|
If `True` (default) return abbreviated names ('S', 'r', 'R', 'g', 'G', ...) |
|
|
|
|
|
If `False`, return long-form names ('Sa', 're', 'Re', 'ga', 'Ga', ...) |
|
|
|
|
|
octave : bool |
|
|
If `True`, decorate svara in neighboring octaves with over- or under-dots. |
|
|
|
|
|
If `False`, ignore octave height information. |
|
|
|
|
|
unicode : bool |
|
|
If `True`, use unicode symbols to decorate octave information. |
|
|
|
|
|
If `False`, use low-order ASCII (' and ,) for octave decorations. |
|
|
|
|
|
This only takes effect if `octave=True`. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
svara : str or list of str |
|
|
The svara corresponding to the given notes |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
midi_to_svara_h |
|
|
hz_to_svara_h |
|
|
note_to_svara_c |
|
|
note_to_midi |
|
|
note_to_hz |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.note_to_svara_h(['C4', 'G4', 'C5', 'G5'], Sa='C5') |
|
|
['Ṣ', 'P̣', 'S', 'P'] |
|
|
""" |
|
|
|
|
|
midis = note_to_midi(notes, round_midi=False) |
|
|
|
|
|
return midi_to_svara_h( |
|
|
midis, Sa=note_to_midi(Sa), abbr=abbr, octave=octave, unicode=unicode |
|
|
) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def midi_to_svara_c(midi, *, Sa, mela, abbr=True, octave=True, unicode=True): |
|
|
"""Convert MIDI numbers to Carnatic svara within a given melakarta raga |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
midi : numeric |
|
|
The MIDI numbers to convert |
|
|
|
|
|
Sa : number > 0 |
|
|
MIDI number of the reference Sa. |
|
|
|
|
|
Default: 60 (261.6 Hz, `C4`) |
|
|
|
|
|
mela : int or str |
|
|
The name or index of the melakarta raga |
|
|
|
|
|
abbr : bool |
|
|
If `True` (default) return abbreviated names ('S', 'R1', 'R2', 'G1', 'G2', ...) |
|
|
|
|
|
If `False`, return long-form names ('Sa', 'Ri1', 'Ri2', 'Ga1', 'Ga2', ...) |
|
|
|
|
|
octave : bool |
|
|
If `True`, decorate svara in neighboring octaves with over- or under-dots. |
|
|
|
|
|
If `False`, ignore octave height information. |
|
|
|
|
|
unicode : bool |
|
|
If `True`, use unicode symbols to decorate octave information and subscript |
|
|
numbers. |
|
|
|
|
|
If `False`, use low-order ASCII (' and ,) for octave decorations. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
svara : str or list of str |
|
|
The svara corresponding to the given MIDI number(s) |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
hz_to_svara_c |
|
|
note_to_svara_c |
|
|
mela_to_degrees |
|
|
mela_to_svara |
|
|
list_mela |
|
|
""" |
|
|
if not np.isscalar(midi): |
|
|
return [ |
|
|
midi_to_svara_c( |
|
|
m, Sa=Sa, mela=mela, abbr=abbr, octave=octave, unicode=unicode |
|
|
) |
|
|
for m in midi |
|
|
] |
|
|
|
|
|
svara_num = int(np.round(midi - Sa)) |
|
|
|
|
|
svara_map = notation.mela_to_svara(mela, abbr=abbr, unicode=unicode) |
|
|
|
|
|
svara = svara_map[svara_num % 12] |
|
|
|
|
|
if octave: |
|
|
if 24 > svara_num >= 12: |
|
|
if unicode: |
|
|
svara = svara[0] + "\u0307" + svara[1:] |
|
|
else: |
|
|
svara += "'" |
|
|
elif -12 <= svara_num < 0: |
|
|
if unicode: |
|
|
svara = svara[0] + "\u0323" + svara[1:] |
|
|
else: |
|
|
svara += "," |
|
|
|
|
|
return svara |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def hz_to_svara_c(frequencies, *, Sa, mela, abbr=True, octave=True, unicode=True): |
|
|
"""Convert frequencies (in Hz) to Carnatic svara |
|
|
|
|
|
Note that this conversion assumes 12-tone equal temperament. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
frequencies : positive number or np.ndarray |
|
|
The frequencies (in Hz) to convert |
|
|
|
|
|
Sa : positive number |
|
|
Frequency (in Hz) of the reference Sa. |
|
|
|
|
|
mela : int [1, 72] or string |
|
|
The melakarta raga to use. |
|
|
|
|
|
abbr : bool |
|
|
If `True` (default) return abbreviated names ('S', 'R1', 'R2', 'G1', 'G2', ...) |
|
|
|
|
|
If `False`, return long-form names ('Sa', 'Ri1', 'Ri2', 'Ga1', 'Ga2', ...) |
|
|
|
|
|
octave : bool |
|
|
If `True`, decorate svara in neighboring octaves with over- or under-dots. |
|
|
|
|
|
If `False`, ignore octave height information. |
|
|
|
|
|
unicode : bool |
|
|
If `True`, use unicode symbols to decorate octave information. |
|
|
|
|
|
If `False`, use low-order ASCII (' and ,) for octave decorations. |
|
|
|
|
|
This only takes effect if `octave=True`. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
svara : str or list of str |
|
|
The svara corresponding to the given frequency/frequencies |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
note_to_svara_c |
|
|
midi_to_svara_c |
|
|
hz_to_svara_h |
|
|
hz_to_note |
|
|
list_mela |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
Convert Sa in three octaves: |
|
|
|
|
|
>>> librosa.hz_to_svara_c([261/2, 261, 261*2], Sa=261, mela='kanakangi') |
|
|
['Ṣ', 'S', 'Ṡ'] |
|
|
|
|
|
Convert one octave worth of frequencies using melakarta #36: |
|
|
|
|
|
>>> freqs = librosa.cqt_frequencies(n_bins=12, fmin=261) |
|
|
>>> librosa.hz_to_svara_c(freqs, Sa=freqs[0], mela=36) |
|
|
['S', 'R₁', 'R₂', 'R₃', 'G₃', 'M₁', 'M₂', 'P', 'D₁', 'D₂', 'D₃', 'N₃'] |
|
|
""" |
|
|
|
|
|
midis = hz_to_midi(frequencies) |
|
|
return midi_to_svara_c( |
|
|
midis, Sa=hz_to_midi(Sa), mela=mela, abbr=abbr, octave=octave, unicode=unicode |
|
|
) |
|
|
|
|
|
|
|
|
@deprecate_positional_args |
|
|
def note_to_svara_c(notes, *, Sa, mela, abbr=True, octave=True, unicode=True): |
|
|
"""Convert western notes to Carnatic svara |
|
|
|
|
|
Note that this conversion assumes 12-tone equal temperament. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
notes : str or list of str |
|
|
Notes to convert (e.g., `'C#'` or `['C4', 'Db4', 'D4']` |
|
|
|
|
|
Sa : str |
|
|
Note corresponding to Sa (e.g., `'C'` or `'C5'`). |
|
|
|
|
|
If no octave information is provided, it will default to octave 0 |
|
|
(``C0`` ~= 16 Hz) |
|
|
|
|
|
mela : str or int [1, 72] |
|
|
Melakarta raga name or index |
|
|
|
|
|
abbr : bool |
|
|
If `True` (default) return abbreviated names ('S', 'R1', 'R2', 'G1', 'G2', ...) |
|
|
|
|
|
If `False`, return long-form names ('Sa', 'Ri1', 'Ri2', 'Ga1', 'Ga2', ...) |
|
|
|
|
|
octave : bool |
|
|
If `True`, decorate svara in neighboring octaves with over- or under-dots. |
|
|
|
|
|
If `False`, ignore octave height information. |
|
|
|
|
|
unicode : bool |
|
|
If `True`, use unicode symbols to decorate octave information. |
|
|
|
|
|
If `False`, use low-order ASCII (' and ,) for octave decorations. |
|
|
|
|
|
This only takes effect if `octave=True`. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
svara : str or list of str |
|
|
The svara corresponding to the given notes |
|
|
|
|
|
See Also |
|
|
-------- |
|
|
midi_to_svara_c |
|
|
hz_to_svara_c |
|
|
note_to_svara_h |
|
|
note_to_midi |
|
|
note_to_hz |
|
|
list_mela |
|
|
|
|
|
Examples |
|
|
-------- |
|
|
>>> librosa.note_to_svara_h(['C4', 'G4', 'C5', 'D5', 'G5'], Sa='C5', mela=1) |
|
|
['Ṣ', 'P̣', 'S', 'G₁', 'P'] |
|
|
""" |
|
|
midis = note_to_midi(notes, round_midi=False) |
|
|
|
|
|
return midi_to_svara_c( |
|
|
midis, Sa=note_to_midi(Sa), mela=mela, abbr=abbr, octave=octave, unicode=unicode |
|
|
) |
|
|
|