How to play multiple audio sources simultaneously in Silverlight - silverlight

I want to play simultaneous multiply audio sources in Silverlight.
So I've created a prototype in Silverlight 4 that should play a two mp3 files containing the same ticks sound with an intervall 1 second. So these files must be sounded as one sound if they will be played together with any whole second offsets (0 and 1, 0 and 2, 1 and 1 seconds, etc.)
I my prototype I use two MediaElement (me and me2) objects.
DateTime startTime;
private void Play_Clicked(object sender, RoutedEventArgs e) {
me.SetSource(new FileStream(file1), FileMode.Open)));
me2.SetSource(new FileStream(file2), FileMode.Open)));
var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1) };
timer.Tick += RefreshData;
timer.Start();
}
First file should be played at 00:00 sec. and the second in 00:02 second.
void RefreshData(object sender, EventArgs e) {
if(me.CurrentState != MediaElementState.Playing) {
startTime = DateTime.Now;
me.Play();
return;
}
var elapsed = DateTime.Now - startTime;
if(me2.CurrentState != MediaElementState.Playing &&
elapsed >= TimeSpan.FromSeconds(2)) {
me2.Play();
((DispatcherTimer)sender).Stop();
}
}
The tracks played every time different and not simultaneous as they should (as one sound).
Addition:
I've tested a code from the Bobby's answer.
private void Play_Clicked(object sender, RoutedEventArgs e) {
me.SetSource(new FileStream(file1), FileMode.Open)));
me2.SetSource(new FileStream(file2), FileMode.Open)));
// This code plays well enough.
// me.Play();
// me2.Play();
// But adding the 2 second offset using the timer,
// they play no simultaneous.
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
timer.Tick += (source, arg) => {
me2.Play();
((DispatcherTimer)source).Stop();
};
timer.Start();
}
Is it possible to play them together using only one MediaElement or any implementation of MediaStreamSource that can play multiply sources?
Addition 2: Debug the playing positions
Adding the debug information shows definitively that the me plays different compare to the timer's ticks
...
me2.Play();
System.Diagnostics.Debug.WriteLine(
me.Position.TotalMilliseconds + " -> " + me2.Position.TotalMilliseconds);
// 1820 -> 0 (but not 2000 -> 0)
Addition3: Experience with markers
I have experienced with the Time in the TimeLineMarker and following code works well enough on my pc
me.Markers.Clear();
me.Markers.Add(new TimelineMarker { Time = TimeSpan.FromMilliseconds(1892) });
me.MarkerReached += (source, args) => {
me2.Play();
me.Markers.Clear();
};
me.Play();

Since you're using FileStreams to load the files, I assume you're not reading them over the web, but rather from the local file system.
You don't want to call Play() from the timer tick handlers if it's not necessary - ie for the first ME.
Also, you're running the timer every millisecond, not second (not sure if that's actually what you meant to say). If you want to kick of the second play 2 seconds after the first, then try calling me.Play() then create the timer to run on a 2-sec interval, and all it does is call me2.Play() and stop itself.
Also, in line with the comment above, to see if the behavior is reasonably deterministic (sufficiently synchronized), try running just this a few times to see if, in principle, they can play together well enough for you.
private void Play_Clicked(object sender, RoutedEventArgs e) {
me.SetSource(new FileStream(file1), FileMode.Open)));
me2.SetSource(new FileStream(file2), FileMode.Open)));
me.Play();
me2.Play();
}
.. Edit:
Timers are not guaranteed to execute
exactly when the time interval occurs,
but they are guaranteed to not execute
before the time interval occurs. This
is because DispatcherTimer operations
are placed on the Dispatcher queue
like other operations. When the
DispatcherTimer operation executes is
dependent on the other jobs in the
queue and their priorities. (msdn :
DispatcherTimer)
It can be very difficult to achieve perfect synchronization in multi-threaded contexts.. The DispatcherTimer is going to place the call to the handler in the UI Dispatcher when the interval ticks, but that doesn't mean it will get immediately invoked. The only thing that might be worth a shot at this point is to adjust the DispatcherPriority (e.g. to 'Send' (the highest)).
Do so with this constructor:
public DispatcherTimer(
DispatcherPriority priority
)

