当前位置: 首页 > ds >正文

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

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;
}
http://www.xdnf.cn/news/16807.html

相关文章:

  • 推扫式和凝视型高光谱相机分别采用哪些分光方式?
  • Web前端实战:Vue工程化+ElementPlus
  • 二叉树算法之【二叉树的层序遍历】
  • 专题:2025机器人产业技术图谱与商业化指南|附130+份报告PDF、数据汇总下载
  • Python爬虫05_Requests肯德基餐厅位置爬取
  • 小架构step系列30:多个校验注解
  • 《Spring Security源码深度剖析:Filter链与权限控制模型》
  • 文件权限值的表示方法
  • 怎样在 Vue 中定义全局方法?
  • 【C语言】深度剖析指针(二):指针与数组,字符,函数的深度关联
  • AWS VPC NAT 网关可观测最佳实践
  • 15、点云<—>深度图转换原理
  • 数据集:机器学习的基石
  • RPA软件推荐:提升企业自动化效率
  • 北京理工大学医工交叉教学实践分享(1)|如何以实践破解数据挖掘教学痛点
  • 在 Elasticsearch 8.19 和 9.1 中引入更强大、更具弹性和可观测性的 ES|QL
  • 《Vuejs设计与实现》第 12 章(组件实现原理 下)
  • 44、鸿蒙HarmonyOS Next开发:视频播放 (Video)组件和进度条 (Progress)组件的使用
  • OSS-服务端签名Web端直传+STS获取临时凭证+POST签名v4版本开发过程中的细节
  • webpack-性能优化
  • STM32CubeMX 生成时钟获取函数的分析
  • 【网络运维】 Linux:使用 Cockpit 管理服务器
  • 矩阵指数函数 e^A
  • 移动管家手机控车系统硬件安装与软件绑定设置
  • LeetCode 4:寻找两个正序数组的中位数
  • DISTILLM:迈向大型语言模型的简化蒸馏方法
  • 基于React+Express的前后端分离的个人相册管理系统
  • OpenBayes 一周速览丨Self Forcing 实现亚秒级延迟实时流视频生成;边缘AI新秀,LFM2-1.2B采用创新性架构超越传统模型
  • 爱车生活汽车GPS定位器:智能监控与安全驾驶的守护者
  • 云原生环境里的显示变革:Docker虚拟浏览器与cpolar穿透技术实战