使用NAudio或CSCore录制音频'剪辑'

时间:2018-04-03 15:07:33

标签: c# naudio cscore

我开发了一个应用程序,允许用户指示以后由其他用户转录的信息。对于录音,我一直在使用NAudio和CSCore库(单独,不在一起 - 我将在下面详细解释)。

我在罐头的第一次踢是使用NAudio,我相对成功。但是,每隔一段时间,应用程序就会崩溃,并显示类似于下面的消息:

  

由于.NET运行时在IP 791F9AAA(79140000)处出现内部错误而导致该进程终止,退出代码为80131506.

令人沮丧的是,我无法在我的开发机器上重现崩溃。由于这些是与.NET运行时内部相关的硬崩溃,因此很难准确诊断导致这些崩溃的原因,但我相信我对NAudio的使用可能会导致问题。

所以...我将库切换到CSCore,崩溃问题自行解决。很好,但我还有一个问题 - 在处理冗长的口述时,我遇到了OutOfMemory异常。最初我将录制的音频内容存储在MemoryStream中,因此我将代码更改为使用FileStream,并写入磁盘而不是将录制的音频保留在内存中。

这解决了我所看到的所有OutOfMemory异常,但是这引入了一个新问题 - 音频现在是'剪辑'。我不确定剪辑是否充分描述了这个问题,但基本上我所看到的(或者更确切地说是听到的)是录制的音频绊倒自身并且部分音频完全丢失。再一次,我没有在我的开发机器上看到这个问题,只是在实际使用该应用程序的工作站上。

作为一项实验,我切换回NAudio(仍使用FileStreams而不是MemoryStreams),发现音频剪辑问题几乎已修复。一般来说,问题似乎并不存在,但我们仍然看到音频剪辑的情况。 .NET运行时中的内部错误问题仍然存在。

总结:

n音讯:

  • 有时会导致与.NET Runtime中的内部错误相关的崩溃
  • 不会像CSCore那样经历音频剪辑,但问题仍然存在

CSCore:

  • 没有造成任何崩溃,但是由于切换到使用FileStream而不是MemoryStream,因此音频剪辑问题非常

我找不到其他人遇到这些问题的案例。有任何一个图书馆经验的人都有一些关于我出错的地方吗?或者,是否有另一种使用.NET录制音频的方法?

编辑:

这是我的WaveRecorder控件的代码,其中处理所有录制。请注意,我正在使用WPF。

partial class WaveRecorder : UserControl
{
    const int samplesPerPoint = 50;
    const int numberOfPoints = 2000;

    private PowerMic powerMic;

    private IWaveIn waveIn;
    private WaveFileWriter writer;

    public bool IsListening
    {
        get { return (bool)GetValue(IsListeningProperty); }
        set { SetValue(IsListeningProperty, value); }
    }
    public static readonly DependencyProperty IsListeningProperty =
        DependencyProperty.Register("IsListening", typeof(bool), typeof(WaveRecorder), new PropertyMetadata(false));



    private PointCollection Points
    {
        get { return (PointCollection)GetValue(PointsProperty); }
        set { SetValue(PointsProperty, value); }
    }
    private static readonly DependencyProperty PointsProperty =
        DependencyProperty.Register("Points", typeof(PointCollection), typeof(WaveRecorder), new PropertyMetadata(new PointCollection()));



    public RecordingSession RecordedData
    {
        get { return (RecordingSession)GetValue(RecordedDataProperty); }
        set { SetValue(RecordedDataProperty, value); }
    }
    public static readonly DependencyProperty RecordedDataProperty =
        DependencyProperty.Register("RecordedData", typeof(RecordingSession), typeof(WaveRecorder), new PropertyMetadata(null, RecordedDataChanged));



    public bool RequiresWindowFocus
    {
        get { return (bool)GetValue(RequiresWindowFocusProperty); }
        set { SetValue(RequiresWindowFocusProperty, value); }
    }
    public static readonly DependencyProperty RequiresWindowFocusProperty =
        DependencyProperty.Register("RequiresWindowFocus", typeof(bool), typeof(WaveRecorder), new PropertyMetadata(true));



    public WaveRecorder()
    {
        InitializeComponent();

        if (DesignerProperties.GetIsInDesignMode(this))
        {
            return;
        }

        powerMic = PowerMic.TryFindPowerMic();

        if (powerMic != null)
        {
            PowerMicConnected(powerMic);
        }
        else
        {
            PowerMic.PowerMicConnected += PowerMicConnected;
        }

        Loaded += ControlLoaded;
        IsVisibleChanged += VisibilityChanged;
    }

    private void PowerMicConnected(PowerMic powerMic)
    {
        this.powerMic = powerMic;
        powerMic.MicrophoneOn += () => MicrophoneStateChanged();
        powerMic.MicrophoneOff += () => MicrophoneStateChanged();

        powerMic.Disconnected += () =>
        {
            PowerMic.PowerMicConnected += PowerMicConnected;
        };
    }

    private void ControlLoaded(object sender, RoutedEventArgs e)
    {
        var parentWindow = Window.GetWindow(this);

        if (parentWindow != null)
        {
            parentWindow.Activated += ParentWindowGotFocus;
            parentWindow.Deactivated += ParentWindowLostFocus;
            parentWindow.Closing += ParentWindowClosing;
        }

        waveIn = new WaveIn();
        waveIn.DataAvailable += WaveInDataAvailable;
        waveIn.RecordingStopped += RecordingStopped;
    }

    private void ParentWindowClosing(object sender, EventArgs e)
    {
        if (waveIn != null)
        {
            waveIn.Dispose();
        }

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

    private void VisibilityChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (IsVisible)
        {
            if (ShouldRecord())
            {
                StartRecording();
            }
        }
        else
        {
            if (IsListening)
            {
                StopRecording();
            }
        }
    }

