T E C H N O T E S
O N N I O
|
|
|
INTRO
I've enjoyed sites/works of Shockwave in which the author provides not only a good piece of work but also some tech notes on the Director programming in Lingo. That's what I want to do here: discuss some of the technical aspects of Nio and Vismu (visual music) more generally; consequently, this article is really for people interested in doing audio programming with Director. I haven't encountered any current articles on Lingo audio; hence this one. The considerably expanded audio capabilities of Director 8 haven't been out for long; they should be discussed since they're a great tool for serious audio/multimedia work on the Web. This article deals with operations performed on sounds in internal or external casts. Sounds from internal or external casts do not play until they are fully downloaded. When attempting to loop sounds or have near instantaneous access to sounds, internal or external cast members should always be used. Avoid using sounds that aren't members of internal or external casts for random playback purposes, because these sounds must be downloaded each time they are played. Internal or external cast sounds can be compressed quite dramatically (you can set the compression ratio), and Vismu deals with fairly short sounds (2-12 seconds long). This means that the individual audio clips download very quickly. In fact, using Vismu is better than being able to cache non-Cast-member sound files. Even if it was possible to cache these files, you would also have to find a way to reliably stream multiple sounds at once, which would not be feasible using slow connection speeds. The streaming strategy implemented in Nio is pretty much optimal, given the form of Vismu. QUEUING AND PLAYING
SOUNDS
|
|
CUEPOINTS
ALTERNATIVES TO CUEPOINTS
|
|
GETPLAYLIST()
Verse One of Nio uses sound(channel).getPlayList().count = 0 to initiate re-queuing. There is a script attached to the frame in which Verse One runs. Verse One stays in one frame after the initial streaming has been completed. In other words Verse One is a 'one frame movie' entirely controlled by Lingo after the streaming is finished (which extends over about sixteen frames). The first thing the frame script does is check to see whether sound(2).getPlayList().count = 0. If this is not so, the frame script does absolutely nothing. I should also mention that the movie frame rate is set at 64 fps while the movie is in that frame and elsewhere in Verse One. In other words, this condition is checked rather frequently. The reason for this fast rate is because if the user clicks a sound icon that isn't queued, which should initiate play of the associated sound and animation, one would like the re-queuing to be done el pronto. And re-queuing is done in this frame script. TIMERS IN NIO AND VIS/AUDIO SYNCHThere are several timers in Nio.
One is used during the streaming process to minimize the number of
times Nio checks (using frameready()) to see if the next frame has
finished downloading, because that seems to suck juice and Nio has
other things to do; it does a lot of sound processing and a lot of
animation processing. Also, since it seems you can't currently put
a cuepoint in a sound near the end of the sound or the beginning of
a sound and expect it to work properly if you are repeatedly queuing
sounds (bug!), I've had to insert a cuepoint about 1.3 seconds before
the end of a sound (the sounds are all 4 seconds long), queue the
next sounds to be played, and set a timer to go off in 1.3 seconds,
because there is some processing to be done when a new sound starts,
such as starting its associated animation. Also, when a new sound starts, that
sets up a timer that goes off at the end of each beat of music. At
the end of each beat of music, Nio adjusts the frame rates of the
animations currently playing so that the animations are synched with
the audio. The more animations playing at once,
the slower each of them plays, if left to their own devices. I've
found that changing the fixedRate
property (basically the frame rate) of the imported Flash animations
speeds them up. Adjusting the frame rate is preferable to adjusting
the frame: if you change the frame the animation is on rather than
the frame rate, the animation will end up looking jerky. In Verse One of Nio, there are times
when the animations speed up dramatically and aren't in synch with
the sound. I could have fixed that, but I thought it looked neat.
Verse Two is more constant in its synchronization of audio and visuals
(but not quite as alive as Verse One, for my money). TIMERS IN DIRECTOR
Here is how to create and use timers
in Director. I first create an object, ie, an
instantiation of a parent script, usually before the movie starts in the 'on
prepareMovie' handler. gResetAnimations = new(script "ResetAnimationsScript", param1,
param2) gResetAnimations
is a global variable. It's not the timer. It's just handy to have
an object for each timer you create if you have parameters (param1, param2) associated with the workings of the timer specific
to what the timer does in your app. There is a cast member in the
movie, a parent script, named ResetAnimationsScript in the "cast" window, which contains the
media elements of the movie. And
then in the next line of the 'on prepareMovie' handler I create the
timer: dummy = timeOut("ResetAnimations").new(VOID, #ResetAnimations, gResetAnimations) Let me explain the above line. The above line names the timer ResetAnimations. The 'period' of the timer, ie, the number of milliseconds it runs before timing out, is set to VOID because I don't want the timer to run just yet. You can assign a handler to a timer so that when the timer times out, the code in the handler runs. When ResetAnimations times out, it runs the ResetAnimations handler. And the associated object of the timer is gResetAnimations; the timer will look in that object first for the ResetAnimations handler, though the handler can just as well be in any movie script. It should also be noted that there is a bug in Director 8, 8.5, and 8.5.1 (the most recent version as of this writing) which necessitates the dummy part in the above line of code. If you do not assign the timeOut object to a variable, Director does not dispose of the memory for the timeOut object correctly and a memory leak (and possibly worse) results. However, the above takes care of the problem. Thanks to Jakob Hede Madsen for pointing this out. Now let's look at the ResetAnimationsScript. What
this code does is reset animations back to their first frame if they
are playing when the timer times out, which happens when new sounds
begin playing. property theParam1, theParam2 on new me, myParam1, myParam2 me.theParam1 = myParam1 me.theParam2 = myParam2 -- This is the constructor of the
object, ie, this code runs only when the object is created. theParam1
and theParam2 are not functional in the example code here but are
included just to show how to pass and use such parameters when creating
objects. return me end on
ResetAnimations me global gSoundIconsPlaying, gBeatLength,
gFreezeSprite, gInVerse1 if gInVerse1 then --if we are in Verse One
of Nio if sprite(gFreezeSprite).up then --if the Start/Stop button is up repeat with Channel= 3 to 8 --Director has 8 channels of stereo sound at once if gSoundIconsPlaying[Channel]<>0 then --if there is an animation playing
in the Channel animToReset
= sprite(gSoundIconsPlaying[Channel]).itsAnimation) --name the animation to
be reset sprite(animToReset).frame=1 --reset the animation
to frame 1 sprite(animToReset).play() --play the animation end if end repeat timeout("ResetAnimations").period = VOID --stop this timer (it will be restarted by the
next cuepoint passed) timeout("AdjustAnimationSpeeds").period = gBeatLength --start the timer that goes off at the end of
each beat end if end if end Note that the timer turns itself off with the line and starts a different timer with the line timeout("AdjustAnimationSpeeds").period = gBeatLength The latter timer is the one that adjusts the frame rate of the animations at the end of each (except the last) beat of music for each sound. In general, once you have created a timer, you start it, as in the last line of code, by assigning its period some number which determines how long it will run before timing out. If you do not set its period to VOID, then the timer will run again. Something else to note about timeOut target child objects is that they receive stopMovie events. Thanks to Alex da Franca for this information. I was experiencing an odd thing: my stopMovie handler in a movie script was getting called twice. The stopMovie event was being sent to the timeOut target child object; the target child object contained no stopMovie handler, so the message was passed along the message hierarchy, and the movie script ran it twice. The moral of the story is that you can put a stopMovie handler in the timeOut target child object which will destroy the timer, and then the stopMovie handler will not be called twice. on stopMovie me,
timeOutObjectRef if
ilk(timeOutObjectRef)= #timeout then end For more information on timer objects, check out Enigma n2, which uses them. The source code is available. Also, see Using Director Shockwave Studio. Additionally, consult the following terms in the Lingo Dictionary:
|
|
LINKS TO USEFUL DIRECTOR-RELATED
SITES
|
|
|