mirror of https://github.com/nealey/vail.git
255 lines
8.7 KiB
JavaScript
255 lines
8.7 KiB
JavaScript
class Keyer {
|
|
constructor(sndr, decoder) {
|
|
this.sndr = sndr; // Sounder instance
|
|
this.decoder = decoder; // Decoder instance
|
|
// Using [ and ] for dits and dahs respectively for compatibility with the vband usb interface
|
|
this.ditKey1 = 'ControlLeft';
|
|
this.dahKey1 = 'ControlRight';
|
|
this.ditKey2 = 'BracketLeft';
|
|
this.dahKey2 = 'BracketRight';
|
|
this.wpm = 20;
|
|
this.unit = 60; // length of dit in milliseconds; 60 is 20wpm
|
|
this.mode = 2; // 1: straight key, 2: iambicA, 3: iambicB, 4: ultimatic
|
|
this.tone = 550;
|
|
this.queue = [];
|
|
this.ditKeyState = 0;
|
|
this.dahKeyState = 0;
|
|
this.lastKey = null;
|
|
this.ditStreak = 0;
|
|
this.dahStreak = 0;
|
|
this.streak = 0;
|
|
this.ditStart = null;
|
|
this.ditStop = null;
|
|
this.dahStart = null;
|
|
this.dahStop = null;
|
|
this.sending = false;
|
|
this.lastSendTimestamp = null;
|
|
this.oscillatorTimer = setInterval(() => {
|
|
this.oscillate();
|
|
}, 0);
|
|
}
|
|
|
|
setWpm(wpm){
|
|
this.wpm = wpm;
|
|
this.unit = 60000 / (wpm * 50) // based on the PARIS method 60 seconds / 50 elements per word * WPM
|
|
this.decoder.unit = this.unit;
|
|
}
|
|
|
|
setMode(mode){
|
|
this.mode = mode;
|
|
}
|
|
|
|
setTone(tone){
|
|
this.tone = tone;
|
|
}
|
|
|
|
sendSignal() {
|
|
this.sending = true;
|
|
//console.log('startSignal');
|
|
if (restartAudioNeeded()) {
|
|
restartAudio();
|
|
}
|
|
this.sndr.setTone(this.tone);
|
|
this.sndr.on();
|
|
this.decoder.keyOn();
|
|
}
|
|
|
|
stopSignal() {
|
|
//console.log('stopSignal');
|
|
this.sndr.off();
|
|
this.decoder.keyOff();
|
|
this.lastSendTimestamp = Date.now();
|
|
setTimeout(() => {
|
|
this.sending = false;
|
|
}, this.unit);
|
|
}
|
|
|
|
press(event, down, mode=this.mode) {
|
|
if (mode > 1 && event.code != this.ditKey1 && event.code != this.dahKey1 && event.code != this.ditKey2 && event.code != this.dahKey2) return;
|
|
if (mode == 1) {
|
|
if (down) {
|
|
if (restartAudioNeeded()) {
|
|
restartAudio();
|
|
}
|
|
this.sndr.setTone(this.tone);
|
|
this.sndr.on();
|
|
this.decoder.keyOn();
|
|
|
|
} else {
|
|
this.sndr.off();
|
|
this.decoder.keyOff();
|
|
}
|
|
} else if (mode > 1) {
|
|
//console.log(key);
|
|
if (event.code == this.ditKey1 || event.code == this.ditKey2) {
|
|
if (down) { // dit key down
|
|
this.ditKeyState = 1;
|
|
this.ditStart = Date.now()
|
|
} else { // dit key up
|
|
this.ditKeyState = 0;
|
|
this.ditStop = Date.now()
|
|
}
|
|
}
|
|
if (event.code == this.dahKey1 || event.code == this.dahKey2) {
|
|
if (down) { // dah key down
|
|
this.dahKeyState = 1;
|
|
this.dahStart = Date.now()
|
|
} else { // dah key up
|
|
this.dahKeyState = 0;
|
|
this.dahStop = Date.now()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
processQueue() {
|
|
//console.log('processQueue');
|
|
if (!this.sending && this.queue.length) {
|
|
this.lastKey = this.queue.shift();
|
|
var signalLength = (this.lastKey == 1) ? this.unit : this.unit * 3;
|
|
this.sendSignal();
|
|
setTimeout(() => {
|
|
this.stopSignal();
|
|
}, signalLength);
|
|
}
|
|
}
|
|
|
|
oscillatev1() {
|
|
if (!this.ditKeyState && !this.dahKeyState) {
|
|
this.queue = [];
|
|
}
|
|
if (this.ditKeyState) {
|
|
if (this.queue.length == 0) {
|
|
if (!this.dahKeyState && !this.sending || this.lastKey == 2) {
|
|
this.queue.push(1);
|
|
}
|
|
}
|
|
}
|
|
if (this.dahKeyState) {
|
|
if (this.queue.length == 0) {
|
|
if (!this.ditKeyState && !this.sending || this.lastKey == 1) {
|
|
this.queue.push(2);
|
|
}
|
|
}
|
|
}
|
|
if (!this.sending && Date.now() - this.lastSendTimestamp > this.unit) {
|
|
this.processQueue();
|
|
}
|
|
}
|
|
|
|
oscillatev2() {
|
|
if (this.mode == 2 && !this.ditKeyState && !this.dahKeyState) { // Iambic B doesn't clear the queue
|
|
if (this.streak > 1) {
|
|
//console.log(this.streak + " queue: " + this.queue[0]);
|
|
this.queue = [];
|
|
}
|
|
this.streak = 0;
|
|
}
|
|
if (this.ditKeyState) {
|
|
if (this.queue.length < 1) {
|
|
if (!this.sending || this.lastKey == 2) {
|
|
this.queue.push(1);
|
|
if (this.lastKey == 2 && this.dahKeyState) {
|
|
this.streak++;
|
|
} else {
|
|
this.streak = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (this.dahKeyState) {
|
|
if (this.queue.length < 1) {
|
|
if (!this.sending || this.lastKey == 1) {
|
|
this.queue.push(2);
|
|
if (this.lastKey == 1 && this.ditKeyState) {
|
|
this.streak++;
|
|
} else {
|
|
this.streak = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!this.sending && Date.now() - this.lastSendTimestamp >= this.unit) {
|
|
this.processQueue();
|
|
}
|
|
}
|
|
|
|
oscillatev3() {
|
|
if (this.mode == 2 && !this.ditKeyState && !this.dahKeyState && this.queue.length) { // Iambic B doesn't clear the queue
|
|
if ((this.ditStreak && this.queue[0] == 1) || (this.dahStreak && this.queue[0] == 2)) {
|
|
console.log("ditStreak: "+this.ditStreak+" dahStreak: "+this.dahStreak+" queue: "+this.queue[0]);
|
|
this.queue = [];
|
|
}
|
|
//console.log("NO CLEAR ditStreak: "+this.ditStreak+" dahStreak: "+this.dahStreak+" queue: "+this.queue[0]);
|
|
this.ditStreak = 0;
|
|
this.dahStreak = 0;
|
|
}
|
|
if (this.ditKeyState) {
|
|
if (!this.queue.length) {
|
|
if (!this.sending || this.lastKey == 2) {
|
|
this.queue.push(1);
|
|
this.ditStreak++;
|
|
}
|
|
} else if (!this.dahKeyState && this.queue[0] == 2) { // dah was canceled. Replace in queue
|
|
this.queue[0] = 1;
|
|
this.dahStreak = 0;
|
|
this.ditStreak++;
|
|
}
|
|
}
|
|
if (this.dahKeyState) {
|
|
if (!this.queue.length) {
|
|
if (!this.sending || this.lastKey == 1) {
|
|
this.queue.push(2);
|
|
this.dahStreak++;
|
|
}
|
|
} else if (!this.ditKeyState && this.queue[0] == 1) { // dit was canceled. Replace in queue
|
|
this.queue[0] = 2;
|
|
this.ditStreak = 0;
|
|
this.dahStreak++;
|
|
}
|
|
}
|
|
if (!this.sending && Date.now() - this.lastSendTimestamp >= this.unit) {
|
|
this.processQueue();
|
|
}
|
|
}
|
|
|
|
oscillate() {
|
|
if (this.mode == 2 && !this.ditKeyState && !this.dahKeyState && this.queue.length) {
|
|
if (this.queue[0] == 1) { // Dit is in the queue
|
|
if (this.ditStart < this.dahStart || this.ditStop - this.ditStart > this.unit * 4) {
|
|
this.queue.pop();
|
|
}
|
|
} else { // Dah is in the queue
|
|
if (this.dahStart < this.ditStart || this.dahStop - this.dahStart > this.unit * 2) {
|
|
this.queue.pop();
|
|
}
|
|
}
|
|
}
|
|
if (this.ditKeyState) {
|
|
if (this.queue.length == 0) {
|
|
if ((!this.dahKeyState && !this.sending) || this.lastKey == 2) {
|
|
this.queue.push(1);
|
|
}
|
|
} else { // dah key was lifted and is still in queue
|
|
if (this.mode == 2 && !this.dahKeyState && this.dahStart < this.ditStart && this.queue[0] == 2) {
|
|
this.queue.pop();
|
|
}
|
|
}
|
|
}
|
|
if (this.dahKeyState) {
|
|
if (this.queue.length == 0) {
|
|
if ((!this.ditKeyState && !this.sending) || this.lastKey == 1) {
|
|
this.queue.push(2);
|
|
}
|
|
} else { // dit key was lifted and is still in queue
|
|
if (this.mode == 2 && !this.ditKeyState && this.ditStart < this.dahStart && this.queue[0] == 1) {
|
|
this.queue.pop();
|
|
}
|
|
}
|
|
}
|
|
if (!this.sending && Date.now() - this.lastSendTimestamp > this.unit) {
|
|
this.processQueue();
|
|
}
|
|
}
|
|
}
|