    private void ParentWindowGotFocus(object sender, EventArgs e)
    {
        if (ShouldRecord())
        {
            StartRecording();
        }
    }

    private void ParentWindowLostFocus(object sender, EventArgs e)
    {
        if (IsListening)
        {
            StopRecording();
        }
    }

    private bool ShouldRecord()
    {
        if (!IsVisible)
        {
            return false;
        }

        var parentWindow = Window.GetWindow(this);

        if (parentWindow != null && !parentWindow.IsActive && RequiresWindowFocus)
        {
            return false;
        }

        if (powerMic != null)
        {
            return powerMic.MicrophoneListening;
        }

        return false;
    }

    private void MicrophoneStateChanged()
    {
        if (!Dispatcher.CheckAccess())
        {
            Dispatcher.Invoke(() => MicrophoneStateChanged());
            return;
        }

        if (ShouldRecord())
        {
            StartRecording();
        }
        else
        {
            StopRecording();
        }
    }

    private void StartRecording()
    {
        if (RecordedData == null)
        {
            RecordedData = new RecordingSession(new MemoryStream());
        }
        else if (RecordedData.Stream == null)
        {
            RecordedData.Stream = new MemoryStream();
        }

        if (waveIn == null)
        {
            waveIn = new WaveIn();
            waveIn.DataAvailable += WaveInDataAvailable;
            waveIn.RecordingStopped += RecordingStopped;
        }

        if (writer == null)
        {
            writer = new WaveFileWriter(new IgnoreDisposeStream(RecordedData.Stream), waveIn.WaveFormat);
        }

        if (!IsListening)
        {
            waveIn.StartRecording();

            IsListening = true;
        }
    }

    private void RecordingStopped(object sender, StoppedEventArgs e)
    {
        if (e.Exception != null)
        {
            if (waveIn != null && IsListening)
            {
                waveIn.StartRecording();
            }
        }
    }

    private void StopRecording()
    {
        IsListening = false;

        if (waveIn != null)
        {
            waveIn.StopRecording();
        }
    }

    private void WaveInDataAvailable(object sender, WaveInEventArgs e)
    {
        writer.Write(e.Buffer, 0, e.BytesRecorded);
        writer.Flush();

        GenerateWaveFormForRecordedData(e.Buffer);
    }

    private static void RecordedDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as WaveRecorder)?.OnRecordedDataChanged();
    }

    protected void OnRecordedDataChanged()
    {
        Points = new PointCollection();

        if (RecordedData?.Stream == null || RecordedData.Stream.Length == 0)
        {
            // No content.                
            return;
        }

        string temporaryFile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString());

        try
        {
            using (var fileStream = File.Create(temporaryFile))
            {
                RecordedData.Stream.Position = 0;
                RecordedData.Stream.CopyTo(fileStream);

                fileStream.Position = 0;
                RecordedData.Stream.Position = 0;

                using (var reader = new WaveFileReader(fileStream))
                {
                    writer = new WaveFileWriter(new IgnoreDisposeStream(RecordedData.Stream), reader.WaveFormat);

                    int read;
                    byte[] buffer = new byte[1024];
                    while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        writer.Write(buffer, 0, read);
                        GenerateWaveFormForRecordedData(buffer);
                    }

                    writer.Flush();
                }
            } 
        }
        finally
        {
            try { File.Delete(temporaryFile); } catch { }
        }            
    }

    private void GenerateWaveFormForRecordedData(byte[] data)
    {
        var newPoints = new List<Tuple<double, double>>();

        int bytesPerSample;

        if (waveIn == null)
        {
            /*using (var reader = new WaveFileReader(new MemoryStream(data)))
            {
                bytesPerSample = (reader.WaveFormat.BitsPerSample / 8) * reader.WaveFormat.Channels;
            }*/

            bytesPerSample = 2;
        }
        else
        {
            bytesPerSample = (waveIn.WaveFormat.BitsPerSample / 8) * waveIn.WaveFormat.Channels;
        }

        byte[] waveData = new byte[bytesPerSample * samplesPerPoint];

        for (float x = 0; x < data.Length; x += bytesPerSample * samplesPerPoint)
        {
            Array.Copy(data, (int)x, waveData, 0, (int)Math.Min(data.Length - x, waveData.Length));
            int bytesRead = waveData.Length;

            if (bytesRead == 0)
            {
                break;
            }

            short low = 0;
            short high = 0;

            for (int n = 0; n < bytesRead; n += 2)
            {
                short sample = BitConverter.ToInt16(waveData, n);
                if (sample < low) low = sample;
                if (sample > high) high = sample;
            }

            float lowPercent = ((((float)low) - short.MinValue) / ushort.MaxValue);
            float highPercent = ((((float)high) - short.MinValue) / ushort.MaxValue);
            float lowValue = (float)WaveFormScrollViewer.ActualHeight * lowPercent;
            float highValue = (float)WaveFormScrollViewer.ActualHeight * highPercent;

            newPoints.Add(new Tuple<double, double>(highValue, lowValue));
        }

        var points = new PointCollection(Points);
        double index;

        if (points.Any())
        {
            index = points.Last().X + 1;

            int maxPoints = 5000;

            if (points.Count > maxPoints)
            {
                points = new PointCollection(points.Skip(points.Count - maxPoints));
            }
        }
        else
        {
            index = 0;
        }

        foreach (var newPoint in newPoints)
        {
            points.Add(new Point(index, newPoint.Item1));
            points.Add(new Point(index++, newPoint.Item2));
        }

        Points = points;

        WaveFormScrollViewer.ScrollToRightEnd();
    }
}

}

0 个答案:

没有答案