Hi everyone! I’m having a problem in Flash CS3 / ActionScript 3.0 and thought maybe someone here might be able to help
I’ve developed an application which uses a (dynamic) XML file to load in several files; a maximum of four MP3 files and a number of JPG files.
When everything has finished loading, the user can press a “Play” button. The application could be described as a “mixer”; it’s used to give online music lessons and to facilitate this, the sheet music is loaded (cut up into into several JPGs) which move horizontally on the screen like a marquee, while the music it represents begins playback. The music exists of several MP3s so that the user can mute (e.g.) the vocals or the drums, allowing him to focus on (e.g.) the guitar.
So what’s going wrong, is that the audio files are not playing in sync. They are all pre-loaded so any delay between files shouldn’t be sought in the files not being fully loaded yet. For initiating playback in the application, I’m using a simple function with a for loop in it, which sets each instance of my sound class to start playing at position 0 of its timeline [SIZE=“1”](I’ve added in highlighting for all of the “code” blocks in my post, to keep everything easy to read through)[/SIZE]:
[color="Blue"]function[/color] playSounds():[color="Blue"]void[/color] {
[color="Blue"]for[/color] ([color="Blue"]var[/color] iCnt:[color="Blue"]Number[/color] = 0; iCnt < aSound.[color="Blue"]length[/color]; iCnt++) {
[COLOR="YellowGreen"]// Start playback for each sound, at the time position set in the
// designated property[/COLOR]
aSound[iCnt].playSong(sPosition);
}
}
The method “playSong” from the class is coded as following:
[COLOR="YellowGreen"]// _iPos = a number in miliseconds where the audio file should begin playback at
// _bIsMute = a boolean which states whether the audio is muted or not[/color]
[color="Blue"]public function[/color] playSong(_iPos:[color="Blue"]Number[/color] = 0, _bIsMute:[color="Blue"]Boolean[/color] = [color="Blue"]false[/color]):[color="Blue"]void[/color] {
bIsMute = _bIsMute;
oSong = soundFactory.[color="Blue"]play[/color](_iPos);
[color="Blue"]if[/color] (bIsMute) {
muteSong([color="Blue"]true[/color]);
}
[COLOR="YellowGreen"]// Call the function soundCompleteHandler when the sound is loaded[/color]
oSong.[color="Blue"]addEventListener[/color]([color="Blue"]Event[/color].[color="Blue"]SOUND_COMPLETE[/color], soundCompleteHandler);
}
As you can see, the playback is all pretty basic. But they don’t seem to all be simultaneously at the same location as far as playback goes - I’ve tested this by using an MP3 which only plays a clicking sound and copy-pasting this file four times, after which I load all four files into the application. When tested, you can definitely hear that they’re not all “clicking” at the same time. They actually do sync sometimes, but more often than not they don’t. What I’ve also noticed is that the two MP3 files which are listed as nodes first in the XML file do actually sync with each other, but that the third and fourth files don’t. It doesn’t matter in which order I place the MP3s (in the XML nodes), the first two will always sync with each other and the third and fourth will not be synced. To clarify, here are the nodes for the application resources:
[color="Blue"]<recources>
<audiofile[/color] [color="Purple"]name=[/color][color="Red"]"Gitaar"[/color] [color="Purple"]order=[/color][color="Red"]"1"[/color][color="Blue"]>
<highquality>[/color]../Mixer_ontwikkeling/Application/media/practice/audio/les1_gitaar_hq.mp3[color="Blue"]</highquality>[/color]
[color="Blue"]<lowquality>[/color]../Mixer_ontwikkeling/Application/media/practice/audio/les1_gitaar_lq.mp3[color="Blue"]</lowquality>
</audiofile>
<audiofile[/color] [color="Purple"]name=[/color][color="Red"]"Zang"[/color] [color="Purple"]order=[/color][color="Red"]"2"[/color][color="Blue"]>
<highquality>[/color]../Mixer_ontwikkeling/Application/media/practice/audio/les1_zang_hq.mp3[color="Blue"]</highquality>
<lowquality>[/color]../Mixer_ontwikkeling/Application/media/practice/audio/les1_zang_lq.mp3[color="Blue"]</lowquality>
</audiofile>
<audiofile[/color] [color="Purple"]name=[/color][color="Red"]"Drums"[/color] [color="Purple"]order=[/color][color="Red"]"3"[/color][color="Blue"]>
<highquality>[/color]../Mixer_ontwikkeling/Application/media/practice/audio/les1_drum_hq.mp3[color="Blue"]</highquality>
<lowquality>[/color]../Mixer_ontwikkeling/Application/media/practice/audio/les1_drum_lq.mp3[color="Blue"]</lowquality>
</audiofile>
<ticker>
<image[/color] [color="Purple"]width=[/color][color="Red"]"5000"[/color][color="Blue"]>[/color]../Mixer_ontwikkeling/Application/media/practice/img/notatiebalk_les3_01.jpg[color="Blue"]</image>
<image[/color] [color="Purple"]width=[/color][color="Red"]"5132"[/color][color="Blue"]>[/color]../Mixer_ontwikkeling/Application/media/practice/img/notatiebalk_les3_02.jpg[color="Blue"]</image>
</ticker>
</recources>[/color]
A solution that I have tried, is to check for each MP3, just after its playback starts, if its current timeline position is the same as the other MP3 files which came before it in the for loop. If that turns out to not be the case, each MP3 is manually set to the same timeline location as the MP3 which resides in the index 0 of the array of the sound class instances.
However, this doesn’t seem to have much effect (it even makes the application go hay-wire on me!). The code which I used for this was:
[COLOR="YellowGreen"]// iRunningTime - an instance of getTimer() which holds the duration of time which has
// passed since the application was started[/color]
[color="Blue"]function[/color] fSyncChannels(iRunningTime:[color="Blue"]uint[/color]) {
syncOutput.[color="Blue"]addEventListener[/color]([color="Blue"]Event[/color].[color="Blue"]ENTER_FRAME[/color], [color="Blue"]function[/color]() {
[color="Blue"]if[/color] (isPlaying) {
[color="Blue"]var[/color] iPlayingTime:[color="Blue"]uint[/color] = [color="Blue"]getTimer[/color]() - iRunningTime;
[COLOR="YellowGreen"]// Don't try to correct the audio channels until after two seconds[/color]
[color="Blue"]if[/color] (iPlayingTime > 2000) {
[COLOR="YellowGreen"]// Iterate through the array of class instances[/color]
[color="Blue"]for[/color] ([color="Blue"]var[/color] iCnt:[color="Blue"]Number[/color] = 0; iCnt < aSound.[color="Blue"]length[/color]; iCnt++) {
[COLOR="YellowGreen"]// If the current position of the current instance differs more than five
// milliseconds from the first instance, stop the instance's playback
// and restart it at the same position as where the first instance is
// currently at[/color]
[color="Blue"]if[/color] ([color="Blue"]Math[/color].[color="Blue"]abs[/color](aSound[iCnt].[color="Blue"]position[/color] - aSound[0].[color="Blue"]position[/color]) >= 5) {
aSound[iCnt].[color="Blue"]stop[/color]();
aSound[iCnt].[color="Blue"]start[/color](aSound[0].[color="Blue"]position[/color] / 1000);
}
}
}
}
});
}
Seeing as this didn’t work, I have no clue what I could give a shot next… so if anyone has any ideas or suggestions, it would be greatly appreciated! Something else I noticed, was that if I run the application on very fast PCs, the MP3s are almost always in sync, but the PC that I’m developing on isn’t that sluggish either and they hardly ever run in sync on it - so it will form a problem for pretty much all of the people who will use the application.
Anyone who has taken the time and effort to read through this huge amount of text and think they have any possible solutions: thanks a lot in advance!