I have a Windows forms application that I am trying to add accessibility to and have run into an issue with the speech synthesizer where it appears that the SpeechAsyncCancelAll runs in the user interface thread. Performance is totally dependent on the power of the PC.
This can be reproduced with a very simple application in Windows forms.
Create a form and add a numeric up down control. Then use this code:
using System.Windows.Forms;
using System.Speech;
using System.Speech.Synthesis;
namespace WindowsFormsApp8
{
public partial class Form1 : Form
{
SpeechSynthesizer _speech = new SpeechSynthesizer();
public Form1()
{
InitializeComponent();
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
_speech.SpeakAsyncCancelAll();
_speech.SpeakAsync(numericUpDown1.Value.ToString());
}
}
}
On my development machine which is very powerful it runs without a problem and very fast when you hold down the up arrow. Each value is cancelled so you do not hear anything as the control increments and when you stop pressing the up arrow it announces the last value properly.
However, the minute this is run on a lesser PC, even a core i9 hexacore machine, the repeat on the increment slows to a crawl.
It looks to me that this is running on the user interface thread.
Any suggestions?
Thanks
Don't get yourself tricked by the "Async" in the name of the SpeakAsyncCancelAll() method name. As one can see in the source code of the SpeechSynthesizer and VoiceSynthesis classes, there is quite some synchronous code involved in order to communicate with a background thread that does the actual voice synthesis. This code is actually quite heavy in that it uses multiple lock statements.
A best practice solution for this situation (multiple successive user interactions could create a series of code reactions but in the end we only want the last one) is to not directly start the reaction, but start a timer and only perform the reaction if there was no other user interaction in the meantime.
public partial class Form1 : Form
{
private SpeechSynthesizer _speech = new SpeechSynthesizer();
public Form1()
{
InitializeComponent();
timer1.Interval = 500;
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
// Reset timer
timer1.Stop();
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
_speech.SpeakAsyncCancelAll();
_speech.SpeakAsync(numericUpDown1.Value.ToString());
}
}
You should allow the user to configure the timer interval to chose a good compromise based on their system performance and their individual usage patterns. People who need audio assistance often consider for good reasons a too long delay between user activity and an audio response as wasting their time. So it is important that users can configure such a delay to best fit their individual needs.
Let's assume you have taken Neil's excellent comment into consideration, and checked the repeat rate of the NumericUpDown control on the other PCs "without" calling the speech engine. Good.
Your code looks right. The SpeakAsyncCancelAll and SpeakAsync do not block and are "expected" to be running on a background thread. When I attempted to reproduce the problem (not a shocker) your code works fine on my PC using the test condition you describe. That being the case, maybe you could try a couple of variations on the slim chance that something makes a difference and yields some kind of clue by ruling out some unlikely issues.
Variation 1
Capture the "text to say" and post the work using BeginInvoke. This ensures that nothing could possibly be interfering with the ValueChanged or MouseDown messages from pumping in the message queue.
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
// Make 100% sure that the up-down ctrl is decoupled from speak call.
var say = $"{numericUpDown1.Value}";
// Ruling out an unlikely problem
BeginInvoke((MethodInvoker)delegate
{
_speech.SpeakAsyncCancelAll();
_speech.SpeakAsync(say);
});
}
Variation 2
Since you have a suspicion that something is running on the UI thread that shouldn't be, go ahead and give explicit instructions to post it on a background Task. At least we can rule that out.
private void numericUpDown2_ValueChanged(object sender, EventArgs e)
{
// Make 100% sure that the up-down ctrl is decoupled from speak call.
var say = $"{numericUpDown2.Value}";
// Ruling out an unlikely problem
Task.Run(() =>
{
_speech.SpeakAsyncCancelAll();
_speech.SpeakAsync(say);
});
}
Variation 3 - Inspired by NineBerry's answer (added to test code project repo)
/// <summary>
/// Watchdog timer inspired by NineBerry.
/// https://stackoverflow.com/a/74975629/5438626
/// Please accept THAT answer if this solves your issue.
/// </summary>
int _changeCount = 0;
private void numericUpDown3_ValueChanged(object sender, EventArgs e)
{
var captureCount = ++_changeCount;
var say = $"{numericUpDown3.Value}";
Task
.Delay(TimeSpan.FromMilliseconds(250))
.GetAwaiter()
.OnCompleted(() =>
{
if(captureCount.Equals(_changeCount))
{
Debug.WriteLine(say);
_speech.SpeakAsyncCancelAll();
_speech.SpeakAsync(say);
}
});
}
Well the above answers do not solve the issue. However, all the tested computers were dell computers. By default when the OS is installed, Dell installs a sound utility called MaxWaves which allows different audio enhancements. Although all options are off in this utility, it appears that it buffers the sound and when an Async.CancelAll() call comes, it blocks until the sound duration is complete. Therefore everything appears to slow to a crawl.
Uninstalling this utility as well as disabling it as a service corrects the problem.
Everything now works correctly. Thank you for your answers.
Related
I'm working on a Windows forms application that needs to perform some logic before the PC goes to sleep. I've looked through many threads and found this which should work perfectly: Link. I can detect when the power is plugged/unplugged just fine, but I've run into serious problems when trying to detect a sleep/suspend event.
Using the logic mentioned, I have this section of code in my program:
public void powerModeChanged(object sender, PowerModeChangedEventArgs args)
{
if (args.Mode == PowerModes.Suspend)
{
Trace.WriteLine("Sleeping.....");
}
else if (args.Mode == PowerModes.StatusChange)
{
Trace.WriteLine("Other Status Change:");
}
}
public MainPage()
{
InitializeComponent();
SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(powerModeChanged);
Per this documentation page - Link, there are 3 types of power modes. The statusChange is detected as expected when I unplug and replug the power adapter into my laptop, and prints to the debug Window just fine. However, it will not detect when I put the computer to sleep. After going over this for hours, my conclusion is that what the version of Windows 10 I'm running defines as "sleep" doesn't match up with the event that I'm checking for.
There is a comment on that initial thread in the first link that says the solution I tried doesn't seem to work with the "new Connected/Modern Standby modes" and provides a link to this thread: Link where it describes using the session switch event handler instead. This works on my laptop as my laptop locks upon sleep, but when testing on a Surface tablet (which is our target device for operation), it doesn't work due to the surface not locking upon sleep.
Of course, I could just set the device to lock on sleep, and that may end up being the only solution, but I wanted to see if there was something I was overlooking or any other way to check for sleep in modern versions of Windows. As it stands, I would hate for this important feature of the application depend on the system having to be setup to lock when sleeping. Thanks!
You could use the SystemEvents.PowerModeChanged event to detect when the computer is about to go to sleep. Within the event handler, you can perform any logic that needs to be done before the computer goes to sleep. For example, you could write commands to the windows registry or perform some other related tasks.
Below is an example of how this could be done:
// Register the SystemEvents.PowerModeChanged event handler
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
// Event handler for SystemEvents.PowerModeChanged
private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
// Check if the computer is about to go to sleep
if (e.Mode == PowerModes.Suspend)
{
// Perform the logic that needs to be done before the computer goes to sleep
// ...
}
}
I ended up finding this thread, the answer on that thread solves this issue.
Link to Solution
I currently work with the Steelseries GameSense SDK to make my own effects etc. for my keyboard and mouse.
To light up my mouse and keyboard on clicks and presses, I use the globalmousekeyhook library.
Unfortunately, the mouse and keyboard events don't get triggered.
Also, my mouse starts lagging, and keyboard input gets delayed.
The lag and the delay only stay for about half a minute.
I suspect that windows removes the hooks because it detects the lag.
I also tried this example program and everything works fine there.
Here is the code:
private static readonly IKeyboardMouseEvents GlobalHook = Hook.GlobalEvents();
static InputManager()
{
Logger.Log("Starting...", Logger.Type.Info);
GlobalHook.KeyDown += KeyEvent;
GlobalHook.MouseDownExt += MouseEvent;
Logger.Log("Ready!", Logger.Type.Info);
}
And the event functions:
private static void KeyEvent(object sender, KeyEventArgs eventArgs)
{
Logger.Log(eventArgs.KeyCode.ToString());
}
private static void MouseEvent(object sender, MouseEventArgs eventArgs)
{
Logger.Log(eventArgs.Button.ToString());
}
You can find the whole class (and the project) here.
The constructor is the only thing that gets executed in the program.
What I found regarding the lag is that the event function has to be fast. That cannot be the problem in my case because the Logger.Log() function is fast, and the lag also occurs when using Console.WriteLine().
As I said, the example program runs fine. I tried copying the example code, but that made no difference. The only real difference between my program and the example program is that the example uses .Net Core but I use .Net Framework (4.8). Could that be the reason? If it is the reason, is there any way to use the library with .Net Framework?
I look forward to any help.
There are two issues:
You need a message pump in order to receive hook messages. For this you can use the following code, as shown in the example you link
Application.Run(new ApplicationContext());
You now have two things you are trying to do on the same thread: pump the messages and wait for input. Instead, split them into different threads:
private static void Main(string[] args)
{
Logger.Log("Program started. Welcome.", Logger.Type.Info);
////GameSense.Controller.Start();
new Thread(() => Console.ReadLine()).Start();
InputManager.Start();
Application.Run(new ApplicationContext());
InputManager.End();
Application.Exit(); // needed to close down the message pump and end the other thread
}
I need to write code in C# that sends data from Serial to an Arduino every 2 seconds.
This is what I tried to do:
Thread sender = new Thread(voidSender);
public static void voidSender() {
serialArduino.WriteLine("Test");
Thread.Sleep(2000);
}
In your example you are starting a thread running through the defined method, sending a single message. To send the message multiple times you need to add a loop to this method like
public void voidSender()
{
//Send forever
while(true)
{
serialArduino.WriteLine("Test");
Thread.Sleep(2000);
}
}
However, periodically events are typically done by using a timer. Simply initialize a timer like
System.Timers.Timer sendMessageTimer = new System.Timers.Timer(2000);
sendMessageTimer.Elapsed += OnSend;
sendMessageTimer.AutoReset = true;
sendMessageTimer.Enabled = true;
Inside the elapsed handler you can send the message like
private void OnSend(Object source, ElapsedEventArgs e)
{
serialArduino.WriteLine("Test");
}
Honestly I don't why you would mix open source platform such as Arduino with C# .net if its not running on core, but that's your concern not mine, I personally wouldn't do that.
if you want to send command every two seconds, I would advise you to use windows scheduler that's integrated in every windows system, however you can implement a clock on your own very easy, still why would you need to put such heavy load of 2 seconds timeframe when it will be almost notable by ordinary user. If you don't want to use clock based system you can use this modified example however it may throw stackoverflow exception sometime since its recursion and you must aways stay away from them if you plan to use this on the long run.
//First sorry for reusing your code but I am writing from my smartphone and
//I am kind of lazy here is quick modification that would work but not the most clever way around
Thread sender = new Thread(voidSender);
public static void voidSender()
{
start:
serialArduino.WriteLine("Test");
Thread.Sleep(2000);
goto start;
}
I develop an application on windowCE 5.0 with opennetcf library.
I want to check WHEN my Device is connected to Cradle. It means I want to handle the event of plugged the device to cradle or other similar.
My purpose is that when Device is connected to Cradle, I disable all forms of my application,
and when it is removed from cradle, all the forms are enabled.
I search much. But the answer is not matched to my expect.
Please help me.
After reading reference of opennetcf, I found out the two events: ACPowerApplied and ACPowerRemoved
Here is my code:
public static event DeviceNotification ACPowerApplied;
public static event DeviceNotification ACPowerRemoved;
void Form1_ACPowerRemoved()
{
MessageBox.Show("Un-cradle");
}
void Form1_ACPowerApplied()
{
MessageBox.Show("Cradle");
}
private void Form1_Load(object sender, EventArgs e)
{
ACPowerApplied += new DeviceNotification(Form1_ACPowerApplied);
ACPowerRemoved += new DeviceNotification(Form1_ACPowerRemoved);
}
But the process did not step into Form1_ACPowerRemoved() and Form1_ACPowerApplied().
Is there any idea for that? Sorry for my poor English. Thank you in advance.
Your code is wrong. You've subscribed to the form's event, and nobody raise it.
Documentation doesn't show how-to-use code, I think. It shows declaraion.
Maybe it will work (not tested):
OpenNETCF.WindowsCE.DeviceManagement.ACPowerApplied += Form1_ACPowerApplied
OpenNETCF.WindowsCE.DeviceManagement.ACPowerRemoved += Form1_ACPowerRemoved
Also you can try to use WinAPI calls: http://blogs.msdn.com/b/davidklinems/archive/2005/02/10/370591.aspx
If you want, I have complete code, but there are a lot of waste and "OnRs232Connect" event.
By the way, what does "craddled" mean for you? Craddle can be disconneted from both AC and PC. I mean, do you want to handle when your device started to get electricity power, or when it started connecting to the PC via Active Sync? If the second, you want to catch "OnRs232Connect" event
I'm building an app that uses and scanner API and a image to other format converter. I have a method (actually a click event) that do this:
private void ButtonScanAndParse_Click(object sender, EventArgs e)
{
short scan_result = scanner_api.Scan();
if (scan_result == 1)
parse_api.Parse(); // This will check for a saved image the scanner_api stores on disk, and then convert it.
}
The problem is that the if condition (scan_result == 1) is evaluated inmediatly, so it just don't work.
How can I force the CLR to wait until the API return the convenient result.
NOTE
Just by doing something like:
private void ButtonScanAndParse_Click(object sender, EventArgs e)
{
short scan_result = scanner_api.Scan();
MessageBox.Show("Result = " + scan_result);
if (scan_result == 1)
parse_api.Parse(); // This will check for a saved image the scanner_api stores on disk, and then convert it.
}
It works and display the results.
Is there a way to do this, how?
Thank you very much!
UPDATE:
Theres an event on the scanner API:
Public Event EndScan() // Occurs when the scanned the image.
But I don't know how to use it. Any Idea?
That really depends on how the API works. If scanner_api.Scan() is blocking, then it will sit at that line waiting for a result. Once it gets the result, the if will evaluate. This can cause your UI to become unresponsive, so you often have to implement some sort of threading to do it in the background. I'm guessing from your question that isn't the way this API works.
Another way this could work is with polling. You check every so often to see what the result is. You don't want to check constantly and use up all your resources (such as CPU), so you check at an interval. Sheldon's answer with a Timer achieves this.
At least one more way this may work is with a callback. You send the API a callback function to call when the status has updated. This can be implemented as events (delegate) you tie into or a regular delegate you pass as a parameter. You'll often see these implemented as "OnStatusChanged", "OnCompleted", etc.
Basically, it's down to what the API supports. Polling usually works, the others have to be supported. Check your API documentation for examples if possible.
You can use a timer (see MSDN: Timer class) that periodically checks whether the scan already completed or not.
You can alternatively use an asynchronous call that calls back when the scanning process is finished. Note that this is the more complicated way.
One way would be with a timer. Set the timer to check every few seconds to check the value in scan_result (which would need to be promoted to a class-level variable for this to work).
So, something like:
public class Scanning
{
private System.Timers.Timer aTimer;
short scan_result;
public Scanning()
{
aTimer = new System.Timers.Timer(1000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
}
private void ButtonScanAndParse_Click(object sender, EventArgs e)
{
aTimer.Enabled = true;
scan_result = scanner_api.Scan();
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (scan_result == 1)
{
aTimer.Enabled = false;
parse_api.Parse(); // This will check for a saved image the scanner_api stores on disk, and then convert it.
}
}
}
(This is untested, of course. YMMV.)