C# NAudio:使用ASIO录制音频并用吉他输出

C# NAudio:使用ASIO录制音频并用吉他输出,c#,naudio,C#,Naudio,我目前正在从事一个SR.design项目,这是一个windows窗体应用程序,它允许用户插入吉他,实时创建失真,用输入和输出播放,并将其自动标记为ASCII标签。目前,我正在尝试让实时收听部分工作,我的录制和失真的实现工作得很好,只是在使用ASIO时遇到了一些问题。我看过这篇文章,但对我的问题没有多大帮助,以下是我的代码: private BufferedWaveProvider buffer; private AsioOut input; private AsioOut output; pr

我目前正在从事一个SR.design项目,这是一个windows窗体应用程序,它允许用户插入吉他,实时创建失真,用输入和输出播放,并将其自动标记为ASCII标签。目前,我正在尝试让实时收听部分工作,我的录制和失真的实现工作得很好,只是在使用ASIO时遇到了一些问题。我看过这篇文章,但对我的问题没有多大帮助,以下是我的代码:

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);
    }
}
private BufferedWaveProvider缓冲区;
专用输入输出;
私有输出;
私有void列表单击(对象发送方,事件参数e)
{
输入=新的AsioOut(RecordInBox.SelectedIndex);
波形格式=新的波形格式();
buffer=新的BufferedWaveProvider(格式);
buffer.DiscardOnBufferOverflow=true;
input.InitRecordAndPlayback(缓冲区,144100);
input.AudioAvailable+=新的EventHandler(AudioAvailable);
//输出=新的AsioOut(RecordInBox.SelectedIndex);
//Init(缓冲区);
input.Play();
//output.Play();
}
公共无效音频可用(对象发送器,ASIOAUDIOAvaailableEventargs e)
{
字节[]buf=新字节[e.SamplesPerBuffer];
e、 WriteToOutputBuffers=true;
for(int i=0;i

目前,它正在获取音频并将其推入缓冲区,但输出不起作用。如果我将录音设置设置为在windows上收听,我可以让它弹吉他,但我觉得这是不必要的,因为我希望能够执行我的失真,并将其作为输出来听。谢谢

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

[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;
    }
[DllImport(“Kernel32.dll”,EntryPoint=“rtlmovemory”,SetLastError=false)]
私有静态不安全外部void MoveMemory(IntPtr dest、IntPtr src、int size);
AudioAvailable上的私有void(对象发送方,AsioAudioAvailableEventArgs e)
{
for(int i=0;i

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

不幸的是,我找不到让ASIO工作的方法,但我想出了一种同样有效的替代方法,对于延迟,我将其降低到50毫秒,但一直在研究NAudio源,看看是否有办法将其降低到50毫秒以下。(大约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();
    }
private BufferedWaveProvider缓冲区;
私人退避退避;
sourceStream中的私有WaveIn=null;
私有布尔监听=假;
私有void列表单击(对象发送方,事件参数e)
{
听,听;
如果(听)
listenBtn.Text=“停止收听”;
其他的
{
listenBtn.Text=“Listen”;
sourceStream.StopRecording();
返回;
}
sourceStream=新的WaveIn();
sourceStream.WaveFormat=新的波形(44100,1);
waveOut=新的waveOut(WaveCallbackInfo.FunctionCallback());
sourceStream.DataAvailable+=新的事件处理程序(sourceStream\u DataAvailable);
sourceStream.RecordingStopped+=新事件处理程序(sourceStream\u RecordingStopped);
buffer=新的BufferedWaveProvider(sourceStream.WaveFormat);
buffer.DiscardOnBufferOverflow=true;
waveOut.DesiredLatency=51;
waveOut.容积=1f;
waveOut.Init(缓冲区);
sourceStream.StartRecording();
}
私有void sourceStream_DataAvailable(对象发送方,WaveInEventArgs e)
{
buffer.AddSamples(e.buffer,0,e.bytes记录);
waveOut.Play();
}
私有void sourceStream_RecordingStopped(对象发送方,StoppedEventArgs e)
{
Dispose();
waveOut.Dispose();
}

我也明白这不是在使用ASIO,但基于我现有的资源和文档,这是一个更好的选择。我不是在使用ASIO,而是在创建waveIn并模仿“录音”但我没有将其写入文件,而是将流放入waveOut缓冲区,在我进行一些声音处理后,该缓冲区将允许它播放。

可能我错了,但我已经成功地使用NAudio以极低的延迟(在非常便宜的USB音频硬件上)管理了同步Asio录制和播放

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

    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缓冲区大小为512个样本时,我也听不到滴落声或嗡嗡声。但我认为这实际上取决于所使用的Asio硬件。 对我来说,最重要的是确保输入和输出同步

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

目前看来,通过使用Asio接口,我已经解决了这个GC问题

希望这有帮助

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