-
Notifications
You must be signed in to change notification settings - Fork 1
/
improvisor.py
160 lines (146 loc) · 4.6 KB
/
improvisor.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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import math
#from midiutil import MIDIFile
from bank import Bank
from chord import Chord
from phrase import Phrase
import numpy
from rhythm1 import Rhythm1
import random
OCTAVE = 12
class Improvisor:
def __init__(self):
self.letters = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11}
#self.MyMIDI = MIDIFile(1, True, True, False, 1, 120, True)
self.banks = {} # The bank of notes corresponding to a specific chord
self.rhythmBank = Rhythm1()
self.chords = []
self.phrases = []
self.patterns = []
self.tempo = 100
self.genre = "Bebop"
"""
Return the pitch of a note symbol
@param s: the note symbol
@return: the pitch represented by an integer in [0, 128)
"""
def GN(self, s):
letter = s[0]
num = s[1] if len(s) == 2 else s[2]
tweak = 0 if len(s) == 2 else (-1 if s[1] == 'b' else 1)
return self.letters[letter] + (int(num) + 1) * OCTAVE + tweak
"""
Return a list of pitches after transposition
@param notes: the list of pitches
@param interval: transpose by that number of semitone
@param up: tranpose upward
@return: the list of notes after tranposition
"""
def transpose(self, notes, interval, up=True):
result = [0] * len(notes)
for i in range(len(notes)):
result[i] = notes[i] + interval * (1 if up else -1)
return result
"""
Convert a list of chord symbols to a a list of Chord objects
Store the list of chords as chords
@param chords: a list of chord symbols
"""
def sheetIntepretor(self, chords):
temp = []
for i in range(len(chords)):
(chord, dur) = chords[i]
temp.append(Chord(chord, dur))
self.chords = temp
"""
Find out where each phrase begins and end
Set phrases as a list of list of chords
@param dynamics: the average dynamics value
"""
def deconstructor(self, dynamics=90):
# Determine the content of each phrase
chords = self.chords
phrases = []
sum = 0 # Once sum hits 2, append the phrase to phrases
chordsInPhrase = []
prevPost = random.randint(60, 70)
for chord in chords:
chordsInPhrase.append(chord)
sum += chord.dur
if(numpy.isclose(sum, 1) and len(chordsInPhrase) >= 3):
phrases.append(Phrase(chordsInPhrase, self.banks, self.rhythmBank,
dynamics, self.genre, 1, prevPost))
prevPost = phrases[-1].lastEnd
chordsInPhrase = []
sum = 0
elif(numpy.isclose(sum, 2) and len(chordsInPhrase) >= 2):
phrases.append(Phrase(chordsInPhrase, self.banks, self.rhythmBank,
dynamics, self.genre, 2, prevPost))
prevPost = phrases[-1].lastEnd
chordsInPhrase = []
sum = 0
elif(numpy.isclose(sum, 4)):
phrases.append(Phrase(chordsInPhrase, self.banks, self.rhythmBank,
dynamics, self.genre, 4, prevPost))
prevPost = phrases[-1].lastEnd
chordsInPhrase = []
sum = 0
self.phrases = phrases
"""
Connect all phrases into one list of tuple
@return the list of tuple
"""
def connect(self):
connected = []
currT = 0
for phrase in self.phrases:
temp = phrase.res
local = 0
for i in range(len(temp)):
connected.append([temp[i][0], temp[i][1] + currT, temp[i][2], temp[i][3]])
currT += int(round(phrase.dur * 480, 0))
return connected
"""
Expand the bank that maps chord name to an object that stores
all chords, scales and licks that may be useful for improvisation
"""
def expandBanks(self):
for chord in self.chords:
if(chord.name in self.banks):
continue
self.banks[chord.name] = Bank(chord)
"""
Generate the midi file that only contains the chord
"""
def purechordsFile(self):
return 42
"""
Generate 1) a midi file that only contains the chords
2) a midi file that solos over the chords
@param chords: a list of tuples of chord name and duration
@param tempo: tempo of the solo
@param genre: a string specifying the genre
"""
def generator(self, chords, tempo=100, genre="Ballad"):
self.tempo = tempo
self.genre = genre
self.sheetIntepretor(chords)
self.expandBanks()
self.deconstructor()
self.connect()
def printchords(self):
for chord in self.chords:
print(chord.quality + " ")
print("\n")
def printPhrases(self):
for phrase in self.phrases:
for chord in phrase:
print(chord.name + " ")
print("")
def merge(chords, durs):
res = []
if(len(chords) != len(durs)):
print(len(chords), len(durs))
return None
for i in range(len(chords)):
res.append((chords[i], durs[i]))
return res