]> git.donarmstrong.com Git - lilypond.git/blob - scripts/midi2ly.py
patch::: 1.5.11.jcn2
[lilypond.git] / scripts / midi2ly.py
1 #!@PYTHON@
2
3 import midi
4 import sys
5
6 scale_steps =[0,2,4,5,7,9,11]
7
8 def split_channels (track):
9         chs = {}
10         for i in range(16):
11                 chs[i] = []
12                 
13         for e in track:
14                 data = list (e[1])              
15                 c =  data[0] & 0x0f
16                 e = (e[0], tuple ([data[0] & 0xf0] + data[1:]))
17                 chs[c].append (e)
18
19         for i in range (16):
20                 if chs[i] == []:
21                         del chs[i]
22
23
24         for v in chs.values ():
25                 ns = notes_on_channel(v)
26                 ns = unthread_notes (ns)
27                 map (dump_thread, ns)
28                 
29         return chs
30
31
32 class Note:
33         def __init__(self, duration, pitch, velocity):
34                 self.velocity = velocity
35                 self.pitch = pitch
36                 self.duration = duration
37
38         def duration_compare(a,b):
39                 if a.duration < b.duration :
40                         return -1
41                 elif a.duration > b.duration:
42                         return 1
43                 else:
44                         return 0
45         
46 def notes_on_channel (channel):
47         pitches = {}
48
49         nch= []
50         for e in channel:
51                 t = e[0]
52
53                 if e[1][0] == midi.NOTE_ON:
54                         if not pitches.has_key (e[1][1]):
55                                 pitches[e[1][1]] = (t, e[1][2])
56                 elif e[1][0] == midi.NOTE_OFF:
57                         try:
58                                 (lt, vel) = pitches[e[1][1]]
59                                 del pitches[e[1][1]]
60                                 
61                                 nch.append ((t, Note (t-lt, e[1][1], vel)))
62                                 
63                         except KeyError:
64                                 pass
65                 else:
66                         pass
67         
68         return nch
69
70 def unthread_notes (channel):
71         threads = []
72         while channel:
73                 thread = []
74                 end_busy_t = 0
75                 start_busy_t = 0
76                 todo = []
77                 for e in channel:
78                         t = e[0]
79                         if (t == start_busy_t and e[1].duration + t == end_busy_t) \
80                             or t >= end_busy_t:
81                                 thread.append (e)
82                                 start_busy_t = t
83                                 end_busy_t = t + e[1].duration
84                         else:
85                                 todo.append(e)
86                 threads.append (thread)
87                 channel = todo
88
89         return threads
90
91 def gcd (a,b):
92         if b == 0:
93                 return a
94         c = a
95         while c: 
96                 c = a % b
97                 a = b
98                 b = c
99         return a
100         
101 def dump_skip (dt):
102         wholes = dt / (4*384)
103         dt = dt % (4*384);
104         quarters = dt / 384
105
106         # etc.
107         if (wholes):
108                 print 's1*%d' % wholes 
109         if quarters:
110                 print 's4*%d' % quarters 
111
112
113
114 def dump_duration (dur):
115         g = gcd (dur, 384)
116         sys.stdout.write ('4')
117         (p,q) = (dur / g, 384 / g)
118         if (p == 1 and q == 1) :
119                 pass
120         else:
121                 if p <> 1:      
122                         sys.stdout.write ('*%d'% p)
123                 if q <> 1:
124                         sys.stdout.write ('*%d'% q)
125         
126
127 def dump_note (note):
128         p = note.pitch
129         oct = p / 12
130         step = p % 12
131
132         i = 0
133         while  i < len (scale_steps):
134                 if scale_steps[i] > step:
135                         break
136                 i = i+1
137                 
138         i = i-1
139         str = chr (i + ord ('a'))
140         if scale_steps[i] <> step:
141                 str = str + 'is'
142
143         sys.stdout.write (' %s' % str);
144         dump_duration (note.duration)
145
146 def dump_chord (ch):
147         if len(ch) == 1:
148                 dump_note (ch[0])
149         else:
150                 sys.stdout.write ("<")
151                 map (dump_note, ch)
152                 sys.stdout.write ('>')
153
154 def dump_thread (thread):
155         last_e = None
156         chs = []
157         ch = []
158
159         for e in thread:
160                 if last_e and last_e[0] == e[0]:
161                         ch.append (e[1])
162                 else:
163                         if ch:
164                                 chs.append ( (last_e[0], ch))
165                                 
166                         ch = [e[1]]
167                         
168                 last_e = e
169
170         if ch: chs.append ((last_e[0] ,ch))
171         t = 0
172         last_t = 0
173
174         for ch in chs:
175                 t = ch[0]
176                 if t - last_t:
177                         dump_skip (t-last_t)
178                         
179                 dump_chord (ch[1])
180                 last_t = t + ch[1][0].duration
181
182 def dump_notes (channel):
183         on_hold = []
184         for e in channel:
185                 if e[0] <> last_t:
186                         dump_chord (on_hold)
187                         on_hold = []
188                         last_t = e[0]
189
190                 on_hold.append (e)
191                 
192
193
194         
195 def convert_midi (f):
196         str = open (f).read ()
197         midi_dump = midi.parse (str)
198
199         channels = []
200
201         for t in midi_dump[1]:
202                 channels.append (split_channels (t))
203
204
205 for f in sys.argv[1:]:
206     convert_midi (f)