mirror of https://github.com/nealey/vail.git
218 lines
4.4 KiB
JavaScript
218 lines
4.4 KiB
JavaScript
|
/** Silent period between words */
|
||
|
const PAUSE_WORD = -7
|
||
|
/** Silent period between letters */
|
||
|
const PAUSE_LETTER = -3
|
||
|
/** Silent period between dits and dash */
|
||
|
const PAUSE = -1
|
||
|
/** Length of a dit */
|
||
|
const DIT = 1
|
||
|
/** Length of a dah */
|
||
|
const DAH = 3
|
||
|
|
||
|
const MorseMap = {
|
||
|
"\x04": ".-.-.", // End Of Transmission
|
||
|
"\x18": "........", // Cancel
|
||
|
"0": "-----",
|
||
|
"1": ".----",
|
||
|
"2": "..---",
|
||
|
"3": "...--",
|
||
|
"4": "....-",
|
||
|
"5": ".....",
|
||
|
"6": "-....",
|
||
|
"7": "--...",
|
||
|
"8": "---..",
|
||
|
"9": "----.",
|
||
|
"a": ".-",
|
||
|
"b": "-...",
|
||
|
"c": "-.-.",
|
||
|
"d": "-..",
|
||
|
"e": ".",
|
||
|
"f": "..-.",
|
||
|
"g": "--.",
|
||
|
"h": "....",
|
||
|
"i": "..",
|
||
|
"j": ".---",
|
||
|
"k": "-.-",
|
||
|
"l": ".-..",
|
||
|
"m": "--",
|
||
|
"n": "-.",
|
||
|
"o": "---",
|
||
|
"p": ".--.",
|
||
|
"q": "--.-",
|
||
|
"r": ".-.",
|
||
|
"s": "...",
|
||
|
"t": "-",
|
||
|
"u": "..-",
|
||
|
"v": "...-",
|
||
|
"w": ".--",
|
||
|
"x": "-..-",
|
||
|
"y": "-.--",
|
||
|
"z": "--..",
|
||
|
".": ".-.-.-",
|
||
|
",": "--..--",
|
||
|
"?": "..--..",
|
||
|
"'": ".----.",
|
||
|
"!": "-.-.--",
|
||
|
"/": "-..-.",
|
||
|
"(": "-.--.",
|
||
|
")": "-.--.-",
|
||
|
"&": ".-...",
|
||
|
":": "---...",
|
||
|
";": "---...",
|
||
|
"=": "-...-",
|
||
|
"+": ".-.-.",
|
||
|
"-": "-....-",
|
||
|
"_": "--..-.",
|
||
|
"\"": ".-..-.",
|
||
|
"$": "...-..-",
|
||
|
"@": ".--.-.",
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Robo Keyer. It sends morse code so you don't have to!
|
||
|
*/
|
||
|
class Keyer {
|
||
|
/**
|
||
|
* Create a Keyer
|
||
|
*
|
||
|
* @param {TxControl} beginTxFunc Callback to begin transmitting
|
||
|
* @param {TxControl} endTxFunc Callback to end transmitting
|
||
|
*/
|
||
|
constructor(beginTxFunc, endTxFunc) {
|
||
|
this.beginTxFunc = beginTxFunc
|
||
|
this.endTxFunc = endTxFunc
|
||
|
this.ditDuration = 100
|
||
|
this.pauseMultiplier = 1
|
||
|
this.queue = []
|
||
|
this.pulseTimer = null
|
||
|
}
|
||
|
|
||
|
pulse() {
|
||
|
let next = this.queue.shift()
|
||
|
|
||
|
if (next == null) {
|
||
|
// Nothing left on the queue, stop the machine
|
||
|
this.pulseTimer = null
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (next < 0) {
|
||
|
next *= -1
|
||
|
if (next > 1) {
|
||
|
// Don't adjust spacing within a letter
|
||
|
next *= this.pauseMultiplier
|
||
|
} else {
|
||
|
this.endTxFunc()
|
||
|
}
|
||
|
} else {
|
||
|
this.beginTxFunc()
|
||
|
}
|
||
|
this.pulseTimer = setTimeout(() => this.pulse(), next * this.ditDuration)
|
||
|
}
|
||
|
|
||
|
maybePulse() {
|
||
|
// If there's no timer running right now, restart the pulse
|
||
|
if (!this.pulseTimer) {
|
||
|
this.pulse()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return true if we are currently playing out something
|
||
|
*/
|
||
|
Busy() {
|
||
|
return Boolean(this.pulseTimer)
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set a new dit interval (transmission rate)
|
||
|
*
|
||
|
* @param {number} duration Dit duration (milliseconds)
|
||
|
*/
|
||
|
SetDitDuration(duration) {
|
||
|
this.ditDuration = duration
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set a new pause multiplier.
|
||
|
*
|
||
|
* This slows down the inter-letter and inter-word pauses,
|
||
|
* which can aid in learning.
|
||
|
*
|
||
|
* @param {number} multiplier Pause multiplier
|
||
|
*/
|
||
|
SetPauseMultiplier(multiplier) {
|
||
|
this.pauseMultiplier = multiplier
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Delete anything left on the queue.
|
||
|
*/
|
||
|
Flush() {
|
||
|
this.queue.splice(0)
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add to the output queue, and start processing the queue if it's not currently being processed.
|
||
|
*
|
||
|
* @param {number} key A duration, in dits. Negative durations are silent.
|
||
|
*/
|
||
|
Enqueue(key) {
|
||
|
this.queue.push(key)
|
||
|
if (key > 0) {
|
||
|
this.queue.push(PAUSE)
|
||
|
}
|
||
|
this.maybePulse()
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enqueue a morse code string (eg "... --- ...")
|
||
|
*
|
||
|
* @param {string} ms String to enqueue
|
||
|
*/
|
||
|
EnqueueMorseString(ms) {
|
||
|
for (let mc of ms) {
|
||
|
switch (mc) {
|
||
|
case ".":
|
||
|
this.Enqueue(DIT)
|
||
|
break
|
||
|
case "-":
|
||
|
this.Enqueue(DAH)
|
||
|
break
|
||
|
case " ":
|
||
|
this.Enqueue(PAUSE_LETTER)
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enqueue an ASCII string (eg "SOS help")
|
||
|
*
|
||
|
* @param {string} s String to enqueue
|
||
|
*/
|
||
|
EnqueueAsciiString(s, {pauseLetter = PAUSE_LETTER, pauseWord = PAUSE_WORD} = {}) {
|
||
|
for (let c of s.toLowerCase()) {
|
||
|
let m = MorseMap[c]
|
||
|
if (m) {
|
||
|
this.EnqueueMorseString(m)
|
||
|
this.Enqueue(pauseLetter)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
switch (c) {
|
||
|
case " ":
|
||
|
case "\n":
|
||
|
case "\t":
|
||
|
this.Enqueue(pauseWord)
|
||
|
break
|
||
|
default:
|
||
|
console.warn("Unable to encode '" + c + "'!")
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export {Keyer}
|