逆再生するwavを作る
dataチャンクから配列として波形を取り出せるか試してみた。Rubyはunpackで取り出してpackでバイナリに戻せる(しかもけっこう高速に)のでこれを使っていこうかな。
nameがdataのchunkのデータ部分が波形データなので、wav formatを見て16bitか8bitか確かめて、それぞれunpackすると1hz分ごとの数値の入った配列が得られる。ステレオだとLRLRLRLRと入っているので左右チャンネルが入れ替わってしまうがまあ、とりあえず気にしなくていいか。
reverseWav.rb
#!/usr/bin/env ruby # -*- coding: utf-8 -*- # wavファイルを逆再生にして保存する # ステレオの場合、左右チャンネルが入れ替わってしまう require 'WavFile' if ARGV.size < 2 puts 'ruby reverseWav.rb input.rb output.wav' exit 1 end f = open(ARGV.shift) format, chunks = WavFile::readAll(f) f.close puts format.to_s dataChunk = nil chunks.each{|c| puts "#{c.name} #{c.size}" dataChunk = c if c.name == 'data' # 波形の入っているchunkを探す } if dataChunk == nil puts 'no data chunk' exit 1 end bit = 's*' if format.bitPerSample == 16 # int16_t bit = 'c*' if format.bitPerSample == 8 # signed char wavs = dataChunk.data.unpack(bit) # 16bit or 8bitずつbinaryから読み出し dataChunk.data = wavs.reverse.pack(bit) # 逆再生、binaryに戻す open(ARGV.shift, "w"){|out| WavFile::write(out, format, [dataChunk]) }
実行
ruby reverseWav.rb ~/test.wav reverse.wav
結果
フォーマットID: 1 チャンネル数: 2 サンプリングレート: 44100 (Hz) byte per sec: 176400 bit per sample: 16 ブロックサイズ: 4 data 44951040
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('v')+ [@channel].pack('v') + [@hz].pack('V') + [@bytePerSec].pack('V') + [@blockSize].pack('v') + [@bitPerSample].pack('v') 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