none
Playing a sound inside a timer tick event RRS feed

  • Question

  • Hi all,

    I am experimenting with a stopwatch app in C# (VS 2013/2015); however, I am not satisfied with the result because the app sometimes behaves in odd ways. I have it working, but the count display stops while the sound plays (roughly 2, 3 seconds). I found that I can use Play() - instead of PlaySync(), but changing this introduces other issues, as described below.

    The app is a simple stopwatch that counts up. At defined intervals during the count, it plays a WAV file using the System Media SoundPlayer. For example, given a 10 second interval, it plays the WAV file every 10 seconds.

    Sometimes, it stutters. For example, if my WAV file plays a ding, it sounds like d-d-d-d-ding.

    In another case, at some intervals, it plays the WAV twice.

    I created a simple demo app. I selected a Win Form App, added two labels and a timer component using the tool box. The code is the following:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace SampleTimer
    {
        public partial class Form1 : Form
        {
    
            Stopwatch stopWatch = new Stopwatch();
            int iInterval = 0;
    
            //File spec for the WAV file.
            // http://www.wavsource.com/sfx/sfx.htm 
            string sSoundFile = @"C:\sgvWork\Projects\SampleTimer\chime.wav";
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                timer1.Interval = 500;
            }
    
            private void timer1_Tick(object sender, EventArgs e)
            {
                // Get the elapsed time as a TimeSpan value.
                TimeSpan ts = stopWatch.Elapsed;
    
                //Display count.
                label1.Text = String.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds);
    
                //Get number of seconds elapsed.
    
                if (ts.Seconds % 10 == 0 && ts.Seconds > 0)
                {
                    using (System.Media.SoundPlayer player = new System.Media.SoundPlayer(sSoundFile))
                    {
                        iInterval++;
                        player.Play();
                        //label2.Text = iInterval.ToString();
    
                    }
                }
    
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
    
                timer1.Enabled = true;
    
                stopWatch.Start();
            }
        }
    }

    Label2 is just to display other data. After this did not 100%. I did further research and found information about how to implement this functionality using threads and system timers. Apparently, using the boxed Timer control as I did is not recommended. I am (slowly) implementing these techniques, but wanted to ask here for any recommendations,advice or orientation about how to best play sounds while the stopwatch s ticking.

    Thank you for your time and assistance. Saga


    You can't take the sky from me


    • Edited by SagaV9 Tuesday, July 30, 2019 2:00 PM Fixed typo: very -> every
    Monday, July 29, 2019 9:42 PM

Answers

  • Update:

    I implemented the idea of a control to ensure that the sound is only played once within the second that triggers the alarm. This seems to have fixed my initial problem. Code follows:

    Original code snippet:

            Stopwatch stopWatch = new Stopwatch();
            int iInterval = 0;
    

    Changed to:

            Stopwatch stopWatch = new Stopwatch();
            int iInterval = 0;
            int iCurSec = 0;


    Original code snippet:

    if (ts.Seconds % 10 == 0 && ts.Seconds > 0)
    {
    
            using (System.Media.SoundPlayer player = 
               new System.Media.SoundPlayer(sSoundFile))
            {
                iInterval++;
                player.Play();
                label2.Text = iInterval.ToString();
            }
    }
    

    Changed to:

    if (ts.Seconds % 10 == 0 && ts.Seconds > 0)
    {
       if (ts.Seconds != iCurSec)
       {
            using (System.Media.SoundPlayer player = 
               new System.Media.SoundPlayer(sSoundFile))
            {
                iInterval++;
                player.Play();
                label2.Text = iInterval.ToString();
            }
    
            iCurSec = ts.Seconds;
       }
    }
    
    Again, thanks for your help. Saga


    You can't take the sky from me

    • Marked as answer by SagaV9 Wednesday, July 31, 2019 1:35 PM
    Tuesday, July 30, 2019 5:01 PM

