web-dev-qa-db-ja.com

バイト配列のpcmサンプルを-1.0から1.0の範囲の浮動小数点数として変換する方法

私が使用するリサンプリングアルゴリズムは、範囲-1.0〜1.の入力サンプルを含むfloat配列を期待しています。オーディオデータは16ビット PCMとサンプルレート22khzです。

オーディオを22khzから8khzにダウンサンプリングしたいのですが、サンプルをバイト配列で浮動小数点数> = -1および<= 1として表現し、バイト配列に戻す方法は?

25
Raneez Ahmed

次の2つの質問をします。

  1. 22kHzから8kHzにダウンサンプリングする方法は?

  2. 浮動小数点数[-1,1]から16ビット整数に変換して戻すにはどうすればよいですか?

質問は1番が別の場所で処理されることを示すように更新されていますが、他の誰かを助ける場合に備えて、私の回答の一部は残しておきます。

1. 22kHzから8kHzにダウンサンプリングするには?

コメンターは、これはFFTで解決できることをほのめかしました。これは正しくありません(リサンプリングの1つのステップはフィルタリングです。興味がある場合は、ここでフィルタリングにFFTを使用しない理由を述べます: http://blog.bjornroche.com/2012/08/when-to -not-use-fft.html )。

信号をリサンプリングする非常に良い方法の1つは、 多相フィルター を使用することです。ただし、信号処理の経験がある人でも、これは非常に複雑です。他にもいくつかのオプションがあります。

  • libsamplerate のような高品質のリサンプリングを実装するライブラリを使用します
  • 素早く汚いことをする

あなたはすでに最初のアプローチを行っているようです、それは素晴らしいです。

速くて汚い解決策はそれほど良く聞こえませんが、8 kHzに下がっているため、音質は最優先事項ではないと思います。すばやく簡単なオプションの1つは次のとおりです。

  • 信号にローパスフィルターを適用します。 4 kHzを超えるオーディオはできるだけ取り除くようにしてください。記述されているフィルターを使用できます ここ (理想的には、これらのフィルターよりも急なものが必要ですが、少なくとも何もないよりは優れています)。
  • 元の信号から2.75番目ごとのサンプルを選択して、新しいリサンプリングされた信号を生成します。整数以外のサンプルが必要な場合は、線形補間を使用してください。線形補間のヘルプが必要な場合は、 ここ を試してください。

この手法は、音声アプリケーションには十分以上のはずです。ただし、まだ試していないので、よくわかりません。他の人のライブラリを使用することを強くお勧めします。

ポリフェーズフィルターなどの独自の高品質サンプルレート変換を本当に実装する場合は、それを調査してから、質問があれば https://dsp.stackexchange.com/ 、ここではありません。

2. float [-1,1]から16ビットintに変換したり、元に戻したりするにはどうすればよいですか?

これはすでにc.fogelklouによって開始されましたが、私に装飾させてください。

まず、16ビット整数の範囲は-32768〜32767です(通常、16ビットオーディオは符号付きです)。 intからfloatに変換するには、次のようにします。

float f;
int16 i = ...;
f = ((float) i) / (float) 32768
if( f > 1 ) f = 1;
if( f < -1 ) f = -1;

通常、追加の「境界」を行う必要はありませんが(実際に実際に16ビット整数を使用している場合は必要ありません)、何らかの理由で16ビットを超える整数がある場合に備えてあります。

元に戻すには、次のようにします。

float f = ...;
int16 i;
f = f * 32768 ;
if( f > 32767 ) f = 32767;
if( f < -32768 ) f = -32768;
i = (int16) f;

この場合、通常は範囲外の値、特に32767より大きい値に注意する必要があります。これにより、f = 1に歪みが生じると不平を言う可能性があります。この問題は熱心に議論されています。これに関する(不完全な)議論については このブログの投稿 を参照してください。

これは「政府の仕事には十分」以上のものです。つまり、究極の音質が気になる場合以外は問題なく動作します。あなたは8kHzに行くつもりなので、そうではないと私たちは確信していると思うので、この答えは結構です。

ただし、完全を期すために、これを追加する必要があります。完全に純粋な状態を維持しようとしている場合は、この変換によって歪みが生じることに注意してください。どうして? floatからintへの変換時のエラーは信号と相関しているためです。そのエラーの相関関係はひどいもので、実際には非常に小さいにもかかわらず、実際に聞くことができることがわかりました。 (幸いにも、スピーチや低ダイナミックレンジの音楽のようなものではそれほど重要ではないほど小さい)このエラーを解消するには、floatからintへの変換で dither と呼ばれるものを使用する必要があります。繰り返しになりますが、それが気になる場合は、調査して https://dsp.stackexchange.com/ に関連する具体的な質問をしてください。ここではありません。

このトピックに関するスライドがある、デジタルオーディオプログラミングの基本に関する私の講演のスライドにも興味があるかもしれませんが、基本的には同じことを言っています(たぶん、私が言ったことよりも少ないかもしれません)。 http ://blog.bjornroche.com/2011/11/slides-from-fundamentals-of-audio.html

52
Bjorn Roche

16ビットPCMの範囲は-32768〜32767です。したがって、各PCMサンプルに(1.0f/32768.0f)を乗算して新しいフロート配列にし、それをリサンプルに渡します。

リサンプリング後にフロートに戻り、32768.0を掛けて、飽和させ(32768〜32767の範囲外のものをクリップ)、丸め(またはビョルンが述べたようにディザリング)、ショートにキャストし直します。

ビットエラーのない乗算を使用した前後の変換を示すテストコード:

// PcmConvertTest.cpp : Defines the entry point for the console application.
//

#include <assert.h>
#include <string.h>
#include <stdint.h>
#define SZ 65536
#define MAX(x,y) ((x)>(y)) ? (x) : (y)
#define MIN(x,y) ((x)<(y)) ? (x) : (y)
int main(int argc, char* argv[])
{
  int16_t *pIntBuf1 = new int16_t[SZ];
  int16_t *pIntBuf2 = new int16_t[SZ];
  float   *pFloatBuf = new float[SZ];

  // Create an initial short buffer for testing
  for( int i = 0; i < SZ; i++) {
    pIntBuf1[i] = (int16_t)(-32768 + i);
  }

  // Convert the buffer to floats. (before resampling)
  const float div = (1.0f/32768.0f);
  for( int i = 0; i < SZ; i++) {
    pFloatBuf[i] = div * (float)pIntBuf1[i];
  }

  // Convert back to shorts
  const float mul = (32768.0f);
  for( int i = 0; i < SZ; i++) {
    int32_t tmp = (int32_t)(mul * pFloatBuf[i]);
    tmp = MAX( tmp, -32768 ); // CLIP < 32768
    tmp = MIN( tmp, 32767 );  // CLIP > 32767
    pIntBuf2[i] = tmp;
  }

  // Check that the conversion went int16_t to float and back to int for every PCM value without any errors.
  assert( 0 == memcmp( pIntBuf1, pIntBuf2, sizeof(int16_t) * SZ) );

  delete pIntBuf1;
  delete pIntBuf2;
  delete pFloatBuf;
  return 0;
}
14
c.fogelklou