WaveOut versus WaveOutEvent - c#

I'm using NAudio with
WaveOutEvent Klangwiedergabegeraet;
private void Play(string Dateiname)
{
Klangwiedergabegeraet = new WaveOutEvent();
Klangwiedergabegeraet.DeviceNumber = comboBox1.SelectedIndex;
ISampleProvider StueckchenHalter = null;
StueckchenHalter = CreateInputStreamS(Dateiname);
Klangwiedergabegeraet.Init(new SampleToWaveProvider(StueckchenHalter));
Klangwiedergabegeraet.Play();
}
private void Cancel()
{
if (Klangwiedergabegeraet != null)
{
Klangwiedergabegeraet.Stop();
Klangwiedergabegeraet.Dispose();
}
}
When running Cancel(), it won't stop. When I used
WaveOut Klangwiedergabegeraet;
private void Play(string Dateiname)
{
Klangwiedergabegeraet = new WaveOut();
...
}
private void Cancel()
{
if (Klangwiedergabegeraet != null)
{
Klangwiedergabegeraet.Stop();
Klangwiedergabegeraet.Dispose();
}
}
It worked. Why is this and what to change?
P.S I am using WaveOutEvent instead of WaveOut because WaveOut does not have the property DeviceNumber.

If you are running on a non-GUI thread, then WaveOutEvent is the recommended way to do things, not WaveOut as that will choose function callbacks which can be unreliable on some soundcards. Also, WaveOut does have a DeviceNumber property.

Related

Using NAudio to record fixed length wav, it crashes with "System.AccessViolationException" after the first file

I'm using NAudio to record a fixed length audio file, but it crashes after the first file at the TimerElapsed method.
I'm using a Timer that stops and starts a new recording after the Interval has elapsed.
The filename is changed after each new recording.
This is the error I get.
System.AccessViolationException
HResult=0x80004003
Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Source=<Cannot evaluate the exception source>
StackTrace:
<Cannot evaluate the exception stack trace>
The class code is here :
namespace AudioService
{
public class AudioRecorder
{
public string Location
{
get => location;
set
{
location = value;
Filename = $"{Location}/test001.wav";
}
}
public string Filename { get; set; }
public double Interval { get; set; } = 10000;
public int DeviceNumber { get; set; } = 0;
private WaveInEvent waveSource = null;
private WaveFileWriter waveFile = null;
public bool LoopRecord { get; set; } = true;
public bool Recording { get; set; } = false;
private System.Timers.Timer timer;
private string location;
public AudioRecorder()
{
Location = Environment.GetEnvironmentVariable("userprofile");
Filename = $"{Location}/test001.wav";
if (LoopRecord) setTimer();
}
private void setTimer()
{
timer = new System.Timers.Timer(Interval);
timer.Elapsed += Timer_Elapsed;
timer.AutoReset = true;
}
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
/// ****************
/// CRASHES HERE
/// ****************
waveSource?.StopRecording();
Filename = GetNewFileName();
StartRecording(Filename);
}
public void StartRecording()
{
if (timer != null)
timer.Enabled = true;
StartRecording(Filename);
}
public async void StartRecording(string filename)
{
if (waveSource == null)
{
waveSource = new WaveInEvent
{
DeviceNumber = DeviceNumber,
WaveFormat = new WaveFormat(44100, 1)
};
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);
}
waveFile = await Task.Run(() => new WaveFileWriter(filename, waveSource.WaveFormat));
waveSource.StartRecording();
Recording = true;
}
private void waveSource_RecordingStopped(object sender, StoppedEventArgs e)
{
if (waveSource != null)
{
waveSource.Dispose();
waveSource = null;
}
if (waveFile != null)
{
waveFile.Dispose();
waveFile = null;
}
Recording = false;
}
private void waveSource_DataAvailable(object sender, WaveInEventArgs e)
{
if (waveFile != null)
{
waveFile.Write(e.Buffer, 0, e.BytesRecorded);
waveFile.Flush();
}
}
public void StopRecording()
{
waveSource?.StopRecording();
timer.Enabled = false;
}
public string GetNewFileName()
{
var tempLocation = $"{Location}/{DateTime.Now:yyMMdd}/";
bool folderExists = Directory.Exists(tempLocation);
if (!folderExists)
Directory.CreateDirectory(tempLocation);
string date = DateTime.Now.ToString("HH.mm.ss");
return $"{tempLocation}{date}.wav";
}
}
}
Thanks for helping
That exception basically means that you are crashing the 3rd party unmanaged code in NAudio, probably by trying to access an object that it is busy using or that it has already closed.
Your timer is going to fire every time your interval elapses, but you don't have any code to make sure that the previous recording is complete before starting a new one. In other words, StartRecording will get called over and over one the timer is started. If you want StartRecording to fire ONCE, then timer.AutoReset should be false.
A good way to troubleshoot these kind of issues is to replace all the calls to NAudio objects with console.writelines describing what is happening, and then run your program and observe the output.

