using PlayStateChange event to loop the song in MediaPlayer - c#

I'm trying to implement the code that will loop current song countless times.
That's my code:
private void axWindowsMediaPlayer1_PlayStateChange(object sender, AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
if (e.newState == 8) // media ended
{
if (repeat)
{
axWindowsMediaPlayer1.Ctlcontrols.currentPosition = 0;
//axWindowsMediaPlayer1.Ctlcontrols.previous();
//axWindowsMediaPlayer1.Ctlcontrols.playItem(axWindowsMediaPlayer1.Ctlcontrols.currentItem);
}
}
}
setting currentPosition = 0 is working fine if I debug the code on the next line.
But after debug - new event is triggered(event 9(Transitioning) followed by event 3(Playing)) and the next song starts to play!
how do I prevent this from happening?
basically that is what happening right now:
event 8 triggered (Media End)
set position 0
event 9 triggered (Transitioning)
event 3 triggered (Playing)
Edit: after messing around with my code I have finally managed to break the chain of Event changes.
public bool ended = false;
public WMPLib.IWMPMedia latest_song;
private void axWindowsMediaPlayer1_PlayStateChange(object sender, AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
if (ended)
{
axWindowsMediaPlayer1.Ctlcontrols.playItem(latest_song);
ended = false;
return;
}
if (e.newState == 8) // media ended
{
if (repeat)
{
ended = true;
latest_song = axWindowsMediaPlayer1.Ctlcontrols.currentItem;
}
}
}
I don't know if the code can get any better than this..

bool ended = false;
bool skipnext = false;
bool skipnextnext = false;
bool skipextra = false;
private void axWindowsMediaPlayer1_PlayStateChange(object sender, AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
if (ended)
{
skipnext = true;
ended = false;
axWindowsMediaPlayer1.Ctlcontrols.playItem(latest_song);
return;
}
if (e.newState == 6) // buffering
{
skipextra = true;
}
if (e.newState == 8) // media ended
{
if (repeat)
{
if (currect_album.Songs.Count < 2) { return; }
ended = true;
latest_song = current_song;
return;
}
}
else if (e.newState == 3) // playing
{
// these "skips" are necessary for scenario: "repeat: on", "click: next"
if (skipnext) { skipnext = false; skipnextnext = true; return; }
if (skipnextnext) { skipnextnext = false; return; }
// Buffering adds 1 more skip! Ask Microfost why ;)
if (skipextra) { skipextra = false; return; }
current_song = axWindowsMediaPlayer1.Ctlcontrols.currentItem;
// your stuff
}
}
the first solution worked fine for half a day ;)
the second solution worked for "repeat", but did not work in case if user clicked "next"
the third and final (I hope) solution finally works in situations when "repeat" is needed, plus it skips the song if "next/prev" command is triggered!
the fourth edition! added extra "skip" if buffering kicked in.. Buffering messed up my code a bit, but now it's all working. Additionally, I've added "1 song in a playlist" checker.
P.S. Microsoft coders - if you see this post - please, add "on_next" and "on_previous" status! Thanks

Related

Button.Enabled not working on first of several buttons in BackgroundWorker

