5.5 拙作 ReadWave.java

(これは古くてバグ入りだが、卒研の記録として残しておく。 既に紹介した『JavaでHelloWorldサウンド編』を参考にしたのだが、 卒研が終ってから、赤間 [7] という本を見つけて、 それを見れば楽だったはず。)

http://nalab.mind.meiji.ac.jp/~mk/program/sound/ReadWave.java

ReadWave.java は、 Waveファイルを読み込んで、音を鳴らしながら、 音声データを数値表示するプログラムである。 オーディオ・データの形式は、 Java のクラス・ライブラリィが調べてくれるので、 自前で解析する手間は省けている (Cf: readwave.c)。

コンパイル&実行
knoppix$ javac ReadWave.java
knoppix$ java ReadWave piano.wav > piano.txt

チェック用に http://nalab.mind.meiji.ac.jp/~mk/labo/2007/piano.wavhttp://nalab.mind.meiji.ac.jp/~mk/labo/2007/piano.txt を公開しておく。

ソース・プログラムは案外長いが、 (i) 16ビット・ステレオ、(ii) 8ビット・ ステレオ、 (iii) 16ビット・モノラル、(iv) 8ビット・モノラル、と4つに場合 分けしてあるからである (本当はこれにエンディアンの違いと、 符号の有無の違いで、さらに場合分けする必要があるが、それは無視した -- と書いたのだけど、これは誤解かも知れない (2013/10)。)。


   1 //
   2 // ReadWave.java
   3 //   2008/1/30 初めて作成
   4 //   2008/2/13 一木君にバグを指摘される。Byte は符号付きだった。
   5 //   2008/2/15 やはりビット演算だけで記述することにした。
   6 //
   7 
   8 import java.io.File;
   9 import javax.sound.sampled.*;
  10 
  11 public class ReadWave {
  12   private static final int EXTERNAL_BUFFER_SIZE = 128000;
  13   public static void main(String[] args) {
  14     int frameSize;       //
  15     int sampleSizeInBits; // 音声データの数値のビット数 (16または8)
  16     int channels;        // チャンネルの数 (2がステレオ, 1がモノラル)
  17     float sampleRate;    // サンプリングレート (1秒間の...)
  18     boolean isStereo;    // ステレオか
  19     boolean isBigEndian; // ビッグエンディアンか (上位バイトが先か)
  20     if (args.length == 0) System.exit(0);
  21     try {
  22       // Fileクラスのインスタンスを生成する
  23       File soundFile = new File(args[0]);
  24       // オーディオ入力ストリームを取得する
  25       AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(soundFile);
  26       // オーディオフォーマットを取得する
  27       AudioFormat audioFormat = audioInputStream.getFormat();
  28 
  29       // データラインの情報オブジェクトを生成する
  30       DataLine.Info info = new DataLine.Info(SourceDataLine.class,audioFormat);
  31       // 指定されたデータライン情報に一致するラインを取得する
  32       SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
  33       // 指定されたオーディオ形式でラインを開きます
  34       line.open(audioFormat);
  35       // ラインでのデータ入出力を可能にします
  36       line.start();
  37 
  38       // データの形式を読み取る
  39       channels = audioFormat.getChannels();
  40       isStereo = (channels == 2);
  41       isBigEndian = audioFormat.isBigEndian();
  42       frameSize = audioFormat.getFrameSize();
  43       sampleSizeInBits = audioFormat.getSampleSizeInBits();
  44       sampleRate = audioFormat.getSampleRate();
  45       System.out.println("#original file: " + args[0]);
  46       System.out.println("#number of channels: " + channels);
  47       System.out.println("#sampling rate: " + sampleRate);
  48       System.out.println("#number of bits per sample: " + sampleSizeInBits);
  49       System.out.println("#FrameSize: " + frameSize);
  50       System.out.println("#isBigEndian: " + isBigEndian);
  51       if (audioFormat.getEncoding() == AudioFormat.Encoding.PCM_SIGNED) {
  52           System.out.println("#PCM Signed");
  53       }
  54       else if (audioFormat.getEncoding() == AudioFormat.Encoding.PCM_UNSIGNED) {
  55           System.out.println("#PCM Unsigned!!!");
  56           System.exit(0);
  57       }
  58       else {
  59           System.out.println("#NO PCM!!");
  60           System.exit(0);
  61       }
  62 
  63       // 音声データを読み取り、鳴らして、数値を表示
  64       int nBytesRead = 0;
  65       byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
  66       if (isStereo) {
  67           if (sampleSizeInBits == 16) {
  68               // 16ビットステレオ (フツー)
  69               while (nBytesRead != -1) {
  70                   // オーディオストリームからデータを読み込みます
  71                   nBytesRead = audioInputStream.read(abData, 0, abData.length);
  72                   if (nBytesRead >= 0) {
  73                       // オーディオデータをミキサーに書き込みます
  74                       int nBytesWritten = line.write(abData, 0, nBytesRead);
  75                       for (int i = 0; i < nBytesRead; i += 4) {
  76                           short left, right;
  77                           left  = (short)(abData[i]   & 0xff | (abData[i+1] << 8));
  78                           right = (short)(abData[i+2] & 0xff | (abData[i+3] << 8));
  79                           System.out.println("" + left + " " + right);
  80                       }
  81                   }
  82               }
  83           }
  84           else { // sampleSizeInBits == 8
  85               // 8ビットステレオ (フツー)
  86               while (nBytesRead != -1) {
  87                   // オーディオストリームからデータを読み込みます
  88                   nBytesRead = audioInputStream.read(abData, 0, abData.length);
  89                   if (nBytesRead >= 0) {
  90                       // オーディオデータをミキサーに書き込みます
  91                       int nBytesWritten = line.write(abData, 0, nBytesRead);
  92                       for (int i = 0; i < nBytesRead; i += 2) {
  93                           short left, right;
  94                           left =  (short)(abData[i]   & 0xff);
  95                           right = (short)(abData[i+1] & 0xff);
  96                           System.out.println("" + left + " " + right);
  97                       }
  98                   }
  99               }
 100           }
 101       }
 102       else {
 103           // モノラル
 104           if (sampleSizeInBits == 16) {
 105               // 16ビットモノラル
 106               while (nBytesRead != -1) {
 107                   // オーディオストリームからデータを読み込みます
 108                   nBytesRead = audioInputStream.read(abData, 0, abData.length);
 109                   if (nBytesRead >= 0) {
 110                       // オーディオデータをミキサーに書き込みます
 111                       int nBytesWritten = line.write(abData, 0, nBytesRead);
 112                       for (int i = 0; i < nBytesRead; i += 2) {
 113                           short c;
 114                           c = (short)(abData[i] & 0xff | (abData[i+1] << 8));
 115                           System.out.println("" + c);
 116                       }
 117                   }
 118               }
 119           }
 120           else { // sampleSizeInBits == 8
 121               // 8ビットモノラル
 122               while (nBytesRead != -1) {
 123                   // オーディオストリームからデータを読み込みます
 124                   nBytesRead = audioInputStream.read(abData, 0, abData.length);
 125                   if (nBytesRead >= 0) {
 126                       // オーディオデータをミキサーに書き込みます
 127                       int nBytesWritten = line.write(abData, 0, nBytesRead);
 128                       for (int i = 0; i < nBytesRead; i++) {
 129                           short c;
 130                           c = (short)(abData[i] & 0xff);
 131                           System.out.println("" + c);
 132                       }
 133                   }
 134               }
 135           }
 136       }
 137       // ラインからキューに入っているデータを排出します
 138       line.drain();
 139       // ラインを閉じます
 140       line.close();
 141 
 142       System.exit(0);
 143     } catch (Exception e) {
 144       e.printStackTrace();
 145       System.exit(1);
 146     }
 147   }
 148 }

桂田 祐史
2017-10-09