0

PulseAudio API を使用して、現在のマイク入力を「リアルタイム」で取得しています。バッファ データは、16 ビットのリトル エンディアン バイト配列として配信されます。私がやりたいことは、バッファ内の最大ピーク レベルを見つけて、それをデシベル値に変換することです。そのためには、2 つのバイト配列値をそれぞれ 1 つの整数値に変換する必要があります。同じループプロセスで、最大値も探しています。その後、最大値をデシベル値に変換します。Cコードは次のとおりです。

static ssize_t loop_write(int fd, const uint8_t *data, size_t size) 
{
int newsize = size / 2;
uint16_t max_value = 0;
int i = 0;

for (i = 0; i < size; i += 2)
{
    // put two bytes into one integer
    uint16_t val = data[i] + ((uint32_t)data[i+1] << 8);

    // find max value
    if(val > max_value)
       max_value = val;
}

// convert to decibel
float decibel = max_value / pow(2, 15);

if(decibel != 0)
    decibel = 20 * log(decibel);

// print result
printf("%f, ", decibel);

return size;
}

私の知る限り、PA_SAMPLE_S16LE の振幅値は 0 から 32768 の間である必要があります。しかし、デシベル変換前に 0 ~ 65536 の値を取得しています。変換に何か問題がありますか?

完全を期すために、pulseaudio のセットアップも投稿しています。

int main(int argc, char*argv[]) 
{
char *device = "alsa_input.usb-041e_30d3_121023000184-00-U0x41e0x30d3.analog-mono";

// The sample type to use
static const pa_sample_spec ss = {
    .format = PA_SAMPLE_S16LE,
    .rate = 44100,
    .channels = 1
};
pa_simple *s = NULL;
int ret = 1;
int error;

// Create the recording stream 
if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, device, "record", &ss, NULL, NULL, &error))) {
    fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
    goto finish;
}

for (;;) {
    uint8_t buf[BUFSIZE];

    // Record some data ...
    if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
        fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
        goto finish;
    }

    // And write it to STDOUT
    if (loop_write(STDOUT_FILENO, buf, sizeof(buf)) != sizeof(buf)) {
        fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
        goto finish;
    }
}

ret = 0;

finish:

if (s)
    pa_simple_free(s);

return 0;
}
4

2 に答える 2

7

私がやりたいことは、バッファ内の最大ピーク レベルを見つけて、それをデシベル値に変換することです。

物理的な観点からは、このアプローチは意味がありません。ダイナミック レンジ全体に対して 1 つのサンプル値を指定することは可能ですが、サウンド レベル、つまり信号のパワーに関心があるでしょう。フルスケールであっても、単一のピークはごくわずかなエネルギーしか運びません。高調波歪みと帯域幅の制限により、非常に大きなポップノイズが発生する可能性がありますが、技術的には、その電力密度は帯域制限されたスペクトル全体に分散されます。

本当にすべきことは、RMS 値 (二乗平均平方根) を決定することです。いえ

RMS = sqrt( sum( square(samples) )/n_samples )

編集: 上記はDC部分のない信号に対してのみ正しいことに注意してください。ほとんどのアナログ サウンド インターフェイスは AC 結合されているため、これは問題になりません。ただし、DC 部分もある場合は、最初にサンプルから平均値を差し引く必要があります。つまり、

RMS_DC_reject = sqrt( sum( square(samples - mean_sample) )/n_samples )

これを以下のコードに追加する読者の演習として残しておきます。

これにより、実際に必要な、処理されたサンプルのパワーが得られます。あなたはデシベルについて尋ねました。今、私はあなたに尋ねなければなりません dB()? Bels (または deciBels) は相対 (比較) 尺度であるため、参照値が必要です。デジタル信号の場合、フルスケールは 0 dB(FS) になり、ゼロラインは になり-20 log10( 2^B )ますB = sampling bit depth。16 ビット信号の場合、約 -96 dB(FS)。

回線上の信号について話している場合、一般的な基準は電力 1 mW であり、この場合のスケールは dB(m) です。オーディオ ライン レベルの場合、フル スケールは 1 mW の信号電力に等しいと定義されています。これは、1 V RMS が 1 kΩ の抵抗で消費するものです (RMS が再び表示されます)。

ここで、フルスケールは dB(m) で定義される入力回路によって即座に決定されるため、後で dB(FS) を dB(m) (または dBm) として表示することができます。

実際のサウンド レベルに関しては、これは入力アンプのゲインと、使用するマイクの変換効率に依存します。


私の知る限り、PA_SAMPLE_S16LE の振幅値は 0 から 32768 の間である必要があります。しかし、デシベル変換前に 0 ~ 65536 の値を取得しています。変換に何か問題がありますか?

符号付き整数形式について質問しました。しかし、値を unsigned int にキャストしています。また、dB_FS はフルスケールに相対的であるため、ビット数で割らないでください。16 ビットのゼロ信号の場合、結果は約 -96 dB になります。とにかく、除算は意味がありません.RMSを範囲[0にスケーリングするだけです。1] ですが、log(0) は -infinity に発散します。したがって、あなたのif声明。しかし、これは物理学であり、物理学は連続的であることを忘れないでください。ここには if ステートメントがあってはなりません。

このように書くべきです

// even for signed values this should be 2^N
// we're going to deal with signed later
double const MAX_SIGNAL = 1 << SAMPLE_BITS;

// using double here, because float offers only 25 bits of
// distortion free dynamic range.
double accum = 0;
int const n_samples = size/2;
for (i = 0; i < size; i += 2)
{
    // put two bytes into one __signed__ integer
    int16_t val = data[i] + ((int16_t)data[i+1] << 8);

    accum += val*val;
}
accum /= n_samples;

// Since we're using signed values we need to
// double the accumulation; of course this could be
// contracted into the statement above
accum *= 2.;

float const dB_FS = -20 * log10( MAX_SIGNAL - sqrt(accum) );
于 2013-02-28T12:14:41.173 に答える
0

PulseAudio Simple APIによると:

接続の使用は、通常のread()およびwrite()システムコールと非常によく似ています。主な違いは、pa_simple_read()とpa_simple_write()と呼ばれることです。これらの操作は常にブロックされることに注意してください。

これは、適切な場所でpa_simple_readの戻り値について他に言及されていないように見えるため、戻り値が非常に類似していることを意味しているようです。opengroupのread()マニュアルには次のように書かれています。

正常に完了すると、read()...は、実際に読み取られたバイト数を示す負でない整数を返します。

pa_simple_readが。未満の値を返すとするとsizeof buffer、loop_write関数は初期化されていない値を使用します。それは未定義の振る舞いです。sizeof(buf)pa_simple_readの戻り値を保存し、エラーをチェックした後ではなく、loop_writeに渡すことをお勧めします。

pa_simple_readに渡される値が奇数であるとすると、loop_writeは最後の反復で初期化されていない値を使用します。おそらく、これに対抗するために、ループを次のように変更for (i = 1; i < size; i += 2)し、val宣言/初期化を次のように変更することができます。uint16_t val = data[i-1] + ((uint32_t)data[i] << 8);

この結論に達するのを手伝ってくれたmtrwに感謝します。

于 2013-02-28T12:16:24.673 に答える