diff --git a/static/index.html b/static/index.html index afb97b9..5d59a53 100644 --- a/static/index.html +++ b/static/index.html @@ -2,138 +2,178 @@ Vail + + + + + + + + + -
- -
- -
- - - - - - - - - -
Dot,, w, left mouse button
Dash., v, right mouse button
-
- -
-
-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 : --..
-0 : -----
-1 : .----
-2 : ..---
-3 : ...--
-4 : ....-
-5 : .....
-6 : -....
-7 : --...
-8 : ---..
-9 : ----.
-. : .-.-.-
-, : --..--
-: : ---...
-? : ..--..
-' : .----.
-- : -....-
-/ : -..-.
-" : .-..-.
-@ : .--.-.
-= : -...-
-! : -.-.--
-      
-
- -
-

Vail

- -

- This is a CW repeater, - named after Alfred Vail, - who may or may not have invented what's called "Morse code", - but clearly had some role in it. -

- -

- Just like a radio repeater, - anybody can connect and start transmitting stuff, - and this will broadcast it to everyone connected. - If there's enough interest, - I'll add something like channels. -

- -

- If you need this to work on a cell phone, - let me know and I'll come up with something for you. -

-
- -
-

Why does this exist?

- -

- I need a place to practice CW with actual human beings, - and I want it to be as close as possible to what I'd experience on a radio. - Also, I don't want to make people buy a bunch of radio hardware. - Nothing else like this exists on the Internet, as far as I can tell. -

- -

Who made it?

- -

- Neale Pickett kd7oqi -

- -

Future plans

- - - - -

How can I help?

- - +
+
+
+ + Vail + +
+ + +
+
+
+ Title + +
+
+
+
+
+

+ Code Tree +

+
+
+ +
+
+ +
+
+

+ Input +

+
+
+ +
+ + + + + + + + + +
+ + + +
+ . or + + / or z +
+
+
+ +
+
+
+ +
+
+

+ Vail +

+
+
+

+ This is a CW repeater, + named after Alfred Vail, + who may or may not have invented what's called "Morse code", + but clearly had some role in it. +

+ +

+ Just like a radio repeater, + anybody can connect and start transmitting stuff, + and this will broadcast it to everyone connected. + If there's enough interest, + I'll add something like channels. +

+ +

+ If you need this to work on a cell phone, + let me know and I'll come up with something for you. +

+
+
+ +
+
+

+ Why Does This Exist? +

+
+
+

+ I need a place to practice CW with actual human beings, + and I want it to be as close as possible to what I'd experience on a radio. + Also, I don't want to make people buy a bunch of radio hardware. + Nothing else like this exists on the Internet, as far as I can tell. +

+ +

Who made it?

+ +

+ Neale Pickett kd7oqi +

+ +
+
+ +
+
+

Future plans

+
+
+
    +
  • Move to a more permanent URL
  • +
  • Make this page less ugly
  • +
  • Arduino program to let you hook up an iambic paddle over USB
  • +
  • Document the protocol
  • +
  • Support multiple channels/frequencies
  • +
  • Sensible way to make this work on a cell phone
  • +
  • Make this page less ugly (I really hate it right now)
  • +
+ + +

How can I help?

+ + +
+
+
+
diff --git a/static/vail.css b/static/vail.css index c776b58..4e6d624 100644 --- a/static/vail.css +++ b/static/vail.css @@ -1,8 +1,13 @@ -body { - background-color: #ccc; - font-family: sans-serif; +.flex { display: flex; flex-wrap: wrap; + align-items: flex-start; + justify-content: space-around; +} + +.key { + width: 100%; + height: 6em; } code { diff --git a/static/vail.js b/static/vail.js index 8753531..45ec6b7 100644 --- a/static/vail.js +++ b/static/vail.js @@ -1,46 +1,89 @@ -var ac = new AudioContext() -var gain = ac.createGain() -gain.connect(ac.destination) -gain.gain.value = 0.1 +// jshint asi:true var short = 80 var long = 200 var audioFreq = 660 var audioFreqMe = audioFreq * 6 / 5 // I think this works out to a minor third -var myosc + +var ac = new AudioContext() + +var mygain = ac.createGain() +mygain.connect(ac.destination) +mygain.gain.value = 0 + +var myosc = ac.createOscillator() +myosc.connect(mygain) +myosc.frequency.value = audioFreqMe +myosc.start() + +var theirgain = ac.createGain() +theirgain.connect(ac.destination) +theirgain.gain.value = 0 + +var theirosc = ac.createOscillator() +theirosc.connect(theirgain) +theirosc.frequency.value = audioFreq +theirosc.start() + +var repeatInterval + function message(event) { + let now = ac.currentTime let duration = Number(event.data) || 0 duration = Math.min(duration, long) - let osc = ac.createOscillator() - osc.connect(gain) - osc.frequency.value = audioFreq - osc.start(ac.currentTime) - osc.stop(ac.currentTime + (duration * 0.001)) + if (now === 0) { + // Audio Context hasn't started, we can't make sound yet + return + } + + theirgain.gain.linearRampToValueAtTime(0.1, now + 0.01) + mygain.gain.setValueAtTime(0.1, now + duration/1000) + theirgain.gain.linearRampToValueAtTime(0.0, now + 0.01 + duration/1000) +} + +function send(duration) { + let now = ac.currentTime + window.socket.send(duration) + + if (now === 0) { + return + } + + mygain.gain.linearRampToValueAtTime(0.1, now + 0.01) + mygain.gain.setValueAtTime(0.1, now + duration/1000) + mygain.gain.linearRampToValueAtTime(0.0, now + 0.01 + duration/1000) } function key(event) { let duration = 0 + + ac.resume() - if ((event.button === 0) || (event.key == ",") || (event.key == "w")) { - duration = short - } - if ((event.button === 2) || (event.key == ".") || (event.key == "v")) { - duration = long - } - // You don't get to hold the key down yet, sorry - if ((event.repeat) || (duration === 0)) { + if (event.repeat) { + // Ignore key repeats generated by the OS, we do this ourselves return } - window.socket.send(duration) + if ((event.button === 0) || (event.code == "Period") || (event.key == "Shift")) { + duration = short + } + if ((event.button === 2) || (event.code == "Slash") || (event.code == "KeyZ")) { + duration = long + } + if (duration === 0) { + return + } + + if (repeatInterval) { + clearInterval(repeatInterval) + } - myosc = ac.createOscillator() - myosc.connect(gain) - myosc.frequency.value = audioFreqMe - myosc.start(ac.currentTime) - myosc.stop(ac.currentTime + duration * 0.001) + if (event.type.endsWith("down")) { + send(duration) + repeatInterval = setInterval(() => {send(duration)}, duration + short) + } } function canWeJustNot(event) { @@ -58,7 +101,9 @@ function init() { // disable RMB context menu document.addEventListener("contextmenu", e => canWeJustNot(e)) document.addEventListener("mousedown", e => key(e)) + document.addEventListener("mouseup", e => key(e)) document.addEventListener("keydown", e => key(e)) + document.addEventListener("keyup", e => key(e)) }