smml 0.0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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