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
)
精彩评论