WinUI3项目中基于NAudio+ASIO4ALL的录音代码失效,请求修复方案
WinUI3项目中基于NAudio+ASIO4ALL的录音代码失效,请求修复方案
我仔细看了你的问题描述和代码,核心问题出在ASIO音频回调的过时API使用、非交错音频数据处理逻辑,还有WinUI3环境下的细节适配上。下面给你一步步梳理修复方案,确保能正常录制声音:
问题根源分析
- 原代码里的
GetAsInterleavedSamples()是NAudio的过时方法,ASIO的音频数据本质是非交错格式(每个通道单独存储数据),直接调用这个方法容易出现数据读取错误; - 没有确保音频文件写入格式和ASIO设备的实际格式完全匹配(采样率、通道数、位深度不匹配会导致空文件或噪音);
- WinUI3的文件选择器需要手动绑定窗口句柄才能正常弹出,原代码缺失这一步。
完整修复代码
补全并修改后的C#代码(MainWindow.xaml.cs)
using System; using System.Collections.Generic; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using NAudio.Wave; using System.Threading.Tasks; using Windows.Storage.Pickers; using Windows.Storage; namespace WinUI_3_AsioRecordingPanel { public sealed partial class MainWindow : Window { private AsioOut _asioOut; private WaveFileWriter _waveWriter; private StorageFile _outputFile; private WaveFormat _asioWaveFormat; public MainWindow() { this.InitializeComponent(); LoadAsioDevices(); } // 加载系统中所有可用的ASIO设备 private void LoadAsioDevices() { var asioDeviceNames = AsioOut.GetDriverNames(); foreach (var name in asioDeviceNames) { comboBoxAsioDevice.Items.Add(name); } if (asioDeviceNames.Length > 0) { comboBoxAsioDevice.SelectedIndex = 0; } } // 切换ASIO设备时重新初始化资源 private void comboBoxAsioDevice_SelectionChanged(object sender, SelectionChangedEventArgs e) { // 释放旧设备资源,防止内存泄漏 if (_asioOut != null) { _asioOut.Stop(); _asioOut.Dispose(); _asioOut = null; } var selectedDevice = comboBoxAsioDevice.SelectedItem as string; if (string.IsNullOrEmpty(selectedDevice)) return; _asioOut = new AsioOut(selectedDevice); // ASIO默认输出32位浮点样本,严格匹配格式 _asioWaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(_asioOut.SampleRate, _asioOut.DriverInputChannelCount); // 注册音频数据回调事件 _asioOut.AudioAvailable += OnAsioOutAudioAvailable; } // 选择录音输出文件 private async void fileButton_Click(object sender, RoutedEventArgs e) { var savePicker = new FileSavePicker(); // WinUI3必须绑定窗口句柄才能正常弹出文件选择器 var windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this); WinRT.Interop.InitializeWithWindow.Initialize(savePicker, windowHandle); savePicker.SuggestedStartLocation = PickerLocationId.Desktop; savePicker.FileTypeChoices.Add("WAV 音频文件", new List<string> { ".wav" }); savePicker.SuggestedFileName = "ASIO_录音文件"; _outputFile = await savePicker.PickSaveFileAsync(); if (_outputFile != null) { OnButtonStart.IsEnabled = true; } } // 开始录音 private void OnButtonStart_Click(object sender, RoutedEventArgs e) { if (_asioOut == null || _outputFile == null) return; int channelCount; int channelOffset; // 校验输入的通道数和偏移量有效性 if (!int.TryParse(textBoxChannelCount.Text, out channelCount) || !int.TryParse(textBoxChannelOffset.Text, out channelOffset)) { // 这里可以加个提示弹窗,比如用ContentDialog提示输入无效 return; } // 初始化音频文件写入器,格式严格匹配ASIO设备 _waveWriter = new WaveFileWriter(_outputFile.Path, _asioWaveFormat); // ASIO是全双工驱动,用Play方法启动录音流程 _asioOut.InitRecordAndPlayback(null, channelCount, channelOffset); _asioOut.Play(); OnButtonStart.IsEnabled = false; OnButtonStop.IsEnabled = true; } // 停止录音 private void OnButtonStop_Click(object sender, RoutedEventArgs e) { if (_asioOut != null) { _asioOut.Stop(); // 确保缓存数据完全写入文件,避免文件损坏 _waveWriter?.Flush(); _waveWriter?.Dispose(); _waveWriter = null; } OnButtonStart.IsEnabled = true; OnButtonStop.IsEnabled = false; } // 打开ASIO4ALL控制面板 private void OnButtonControlPanel_Click(object sender, RoutedEventArgs e) { _asioOut?.ShowControlPanel(); } // 修复后的ASIO音频数据回调处理 private void OnAsioOutAudioAvailable(object sender, AsioAudioAvailableEventArgs e) { if (_waveWriter == null) return; // 将ASIO的非交错音频数据转换为交错格式(适配标准WAV文件) var interleavedSamples = new float[e.SamplesPerBuffer * e.InputChannelCount]; int bufferOffset = 0; // 遍历每个输入通道,合并成交错缓冲区 for (int channel = 0; channel < e.InputChannelCount; channel++) { var channelBuffer = e.InputBuffers[channel]; for (int sample = 0; sample < e.SamplesPerBuffer; sample++) { interleavedSamples[bufferOffset + channel] = channelBuffer[sample]; bufferOffset += e.InputChannelCount; } } // 写入处理后的音频数据到文件 _waveWriter.WriteSamples(interleavedSamples, 0, interleavedSamples.Length); } } }
关键修改点说明
替换过时API,正确处理非交错音频:
抛弃了过时的GetAsInterleavedSamples(),手动实现非交错到交错的音频数据转换——这是ASIO录音的核心,因为ASIO驱动的音频数据是按通道独立存储的,必须合并成交错格式才能写入标准WAV文件。严格匹配音频格式:
用WaveFormat.CreateIeeeFloatWaveFormat初始化文件写入器,因为ASIO设备默认输出32位浮点样本,格式不匹配会导致录到空文件或噪音。适配WinUI3的文件选择器:
补充了窗口句柄绑定代码,解决WinUI3中文件选择器无法弹出的问题。完善资源释放逻辑:
在停止录音时确保WaveFileWriter的缓存数据完全写入文件,避免文件损坏;切换设备时及时释放旧的ASIO设备资源,防止内存泄漏。
额外注意事项
- 打开ASIO4ALL控制面板,确保你要录音的输入设备(比如麦克风、线路输入)已经被激活,并且设置为默认输入;
- 测试时尽量用低延迟的ASIO配置,避免音频卡顿;
- 如果还是录不到声音,可以检查
textBoxChannelCount的数值是否超过ASIO设备的实际输入通道数,比如ASIO4ALL显示有2个输入通道,就不要填3。
内容来源于stack exchange




