NAudio:使用ASIO用吉他录制音频和输出

我目前正在研究SR。 设计项目是一个Windows窗体应用程序,允许用户插入他们的吉他并实时创建扭曲并使用输入和输出播放,并自动将其选中到ASCII选项卡。 目前我正在努力让实时听力部分正常工作,我对失真的记录和实现工作得很好,只是在使用ASIO时遇到了一些问题。 我看过这篇文章如何使用AsioOut录制和播放NAudio,但它对我的问题没什么帮助,这是我的代码:

private BufferedWaveProvider buffer; private AsioOut input; private AsioOut output; private void listenBtn_Click(object sender, EventArgs e) { input = new AsioOut(RecordInCbox.SelectedIndex); WaveFormat format = new WaveFormat(); buffer = new BufferedWaveProvider(format); buffer.DiscardOnBufferOverflow = true; input.InitRecordAndPlayback(buffer, 1, 44100); input.AudioAvailable += new EventHandler(AudioAvailable); //output = new AsioOut(RecordInCbox.SelectedIndex); //output.Init(buffer); input.Play(); //output.Play(); } public void AudioAvailable(object sender, AsioAudioAvailableEventArgs e) { byte[] buf = new byte[e.SamplesPerBuffer]; e.WrittenToOutputBuffers = true; for (int i = 0; i < e.InputBuffers.Length; i++) { Array.Copy(e.InputBuffers, e.OutputBuffers, 1); Marshal.Copy(e.InputBuffers[i], buf, 0, e.SamplesPerBuffer); buffer.AddSamples(buf, 0, buf.Length); } } 

目前它正在获取音频并将其推入缓冲区但输出无效。 如果我将录制设置设置为在窗口上监听,我可以让它弹吉他,但我觉得这是不必要的,因为我希望能够执行失真并将其视为输出。 谢谢!

您不需要在缓冲区中添加样本。 缓冲区仅用于确定所需的输出通道数。 我这样做了:

 [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)] private static unsafe extern void MoveMemory(IntPtr dest, IntPtr src, int size); private void OnAudioAvailable(object sender, AsioAudioAvailableEventArgs e) { for (int i = 0; i < e.InputBuffers.Length; i++) { MoveMemory(e.OutputBuffers[i], e.InputBuffers[i], e.SamplesPerBuffer * e.InputBuffers.Length); } e.WrittenToOutputBuffers = true; } 

但这样做会感觉有点延迟和一点回声,我不知道如何解决它们。 所以,如果你有任何想法我会在这里听。

不幸的是我找不到让ASIO工作的方法,但是我想出了另一种方法,它的工作原理也一样,因为延迟时间让它降到了50毫秒,但是我一直在研究NAudio的来源。看看是否有办法让它低于它。 (大约20-30毫秒)为了更好的实时游戏。

  private BufferedWaveProvider buffer; private WaveOut waveOut; private WaveIn sourceStream = null; private bool listen = false; private void listenBtn_Click(object sender, EventArgs e) { listen = !listen; if (listen) listenBtn.Text = "Stop listening"; else { listenBtn.Text = "Listen"; sourceStream.StopRecording(); return; } sourceStream = new WaveIn(); sourceStream.WaveFormat = new WaveFormat(44100, 1); waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()); sourceStream.DataAvailable += new EventHandler(sourceStream_DataAvailable); sourceStream.RecordingStopped += new EventHandler(sourceStream_RecordingStopped); buffer = new BufferedWaveProvider(sourceStream.WaveFormat); buffer.DiscardOnBufferOverflow = true; waveOut.DesiredLatency = 51; waveOut.Volume = 1f; waveOut.Init(buffer); sourceStream.StartRecording(); } private void sourceStream_DataAvailable(object sender, WaveInEventArgs e) { buffer.AddSamples(e.Buffer, 0, e.BytesRecorded); waveOut.Play(); } private void sourceStream_RecordingStopped(object sender, StoppedEventArgs e) { sourceStream.Dispose(); waveOut.Dispose(); } 

