cpp实现音频重采样8k->16k及16k->8k

发布于:2025-07-31 ⋅ 阅读:(20) ⋅ 点赞:(0)
static int convert_8khz_to_16khz(void* dst_buf, void* src_buf, int src_size) {
    short* in = static_cast<short*>(src_buf);
    short* out = static_cast<short*>(dst_buf);
    int in_samples = src_size / sizeof(short);

    // 边界处理:前两个样本
    out[0] = in[0];
    out[1] = static_cast<short>(0.75f * in[0] + 0.25f * in[1]);

    // 主处理循环(使用三次Hermite插值)
    for (int i = 1; i < in_samples - 2; ++i) {
        // 原始样本点(应用抗混叠滤波)
        out[i*2] = static_cast<short>(
            0.1f * in[i-1] + 0.8f * in[i] + 0.1f * in[i+1]
        );

        // 插值点(三次Hermite插值)
        float t = 0.5f; // 中间位置
        float y0 = in[i-1], y1 = in[i], y2 = in[i+1], y3 = in[i+2];
        float a0 = y3 - y2 - y0 + y1;
        float a1 = y0 - y1 - a0;
        float a2 = y2 - y0;
        float a3 = y1;
        float interpolated = a0*t*t*t + a1*t*t + a2*t + a3;

        out[i*2 + 1] = static_cast<short>(interpolated);
    }

    // 边界处理:最后两个样本
    out[(in_samples-2)*2] = in[in_samples-2];
    out[(in_samples-2)*2 + 1] = static_cast<short>(0.25f * in[in_samples-2] + 0.75f * in[in_samples-1]);
    out[(in_samples-1)*2] = in[in_samples-1];
    out[(in_samples-1)*2 + 1] = in[in_samples-1];

    return src_size * 2;
}

// 16kHz -> 8kHz 高质量降采样
static int convert_16khz_to_8khz(void* dst_buf, const void* src_buf, int src_size) {
    short* in = static_cast<short*>(const_cast<void*>(src_buf));
    short* out = static_cast<short*>(dst_buf);
    int in_samples = src_size / sizeof(short);

    // 边界处理
    out[0] = static_cast<short>(0.8f * in[0] + 0.2f * in[1]);

    // 主处理循环(带抗混叠滤波的降采样)
    for (int i = 1; i < in_samples / 2 - 1; ++i) {
        out[i] = static_cast<short>(
            0.1f * in[i*2-1] + 0.8f * in[i*2] + 0.1f * in[i*2+1]
        );
    }

    // 边界处理
    out[in_samples/2 - 1] = static_cast<short>(0.2f * in[in_samples-2] + 0.8f * in[in_samples-1]);

    return src_size / 2;
}

完整代码

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <windows.h>

// 获取当前可执行文件所在目录
std::string GetExeDirectory() {
    char path[MAX_PATH];
    GetModuleFileNameA(NULL, path, MAX_PATH);
    std::string exePath(path);
    size_t lastSlash = exePath.find_last_of("\\/");
    return exePath.substr(0, lastSlash + 1);
}

// 获取项目根目录
std::string GetProjectRoot() {
    std::string exeDir = GetExeDirectory();
    size_t debugPos = exeDir.find("cmake-build-debug");
    if (debugPos != std::string::npos) {
        return exeDir.substr(0, debugPos);
    }
    return exeDir;
}

// 读取PCM文件
std::vector<short> ReadPCMFile(const std::string &filename) {
    std::ifstream file(filename, std::ios::binary | std::ios::ate);
    if (!file) {
        throw std::runtime_error("无法打开文件: " + filename);
    }

    std::streamsize size = file.tellg();
    file.seekg(0, std::ios::beg);

    if (size % sizeof(short) != 0) {
        throw std::runtime_error("文件大小不是16-bit PCM的整数倍");
    }

    std::vector<short> buffer(size / sizeof(short));
    if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
        throw std::runtime_error("读取文件失败");
    }

    return buffer;
}

// 写入PCM文件
void WritePCMFile(const std::string &filename, const std::vector<short> &data) {
    std::ofstream file(filename, std::ios::binary);
    if (!file) {
        throw std::runtime_error("无法创建文件: " + filename);
    }
    file.write(reinterpret_cast<const char *>(data.data()), data.size() * sizeof(short));
}

