fresh digitable

めんどくさかったなってことを振り返ったり振り返らなかったりするための記録

byte[]からdouble[]への変換でByteBufferを使う

昔とった私のHRTF(Head-Related Transfer Function)のインパルス応答(以降, hrir: head-related impulse response)を音楽に畳み込みながら再生するアプリを作り始めた。

hrirdoubleのバイナリ形式で保存してあったので、ファイルからbyte[]形式で読み込んでdouble[]に変換する必要がある。 今回はByteBufferを使うことにした。ちなみに、このクラスを使わないとすればbyte[8] -> long -> Double.longBitsToDouble(long)みたいな感じでコツコツ変換していく方法もある。

使い方はざっくり書くとこんなかんじ。

AssetFileDescriptor fd = assets.openFd(string);
// バッファサイズは使いながら増やせないので、最初に必要なだけ確保する。
// バイトオーダのデフォルトはビッグエンディアン
ByteBuffer byteBuf = ByteBuffer.allocate((int)fd.getLength())
                               .order(ByteOrder.LITTLE_ENDIAN);

// ファイルチャネルから直接読む
FileChannel channel = fd.createInputStream().getChannel();
channel.read(byteBuf);

// バッファ書き込みから読み出しモードに切り替える
byteBuf.flip();

// バッファの中身をdoubleの値として書き出す
double[] res = new double[byteBuf.capacity() / (Double.SIZE / 8)];
for (int i = 0; i < res.length; i++) {
   res[i] = byteBuf.getDouble();
}

最初はres = byteBuf.asDoubleBuffer().array()としていたが、これだとUnexpectedOperationみたいな例外が飛んできて怒られた。ByteBuffer#asDoubleBuffer()double[]をallocateしないということなのだろうか。いろいろ悩んだ挙句最後のfor文のようにした。

ところで、件のメソッドのテストコードを読んでいると、これでいいのかな、と感じたところがあった。何かのご縁でこのページにやってきた方々にもぜひこのコードを読んでいただき、なんかおかしいような、、、とかいや、これでよいのだというようなことをお教えいただければありがたい。

具体的には、doubleBuffer.remaining()が0のとき、アサーションを全く通らずにテストが成功してしまうという点。私の上のコードではbyteBuf.asDoubleBuffer().remaining()が0になるんだよなー…

今回の実装で参考にさせていただきました。ありがとうございました。