smml 0.0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +2 -0
- data/lib/smml/midi-percussion-map.txt +47 -0
- data/lib/smml/midi-programChange-list.txt +144 -0
- data/lib/smml/msm.rb +2270 -0
- data/lib/smml/version.rb +3 -0
- data/lib/smml.rb +6 -0
- data/smml.gemspec +23 -0
- metadata +81 -0
data/lib/smml/msm.rb
ADDED
@@ -0,0 +1,2270 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# -*- encoding: utf-8 -*-
|
3
|
+
require 'kconv'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
def hint
|
7
|
+
cmd=File.basename($0)
|
8
|
+
puts <<EOF
|
9
|
+
usage: #{cmd} -d \"dddd dr3 dddd r4 drdrdrdr dddd dr3\" -o outfile.mid -t bpm
|
10
|
+
#{cmd} -i infile.txt -o outfile.mid -t bpm
|
11
|
+
|
12
|
+
syntax: ...( will be changed time after time)
|
13
|
+
abcdefg =tone; capital letters are sharps. followed by number as length.
|
14
|
+
+- =octave change
|
15
|
+
r =rest
|
16
|
+
>< =tempo up-down(percent)
|
17
|
+
a4 =4 beats of note 'a'. in length words, integers or flout numbers can be used.
|
18
|
+
a =one beat of note 'a'. default length equals 1 now.
|
19
|
+
A*120 =120 ticks of note 'a #'
|
20
|
+
(v:60) =velocity set to 60 (0-127)
|
21
|
+
&(00 00) =set hex data directly. This can include ...
|
22
|
+
'$delta(240)' for deltaTime data making
|
23
|
+
'$se(F0 41 ..)' system exclusive to system exclusive message
|
24
|
+
(p:11) =ProgramChange channel here, instrument 11
|
25
|
+
(p:organ) =ProgramChange channel here, instrument ?(search word like 'organ' from list if exist)
|
26
|
+
map text must start with instrument number
|
27
|
+
channel number can be used, but not recommended. '(p:0,11)'
|
28
|
+
(key:-4) =transpose -4 except percussionSoundName like '_snare!'
|
29
|
+
[...] =repeat 2 times for first time
|
30
|
+
[...]3 =3 times of inside block []
|
31
|
+
/2:abcd/ =(triplet etc.) notes 'abcd' in 2 beats measure
|
32
|
+
/:abc/ =triplet 'a''b''c' in one beat.
|
33
|
+
/*120:abcd/ = notes 'abcd' in 120 ticks measure. now, default measure is 480 ticks per one beat.
|
34
|
+
/:cd/ ~2e /:~fga/ =(tie) each length : c 0.5 d 0.5+2 e 1+0.25 f 0.25 g 0.25 a 0.25
|
35
|
+
after '~' length needed. if not length 1 is automaticaly inserted.
|
36
|
+
'c~~~' = 'c4'
|
37
|
+
= = same note and length as the previous note. 'c2c2c2c2' = 'c2==='
|
38
|
+
(tempo:120) =tempo set
|
39
|
+
(ch:1) =set this track's channel 1
|
40
|
+
(cc:10,64) =controlChange number10 value 64. see SMF format.
|
41
|
+
(pan:>64) =panpot right 64. ( pan:>0 set center )
|
42
|
+
(bend:100) =pitch bend 100
|
43
|
+
(bendRange:12) =set bend range 12. default is normaly 2.
|
44
|
+
(bendCent:on) =set bend value unit cent (half tone = 100). default is 'off' and value is between -8192 and +8192.
|
45
|
+
'(bendCent:off)(bend:8192)' = '(bendCent:on)(bend:100)'
|
46
|
+
(on:a) =note 'a' sound on only. take no ticks.; the event 'a' is the same as '(on:a)(wait:1)(off:a)'.
|
47
|
+
(wait:1) =set waiting time 1 for next event
|
48
|
+
(off:a) =note 'a' sound off
|
49
|
+
(g:10) =set sound gate-rate 10% (staccato etc.)
|
50
|
+
{64} =tone '64' by absolute tone number. ='(x:64)'
|
51
|
+
{c,e,g} =multi tone. use similar way to tone 'a' etc. = '(on:c)(on:e)(on:g)(wait:1)(off:c)(off:e)(off:g)'
|
52
|
+
:cmaj7, =use chord name. the first letter is tone name 'c'. so using capital one is with sharp.
|
53
|
+
(stroke:4) =chord stroke interval ticks '4'. if '-4' down-up reversed.
|
54
|
+
(V:o,o,110) =preceding modifier velocities. if next notes are 'abc' ,third tone 'c' is with velocity 110. a blank or 'o' mean default value.
|
55
|
+
(G:,,-) =preceding modifier gate rates. if next notes are 'abc' ,third tone 'c' is with gate rate shorter.
|
56
|
+
new preceding modifiers cancel old rest preceding values.
|
57
|
+
^ =accent
|
58
|
+
` =too fast note, play ahead
|
59
|
+
' =too late note, lay back
|
60
|
+
(-)c =c flat
|
61
|
+
(+)c =c sharp; equals 'C'
|
62
|
+
(+2)c,(++)c =c double sharp
|
63
|
+
(0)c =c natural
|
64
|
+
(gm:on)
|
65
|
+
(gs:reset)
|
66
|
+
(xg:on)
|
67
|
+
(syswait:) =when using '(gm:on)' etc., this command is needed for all other tracks to adjust wait-time.
|
68
|
+
||| = track separater
|
69
|
+
/// = page separater
|
70
|
+
(mark:posname) =position name for adjustment of tracks after long rest etc.
|
71
|
+
.DC .DS .toCODA .CODA .FINE =coda mark etc.
|
72
|
+
.SKIP =skip mark on over second time
|
73
|
+
.$ =DS point
|
74
|
+
_snare! =percussion sound ( search word like 'snare' (can use tone number) from percussion list if exist )
|
75
|
+
similarly, _s!=snare, k:bassKick, o:openHighHat, c:closedHighHat, cc:CrachCymbal, h:highTom, l:lowTom as default.
|
76
|
+
map text personaly you set must start with tone number.
|
77
|
+
(loadf:filename.mid,2) =load filename.mid, track 2. Track must be this only and seperated by '|||'.
|
78
|
+
W:=abc =macro definition. One Charactor macro can be used. use prefix '$' for refering.
|
79
|
+
macro W:=abc =macro definition.
|
80
|
+
fn(x):=ab$x =macro with args. in this case, '$fn(10)' is substituded by 'ab10'. similarly,
|
81
|
+
'$fn(:10,20,30)' = 'ab10ab20ab30'.
|
82
|
+
'$fn(4:10,20,30)' = 'ab10(wait:4)ab20(wait:4)ab30'.
|
83
|
+
compile order is : page,track seperate => macro set and replace => repeat check => sound data make
|
84
|
+
; =seperater. same to a new line
|
85
|
+
blank =ignored
|
86
|
+
;; comment =ignored after ';;' of each line
|
87
|
+
;;;;;; =start mark of multi-line comment. end mark is same or longer mark of ';;'. these must start from the top of line.
|
88
|
+
|
89
|
+
basicaly, one sound is a tone command followed by length number. now, tone type commands are :
|
90
|
+
'c', '{64}', '_snare!', '{d,g,-b}', ':cmaj7,'
|
91
|
+
and other commands are with parentheses.
|
92
|
+
EOF
|
93
|
+
end
|
94
|
+
|
95
|
+
1.round(2) rescue (
|
96
|
+
class Float
|
97
|
+
def round n=0
|
98
|
+
c=10**(n+1)
|
99
|
+
f=(((self*c).to_i+5)/10).to_f/(10**n)
|
100
|
+
n>0 ? f : f.to_i
|
101
|
+
end
|
102
|
+
end
|
103
|
+
class Fixnum
|
104
|
+
def round n=0
|
105
|
+
return self if n==0
|
106
|
+
c=10**(n+1)
|
107
|
+
(((self*c).to_i+5)/10).to_f/(10**n)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
)
|
111
|
+
def multilineTrim l,com
|
112
|
+
r=[]
|
113
|
+
on=false
|
114
|
+
mark=""
|
115
|
+
l.each{|i|
|
116
|
+
if on
|
117
|
+
on=false if i=~/^#{mark}/
|
118
|
+
else
|
119
|
+
i=~/^(#{com}#{com}+)/
|
120
|
+
if $&
|
121
|
+
mark=$1
|
122
|
+
on=true
|
123
|
+
else
|
124
|
+
r<<i
|
125
|
+
end
|
126
|
+
end
|
127
|
+
}
|
128
|
+
puts " ? mismatch end mark of multiline comment." if on
|
129
|
+
r
|
130
|
+
end
|
131
|
+
class String
|
132
|
+
def setcmark c
|
133
|
+
@@cmark=c
|
134
|
+
end
|
135
|
+
def cmark
|
136
|
+
@@cmark
|
137
|
+
end
|
138
|
+
def trim ofs="",com=@@cmark
|
139
|
+
lines=self.split("\n")
|
140
|
+
d=multilineTrim(lines,com)
|
141
|
+
d=d.map{|i|i.sub(/(#{com}).*/){}.chomp}*ofs
|
142
|
+
d
|
143
|
+
end
|
144
|
+
def sharp2cmark
|
145
|
+
self.gsub!("#"){@@cmark}
|
146
|
+
end
|
147
|
+
def tracks pspl
|
148
|
+
tracks={}
|
149
|
+
pages=self.split(/#{pspl}+/)
|
150
|
+
pages.each{|p|
|
151
|
+
p.split('|||').each_with_index{|t,i|
|
152
|
+
if tracks[i]
|
153
|
+
tracks[i] << t
|
154
|
+
else
|
155
|
+
tracks[i] = [t]
|
156
|
+
end
|
157
|
+
}
|
158
|
+
}
|
159
|
+
tracks.keys.sort.map{|k|tracks[k]*";"}
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
class Array
|
165
|
+
def rotatePlus
|
166
|
+
self[1..-1]+[self.first+12]
|
167
|
+
end
|
168
|
+
def rotateMinus
|
169
|
+
[self.last-12]+self[0..-2]
|
170
|
+
end
|
171
|
+
def orotate n=1
|
172
|
+
r=self
|
173
|
+
if n>0
|
174
|
+
n.times{r=r.rotatePlus}
|
175
|
+
else
|
176
|
+
(-n).times{r=r.rotateMinus}
|
177
|
+
end
|
178
|
+
r
|
179
|
+
end
|
180
|
+
end
|
181
|
+
def unirand n,c,reset=false
|
182
|
+
a=[0,n]
|
183
|
+
while c>a.size
|
184
|
+
t=rand(n-2)+1
|
185
|
+
a<<t if ! a.member?(t)
|
186
|
+
end
|
187
|
+
a.sort_by{rand}
|
188
|
+
end
|
189
|
+
|
190
|
+
class Event
|
191
|
+
attr_accessor :type, :time, :value
|
192
|
+
def initialize ty=:e,*arg
|
193
|
+
@type=ty
|
194
|
+
@pos=0
|
195
|
+
@value=""
|
196
|
+
case @type
|
197
|
+
when :c,:raw
|
198
|
+
@time=0
|
199
|
+
@value=arg[0]
|
200
|
+
when :ahead
|
201
|
+
@time=arg[0]
|
202
|
+
when :o,:off
|
203
|
+
when :mark
|
204
|
+
@mark,@track,@value=arg
|
205
|
+
else
|
206
|
+
@time=arg[0]
|
207
|
+
@value=arg[1]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
def data
|
211
|
+
case @type
|
212
|
+
when :raw
|
213
|
+
rawdata(@value)
|
214
|
+
when :mark
|
215
|
+
"# marktrack(#{@track}_#{@mark}) [#{@value}]\n"
|
216
|
+
when :c,:o,:off
|
217
|
+
@value
|
218
|
+
else
|
219
|
+
varlenHex(@time)+@value
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
# arg=[steps],[values]
|
224
|
+
def mymerge span,*arg
|
225
|
+
r=[]
|
226
|
+
arg.each{|ar|
|
227
|
+
next if ! ar
|
228
|
+
m,steps,vs=ar
|
229
|
+
steps=[steps] if steps.class!=Array
|
230
|
+
steps=steps*(vs.size-1) if steps.size==1
|
231
|
+
r<<[0,m,vs[0]]
|
232
|
+
if vs.size>1
|
233
|
+
stepsum=steps.inject{|s,i|s+i}
|
234
|
+
vsize=vs.size-1
|
235
|
+
if stepsum>span
|
236
|
+
rate=span*1.0/stepsum
|
237
|
+
steps=steps.map{|i|(i*rate).to_i}
|
238
|
+
end
|
239
|
+
n=1
|
240
|
+
r+=vs[1..-1].map{|i|
|
241
|
+
t=steps.shift*n
|
242
|
+
n+=1
|
243
|
+
[t,m,i]
|
244
|
+
}
|
245
|
+
end
|
246
|
+
}
|
247
|
+
n=0
|
248
|
+
all=r.sort_by{|t,m,e|t}
|
249
|
+
rest=span-all[-1][0]
|
250
|
+
all.map{|t,m,e|
|
251
|
+
tt=t-n
|
252
|
+
n=t
|
253
|
+
[tt,m,e]
|
254
|
+
}+[[rest,:rest]]
|
255
|
+
end
|
256
|
+
def rawdata d
|
257
|
+
d.gsub(","){" "}
|
258
|
+
end
|
259
|
+
def trackSizeHex d,cmark="#"
|
260
|
+
d=d.trim("",cmark).split.join
|
261
|
+
i=(d.size+8)/2
|
262
|
+
# p [d,i,i.to_s(16)]
|
263
|
+
#("00000000"+i.to_s(16))[-8..-1]
|
264
|
+
format("%08x",i)+" # size: #{i}"
|
265
|
+
end
|
266
|
+
# 可変長数値表現
|
267
|
+
# 7bitずつに区切り最後以外のbyteは先頭bitを立てる
|
268
|
+
def varlen(v)
|
269
|
+
if v < 0x80
|
270
|
+
return v
|
271
|
+
else
|
272
|
+
v1 = v & 0b01111111
|
273
|
+
v2=(v-v1)>>7
|
274
|
+
v2 =varlen(v2)
|
275
|
+
return [v2,v1]
|
276
|
+
end
|
277
|
+
end
|
278
|
+
def hex2digit d
|
279
|
+
if d.class==String
|
280
|
+
if d=~/^0(x|X)/
|
281
|
+
d=d.to_i(16)
|
282
|
+
else
|
283
|
+
d=d.to_i
|
284
|
+
end
|
285
|
+
end
|
286
|
+
d
|
287
|
+
end
|
288
|
+
def varlenHex(v)
|
289
|
+
v=hex2digit(v)
|
290
|
+
raise if v<0
|
291
|
+
b=[varlen(v.round)]
|
292
|
+
b=b.flatten
|
293
|
+
c=b[0..-2].map{|i| i | 0x80 }
|
294
|
+
r=[c,b[-1]].flatten
|
295
|
+
res=0
|
296
|
+
r.each{|i|
|
297
|
+
res=res*0x100+i
|
298
|
+
}
|
299
|
+
format("%0#{b.size*2}x",res)
|
300
|
+
end
|
301
|
+
def hext2hex d
|
302
|
+
ar=d.split(/[ ,]+/)-[""]
|
303
|
+
hex=ar.map{|i|i.size==2}.uniq==[true]
|
304
|
+
lasth=ar.map{|i|true if i=~/h$/}.uniq==[true]
|
305
|
+
zx=ar.map{|i|true if i=~/^0x/}.uniq==[true]
|
306
|
+
r=ar.map{|i|i[0..1]} if lasth
|
307
|
+
r=ar.map{|i|i[2..-1]} if zx
|
308
|
+
r=ar if hex
|
309
|
+
r
|
310
|
+
end
|
311
|
+
def rolandcheck d
|
312
|
+
return d if $ignoreChecksum
|
313
|
+
if d[1]=="41"
|
314
|
+
org=d[2]
|
315
|
+
s=0
|
316
|
+
[*5..(d.size-3)].each{|i|s+=d[i].to_i(16)}
|
317
|
+
csum=0x80-s%0x80
|
318
|
+
if $DEBUG && csum!=org.to_i(16)
|
319
|
+
"# sysEx: roland check sum bad?"
|
320
|
+
end
|
321
|
+
d[-2]=format("%02X",csum)
|
322
|
+
end
|
323
|
+
d
|
324
|
+
end
|
325
|
+
def sysEx2mes d
|
326
|
+
r=hext2hex(d)
|
327
|
+
r=rolandcheck(r)
|
328
|
+
"#{r[0]} #{varlenHex(r.size-1)} #{r[1..-1]*" "}"
|
329
|
+
end
|
330
|
+
def txt2hex t
|
331
|
+
r=[]
|
332
|
+
t.each_byte{|i|
|
333
|
+
r<<format("%02x",i)
|
334
|
+
}
|
335
|
+
size=r.size
|
336
|
+
[r*" ",varlenHex(size)]
|
337
|
+
end
|
338
|
+
def bendHex d
|
339
|
+
c=d.to_i+8192
|
340
|
+
c=[[c,0].max,16383].min
|
341
|
+
a=c>>7
|
342
|
+
b=c & 0b01111111
|
343
|
+
r=[a,b,b*0x100+a]
|
344
|
+
format("%04x ",r[2])
|
345
|
+
end
|
346
|
+
|
347
|
+
module MidiRead
|
348
|
+
def self.msplit s,dat
|
349
|
+
li=s.split('')
|
350
|
+
dat=dat.split('')
|
351
|
+
block={}
|
352
|
+
num=0
|
353
|
+
li.size.times{|i|
|
354
|
+
num+=1 if li[i,4]==dat
|
355
|
+
block[num] ? block[num]+=li[i] : block[num]=li[i]
|
356
|
+
}
|
357
|
+
block.keys.sort.map{|i|block[i]}
|
358
|
+
end
|
359
|
+
def self.head d
|
360
|
+
return @head if @head
|
361
|
+
r=self.msplit(d,'MTrk')
|
362
|
+
@head,@tracks=r[0],r[1..-1]
|
363
|
+
@head
|
364
|
+
end
|
365
|
+
def self.tracks d
|
366
|
+
return @tracks if @tracks
|
367
|
+
r=self.msplit(d,'MTrk')
|
368
|
+
@head,@tracks=r[0],r[1..-1]
|
369
|
+
@tracks
|
370
|
+
end
|
371
|
+
def self.read file,tracknum=false
|
372
|
+
@head=false
|
373
|
+
@tracks=false
|
374
|
+
d=""
|
375
|
+
if File.exist?(file)
|
376
|
+
open(file,"rb"){|f|
|
377
|
+
d=f.read
|
378
|
+
}
|
379
|
+
@head=self.head(d)
|
380
|
+
@tracks=self.tracks(d)
|
381
|
+
else
|
382
|
+
STDERR.puts" can't read file #{file}"
|
383
|
+
end
|
384
|
+
if tracknum
|
385
|
+
tracknum<tracks.size ? @tracks[tracknum-1] : @tracks[0]
|
386
|
+
else
|
387
|
+
[@head,@tracks]
|
388
|
+
end
|
389
|
+
end
|
390
|
+
def self.readtrack file,num=false
|
391
|
+
self.read file
|
392
|
+
if num
|
393
|
+
@tracks[num]
|
394
|
+
else
|
395
|
+
@tracks
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
def apply d,macro
|
400
|
+
d.scan(/\$\{[^ ;\$_*^,\)\(`'\/+-]+\}|\$[^ ;\$_*^,\)\(`'\/+-]+|./).map{|i|
|
401
|
+
case i
|
402
|
+
when /^\$\{([^ ;\$_*^,\)\(`'\/+-]+)\}|^\$([^ ;\$_*^,\)\(`'\/+-]+)/
|
403
|
+
key=$1||$2
|
404
|
+
if macro.keys.member?(key)
|
405
|
+
macro[key]
|
406
|
+
else
|
407
|
+
i
|
408
|
+
end
|
409
|
+
else
|
410
|
+
i
|
411
|
+
end
|
412
|
+
}*""
|
413
|
+
end
|
414
|
+
def rawHexPart d,macro={}
|
415
|
+
li=d.scan(/\$se\([^)]*\)|\$delta\([^)]*\)|\$bend\([^)]*\)|\(bend:[^)]*\)|\(expre:[^)]*\)|./)
|
416
|
+
res=[]
|
417
|
+
li.map{|i|
|
418
|
+
case i
|
419
|
+
when /\$se\(([^)]*)\)/
|
420
|
+
d=apply($1,macro)
|
421
|
+
sysEx2mes(d)
|
422
|
+
when /\$delta\(([^)]*)\)/
|
423
|
+
varlenHex($1)
|
424
|
+
when /\$bend\(([^)]*)\)/
|
425
|
+
d=apply($1,macro)
|
426
|
+
bendHex(d)
|
427
|
+
when /\(bend:([^)]*)\)/
|
428
|
+
"_b__#{$1.split(',')*"_"}?"
|
429
|
+
when /\(expre:([^)]*)\)/
|
430
|
+
"_e__#{$1.split(',')*"_"}?"
|
431
|
+
else
|
432
|
+
i
|
433
|
+
end
|
434
|
+
}*""
|
435
|
+
end
|
436
|
+
def revertPre d
|
437
|
+
d.gsub(/_b__([^?]*)\?/){"(bend:#{$1.split("_")*","})"}.
|
438
|
+
gsub(/_e__([^?]*)\?/){"(expre:#{$1.split("_")*","})"}
|
439
|
+
end
|
440
|
+
def worddata word,d
|
441
|
+
d=~/\(#{word}:(([[:digit:].]+),)?([-,.[:digit:]]+)\)/
|
442
|
+
if $&
|
443
|
+
pos=$1 ? $2.to_i : 0
|
444
|
+
depth=$3.split(',').map{|i|i.to_f}
|
445
|
+
[:"#{word}",pos,depth]
|
446
|
+
else
|
447
|
+
false
|
448
|
+
end
|
449
|
+
end
|
450
|
+
class OrderedSet
|
451
|
+
def initialize
|
452
|
+
@cyclecheck=false
|
453
|
+
end
|
454
|
+
def cyclecheck on=true
|
455
|
+
@cyclecheck=on
|
456
|
+
self
|
457
|
+
end
|
458
|
+
def orderby smaller,a,b
|
459
|
+
if smaller[a].member?(b)
|
460
|
+
return -1
|
461
|
+
elsif smaller[b].member?(a)
|
462
|
+
return 1
|
463
|
+
end
|
464
|
+
0
|
465
|
+
end
|
466
|
+
def ccheck s,depth=2
|
467
|
+
return false if depth<1 or not @cyclecheck
|
468
|
+
keys=s.keys
|
469
|
+
keys.each{|i|
|
470
|
+
tmp=s[i]
|
471
|
+
depth.times{
|
472
|
+
tmp=tmp.map{|k|[k,s[k]]}.flatten
|
473
|
+
}
|
474
|
+
if tmp.member?(i)
|
475
|
+
p "bad cycle #{i}, depth:#{depth}"
|
476
|
+
return true
|
477
|
+
end
|
478
|
+
}
|
479
|
+
false
|
480
|
+
end
|
481
|
+
def smerge o, depth=2
|
482
|
+
all=o.flatten.uniq
|
483
|
+
smaller={}
|
484
|
+
all.each{|i|
|
485
|
+
o.each{|k|
|
486
|
+
if k.member?(i)
|
487
|
+
smaller[i]=smaller[i] ? smaller[i]+k.select{|n|k.index(i)<k.index(n)} : k.select{|n|k.index(i)<k.index(n)}
|
488
|
+
end
|
489
|
+
}
|
490
|
+
}
|
491
|
+
raise if ccheck(smaller,depth)
|
492
|
+
smaller
|
493
|
+
end
|
494
|
+
def calc smaller,ar
|
495
|
+
s=ar.size
|
496
|
+
ar.each{|i|
|
497
|
+
ind=ar.index(i)
|
498
|
+
(s-ind-1).times{|n|
|
499
|
+
j=ar[ind+n+1]
|
500
|
+
r=orderby(smaller,i,j)
|
501
|
+
puts "#{r} #{i} #{j}" if $DEBUG && $debuglevel>1
|
502
|
+
return false if r>0
|
503
|
+
}
|
504
|
+
}
|
505
|
+
true
|
506
|
+
end
|
507
|
+
def common a,b
|
508
|
+
(a+b).uniq-(a-b)-(b-a)
|
509
|
+
end
|
510
|
+
def omerge base,o
|
511
|
+
return base if o==[[]]
|
512
|
+
return o[0] if base.size==0 && o.size==1
|
513
|
+
rest=o-[base]
|
514
|
+
r=base.dup
|
515
|
+
rest.each{|i|
|
516
|
+
n=i.dup
|
517
|
+
cmn=common(r,n)
|
518
|
+
if cmn.size>0
|
519
|
+
p1,p2=cmn[0],cmn[-1]
|
520
|
+
r1=r.select{|j|r.index(j)<r.index(p1)}
|
521
|
+
r3=r.select{|j|r.index(j)>r.index(p2)}
|
522
|
+
r2=r-r1-r3-[p1,p2]
|
523
|
+
n1=n.select{|j|n.index(j)<n.index(p1)}
|
524
|
+
n3=n.select{|j|n.index(j)>n.index(p2)}
|
525
|
+
n2=n-n1-n3-[p1,p2]
|
526
|
+
if p1==p2
|
527
|
+
mid=[p1]
|
528
|
+
else
|
529
|
+
mid=[p1]+omerge(r2,[n2])+[p2]
|
530
|
+
end
|
531
|
+
r=omerge(r1,[n1])+mid+omerge(r3,[n3])
|
532
|
+
else
|
533
|
+
ins0=r.size
|
534
|
+
n.size.times{
|
535
|
+
pop=n.pop
|
536
|
+
size=r.size
|
537
|
+
ins=[rand(size+1),ins0].min
|
538
|
+
r.insert(ins,pop)
|
539
|
+
ins0=ins
|
540
|
+
}
|
541
|
+
end
|
542
|
+
}
|
543
|
+
r
|
544
|
+
end
|
545
|
+
def sort o, depth=2
|
546
|
+
return [] if o==[]
|
547
|
+
stime=Time.now
|
548
|
+
o=o.sort_by{|i|i.size}
|
549
|
+
base=o[-1]
|
550
|
+
begin
|
551
|
+
s=smerge(o,depth)
|
552
|
+
rescue
|
553
|
+
return []
|
554
|
+
end
|
555
|
+
f=o.flatten.uniq
|
556
|
+
rest=f-base
|
557
|
+
c=0
|
558
|
+
cc=0
|
559
|
+
print "sort" if $DEBUG
|
560
|
+
while 1
|
561
|
+
c+=1
|
562
|
+
print "," if $DEBUG && c%20==0
|
563
|
+
r=rest.sort_by{|i|rand(rest.size*2+c)-s[i].size}
|
564
|
+
begin
|
565
|
+
cc+=1
|
566
|
+
f=omerge(base,o)
|
567
|
+
end until f
|
568
|
+
break if calc(s,f)
|
569
|
+
p f if $DEBUG && $debuglevel>1
|
570
|
+
end
|
571
|
+
t=Time.now-stime
|
572
|
+
puts " try: #{c} (check #{cc}) #{t}sec." if $DEBUG || t>10
|
573
|
+
f
|
574
|
+
end
|
575
|
+
end
|
576
|
+
class MarkTrack
|
577
|
+
def initialize
|
578
|
+
@mt={}
|
579
|
+
@maxtrack=0
|
580
|
+
@marks=[]
|
581
|
+
@markstracks={}
|
582
|
+
@added={}
|
583
|
+
@diff={}
|
584
|
+
end
|
585
|
+
def makekey t,m
|
586
|
+
"#{t}_#{m}"
|
587
|
+
end
|
588
|
+
def set m,t,pos
|
589
|
+
@maxtrack=t if t>@maxtrack
|
590
|
+
@marks<<m if not @marks.member?(m)
|
591
|
+
@markstracks[t]=[] if not @markstracks[t]
|
592
|
+
@markstracks[t]<<m if not @markstracks[t].member?(m)
|
593
|
+
@mt[makekey(t,m)]=pos
|
594
|
+
end
|
595
|
+
def getcount m,t
|
596
|
+
key=makekey(t,m)
|
597
|
+
@mt.keys.select{|k|k==key||k=~/^#{key}@/}.size
|
598
|
+
end
|
599
|
+
def get m,t
|
600
|
+
@mt[makekey(t,m)]
|
601
|
+
end
|
602
|
+
def getmax m
|
603
|
+
[*1..@maxtrack].map{|t|
|
604
|
+
key=makekey(t,m)
|
605
|
+
(@mt[key] ? @mt[key] : 0)+(@added[t] ? @added[t] : 0)
|
606
|
+
}.max
|
607
|
+
end
|
608
|
+
def sortmark
|
609
|
+
marks=@marks
|
610
|
+
s=[]
|
611
|
+
s=@markstracks.keys.map{|k|@markstracks[k]}
|
612
|
+
OrderedSet.new.cyclecheck.sort(s)
|
613
|
+
end
|
614
|
+
def calc
|
615
|
+
marks=sortmark
|
616
|
+
p marks if $DEBUG
|
617
|
+
@diff={}
|
618
|
+
@added={}
|
619
|
+
marks.each{|k|
|
620
|
+
max=getmax(k)
|
621
|
+
@maxtrack.times{|i|
|
622
|
+
t=i+1
|
623
|
+
key=makekey(t,k)
|
624
|
+
if @mt[key]
|
625
|
+
@diff[key]=max-@mt[key]-(@added[t]||0)
|
626
|
+
@added[t]=@added[t] ? @added[t]+@diff[key] : @diff[key]
|
627
|
+
end
|
628
|
+
}
|
629
|
+
}
|
630
|
+
puts ["mt:",@mt],["diff",@diff],["added",@added] if $DEBUG
|
631
|
+
@diff
|
632
|
+
end
|
633
|
+
end
|
634
|
+
module MidiHex
|
635
|
+
# 設定のため最初に呼ばなければならない
|
636
|
+
def self.prepare bpm,tbase=480,vel=0x40,oct=:near,vfuzzy=2
|
637
|
+
@startBpm=bpm
|
638
|
+
@midiname=false
|
639
|
+
@cmark="#"
|
640
|
+
@marktrack=MarkTrack.new
|
641
|
+
@octmode=oct
|
642
|
+
@tbase=tbase
|
643
|
+
@gateRate=100
|
644
|
+
@nowtime=0
|
645
|
+
@onlist=[]
|
646
|
+
@waitingtime=0
|
647
|
+
@rythmChannel=9
|
648
|
+
@notes={
|
649
|
+
"c"=>0,
|
650
|
+
"C"=>1,
|
651
|
+
"d"=>2,
|
652
|
+
"D"=>3,
|
653
|
+
"e"=>4,
|
654
|
+
"f"=>5,
|
655
|
+
"F"=>6,
|
656
|
+
"g"=>7,
|
657
|
+
"G"=>8,
|
658
|
+
"a"=>9,
|
659
|
+
"A"=>10,
|
660
|
+
"b"=>11,
|
661
|
+
"t"=>[0,@rythmChannel],
|
662
|
+
"s"=>[3,@rythmChannel],
|
663
|
+
"u"=>[6,@rythmChannel]
|
664
|
+
}
|
665
|
+
@ch=0
|
666
|
+
@velocity=vel
|
667
|
+
@velocityOrg=vel
|
668
|
+
@velocityFuzzy=vfuzzy
|
669
|
+
@accentPlus=10
|
670
|
+
@basekey=0x3C
|
671
|
+
@chordCenter=@chordCenterOrg=@basekey
|
672
|
+
@basekeyRythm=@basekeyOrg=@basekey
|
673
|
+
@bendrange=2
|
674
|
+
@bendCent=1
|
675
|
+
@prepareSet=[@tbase,@ch,@velocity,@velocityFuzzy,@basekey,@gateRate,@bendrange,@bendCent]
|
676
|
+
@chmax=15
|
677
|
+
@bendrangemax=127
|
678
|
+
file="midi-programChange-list.txt"
|
679
|
+
pfile="midi-percussion-map.txt"
|
680
|
+
base=File.dirname(__FILE__)
|
681
|
+
file=File.expand_path(file,base) if not File.exist?(file)
|
682
|
+
pfile=File.expand_path(pfile,base) if not File.exist?(pfile)
|
683
|
+
self.loadProgramChange(file)
|
684
|
+
self.loadPercussionMap(pfile)
|
685
|
+
end
|
686
|
+
def self.setmidiname name
|
687
|
+
@midiname=name
|
688
|
+
end
|
689
|
+
def self.getmidiname
|
690
|
+
@midiname
|
691
|
+
end
|
692
|
+
def self.setfile name
|
693
|
+
@title=false
|
694
|
+
cmark="".cmark
|
695
|
+
if name && File.exist?(name)
|
696
|
+
list=File.readlines(name).select{|i|i=~/^#{cmark}/}
|
697
|
+
t=list.map{|i|i=~/^#{cmark} *title */;$'}-[nil]
|
698
|
+
@title=t[0].chomp if t.size>0
|
699
|
+
m=list.map{|i|i=~/^#{cmark} *midifilename */;$'}-[nil]
|
700
|
+
if m.size>0
|
701
|
+
@midiname=m[0].chomp
|
702
|
+
@midiname+=".mid" if @midiname !~ /\.mid$/
|
703
|
+
end
|
704
|
+
end
|
705
|
+
@data=File.read(name).trim(" ;").toutf8 if name && File.exist?(name)
|
706
|
+
end
|
707
|
+
def self.getdata
|
708
|
+
@data
|
709
|
+
end
|
710
|
+
def self.setdata d
|
711
|
+
@data=d
|
712
|
+
end
|
713
|
+
def self.accent a
|
714
|
+
@accentPlus=a.to_i
|
715
|
+
end
|
716
|
+
def self.setGateRate g
|
717
|
+
@gateRate=[g,100].min
|
718
|
+
end
|
719
|
+
def self.bendRange v
|
720
|
+
case v
|
721
|
+
when /^\+/
|
722
|
+
@bendrange+=$'.to_i
|
723
|
+
when /^\-/
|
724
|
+
@bendrange-=$'.to_i
|
725
|
+
else
|
726
|
+
@bendrange=v.to_i
|
727
|
+
end
|
728
|
+
@bendrange=[[@bendrange,@bendrangemax].min,0].max
|
729
|
+
r=[]
|
730
|
+
r<<self.controlChange("101,0")
|
731
|
+
r<<self.controlChange("100,0")
|
732
|
+
r<<self.controlChange("6,#{@bendrange}")
|
733
|
+
r
|
734
|
+
end
|
735
|
+
def self.bendCent on
|
736
|
+
@bendCent=1
|
737
|
+
@bendCent=8192/@bendrange/100.0 if on
|
738
|
+
end
|
739
|
+
def self.trackPrepare tc=0
|
740
|
+
@tbase,@ch,@velocity,@velocityFuzzy,@basekey,@gateRate,@bendrange,@bendCent=@prepareSet
|
741
|
+
@strokespeed=0
|
742
|
+
@preGate=[]
|
743
|
+
@preVelocity=[]
|
744
|
+
@preNote=[]
|
745
|
+
@preLength=[]
|
746
|
+
@preBefore=[]
|
747
|
+
@preAfter=[]
|
748
|
+
@tracknum=tc+1
|
749
|
+
tc+=1 if tc>=9 # ch10 is drum kit channel
|
750
|
+
tc=@chmax if tc>@chmax
|
751
|
+
@ch=tc
|
752
|
+
end
|
753
|
+
def self.header format,track,tbase=@tbase
|
754
|
+
format=[format,0xff].min
|
755
|
+
track=[track,0xff].min
|
756
|
+
tbase=[tbase,0x7fff].min
|
757
|
+
@tbase=tbase
|
758
|
+
format=format("%02x",format)
|
759
|
+
track=format("%02x",track)
|
760
|
+
tbase=format("%04x",tbase)
|
761
|
+
"
|
762
|
+
# Standard MIDI File data start
|
763
|
+
# header
|
764
|
+
4D 54 68 64 # ヘッダ
|
765
|
+
00 00 00 06 # データ長:6[byte]
|
766
|
+
00 #{format} # フォーマット
|
767
|
+
00 #{track} # トラック数
|
768
|
+
#{tbase} # 1 拍の分解能 #{@tbase}
|
769
|
+
"
|
770
|
+
end
|
771
|
+
def self.byGate len,g=@gateRate
|
772
|
+
g=@preGate.shift if @preGate.size>0
|
773
|
+
l=(len*1.0*g/100).to_i
|
774
|
+
r=len-l
|
775
|
+
[l,r]
|
776
|
+
end
|
777
|
+
def self.soundOn key=@basekey,velocity=@velocity,ch=@ch,sharp=0
|
778
|
+
key=self.note2key(key) if key.class==String
|
779
|
+
key,ch=key if key.class==Array
|
780
|
+
ch=[ch,0x0f].min
|
781
|
+
velocity-=rand(@velocityFuzzy) if @velocityFuzzy>0
|
782
|
+
velocity=[velocity,0x7f].min
|
783
|
+
key+=sharp
|
784
|
+
@key=[[key,0x7f].min,0].max
|
785
|
+
@onlist<<@key
|
786
|
+
key=format("%02x",@key)
|
787
|
+
ch=format("%01x",ch)
|
788
|
+
vel=format("%02x",velocity)
|
789
|
+
start=@waitingtime
|
790
|
+
@waitingtime=0
|
791
|
+
@nowtime+=start
|
792
|
+
r=[Event.new(:o)]
|
793
|
+
r<<Event.new(:e,start," 9#{ch} #{key} #{vel} # #{start}後, sound on only , note #{@key} velocity #{velocity}\n")
|
794
|
+
r
|
795
|
+
end
|
796
|
+
def self.soundOff key=@basekey,ch=@ch,sharp=0
|
797
|
+
key=self.note2key(key) if key.class==String
|
798
|
+
key,ch=key if key.class==Array
|
799
|
+
ch=[ch,0x0f].min
|
800
|
+
key+=sharp
|
801
|
+
@key=[[key,0x7f].min,0].max
|
802
|
+
@onlist-=[@key]
|
803
|
+
key=format("%02x",@key)
|
804
|
+
ch=format("%01x",ch)
|
805
|
+
start=@waitingtime
|
806
|
+
@waitingtime=0
|
807
|
+
@nowtime+=start
|
808
|
+
r=[Event.new(:off)]
|
809
|
+
r<<Event.new(:end,start," 8#{ch} #{key} 00 # #{start} sound off only [#{(@nowtime/@tbase).to_i}, #{@nowtime%@tbase}]\n")
|
810
|
+
r
|
811
|
+
end
|
812
|
+
def self.oneNote len=@tbase,key=@basekey,velocity=@velocity,ch=@ch,sharp=0
|
813
|
+
velocity=@preVelocity.shift if @preVelocity.size>0
|
814
|
+
gate=@gateRate
|
815
|
+
ch=[ch,0x0f].min
|
816
|
+
velocity-=rand(@velocityFuzzy) if @velocityFuzzy>0
|
817
|
+
velocity=[velocity,0x7f].min
|
818
|
+
key+=sharp
|
819
|
+
@key=[[key,0x7f].min,0].max
|
820
|
+
key=format("%02x",@key)
|
821
|
+
ch=format("%01x",ch)
|
822
|
+
vel=format("%02x",velocity)
|
823
|
+
start=@waitingtime
|
824
|
+
@waitingtime=0
|
825
|
+
slen,rest=self.byGate(len,gate)
|
826
|
+
@nowtime+=start
|
827
|
+
r=[]
|
828
|
+
r<<Event.new(:e,start," 9#{ch} #{key} #{vel} # #{start}後, soundオン note #{@key} velocity #{velocity}\n")
|
829
|
+
b=@preAfter.shift
|
830
|
+
bends=expre=false
|
831
|
+
if b
|
832
|
+
bends=worddata("bend",b)
|
833
|
+
expre=worddata("expre",b)
|
834
|
+
mymerge(slen,bends,expre).each{|t,m,d|
|
835
|
+
case m
|
836
|
+
when :bend
|
837
|
+
r<<self.bend(t,d)
|
838
|
+
when :expre
|
839
|
+
r<<self.expre(t,d)
|
840
|
+
when :rest
|
841
|
+
slen=t
|
842
|
+
end
|
843
|
+
}
|
844
|
+
end
|
845
|
+
@nowtime+=slen
|
846
|
+
r<<Event.new(:e,slen," 8#{ch} #{key} 00 # #{slen}(gate:#{@gateRate})- #{len.to_i}(#{len.round(2)})tick後, soundオフ [#{(@nowtime/@tbase).to_i}, #{@nowtime%@tbase}]\n")
|
847
|
+
r<<self.bend(0,0) if bends
|
848
|
+
r<<self.expre(0,127) if expre
|
849
|
+
if rest>0
|
850
|
+
@nowtime+=rest
|
851
|
+
r<<Event.new(:end,rest," 8#{ch} #{key} 00 # #{rest} len-gate\n")
|
852
|
+
end
|
853
|
+
r
|
854
|
+
end
|
855
|
+
def self.dummyNote key,len,accent=false,sharp=0
|
856
|
+
vel=@velocity
|
857
|
+
vel+=@accentPlus
|
858
|
+
key=@preNote.shift if @preNote.size>0
|
859
|
+
len=@preLength.shift if @preLength.size>0
|
860
|
+
self.oneNote(len,key,vel,sharp)
|
861
|
+
end
|
862
|
+
def self.byKey key,len,accent=false,sharp=0
|
863
|
+
vel=@velocity
|
864
|
+
vel+=@accentPlus
|
865
|
+
self.oneNote(len,key,vel,@ch,sharp)
|
866
|
+
end
|
867
|
+
def self.notekey key,length=false,accent=false,sharp=0
|
868
|
+
len,velocity,ch=[@tbase,@velocity,@ch]
|
869
|
+
velocity+=@accentPlus if accent
|
870
|
+
len=length if length
|
871
|
+
if key.class==Fixnum
|
872
|
+
else
|
873
|
+
key,ch=key
|
874
|
+
end
|
875
|
+
key=key+@basekey
|
876
|
+
self.oneNote(len,key,velocity,ch,sharp)
|
877
|
+
end
|
878
|
+
def self.percussionNote key,len=@tbase,accent=false,sharp=0
|
879
|
+
vel=@velocity
|
880
|
+
vel+=@accentPlus if accent
|
881
|
+
self.oneNote(len,key,vel,@rythmChannel,sharp)
|
882
|
+
end
|
883
|
+
def self.notes c,l=false,accent=false,sharp=0
|
884
|
+
n=@notes[c]
|
885
|
+
if @octmode==:near && n.class != Array
|
886
|
+
if @lastnote
|
887
|
+
n+=12 if @lastnote-n>6
|
888
|
+
n-=12 if @lastnote-n<-6
|
889
|
+
end
|
890
|
+
@lastnote=n
|
891
|
+
(@basekey+=12;@lastnote-=12) if n>=12
|
892
|
+
(@basekey-=12;@lastnote+=12) if n<0
|
893
|
+
n=@lastnote
|
894
|
+
end
|
895
|
+
self.notekey(n,l,accent,sharp)
|
896
|
+
end
|
897
|
+
def self.shiftChord chord, base, limit=6
|
898
|
+
octave=12
|
899
|
+
chord=chord.orotate(-1) while chord[0]>base+limit
|
900
|
+
chord=chord.orotate(1) while chord[0]<base-limit
|
901
|
+
chord
|
902
|
+
end
|
903
|
+
def self.chordName c,l=false,accent=false,sharp=0
|
904
|
+
c=~/(.)([^(]*)(\((.*)\))?/
|
905
|
+
root=$1
|
906
|
+
type=$2
|
907
|
+
subtype=$4
|
908
|
+
same=false
|
909
|
+
same=(@lastchordName==c) if @lastchordName
|
910
|
+
if same
|
911
|
+
chord=@lastchord
|
912
|
+
if not (chord[0]-@firstchordbase).abs<7
|
913
|
+
puts "# same chord auto inversion, too far" if $DEBUG && $debuglevel>1
|
914
|
+
chord=self.shiftChord(chord,@firstchordbase)
|
915
|
+
end
|
916
|
+
else
|
917
|
+
@lastchordName=c
|
918
|
+
base=self.note2key(root)+sharp
|
919
|
+
ten=[0]
|
920
|
+
case type
|
921
|
+
when "power"
|
922
|
+
ten+=[7]
|
923
|
+
when "7"
|
924
|
+
ten+=[4,7,10]
|
925
|
+
when "m7"
|
926
|
+
ten+=[3,7,10]
|
927
|
+
when "maj7"
|
928
|
+
ten+=[4,7,11]
|
929
|
+
when "mmaj7"
|
930
|
+
ten+=[3,7,11]
|
931
|
+
when "maj" # no need
|
932
|
+
ten+=[4,7]
|
933
|
+
when "m"
|
934
|
+
ten+=[3,7]
|
935
|
+
when "6"
|
936
|
+
ten+=[4,7,9]
|
937
|
+
when "m6"
|
938
|
+
ten+=[3,7,9]
|
939
|
+
when "sus4"
|
940
|
+
ten+=[5,7]
|
941
|
+
when "aug" || "+"
|
942
|
+
ten+=[4,8]
|
943
|
+
when "dim"
|
944
|
+
ten+=[3,6]
|
945
|
+
when "dim7"
|
946
|
+
ten+=[3,6,9]
|
947
|
+
when ""
|
948
|
+
ten+=[4,7]
|
949
|
+
else
|
950
|
+
STDERR.puts "unknown chord type? #{type}"
|
951
|
+
end
|
952
|
+
if subtype
|
953
|
+
tention=subtype.split(',')
|
954
|
+
tention.each{|i|
|
955
|
+
case i
|
956
|
+
when "+5"
|
957
|
+
ten=ten-[7]+[8]
|
958
|
+
when "-5"
|
959
|
+
ten=ten-[7]+[6]
|
960
|
+
when "9"||"add9"
|
961
|
+
ten=ten+[14]
|
962
|
+
when "+9"
|
963
|
+
ten=ten+[15]
|
964
|
+
when "-9"
|
965
|
+
ten=ten+[13]
|
966
|
+
when "+11"
|
967
|
+
ten=ten+[6]
|
968
|
+
when "13"
|
969
|
+
ten=ten+[9]
|
970
|
+
when "-13"
|
971
|
+
ten=ten+[8]
|
972
|
+
end
|
973
|
+
}
|
974
|
+
end
|
975
|
+
ten=ten.sort
|
976
|
+
p "#{root} #{ten*','}" if $DEBUG
|
977
|
+
chord=ten.sort.map{|i|base+i}
|
978
|
+
chord=self.invert(@lastchord,chord)
|
979
|
+
if @firstchordbase && ! ((chord[0]-@firstchordbase).abs<12)
|
980
|
+
puts "# chord auto inversion, too far." if $DEBUG && $debuglevel>1
|
981
|
+
chord=self.shiftChord(chord,@firstchordbase)
|
982
|
+
end
|
983
|
+
end
|
984
|
+
@lastchord=chord
|
985
|
+
if ! @firstchord
|
986
|
+
@firstchord=chord
|
987
|
+
@firstchordbase=@firstchord[0]
|
988
|
+
end
|
989
|
+
self.chord(chord,l,accent)
|
990
|
+
end
|
991
|
+
def self.invert last,c
|
992
|
+
last=c if ! last
|
993
|
+
root=last[0]
|
994
|
+
r=c.map{|i|
|
995
|
+
s=(root-i).abs%12
|
996
|
+
if s>6
|
997
|
+
s=12-s
|
998
|
+
end
|
999
|
+
[s,i]
|
1000
|
+
}.sort_by{|s,i|s}[0][1]
|
1001
|
+
n=(root-r)%12
|
1002
|
+
r=n>6 ? root-(12-n) : root+n
|
1003
|
+
cc=c.map{|i|(i-r)%12}.sort.map{|i|i+r}
|
1004
|
+
cc
|
1005
|
+
end
|
1006
|
+
def self.chord c,l=false,accent=false,sharp=0
|
1007
|
+
r=[]
|
1008
|
+
sspeed=@strokespeed
|
1009
|
+
(c=c.reverse;sspeed=-sspeed) if sspeed<0
|
1010
|
+
span=c.size
|
1011
|
+
sspeed=l/span if span*sspeed>l
|
1012
|
+
c.each{|i|
|
1013
|
+
r+=self.soundOn(i,@velocity,@ch,sharp)
|
1014
|
+
@waitingtime+=sspeed
|
1015
|
+
}
|
1016
|
+
l-=sspeed*(span-1)
|
1017
|
+
@waitingtime,rest=self.byGate(l)
|
1018
|
+
c.each{|i|
|
1019
|
+
r+=self.soundOff(i,@ch,sharp)
|
1020
|
+
}
|
1021
|
+
r+=self.rest(rest) if rest>0
|
1022
|
+
r
|
1023
|
+
end
|
1024
|
+
def self.rest len=@tbase,ch=@ch
|
1025
|
+
chx=format("%01x",ch)
|
1026
|
+
@nowtime+=len
|
1027
|
+
r=[]
|
1028
|
+
r<<Event.new(:end,len," 8#{chx} 3C 00 # rest #{len.to_i}(#{len.round(2)})tick後, オフ:ch#{ch}, key:3C\n")
|
1029
|
+
r
|
1030
|
+
end
|
1031
|
+
def self.restHex len=@tbase,ch=@ch
|
1032
|
+
r=self.rest(len,ch)
|
1033
|
+
r[0].data
|
1034
|
+
end
|
1035
|
+
# d : hex data
|
1036
|
+
def self.metaEvent d,type=1
|
1037
|
+
t=format("%02X",type)
|
1038
|
+
len=varlenHex(d.split.join.size/2)
|
1039
|
+
" FF #{t} #{len} #{d}\n"
|
1040
|
+
end
|
1041
|
+
def self.metaHook d,type,pos=0
|
1042
|
+
hexd,len=txt2hex(d)
|
1043
|
+
delta=varlenHex(pos)
|
1044
|
+
e=self.metaEvent hexd,type
|
1045
|
+
"#{delta} #{e} # #{d}\n"
|
1046
|
+
end
|
1047
|
+
def self.metaTitle d=@title,pos=0
|
1048
|
+
return "" if not d
|
1049
|
+
self.metaHook d,3,pos
|
1050
|
+
end
|
1051
|
+
def self.metaCopyright d,pos=0
|
1052
|
+
self.metaHook d,2,pos
|
1053
|
+
end
|
1054
|
+
def self.metaText d,pos=0
|
1055
|
+
self.metaHook d,1,pos
|
1056
|
+
end
|
1057
|
+
def self.generaterText
|
1058
|
+
file=__FILE__
|
1059
|
+
thisVer=File.mtime(file).strftime("%Y-%m-%d")
|
1060
|
+
thisVer="" if $debuglevel && $debuglevel>1
|
1061
|
+
pos=@tbase
|
1062
|
+
self.metaText("generated by midi-simple-make.rb (#{thisVer})",pos)
|
1063
|
+
end
|
1064
|
+
def self.lastrest
|
1065
|
+
self.metaText("data end",@tbase)
|
1066
|
+
end
|
1067
|
+
def self.dummyEvent comment,pos=0,d="00",type=1
|
1068
|
+
delta=varlenHex(pos)
|
1069
|
+
t=format("%02X",type)
|
1070
|
+
len=varlenHex(d.split.join.size/2)
|
1071
|
+
"#{delta} FF #{t} #{len} #{d} # #{pos} #{comment}\n"
|
1072
|
+
end
|
1073
|
+
def self.tempo bpm, len=0
|
1074
|
+
@bpmStart=bpm if ! @bpm
|
1075
|
+
@bpm=bpm
|
1076
|
+
d_bpm=self.makebpm(@bpm)
|
1077
|
+
@nowtime+=len
|
1078
|
+
Event.new(:e,len,"#{self.metaEvent(d_bpm,0x51)} # 四分音符の長さ (bpm: #{@bpm}) マイクロ秒で3byte\n")
|
1079
|
+
end
|
1080
|
+
def self.starttempo
|
1081
|
+
self.tempo(@startBpm)
|
1082
|
+
end
|
1083
|
+
def self.makebpm bpm
|
1084
|
+
d="000000"+(60_000_000/bpm.to_f).to_i.to_s(16)
|
1085
|
+
d[-6..-1]
|
1086
|
+
end
|
1087
|
+
def self.controlChange v
|
1088
|
+
v=~/^([^,]*),([^,]*)(,(.*))?/
|
1089
|
+
n,v,len=$1.to_i,$2.to_i,$4.to_i
|
1090
|
+
ch=@ch
|
1091
|
+
v=[0,[v,0x7f].min].max
|
1092
|
+
ch=format("%01x",ch)
|
1093
|
+
n=format("%02x",n)
|
1094
|
+
data=format("%02x",v)
|
1095
|
+
t=@waitingtime+len
|
1096
|
+
@waitnigtime=0
|
1097
|
+
@nowtime+=t
|
1098
|
+
Event.new(:e,t," B#{ch} #{n} #{data}\n")
|
1099
|
+
end
|
1100
|
+
def self.expre len,d
|
1101
|
+
self.controlChange("11,#{d},#{len}")
|
1102
|
+
end
|
1103
|
+
def self.ProgramChange ch,inst,len=0
|
1104
|
+
ch=@ch if ch==false
|
1105
|
+
ch=[ch,0x0f].min
|
1106
|
+
inst=[inst,0xff].min
|
1107
|
+
chx=format("%01x",ch)
|
1108
|
+
instx=format("%02x",inst)
|
1109
|
+
@nowtime+=len
|
1110
|
+
Event.new(:e,len," C#{chx} #{instx} # program change ch#{ch} #{inst} [#{@programList[inst][1]}]\n")
|
1111
|
+
end
|
1112
|
+
# system exclusive message event
|
1113
|
+
# = F0 [len] [maker id(1-3 byte)] [data] F7
|
1114
|
+
def self.sysExEvent d
|
1115
|
+
d=d+" F7"
|
1116
|
+
s=varlenHex(d.split.join.size/2)
|
1117
|
+
" F0 #{s} #{d}"
|
1118
|
+
end
|
1119
|
+
# GM,GS,XG wakeup command need over 50milisec. ?
|
1120
|
+
# if not, midi player may hung up.
|
1121
|
+
def self.GMsystemOn len=0,mode=1
|
1122
|
+
# GM1,GM2
|
1123
|
+
m=mode==2 ? 3 : 1
|
1124
|
+
@nowtime+=len
|
1125
|
+
ex=self.sysExEvent("7E 7F 09 0#{m}")
|
1126
|
+
Event.new(:sys,len," #{ex} # GM\n")
|
1127
|
+
end
|
1128
|
+
def self.XGsystemOn len=0
|
1129
|
+
@nowtime+=len
|
1130
|
+
ex=self.sysExEvent("43 10 4C 00 00 7E 00")
|
1131
|
+
Event.new(:sys,len," #{ex} # XG\n")
|
1132
|
+
end
|
1133
|
+
def self.xgMasterTune d,len=0
|
1134
|
+
d=[[d.to_i,-100].max,100].min
|
1135
|
+
n=(d+100)*256/200
|
1136
|
+
m=n/16
|
1137
|
+
l=n%16
|
1138
|
+
ex=self.sysExEvent("43 10 27 30 00 00 #{format"%02x",m} #{format"%02x",l} 00")
|
1139
|
+
Event.new(:sys,len," #{ex} # XG midi master tune \n")
|
1140
|
+
end
|
1141
|
+
def self.GSreset len=0
|
1142
|
+
@nowtime+=len
|
1143
|
+
ex=self.sysExEvent("41 10 42 12 40 00 7F 00 41")
|
1144
|
+
Event.new(:sys,len," #{ex} # GS \n")
|
1145
|
+
end
|
1146
|
+
def self.bankSelect d
|
1147
|
+
d=~/([^,]*),([^,]*)(,(.*))?/
|
1148
|
+
msb,lsb=$1.to_i,$2.to_i
|
1149
|
+
msb=[msb,0x7f].min
|
1150
|
+
lsb=[lsb,0x7f].min
|
1151
|
+
len=$4.to_i
|
1152
|
+
msb=format("%02x",msb)
|
1153
|
+
lsb=format("%02x",lsb)
|
1154
|
+
ch=@ch
|
1155
|
+
ch=format("%01x",ch)
|
1156
|
+
@nowtime+=len
|
1157
|
+
@nowtime+=len
|
1158
|
+
r=[]
|
1159
|
+
r<<Event.new(:sys,len," B#{ch} 00 #{msb} # BankSelect MSB\n")
|
1160
|
+
r<<Event.new(:sys,len," B#{ch} 20 #{lsb} # BankSelect LSB\n")
|
1161
|
+
r
|
1162
|
+
end
|
1163
|
+
def self.bankSelectPC d
|
1164
|
+
d="#{rand(0x7f)},rand(0x7f)},#{rand(0x7f)}" if d=="?"
|
1165
|
+
d=~/(([^,]*),([^,]*)),([^,]*)(,(.*))?/
|
1166
|
+
len=$6.to_i
|
1167
|
+
inst=$4.to_i ##
|
1168
|
+
bs=self.bankSelect("#{$1},#{len}")
|
1169
|
+
pc=self.ProgramChange(@ch,inst,len)
|
1170
|
+
[bs,pc]
|
1171
|
+
end
|
1172
|
+
def self.programGet p,num=false
|
1173
|
+
return 0 if not @programList
|
1174
|
+
if p=~/\?/
|
1175
|
+
r=[@programList[rand(@programList.size)]]
|
1176
|
+
p "random: ",r if $DEBUG
|
1177
|
+
else
|
1178
|
+
r=@programList.select{|n,line|line=~/#{p}/i}
|
1179
|
+
puts "no instrument name like '#{p}' in list" if $DEBUG && r.size==0
|
1180
|
+
end
|
1181
|
+
num=[num,r.size].min if num
|
1182
|
+
if num && r.size>0
|
1183
|
+
res=r[num-1][0]
|
1184
|
+
else
|
1185
|
+
res=r.size>0 ? r[0][0] : 0
|
1186
|
+
end
|
1187
|
+
res
|
1188
|
+
end
|
1189
|
+
def self.percussionGet p
|
1190
|
+
p=~/^_([^!]+)/
|
1191
|
+
p=$1 if $1
|
1192
|
+
return p.to_i if p=~/^[[:digit:]]+$/
|
1193
|
+
return @snare if not @percussionList
|
1194
|
+
return @gmKit[p] if @gmKit.keys.member?(p)
|
1195
|
+
r=@percussionList.select{|num,line|line=~/#{p}/i}
|
1196
|
+
puts "no percussion name like '#{p}' in list" if $DEBUG && r.size==0
|
1197
|
+
r.size>0 ? r[0][0] : @snare
|
1198
|
+
end
|
1199
|
+
def self.bend pos,depth,ch=false
|
1200
|
+
ch=@ch if ! ch
|
1201
|
+
depth=(depth*@bendCent).to_i
|
1202
|
+
pos+=@waitingtime
|
1203
|
+
@waitingtime=0
|
1204
|
+
@nowtime+=pos
|
1205
|
+
Event.new(:e,pos," e#{format"%01x",ch} #{bendHex(depth)} # t:#{pos} bend #{depth}\n")
|
1206
|
+
end
|
1207
|
+
def self.masterVolume v,pos=0
|
1208
|
+
v=[[0,v].max,0x7f].min
|
1209
|
+
vol=format("%02x",v)
|
1210
|
+
ex=self.sysExEvent("7F 04 01 00 #{vol}")+" # master volume #{v}"
|
1211
|
+
Event.new(:e,pos,ex)
|
1212
|
+
end
|
1213
|
+
def self.note2key i
|
1214
|
+
# inside parenthesis -+ are octave
|
1215
|
+
oct=0
|
1216
|
+
i=~/^([-+]+)?(.+)/
|
1217
|
+
octave,tone=$1,$2
|
1218
|
+
i=tone if octave
|
1219
|
+
case octave
|
1220
|
+
when /-+/
|
1221
|
+
oct=-octave.size
|
1222
|
+
when /\++/
|
1223
|
+
oct=octave.size
|
1224
|
+
end
|
1225
|
+
k=if tone=~/^_/
|
1226
|
+
[self.percussionGet(tone),@rythmChannel]
|
1227
|
+
elsif @notes.keys.member?(i)
|
1228
|
+
@basekey+oct*12+@notes[i]
|
1229
|
+
else
|
1230
|
+
i.to_i
|
1231
|
+
end
|
1232
|
+
if k.class!=Array && k<0
|
1233
|
+
k+=12 while k<0
|
1234
|
+
elsif k.class!=Array && k>0x7f
|
1235
|
+
k-=12 while k>0x7f
|
1236
|
+
end
|
1237
|
+
k
|
1238
|
+
end
|
1239
|
+
def self.basekeySet d
|
1240
|
+
case d
|
1241
|
+
when "-"
|
1242
|
+
if @basekey<12
|
1243
|
+
STDERR.puts "octave too low."
|
1244
|
+
else
|
1245
|
+
@basekey-=12
|
1246
|
+
end
|
1247
|
+
when "+"
|
1248
|
+
if @basekey>0x7f-12
|
1249
|
+
STDERR.puts "octave too high."
|
1250
|
+
else
|
1251
|
+
@basekey+=12
|
1252
|
+
end
|
1253
|
+
else
|
1254
|
+
@basekey=d
|
1255
|
+
end
|
1256
|
+
end
|
1257
|
+
def self.chordCenter c
|
1258
|
+
case c
|
1259
|
+
when "reset"
|
1260
|
+
@chordCenter=@chordCenterOrg
|
1261
|
+
when "+"
|
1262
|
+
@chordCenter-=6
|
1263
|
+
when "-"
|
1264
|
+
@chordCenter+=6
|
1265
|
+
when /^\+(.+)/
|
1266
|
+
@chordCenter+=$1.to_i
|
1267
|
+
when /^-(.+)/
|
1268
|
+
@chordCenter-=$1.to_i
|
1269
|
+
else
|
1270
|
+
@chordCenter=c.to_i
|
1271
|
+
end
|
1272
|
+
@chordCenter=[[0,@chordCenter].max,0x7f].min
|
1273
|
+
@firstchordbase=@chordCenter
|
1274
|
+
end
|
1275
|
+
def self.preLength v
|
1276
|
+
@preLength=v.map{|i|
|
1277
|
+
case i
|
1278
|
+
when "o",""
|
1279
|
+
@tbase
|
1280
|
+
when /^\*/
|
1281
|
+
$'.to_f
|
1282
|
+
else
|
1283
|
+
i.to_f*@tbase
|
1284
|
+
end
|
1285
|
+
}
|
1286
|
+
end
|
1287
|
+
def self.preNote v
|
1288
|
+
@preNote=v.map{|i|
|
1289
|
+
case i
|
1290
|
+
when "?"
|
1291
|
+
rand(0x7f)
|
1292
|
+
else
|
1293
|
+
self.note2key(i)
|
1294
|
+
end
|
1295
|
+
}
|
1296
|
+
end
|
1297
|
+
def self.preVelocity v
|
1298
|
+
@preVelocity=v.map{|i|
|
1299
|
+
case i
|
1300
|
+
when "o",""
|
1301
|
+
@velocity
|
1302
|
+
when "-"
|
1303
|
+
@velocity-10
|
1304
|
+
when "+"
|
1305
|
+
@velocity+10
|
1306
|
+
else
|
1307
|
+
i.to_i
|
1308
|
+
end
|
1309
|
+
}
|
1310
|
+
end
|
1311
|
+
def self.preGate v
|
1312
|
+
@preGate=v.map{|i|
|
1313
|
+
case i
|
1314
|
+
when "o",""
|
1315
|
+
@gateRate
|
1316
|
+
when "-"
|
1317
|
+
@gateRate*0.5
|
1318
|
+
else
|
1319
|
+
i.to_i
|
1320
|
+
end
|
1321
|
+
}
|
1322
|
+
end
|
1323
|
+
def self.preAfter v
|
1324
|
+
@preAfter=v.map{|i|
|
1325
|
+
case i
|
1326
|
+
when "o",""
|
1327
|
+
false
|
1328
|
+
else
|
1329
|
+
revertPre(i)
|
1330
|
+
end
|
1331
|
+
}
|
1332
|
+
end
|
1333
|
+
def self.preBefore v
|
1334
|
+
@preBefore=v.map{|i|
|
1335
|
+
case i
|
1336
|
+
when "o",""
|
1337
|
+
false
|
1338
|
+
else
|
1339
|
+
i
|
1340
|
+
end
|
1341
|
+
}
|
1342
|
+
end
|
1343
|
+
def self.strokeSpeed s
|
1344
|
+
s=s.to_i
|
1345
|
+
@strokespeed=s
|
1346
|
+
end
|
1347
|
+
def self.setmark m
|
1348
|
+
n=@marktrack.getcount(m,@tracknum)
|
1349
|
+
m="#{m}@#{n+1}" if n>0
|
1350
|
+
@marktrack.set(m,@tracknum,@nowtime)
|
1351
|
+
Event.new(:mark,m,@tracknum,@nowtime)
|
1352
|
+
end
|
1353
|
+
def self.eventlist2str elist
|
1354
|
+
r=[]
|
1355
|
+
# EventList : [func,args] or [callonly, func,args] or others
|
1356
|
+
elist.each{|h|
|
1357
|
+
cmd,*arg=h
|
1358
|
+
r<<Event.new(:c,"# #{cmd} #{arg}")
|
1359
|
+
case cmd
|
1360
|
+
when :basekeyPlus
|
1361
|
+
@basekey+=arg[0]
|
1362
|
+
when :raw
|
1363
|
+
r<<Event.new(:raw,arg[0])
|
1364
|
+
when :ahead
|
1365
|
+
r<<Event.new(:ahead,arg[0])
|
1366
|
+
when :velocity
|
1367
|
+
@velocity=arg[0]
|
1368
|
+
when :velocityFuzzy
|
1369
|
+
@velocityFuzzy=arg[0]
|
1370
|
+
when :ch
|
1371
|
+
@ch=arg[0]
|
1372
|
+
when :waitingtime
|
1373
|
+
@waitingtime+=arg[0]
|
1374
|
+
when :call
|
1375
|
+
cmd,*arg=arg
|
1376
|
+
method(cmd).call(*arg)
|
1377
|
+
when :soundOff
|
1378
|
+
if arg[0]=="all"
|
1379
|
+
@onlist.each{|o|
|
1380
|
+
r<<method(cmd).call(o)
|
1381
|
+
}
|
1382
|
+
else
|
1383
|
+
r<<method(cmd).call(*arg)
|
1384
|
+
end
|
1385
|
+
else
|
1386
|
+
r<<method(cmd).call(*arg)
|
1387
|
+
end
|
1388
|
+
}
|
1389
|
+
rr=[]
|
1390
|
+
ahead=0
|
1391
|
+
after=0
|
1392
|
+
r.flatten!
|
1393
|
+
r.each{|i|
|
1394
|
+
if i.class==String
|
1395
|
+
rr<<i
|
1396
|
+
else
|
1397
|
+
case i.type
|
1398
|
+
when :ahead
|
1399
|
+
ahead=i.time
|
1400
|
+
next if ahead==0
|
1401
|
+
n=0
|
1402
|
+
n-=1 until (rr[n].time>0) || n<-10
|
1403
|
+
if n>-10
|
1404
|
+
ahead=[ahead,-rr[n].time].max
|
1405
|
+
rr[n].time+=ahead
|
1406
|
+
after=-ahead
|
1407
|
+
else
|
1408
|
+
after=0
|
1409
|
+
end
|
1410
|
+
when :end,:e,:sys
|
1411
|
+
(i.time+=after;after=0) if after>0
|
1412
|
+
(i.time+=after;after=0) if after<0 && i.time+after>=0
|
1413
|
+
rr<<i
|
1414
|
+
when :c,:raw,:mark
|
1415
|
+
rr<<i
|
1416
|
+
else
|
1417
|
+
"? #{i}"
|
1418
|
+
end
|
1419
|
+
end
|
1420
|
+
}
|
1421
|
+
rr.map{|i|
|
1422
|
+
case i
|
1423
|
+
when String
|
1424
|
+
i
|
1425
|
+
when Event
|
1426
|
+
i.data
|
1427
|
+
end
|
1428
|
+
}
|
1429
|
+
end
|
1430
|
+
def self.makefraze rundata,tc
|
1431
|
+
return "" if not rundata
|
1432
|
+
self.trackPrepare(tc)
|
1433
|
+
@systemWait=120
|
1434
|
+
@h=[]
|
1435
|
+
wait=[]
|
1436
|
+
@frest=0
|
1437
|
+
@frestc=0
|
1438
|
+
@nowtime=0
|
1439
|
+
@shiftbase=40
|
1440
|
+
accent=false
|
1441
|
+
sharp=0
|
1442
|
+
cmd=rundata.scan(/&\([^)]+\)|\([-+]*[[:digit:]]?\)|:[^\(,]+\([^\)\(]+\),|:[^,]+,|\([^:]*:[^)\(]*\)|_[^!_]+!|_[^_]__[^\?]+\?|v[[:digit:]]+|[<>][[:digit:]]*|\*?[[:digit:]]+\.[[:digit:]]+|\*?[[:digit:]]+|[-+[:alpha:]]|\^|`|'|./)
|
1443
|
+
cmd<<" " # dummy
|
1444
|
+
p "make start: ",cmd if $DEBUG
|
1445
|
+
cmd.each{|i|
|
1446
|
+
if wait.size>0
|
1447
|
+
t=@tbase
|
1448
|
+
i=~/^(\*)?([[:digit:]]+(\.[[:digit:]]+)?)/
|
1449
|
+
tickmode=$1
|
1450
|
+
t=$2.to_f if $&
|
1451
|
+
if $&
|
1452
|
+
if tickmode
|
1453
|
+
puts "tick: #{t}" if $DEBUG && $debuglevel>1
|
1454
|
+
else
|
1455
|
+
t*=@tbase
|
1456
|
+
end
|
1457
|
+
@frest+=(t-t.to_i)
|
1458
|
+
t=t.to_i
|
1459
|
+
if @frest>=1
|
1460
|
+
t+=@frest.to_i
|
1461
|
+
@frest=@frest-@frest.to_i
|
1462
|
+
@frestc+=1
|
1463
|
+
end
|
1464
|
+
end
|
1465
|
+
wait.each{|m,c|
|
1466
|
+
case m
|
1467
|
+
when :percussion
|
1468
|
+
@h<<[:percussionNote,c,t,accent,sharp]
|
1469
|
+
when :rawsound
|
1470
|
+
@h<<[:byKey,c,t,accent,sharp]
|
1471
|
+
when :sound
|
1472
|
+
@h<<[:notes,c,t,accent,sharp]
|
1473
|
+
when :dummyNote
|
1474
|
+
@h<<[:dummyNote,c,t,accent,sharp]
|
1475
|
+
when :chord
|
1476
|
+
@h<<[:chord,c,t,accent,sharp]
|
1477
|
+
when :chordName
|
1478
|
+
@h<<[:chordName,c,t,accent,sharp]
|
1479
|
+
when :rest
|
1480
|
+
@h<<[:rest,t]
|
1481
|
+
end
|
1482
|
+
}
|
1483
|
+
wait=[]
|
1484
|
+
accent=false
|
1485
|
+
sharp=0
|
1486
|
+
end
|
1487
|
+
case i
|
1488
|
+
when /^\(([-+]*)([[:digit:]])?\)/
|
1489
|
+
n=$2 ? $2.to_i : 1
|
1490
|
+
if $1.size>1
|
1491
|
+
n=$1.size
|
1492
|
+
end
|
1493
|
+
sh=$1 ? $1[0..0] : "+"
|
1494
|
+
sh="#{sh}#{n}".to_i
|
1495
|
+
sharp=sh
|
1496
|
+
when /^\(key:(-?)\+?([[:digit:]]+)\)/
|
1497
|
+
tr=$2.to_i
|
1498
|
+
tr*=-1 if $1=="-"
|
1499
|
+
@h<<[:basekeyPlus,tr]
|
1500
|
+
when /^\(mark:(.*)\)/
|
1501
|
+
@h<<[:setmark,$1]
|
1502
|
+
when /^\(V:(.*)\)/
|
1503
|
+
vs=$1.split(",")
|
1504
|
+
@h<<[:call,:preVelocity,vs]
|
1505
|
+
when /^\(N:(.*)\)/
|
1506
|
+
s=$1.split(",")
|
1507
|
+
@h<<[:call,:preNote,s]
|
1508
|
+
when /^\(L:(.*)\)/
|
1509
|
+
s=$1.split(",")
|
1510
|
+
@h<<[:call,:preLength,s]
|
1511
|
+
when /^\(G:(.*)\)/
|
1512
|
+
gs=$1.split(",")
|
1513
|
+
@h<<[:call,:preGate,gs]
|
1514
|
+
when /^\(A:(.*)\)/
|
1515
|
+
s=$1.split(",")
|
1516
|
+
@h<<[:call,:preAfter,s]
|
1517
|
+
when /^\(B:(.*)\)/
|
1518
|
+
s=$1.split(",")
|
1519
|
+
@h<<[:call,:preBefore,s]
|
1520
|
+
when /^\(roll:(.*)\)/
|
1521
|
+
@shiftbase=$1.to_i
|
1522
|
+
when /^\(key:reset\)/
|
1523
|
+
@h<<[:call,:basekeySet,@basekeyOrg]
|
1524
|
+
when /^\(p:(([[:digit:]]+),)?(([[:digit:]]+)|([\?[:alnum:]]+)(,([[:digit:]]))?)\)/
|
1525
|
+
channel=$1 ? $2.to_i : false
|
1526
|
+
subNo=false
|
1527
|
+
if $5
|
1528
|
+
subNo=$7.to_i if $7
|
1529
|
+
instrument=self.programGet($5,subNo)
|
1530
|
+
else
|
1531
|
+
instrument=$4.to_i
|
1532
|
+
end
|
1533
|
+
@h<<[:ProgramChange,channel,instrument]
|
1534
|
+
when /^\(bend:(([[:digit:]]+),)?([-,.[:digit:]]+)\)|^_b__([^?]+)\?/
|
1535
|
+
i="(bend:#{$4.gsub('_'){','}})" if $4
|
1536
|
+
x,pos,b=worddata("bend",i)
|
1537
|
+
npos=0
|
1538
|
+
b.each{|depth|
|
1539
|
+
@h<<[:bend,npos,depth]
|
1540
|
+
npos=pos
|
1541
|
+
}
|
1542
|
+
when /^\(bendCent:([^\)]*)\)/
|
1543
|
+
$1=~/on/i
|
1544
|
+
on=$& ? true : false
|
1545
|
+
@h<<[:call,:bendCent,on]
|
1546
|
+
when /^\(bendRange:([^\)]*)\)/
|
1547
|
+
@h<<[:bendRange,$1]
|
1548
|
+
when /^&\((.+)\)/
|
1549
|
+
raw=rawHexPart($1)
|
1550
|
+
@h<<[:raw,raw]
|
1551
|
+
when /^:([^\(,]+(\([^\)]+\))?),/
|
1552
|
+
wait<<[:chordName,$1]
|
1553
|
+
when /^_(([[:digit:]]+)|([[:alnum:]]+))!/
|
1554
|
+
if $2
|
1555
|
+
perc=$2.to_i
|
1556
|
+
else
|
1557
|
+
perc=self.percussionGet($3)
|
1558
|
+
end
|
1559
|
+
wait<<[:percussion,perc]
|
1560
|
+
when /^\(vFuzzy:([0-9]+)\)/
|
1561
|
+
@h<<[:velocityFuzzy,$1.to_i]
|
1562
|
+
when /^\(v:([0-9]+)\)/, /^v([0-9]+)/
|
1563
|
+
@h<<[:velocity,$1.to_i]
|
1564
|
+
when /^\(g:([0-9]+)\)/
|
1565
|
+
@h<<[:call,:setGateRate,$1.to_i]
|
1566
|
+
when /^\(volume:(.*)\)/
|
1567
|
+
@h<<[:masterVolume,$1.to_i]
|
1568
|
+
when /^\(tempo:reset\)/
|
1569
|
+
@h<<[:tempo,@bpmStart]
|
1570
|
+
when /^\(ch:(.*)\)/
|
1571
|
+
ch=$1.to_i
|
1572
|
+
ch=9 if $1=="drum"
|
1573
|
+
@h<<[:ch,ch]
|
1574
|
+
when /^\(cc:(.*)\)/
|
1575
|
+
@h<<[:controlChange,$1]
|
1576
|
+
when /^\(bs:(.*)\)/
|
1577
|
+
@h<<[:bankSelect,$1]
|
1578
|
+
when /^\(bspc:(.*)\)/
|
1579
|
+
@h<<[:bankSelectPC,$1]
|
1580
|
+
when /^\(gs:reset\)/
|
1581
|
+
@h<<[:GSreset,0]
|
1582
|
+
@h<<[:rest,@systemWait]
|
1583
|
+
when /^\(gm(2)?:on\)/
|
1584
|
+
gm=$1 ? 2 : 1
|
1585
|
+
@h<<[:GMsystemOn,0,gm]
|
1586
|
+
@h<<[:rest,@systemWait]
|
1587
|
+
when /^\(xg:on\)/
|
1588
|
+
@h<<[:XGsystemOn,0]
|
1589
|
+
@h<<[:rest,@systemWait]
|
1590
|
+
when /^\(syswait:\)/
|
1591
|
+
@h<<[:rest,@systemWait]
|
1592
|
+
when /^\(xgMasterTune:(.*)\)/
|
1593
|
+
@h<<[:xgMasterTune,$1]
|
1594
|
+
when /^\(pan:(<|>)?(.*)\)/
|
1595
|
+
pan=$2.to_i
|
1596
|
+
case $1
|
1597
|
+
when ">"
|
1598
|
+
pan+=64
|
1599
|
+
when "<"
|
1600
|
+
pan-=64
|
1601
|
+
else
|
1602
|
+
end
|
1603
|
+
@h<<[:controlChange,"10,#{pan}"]
|
1604
|
+
when /^\(wait:(\*)?(.*)\)/
|
1605
|
+
@h<<[:waitingtime,$1? $2.to_i : $2.to_f*@tbase]
|
1606
|
+
when /^\(accent:([^)]*)\)/
|
1607
|
+
@h<<[:call,:accent,$1]
|
1608
|
+
when /^\(on:(.*)\)/
|
1609
|
+
i=$1
|
1610
|
+
@h<<[:soundOn,i]
|
1611
|
+
when /^\(off:(.*)\)/
|
1612
|
+
@h<<[:soundOff,$1]
|
1613
|
+
when /^\(chordcenter:(.*)\)/
|
1614
|
+
@h<<[:call,:chordCenter,$1]
|
1615
|
+
when /^\(stroke:(.*)\)/
|
1616
|
+
@h<<[:call,:strokeSpeed,$1]
|
1617
|
+
when /^\((chord|C):(.*)\)/
|
1618
|
+
chord=$2.split.join.split(",") # .map{|i|self.note2key(i)}
|
1619
|
+
wait<<[:chord,chord]
|
1620
|
+
when /^\(text:(.*)\)/
|
1621
|
+
@h<<[:text,$1]
|
1622
|
+
when /^\(tempo:(.*)\)/
|
1623
|
+
@bpm=$1.to_i
|
1624
|
+
@h<<[:tempo,@bpm] if @bpm>0
|
1625
|
+
when /^\(\?:(.*)\)/
|
1626
|
+
ks=$1
|
1627
|
+
if ks=~/\-/
|
1628
|
+
ks=[*($`.to_i)..($'.to_i)]
|
1629
|
+
key=ks[rand(ks.size)]
|
1630
|
+
wait<<[:rawsound,key]
|
1631
|
+
else
|
1632
|
+
ks=ks.split(",")
|
1633
|
+
key=ks[rand(ks.size)]
|
1634
|
+
wait<<[:sound,key]
|
1635
|
+
end
|
1636
|
+
when /^\(x:(.*)\)/
|
1637
|
+
key=$1.to_i
|
1638
|
+
wait<<[:rawsound,key]
|
1639
|
+
when /^<(.*)/
|
1640
|
+
rate=1.25
|
1641
|
+
if $1.size>0
|
1642
|
+
rate=$1.to_i/100.0
|
1643
|
+
end
|
1644
|
+
@bpm=@bpm/rate
|
1645
|
+
@h<<[:tempo,@bpm]
|
1646
|
+
when /^>(.*)/
|
1647
|
+
rate=1.25
|
1648
|
+
if $1.size>0
|
1649
|
+
rate=$1.to_i/100.0
|
1650
|
+
end
|
1651
|
+
@bpm=@bpm*rate
|
1652
|
+
@h<<[:tempo,@bpm]
|
1653
|
+
when "-"
|
1654
|
+
@h<<[:call,:basekeySet,"-"]
|
1655
|
+
when "+"
|
1656
|
+
@h<<[:call,:basekeySet,"+"]
|
1657
|
+
when /^\*?[0-9]+/
|
1658
|
+
# (i.to_i-1).times{@h<<@h[-1]}
|
1659
|
+
when "`"
|
1660
|
+
@h<<[:ahead,-@shiftbase]
|
1661
|
+
when "'"
|
1662
|
+
@h<<[:ahead,@shiftbase]
|
1663
|
+
when "^"
|
1664
|
+
p "accent" if $DEBUG
|
1665
|
+
accent=true
|
1666
|
+
when "="
|
1667
|
+
@h<<@h[-1]
|
1668
|
+
when "r"
|
1669
|
+
wait<<[:rest,i]
|
1670
|
+
when "o"
|
1671
|
+
wait<<[:dummyNote,i]
|
1672
|
+
when " "
|
1673
|
+
when "?"
|
1674
|
+
wait<<[:rawsound,rand(0x7f)]
|
1675
|
+
else
|
1676
|
+
if @notes.keys.member?(i)
|
1677
|
+
wait<<[:sound,i]
|
1678
|
+
else
|
1679
|
+
STDERR.puts "[#{i}] undefined note?" # if $DEBUG
|
1680
|
+
end
|
1681
|
+
end
|
1682
|
+
}
|
1683
|
+
p @h if $DEBUG
|
1684
|
+
puts "float rest add times: #{@frestc}" if $DEBUG
|
1685
|
+
@h=self.eventlist2str(@h)
|
1686
|
+
@h*"\n# track: #{@tracknum} ==== \n"
|
1687
|
+
end
|
1688
|
+
def self.loadMap file, base=0
|
1689
|
+
if not File.exist?(file)
|
1690
|
+
map=false
|
1691
|
+
else
|
1692
|
+
category=""
|
1693
|
+
li=File.readlines(file).map{|i|
|
1694
|
+
i=i.toutf8
|
1695
|
+
# zero base
|
1696
|
+
# category name plus
|
1697
|
+
if i=~/^[[:digit:]]+/
|
1698
|
+
[i.split[0].to_i-base,"#{i.chomp.toutf8} #{category}"]
|
1699
|
+
else
|
1700
|
+
category=i.chomp.toutf8 if i.chomp.size>0
|
1701
|
+
false
|
1702
|
+
end
|
1703
|
+
}-[false]
|
1704
|
+
map=li.size>0 ? li : false
|
1705
|
+
end
|
1706
|
+
end
|
1707
|
+
def self.loadProgramChange file
|
1708
|
+
if not File.exist?(file)
|
1709
|
+
@programList=false
|
1710
|
+
else
|
1711
|
+
@programList=self.loadMap(file,1)
|
1712
|
+
end
|
1713
|
+
end
|
1714
|
+
def self.testGs cycle,key,scaleAll,intro,mapnum=3
|
1715
|
+
d=@programList.select{|i,v|v=~/#{key}/i}.map{|i,data|
|
1716
|
+
[mapnum].map{|lsb|
|
1717
|
+
msbs=[0,8,16,24,32]
|
1718
|
+
msbs=[0] if i>63
|
1719
|
+
msbs=[*0..5] if i>121
|
1720
|
+
msbs.map{|msb|
|
1721
|
+
#msb*=8
|
1722
|
+
"(bspc:#{msb},#{lsb},#{i},4) #{cycle}"
|
1723
|
+
}*""
|
1724
|
+
}*""
|
1725
|
+
}*""
|
1726
|
+
scale=scaleAll # mapnum<4 ? [*25..87].map{|i|"(x:#{i})"}*"" : scaleAll
|
1727
|
+
lsb=mapnum
|
1728
|
+
ps=[1,2,3,9,10,11,12,17,25,26,27,28,29,30,31,33,41,49,50,51,53,54,57,58,59,128].map{|i|i-1}
|
1729
|
+
perc=ps.map{|p|
|
1730
|
+
[0,8,16,24,25,32,40,48,56,127].map{|msb|
|
1731
|
+
"(bspc:#{msb},#{lsb},#{p},4) #{intro} #{scale}"
|
1732
|
+
}*""
|
1733
|
+
}*""
|
1734
|
+
d="(gs:reset)r14 "+d+"(ch:9)"+perc
|
1735
|
+
end
|
1736
|
+
def self.test cycle,mode=1
|
1737
|
+
cycle="cdef" if ! cycle
|
1738
|
+
key=$test
|
1739
|
+
p key
|
1740
|
+
inGM=[*35..81]
|
1741
|
+
outGM=[*0..127]-inGM
|
1742
|
+
# make sounds outside GM map shorter
|
1743
|
+
scaleAll=(inGM.map{|i|"(x:#{i})"}+outGM.map{|i|"(x:#{i})0.2"})*""
|
1744
|
+
s,k,h,l,o,c,cc=@gmSnare,@gmKick,@gmHiTom,@gmLoTom,@gmOpenH,@gmCloseH,@gmCrashCym
|
1745
|
+
intro="(x:#{k})0.2(x:#{cc})0.8(x:#{k})(x:#{s})(x:#{s})(x:#{c})(x:#{c})(x:#{o})(x:#{c})
|
1746
|
+
v64(x:#{h})0.68(x:#{l})0.66(x:#{l})0.66
|
1747
|
+
v42(x:#{o})0.34v32(x:#{c})0.33(x:#{c})0.33 v20(x:#{o})0.12(x:#{c})0.11(x:#{c})0.11v92(x:#{o})0.66v64"
|
1748
|
+
mode=1 if ! mode
|
1749
|
+
@data=(
|
1750
|
+
case mode
|
1751
|
+
when 1 || "gm"
|
1752
|
+
d=@programList.select{|i,v|v=~/#{key}/i}.map{|i,data|"(p:#{i})#{cycle}"}*""
|
1753
|
+
perc=scaleAll
|
1754
|
+
"(gm:on)r14 "+d+"(ch:9)"+intro+perc
|
1755
|
+
when 2 || "xg"
|
1756
|
+
d=@programList.select{|i,v|v=~/#{key}/i}.map{|i,data|
|
1757
|
+
[0,1,18,32,33,34,40,41,45,64,65,70,71,97,98].map{|lsb|
|
1758
|
+
[0].map{|msb|
|
1759
|
+
#msb*=8
|
1760
|
+
"(bspc:#{msb},#{lsb},#{i},4) #{cycle}"
|
1761
|
+
}*""
|
1762
|
+
}*""
|
1763
|
+
}*""
|
1764
|
+
lsb=0
|
1765
|
+
msb=127
|
1766
|
+
perc0=[0].map{|p| "(bspc:#{msb},#{lsb},#{p},4) #{scaleAll}"}*""
|
1767
|
+
scale=([*28..79]-[51,52,54,55,58,60,61,65,66,67,68,69,71,72,73,74]).map{|i|"(x:#{i})"}*"" # main unique sound maybe
|
1768
|
+
perc=[*1..48].map{|p| "(bspc:#{msb},#{lsb},#{p},4) #{intro} #{scaleAll}"}*""
|
1769
|
+
msb=126
|
1770
|
+
scale=([*36..42]+[*52..62]+[*68..73]+[*84..91]).map{|i|"(x:#{i})"}*""
|
1771
|
+
perc2=[*0..1].map{|p| "(bspc:#{msb},#{lsb},#{p},4) #{intro} #{scale}"}*""
|
1772
|
+
d="(xg:on)r14 "+d+"(ch:9)"+perc+perc2+perc0
|
1773
|
+
when 3 || "gs"
|
1774
|
+
mapnum=3 # 0(gm),1(sc-55),2(sc-88),3(sc-88pro),4(sc-8850)
|
1775
|
+
self.testGs(cycle,key,scaleAll,intro,mapnum)
|
1776
|
+
else
|
1777
|
+
end
|
1778
|
+
)
|
1779
|
+
end
|
1780
|
+
def self.loadPercussionMap file
|
1781
|
+
@snare=35
|
1782
|
+
@gmSnare,@gmKick,@gmHiTom,@gmLoTom,@gmOpenH,@gmCloseH,@gmCrashCym=38,35,50,45,46,42,49
|
1783
|
+
@gmKit={}
|
1784
|
+
@gmKit["s"],@gmKit["k"],@gmKit["h"],@gmKit["l"],@gmKit["o"],@gmKit["c"],@gmKit["cc"]=@gmSnare,@gmKick,@gmHiTom,@gmLoTom,@gmOpenH,@gmCloseH,@gmCrashCym
|
1785
|
+
if not File.exist?(file)
|
1786
|
+
@percussionList=false
|
1787
|
+
else
|
1788
|
+
@percussionList=self.loadMap(file,0)
|
1789
|
+
@snare=self.percussionGet("snare")
|
1790
|
+
end
|
1791
|
+
end
|
1792
|
+
def self.trackMake data
|
1793
|
+
@marksh||=@marktrack.calc
|
1794
|
+
data=data.split("\n").map{|i|
|
1795
|
+
i=~/^# marktrack\(([^\)]+)\)/
|
1796
|
+
if $&
|
1797
|
+
key=$1
|
1798
|
+
pos=@marksh[key]
|
1799
|
+
c="position mark: #{key}, #{pos*1.0/@tbase}"
|
1800
|
+
@marksh.keys.member?(key) ? self.dummyEvent(c,pos) : i
|
1801
|
+
else
|
1802
|
+
i
|
1803
|
+
end
|
1804
|
+
}*"\n"
|
1805
|
+
start="
|
1806
|
+
# track header
|
1807
|
+
4D 54 72 6B # MTrk
|
1808
|
+
"
|
1809
|
+
dsize=trackSizeHex(data,@cmark)
|
1810
|
+
trackend="
|
1811
|
+
00 FF 2F 00 # end of track
|
1812
|
+
"
|
1813
|
+
[start,dsize,data,trackend]
|
1814
|
+
end
|
1815
|
+
def self.dumpHex
|
1816
|
+
@h
|
1817
|
+
end
|
1818
|
+
end
|
1819
|
+
def multiplet d,tbase
|
1820
|
+
d=~/\/((\*)?([[:digit:].]+)?:)?(.*)\//
|
1821
|
+
tickmode=$2
|
1822
|
+
i=$4
|
1823
|
+
rate=$3 ? $3.to_f : 1
|
1824
|
+
rate=1 if rate==0
|
1825
|
+
if tickmode
|
1826
|
+
total=$3.to_i
|
1827
|
+
else
|
1828
|
+
total=tbase*rate
|
1829
|
+
end
|
1830
|
+
r=i.scan(/\(\?:[^\]]+\)|\(x:[^\]]+\)|\(chord:[^)]+\)|\(C:[^)]+\)|:[^\(,]+\([^\)]+\),|:[^,]+,|[[:digit:]\.]+|_[^!]+!|~|\([-+]*[[:digit:]]?\)|[-+^`']|./)
|
1831
|
+
wait=[]
|
1832
|
+
notes=[]
|
1833
|
+
mod=[]
|
1834
|
+
r.each{|i|
|
1835
|
+
case i
|
1836
|
+
when "-","+","^","`","'",/^\([-+]*[[:digit:]]?\)/
|
1837
|
+
mod<<i
|
1838
|
+
when /^\((\?|x|C|chord):[^\)]+\)|^\^?:[^,]+,/
|
1839
|
+
wait<<1
|
1840
|
+
notes<<"#{mod*""}#{i}"
|
1841
|
+
mod=[]
|
1842
|
+
when /^[[:digit:]]+/
|
1843
|
+
wait[-1]*=i.to_f
|
1844
|
+
when "="
|
1845
|
+
wait<<wait[-1]
|
1846
|
+
notes<<notes[-1]
|
1847
|
+
when " "
|
1848
|
+
else
|
1849
|
+
wait<<1
|
1850
|
+
notes<<"#{mod*""}#{i}"
|
1851
|
+
mod=[]
|
1852
|
+
end
|
1853
|
+
}
|
1854
|
+
sum=wait.inject{|s,i|s+i}
|
1855
|
+
ls=wait.map{|i|(i*1.0/sum*total).round} # .map{|i|i.round(dep)}
|
1856
|
+
er=(total-ls.inject{|s,i|s+i}).to_i
|
1857
|
+
if er>0
|
1858
|
+
er.times{|i|
|
1859
|
+
ls[i]+=1
|
1860
|
+
}
|
1861
|
+
else
|
1862
|
+
(-er).times{|i|
|
1863
|
+
ls[-1-i]-=1
|
1864
|
+
}
|
1865
|
+
end
|
1866
|
+
er=(total-ls.inject{|s,i|s+i})
|
1867
|
+
ls[-1]+=er
|
1868
|
+
result=[]
|
1869
|
+
notes.size.times{|i|
|
1870
|
+
result<<notes[i]
|
1871
|
+
result<<"*#{ls[i]}"
|
1872
|
+
}
|
1873
|
+
p "multiplet: ",total,ls.inject{|s,i|s+i} if $DEBUG && $debuglevel>1
|
1874
|
+
result*""
|
1875
|
+
end
|
1876
|
+
|
1877
|
+
def funcApply m,name,x
|
1878
|
+
a=m.keys.select{|k|k=~/^#{name}\(/}[0]
|
1879
|
+
fbodyOrg,*default=m[a]
|
1880
|
+
return false if ! fbodyOrg
|
1881
|
+
x=~/(([^:]*):)?/
|
1882
|
+
interval,x=$2,$'
|
1883
|
+
x=x.split(",")
|
1884
|
+
max=x.size
|
1885
|
+
x+=default
|
1886
|
+
a=~/\((.*)\)/
|
1887
|
+
args=$1.split(',').map{|k|"$#{k}"}
|
1888
|
+
n=0
|
1889
|
+
# p "x,args,mac:",x,args,fbody
|
1890
|
+
r=[]
|
1891
|
+
max.times{|i|
|
1892
|
+
fbody=fbodyOrg
|
1893
|
+
args.each{|k|
|
1894
|
+
fbody=fbody.gsub(k){x[n]}
|
1895
|
+
n+=1
|
1896
|
+
}
|
1897
|
+
r<<fbody
|
1898
|
+
}
|
1899
|
+
sep=""
|
1900
|
+
sep="(wait:#{interval})" if interval
|
1901
|
+
r*sep
|
1902
|
+
end
|
1903
|
+
def macroDef data
|
1904
|
+
macro={}
|
1905
|
+
mline=false
|
1906
|
+
tmp=[]
|
1907
|
+
name=""
|
1908
|
+
num=1
|
1909
|
+
s=data.scan(/macro +[^ ;:=]+ *:= *\( *;|macro +[^ ;:=]+ *:=[^;]+|[^ ;:=]+ *:= *\( *;|[^ ;:=]+ *:=[^;]+| *\) *;|[^;]+|./)
|
1910
|
+
data=s.map{|i|
|
1911
|
+
case i
|
1912
|
+
when /^(macro +)? *([^ ;:=]+) *:= *\( *;/
|
1913
|
+
mline=true
|
1914
|
+
num=1
|
1915
|
+
name=$2
|
1916
|
+
tmp=[]
|
1917
|
+
""
|
1918
|
+
when /^(macro +)? *([^ ;:=]+) *:=([^;]+)/
|
1919
|
+
r=$3
|
1920
|
+
key=$2
|
1921
|
+
chord=/([^$]|^)\{([^\{\}]*)\}/
|
1922
|
+
r.gsub!(chord){"#{$1}(C:#{$2})"} while r=~chord
|
1923
|
+
macro[key]=modifierComp(r,macro)
|
1924
|
+
""
|
1925
|
+
when / *\) *;/
|
1926
|
+
mline=false
|
1927
|
+
name=""
|
1928
|
+
when ";",/^ *$/
|
1929
|
+
i
|
1930
|
+
else
|
1931
|
+
if mline
|
1932
|
+
key="#{name}[#{num}]"
|
1933
|
+
macro[key]=i
|
1934
|
+
num+=1
|
1935
|
+
""
|
1936
|
+
else
|
1937
|
+
i
|
1938
|
+
end
|
1939
|
+
end
|
1940
|
+
}*""
|
1941
|
+
[macro,data]
|
1942
|
+
end
|
1943
|
+
def nestsearch d,macro
|
1944
|
+
a=d.scan(/\[[^\[\]]*\] *[[:digit:]]+/)!=[]
|
1945
|
+
r=d.scan(/\/[^\/]+\/|\[|\]|\.FINE|\.DS|\.DC|\.\$|\.toCODA|\.CODA|\.SKIP|\$\{[^ \{\}]+\}|\$[^ ;\$*_^`'+-]+|;|./).map{|i|
|
1946
|
+
case i
|
1947
|
+
when /^\$\{([^\}]+)\}/
|
1948
|
+
$1
|
1949
|
+
when /^\$/
|
1950
|
+
$'
|
1951
|
+
when /\/[^\/]+\//
|
1952
|
+
true
|
1953
|
+
else
|
1954
|
+
""
|
1955
|
+
end
|
1956
|
+
}
|
1957
|
+
b=(macro.keys-r).size<macro.keys.size
|
1958
|
+
c=r.member?(true)
|
1959
|
+
chord=false
|
1960
|
+
d=~/([^$]|^)\{([^\}]*)\}/
|
1961
|
+
chord=true if $&
|
1962
|
+
p "nest? #{a} #{b} #{c} #{chord}",r,macro if $DEBUG
|
1963
|
+
a||b||c||chord
|
1964
|
+
end
|
1965
|
+
def tie d,tbase
|
1966
|
+
res=[]
|
1967
|
+
# if no length word after '~' length is 1
|
1968
|
+
d.gsub!(/~([^*[:digit:]])?/){$1 ? "~1#{$1}" : $&} while d=~/~[^*[:digit:]]/
|
1969
|
+
li=d.scan(/\$\{[^\}]+\}|\$[^ ;\$_*^`'+-]+|\([^)\(]*\)|:[^\(,]+\([^)]+\),|:[^,]+,|_[^!]+!|_[^_]__[^?]+\?|v[[:digit:]]+|[<>][[:digit:]]*|\*?[[:digit:].]+|\([VGABLN]:[^)]+\)|~|./)
|
1970
|
+
li.each{|i|
|
1971
|
+
case i
|
1972
|
+
when /^(\*)?([[:digit:].]+)/
|
1973
|
+
tick=$1? $2.to_f : $2.to_f*tbase
|
1974
|
+
if res[-1][0]==:tick
|
1975
|
+
res[-1][1]+=tick
|
1976
|
+
else
|
1977
|
+
res<<[:tick,tick]
|
1978
|
+
end
|
1979
|
+
when "~"
|
1980
|
+
res<<[:tick,tbase] if res[-1][0]==:e
|
1981
|
+
when /^\([VGABLN]:[^)]+/
|
1982
|
+
res<<[:modifier,i]
|
1983
|
+
else
|
1984
|
+
res<<[:e,i]
|
1985
|
+
end
|
1986
|
+
}
|
1987
|
+
line=""
|
1988
|
+
frest=0
|
1989
|
+
(res.size-1).times{|i|
|
1990
|
+
next if res[i][0]!=:modifier
|
1991
|
+
next if res[i+1][0]!=:tick
|
1992
|
+
# if tick after modifier, it must be by tie mark
|
1993
|
+
n=i-1
|
1994
|
+
n-=1 while res[n][0]!=:tick
|
1995
|
+
res[n][1]+=res[i+1][1]
|
1996
|
+
res[i+1][0]=:omit
|
1997
|
+
}
|
1998
|
+
res.each{|mark,data|
|
1999
|
+
case mark
|
2000
|
+
when :e , :modifier
|
2001
|
+
line<<data
|
2002
|
+
when :tick
|
2003
|
+
tick=data.to_i
|
2004
|
+
frest+=data-tick
|
2005
|
+
(tick+=frest;puts "frest:#{frest}" if $DEBUG && $debuglevel>1;frest=0) if frest>1
|
2006
|
+
line<<"*#{tick}"
|
2007
|
+
when :omit
|
2008
|
+
puts "# shift tick data by tie part" if $DEBUG
|
2009
|
+
else
|
2010
|
+
STDERR.puts "tie?"
|
2011
|
+
end
|
2012
|
+
}
|
2013
|
+
p res,line if $DEBUG && $debuglevel>1
|
2014
|
+
line
|
2015
|
+
end
|
2016
|
+
# repeat block analysis: no relation with MIDI format
|
2017
|
+
def repCalc line,macro,tbase
|
2018
|
+
rpt=/\[([^\[\]]*)\] *([[:digit:]]+)/
|
2019
|
+
line.gsub!(rpt){$1*$2.to_i} while line=~rpt
|
2020
|
+
chord=/([^$]|^)\{([^\{\}]*)\}/
|
2021
|
+
line.gsub!(chord){"#{$1}(C:#{$2})"} while line=~chord
|
2022
|
+
line=line.scan(/(\.\$)|(\$([[:alnum:]]+)\(([^\)]+)\))|(.)/).map{|a,b,bname,barg,c|
|
2023
|
+
if a
|
2024
|
+
a
|
2025
|
+
elsif c
|
2026
|
+
c
|
2027
|
+
else
|
2028
|
+
r=funcApply(macro,bname,barg)
|
2029
|
+
r ? r : b
|
2030
|
+
end
|
2031
|
+
}*""
|
2032
|
+
a=line.scan(/\/[^\/]+\/|\[|\]|\.FINE|\.DS|\.DC|\.\$|\.toCODA|\.CODA|\.SKIP|\$\{[^ \{\}]+\}|\$[^ ;\$_*^,\)\(`'\/+-]+|\([^\)]*:|\)|./)
|
2033
|
+
a=a.map{|i|
|
2034
|
+
if i=~/^\/[^\/]+\//
|
2035
|
+
if i=~/\$/
|
2036
|
+
i=i.gsub(/\$\{([^ ;\$_*^,\)\(`'\/+-]+)\}/){macro[$1]}.gsub(/\$([^ ;\$_*^,\)\(`'\/+-]+)/){macro[$1]}
|
2037
|
+
end
|
2038
|
+
multiplet(i,tbase)
|
2039
|
+
else
|
2040
|
+
i
|
2041
|
+
end
|
2042
|
+
}
|
2043
|
+
hs={}
|
2044
|
+
a.each_with_index{|d,i|hs[i]=d}
|
2045
|
+
hs=hs.invert
|
2046
|
+
res=[]
|
2047
|
+
done=[]
|
2048
|
+
dsflag=dcflag=false
|
2049
|
+
counter=0
|
2050
|
+
repcount=0
|
2051
|
+
pointDS=0
|
2052
|
+
rep=[]
|
2053
|
+
while true
|
2054
|
+
countertmp=counter
|
2055
|
+
counter+=1 # next
|
2056
|
+
current=a[countertmp]
|
2057
|
+
puts "#{countertmp}: #{current}, #{rep},done: #{done*","}" if $DEBUG
|
2058
|
+
break if ! current
|
2059
|
+
case current
|
2060
|
+
when "["
|
2061
|
+
if ! done.member?(countertmp)
|
2062
|
+
repcount+=1
|
2063
|
+
rep<<countertmp
|
2064
|
+
done<<countertmp
|
2065
|
+
end
|
2066
|
+
when "]"
|
2067
|
+
if ! done.member?(countertmp)
|
2068
|
+
done<<countertmp
|
2069
|
+
counter=rep.shift+1
|
2070
|
+
end
|
2071
|
+
when ".DS"
|
2072
|
+
counter=pointDS
|
2073
|
+
dsflag=true
|
2074
|
+
when ".DC"
|
2075
|
+
counter=0
|
2076
|
+
dsflag=true
|
2077
|
+
when ".SKIP"
|
2078
|
+
if done.member?(countertmp)
|
2079
|
+
counter=done[-1]
|
2080
|
+
else
|
2081
|
+
done<<countertmp
|
2082
|
+
end
|
2083
|
+
when ".toCODA"
|
2084
|
+
if dsflag
|
2085
|
+
counter=hs[".CODA"]
|
2086
|
+
end
|
2087
|
+
when ".FINE"
|
2088
|
+
if (dsflag || dcflag)
|
2089
|
+
break
|
2090
|
+
end
|
2091
|
+
when /^\$\{([^ \{\}]+)\}/
|
2092
|
+
current=macro[$1]
|
2093
|
+
when /^\$([^ ;]+)/
|
2094
|
+
current=macro[$1]
|
2095
|
+
when ".$"
|
2096
|
+
pointDS=countertmp
|
2097
|
+
when ";"
|
2098
|
+
current=""
|
2099
|
+
else
|
2100
|
+
current
|
2101
|
+
end
|
2102
|
+
res<<current
|
2103
|
+
end
|
2104
|
+
res=(res-["[","]",".CODA",".DS",".DC",".FINE",".toCODA",".$",".SKIP"])*""
|
2105
|
+
res=repCalc(res,macro,tbase) while macro.keys.size>0 && nestsearch(res,macro)
|
2106
|
+
p res if $DEBUG && $debuglevel>1
|
2107
|
+
# 空白
|
2108
|
+
res=res.split.join
|
2109
|
+
res=tie(res,tbase)
|
2110
|
+
end
|
2111
|
+
def loadCalc d
|
2112
|
+
if d=~/\(loadf:(.+)(,(.+))?\)/
|
2113
|
+
file=$1
|
2114
|
+
num=$3 ? $3.to_i : false
|
2115
|
+
[:raw,MidiRead.readtrack(file,num)]
|
2116
|
+
else
|
2117
|
+
[:seq,d]
|
2118
|
+
end
|
2119
|
+
end
|
2120
|
+
def modifierComp t,macro
|
2121
|
+
rawHexPart(t,macro).scan(/\([VGABLN]:[^)]+\)|./).map{|i|
|
2122
|
+
case i
|
2123
|
+
when /^\((V|G):([^)]+)\)/
|
2124
|
+
mode=$1
|
2125
|
+
n=0
|
2126
|
+
v=$2.split(/,/).map{|i|i.split(/ +/)-[""]}.map{|i|
|
2127
|
+
i=["o"] if i==[]
|
2128
|
+
i.map{|c|
|
2129
|
+
c=c.split('') if c=~/^[-\+o]+$/
|
2130
|
+
c
|
2131
|
+
}
|
2132
|
+
}
|
2133
|
+
"(#{mode}:#{v*","})"
|
2134
|
+
when /^\((A|B|L|N):([^)]+)\)/
|
2135
|
+
mode=$1
|
2136
|
+
n=0
|
2137
|
+
v=$2.split(/,/).map{|i|i.split(/ +/)-[""]}.map{|i|
|
2138
|
+
i=["o"] if i==[]
|
2139
|
+
i.map{|c|
|
2140
|
+
c=c.split('') if c=~/^o+$/
|
2141
|
+
c
|
2142
|
+
}
|
2143
|
+
}
|
2144
|
+
"(#{mode}:#{v*","})"
|
2145
|
+
else
|
2146
|
+
i
|
2147
|
+
end
|
2148
|
+
}*""
|
2149
|
+
end
|
2150
|
+
|
2151
|
+
class MmlTracks
|
2152
|
+
attr_accessor :tracknum, :tbase, :rundatas, :rawdatas, :mx
|
2153
|
+
attr_accessor :bpm, :velocity, :octave, :vfuzzy, :data, :infile, :outfile
|
2154
|
+
def initialize tbase=480,pagesep='///',expfile='expfile.txt',cmark=';;'
|
2155
|
+
String.new.setcmark(cmark)
|
2156
|
+
@mx=MidiHex
|
2157
|
+
@rundatas=[]
|
2158
|
+
@rawdatas=[]
|
2159
|
+
@macro={}
|
2160
|
+
@tbase=tbase
|
2161
|
+
@tracks=[]
|
2162
|
+
@fuzzymode=false
|
2163
|
+
@fuzz=false
|
2164
|
+
@expfile=expfile
|
2165
|
+
@pagesep=pagesep
|
2166
|
+
@bpm=120
|
2167
|
+
@velocity=0x40
|
2168
|
+
@octave=:near
|
2169
|
+
@vfuzzy=2
|
2170
|
+
end
|
2171
|
+
def init test,fz
|
2172
|
+
@mx.prepare(@bpm,@tbase,@velocity,@octave,@vfuzzy)
|
2173
|
+
@mx.setfile(@infile)
|
2174
|
+
@mx.setmidiname(@outfile) if @outfile
|
2175
|
+
@mx.setdata(@data) if ! @mx.getdata
|
2176
|
+
(hint;exit) if (! @mx.getdata || ! @mx.getmidiname ) && ! test
|
2177
|
+
settest if test
|
2178
|
+
@tracks=@mx.getdata.tracks(@pagesep)
|
2179
|
+
showtracks if $DEBUG && $debuglevel>1
|
2180
|
+
fuzzy(fz)
|
2181
|
+
end
|
2182
|
+
def settest
|
2183
|
+
@mx.test($testdata,$testmode)
|
2184
|
+
end
|
2185
|
+
def fuzzy fz
|
2186
|
+
@fuzzymode=fz
|
2187
|
+
@fuzz=unirand(fz,@tracks.size) if fz
|
2188
|
+
if fz && (@tbase/fz<8)
|
2189
|
+
STDERR.puts "really?#{"?"*(8*fz/@tbase)}"
|
2190
|
+
end
|
2191
|
+
end
|
2192
|
+
def showtracks
|
2193
|
+
p @tracks
|
2194
|
+
end
|
2195
|
+
def setmacro
|
2196
|
+
@tracks.map{|track|
|
2197
|
+
m,track=macroDef(track)
|
2198
|
+
@macro.merge!(m)
|
2199
|
+
track=modifierComp(track,@macro)
|
2200
|
+
repCalc(track,@macro,tbase)
|
2201
|
+
}.each{|t|
|
2202
|
+
r=loadCalc(t)
|
2203
|
+
if @fuzzymode
|
2204
|
+
n=@fuzz.shift
|
2205
|
+
STDERR.puts "track shift: #{n} tick#{n>1 ? 's' : ''}"
|
2206
|
+
pre="r*#{n} "
|
2207
|
+
else
|
2208
|
+
pre=""
|
2209
|
+
end
|
2210
|
+
case r[0]
|
2211
|
+
when :raw
|
2212
|
+
@rawdatas<<r[1]
|
2213
|
+
when :seq
|
2214
|
+
@rundatas<<pre+r[1]
|
2215
|
+
end
|
2216
|
+
}
|
2217
|
+
p @macro if$DEBUG
|
2218
|
+
@rawdatas.flatten!
|
2219
|
+
open(@expfile,"w"){|f|f.puts @rundatas*"|||"} if @expfile
|
2220
|
+
@tracknum=@rawdatas.size+@rundatas.size
|
2221
|
+
@tracknum=@tracks.size
|
2222
|
+
end
|
2223
|
+
def settracks
|
2224
|
+
@htracks=[]
|
2225
|
+
tc=0
|
2226
|
+
# remember starting position check if data exist before sound
|
2227
|
+
@htracks << @mx.metaTitle + @mx.generaterText + @mx.starttempo.data + @mx.makefraze(@rundatas[0],tc) + @mx.lastrest
|
2228
|
+
@rundatas[1..-1].each{|track|
|
2229
|
+
tc+=1
|
2230
|
+
@htracks<< @mx.restHex + @mx.makefraze(track,tc) + @mx.lastrest
|
2231
|
+
}
|
2232
|
+
end
|
2233
|
+
def pack
|
2234
|
+
@header=@mx.header(1, @tracknum, @tbase)
|
2235
|
+
alla=[@header]+@htracks.map{|t|@mx.trackMake(t)}.flatten
|
2236
|
+
puts alla if $DEBUG
|
2237
|
+
all=alla.map{|i|i.trim("","#")}*""
|
2238
|
+
array=[all.split.join]
|
2239
|
+
@binary = array.pack( "H*" )
|
2240
|
+
end
|
2241
|
+
def make test=false,fz=false
|
2242
|
+
init(test,fz)
|
2243
|
+
setmacro
|
2244
|
+
settracks
|
2245
|
+
pack
|
2246
|
+
end
|
2247
|
+
def save outfile=@mx.getmidiname
|
2248
|
+
# save data. data = MIDI-header + seq-made MIDI-tracks + loaded extra MIDI-tracks.
|
2249
|
+
if outfile==""
|
2250
|
+
print @binary
|
2251
|
+
@rawdatas.each{|i|
|
2252
|
+
print i
|
2253
|
+
}
|
2254
|
+
else
|
2255
|
+
open(outfile,"wb"){|f|
|
2256
|
+
f.write @binary
|
2257
|
+
@rawdatas.each{|i|
|
2258
|
+
f.write i
|
2259
|
+
}
|
2260
|
+
}
|
2261
|
+
end
|
2262
|
+
end
|
2263
|
+
def compile infile,outfile='out.mid',data=""
|
2264
|
+
@infile=infile
|
2265
|
+
@outfile=outfile
|
2266
|
+
@data=data
|
2267
|
+
make
|
2268
|
+
save
|
2269
|
+
end
|
2270
|
+
end
|