Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
tandav committed Jan 20, 2024
1 parent 7023438 commit f598fe6
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 76 deletions.
4 changes: 2 additions & 2 deletions src/musiclib/midi/pitchbend.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import bisect
import operator
import dataclasses
import itertools
import operator
from typing import no_type_check

import numpy as np
Expand Down Expand Up @@ -72,7 +72,7 @@ def insert_pitch_pattern(
notes=midi.notes,
pitchbend=pitchbend,
ticks_per_beat=midi.ticks_per_beat,
)
)


def make_notes_pitchbends(midi: Midi) -> dict[MidiNote, list[MidiPitch]]:
Expand Down
9 changes: 9 additions & 0 deletions tests/midi/data/notation/0/code.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
header
version 2.2.1
root C3
ticks_per_beat 96

flute 17 14 10 17
flute 14 10 07 14
piano 10 07 04 10
bass 00 05 07 00
44 changes: 44 additions & 0 deletions tests/midi/data/notation/0/midi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"type": 1,
"ticks_per_beat": 96,
"tracks": [
[
{"type": "note_on", "time": 0, "channel": 0, "note": 48, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 48, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 53, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 53, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 55, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 55, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 48, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 48, "velocity": 100, "is_meta": false}
],
[
{"type": "note_on", "time": 0, "channel": 0, "note": 67, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 64, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 67, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 0, "channel": 0, "note": 64, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 69, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 65, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 69, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 0, "channel": 0, "note": 65, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 67, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 62, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 67, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 0, "channel": 0, "note": 62, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 67, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 64, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 67, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 0, "channel": 0, "note": 64, "velocity": 100, "is_meta": false}
],
[
{"type": "note_on", "time": 0, "channel": 0, "note": 60, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 60, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 60, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 60, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 59, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 59, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 60, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 60, "velocity": 100, "is_meta": false}
]
]
}
9 changes: 9 additions & 0 deletions tests/midi/data/notation/1/code.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
header
version 2.2.1
root C3
ticks_per_beat 96

flute 17 14 10 20
flute 14 10 07 17
piano 10 -- -- 14
bass 00 05 07 00
40 changes: 40 additions & 0 deletions tests/midi/data/notation/1/midi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"type": 1,
"ticks_per_beat": 96,
"tracks": [
[
{"type": "note_on", "time": 0, "channel": 0, "note": 48, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 48, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 53, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 53, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 55, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 55, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 48, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 48, "velocity": 100, "is_meta": false}
],
[
{"type": "note_on", "time": 0, "channel": 0, "note": 67, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 64, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 67, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 0, "channel": 0, "note": 64, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 69, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 65, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 69, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 0, "channel": 0, "note": 65, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 67, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 62, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 67, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 0, "channel": 0, "note": 62, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 72, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 67, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 72, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 0, "channel": 0, "note": 67, "velocity": 100, "is_meta": false}
],
[
{"type": "note_on", "time": 0, "channel": 0, "note": 60, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 288, "channel": 0, "note": 60, "velocity": 100, "is_meta": false},
{"type": "note_on", "time": 0, "channel": 0, "note": 64, "velocity": 100, "is_meta": false},
{"type": "note_off", "time": 96, "channel": 0, "note": 64, "velocity": 100, "is_meta": false}
]
]
}
132 changes: 58 additions & 74 deletions tests/midi/notation_test.py
Original file line number Diff line number Diff line change
@@ -1,77 +1,61 @@
import json
import pytest

from pathlib import Path
from musiclib.midi import notation
from musiclib.midi.notation import IntervalEvent



@pytest.mark.parametrize('code, name', [
('flute 1 2 3 4', 'flute'),
('bass 9 8 -7 17 4 -12 0', 'bass'),
])
def test_voice_name(code, name):
assert notation.Voice(code).name == name


