c# - how to remove the interval between two timer ticks - c#

I am using timer to connect several audio files together. There are a list of audio files and the timer will read files one by one. Once there is an audio read, it will be played. The goal is to play the audio file right after the previous one when it is finished(without any break).
(I am using Naudio)
Here is the code:
private void timer_key_Tick(object sender, EventArgs e)
{
if(!isPlaying)
{
//play the current audio
DirectSoundOut dso = sounds2[key_indexer];
dso.Play();
targetMusics.Add(dso);
}
else
{
foreach (DirectSoundOut dso in targetMusics)
{//stop the current audio
dso.Stop();
}
targetMusics.Clear();
key_indexer++; //switch to the next audio
if (key_indexer >= myMT.Keys.Count)
{
key_indexer = 0;
timer_key.Stop();
}
}
isPlaying = !isPlaying;
}
However, the fact is, when the first music finished, the second one didn't play at once. It comes after one second break. Is this the problem about timer itself? How should I change it?

Thanks for the help from #HansPassant. I made a mistake about logic in this timer. Here is the correct code after I applied his suggestion:
//stop the current playing
foreach(DirectSoundOut d in targetMusics)
{
d.Stop();
}
targetMusics.Clear();
//stop the entire process
if (key_indexer >= myMT.Keys.Count)
{
key_indexer = 0;
timer_key.Stop();
}
else
{ //play the current audio
DirectSoundOut dso = sounds2[key_indexer];
targetMusics.Add(dso);
dso.Play();
key_indexer++;
}

Related

Playing sound at a certain time

