smf 0.15.12
Sign up to get free protection for your applications and to get access to all the features.
- data/MANUAL +172 -0
- data/MANUAL.en +135 -0
- data/MANUAL.en.html +150 -0
- data/MANUAL.en.rd +144 -0
- data/MANUAL.html +201 -0
- data/MANUAL.rd +179 -0
- data/README +33 -0
- data/README.en +33 -0
- data/lib/smf.rb +736 -0
- data/lib/smf/divert.rb +21 -0
- data/lib/smf/io.rb +1278 -0
- data/lib/smf/toy/beatmap.rb +73 -0
- data/lib/smf/toy/gm.rb +327 -0
- data/lib/smf/toy/groove.rb +34 -0
- data/lib/smf/toy/macro.rb +282 -0
- data/lib/smf/toy/macro/mml.rb +34 -0
- data/lib/smf/toy/macro/mml/parser.rb +545 -0
- data/lib/smf/toy/macro/mml/parser.ry +239 -0
- data/lib/smf/toy/macro/stt.rb +33 -0
- data/lib/smf/toy/morse.rb +126 -0
- data/lib/smf/toy/quantize.rb +32 -0
- data/lib/smf/toy/rmi.rb +29 -0
- data/lib/smf/toy/searchsegment.rb +24 -0
- data/lib/smf/toy/shuffle.rb +39 -0
- data/lib/smf/toy/tempomap.rb +75 -0
- data/lib/smf/toy/text.rb +369 -0
- data/lib/smf/toy/velcomp.rb +42 -0
- data/lib/smf/toy/virtual.rb +118 -0
- data/lib/smf/toy/xml.rb +377 -0
- data/sample/Makefile +58 -0
- data/sample/bwv772.mid +0 -0
- data/sample/bwv772.mml +94 -0
- data/sample/bwv775.mid +0 -0
- data/sample/bwv775.mml +157 -0
- data/sample/bwv787.mid +0 -0
- data/sample/bwv787.mml +129 -0
- data/sample/groove.grv +33 -0
- data/sample/groove.rb +45 -0
- data/sample/ltvddpd2.mid +0 -0
- data/sample/ltvddpd2.stt +60 -0
- data/sample/merge.rb +38 -0
- data/sample/mml-samp.rb +19 -0
- data/sample/mml.rb +36 -0
- data/sample/morse-samp.rb +11 -0
- data/sample/morse.rb +31 -0
- data/sample/play-oss.rb +215 -0
- data/sample/play-oss2.rb +253 -0
- data/sample/play-oss3.rb +150 -0
- data/sample/play-spkr.rb +97 -0
- data/sample/play-win.rb +195 -0
- data/sample/quantize.rb +41 -0
- data/sample/rand1.rb +21 -0
- data/sample/rand2.rb +24 -0
- data/sample/rmi2smf.rb +26 -0
- data/sample/shuffle.rb +43 -0
- data/sample/smf2rmi.rb +26 -0
- data/sample/smf2smf.rb +26 -0
- data/sample/smf2text.rb +27 -0
- data/sample/smf2wav.rb +123 -0
- data/sample/smf2xml.rb +27 -0
- data/sample/split.rb +40 -0
- data/sample/stt-samp.rb +19 -0
- data/sample/stt.rb +36 -0
- data/sample/text2smf.rb +28 -0
- data/sample/velcomp.rb +45 -0
- data/sample/virtual-samp.rb +19 -0
- data/sample/xml2smf.rb +28 -0
- metadata +128 -0
data/sample/play-win.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# play-win.rb: Written by Tadayoshi Funaba 2005,2006
|
4
|
+
# $Id: play-win.rb,v 1.4 2006-11-10 21:57:06+09 tadf Exp $
|
5
|
+
|
6
|
+
require 'smf'
|
7
|
+
require 'smf/toy/tempomap'
|
8
|
+
require 'Win32API'
|
9
|
+
require 'gopt'
|
10
|
+
include SMF
|
11
|
+
|
12
|
+
module SMF
|
13
|
+
|
14
|
+
class Kernel32 < Win32API
|
15
|
+
|
16
|
+
def initialize(proc, import, export)
|
17
|
+
super('kernel32', proc, import, export)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class WinMM < Win32API
|
23
|
+
|
24
|
+
def initialize(proc, import, export)
|
25
|
+
super('winmm', proc, import, export)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
module WinBase
|
31
|
+
|
32
|
+
NORMAL_PRIORITY_CLASS = 0x00000020
|
33
|
+
IDEL_PRIORITY_CLASS = 0x00000040
|
34
|
+
HIGH_PRIORITY_CLASS = 0x00000080
|
35
|
+
REALTIME_PRIORITY_CLASS = 0x00000100
|
36
|
+
|
37
|
+
@@GetCurrentProcess = Kernel32.new('GetCurrentProcess', %w(), 'l')
|
38
|
+
@@SetPriorityClass = Kernel32.new('SetPriorityClass', %w(l l), 'l')
|
39
|
+
|
40
|
+
def setpriorityclass(prc=NORMAL_PRIORITY_CLASS)
|
41
|
+
prh = @@GetCurrentProcess.call
|
42
|
+
@@SetPriorityClass.call(prh, prc)
|
43
|
+
end
|
44
|
+
|
45
|
+
module_function :setpriorityclass
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class DevMidiOut
|
50
|
+
|
51
|
+
@@GetNumDevs = WinMM.new('midiOutGetNumDevs', %w(), 'l')
|
52
|
+
@@GetDevCaps = WinMM.new('midiOutGetDevCaps', %w(l p l), 'l')
|
53
|
+
@@Open = WinMM.new('midiOutOpen', %w(p l l l l), 'l')
|
54
|
+
@@Reset = WinMM.new('midiOutReset', %w(l), 'l')
|
55
|
+
@@Close = WinMM.new('midiOutClose', %w(l), 'l')
|
56
|
+
@@ShortMsg = WinMM.new('midiOutShortMsg', %w(l l), 'l')
|
57
|
+
@@LongMsg = WinMM.new('midiOutLongMsg', %w(l p l), 'l')
|
58
|
+
@@PrepareHeader = WinMM.new('midiOutPrepareHeader', %w(l p l), 'l')
|
59
|
+
@@UnprepareHeader = WinMM.new('midiOutUnprepareHeader', %w(l p l), 'l')
|
60
|
+
|
61
|
+
def self.getnumdev() @@GetNumDevs.call end
|
62
|
+
|
63
|
+
def self.getdevcaps(did)
|
64
|
+
caps = "\000" * 52
|
65
|
+
@@GetDevCaps.call(did, caps, caps.size)
|
66
|
+
caps.unpack('S2LZ32S4L')
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize(did)
|
70
|
+
mo = "\000" * 4
|
71
|
+
@@Open.call(mo, did, 0, 0, 0)
|
72
|
+
@mo = mo.unpack('L')[0]
|
73
|
+
end
|
74
|
+
|
75
|
+
def reset() @@Reset.call(@mo) end
|
76
|
+
def close() @@Close.call(@mo) end
|
77
|
+
def shortmsg(msg) @@ShortMsg.call(@mo, msg) end
|
78
|
+
|
79
|
+
def longmsg(msg)
|
80
|
+
moh = [msg, msg.size, 0, 0, 0, 0, 0, 0, ''].pack('PL7A32')
|
81
|
+
@@PrepareHeader.call(@mo, moh, moh.size)
|
82
|
+
@@LongMsg.call(@mo, moh, moh.size)
|
83
|
+
@@UnprepareHeader.call(@mo, moh, moh.size)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
class Sequence
|
89
|
+
|
90
|
+
class Timer
|
91
|
+
|
92
|
+
def initialize() @start = Time.now end
|
93
|
+
def elapse() Time.now - @start end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
class Play < XSCallback
|
98
|
+
|
99
|
+
def initialize(tm, num) @tm, @num = tm, num end
|
100
|
+
|
101
|
+
def header(format, ntrks, division, tc)
|
102
|
+
WinBase.setpriorityclass(WinBase::HIGH_PRIORITY_CLASS)
|
103
|
+
ndev = DevMidiOut.getnumdev
|
104
|
+
puts(DevMidiOut.getdevcaps(@num)[3]) if $VERBOSE
|
105
|
+
unless @num < ndev
|
106
|
+
raise 'device not available'
|
107
|
+
end
|
108
|
+
@mo = DevMidiOut.new(@num)
|
109
|
+
end
|
110
|
+
|
111
|
+
def track_start() @offset = 0 end
|
112
|
+
|
113
|
+
def delta(delta)
|
114
|
+
@timer ||= Timer.new
|
115
|
+
if delta.nonzero?
|
116
|
+
@offset += delta
|
117
|
+
e = @tm.offset2elapse(@offset) - @timer.elapse
|
118
|
+
if e > 0
|
119
|
+
sleep(e.to_f)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def midimsg(sb, db1, db2=0)
|
125
|
+
@mo.shortmsg(sb | (db1 << 8) | (db2 << 16))
|
126
|
+
end
|
127
|
+
|
128
|
+
private :midimsg
|
129
|
+
|
130
|
+
def noteoff(ch, note, vel) midimsg(ch | 0x80, note, vel) end
|
131
|
+
def noteon(ch, note, vel) midimsg(ch | 0x90, note, vel) end
|
132
|
+
|
133
|
+
def polyphonickeypressure(ch, note, val)
|
134
|
+
midimsg(ch | 0xa0, note, val)
|
135
|
+
end
|
136
|
+
|
137
|
+
def controlchange(ch, num, val) midimsg(ch | 0xb0, num, val) end
|
138
|
+
def programchange(ch, num) midimsg(ch | 0xc0, num) end
|
139
|
+
def channelpressure(ch, val) midimsg(ch | 0xd0, val) end
|
140
|
+
|
141
|
+
def pitchbendchange(ch, val)
|
142
|
+
val += 0x2000
|
143
|
+
lsb = val & 0x7f
|
144
|
+
msb = (val >> 7) & 0x7f
|
145
|
+
midimsg(ch | 0xe0, lsb, msb)
|
146
|
+
end
|
147
|
+
|
148
|
+
def channelmodemessage(ch, num, val) controlchange(ch, num, val) end
|
149
|
+
|
150
|
+
private :channelmodemessage
|
151
|
+
|
152
|
+
def allsoundoff(ch) channelmodemessage(ch, 0x78, 0) end
|
153
|
+
def resetallcontrollers(ch) channelmodemessage(ch, 0x79, 0) end
|
154
|
+
def localcontrol(ch, val) channelmodemessage(ch, 0x7a, val) end
|
155
|
+
def allnotesoff(ch) channelmodemessage(ch, 0x7b, 0) end
|
156
|
+
def omnioff(ch) channelmodemessage(ch, 0x7c, 0) end
|
157
|
+
def omnion(ch) channelmodemessage(ch, 0x7d, 0) end
|
158
|
+
def monomode(ch, val) channelmodemessage(ch, 0x7e, val) end
|
159
|
+
def polymode(ch) channelmodemessage(ch, 0x7f, 0) end
|
160
|
+
|
161
|
+
def exclusivefx(data) @mo.longmsg(data) end
|
162
|
+
|
163
|
+
private :exclusivefx
|
164
|
+
|
165
|
+
def exclusivef0(data) exclusivefx("\xf0" + data) end
|
166
|
+
def exclusivef7(data) exclusivefx(data) end
|
167
|
+
|
168
|
+
def result() @mo.close end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
def play(num=0)
|
173
|
+
j = join
|
174
|
+
tm = TempoMap.new(j)
|
175
|
+
WS.new(j, Play.new(tm, num)).read
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
def usage
|
183
|
+
warn 'usage: play-win [-d num] [input]'
|
184
|
+
exit 1
|
185
|
+
end
|
186
|
+
|
187
|
+
usage unless opt = Gopt.gopt('d:')
|
188
|
+
usage unless $*.size >= 0 && $*.size <= 1
|
189
|
+
file = $*.shift
|
190
|
+
file = nil if file == '-'
|
191
|
+
|
192
|
+
num = (opt[:d] || '0').to_i
|
193
|
+
|
194
|
+
sq = unless file then Sequence.read($stdin) else Sequence.load(file) end
|
195
|
+
sq.play(num)
|
data/sample/quantize.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# quantize.rb: Written by Tadayoshi Funaba 1999-2006
|
4
|
+
# $Id: quantize.rb,v 1.10 2006-11-10 21:57:06+09 tadf Exp $
|
5
|
+
|
6
|
+
require 'smf'
|
7
|
+
require 'smf/toy/quantize'
|
8
|
+
require 'gopt'
|
9
|
+
include SMF
|
10
|
+
|
11
|
+
def usage
|
12
|
+
warn 'usage: quantize [-o output] [-u n/d] [input]'
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
usage unless opt = Gopt.gopt('o:u:')
|
17
|
+
usage unless $*.size >= 0 && $*.size <= 1
|
18
|
+
|
19
|
+
ifile = $*.shift
|
20
|
+
ifile = nil if ifile == '-'
|
21
|
+
|
22
|
+
ofile = opt[:o] if opt[:o]
|
23
|
+
ofile ||= File.basename(ifile, '.mid') + '-quantize.mid' if ifile
|
24
|
+
ofile = nil if ofile == '-'
|
25
|
+
|
26
|
+
n, d = (opt[:u] || '1/32').split('/')
|
27
|
+
unit = n.to_f / (d || '1').to_f
|
28
|
+
|
29
|
+
sq = unless ifile then Sequence.read($stdin) else Sequence.load(ifile) end
|
30
|
+
qu = Quantize.new(sq.division, unit)
|
31
|
+
|
32
|
+
sq.each do |tr|
|
33
|
+
tr.pop
|
34
|
+
tr.each do |ev|
|
35
|
+
case ev
|
36
|
+
when NoteOn
|
37
|
+
qu.quantize(ev)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
unless ofile then sq.write($stdout) else sq.save(ofile) end
|
data/sample/rand1.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'smf'
|
4
|
+
include SMF
|
5
|
+
|
6
|
+
sq = Sequence.new
|
7
|
+
tr = Track.new
|
8
|
+
sq << tr
|
9
|
+
tr << SequenceName.new(0, 'rand1')
|
10
|
+
srand
|
11
|
+
for ch in 0..7
|
12
|
+
offset = 0
|
13
|
+
for i in 0..127
|
14
|
+
note = rand(128)
|
15
|
+
vel = rand(127) + 1
|
16
|
+
tr << NoteOn .new(offset, ch, note, vel)
|
17
|
+
offset += rand(128)
|
18
|
+
tr << NoteOff.new(offset, ch, note, 64)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
sq.save('rand1.mid')
|
data/sample/rand2.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'smf'
|
4
|
+
include SMF
|
5
|
+
|
6
|
+
class Array; def rand() self[Kernel.rand(self.size)] end end
|
7
|
+
|
8
|
+
sq = Sequence.new
|
9
|
+
tr = Track.new
|
10
|
+
sq << tr
|
11
|
+
tr << SequenceName.new(0, 'rand2')
|
12
|
+
srand
|
13
|
+
for ch in 0..2
|
14
|
+
offset = 0
|
15
|
+
for i in 0..127
|
16
|
+
note = [0, 2, 4, 5, 7, 9, 10].rand
|
17
|
+
note += (rand(1) + 4 + ch) * 12
|
18
|
+
vel = rand(127) + 1
|
19
|
+
tr << NoteOn. new(offset, ch, note, vel)
|
20
|
+
offset += [12, 24, 48, 96, 192, 384].rand
|
21
|
+
tr << NoteOff.new(offset, ch, note, 64)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
sq.save('rand2.mid')
|
data/sample/rmi2smf.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# rmi2smf.rb: Written by Tadayoshi Funaba 2001-2006
|
4
|
+
# $Id: rmi2smf.rb,v 1.5 2006-11-10 21:57:06+09 tadf Exp $
|
5
|
+
|
6
|
+
require 'smf/toy/rmi'
|
7
|
+
require 'gopt'
|
8
|
+
|
9
|
+
def usage
|
10
|
+
warn 'usage: rmi2smf [-o output] [input]'
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
usage unless opt = Gopt.gopt('o:')
|
15
|
+
usage unless $*.size >= 0 && $*.size <= 1
|
16
|
+
|
17
|
+
ifile = $*.shift
|
18
|
+
ifile = nil if ifile == '-'
|
19
|
+
|
20
|
+
ofile = opt[:o] if opt[:o]
|
21
|
+
ofile ||= File.basename(ifile, '.rmi') + '.mid' if ifile
|
22
|
+
ofile = nil if ofile == '-'
|
23
|
+
|
24
|
+
i = (unless ifile then $stdin else open(ifile) end).binmode.read
|
25
|
+
o = SMF::RMI::rmi2smf(i)
|
26
|
+
(unless ofile then $stdout else open(ofile, 'w') end).binmode.write(o)
|
data/sample/shuffle.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# shuffle.rb: Written by Tadayoshi Funaba 2005,2006
|
4
|
+
# $Id: shuffle.rb,v 1.3 2006-11-10 21:57:06+09 tadf Exp $
|
5
|
+
|
6
|
+
require 'smf'
|
7
|
+
require 'smf/toy/shuffle'
|
8
|
+
require 'gopt'
|
9
|
+
include SMF
|
10
|
+
|
11
|
+
def usage
|
12
|
+
warn 'usage: shuffle [-o output] [-u n/d] [-a amount] [input]'
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
usage unless opt = Gopt.gopt('o:u:a:')
|
17
|
+
usage unless $*.size >= 0 && $*.size <= 1
|
18
|
+
|
19
|
+
ifile = $*.shift
|
20
|
+
ifile = nil if ifile == '-'
|
21
|
+
|
22
|
+
ofile = opt[:o] if opt[:o]
|
23
|
+
ofile ||= File.basename(ifile, '.mid') + '-shuffle.mid' if ifile
|
24
|
+
ofile = nil if ofile == '-'
|
25
|
+
|
26
|
+
n, d = (opt[:u] || '1/8').split('/')
|
27
|
+
unit = n.to_f / (d || '1').to_f
|
28
|
+
amount = (opt[:a] || '0.5').to_f
|
29
|
+
|
30
|
+
sq = unless ifile then Sequence.read($stdin) else Sequence.load(ifile) end
|
31
|
+
sh = Shuffle.new(sq.division, unit)
|
32
|
+
sh.amount = amount
|
33
|
+
|
34
|
+
sq.each do |tr|
|
35
|
+
tr.pop
|
36
|
+
tr.each do |ev|
|
37
|
+
case ev
|
38
|
+
when NoteOff, NoteOn
|
39
|
+
sh.shuffle(ev)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
unless ofile then sq.write($stdout) else sq.save(ofile) end
|
data/sample/smf2rmi.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# smf2rmi.rb: Written by Tadayoshi Funaba 2001-2006
|
4
|
+
# $Id: smf2rmi.rb,v 1.6 2006-11-10 21:57:06+09 tadf Exp $
|
5
|
+
|
6
|
+
require 'smf/toy/rmi'
|
7
|
+
require 'gopt'
|
8
|
+
|
9
|
+
def usage
|
10
|
+
warn 'usage: smf2rmi [-o output] [input]'
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
usage unless opt = Gopt.gopt('o:')
|
15
|
+
usage unless $*.size >= 0 && $*.size <= 1
|
16
|
+
|
17
|
+
ifile = $*.shift
|
18
|
+
ifile = nil if ifile == '-'
|
19
|
+
|
20
|
+
ofile = opt[:o] if opt[:o]
|
21
|
+
ofile ||= File.basename(ifile, '.mid') + '.rmi' if ifile
|
22
|
+
ofile = nil if ofile == '-'
|
23
|
+
|
24
|
+
i = (unless ifile then $stdin else open(ifile) end).binmode.read
|
25
|
+
o = SMF::RMI::smf2rmi(i)
|
26
|
+
(unless ofile then $stdout else open(ofile, 'w') end).binmode.write(o)
|
data/sample/smf2smf.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# smf2smf.rb: Written by Tadayoshi Funaba 1999-2006
|
4
|
+
# $Id: smf2smf.rb,v 1.8 2006-11-10 21:57:06+09 tadf Exp $
|
5
|
+
|
6
|
+
require 'smf'
|
7
|
+
require 'gopt'
|
8
|
+
include SMF
|
9
|
+
|
10
|
+
def usage
|
11
|
+
warn 'usage: smf2smf [-o output] [input]'
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
|
15
|
+
usage unless opt = Gopt.gopt('o:')
|
16
|
+
usage unless $*.size >= 0 && $*.size <= 1
|
17
|
+
|
18
|
+
ifile = $*.shift
|
19
|
+
ifile = nil if ifile == '-'
|
20
|
+
|
21
|
+
ofile = opt[:o] if opt[:o]
|
22
|
+
ofile ||= File.basename(ifile, '.mid') + '-smf2smf.mid' if ifile
|
23
|
+
ofile = nil if ofile == '-'
|
24
|
+
|
25
|
+
sq = unless ifile then Sequence.read($stdin) else Sequence.load(ifile) end
|
26
|
+
unless ofile then sq.write($stdout) else sq.save(ofile) end
|
data/sample/smf2text.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# smf2text.rb: Written by Tadayoshi Funaba 1999-2006
|
4
|
+
# $Id: smf2text.rb,v 1.14 2006-11-10 21:57:06+09 tadf Exp $
|
5
|
+
|
6
|
+
require 'smf'
|
7
|
+
require 'smf/toy/text'
|
8
|
+
require 'gopt'
|
9
|
+
include SMF
|
10
|
+
|
11
|
+
def usage
|
12
|
+
warn 'usage: smf2text [-o output] [input]'
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
usage unless opt = Gopt.gopt('o:')
|
17
|
+
usage unless $*.size >= 0 && $*.size <= 1
|
18
|
+
|
19
|
+
ifile = $*.shift
|
20
|
+
ifile = nil if ifile == '-'
|
21
|
+
|
22
|
+
ofile = opt[:o] if opt[:o]
|
23
|
+
ofile ||= File.basename(ifile, '.mid') + '.txt' if ifile
|
24
|
+
ofile = nil if ofile == '-'
|
25
|
+
|
26
|
+
sq = unless ifile then Sequence.read($stdin) else Sequence.load(ifile) end
|
27
|
+
unless ofile then sq.write_text($stdout) else sq.save_text(ofile) end
|
data/sample/smf2wav.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# smf2wav.rb: Written by Tadayoshi Funaba 1999-2006
|
4
|
+
# $Id: smf2wav.rb,v 1.20 2006-11-10 21:57:06+09 tadf Exp $
|
5
|
+
|
6
|
+
require 'smf'
|
7
|
+
require 'smf/toy/tempomap'
|
8
|
+
require 'gopt'
|
9
|
+
include SMF
|
10
|
+
|
11
|
+
module SMF
|
12
|
+
|
13
|
+
class Wave
|
14
|
+
|
15
|
+
def initialize(rate=8000)
|
16
|
+
@rate = rate
|
17
|
+
@wave = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def sin(start, stop, freq, amp)
|
21
|
+
(start..stop).each do |i|
|
22
|
+
r = amp * Math.sin(2 * Math::PI * freq * (i.to_f / @rate))
|
23
|
+
@wave[i] ||= 0.0
|
24
|
+
@wave[i] += r
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def dump
|
29
|
+
out = [ 'RIFF', @wave.length + 36, 'WAVE', 'fmt', 16, 1, 1, @rate, @rate,
|
30
|
+
1, 8, 'data', @wave.length].pack('A4 V A4 A4 V v v V V v v A4 V')
|
31
|
+
@wave.collect!{|x| x || 0.0}
|
32
|
+
peak = @wave.max
|
33
|
+
@wave.each_with_index do |x, i|
|
34
|
+
@wave[i] = 128 + (127 * x / peak).round
|
35
|
+
end
|
36
|
+
out << @wave.pack('C*')
|
37
|
+
out
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class Sequence
|
43
|
+
|
44
|
+
class EncodeWav < XSCallback
|
45
|
+
|
46
|
+
FREQ = []
|
47
|
+
(0..127).each do |n|
|
48
|
+
FREQ << 440 * 2**((n-69.0)/12)
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(tm)
|
52
|
+
@tm = tm
|
53
|
+
@rate = 8000
|
54
|
+
@wave = Wave.new(@rate)
|
55
|
+
end
|
56
|
+
|
57
|
+
def track_start
|
58
|
+
@offset = 0
|
59
|
+
@noteon = []
|
60
|
+
end
|
61
|
+
|
62
|
+
def delta(delta) @offset += delta end
|
63
|
+
|
64
|
+
def noteoff(ch, note, vel)
|
65
|
+
return if ch == 9 # GM perc.
|
66
|
+
val = (@noteon[ch] || {}).delete(note)
|
67
|
+
if val
|
68
|
+
start, vel = val
|
69
|
+
@wave.sin(pos(start), pos(@offset), FREQ[note], vel)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def noteon(ch, note, vel)
|
74
|
+
return if ch == 9 # GM perc.
|
75
|
+
@noteon[ch] ||= {}
|
76
|
+
@noteon[ch][note] ||= [@offset, vel]
|
77
|
+
end
|
78
|
+
|
79
|
+
def pos(offset)
|
80
|
+
e = @tm.offset2elapse(offset)
|
81
|
+
(e * @rate).round
|
82
|
+
end
|
83
|
+
|
84
|
+
def result() @wave.dump end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
def encode_wav
|
89
|
+
tm = TempoMap.new(self)
|
90
|
+
self.class::WS.new(self, self.class::EncodeWav.new(tm)).read
|
91
|
+
end
|
92
|
+
|
93
|
+
def write_wav(io)
|
94
|
+
io.binmode.write(encode_wav)
|
95
|
+
end
|
96
|
+
|
97
|
+
def save_wav(fn)
|
98
|
+
open(fn, 'w') do |io|
|
99
|
+
write_wav(io)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
def usage
|
108
|
+
warn 'usage: smf2wav [-o output] [input]'
|
109
|
+
exit 1
|
110
|
+
end
|
111
|
+
|
112
|
+
usage unless opt = Gopt.gopt('o:')
|
113
|
+
usage unless $*.size >= 0 && $*.size <= 1
|
114
|
+
|
115
|
+
ifile = $*.shift
|
116
|
+
ifile = nil if ifile == '-'
|
117
|
+
|
118
|
+
ofile = opt[:o] if opt[:o]
|
119
|
+
ofile ||= File.basename(ifile, '.mid') + '.wav' if ifile
|
120
|
+
ofile = nil if ofile == '-'
|
121
|
+
|
122
|
+
sq = unless ifile then Sequence.read($stdin) else Sequence.load(ifile) end
|
123
|
+
unless ofile then sq.write_wav($stdout) else sq.save_wav(ofile) end
|