diff --git a/static/vail.js b/static/vail.js index 3d4bbd8..ead8c0e 100644 --- a/static/vail.js +++ b/static/vail.js @@ -11,8 +11,8 @@ class Iambic { constructor(beginTxFunc, endTxFunc) { this.beginTxFunc = beginTxFunc this.endTxFunc = endTxFunc - this.interval = null - this.state = this.stateSpace + this.intervalDuration = null + this.state = this.stateBegin this.keyState = null } @@ -21,9 +21,12 @@ class Iambic { * * @param {number} duration New interval duration, in ms */ - SetInterval(duration) { - clearInterval(this.interval) - this.interval = setInterval(e => this.pulse(), duration) + SetIntervalDuration(duration) { + this.intervalDuration = duration + if (this.interval) { + clearInterval(this.interval) + this.interval = setInterval(e => this.pulse(), duration) + } } // An interval has passed, call whatever the current state function is @@ -31,9 +34,17 @@ class Iambic { this.state() } - stateSpace() { - // Don't transmit for one interval. - this.state = this.keyState || this.stateSpace + + stateBegin() { + if (this.keyState) { + // Don't transmit for one interval. + this.state = this.keyState + this.state() + } else { + // No key pressed, go back to sleep. + clearInterval(this.interval) + this.interval = null + } } stateDit() { // Send a dit @@ -54,8 +65,7 @@ class Iambic { stateEnd() { // Stop sending this.endTxFunc() - this.state = this.stateSpace - this.state() + this.state = this.stateBegin } /** @@ -80,6 +90,12 @@ class Iambic { // Only stop when we've released the right key this.keyState = null } + + // Not pulsing yet? Start right away! + if (! this.interval) { + this.interval = setInterval(e => this.pulse(), this.intervalDuration) + this.pulse() + } } } @@ -226,12 +242,16 @@ class Vail { this.buzzer = new Buzzer() // Listen for slider values - this.inputInit("#iambic-duration", e => this.iambic.SetInterval(e.target.value)) + this.inputInit("#iambic-duration", e => this.iambic.SetIntervalDuration(e.target.value)) this.inputInit("#rx-delay", e => {this.rxDelay = Number(e.target.value)}) // Show what repeater we're on let repeater = (new URL(location)).searchParams.get("repeater") || "Default" document.querySelector("#repeater").textContent = repeater + + // Request MIDI access + navigator.requestMIDIAccess() + .then(a => this.midiInit(a)) } inputInit(selector, func) { @@ -250,6 +270,51 @@ class Vail { }) element.dispatchEvent(new Event("input")) } + + midiInit(access) { + this.midiAccess = access + for (let input of this.midiAccess.inputs.values()) { + input.addEventListener("midimessage", e => this.midiMessage(e)) + } + this.midiAccess.addEventListener("statechange", e => this.midiStateChange(e)) + } + + midiStateChange(event) { + if (event.port.connection == "open") { + console.log(event) + event.port.addEventListiner("midimessage", e => this.midiMessage(e)) + } + } + + midiMessage(event) { + let data = Array.from(event.data) + + let begin + switch (Number(data[0])) { + case 0x90: + begin = true + break + case 0x80: + begin = false + break + default: + return + } + + switch (data[1] % 12) { + case 0: // C + this.straightKey(begin) + break + case 1: // C# + this.iambic.Key(begin, DIT) + break + case 2: // D + this.iambic.Key(begin, DAH) + break + default: + return + } + } error(msg) { let now = new Date() @@ -318,7 +383,14 @@ class Vail { wsMessage(event) { let now = Date.now() let jmsg = event.data - let msg = JSON.parse(jmsg) + let msg + try { + msg = JSON.parse(jmsg) + } + catch(err) { + console.log(err, msg) + return + } let beginTxTime = msg[0] let durations = msg.slice(1) @@ -352,6 +424,14 @@ class Vail { } } + straightKey(begin) { + if (begin) { + this.beginTx() + } else { + this.endTx() + } + } + key(event) { if (event.repeat) { // Ignore key repeats generated by the OS, we do this ourselves @@ -388,11 +468,7 @@ class Vail { } else if (event.target.id == "dit") { this.iambic.Key(begin, DIT) } else if (event.target.id == "key") { - if (begin) { - this.beginTx() - } else { - this.endTx() - } + this.straightKey(begin) } else if (event.target.id == "ck") { this.Test() }