gemにしました → 橋本商会 wavファイルをRubyで編集する

*****

Rubyでwavファイルを操作するためにWavFile.rbを作った。スピーカから音を鳴らすのではなくて、wavファイルそのものをいじって合成したりつなげたり、逆再生や左右反転させたりした後ファイルに書き出す為に作った。

packやunpackを使ってRubyでバイナリを読み書きする部分でかなり苦戦したけど、WAVファイル – MoonRock@MoonRock/A mere diary (2002-2)(7年も前に同じような事やってる!)がすごく参考になった。attr_accessorとかも知らなかったから勉強になった。


http://shokai.org/projects/ruby-wavfile/にサンプルを色々置いておく。


例えば、逆再生のwavファイルを作るコードはこう書ける
reverseWav.rb

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
# wavファイルを逆再生にして保存する
# ステレオの場合、左右チャンネルが入れ替わってしまうがまあいい
require File.dirname(__FILE__) + '/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の配列として取り出して処理する。ファイルに戻す部分はWavFile.rbがやってくれるようにした。

実行
ruby reverseWav.rb test.wav reverse.wav
結果
フォーマットID: 1
チャンネル数: 1
サンプリングレート: 8000 (Hz)
byte per sec: 16000
bit per sample: 16
ブロックサイズ: 2
data 4077172



主にできる事と、処理の手順はこんな感じ
  1. wavファイルのファイルヘッダ、フォーマットチャンク、データチャンクをメモリに読み込む。WavFile.rbではファイルヘッダとフォーマットチャンクをまとめて管理するためにWavFile::Formatクラスを作ってある。
  2. データチャンクを波形として扱う処理は時前でやってください。ステレオ・モノラルやbpsなどのフォーマットが全て読み込まれているのでそんなに大変ではないはず。format.bitPerSampleやunpackを使う。ただしCPUのエンディアンが違うとおかしくなるかも。
  3. 最後にいじったデータチャンクをpackでバイト列に戻して、ファイルヘッダと合わせてwavファイルに戻す。


上の例ではWavFile::readAllを使ってフォーマットと全チャンクを読み込んでいるけど、wavファイルにはデータチャンク(波形)以外のチャンクもある。でもデータチャンクとフォーマットさえあれば後は必要ない場合が多いので、フォーマットとデータチャンクのみを取り出す関数も用意してある。
require 'WavFile'
File.open("test.wav"){|file|
  format = WavFile::readFormat(file)
  dataChunk = WavFile::readDataChunk(file)
}

タプルを使ってこう受け取る事もできる
format, dataChunk = WavFile::read(file)

wavファイルへ保存
open("out.wav"){|out|
  WavFile.write(out, format, [dataChunk])
}


他にも色々やった。下にいくほど新しい。
上の方はWavFile.rbのバージョンが古い頃の物なので読み書きまわりが少し違うかもしれないが、解説が書いてあるので列挙しておく。最新のWavFile.rbで動くコードはリポジトリに置いておく


wavファイルの扱いについては、C言語で書かれたこの本を参考にした。この本ではファイルを先頭からseekして逐次処理して結果をwavファイルとして書き込んでいる。
でもwavファイルなんて数百MB程度だから、WavFile.rbではメモリ上で処理した方が後々便利そうだから今回は富豪的に全部メモリに読み込むようにした。
WAVプログラミング—C言語で学ぶ音響処理
北山 洋幸
カットシステム
売り上げランキング: 160207
おすすめ度の平均: 3.0
2 C言語初心者向けでわかりやすいが本格的な音響処理はいまいち
3 分かりやすいが…
4 あまり見かけないジャンルの本ですね



作ってる時に大変だったのは音を出さないとデバッグできないので電車の中で作業するためにイヤホンは必須。
バイナリとRubyのオブジェクトとのやりとりの部分は音で聞いてもなんだかわからない時もあるので、putsで波形を数字としてdumpしてエクセルで描画すると原因がすぐわかる。