NAudio ASIO录制MIDI键盘音频仅存噪声问题求助
解决ASIO录制保存文件全是噪声的问题
看起来你遇到的核心问题是音频数据的格式转换和字节长度计算错误——监听正常是因为你直接把输入缓冲区的数据原封不动复制到输出缓冲区,但写入文件时没处理ASIO的浮点格式,还搞错了字节长度,最终导致文件数据错位产生噪声。咱们一步步来修正:
问题分析
你的代码里有几个关键错误:
- 你设置的WaveFormat是
48000Hz、16位、1声道,每个音频样本占2字节,但你用了e.SamplesPerBuffer *4计算缓冲区大小,相当于按32位双声道处理,直接导致数据错位。 - ASIO的输入输出缓冲区默认是32位浮点格式(数值范围[-1,1]),但你要保存的Wave文件是16位有符号整数格式,直接复制浮点数据到字节数组会变成乱码噪声。
- 循环处理多缓冲区的逻辑对单声道场景没必要,反而可能引入潜在问题。
修正后的代码
private AsioOut asioOut; private WaveFileWriter asioFile; private BufferedWaveProvider wavprov; void asioStartPlaying(object sender, EventArgs e) { // 确认设备索引2是你要使用的ASIO设备 asioOut = new AsioOut(2); // 明确指定Wave格式:48kHz、16位、单声道 wavprov = new BufferedWaveProvider(new WaveFormat(48000, 16, 1)); asioOut.AudioAvailable += asio_DataAvailable; // InitRecordAndPlayback参数:播放提供器、播放通道数、录制通道数(单声道都设为1) asioOut.InitRecordAndPlayback(wavprov, 1, 1); asioOut.Play(); AsioRecordPath = filepath.Text; asioFile = new WaveFileWriter(GetAsioFileName(), wavprov.WaveFormat); button1.Enabled = false; } void asio_DataAvailable(object sender, AsioAudioAvailableEventArgs e) { // 16位单声道:每个样本占2字节,总字节数=样本数×2 int byteCount = e.SamplesPerBuffer * 2; byte[] int16Buf = new byte[byteCount]; if (e.InputBuffers.Length > 0 && e.OutputBuffers.Length > 0) { // 1. 从ASIO输入缓冲区读取32位浮点数据 float[] floatInput = new float[e.SamplesPerBuffer]; Marshal.Copy(e.InputBuffers[0], floatInput, 0, e.SamplesPerBuffer); // 2. 把32位浮点转成16位有符号整数(Wave文件的标准格式) for (int i = 0; i < floatInput.Length; i++) { // 先把浮点值限制在[-1,1]范围内,再转换为Int16 float clampedSample = Math.Clamp(floatInput[i], -1f, 1f); short intSample = (short)(clampedSample * short.MaxValue); // 按小端格式把Int16拆成两个字节(Wave文件默认小端) int16Buf[i * 2] = (byte)(intSample & 0xFF); int16Buf[i * 2 + 1] = (byte)(intSample >> 8); } // 3. 把转换后的数据转回浮点,写入输出缓冲区实现监听 float[] floatOutput = new float[e.SamplesPerBuffer]; for (int i = 0; i < floatOutput.Length; i++) { short intSample = (short)(int16Buf[i * 2] | (int16Buf[i * 2 + 1] << 8)); floatOutput[i] = intSample / (float)short.MaxValue; } Marshal.Copy(floatOutput, 0, e.OutputBuffers[0], e.SamplesPerBuffer); } // 4. 把正确格式的16位数据写入Wave文件 asioFile.Write(int16Buf, 0, byteCount); asioFile.Flush(); e.WrittenToOutputBuffers = true; } // 记得在停止录制时释放资源,避免内存泄漏 void asio_StoppedRecording(object sender, StoppedEventArgs e) { asioFile?.Dispose(); asioOut?.Dispose(); button1.Enabled = true; }
关键修改点说明
- 字节长度修正:根据16位单声道的格式,将缓冲区大小从
e.SamplesPerBuffer *4改为e.SamplesPerBuffer *2,确保数据长度匹配。 - 格式转换:新增了32位浮点到16位整数的转换逻辑,这是解决噪声问题的核心——ASIO的原始数据是浮点,直接存成16位Wave文件必须做格式转换。
- 通道简化:针对单声道场景直接处理第一个缓冲区,去掉多余的循环逻辑。
- 资源释放:添加了停止录制时的资源释放代码,防止程序运行中出现内存泄漏。
另外还要注意:
- 确认ASIO设备索引2对应的设备确实连接了MIDI键盘的音频输出。
- 检查
GetAsioFileName()返回的路径是否有写入权限,避免文件写入失败。
内容的提问来源于stack exchange,提问作者someoneunimportant
相关产品推荐
相关产品推荐

