Skip to content

Instantly share code, notes, and snippets.

@aeschylus
Last active October 22, 2020 23:18
Show Gist options
  • Save aeschylus/a66760a6ce8f3b46c9c203084368002d to your computer and use it in GitHub Desktop.
Save aeschylus/a66760a6ce8f3b46c9c203084368002d to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
// Automatic learning mode
// Available variables:
// - Machine
// - interpret
// - assign
// - send
// - sendParent
// - spawn
// - raise
// - actions
// - XState (all XState exports)
const nodes = [
"estoy emocionado por verte",
"estoy",
"emocionado",
"por",
"verte",
"el mes que viene",
"el",
"mes",
"que",
"viene"
];
const durations = [
2000,
300,
400,
200,
500,
2000,
100,
200,
200,
400
];
const listeningReps = 3;
const recordingPrimingLoops = 3;
const recordingsBeforeEval = 3;
const comparisonReps = 3;
const totalRequiredRecordCompareReps = 4;
const speedLevels = 4;
const learningMachine = Machine({
id: 'autoStudy',
initial: 'getReady',
context: {
listens: 0,
primings: 0,
duration: 5,
elapsed: 0,
interval: 0.02,
preCompareRecordings: 0,
comparisons: 0,
totalRecordCompareReps: 0,
currentFocusIndex: 0,
speedLevel: 0
},
states: {
getReady: {
on: {
START: 'studying'
}
},
studying: {
initial: 'listening',
on: {
PAUSE: 'paused',
SKIP_NODE: {
target: '.listening',
actions: ['skipNode']
},
BACK: {
target: '.listening',
actions: ['back']
},
//SLOW_DOWN: {
// target: '.previousLearningStep',
// actions: assign({
// speedLevel: (context, event) => {
// return context.speedLevel + 1;
// }
// })
// },
// SPEED_UP: {
// target: '.previousLearningStep',
// actions: assign({
// speedLevel: (context, event) => {
// return context.speedLevel - 1;
// }
// })
//}
},
states: {
listening: {
id: 'listening',
initial: 'looping',
states: {
looping: {
invoke: {
src: context => cb => {
const interval = setInterval(() => {
cb('TICK');
}, 16);
setTimeout(()=>cb('FINISH_LISTENING_PLAYBACK'), durations[context.currentFocusIndex]);
return () => {
clearInterval(interval);
};
}
},
on: {
TICK: {
actions: assign({
elapsed: context => +(context.elapsed + .16).toFixed(2)
})
},
FINISH_LISTENING_PLAYBACK: [
{
target: 'done',
cond: 'finishedListeningReps'
},
{
target: 'looping',
actions: ['incrementListens']
}
]
}
},
done: {
type: 'final'
}
},
onDone: {
target: 'recordingAndComparing',
actions: ['resetListens']
}
},
recordingAndComparing: {
initial: 'recording',
id: 'recordingAndComparing',
onDone: [
{
target: 'done',
cond: 'finishedAllRecordingReps',
actions: ['resetTotalRecordCompareReps']
},
{
target: 'recordingAndComparing',
actions: ['incrementTotalRecordCompareReps']
}
],
states: {
recording: {
initial: 'playingOriginal',
onDone: [
{
target: 'comparingAndEvaluating',
cond: 'finishedPreEvalRecordingReps',
actions: ['resetPreCompareRecordings']
},
{
target: 'recording',
actions: ['incrementPreCompareRecordings']
}
],
states: {
playingOriginal: {
initial: 'looping',
states: {
looping: {
on: {
FINISH_PRIMING_PLAYBACK: [
{
target: 'done',
cond: 'finishedRecordingPrimingReps'
},
{
target: 'looping',
actions: ['incrementPrimings']
}
]
}
},
done: {
type: 'final'
}
},
onDone: [
{
target: 'countingDownToRecordingStart',
actions: ['resetPrimings']
}
]
},
countingDownToRecordingStart: {
on: {
FINISH_COUNTDOWN: 'userRecording'
}
},
userRecording: {
on: {
FINISH_RECORDING: 'done'
}
},
done: {
type: 'final'
}
},
done: {
type: 'final'
}
},
comparingAndEvaluating: {
initial: 'comparing',
onDone: 'done',
states: {
comparing: {
initial: 'playingOriginal',
onDone: [
{
target: 'evaluating',
cond: 'finishedPreEvalComparisonReps',
actions: ['resetComparisons']
},
{
target: 'comparing',
actions: ['incrementComparisons']
}
],
states: {
playingOriginal: {
on: {
FINISH_ORIGINAL_PLAYBACK: 'playingRecording'
}
},
playingRecording: {
on: {
FINISH_RECORDING_PLAYBACK: 'done'
}
},
done: {
type: 'final'
}
}
},
evaluating: {
on: {
MOVE_ON: {
target: 'done'
},
LISTEN_AGAIN: 'comparing'
},
},
done: {
type: 'final'
}
}
},
done: {
type: 'final'
}
}
},
done: {
type: 'final'
},
previousLearningStep: {
type: 'history',
history: 'deep'
},
},
done: {
type: 'final'
}
},
paused: {
on: {
RESUME: 'studying.previousLearningStep'
}
},
done: {
type:'final'
}
},
},
// Options
{
actions: {
skipNode: assign({
currentFocusIndex: (context, event) => {
return context.currentFocusIndex + 1
}
}),
back: assign({
currentFocusIndex: (context, event) => {
return context.currentFocusIndex - 1;
}
}),
incrementListens: assign({
listens: (context, event) => {
return context.listens + 1;
}
}),
incrementPrimings: assign({
primings: (context, event) => {
return context.primings + 1;
}
}),
incrementPreCompareRecordings: assign({
preCompareRecordings: (context, event) => {
return context.preCompareRecordings + 1;
}
}),
incrementTotalRecordCompareReps: assign({
totalRecordCompareReps: (context, event) => {
console.log('incrementing record compare reps');
return context.totalRecordCompareReps + 1;
}
}),
incrementComparisons: assign({
comparisons: (context, event) => {
return context.comparisons + 1;
}
}),
resetListens: assign({
listens: () => 0
}),
resetPrimings: assign({
primings: () => 0
}),
resetPreCompareRecordings: assign({
preCompareRecordings: () => 0
}),
resetComparisons: assign({
comparisons: () => 0
}),
resetTotalRecordCompareReps: assign({
totalRecordCompareReps: () => {
console.log('reseting record compare reps');
return 0;
}
}),
resetStudyMachine: assign({
listens: () => 0,
primings: () => 0,
preCompareRecordings: () => 0,
comparisons: () => 0,
totalRecordCompareReps: () => 0,
speedLevel: () => 0
})
},
guards: {
finishedListeningReps: (context) => {
return context.listens === listeningReps - 1;
},
finishedRecordingPrimingReps: (context) => {
return context.primings === recordingPrimingLoops - 1;
},
finishedPreEvalRecordingReps: (context) => {
return context.preCompareRecordings === recordingsBeforeEval - 1;
},
finishedPreEvalComparisonReps: (context) => {
console.log('finished comparison reps');
return context.comparisons === comparisonReps - 1;
},
finishedAllRecordingReps: (context) => {
console.log('finished all reps');
return context.totalRecordCompareReps === totalRequiredRecordCompareReps - 1;
}
},
services: {
audioPlayer: () => {
// Select the appropriate clip
// from the sound atlas.
// Call 'play'
// Bind a state machine event to
// the 'end' playback
// Include a resource for pausing
// or stoping in the cleanup step
// of the service.
},
countdownTimer: () => {
// - specify a duration and an
// update interval
// - call state machine events
// when there is a tick at the
// specified interval(s), and
// when the duration has been
// reached.
},
recordingSession: () => {
// - Access the WebAudio recording
// context.
// - Initiate recording
// - measure or somehow receive the
// update about the duration of
// the recording.
// - Assign the result to the context
// or to another store
// -
},
recordingPlayback: () => {
// This differs from the audio
// player above because of its use
// of the webAudio API to realise
// the service. APIs differ between
// Howl and WebAudio.
//
// Requires access to the previous
// recording the user has made.
// Identify
}
}
});
window.mach = learningMachine;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment