mirror of https://github.com/nealey/vail.git
Working up to single dot
This commit is contained in:
parent
af21b30afc
commit
4950042e6c
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* A time duration.
|
||||
*
|
||||
* JavaScript uses milliseconds in most (but not all) places.
|
||||
* I've found it helpful to be able to multiply by a unit, so it's clear what's going on.
|
||||
*
|
||||
* @typedef {number} Duration
|
||||
*/
|
||||
|
||||
/** @type {Duration} */
|
||||
export const Millisecond = 1
|
||||
|
||||
/** @type {Duration} */
|
||||
export const Second = 1000 * Millisecond
|
||||
|
||||
/** @type {Duration} */
|
||||
export const Minute = 60 * Second
|
||||
|
||||
/** @type {Duration} */
|
||||
export const Hour = 60 * Minute
|
|
@ -5,10 +5,9 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- Material Design Lite -->
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.teal-purple.min.css">
|
||||
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
|
||||
<!-- Bulma CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@6.5.95/css/materialdesignicons.min.css">
|
||||
|
||||
<!-- Vail stuff -->
|
||||
<link rel="manifest" href="manifest.json">
|
||||
|
@ -18,50 +17,46 @@
|
|||
<link rel="stylesheet" href="vail.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="mdl-layout mdl-js-layout">
|
||||
<header class="mdl-layout__header mdl-layout__header--scroll">
|
||||
<div class="mdl-layout__header-row">
|
||||
<!-- Title -->
|
||||
<span class="mdl-layout-title">Vail</span>
|
||||
<!-- Add spacer, to align navigation to the right -->
|
||||
<div class="mdl-layout-spacer"></div>
|
||||
<!-- Navigation -->
|
||||
<nav class="mdl-navigation">
|
||||
<a class="mdl-navigation__link" href="https://github.com/nealey/vail">Source Code</a>
|
||||
</nav>
|
||||
<nav class="navbar">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item">
|
||||
<img src="vail.svg" alt="">
|
||||
Vail
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-menu">
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<a href="https://github.com/nealey/vail/">Source Code</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="mdl-layout__drawer">
|
||||
<span class="mdl-layout-title">Repeaters</span>
|
||||
<nav class="mdl-navigation">
|
||||
<a class="mdl-navigation__link" href="#">General</a>
|
||||
<a class="mdl-navigation__link" href="#1">Channel 1</a>
|
||||
<a class="mdl-navigation__link" href="#2">Channel 2</a>
|
||||
<a class="mdl-navigation__link" href="#3">Channel 3</a>
|
||||
</nav>
|
||||
<hr>
|
||||
<span class="mdl-layout-title">Local Practice</span>
|
||||
<nav class="mdl-navigation">
|
||||
<a class="mdl-navigation__link" href="#Echo">Echo</a>
|
||||
<a class="mdl-navigation__link" href="#Fortunes">Fortunes</a>
|
||||
<a class="mdl-navigation__link" href="#Fortunes: Pauses ×2">Fortunes (slow)</a>
|
||||
<a class="mdl-navigation__link" href="#Fortunes: Pauses ×4">Fortunes (very slow)</a>
|
||||
<a class="mdl-navigation__link" href="#Fortunes: Pauses ×6">Fortunes (very very slow)</a>
|
||||
<a class="mdl-navigation__link" href="#Fortunes: Pauses ×10">Fortunes (crazy slow)</a>
|
||||
</nav>
|
||||
<hr>
|
||||
<span class="mdl-layout-title">Resources</span>
|
||||
<nav class="mdl-navigation">
|
||||
<a class="mdl-navigation__link" href="https://github.com/nealey/vail/wiki" target="_blank">Wiki</a>
|
||||
<a class="mdl-navigation__link" href="https://discord.gg/GBzj8cBat7" target="_blank">Discord</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="snackbar" class="mdl-js-snackbar mdl-snackbar">
|
||||
<div class="mdl-snackbar__text"></div>
|
||||
<button class="mdl-snackbar__action" type="button"></button>
|
||||
</nav>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="field">
|
||||
<label class="mdl-textfield__label" for="repeater">Repeater</label>
|
||||
<div class="control">
|
||||
<input class="mdl-textfield__input" type="text" id="repeater" list="repeater-list">
|
||||
<datalist id="repeater-list">
|
||||
<option>General</option>
|
||||
<option value="1">Channel 1</option>
|
||||
<option value="2">Channel 2</option>
|
||||
<option value="3">Channel 3</option>
|
||||
<option value="Null">No transmit</option>
|
||||
<option>Echo</option>
|
||||
<option>Echo 5s</option>
|
||||
<option>Echo 10s</option>
|
||||
<option>Fortunes</option>
|
||||
<option>Fortunes: Pauses ×2</option>
|
||||
<option>Fortunes: Pauses ×4</option>
|
||||
<option>Fortunes: Pauses ×8</option>
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<main class="mdl-layout__content">
|
||||
<div class="flex">
|
||||
|
@ -73,22 +68,6 @@
|
|||
<div class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
|
||||
<input class="mdl-textfield__input" type="text" id="repeater" list="repeater-list">
|
||||
<datalist id="repeater-list">
|
||||
<option value="">General</option>
|
||||
<option value="1">Channel 1</option>
|
||||
<option value="2">Channel 2</option>
|
||||
<option value="3">Channel 3</option>
|
||||
<option value="Null">Null (dummy load)</option>
|
||||
<option>Echo</option>
|
||||
<option>Echo 5s</option>
|
||||
<option>Echo 10s</option>
|
||||
<option>Fortunes</option>
|
||||
<option>Fortunes: Pauses ×2</option>
|
||||
<option>Fortunes: Pauses ×4</option>
|
||||
<option>Fortunes: Pauses ×8</option>
|
||||
</datalist>
|
||||
<label class="mdl-textfield__label" for="repeater">Repeater</label>
|
||||
</div>
|
||||
</h2>
|
||||
</div>
|
||||
|
@ -118,7 +97,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<i class="material-icons" role="presentation">keyboard</i>
|
||||
<i class="mdi mdi-keyboard" title="Keyboard"></i>
|
||||
</td>
|
||||
<td>
|
||||
<kbd>c</kbd>
|
||||
|
@ -128,11 +107,11 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<i class="material-icons" role="presentation">gamepad</i>
|
||||
<i class="mdi mdi-controller-classic"></i>
|
||||
</td>
|
||||
<td>
|
||||
<img class="gamepad b0" title="Gamepad Bottom Button" src="b0.svg" alt="Bottom button">
|
||||
<img class="gamepad b1" title="Gamepad Right Button" src="b1.svg" alt="Right button">
|
||||
<i class="mdi mdi-gamepad-circle-down" title="Gamepad Bottom Button"></i>
|
||||
<i class="mdi mdi-gamepad-circle-right" title="Gamepad Right Button"></i>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -153,14 +132,14 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<i class="material-icons" role="presentation">keyboard</i>
|
||||
<i class="mdi mdi-keyboard" title="Keyboard"></i>
|
||||
</td>
|
||||
<td>
|
||||
<kbd>.</kbd>
|
||||
<kbd>x</kbd>
|
||||
</td>
|
||||
<td>
|
||||
<i class="material-icons" role="presentation">keyboard</i>
|
||||
<i class="mdi mdi-keyboard" title="Keyboard"></i>
|
||||
</td>
|
||||
<td>
|
||||
<kbd>/</kbd>
|
||||
|
@ -169,17 +148,17 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<i class="material-icons" role="presentation">gamepad</i>
|
||||
<i class="mdi mdi-controller-classic"></i>
|
||||
</td>
|
||||
<td>
|
||||
<img class="gamepad b2" title="Gamepad Left Button" src="b2.svg" alt="Left Button">
|
||||
<i class="mdi mdi-gamepad-circle-left" title="Gamepad Left Button"></i>
|
||||
<kbd class="gamepad" title="Gamepad Left Shoulder Button">L1</kbd>
|
||||
</td>
|
||||
<td>
|
||||
<i class="material-icons" role="presentation">gamepad</i>
|
||||
<i class="mdi mdi-controller-classic"></i>
|
||||
</td>
|
||||
<td>
|
||||
<img class="gamepad b3" title="Gamepad Top Button" src="b3.svg" alt="Top Button">
|
||||
<i class="mdi mdi-gamepad-circle-up" title="Gamepad Top Button"></i>
|
||||
<kbd class="gamepad" title="Gamepad Right Shoulder Button">R1</kbd>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -319,7 +298,6 @@
|
|||
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
<!-- vim: set noet ts=2 sw=2 : -->
|
||||
|
|
|
@ -2,7 +2,7 @@ class Input {
|
|||
constructor(keyer) {
|
||||
this.keyer = keyer
|
||||
}
|
||||
SetIntervalDuration(delay) {
|
||||
SetDitDuration(delay) {
|
||||
// Nothing
|
||||
}
|
||||
}
|
||||
|
|
296
static/keyer.mjs
296
static/keyer.mjs
|
@ -1,3 +1,11 @@
|
|||
/**
|
||||
* A number of keyers.
|
||||
*
|
||||
* The document "All About Squeeze-Keying" by Karl Fischer, DJ5IL, was
|
||||
* absolutely instrumental in correctly (I hope) implementing everything more
|
||||
* advanced than the bug keyer.
|
||||
*/
|
||||
|
||||
/** Silent period between words */
|
||||
const PAUSE_WORD = -7
|
||||
/** Silent period between letters */
|
||||
|
@ -9,6 +17,23 @@ const DIT = 1
|
|||
/** Duration of a dah */
|
||||
const DAH = 3
|
||||
|
||||
/**
|
||||
* A time duration.
|
||||
*
|
||||
* JavaScript uses milliseconds in most (but not all) places.
|
||||
* I've found it helpful to be able to multiply by a unit, so it's clear what's going on.
|
||||
*
|
||||
* @typedef {number} Duration
|
||||
*/
|
||||
/** @type {Duration} */
|
||||
const Millisecond = 1
|
||||
/** @type {Duration} */
|
||||
const Second = 1000 * Millisecond
|
||||
/** @type {Duration} */
|
||||
const Minute = 60 * Second
|
||||
/** @type {Duration} */
|
||||
const Hour = 60 * Minute
|
||||
|
||||
const MorseMap = {
|
||||
"\x04": ".-.-.", // End Of Transmission
|
||||
"\x18": "........", // Cancel
|
||||
|
@ -68,11 +93,6 @@ const MorseMap = {
|
|||
"@": ".--.-.",
|
||||
}
|
||||
|
||||
// iOS kludge
|
||||
if (!window.AudioContext) {
|
||||
window.AudioContext = window.webkitAudioContext
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the inverse of the input.
|
||||
* If you give it dit, it returns dah, and vice-versa.
|
||||
|
@ -80,7 +100,7 @@ if (!window.AudioContext) {
|
|||
* @param ditdah What to invert
|
||||
* @returns The inverse of ditdah
|
||||
*/
|
||||
function morseNot(ditdah) {
|
||||
function not(ditdah) {
|
||||
if (ditdah == DIT) {
|
||||
return DAH
|
||||
}
|
||||
|
@ -93,6 +113,264 @@ function morseNot(ditdah) {
|
|||
* @callback TxControl
|
||||
*/
|
||||
|
||||
/**
|
||||
* A straight keyer.
|
||||
*
|
||||
* This is one or more relays wired in parallel. Each relay has an associated
|
||||
* input key. You press any key, and it starts transmitting until all keys are
|
||||
* released.
|
||||
*/
|
||||
class StraightKeyer {
|
||||
/**
|
||||
* @param {TxControl} beginTxFunc Callback to begin transmitting
|
||||
* @param {TxControl} endTxFunc Callback to end transmitting
|
||||
*/
|
||||
constructor(beginTxFunc, endTxFunc) {
|
||||
this.beginTxFunc = beginTxFunc
|
||||
this.endTxFunc = endTxFunc
|
||||
this.Reset()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of names for keys supported by this keyer.
|
||||
*
|
||||
* @returns {Array.<string>} A list of key names
|
||||
*/
|
||||
KeyNames() {
|
||||
return ["Key"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset state and stop all transmissions.
|
||||
*/
|
||||
Reset() {
|
||||
this.endTxFunc()
|
||||
this.txRelays = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of a single transmit relay.
|
||||
*
|
||||
* If n is not provided, return the state of all relays wired in parallel.
|
||||
*
|
||||
* @param {number} n Relay number
|
||||
* @returns {bool} True if relay is closed
|
||||
*/
|
||||
TxClosed(n=null) {
|
||||
if (n == null) {
|
||||
return this.txRelays.some(Boolean)
|
||||
}
|
||||
return this.txRelays[n]
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a transmit relay.
|
||||
*
|
||||
* In most of these keyers, you have multiple things that can transmit. In
|
||||
* the circuit, they'd all be wired together in parallel. We instead keep
|
||||
* track of relay state here, and start or stop transmitting based on the
|
||||
* logical of of all relays.
|
||||
*
|
||||
* @param {number} n Relay number
|
||||
* @param {bool} closed True if relay should be closed
|
||||
*/
|
||||
Tx(n, closed) {
|
||||
let wasClosed = this.TxClosed()
|
||||
this.txRelays[n] = closed
|
||||
let nowClosed = this.TxClosed()
|
||||
|
||||
if (wasClosed != nowClosed) {
|
||||
if (nowClosed) {
|
||||
this.beginTxFunc()
|
||||
} else {
|
||||
this.endTxFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* React to a key being pressed.
|
||||
*
|
||||
* @param {number} key Which key was pressed
|
||||
* @param {bool} pressed True if the key was pressed
|
||||
*/
|
||||
Key(key, pressed) {
|
||||
this.Tx(key, pressed)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A "Cootie" or "Double Speed Key" is just two straight keys in parallel.
|
||||
*/
|
||||
class CootieKeyer extends StraightKeyer {
|
||||
KeyNames() {
|
||||
return ["Key", "Key"]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Vibroplex "Bug".
|
||||
*
|
||||
* Left key send dits over and over until you let go.
|
||||
* Right key works just like a stright key.
|
||||
*/
|
||||
class BugKeyer extends StraightKeyer {
|
||||
KeyNames() {
|
||||
return ["· ", "Key"]
|
||||
}
|
||||
|
||||
Reset() {
|
||||
super.Reset()
|
||||
this.SetDitDuration(100 * Millisecond)
|
||||
if (this.pulseTimer) {
|
||||
clearInterval(this.pulseTimer)
|
||||
this.pulseTimer = null
|
||||
}
|
||||
this.keyPressed = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the duration of dit.
|
||||
*
|
||||
* @param {Duration} d New dit duration
|
||||
*/
|
||||
SetDitDuration(d) {
|
||||
this.ditDuration = d
|
||||
}
|
||||
|
||||
Key(key, pressed) {
|
||||
this.keyPressed[key] = pressed
|
||||
if (key == 0) {
|
||||
this.beginPulsing()
|
||||
} else {
|
||||
super.Key(key, pressed)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a pulse if it hasn't already begun
|
||||
*/
|
||||
beginPulsing() {
|
||||
if (!this.pulseTimer) {
|
||||
this.pulse()
|
||||
}
|
||||
}
|
||||
|
||||
pulse() {
|
||||
if (this.TxClosed(0)) {
|
||||
// If we were transmitting, pause
|
||||
this.Tx(0, false)
|
||||
} else if (this.keyPressed[0]) {
|
||||
// If the key was pressed, transmit
|
||||
this.Tx(0, true)
|
||||
} else {
|
||||
// If the key wasn't pressed, stop pulsing
|
||||
this.pulseTimer = null
|
||||
return
|
||||
}
|
||||
this.pulseTimer = setTimeout(() => this.pulse(), this.ditDuration)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Electronic Bug Keyer
|
||||
*
|
||||
* Repeats both dits and dahs, ensuring proper pauses.
|
||||
*
|
||||
* I think the original ElBug Keyers did not have two paddles, so I've taken the
|
||||
* liberty of making it so that whatever you pressed last is what gets repeated,
|
||||
* similar to a modern computer keyboard.
|
||||
*/
|
||||
class ElBugKeyer extends BugKeyer {
|
||||
KeyNames() {
|
||||
return ["· ", "−"]
|
||||
}
|
||||
|
||||
Reset() {
|
||||
super.Reset()
|
||||
this.lastPressed = -1
|
||||
}
|
||||
|
||||
Key(key, pressed) {
|
||||
this.keyPressed[key] = pressed
|
||||
if (pressed) {
|
||||
this.lastPressed = key
|
||||
} else {
|
||||
this.lastPressed = this.keyPressed.findIndex(Boolean)
|
||||
}
|
||||
this.beginPulsing()
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the duration of the next transmission to send.
|
||||
*
|
||||
* If there is nothing to send, returns 0.
|
||||
*
|
||||
* @returns {Duration} Duration of next transmission
|
||||
*/
|
||||
nextTxDuration() {
|
||||
switch (this.lastPressed) {
|
||||
case 0:
|
||||
return this.ditDuration * DIT
|
||||
case 1:
|
||||
return this.ditDuration * DAH
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pulse() {
|
||||
let nextPulse = 0
|
||||
|
||||
// This keyer only drives one transmit relay
|
||||
if (this.TxClosed()) {
|
||||
// If we're transmitting at all, pause
|
||||
this.Tx(0, false)
|
||||
nextPulse = this.ditDuration
|
||||
} else if (this.keyPressed.some(Boolean)) {
|
||||
// If there's a key down, transmit.
|
||||
//
|
||||
// Wait until here to ask for next duration, so things with memories
|
||||
// don't flush that memory for a pause.
|
||||
this.Tx(0, true)
|
||||
nextPulse = this.nextTxDuration()
|
||||
}
|
||||
|
||||
if (nextPulse) {
|
||||
this.pulseTimer = setTimeout(() => this.pulse(), nextPulse)
|
||||
} else {
|
||||
this.pulseTimer = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Single dot memory keyer.
|
||||
*
|
||||
* If you tap dit while a dah is sending, it queues up a dit to send, even if
|
||||
* the dit key is no longer being held at the start of the next cycle.
|
||||
*/
|
||||
class SingleDotKeyer extends ElBugKeyer {
|
||||
Reset() {
|
||||
super.Reset()
|
||||
this.queue = []
|
||||
}
|
||||
|
||||
Key(key, pressed) {
|
||||
super.Key(key, pressed)
|
||||
if (pressed && (key == 0) && this.keyPressed[1]) {
|
||||
this.queue = [DIT]
|
||||
}
|
||||
}
|
||||
|
||||
nextTxDuration() {
|
||||
if (this.queue.length) {
|
||||
let dits = this.queue.shift()
|
||||
return dits * this.ditDuration
|
||||
}
|
||||
return super.nextTxDuration()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keyer class. This handles iambic and straight key input.
|
||||
*
|
||||
|
@ -101,7 +379,7 @@ function morseNot(ditdah) {
|
|||
* - Typematic: you hold the key down and it repeats evenly-spaced tones
|
||||
* - Typeahead: if you hit a key while it's still transmitting the last-entered one, it queues up your next entered one
|
||||
*/
|
||||
class Keyer {
|
||||
class OldKeyer {
|
||||
/**
|
||||
* Create a Keyer
|
||||
*
|
||||
|
@ -169,7 +447,7 @@ class Keyer {
|
|||
typematic() {
|
||||
if (this.ditDown && this.dahDown) {
|
||||
this.modeBQueue = this.last
|
||||
this.last = morseNot(this.last)
|
||||
this.last = not(this.last)
|
||||
} else if (this.ditDown) {
|
||||
this.modeBQueue = null
|
||||
this.last = DIT
|
||||
|
@ -365,4 +643,4 @@ class Keyer {
|
|||
}
|
||||
}
|
||||
|
||||
export {Keyer}
|
||||
export {StraightKeyer, CootieKeyer, BugKeyer, ElBugKeyer, SingleDotKeyer}
|
||||
|
|
|
@ -67,12 +67,9 @@ export class Vail {
|
|||
this.lagDurations.unshift(now - this.clockOffset - beginTxTime - totalDuration)
|
||||
this.lagDurations.splice(20, 2)
|
||||
this.rx(0, 0, this.stats())
|
||||
console.debug("Vail.wsMessage() SQUELCH", msg)
|
||||
return
|
||||
}
|
||||
|
||||
console.debug("Vail.wsMessage()", this.socket, msg)
|
||||
|
||||
// The very first packet is the server telling us the current time
|
||||
if (durations.length == 0) {
|
||||
if (this.clockOffset == 0) {
|
||||
|
@ -112,7 +109,6 @@ export class Vail {
|
|||
console.error("Not connected, dropping", jmsg)
|
||||
return
|
||||
}
|
||||
console.debug("Transmit", this.socket, msg)
|
||||
this.socket.send(jmsg)
|
||||
if (squelch) {
|
||||
this.sent.push(jmsg)
|
||||
|
|
|
@ -1,14 +1,3 @@
|
|||
.flex {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.mdl-card {
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.key {
|
||||
width: 100%;
|
||||
height: 6em;
|
||||
|
@ -62,10 +51,10 @@ kbd {
|
|||
background-color: #eee;
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 3px;
|
||||
font-size: 9pt;
|
||||
font-size: 66%;
|
||||
padding: .1em .6em;
|
||||
cursor: default;
|
||||
vertical-align: top;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
kbd.gamepad {
|
||||
|
@ -76,10 +65,6 @@ kbd.gamepad {
|
|||
height: 10px;
|
||||
width: 10px;
|
||||
}
|
||||
img.gamepad {
|
||||
height: 1.5em;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #333;
|
||||
|
|
|
@ -25,6 +25,11 @@ function toast(msg) {
|
|||
})
|
||||
}
|
||||
|
||||
// iOS kludge
|
||||
if (!window.AudioContext) {
|
||||
window.AudioContext = window.webkitAudioContext
|
||||
}
|
||||
|
||||
class VailClient {
|
||||
constructor() {
|
||||
this.sent = []
|
||||
|
@ -37,8 +42,9 @@ class VailClient {
|
|||
// Make helpers
|
||||
this.lamp = new Buzzer.Lamp()
|
||||
this.buzzer = new Buzzer.ToneBuzzer()
|
||||
this.keyer = new Keyer.Keyer(() => this.beginTx(), () => this.endTx())
|
||||
this.roboKeyer = new Keyer.Keyer(() => this.Buzz(), () => this.Silence())
|
||||
this.straightKeyer = new Keyer.StraightKeyer(() => this.beginTx(), () => this.endTx())
|
||||
this.keyer = new Keyer.SingleDotKeyer(() => this.beginTx(), () => this.endTx())
|
||||
this.roboKeyer = new Keyer.ElBugKeyer(() => this.Buzz(), () => this.Silence())
|
||||
|
||||
// Set up various input methods
|
||||
// Send this as the keyer so we can intercept dit and dah events for charts
|
||||
|
@ -62,10 +68,10 @@ class VailClient {
|
|||
|
||||
// Set up inputs
|
||||
this.inputInit("#iambic-duration", e => {
|
||||
this.keyer.SetIntervalDuration(e.target.value)
|
||||
this.roboKeyer.SetIntervalDuration(e.target.value)
|
||||
this.keyer.SetDitDuration(e.target.value)
|
||||
this.roboKeyer.SetDitDuration(e.target.value)
|
||||
for (let i of Object.values(this.inputs)) {
|
||||
i.SetIntervalDuration(e.target.value)
|
||||
i.SetDitDuration(e.target.value)
|
||||
}
|
||||
})
|
||||
this.inputInit("#rx-delay", e => {
|
||||
|
@ -103,7 +109,7 @@ class VailClient {
|
|||
* @param down If key has been depressed
|
||||
*/
|
||||
Straight(down) {
|
||||
this.keyer.Straight(down)
|
||||
this.straightKeyer.Key(0, down)
|
||||
if (this.straightChart) this.straightChart.Set(down?1:0)
|
||||
}
|
||||
|
||||
|
@ -113,7 +119,7 @@ class VailClient {
|
|||
* @param down If the key has been depressed
|
||||
*/
|
||||
Dit(down) {
|
||||
this.keyer.Dit(down)
|
||||
this.keyer.Key(0, down)
|
||||
if (this.ditChart) this.ditChart.Set(down?1:0)
|
||||
}
|
||||
|
||||
|
@ -123,7 +129,7 @@ class VailClient {
|
|||
* @param down If the key has been depressed
|
||||
*/
|
||||
Dah(down) {
|
||||
this.keyer.Dah(down)
|
||||
this.keyer.Key(1, down)
|
||||
if (this.dahChart) this.dahChart.Set(down?1:0)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue