How to stop sound playing again when navigating to another form - c#

I'm currently writing an app in c#, where this app will play background music once it started. And the button clicking sound and music shall stop when I press the mute button, or play again when unmuted.
namespace WindowsFormsApp1
{
public partial class mainMenu : Form
{
public bool sound = true; //variable of whether the sound is on or not
System.Media.SoundPlayer bgm = new System.Media.SoundPlayer(#"C:\Users\User\Desktop\HCI\New folder\bgm.wav");
public bool soundval //for other form to access sound variable value
{
get
{
return sound;
}
set
{
sound = value;
}
}
public mainMenu()
{
InitializeComponent();
play();
}
public void play() //function to play music
{
bgm.PlayLooping();
}
public void stop//function to stop music
{
bgm.Stop();
}
private void soundButton_Click(object sender, EventArgs e) //button that mute or unmute the sound (by changing sound value)
{
if (sound)
{
bgm.Stop();
sound = false;
}
else
{
var p2 = new System.Windows.Media.MediaPlayer();
p2.Open(new System.Uri(#"C:\Users\User\Desktop\HCI\New folder\button.wav"));
p2.Play();
play();
sound = true;
}
}
private void easy_Click(object sender, EventArgs e) //proceed to other form
{
if (sound)
{
var p2 = new System.Windows.Media.MediaPlayer();
p2.Open(new System.Uri(#"C:\Users\User\Desktop\HCI\New folder\button.wav"));
p2.Play();
}
Easy game2 = new Easy();
game2.Tag = this;
game2.Show(this);
Hide();
}
}
}
Until here, the app works fine. But then if I muted the sound and proceed to other form, the music will play again, the button sound will also play if I press anything on any button on that particular form.
Partial code of my other form(easy_Click):
mainMenu mmenu = new mainMenu();
private void thirdchoice_Click(object sender, EventArgs e)
{
if (mmenu.soundval) //to get the value from first form, button sound should play only when value is true
{
var p2 = new System.Windows.Media.MediaPlayer();
p2.Open(new System.Uri(#"C:\Users\User\Desktop\HCI\New folder\button.wav"));
p2.Play();
}
if (ansNum == choice3num) goodnews();
else badnews();
}
Not only music, but the button clicking sound is playing as well. It looks like the sound value set to true when proceed to second form, even though it was false in first form.
I been stuck on this issue for a long time, changing here and there, but nothing work.
Really appreciate if someone could help on this. Thanks!

Related

Playback of Audio file on key press is delayed

I made a small program that plays back sounds when you press keys.
It uses a global keyboard hook to capture key presses and play back wav files using NAudio.
However playback lags on some computers and plays a few seconds after the key has been pressed. Could this be an HDD/SSD or CPU speed issue or is it a programming issue? What can be done to solve it?
Tried on 4 computers, 2 lagged, 2 did not.
My SSD/i7 - did not lag.
My HDD/Core2Duo - did not lag.
Friend's SSD/i7 - lagged.
Friend's HDD/i7 - lagged.
Program
Info
https://github.com/MattMcManis/Ink
Source
https://github.com/MattMcManis/Ink/tree/master/source/Ink
Download
https://github.com/MattMcManis/Ink/releases
App.xaml.cs
Start the Keyboard Listener.
// Application Startup
//
private void Application_Startup(object sender, StartupEventArgs e)
{
th = new Thread(() => RunKeyListener());
th.IsBackground = true;
th.Start();
th.Join();
}
// Keyboard Listener
//
private void RunKeyListener()
{
KListener.KeyDown += new RawKeyEventHandler(KListener_KeyDown);
}
// Key Down
//
void KListener_KeyDown(object sender, RawKeyEventArgs args)
{
Sound.KeyPressed(args);
}
MainWindow.xaml.cs
KeyboardListener Class is in here.
https://gist.github.com/Ciantic/471698
Sound.cs
private static string wavKeyChar = "Sounds\\character.wav";
private static string wavKeyNum = "Sounds\\number.wav";
public static WaveFileReader wav = null;
public static WaveOutEvent output = null;
// Key Pressed
//
public static void KeyPressed(RawKeyEventArgs args)
{
// Letters
if (args.Key >= Key.A && args.Key <= Key.Z)
{
PlaySound(wavKeyChar);
}
// Numbers
else if (args.Key >= Key.D0 && args.Key <= Key.D9)
{
PlaySound(wavKeyNum);
}
}
// Play Sound
//
public static void PlaySound(string sound)
{
wav = new WaveFileReader(sound);
output = new WaveOutEvent();
output.NumberOfBuffers = 3;
output.DesiredLatency = 500;
output.Init(wav);
output.Play();
}
Try to show a MessageBox or something, to understand if the delayed event is the sound itself or the keypress event.
If the MessageBox shows before the sound is played then it's not a problem of keyboard hook library.

(C# Windows Forms Apps) How to Restart App

I just finished an exercise from Head First C# where I built a Typing Game. The book leaves it to the reader to figure out how to make it so the player can start a new game once they've lost. After the user loses the game, the window shows the message "Game Over". I would like to have a new window pop up and ask the user if they would like to play again once they've closed out of the game over screen. I'd like there to be two buttons; one that says "no" and one that says "yes". What I'm stuck on is how I should (or would) go about restarting the app if the user decides they want to play again. I'll copy and paste my code below:
namespace _7HeadFirstProject
{
public partial class Form1 : Form
{
Random random = new Random();
Stats stats = new Stats();
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
// Add a random key to the ListBox
listBox1.Items.Add((Keys)random.Next(65, 90));
if (listBox1.Items.Count > 7)
{
listBox1.Items.Clear();
listBox1.Items.Add("Game Over");
timer1.Stop();
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
// If the user pressed a key that's in the ListBox...
// ... remove it and then make the game a little faster
if (listBox1.Items.Contains(e.KeyCode))
{
listBox1.Items.Remove(e.KeyCode);
listBox1.Refresh();
if (timer1.Interval > 400)
timer1.Interval -= 10;
if (timer1.Interval > 250)
timer1.Interval -= 7;
if (timer1.Interval > 100)
timer1.Interval -= 2;
difficultyProgressBar.Value = 800 - timer1.Interval;
// The user pressed a correct key, so update the Stats object...
// ...by calling its Update() method with the argument true
stats.Update(true);
}
else
{
// The user pressed an incorrect key, so update the Stats object...
// ...by calling its Update() method with the argument false
stats.Update(false);
}
// Update the labels on the StatusStrip
correctLabel.Text = "Correct: " + stats.Correct;
missedLabel.Text = "Missed: " + stats.Missed;
totalLabel.Text = "Total: " + stats.Total;
accuracyLabel.Text = "Accuracy: " + stats.Accuracy + "%";
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
MessageBox.Show("Would you like to play again?");
if
}
}
}
DIFFERENT CLASS:
namespace _7HeadFirstProject
{
class Stats
{
public int Total = 0;
public int Missed = 0;
public int Correct = 0;
public int Accuracy = 0;
public void Update(bool correctKey)
{
Total++;
if (!correctKey)
{
Missed++;
}
else
{
Correct++;
}
Accuracy = 100 * Correct / Total;
}
}
}
You have the whole game working so leave that form alone. Add another form to your project and then set the new form as the startup form. You can set it as the startup form by opening Program.cs and modifying this line:
// Instead of Form1 put the name of your new form
Application.Run(new Form1());
Double click the new form and put this code in it:
// Note: Your load method may have a different name.
private void Form2_Load(object sender, EventArgs e)
{
this.StartNewGame();
}
private void GameForm_FormClosed(object sender, FormClosedEventArgs e)
{
if (MessageBox.Show("Continue?", "Continue?", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
this.StartNewGame();
}
}
private void StartNewGame()
{
// Your game form may have a different name so change this to that name
var gameForm = new Form2();
gameForm.FormClosed += GameForm_FormClosed;
gameForm.Show();
}
Every time the user presses the yes button on the dialog, you are creating a brand new instance of the form (of the game). In this new form, you can also have an array which keeps track of the total number of games and what the score of each game was so you can show it in case the user selected No. All you need is something like this:
var games = new List<Stats>();
// keep adding to it every time you call StartNewGame() method.
Try this:
if ((MessageBox.Show("Would you like to play again?", "Message", MessageBoxButtons.YesNo)) ==
DialogResult.Yes)
{
Application.Restart();
}

Play a list of songs with media player on WinForm

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string[] nomi, percorsi; //nomi means names and percorsi means paths. I'm italian, that's why
private void apri_Click(object sender, EventArgs e)
{
OpenFileDialog apri = new OpenFileDialog();
apri.Filter = "File *.mp3|*.mp3";
apri.Multiselect = true;
DialogResult scelta = apri.ShowDialog();
if (scelta == DialogResult.OK)
{
nomi = apri.SafeFileNames;
percorsi = apri.FileNames;
for (int i = 0; i < nomi.Length; i++)
Files.Items.Add(nomi[i]);
}
}
private void Files_SelectedIndexChanged(object sender, EventArgs e)
{
player.URL = percorsi[Files.SelectedIndex];
}
}
}
I have this code to create a simple mp3 player. Is there a wayy to enable the previous/next buttons on the media player and play the next/previous song in the listbox? And also is there a way to find if the song ended so that I can play the next one? Thanks in advance!
private void player_PlayStateChange(object sender, AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
if (e.newState == 8)
{
}
}
Found a solution. I'll post it so maybe it will help someone else. e.newState == 8 is true if the song is finished, so then I can start playing the next song.

Replaying a video continuously in a WPF media element

I have a video file playing in a media element. I need to keep it playing, thus I tried:
me.play();
me.MediaEnded += new RoutedEventHandler(me_MediaEnded);
With this event method:
//loop to keep video playing continuously
void me_MediaEnded(object sender, EventArgs e)
{
//play video again
me.Play();
}
However the above does not replay the video file. Why? What did I do wrong?
According to a post on MSDN:
Play() starts from the current position therefore you have to first go to the starting place and then play it again.
So you have to reset the position before replaying:
me.Position = TimeSpan.FromSeconds(0);
me.Play();
You could also use a Storyboard to control and loop the video without needing to hook events or do any code-behind work. This is the recommended solution on MSDN and is a more elegant and proper solution for MVVM design.
Read more on MSDN here, includes examples:
http://msdn.microsoft.com/en-us/library/ms741866%28v=vs.100%29.aspx
I got it to work with this:
class MyMediaPlayer : MediaPlayer
{
private bool looping;
public MyMediaPlayer() : base()
{
looping = false;
base.MediaEnded += new EventHandler(mediaEnded);
}
public MyMediaPlayer(string _file) : base()
{
looping = false;
base.Open(new Uri(_file, UriKind.Relative));
base.MediaEnded += new EventHandler(mediaEnded);
}
public bool Looping
{
get { return looping;}
set { looping = value; }
}
public void playLooping()
{
looping = true;
base.Play();
}
public void playLooping(string _file)
{
looping = true;
base.Open(new Uri(_file, UriKind.Relative));
base.Play();
}
public void play()
{
looping = false;
base.Play();
}
public void play(string _file)
{
looping = false;
base.Open(new Uri(_file, UriKind.Relative));
base.Play();
}
public void stop()
{
looping = false;
base.Stop();
}
private void mediaEnded(object sender, EventArgs e)
{
if(looping)
{
base.Position = new TimeSpan(0, 0, 0);
base.Play();
}
}
}
Hope this answers your question.
I don't know the reason but this never worked for me with a GIF:
me.Position = TimeSpan.FromSeconds(0);
me.Play();
My GIF stops playing after the first iteration.
The solution is:
<MediaElement x:Name="me"
MediaEnded="MediaElement_MediaEnded"
LoadedBehavior="Play" />
Code:
private void MediaElement_MediaEnded(object sender, RoutedEventArgs e)
{
me.Position = TimeSpan.FromMilliseconds(1);
}
1 ms is used while 0ms might stop the playback you cannot run it again with a 'Play' method.

How to display animated gif during long asynchronous operation?

I have a Winforms app in C# that calls calls a method asynchronously and uses a callback.
I would like to display an animated gif to let the end user know that work is being done.
I would like to have the animated gif hover over the center of the form.
How can I do this?
Update:
Thanks. I guess the step I was missing was to use a Picture Box to hold the gif.
The following seems to be doing the trick of showing the gif and like jmatthews3865 said below I can just set the visible property of the PictureBox to false to hide it.
private ShowAnimatedGif()
{
PictureBox pb = new PictureBox();
this.Controls.Add(pb);
pb.Left = (this.Width / 2) - (pb.Width / 2);
pb.Top = (this.Height / 2) - (pb.Height / 2);
pb.Image = Resources.AnimatedGifHere;
pb.Visible = true;
}
in your form, simply include the image with it's visible property set to false.
from the event which calls the long running async process (button1_click etc.), set the images visibility property to true. event fires, image appears, async process runs and your ui thread should still be responsive.
in your callback event set the images visible property to false to indicate that the process is complete.
Need some code to give an exact answer, but this is fairly trivial, insert the gif before you make the asynchronous call, then remove it in the callback.
This is the answer. I'm using LoadingCircle which is an animated gif component.
public partial class Form1 : Form
{
public delegate void ProcessAnimation(bool show);
ProcessAnimation pa;
public Form1()
{
InitializeComponent();
pa = this.ShowAnimation;
}
private void button2_Click(object sender, EventArgs e)
{
Thread tr = new Thread(FlushToServer);
tr.Start();
}
private void ShowAnimation(bool show)
{
if (show)
{
loadingCircle1.Visible = true;
loadingCircle2.Active = true;
}
else
{
loadingCircle1.Visible = false;
loadingCircle1.Active = false;
}
}
private void FlushToServer()
{
this.Invoke(this.pa,true);
//your long running process
System.Threading.Thread.Sleep(5000);
this.Invoke(this.pa,false);
}
}
i modify the above code a bit and it will not throw error "c# invoke or begininvoke cannot be called on a control until the window handle has been created."
namespace AnimateUI
{
public partial class Form1 : Form
{
public delegate void ProcessAnimation(bool show);
ProcessAnimation pa;
public Form1()
{
InitializeComponent();
pa = this.ShowAnimation;
pictureBox1.Visible = false;
}
private void ShowAnimation(bool show)
{
if (show)
{
pictureBox1.Visible = true;
}
else
{
pictureBox1.Visible = false;
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread tr = new Thread(StartTask);
tr.Start();
}
private void StartTask()
{
if (!this.IsHandleCreated)
this.CreateControl();
this.Invoke(this.pa, true);
System.Threading.Thread.Sleep(15000);
this.Invoke(this.pa, false);
}
}
}

Categories

Resources