Example #1
0
    def __init__(
            self,
            pulses_per_quarter: int = 96,
            middle_c: Union[Tone, int, str] = Tone.from_notation('C3'),
            bpm: float = 120.0,
            channel: int = 0
    ):
        """
        Initializes MIDIWriter object.

        Pulses per quarter note defines maximum resolution of the MIDI events.
        With this parameter equal to 1 one can not have notes shorter than 1/4.
        Default value should be suitable for any software and track.

        Middle C is a tone corresponding to integer value 60 in the MIDI specification.
        For most of the modern software it is C3. If resulting MIDI files
        is shifted by an octave in your software, try using value 'C4'.

        Beats per minute defines tempo of the track.

        MIDI channel can be any value from 0 to 15.

        :param pulses_per_quarter: number of pulses per quarter note (default: 96)
        :param middle_c: middle c tone (default: 'C3')
        :param bpm: beats per minute (default: 120.0)
        :param channel: MIDI channel to write to (default: 0)
        """
        if not 1 <= pulses_per_quarter <= 32767:
            raise ValueError('pulses_per_quarter must be in range [1, 32767]')

        if not 0 <= channel <= 15:
            raise ValueError('channel must be in range [0, 15]')

        self._pulses_per_quarter: int = pulses_per_quarter
        self._pulses_per_whole: int = pulses_per_quarter * 4

        self._middle_c = middle_c

        self._middle_c_delta: int
        if isinstance(middle_c, Tone):
            self._middle_c_delta = 60 - middle_c.pitch
        elif isinstance(middle_c, int):
            self._middle_c_delta = 60 - Tone(middle_c).pitch
        elif isinstance(middle_c, str):
            self._middle_c_delta = 60 - Tone.from_notation(middle_c).pitch
        else:
            raise TypeError('middle_c must be a Tone object, an integer or a string')

        self._bpm = bpm
        self._channel = channel
Example #2
0
def test_note_with_transposed():
    n1 = Tone.from_notation('C4')
    n2 = n1.transposed(1)

    assert n1.to_notation() == 'C4'
    assert n2.to_notation() == 'C#4'

    assert Tone.from_notation('C4').transposed(2).to_notation() == 'D4'
    assert Tone.from_notation('C4').transposed(-1).to_notation() == 'B3'
    assert Tone.from_notation('C4').transposed(-2).to_notation() == 'A#3'

    n = Tone(152)

    assert n.transposed(1).transposed(5).transposed(-3) == n.transposed(3)
Example #3
0
def test_tone_from_notation():
    for notation, pitch in [
        ('C', 0),
        ('C#', 1),
        ('D', 2),
        ('D#', 3),
        ('E', 4),
        ('F', 5),
        ('F#', 6),
        ('G', 7),
        ('G#', 8),
        ('A', 9),
        ('A#', 10),
        ('B', 11),
        ('C1', 12),
        ('C#1', 13),
        ('Db', 1),
        ('Eb', 3),
        ('Fb', 4),
        ('E#', 5),
        ('Ab', 8),
        ('C2', 24),
        ('C3', 36),
        ('C#3', 37),
        ('Db3', 37),
        ('C-1', -12),
        ('C#-1', -11),
        ('B-1', -1)
    ]:
        assert Tone.from_notation(notation).pitch == pitch
Example #4
0
    def __init__(
            self,
            middle_c: Union[Tone, int, str] = Tone.from_notation('C3')
    ):
        """
        Initializes MIDIReader object.

        Middle C is a tone corresponding to integer value 60 in the MIDI specification.
        For most of the modern software it is C3. If parsed track
        is shifted by an octave, try using value 'C4'.

        :param middle_c: middle c tone (default: 'C3')
        """
        self._middle_c = middle_c

        self._middle_c_delta: int
        if isinstance(middle_c, Tone):
            self._middle_c_delta = 60 - middle_c.pitch
        elif isinstance(middle_c, int):
            self._middle_c_delta = 60 - Tone(middle_c).pitch
        elif isinstance(middle_c, str):
            self._middle_c_delta = 60 - Tone.from_notation(middle_c).pitch
        else:
            raise TypeError('middle_c must be a Tone object, an integer or a string')
