12

音声の音声ファイルを正規化しようとしています。

具体的には、オーディオファイルに音量のピークが含まれている場合、それを平準化しようとしているので、静かなセクションは大きくなり、ピークは静かになります。

このタスクに取り組むことから学んだことを除いて、私はオーディオ操作についてほとんど知りません。また、私の数学は恥ずかしいほど弱いです。

私はいくつかの調査を行いましたが、Xuggleサイトには、次のコードを使用して音量を下げることを示すサンプルがあります:(完全版はこちら

@Override
  public void onAudioSamples(IAudioSamplesEvent event)
{
  // get the raw audio byes and adjust it's value 

  ShortBuffer buffer = event.getAudioSamples().getByteBuffer().asShortBuffer();
  for (int i = 0; i < buffer.limit(); ++i)
    buffer.put(i, (short)(buffer.get(i) * mVolume));

  super.onAudioSamples(event);
}

getAudioSamples()ここでは、定数でバイトを変更しますmVolume

このアプローチに基づいてgetAudioSamples()、ファイル内の最大/最小を考慮して、正規化によってバイトを正規化された値に変更しようとしました。(詳細については、以下を参照してください)。「無音」(つまり、値を下回るもの)をそのままにしておくための単純なフィルターがあります。

出力ファイルのノイズが非常に多い(つまり、品質が大幅に低下している)ことがわかりました。エラーは、正規化アルゴリズムか、バイトの操作方法のいずれかにあると思います。しかし、次にどこに行けばいいのかわかりません。

これが私が現在行っていることの要約版です。

ステップ1:ファイル内のピークを見つける:

完全なオーディオファイルを読み取り、buffer.get()すべてのAudioSamplesのこの最高値と最低値を見つけます

    @Override
    public void onAudioSamples(IAudioSamplesEvent event) {
        IAudioSamples audioSamples = event.getAudioSamples();
        ShortBuffer buffer = 
           audioSamples.getByteBuffer().asShortBuffer();

        short min = Short.MAX_VALUE;
        short max = Short.MIN_VALUE;
        for (int i = 0; i < buffer.limit(); ++i) {
            short value = buffer.get(i);
            min = (short) Math.min(min, value);
            max = (short) Math.max(max, value);
        }
        // assign of min/max ommitted for brevity.
        super.onAudioSamples(event);

    }

手順2:すべての値を正規化します。

手順1と同様のループで、バッファを正規化された値に置き換え、次を呼び出します。

    buffer.put(i, normalize(buffer.get(i));

public short normalize(short value) {
    if (isBackgroundNoise(value))
        return value;

    short rawMin = // min from step1
    short rawMax = // max from step1
    short targetRangeMin = 1000;
    short targetRangeMax = 8000;

    int abs = Math.abs(value);
    double a = (abs - rawMin) * (targetRangeMax - targetRangeMin);
    double b = (rawMax - rawMin);
    double result = targetRangeMin + ( a/b );

     // Copy the sign of value to result.
    result = Math.copySign(result,value);
    return (short) result;
}

質問:

  • これは、オーディオファイルの正規化を試みるための有効なアプローチですか?
  • 私の数学はnormalize()有効ですか?
  • デモコードの同様のアプローチではないのに、なぜこれによってファイルのノイズが増えるのでしょうか。
4

2 に答える 2

9

サンプル値は特定の時点での音波の現在の「高さ」を表しているだけなので、「最小サンプル値」の概念はあまり意味がないと思います。つまり、その絶対値は、オーディオクリップのピーク値とゼロの間で変化します。したがって、を持ってtargetRangeMinいることは間違っているように思われ、おそらく波形の歪みを引き起こすでしょう。

より良いアプローチは、サイズに基づいてサンプル値を減らすある種の重み関数を持つことかもしれないと思います。つまり、大きい値は小さい値よりも大幅に減少します。これも多少の歪みをもたらしますが、おそらくあまり目立たないでしょう。

編集:これはそのようなメソッドのサンプル実装です:

public short normalize(short value) {
    short rawMax = // max from step1
    short targetMax = 8000;

    //This is the maximum volume reduction
    double maxReduce = 1 - targetMax/(double)rawMax;

    int abs = Math.abs(value);
    double factor = (maxReduce * abs/(double)rawMax);

    return (short) Math.round((1 - factor) * value); 
}

参考までに、これは、振幅が10000の正弦曲線に対してアルゴリズムが行ったことです。元のアルゴリズム

これは、正規化後にオーディオ品質が大幅に悪化する理由を説明しています。

これは、私の提案したnormalize方法で実行した後の結果です。推奨されるアルゴリズム

于 2012-09-18T09:13:30.797 に答える
5

オーディオの「正規化」とは、最大値が特定の値、通常は可能な最大値に等しくなるように、オーディオのレベルを上げるプロセスです。今日、別の質問で、誰かがこれを行う方法を説明しました(#1を参照):オーディオボリュームの正規化

ただし、「具体的には、オーディオファイルに音量のピークが含まれている場合、それを平準化しようとしているので、静かなセクションは大きくなり、ピークは静かになります」と続けます。これは「圧縮」または「制限」と呼ばれます(MP3のエンコードに使用されるような圧縮のタイプと混同しないでください!)。詳細については、http://en.wikipedia.org/wiki/Dynamic_range_compressionをご覧ください。

単純なコンプレッサーを実装するのは特に難しいことではありませんが、あなたはあなたの数学が「恥ずかしいほど弱い」と言います。したがって、すでに構築されているものを見つけたいと思うかもしれません。http://sox.sourceforge.net/に実装されているコンプレッサーを見つけて、それをCからJavaに変換できる場合があります。私が知っているコンプレッサーの唯一のJava実装は、誰のソースが利用可能であるか(そしてそれはあまり良くありません)がこの本にあります

問題を解決する別の方法として、ファイルをたとえば1秒半のセグメントで正規化し、線形補間を使用して各セグメントに使用するゲイン値を接続できる場合があります。オーディオの線形補間については、http://blog.bjornroche.com/2010/10/linear-interpolation-for-audio-in-cc.htmlで読むことができます。

レベレーターでソースコードが利用できるかどうかはわかりませんが、それは別の方法で試すことができます。

于 2012-09-19T02:08:55.167 に答える