NAudio
NAudio copied to clipboard
Sometimes waveIn_DataAvailable gets called with BytesRecorded = 0 after recording is started
After such strange call, waveIn_RecordingStopped gets invoked with ev.Exception = NAudio.MmException: WaveStillPlaying calling waveUnprepareHeader.
Hardware used: A4 TECH PK-130MG USB2.0 Web Camera (USB\VID_0AC8&PID_0328&REV_0100&MI_01) upd. Exactly the same problem with Realtek High Definition Audio (HDAUDIO\FUNC_01&VEN_10EC&DEV_0892&SUBSYS_10438613&REV_1003) and microphone МД-47 (yes, it is really old, but it works). NAudio.dll: version 1.7.3, 471040 bytes OS: Windows 7 x64 SP1
Test program:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using NAudio.Wave;
namespace NAudioTest
{
public class MyWindow : Window
{
private TextBox log;
WaveIn waveIn;
DispatcherTimer startTimer;
DispatcherTimer stopTimer;
public MyWindow()
{
Width = 640;
Height = 480;
Grid grid = new Grid();
Content = grid;
log = new TextBox();
log.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
grid.Children.Add(log);
Loaded += MyWindow_Loaded;
startTimer = new DispatcherTimer();
stopTimer = new DispatcherTimer();
startTimer.Interval = TimeSpan.FromMilliseconds(200);
stopTimer.Interval = TimeSpan.FromMilliseconds(300);
startTimer.Tick += startTimer_Tick;
stopTimer.Tick += stopTimer_Tick;
}
void stopTimer_Tick(object sender, EventArgs e)
{
stopTimer.Stop();
Log("Stop recording");
waveIn.StopRecording();
}
void startTimer_Tick(object sender, EventArgs e)
{
startTimer.Stop();
Log("Start recording");
waveIn.StartRecording();
stopTimer.Start();
}
void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
waveIn = new WaveIn();
waveIn.WaveFormat = new WaveFormat(44100, 1);
waveIn.DataAvailable += waveIn_DataAvailable;
waveIn.RecordingStopped += waveIn_RecordingStopped;
startTimer.Start();
}
void Log(string message)
{
log.Text += message + Environment.NewLine;
log.ScrollToEnd();
}
void waveIn_RecordingStopped(object sender, StoppedEventArgs ev)
{
if (ev.Exception == null)
{
Log("waveIn_RecordingStopped");
startTimer.Start();
}
else
{
Log(string.Format("waveIn_RecordingStopped: exception={0}", ev.Exception.ToString()));
Log("Logging stopped");
}
}
void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
Log(string.Format("waveIn_DataAvailable: BytesRecorded={0}", e.BytesRecorded));
}
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new MyWindow());
}
}
}
Example logs: 1.
Start recording
waveIn_DataAvailable: BytesRecorded=8820
waveIn_DataAvailable: BytesRecorded=8820
Stop recording
waveIn_DataAvailable: BytesRecorded=7938
waveIn_RecordingStopped
Start recording
waveIn_DataAvailable: BytesRecorded=0
waveIn_RecordingStopped: exception=NAudio.MmException: WaveStillPlaying calling waveUnprepareHeader
в NAudio.Wave.WaveInBuffer.Reuse()
в NAudio.Wave.WaveIn.Callback(IntPtr waveInHandle, WaveMessage message, IntPtr userData, WaveHeader waveHeader, IntPtr reserved)
Logging stopped
Stop recording
2.
Start recording
waveIn_DataAvailable: BytesRecorded=8820
waveIn_DataAvailable: BytesRecorded=8820
waveIn_DataAvailable: BytesRecorded=8820
Stop recording
waveIn_DataAvailable: BytesRecorded=882
waveIn_RecordingStopped
Start recording
waveIn_DataAvailable: BytesRecorded=8820
waveIn_DataAvailable: BytesRecorded=8820
Stop recording
waveIn_DataAvailable: BytesRecorded=7938
waveIn_RecordingStopped
Start recording
waveIn_DataAvailable: BytesRecorded=0
waveIn_RecordingStopped: exception=NAudio.MmException: WaveStillPlaying calling waveUnprepareHeader
в NAudio.MmException.Try(MmResult result, String function)
в NAudio.Wave.WaveInBuffer.Reuse()
в NAudio.Wave.WaveIn.Callback(IntPtr waveInHandle, WaveMessage message, IntPtr userData, WaveHeader waveHeader, IntPtr reserved)
Logging stopped
Stop recording
Not seen this myself. You might want to try WaveInEvent instead
@markheath, did you tried my test program with 1.7.3 version or newer?
sorry, haven't had a chance to try this myself. However, if I had an application that needed to stop and start recording frequently with small gaps, I would actually not use StopRecording and StartRecording. Instead I would just have a boolean flag saying whether I was currently recording and in my DataAvailable event I'd just ignore it if my flag wasn't set
that needed to stop and start recording frequently with small gaps
Gaps are small in this test program because it is easier to reproduce the problem in such way.
Instead I would just have a boolean flag
I was thinking about solution with data discarding, but it have some problems:
- It uses more system resources that is needed for such task. I know that overhead is low, but ideally it should be zero.
- User may want to disconnect recording device between transmissions.
sorry, haven't had a chance to try this myself
So this report is valid and should not be closed? I was created it not because I can't think of some workaround. Just because I wanted to help in making this library better.
OK thanks I see. I was just doing a cleanup of old stuff, sounds like this is a genuine bug, but not one I've had time to look at. Very happy to accept PRs for anyone who wants to tackle fixing this. One idea I've had for some time is that the WaveIn and WaveOut statuses need to include Starting and Stopping states which will make managing edge cases like restarting while we're still stopping much easier.
I just ran into this problem myself. If I record a short clip (say, a second or so) and then call StopRecording(), then StartRecording() very shortly thereafter, I get that exception.
thanks for reporting. If I were doing NAudio again from scratch I'd have some additional "starting" and "stopping" states on WaveIn, which I think would make it easier to code around issues where recording (and playback) start and stop very quickly. Remember with recording that you can always capture soundcard data without necessarily "recording" it to a file. This is quite a common approach, so starting recording is just flipping a boolean flag to say we want to keep captured audio. It might help in the short term until we can get this bug fixed.
@markheath Thanks. 👍 Nice little library. Took me no longer than 5 minutes to get it recording to a MemoryStream.
I found a workaround, definely not the best way to handle it but...
You have to create a new WaveIn and use use the same MemoryStream each time you want to record.
MemoryStream stream = waveIn.MemoryStream;
waveIn = new WaveIn();
waveIn.MemoryStream = stream;
waveIn.WaveFormat = new WaveFormat(44100, 1);
waveIn.DataAvailable += waveIn_DataAvailable;
waveIn.RecordingStopped += waveIn_RecordingStopped;
// record one time, then stop recording, then create a new WaveIn, and so on...
It's dirty, but it works.
not sure why you need a new instance of WaveIn here? Just start recording again and write to the same memory stream if you want
Do we have a solution for this Exception? waveIn_RecordingStopped: exception=NAudio.MmException: WaveStillPlaying calling waveUnprepareHeader в NAudio.MmException.Try(MmResult result, String function) в NAudio.Wave.WaveInBuffer.Reuse()
// record one time, then stop recording, then create a new WaveIn, and so on...
@anthonyraymond thanks. I tried it and it works, but not for too long.
In my case (I don't use MemoryStream), program leaks handles with every reinitialization.
Which means that after some amount of start-stop cycles, not only your program can't start recording anymore, but any other program in the system.
After I added waveIn?.Dispose(); before waveIn = new WaveIn();, handle leak stopped.
But who knows what other surprises are still waiting with this solution.
(My comment is vaild for version 1.7.3. What is the behaviour of previous and later versions, I don't know)
Might be worth looking at WasapiCapture now that it supports built-in resampling in NAudio 2.1.0
Might be worth looking at
WasapiCapturenow that it supports built-in resampling in NAudio 2.1.0
This worked for me! Thanks for the suggestion!