experimentalism->unreal

2008/07/02
AudioInputStreamは長さFrameSize以下の配列だと読み込まない
JavaでWaveファイルのデータを読み込む時は、AudioInputStreamのread(byte[] b)メソッドを使うのが普通みたいなのですが、変な所で引っかかってしまったので備忘メモ。


読み込むファイルは16bitステレオでPCMです。
とりあえず、下準備として
AudioInputStream ais = AudioSystem.getAudioInputStream(new File(filePath));
としておきます。

ところで、16bitステレオのWave(RIFF)ファイルは

0123...(n-1)*4(n-1)*4 +1(n-1)*4 +2(n-1)*4+3
L11L12R11R12...Ln1Ln2Rn1Rn2

というようにデータが並んでいます。
いかにも「2バイト単位で読み込んで左右のデータを振り分ければいいじゃないの」と言っているかのようです。
それじゃあ、ということで
//長さ2のバイトの配列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));
とすると
4
となって、ちゃんとデータを読み込んでくれた事がわかります。
ちなみに、配列の長さを5にすると読み込むバイト数は4になります。
フレーム単位で読むんでしょうね。たぶん。
2008/06/23
PureJavaなFFTライブラリ JTransforms
JTransforms
Piotr Wendykirさんが公開してる、JavaによるJavaのためのFFTライブラリ。
Takuya Oouraさん数値計算ライブラリにある汎用FFTパッケージをJavaに翻訳したものとかなんとか。
FFTしてIFFTするだけのお試しは成功しました。
他にも色々できるみたいなので、興味ある方は上のリンクとかJavaDocを読んでみてください。
2008/06/22
メソッドから呼び出されるメソッドをオーバーライドするとどうなるか
親クラス(orスーパークラスor基本クラス)があります。
親クラスを継承(or汎化)した子クラス(orサブクラスor派生クラス)があります。
親クラスには、protectedなサブメソッドを呼ぶメソッドがあります。
子クラスで、親クラスのサブメソッドをオーバーライドしました。
この時、子クラスのインスタンスからメソッドを呼ぶと、メソッドは
  • 親クラスで定義された元々のサブメソッドを使う
  • 子クラスでオーバーライドしたサブメソッドを使う
どっちでしょうか。
具体的に書くと
public class BaseClass {
public BaseClass(){
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();
}
}

