Arei's blog

JavaScriptでSOFAファイルを扱う


SOFAとは?

ここでいうのは、HRTF(頭部伝達関数)の入ったバイノーラル音声処理に用いられるファイルのことだ。

SOFAのファイル形式

Hierarchical Data Format version 5, HDF5と呼ばれるものだ。

Matlabの関連ページにNetCDFだと書いていたがあながち間違いではないらしい。NetCDFはHDF5の拡張であるからだ。だがマジックナンバーはHDFだと言っている。

NetCDF用のncdump(aptのnetcdf-bin内)でも、HDF5用のh5dump(同h5utils内)で中を見ることができる。

以下、例

Bash
user@foo:~$ ncdump -h HRIR_CIRC360_NF100.sofa
netcdf HRIR_CIRC360_NF100 {
dimensions:
	I = 1 ;
	C = 3 ;
	R = 2 ;
	E = 1 ;
	N = 128 ;
	M = 360 ;
	S = UNLIMITED ; // (0 currently)
variables:
	double ListenerPosition(I, C) ;
		ListenerPosition:Type = "cartesian" ;
		ListenerPosition:Units = "metre" ;
	double ReceiverPosition(R, C, I) ;
		ReceiverPosition:Type = "cartesian" ;
		ReceiverPosition:Units = "metre" ;
	double SourcePosition(M, C) ;
		SourcePosition:Type = "spherical" ;
		SourcePosition:Units = "degree, degree, metre" ;
	double EmitterPosition(E, C, I) ;
		EmitterPosition:Type = "cartesian" ;
		EmitterPosition:Units = "metre" ;
	double ListenerUp(I, C) ;
	double ListenerView(I, C) ;
		ListenerView:Type = "cartesian" ;
		ListenerView:Units = "metre" ;
	double Data.IR(M, R, N) ;
	double Data.SamplingRate(I) ;
		Data.SamplingRate:Units = "hertz" ;
	double Data.Delay(I, R) ;

// global attributes:
		:Conventions = "SOFA" ;
		:Version = "1.0" ;
		:SOFAConventions = "SimpleFreeFieldHRIR" ;
		:SOFAConventionsVersion = "1.0" ;
		:APIName = "ARI SOFA API for Matlab/Octave" ;
		:APIVersion = "1.0.3" ;
		:ApplicationName = "" ;
		:ApplicationVersion = "" ;
		:AuthorContact = "Johannes.Arend@th-koeln.de" ;
		:Comment = "KU100 nearfield HRIRs" ;
		:DataType = "FIR" ;
		:History = "Converted from the miro file format" ;
		:License = "CC 3.0 BY-SA" ;
		:Organization = "Technische Hochschule Köln, Germany" ;
		:References = "Arend, J.M., Neidhardt, A., Pörschmann, C. (2016). Measurement and Perceptual Evaluation of a Spherical Near-Field HRTF Set. In: Proceedings of the 29th VDT International Convention" ;
		:RoomType = "free field" ;
		:Origin = "http://audiogroup.web.th-koeln.de" ;
		:DateCreated = "2016-05-25 15:04:51" ;
		:DateModified = "2018-05-04 14:41:12" ;
		:Title = "HRTF" ;
		:DatabaseName = "THK" ;
		:ListenerShortName = "HRIR_CIRC360_NF100" ;
		:Author = "Johannes M. Arend" ;
		:ListenerDescription = "Neumann KU100" ;
		:ReceiverDescription = "Neumann KU100; Internal RME Firefac UFX Preamps" ;
		:SourceDescription = "Geithain RL906" ;
		:RoomDescription = "THK ZW8-4 (Anechoic Chamber)" ;
}

中身

ほとんどのSOFAファイルはにはインパルス応答が時間領域で記録されている。

SOFAにはそれ以外の形式もサポートされているようだが、出くわしたことがないので割愛。

以下では、音声処理に必要なところのみ記載する。

N一つのIRデータの長さ(サンプル数)
M計測したIRの数
Data.IRインパルス応答のデータ
SourcePosition音源の場所
SamplingRateサンプリングレート

JavaScriptでの読み込み

WebSofa

使えなくはないが、どうやら壊れている。ある程度の大きさ以上のファイルが読めないし、指定したところから一番近い音源座標のIRを持ってくる機能に不具合があるらしく、どうやっても同じ座標のデータしか取得できない。

netcdfjs

残念ながらnode上でしか使えない。

jsfive

これはブラウザ上でも問題なく使える。

ただし、SourcePositionとIRは1次元の配列として帰ってくる。そのためにサンプル数と測定数を取得するのだが。本当はなにかいい方法があるのか?

JavaScript
var file = await fetch("HRIR_CIRC360_NF100.sofa");
var buffer = file.arrayBuffer();
var f = new hdf5.File(buffer, filename);

f.keys; // (16) ['I', 'C', 'EmitterPosition', 'ListenerUp', 'ListenerView', 'Data.IR', 'Data.SamplingRate', 'Data.Delay', 'R', 'E', 'N', 'M', 'S', 'ListenerPosition', 'ReceiverPosition', 'SourcePosition']
f.get("N").value.length; // 128

var IR = f.get("Data.IR).value;

SofaJS

jsfiveでSOFAを扱いやすくするためにライブラリを自作した。

JavaScript
var file = await fetch("HRIR_CIRC360_NF100.sofa");
var buffer = file.arrayBuffer();
var Sofa = Sofa(buffer);
var IR[L,R] = Sofa.getFilter(Phi, Theta, Radius); 

degreeの角度でIRデータを簡単に取得できる。

あとはこれを煮るなり焼くなり、audioBufferにしてConvolverNodeに入れるなりすればいい。・

音声処理のためのメソッドも存在する。詳しくはgithubのページで。


投稿日

カテゴリー:

,

投稿者:

タグ:

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です