我再次明白,这不是使用ASIO,但根据我可用的资源和文档,它是一个更好的选择。 我没有使用ASIO,而只是创建waveIn并模拟“录制”,而不是将其写入文件,而是将其传输到waveOut缓冲区,这将允许它在我进行一些声音操作后播放。

可能我错了,但我已成功管理同步Asio记录和播放使用NAudio具有非常低的延迟(非常便宜的USB音频硬件;)。

您可以尝试这样做,而不是在第一个示例中使用的事件处理程序方法:

  private float[] recordingBuffer = null; private byte[] recordingByteBuffer = null; private BufferedWaveProvider bufferedWaveProvider; private BufferedSampleProvider bsp; private SampleToWaveProvider swp; // somewhere in eg constructor // set up our signal chain bufferedWaveProvider = new BufferedWaveProvider(waveFormat); //bufferedWaveProvider.DiscardOnBufferOverflow = true; bsp = new BufferedSampleProvider(waveFormat); swp = new SampleToWaveProvider(bsp); // ... private void OnAudioAvailable(object sender, AsioAudioAvailableEventArgs e) { this.recordingBuffer = BufferHelpers.Ensure(this.recordingBuffer, e.SamplesPerBuffer * e.InputBuffers.Length); this.recordingByteBuffer = BufferHelpers.Ensure(this.recordingByteBuffer, e.SamplesPerBuffer * 4 * e.InputBuffers.Length); int count = e.GetAsInterleavedSamples(this.recordingBuffer); this.bsp.CurrentBuffer = this.recordingBuffer; int count2 = this.swp.Read(this.recordingByteBuffer, 0, count * 4); bufferedWaveProvider.AddSamples(this.recordingByteBuffer, 0, this.recordingByteBuffer.Length); } 

使用类BufferedSampleProvider.cs:

 public class BufferedSampleProvider : ISampleProvider { private WaveFormat waveFormat; private float[] currentBuffer; public BufferedSampleProvider(WaveFormat waveFormat) { this.waveFormat = waveFormat; this.currentBuffer = null; } public float[] CurrentBuffer { get { return this.currentBuffer; } set { this.currentBuffer = value; } } public int Read(float[] buffer, int offset, int count) { if (this.currentBuffer != null) { if (count <= currentBuffer.Length) { for (int i = 0; i < count; i++) { buffer[i] = this.currentBuffer[i]; } return count; } } return 0; } public WaveFormat WaveFormat { get { return this.waveFormat; } } } 

我已经完成了这个(杂乱)方式,因为否则我将不得不从asio缓冲区复制字节,这取决于样本字节数等等(查看来自GetAsInterleavedSamples(...)方法的源代码)。 为了让我保持简单,我使用BufferedWaveProvider来确保在我的信号链的输出端有足够的(填充的)缓冲区,即使我不是真的需要它,但它是安全的。 在该提供者之后的几个处理块之后,链最终在最后一个提供者“输出”中。 最后一个提供商被传入

 asioOut.InitRecordAndPlayback(output, this.InputChannels, this.SampleRate); 

初始化整个对象时。 即使我在我的链中使用了很多处理块,我也没有可用的下降或嗡嗡声,asio缓冲区大小为512个样本。 但我认为这实际上取决于使用的Asio硬件。 对我来说最重要的是确保输入和输出同步。

比较:如果我以相同的方式使用WaveIn / WaveOutEvent,我可以达到几乎相同的延迟(在相同的廉价硬件上),但由于我的测试也在两个独立的声音设备上,输入缓冲持续时间在一段时间后由于一些增加丢弃或非同步音频时钟;)为了达到非常低的延迟,即使在WPF应用程序中使用时,我必须修补WaveOutEvent类以提高播放线程的优先级,这有助于“对抗”大多数可能的GC中断。

目前似乎使用Asio接口我已经解决了这个GC问题。

希望这可以帮助。