stop sounds in threads c# - c#

I've got two questions about some problems on this code:
1) How can I stop the sounds? Sounds are in a separate thread, and I don't know how to stop them.
2) If I continue pressing the key this code will play a lot of times the same sounds and this is not realistic (imagine a piano keyboard: if I press a key and I continue pressing it just one sounds (the firs) will play). How to solve this problem?
I found a solution but now with threads I don't know how to do.
private void Form1_KeyDown(object sender, KeyPressEventArgs e)
{
[...] // Other code
th = new Thread(press));
th.Start(new object[] { key, name });
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
[...] // Other code
th = new Thread(leave);
th.Start(new object[] { key, name });
}
private void press(object data)
{
[...] // Other code
playSound(name);
}
private void leave(object data)
{
[...] // Other code
stopSound(name);
}
private void playSound(string name)
{
[...] // Other code
string url = Application.StartupPath + "\\notes\\" + name + ".wav";
var sound = new System.Windows.Media.MediaPlayer();
sound.Open(new Uri(url));
sound.play();
}
private void stopSound(string name)
{
???
}
Thank'you so much!

Create the thread as a variable in the class so that it can be accessed whenever you want.
Use the Handled Property to prevent the event from being handled again
e.Handled = true;

Related

KeyDown Event fires faster then a camera movement can proccess, how to queue the event?

I have a camera that I move left and right with the arrows or 'A' and 'D' keys, unfortunately the camera response time is slower than the key held options in the application. I would like to move the camera smoothly while pressing the arrows but the program is stuck when I do it because it's too much pressing for the camera to process.
How can I hold the key and reduce the big number of commands that the camera gets and can't handle?
#Webnoob is wise ... i never knew they have a name.
You'll want to do something like :
private DateTime _lastValidKey;
private int delay;
private void SomePreviewKeyDown(object sender, KeyEventArgs e)
{
// follow line need tweaking to your liking and needs
if (now - delay > lastValidKey){
// update _lastValidKey and forward key
}
else {
// return, ignoring the keys
}
switch (e.Key)
{
case Key.Up:
// do something
break;
case Key.Down:
// do something
break;
}
}
basically ...you're filtering out extra keypresses
You could define a "time quantum" for user input processing. Let say we have some event (mouse, keyboard) that may cause lengthy processing in model and/or view. In simplest case we receive the keypress and process it immediately.
public void OnUserInput( )
{
DoSomething( );
}
private void DoSomething( )
{
// and here we do it
}
We may change this into:
public void OnUserInput( )
{
// DoSomething( );
StartTimedUpdate( );
}
private void DoSomething( )
{
// and here we do it
}
DispatcherTimer m_timer = null;
private void StartTimedUpdate()
{
if (m_timer == null)
{
m_timer = new System.Windows.Threading.DispatcherTimer();
m_timer.Tick += TimedRefresh;
m_timer.Interval = TimeSpan.FromMilliseconds(100);
m_timer.Start();
}
}
private void TimedRefresh(object sender, EventArgs e)
{
if (m_timer != null)
m_timer.Stop();
m_timer = null;
DoSomething();
}
You may also record all received keystrokes in list and process them in timer event handler. The 100 ms delay is just an example.

C# - Make UI function during like a infinite loop until button pressed