C# Tasks - am I experiencing a suspicious increasing of number of handles

I am dipping my toes in Tasks (.NET 4.5) and am experiencing increasing handles (in task manager). I have a class doing a simple Play/Stop of audio file using MediaPlayer class (System.Windows.Media namespace). I have a second class which wraps it and exposes sync/async playback.
All is working fine and functionality is OK, but see the number of handles increasing in Task manager which worries me.... Am I doing something wrong here?
Important note: if I comment our the "await Task.Delay(1000);" --> then all is just fine and no leaks are observed... How come??
public partial class Form1 : Form
{
AudioActions syncAction = new AudioActions(#"c:\1.wav", false);
AudioActions asyncAction = new AudioActions(#"c:\1.wav", true);
public Form1()
{
InitializeComponent();
}
private async void syncPlayback(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
await syncAction.Start();
}
}
private async void asyncPlayback(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
await asyncAction.Start();
await Task.Delay(100); //remove this line and all is fine!!!!
asyncAction.Stop();
}
Console.WriteLine("done");
}
}
public class AudioActions
{
private Audio audio = null;
private TaskCompletionSource<bool> tcs = null;
private string pathToWaveFile;
private bool async;
public AudioActions(string pathToWaveFile, bool async)
{
this.pathToWaveFile=pathToWaveFile;
this.async=async;
}
public Task Start()
{
tcs = new TaskCompletionSource<bool>();
audio = new Audio();
audio.mediaPlayerPlaybackStoppedEvent += Audio_wmPlaybackStopped;
if (async)
tcs.TrySetResult(true); //since its async operation, lets return immediately and free the task from waiting
audio.PlayAudioFileInMediaPlayer(pathToWaveFile);
return tcs.Task;
}
private void Audio_wmPlaybackStopped()
{
audio.mediaPlayerPlaybackStoppedEvent -= Audio_wmPlaybackStopped;
tcs.TrySetResult(true); //playback stopped. Lets free the task from waiting
}
public void Stop()
{
audio.StopAudioFilePlaybackInMediaPlayer();
}
}
public class Audio
{
MediaPlayer mediaPlayer = null;
public delegate void MediaPlayerPlaybackStoppedDelegate();
public event MediaPlayerPlaybackStoppedDelegate mediaPlayerPlaybackStoppedEvent;
public void PlayAudioFileInMediaPlayer(string pathToWavFile)
{
mediaPlayer = new MediaPlayer();
mediaPlayer.MediaEnded += mediaPlayer_MediaEnded;
mediaPlayer.Open(new Uri(pathToWavFile));
mediaPlayer.Play();
}
void mediaPlayer_MediaEnded(object sender, EventArgs e)
{
mediaPlayerPlaybackStoppedEvent.Invoke();
MediaPlayer mediaPlayer = (MediaPlayer)sender;
mediaPlayer.MediaEnded -= mediaPlayer_MediaEnded;
mediaPlayer.Close();
mediaPlayer = null;
}
public void StopAudioFilePlaybackInMediaPlayer()
{
mediaPlayer.Stop();
mediaPlayer.Close();
mediaPlayer = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
Adding a strip down code:
private async void asyncPlayback(object sender, EventArgs e)
{
AudioActions asyncAction = new AudioActions();
for (int i = 0; i < 100; i++)
{
await asyncAction.Start();
await Task.Delay(1000); //remove this line and all is fine!!!!
asyncAction.Stop();
}
}
public class AudioActions
{
private Audio audio = null;
private TaskCompletionSource<bool> tcs = null;
public Task Start()
{
tcs = new TaskCompletionSource<bool>();
audio = new Audio();
tcs.TrySetResult(true); //since its async operation, lets return immediately and free the task from waiting
audio.PlayAudioFileInMediaPlayer(#"c:\1.wav");
return tcs.Task;
}
public void Stop()
{
audio.StopAudioFilePlaybackInMediaPlayer();
}
}
public class Audio
{
MediaPlayer mediaPlayer = null;
public void PlayAudioFileInMediaPlayer(string pathToWavFile)
{
mediaPlayer = new MediaPlayer();
mediaPlayer.Open(new Uri(pathToWavFile));
mediaPlayer.Play();
}
public void StopAudioFilePlaybackInMediaPlayer()
{
mediaPlayer.Stop();
mediaPlayer.Close();
mediaPlayer = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
Modified class solving the problem:
public class Audio
{
public delegate void NaudioPlaybackStoppedDelegate();
public event NaudioPlaybackStoppedDelegate naudioPlaybackStoppedEvent;
private WaveOut player = null;
public void PlayAudioFileUsingNaudio(string pathToWavFile)
{
player = new WaveOut();
AudioFileReader waveFileReader = new AudioFileReader(pathToWavFile);
player.Init(waveFileReader);
player.PlaybackStopped += NAudio_Stopped;
player.Play();
}
private void NAudio_Stopped(object sender, StoppedEventArgs e)
{
player.PlaybackStopped -= NAudio_Stopped;
if (naudioPlaybackStoppedEvent!=null)
naudioPlaybackStoppedEvent.Invoke();
player.Dispose();
}
public void StopAudioFilePlaybackInNaudio()
{
player.Stop();
}
}
Replacing MediaPlayer with NAudio 3rd party solved it.
public class Audio
{
public delegate void NaudioPlaybackStoppedDelegate();
public event NaudioPlaybackStoppedDelegate naudioPlaybackStoppedEvent;
private WaveOut player = null;
public void PlayAudioFileUsingNaudio(string pathToWavFile)
{
player = new WaveOut();
AudioFileReader waveFileReader = new AudioFileReader(pathToWavFile);
player.Init(waveFileReader);
player.PlaybackStopped += NAudio_Stopped;
player.Play();
}
private void NAudio_Stopped(object sender, StoppedEventArgs e)
{
player.PlaybackStopped -= NAudio_Stopped;
if (naudioPlaybackStoppedEvent!=null)
naudioPlaybackStoppedEvent.Invoke();
player.Dispose();
}
public void StopAudioFilePlaybackInNaudio()
{
player.Stop();
}
}

C# Producer/Consumer implementation issue

I have a producer-consumer class as following.
public class ProducerConsumer<T> where T : class
{
private Thread _workerThread;
private readonly Queue<T> _workQueue;
private readonly object _lockObject = new object();
private readonly Action<T> _workCallbackAction;
private ManualResetEvent _workerWaitSignal;
public ProducerConsumer(Action<T> action)
{
_workCallbackAction = action;
_workQueue = new Queue<T>();
}
private void DoWork()
{
while (true)
{
T workItemToBeProcessed = default(T);
bool hasSomeWorkItem = false;
lock (_lockObject)
{
hasSomeWorkItem = _workQueue.Count > 0;
if (hasSomeWorkItem)
{
workItemToBeProcessed = _workQueue.Dequeue();
if (workItemToBeProcessed == null)
{
return;
}
}
}
if (hasSomeWorkItem)
{
if (_workCallbackAction != null)
{
_workCallbackAction(workItemToBeProcessed);
}
}
else
{
_workerWaitSignal.WaitOne();
Debug.WriteLine("Waiting for signal.");
}
}
}
public void EnQueueWorkItem(T workItem)
{
lock (_lockObject)
{
_workQueue.Enqueue(workItem);
_workerWaitSignal.Set();
}
}
public void StopWork(ManualResetEvent stopSignal)
{
EnQueueWorkItem(null);
_workerThread.Join();
_workerWaitSignal.Close();
_workerWaitSignal = null;
if (stopSignal != null)
{
stopSignal.Set();
}
}
public void ReStart()
{
_workerWaitSignal = new ManualResetEvent(false);
_workerThread = new Thread(DoWork) { IsBackground = true };
_workerThread.Start();
}
}
and i am using this in the following way:
public partial class Form1 : Form
{
private RecordProducerConsumer<string> _proConsumer;
public Form1()
{
InitializeComponent();
_proConsumer = new RecordProducerConsumer<string>(DoAction);
}
private bool restart=true;
private int item = 0;
private void button1_Click(object sender, EventArgs e)
{
if (restart)
{
_proConsumer.ReStart();
restart = false;
}
item++;
_proConsumer.EnQueueWorkItem(item.ToString());
}
private void DoAction(string str)
{
Debug.WriteLine(str);
}
private void btnStop_Click(object sender, EventArgs e)
{
ManualResetEvent mre = new ManualResetEvent(false);
_proConsumer.StopWork(mre);
mre.WaitOne();
restart = true;
}
private void Stop(ManualResetEvent mre)
{
mre.WaitOne();
}
}
My problem or what I can not understand is when I click Start button I am adding only one item and it Dequeue that item but keeps on running the loop so I see lot of "Waiting for signal." print outs on the Output window in Visual Studio.
Why does it not stop on _workerWaitSignal.WaitOne(); in DoWork() method , why is it running all the time ?
Couple of issues:
It makes hardly any sense to output 'Waiting for issue' after executing the wait. Consider moving the write before the actual wait.
You are using a ManualResetEvent — as its name indicates, it requires a manual reset to revert from the signalled state. However, I can't see a call to Reset in your code.
To avoid other concurrency issues (e.g. a race condition when when resetting the event while other thread set the event after enqueuing another work item), consider using a Semaphore for your scenario instead.
try this... I could be wrong...but that is all i could figure out by reading through your code. Hope this helps :)
private void button1_Click(object sender, EventArgs e)
{
if (restart)
{
restart = false;
_proConsumer.ReStart();
}
item++;
_proConsumer.EnQueueWorkItem(item.ToString());
}
I haven't read the code thoroughly, but I can venture a guess that you meant to use an AutoResetEvent (which resets automatically after some WaitOne() is released) rather than a ManualResetEvent (which stays set until you explicitly call Reset()).
Also, is there any reason you're not using .NET's BlockingCollection<T> ? It's the framework implementation of the producer/consumer pattern, and it works very well.

NAudio: WaveInEvent handler not working on its own thread

I have this class called AudioInput:
class AudioInput
{
private WaveIn waveIn;
public delegate void DataAvailableEventHandler(byte[] data, int size);
private DataAvailableEventHandler dataAvailableProc;
public AudioInput(DataAvailableEventHandler dataHandlerProc)
{
dataAvailableProc = dataHandlerProc;
}
private void initWaveInMic()
{
Console.WriteLine("initWaveInMic");
waveIn = new WaveIn();
waveIn.BufferMilliseconds = 50;
waveIn.DeviceNumber = 0;
waveIn.WaveFormat = new WaveFormat(8000, 1);
waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);
waveIn.StartRecording();
}
void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
Console.WriteLine("waveIn_DataAvailable e.buffer length: {0}", e.Buffer.Length);
dataAvailableProc(e.Buffer, e.Buffer.Length);
}
public void startNAudio()
{
this.initWaveInMic(); //start mic wavein
}
}
From the calling class:
public partial class AudioTest : Form
{
Thread audioInputThread;
AudioInput audioInput;
private void audioInputCreateThread()
{
audioInput = new AudioInput(audioDataToSend);
audioInput.startNAudio();
Console.WriteLine("audioInputCreateThread at the end");
}
private void AudioTest_Load(object sender, EventArgs e)
{
// this will work
//audioInputCreateThread();
//this will not work
audioInputThread = new Thread(audioInputCreateThread);
audioInputThread.Start();
}
private void audioDataToSend(byte[] data, int size)
{
Console.WriteLine("audioDataToSend size: {0}", size);
}
}
The waveIn_DataAvailable callback in the AudioInput class is not getting called. Any suggestions what I did wrong?
The WaveInEvent class should be used in this instance. It will create its own background thread, and use a SyncContext to marshal callbacks onto the GUI thread if possible.

Why does PlaybackState remain at Playing after PlaybackStopped fires?

I'm a little new to using NAudio so I'm surely missing something important, hence this question.
Starting with example/demo code, I put together a class as simple as I could make it to play MP3 or WAV files:
public class AudioPlayer : IDisposable
{
WaveStream _waveStream; // sound data stream
WaveChannel32 _waveChannel32; // sound channel ??
IWavePlayer _iWavePlayer; // sound output driver
public bool IsPlaying
{
get { return _iWavePlayer != null
&& _iWavePlayer.PlaybackState
!= Wave.PlaybackState.Stopped; }
}
public PlaybackState PlaybackState
{
get { return _iWavePlayer.PlaybackState; }
}
public void LoadMp3(byte[] mp3Bytes)
{
Load(CreateInputStreamFromMp3Bytes(mp3Bytes));
}
public void LoadFile(string filename)
{
Load(CreateInputStream(filename));
}
void Load(WaveStream waveStream)
{
if (_iWavePlayer != null)
Dispose();
_iWavePlayer = new WaveOut();
_iWavePlayer.PlaybackStopped +=
new EventHandler(_iWavePlayer_PlaybackStopped);
_waveStream = waveStream;
_waveChannel32 = new WaveChannel32(_waveStream);
_waveChannel32.PadWithZeroes = false;
_iWavePlayer.Init(_waveChannel32);
}
void _iWavePlayer_PlaybackStopped(object sender, EventArgs e)
{
Stop();
}
static WaveStream CreateInputStreamFromMp3Bytes(byte[] mp3Bytes)
{
return new Mp3FileReader(new MemoryStream(mp3Bytes), false);
}
static WaveStream CreateInputStream(string fileName)
{
if (fileName.EndsWith(".wav"))
return new WaveFileReader(fileName);
else if (fileName.EndsWith(".mp3"))
return new Mp3FileReader(fileName);
else
throw new InvalidOperationException("Unsupported extension");
}
public void Play()
{
_iWavePlayer.Play();
}
public void Stop()
{
if (_iWavePlayer != null
&& _iWavePlayer.PlaybackState != PlaybackState.Stopped) {
_iWavePlayer.Stop();
_waveStream.Position = 0;
}
}
public void Dispose()
{
Stop();
if (_iWavePlayer != null) {
_iWavePlayer.Dispose();
_iWavePlayer = null;
}
if (_waveChannel32 != null) {
_waveChannel32.Dispose();
_waveChannel32 = null;
}
if (_waveStream != null) {
_waveStream.Dispose();
_waveStream = null;
}
}
}
I'm using the code in question to play MP3 files (not WAVs).
It works ok for the most part, which is pretty awesome considering that I'll be able to replace MediaElements in my project. The issue I'm having is that the PlaybackState stays at Playing rather than changing to Stopped once PlaybackStopped fires. To go around that, I subscribe to PlaybackStopped and call Stop() from there.
Is it normal that I have to call Stop() like that, or is there is something I am missing here?
Looks like a bug. If you look at NAudio code WaveOut will not change PlaybackState in RaisePlaybackStoppedEvent. But DirectSoundOut will change it to stopped. A little crazy.
Try this:
In the Load method change this line:
_iWavePlayer = new WaveOut();
by this:
_iWavePlayer = new WaveOut(WaveCallbackInfo.FunctionCallback());

Categories

Resources