SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|

2006-05-30(Tue) プログラムでサウンドをジェネレートする

  今日は「プログラムでサウンドをジェネレートする」つまり「PでSをGする」わけなので、これは「PSG」なのであった。

  昨日、キーボードをキーボード化するためのキー入力部を作ったが、今日は実際の音をサンプリングしてみる……つもりだったのだが、昨日の晩はギターを弾いているうちに眠くなってしまったので、サンプリングしそこねてしまった。サンプル元としては、例のキッズキーボードか手持ちのギターが考えられるが、厳密に言うとキッズキーボードの音色には権利が存在するであろうし……かといって、ギターの音は音長が長く、キーボード用には向かないコトは想像に難くない。

  じゃ、どうすりゃいいか? そこで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データを作成するツールができてしまったッ!!

  さらに職場の昼休みには、以前に作ったtune.rbを改造して、各音階の周波数を生成するためのコマンドライン生成ツールを作成して……

      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 }

  ……実行結果を、ベロッとやって、プピッとやると、一瞬で各音階のWAVファイルが揃った!! あとは、昨日のキー入力部に……

system("play wav/#{table[key][1]}.wav &")

  ……の行を付加するだけで、キーボードのキーボード化アプリケーション「mukey」の完成であるッ!!

  「ところがギッチョンチョン!!」

  systemで外部コマンドを呼び出すと、プロセス生成(fork?)が重いらしくて、キーを叩いてから発音までのタイムラグが大きすぎてダメダメ……せめて一定ならまだしも、バラつきまであるし……さすがに音楽関係はタイミングにシビアだから、こんな作りじゃ無理があったか……Cでデーモンでも作らないとダメかな……トホホ。

  あ、遅ればせながら、SVX2周年。もう、2年も経ったのか……。