"EG Serene" script (main algorithm)  

-- EG Serene Software
-- written by Barbara Lattanzi
-- copyleft 07.20.02

------------------

-- Copyleft for this code in summary:
-- 1. Use this code or modify it however you want.
-- 2. For any use or modification you make of it, you will
-- make that same or modified code freely available to others.
-- (That's all.)   Read more about it.

------------------

-- EG Serene Software is based on the structural algorithm,
-- conceived by Ernie Gehr for his structural film,
-- "Serene Velocity" (1970).

------------------

-- This script is applied to a Quicktime video sprite.
-- This script is written in Lingo, the programming language associated
-- with Macromedia Director, but is translatable to any language capable
-- of controlling digital video.

-- What the algorithm derives from "Serene Velocity" is its overarching time structure
-- in which simulated perceptual events emerge out of the incremental alternation of the two halves
-- of a bisected passage of film.

-- Video playback begins in the middle of the Quicktime movie.
-- The playback is always "foward" (never reverse).
-- Call the first half of the movie "part A", the second half "part B"...
-- The time interval for playback is determined by the viewer (within a certain range).
-- 1. The Quicktime movie starts playing from its midpoint, for the given time interval.
-- 2. After the given interval, the movie begins from the same midpoint and plays it again.
-- 3. After the same given time interval, the movie increments forward into "part B" -- in the amount of 1 tic (60 tics per second) and plays the given time interval.
-- 4. Then the movie leaps back (into "part A") in the amount of 1 tic and plays the given time interval.
-- 5. Repeat steps 3 and 4, continuously incrementing the 2 playback points further and further apart.

-- The parametized values for viewer determination include:
-- 1. actual starting point (the "mid-point") can be placed anywhere on the timeline, because
-- the 2 portions of the Quicktime movie can each autonomously loop through their respective portions.
-- The starting point is a message sent from the timeline bar sprite.
-- 2. the increment (within a preset range) that the Quicktime movie advances or retreats
-- at the start of each interval of playback.
-- 3. the interval (within a preset range), i.e., the amount of time (measured in tics)
-- that the Quicktime movie plays the video before the timer resets the movie to the next point of playback.

property sereneQT, sereneDur, sereneStart, someFPS
property flagDirection, sereneInterval, sereneIncrement 
property sereneForwardMemory, sereneBackwardMemory, bodyFrameLbl
property sereneMem, setDirection, thisCalc, sereneDurMod, sereneDurModMinus1

property forwardAmt, rewindAmt, startTheAlgorithm, stopTheAlgorithm,  tempInterval, diffValue, reversalAmt 
property videoPlaybackMemberNum, videoForwardSome, fixedFraction, someFloatNumber  
property startThisPoint, stopAtThisPoint, sprChannelForRewindControl, controllerBarSpr, manipulatorSpr

property   sereneNum, sereneBody, checkStoredValFlag
property ancestor


on beginSprite me
  me.ancestor = script "calculateMovieTimeAncestorObject"
  
  sereneQT = sprite(me.spriteNum)
  sereneNum = me.spriteNum
  
  sereneQT.movieRate = 0
  sereneMem = sereneQT.member
  sereneMem.directToStage = 1
  sereneMem.crop = 1 
  sereneMem.video =  1
  sereneMem.sound = 0
  sereneMem.loop = 1
  sereneBody = "body"
  
  sereneDur = sereneQT.duration                                -- this stores a value measured in tics (60 tics per second)
  sereneDurMod = sereneDur - (sereneDur mod 2)
  sereneStart = sereneDurMod / 2                               -- parametize later with sendsprite message
  sereneDurModMinus1 =  sereneDurMod - 1
  sereneQT.movieTime = sereneStart                         -- position the playback at the determined starting point
  sereneForwardMemory = sereneStart        
  sereneBackwardMemory = sereneStart
  me.sereneMidStorage(sereneStart)                      --send ancestor script info for placing movietimelocation on initial timeline bar
  
  controllerBarSpr = sprChannelForRewindControl
  flagMovieRate = 1
  checkStoredValFlag = 1
  tempStartFlag = 1
  circleSpr = manipulatorSpr  
  
  bodyFrameLbl = bodyFrameLbl
  
  sereneIncrement = 1              --value should be determined by length of secondary (LEFT) gadget                       
  sereneInterval =sprite(controllerBarSpr).width    --value should be determined by length of primary (RIGHT) gadget    
    
  -- these next 2 properties allow for later changes in movieRate via sendsprite messages from "faster_slower" behavior
  setDirection = 1 
  thisCalc = 1      
  flagDirection = 0
  if sereneQT.movieTime <= startTheAlgorithm  then         -- the movie is under one-second playback
    sereneQT.movieTime = startTheAlgorithm
  end if
   
  startTimer
  flagDirection = 1                                 -- toggles the playback between the 2 portions of the Quicktime movie  
end beginSprite

on exitFrame me
  if the framelabel = sereneBody then            ---necessary to prevent motion at relocater frame
    if checkStoredValFlag = 1 then      
      checkStoredValFlag = 0     
      me.divulgeStoredMovieTime(sereneNum)
    end if
    if flagDirection = 1 then     
      if the timer >= sereneInterval then               --should be determined by length of right gadget
        checkForwardAmount 
        tmp = sereneForwardMemory + sereneIncrement   --increment should be determined by length of left gadget
        if tmp > sereneDurModMinus1  then  
          tmp = sereneStart  
        end if 
        sereneQT.movieTime = tmp
        --revise memory of position
        sereneForwardMemory = tmp      
        startTimer    
      end if
    end if
    
    if flagDirection = 0 then
      if the timer >= sereneInterval then         --should be determined by length of right gadget
        checkReversalAmount                        
        tmp = sereneBackwardMemory  - sereneIncrement  --increment should be determined by length of left gadget
        if tmp < 2  then  
          tmp = sereneStart  
        end if 
        sereneQT.movieTime = tmp
        sereneBackwardMemory = tmp
        startTimer      
      end if
    end if
  end if
end

--  a sendsprite message from the ancestor script and/or bttnBetweenReloc&Body script
on getStoredValue me, timeLoc  
  sereneForwardMemory = timeLoc
  sereneBackwardMemory = timeLoc
  checkStoredValFlag = 0
end 
---------------------------------
---------------------------------
-- This handler called by script attached to LEFT slider bar gadget sprite  (see script "lengthForSereneIncrement")
on changeTimeOverlap me, newAmount     
  -- Subtract small amount to insure that the video segment doesn't go into an endless loop.
  newAmt = (newAmount * 2) - 4                           
  fixedFraction =  integer(.1* newAmt)  --.01 * newAmt
  sereneIncrement  = fixedFraction    
end

on changeTimeOverlapNow me, newAmount
  -- Subtract small amount to insure that the video segment doesn't go into an endless loop.
  tempStartFlag = 1
  newAmt = (newAmount * 2) - 4                           
  fixedFraction =  integer(.1* newAmt)  --.01 * newAmt
  sereneIncrement = fixedFraction   
end
---------------------------------
---------------------------------
on checkForwardAmount me
  if sprite(controllerBarSpr).width > 2 then                   -- This value is problematic if it = 0 or 1, so set at a min. of 2
    forwardAmt = sprite(controllerBarSpr).width                -- measure this width to determine time-span measure. 
  else
    forwardAmt = 2                                                      
  end if
  rewindAmt = forwardAmt  * fixedFraction  
  tempInterval =  sereneQT.movieTime + forwardAmt     
  sereneInterval  =   forwardAmt  
  flagDirection = 0   
end checkForwardAmount
---------------------------------
---------------------------------
on checkReversalAmount me
  if sprite(controllerBarSpr).width > 2 then                   -- This value is problematic if it = 0 or 1, so set at a min. of 2                      
    reversalAmt = sprite(controllerBarSpr).width               -- measure this width to determine time-span measure. 
  else
    reversalAmt = 2
  end if
  reversalJumpAmt = reversalAmt  * fixedFraction  
  tempInterval =  sereneQT.movieTime - reversalAmt    
  sereneInterval  =   reversalAmt  
  flagDirection = 1  
end checkReversalAmount
---------------------------------
---------------------------------
on forwardOrBackward me, setDirectionNow  
  -- Parameter passed in by another sprite broadcasting with "sendSprite" message, based on 
  --  whether the interactor has moved the moveable sprite across the middle boundary 
  --  between forward and reverse.
  -- NOTE: in order for the parameter to be read as a value, use keyword "me". 
  
  setDirection = setDirectionNow
  
  if setDirection = 1 then
    sereneQT.movieRate = 1 * thisCalc
    flagMovieRate = 1
  else
    if setDirection = -1 then      
      sereneQT.movieRate = -1 * thisCalc
      flagMovieRate = 0
    end if
  end if  
end forwardOrBackward

on setNewRate me, thisCalcRate     --sendsprite message from faster_slower sprite behavior
  thisCalc = thisCalcRate
  sereneQT.movieRate = thisCalc  * setDirection
end
---------------------------------
---------------------------------
---------------------------------
on getPropertyDescriptionList
  sereneList = [:]
  addProp sereneList , #videoPlaybackMemberNum, [#default :0, #format :#integer, #comment:"slot into which video is imported for playback algorithm"]
  addProp sereneList , #videoForwardSome, [#default :120, #format :#integer, #comment:"advance how many tics, 60/sec"]
  addProp sereneList , #someFloatNumber, [#default :.90, #format :#float, #comment:"rewind by what fraction of forward amount"]
  addProp sereneList , #startThisPoint, [#default :0, #format :#integer, #comment:"when to begin algorithm"]
  addProp sereneList , #stopAtThisPoint, [#default :#0, #format :#integer, #comment:"stop algorithm this amount prior to end of clip"]  
  addProp sereneList , #manipulatorSpr, [#default :#0, #format :#integer, #comment:"sprite that viewer uses to change playback"]
  -- The following property is to accomodate user-interface for manipulating the video:
  addProp sereneList , #sprChannelForRewindControl, \
 [#default :0, #format :#integer, #comment:"leave at 0 if viewer does not control, else spriteNum that changes length"]     
  return sereneList 
end