mirror of
https://github.com/genemecija/learn-morse-code.git
synced 2026-01-09 18:20:07 +01:00
Electronic key functionality progress, context separation
This commit is contained in:
parent
dd5b0ec4a0
commit
92b5286165
25
src/App.js
25
src/App.js
|
|
@ -1,12 +1,19 @@
|
|||
import React, {useContext, useEffect} from 'react';
|
||||
import React, {useContext} from 'react';
|
||||
import './css/App.css';
|
||||
import MorseButton from './components/MorseButton'
|
||||
import ModePicker from './components/ModePicker'
|
||||
import KeyTypePicker from './components/KeyTypePicker'
|
||||
// import MorseDisplay from './components/MorseDisplay'
|
||||
// import MorseBufferDisplay from './components/MorseBufferDisplay'
|
||||
// import GameClock from "./components/GameClock"
|
||||
// import ChallengeWord from "./components/ChallengeWord"
|
||||
import {GameModeContext} from "./gameContext"
|
||||
import {GameModeContext} from "./contexts/gameContext"
|
||||
import {KeyTypeContext} from "./contexts/keyTypeContext"
|
||||
import {MorseBufferContextProvider} from "./contexts/morseBufferContext"
|
||||
|
||||
import StraightKey from './components/StraightKey'
|
||||
import ElectronicKey from './components/ElectronicKey'
|
||||
|
||||
import PracticeMode from './app-modes/PracticeMode';
|
||||
import TimedMode from './app-modes/TimedMode'
|
||||
import ChallengeMode from './app-modes/ChallengeMode'
|
||||
|
|
@ -16,15 +23,23 @@ function App() {
|
|||
|
||||
console.log('App.js rendered')
|
||||
const {gameMode} = useContext(GameModeContext)
|
||||
const {keyType} = useContext(KeyTypeContext)
|
||||
|
||||
|
||||
return (
|
||||
<div id='main-content'>
|
||||
<Legend />
|
||||
<ModePicker />
|
||||
<KeyTypePicker />
|
||||
|
||||
<MorseBufferContextProvider>
|
||||
{keyType === "straight" && <StraightKey />}
|
||||
{keyType === "electronic" && <ElectronicKey />}
|
||||
|
||||
{gameMode === 'practice' && <PracticeMode />}
|
||||
{gameMode === 'timed' && <TimedMode />}
|
||||
{gameMode === 'challenge' && <ChallengeMode />}
|
||||
{gameMode === 'practice' && <PracticeMode />}
|
||||
{gameMode === 'timed' && <TimedMode />}
|
||||
{gameMode === 'challenge' && <ChallengeMode />}
|
||||
</MorseBufferContextProvider>
|
||||
|
||||
<MorseButton />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import '../css/App.css';
|
||||
import morseCode from '../data/morse-reverse.json'
|
||||
import useTelegraph from '../hooks/useTelegraph';
|
||||
import useStraightKey from '../hooks/useStraightKey';
|
||||
// import ChallengeWord from '../components/ChallengeWord'
|
||||
// import MorseBufferDisplay from '../components/MorseBufferDisplay'
|
||||
// import ChallengeDisplay from '../components/ChallengeDisplay';
|
||||
|
|
@ -11,7 +11,7 @@ function ChallengeMode() { console.log("ChallengeMode loaded");
|
|||
|
||||
let word = "morse"
|
||||
|
||||
const {morseCharBuffer} = useTelegraph('challenge')
|
||||
const {morseCharBuffer} = useStraightKey('challenge')
|
||||
// console.log('morseCharBuffer:', morseCharBuffer, '|END');
|
||||
let morseLetters = morseCharBuffer.split('_').filter(l => l !== '')
|
||||
// console.log('morseLetters:', morseLetters, morseLetters.length);
|
||||
|
|
|
|||
|
|
@ -1,23 +1,28 @@
|
|||
import React from 'react';
|
||||
import React, {useContext} from 'react';
|
||||
import '../css/App.css';
|
||||
// import useTelegraph from '../hooks/useTelegraph';
|
||||
import usePaddleTelegraph from '../hooks/usePaddleTelegraph';
|
||||
import useStraightKey from '../hooks/useStraightKey';
|
||||
import useElectronicKey from '../hooks/useElectronicKey';
|
||||
import MorseBufferDisplay from '../components/MorseBufferDisplay'
|
||||
import MorseDisplay from '../components/MorseDisplay'
|
||||
import {MorseBufferContext} from "../contexts/morseBufferContext"
|
||||
|
||||
|
||||
function PracticeMode() {
|
||||
console.log("PracticeMode loaded");
|
||||
// const {morseCharBuffer, morseWords, clearHistory} = useTelegraph('practice')
|
||||
usePaddleTelegraph()
|
||||
|
||||
// const [telegraphType, setTelegraphType] = useState('electronic')
|
||||
|
||||
// useElectronicKey()
|
||||
// const {morseCharBuffer, morseWords, clearHistory} = useStraightKey('practice')
|
||||
const {morseCharBuffer, morseWords} = useContext(MorseBufferContext)
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <MorseBufferDisplay buffer={morseCharBuffer} /><br/>
|
||||
<MorseDisplay morseWords={morseWords} /><br/> */}
|
||||
<button onClick={console.log('hi')}>Clear Morse History</button><br/>
|
||||
<MorseBufferDisplay buffer={morseCharBuffer} /><br/>
|
||||
<MorseDisplay morseWords={morseWords} /><br/>
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default React.memo(PracticeMode);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import React from 'react';
|
||||
import '../css/App.css';
|
||||
import useTelegraph from '../hooks/useTelegraph';
|
||||
import useStraightKey from '../hooks/useStraightKey';
|
||||
import GameClock from '../components/GameClock'
|
||||
import MorseBufferDisplay from '../components/MorseBufferDisplay'
|
||||
import MorseDisplay from '../components/MorseDisplay'
|
||||
|
||||
function TimedMode() {
|
||||
|
||||
const {morseCharBuffer, morseWords} = useTelegraph('timed')
|
||||
const {morseCharBuffer, morseWords} = useStraightKey('timed')
|
||||
|
||||
console.log('TimedMode.js rendered')
|
||||
|
||||
|
|
|
|||
7
src/components/ElectronicKey.js
Normal file
7
src/components/ElectronicKey.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import React from 'react'
|
||||
// import useStraightKey from '../hooks/useStraightKey';
|
||||
import useElectronicKey from '../hooks/useElectronicKey';
|
||||
|
||||
export default React.memo(function StraightKey() {
|
||||
useElectronicKey()
|
||||
})
|
||||
25
src/components/KeyTypePicker.js
Normal file
25
src/components/KeyTypePicker.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import React, {useContext} from "react"
|
||||
import {KeyTypeContext} from "../contexts/keyTypeContext"
|
||||
|
||||
function KeyTypePicker() {
|
||||
|
||||
const {switchKeyType} = useContext(KeyTypeContext)
|
||||
|
||||
function handleClick(e) {
|
||||
switchKeyType(e.target.id)
|
||||
console.log("Switched to " + e.target.id + " keyType.");
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="mode-picker">
|
||||
<button id="straight" onClick={handleClick}>
|
||||
Straight Key
|
||||
</button>
|
||||
<button id="electronic" onClick={handleClick}>
|
||||
Electronic Key
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(KeyTypePicker)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React, {useContext} from "react"
|
||||
import {GameModeContext} from "../gameContext"
|
||||
import {GameModeContext} from "../contexts/gameContext"
|
||||
|
||||
function ModePicker() {
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import DitDahDisplay from "./DitDahDisplay"
|
|||
import morseCode from '../data/morse-reverse.json'
|
||||
|
||||
function MorseBufferDisplay(props) {
|
||||
|
||||
|
||||
let ditDahs = props.buffer.split('').map((ditdah,index) => <DitDahDisplay key={index} dd={ditdah} />)
|
||||
|
||||
let alphanumeric = ''
|
||||
|
|
|
|||
9
src/components/StraightKey.js
Normal file
9
src/components/StraightKey.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react'
|
||||
import useStraightKey from '../hooks/useStraightKey';
|
||||
// import useElectronicKey from '../hooks/useElectronicKey';
|
||||
|
||||
export default React.memo(function StraightKey(props) {
|
||||
|
||||
useStraightKey(props.gameMode)
|
||||
|
||||
})
|
||||
|
|
@ -7,5 +7,6 @@
|
|||
"slow": 40,
|
||||
"normal": 24,
|
||||
"fast": 17
|
||||
}
|
||||
},
|
||||
"historySize": 5
|
||||
}
|
||||
24
src/contexts/keyTypeContext.js
Normal file
24
src/contexts/keyTypeContext.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import React, {Component} from "react"
|
||||
|
||||
const KeyTypeContext = React.createContext()
|
||||
|
||||
class KeyTypeContextProvider extends Component {
|
||||
state = {
|
||||
keyType: "straight"
|
||||
}
|
||||
|
||||
switchKeyType = (type = "straight") => {
|
||||
this.setState({keyType: type})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<KeyTypeContext.Provider value={{keyType: this.state.keyType, switchKeyType: this.switchKeyType}}>
|
||||
{this.props.children}
|
||||
</KeyTypeContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export {KeyTypeContextProvider, KeyTypeContext}
|
||||
42
src/contexts/morseBufferContext.js
Normal file
42
src/contexts/morseBufferContext.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import React, {useState} from "react"
|
||||
|
||||
const MorseBufferContext = React.createContext()
|
||||
|
||||
function MorseBufferContextProvider(props) {
|
||||
// state = {
|
||||
// morseCharBuffer: '',
|
||||
// morseWords: []
|
||||
// //morseCharBuffer, morseWords, clearHistory, setMorseCharBuffer, setMorseWords
|
||||
// }
|
||||
|
||||
const [morseCharBuffer, setMorseCharBuffer] = useState('')
|
||||
const [morseWords, setMorseWords] = useState([])
|
||||
|
||||
|
||||
// switchKeyType = (type = "straight") => {
|
||||
// this.setState({keyType: type})
|
||||
// }
|
||||
|
||||
// setMorseCharBuffer = (value) => {
|
||||
// this.setState({morseCharBuffer: value})
|
||||
// }
|
||||
// setMorseWords = (value) => {
|
||||
// this.setState({morseWords: value})
|
||||
// }
|
||||
|
||||
// render() {
|
||||
return (
|
||||
<MorseBufferContext.Provider value={{
|
||||
morseCharBuffer: morseCharBuffer,
|
||||
morseWords: morseWords,
|
||||
setMorseCharBuffer: setMorseCharBuffer,
|
||||
setMorseWords: setMorseWords
|
||||
}}>
|
||||
{props.children}
|
||||
</MorseBufferContext.Provider>
|
||||
)
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
export {MorseBufferContextProvider, MorseBufferContext}
|
||||
335
src/hooks/useElectronicKey.js
Normal file
335
src/hooks/useElectronicKey.js
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
import {useEffect, useContext, useState} from 'react'
|
||||
import config from '../config.json'
|
||||
import {MorseBufferContext} from '../contexts/morseBufferContext'
|
||||
|
||||
// SINGLE/DUAL LEVER TELEGRAPH
|
||||
|
||||
function useElectronicKey(mode = 'practice') {
|
||||
|
||||
const {morseCharBuffer, setMorseCharBuffer, morseWords, setMorseWords} = useContext(MorseBufferContext)
|
||||
|
||||
const timingUnit = config.timingUnit
|
||||
|
||||
const ditMaxTime = 85 // ditMaxTime * 0.365 to get ms, e.g. 85 * 0.365 ~= 31ms
|
||||
const letterGapMinTime = ditMaxTime*0.36*3 //config.practiceSpeed.normal*3
|
||||
const wordGapMaxTime = ditMaxTime*0.36*7 // config.practiceSpeed.normal*7
|
||||
const morseHistorySize = config.historySize
|
||||
|
||||
let leftIsPressed = false
|
||||
let rightIsPressed = false
|
||||
let queueRunning = false
|
||||
let queue = []
|
||||
let pressedFirst = null
|
||||
|
||||
let depressSyncTime
|
||||
let depressSyncTimer
|
||||
let depressSyncTimerRunning = false
|
||||
let gapTimer = 0
|
||||
let gapTime = 0
|
||||
let paddlesReleasedSimultaneously = false
|
||||
|
||||
|
||||
let currentPromise = Promise.resolve()
|
||||
|
||||
// Audio Setup
|
||||
let AudioContext = window.AudioContext || window.webkitAudioContext || false
|
||||
let context
|
||||
window.AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
if (AudioContext) {
|
||||
context = new AudioContext()
|
||||
} else {
|
||||
context = null
|
||||
}
|
||||
let frequency = config.frequency
|
||||
|
||||
let toneTimer = 0
|
||||
let toneTime = 0
|
||||
let start = 0
|
||||
let end = 0
|
||||
|
||||
// Promisify playing Dits and Dahs
|
||||
function play(ditDah) {
|
||||
let playDuration = ((ditDah === '.') ? ditMaxTime : ditMaxTime*3)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (context.state === 'interrupted') {
|
||||
context.resume()
|
||||
}
|
||||
|
||||
let o = context.createOscillator()
|
||||
o.frequency.value = frequency
|
||||
o.type = "sine"
|
||||
o.onended = () => {
|
||||
// clearInterval(toneTimer)
|
||||
// end = toneTime
|
||||
// console.log('duration:', end-start);
|
||||
resolve()
|
||||
}
|
||||
|
||||
let startTime = context.currentTime;
|
||||
|
||||
let g = context.createGain()
|
||||
g.gain.exponentialRampToValueAtTime(config.mainVolume, startTime)
|
||||
g.gain.setValueAtTime(config.mainVolume, startTime)
|
||||
o.connect(g)
|
||||
g.connect(context.destination)
|
||||
|
||||
o.start(startTime)
|
||||
|
||||
// // for troubleshooting ditDah length in milliseconds
|
||||
// toneTimer = setInterval(() => {
|
||||
// toneTime += 1
|
||||
// }, 1);
|
||||
// start = toneTime
|
||||
// // </>
|
||||
|
||||
g.gain.setTargetAtTime(0.0001, startTime + playDuration/1000, 0.001)
|
||||
o.stop(startTime + playDuration/1000 + 0.05)
|
||||
})
|
||||
}
|
||||
|
||||
function playWithSpaces(ditDah) {
|
||||
let delay = (ditDah === '.') ? ditMaxTime + ditMaxTime : ditMaxTime*3 + ditMaxTime
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
if (ditDah === '.' || ditDah === '-') {
|
||||
play(ditDah)
|
||||
.then(setTimeout(() => {
|
||||
setMorseCharBuffer(prev => prev + ditDah)
|
||||
resolve();
|
||||
}, delay))
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
// setMorseCharBuffer(prev => prev + ' ')
|
||||
resolve();
|
||||
}, delay)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function executeQueue() {
|
||||
let localQueue = queue
|
||||
|
||||
// Set waitTime to completion of queue (including spaces)
|
||||
let waitTime = 0
|
||||
for (let i in localQueue) {
|
||||
if (localQueue[i] === '.') {
|
||||
waitTime += ditMaxTime*2
|
||||
} else if (localQueue[i] === '-') {
|
||||
waitTime += ditMaxTime*4
|
||||
}
|
||||
}
|
||||
|
||||
queueRunning = true
|
||||
|
||||
// Wait till completion of queue to execute
|
||||
const clear = setTimeout(() => {
|
||||
queueRunning = false
|
||||
queue = []
|
||||
checkPressed()
|
||||
}, waitTime)
|
||||
|
||||
// Execute queue
|
||||
for (let i = 0; i < localQueue.length; i++) {
|
||||
currentPromise = currentPromise.then(() => {
|
||||
return playWithSpaces(localQueue[i])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkPressed() {
|
||||
if (leftIsPressed && rightIsPressed) {
|
||||
// queue.push(queue.slice(-1) === '.' ? '-' : '.')
|
||||
if (pressedFirst === 'left') {
|
||||
queue.push('-')
|
||||
if (!paddlesReleasedSimultaneously) {
|
||||
// console.log('one');
|
||||
queue.push('.')
|
||||
}
|
||||
} else {
|
||||
queue.push('.')
|
||||
if (!paddlesReleasedSimultaneously) {
|
||||
// console.log('two');
|
||||
queue.push('-')
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (leftIsPressed && !rightIsPressed) {
|
||||
queue.push('.')
|
||||
}
|
||||
else if (rightIsPressed && !leftIsPressed) {
|
||||
queue.push('-')
|
||||
}
|
||||
if (queue.length > 0) {
|
||||
executeQueue()
|
||||
}
|
||||
}
|
||||
|
||||
function handleInputStart(event) {
|
||||
event.preventDefault()
|
||||
|
||||
// if (event.keyCode === 188) {
|
||||
// depressSyncTimeout()
|
||||
// }
|
||||
// else if (event.keyCode === 190) {
|
||||
// testQueue.push('.')
|
||||
// console.log('testQueue', testQueue);
|
||||
// }
|
||||
// else if (event.target.id === 'morseButton') {
|
||||
// testQueue = []
|
||||
// }
|
||||
// while (testQueue.length > 0) {
|
||||
// currentPromise = currentPromise.then(() => {
|
||||
// return playWithSpaces(testQueue.shift())
|
||||
// })
|
||||
// }
|
||||
paddlesReleasedSimultaneously = false
|
||||
|
||||
if (event.repeat) { return }
|
||||
|
||||
// if (!leftIsPressed && !rightIsPressed) {
|
||||
// clearInterval(gapTimer)
|
||||
// checkGapBetweenInputs()
|
||||
// }
|
||||
|
||||
if (event.keyCode === 188) {
|
||||
leftIsPressed = true
|
||||
if (!rightIsPressed) { pressedFirst = 'left'}
|
||||
|
||||
// Prevent further input if queue is executing
|
||||
if (!queueRunning) {
|
||||
checkPressed()
|
||||
}
|
||||
}
|
||||
else if (event.keyCode === 190) {
|
||||
rightIsPressed = true
|
||||
if (!leftIsPressed) { pressedFirst = 'right'}
|
||||
|
||||
// Prevent further input if queue is executing
|
||||
if (!queueRunning) {
|
||||
checkPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function startDepressSyncTimer() {
|
||||
depressSyncTimerRunning = true
|
||||
// Reset depressSyncTime
|
||||
depressSyncTime = 0
|
||||
// Start depressSyncTimer
|
||||
depressSyncTimer = setInterval(() => {
|
||||
depressSyncTime += 1
|
||||
if (depressSyncTime > 20) {
|
||||
depressSyncTimerRunning = false
|
||||
clearInterval(depressSyncTimer)
|
||||
depressSyncTime = 0
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
function stopDepressSyncTimer() {
|
||||
depressSyncTimerRunning = false
|
||||
clearInterval(depressSyncTimer)
|
||||
if (depressSyncTime < 10) {
|
||||
paddlesReleasedSimultaneously = true
|
||||
queue.pop()
|
||||
console.log('paddles released', queue);
|
||||
}
|
||||
depressSyncTime = 0
|
||||
}
|
||||
function startGapTimer() {
|
||||
gapTime = 0
|
||||
gapTimer = setInterval(() => {
|
||||
gapTime += 1
|
||||
|
||||
// Gap between words
|
||||
if (mode === 'practice' && gapTime >= wordGapMaxTime) {
|
||||
setMorseCharBuffer(prev => prev + '/')
|
||||
clearInterval(gapTimer)
|
||||
gapTimer = 0
|
||||
gapTime = 0
|
||||
}
|
||||
if (mode === 'challenge' && gapTime >= letterGapMinTime) {
|
||||
setMorseCharBuffer(prev => prev + '_')
|
||||
clearInterval(gapTimer)
|
||||
gapTimer = 0
|
||||
gapTime = 0
|
||||
}
|
||||
}, timingUnit);
|
||||
}
|
||||
function checkGapBetweenInputs() {
|
||||
// Check Gap between letters
|
||||
if (gapTime >= letterGapMinTime && gapTime < wordGapMaxTime) {
|
||||
console.log('letterGapMinTime <= gapTime < wordGapMaxTime:',letterGapMinTime, gapTime, wordGapMaxTime);
|
||||
if (mode === 'practice') {
|
||||
setMorseCharBuffer(prev => prev + ' ')
|
||||
} else if (mode === 'challenge') {
|
||||
console.log("UNDERSCORE ADDED");
|
||||
setMorseCharBuffer(prev => prev + '_')
|
||||
}
|
||||
clearInterval(gapTimer)
|
||||
gapTimer = 0
|
||||
}
|
||||
}
|
||||
|
||||
function handleInputEnd(event) {
|
||||
event.preventDefault()
|
||||
|
||||
if (event.keyCode === 188) {
|
||||
leftIsPressed = false
|
||||
|
||||
if (pressedFirst === 'left') { pressedFirst = null }
|
||||
|
||||
if (!depressSyncTimerRunning) { startDepressSyncTimer() }
|
||||
else { stopDepressSyncTimer() }
|
||||
}
|
||||
else if (event.keyCode === 190) {
|
||||
rightIsPressed = false
|
||||
if (pressedFirst === 'right') { pressedFirst = null }
|
||||
|
||||
if (!depressSyncTimerRunning) { startDepressSyncTimer() }
|
||||
else { stopDepressSyncTimer() }
|
||||
}
|
||||
// if (!leftIsPressed && !rightIsPressed ) { startGapTimer() }
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('keydown', handleInputStart)
|
||||
document.addEventListener('keyup', handleInputEnd)
|
||||
|
||||
const morseButton = document.getElementById('morseButton')
|
||||
morseButton.addEventListener('mousedown', handleInputStart)
|
||||
morseButton.addEventListener('touchstart', handleInputStart)
|
||||
morseButton.addEventListener('mouseup', handleInputEnd)
|
||||
morseButton.addEventListener('touchend', handleInputEnd)
|
||||
|
||||
return function cleanup() {
|
||||
document.removeEventListener('keydown', handleInputStart)
|
||||
document.removeEventListener('keyup', handleInputEnd)
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// PRACTICE MODE
|
||||
if (morseCharBuffer.slice(-1) === '/' && mode === 'practice') {
|
||||
// Remove forward slash
|
||||
let val = morseCharBuffer.slice(0,morseCharBuffer.length-1)
|
||||
|
||||
setMorseWords(prev => [val, ...prev])
|
||||
|
||||
if (morseWords.length >= morseHistorySize) {
|
||||
setMorseWords(prev => prev.slice(0,prev.length-1))
|
||||
}
|
||||
setMorseCharBuffer('')
|
||||
}
|
||||
// CHALLENGE MODE: leave forward slash there; to be parsed by ChallengeDisplay.js
|
||||
// else if (morseCharBuffer.slice(-1) === '/' && mode === 'challenge') {
|
||||
|
||||
// }
|
||||
|
||||
// eslint-disable-next-line
|
||||
}, [morseCharBuffer])
|
||||
}
|
||||
|
||||
export default useElectronicKey
|
||||
344
src/hooks/useElectronicKey_iambicB.js
Normal file
344
src/hooks/useElectronicKey_iambicB.js
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
import {useEffect, useContext, useState} from 'react'
|
||||
import config from '../config.json'
|
||||
import {MorseBufferContext} from '../contexts/morseBufferContext'
|
||||
|
||||
// SINGLE/DUAL LEVER TELEGRAPH
|
||||
|
||||
function useElectronicKey(mode = 'practice') {
|
||||
|
||||
const {morseCharBuffer, setMorseCharBuffer, morseWords, setMorseWords} = useContext(MorseBufferContext)
|
||||
|
||||
const timingUnit = config.timingUnit
|
||||
|
||||
const ditMaxTime = 85 // ditMaxTime * 0.365 to get ms, e.g. 85 * 0.365 ~= 31ms
|
||||
const letterGapMinTime = ditMaxTime*0.36*3 //config.practiceSpeed.normal*3
|
||||
const wordGapMaxTime = ditMaxTime*0.36*7 // config.practiceSpeed.normal*7
|
||||
const morseHistorySize = config.historySize
|
||||
|
||||
let leftIsPressed = false
|
||||
let rightIsPressed = false
|
||||
let queueRunning = false
|
||||
let queue = []
|
||||
let pressedFirst = null
|
||||
|
||||
let depressSyncTime
|
||||
let depressSyncTimer
|
||||
let depressSyncTimerRunning = false
|
||||
let gapTimer = 0
|
||||
let gapTime = 0
|
||||
const [paddlesReleasedSimultaneously, setPaddlesReleasedSimultaneously] = useState(false)
|
||||
|
||||
|
||||
let currentPromise = Promise.resolve()
|
||||
|
||||
// Audio Setup
|
||||
let AudioContext = window.AudioContext || window.webkitAudioContext || false
|
||||
let context
|
||||
window.AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
if (AudioContext) {
|
||||
context = new AudioContext()
|
||||
} else {
|
||||
context = null
|
||||
}
|
||||
let frequency = config.frequency
|
||||
|
||||
let toneTimer = 0
|
||||
let toneTime = 0
|
||||
let start = 0
|
||||
let end = 0
|
||||
|
||||
// Promisify playing Dits and Dahs
|
||||
function play(ditDah) {
|
||||
let playDuration = ((ditDah === '.') ? ditMaxTime : ditMaxTime*3)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (context.state === 'interrupted') {
|
||||
context.resume()
|
||||
}
|
||||
|
||||
let o = context.createOscillator()
|
||||
o.frequency.value = frequency
|
||||
o.type = "sine"
|
||||
o.onended = () => {
|
||||
// clearInterval(toneTimer)
|
||||
// end = toneTime
|
||||
// console.log('duration:', end-start);
|
||||
resolve()
|
||||
}
|
||||
|
||||
let startTime = context.currentTime;
|
||||
|
||||
let g = context.createGain()
|
||||
g.gain.exponentialRampToValueAtTime(config.mainVolume, startTime)
|
||||
g.gain.setValueAtTime(config.mainVolume, startTime)
|
||||
o.connect(g)
|
||||
g.connect(context.destination)
|
||||
|
||||
o.start(startTime)
|
||||
|
||||
// // for troubleshooting ditDah length in milliseconds
|
||||
// toneTimer = setInterval(() => {
|
||||
// toneTime += 1
|
||||
// }, 1);
|
||||
// start = toneTime
|
||||
// // </>
|
||||
|
||||
g.gain.setTargetAtTime(0.0001, startTime + playDuration/1000, 0.001)
|
||||
o.stop(startTime + playDuration/1000 + 0.05)
|
||||
})
|
||||
}
|
||||
|
||||
function playWithSpaces(ditDah) {
|
||||
let delay = (ditDah === '.') ? ditMaxTime + ditMaxTime : ditMaxTime*3 + ditMaxTime
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
if (ditDah === '.' || ditDah === '-') {
|
||||
play(ditDah)
|
||||
.then(setTimeout(() => {
|
||||
setMorseCharBuffer(prev => prev + ditDah)
|
||||
resolve();
|
||||
}, delay))
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
// setMorseCharBuffer(prev => prev + ' ')
|
||||
resolve();
|
||||
}, delay)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function executeQueue() {
|
||||
let localQueue = queue
|
||||
|
||||
// Set waitTime to completion of queue (including spaces)
|
||||
let waitTime = 0
|
||||
for (let i in localQueue) {
|
||||
if (localQueue[i] === '.') {
|
||||
waitTime += ditMaxTime*2
|
||||
} else if (localQueue[i] === '-') {
|
||||
waitTime += ditMaxTime*4
|
||||
}
|
||||
}
|
||||
|
||||
queueRunning = true
|
||||
|
||||
// Wait till completion of queue to execute
|
||||
const clear = setTimeout(() => {
|
||||
queueRunning = false
|
||||
queue = []
|
||||
checkPressed()
|
||||
}, waitTime)
|
||||
|
||||
// Execute queue
|
||||
for (let i = 0; i < localQueue.length; i++) {
|
||||
currentPromise = currentPromise.then(() => {
|
||||
return playWithSpaces(localQueue[i])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkPressed() {
|
||||
if (leftIsPressed && rightIsPressed) {
|
||||
if (pressedFirst === 'left') {
|
||||
queue.push('-')
|
||||
if (!paddlesReleasedSimultaneously) {
|
||||
console.log('one');
|
||||
queue.push('.')
|
||||
}
|
||||
} else {
|
||||
queue.push('.')
|
||||
if (!paddlesReleasedSimultaneously) {
|
||||
console.log('two');
|
||||
queue.push('-')
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (leftIsPressed && !rightIsPressed) {
|
||||
queue.push('.')
|
||||
}
|
||||
else if (rightIsPressed && !leftIsPressed) {
|
||||
queue.push('-')
|
||||
}
|
||||
if (queue.length > 0) {
|
||||
executeQueue()
|
||||
}
|
||||
}
|
||||
|
||||
// function depressSyncTimeout() {
|
||||
// let depressSyncTimeO = setTimeout(() => {
|
||||
// if (testQueue.length > 0) {
|
||||
// let x = testQueue.shift()
|
||||
// console.log('testQueue', x, testQueue);
|
||||
// depressSyncTimeout()
|
||||
// } else {
|
||||
// clearTimeout(depressSyncTimeO)
|
||||
// }
|
||||
// }, 1000)
|
||||
// }
|
||||
|
||||
function handleInputStart(event) {
|
||||
event.preventDefault()
|
||||
|
||||
// if (event.keyCode === 188) {
|
||||
// depressSyncTimeout()
|
||||
// }
|
||||
// else if (event.keyCode === 190) {
|
||||
// testQueue.push('.')
|
||||
// console.log('testQueue', testQueue);
|
||||
// }
|
||||
// else if (event.target.id === 'morseButton') {
|
||||
// testQueue = []
|
||||
// }
|
||||
// while (testQueue.length > 0) {
|
||||
// currentPromise = currentPromise.then(() => {
|
||||
// return playWithSpaces(testQueue.shift())
|
||||
// })
|
||||
// }
|
||||
setPaddlesReleasedSimultaneously(false)
|
||||
|
||||
if (event.repeat) { return }
|
||||
|
||||
// if (!leftIsPressed && !rightIsPressed) {
|
||||
// clearInterval(gapTimer)
|
||||
// checkGapBetweenInputs()
|
||||
// }
|
||||
|
||||
if (event.keyCode === 188) {
|
||||
leftIsPressed = true
|
||||
if (!rightIsPressed) { pressedFirst = 'left'}
|
||||
|
||||
// Prevent further input if queue is executing
|
||||
if (!queueRunning) {
|
||||
checkPressed()
|
||||
}
|
||||
}
|
||||
else if (event.keyCode === 190) {
|
||||
rightIsPressed = true
|
||||
if (!leftIsPressed) { pressedFirst = 'right'}
|
||||
|
||||
// Prevent further input if queue is executing
|
||||
if (!queueRunning) {
|
||||
checkPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function startDepressSyncTimer() {
|
||||
depressSyncTimerRunning = true
|
||||
// Reset depressSyncTime
|
||||
depressSyncTime = 0
|
||||
// Start depressSyncTimer
|
||||
depressSyncTimer = setInterval(() => {
|
||||
depressSyncTime += 1
|
||||
if (depressSyncTime > 20) {
|
||||
depressSyncTimerRunning = false
|
||||
clearInterval(depressSyncTimer)
|
||||
depressSyncTime = 0
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
function stopDepressSyncTimer() {
|
||||
depressSyncTimerRunning = false
|
||||
clearInterval(depressSyncTimer)
|
||||
if (depressSyncTime < 10) {
|
||||
setPaddlesReleasedSimultaneously(true)
|
||||
}
|
||||
depressSyncTime = 0
|
||||
}
|
||||
function startGapTimer() {
|
||||
gapTime = 0
|
||||
gapTimer = setInterval(() => {
|
||||
gapTime += 1
|
||||
|
||||
// Gap between words
|
||||
if (mode === 'practice' && gapTime >= wordGapMaxTime) {
|
||||
setMorseCharBuffer(prev => prev + '/')
|
||||
clearInterval(gapTimer)
|
||||
gapTimer = 0
|
||||
gapTime = 0
|
||||
}
|
||||
if (mode === 'challenge' && gapTime >= letterGapMinTime) {
|
||||
setMorseCharBuffer(prev => prev + '_')
|
||||
clearInterval(gapTimer)
|
||||
gapTimer = 0
|
||||
gapTime = 0
|
||||
}
|
||||
}, timingUnit);
|
||||
}
|
||||
function checkGapBetweenInputs() {
|
||||
// Check Gap between letters
|
||||
if (gapTime >= letterGapMinTime && gapTime < wordGapMaxTime) {
|
||||
console.log('letterGapMinTime <= gapTime < wordGapMaxTime:',letterGapMinTime, gapTime, wordGapMaxTime);
|
||||
if (mode === 'practice') {
|
||||
setMorseCharBuffer(prev => prev + ' ')
|
||||
} else if (mode === 'challenge') {
|
||||
console.log("UNDERSCORE ADDED");
|
||||
setMorseCharBuffer(prev => prev + '_')
|
||||
}
|
||||
clearInterval(gapTimer)
|
||||
gapTimer = 0
|
||||
}
|
||||
}
|
||||
|
||||
function handleInputEnd(event) {
|
||||
event.preventDefault()
|
||||
|
||||
if (event.keyCode === 188) {
|
||||
leftIsPressed = false
|
||||
|
||||
if (pressedFirst === 'left') { pressedFirst = null }
|
||||
|
||||
if (!depressSyncTimerRunning) { startDepressSyncTimer() }
|
||||
else { stopDepressSyncTimer() }
|
||||
}
|
||||
else if (event.keyCode === 190) {
|
||||
rightIsPressed = false
|
||||
if (pressedFirst === 'right') { pressedFirst = null }
|
||||
|
||||
if (!depressSyncTimerRunning) { startDepressSyncTimer() }
|
||||
else { stopDepressSyncTimer() }
|
||||
}
|
||||
// if (!leftIsPressed && !rightIsPressed ) { startGapTimer() }
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('keydown', handleInputStart)
|
||||
document.addEventListener('keyup', handleInputEnd)
|
||||
|
||||
const morseButton = document.getElementById('morseButton')
|
||||
morseButton.addEventListener('mousedown', handleInputStart)
|
||||
morseButton.addEventListener('touchstart', handleInputStart)
|
||||
morseButton.addEventListener('mouseup', handleInputEnd)
|
||||
morseButton.addEventListener('touchend', handleInputEnd)
|
||||
|
||||
return function cleanup() {
|
||||
document.removeEventListener('keydown', handleInputStart)
|
||||
document.removeEventListener('keyup', handleInputEnd)
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// PRACTICE MODE
|
||||
if (morseCharBuffer.slice(-1) === '/' && mode === 'practice') {
|
||||
// Remove forward slash
|
||||
let val = morseCharBuffer.slice(0,morseCharBuffer.length-1)
|
||||
|
||||
setMorseWords(prev => [val, ...prev])
|
||||
|
||||
if (morseWords.length >= morseHistorySize) {
|
||||
setMorseWords(prev => prev.slice(0,prev.length-1))
|
||||
}
|
||||
setMorseCharBuffer('')
|
||||
}
|
||||
// CHALLENGE MODE: leave forward slash there; to be parsed by ChallengeDisplay.js
|
||||
// else if (morseCharBuffer.slice(-1) === '/' && mode === 'challenge') {
|
||||
|
||||
// }
|
||||
|
||||
// eslint-disable-next-line
|
||||
}, [morseCharBuffer])
|
||||
}
|
||||
|
||||
export default useElectronicKey
|
||||
0
src/hooks/useKeySelection.js
Normal file
0
src/hooks/useKeySelection.js
Normal file
|
|
@ -1,175 +0,0 @@
|
|||
import {useEffect} from 'react'
|
||||
import config from '../config.json'
|
||||
|
||||
// SINGLE/DUAL LEVER TELEGRAPH
|
||||
|
||||
function usePaddleTelegraph() {
|
||||
|
||||
const ditMaxTime = 85
|
||||
let leftIsPressed = false
|
||||
let rightIsPressed = false
|
||||
let queueRunning = false
|
||||
let queue = []
|
||||
|
||||
let currentPromise = Promise.resolve()
|
||||
|
||||
// Audio Setup
|
||||
let AudioContext = window.AudioContext || window.webkitAudioContext || false
|
||||
let context
|
||||
window.AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
if (AudioContext) {
|
||||
context = new AudioContext()
|
||||
} else {
|
||||
context = null
|
||||
}
|
||||
let frequency = config.frequency
|
||||
|
||||
|
||||
// Promisify playing Dits and Dahs
|
||||
function play(ditDah) {
|
||||
let playDuration = ((ditDah === '.') ? ditMaxTime : ditMaxTime*3)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (context.state === 'interrupted') {
|
||||
context.resume()
|
||||
}
|
||||
|
||||
let o = context.createOscillator()
|
||||
o.frequency.value = frequency
|
||||
o.type = "sine"
|
||||
o.onended = () => {
|
||||
resolve()
|
||||
}
|
||||
|
||||
let startTime = context.currentTime;
|
||||
|
||||
let g = context.createGain()
|
||||
g.gain.exponentialRampToValueAtTime(config.mainVolume, startTime)
|
||||
g.gain.setValueAtTime(config.mainVolume, startTime)
|
||||
o.connect(g)
|
||||
g.connect(context.destination)
|
||||
o.start(startTime)
|
||||
|
||||
g.gain.setTargetAtTime(0.0001, startTime + playDuration/1000, 0.001)
|
||||
o.stop(startTime + playDuration/1000 + 0.05)
|
||||
})
|
||||
}
|
||||
|
||||
function playWithSpaces(ditDah) {
|
||||
let delay = (ditDah === '.') ? ditMaxTime + ditMaxTime : ditMaxTime*3 + ditMaxTime
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
if (ditDah === '.' || ditDah === '-') {
|
||||
play(ditDah)
|
||||
.then(setTimeout(() => {
|
||||
resolve();
|
||||
}, delay))
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, delay)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function executeQueue() {
|
||||
let localQueue = queue
|
||||
console.log('localQueue',localQueue);
|
||||
|
||||
// Set wait time to completion of queue (including spaces)
|
||||
let waitTime = 0
|
||||
for (let i in localQueue) {
|
||||
if (localQueue[i] === '.') {
|
||||
waitTime += ditMaxTime*2
|
||||
} else if (localQueue[i] === '-') {
|
||||
waitTime += ditMaxTime*4
|
||||
}
|
||||
}
|
||||
|
||||
queueRunning = true
|
||||
|
||||
// Wait till completion of queue to execute
|
||||
setTimeout(() => {
|
||||
queueRunning = false
|
||||
queue = []
|
||||
checkPressed()
|
||||
}, waitTime)
|
||||
|
||||
// Execute queue
|
||||
for (let i = 0; i < localQueue.length; i++) {
|
||||
currentPromise = currentPromise.then(() => {
|
||||
return playWithSpaces(localQueue[i])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkPressed() {
|
||||
if (leftIsPressed) {
|
||||
queue.push('.')
|
||||
}
|
||||
if (rightIsPressed) {
|
||||
queue.push('-')
|
||||
}
|
||||
if (queue.length > 0) {
|
||||
executeQueue()
|
||||
}
|
||||
}
|
||||
|
||||
function handleInputStart(event) {
|
||||
event.preventDefault()
|
||||
|
||||
if (event.repeat) { return }
|
||||
|
||||
if (event.keyCode === 188) {
|
||||
leftIsPressed = true
|
||||
console.log("LEFT DOWN");
|
||||
|
||||
// Prevent further input if queue is executing
|
||||
if (!queueRunning) {
|
||||
checkPressed()
|
||||
}
|
||||
}
|
||||
else if (event.keyCode === 190) {
|
||||
rightIsPressed = true
|
||||
console.log("RIGHT DOWN");
|
||||
|
||||
// Prevent further input if queue is executing
|
||||
if (!queueRunning) {
|
||||
checkPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleInputEnd(event) {
|
||||
event.preventDefault()
|
||||
|
||||
if (event.keyCode === 188) {
|
||||
leftIsPressed = false
|
||||
console.log("LEFT UP");
|
||||
}
|
||||
else if (event.keyCode === 190) {
|
||||
rightIsPressed = false
|
||||
console.log("RIGHT UP");
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('keydown', handleInputStart)
|
||||
document.addEventListener('keyup', handleInputEnd)
|
||||
|
||||
const morseButton = document.getElementById('morseButton')
|
||||
morseButton.addEventListener('mousedown', handleInputStart)
|
||||
morseButton.addEventListener('touchstart', handleInputStart)
|
||||
morseButton.addEventListener('mouseup', handleInputEnd)
|
||||
morseButton.addEventListener('touchend', handleInputEnd)
|
||||
|
||||
return function cleanup() {
|
||||
document.removeEventListener('keydown', handleInputStart)
|
||||
document.removeEventListener('keyup', handleInputEnd)
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [])
|
||||
|
||||
}
|
||||
|
||||
export default usePaddleTelegraph
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
import {useState, useEffect} from 'react'
|
||||
import {useEffect, useContext} from 'react'
|
||||
import {MorseBufferContext} from '../contexts/morseBufferContext'
|
||||
import config from '../config.json'
|
||||
|
||||
// STRAIGHT KEY TELEGRAPH
|
||||
|
||||
function useTelegraph(mode = 'practice') {
|
||||
function useStraightKey(mode = 'practice') {
|
||||
|
||||
const [morseCharBuffer, setMorseCharBuffer] = useState('') // e.g. '-..'
|
||||
const [morseWords, setMorseWords] = useState([]) // e.g. [['-..','.','-,'], ['...','---','...']]
|
||||
const {morseCharBuffer, setMorseCharBuffer, morseWords, setMorseWords} = useContext(MorseBufferContext)
|
||||
// const [morseCharBuffer, setMorseCharBuffer] = useState('') // e.g. '-..'
|
||||
// const [morseWords, setMorseWords] = useState([]) // e.g. [['-..','.','-,'], ['...','---','...']]
|
||||
|
||||
|
||||
let charTimer = 0
|
||||
let charTime = 0
|
||||
|
|
@ -18,7 +21,7 @@ function useTelegraph(mode = 'practice') {
|
|||
const ditMaxTime = config.practiceSpeed.normal
|
||||
const letterGapMinTime = ditMaxTime*3
|
||||
const wordGapMaxTime = ditMaxTime*7
|
||||
const morseHistorySize = 5
|
||||
const morseHistorySize = config.historySize
|
||||
|
||||
// Tone Setup
|
||||
let AudioContext = window.AudioContext || window.webkitAudioContext || false
|
||||
|
|
@ -169,6 +172,11 @@ function useTelegraph(mode = 'practice') {
|
|||
return function cleanup() {
|
||||
document.removeEventListener('keydown', handleInputStart)
|
||||
document.removeEventListener('keyup', handleInputEnd)
|
||||
const morseButton = document.getElementById('morseButton')
|
||||
morseButton.removeEventListener('mousedown', handleInputStart)
|
||||
morseButton.removeEventListener('touchstart', handleInputStart)
|
||||
morseButton.removeEventListener('mouseup', handleInputEnd)
|
||||
morseButton.removeEventListener('touchend', handleInputEnd)
|
||||
clearHistory()
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
|
|
@ -199,4 +207,4 @@ function useTelegraph(mode = 'practice') {
|
|||
return {morseCharBuffer, morseWords, clearHistory, setMorseCharBuffer, setMorseWords}
|
||||
}
|
||||
|
||||
export default useTelegraph
|
||||
export default useStraightKey
|
||||
|
|
@ -3,11 +3,15 @@ import ReactDOM from 'react-dom';
|
|||
import './index.css';
|
||||
import App from './App';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
import {GameModeContextProvider} from "./gameContext"
|
||||
import {GameModeContextProvider} from "./contexts/gameContext"
|
||||
import {KeyTypeContextProvider} from "./contexts/keyTypeContext"
|
||||
|
||||
|
||||
ReactDOM.render(
|
||||
<GameModeContextProvider>
|
||||
<App />
|
||||
<KeyTypeContextProvider>
|
||||
<App />
|
||||
</KeyTypeContextProvider>
|
||||
</GameModeContextProvider>
|
||||
, document.getElementById('root'));
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue