如何使用NAudio的Mp3FileReader读取MP3文件并将原始PCM数据存入数组?
如何使用NAudio的Mp3FileReader读取MP3文件并将原始PCM数据存入数组?
嘿,我来帮你把这个问题捋清楚,你遇到的坑其实很多刚用NAudio的开发者都会踩~
首先说你第一段代码为啥读不出正确数据,甚至返回0,核心有两个问题:
- 格式完全不匹配:CD级别的MP3解码后是16位整数PCM格式,每个样本只占2字节,你却强行拿8字节去调用
BitConverter.ToSingle——这方法只需要4字节,而且原始PCM根本不是浮点格式,结果自然是无效的,甚至会得到0。 - Position属性理解错了:
Mp3FileReader的Position是基于MP3帧的,不是普通文件的字节偏移,你直接设置Position = x根本不会定位到你想要的字节位置,它会自动对齐到最近的MP3帧开头,这也会导致你读不到预期的数据。
那正确用Mp3FileReader读取原始PCM数据应该怎么写?看下面的代码,我给你注释清楚了:
using NAudio.Wave; // 读取MP3并获取原始16位PCM数据 using (var mp3Reader = new Mp3FileReader("你的音频文件.mp3")) { // 先获取PCM的格式信息:比特率、声道数 int bytesPerSample = mp3Reader.WaveFormat.BitsPerSample / 8; // 16位的话就是2字节 int channelCount = mp3Reader.WaveFormat.Channels; int bytesPerFrame = bytesPerSample * channelCount; // 每帧(左右声道各一个样本)的字节数 // 计算总样本数,分配对应的short数组(因为16位PCM对应C#的short类型) long totalSampleCount = (mp3Reader.Length / bytesPerFrame) * channelCount; short[] rawPcmSamples = new short[totalSampleCount]; byte[] tempBuffer = new byte[4096]; // 用4096作为缓冲区,流式读取更高效 int bytesRead; int currentSampleIndex = 0; while ((bytesRead = mp3Reader.Read(tempBuffer, 0, tempBuffer.Length)) > 0) { // 把读取到的PCM字节转成short数组 for (int i = 0; i < bytesRead; i += bytesPerSample) { rawPcmSamples[currentSampleIndex++] = BitConverter.ToInt16(tempBuffer, i); } } // 现在rawPcmSamples里就是完整的原始16位PCM数据啦 }
再来说你找到的那段AudioFileReader的代码为啥能返回数据——因为AudioFileReader是NAudio提供的上层封装类,它会自动处理所有音频格式的解码,包括MP3,而且会统一输出32位浮点格式的PCM数据(样本值范围是-1.0到1.0)。它的Read方法直接返回解码后的PCM字节流,所以你能读到数据,但那段代码其实有个小问题:
原代码里把byte数组转成short数组是错误的!因为AudioFileReader输出的是32位浮点PCM,每个样本占4字节,转成short(2字节)会把浮点数据拆成无效的短整数,完全不是正确的PCM样本值。正确的写法应该是转成float数组:
using NAudio.Wave; using (var audioReader = new AudioFileReader("你的音频文件.mp3")) { // AudioFileReader输出32位浮点PCM,每个样本4字节 int bytesPerSample = 4; int totalBytes = (int)audioReader.Length; byte[] buffer = new byte[totalBytes]; int actualBytesRead = audioReader.Read(buffer, 0, totalBytes); // 转成float数组才是正确的 float[] pcmSamples = new float[actualBytesRead / bytesPerSample]; Buffer.BlockCopy(buffer, 0, pcmSamples, 0, actualBytesRead); // 现在pcmSamples里就是归一化后的浮点PCM样本 }
总结一下:
- 用
Mp3FileReader的话,要严格按照它输出的PCM格式(通常是16位整数)来处理字节,转成short数组。 - 用
AudioFileReader的话,它帮你做了格式统一,直接转成float数组就能得到正确的归一化PCM数据。 - 你之前的代码失败,核心就是没搞懂PCM的格式要求,以及
Mp3FileReader的Position属性的特殊性。
内容来源于stack exchange
相关产品推荐
相关产品推荐

