Last active
October 22, 2020 23:18
-
-
Save aeschylus/a66760a6ce8f3b46c9c203084368002d to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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