@pytest.mark.parametrize('code, intervals', [
(
'flute 1 2 3 4',
[
IntervalEvent(interval=1, on=0, off=96),
IntervalEvent(interval=2, on=96, off=192),
IntervalEvent(interval=3, on=192, off=288),
IntervalEvent(interval=4, on=288, off=384),
],
),
(
'flute 15 -3 .. -10 26 17 28 -- 17 29 -15 27 -8 -5 25 23',
[
IntervalEvent(interval=17, on=0, off=96),
IntervalEvent(interval=-3, on=96, off=288),
IntervalEvent(interval=-12, on=288, off=384),
IntervalEvent(interval=30, on=384, off=480),
IntervalEvent(interval=19, on=480, off=576),
IntervalEvent(interval=32, on=576, off=768),
IntervalEvent(interval=19, on=768, off=864),
IntervalEvent(interval=33, on=864, off=960),
IntervalEvent(interval=-17, on=960, off=1056),
IntervalEvent(interval=31, on=1056, off=1152),
IntervalEvent(interval=-8, on=1152, off=1248),
IntervalEvent(interval=-5, on=1248, off=1344),
IntervalEvent(interval=29, on=1344, off=1440),
IntervalEvent(interval=27, on=1440, off=1536),
],
),
])
def test_voice(code, intervals):
assert notation.Voice(code).intervals == intervals


code = '''\
header
version 2.2.1
root C1
ticks_per_beat 96
flute 13 29 13 26 22 22 -14 0 -1 4 2 19 11 27 -2 0
flute 4 -7 -8 7 5 18 18 23 26 24 12 3 -1 12 -13 17
piano 21 29 8 23 19 5 28 -8 26 16 -1 0 23 -7 27 25
bass 9 8 -7 17 4 -12 0 -6 -10 14 25 6 -3 13 -1 29
modulation
root A0
flute 16 5 24 15 0 7 -6 7 4 -15 2 26 12 20 16 -11
flute 15 -3 0 -10 26 17 28 28 17 29 -15 27 -8 -5 25 23
piano 9 19 -11 -15 16 -8 12 -6 10 7 22 22 24 29 18 -4
bass -13 28 7 5 8 1 16 9 -3 -11 -7 27 5 2 4 18
flutffsdfsdfsdf 16 5 24 15 0 7 -6 7 4 -15 2 26 12 20 16 -11
flutesdfsdfsd -3 0 -10 26 17 28 28 17 29 -15 27 -8 -5 25 23
pianosssssssssssssssssss 9 19 -11 -15 16 -8 12 -6 10 7 22 22 24 29 18 -4
bass -13 28 7 5 8 1 16 9 -3 -11 -7 27 5 2 4 18
'''


def test_notation():
notation.Notation(code)
from musiclib.midi import parse


@pytest.mark.parametrize(
'code, channel', [
('flute 1 2 3 4', 'flute'),
('bass 9 8 -7 17 4 -12 0', 'bass'),
],
)
def test_voice_channel(code, channel):
assert notation.Voice(code).channel == channel


@pytest.mark.parametrize(
'code, interval_events', [
(
'flute 1 2 3 4',
[
IntervalEvent(interval=1, on=0, off=96),
IntervalEvent(interval=2, on=96, off=192),
IntervalEvent(interval=3, on=192, off=288),
IntervalEvent(interval=4, on=288, off=384),
],
),
(
'flute 15 -3 .. -10 26 17 28 -- 17 29 -15 27 -8 -5 25 23',
[
IntervalEvent(interval=17, on=0, off=96),
IntervalEvent(interval=-3, on=96, off=288),
IntervalEvent(interval=-12, on=288, off=384),
IntervalEvent(interval=30, on=384, off=480),
IntervalEvent(interval=19, on=480, off=576),
IntervalEvent(interval=32, on=576, off=768),
IntervalEvent(interval=19, on=768, off=864),
IntervalEvent(interval=33, on=864, off=960),
IntervalEvent(interval=-17, on=960, off=1056),
IntervalEvent(interval=31, on=1056, off=1152),
IntervalEvent(interval=-8, on=1152, off=1248),
IntervalEvent(interval=-5, on=1248, off=1344),
IntervalEvent(interval=29, on=1344, off=1440),
IntervalEvent(interval=27, on=1440, off=1536),
],
),
],
)
def test_voice(code, interval_events):
assert notation.Voice(code).interval_events == interval_events


@pytest.mark.parametrize('example', [0])
def test_to_midi(example):
example_dir = Path(__file__).parent / f'data/notation/{example}'
code = (example_dir / 'code.txt').read_text()
with open(example_dir / 'midi.json') as f:
midi_dict = json.load(f)
assert parse.to_dict(notation.Notation(code).to_midi()) == midi_dict

0 comments on commit f598fe6

Please sign in to comment.