我的服务器程序从客户端软件输入音频数据,该软件采用PCM Wave格式,具有正确的RIFF标题(48000 Hz,16位,可以是1或2个通道)。之后,它需要将该数据重新采样为16000 Hz,16位且仅1个通道,但有一个例外:它必须是没有任何RIFF标头的原始PCM数据,以便数据可以传输到要播放的外部服务。此外,该服务将流回相同类型的原始PCM音频数据。因此,程序也需要反向(上采样到48000Hz和RIFF波形)。
目前,我正在使用NAudio完成大部分工作,因为我需要使用二进制数据动态执行该过程,而不是将其保存到文件并处理该文件。
以下是我用于下采样并转换为raw的代码:
var processingData = GetAudioData(); // This is the input audio data in Wav format
using (Stream stream = new MemoryStream(processingData))
{
using (var wavFileReader = new WaveFileReader(stream))
{
var resampler = new WdlResamplingSampleProvider(wavFileReader.ToSampleProvider(), 16000);
var monoSource = resampler.ToMono().ToWaveProvider16();
using (var outputStream = new MemoryStream())
{
byte[] bytesOutput = new byte[monoSource.WaveFormat.AverageBytesPerSecond];
while (true)
{
int bytesRead = monoSource.Read(bytesOutput, 0, bytesOutput.Length);
if (bytesRead == 0)
break;
outputStream.Write(bytesOutput, 0, bytesRead);
}
var outputData = outputStream.ToArray(); // This is raw PCM data
}
}
}
我想知道是否有更好更有效的方法从WavProvider获取原始PCM而不是逐字节读取到MemoryStream
。例如,我尝试过ACM重采样器,它是一个WaveStream,因此很容易将流数据复制到内存流,但重采样器无法同时更改采样率和通道数。我希望这部分尽可能快,以便获得原始PCM,并以最小的延迟将其快速发送到外部服务。
同样,这是逆转过程逻辑代码:
var processingData = GetRawData(); // This is the raw PCM data
using (Stream stream = new MemoryStream(processingData))
{
using (var rawStream = new RawSourceWaveStream(stream, new WaveFormat(16000, 16, 1)))
using (var upsample = new WaveFormatConversionStream(new WaveFormat(48000, 16, 2), rawStream))
using (var outputStream = new MemoryStream())
{
WaveFileWriter.WriteWavFileToStream(outputStream, upsample.ToSampleProvider().ToWaveProvider16());
var outputData = outputStream.ToArray();
}
}
由于NAudio有一些用于生成Wave数据的实用方法,我使用WaveFileWriter.WriteWavFileToStream
方法生成所有必需的RIFF标头,但不是将其保存到文件,而是将数据传输到MemoryStream
。我仍然对兼容性有一些担忧,并且这种方式也很有效。我相信WaveFormatConversionStream
使用ACM而不是Wdl,因此它可能会对较新的服务器软件/硬件产生一些问题。无论如何,还是像以前那样使用样本Wdl进行此步骤吗?