public class ExtendClass extends BaseClass {
public ExtendClass(){
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();
としてみると
create ExtendClass
create BaseClass
this is BaseClass.method
BaseClass.method calls subMethod
this is ExtendClass.subMethod. D'OH!
と表示されます。
ちゃんとオーバライドしたサブメソッドが使われてますね。
よかったよかった。

ちなみに、これはサブメソッドがprotectedでオーバーライドできるからこその現象で、たとえば
public class BaseClass {
public BaseClass(){
System.out.println("create BaseClass");
}

protected void subMethod(){
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();
}
}

public class ExtendClass extends BaseClass {
public ExtendClass(){
System.out.println("create ExtendClass"); super();
}

//Overrideできない!
@Override
protected void subMethod(){
private void subMetyod(){
System.out.println("this is ExtendClass.subMethod. D'OH!");
}
}
という風にBaseClassのsubMethodをprivateにすると、子クラスでsubMethodという同じ名前のメソッドを定義しても
create ExtendClass
create BaseClass
this is BaseClass.method
BaseClass.method calls subMethod
this is BaseClass.subMethod. Woohoo!
というように親クラスのsubMethodが実行されます。


で、こちらはちなむ必要もないんですが、一応ちなんでおくと
protectedなsubMethodをオーバーライドされた状態でも
BaseClass baseClass = new BaseClass();
baseClass.method();
という風に親クラスのインスタンスを作ってメソッドを呼ぶと
create BaseClass
this is BaseClass.method
BaseClass.method calls subMethod
this is BaseClass.subMethod. Woohoo!
ちゃんとこうなります。
よかったよかった。

一回試せばわざわざ書くこともない類のもんですが、検索しても出てこない、というか、コレを検索する方法がよく分からないので、忘れた時用に書いておきます。
JavaでできたOS
Java製OS JNode
2008/06/16
Execute Gnuplot from Java
JavaからGnuplotを使ってプロットする方法

sin(x)

f(x)

Javaは
Runtime.getRuntime().exec("コマンド");
とすることで外部のプログラムを起動することができます。
なので、普通に
Runtime.getRuntime().exec("gnuplot");
でgnuplotは起動します。
が、起動したって命令しなきゃ何もしないで終わるだけ。
Cとかだとここからパイプ使ってgnuplotにコマンドを渡せるみたいですが、
Javaだとどうやりゃ良いのかよくわかりませんでした。
幸い、gnuplotはコマンドライン引数にファイルのパスを書いてあげれば、そのファイルに書かれたコマンドを実行してくれます。
そこで、『データを記述した一時ファイル』(tmpDataFile)と『そのファイルに書かれたデータをプロットする命令を書いた一時ファイル』(tmpCommandFile)の二つを用意し、Javaから外部プロセスとしてgunuplotを実行するという、なんとも面倒臭い形になりました。
以下がその方法になります。

//一時ファイル作る
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);
というようにしておいて
//配列に突っ込んであるデータをtmpDataFileに書き出してみる
for (int i=0; i dataFileWriter.println(i + ", " + dataArray[i]);
//開いてたファイルを閉じる
dataFileWriter.close();
dosw.close();
dfos.close();

//tmpDataFileを読み込んでプロットするコマンドをtmpCommandFileに書いてみる
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がグラフを作ってくれるはずです。


ゴミのようなライブラリも作りましたので、指動かすのが面倒臭ければ使ってみてください。
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);
です。
・データを入れた配列を引数に入れたコンストラクタ
・show(表示時間)
の二手間でグラフが表示できる!かもしれない!
Win版のgnuplotでどう動くかは分りません。
Windows使ってないからね!
2008/05/29
MATLAB File into the Java
うほ、良いデータ!と思ったら拡張子が.mat
MATLABなんて持ってねーよー… orz
バイナリこわいよー

そんなわけで今までは.matの時点で諦めてたんですが、ふと検索してみたらJavaで.matを読み書きするライブラリがありました。
JMatIO

Wojciech最高!なんですが、ドキュメントが超ぶっきらぼうです。
気持ちはよく分かるんですけどね...

MLArrayにisComplex()というメソッドがあるのにcom.jmatio.typesにはMLComplexがなかったり、いまいちアヤシゲな部分もあるんですが、とりあえず自分の用途には足りました。
ということで、以下使い方。

とりあえずeclipseで
[Build Path -> add Libraries]
でJMatIO.jarを入れておいて、
//インポート
import java.utli.ArrayList;
import com.jmatio.io.*;
import com.jmatio.types.*;
インポートして、
//ファイル読み込み
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());
}
とすればさっき取ってきたデータの中身を見ることができますから、
get("name")などを使って一段奥に入るコード書き足してcontetntToString()を表示
を繰り返します。これバカだけど間違いはない方法。

おそらく、最終的にはMLDoubleとかMLInt64が出てくると思います。
これらはdoubleやintの2次元配列になっているので、
MLDouble hogeDataArray;
//必要なら
// double[][] doubleArray = new double[hogeDataArray.getM()][hogeDataArray.getN()];
//みたいなの入れる
doubleArray = hogeDataArray.getArray();
この後は普通に数値が詰まった配列を使うときと同じです。


XMLのXPathみたいにできれば便利なんだけど、まあ、そこまでは求めません。
仕事で.matをいじり倒さなきゃいけないってわけでもないしねぇ。


*ファイルの中身が分かっている時は
//フィルタの設定
MatFileFilter filter = new
MatFileFilter();
filter.addArrayName("hogeArray");
という風にフィルタを作っておいて、ファイルを読み込む時
//ファイル読み込み
MatFileReader matlabFile = new
MatFileReader(fileName, filter);
とすると『matファイルの指定された部分だけがメモリにロードされるから、大きなファイルの場合は便利よ』というような事が書いてありました。