vail/static/scripts/keyer.js

255 lines
8.7 KiB
JavaScript
Raw Normal View History

2024-10-25 13:16:42 -06:00
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();
}
}
}