简单线性重采样
/// 简单线性重采样(单声道16位)
- (NSData *)resamplePCMData:(NSData *)pcmData
fromSampleRate:(double)srcRate
toSampleRate:(double)dstRate {
// 参数校验
if (!pcmData || pcmData.length < 2 || srcRate <= 0 || dstRate <= 0) {
return [NSData data];
}
if (srcRate == dstRate) return pcmData;
// 计算采样点数量(16位单声道)
NSUInteger srcCount = pcmData.length / 2;
NSUInteger dstCount = (NSUInteger)ceil((double)srcCount * dstRate / srcRate);
if (dstCount == 0) return [NSData data];
// 准备缓冲区
const int16_t *srcPCM = (const int16_t *)pcmData.bytes;
int16_t *dstPCM = malloc(sizeof(int16_t) * dstCount);
if (!dstPCM) return [NSData data];
// 使用定点数运算提高效率(16位小数精度)
uint64_t step = ((uint64_t)srcCount << 32) / dstCount; // 64位步长防止溢出
uint64_t current = 0;
const NSUInteger lastIndex = srcCount - 1;
for (NSUInteger i = 0; i < dstCount; i++) {
uint32_t pos = (uint32_t)(current >> 32); // 高32位作为整数位置
uint32_t frac = (uint32_t)(current >> 16) & 0xFFFF; // 中16位作为小数部分
// 边界安全处理
if (pos >= lastIndex) {
dstPCM[i] = srcPCM[lastIndex];
} else {
int32_t s1 = srcPCM[pos];
int32_t s2 = srcPCM[pos + 1];
// 定点数插值: s1 + (s2 - s1) * frac
int32_t val = s1 + (((s2 - s1) * (int32_t)frac) >> 16);
// 限制在16位有符号范围内
dstPCM[i] = (int16_t)MAX(INT16_MIN, MIN(INT16_MAX, val));
}
current += step;
}
NSData *result = [NSData dataWithBytes:dstPCM length:dstCount * sizeof(int16_t)];
free(dstPCM);
return result;
}
优化进阶版 这个方案包含:
- 抗混叠滤波器:防止高频失真
- 汉明窗:减少振铃效应
- 边界镜像:正确处理音频边界
- 整形滤波:保证输出质量
#define MAX_FILTER_LENGTH 256
- (NSData *)resamplePCMData:(NSData *)pcmData
fromSampleRate:(double)srcRate
toSampleRate:(double)dstRate {
// 参数验证
if (!pcmData || srcRate <= 0 || dstRate <= 0) return nil;
if (srcRate == dstRate) return pcmData;
// 1. 准备参数
const int16_t *src = (const int16_t *)pcmData.bytes;
size_t srcCount = pcmData.length / sizeof(int16_t);
// 2. 计算输出大小
size_t dstCount = ceil(srcCount * dstRate / srcRate);
NSMutableData *outputData = [NSMutableData dataWithLength:dstCount * sizeof(int16_t)];
int16_t *dst = outputData.mutableBytes;
// 3. 计算重采样比例
double ratio = srcRate / dstRate;
double pos = 0.0;
// 4. 创建抗混叠滤波器
const size_t filterLength = 64; // 滤波器长度
float filter[MAX_FILTER_LENGTH] = {0};
[self createLowPassFilter:filter length:filterLength cutoff:MIN(0.45, 0.45 * MIN(1.0, ratio))];
// 5. 执行高质量重采样
for (size_t i = 0; i < dstCount; i++) {
// 计算当前采样位置
double floatIndex = pos;
size_t baseIndex = (size_t)floatIndex;
double frac = floatIndex - baseIndex;
// 应用FIR滤波器
double sum = 0.0;
for (int j = 0; j < filterLength; j++) {
int index = (int)baseIndex - filterLength/2 + j;
// 边界处理
if (index < 0) {
// 镜像边界
index = -index;
} else if (index >= srcCount) {
// 镜像边界
index = 2 * srcCount - index - 2;
}
if (index >= 0 && index < srcCount) {
sum += src[index] * filter[j];
}
}
// 存储结果并限制范围
int32_t sample = (int32_t)round(sum);
dst[i] = (int16_t)MAX(-32768, MIN(32767, sample));
// 更新位置
pos += ratio;
}
return outputData;
}
- (void)createLowPassFilter:(float *)filter length:(size_t)length cutoff:(double)cutoff {
// 创建sinc函数滤波器
for (size_t n = 0; n < length; n++) {
double x = n - (length-1)/2.0;
if (x == 0) {
filter[n] = 2.0 * M_PI * cutoff; // 避免除以零
} else {
filter[n] = sin(2.0 * M_PI * cutoff * x) / (M_PI * x);
}
}
// 应用汉明窗减少振铃
for (size_t n = 0; n < length; n++) {
double window = 0.54 - 0.46 * cos(2.0 * M_PI * n / (length-1));
filter[n] *= window;
}
// 归一化滤波器
float sum = 0.0;
for (size_t n = 0; n < length; n++) {
sum += filter[n];
}
for (size_t n = 0; n < length; n++) {
filter[n] /= sum;
}
}