-
Notifications
You must be signed in to change notification settings - Fork 0
/
Song.py
94 lines (72 loc) · 3.01 KB
/
Song.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
88
89
90
91
92
93
94
import mido
from mido import Message, MidiFile, MidiTrack, MetaMessage
import time
from Poem import Poem
class Song:
def __init__(self, bpm):
self.notes = []
self.bpm = bpm
self.beat = mido.bpm2tempo(bpm)
self._scale_intervals = [0, 2, 4, 5, 7, 9, 11, 12]
# define arpeggios
self._arpeggios = {"major": [0, 4, 7, 12],
"minor": [0, 3, 7, 12],
"diminished": [0, 3, 6, 12],
"major7": [0, 4, 7, 11],
"minor7": [0, 3, 7, 10],
"dom7": [0, 4, 7, 10]}
def sum_string(self, syllable):
sum = 0
for letter in str(syllable):
sum += ord(letter)
return sum
def song_from_poem(self, text_file):
poem = Poem(text_file) # read the file as a Poem object
# choose a root to our arpeggio
root = 60 + (poem.total_syllables() % 12)
# iterate over each syllable
for line in poem.lines:
chord_root = root + self._scale_intervals[(self.sum_string(str(line)) % 8)]
for word in line.words:
if chord_root == 1 or chord_root == 3 or chord_root == 5:
chord_type = "major"
else:
chord_type = "minor"
# determine pitch by summing characters
pitch = (self.sum_string(str(word))) % len(self._arpeggios[chord_type])
# determine duration by length of syllable
duration = (word.syllables % 3)+1
# add the new note to our local list of notes
self.notes.append(self.Note(chord_root+self._arpeggios[chord_type][pitch], duration))
def write_song(self, file_name):
mid = MidiFile(type=1) # 0 type means all messages are on one track
track_pitch = [0, -12]
track_num = 2
tracks = []
for i in range(track_num):
track = MidiTrack()
tracks.append(track)
mid.tracks.append(track)
track.append(MetaMessage("set_tempo", tempo=mido.bpm2tempo(self.bpm)))
for i, note in enumerate(self.notes):
tracks[i%track_num].append(Message("note_on", note=note.pitch+track_pitch[i%track_num], velocity=127, time=0))
tracks[i%track_num].append(Message("note_on", note=note.pitch+track_pitch[i%track_num], velocity=0, time=note.duration*mid.ticks_per_beat))
mid.save(file_name)
def play(self, midi_file, port):
outport = mido.open_output(port)
for msg in MidiFile(midi_file):
time.sleep(msg.time)
if not msg.is_meta:
outport.send(msg)
class Note:
def __init__(self, pitch, duration):
self.pitch = pitch
self.duration = duration
def __main__():
song = Song(360)
file = "mariner.mid"
song.song_from_poem("mariner.txt")
song.write_song(file)
# song.play(file, 'IAC Driver Bus 1')
if __name__ == "__main__":
__main__()