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

时间:2014-03-20 19:44:32

标签: c# naudio

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

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<AsioAudioAvailableEventArgs>(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);
    }
}

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

3 个答案:

答案 0 :(得分:1)

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

[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;
    }

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

答案 1 :(得分:0)

不幸的是我找不到让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<WaveInEventArgs>(sourceStream_DataAvailable);
        sourceStream.RecordingStopped += new EventHandler<StoppedEventArgs>(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并嘲笑&#34;录制&#34;但是不是把它写到文件中,而是将数据流写入wave并将其推入waveOut缓冲区,这将允许它在我进行一些声音操作后播放。

答案 2 :(得分:0)

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

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

    private float[] recordingBuffer = null;
    private byte[] recordingByteBuffer = null;

    private BufferedWaveProvider bufferedWaveProvider;
    private BufferedSampleProvider bsp;
    private SampleToWaveProvider swp;



    // somewhere in e.g. 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确保我的信号链的输出端有足够的(填充的)缓冲区,即使我并不真的需要它,但它是安全的。 在该提供者之后的几个处理块之后,链终止于最后一个提供者&#34;输出&#34;。最后一个提供者被传递到

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

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

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

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

希望这有帮助。