Related

How to run a thread for specific time in c#? Something like executing the same code again and again like a script for 5 seconds

I want to run a thread, a small part of code to be executed for 5 seconds.
Execution should be only once, it should keep executing continuously for a specific time.
Note: It should not keep executing, something like a timer. Execution should be only once.
Real Problem:
There is a script called AHK. (Auto Hot Key).
It does some task like hiding a taskbar for specific time.
I am not allowed to modify the script.
I have to modify the same in application part(C#).
Sample Tried out codes:
void StartConnection()
{
stopwatch.Start();
Thread threadObj = new Thread(ThreadFunc);
threadObj.Start();
}
void ThreadFunc()
{
for (; stopwatch.Elapsed.TotalSeconds < 6; )
{
WindowsNativeCalls.HideTaskbar();
}
}
Is there any other dot net concept available to achieve this?
See if this fits your bill :
private void Button_Click(object sender, RoutedEventArgs e)
{
System.Threading.Tasks.Task.Factory.StartNew(() => { mycallBack(); });
}
private void mycallBack()
{
System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
stopWatch.Start();
while (true)
{
System.Diagnostics.Debug.WriteLine("Running !");
if (stopWatch.Elapsed.Seconds >= 5)
break;
}
System.Diagnostics.Debug.WriteLine("Done !");
}
You can use DispatcherTimer
private DispatcherTimer myTimer;
private void StartMyTimer()
{
if (myTimer != null)
myTimer.Stop();
myTimer = new DispatcherTimer();
myTimer.Interval = TimeSpan.FromSeconds(5);// You can change this for minutes, hours and etc
myTimer.Tick += myTimer_Tick;
myTimer.Start();
}
void myTimer_Tick(object sender, EventArgs e)
{
//Do something here every 5 seconds
}
I hope this helps.

Windows Forms: background worker synchronization and management

I have a problem with following, very simplified case being part of my project. Consider we have GUI like below:
I have two background workers:
plot_bgworker - in this example, it increments plot counter,
data_bgworker - in this example, it increments data counter.
I also have label_timer, which updates incremented values diplayed on my form.
To manage both background workers and timer, I wrote two functions:
private: void turnOnAcquisition() {
if (!counting_paused)
return;
if (!plot_bgworker->IsBusy)
plot_bgworker->RunWorkerAsync();
if (!data_bgworker->IsBusy)
data_bgworker->RunWorkerAsync();
label_timer->Enabled = true;
counting_paused = false;
}
private: void turnOffAcquisition() {
if (counting_paused)
return;
if (plot_bgworker->IsBusy)
plot_bgworker->CancelAsync();
if (data_bgworker->IsBusy)
data_bgworker->CancelAsync();
label_timer->Enabled = false;
counting_paused = true;
}
Then, here is what happens when I click each of my buttons:
// Pauses counting on click
private: System::Void stop_btn_Click(System::Object^ sender, System::EventArgs^ e) {
turnOffAcquisition();
}
// Starts counting on click
private: System::Void start_btn_Click(System::Object^ sender, System::EventArgs^ e) {
turnOnAcquisition();
}
// Should restart counting on click, beginning from 0 (no matter what state counting is in right now)
private: System::Void restart_btn_Click(System::Object^ sender, System::EventArgs^ e) {
plot_counter = 0;
data_counter = 0;
turnOffAcquisition();
turnOnAcquisition();
}
Finally, here are my background workers (turned off / on by CancelAsync() / RunWorkerAsync() ) and timer:
// Calculating data counter
private: System::Void data_bgworker_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
for (;;) {
data_counter++;
Sleep(50);
if (data_bgworker->CancellationPending) {
e->Cancel = true;
return;
}
}
}
// Calculating plot counter
private: System::Void plot_bgworker_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
for (;;) {
plot_counter++;
Sleep(120);
if (plot_bgworker->CancellationPending) {
e->Cancel = true;
return;
}
}
}
// Display counters
private: System::Void label_timer_Tick(System::Object^ sender, System::EventArgs^ e) {
plot_counter_label->Text = numToMStr(plot_counter);
data_counter_label->Text = numToMStr(data_counter);
}
Start button and stop button work both as expected, but now I have a problem with restart button. When I click it in the middle of counting, it seems to reset values and stop background workers, but never start them again (as I would expect after calling turnOnAcquisition). However, when I click it when counting is off, I am able to turn on counting as expected.
My first shot was that cancellation flag is not yet set to another value when I tried to check if my workers were busy, but using Sleep() between calls didn't work. Another guess is that it is due to race condition failure, so I tried using MemoryBarrier(), but I don't know the libraries and I'm not sure if it would work. Also, I tried to use Interlocked class, but couldn't use it properly for void functions.
1. Is this way of thinking correct?
2. If yes, why simple Sleep() doesn't do the trick?
3. How would I use any of mentioned methods in this case and which one would be the best match?
Ok, I found the solution by myself. The problem here was about the race condition - one event tried to stop counting (which meant raising another event) and then starting it again (which was problematic, as my function (I guess) was already cluttered with the first one and probably the second event wasn't even added to the event detected queue). If I am wrong with the explanation, I would appreciate some criticism down there ;)
Here are two modified functions, which solved thread management correctly. The key was to let the other events do their work until I get desired state.
When I want to turn off counting, I let the applications do the events from the queue until both threads will not be busy (the 'while' loop):
private: void turnOffAcquisition() {
if (counting_paused)
return;
if (plot_bgworker->IsBusy)
plot_bgworker->CancelAsync();
if (data_bgworker->IsBusy)
data_bgworker->CancelAsync();
while((plot_bgworker->IsBusy) || (data_bgworker->IsBusy)) // Continue to process events until both workers stop working
Application::DoEvents(); // Then, you can process another thread requests! :)
label_timer->Enabled = false;
counting_paused = true;
}
Similarily, when I want to restart counting, I let the application do the events until I check that both threads are busy (again, the 'while' loop):
private: void turnOnAcquisition() {
if (!counting_paused)
return;
if (!plot_bgworker->IsBusy)
plot_bgworker->RunWorkerAsync();
if (!data_bgworker->IsBusy)
data_bgworker->RunWorkerAsync();
while((!plot_bgworker->IsBusy) || (!data_bgworker->IsBusy)) // Continue to process events until both workers start working
Application::DoEvents(); // Then, you can process another thread requests! :)
label_timer->Enabled = true;
counting_paused = false;
}

Making smooth effect in WPF manually in C# with DispatcherTimer

I'm trying to make pretty effect with not using Storyboard or another ready/already done stuff in WPF.
I want to make smooth effect, where on some event (like click) the UI element resizes for 2-3 seconds and bluring with changing color. All these items I want to make in smooth pretty way.
I have prepared such class to render each frame of my effect:
public static class ApplicationHelper
{
[SecurityPermissionAttribute(SecurityAction.Demand,
Flags=SecurityPermissionFlag.UnmanagedCode)]
public static void DoEvents(DispatcherPriority priority)
{
DispatcherFrame frame = new DispatcherFrame();
DispatcherOperation oper = Dispatcher.CurrentDispatcher.
BeginInvoke(priority,
new DispatcherOperationCallback(ExitFrameOperation),
frame);
Dispatcher.PushFrame(frame);
if (oper.Status != DispatcherOperationStatus.Completed)
{
oper.Abort();
}
}
private static object ExitFrameOperation(object obj)
{
((DispatcherFrame)obj).Continue = false;
return null;
}
[SecurityPermissionAttribute(SecurityAction.Demand,
Flags=SecurityPermissionFlag.UnmanagedCode)]
public static void DoEvents()
{
DoEvents(DispatcherPriority.Background);
}
}
Here I'm trying to make it work with DispatcherTimer:
void vb1_click(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = new TimeSpan(0, 0, 0, 0, 500);
dt.Tick += new System.EventHandler(dt_Tick);
dt.Start();
}
void dt_Tick(object sender, System.EventArgs e)
{
for(int i = 0; i < 20; i++)
{
this.vb2_blur_eff.Radius = (double)i;
ApplicationHelper.DoEvents();
}
}
The main problem is, that when I'm launcing it, I'm only waiting and at the final time ( when must last frame be rendered ) , I'm getting in a very quick speed all frames, but perviously there was nothing.
How to solve it and make perfect smooth effect in pure C# way with not using some ready/done stuff?
Thank you!
The ApplicationHelper.DoEvents() in dt_Tick probably does nothing, since there are no events to process. At least not the ones you're probably expecting.
If I'm not mistaken, your code will just quickly set the Radius to 0, then 1, 2, and so on in quick succession, and finally to 19. All of that will happen every 500 milliseconds (on every Tick, that is).
I think you might believe that each Tick will only set Radius to one value and then wait for the next Tick, but it does not. Every Tick will set the Radius to all the values, ending at 19. That is one possible explanation for what you're experiencing.
I would also like to comment on the DoEvents approach. It's most likely a bad idea. Whenever I see a DoEvents I get chills up my spine. (It reminds me of some seriously bad Visual Basic 5/6 code I stumbled across 10-15 years ago.) As I see it, an event handler should return control of the GUI thread as quickly as possible. If the operation takes a not insignificant amount of time, then you should delegate that work to a worker thread. And nowadays, you have plenty of options for writing asynchronous code.

Update GUI using BackgroundWorker

I've been searching and found that a good way to perform background work and update the GUI is using background workers. However, doing this (stupid) little task (counting from 1 to 10000) it doesn't update the label content but prints to the debug! (This is just a spike solution for another project of course...)
Here's the code:
public partial class MainWindow : Window
{
BackgroundWorker bw = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.WorkerReportsProgress = true;
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("DONE");
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Content = "going here: "+e.ProgressPercentage;
Debug.WriteLine(e.ProgressPercentage);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i=0; i < 10000; i++)
{
bw.ReportProgress((i*100)/10000);
}
}
}
The ProgressChanged event is raised on the UI thread, not the worker thread. In your code, the worker thread is doing almost nothing (just loop from 0 to 10000 and call ReportProgress), most of the work is done on the UI thread. Basically, you're sending too many progress notifications. Because of this, the UI thread is almost always busy and has no time to render the new content of the label.
Rendering in WPF is not performed immediately when you change a property of a control, it is done on a separate dispatcher frame, which is processed when the dispatcher has nothing more urgent to do, based on the priority of the task. The priority used for rendering has a value of 7 (DispatcherPriority.Render); the ProgressChanged event is marshalled to the UI thread with a priority of 9 (DispatcherPriority.Normal), as specified on MSDN. So the ProgressChanged notifications always have a higher priority than rendering, and since they keep coming, the dispatcher never has time to process the rendering tasks.
If you just decrease the frequency of the notifications, your app should work fine (currently you're sending 100 notifications for each percentage value, which is useless):
void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 10000; i++)
{
if (i % 100 == 0)
bw.ReportProgress(i / 100);
}
}
this.Dispatcher.BeginInvoke( (Action) delegate(){
label1.Content = "going here: "+e.ProgressPercentage;
});
Try to change the label using womething like this:
string Text = "going here: " + e.ProgressPercentage;
this.Invoke((MethodInvoker)delegate {
label1.Content = newText;
});
Note that i'm not sure it will work. I can not test it now. If it does not work, let me know and I will delete the answer.
If you need the a canonical way to do exactly what you want, look at the Hath answer in this post: How do I update the GUI from another thread?

