NAudio hanging and crashing when default device is changed - c#

I've been stuck on this all day, so I'm going to post everything I've been able to find today that might be useful to helping me, it will be a long post. I'm having 2 issues that I believe are related to the same problem. First, let me explain what I am doing. I have 3 Winforms combo boxes that are bound to lists of all of the devices found by MMDeviceEnumerator. Two output device boxes, one input device. I am using the MMDeviceEnumerator to register a callback for whenever the devices are changed, removed, or a default device is set. The callback fires an event that then invokes a delegate to the form thread to re-enumerate the devices into combo boxes. It looks like this:
public void OnDefaultDeviceChanged(DataFlow dataFlow, Role deviceRole, string defaultDeviceId)
{
Devices.OnDevicesUpdated();
}
//The handler called by this event:
private void UpdateDeviceSelectors(object? sender = null, EventArgs? e = null)
{
Invoke(delegate ()
{
int primaryIndex = Devices.PrimaryOutput + 1, secondaryIndex = Devices.SecondaryOutput + 2, microphoneIndex = Devices.Microphone + 1;
Devices.Refresh();
try
{
SecondaryOutputComboBox.SelectedIndex = secondaryIndex;
PrimaryOutputComboBox.SelectedIndex = primaryIndex;
MicrophoneSelectComboBox.SelectedIndex = microphoneIndex;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
});
}
Now for the two issues I have. The first one involves a semi-random crash that leads back to NAudio.Wasapi.dll, where a System.ExecutionEngineException is thrown. It is kind of easy to reproduce. All I do is change the values of the combo boxes, switch the default devices around, and it will randomly crash.
The second issue occurs when another part of my code is involved. I have a microphone injector, which redirects a WaveInEvent that records a selected input device to a WaveOutEvent, like a loopback. Here is the relevant code for this:
public void Start()
{
if (Soundboard.Devices.SecondaryOutput == -2) return;
micStream = new WaveInEvent();
micStream.BufferMilliseconds = 50;
micStream.WaveFormat = new WaveFormat(44100, WaveIn.GetCapabilities(Soundboard.Devices.Microphone).Channels);
micStream.DeviceNumber = Soundboard.Devices.Microphone;
WaveInProvider waveIn = new(micStream);
var volumeSampleProvider = new VolumeSampleProvider(waveIn.ToSampleProvider());
volumeSampleProvider.Volume = 1 + Settings.Default.MicrophoneGain;
virtualCable = new WaveOutEvent();
virtualCable.DesiredLatency = 150;
virtualCable.DeviceNumber = Soundboard.Devices.SecondaryOutput;
virtualCable.Init(volumeSampleProvider);
micStream.StartRecording();
virtualCable.Play();
}
public void Stop()
{
try
{
if (micStream != null && virtualCable != null)
{
micStream.Dispose();
micStream = null;
virtualCable.Dispose();
virtualCable = null;
}
}
catch
{
micStream = null;
virtualCable = null;
}
}
In the delegate mentioned earlier, I am calling the Stop method of the Mic Injector and then the Start method to refresh the WaveIn and WaveOut devices to use the current device numbers so users do not see a device selected when a different device is being used. When this happens, the program, rather than crashing instantly and inconsistently, always hangs and has to be killed from the task manager. I am certain these 2 problems are related to the same root cause, but I have no idea what that root cause may be. Switching to Wasapi, DirectSound, or ASIO won't work because they lack certain functionalities I need, so I would really like to get this working still using Wave streams.
I've tried to find different ways to detect the device changes, assuming it is an issue deep inside NAudio, but I just can't find anything else. For the second problem specifically, I have moved the calls to the Mic Injector around thinking it may be a threading issue or something, but it didn't work or change the behavior.

Related

C# UWP, reusing geofences in geofencemonitor

I'm developing UWP app which will assist people with public transportation in my city. It uses geofencing to notify when the device is near some station. All is working fine, but slow. On my lumia 830 it took 4-5 seconds to prepare data for all 250 geofences and create them. Since, geofencemonitor runs under system and can store my geofences even when my app is terminated, I thought it would be smart to not recreate all geofences again and again on every app startup, but create them once and then just reuse them.
But I cant figure out how. When I start my app, Geofencemonitor.Current.Count is 250 and GeofenceMonitor.Current.Status is Ready, but it never fires Geoface state changed event.
My problem:
I m trying to reuse geofences in geofencemonitor. When I run my app and during startup I dont call Geofencemonitor.Current.Geofences.Clear(); and then create and add all my geofences -> geofence state change event is never fired.
Edit: It seems, that geofence status change fires only once after I add geofence to geofencemonitor, independently on if the app was restarted or not. But "used" geofence still remains in geofence monitor. Does geofence have some supended state? Am I missing some geofence property?
Here is how I create geofence:
var geofence = new Geofence(id, geocircle, mask, false, dwellTime);
GeofenceMonitor.Current.Geofences.Add(geofence);
My questions:
"Since, geofencemonitor runs under system and can store my geofences even when my app is terminated" - Is this really true?
If my first questions is true, and it is possible to reuse geofences, what am I doing wrong or do you know some place where I can go deeper into Geofencing in UWP?
Thanks for advice.
Here is how I listen geofences state change event:
ItsValueIs250 = Geofencemonitor.Current.Geofences.Count;
ItsValueIsReady = Geofencemonitor.Current.Status;
public async void OnGeofenceStateChanged(GeofenceMonitor sender, object e)
{
var reports = sender.ReadReports();
await Dispatcher.DispatchAsync( () =>
{
foreach (GeofenceStateChangeReport report in reports)
{
GeofenceState state = report.NewState;
Geofence geofence = report.Geofence;
if (state == GeofenceState.Entered)
{
stationInRange.Add(geofence.Id);
StationInRangeCount = stationInRange.Count().ToString();
}
else if (state == GeofenceState.Exited)
{
stationInRange.Remove(geofence.Id);
}
}
});
}
And maybe this method which I call during startup.
async public Task<string> GeomonitorInitialize()
{
string message = string.Empty;
var accessStatus = await Geolocator.RequestAccessAsync();
switch (accessStatus)
{
case GeolocationAccessStatus.Allowed:
int i = 0;
i = GeofenceMonitor.Current.Geofences.Count;
return message;
case GeolocationAccessStatus.Denied:
message = "GeoDenied";
return message;
case GeolocationAccessStatus.Unspecified:
message = "GeoError";
return message;
default:
message = "Default";
return message;
}
}
I will add any piece of code I use as you will wish. Thank you.

Windows phone crash device.Sensors.ni.dll

I am experiencing some crashes on my app (somewhat similar to a compass app). The windows crash report is telling me this:
frame 0:
Microsoft.Devices.Sensors.ni.dll; Microsoft.Devices.Sensors.Accelerometer.Start; 0x0000006c
frame 1:
PivotApp1.ni.DLL; PivotApp1.MainPage+_start_d__0.MoveNext; 0x000001d4
frame 2:
mscorlib.ni.dll; System.Runtime.CompilerServices.AsyncMethodBuilderCore._ThrowAsync_b__3; 0x00000036
I am unable to understand what this exactly means here. Based on what I get from it, it is probably because of the accelerometer.
This is my current code and It seems like the error is generated from somewhere in here :
private async void start()
{
//Check for the user agreement in use his position. If not, method returns.
if ((bool)IsolatedStorageSettings.ApplicationSettings["LocationConsent"] != true)
{
// The user has opted out of Location.
MessageBox.Show(AppResources.main_cantuse);
Application.Current.Terminate();
}
else
{
//KOMPASS
if (compass == null)
{
// Instantiate the compass.
compass = new Compass();
// Specify the desired time between updates. The sensor accepts
// intervals in multiples of 20 ms.
compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
compass.CurrentValueChanged +=
new EventHandler<SensorReadingEventArgs<CompassReading>>(compass_CurrentValueChanged);
compass.Calibrate +=
new EventHandler<CalibrationEventArgs>(compass_Calibrate);
}
try
{
compass.Start();
timer.Start();
accelerometer = new Accelerometer();
accelerometer.CurrentValueChanged +=
new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(accelerometer_CurrentValueChanged);
accelerometer.Start();
}
catch (InvalidOperationException)
{
MessageBox.Show(AppResources.main_unabletostart);
}
I would like to ask, Do I have to check if the accelerometer is ready (or null check or something else) ?
Any help or guidance here is appreciated.
Those crashes are probably from devices that do not have a compass (yes, there are devices like this out there).
You should check for the compass with Compass.IsSupported and only use it when it returns true.

wpf combobox function operating on a delay

private void messageTypeMenu_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string option = messageTypeMenu.Text;
if (option.Equals("Errors"))
{
LogParser.information = false;
LogParser.messages = false;
LogParser.errors = true;
}
if (option.Equals("Information"))
{
LogParser.information = true;
LogParser.messages = false;
LogParser.errors = false;
}
if (option.Equals("Messages"))
{
LogParser.information = false;
LogParser.messages = true;
LogParser.errors = false;
}
}
is a function that is supposed to update a static class depending on the selected value. however, its operating on a delay and im not sure why.
for some context, information, messages, and errors are 3 different message type for logs stored in a file that i am parsing. when someone selected information and hits view or save, they should only get the logs of that type. however, it appears to be operating on a delay.
for example, if i load in a file of logs and select information, it will show me the whoel file. then if i switch to messages, i will see only the information. then if i switch back to information, it will only show messages and so on. it seems like the combobox is looking at the value that was already present when they go to change this, but im not sure why.
In order to stay synchronized with the window, you should consider using the observable pattern and implement INotifyPropertyChanged combined with bindings. You will need to redesign your application since you can't implement interfaces on static classes but that may be worth it.
Another way would be using Dependency Properties.

C# Continuously / uninterrupted reading data (from third party SDK)

Intro:
I am developing software that uses motion trackers to analyse human motor systems. Currently I am implementing hardware from xsens and using their SDK to receive data from their wireless sensors.
The SDK offers a COM interface with a "getData" method which you call to receive the currently available xyz axis data (simplified). If you do not call getData, you skip that "beat" so you will be missing data, there is no caching in their hardware/SDK.
Problem:
My problem is that I need to get data at a rate of at least 75Hz, preferably a bit more, but 75 would be acceptable, but I am currently quickly dropping to just 20 signals per second...
If I remove the processing bit (see the sample below) I get perfect sample rates, so I think either the dequeue is causing the enqueue to pause. Or the "heavy" CPU load is causing all threads to wait. I have no idea how to figure out what is actually causing it, the profiler (EQATEC) just shows my "GetData" method is taking longer after a while.
Question:
What is the best technique to use to accomplish this? Why would my "reading" thread be interrupted/blocked? There must be more cases where people need to read from something without being interrupted, but I have been Googleing for 2 weeks now and apparently I can't find the correct words.
Please advise.
Thanks
Simplified code sample, version 4, using a MultiMedia timer (http://www.codeproject.com/Articles/5501/The-Multimedia-Timer-for-the-NET-Framework) and a BackgroundWorker
public class Sample
{
private MultiMediaTimer _backgroundGetData;
private bool _backgroundGettingData;
private BackgroundWorker _backgroundProcessData;
private ConcurrentQueue<double> _acceleration = new ConcurrentQueue<double>();
private void StartProcess()
{
if (_backgroundGetData == null)
{
_backgroundGetData = new MultiMediaTimer {Period = 10, Resolution = 1, Mode = TimerMode.Periodic, SynchronizingObject = this};
_backgroundGetData.Tick += BackgroundGetDataOnTick;
}
_backgroundProcessData = new BackgroundWorker {WorkerReportsProgress = false, WorkerSupportsCancellation = true};
_backgroundProcessData.DoWork += BackgroundProcessDataOnDoWork;
_backgroundGetData.Start();
}
private void BackgroundProcessDataOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
double value;
if (!_acceleration.TryDequeue(out value)) value = 0;
//Do a lot of work with the values collected so far,
//this will take some time and I suspect it's the cause of the delays?
}
private void BackgroundGetDataOnTick(object sender, EventArgs eventArgs)
{
if (_backgroundGettingData) return;
_backgroundGettingData = true;
//123 represents a value I am reading from the sensors using the SDK
double value = 123;
if (value == -1)
{
Thread.Sleep(5);
continue;
}
_acceleration.Enqueue(value);
if (_acceleration.Count < 5) continue;
if (!_backgroundProcessData.IsBusy)
{
_backgroundProcessData.RunWorkerAsync();
}
_backgroundGettingData = false;
}
}
I am seeing the problem here
_backgroundProcessDataThread.Start();
while (!_backgroundProcessDataThread.IsAlive){}
_backgroundGetDataThread.Start();
while (!_backgroundGetDataThread.IsAlive) {}
Well, you can see here that you are having infinite loop here and the second thread starts only after first has finished its work. i.e. first thread is done. This is in no way an ideal model.
Sorry, I recognized the issue later.
The problem is, _backgroundGetDataThread will start only after _backgroundProcessDataThread has done its work.

Chip8 Emulator - User Input

I have been writing a chip8 Emulator -- http://en.wikipedia.org/wiki/CHIP-8
I have tested all of the opcodes as well as the graphics calculations and I am now struggling with the user input. I have the following method monitoring user input and altering the registers as needed (When using the chip8 the user input alters the corresponding memory register - E.G. hitting '0' sets the V0 register to 0.
My problem is that I have the following code fetching and calculating each opcode and its operation contained in a while loop. And while this is running my application cannot detect user input. So the ROMS start and just stay locked in place waiting on a register change or user input. It keeps getting stuck in an infinite loop, I tried to implement a global Boolean RUN, and it is not detected after the while loop is initiated. I'm assuming it is out of the scope of the loop, but from what I have read, the keyboard event triggers an interrupt that should be visible from almost anywhere, any Thoughts?
This is what calculates and parses the opcode
private void button1_Click(object sender, EventArgs e)
{
// This will become the run function
do{
for (int i = 0; i < 2; i++)
{
opc[i] = mem[mc]; // fetching the instruction from the memory array
mc++;
}
cibox.Clear(); // Just clearing Debugging text boxes in the UI
pcbox.Clear();
pc++;
pcbox.Text += pc;
cibox.Text += opc[0].ToString("X2") + "-" + opc[1].ToString("X2");
calculations(opc); // Parses the Opcode and does the corresponding operation
}while(run);
}
And this method is controlling the user input...
protected override void OnKeyDown(KeyEventArgs keyEvent) // Listens for Keyboard events! Read More: http://www.geekpedia.com/tutorial53_Getting-input-from-keyboard.html
{
keyPress = true;
//Gets the key code found at keyEvent...
MessageBox.Show("KeyCode: " + keyEvent.KeyCode.ToString());
String register = keyEvent.KeyCode.ToString();
if (register == "Escape")
{
Application.Exit();
run = false;
}
try
{
registerVal = int.Parse(register, System.Globalization.NumberStyles.HexNumber); // Second Nibble! --> Int Format
}
catch (System.ArgumentNullException e)
{
return;
}
catch (System.ArgumentException)
{
return;
}
catch (System.FormatException)
{
return;
}
catch (System.OverflowException)
{
return;
}
if (registerVal >= 208)
{
registerVal = registerVal - 208;
}
if (registerVal <= 15)
{
mem[registerVal] = (byte)registerVal;
}
display(); // Alters UI to display state of registers, etc
}
So I have now tried the Game Loop Idea, but I cannot find a way to make a method in C# that will return a key press. Maybe I am missing something here, but I cannot seem to figure it out!
I also tried another method involving running the CPU calculations in a separate thread, and this is causing a slight delay issue.
I would really like to see an example of a method in C# that returns the value of a key being pressed that I can call within the While loop!
I suggest you set up your emulator's loop like a game loop. You have one global loop which triggers updates to each module in your emulator. In the loop, you call your input (or generic "event") processor, then your opcode/hardware emulation, then your screen update.
Here's some pseudo-code.
while (True):
processInput()
updateCPU()
outputGraphics()
I don't know how the key input works in C#, if it's an asynchronous event trigger (i.e. the "OnKeyDown" method gets called outside of your loop), you can set up "OnKeyDown" to just send the key events to an intermediate event manager, then in "processInput()" you actually resolve the events and use them to update registers on the emulated CPU etc.

Categories