simetri.extensions.music

Musical notes and scales. MIDI and frequency conversions.

 1'''Musical notes and scales.  MIDI and frequency conversions.'''
 2
 3from math import log2
 4
 5from typing_extensions import Sequence, Any
 6
 7# from simetri.graphics.extensions.all_enums import MusicScale
 8
 9def midi_freq(m: int) -> float:
10    '''Return the frequency of a MIDI note number.'''
11    return 2**((m-69)/12) * 440
12
13def midi_m(freq: float) -> int:
14    '''Return the MIDI note number of a frequency.'''
15    return 12 * log2(freq / 440) + 69
16
17
18def note_name(midi_note: int) -> str:
19    """
20    Returns the name of a MIDI note given its number.
21
22    Args:
23        midi_note: An integer representing the MIDI note number (0-127).
24
25    Returns:
26        A string representing the note name (e.g., "C4", "G#5"), or None if the input is invalid.
27    """
28    if not isinstance(midi_note, int) or midi_note < 0 or midi_note > 127:
29        return None
30
31    notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
32    octave = midi_note // 12 - 1
33    note_index = midi_note % 12
34    return notes[note_index] + str(octave)
35
36def scale(m: int, scale_type:str = 'major') -> list:
37    '''Return a list of MIDI notes in a scale.'''
38    steps = {'major': [2, 2, 1, 2, 2, 2, 1],
39             'minor': [2, 1, 2, 2, 1, 2, 2],
40             'chromatic': [1] * 12,
41             'pentatonic': [2, 2, 3, 2, 3],}
42    s = [m]
43    for step in steps[scale_type]:
44        m += step
45        s.append(m)
46    return s
47
48
49
50def jump_scale(scale: Sequence, reference: Any, step:int) -> Any:
51    '''Return the note in a scale that is a certain number of steps away from a reference note.
52        Args:
53            scale: A list of MIDI notes in a scale.
54            reference: The reference note.
55            step: The number of steps away from the reference note.
56        Returns:
57            The note in the scale that is a certain number of steps away from the reference note.
58
59        Example:
60            jump_scale([60, 62, 64, 65, 67, 69, 71], 60, -2) returns 69.
61    '''
62    i = scale.index(reference)
63    n = len(scale)
64
65    return scale[(i + step) % n]
def midi_freq(m: int) -> float:
10def midi_freq(m: int) -> float:
11    '''Return the frequency of a MIDI note number.'''
12    return 2**((m-69)/12) * 440

Return the frequency of a MIDI note number.

def midi_m(freq: float) -> int:
14def midi_m(freq: float) -> int:
15    '''Return the MIDI note number of a frequency.'''
16    return 12 * log2(freq / 440) + 69

Return the MIDI note number of a frequency.

def note_name(midi_note: int) -> str:
19def note_name(midi_note: int) -> str:
20    """
21    Returns the name of a MIDI note given its number.
22
23    Args:
24        midi_note: An integer representing the MIDI note number (0-127).
25
26    Returns:
27        A string representing the note name (e.g., "C4", "G#5"), or None if the input is invalid.
28    """
29    if not isinstance(midi_note, int) or midi_note < 0 or midi_note > 127:
30        return None
31
32    notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
33    octave = midi_note // 12 - 1
34    note_index = midi_note % 12
35    return notes[note_index] + str(octave)

Returns the name of a MIDI note given its number.

Arguments:
  • midi_note: An integer representing the MIDI note number (0-127).
Returns:

A string representing the note name (e.g., "C4", "G#5"), or None if the input is invalid.

def scale(m: int, scale_type: str = 'major') -> list:
37def scale(m: int, scale_type:str = 'major') -> list:
38    '''Return a list of MIDI notes in a scale.'''
39    steps = {'major': [2, 2, 1, 2, 2, 2, 1],
40             'minor': [2, 1, 2, 2, 1, 2, 2],
41             'chromatic': [1] * 12,
42             'pentatonic': [2, 2, 3, 2, 3],}
43    s = [m]
44    for step in steps[scale_type]:
45        m += step
46        s.append(m)
47    return s

Return a list of MIDI notes in a scale.

def jump_scale( scale: Sequence, reference: typing_extensions.Any, step: int) -> typing_extensions.Any:
51def jump_scale(scale: Sequence, reference: Any, step:int) -> Any:
52    '''Return the note in a scale that is a certain number of steps away from a reference note.
53        Args:
54            scale: A list of MIDI notes in a scale.
55            reference: The reference note.
56            step: The number of steps away from the reference note.
57        Returns:
58            The note in the scale that is a certain number of steps away from the reference note.
59
60        Example:
61            jump_scale([60, 62, 64, 65, 67, 69, 71], 60, -2) returns 69.
62    '''
63    i = scale.index(reference)
64    n = len(scale)
65
66    return scale[(i + step) % n]

Return the note in a scale that is a certain number of steps away from a reference note.

Arguments:
  • scale: A list of MIDI notes in a scale.
  • reference: The reference note.
  • step: The number of steps away from the reference note.
Returns:

The note in the scale that is a certain number of steps away from the reference note.

Example:

jump_scale([60, 62, 64, 65, 67, 69, 71], 60, -2) returns 69.