我有一个 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);
}
我不太关心音频质量的损失,只要你能清楚、轻松地从中理解人类的语音即可。
音频失真的示例:
上面的代码是多次不同尝试后的终点。
最后还是想通了。来自 PortAudio 的样本被交错为 1 个样本。所以一个样本包含左和右,我将左和右对与其他左和右对组合起来。
我需要将“样本”数组中的各个样本分成两部分,以获得左右音频流。一旦我弄清楚了这一点,我实际上可以通过将它们加在一起并除以 2 得到单个有符号 int16,将它们平均为 1 个流。