smf 0.15.12
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/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
|