i make a code in C# where i extract some records from an Access database , but i need the while going to the next iteration to depend on the click of a button. i tried with some Thread or Tasks , but it didn't worked because it blocked the UI which i need it to be seen and clickable.
Here's the code:
bool nextClick = false ;
while (readerSelect.Read())
{
// show the correct panel
if (string.Compare(readerSelect[2].ToString(), "P1") == 0)
{
// panel with type 1
textBoxP1Text1.Text = readerSelect[3].ToString();
textBoxP1Text2.Text = readerSelect[4].ToString();
pictureBoxP1Image.ImageLocation = readerSelect[6].ToString();
}
else
{
// panel with type 2
textBoxP1Text2.Text = readerSelect[5].ToString();
}
//this while need to be kind of infinite so the interation can't be processed and
//so when i need to change iteration i click the buttonNext
while (!nextClick) {
startWhile:;
MethodInvoker mi = delegate () {
if (nextClick)
{
Application.DoEvents();
// System.Windows.Forms.Application.Run();
}
};
this.Invoke(mi);
//break;
goto startWhile;
}
private void buttonNext_Click(object sender, EventArgs e)
{
// click on the next button
nextClick = true;
}
You can use a semaphore within an async task, have the button Release it during each click, and have the while loop await it each time through. Here's a quick example, using a form that has a button1 and a label1 added to it:
public partial class Form1 : Form
{
private readonly SemaphoreSlim signal = new SemaphoreSlim(0, int.MaxValue);
public Form1()
{
this.InitializeComponent();
this.RunLoop();
}
private async void RunLoop()
{
var i = 0;
while (true)
{
this.label2.Text = $"Enqueued: {this.signal.CurrentCount}";
await this.signal.WaitAsync(); // Wait button click async
await Task.Delay(1000); // Simulate work
this.label1.Text = $"Completed: {++i}";
}
}
private void button1_Click(object sender, EventArgs e)
{
this.signal.Release();
this.label2.Text = $"Enqueued: {this.signal.CurrentCount + 1}";
// Or if you want to limit the # people can queue up, then put this whole
// thing in an `if (signal.CurrentCount < myLimit)` block, and optionally
// disable the button once limit has been reached, and re-enable it right
// before the `WaitAsync` call above.
}
}
While Dax Fohl's answer works, it seems like you've got a problem in your design. I think you're violating the Single Responsibility Principle by having too much business logic going on in the Form class.
I'd recommend factoring out the business logic into its own class. Then rather than running through everything in a loop, you simply have the button click event process the next record and display the result. Here's an example of what I mean:
public partial class Form1 : Form
{
private readonly DataProcessor dataProcessor = new DataProcessor();
public Form1()
{
this.InitializeComponent();
}
private void button1Next_Click(object sender, EventArgs e)
{
this.buttonNext.Enabled = false;
this.ProcessNext();
}
private async void ProcessNext()
{
string s = await this.dataProcessor.ProcessNext();
this.textBoxP1Text1.Text = s;
this.buttonNext.Enabled = true;
}
}
public class DataProcessor
{
private readonly Random r = new Random(); // Or reader or whatever.
public async Task<string> ProcessNext() // Just using `string` as an example.
{
await Task.Delay(1000);
return this.r.Next().ToString();
}
}
I think this will be easier to understand and more maintainable in the future. When a new team member looks at semaphore stuff (or your future self), it'll be hard to understand/remember what the point of all that was. Here, you just have a local function that does one thing and is easy to follow.

How to display data received from serial port in a textbox without the text disappearing in Visual Studio C#?

So, I'm trying to develop a simple application in visual C# which gets data from serial port and displays it in a textbox (to monitor temperature). I'm acquiring and displaying the data successfully, using the DataReceived event to update a global string variable and a timer to update the text field on my text box, as shown:
private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
try
{
globalVar.updateTemp = port.ReadLine(); //This is my global string
}
catch (IOException)
{
}
catch (InvalidOperationException)
{
}
catch (TimeoutException)
{
}
}
private void timer1_Tick(object sender, EventArgs e)
{
tempDisplayBox.Text = globalVar.updateTemp; //This is my textbox updating
}
The only issue I have is that the value shown in the textbox keeps flashing, making it hard to read. My timer is set to trigger every 10 ms (which should be fast enough, right?). Is there any way to make it more stable? I realize this may be a newb question, but to be fair I am a newb :) Any help is appreciated! Thanks!
Do you really need it updating every 10ms? What about every 500 ms or if not that then 100ms. 100ms will require your update method run 10 times less and therefore update 10 times less. The flickering you are expiriencing is due to the refresh speed. You could create custom method which will only update the temp only when target Label or textBox value is different than source port. But that will only sort the flickering when temp is steady, when temp will start vary it will bring back the flickering. Good luck ;-)
UPDATE
Hi I tried to reproduce the conditions and could not make my textbox nor Label flash. The way I tested it was by assigning int ntick = 0; and then increment the ++ntick; inside of the timer_tick method. The results didn't make any of the controls flash and were updated even every milisecond at some point. I also tried string.Format to put some load on the method. Is your app responsive?
The trick is to use double buffering. This way the operating system will redraw the Control off-screen, and only show the control when it is fully redrawn.
I have had the same problem, and solved it by extending the TextBox control like this:
public FastLogBox()
{
InitializeComponent();
_logBoxText = new StringBuilder(150000);
timer1.Interval = 20;
timer1.Tick += timer1_Tick;
timer1.Start();
SetStyle(ControlStyles.DoubleBuffer, true);
}
void timer1_Tick(object sender, EventArgs e)
{
if (_timeToClear)
{
_logBoxText.Clear();
_timeToClear = false;
}
if (_logQueue.Count <= 0) return;
while (!_logQueue.IsEmpty)
{
string element;
if (!_logQueue.TryDequeue(out element)) continue;
{
_logBoxText.Insert(0, element + "\r\n");
}
}
if (_logBoxText.Length > 150000)
{
_logBoxText.Remove(150000, _logBoxText.Length - 150001);
}
Text = _logBoxText.ToString();
}
public new void Clear()
{
_timeToClear = true;
while (!_logQueue.IsEmpty)
{
string element;
_logQueue.TryDequeue(out element);
}
}
public void AddToQueue(string message)
{
_logQueue.Enqueue(message);
}
}
I also use a timer and a concurrentQueue to avoid using Invoke to update the control from another thread. I also use a StringBuilder to prepare the string before putting it into the TextBox. StringBuilder is faster when building larger strings.
You can use ReadExisting() to read the whole data at a time.
You need to handle DataReceived Event of SerialPort
serialPort1.ReadExisting();
Sample:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
String myData=serialPort1.ReadExisting();
}
Example Code: Here i would like to show you the code to Read Data(RFID Tag Code which is basically of length 12)
String macid = "";
private void DoWork()
{
Invoke(
new SetTextDeleg(machineExe ),
new object[] { macid });
macid = "";
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string str1;
macid += serialPort1.ReadExisting();
if (macid.Length == 12)
{
macid = macid.Substring(0, 10);
Thread t = new Thread(new ThreadStart(DoWork));
t.Start();
}
}
public void machineExe(string text)
{
TextBox1.Text=text;
}
Thank you so much for the answers! I found a way to work around this issue:
Instead of replacing the contents of my textbox by rewriting the TextBox.Text property - which, as HenningNT implied, refreshes the control and causes the flickering - I'm now using the TextBox.AppendText method. Though, as I want to display only one line of data at a time, I use the textbox in multiline mode and the Environment.NewLine to jump to a new line before appending the text. As for the method of updating, I've gone back to using the timer because with the invoke method was crashing my application when I close the form, for some reason. Also, enabling double buffering didn't do me much good, although I guess I was doing it wrong... It still flickers a bit, but it's much better now :) I know this is not really a perfect solution (much more of a workaround), so I'll keep looking for it. If I find it, I'll be sure to update it here ;) My code:
private void timer1_Tick(object sender, EventArgs e) //Timer to update textbox
{
if (tempDisplayBox.Text != globalVar.updateTemp) //Only update if temperature is different
{
try
{
tempDisplayBox.AppendText(Environment.NewLine);
tempDisplayBox.AppendText(globalVar.updateTemp);
}
catch (NullReferenceException)
{
}
}
}

