SVX日記
2006-05-30(Tue) プログラムでサウンドをジェネレートする
昨日、キーボードをキーボード化するためのキー入力部を作ったが、今日は実際の音をサンプリングしてみる……つもりだったのだが、昨日の晩はギターを弾いているうちに眠くなってしまったので、サンプリングしそこねてしまった。サンプル元としては、例のキッズキーボードか手持ちのギターが考えられるが、厳密に言うとキッズキーボードの音色には権利が存在するであろうし……かといって、ギターの音は音長が長く、キーボード用には向かないコトは想像に難くない。
じゃ、どうすりゃいいか? そこでPSGするのである。プログラムで理論的にWAVを生成してしまうのだ。昨日のネタと被るが、オイラは以前にcccdctというWAVカッターを作った。その時に作ったWAVデータのハンドリングを行うクラスを流用して、理論的に矩形波を生成してしまうのだッ!!
1 *** ../cccdct/cccdct Sat Mar 4 20:39:53 2006
2 --- maketone Tue May 30 20:22:30 2006
3 ***************
4 *** 32,38 ****
5 attr_reader :freq
6
7 def initialize(name)
8 ! @file = open(@name = name)
9 @riff = @file.read(4)
10 @tsize = @file.read(4).unpack('V')[0]
11 @wave = @file.read(4)
12 --- 32,38 ----
13 attr_reader :freq
14
15 def initialize(name)
16 ! @file = open(@name = name, 'r+')
17 @riff = @file.read(4)
18 @tsize = @file.read(4).unpack('V')[0]
19 @wave = @file.read(4)
20 ***************
21 *** 344,349 ****
22 --- 344,389 ----
23
24 #-------------------------------------------------------------------------------
25 #
26 + # MakeTone
27 + #
28 + wav = WavFile.new(ARGV[0])
29 + x = wav.get_info
30 + p x
31 +
32 + def wav.set_gain(b, l, r) # 特異メソッドを付加
33 + l -= 0x10000 if l > 0x7fff
34 + r -= 0x10000 if r > 0x7fff
35 + ll = [l].pack('v')
36 + rr = [r].pack('v')
37 + if -1 < b and b < @maxBlk
38 + @file.pos = @top + b * @block
39 + @file.write(ll)
40 + @file.write(rr)
41 + end
42 + c = [l, r].absmax # mixing
43 + [c, l, r]
44 + end
45 +
46 + freq = ARGV[1].to_i
47 + span = 0.8
48 + maxs = wav.freq * span - 1
49 + step = wav.freq / freq
50 + gain = 0x2000
51 +
52 + (0..maxs).each {|i|
53 + gg = 0xffff - (g = gain * (i - maxs) / maxs)
54 + if(i % step > (step / 2))
55 + wav.set_gain(i, g, g)
56 + else
57 + wav.set_gain(i, gg, gg)
58 + end
59 + }
60 + wav.save_phrase(0, maxs, "wav/#{ARGV[2]}.wav") {|dmy| }
61 +
62 + exit
63 +
64 + #-------------------------------------------------------------------------------
65 + #
66 # Main
67 #
68 Curses.init_screen
ちゃんとクラスとして作っておくとホントにラクできるね。cccdctのWavFileクラスに、ほんのちょっと手を加え、get_gainメソッドの逆にあたる、set_gainメソッドを特異メソッドとして実装する。あとは、周波数を考慮して矩形波を刻んでやったら、既存のsave_phraseメソッドを呼び出せばWAVファイルが書き出される……たったの40行程度の追加で目的が達成できてしまった。試しに再生してみたところ、単なる矩形波だと耳に痛いので、減衰するように修正して……おぉ、正味1時間で任意の周波数のWAVデータを作成するツールができてしまったッ!!
1 #!/usr/bin/ruby
2
3 key = Array['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#']
4 fb = 55.0
5 (0..60).each {|k|
6 f = fb * 2 ** (k * 1.0 / 12)
7 us = 1000000 / f
8 printf "./maketone cccdct_temp.wav %5d %3s\n",
9 f + 0.5, (k / 12).to_s + key[(k + 3) % 12].tr('A-Z', 'a-z').gsub(/#/, 's')
10 }
system("play wav/#{table[key][1]}.wav &")