使用录制的波形文件(NAudio)

时间:2013-12-21 12:23:14

标签: c# multithreading naudio

这是我用来录制音频文件的代码:

internal class AudioRecorder
{
    public WaveIn waveSource = null;
    public WaveFileWriter waveFile = null;
    public string RECORDING_PATH;

    public AudioRecorder(string fileName)
    {
        RECORDING_PATH = fileName;
    }

    public void Start()
    {
        waveSource = new WaveIn();
        waveSource.WaveFormat = new WaveFormat(44100, 1);
        waveSource.DeviceNumber = 0;
        waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
        waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);

        waveFile = new WaveFileWriter(RECORDING_PATH, waveSource.WaveFormat);

        System.Timers.Timer t = new System.Timers.Timer(30000);

        t.Elapsed += new ElapsedEventHandler(Stop);

        waveSource.StartRecording();

        t.Start();


    }

    private void Stop(object sender, ElapsedEventArgs args)
    {
        waveSource.StopRecording();
    }

    private void waveSource_DataAvailable(object sender, WaveInEventArgs e)
    {
        if (waveFile != null)
        {
            waveFile.Write(e.Buffer, 0, e.BytesRecorded);
            waveFile.Flush();
        }
    }

    private void waveSource_RecordingStopped(object sender, StoppedEventArgs e)
    {
        if (waveSource != null)
        {
            waveSource.Dispose();
            waveSource = null;
        }

        if (waveFile != null)
        {
            waveFile.Dispose();
            waveFile = null;
        }

    }
}

在我做的主要方法中:

AudioRecorder r = new AudioRecorder(dialog.FileName);
r.Start();
FileInfo file = new FileInfo(r.RECORDING_PATH);
// Do somehting with the recorded audio //

问题在于,当我执行r.Start()时,线程不会阻塞并继续运行。所以我得到了一个损坏的文件错误。当我尝试像Thread.Sleep这样的东西让线程等到录制结束时,这次AudioRecorder代码不能正常工作(即录制永远不会完成)。

关于正确应该做什么的任何想法等待录制完成,以便我可以安全地使用录制的文件?

3 个答案:

答案 0 :(得分:1)

如果要准确记录30秒,只要有足够的数据,就可以在DataAvailable事件处理程序中调用StopRecording。绝对不需要复杂的线程策略。我在开源.NET voice recorder application中完成了这一点。

在RecordingStopped事件中处理WaveFileWriter。

如果绝对必须有阻塞调用,则使用WaveInEvent,并按照Rene的建议等待在RecordingStopped处理程序中设置的事件。通过使用WaveInEvent,您无需使用Windows消息泵即可运行。

答案 1 :(得分:0)

使用ManualResetEvent等待调用Stop事件,为其他线程提供更改以继续。 我只添加了新位......

internal class AudioRecorder
{  
    private ManualResetEvent mre = new ManualResetEvent(false);

    public void Start()
    {

        t.Start();
        while (!mre.WaitOne(200))
        {   
            // NAudio requires the windows message pump to be operational
            // this works but you better raise an event
            Application.DoEvents(); 
        }
    }

    private void Stop(object sender, ElapsedEventArgs args)
    {
        // better: raise an event from here!
        waveSource.StopRecording();
    }

    private void waveSource_RecordingStopped(object sender, EventArgs e)
    {
       /// ... your code here

       mre.Set();  // signal thread we're done!
    }

答案 2 :(得分:0)

如果不需要任何多线程代码,最好避免使用,Mark的答案就是完美地解释这一点。

但是,如果您正在编写一个Windows应用程序,并且要求记录30秒,则必须不要在等待(30秒)时阻止主线程。新的异步C#功能在这里非常方便。它将使您能够保持代码逻辑的简单性,并以非常有效的方式实现等待。

我稍微修改了您的代码,以显示在这种情况下如何使用异步功能。

这是Record方法:

    public async Task RecordFixedTime(TimeSpan span)
    {
        waveSource = new WaveIn {WaveFormat = new WaveFormat(44100, 1), DeviceNumber = 0};
        waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
        waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);

        waveFile = new WaveFileWriter(RECORDING_PATH, waveSource.WaveFormat);

        waveSource.StartRecording();
        await Task.Delay(span);
        waveSource.StopRecording();
    }

使用WPF app点击处理程序中的Record的示例:

    private async void btnRecord_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            btnRecord.IsEnabled = false;
            var fileName = Path.GetTempFileName() + ".wav";
            var recorder = new AudioRecorder(fileName);
            await recorder.RecordFixedTime(TimeSpan.FromSeconds(5));
            Process.Start(fileName);
        }
        finally
        {
            btnRecord.IsEnabled = true;
        }
    }

但是,你必须注意这里的时间安排。 Task.Delay不保证在确切的指定时间跨度后它将继续执行。您可能会获得比所需记录略长的记录。