feat: implement speed controls

This commit is contained in:
Carlos Valente
2024-09-02 20:08:13 +02:00
parent 9167a50eda
commit ae1639802c
20 changed files with 386 additions and 89 deletions
@@ -259,6 +259,24 @@ const actionHandlers: Record<string, ActionHandler> = {
}
throw new Error('No matching method provided');
},
/* Speed */
'calculate-speed': () => {
const factor = runtimeService.calculateSpeed();
return { payload: factor };
},
'get-speed': () => {
const factor = runtimeService.getSpeed();
return { payload: factor };
},
'set-speed': (payload) => {
const speedToSet = numberOrError(payload);
const speedSet = runtimeService.setSpeed(speedToSet);
return { payload: speedSet };
},
'reset-speed': () => {
const factor = runtimeService.resetSpeed();
return { payload: factor };
},
/* Client */
client: (payload) => {
assert.isObject(payload);
@@ -666,6 +666,42 @@ class RuntimeService {
logger.info(LogOrigin.Playback, `${time > 0 ? 'Added' : 'Removed'} ${millisToString(time)}`);
}
}
/**
* Utility calculates the speed factor necessary to finish on time
* @returns {number} speed factor needed to meet schedule
*/
public calculateSpeed(): number {
return runtimeState.calculateSpeed();
}
/**
* @returns {number} speed factor currently applied
*/
public getSpeed(): number {
return runtimeState.getSpeed();
}
/**
* Applies a speed factor to current timer
* @param {number} speed - speed factor
* @returns {number} applied speed factor
*/
public setSpeed(speed: number): number {
// TODO: validate state
// TODO: validate value
runtimeState.setSpeed(speed);
return runtimeState.getSpeed();
}
/**
* Resets the speed of the current timer
* @returns {number} applied speed factor
*/
public resetSpeed(): number {
runtimeState.resetSpeed();
return runtimeState.getSpeed();
}
}
// calculate at 30fps, refresh at 1fps
@@ -32,6 +32,7 @@ const baseState: RuntimeState = {
playback: Playback.Stop,
secondaryTimer: null,
startedAt: null,
speed: 1,
},
_timer: {
forceFinish: null,
+34
View File
@@ -350,6 +350,33 @@ export function updateAll(rundown: OntimeRundown) {
loadBlock(rundown);
}
export function setSpeed(speed: number) {
runtimeState.timer.speed = speed;
}
export function resetSpeed() {
runtimeState.timer.speed = 1.0;
}
export function getSpeed() {
return runtimeState.timer.speed;
}
export function calculateSpeed() {
// TODO: what should this return if no timer is running?
if (runtimeState.eventNow !== null) {
const timeToDesiredFinish = runtimeState.eventNow.timeEnd - runtimeState.clock;
const timeToActualFinish = runtimeState.timer.expectedFinish - runtimeState.clock;
const factor = 1 / (timeToDesiredFinish / timeToActualFinish);
// TODO: this can produce negative speeds (i.e. the desired finish time is in the past)
// TODO: should there be some clamping or rounding on this number?
return factor;
}
return 1.0;
}
export function start(state: RuntimeState = runtimeState): boolean {
if (state.eventNow === null) {
return false;
@@ -464,6 +491,7 @@ export type UpdateResult = {
};
export function update(): UpdateResult {
const timeSinceLastUpdate = clock.timeNow() - runtimeState.clock;
// 0. there are some things we always do
const previousClock = runtimeState.clock;
runtimeState.clock = clock.timeNow(); // we update the clock on every update call
@@ -490,6 +518,12 @@ export function update(): UpdateResult {
}
}
const catchUpMultiplier = 1 - runtimeState.timer.speed;
if (runtimeState.timer.playback === Playback.Play) {
runtimeState.timer.addedTime += timeSinceLastUpdate * catchUpMultiplier;
}
// update timer state
runtimeState.timer.current = getCurrent(runtimeState);
runtimeState.timer.expectedFinish = getExpectedFinish(runtimeState);