mirror of https://github.com/nealey/vail.git
Merge branch 'main' of https://github.com/nealey/vail
This commit is contained in:
commit
9df4c09229
|
@ -8,6 +8,6 @@ case "$1" in
|
|||
docker -H ssh://melville.woozle.org service update --image ghcr.io/nealey/vail:main melville_vail
|
||||
;;
|
||||
"")
|
||||
rsync -va static/ melville.woozle.org:/srv/vail/testing/
|
||||
rsync --delete -va static/ melville.woozle.org:/srv/vail/testing/
|
||||
;;
|
||||
esac
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* @file Provides some base audio tools.
|
||||
*/
|
||||
|
||||
import * as time from "./time.mjs"
|
||||
|
||||
/**
|
||||
* Compute the special "Audio Context" time
|
||||
*
|
||||
|
@ -13,8 +15,8 @@
|
|||
*/
|
||||
function AudioContextTime(context, when) {
|
||||
if (!when) return 0
|
||||
let acOffset = Date.now() - (context.currentTime * Second)
|
||||
return Math.max(when - acOffset, 0) / Second
|
||||
let acOffset = Date.now() - (context.currentTime * time.Second)
|
||||
return Math.max(when - acOffset, 0) / time.Second
|
||||
}
|
||||
|
||||
class AudioSource {
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
/** @typedef {Number} Duration */
|
||||
const Millisecond = 1
|
||||
const Second = 1000 * Millisecond
|
||||
import * as time from "./time.mjs"
|
||||
|
||||
/**
|
||||
* A chart of historical values.
|
||||
|
@ -14,7 +12,7 @@ class HistoryChart {
|
|||
* @param {string} style style to draw in; falls back to the `data-style` attribute
|
||||
* @param {Duration} duration Time to display history for
|
||||
*/
|
||||
constructor(canvas, style=null, duration=20*Second) {
|
||||
constructor(canvas, style=null, duration=20*time.Second) {
|
||||
this.canvas = canvas
|
||||
this.ctx = canvas.getContext("2d")
|
||||
this.duration = duration
|
||||
|
@ -22,7 +20,7 @@ class HistoryChart {
|
|||
this.data = []
|
||||
|
||||
// One canvas pixel = 20ms
|
||||
canvas.width = duration / (20 * Millisecond)
|
||||
canvas.width = duration / (20 * time.Millisecond)
|
||||
|
||||
// Set origin to lower-left corner
|
||||
this.ctx.scale(1, -1)
|
||||
|
@ -113,7 +111,7 @@ class HistoryChart {
|
|||
* @param duration duration of chart window
|
||||
* @returns new chart, or null if it couldn't find a canvas
|
||||
*/
|
||||
function FromSelector(selector, style, duration=20*Second) {
|
||||
function FromSelector(selector, style, duration=20*time.Second) {
|
||||
let canvas = document.querySelector(selector)
|
||||
if (canvas) {
|
||||
return new HistoryChart(canvas, style, duration)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import * as RoboKeyer from "./robokeyer.mjs"
|
||||
import * as time from "./time.mjs"
|
||||
|
||||
/** Silent period between dits and dash */
|
||||
const PAUSE = -1
|
||||
|
@ -15,24 +16,6 @@ const DIT = 1
|
|||
/** Length 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
|
||||
|
||||
|
||||
/**
|
||||
* Queue Set: A Set you can shift and pop.
|
||||
*
|
||||
|
@ -185,7 +168,7 @@ class BugKeyer extends StraightKeyer {
|
|||
|
||||
Reset() {
|
||||
super.Reset()
|
||||
this.SetDitDuration(100 * Millisecond)
|
||||
this.SetDitDuration(100 * time.Millisecond)
|
||||
if (this.pulseTimer) {
|
||||
clearInterval(this.pulseTimer)
|
||||
this.pulseTimer = null
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {AudioSource, AudioContextTime} from "./audio.mjs"
|
||||
import * as time from "./time.mjs"
|
||||
|
||||
const HIGH_FREQ = 555
|
||||
const LOW_FREQ = 444
|
||||
|
@ -19,36 +20,33 @@ const LOW_FREQ = 444
|
|||
* @typedef {number} Date
|
||||
*/
|
||||
|
||||
const Millisecond = 1
|
||||
const Second = 1000 * Millisecond
|
||||
|
||||
/** The amount of time it should take an oscillator to ramp to and from zero gain
|
||||
*
|
||||
* @constant {Duration}
|
||||
*/
|
||||
const OscillatorRampDuration = 5*Millisecond
|
||||
const OscillatorRampDuration = 5*time.Millisecond
|
||||
|
||||
|
||||
class Oscillator extends AudioSource {
|
||||
/**
|
||||
* Create a new oscillator, and encase it in a Gain for control.
|
||||
*
|
||||
* @param {AudioContext} context Audio context
|
||||
* @param {AudioContext} context Audio context
|
||||
* @param {number} frequency Oscillator frequency (Hz)
|
||||
* @param {number} maxGain Maximum gain (volume) of this oscillator (0.0 - 1.0)
|
||||
* @param {string} type Oscillator type
|
||||
*/
|
||||
constructor(context, frequency, maxGain = 0.5, type = "sine") {
|
||||
super(context)
|
||||
super(context)
|
||||
this.maxGain = maxGain
|
||||
|
||||
// Start quiet
|
||||
this.masterGain.gain.value = 0
|
||||
// Start quiet
|
||||
this.masterGain.gain.value = 0
|
||||
|
||||
this.osc = new OscillatorNode(this.context)
|
||||
this.osc.type = type
|
||||
this.osc.type = type
|
||||
this.osc.connect(this.masterGain)
|
||||
this.setFrequency(frequency)
|
||||
this.setFrequency(frequency)
|
||||
this.osc.start()
|
||||
}
|
||||
|
||||
|
@ -88,7 +86,7 @@ class Oscillator extends AudioSource {
|
|||
this.masterGain.gain.setTargetAtTime(
|
||||
target,
|
||||
AudioContextTime(this.context, when),
|
||||
timeConstant/Second,
|
||||
timeConstant/time.Second,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -227,7 +225,7 @@ class AudioBuzzer extends Buzzer {
|
|||
Error() {
|
||||
let now = Date.now()
|
||||
this.errorTone.SoundAt(now)
|
||||
this.errorTone.HushAt(now + 200*Millisecond)
|
||||
this.errorTone.HushAt(now + 200*time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import {GetFortune} from "./fortunes.mjs"
|
||||
|
||||
const Millisecond = 1
|
||||
const Second = 1000 * Millisecond
|
||||
const Minute = 60 * Second
|
||||
import * as time from "./time.mjs"
|
||||
|
||||
/**
|
||||
* Compare two messages
|
||||
|
@ -61,7 +58,7 @@ export class Vail {
|
|||
msg => {
|
||||
this.rx(0, 0, {connected: false, notice: `Repeater disconnected: ${msg.reason}`})
|
||||
console.error("Repeater connection dropped:", msg.reason)
|
||||
setTimeout(() => this.reopen(), 2*Second)
|
||||
setTimeout(() => this.reopen(), 2*time.Second)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -157,7 +154,7 @@ export class Vail {
|
|||
}
|
||||
|
||||
export class Null {
|
||||
constructor(rx, interval=3*Second) {
|
||||
constructor(rx, interval=3*time.Second) {
|
||||
this.rx = rx
|
||||
this.init()
|
||||
}
|
||||
|
@ -219,7 +216,7 @@ export class Fortune extends Null {
|
|||
if (this.timeout) {
|
||||
clearTimeout(this.timeout)
|
||||
}
|
||||
this.timeout = setTimeout(() => this.pulse(), 3 * Second)
|
||||
this.timeout = setTimeout(() => this.pulse(), 3 * time.Second)
|
||||
}
|
||||
|
||||
Close() {
|
||||
|
|
|
@ -6,15 +6,13 @@
|
|||
*
|
||||
* @typedef {number} Duration
|
||||
*/
|
||||
|
||||
/** @type {Duration} */
|
||||
export const Millisecond = 1
|
||||
|
||||
const Millisecond = 1
|
||||
/** @type {Duration} */
|
||||
export const Second = 1000 * Millisecond
|
||||
|
||||
const Second = 1000 * Millisecond
|
||||
/** @type {Duration} */
|
||||
export const Minute = 60 * Second
|
||||
|
||||
const Minute = 60 * Second
|
||||
/** @type {Duration} */
|
||||
export const Hour = 60 * Minute
|
||||
const Hour = 60 * Minute
|
||||
|
||||
export {Millisecond, Second, Minute, Hour}
|
|
@ -4,12 +4,10 @@ import * as Inputs from "./inputs.mjs"
|
|||
import * as Repeaters from "./repeaters.mjs"
|
||||
import * as Chart from "./chart.mjs"
|
||||
import * as I18n from "./i18n.mjs"
|
||||
import * as time from "./time.mjs"
|
||||
import * as Music from "./music.mjs"
|
||||
|
||||
const DefaultRepeater = "General"
|
||||
const Millisecond = 1
|
||||
const Second = 1000 * Millisecond
|
||||
const Minute = 60 * Second
|
||||
|
||||
console.warn("Chrome will now complain about an AudioContext not being allowed to start. This is normal, and there is no way to make Chrome stop complaining about this.")
|
||||
const globalAudioContext = new AudioContext({
|
||||
|
@ -21,7 +19,7 @@ const globalAudioContext = new AudioContext({
|
|||
*
|
||||
* @param {string} msg Message to display
|
||||
*/
|
||||
function toast(msg, timeout=4*Second) {
|
||||
function toast(msg, timeout=4*time.Second) {
|
||||
console.info(msg)
|
||||
|
||||
let errors = document.querySelector("#errors")
|
||||
|
@ -41,7 +39,7 @@ class VailClient {
|
|||
this.lagTimes = [0]
|
||||
this.rxDurations = [0]
|
||||
this.clockOffset = null // How badly our clock is off of the server's
|
||||
this.rxDelay = 0 * Millisecond // Time to add to incoming timestamps
|
||||
this.rxDelay = 0 * time.Millisecond // Time to add to incoming timestamps
|
||||
this.beginTxTime = null // Time when we began transmitting
|
||||
|
||||
// Outputs
|
||||
|
@ -76,7 +74,7 @@ class VailClient {
|
|||
this.inputInit("#keyer-mode", e => this.setKeyer(e.target.value))
|
||||
this.inputInit("#keyer-rate", e => {
|
||||
let rate = e.target.value
|
||||
this.ditDuration = Math.round(Minute / rate / 50)
|
||||
this.ditDuration = Math.round(time.Minute / rate / 50)
|
||||
for (let e of document.querySelectorAll("[data-fill='keyer-ms']")) {
|
||||
e.textContent = this.ditDuration
|
||||
}
|
||||
|
@ -85,7 +83,7 @@ class VailClient {
|
|||
this.inputs.SetDitDuration(this.ditDuration)
|
||||
})
|
||||
this.inputInit("#rx-delay", e => {
|
||||
this.rxDelay = e.target.value * Second
|
||||
this.rxDelay = e.target.value * time.Second
|
||||
})
|
||||
this.inputInit("#masterGain", e => {
|
||||
this.outputs.SetGain(e.target.value / 100)
|
||||
|
@ -464,7 +462,7 @@ class VailClient {
|
|||
|
||||
function init() {
|
||||
if (navigator.serviceWorker) {
|
||||
navigator.serviceWorker.register("sw.js")
|
||||
navigator.serviceWorker.register("scripts/sw.js")
|
||||
}
|
||||
I18n.Setup()
|
||||
try {
|
||||
|
|
Loading…
Reference in New Issue