wavread

Rで、Matlab互換のwavreadが欲しかったのだけど、いろいろ探しても3ch以上のファイルに対応したものがなかったので、しょうがなく自分で作ってみた。これで5.1chのファイルもいじれる。

source("wavread.R")
x <- wavread("filename.wav")

とするとfilename.wavという名前のWAVファイルが読み込まれる。source()はwavread関数を使えるようにするための文なので、一回だけやっておけばOK。

その後、画面に波形を表示したければ

plot(x$time, x$data[,1], type="l")

などとすればよい。1チャンネル目だけを描画している。

参考にしたのは『WAVプログラミング―C言語で学ぶ音響処理』と、以前、本家Microsoftから入手したフォーマット規格書。規格書はWotsitからリンクがあったはず。

コードは以下。バグ等あるかもしれないけど責任持たないのでよろしくー(24bitのWAVが読み込めないかもしれない)。いつかwavwriteも作りますが、writeの方がほんのちょっとだけ面倒くさいんだよね。

wavread <- function(filename) {
  ## open connection to the file
  con <- file(filename, open="rb")
  
  ## check if the file is canonical WAV file
  if (readChar(con, nchars=4, useBytes=TRUE) != "RIFF") {
    close(con)
    stop(paste(filename, "is not a RIFF file."))
  }
  size.riff <- readBin(con, integer(), 1)
  if (readChar(con, nchars=4, useBytes=TRUE) != "WAVE") {
    close(con)
    stop(paste(filename, "is not a WAVE file."))
  }
  if (readChar(con, nchars=4, useBytes=TRUE) != "fmt ") {
    close(con)
    stop(paste(filename, "does not have a 'fmt ' chunk."))
  }
  size.fmt <- readBin(con, integer(), 1, endian="little")
  
  ## read fmt chunk
  format.id <- readBin(con, what="raw", n=2, endian="little")
  if (prod(format.id != c(1, 0))) {
    close(con)
    stop(paste(filename, "has to be in Linear PCM format."))
  }  
  num.channels <- readBin(con, what="int", size=2, n=1, endian="little")
  fs <- readBin(con, integer(), n=1, endian="little")
  bps <- readBin(con, integer(), n=1, endian="little")
  block.size <- readBin(con, what="int", size=2, n=1, endian="little")
  nbits <- readBin(con, what="int", size=2, n=1, endian="little")
  if (bps != fs * nbits/8 * num.channels) {
    close(con)
    stop(paste(filename, "has something wrong in the 'fmt ' chunk."))
  }
  
  ## skip ext chunk (if exists)
  chunk.name <- readChar(con, nchars=4, useBytes=TRUE)
  if (chunk.name == "ext ") {
    size.ext <- readBin(con, integer(), 1, endian="little")
    skip.ext <- readBin(con, what="raw", size.ext)
  }

  ## read data chunk
  if (chunk.name != "data") {
    close(con)
    stop(paste(filename, "does not have a 'data' chunk."))
  }
  size.data <- readBin(con, integer(), 1, endian="little")
  data <- readBin(con, what="int", size=nbits/8, size.data) / (2^(nbits-1))
  data <- matrix(data, ncol=num.channels, byrow=TRUE)

  ## close connection to the file
  close(con)

  ## time in seconds
  t <- (seq(max(dim(data)))-1) / fs

  return(list(data=data, fs=fs, nbit=nbits, nch=num.channels, time=t))
}