Example #5
0
    def __init__(self,
                 tone: Union[Tone, int, str],
                 duration: Union[Signature, Tuple[int, int]] = Signature(1, 4),
                 velocity: float = 0.75):
        """
        Initializes Note object. Tone can be a Tone object, an integer or a string.
        In the case of an integer, Tone constructor is called.
        In the case of a string, Tone.from_notation is called.
        Duration can be a Signature object or a pair of integers.
        In the case of a pair of integers Signature constructor is called.
        Velocity must be a float in the range [0.0, 1.0] where 0.0 is the minimum velocity and
        1.0 is the maximum velocity. By default velocity is 0.75 (as in many DAWs).

        :param tone: tone of the note, must be Tone object, integer or string
        :param duration: duration of the note, must be Signature or pair of integers (default: (1, 4))
        :param velocity: float number in the range [0.0, 1.0] (default: 0.75)
        """
        self._tone: Tone
        if isinstance(tone, Tone):
            self._tone = tone
        elif isinstance(tone, int):
            self._tone = Tone(tone)
        elif isinstance(tone, str):
            self._tone = Tone.from_notation(tone)
        else:
            raise TypeError(
                'tone must be a Tone object, an integer or a string')

        self._duration: Signature
        if isinstance(duration, Signature):
            self._duration = duration
        elif isinstance(duration, Iterable):
            self._duration = Signature(*duration)
        else:
            raise TypeError(
                'duration must be a Signature object or a pair of integers')

        if not 0.0 <= velocity <= 1.0:
            raise ValueError('velocity must be in range [0.0, 1.0]')

        self._velocity: float = velocity
Example #6
0
def test_tone_octave():
    for tone in 'ABCDEFG':
        for octave in range(-5, 5):
            assert Tone.from_notation(f'{tone}{octave}').octave == octave
Example #7
0
def test_tone_to_frequency():
    assert Tone.from_notation('A-1').to_frequency() == 13.75
    assert Tone.from_notation('A0').to_frequency() == 27.5
    assert Tone.from_notation('A1').to_frequency() == 55.0
    assert Tone.from_notation('A2').to_frequency() == 110.0
    assert Tone.from_notation('A3').to_frequency() == 220.0
    assert Tone.from_notation('A4').to_frequency() == 440.0
    assert Tone.from_notation('A5').to_frequency() == 880.0
    assert Tone.from_notation('A6').to_frequency() == 1760.0
    assert Tone.from_notation('A7').to_frequency() == 3520.0
    assert Tone.from_notation('A8').to_frequency() == 7040.0
    assert Tone.from_notation('A9').to_frequency() == 14080.0

    assert abs(Tone.from_notation('C5').to_frequency() - 523.2511306011972) < 1e-3
    assert abs(Tone.from_notation('F4').to_frequency() - 349.2282314330039) < 1e-4
Example #8
0
def test_tone_to_notation():
    assert Tone.from_notation('C#').to_notation() == 'C#0'
    assert Tone.from_notation('C#b').to_notation() == 'C0'
    assert Tone.from_notation('C##b').to_notation() == 'C#0'
    assert Tone.from_notation('C#b#').to_notation() == 'C#0'
    assert Tone.from_notation('Cb##').to_notation() == 'C#0'
    assert Tone.from_notation('Ab##43').to_notation() == 'A#43'
    assert Tone.from_notation('Ab##-43').to_notation() == 'A#-43'

    assert Tone.from_notation('C#').to_notation(transpose_down=True) == 'Db0'
    assert Tone.from_notation('C#b').to_notation(transpose_down=True) == 'C0'
    assert Tone.from_notation('C##b').to_notation(transpose_down=True) == 'Db0'
    assert Tone.from_notation('C#b#').to_notation(transpose_down=True) == 'Db0'
    assert Tone.from_notation('Cb##').to_notation(transpose_down=True) == 'Db0'
    assert Tone.from_notation('Ab##43').to_notation(transpose_down=True) == 'Bb43'
    assert Tone.from_notation('Ab##-43').to_notation(transpose_down=True) == 'Bb-43'