如何在将 2 个通道合并为 1 个通道时避免音频失真

问题描述 投票:0回答:1

我有一个 Mac .net 应用程序,它所需要做的就是录制音频,然后将其写入 WAV 文件。

我正在使用 PortAudio 的 C# 包装器来录制音频。我已经成功地为单通道音频录制并写入了 WAV 文件,对于两通道音频也是如此。但是,当我尝试录制两通道音频然后将其写入一通道音频时,它会失真。将 2 个通道组合为 1 个通道是通过获取交错的样本对,将它们相加,然后除以 2 来完成的。采取措施尽量避免溢出。

然而,当组合通道时,最终结果会变得扭曲。我确信这是一些基本且简单的事情,我做错了可怕的事情,但我再也找不到它了。

录制代码:


        private static readonly int _sampleRate = 44100; 
        private static int _totalSamplesWritten = 0;
        private const ushort BIT_DEPTH = 16 ;

        var param = new StreamParameters
        {
            device = _indexOfDevice,
            channelCount = device.maxInputChannels > 1 ? 2 : 1,
            sampleFormat = SampleFormat.Int16,
            suggestedLatency = device.defaultLowInputLatency,
            hostApiSpecificStreamInfo = IntPtr.Zero
        };

        StreamCallbackResult CallbackStereoInput(
            IntPtr input, 
            IntPtr output, 
            uint frameCount, 
            ref StreamCallbackTimeInfo timeInfo, 
            StreamCallbackFlags statusFlags, 
            IntPtr userData
        )
        {
            var samples = new short[frameCount];
            Marshal.Copy(input, samples, 0, (int)frameCount);
            
            for (var i = 0; i < frameCount; i++)
            {
                var sampleL = samples[i];
                var overflowSafeSampleL = Convert.ToInt32(sampleL);
                var sampleR = samples[i + 1];
                var overflowSafeSampleR = Convert.ToInt32(sampleR);
                
                var combinedSample = overflowSafeSampleL + overflowSafeSampleR;
                var dividedSample = Convert.ToInt16(combinedSample / 2);
                
                _outputFileWriter.Write(dividedSample);
                i++;
            }
            
            _totalSamplesWritten += (int)frameCount;

            return StreamCallbackResult.Continue;
        }

        _stream = new PortAudioSharp.Stream(
            inParams: param, outParams: null, 
            sampleRate: _sampleRate,
            framesPerBuffer: 256,
            streamFlags: StreamFlags.ClipOff,
            callback: param.channelCount > 1 ? CallbackStereoInput : CallbackMonoInput,
            userData: IntPtr.Zero
        );

写入WAV标题的代码:

WriteWavHeader(_outputFileWriter, 1, BIT_DEPTH, _sampleRate / 2, _totalSamplesWritten);

private static void WriteWavHeader(BinaryWriter writer, ushort channelCount, ushort bitDepth, int sampleRate, int totalSampleCount)
    {
        writer.Seek(0, SeekOrigin.Begin);
        writer.Write(Encoding.ASCII.GetBytes("RIFF"));
        writer.Write((bitDepth / 8 * totalSampleCount) + 36);
        writer.Write(Encoding.ASCII.GetBytes("WAVE"));
        writer.Write(Encoding.ASCII.GetBytes("fmt "));
        writer.Write(16);
        writer.Write((ushort)1);
        writer.Write(channelCount);
        writer.Write(sampleRate);
        writer.Write(sampleRate * channelCount * bitDepth / 8);
        writer.Write((ushort)(channelCount * bitDepth / 8));
        writer.Write(bitDepth);
        writer.Write(Encoding.ASCII.GetBytes("data"));
        writer.Write(bitDepth / 8 * totalSampleCount);
    }

我不太关心音频质量的损失,只要你能清楚、轻松地从中理解人类的语音即可。

音频失真的示例:

https://www.dropbox.com/scl/fi/5204zxtpjkqa4ewxhph0j/Audio.wav?rlkey=9urdo4s0zqtyd3hxi1oscko9b&st=w02xj8pm&dl=0

上面的代码是多次不同尝试后的终点。

  • 目前,我们写入 WAV 的采样率是录制速率的一半,但如果没有这种更改,音频长度将减半并以双倍的速度播放。
  • 采样率曾经是 16000,这似乎产生了稍微好一点的结果,但从我在网上阅读的所有内容来看,44100 似乎是更好的选择。
  • 最初,当我组合通道时它只是静态的,但是在组合它们时将样本转换为 32 位似乎有助于避免溢出。
  • 我尝试过不同的样本格式,并且尝试过非交错音频,但它似乎并没有使任何事情变得更好或更糟。
  • 它绝对可以很好地工作,因为录制 2 通道音频并将其写入为 2 通道音频效果很好,所以我相当有信心这不是硬件问题。
  • 我尝试将 2 个音频通道合并为 1 个通道的主要原因是我需要最终的 WAV 文件尽可能小。我需要良好的音频质量,并且可以节省空间,这是一个额外的好处,将 2 个通道合并为 1 个通道将是一个巨大的文件大小削减。
c# .net macos audio portaudio
1个回答
0
投票

最后还是想通了。来自 PortAudio 的样本被交错为 1 个样本。所以一个样本包含左和右,我将左和右对与其他左和右对组合起来。

我需要将“样本”数组中的各个样本分成两部分,以获得左右音频流。一旦我弄清楚了这一点,我实际上可以通过将它们加在一起并除以 2 得到单个有符号 int16,将它们平均为 1 个流。

© www.soinside.com 2019 - 2024. All rights reserved.