wavファイルのフォーマットを書き換えて倍速再生にする
wavのDSPをやる準備が整った。
wavヘッダをメモリに読み込んでrubyの変数として扱って、バイナリに書き戻せるようになった。dataチャンクの中の波形をいじれば音を変えれる。
まずは波形ではなくwavヘッダの周波数とbpsの値をいじって、倍速再生のwavファイルを作る例。
かなりバイナリをすんなり扱えるラッパーができた。
(後述のWavFile.rbを使う)
test.rb
#!/usr/bin/env ruby require 'WavFile' if ARGV.size < 2 puts 'ruby test.rb input.wav output.wav' exit 1 end f = open(ARGV.shift) format, chunks = WavFile::readAll(f) # Format, Array f.close puts format.to_s # フォーマット表示 chunks.each{|c| puts "#{c.name} #{c.size}" # 各チャンクの中身を見てみる } puts '=====ここまで元ファイルの情報=====' format.hz *= 2 # 倍速 format.bytePerSec *= 2 # 倍速設定 puts format.to_s # 倍速になってるか確かめる out = open(ARGV.shift, "w") WavFile::write(out, format, chunks) # 倍速フォーマットで保存 out.close
実行してみる
ruby test.rb input.wav out.wav
フォーマットID: 1 チャンネル数: 2 サンプリングレート: 44100 (Hz) byte per sec: 176400 bit per sample: 16 ブロックサイズ: 4 data 43720704 =====ここまで元ファイルの情報===== フォーマットID: 1 チャンネル数: 2 サンプリングレート: 88200 (Hz) byte per sec: 352800 bit per sample: 16 ブロックサイズ: 4
サンプリングレートとbyte per secが倍になっている。再生すると倍速になってる。
bpsがbytes per secondsじゃなくてbits per secondsなのに注意。
WavFile.rb
module WavFile class WavFormatError < StandardError end class Chunk attr_accessor(:name, :size, :data) def initialize(file) @name = file.read(4) @size = file.read(4).unpack("V")[0].to_i @data = file.read(@size) end def to_bin @name + [@data.size].pack('V') + @data end end class Format attr_accessor(:id, :channel, :hz, :bytePerSec, :blockSize, :bitPerSample) def initialize(chunk) return if chunk.class != Chunk return if chunk.name != 'fmt ' @id = chunk.data.slice(0,2)[0].to_i @channel = chunk.data.slice(2,2)[0].to_i @hz = chunk.data.slice(4,4).unpack('V').join.to_i @bytePerSec = chunk.data.slice(8,4).unpack('V').join.to_i @blockSize = chunk.data.slice(12,2)[0].to_i @bitPerSample = chunk.data.slice(14,2)[0].to_i end def to_s <<EOS フォーマットID: #{@id} チャンネル数: #{@channel} サンプリングレート: #{@hz} (Hz) byte per sec: #{@bytePerSec} bit per sample: #{@bitPerSample} ブロックサイズ: #{blockSize} EOS end def to_bin [@id].pack('S')+ [@channel].pack('S') + [@hz].pack('V') + [@bytePerSec].pack('V') + [@blockSize].pack('S') + [@bitPerSample].pack('S') end end def WavFile.readFormat(f) f.binmode f.seek(0) header = f.read(12) riff = header.slice(0,4) data_size = header.slice(4,4).unpack('V')[0].to_i wave = header.slice(8,4) raise(WavFormatError) if riff != 'RIFF' or wave != 'WAVE' formatChunk = Chunk.new(f) Format.new(formatChunk) end def WavFile.readAll(f) format = readFormat(f) chunks = Array.new while !f.eof? chunk = Chunk.new(f) chunks << chunk end return format, chunks end def WavFile.write(f, format, dataChunks) header_file_size = 4 dataChunks.each{|c| header_file_size += c.data.size + 8 } f.write('RIFF' + [header_file_size].pack('V') + 'WAVE') f.write("fmt ") f.write([format.to_bin.size].pack('V')) f.write(format.to_bin) dataChunks.each{|c| f.write(c.to_bin) } end end