4.2 拙作 readwave.c (WAVE→テキスト・ファイル)

http://nalab.mind.meiji.ac.jp/~mk/program/sound/readwave.c

やっつけの、リバースエンジニアリングの、あまり人に見せたくない…


ピアノの音を録音した piano.wav という WAVE ファイルの内容をテキスト・ファイルに変換してみる。

oyabun% gcc -o readwave readwave.c
oyabun% ./readwave piano.wav piano.txt
oyabun% ./readwave piano.wav > piano2.txt
oyabun% ls -l piano.wav piano.txt piano2.txt
-rw-r--r--   1 mk       lab00    2814838  3月  6日  13:44 piano.txt
-rw-r--r--   1 mk       lab00    2828151  3月  6日  13:49 piano2.txt
-rw-r--r--   1 mk       lab00    1234592  1月 23日  20:39 piano.wav
oyabun% head piano.txt
#original file: piano.wav
#number of channels: 2
#sampling rate: 44100
#number of bits (per sample): 16
#number of samples: 307017
64 64
62 62
65 62
65 63
65 60

piano2.txt (少し冗長)
# RIFF データのサイズ=1234584
# fmt データのサイズ=18
#  01 00 02 00 44 ac 00 00 10 b1 02 00 04 00 10 00
#  00 00
# 非圧縮PCMです。
# ステレオです。
# サンプリング・レート(標本化周波数)=44100
# 1秒当りのバイト数=176400
# ブロック境界=4
# ビット数/サンプル=16
# 拡張情報サイズ=0
# PAD チャンクがあります。# PAD チャンクのサイズ=4042
#  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 (中略)
#  00 00 00 00 00 00 00 00 00 00
# fact チャンクがあります。# fact データのサイズ=4
#  49 af 04 00
# data データのサイズ=1228068
#original file: piano.wav
#number of channels: 2
#sampling rate: 44100
#number of bits (per sample): 16
#number of samples: 307017
64 64
62 62
  (中略 --- 結構長い)
-114 -63
-111 -59
-112 -61
-109 -53
-108 -51


/*
 * readwave.c --- 無圧縮 WAVE ファイルを読んでテキスト・ファイルにする
 *                動作することはまったく保証しません
 *
 *  version 2 (2003/12/21)
 *  version 3 (2005/7/4) "PAD " チャンクの存在を知りそれに対応
 *  version 4 (2013/10/26) #include 増やした。文字コードをUTF8にした。
 *  version 5 (2014/8/12)  FLLR チャンクの存在を知りそれに対応
 *  version 5.1 (2017/10/9)  www.math.meiji.ac.jp を変える
 *  version 6 (2020/12/22) LIST チャンクの存在を知りそれに対応
 *  version 7 (2020/12/24) fread() を使うようにした
 *
 *  コンパイル: gcc -o readwave readwave.c
 *
 *  使い方:
 *   (1) ./readwave <WAVEファイル名> <テキスト・ファイル名>
 *   (2) ./readwave <WAVEファイル名>
 *          標準出力に出力
 *   (3) ./readwave
 *          標準入力から入力、標準出力に出力
 *
 *  入手:
 *    http://nalab.mind.meiji.ac.jp/~mk/program/
 *
 *  参考にした情報
 *   URL: http://member.nifty.ne.jp/Ryuz/programing/wavefmt.html
 *        -> http://homepage3.nifty.com/ryuz/programing/wavefmt.html
 *
 *  普段置いてある場所
 *    ~/Sotsuken/2007/sound/readwave/
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const int verbose = 1;
const int debug = 0;
FILE *out;

void usage(char *prog)
{
  fprintf(stderr, "usage (1): %s\n", prog);
  fprintf(stderr, "usage (2): %s <wave-file-name>\n", prog);
  fprintf(stderr, "usage (3): %s <wave-file-name> <text-file-name>\n", prog);
  exit(0);
}

int readint(FILE *in)
{
  int c1, c2, c3, c4;
  c1 = getc(in); c2 = getc(in); c3 = getc(in); c4 = getc(in);
  return c1 + 256 * (c2 + 256 * (c3 + 256 * c4));
}

int checkstring(unsigned char *buf, char *key)
{
  int i, n = strlen(key);
  for (i = 0; i < n; i++)
    if (buf[i] != key[i]) {
      fprintf(stderr, "%s は \"%s\" と一致しない\n", buf, key);
      exit(1);
    }
  return 0;
}

void dump(unsigned char *buf, int n)
{
  int i;
  printf("# ");
  for (i = 0; i < n; i++) {
    printf(" %02x", buf[i]);
    if ((i + 1) % 16 == 0)
      printf("\n# ");
  }
  printf("\n");
}

int read2byte(unsigned char *buf)
{
  return buf[0] + 256 * buf[1];
}

int read1(FILE *in)
{
  int c = getc(in);
  if (debug) {
    printf("%02x ", c);
    fprintf(out, "%02x ", c);
  }
  return c;
}

int read2(FILE *in)
{
  int c1, c2, c;
  c1 = getc(in); c2 = getc(in);
  if (debug) {
    printf("%02x %02x ", c1, c2);
    fprintf(out, "%02x %02x ", c1, c2);
  }
  c = c1 + (c2 << 8);
  if (c >= 32768)
    c -= 65536;
  return c;
}

int read4byte(unsigned char *buf)
{
  return buf[0] + 256 * (buf[1] + 256 * (buf[2] + 256 * buf[3]));
}

void analyze(unsigned char *buf,
             int *fmt_tag, int *num_channel, int *sampling_rate,
             int *bytes_per_seconds, int *block, int *num_bits,
             int *extended_information_size)
{
  *fmt_tag = read2byte(buf);
  *num_channel = read2byte(buf + 2);
  *sampling_rate = read4byte(buf + 4);
  *bytes_per_seconds = read4byte(buf + 8);
  *block = read2byte(buf + 12);
  *num_bits = read2byte(buf + 14);
  *extended_information_size = read2byte(buf + 16);
}

void readpcm(FILE *in, int size, int bits, int channel)
{
  int i, c, right;
  if (bits == 16) {
    if (channel == 2) {
      for (i = 0; i < size / 4; i++) {
        c = read2(in); right = read2(in);
        if (verbose) printf("%d %d\n", c, right);
        fprintf(out, "%d %d\n", c, right);
      }
    }
    else {
      for (i = 0; i < size / 2; i++) {
        c = read2(in);
        if (verbose) printf("%d\n", c);
        fprintf(out, "%d\n", c);
      }
    }
  }
  else {
    if (channel == 2) {
      for (i = 0; i < size / 2; i++) {
        c = read1(in); right = read1(in);
        if (verbose) printf("%d %d\n", c, right);
        fprintf(out, "%d %d\n", c, right);
      }
    }
    else {
      for (i = 0; i < size; i++) {
        c = read1(in);
        if (verbose) printf("%d\n", c);
        fprintf(out, "%d\n", c);
      }
    }
  }
}

/* 取り扱うファイルの名前 */
char *input_fname, *output_fname, *prog_name;

