Skip to main content
Known Participant
July 14, 2008
Answered

how to calculate second and minisecond for audio cue point

  • July 14, 2008
  • 2 replies
  • 2298 views
I'm trying to do a sound sync for the audio and movie clip in Flash. I found a tutorial on the web and it seems to work. However, I don't know how to calculate the second and mini second from the mp3 file. The first frame has the following lines to define the time for each animation:
// Import class
import net.quip.sound.SoundSync;

// Stop main timeline
stop();

// Create an instance of SoundSync
var ss:SoundSync = new SoundSync();

ss.addCuePoint("IT", 20100);
ss.addCuePoint("admin", 20000);
ss.addCuePoint("maitenance", 19800);
ss.addCuePoint("treatment", 16479);
ss.addCuePoint("engineering", 14598);
as.addCuePoint("logo_epa_npdes", 12356);
ss.addCuePoint("construction", 8967);
ss.addCuePoint("370mgd", 7896);
ss.addCuePoint("mc_650,000",5439 );
ss.addCuePoint("130mgd", 3254);
ss.addCuePoint("1938", 2439);
ss.addCuePoint("vintage_mc", 0);

// Use instance to load external MP3
ss.loadSound("Scn04-05.mp3", true);

// Create a listener object for the
// cuePoint and onSoundComplete events
var listener:Object = new Object();
listener.cuePoint = function():Void {
play();
}
listener.onSoundComplete = function():Void {
play();
}
ss.addEventListener("cuePoint", listener);
ss.addEventListener("onSoundComplete", listener);

and the soundsync.as file as follows:
import mx.events.EventDispatcher;
import mx.utils.Delegate;
class net.quip.sound.SoundSync extends Sound {
// PROPERTIES
private var _cuePoints:Array;
private var _currentCuePoint:Number;
private var _interval:Number;
private var _intervalDuration:Number;
private var _secondOffset:Number;
// Event dispatcher
public var dispatchEvent:Function;
public var addEventListener:Function;
private var removeEventListener:Function;
// CONSTRUCTOR
public function SoundSync(target:MovieClip) {
super(target);
init();
}
// METHODS
private function init():Void {
// Initialize properties
_cuePoints = new Array();
_currentCuePoint = 0;
_intervalDuration = 200;
_secondOffset = 0;
// Initialize class instance as valid event broadcaster
EventDispatcher.initialize(this);
}
// Add Cue Point
public function addCuePoint(cuePointName:String, cuePointTime:Number):Void {
_cuePoints.push(
{
type: "cuePoint",
name: cuePointName,
time: cuePointTime,
target: this
}
);
_cuePoints.sortOn("time", Array.NUMERIC);
}
// Get Cue Point
public function getCuePoint(nameOrTime:Object):Object {
var counter:Number = 0;
while (counter < _cuePoints.length) {
if (typeof(nameOrTime) == "string") {
if (_cuePoints[counter].name == nameOrTime) {
return _cuePoints[counter];
}
} else if (typeof(nameOrTime) == "number") {
if (_cuePoints[counter].time == nameOrTime) {
return _cuePoints[counter];
}
}
counter++;
}
return null;
}
// Get Current Cue Point Index
private function getCurrentCuePointIndex(cuePoint:Object):Number {
var counter:Number = 0;
while (counter < _cuePoints.length) {
if (_cuePoints[counter].name == cuePoint.name) {
return counter;
}
counter++;
}
return null;
}
// Get Next Cue Point Index
private function getNextCuePointIndex(seconds:Number):Number {
seconds = (seconds) ? seconds : 0;
var counter:Number = 0;
while (counter < _cuePoints.length) {
if (_cuePoints[counter].time >= seconds * 1000) {
return counter;
}
counter++;
}
return null;
}
// Remove Cue Point
public function removeCuePoint(cuePoint:Object):Void {
_cuePoints.splice(getCurrentCuePointIndex(cuePoint), 1);
}
// Remove All Cue Points
public function removeAll_cuePoints():Void {
_cuePoints = new Array();
}
// Start
public function start(secondOffset:Number, loops:Number):Void {
super.start(secondOffset, loops);
dispatchEvent({type:"onStart", target:this});
// Reset current cue point
_secondOffset = secondOffset;
_currentCuePoint = getNextCuePointIndex(secondOffset);
// Poll for cue points
clearInterval(_interval);
_interval = setInterval(Delegate.create(this, pollCuePoints), _intervalDuration);
}
// Load Sound
public function loadSound(url:String, isStreaming:Boolean):Void {
super.loadSound(url, isStreaming);
clearInterval(_interval);
_interval = setInterval(Delegate.create(this, pollCuePoints), _intervalDuration);
}
// Stop
public function stop(linkageID:String):Void {
if (linkageID) {
super.stop(linkageID);
} else {
super.stop();
}
dispatchEvent({type:"onStop", target:this});
// Kill polling
clearInterval(_interval);
}
// Poll Cue Points
private function pollCuePoints():Void {
// If current position is near the current cue point's time ...
var time:Number = _cuePoints[_currentCuePoint].time;
var span:Number = (_cuePoints[_currentCuePoint + 1].time) ? _cuePoints[_currentCuePoint + 1].time : time + _intervalDuration * 2;
if (position >= time && position <= span) {
// Dispatch event
dispatchEvent(_cuePoints[_currentCuePoint]);
// Advance to next cue point ...
if (_currentCuePoint < _cuePoints.length) {
_currentCuePoint++;
} else {
_currentCuePoint = getNextCuePointIndex(_secondOffset);
}
}
}
// EVENT HANDLERS
// onSoundComplete
public function onSoundComplete():Void {
// Kill polling
clearInterval(_interval);
// Reset current cue point
_currentCuePoint = 0;
// Dispatch event
dispatchEvent({type:"onSoundComplete", target:this});
}
}

any help will be greatly appreciated.
This topic has been closed for replies.
Correct answer Newsgroup_User
wuzhishan,

> Hi Dave, your tutorial is a cute one and it is very useful.

Thanks! My daughter is the cute one. She inherited all of that from
me, so I don't have any left. ;)

> I'm sorry I didn't make my question clear.

No worries. :)

> I listen to the mp3 in Windows Media Player and try to
> get the time for the cue, I found it on the bottom of the
> player the time shows 00:26.

I'm with ya.

> How do I writer this number in the code? Is it 0026 or 26?

The code is expecting milliseconds, so 26 seconds would be written like
this:

26000

If it said 1:22 (a minute and 22 seconds), you would write it like this:

82000

... which is 60 seconds for the single minute, plus 22 from the seconds
side, then multiplied by 1,000 to get milliseconds.


David Stiller
Adobe Community Expert
Dev blog, http://www.quip.net/blog/
"Luck is the residue of good design."


2 replies

Inspiring
July 14, 2008
wuzhishan,

> I'm trying to do a sound sync for the audio and movie clip in Flash.
> I found a tutorial on the web and it seems to work. However, I
> don't know how to calculate the second and mini second from
> the mp3 file.

I wrote that article and the code, so I'll help if I can. :) But I'm
not sure I understand your question yet.

Are you asking, "How do I convert minutes and seconds into
milliseconds?" If so, here's an example. Let's say you want to add a cue
point at 4 minutes and 41 seconds. Converting the 41 seconds part is easy:
just mutiply that number by 1,000. So far, you've got 41,000 milliseconds.
Now set that number aside for just a moment and turn your attention to the
minutes.

You've got 4 minutes. There are 60 seconds in a minute, so convert to
seconds first. 4 * 60 is 240. At this point, you're back to where you were
before (a seconds value). Multiply that 240 by a thousand -- 240,000 -- and
add it to the other milliseconds value: 240,000 plus 41,000 equals 281,000
milliseconds.

So 4min 41sec is 281,000 milliseconds.

Does that help?


David Stiller
Adobe Community Expert
Dev blog, http://www.quip.net/blog/
"Luck is the residue of good design."


Newsgroup_UserCorrect answer
Inspiring
July 16, 2008
wuzhishan,

> Hi Dave, your tutorial is a cute one and it is very useful.

Thanks! My daughter is the cute one. She inherited all of that from
me, so I don't have any left. ;)

> I'm sorry I didn't make my question clear.

No worries. :)

> I listen to the mp3 in Windows Media Player and try to
> get the time for the cue, I found it on the bottom of the
> player the time shows 00:26.

I'm with ya.

> How do I writer this number in the code? Is it 0026 or 26?

The code is expecting milliseconds, so 26 seconds would be written like
this:

26000

If it said 1:22 (a minute and 22 seconds), you would write it like this:

82000

... which is 60 seconds for the single minute, plus 22 from the seconds
side, then multiplied by 1,000 to get milliseconds.


David Stiller
Adobe Community Expert
Dev blog, http://www.quip.net/blog/
"Luck is the residue of good design."


Inspiring
July 29, 2008
wuzhishan,

> Could you please provide the code for this issue, as you
> described: attach movie clips from library, or turn on (or
> off) the visibility of movie clips already on the Stage?

That would depend on the information you provide with your cue points.
If the cue points include the name of a particular movie clip with them --
as yours seem to (IT, admin, maintenance, treatment, etc.) -- then you can
use that information for attaching rather than as the name of a frame label.
If you want to attach a movie clip at runtime, and if you're using AS2
(which you are), then you'll use the MovieClip.attachMovie() method, which
works like this:

someMovieClipReference.attachMovie("linkageID", "instanceName", depth);

... where linkageID refers to the linkage identifier you give the symbol in
your Library. Right-click a given movie clip and you'll see what I mean.
Select Linkage, and you'll have a place to put your linkage identifier,
which is simply an arbitrary name ... just like any of the strings you're
using (IT, admin, maintenance, treatment, etc.). The instance name can be
the same string, or make up something else -- as long as it's unique to the
scope you're in. The depth can be handled automtatically by way of the
MovieClip.getNextHightestDepth() method. Here's one way you could do it:

