我开发了一个应用程序,允许用户指示以后由其他用户转录的信息。对于录音,我一直在使用NAudio和CSCore库(单独,不在一起 - 我将在下面详细解释)。
我在罐头的第一次踢是使用NAudio,我相对成功。但是,每隔一段时间,应用程序就会崩溃,并显示类似于下面的消息:
由于.NET运行时在IP 791F9AAA(79140000)处出现内部错误而导致该进程终止,退出代码为80131506.
令人沮丧的是,我无法在我的开发机器上重现崩溃。由于这些是与.NET运行时内部相关的硬崩溃,因此很难准确诊断导致这些崩溃的原因,但我相信我对NAudio的使用可能会导致问题。
所以...我将库切换到CSCore,崩溃问题自行解决。很好,但我还有一个问题 - 在处理冗长的口述时,我遇到了OutOfMemory异常。最初我将录制的音频内容存储在MemoryStream中,因此我将代码更改为使用FileStream,并写入磁盘而不是将录制的音频保留在内存中。
这解决了我所看到的所有OutOfMemory异常,但是这引入了一个新问题 - 音频现在是'剪辑'。我不确定剪辑是否充分描述了这个问题,但基本上我所看到的(或者更确切地说是听到的)是录制的音频绊倒自身并且部分音频完全丢失。再一次,我没有在我的开发机器上看到这个问题,只是在实际使用该应用程序的工作站上。
作为一项实验,我切换回NAudio(仍使用FileStreams而不是MemoryStreams),发现音频剪辑问题几乎已修复。一般来说,问题似乎并不存在,但我们仍然看到音频剪辑的情况。 .NET运行时中的内部错误问题仍然存在。
总结:
n音讯:
CSCore:
我找不到其他人遇到这些问题的案例。有任何一个图书馆经验的人都有一些关于我出错的地方吗?或者,是否有另一种使用.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();
}
}
}