I'm trying to make a "notification sound" that plays at certain time specified in the app, but for some reason it won't work. If I compare against a time interval it keeps playing the sound in a loop, but I cannot get it to play the sound once at the very moment the time matches.
System.Media.SoundPlayer player = new System.Media.SoundPlayer(#"c:\Windows\Media\ding.wav");
private void timer1_Tick(object sender, EventArgs e)
{
TimeSpan currentTime = DateTime.Now.TimeOfDay;
if (currentTime == TimeSpan.Parse("15:00"))
{
player.Play();
}
}
The timer compares current time every 100ms so I don't think it's down to timing but maybe I'm just missing something. Any ideas what's wrong or if I should use a different approach?

Wait until a click event has been fired C#

I'm developing a card game but I need to have a function that stops the program until the player hasn't clicked in the PictureBox of his card to discard it.
The algorithm of my game is this:
int nextDrawer = 0; // the players which will discard a card are determinated in counterclockwise starting from the human player
for (int i = 0; i < players; i++) // untill all the players hasn't drawed a card
{
if (i == 0) .... // the human player has to click on a picture box to discard a card
else .... // an AI player will discard a card which is selected randomly from the 3 cards which AI has got in its hand
}
The problem is that when a mance ends, the first who will discard a card could change. If the players are numerated with 0 (human player), 1 (first AI player), 2 (second AI player) and 3 (third AI player), at the first mance the first to discard a card is the human player, but at the second mance the first to discard could be the 2 AI player and the human player has to wait until all the AI players before him discard a card (in this case, the round would be 2-3-0-1).
How can I cancel the click event if the AI players hasn't discarded a card yet?
UPDATE
I don't always need to wait that all AI players had drawed a card: if the winner of the mance is the number 2, the round would be 2-3-0-1: that means the player has to wait the AI players 2 and 3 drawed, then the player has to click one PictureBox, and the loop will return back to the AI players and then the AI player 1 is allowed to discard its card.
UPDATE 2
I've thought something like that:
int leader = 0; // who is going to discard first
int nextDiscarder = leader; // next player who's going to discard
for (int i = 0; i < nPlayers; i++) // until all the players hasn't discarded
{
if (nextDiscarder == 0) // the human has to discard
{
enablePictureBoxClickEvent;
// now before the loop continue the program has to wait the event click on a picture box
}
else
{
AI[nextDiscarder].discard(); // the ai player will discard
}
if (nextDiscarder == players - 1) // if nextDiscarder has reached the end of the table
nextDiscarder = 0; // return to the begin until all player has discarded a card
else
++nextDiscarder; // continue to discard with the next player
}
and in my event click I'd do something like this:
private myEventClick(object sender, EventArgs e)
{
.... // do the instructions needed to discard a card
disableMyEventClick;
returnToLoop;
}
but the main problem is that I don't know how to write in code my instruction returnToLoop.
I know most of the people will argue that you should use event-driven approach, but async/await feature can be used for easily implementing things like this w/o the need of implementing manually state machines.
I already posted similar approach in Force loop to wait for an event and A Better Way to Implement a WaitForMouseUp() Function?, so basically this is the same helper as in the former with Button replaced with Control:
public static class Utils
{
public static Task WhenClicked(this Control target)
{
var tcs = new TaskCompletionSource<object>();
EventHandler onClick = null;
onClick = (sender, e) =>
{
target.Click -= onClick;
tcs.TrySetResult(null);
};
target.Click += onClick;
return tcs.Task;
}
}
Now all you need is to mark your method as async and use await:
// ...
if (nextDiscarder == 0) // the human has to discard
{
// now before the loop continue the program has to wait the event click on a picture box
await pictureBox.WhenClicked();
// you get here after the picture box has been clicked
}
// ...
I like Ivan solution, because it looks good, and is reusable easily anywhere else you need to wait for a control.
However, I wanted to provide another solution, because I feel like the way are doing this is far more complicated that it could be.
So let's resume this :
At some point in the game, you need players to select a card they don't want to throw it away
There is one human player, which is number 0 in your array
The human player is not always the first to decide which card to throw away.
To decide which card to throw away, you display a picturebox to the player and you wait for him to click on it.
I believe a simple solution could be :
You start by removing the card for the AI players before the human (if human is first to discard, this will do nothing, if human is last, all AI will discard here)
You enable the PictureBox and you end your function
In the click event of the PictureBox, you remove the user card, then you remove the card for the remaining AI players that are after the human (if human is first, all AI will remove a card here, if human is last, you do nothing)
Done...
So this would look like this :
//We need an instance variable, to keep track of the first player
int _firstPlayerToDiscard = 0;
private void StartDiscardingProcess(int FirstToDiscard)
{
_firstPlayerToDiscard = FirstToDiscard;
if (FirstToDiscard != 0) //If the player is the first, we do nothing
{
//We discard for every other AI player after the human player
for (int i = FirstToDiscard; i < nPlayers; i++)
{
AI[i].Discard();
}
}
//Now we fill the PictureBox with the cards and we display it to the player
DiscardPictureBox.Enabled = true;
//or DiscardPictureBox.Visible = true;
//and we are done here, we know basically wait for the player to click on the PictureBox.
}
private void pictureBox_click(Object sender, EventArgs e)
{
//Now we remove the card selected by the player
// ...
//And we remove the cards from the other AI players
//Note that if the player was first to discard, we need to change the instance variable
if (_firstPlayerToDiscard == 0) { _firstPlayerToDiscard = nbPlayers; }
for (int i = 1; i < _firstPlayerToDiscard; i++)
{
AI[i].Discard();
}
}
And you're pretty much done...
NB: Sorry if syntax is bad or unusual, I usually code in VB .Net... Feel free to edit syntax issues...
The following code demonstrates a simple timer based state machine. In this case, the machine's state is the current Player's Turn. This example lets each Play decide when to let the next player have her turn by setting the state to the next player. Add additional states for other things the program should check for. This program architecture runs relatively smoothly because the program threads are not blocked in tight loops. The "faster" each player can complete and exit the turn, the better - even if the player's turn repeats 10000 times without doing anything before letting the next player play.
In the example below, the Click event handler advances the machine state from the Human's turn to the AI's turn. This effectively pauses the game until the Human Clicks. Since the Turn is not blocked in a tight loop, you can have other buttons for the Human to click on like "Pass", "Start Over", and "Quit".
using System;
using System.Windows.Forms;
using System.Timers;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private System.Timers.Timer machineTimer = new System.Timers.Timer();
// These are our Machine States
private const int BEGIN_PLAY = 0;
private const int HUMAN_PLAYER_TURN = 1;
private const int AI_PLAYER_TURN = 2;
// This is the Current Machine State
private int currentPlayer = BEGIN_PLAY;
// Flag that lets us know that the Click Event Handler is Enabled
private bool waitForClick = false;
// The AI members, for example 100 of them
private const int AIcount = 100;
private object[] AIplayer = new object[AIcount];
private int AIcurrentIndex = 0; // values will be 0 to 99
public Form1()
{
InitializeComponent();
this.Show();
// The Timer Interval sets the pace of the state machine.
// For example if you have a lot of AIs, then make it shorter
// 100 milliseconds * 100 AIs will take a minimum of 10 seconds of stepping time to process the AIs
machineTimer.Interval = 100;
machineTimer.Elapsed += MachineTimer_Elapsed;
MessageBox.Show("Start the Game!");
machineTimer.Start();
}
private void MachineTimer_Elapsed(object sender, ElapsedEventArgs e)
{
// Stop the Timer
machineTimer.Stop();
try
{
// Execute the State Machine
State_Machine();
// If no problems, then Restart the Timer
machineTimer.Start();
}
catch (Exception stateMachineException)
{
// There was an Error in the State Machine, display the message
// The Timer is Stopped, so the game will not continue
if (currentPlayer == HUMAN_PLAYER_TURN)
{
MessageBox.Show("Player Error: " + stateMachineException.Message, "HUMAN ERROR!",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else if (currentPlayer == AI_PLAYER_TURN)
{
MessageBox.Show("Player Error: " + stateMachineException.Message, "AI ERROR!",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
MessageBox.Show("Machine Error: " + stateMachineException.Message, "Machine ERROR!",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void State_Machine()
{
// This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
switch (currentPlayer)
{
case HUMAN_PLAYER_TURN:
Play_Human();
break;
case AI_PLAYER_TURN:
Play_AI();
break;
default:
Play_Begin();
break;
}
}
private void Play_Human()
{
// This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
// My Turn!
if (!waitForClick)
{
// Please Wait until I take a card...
// I am using this.Invoke here because I am not in the same thread as the main form GUI
// If we do not wrap the code that accesses the GUI, we may get threading errors.
this.Invoke((MethodInvoker)delegate
{
pictureBox1.Click += PictureBox1_Click;
});
// set this flag so we do not re-enable the click event until we are ready next time
waitForClick = true;
}
}
private void PictureBox1_Click(object sender, EventArgs e)
{
// This routine is executing in the Main Form's Thread, not the Timer's Thread
// Stop the game for a little bit so we can process the Human's turn
machineTimer.Stop();
// Disable the Click Event, we don't need it until next time
pictureBox1.Click -= PictureBox1_Click;
waitForClick = false;
// To Do: Human's Turn code...
// Let the AI Play now
currentPlayer = AI_PLAYER_TURN;
machineTimer.Start();
}
private void Play_AI()
{
// This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
if (AIcurrentIndex < AIcount)
{
// If we do not wrap the code that accesses the GUI, we may get threading errors.
this.Invoke((MethodInvoker)delegate
{
// To Do: AI Player's Turn code...
});
// Advance to the next AI
AIcurrentIndex++;
}
else
{
// Reset to the beginning
AIcurrentIndex = 0;
currentPlayer = BEGIN_PLAY;
}
}
private void Play_Begin()
{
// This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
// If we do not wrap the code that accesses the GUI, we may get threading errors.
this.Invoke((MethodInvoker)delegate
{
// ... do stuff to setup the game ...
});
// Now let the Human Play on the next Timer.Elapsed event
currentPlayer = HUMAN_PLAYER_TURN;
// After the Human is done, start with the first AI index
AIcurrentIndex = 0;
}
}
}
i would have design the process in a different way based on events without loop, but following your way you should use an autoreset event to notify your loop myEvent have been fired.
AutoResetEvent clickEventFired = new AutoResetEvent(false); // instanciate event with nonsignaled state
AutoResetEvent clickEventFired = new AutoResetEvent(true); // instanciate event with signaled state
clickEventFired.Reset(); // set state to nonsignaled
clickEventFired.Set(); // set state to signaled
clickEventFirect.WaitOne(); // wait state to be signaled
https://msdn.microsoft.com/en-us/library/system.threading.autoresetevent(v=vs.110).aspx
public static void yourLoop()
{
int leader = 0; // who is going to discard first
int nextDiscarder = leader; // next player who's going to discard
// instanciate auto reset event with signaled state
AutoResetEvent clickEventFired = new AutoResetEvent(true);
for (int i = 0; i < nPlayers; i++) // until all the players hasn't discarded
{
if (nextDiscarder == 0) // the human has to discard
{
enablePictureBoxClickEvent;
clickEventFired.WaitOne(); // wait for event to be signaled
}
else
{
AI[nextDiscarder].discard(); // the ai player will discard
clickEventFired.Reset(); // set event state to unsignaled
}
if (nextDiscarder == players - 1) // if nextDiscarder has reached the end of the table
nextDiscarder = 0; // return to the begin until all player has discarded a card
else
++nextDiscarder; // continue to discard with the next player
}
}
private myEventClick(object sender, EventArgs e)
{
.... // do the instructions needed to discard a card
disableMyEventClick;
clickEventFired.Set(); // signal event
}

Playing music from collection with SoundPlayer

So I am doing a simple piano and trying to traverse the collection where I stored the notes, but the SoundPlayer doesn't want to play them properly in "without debugging mode", playing only the last one. However when I put a breakpoint there it plays all of them
public static List<MusicNote> music = new List<MusicNote>(15);
public static void PlayAll()
{
SoundPlayer sp = new SoundPlayer();
for (int i = 0; i <= music.Count - 1; i++)
{
string text = music[i].pitch.ToString();
sp.SoundLocation = (#"c:\my path here\" + text + ".wav");
sp.Play();
sp.Stop();
}
}
Pitch is simply ordinal number to link to file.
Thanks in advance
U better use PlaySyn in order to tell your program to wait until the music complete
// Create new SoundPlayer in the using statement.
using (SoundPlayer player = new SoundPlayer())
{
for (int i = 0; i <= music.Count - 1; i++)
{
string text = music[i].pitch.ToString();
sp.SoundLocation = (#"c:\my path here\" + text + ".wav");
// Use PlaySync to load and then play the sound.
// ... The program will pause until the sound is complete.
player.PlaySync();
}
}
I think its better when you use PlaySync(); instead of Play();
Because then you don't need the Stop() methode.
Here a link to the docu of SoundPlayer
Why use PlaySync? If you just call the Play method in this program, the program will terminate before the sound plays. The Sync indicates that the program should pause while the sound plays.

How to get the duration of mp3 file without playing or storing them

I am building a wp8 app which could play poems line by line displayed in listbox.
Say I have 20 lines of Poems showing as 20 items in listbox. I have 20 mp3 files (one for each line), I used BackgroundAudioPlayer to play individual files and select each item (poem) in listbox. During playback of these mp3 files, there were delays of 1-3 seconds, so I found the full version of these mp3 files (20 files merged into one), reading poems line by line.
I am playing this mp3 file using BackgroundAudioPlayer. Playing as one file, I cannot select each item in listbox as I don't know the line number by playing one mp3 file.
Now what I want is to store the duration of 20 files in seconds, without storing and playing. The files are located in a link (http:123.com/1.mp3, /2.mp3, ...)
I used AudioTrack with absolute path and then used duration property, but it always returns zero. How can I get the duration of an mp3 file without playing them?
Edited:
In the background Agent I did below:
protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
{
switch (playState)
{
case PlayState.TrackReady:
TrackDuration tr = DataSource.Connection.Table<TrackDuration>().SingleOrDefault();
if (tr.IsGetTrackDuration)
{
if (player.Track != null)
{
player.Position = TimeSpan.FromSeconds(player.Track.Duration.TotalSeconds);
Recitation r = DataSource.Connection.Query<Recitation>("select * from Recitation where ID = " + player.Track.Title).SingleOrDefault();
r.AyaDuration = player.Track.Duration.TotalSeconds;
DataSource.saveRecitionDownloaded(r);
PlayNextTrack(player);
}
}
break;
}
NotifyComplete();
}
protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
{
switch (action)
{
case UserAction.Play:
PlayTrack(player);
break;
case UserAction.Seek:
if (null!=player.Track)
player.Position = (TimeSpan)param;
break;
}
NotifyComplete();
}
private void PlayNextTrack(BackgroundAudioPlayer player)
{
if (++currentTrackNumber >= audioTrack.Count)
return;
PlayTrack(player);
}
private void PlayTrack(BackgroundAudioPlayer player)
{
//If No Track then load Track
if (audioTrack.Count == 0)
{
//Load Tracks and Add them into Track Play List
}
if ((player.Track == null) || (player.Track.Title != audioTrack[currentTrackNumber].Title))
player.Track = audioTrack[currentTrackNumber];
if ((player.Track != null) && (player.PlayerState != PlayState.Playing))
player.Play();
}
The question is It plays some of the files which I don't want. (tr.IsGetTrackDuration) is always true, but I don't know why it plays some?
MusicProperties class has a property for the duration
MusicProperties musicProperties = await file.Properties.GetMusicPropertiesAsync();
outputText.AppendLine("Duration: " + musicProperties.Duration);
Ok I have a thought here. Under the AudioPlayer class, there is a TrackReady event that is set by default to play the track. Instead of playing by default, set a bool to true if you're simply wanting to obtain the duration, and false if you actually wish to play a track (when you're wanting to play the full mp3).
The bool value would have to be set in the main app before the track is set, and then read in the AudioPlayer each time the OnPlayStateChanged is called.
protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
{
//read GettingDuration value here
try
{
switch (playState)
{
case PlayState.TrackReady:
if (GettingDuration)
{
//store duration somehow and get the next track in line
}
else
{
player.Play();
}
break;
}
}
}

audio.Play() not working

I have the a script called Timer.cs. This script is connected to some GUI Text, which displays the amount of time remaining in the game.
Also attached to this script is an Audio Source with my desired sound selected. When the clock reaches zero, the text changes to say "GAME OVER!" and the character controls lock up; however, the sound does not play.
All other instances of audio.Play() in my scene are working fine, and when I set the Audio Source to "Play On Awake", it plays without a problem. What could be the problem?
Using UnityEngine;
using System.Collections;
public class Timer : MonoBehaviour {
public float timer = 300; // set duration time in seconds in the Inspector
public static int sound = 1;
public static int go = 1;
bool isFinishedLevel = false; // while this is false, timer counts down
void Start(){
PlayerController.speed = 8;
PlayerController.jumpHeight = 12;
}
void Update (){
if (!isFinishedLevel) // has the level been completed
{
timer -= Time.deltaTime; // I need timer which from a particular time goes to zero
}
if (timer > 0)
{
guiText.text = timer.ToString();
}
else
{
guiText.text = "GAME OVER!"; // when it goes to the end-0,game ends (shows time text over...)
audio.Play();
int getspeed = PlayerController.speed;
PlayerController.speed = 0;
int getjumpHeight = PlayerController.jumpHeight;
PlayerController.jumpHeight = 0;
}
if (Input.GetKeyDown("r")) // And then i can restart game: pressing restart.
{
Application.LoadLevel(Application.loadedLevel); // reload the same level
}
}
}
Given that you are calling it as part of your Update routine, I'd have to guess that the problem is you calling it repeatedly. I.e. you're calling it every frame as long as timer <= 0.
You shouldn't call Play() more than once. Or at least not again while it is playing. A simple fix would be something along the lines of
if(!audio.isPlaying)
{
audio.Play();
}
See if that solves your problem, and then you can take it from there.
I had error using audio.Play(); and used following it fixed the error for me
GetComponent<AudioSource>().Play();

Categories