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

FFmpeg SwrContext单声道音频重采样出现噪音问题寻求修复

修复FFmpeg SwrContext重采样后音频噪音问题

我实现了一个基于FFmpeg SwrContext的单声道音频重采样函数,输入为float数组,搭配自定义write_wav函数将重采样后的音频保存为WAV文件。但重采样后的音频(如24000Hz转48000Hz)出现大量类似丢样的噪音,原始音频与重采样后音频的波形图差异明显,请问如何修复该问题?

重采样函数代码

int resample_and_save(float *audio_input, int input_sample_rate, int output_sample_rate, int input_num_of_samples, const char *output_file) {
    AVChannelLayout ch_layout = AV_CHANNEL_LAYOUT_MONO;
    SwrContext *swr_ctx = swr_alloc();
    uint8_t **resampled_data = NULL;
    float *audio_output = NULL;
    int ret = SUCCESS;

    if (!swr_ctx) {
        fprintf(stderr, "Could not allocate resampler context\n");
        return ERROR_NOTENOUGH_MEMORY;
    }

    av_opt_set_chlayout(swr_ctx, "in_chlayout", &ch_layout, 0);
    av_opt_set_int(swr_ctx, "in_sample_rate", input_sample_rate, 0);
    av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0);

    av_opt_set_chlayout(swr_ctx, "out_chlayout", &ch_layout, 0);
    av_opt_set_int(swr_ctx, "out_sample_rate", output_sample_rate, 0);
    av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0);

    if ((ret = swr_init(swr_ctx)) < 0) {
        ret = handle_av_error(ret);
        goto cleanup;
    }

    int output_samples_count = av_rescale_rnd(swr_get_delay(swr_ctx, input_sample_rate) + input_num_of_samples, output_sample_rate, input_sample_rate, AV_ROUND_UP);
    ret = av_samples_alloc_array_and_samples(&resampled_data, NULL, 1, output_samples_count, AV_SAMPLE_FMT_FLT, 0);
    if (ret < 0) {
        ret = handle_av_error(ret);
        goto cleanup;
    }

    const uint8_t *in_samples[1] = { (const uint8_t *)audio_input };
    int frame_count = swr_convert(swr_ctx, resampled_data, output_samples_count, in_samples, input_num_of_samples);
    if (frame_count < 0) {
        fprintf(stderr, "Error while resampling\n");
        ret = ERROR_UNKNOWN;
        goto cleanup;
    }

    while (1) {
        int frame_count_flush = swr_convert(swr_ctx, resampled_data, output_samples_count, NULL, 0);
        if (frame_count_flush <= 0) {
            break;
        }
        frame_count += frame_count_flush;
    }

    audio_output = (float *)malloc(frame_count * sizeof(float));
    if (!audio_output) {
        fprintf(stderr, "Could not allocate memory for output\n");
        ret = ERROR_NOTENOUGH_MEMORY;
        goto cleanup;
    }

    memcpy(audio_output, resampled_data[0], frame_count);
    ret = write_wav(output_file, audio_output, frame_count, output_sample_rate);

cleanup:
    if (resampled_data != NULL)
        av_freep(&resampled_data[0]);
    swr_free(&swr_ctx);
    if (audio_output != NULL)
        free(audio_output);
    return ret;
}

write_wav函数代码

int write_wav(const char *filename, float *samples, int num_samples, int sample_rate) {
    FILE *file = fopen(filename, "wb");
    if (!file) {
        fprintf(stderr, "Unable to open file for writing\n");
        return ERROR_UNKNOWN;
    }

    unsigned char header[44] = {
        'R','I','F','F', // RIFF
        0,0,0,0, // Chunk size (to be filled later)
        'W','A','V','E', // WAVE
        'f','m','t',' ', // fmt
        16,0,0,0, // Subchunk1Size (16 for PCM)
        1,0, // AudioFormat (1 for PCM)
        1,0, // NumChannels (1 for mono)
        0,0,0,0, // SampleRate (to be filled later)
        0,0,0,0, // ByteRate (to be filled later)
        4,0, // BlockAlign (4 bytes per sample frame for float)
        32,0, // BitsPerSample (32 for float samples)
        'd','a','t','a', // Subchunk2ID
        0,0,0,0  // Subchunk2Size (to be filled later)
    };

    int byte_rate = sample_rate * 4;
    int subchunk2_size = num_samples * 4;
    int chunk_size = 36 + subchunk2_size;

    memcpy(header + 24, &sample_rate, 4);
    memcpy(header + 28, &byte_rate, 4);
    memcpy(header + 40, &subchunk2_size, 4);
    memcpy(header + 4, &chunk_size, 4);

    fwrite(header, sizeof(header), 1, file);

    fwrite(samples, sizeof(float), num_samples, file);

    fclose(file);
    return 0;
}

波形对比

原始音频(24000Hz)波形:
原始音频波形图

重采样后音频(48000Hz)波形:
重采样后音频波形图


问题根源与修复方案

1. 内存拷贝长度错误

重采样函数中memcpy(audio_output, resampled_data[0], frame_count);存在致命问题:frame_count是采样点数量,但每个float类型采样点占4字节,当前代码只拷贝了1/4的有效数据,剩余内存为随机值,直接导致噪音。

修复代码:

memcpy(audio_output, resampled_data[0], frame_count * sizeof(float));

2. 重采样缓存覆盖问题

flush阶段调用swr_convert时,直接使用原始缓存起始地址,会覆盖之前重采样生成的有效数据。需要从已写入的采样点后方追加数据:

修改flush循环代码:

int output_offset = frame_count; // 从已生成的采样点末尾开始追加
while (1) {
    // 传入缓存的偏移地址,避免覆盖已有数据
    int frame_count_flush = swr_convert(swr_ctx, (uint8_t **)((float *)resampled_data[0] + output_offset), output_samples_count - output_offset, NULL, 0);
    if (frame_count_flush <= 0) {
        break;
    }
    frame_count += frame_count_flush;
    output_offset += frame_count_flush;
}

3. 可选:提升重采样质量

默认重采样算法质量一般,可在swr_init前添加以下配置,启用更高质量的重采样参数:

av_opt_set_int(swr_ctx, "filter_size", 16, 0); // 增大滤波器尺寸
av_opt_set_int(swr_ctx, "cutoff", 99, 0); // 设置99%的带宽截止,保留更多高频
av_opt_set_int(swr_ctx, "linear_interp", 0, 0); // 禁用线性插值,使用更高质量算法

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:39:52