Silverlight 1 second DispatcherTimer only firing every 30 seconds or so on certain machines

I've got a Silverlight 3 app that works great, except on 4 machines. DispatcherTimer and Storyboards are not firing consistently on these 4 machines. I've created a very simple test app to try to figure this out, I'll list the code below.
Basically the test app updates two TextBlocks every second. One using DispatcherTimer, one using a Storyboard Animation. This works great - the text boxes update "1, 2, 3..." every second. But on the 4 affected machines the TextBlocks don't update every second, they update between 27 and 33 seconds. The DispatcherTimer and Storyboard updates are done at the exact same time.
CPU, Memory, HD are all fine. Task Manager and SilverlightSpy shows that everything this is fine. These are all 3 Ghz workstations with 3GB of RAM with nothing else running on them.
XAML:
<TextBlock Text="0" Name="DispatcherTimerText" Grid.Column="0" />
<TextBlock Text="0" Name="SBLoopTimerText" Grid.Column="1" />
C#:
Storyboard _sbLoop = new Storyboard();
public MainPage()
{
InitializeComponent();
Storyboard_Start();
Timer_Start();
}
void Timer_Start()
{
System.Windows.Threading.DispatcherTimer dt1 = new System.Windows.Threading.DispatcherTimer();
dt1.Interval = new TimeSpan(0, 0, 1); // 1 second
dt1.Tick += new EventHandler(Timer_Tick);
dt1.Start();
}
void Timer_Tick(object sender, EventArgs e)
{
TextBlock txt = ((TextBlock)LayoutRoot.Children.Single(t => ((TextBlock)t).Name == "DispatcherTimerText"));
txt.Text = (int.Parse(txt.Text) + 1).ToString();
}
void Storyboard_Start()
{
_sbLoop.Duration = TimeSpan.FromSeconds(1);
_sbLoop.Completed += new EventHandler(StoryboardLoop);
_sbLoop.Begin();
}
void StoryboardLoop(object sender, EventArgs e)
{
TextBlock txt = ((TextBlock)LayoutRoot.Children.Single(t => ((TextBlock)t).Name == "SBLoopTimerText"));
txt.Text = (int.Parse(txt.Text) + 1).ToString();
_sbLoop.Begin(); // Restart sb animation
}
Just a guess, but it could be the fact that you're trying to update the UI directly in the events. I'd be curious to see if this code would work better for you and fix the problem on the machines that don't work.
public partial class MainPage : UserControl
{
Storyboard _sbLoop = new Storyboard();
private int _slCounter = 0;
private int _tmrCounter = 0;
private TimeSpan interval = TimeSpan.FromMilliseconds(100);
public MainPage()
{
InitializeComponent();
Storyboard_Start();
Timer_Start();
}
void Timer_Start()
{
System.Windows.Threading.DispatcherTimer dt1 = new System.Windows.Threading.DispatcherTimer();
dt1.Interval = interval;
// 1 second
dt1.Tick += new EventHandler(Timer_Tick);
dt1.Start();
}
void Timer_Tick(object sender, EventArgs e)
{
_tmrCounter++;
Dispatcher.BeginInvoke(() => DispatcherTimerText.Text = _tmrCounter.ToString());
}
void Storyboard_Start()
{
_sbLoop.Duration = interval;
_sbLoop.Completed += new EventHandler(StoryboardLoop);
_sbLoop.Begin();
}
void StoryboardLoop(object sender, EventArgs e)
{
_slCounter++;
Dispatcher.BeginInvoke(() => SBLoopTimerText.Text = _slCounter.ToString());
_sbLoop.Begin();
// Restart sb animation
}
}
This code uses BeginInvoke to update the UI instead of directly updating it. You might need to change my interval since I put it as 100 ms instead of one second just for testing.
I'm not sure why this is happening, but I'm now positive this isn't a code issue.
Everything works great if I'm actively interacting with the workstation. Opening browsers, copying files, etc. But if the workstation is sitting idle - just running the Silverlight test app, it quickly goes back to taking 30 seconds to fire the timers.
I've verified this happens not only with my app, and my test app, but also with other Silverlight apps like the demos here:
http://wpierdalaj.pl/SWG/SilverlightCountDown/Run/SilverlightCountDownTimerExampleTestPage.html
For a workaround I created a batch file that copies a 100MB file, deletes it, and then starts over. As long as this batch file is running everything works great. :)
For a long term solution IT is going to figure out why these workstations go to "sleep" so quickly.
I wrote a simple extension method that uses a Task. Your more than welcome to alter the main DelayInvoke method to use a DispatcherTimer.
See This Post for my solution.

Resources