私が使用するリサンプリング アルゴリズムは、範囲-1.0 から 1.0の入力サンプルを含むfloat配列を想定しています。オーディオ データは、サンプルレート 22khzの16 ビットPCMです。
オーディオを 22khz から 8khz にダウンサンプリングしたいのですが、バイト配列のサンプルを浮動小数点数>= -1 および <= 1として表し、バイト配列に戻す方法を教えてください。
私が使用するリサンプリング アルゴリズムは、範囲-1.0 から 1.0の入力サンプルを含むfloat配列を想定しています。オーディオ データは、サンプルレート 22khzの16 ビットPCMです。
オーディオを 22khz から 8khz にダウンサンプリングしたいのですが、バイト配列のサンプルを浮動小数点数>= -1 および <= 1として表し、バイト配列に戻す方法を教えてください。
次の 2 つの質問をします。
22kHz から 8kHz にダウンサンプリングする方法は?
float [-1,1] から 16 ビット int に、またはその逆に変換する方法は?
#1 が他の場所で処理されていることを示すために質問が更新されていることに注意してください。
コメンターは、これは FFT で解決できるとほのめかしました。これは正しくありません (リサンプリングの 1 つのステップはフィルタリングです。興味がある場合に備えて、ここでフィルタリングに FFT を使用しない理由について言及します: http://blog.bjornroche.com/2012/08/when-to-not-use -fft.html )。
信号をリサンプリングする非常に優れた方法の 1 つは、ポリフェーズ フィルターを使用することです。ただし、信号処理の経験者にとっても、これは非常に複雑です。他にもいくつかのオプションがあります。
すでに最初のアプローチを行っているようですね。これは素晴らしいことです。
クイック&ダーティな解決策は良い音にはなりませんが、8 kHz まで下げるので、音質は最優先事項ではないと思います。手っ取り早い方法の 1 つは、次のことです。
この手法は、音声アプリケーションには十分すぎるはずです。ただし、試したことがないのでよくわからないので、他の人のライブラリを使用することを強くお勧めします。
多相フィルターなど、独自の高品質のサンプルレート変換を本当に実装したい場合は、それを調査し、ここではなくhttps://dsp.stackexchange.com/で質問する必要があります。
これはすでに 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 への変換でディザと呼ばれるものを使用する必要があります。繰り返しますが、それが気になる場合は、それを調査し、ここではなくhttps://dsp.stackexchange.com/で関連する具体的な質問をしてください。
また、デジタル オーディオ プログラミングの基本に関する私の講演のスライドにも興味があるかもしれません。このトピックに関するスライドがありますが、基本的には同じことを言っています (私が言ったことよりも少ないかもしれません): http://blog .bjornroche.com/2011/11/slides-from-fundamentals-of-audio.html
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;
}