RubyでWavファイルを扱う
すっかり「Ruby猿」になっている私です(笑)が、今度はデジタルオーディオを扱ってみることにします。とりあえず、WAVファイルを扱うところを書いてみました。WAVファイル(というかRIFFフォーマット)は、ここhttp://www.kk.iij4u.or.jp/~kondo/wave/の記述が参考になります。思ったより、時間がかかりましたが、WAVファイルの読み書きはできました。pack(),unpack()の使い方のいい勉強になりました。 書き込みはとりあえず44.1k、16ビットで決めうちです。 早速動かしてみます。WAVファイルをコピーしているだけですが、遅いっすね~。 orzちゃんとThread化すれば少しはましになるかしら?RubyのTheradって、Native Threadではないからダメかな・・・----require "thread"module DigitalAudio def bytes2value( ar ) bytePerSample = ar.length bitPerSample = bytePerSample * 8 sum = 0 (bytePerSample -1).to_i.downto( 0 ) { |i| sum = (sum << 8 ) + ar[i] } if bytePerSample == 1 return sum - 128 end if sum > ( 0x1 << ( bitPerSample -1 )) sum -= ( 0x1 << bitPerSample ) end sum end #def bytes2value( ar )end #module DigitalAudioclass DigitalAudioElement include Math include DigitalAudio @@S48k = 48000end #class DigitalAudioElementclass ToFile < DigitalAudioElement def initialize( fspec , inQArray , pHash=nil ) f = open(fspec , "wb" ) totalDataChunkByte = 0 totalByte = totalDataChunkByte +36 fomatId = 1 # must be 1 (=LPCM) noOfChannel = inQArray.length samplingRate = 44100 # tentative bitPerSample = 16 # tentative blockSize = noOfChannel *( bitPerSample /8 ) bytePerSec = samplingRate * blockSize x = ["RIFF", totalByte, "WAVE" ] f.write( x.pack( "Z4VZ4")) x = [ "fmt ", 16, fomatId, noOfChannel, samplingRate, bytePerSec, blockSize, bitPerSample ] p x f.write( x.pack( "Z4VvvVVvv")) x = [ "data", totalDataChunkByte ] f.write( x.pack( "Z4V")) d = true while d for i in 0..(noOfChannel-1) lsbArray = Array.new d = inQArray[i].pop #p __FILE__, __LINE__, d break if !d #p __FILE__, __LINE__, d d = d.to_i for j in 1..( bitPerSample /8 ) lsbArray << (d & 0xff) d >>= 8 end f.write( lsbArray.pack( "C*" )) #f.write( "XXXX" ) end break if !d totalDataChunkByte += blockSize end f.seek( 0) # rewind to begin totalByte = totalDataChunkByte +36 x = ["RIFF", totalByte, "WAVE" ] f.write( x.pack( "Z4VZ4")) x = [ "fmt ", 16, fomatId, noOfChannel, samplingRate, bytePerSec, blockSize, bitPerSample ] p x f.write( x.pack( "Z4VvvVVvv")) x = [ "data", totalDataChunkByte ] f.write( x.pack( "Z4V")) f.close end #def initialize( fspec , outQ, pHash=nil )end #class ToFile < DigitalAudioElementclass FromFile < DigitalAudioElement include DigitalAudio def initialize( fspec , outQ, pHash=nil ) File::open( fspec ) { |f| f.binmode p "-----------------" if( !(buf = f.read(12 ))) raise "File read error : #{fspec}" end riffHeader = buf.unpack("Z4VZ4") p riffHeader if !((riffHeader[0] == "RIFF") && (riffHeader[2] == "WAVE")) raise "File format error error : #{fspec}" end # parse 'chunk' while( buf = f.read(8) ) chunkHeader = buf.unpack("Z4V") if chunkHeader[0] == "fmt " buf = f.read( chunkHeader[1]) fmtChunk = buf.unpack("vvVVvvvc*") elsif chunkHeader[0] == "fact" buf = f.read( chunkHeader[1]) factChunk = buf.unpack("c*") elsif chunkHeader[0] == "data" break else raise "unknown chunk type : #{chunkHeader[0]}" end end p fmtChunk, factChunk fomatId, noOfChannel, samplingRate, bytePerSec, blockSize, bitPerSample= fmtChunk[0..6] p fomatId, noOfChannel, samplingRate, bytePerSec, blockSize, bitPerSample if fomatId != 1 raise "unknown data format. Only LPCM is supported" end bytePerSample = bitPerSample / 8 count = chunkHeader[1] # total bytes in data chunk p "--datachunk--" ,count while( buf = f.read( blockSize ) ) d = buf.unpack( "C*" ) for ch in 0..(noOfChannel -1) val = bytes2value( d[0..(bytePerSample-1)]) outQ[ch].push( val ) d = d[bytePerSample..-1] end for ch in noOfChannel..( outQ.length() -1) outQ[ch].push( 0 ) end count -= blockSize break if count <= 0 end for ch in 0..( outQ.length() -1) outQ[ch].push( nil ) end } p "----Done" end #def initialize( fspec , outQ, pHash=nil )end # class FromFile < DigitalAudioElement#-------- test w1 = Queue.new w2 = Queue.new w3 = Queue.new w4 = Queue.new fm = FromFile.new( "WAVE.WAV", [w1, w2 ] ) fm1 = ToFile.new( "aaa.WAV", [w1, w2] )# fm2 = FromFile.new( "aaa.WAV", [w3, w4] ) exit