Commit 417ce8c3 authored by Michael Murtaugh's avatar Michael Murtaugh
Browse files

added masterful timeline example

parent 00b1af43
<!DOCTYPE html>
<html>
<head>
<title>MFTL demo</title>
<!-- jquery -->
<script src="../lib/jquery/jquery-1.7.1.min.js"></script>
<script src="../lib/jquery/jquery-ui-1.8.17.custom.min.js"></script>
<script src="../source/jquery.voidplayer_simple.js"></script>
<link rel="stylesheet" href="../lib/jquery/css/ui-lightness/jquery-ui-1.8.17.custom.css" />
<script>
$(document).ready(function () {
// RINGING: audio sets video sets audio...
// SOLUTION: ONLY ALLOW "SYNCING" FROM ONE SOURCE AT A TIME
// ENTER into a state of FOLLOWING a particular time source ...
// ... follow it, setting the rest from that info
// ... EXIT the state when seeking is complete
// NEXT STEP
// MANAGE PLAY STATES
// on SCRUB: all elements to pause
// end of seek... wait for all elements to be ready => then play (when playing)
// HEY! is not dragging state a form of driver ! :)
function when_all_ready (callback) {
// console.log("cleardriver");
if ((audio.readyState == 4 && !audio.seeking) && (video.readyState == 4 && !video.seeking)) {
callback();
} else {
window.setTimeout(function () { when_all_ready(callback) }, 100);
}
}
///////////TIMELINE
var timeline_duration = 60 * 60,
video_start_time = 1 * 60,
audio_start_time = 2 * 60,
audio = $("audio").get(0),
video = $("video").get(0),
timeline = new VoidPlayer($("#timeline").get(0)),
slider_max = 100000,
driver = null,
driver_scrubstart_paused;
$("#timeline").bind("timeupdate", function () {
// console.log("timeline timeupdate", timeline.currentTime);
var slider_val = (timeline.currentTime / timeline.duration) * slider_max;
$("#timeline").slider("option", "value", slider_val);
$("#timedisplay").text(timeline.currentTime.secondsTo("mm:ss"));
}).bind("play", function () {
$("#play").text("pause");
}).bind("pause", function () {
$("#play").text("play");
});
// timeline controls
$("#play").click(function () {
if (timeline.paused) {
var playbutton = this;
playbutton.disabled = true;
when_all_ready(function () {
timeline.play();
video.play();
audio.play();
playbutton.disabled = false;
});
} else {
timeline.pause();
video.pause();
audio.pause();
}
});
$("#timeline").slider({
start: function (evt, ui) {
if (driver === null) {
driver = timeline;
// RECORD PLAY STATE
driver_scrubstart_paused = timeline.paused;
// PAUSE ALL
timeline.pause();
audio.pause();
video.pause();
}
},
slide: function (evt, ui) {
// console.log("slide", ui.value);
var ct = timeline.duration * (ui.value/slider_max);
timeline.setCurrentTime(ct);
if (ct > audio_start_time) {
audio.currentTime = ct - audio_start_time;
}
if (ct > video_start_time) {
video.currentTime = ct - video_start_time;
}
},
stop: function (evt, ui) {
if (driver == timeline) {
// driver = null;
when_all_ready(function () {
driver = null;
// restore play state
if (!driver_scrubstart_paused) {
timeline.play();
video.play();
audio.play();
}
});
}
},
max: slider_max
});
///////////////////////////////
// VIDEO
$("video").bind("seeking", function () {
// console.log("video, seeking");
if (driver === null) {
driver = video;
// RECORD PLAY STATE
driver_scrubstart_paused = video.paused;
// PAUSE ALL
timeline.pause();
audio.pause();
video.pause();
}
}).bind("seeked", function () {
// PROBLEM FOR CHROME: canplay does not fire on seeking...
// console.log("video, canplay");
// PROBLEM for firefox seeked fires a bit early
if (driver === video) {
// wait until all media is ready to set driver to null
// otherwise slower media (video) can trigger more "scrub" events
when_all_ready(function () {
driver = null;
// restore play state
if (!driver_scrubstart_paused) {
// console.log("restore play state video");
timeline.play();
video.play();
audio.play();
}
});
}
}).bind("timeupdate", function () {
// console.log("timeupdate", this);
if (driver === video) {
// Video is being scrubbed (timeupdate while buffering = scrub event ?!)
console.log("video scrub " + video.currentTime);
// set timeline time
timeline.setCurrentTime(video_start_time + this.currentTime);
// set audio time
var audio_time = (timeline.currentTime - audio_start_time);
if (audio_time >= 0) {
audio.currentTime = audio_time;
}
}
}).bind("pause", function () {
// console.log("video: pause");
});
///////////////////////////////
// AUDIO
$("audio").bind("seeking", function () {
if (driver === null) {
driver = audio;
driver_scrubstart_paused = audio.paused;
// PAUSE ALL
// console.log("pausing all media, scrubstart_paused", driver_scrubstart_paused);
timeline.pause();
audio.pause();
video.pause();
}
}).bind("seeked", function () {
// console.log("audio seeked", driver_scrubstart_paused);
if (driver === audio) {
// driver = null;
audio.pause(); // HACK TO OVERRIDE (firefox) scrub resuming play
when_all_ready(function () {
// console.log("driver = null", driver_scrubstart_paused);
driver = null;
// restore play state
if (!driver_scrubstart_paused) {
// console.log("restore play state audio");
timeline.play();
video.play();
audio.play();
}
});
}
}).bind("timeupdate", function () {
if (driver === audio) {
// SCRUB (timeupdate while buffering = scrub event ?!)
console.log("audio scrub " + audio.currentTime);
// set timeline time
timeline.setCurrentTime(audio_start_time + this.currentTime);
// set video time
var video_time = (timeline.currentTime - video_start_time);
if (video_time >= 0) {
video.currentTime = video_time;
}
}
}).bind("play", function () {
// if received "cold" (driver === null)
// initiate a group play... pause this, and when_all_ready set all playstates to play
// then wait for all play events to be received to complete the "group play" event
// once the "group play" event has been received, pause/play events are swallowed / handled differently
if (driver === null) {
console.log("initiate play when ready");
driver = audio;
audio.pause();
when_all_ready(function () {
console.log("ready, calling play");
audio.play();
video.play();
timeline.play();
});
} else if (driver === audio) {
window.setTimeout(function () {
console.log("setting driver to null");
driver = null;
}, 50);
}
}).bind("pause", function () {
// console.log("audio: pause");
if (driver === null) {
video.pause();
timeline.pause();
}
});
$("div.annotation [data-start]").each(function () {
var secs = $(this).attr("data-start").toSeconds();
$(this).click(function () {
audio.currentTime = secs;
});
});
});
</script>
<style>
.annotation {
font-size: 11px;
border: 1px solid black;
width: 320px;
height: 360px;
overflow: auto;
margin-bottom: 10px;
}
.title {
border-top: 1px dotted black;
padding: 4px;
}
.section {
border-top: 1px dotted black;
padding: 4px;
}
.active {
background: pink;
}
td {
vertical-align: top;
}
.timecodes {
font-family: monospace;
font-size: 10px;
color: gray;
cursor: pointer;
}
</style>
</head>
<body>
<div id="timeline" data-duration="01:00:00" style="margin-bottom: 20px; margin: 10px;">
</div>
<div>
<span id="timedisplay"></span>
<button id="play">play</button>
<button id="lock">locked</button>
</div>
<table>
<tr>
<td>
<video controls src="/videos/Michael_Moss.ogv" data-start="00:00:05"></video>
<audio controls src="Jonathan Burrows on scores.ogg" data-start="00:00:05"></audio>
</td>
<td>
<div class="annotation" about="http://video.constantvzw.org/vj12/Michael_Moss.ogv">
<div style="border-bottom: 2px solid black">about="http://video.constantvzw.org/vj12/Michael_Moss.ogv"</div>
<div class="title" data-start="01:27" data-end="01:54">
stats - information as much as the math that goes along with it
</div>
<div class="title" data-start="01:54" data-end="02:10">
migration of information broadly conceived from analog to digital
</div>
<div class="title" data-start="02:10" data-end="02:30">
The poem is called the Walrus and the carpenter Lewis Carroll
<img src="http://upload.wikimedia.org/wikipedia/commons/thumb/f/fb/LewisCarrollSelfPhoto.jpg/240px-LewisCarrollSelfPhoto.jpg" />
</div>
<div class="title" data-start="02:30" data-end="02:36">
<a href="http://en.wikipedia.org/wiki/Lewis_Carrol">Charles Lutwidge Dodgson</a> is a mathematician
</div>
<div class="title" data-start="02:36" data-end="03:20">
Part of the nonsense that he writes destabilize the reader from their comfortable world of reference
</div>
<div class="title" data-start="03:20" data-end="03:53">
<img src="http://www.jabberwocky.com/carroll/pics/glass20-small.gif" />
<pre>
The sun was shining on the sea,
Shining with all his might:
He did his very best to make
The billows smooth and bright--
And this was odd, because it was
The middle of the night.
The moon was shining sulkily,
Because she thought the sun
Had got no business to be there
After the day was done--
"It's very rude of him," she said,
"To come and spoil the fun!"
</pre>
</div>
<div class="title" data-start="03:53" data-end="04:35">
nonsense needs to have reference in the authentic
</div>
<div class="title" data-start="04:35" data-end="05:08">
Difference between inauthentic and authentic
</div>
<div class="title" data-start="05:08" data-end="05:43">
authenticity comes from a discipline called diplomatique 17th century. Much discussion about authenticity of the titles of monastic houses.
</div>
<div class="title" data-start="05:43" data-end="05:58">
<pre>
The sea was wet as wet could be,
The sands were dry as dry.
You could not see a cloud, because
No cloud was in the sky:
No birds were flying overhead--
There were no birds to fly.
</pre>
</div>
<div class="title" data-start="05:58" data-end="06:51">
authenticity is inevitably a retrospective activity
</div>
<div class="title" data-start="06:51" data-end="08:27">
authentication depends on absences; missing tokens. There is no day, no signature, no address that doesn't make a document inauthentic.
but absence can have been always been there; original document may miss signature.
</div>
<div class="title" data-start="08:27" data-end="09:00">
<pre>
The Walrus and the Carpenter
Were walking close at hand;
They wept like anything to see
Such quantities of sand:
"If this were only cleared away,"
They said, "it would be grand!"
"If seven maids with seven mops
Swept it for half a year.
Do you suppose," the Walrus said,
"That they could get it clear?"
"I doubt it," said the Carpenter,
And shed a bitter tear.
</pre>
</div>
<div class="title" data-start="09:00" data-end="09:28">
such quantities of sand - vast amount of granularity and stand alone objects in the digital world.
</div>
<div class="title" data-start="09:28" data-end="10:28">
financial records and granularity
in digital financial systems - no hierarchy of record keeping - no hierarchies of information - balance sheet made of quantities of sand - how many cans of beer are sold in the different shops I own. All that exist in the system are quantities of sand.
Here is an extra note
</div>
<div class="title" data-start="10:28" data-end="10:47">
Very different from analog financial systems 15th century, Venice and Genoa
<img src="http://upload.wikimedia.org/wikipedia/commons/thumb/2/2a/Pacioli.jpg/300px-Pacioli.jpg" />
Analog financial systems were systematized by a Franciscan friar Pacioli.
</div>
<div class="title" data-start="10:47" data-end="11:06">
He wrote the book _De Divina Proportione_ (About the divine proportions).
<img src="http://upload.wikimedia.org/wikipedia/commons/thumb/1/12/Fra_Luca_Pacioli_Letter_M_1509.png/250px-Fra_Luca_Pacioli_Letter_M_1509.png" />
</div>
<div class="title" data-start="11:06" data-end="13:21">
His legacy
-> double entry book keeping (refer to aggregations of other books)
-> Proportionality in the media
Those constructs have disappeared in the digital age and we are left with quantities of sand.
Can we capture the metadata that goes with these grains of sand? And have the tool to make aggregation/disaggregation?
grain of sand = digital object -> the bit stream has identity of reference even if its identity changes
every time it is opened or closed.
[[Messiness]]
</div>
</div>
</td>
</tr>
</table>
</body>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment