Skip to content

Instantly share code, notes, and snippets.

@dukuo
Last active March 9, 2020 23:40
Show Gist options
  • Save dukuo/29f8a97a81ba2b4cce832eb410025e77 to your computer and use it in GitHub Desktop.
Save dukuo/29f8a97a81ba2b4cce832eb410025e77 to your computer and use it in GitHub Desktop.
Spark Face Tracker Timer
// padStart polyfill
if (!String.prototype.padStart) {
String.prototype.padStart = function (n,str){
return Array(n-String(this).length+1).join(str||'0')+this;
}
}
const states = {
PAUSE: 1,
START: 2,
RESET: 4,
STOP: 8
}
const Patches = require ('Patches');
const FT = require('FaceTracking');
const Diagnostics = require('Diagnostics')
const Time = require('Time');
const face = FT.face(0);
const ms = 30; // run each 30 milliseconds
const openSensitivity = 0.1; // to trigger a timer if the mouth is X open. Set to a relatively low value to avoid flickering.
let elapsedTime = 0;
let pausedTime = 0;
const currentInterval = {
isPausedTimer: false,
interval: undefined
}
function setElapsedTime(time) {
elapsedTime = time;
}
function getElapsedTime() {
return elapsedTime;
}
function getPausedTime(){
return pausedTime;
}
function setPauseTime(time) {
pausedTime = time;
}
function setCurrentInterval(state, interval) {
const current = getCurrentInterval()
if(!!!interval && !!current.interval) { // remove the current interval from Time.
Time.clearInterval(current.interval)
}
Diagnostics.log(`Current interval ${state} ${interval}`)
currentInterval.state = state;
currentInterval.interval = interval;
}
function getCurrentInterval(){
return currentInterval;
}
function buildFormattedTimer(delta) {
const format = function (value, scale, modulo, padding) {
value = Math.floor(value / scale) % modulo;
return value.toString().padStart(padding, 0);
}
const s = format(delta, 1000, 60, 2);
const m = format(delta, 60000, 60, 2);
const ms = format(delta, 1, 1000, 3);
const _s = ':';
return [m, _s, s, _s, ms];
}
function handleTimer(state){
const currInterval = getCurrentInterval()
if(!!(state & states.STOP)) {
Diagnostics.log("Stopping timer.")
setCurrentInterval( states.STOP, undefined )
} else
if(!!(state & states.RESET)) {
// resetTimer()
if( !!currentInterval.interval ) { // If timer exists, remove it and reset the overall state of the timer.
Diagnostics.log("resetting timer.")
setCurrentInterval( states.STOP, undefined);
setElapsedTime(0)
const _s = ':'
const [m, s, ms] = ['00', '00', '000'];
printTimer([m, _s, s, _s, ms])
return;
}
} else if (!!(state & states.START | state & states.PAUSE) && !!!currInterval.interval) {
Diagnostics.log("Setting new timer interval")
const start = Date.now();
const interval = Time.setInterval(function (){
let delta = Date.now() - start;
if(!!(state & states.PAUSE)) {
setPauseTime(getPausedTime() + delta)
} else {
const elapsedTime = getElapsedTime();
if( getPausedTime() > 0) {
delta = Math.abs(Date.now() - (start - getPausedTime() ));
setPauseTime(0)
}
const timer = buildFormattedTimer(delta);
printTimer(timer);
setElapsedTime (elapsedTime + delta);
}
}, ms);
setCurrentInterval(states.STOP, undefined) // Always clear the previous timer.
setCurrentInterval(state, interval)
}
}
// function resetTimer() {
// const currentInterval = getCurrentInterval();
// }
function printTimer(timer){
Patches.setStringValue('timer', timer.join(''))
}
face.mouth.openness.monitor().subscribe((e) => {
const currentInterval = getCurrentInterval();
let intervalState = states.START; // Start by default.
if(e.newValue < openSensitivity && !!currentInterval.interval && !!(states.PAUSE & currentInterval.state | states.START & currentInterval.state) ) // mouth is closing / closed
{
Diagnostics.log("Mouth is closing")
intervalState = states.STOP // states.[STOP | RESET | PAUSE]
handleTimer(intervalState)
}
if( e.newValue > openSensitivity && (!!!currentInterval.interval || !!(states.PAUSE & currentInterval.state | states.STOP & currentInterval.state))){
handleTimer(intervalState)
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment