すっかり「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 DigitalAudio
class DigitalAudioElement
include Math
include DigitalAudio
@@S48k = 48000
end #class DigitalAudioElement
class 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 < DigitalAudioElement
class 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