AudioInputStreamは長さFrameSize以下の配列だと読み込まない
2008/06/23
JavaでWaveファイルのデータを読み込む時は、AudioInputStreamのread(byte[] b)メソッドを使うのが普通みたいなのですが、変な所で引っかかってしまったので備忘メモ。
読み込むファイルは16bitステレオでPCMです。
とりあえず、下準備として
ところで、16bitステレオのWave(RIFF)ファイルは
というようにデータが並んでいます。
いかにも「2バイト単位で読み込んで左右のデータを振り分ければいいじゃないの」と言っているかのようです。
それじゃあ、ということで
読んでない!
それじゃあ、読んでもらうにはどうしたらいいか、というと
ちなみに、配列の長さを5にすると読み込むバイト数は4になります。
フレーム単位で読むんでしょうね。たぶん。
読み込むファイルは16bitステレオでPCMです。
とりあえず、下準備として
AudioInputStream ais = AudioSystem.getAudioInputStream(new File(filePath));
としておきます。ところで、16bitステレオのWave(RIFF)ファイルは
| 0 | 1 | 2 | 3 | ... | (n-1)*4 | (n-1)*4 +1 | (n-1)*4 +2 | (n-1)*4+3 |
| L11 | L12 | R11 | R12 | ... | Ln1 | Ln2 | Rn1 | Rn2 |
というようにデータが並んでいます。
いかにも「2バイト単位で読み込んで左右のデータを振り分ければいいじゃないの」と言っているかのようです。
それじゃあ、ということで
//長さ2のバイトの配列bを作る
byte[] b = new byte[2];
/*
* オーディオ入力ストリームから数バイトを読み込み、それをバッファ配列 b に格納します。 * 実際に読み込まれたバイト数は整数として返されます。
*/
System.out.println(ais.read(b));
とやってみるとbyte[] b = new byte[2];
/*
* オーディオ入力ストリームから数バイトを読み込み、それをバッファ配列 b に格納します。 * 実際に読み込まれたバイト数は整数として返されます。
*/
System.out.println(ais.read(b));
0
となります。読んでない!
それじゃあ、読んでもらうにはどうしたらいいか、というと
//長さ2ais.getFrameSizeのバイトの配列bを作る
byte[] b = new byte[2];
byte[] b = new byte[ais.getFormat.getFrameSize];
System.out.println(ais.read(b));
とするとbyte[] b = new byte[ais.getFormat.getFrameSize];
System.out.println(ais.read(b));
4
となって、ちゃんとデータを読み込んでくれた事がわかります。ちなみに、配列の長さを5にすると読み込むバイト数は4になります。
フレーム単位で読むんでしょうね。たぶん。
PureJavaなFFTライブラリ JTransforms
2008/06/22
JTransforms
Piotr Wendykirさんが公開してる、JavaによるJavaのためのFFTライブラリ。
Takuya Oouraさんの数値計算ライブラリにある汎用FFTパッケージをJavaに翻訳したものとかなんとか。
FFTしてIFFTするだけのお試しは成功しました。
他にも色々できるみたいなので、興味ある方は上のリンクとかJavaDocを読んでみてください。
Piotr Wendykirさんが公開してる、JavaによるJavaのためのFFTライブラリ。
Takuya Oouraさんの数値計算ライブラリにある汎用FFTパッケージをJavaに翻訳したものとかなんとか。
FFTしてIFFTするだけのお試しは成功しました。
他にも色々できるみたいなので、興味ある方は上のリンクとかJavaDocを読んでみてください。
メソッドから呼び出されるメソッドをオーバーライドするとどうなるか
2008/06/16
親クラス(orスーパークラスor基本クラス)があります。
親クラスを継承(or汎化)した子クラス(orサブクラスor派生クラス)があります。
親クラスには、protectedなサブメソッドを呼ぶメソッドがあります。
子クラスで、親クラスのサブメソッドをオーバーライドしました。
この時、子クラスのインスタンスからメソッドを呼ぶと、メソッドは
具体的に書くと
なんとなく騙くらかすポイントは
「あれ?これ親クラスのサブメソッドが実行されちゃうんじゃないの?」みたいな疑問が出てきませんか?
疑問をほっとくのは気持ち悪いので、とりあえず
ちゃんとオーバライドしたサブメソッドが使われてますね。
よかったよかった。
ちなみに、これはサブメソッドがprotectedでオーバーライドできるからこその現象で、たとえば
で、こちらはちなむ必要もないんですが、一応ちなんでおくと
protectedなsubMethodをオーバーライドされた状態でも
よかったよかった。
一回試せばわざわざ書くこともない類のもんですが、検索しても出てこない、というか、コレを検索する方法がよく分からないので、忘れた時用に書いておきます。
親クラスを継承(or汎化)した子クラス(orサブクラスor派生クラス)があります。
親クラスには、protectedなサブメソッドを呼ぶメソッドがあります。
子クラスで、親クラスのサブメソッドをオーバーライドしました。
この時、子クラスのインスタンスからメソッドを呼ぶと、メソッドは
- 親クラスで定義された元々のサブメソッドを使う
- 子クラスでオーバーライドしたサブメソッドを使う
具体的に書くと
public class BaseClass {
public class ExtendClass extends BaseClass {
となります。
public BaseClass(){
protected void subMethod(){
public void method(){
}
System.out.println("create BaseClass");
}protected void subMethod(){
System.out.println("this is BaseClass.subMethod. Woohoo!");
}public void method(){
System.out.println("this is BaseClass.method");
System.out.println("BaseClass.method calls subMethod");
subMethod();
}System.out.println("BaseClass.method calls subMethod");
subMethod();
public class ExtendClass extends BaseClass {
public ExtendClass(){
@Override
protected void subMethod(){
}
System.out.println("create ExtendClass");
super();
}@Override
protected void subMethod(){
System.out.println("this is ExtendClass.subMethod. D'OH!");
}なんとなく騙くらかすポイントは
- サブメソッドだけオーバーライドしてる
- メソッドは親クラスのをそのまま使ってる
「あれ?これ親クラスのサブメソッドが実行されちゃうんじゃないの?」みたいな疑問が出てきませんか?
疑問をほっとくのは気持ち悪いので、とりあえず
ExtendClass extendClass = new ExtendClass();
extendClass.method();
としてみるとextendClass.method();
create ExtendClass
create BaseClass
this is BaseClass.method
BaseClass.method calls subMethod
this is ExtendClass.subMethod. D'OH!
と表示されます。create BaseClass
this is BaseClass.method
BaseClass.method calls subMethod
this is ExtendClass.subMethod. D'OH!
ちゃんとオーバライドしたサブメソッドが使われてますね。
よかったよかった。
ちなみに、これはサブメソッドがprotectedでオーバーライドできるからこその現象で、たとえば
public class BaseClass {
public class ExtendClass extends BaseClass {
という風にBaseClassのsubMethodをprivateにすると、子クラスでsubMethodという同じ名前のメソッドを定義しても
public BaseClass(){
protected void subMethod(){
private void subMethod(){
public void method(){
}
System.out.println("create BaseClass");
}private void subMethod(){
System.out.println("this is BaseClass.subMethod. Woohoo!");
}public void method(){
System.out.println("this is BaseClass.method");
System.out.println("BaseClass.method calls subMethod");
subMethod();
}System.out.println("BaseClass.method calls subMethod");
subMethod();
public class ExtendClass extends BaseClass {
public ExtendClass(){
//Overrideできない!
@Override
protected void subMethod(){
private void subMetyod(){
}
System.out.println("create ExtendClass");
super();
}//Overrideできない!
private void subMetyod(){
System.out.println("this is ExtendClass.subMethod. D'OH!");
}
create ExtendClass
create BaseClass
this is BaseClass.method
BaseClass.method calls subMethod
this is BaseClass.subMethod. Woohoo!
というように親クラスのsubMethodが実行されます。create BaseClass
this is BaseClass.method
BaseClass.method calls subMethod
this is BaseClass.subMethod. Woohoo!
で、こちらはちなむ必要もないんですが、一応ちなんでおくと
protectedなsubMethodをオーバーライドされた状態でも
BaseClass baseClass = new BaseClass();
baseClass.method();
という風に親クラスのインスタンスを作ってメソッドを呼ぶとbaseClass.method();
create BaseClass
this is BaseClass.method
BaseClass.method calls subMethod
this is BaseClass.subMethod. Woohoo!
ちゃんとこうなります。this is BaseClass.method
BaseClass.method calls subMethod
this is BaseClass.subMethod. Woohoo!
よかったよかった。
一回試せばわざわざ書くこともない類のもんですが、検索しても出てこない、というか、コレを検索する方法がよく分からないので、忘れた時用に書いておきます。
Execute Gnuplot from Java
2008/05/29
JavaからGnuplotを使ってプロットする方法


Javaは
なので、普通に
が、起動したって命令しなきゃ何もしないで終わるだけ。
Cとかだとここからパイプ使ってgnuplotにコマンドを渡せるみたいですが、
Javaだとどうやりゃ良いのかよくわかりませんでした。
幸い、gnuplotはコマンドライン引数にファイルのパスを書いてあげれば、そのファイルに書かれたコマンドを実行してくれます。
そこで、『データを記述した一時ファイル』(tmpDataFile)と『そのファイルに書かれたデータをプロットする命令を書いた一時ファイル』(tmpCommandFile)の二つを用意し、Javaから外部プロセスとしてgunuplotを実行するという、なんとも面倒臭い形になりました。
以下がその方法になります。
たとえばjava.io.PrintWriterを使って
これで一時ファイルの準備は終わったので、
ゴミのようなライブラリも作りましたので、指動かすのが面倒臭ければ使ってみてください。
jarとsrc入りのzip
使い方は、とりあえず
import com.magiciansforest.gpj.GnuplotExecuter
して、
・データを入れた配列を引数に入れたコンストラクタ
・show(表示時間)
の二手間でグラフが表示できる!かもしれない!
Win版のgnuplotでどう動くかは分りません。
Windows使ってないからね!


Javaは
Runtime.getRuntime().exec("コマンド");
とすることで外部のプログラムを起動することができます。なので、普通に
Runtime.getRuntime().exec("gnuplot");
でgnuplotは起動します。が、起動したって命令しなきゃ何もしないで終わるだけ。
Cとかだとここからパイプ使ってgnuplotにコマンドを渡せるみたいですが、
Javaだとどうやりゃ良いのかよくわかりませんでした。
幸い、gnuplotはコマンドライン引数にファイルのパスを書いてあげれば、そのファイルに書かれたコマンドを実行してくれます。
そこで、『データを記述した一時ファイル』(tmpDataFile)と『そのファイルに書かれたデータをプロットする命令を書いた一時ファイル』(tmpCommandFile)の二つを用意し、Javaから外部プロセスとしてgunuplotを実行するという、なんとも面倒臭い形になりました。
以下がその方法になります。
//一時ファイル作る
File tmpFile = File.createTmpFile("ファイル名", "拡張子");
//プログラム終了時に一時ファイルを消すように設定する
tmpFile.deleteOnExit();
という風にすれば一時ファイルを作れるので、File tmpFile = File.createTmpFile("ファイル名", "拡張子");
//プログラム終了時に一時ファイルを消すように設定する
tmpFile.deleteOnExit();
たとえばjava.io.PrintWriterを使って
//コマンド書いた一時ファイル
File tmpCommandFile = File.createTmpFile("gpcommand", "tmp");
//プログラム終了時に消すように設定
tmpFile.deleteOnExit();
//PrintWriterを作る
FileOutputStream cfos = new FileOutputStream(tmpCommandFile);
OutputStreamWriter cosw = new OutputStreamWriter(fos);
commandFileWriter = new PrintWriter(osw);
//データ書いた一時ファイル
File tmpDataFile = File.createTmpFile("gpdata", "tmp");
//プログラム終了時に消すように設定
tmpDataFile.deleteOnExit();
//PrintWriterを作る
FileOutputStream dfos = new FileOutputStream(tmpDataFile);
OutputStreamWriter dosw = new OutputStreamWriter(fos);
dataFileWriter = new PrintWriter(osw);
というようにしておいて
File tmpCommandFile = File.createTmpFile("gpcommand", "tmp");
//プログラム終了時に消すように設定
tmpFile.deleteOnExit();
//PrintWriterを作る
FileOutputStream cfos = new FileOutputStream(tmpCommandFile);
OutputStreamWriter cosw = new OutputStreamWriter(fos);
commandFileWriter = new PrintWriter(osw);
//データ書いた一時ファイル
File tmpDataFile = File.createTmpFile("gpdata", "tmp");
//プログラム終了時に消すように設定
tmpDataFile.deleteOnExit();
//PrintWriterを作る
FileOutputStream dfos = new FileOutputStream(tmpDataFile);
OutputStreamWriter dosw = new OutputStreamWriter(fos);
dataFileWriter = new PrintWriter(osw);
//配列に突っ込んであるデータをtmpDataFileに書き出してみる
for (int i=0; i
dataFileWriter.println(i + ", " + dataArray[i]);
//開いてたファイルを閉じる
dataFileWriter.close();
dosw.close();
dfos.close();
for (int i=0; i
//開いてたファイルを閉じる
dataFileWriter.close();
dosw.close();
dfos.close();
//tmpDataFileを読み込んでプロットするコマンドをtmpCommandFileに書いてみる
commandFileWriter.println("plot \"" + tmpDataFile.getAbsolutePath() + "\" with lines");
commandFileWriter.println("pause -1");
//以下tmpDataFileと同じなので略
とします。commandFileWriter.println("plot \"" + tmpDataFile.getAbsolutePath() + "\" with lines");
commandFileWriter.println("pause -1");
//以下tmpDataFileと同じなので略
これで一時ファイルの準備は終わったので、
//gnuplot起動
Process gnuplot = Runtime.getRuntime().exec("gnuplot " + tmpCommandFile.getAbsolutePath());
//gnuplotが終わるまで待機したければ
//gnuplot.waitFor();
これでgnuplotがグラフを作ってくれるはずです。Process gnuplot = Runtime.getRuntime().exec("gnuplot " + tmpCommandFile.getAbsolutePath());
//gnuplotが終わるまで待機したければ
//gnuplot.waitFor();
ゴミのようなライブラリも作りましたので、指動かすのが面倒臭ければ使ってみてください。
jarとsrc入りのzip
使い方は、とりあえず
import com.magiciansforest.gpj.GnuplotExecuter
して、
//GnuplotExecuter(int[] X)
//GnuplotExecuter(int[] X, int[] Y)
//GnuplotExecuter(double[] X)
//GnuplotExecuter(double[] X, double[] Y)
//もあります
GnuplotExecuter ge = new GnuplotExecuter(double[] data);
//showメソッドで表示します。
//secは表示時間…苦し紛れです。
ge.show(sec);
です。//GnuplotExecuter(int[] X, int[] Y)
//GnuplotExecuter(double[] X)
//GnuplotExecuter(double[] X, double[] Y)
//もあります
GnuplotExecuter ge = new GnuplotExecuter(double[] data);
//showメソッドで表示します。
//secは表示時間…苦し紛れです。
ge.show(sec);
・データを入れた配列を引数に入れたコンストラクタ
・show(表示時間)
の二手間でグラフが表示できる!かもしれない!
Win版のgnuplotでどう動くかは分りません。
Windows使ってないからね!
MATLAB File into the Java
うほ、良いデータ!と思ったら拡張子が.mat
MATLABなんて持ってねーよー… orz
バイナリこわいよー
そんなわけで今までは.matの時点で諦めてたんですが、ふと検索してみたらJavaで.matを読み書きするライブラリがありました。
JMatIO
Wojciech最高!なんですが、ドキュメントが超ぶっきらぼうです。
気持ちはよく分かるんですけどね...
MLArrayにisComplex()というメソッドがあるのにcom.jmatio.typesにはMLComplexがなかったり、いまいちアヤシゲな部分もあるんですが、とりあえず自分の用途には足りました。
ということで、以下使い方。
MATLABなんて持ってねーよー… orz
バイナリこわいよー
そんなわけで今までは.matの時点で諦めてたんですが、ふと検索してみたらJavaで.matを読み書きするライブラリがありました。
JMatIO
Wojciech最高!なんですが、ドキュメントが超ぶっきらぼうです。
気持ちはよく分かるんですけどね...
MLArrayにisComplex()というメソッドがあるのにcom.jmatio.typesにはMLComplexがなかったり、いまいちアヤシゲな部分もあるんですが、とりあえず自分の用途には足りました。
ということで、以下使い方。
とりあえずeclipseで
[Build Path -> add Libraries]
でJMatIO.jarを入れておいて、
Deprecatedとか書いてありますが、getData()以外は
"名前を指定して特定の配列を持ってくるメソッド"
しかないし、別に「使うな」ってわけでもないし、とりあえず無視します。(*)
get("name")などを使って一段奥に入るコード書き足してcontetntToString()を表示
を繰り返します。これバカだけど間違いはない方法。
おそらく、最終的にはMLDoubleとかMLInt64が出てくると思います。
これらはdoubleやintの2次元配列になっているので、
XMLのXPathみたいにできれば便利なんだけど、まあ、そこまでは求めません。
仕事で.matをいじり倒さなきゃいけないってわけでもないしねぇ。
*ファイルの中身が分かっている時は
[Build Path -> add Libraries]
でJMatIO.jarを入れておいて、
//インポート
import java.utli.ArrayList;
import com.jmatio.io.*;
import com.jmatio.types.*;
インポートして、import java.utli.ArrayList;
import com.jmatio.io.*;
import com.jmatio.types.*;
//ファイル読み込み
MatFileReader matlabFile = new
MatFileReader("path/matfile.mat");
ファイルを読み込んで、MatFileReader matlabFile = new
MatFileReader("path/matfile.mat");
ArrayList dataList = matlabFile.getData();
こうするとファイルの中身がArrayListで出てきます。Deprecatedとか書いてありますが、getData()以外は
"名前を指定して特定の配列を持ってくるメソッド"
しかないし、別に「使うな」ってわけでもないし、とりあえず無視します。(*)
//matファイルの中身を書き出す
for(i=0; i<data.size(); i++){
MLArray data = (MLArray) dataLsit.get(i);
System.out.println(data.getName());
System.out.printLn(data.contentToString());
}
とすればさっき取ってきたデータの中身を見ることができますから、for(i=0; i<data.size(); i++){
MLArray data = (MLArray) dataLsit.get(i);
System.out.println(data.getName());
System.out.printLn(data.contentToString());
}
get("name")などを使って一段奥に入るコード書き足してcontetntToString()を表示
を繰り返します。これバカだけど間違いはない方法。
おそらく、最終的にはMLDoubleとかMLInt64が出てくると思います。
これらはdoubleやintの2次元配列になっているので、
MLDouble hogeDataArray;
//必要なら
// double[][] doubleArray = new double[hogeDataArray.getM()][hogeDataArray.getN()];
//みたいなの入れる
doubleArray = hogeDataArray.getArray();
この後は普通に数値が詰まった配列を使うときと同じです。//必要なら
// double[][] doubleArray = new double[hogeDataArray.getM()][hogeDataArray.getN()];
//みたいなの入れる
doubleArray = hogeDataArray.getArray();
XMLのXPathみたいにできれば便利なんだけど、まあ、そこまでは求めません。
仕事で.matをいじり倒さなきゃいけないってわけでもないしねぇ。
*ファイルの中身が分かっている時は
//フィルタの設定
MatFileFilter filter = new
MatFileFilter();
filter.addArrayName("hogeArray");
という風にフィルタを作っておいて、ファイルを読み込む時
MatFileFilter filter = new
MatFileFilter();
filter.addArrayName("hogeArray");
//ファイル読み込み
MatFileReader matlabFile = new
MatFileReader(fileName, filter);
とすると『matファイルの指定された部分だけがメモリにロードされるから、大きなファイルの場合は便利よ』というような事が書いてありました。
MatFileReader matlabFile = new
MatFileReader(fileName, filter);
