You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为44100采样率立体声16位PCM数据实现无舍入误差的整数型简易IIR低通滤波器?

Integer-Based First-Order Low Pass Filter for 16-bit Stereo PCM

Got it, let's convert your existing floating-point low-pass filter to pure integer operations step by step—this will eliminate rounding noise and keep your waveform precision intact, while sticking to the exact filter logic you already understand.

First, Recap Your Filter Logic

Your floating-point code uses the standard first-order RC low-pass formula:

filtered = last_filtered + alpha * (input - last_filtered)

Which simplifies to:

filtered = alpha * input + (1 - alpha) * last_filtered

Where alpha is the coefficient calculated by your filter_freq function (a value between 0 and 1).

To convert this to integers, we'll use fixed-point arithmetic—we'll scale all values by a power of 2 (32768, since we're working with 16-bit ints) to turn decimals into integers, then scale back down at the end.

Step 1: Precompute Integer Coefficients

First, convert your floating-point alpha to an integer by scaling it by 32768 (2^15, which fits perfectly for Q15 fixed-point format):

#include <math.h>

#define SCALE_FACTOR 32768
#define HALF_SCALE 16384 // For proper rounding

INT32 calculate_alpha_int(DOUBLE cut_freq) {
    DOUBLE alpha_float = filter_freq(cut_freq);
    // Round to nearest integer to avoid truncation error
    return (INT32)(alpha_float * SCALE_FACTOR + 0.5);
}

// Keep your existing coefficient calculation function
DOUBLE filter_freq(DOUBLE cut_freq) {
    DOUBLE a = 1.0/(cut_freq * 2 * M_PI);
    DOUBLE b = 1.0/44100.0; // Replace with your SAMPLE_RATE define if needed
    return b/(a+b);
}

Step 2: Integer Filter Implementation

We'll use 64-bit integers for intermediate calculations to avoid overflow, and keep the filtered state in scaled integer form to preserve precision between samples.

void integer_low_pass_filter(INT16* input_buffer, INT16* output_buffer, UINT32 buffer_size, DOUBLE cut_freq) {
    // Initialize state variables (scaled by SCALE_FACTOR, stored as 64-bit to avoid overflow)
    INT64 last_filtered_left = 0;
    INT64 last_filtered_right = 0;

    // Precompute integer coefficients once outside the loop
    INT32 alpha = calculate_alpha_int(cut_freq);
    INT32 one_minus_alpha = SCALE_FACTOR - alpha;

    INT32 left_id = 0;
    INT32 right_id = 1;

    for(UINT32 a = 0; a < (buffer_size/2); ++a) {
        // Grab raw 16-bit input samples
        INT16 input_l = input_buffer[left_id];
        INT16 input_r = input_buffer[right_id];

        // Perform integer filter calculation (scaled by SCALE_FACTOR)
        // filtered = alpha*input + (1-alpha)*last_filtered
        INT64 filtered_l_int = (INT64)alpha * input_l + (INT64)one_minus_alpha * last_filtered_left;
        INT64 filtered_r_int = (INT64)alpha * input_r + (INT64)one_minus_alpha * last_filtered_right;

        // Update state with full-precision filtered value (don't scale back yet!)
        last_filtered_left = filtered_l_int;
        last_filtered_right = filtered_r_int;

        // Scale back to 16-bit int with proper rounding (add half the scale before shifting)
        INT16 output_l = (INT16)((filtered_l_int + HALF_SCALE) >> 15);
        INT16 output_r = (INT16)((filtered_r_int + HALF_SCALE) >> 15);

        // Optional: Add saturation to prevent overflow when accumulating to output
        INT32 temp_l = output_buffer[left_id] + output_l;
        INT32 temp_r = output_buffer[right_id] + output_r;
        output_buffer[left_id] = (temp_l > 32767) ? 32767 : (temp_l < -32768) ? -32768 : (INT16)temp_l;
        output_buffer[right_id] = (temp_r > 32767) ? 32767 : (temp_r < -32768) ? -32768 : (INT16)temp_r;

        // Move to next stereo sample pair
        left_id += 2;
        right_id += 2;
    }
}

Key Details Explained

  1. Fixed-Point Scaling: By multiplying everything by 32768, we turn the 0-1 range of alpha into a 0-32768 integer range. This lets us use pure integer multiplication/addition instead of floating-point operations.
  2. 64-Bit Intermediate Values: Using INT64 for filtered state and calculations prevents overflow—since a 16-bit int multiplied by a 16-bit coefficient gives a 32-bit value, and adding two of those can exceed 32-bit limits.
  3. Proper Rounding: Adding HALF_SCALE (16384) before shifting right by 15 bits ensures we round to the nearest integer instead of truncating, which reduces quantization noise that causes pop sounds.
  4. Saturation: The optional saturation step ensures we don't exceed the 16-bit int range when accumulating to the output buffer, which eliminates another common source of audio distortion.

Why This Fixes Your Pop Issue

Your original floating-point code introduced small rounding errors every time you converted between float and int16. These errors accumulate over samples and create audible pops. By keeping all calculations in integer form (with scaled state to preserve precision), we eliminate those floating-point rounding artifacts entirely.

内容的提问来源于stack exchange,提问作者1bytecharhaterxxx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 07:59:08