コンピュータの中にあるWAVファイルどうしの音量比較をしたいときはけっこうあります。音量の指標として「ラウドネス(sone)」があり、以前その計算プログラムも書いたのですが、その音を鳴らしたときの音圧値(dBSPL値)が分からないと正確な値が得られないというものでした。そのため、実際にスピーカーで鳴らして、レベルメーターで計測して、という手間がかかります。複数のWAVファイルの音量をある程度そろえたいときに、いちいち実験室をセッティングするのはめんどうです。どうせWAVファイルはすべてコンピュータ内にあるので、コンピュータ内で完結させられればいいなぁ、なんて思ってました。
そんなときに見つけたのがITU-R BS.1770-1勧告。複数ファイルの相対的な音量を計算するための方法が書かれた国際基準です。単位はLKFSで、0 dBFS、1 kHzのサイン波が約-3.01 LKFSとなります。規格書を読んでみたらそれほど難しくなかったので、規格書通り*1にちゃちゃっとMatlabプログラムを書いてみました。ハードウェアやプラグイン・ソフトとしても実装されているんですが、単純なわりに買うと意外に高いので。
function lkfs = loudness(X, fs) %LOUDNESS Loudness in LKFS based on Rec. ITU-R BS.1770-1 % lkfs=loudness(X,fs) computes loudness level in Rec. ITU-R BS.1770-1. % % X - input signal % fs - sampling frequency of the signal X % % Input signal can be 1 to 5 channels from mono, stereo, to 5.0ch % surround sound. Exclude LFE channel when using 5.1ch source. % % 2010-02-23 by MARUI Atsushi %% check matrix size if size(X,1) < size(X,2) X = X'; end if size(X,2)>5 error('Number of channels must be less than or equal to five.'); end numCh = size(X,2); X = resample(X, 48000, fs); %% pre-filter b0 = 1.53512485958697; b1 = -2.69169618940638; b2 = 1.19839281085285; a1 = -1.69065929318241; a2 = 0.73248077421585; A = [ 1 a1 a2]; B = [b0 b1 b2]; Y1 = zeros(size(X)); for n=1:numCh Y1(:,n) = filter(B, A, X(:,n)); end %% RLB weighting curve %[B,A] = butter(2, 50/(48000/2)); b0 = 1.0; a1 = -1.99004745483398; b1 = -2.0; a2 = 0.99007225036621; b2 = 1.0; A = [ 1 a1 a2]; B = [b0 b1 b2]; Y = zeros(size(Y1)); for n=1:numCh Y(:,n) = filter(B, A, Y1(:,n)); end %% mean square z = sum(Y .^ 2) / length(Y); G = [1 1 1 sqrt(2) sqrt(2)]; for n=1:numCh u(n) = G(n) * z(n); end lkfs = -0.691 + 10 * log10(sum(u));
これはこれで便利ですが、ふたつのWAVファイルを同じLKFS値にするためにはもうひと工夫が必要です。ある音信号X(サンプリング周波数fs)を、指定したLKFS値にするためには何倍しなくてはならないかを計算してるものです。factor=loudness_match(x, 44100, -20);などとすれば、factor*xが-20LKFSとなるようなfactorを追い込んでくれます。
function factor = loudness_match (X, fs, lkfs) %LOUDNESS_MATCH % LOUDNESS_MATCH (X, fs, lkfs) calculates a amplitude multiplication factor % which equalizes a sound to match the specified loudness (in LKFS). % % See also LOUDNESS by the same author % % 2010-02-23 by MARUI Atsushi factor = 1.0; factorHigh = 10.0; factorLow = 0.0; i = 1; while i s = loudness(factor * X, fs); k = (s - lkfs) / lkfs; disp(sprintf('%3d: Factor %7.5f LKFS %7.3f (%6.2f%% difference)', i, factor, s, k*100)); i = i + 1; if abs(k) < 0.0005 return; end if k < 0.0 factorOld = factor; factor = (factorLow + factor) / 2.0; factorHigh = factorOld; elseif k > 0.0 factorOld = factor; factor = (factorHigh + factor) / 2.0; factorLow = factorOld; end end
【2011-07-13追記】ITU-R BS.1770-2になって、時間変動するコンテンツに対応するためにゲーティングが導入されています。このページで紹介したのは1770-1なので、新しい計算方法とは異なる部分があります。注意して下さい。
*1:規格書はfs=48000 Hzのときのフィルタ係数しか書いていなかったので、めんどくさくなってプログラム中で入力信号を全て48000 Hzにリサンプリングしています。本当は信号のサンプリング周波数にあったフィルタ係数を計算するほうがいいのでしょう。ここは改良の余地有りです。RLB重み曲線についてはコメント中にフィルタ係数計算の形跡を残しておきました。