Skip to content

Latest commit

 

History

History
128 lines (94 loc) · 4.88 KB

README.md

File metadata and controls

128 lines (94 loc) · 4.88 KB

crimp-monster

Major components:

  • useWorkout, based on useReducer. Its dispatch and state are exposed to all components via WorkoutContext.
  • useClock, a monotonically increasing clock. Can be paused, can be set to a specific timestamp.
  • useBeepPlayer; plays beeps according to schedule and current time.

2024-05-12:

Need to write

2023-11-08:

We have a functional useBeepPlayer and useClock. Example of using them together is in useBeepPlayer.stories.tsx. For now, accepting those as "good enough".

2023-07-21:

TODO:

  • Refactor to using useClock, instead of the timer inside useWorkout
    • Challenge: will need to track current workout item, and relative time to show the Timer.
  • As long as clock goes forward-only, we have no jitter
  • We need to ensure to trigger all timed events ->
    • This can be ensured by discretization of time OR by tracking intervals between previous and next tick.
    • Providing discrete time-steps reliably is hard -> prone to drift.
    • Discrete clock = continuous clock, where all discrete events between two ticks of continuous time are fired.
    • How to cross into React hooks boundary, and ensure all these ticks are fired? Rendering cycles could be skipped -> not guaranteed we'll see all times.
    • We'll need current and previous values from useClock. Previous and current value should be the same after calling setTime(...), such that the interval of events to trigger is effectively empty.

2023-04-01:

Pivoted approach to beeps; Timestamps of beeps are now calculated in model.ts directly from workout. To support playing these beeps, we'll have a global workout clock with signature:

const { paused, setPaused, time, setTime } = useClock();

And a beep player, perhaps like this:

useBeepPlayer(time, paused, scheduledBeeps);

On start of workout, setTime() will be called with a negative value to allow the initial countdown and playback of beeps before time=0.

TODO:

  • Workout start:
    • Countdown timer: on start, begin with a 10s countdown.
      • Add countdown state to WorkoutState
      • Add phase to WorkoutState (initialized, counting down, working out)
      • Add paused? to WorkoutState
      • Add play/pause buttons to timer screen
      • Visualize countdown on timer screen
    • Start executing the workout plan

Audio subsystem:

  • Options: - WebAudio API: control over precise timing of beeps - Playing audio files: file + offset information
  • Features:
    • Play "countdown" when transitioning into HANG (either from initial countdown or from PAUSE)
      • Play "ending" at end of a hang
      • Pause playback, when workout paused
      • On continuation, resume with next beep.

API A:

  • clear() or stop() or something similar to clear all scheduled beeps
  • scheduleStartBeep(finalStartBeepAfterTime) (afterTime = seconds relative from now)
  • scheduleEndBeep(lastBeepEndsAfterTime) (afterTime = seconds relative from now)
  • Internally, the audio subsystem would have schedule for each of the beep sequences.
  • It needs to gracefully handle possible negative start time of a beep by not playing it.
  • Between reps, we have very little time. Distinguish short start beeps and long start beeps?

API B:

useBeeps({
  paused: boolean,
  playStartBeepsAfter: number, // calculated from workoutState.secondsRemaining and if next workout item is a HANG
  playEndBeepsAfter: number, // calculated from workoutState.secondsRemaining, and if current workout item is a HANG
});

It's likely there will be jitter in the timestamps (for both API variants). When exactly to call start and end on the oscillator? Perhaps, the system could work in discrete ticks (maybe each 10ms?). If the beep start/stop falls within the tick, we execute it. When paused goes from false to true, we pause all oscillators.

What about declarative audio subsystem, that plays audio files?

API:

useBeeps({
  paused: boolean,
  playStartBeepsAfter: number, // calculated from workoutState.secondsRemaining and if next workout item is a HANG
  playEndBeepsAfter: number, // calculated from workoutState.secondsRemaining, and if current workout item is a HANG
});
  • When paused becomes true, stop playing audio.
  • When we get within 10ms of a scheduled start of file, play it

Audio files required:

  • beep 880Hz
  • longer beep 1760Hz
  • two short 1760Hz beeps

After starting to implement declarative variant of useBeeps()

Shortcomings were found; For example, when playing the start beeps, we correctly transition from positive to negative numbers for []After parameters for the two beeps, that come before starting the workout. When we transition across that point, the remaining start beep doesn't play. We have similar issues around positions of other beeps.

Candidate solution:

  • don't let []After offset go to null
  • use imperative API, with triggers on certain props transitions in TimerPage.