I have several buttons that I want turned off/on at different times. I put these into a function for simplicity. Enabling the buttons is called by a BackgroundWorker so that I can update a camera view at the same time as listening to a Serial port for an 'enable' signal. Disabling the controls is called by the button click, while enable is handled by the BackgroundWorker (it watches for the Serial port signal). All buttons re-enable, except for the first one listed in enableControls();. If I change the order of enableControls() then the button at top is left disabled. What is causing this? I would think that either all buttons or no buttons would be re-enabled.
private void btnLeft_Click(object sender, EventArgs e) // All buttons are similar, just different output to port
{
disableControls();
hardWorker.RunWorkerAsync();
int stepSize = Convert.ToInt32(ddStepSize.Text);
string outString;
if (moveSampHome == false)
{
outString = "#MOVER" + stepSize;
posX = posX - RcDrawSlide.Width * stepSize / (1000 * Convert.ToInt32(slideSizeX));
}
else { outString = "#MSMPR" + stepSize; }
port.Write(outString + "\n");
}
private void hardWorker_DoWork(object sender2, DoWorkEventArgs f)
{
arduinoIn = "0";
for (int i = 0; i == 0;)
{
if (arduinoIn != null)
{
if (arduinoIn.Contains("1"))
{
i = 1;
arduinoIn = "0";
}
}
}
port.Write("1");
}
private void hardWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
enableControls();
}
private void enableControls()
{
btnBack.Enabled = true; // Swap this for any other button - that one will not be enabled.
btnForward.Enabled = true;
btnLeft.Enabled = true;
btnRight.Enabled = true;
}
private void disableControls()
{
btnBack.Enabled = false;
btnForward.Enabled = false;
btnLeft.Enabled = false;
btnRight.Enabled = false;
}
If I add a button that runs enableControls() on click everything is reenabled, so I know this is due to having it run from a BackgroundWorker. I think that BackgroundWorker is necessary, though. I'm just unsure why it's only the first btn.Enabled = true; that doesn't work. Is there a workaround for this?
For hardWorker_DoWork() try something more like:
private void hardWorker_DoWork(object sender2, DoWorkEventArgs f)
{
arduinoIn = "0";
while (arduino == null || !arduinoIn.Contains("1")) {
System.Threading.Thread.Sleep(0); // or try a SMALL number like 50!
}
arduinoIn = "0";
port.Write("1");
}
Is it possible the buttons are being enabled again when data is received on the port? You haven't shown any code relating to that side...

While Loop Function after a button is pressed and stop looping when another button is pressed C#

