You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MAUI 8中使用NAudio混音录制音频出现失真问题求助

解决NAudio混音音频失真问题

核心问题分析

  • Resampler被提前释放:代码中用using包裹micResampler和speakerResampler,导致创建混音器后立即释放资源,后续录制过程无法正常处理音频数据。
  • 扬声器数据流未关联缓冲提供者:WasapiLoopbackCapture未绑定DataAvailable事件,扬声器音频数据从未写入speakerProvider,混音时数据异常。
  • 音频叠加导致削波失真:两个音频流直接叠加可能超出float音频的幅值范围(-1.0到1.0),引发失真。
  • 资源释放时机错误:提前释放关键处理组件,而非在录制停止时统一清理。

修复后的代码

private BufferedWaveProvider _micProvider;
private BufferedWaveProvider _speakerProvider;
private MediaFoundationResampler _micResampler;
private MediaFoundationResampler _speakerResampler;
private MixingSampleProvider _mixer;
private bool _isRecording = false;

public void StartRecording(int micIndex, int speakerIndex)
{
    var devices = new MMDeviceEnumerator().EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active);
    var selectedDevice = devices[speakerIndex];

    _captureSpeaker = new WasapiLoopbackCapture(selectedDevice)
    {
        WaveFormat = new WaveFormat(48000, 32, 2)
    };
    _captureSpeaker.DataAvailable += OnSpeakerDataAvailable;
    _captureSpeaker.RecordingStopped += (s, e) =>
    {
        _captureSpeaker?.Dispose();
    };

    _waveIn = new WaveInEvent
    {
        DeviceNumber = micIndex,
        WaveFormat = new WaveFormat(48000, 32, 2)
    };

    _waveIn.DataAvailable += OnDataAvailable;
    _waveIn.RecordingStopped += OnRecordingStopped;

    _micProvider = new BufferedWaveProvider(_waveIn.WaveFormat);
    _speakerProvider = new BufferedWaveProvider(_captureSpeaker.WaveFormat);
    // 设置缓冲时长避免数据溢出
    _micProvider.BufferDuration = TimeSpan.FromSeconds(5);
    _speakerProvider.BufferDuration = TimeSpan.FromSeconds(5);

    var targetFormat = WaveFormat.CreateIeeeFloatWaveFormat(48000, 2);
    // 改为类成员变量,避免提前释放
    _micResampler = new MediaFoundationResampler(_micProvider, targetFormat) { ResamplerQuality = 30 };
    _speakerResampler = new MediaFoundationResampler(_speakerProvider, targetFormat) { ResamplerQuality = 30 };

    // 调整音量并添加限幅器防止削波
    var micSample = _micResampler.ToSampleProvider().VolumeSampleProvider(0.707f);
    var speakerSample = _speakerResampler.ToSampleProvider().VolumeSampleProvider(0.707f);
    var limitedMic = new SampleProviderConverters.LimitingSampleProvider(micSample);
    var limitedSpeaker = new SampleProviderConverters.LimitingSampleProvider(speakerSample);
    
    _mixer = new MixingSampleProvider(new[] { limitedMic, limitedSpeaker });
    _writer = new WaveFileWriter(_outputFilePath, _mixer.WaveFormat);
    _isRecording = true;

    // 启动独立线程处理混音写入,避免阻塞录制流程
    Task.Run(() =>
    {
        var buffer = new float[_mixer.WaveFormat.SampleRate * _mixer.WaveFormat.Channels];
        while (_isRecording && (read = _mixer.Read(buffer, 0, buffer.Length)) > 0)
        {
            _writer.WriteSamples(buffer, 0, read);
        }
    });

    _waveIn.StartRecording();
    _captureSpeaker.StartRecording();
}

private void OnDataAvailable(object sender, WaveInEventArgs e)
{
    _micProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);
}

private void OnSpeakerDataAvailable(object sender, WaveInEventArgs e)
{
    _speakerProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);
}

private void OnRecordingStopped(object sender, StoppedEventArgs e)
{
    _isRecording = false;
    _waveIn?.Dispose();
    _captureSpeaker?.StopRecording();
    _micResampler?.Dispose();
    _speakerResampler?.Dispose();
    _writer?.Flush();
    _writer?.Dispose();
}

关键修改说明

  • 移除Resampler的using块:将处理组件改为类成员,在录制停止时统一释放,保证录制全程资源可用。
  • 绑定扬声器数据事件:新增OnSpeakerDataAvailable方法,将扬声器音频数据写入缓冲提供者,确保混音器能获取到完整数据流。
  • 添加音量控制与限幅器:通过VolumeSampleProvider给每个音频流降3dB(0.707f),再用LimitingSampleProvider限制幅值范围,彻底解决削波失真。
  • 独立线程处理混音写入:避免混音操作阻塞录制线程,保证音频数据实时处理。
  • 优化缓冲设置:设置合理的缓冲时长,防止数据溢出或丢失。

内容的提问来源于stack exchange,提问作者shri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 20:46:18