--*******************************************************************************************
--*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.
  dummy1 = timeOut("playAndUpdate").new(VOID, #playAndUpdate, me)
  dummy2 = 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
 
 
| 
 |  |