int main(int argc, char **argv)
{
  int size;
  FILE *in;
  unsigned char buf[5120];
  unsigned char *buf2;
  int fmt_tag, num_channel, sampling_rate, bytes_per_seconds;
  int block, num_bits, extended_information_size;
  
  prog_name = argv[0];
  if (argc > 3)
    usage(prog_name);
    
  if (argc == 3) {
    output_fname = argv[2];
    if ((out = fopen(output_fname, "w")) == NULL) {
      fprintf(stderr, "%s がオープンできません。\n", output_fname);
      exit(1);
    }
  }
  else {
      output_fname = "stdout";
      out = stdout;
  }
  if (argc >= 2) {
    input_fname = argv[1];
    if ((in = fopen(input_fname, "r")) == NULL) {
      fprintf(stderr, "%s がオープンできません。\n", input_fname);
      exit(1);
    }
  }
  else {
    input_fname = "input"; in = stdin;
  }

  // setstring(buf, in, 4) は fread(buf, 1, 4, in) で良いかな。
  /* ヘッダーを読む */
  fread(buf, 1, 4, in); checkstring(buf, "RIFF");
  size = readint(in); printf("# RIFF データのサイズ=%d\n", size);
  fread(buf, 1, 4, in); checkstring(buf, "WAVE");
  fread(buf, 1, 4, in); checkstring(buf, "fmt ");
  size = readint(in); printf("# fmt データのサイズ=%d\n", size);
  fread(buf, 1, size, in); dump(buf, size);
  analyze(buf, &fmt_tag, &num_channel, &sampling_rate,
          &bytes_per_seconds, &block, &num_bits,
          &extended_information_size);
  printf("# 非圧縮PCM%s。\n", (fmt_tag == 1) ? "です" : "ではありません");
  printf("# %sです。\n", (num_channel == 1) ? "モノラル" : "ステレオ");
  printf("# サンプリング・レート(標本化周波数)=%d\n", sampling_rate);
  printf("# 1秒当りのバイト数=%d\n", bytes_per_seconds);
  printf("# ブロック境界=%d\n", block);
  printf("# ビット数/サンプル=%d\n", num_bits);
  printf("# 拡張情報サイズ=%d\n", extended_information_size);
  while (1) {
    fread(buf, 1, 4, in);
    if (strncmp("data", (char *)buf, 4) == 0) {
      if (verbose)
	printf("# dataチャンク見つかった\n");
      break;
    }
    else {
      // dataチャンク以外。"PAD "とか "FLLR" とか "fact" とか "LIST" とか
      if (verbose) {
	buf[4] = 0;
	printf("# %s チャンクがある。サイズは%d\n", buf, size = readint(in));
	
	fread(buf, 1, size, in);
	dump(buf, size);
      }
      else {
	size = readint(in);
	fseek(in, size, SEEK_CUR);
      }
    }
  }
  size = readint(in);
  if (verbose)
    printf("# data データのサイズ=%d\n", size);    

  /* 書き出す */
  fprintf(out, "#original file: %s\n", input_fname);
  fprintf(out, "#number of channels: %d\n", num_channel);
  fprintf(out, "#sampling rate: %d\n", sampling_rate);
  fprintf(out, "#number of bits (per sample): %d\n", num_bits);
  fprintf(out, "#number of samples: %d\n",
          size / num_channel / (num_bits / 8));
  readpcm(in, size, num_bits, num_channel);
  fclose(out);

  return 0;
}



Subsections

桂田 祐史