C# Timers and repeating code

I am writing an IRC bot that needs to advertise something every few minutes to the channel. I made an attempt with the Timer but it's so messy I can't paste it here because it will be unreadable, I can post the entire file's code to pastebin though.
What needs to be looped/timed to send every 5 minutes to the channel:
public static void ChannelAdvertise(object sender, IrcEventArgs e)
{
string advertiseStream = "Live right now: ";
foreach (Monitor stream in streams)
{
if (stream.streamOnline)
{
advertiseStream += (char)3 + "03" + stream.nick +
(char)15 + " - " + stream.weblink() + " ";
}
}
irc.SendMessage(SendType.Message, e.Data.Channel,
advertiseString);
}
Just that piece of code needs to be sent to the channel every 10 minutes. Any help/pointers would be appreciated.
class Bot
{
private static System.Timers.Timer advertiseTimer;
static void Main(string[] args)
{
advertiseTimer = new System.Timers.Timer(60000);
advertiseTimer.Elapsed += new ElapsedEventHandler(advertiseTimer_Elapsed);
advertiseTimer.Start();
}
public static void ChannelAdvertise(object sender, IrcEventArgs e)
{
string advertiseStream = "Live right now :";
foreach (Monitor stream in streams)
{
if (stream.streamOnline)
{
advertiseStream += (char)3 + "03" + stream.nick + (char)15 + " - " + stream.weblink() + " ";
}
irc.SendMessage(SendType.Message, e.Data.Channel, advertiseStream);
}
}
static void advertiseTimer_Elapsed(object sender, ElapsedEventArgs e)
{
ChannelAdvertise();
}
}
You can use DispatcherTimer of the System.Windows.Threading namespace.
You can refer toMSDN Reference. Set the interval of this timer set as per your requirement and you can write this code in the timer tick event handler. Hope this helps
You should use the Dispatch Timer as #Aashish Thite has suggested.
additionally, here is your problem: you define
public static void ChannelAdvertise(object sender, IrcEventArgs e)
{
}
to recieve two arguments, but you are trying to call it with no arguments:
static void advertiseTimer_Elapsed(object sender, ElapsedEventArgs e)
{
ChannelAdvertise();
}
this is what your error message "No overload for method 'ChannelAdvertise' takes 0 arguments." is telling you.
that said, you have two options: call it with the required 2 arguments (as you have defined it), or redefine it so that it does not take any arguments (as you are calling it from the advertiseTimer_Elapsed method)
basically, you need to pass in a sender object and an IrcEventArgs argument.
so, you could call it like so:
static void advertiseTimer_Elapsed(object sender, ElapsedEventArgs e)
{
// make a new one, or get it form some collection, or whatever
IrcEventArgs args = new IrcEventArgs( /* initialize the object */ );
ChannelAdvertise(this,args);
}
or you could redefine like so:
// looking at your code, you are only using the channel from the IrcEventArgs.Data object
public static void ChannelAdvertise( channelobject channel )
{
}
Try:
void Main()
{
System.Threading.Timer timer = new System.Threading.Timer(
Callback,
channel,
0,
10 * 60 * 1000);
}
void Callback(object state)
{
ChannelAdvertise(this, (Channel)state);
}
I have recently written an article that may be just what you are looking for. It demonstrates in c# a generic polling component that runs at a specified interval and uses a background thread to perform the user action specified.
Sample usage:
IPoller poller = new UrlPoller(args[0], TimeSpan.FromSeconds(7));
IPolling pollingComponent = new Polling.Core.Polling(poller);
pollingComponent.SubscribeForPollingUpdates(PollingAction);
pollingComponent.Start();
For the code and complete sample see:
http://www.avantprime.com/blog/24/an-example-of-repeating-code-using-a-worker-thread-without-using-timers-c

