-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompose.py
executable file
·87 lines (65 loc) · 3.08 KB
/
compose.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import json
from midiutil import MIDIFile
from grid.generate_grid import get_grid
from rhythm.generate_rhythm import get_rhythm
from notes.generate_notes import get_notes
import argparse
parser = argparse.ArgumentParser(description='Generate MIDI music')
parser.add_argument('--config-path', help='Path to config file with desired MIDI properties')
args = parser.parse_args()
assert args.config_path and len(args.config_path) > 0
stress_velocity_map = {
'stressed': 88,
'slightly stressed': 78,
'unstressed': 72
}
def get_config_json(config_path):
with open(config_path, 'r') as f:
return json.loads(f.read())
config_json = get_config_json(args.config_path)
print(f'Config properties:\n{json.dumps(config_json, indent=2)}')
tempo = config_json['beatsPerMinute']
grid_atom_map, grid_class_map = get_grid(config_json)
print(grid_atom_map)
fact_list_for_rhythm = []
for _, atom_list in grid_atom_map.items():
fact_list_for_rhythm.extend(atom_list)
rhythm_atom_map = get_rhythm(config_json, fact_list_for_rhythm)
print(rhythm_atom_map)
fact_list_for_notes = []
for _, atom_list in rhythm_atom_map.items():
fact_list_for_notes.extend(atom_list)
notes_class_map = get_notes(config_json, fact_list_for_notes)
print(notes_class_map)
assert 'ticks_per_beat' in grid_class_map and len(grid_class_map['ticks_per_beat']) == 1
assert 'note_number' in notes_class_map and len(notes_class_map['note_number']) > 0
assert 'note_ticks_start' in notes_class_map and len(notes_class_map['note_ticks_start']) == len(notes_class_map['note_number'])
assert 'note_ticks_duration' in notes_class_map and len(notes_class_map['note_ticks_duration']) == len(notes_class_map['note_number'])
assert 'note_stress' in notes_class_map and len(notes_class_map['note_stress']) == len(notes_class_map['note_number'])
assert 'note_pitch' in notes_class_map and len(notes_class_map['note_pitch']) == len(notes_class_map['note_number'])
ticks_per_beat: int = grid_class_map['ticks_per_beat'][0].ticks_per_beat
note_number_list = [x.grid_number for x in notes_class_map['note_number']]
note_ticks_start_map = {x.grid_number: x.start__grid__ticks for x in notes_class_map['note_ticks_start']}
note_ticks_duration_map = {x.grid_number: x.ticks__duration for x in notes_class_map['note_ticks_duration']}
note_stress_map = {x.grid_number: x.stress for x in notes_class_map['note_stress']}
note_pitch_map = {x.grid_number: x.pitch for x in notes_class_map['note_pitch']}
# Write MIDI file
MyMIDI = MIDIFile(
numTracks=1,
ticks_per_quarternote=ticks_per_beat,
eventtime_is_ticks=True
)
MyMIDI.addTempo(0, 0, tempo)
for note_number in sorted(note_number_list):
if note_pitch_map[note_number] > 0:
MyMIDI.addNote(
0, 0,
note_pitch_map[note_number],
note_ticks_start_map[note_number],
note_ticks_duration_map[note_number],
stress_velocity_map[note_stress_map[note_number]]
)
output_path = f'output/test-{args.config_path.replace(".json","")}.mid'
with open(output_path, 'wb') as output_file:
MyMIDI.writeFile(output_file)
print(f'Wrote {output_path}')