基于Apple Mel谱示例扩展至录制样本的矩阵计算问题排查
背景与问题
基于Apple开发者文档《Computing the Mel Spectrum Using Linear Algebra》示例开发,目标扩展至处理麦克风录制的音频样本。先通过循环分窗计算STFT,得到time_bins × window_size的矩阵(time_bins=(样本数-窗口大小)/步长+1)。
原示例针对单时间窗的FFT结果(1×window_size矩阵),用cblas_sgemm结合Mel滤波器组计算Mel谱。但修改代码批量处理多时间窗STFT矩阵后,得到的Mel谱出现**低频仅占约20%高度、高频占剩余80%**的频率分布翻转问题。
原示例代码(单时间窗)
cblas_sgemm(CblasRowMajor, CblasTrans, CblasTrans, Int32(1), Int32(self.filterbanksCount), Int32(self.windowSize), 1, fftResultPtr.baseAddress, Int32(1), filterBank.baseAddress, Int32(self.windowSize), 0, sgemmResult!.baseAddress, Int32(self.filterbanksCount) )
修改后的批量处理代码
cblas_sgemm(CblasRowMajor, //ORDER CblasNoTrans, //Transpose A? if so, op(A) = A^t, else op(A) = A CblasNoTrans, //Transpose B? if so, op(B) = B^t, else op(B) = B Int32(fftResult.count/self.windowSize), //A and C's rows. Int32(self.filterbanksCount), //B and C's cols. Int32(self.windowSize), //A's cols, B's rows 1, //Scale A and B's product fftResultPtr.baseAddress, //A Int32(self.windowSize), //rows of op(A)^t filterBank.baseAddress, //B Int32(self.filterbanksCount), //rows of op(B)^t 0, //Result scale sgemmResult!.baseAddress, //C Int32(self.filterbanksCount) //rows of C^t )
错误分析与修复方案
核心错误点
矩阵转置逻辑与维度不匹配:
原单窗代码中,fftResult是1×window_size行向量,通过CblasTrans转置为window_size×1列向量;filterBank是filterbanksCount×window_size矩阵(每行对应一个Mel滤波器,覆盖window_size个频率点),通过CblasTrans转置为window_size×filterbanksCount矩阵,相乘后得到1×filterbanksCount的Mel谱行向量,频率顺序正确。
你修改后的代码中,fftResult是time_bins×window_size矩阵,但错误设置了CblasNoTrans同时乱定义了filterBank的转置参数,导致维度匹配逻辑完全错误。filterBank的leading dimension参数错误:
Mel滤波器组通常是filterbanksCount × window_size的矩阵,原代码中filterBank的leading dimension(lda)是window_size(每行元素数),但你修改后的代码中将其设为filterbanksCount,导致矩阵元素读取顺序混乱,直接引发频率翻转。
修复后的代码
cblas_sgemm(CblasRowMajor, CblasNoTrans, // A是time_bins×window_size,无需转置 CblasTrans, // B转置:原filterBank是filterbanksCount×window_size,转置后为window_size×filterbanksCount Int32(fftResult.count / self.windowSize), // C的行数 = A的行数(time_bins) Int32(self.filterbanksCount), // C的列数 = B转置后的列数(filterbanksCount) Int32(self.windowSize), // 公共维度:A的列数 = B转置后的行数(window_size) 1, fftResultPtr.baseAddress, Int32(self.windowSize), // A的leading dimension:每行元素数(window_size) filterBank.baseAddress, Int32(self.windowSize), // B的leading dimension:原filterBank每行元素数(window_size) 0, sgemmResult!.baseAddress, Int32(self.filterbanksCount) // C的leading dimension:每行元素数(filterbanksCount) )
修复逻辑说明
- 转置设置:保持STFT矩阵(A)不转置(每行对应一个时间窗的FFT结果),将Mel滤波器组(B)转置,维度匹配为
time_bins×window_size×window_size×filterbanksCount=time_bins×filterbanksCount,每行对应一个时间窗的Mel谱。 - leading dimension修正:
filterBank的leading dimension设为原始每行元素数(window_size),确保正确读取滤波器组的元素。 - 修正后频率点顺序与原单窗逻辑一致,不会出现高低频翻转问题。
内容的提问来源于stack exchange,提问作者Baffo rasta