static int convert_8khz_to_16khz(void *dst_buf, void *src_buf, int src_size) {
    short *in = static_cast<short *>(src_buf);
    short *out = static_cast<short *>(dst_buf);
    int in_samples = src_size / sizeof(short);

    // 边界处理:前两个样本
    out[0] = in[0];
    out[1] = static_cast<short>(0.75f * in[0] + 0.25f * in[1]);

    // 主处理循环(使用三次Hermite插值)
    for (int i = 1; i < in_samples - 2; ++i) {
        // 原始样本点(应用抗混叠滤波)
        out[i * 2] = static_cast<short>(
            0.1f * in[i - 1] + 0.8f * in[i] + 0.1f * in[i + 1]
        );

        // 插值点(三次Hermite插值)
        float t = 0.5f; // 中间位置
        float y0 = in[i - 1], y1 = in[i], y2 = in[i + 1], y3 = in[i + 2];
        float a0 = y3 - y2 - y0 + y1;
        float a1 = y0 - y1 - a0;
        float a2 = y2 - y0;
        float a3 = y1;
        float interpolated = a0 * t * t * t + a1 * t * t + a2 * t + a3;

        out[i * 2 + 1] = static_cast<short>(interpolated);
    }

    // 边界处理:最后两个样本
    out[(in_samples - 2) * 2] = in[in_samples - 2];
    out[(in_samples - 2) * 2 + 1] = static_cast<short>(0.25f * in[in_samples - 2] + 0.75f * in[in_samples - 1]);
    out[(in_samples - 1) * 2] = in[in_samples - 1];
    out[(in_samples - 1) * 2 + 1] = in[in_samples - 1];

    return src_size * 2;
}

// 16kHz -> 8kHz 高质量降采样
static int convert_16khz_to_8khz(void *dst_buf, const void *src_buf, int src_size) {
    short *in = static_cast<short *>(const_cast<void *>(src_buf));
    short *out = static_cast<short *>(dst_buf);
    int in_samples = src_size / sizeof(short);

    // 边界处理
    out[0] = static_cast<short>(0.8f * in[0] + 0.2f * in[1]);

    // 主处理循环(带抗混叠滤波的降采样)
    for (int i = 1; i < in_samples / 2 - 1; ++i) {
        out[i] = static_cast<short>(
            0.1f * in[i * 2 - 1] + 0.8f * in[i * 2] + 0.1f * in[i * 2 + 1]
        );
    }

    // 边界处理
    out[in_samples / 2 - 1] = static_cast<short>(0.2f * in[in_samples - 2] + 0.8f * in[in_samples - 1]);

    return src_size / 2;
}

std::vector<short> ResamplePCM(const std::vector<short> &input,
                               unsigned int inputRate,
                               unsigned int outputRate) {
    if (inputRate == outputRate) {
        return input;
    }

    std::vector<short> output;

    if (inputRate == 16000 && outputRate == 8000) {
        output.resize(input.size() / 2);
        convert_16khz_to_8khz(output.data(), input.data(), input.size() * sizeof(short));
    } else if (inputRate == 8000 && outputRate == 16000) {
        output.resize(input.size() * 2);
        convert_8khz_to_16khz(output.data(), (void *) input.data(), input.size() * sizeof(short));
    } else {
        throw std::runtime_error("仅支持8k<->16k的采样率转换");
    }

    return output;
}

int main() {
    try {
        // 获取项目根目录
        std::string projectRoot = GetProjectRoot();

        // 构造完整文件路径
        std::string inputFile = projectRoot + "bt_pcm_8k.pcm";
        std::string outputFile = projectRoot + "16k.pcm";

        const unsigned int inputRate = 8000;
        const unsigned int outputRate = 16000;

        // 打印完整路径用于调试
        std::cout << "输入文件路径: " << inputFile << std::endl;
        std::cout << "输出文件路径: " << outputFile << std::endl;

        // 读取PCM文件
        std::cout << "正在读取文件..." << std::endl;
        auto pcmData = ReadPCMFile(inputFile);

        // 重采样
        std::cout << "正在重采样: " << inputRate << "Hz -> " << outputRate << "Hz\n";
        auto resampledData = ResamplePCM(pcmData, inputRate, outputRate);

        // 写入文件
        std::cout << "正在写入文件..." << std::endl;
        WritePCMFile(outputFile, resampledData);

        std::cout << "处理完成! 输出文件已保存为: " << outputFile << std::endl;
    } catch (const std::exception &e) {
        std::cerr << "错误: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到