All replies

  • Hi SagaV9,

    Thank you for posting here.

    >>For example, given a 10 second interval, it plays the WAV file very 10 seconds.

    First I want to know if the sentence has a spelling error. If so, the correct word is 'every' instead of 'very', right?

    >>In another case, at some intervals, it plays the WAV twice.

    Second, I could not reproduce your problem.Maybe it depends on the size of your wav file. 

    Third, I noted that there is a iInterval filed in your code. what is it used for?

    Last, What is your expected result?

    It will help us to solve your problem better if provide the above information.

    Best Regards,

    Jack


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, July 30, 2019 5:33 AM
    Moderator
  • Maybe try this:

    System.Media.SoundPlayer player = new System.Media.SoundPlayer(sSoundFile))

    private void Form1_Load(object sender, EventArgs e)

    {

       timer1.Interval = 10000;

    }

    private void timer1_Tick(object sender, EventArgs e)

    {

       player.Play();

    }

    Tuesday, July 30, 2019 6:47 AM
  • Thanks for following up Jack:

    1. Yes, bad fingers, bad fingers! I've edited the OP so that it reads "every" and not "very". Oops!

    2. iInterval is a variable that I added to count the number of times that the Tick event gets executed. I then pass this value to Label2.Text. This is just for debugging.

    If you uncomment the label2 assignment in the sample code, you'll see that iInterval gets incremented twice. The audio file gets played twice.

    Ok, I see something. Since the timer interval is 500ms, the tick event gets executed twice per second, and the audio file plays because it is still in the same second. For example, if I want a 100ms resolution, the Tick event will run 10 times per second, so it will try to play the audio file that many times.

    I need a way to make sure that the audio file plays only once per second, independently on how many times the Tick event runs per second.

    To answer your last question, about the expected result. This sample app only demonstrates the issue that I am having in its simplest form. The full app is much more complex. It allows the user to specify a time limit and various sound files to be played at different intervals, such as at the beginning, middle and end of the time limit (if defined). For example, at a 2 minute competition, I want the app to sound off when it starts, at every 30 second interval and then at the end. I could potentially have up to 5 different sound files:

    Start: audio file 1

    30 second: audio file 2

    1 min: audio file 3

    1:30 min: audio file 4

    end: audio file 5

    Although the audio files may have a duration of 1 - 3 seconds, I can expect that other might take as long as 4 minutes, depending on the app's use.

    Additionally, the app keeps a list of participants, so when the first competitor runs, I select it from a list and when the competition is over, the app automatically assigns the time measured to that competitor's slot.

    I also have the option to display milliseconds. That is why I can't use a timer interval of 1000 (or greater).

    Right now I have been able to solve this problem by using PlaySync(), but this stops the count in the display (label1).

    If you need more information, I'll be happy to provide it. Again, thanks for your time and assistance. Saga.


    You can't take the sky from me

    Tuesday, July 30, 2019 2:32 PM
  • Thanks for the follow up, much appreciated!

    I see what you are doing:

    1. Instantiating the player outside the event
    2. Setting the interval to 10 seconds

    I hope that the reply to Jack helps you understand what I am trying to accomplish and why setting the interval to 10 sec will not work.

    I need a counter that marks the seconds and have the audio play only when the interval arrives and the second changes. I should not play the audio if the seconds do not change. Hmmmm... :-)

    Again, thanks for you replay and assistance. Saga


    You can't take the sky from me

    Tuesday, July 30, 2019 2:40 PM
  • Update:

    I implemented the idea of a control to ensure that the sound is only played once within the second that triggers the alarm. This seems to have fixed my initial problem. Code follows:

    Original code snippet:

            Stopwatch stopWatch = new Stopwatch();
            int iInterval = 0;
    

    Changed to:

            Stopwatch stopWatch = new Stopwatch();
            int iInterval = 0;
            int iCurSec = 0;


    Original code snippet:

    if (ts.Seconds % 10 == 0 && ts.Seconds > 0)
    {
    
            using (System.Media.SoundPlayer player = 
               new System.Media.SoundPlayer(sSoundFile))
            {
                iInterval++;
                player.Play();
                label2.Text = iInterval.ToString();
            }
    }
    

    Changed to:

    if (ts.Seconds % 10 == 0 && ts.Seconds > 0)
    {
       if (ts.Seconds != iCurSec)
       {
            using (System.Media.SoundPlayer player = 
               new System.Media.SoundPlayer(sSoundFile))
            {
                iInterval++;
                player.Play();
                label2.Text = iInterval.ToString();
            }
    
            iCurSec = ts.Seconds;
       }
    }
    
    Again, thanks for your help. Saga


    You can't take the sky from me

    • Marked as answer by SagaV9 Wednesday, July 31, 2019 1:35 PM
    Tuesday, July 30, 2019 5:01 PM
  • Hi SagaV9,

    Thanks for the feedback.

    >>This seems to have fixed my initial problem.

    I am glad to hear that your problem has been solved. If so, please post "Mark as answer" to the appropriate answer. So that it will help other members to find the solution quickly if they face the similar issue.

    Best Regards,

    Jack


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, July 31, 2019 5:55 AM
    Moderator