-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathTrackBuilder.py
148 lines (106 loc) · 3.23 KB
/
TrackBuilder.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
from midi import *
import sys
if len(sys.argv) != 2:
print "Usage: {0} <midifile>".format(sys.argv[0])
sys.exit(2)
class TrackBuilder:
bottom = 0
tick = 0
pending = []
def __init__( self, duration = 60, velocity = 70, channel = 0 ):
self.duration = duration
self.velocity = velocity
self.channel = channel
self.track = Track( tick_relative = False )
def _defaults( self, kw ):
if not 'tick' in kw:
kw['tick'] = self.tick
if not 'channel' in kw:
kw['channel'] = self.channel
def note( self, pitch, **kw ):
kw['pitch'] = pitch
if not 'duration' in kw:
kw['duration'] = self.duration
if not 'velocity' in kw:
kw['velocity'] = self.velocity
self._defaults(kw)
self.flush( kw['tick'] )
self._commit( NoteOnEvent( **kw ) )
kw['tick'] = kw['tick'] + kw['duration']
self._queue( NoteOffEvent( **kw ) )
return self.tick
def control( self, value, **kw ):
kw['value'] = value
self._defaults(kw)
self.flush( kw['tick'] )
self._commit( ControlChangeEvent( **kw ) )
def program( self, value, **kw ):
kw['value'] = value
self._defaults(kw)
self.flush( kw['tick'] )
self._commit( ProgramChangeEvent( **kw ) )
def _commit( self, event ):
if ( event.tick < self.bottom ):
raise Exception( "Cannot commit past events! %i < %i" % ( event.tick, self.bottom ) )
self.track.append( event )
self.bottom = event.tick
self.tick = max( self.tick, self.bottom )
def _queue( self, event ):
if ( event.tick < self.bottom ):
raise Exception( "Cannot queue past events! %i < %i" % ( event.tick, self.bottom ) )
self.pending.append( event )
self.tick = max( self.tick, event.tick )
def flush( self, upto = None ):
queue = sorted( self.pending, key = lambda event: event.tick )
self.pending = []
for event in queue:
if upto is None or event.tick < upto:
self._commit( event )
else:
self._queue( event )
def seek( self, tick ):
if ( tick < self.bottom ):
raise Exception( "Cannot seek back past played notes! %i < %i" % ( event.tick, self.bottom ) )
self.tick = tick
def shift( self, ticks ):
self.seek( self.tick + ticks )
def pause( self, notes ):
self.shift( notes * self.duration )
def close( self ):
self.flush()
self.track.append( EndOfTrackEvent( tick = self.tick ) )
track = self.track
self.track = None
return track
class ChordBuilder:
def __init__( self, trackBuilder ):
self.tick = trackBuilder.tick
self.trackBuilder = trackBuilder
def note( self, pitch, **kw ):
kw['pitch'] = pitch
if not 'tick' in kw:
kw['tick'] = self.tick
self.trackBuilder.note( **kw )
def makeTrack():
builder = TrackBuilder()
builder.note( C_3 )
builder.note( E_3 )
builder.note( G_3 )
builder.note( C_4 )
builder.pause( 2 )
chord = ChordBuilder( builder )
chord.note( C_3 )
chord.note( E_3 )
chord.note( G_3 )
chord.note( C_4 )
return builder.close()
def makeMidiFile( midifile ):
# Instantiate a MIDI Pattern (contains a list of tracks)
pattern = Pattern( format = 1, resolution= 220, tick_relative=False )
track = makeTrack()
pattern.append(track)
print pattern
# Save the pattern to disk
write_midifile(midifile, pattern)
midifile = sys.argv[1]
makeMidiFile( midifile )