--*******************************************************************************************
--*ENIGMA N^2
--*by Jim Andrews, vispo.com, jim@vispo.com, August 2002
--*Sound as object, meaning as constructed, sound poetry as
generative music,
--application as machine made out of words.
--*A two-frame movie.
--*The below is the one and only script, a frame script.
--*Source code available below.
--*If you use this code, credit where credit is due, please.
--*Comments are this color. Properties start with p. Globals
start with g.
--*Variables of local scope start with 'the'.
--*******************************************************************************************
global gWaveForm, gCurrentArea, gBar --Spritenums of the wave form, currently playing area, and bar.
property pDuration --Length of
the sound in milliseconds.
property pWaveFormLength --Length in
pixels of the waveform graphic.
property pConstant --Conversion
factor of time to length (used to synch
the sonic and visual).
property pConstantReciprocal --The reciprocal of pConstant. Used
to convert length to time.
--Maya must be cleverly synchronized but the world just is.
Digital media objects are independent
--of one another, whereas material objects cause sound. Except
via imagining. You are currently
--located somewhere between the two.
property pAnchorTime --Total milliseconds during which
pFrameCounter frames played.
property pFrameCounter --Counts the number of frames the
movie has processed during pAnchorTime.
global pBarIncrement --This is the number of pixels to
move the 'sliver of now' each frame.
property pBarPosition --Horizontal position of the sliver
of now.
property pPlayerHasSelected
--Set to TRUE when the player clicks. Set to FALSE after the
player's click is processed.
property pStartTime --The
millisecond in the sound where play begins.
on beginsprite me
--This runs
once and only once.
gWaveForm = 1
gCurrentArea = 2
gBar = 3
sprite(gCurrentArea).locV = sprite(gWaveForm).top
sprite(gBar).locV = sprite(gWaveForm).top
--The above
two lines position the current area and the sliver of now vertically. Note that
for these lines to
--work, these
sprites must exist when this code is run. Which is why, if you look at the
source code, the
--gWaveForm,
gCurrentArea, and gBar sprites exist in frame 1, but this script is in frame 2.
Before this
--script is
run for the first time, gWaveForm, gCurrentArea, and gBar need a frame to come
into existence.
pDuration = member("meaning").duration
--If you want
to use a different sound and graphic, just change the code in three ways:
change "meaning"
--in the
above line to the name of the sound you want to use, and change
"waveForm" in the below line
--to the name
of the wave form graphic representation of the sound. Drag a copy of your wave
form
--graphic
onto the stage. Also, make sure that the wave graphic is sprite 1, or change
gWaveForm.
pWaveFormLength = member("waveForm").width --The length
of the wave graphic in pixels.
pConstant = pWaveFormLength/float(pDuration) --SelectedLengthInPixels = pConstant
* DurationOfSelectedPortionOfSound
pConstantReciprocal = 1/pConstant --DurationOfSelectedPortionOfSound =
pConstantReciprocal * SelectedLengthInPixels
pAnchorTime = float(the milliseconds)
pFramecounter = 0
pBarIncrement = 1.5 --This value
is updated in calculatepBarIncrement.
pPlayerHasSelected = FALSE --Determines whether the player or
computer sets pStartTime.
member("selected").rect = rect(0, 0, 0, member("waveForm").height)
member("sliver
of now").rect = rect(0, 0, 1, member("waveForm").height)
--The above
lines change the height of the selected area and the sliver of now to be the
same as the height
--of the wave
form graphic if you download the code and use a different wave form graphic and
sound,
--which I
encourage you to do to discover the music of different types of sounds.
Burroughs said that when
--you cut
tape, the future leaks out. Same with cutting into digital sound. The size of
your graphic and the
--length of
your sound can be different from the source code's graphic and sound.
timeOut("playAndUpdate").new(VOID, #playAndUpdate, me)
timeOut("updateBar").new(VOID, #updateBar, me)
--The
"playAndUpdate" timer is used to play sounds and move the graphic of
the currently selected area to
--its proper
spot and width. The "updateBar" timer is used to move the sliver of
now back to the beginning
--at the end
of each loop. If you want more info on timers in Director, click the link at
the bottom of this page.
playAndUpdate me --Start the
sound.
end beginsprite
on exitFrame me
--Updates the
position of the bar. The 'if' statement means: "If the position of the bar
is not going to end up to
--the right
of the selected area, then move the bar. " Now, we should not need that
'if' if pBarIncrement were
--calculated
with total precision. But if you set a Director movie to x fps, it may run
faster than that, it may run
--slower,
depending on the speed of the machine and the amount of background processing.
And
--pBarIncrement
depends on the framerate. So I calculate the effective frame rate, as you can
see in
--the
calculatepBarIncrement handler at the bottom of the page.
if pBarPosition
+ pBarIncrement <= sprite(gCurrentArea).right then
pBarPosition =
pBarPosition + pBarIncrement
sprite(gBar).locH =
pBarPosition
end if
pFrameCounter =
pFrameCounter + 1
go to the frame
end exitFrame
on playAndUpdate me
--This is
called when the "playAndUpdate" timer times out, which happens when a
sound finishes, after
--theLoopCount
repetitions of the sound. This handler is also called if the player clicks.
This handler is
--the brains
of the show. First it recalculates
pBarIncrement. Then it sets a startTime,
if the user hasn't
--clicked to
establish it, and a random endTime, and a loop count. Then it does other
nefarious things,
--as below,
including playing the sound. Finally, it sets itself to time out when the next
sound ends.
calculatepBarIncrement me
--Randomly
select a new current playing area and play the sound.
--If the
player has clicked, use their pStartTime.
if not pPlayerHasSelected then
pStartTime = random(pDuration-250)
end if
theRemainder =
pDuration - pStartTime
theEndTime = random(theRemainder)
+ pStartTime
theLoopCount = random(6)
sound(1).play([#member: member("meaning"), #startTime: pStartTime, #endTime: theEndTime, #loopCount:
theLoopCount])
--Adjust the
visible width and location of the current playing area.
sprite(gCurrentArea).width =
(theEndTime - pStartTime) * pConstant
sprite(gCurrentArea).locH = sprite(gWaveForm).left + pConstant
* pStartTime
--Set the
bar.
pBarPosition = sprite(gCurrentArea).locH
sprite(gBar).locH =
pBarPosition
--Set the
timer alarm to when the current sound ends.
timeOut("playAndUpdate").period =
(theEndTime - pStartTime) * theLoopCount
--This timer
updates the bar at the end of each loop.
timeOut("updateBar").period = theEndTime
- pStartTime
pPlayerHasSelected = FALSE
end playAndUpdate
on updateBar me
--Position
the bar at the beginning of the selected current area.
pBarPosition = sprite(gCurrentArea).locH
sprite(gBar).locH =
pBarPosition
end updateBar
on mousedown me
--If the
player clicks, they establish the new startTime.
theMousieLocH = the mouseloc.locH
if
(theMousieLocH >= sprite(gWaveForm).left) and
(theMousieLocH < (sprite(gWaveForm).right - 250 * pConstant)) then
--If the
player clicked further than 250 ms from the end of the sound.
pPlayerHasSelected = TRUE
pStartTime =
(theMousieLocH - sprite(gWaveForm).left) * pConstantReciprocal
playAndUpdate me
end if
end mousedown
on calculatepBarIncrement me
--This
handler recalculates pBarIncrement periodically. pBarIncrement is the number
--of pixels
to move the bar each frame. This number depends on how fast the machine
--is running.
The idea is to check and see how fast the machine is running and calculate
--pBarIncrement
accordingly.
if
pFrameCounter >10 then
--Then we
have some data to work with.
effectiveFrameRate =
pFrameCounter / (the milliseconds - pAnchorTime) --frames / ms
pBarIncrement =
pWaveFormLength / (pDuration * effectiveFrameRate) --pixels /
frame
else
pBarIncrement = 1.5 --This value
is quickly replaced with a more accurate one.
end if
if pFrameCounter > 500 then
--Time to
reset pFrameCounter and pAnchorTime to keep tabs on the current frame rate.
--Although in
the source code the tempo is set at 20 frames per second, an empirical
--measure of
the frameRate I did measured the effective frameRate at 53 fps on my
--P2 450 MHz
machine. So, if we say that the framerate is roughly 50 fps, then it takes
--ten seconds
before this code is executed, ie, we reset pFrameCounter and
--pAnchorTime once every 10 seconds
or so. That will vary between machines,
--but that's
OK. The main thing is to keep accurate tabs on the current frame rate,
--which will
vary throughout the playing of the piece, owing to the machine doing
--other things in the background.
Never assume you know what the frame rate
--of a movie
is: measure it periodically. Otherwise, your piece may run fine on your
--machine but
not likely very well on other machines.
pAnchorTime = float(the milliseconds)
pFrameCounter = 0
end if
end calculatepBarIncrement
|
|