var timeline:MovieClip = this;
var listener:Object = new Object();
listener.cuePoint = function(evt:Object):Void {
timeline.attachMovie(evt.name, evt.name, timeline.getNextHighestDepth());
}

This is the same code block I suggested earlier (the event handler --
the handler listening for the cuePoint event). In this case, you're once
again using the evt parameter. This time, instead of referencing evt.time,
as I suggested in my previous reply, you're using evt.name, which would be
the IT, admin, maintenance, treatment, etc., I keep mentioning. In this
event handler, you're invoking the MovieClip.attachMovie() on a reference
called timeline, which is simply a MovieClip reference to the main timeline.
The main timeline *is* a movie clip, and a movie clip reference is all you
need to invoke any method in the MovieClip class.

Incidentally, methods are simply things an object can do. If you look
up "MovieClip class" in the ActionScript 2.0 Language Refernece, you'll see
the class -- the blueprint -- that defines all movie clips. Methods are the
things a movie clip, such as the timeline, can do. The things it can react
to are called events. The characteristics it has are called properties.
This approach toward discovering an object's functionality ... it works for
all object classes. You might get something out of this article, which goes
into greater detail on how to use the AS2 language reference in Flash:

http://www.communitymx.com/abstract.cfm?cid=01B54 (free content)

So ... the timeline variable gives you a handy reference to the main
timeline, which is where this code appears (which is why the keyword "this"
refers the main timeline when written there). The timeline variable gives
you a reference used twice in my suggested code. First, to invoke the
attachMovie() method, which requires at least three parameters: a linkage
ID (taken from the cue point's name property; an instance name, also taken
from the cue point's name property; and finally a depth value, taken from
the second timeline refernce, along with a reference to the
MovieClip.getNextHighestDepth() method. In other words, this will set your
movie clip to the next available depth in the main timeline.

To use a different approach -- such as turning on the visibility of a
movie clip already on the Stage -- you'll need to make sure your clips
already have unique instance names, which you can provide using the Property
inspector. Give each movie clip an instance name, then invoke the
MovieClip.stop() method on each in if the first frame of your movie --
assuming that each movie clip appears in the first frame. In fact, you
should also use the same opportunity to set the MovieClip._visible property
of each to false. Again, using the same instance names:

IT.stop();
IT._visible = false;
admin.stop();
admin._visible = false;
maintenance.stop();
maintenance._visible = false;
treatment.stop();
treatment._visible = false;
// etc.

Then, in your event handler, you might use something like this:

var timeline:MovieClip = this;
var listener:Object = new Object();
listener.cuePoint = function(evt:Object):Void {
timeline[evt.name]._visible = true;
timeline[evt.name].play();
}

In this case, you're using something called the array access operator,
[], to convert a string -- the cue point's name value -- into an object
reference. This article explains the technique:

http://www.quip.net/blog/2006/flash/actionscript-20/reference-objects-dynamically

I can understand if any of these new suggestions are bewildering.
Programming isn't especially easy ... otherwise anyone could do it, and we
wouldn't need forums like this one. :) I hope these suggestions at least
give you a bit more to experiment with.

> I have two more questions:
> 1. how to tell the movie gotoAndPlay next scene after the audio
> finish?

For that, you'll use the SoundSync.onSoundComplete event, as shown in
the samle file with the original article. Instead of invoking the play()
method, you would have to use the gotoAndPlay() funtion -- not a method!
The method version of this functionality doesn't accept scenes as a
parameter. See this for more info:

http://www.quip.net/blog/2006/flash/actionscript-20/gotoandplay-with-scenes

> 2. how to program a preloader for this audio cue point movie?

A preloader for the MP3? Preloading, in general, means comparing the
amount of a file that has already loaded with the total amount of bytes it
contains. Because the SoundSync class extends the native Sound class,
you'll see that your SoundSync instance has access to two useful methods:
getBytesLoaded() and getBytesTotal(). You can see an article that
illustrates these methods (for a different object class) here:

http://www.quip.net/blog/2006/flash/how-to-tell-when-external-swf-loaded


David Stiller
Co-author, Foundation Flash CS3 for Designers
http://tinyurl.com/2k29mj
"Luck is the residue of good design."



wuzhishan,

> Thank you so much for such a detailed, step by step explaination
> on how to make my audio cue point work. I really appreciate your
> help.

Sure thing. :)

> I found a more simpler way of doing audio cue point. Just convert
> the mp3 to flv and add ac cue point to the audio flv.

Yup, that'll do it too, so now you have a number of flexible options at
your fingertips!


David Stiller
Adobe Community Expert
Dev blog, http://www.quip.net/blog/
"Luck is the residue of good design."


Inspiring
July 14, 2008
Why do you need to compute the milli/second (I assume that is what you mean)? The cuepoints know what the time is and when they need to be called. What are you actually trying to accomplish?

In that part in your file you could change this:

listener.cuePoint = function():Void {
play();
}

to something more useful, perhaps that would help:

listener.cuePoint=function(event:Object):Void{
trace("Cuepoint "+event.name+" at: "+event.time);
play();
}