214 lines
4.0 KiB
CoffeeScript
214 lines
4.0 KiB
CoffeeScript
class AsciiIo.Movie
|
|
MIN_DELAY: 0.01
|
|
|
|
constructor: (@options) ->
|
|
_.extend(this, Backbone.Events)
|
|
@reset()
|
|
@startTimeReporter()
|
|
|
|
reset: ->
|
|
@frameNo = 0
|
|
@completedFramesTime = 0
|
|
@playing = false
|
|
@lastFrameAt = undefined
|
|
@framesProcessed = 0
|
|
@clearPauseState()
|
|
@trigger 'reset'
|
|
|
|
call: (method, args...) ->
|
|
@[method].apply this, args
|
|
|
|
now: ->
|
|
(new Date()).getTime()
|
|
|
|
stdout: ->
|
|
@options.stdout
|
|
|
|
play: ->
|
|
return if @isPlaying()
|
|
|
|
if @isFinished()
|
|
@restart()
|
|
else if @isPaused()
|
|
@resume()
|
|
else
|
|
@start()
|
|
|
|
start: ->
|
|
@playing = true
|
|
@trigger 'started'
|
|
@lastFrameAt = @now()
|
|
@nextFrame()
|
|
|
|
stop: ->
|
|
@playing = false
|
|
@cancelNextFrameProcessing()
|
|
now = @now()
|
|
@adjustFrameWaitTime(now)
|
|
@pausedAt = now
|
|
|
|
cancelNextFrameProcessing: ->
|
|
clearInterval @nextFrameTimeoutId
|
|
|
|
adjustFrameWaitTime: (now) ->
|
|
resumedAt = @resumedAt or @lastFrameAt
|
|
currentWaitTime = now - resumedAt
|
|
@totalFrameWaitTime += currentWaitTime
|
|
|
|
restart: ->
|
|
@reset()
|
|
@start()
|
|
|
|
pause: ->
|
|
return if @isPaused()
|
|
|
|
@stop()
|
|
@trigger 'paused'
|
|
|
|
resume: ->
|
|
return if @isPlaying()
|
|
|
|
@playing = true
|
|
@resumedAt = @now()
|
|
frame = @stdout()[@frameNo]
|
|
[delay, data] = frame
|
|
delayMs = delay * 1000
|
|
delayLeft = delayMs - @totalFrameWaitTime
|
|
@processFrameWithDelay(delayLeft)
|
|
@trigger 'resumed'
|
|
|
|
togglePlay: ->
|
|
if @isPlaying() then @pause() else @play()
|
|
|
|
isPlaying: ->
|
|
@playing
|
|
|
|
isPaused: ->
|
|
!@isPlaying() and !@isFinished() and @frameNo > 0
|
|
|
|
isFinished: ->
|
|
!@isPlaying() and @frameNo >= (@stdout().length - 1)
|
|
|
|
seek: (percent) ->
|
|
@stop()
|
|
@rewindTo(percent)
|
|
@resume()
|
|
|
|
rewindTo: (percent) ->
|
|
duration = @options.duration
|
|
requestedTime = duration * percent / 100
|
|
|
|
frameNo = 0
|
|
time = 0
|
|
totalCount = 0
|
|
delay = data = undefined
|
|
|
|
@trigger 'reset'
|
|
|
|
while time < requestedTime
|
|
[delay, data] = @stdout()[frameNo]
|
|
|
|
if time + delay >= requestedTime
|
|
break
|
|
|
|
frameData = String.fromCharCode.apply(String, data)
|
|
@trigger 'data', [frameData]
|
|
time += delay
|
|
frameNo += 1
|
|
|
|
@frameNo = frameNo
|
|
@completedFramesTime = time * 1000
|
|
|
|
@lastFrameAt = @now()
|
|
wait = requestedTime - time
|
|
@totalFrameWaitTime = wait * 1000
|
|
|
|
startTimeReporter: ->
|
|
@timeReportId = setInterval(
|
|
=> @trigger('time', @currentTime())
|
|
500
|
|
)
|
|
|
|
stopTimeReporter: ->
|
|
clearInterval @timeReportId
|
|
|
|
currentTime: ->
|
|
@completedFramesTime + @currentFrameTime()
|
|
|
|
currentFrameTime: ->
|
|
if @isPlaying()
|
|
@playingFrameTime()
|
|
else if @isPaused()
|
|
@pausedFrameTime()
|
|
else
|
|
0
|
|
|
|
playingFrameTime: ->
|
|
if @frameWasPaused()
|
|
@currentFrameWithPauseTime()
|
|
else
|
|
@currentFrameWithNoPauseTime()
|
|
|
|
frameWasPaused: ->
|
|
!!@pausedAt
|
|
|
|
currentFrameWithPauseTime: ->
|
|
@totalFrameWaitTime + @sinceResumeTime()
|
|
|
|
currentFrameWithNoPauseTime: ->
|
|
@now() - @lastFrameAt
|
|
|
|
sinceResumeTime: ->
|
|
@now() - @resumedAt
|
|
|
|
pausedFrameTime: ->
|
|
@totalFrameWaitTime
|
|
|
|
clearPauseState: ->
|
|
@pausedAt = undefined
|
|
@resumedAt = undefined
|
|
@totalFrameWaitTime = 0
|
|
|
|
nextFrame: ->
|
|
frame = @stdout()[@frameNo]
|
|
|
|
if not frame or frame.length is 0
|
|
@playing = false
|
|
@trigger 'finished'
|
|
|
|
return false
|
|
|
|
[delay, data] = frame
|
|
|
|
if delay <= @MIN_DELAY and @framesProcessed < 100
|
|
@framesProcessed += 1
|
|
@processFrame()
|
|
else
|
|
@framesProcessed = 0
|
|
realDelay = delay * 1000 * (1.0 / @options.speed)
|
|
@processFrameWithDelay(realDelay)
|
|
|
|
true
|
|
|
|
processFrameWithDelay: (delay) ->
|
|
@nextFrameTimeoutId = setTimeout(
|
|
=>
|
|
@trigger 'wakeup'
|
|
@processFrame()
|
|
delay
|
|
)
|
|
|
|
processFrame: ->
|
|
frame = @stdout()[@frameNo]
|
|
[delay, data] = frame
|
|
|
|
frameData = String.fromCharCode.apply(String, data)
|
|
@trigger 'data', [frameData]
|
|
|
|
@frameNo += 1
|
|
@completedFramesTime += delay * 1000
|
|
@lastFrameAt = @now()
|
|
|
|
@clearPauseState()
|
|
@nextFrame()
|