In my Windorm Form application, I have two buttons which a loop function will begin to do the work when button1 is pressed and stop executing after button2 is pressed. How could i do this to prevent my GUI from non-responsive.
How could i insert command while(button2.clicked != true)
My code for button 1:
private async void EmoStart_Click_1(object sender, EventArgs e)
{
//var repeat = "true";
string imageFilePath = "C:\\Users\\Administrator\\source\\repos\\FaceDetection\\FaceDetection\\test3.jpg";
while (VoiceStart_Click_2 != "true")
{
var image = pictureBox1.Image;
image = resizeImage(image, new Size(1209, 770));
image.Save(imageFilePath);
if (File.Exists(imageFilePath))
{
var Emo = await FaceEmotion.MakeAnalysisRequest(imageFilePath);
if (Emo[0].FaceAttributes.Emotion.Anger >= 0.5)
{
EmoBox.Text = "Anger, Bad Driving Condition, Soft Music will be played";
}
else if (Emo[0].FaceAttributes.Emotion.Contempt >= 0.5)
{
EmoBox.Text = "Contempt, Bad Driving Condition, Soft Music will be played";
}
else if (Emo[0].FaceAttributes.Emotion.Disgust >= 0.5)
{
EmoBox.Text = "Disgust, Bad Driving Condition, Soft Music will be played";
}
else if (Emo[0].FaceAttributes.Emotion.Fear >= 0.5)
{
EmoBox.Text = "Fear, Bad Driving Condition, Soft Music will be played";
}
else if (Emo[0].FaceAttributes.Emotion.Happiness >= 0.5)
{
EmoBox.Text = "Happiness, Good Driving Condition";
}
else if (Emo[0].FaceAttributes.Emotion.Neutral >= 0.5)
{
EmoBox.Text = "Neutral, Good Driving Condition";
}
else if (Emo[0].FaceAttributes.Emotion.Sadness >= 0.5)
{
EmoBox.Text = "Sadness, Bad Driving Condition, Rock Music will be played";
}
else if (Emo[0].FaceAttributes.Emotion.Surprise >= 0.5)
{
EmoBox.Text = "Happiness, Bad Driving Condition, Soft Music will be played";
}
else
{
EmoBox.Text = "Stable Condition, Good Driving Condition";
}
}
}
While for my button2 code:
private async void VoiceStart_Click_2(object sender, EventArgs e)
{
string command = await Voice.RecognizeSpeechAsync();
VoiceBox.Text = command;
}
Thank you!
You have to run your loop in a separate thread. For example, you can run it asynchroniously. Something like this:
// start the loop
private async void button1_Click(object sender, EventArgs e)
{
LoopStopped = false;
await StartLoopAsync();
}
// put yor while loop here
private Task StartLoopAsync()
{
return Task.Run(() =>
{
while (LoopStopped == false)
{
var date = DateTime.Now;
System.Diagnostics.Debug.WriteLine(date);
}
System.Diagnostics.Debug.WriteLine("Thread stopped.");
});
}
// stop the loop
private void button2_Click(object sender, EventArgs e)
{
LoopStopped = true;
}
Where LoopStopped is global boolean variable.
All the operations you put into the EmoStart_Click_1 is running synchronously, except:
FaceEmotion.MakeAnalysisRequest(imageFilePath)
thus the interface (UI) is frozen.
It is not enough to change the signature of the method to async as you did. You must tell the compiler, which other parts should be awaited. You want your whole while function async!
private async void EmoStart_Click_1(object sender, EventArgs e)
{
EmoStart.Enabled = false; //I assume EmoStart is the name of your button
await Task.Factory.StartNew(Loop);
EmoStart.Enabled = true;
}
private void Loop() //since this method doesn't have async in its signature "var Emo = await FaceEmotion.MakeAnalysisRequest(imageFilePath);" won't compile, so you should change to the synchronous equivalent "var Emo = FaceEmotion.MakeAnalysisRequest(imageFilePath).Result;" --> note that it won't block due to "Task.Factory.StartNew".
{
string imageFilePath = "C:\\Users\\Administrator\\source\\repos\\FaceDetection\\FaceDetection\\test3.jpg";
while (...)
{
// do your stuff
}
Then you can decide how you want to cancel the while loop.
Option 1.: you could use a global, bool variable:
private bool emotionsShouldBeProcessed;
and you set it true in EmoStart_Click_1 and false like this:
private async void VoiceStart_Click_2(object sender, EventArgs e)
{
VoiceStart.Enabled = false;
emotionsShouldBeProcessed = false;
// start and await voice stuff
VoiceStart.Enabled = true;
}
Option 2.: you could use a CancellationToken to track if cancel is necessary.
CancellationTokenSource cSource;
private async void EmoStart_Click_1(object sender, EventArgs e)
{
EmoStart.Enabled = false;
cSource = new CancellationTokenSource();
await Task.Factory.StartNew(() => Loop(cSource.Token));
EmoStart.Enabled = true;
}
private void Loop(CancellationToken cToken)
{
string imageFilePath = "C:\\Users\\Administrator\\source\\repos\\FaceDetection\\FaceDetection\\test3.jpg";
while (true)
{
if (cToken.IsCancellationRequested)
break;
// otherwise do your stuff
}
// some clean up here if necessary
}
private async void VoiceStart_Click_2(object sender, EventArgs e)
{
VoiceStart.Enabled = false;
cSource.Cancel();
VoiceStart.Enabled = true;
}
So far so good! But your code will crash
It will crash whenever you want to set EmoBox.Text as this can happen only on the UI thread. To avoid this, you need to ask the UI thread to interrupt until Textbox/Label/etc operations are going on, like this:
this.Invoke((MethodInvoket)delegate
{
EmoBox.Text = "...";
});
Edit:
I would also check the Emo array whether it is empty since face- and emotion recognitions not always succeeds! Thus, Emo[0] can result in "Index was out of range" exception. Following code makes sure it isn't empty:
var Emo = ...;
if (Emo.Length > 0)
{
if (...)
// use Emo[0]
else if (...)
// use Emo[0] differently
}
Let me know if there is anything unclear.
You can create a bool variable and make the loop while your variable is true.
So set the variable to true, when button1 is clicked.
Then your while would look like this:
while(myBoolVariable)
And when button2 is clicked you can change the value to false and the while will stop.
You can use a global variable(bool is a good choice)
when VoiceStart_Click_2 change the variable
and do check the variable when EmoStart_Click_1 is clicked
if (variable==true)
{
var Emo = await FaceEmotion.MakeAnalysisRequest(imageFilePath);
if (Emo[0].FaceAttributes.Emotion.Anger >= 0.5)
{
EmoBox.Text = "Anger, Bad Driving Condition, Soft Music will be played";
}
...
}

Is it possible to have a while loop slowed?

Rewrite: I'm writing a program to get a better understanding of C# and taking user input in. Right now I'm using WFA in C# with a textbox where I put the text of what I want pasted/typed out. Then I have two other boxes that have an interval input and a amount input. Everything worked perfectly fine until I added the amount input, I've added a while loop to a timer that is used to type/paste the text. However, for some reason that I'm not sure of.. The program disregards any input for the interval.
I have it basically setup like this
private void timerPaste_Tick(object sender, EventArgs e)
{
interval = Int32.Parse(interNum.Text);
timerPaste.Interval = interval;
if (typeModebool == true && pasteModebool == false)
{
while (amount > 0) //Need to find a way to make GUI responsive during while loop.
{
SendKeys.Send(textBox1.Text);
SendKeys.Send("{Enter}");
amount--;
}
timerPaste.Enabled = false;
amount = Int32.Parse(amountSetBox.Text);
}
if (pasteModebool == true && typeModebool == false)
{
while (amount > 0) //Need to find a way to make GUI responsive during while loop.
{
Clipboard.SetText(textBox1.Text);
SendKeys.Send("^{c}");
SendKeys.Send("^{v}");
SendKeys.Send("{Enter}");
amount--;
}
timerPaste.Enabled = false;
amount = Int32.Parse(amountSetBox.Text);
}
}
something like that..?
timerPaste.Elapsed += OnTickEvent;
private async void OnTickEvent(Object source,EventArgs e)
{
interval = Int32.Parse(interNum.Text);
timerPaste.Interval = interval;
if (typeModebool == true && pasteModebool == false)
{
while (amount > 0) //Need to find a way to make GUI responsive during while loop.
{
SendKeys.Send(textBox1.Text);
SendKeys.Send("{Enter}");
amount--;
}
timerPaste.Enabled = false;
amount = Int32.Parse(amountSetBox.Text);
}
if (pasteModebool == true && typeModebool == false)
{
while (amount > 0) //Need to find a way to make GUI responsive during while loop.
{
Clipboard.SetText(textBox1.Text);
SendKeys.Send("^{c}");
SendKeys.Send("^{v}");
SendKeys.Send("{Enter}");
amount--;
}
timerPaste.Enabled = false;
amount = Int32.Parse(amountSetBox.Text);
}
}
I actually was able to fix this, the problem was that the while loop listened to the timer the first time, then after that just started going off on it's own. I switched it to a if, with an else statement and everything works as intended now.
int i = 0;
private void timerPaste_Tick(object sender, EventArgs e)
{
if (typeModebool == true && pasteModebool == false) //Check what mode is being used
{
if (i < amount) //If runs until i == amount
{
SendKeys.Send(textBox1.Text);
SendKeys.Send("{Enter}");
amount--;
}
else
{
timerPaste.Enabled = false;
}
}
if (pasteModebool == true && typeModebool == false)
{
if (i < amount)
{
Clipboard.SetText(textBox1.Text);
SendKeys.Send("^{c}");
SendKeys.Send("^{v}");
SendKeys.Send("{Enter}");
amount--;
}
else
{
timerPaste.Enabled = false;
}
}
}

Visual Studio 2017 Create method for Multiple button click events on form class

Hi all firstly thanks for taking the time to read. I am trying to clean up my code and make it more efficient and want to put some code into one method that will be use for both boutton clicks.
private void buttonNext_Click(object sender, EventArgs e)
{
y += 1;
QuestionsFromDb.qid += 1;
qdb.MultiChoiceFunctionsLight();
Questions();
FlagColour();
//Shows visability for next and previous buttons
if (y >= 9)
{
buttonNext.Visible = false;
buttonEndQuiz.Visible = true;
}
if (y == 1)
{
buttonPrevious.Visible = true;
}
}
private void buttonNext_Click(object sender, EventArgs e)
{
y += 1;
QuestionsFromDb.qid += 1;
qdb.MultiChoiceFunctionsLight();
Questions();
FlagColour();
//Shows visability for next and previous buttons
if (y >= 9)
{
buttonNext.Visible = false;
buttonEndQuiz.Visible = true;
}
if (y == 1)
{
buttonPrevious.Visible = true;
}
}
So as you can see i hate repeating my code but with the Button_Click methods im not sure how to set the click to true, false or clicked. This is my idea.
private void ButtonClicked()
{
if(buttonNext_Click == SOMETHING!)
{
if (y >= 9)
{
buttonNext.Visible = false;
buttonEndQuiz.Visible = true;
}
if (y == 1)
{
buttonPrevious.Visible = true;
}
}
else
{
//Shows visability for next and previous buttons
if (y == 0)
{
buttonPrevious.Visible = false;
}
if (y < 10)
{
buttonNext.Visible = true;
}
}
}
The code works fine but im am starting to get code OCD i am under the impression that doing it this way will help keep my code more readable and maybe make processing and memory run more effricient. Any ideas are greatly appreciated. Thanks

How do I enable this timer in C#?

I've started a course of c# and I cant get my timer to run. Its probably pretty simple and I've just missed something here. Basically I have a button to start and stop a traffic light sequence. I wanted an interval of 1 second. Heres what I've written. It doesn't work as intended when I press start. Thank you.
}
public int counter = 0;
private void rbStart_CheckedChanged(object sender, EventArgs e)
{
counter++;
if (counter == 1)
{
pbRed.Visible = true;
pbAmber.Visible = false;
pbGreen.Visible = false;
}
else if (counter == 2)
{
pbRed.Visible = true;
pbAmber.Visible = true;
pbGreen.Visible = false;
}
else if (counter == 3)
{
pbRed.Visible = false;
pbAmber.Visible = false;
pbGreen.Visible = true;
}
else if (counter == 4)
{
pbRed.Visible = false;
pbAmber.Visible = true;
pbGreen.Visible = false;
}
else if (counter == 5)
{
pbRed.Visible = true;
pbAmber.Visible = false;
pbGreen.Visible = false;
}
else
{
counter = 0;
}
}
private void rbStop_CheckedChanged(object sender, EventArgs e)
{
pbRed.Visible = false;
pbAmber.Visible = false;
pbGreen.Visible = false;
}
private void Form1_Load(object sender, EventArgs e)
{
Light_timer.Tick += new EventHandler(rbStart_CheckedChanged);
Light_timer.Interval = 1000;
}
}
The Interval property merely designates how much time elapses between Tick events. You might want to consider a separate variable to track the "state" of your light, and then "bump" that variable with each "Tick" in your event handler. Then just adjust your UI elements to reflect the proper state of your traffic light. You might have a "stopped" state, a "careful" state, and a "green" state, and your light might just "cycle" between each on on each tick. I'll leave you to write the details as it appears to be an assignment. Good luck.
I think you probably misinterpreted how the Timer works. The Timer.Tick Event fires when the Interval has elapsed. Interval is used by the Timer to determine how long to run between ticks. Its value is never changed by the Timer. In fact, a System.Windows.Forms.Timer has no way to retrieve elapsed time, which means you'll need a state tracking mechanism of your own that doesn't depend on that. Take a good look at the example on the page I referenced above and make sure you understand how it works, then give it another shot.

Categories