Stopping keys from repeating (C#)

In this application, I need to be able to stop the response from a key which is held down in order to prevent unnessecary data from entering the output. The problem I'm having is, using the methods in my code below does prevent the keys from repeating, but it also stops them from being responsive enough - as the users are hitting the keys very quickly.
I'm not sure if it's my hardware, api restriction or a problem with my code, but the routines I have below do not simply come round fast enough to work without making the program impossible to use. A way of identifying if a key is being actively held down (and for how long) would also help another feature for the program and solve this current issue.
Any ideas?
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = isKeyDown;
isKeyDown = true;
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
isKeyDown = false;
}
private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
if (!isStreamPlaying) return;
if (e.KeyChar.Equals('d') || e.KeyChar.Equals('j'))
{
//red hit
SoundPlayer hitSounds = new SoundPlayer(taikoLiveMapper.Properties.Resources.normal_hitnormal);
hitSounds.Play();
outputlist.Add(string.Format("320,240,{0},1,{1}", ms, 0));
lastms = ms;
}
else if (e.KeyChar.Equals('s') || e.KeyChar.Equals('k'))
{
//blue hit
SoundPlayer hitSounds = new SoundPlayer(taikoLiveMapper.Properties.Resources.normal_hitclap);
hitSounds.Play();
outputlist.Add(string.Format("320,240,{0},1,{1}", ms, 8));
lastms = ms;
}
}
You can use GetKeyState to find out if a key is down and use that to track the keys:
[DllImport("user32.dll")]
static extern short GetKeyState(int key);
static bool IsKeyPressed(Keys key)
{
short state = GetKeyState((int)key);
return ((state & 128) != 0);
}
int i = 0;
Dictionary<Keys, DateTime> downSince = new Dictionary<Keys, DateTime>();
private void UpdateKeyStates()
{
foreach (var entry in downSince.ToArray())
{
if (!IsKeyPressed(entry.Key))
downSince.Remove(entry.Key);
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
UpdateKeyStates();
if (!downSince.ContainsKey(e.KeyCode))
{
downSince.Add(e.KeyCode, DateTime.UtcNow);
i++;
}
Text = i.ToString() + " " +(int)(DateTime.UtcNow - downSince[e.KeyCode]).TotalMilliseconds;
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
UpdateKeyStates();
}
This example counts i up every time a key is pressed, and shows for how long it has been pressed. It uses GetKeyState instead of tracking KeyDown/KeyUp since you might miss those messages if something else has focus.
According to the documentation, "[d]uplicate KeyDown events occur each time the key repeats, if the key is held down, but only one KeyUp event is generated when the user releases the key."
So the simplest solution is to ignore a repeated KeyDown event unless its corresponding KeyUp event has been seen.
Just worked for me.
Use Timers instead: initialize timers, one for each "action" (e.g. pressing d/j or s/k) move the red hit/blue hit code inside the timer and instead of your current code, have this:
if (e.KeyChar.Equals('d') || e.KeyChar.Equals('j'))
{
//red hit
if (!tmrRedHit.Enabled)
tmrRedHit.Enabled = true;
}
else if (e.KeyChar.Equals('s') || e.KeyChar.Equals('k'))
{
//blue hit
if (!tmrBlueHit.Enabled)
tmrBlueHit.Enabled = true;
}
And in the timers Elpased event also set their Enabled to false after the code is executed.

Categories

Resources