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.
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