diff --git a/mappings/numark-mixtrackplatinum.json b/mappings/numark-mixtrackplatinum.json index 0ef1062..b537ce2 100644 --- a/mappings/numark-mixtrackplatinum.json +++ b/mappings/numark-mixtrackplatinum.json @@ -4,18 +4,50 @@ "bf 44 [0-9a-f]{2} 45 ([0-9a-f]{2})": "master.volume.right" }, "outbound": { - "deck0.load": [144, 16, 127], - "deck1.load": [145, 16, 127], - "deck2.load": [146, 16, 127], - "deck3.load": [147, 16, 127], - "deck0.play_pause": [144, 4, 127], - "deck1.play_pause": [145, 4, 127], - "deck2.play_pause": [146, 4, 127], - "deck3.play_pause": [147, 4, 127], + "deck0.load": [144, 16, 127, 144, 16, 0], + "deck1.load": [145, 16, 127, 145, 16, 0], + "deck2.load": [146, 16, 127, 146, 16, 0], + "deck3.load": [147, 16, 127, 147, 16, 0], + "deck0.play_pause": [144, 4, 127, 144, 4, 0], + "deck1.play_pause": [145, 4, 127, 145, 4, 0], + "deck2.play_pause": [146, 4, 127, 146, 4, 0], + "deck3.play_pause": [147, 4, 127, 147, 4, 0], + "deck0.sync": [144, 2, 127, 144, 2, 0], + "deck1.sync": [145, 2, 127, 145, 2, 0], + "deck2.sync": [146, 2, 127, 146, 2, 0], + "deck3.sync": [147, 2, 127, 147, 2, 0], + "deck0.pad0": [159, 33, 127, 159, 33, 0], + "deck0.pad1": [159, 34, 127, 159, 34, 0], + "deck0.pad2": [159, 35, 127, 159, 35, 0], + "deck0.pad3": [159, 36, 127, 159, 36, 0], + "deck0.pad4": [148, 24, 127, 148, 24, 0], + "deck0.pad5": [148, 25, 127, 148, 25, 0], + "deck0.pad6": [148, 26, 127, 148, 26, 0], + "deck0.pad7": [148, 27, 127, 148, 27, 0], + "deck1.pad0": [159, 33, 127, 159, 33, 0], + "deck1.pad1": [159, 34, 127, 159, 34, 0], + "deck1.pad2": [159, 35, 127, 159, 35, 0], + "deck1.pad3": [159, 36, 127, 159, 36, 0], + "deck1.pad4": [149, 24, 127, 148, 24, 0], + "deck1.pad5": [149, 25, 127, 148, 25, 0], + "deck1.pad6": [149, 26, 127, 148, 26, 0], + "deck1.pad7": [149, 27, 127, 148, 27, 0], "deck0.volume": [176, 28, ""], "deck1.volume": [177, 28, ""], "deck2.volume": [178, 28, ""], "deck3.volume": [179, 28, ""], + "deck0.eq_high": [176, 23, ""], + "deck0.eq_mid": [176, 24, ""], + "deck0.eq_low": [176, 25, ""], + "deck1.eq_high": [177, 23, ""], + "deck1.eq_mid": [177, 24, ""], + "deck1.eq_low": [177, 25, ""], + "deck2.eq_high": [178, 23, ""], + "deck2.eq_mid": [178, 24, ""], + "deck2.eq_low": [178, 25, ""], + "deck3.eq_high": [179, 23, ""], + "deck3.eq_mid": [179, 24, ""], + "deck3.eq_low": [179, 25, ""], "deck0.speed": [176, 9, "", 176, 119, ""], "deck1.speed": [177, 9, "", 177, 119, ""], "deck2.speed": [178, 9, "", 178, 119, ""], diff --git a/preload.js b/preload.js index 3f171ef..5b34262 100644 --- a/preload.js +++ b/preload.js @@ -14,12 +14,21 @@ electron.contextBridge.exposeInMainWorld("controller", { "play_pause": function (deckIndex) { sendControl("deck" + String(deckIndex) + ".play_pause"); }, + "sync": function (deckIndex) { + sendControl("deck" + String(deckIndex) + ".sync"); + }, "volume": function (deckIndex, value) { sendControl("deck" + String(deckIndex) + ".volume", value); }, + "eq": function (deckIndex, freq, value) { + sendControl("deck" + String(deckIndex) + ".eq_" + freq, value); + }, "speed": function (deckIndex, value) { sendControl("deck" + String(deckIndex) + ".speed", value); }, + "pad": function (deckIndex, padIndex) { + sendControl("deck" + String(deckIndex) + ".pad" + String(padIndex)); + }, "crossfade": function (value) { sendControl("master.crossfade", value); } diff --git a/view/assets/scripts/knobs.js b/view/assets/scripts/knobs.js new file mode 100644 index 0000000..d749106 --- /dev/null +++ b/view/assets/scripts/knobs.js @@ -0,0 +1,148 @@ +class KnobElement extends HTMLElement { + static observedAttributes = ["min", "max", "value", "step"]; + + _min = -1; + rawValue = 0; + _value = 0; + step = 1; + _max = 1; + + constructor() { + super(); + } + connectedCallback() { + // if ("min" in this.attributes) { + // this._min = parseFloat(this.attributes["min"].value); + // } + // if ("max" in this.attributes) { + // this._max = parseFloat(this.attributes["max"].value); + // } + if ("value" in this.attributes) { + this.value = parseFloat(this.attributes["value"].value); + } + // if ("step" in this.attributes) { + // this.step = parseFloat(this.attributes["step"].value); + // } + + var shadow = this.attachShadow({ + "mode": "open" + }); + + var style = document.createElement("style"); + style.textContent = ` + #wrapper { + background-color: rgba(24, 24, 24, 1.0); + width: 40px; + height: 40px; + border-radius: 100%; + } + + #cursor { + position: relative; + background-color: rgba(232, 232, 232, 1.0); + width: 4px; + height: 16px; + left: calc(50% - 2px); + } + `; + + var wrapper = document.createElement("div"); + wrapper.id = "wrapper"; + + wrapper.onmousedown = (function (event) { + window.onmousemove = (function (event) { + this.rawValue += event.movementX / 2; + if (this.rawValue < -128) { + this.rawValue = -128; + } + if (this.rawValue > 127) { + this.rawValue = 127; + } + var range = this._max - this._min; + var theoreticalValue = (((this.rawValue + Math.abs(-128)) * range) / 255) + this._min; + + var invertedStep = 1 / this.step; + this._value = Math.round(theoreticalValue * invertedStep) / invertedStep; + + + this.wrapper.style.rotate = String((this.rawValue * 135) / 128) + "deg"; + + var inputEvent = new InputEvent("input"); + this.dispatchEvent(inputEvent); + }).bind(this); + + window.onmouseup = function (event) { + window.onmousemove = null; + window.onmouseup = null; + }; + }).bind(this); + + var cursor = document.createElement("div"); + cursor.id = "cursor"; + + wrapper.appendChild(cursor); + + shadow.appendChild(style); + shadow.appendChild(wrapper); + + this.wrapper = wrapper; + } + attributeChangedCallback(name, oldValue, newValue) { + this[name] = parseFloat(newValue); + } + get min() { + return this._min; + } + set min(value) { + if (isNaN(value)) return; + + this._min = value; + + if (this._value < this._min) { + this._value = this._min; + } + + this.rawValue = (((this._value + Math.abs(this._min)) * 255) / (this._max - this._min)) + -128; + + if ("wrapper" in this) + this.wrapper.style.rotate = String((this.rawValue * 135) / 128) + "deg"; + } + get value() { + return this._value; + } + set value(value) { + if (isNaN(value)) return; + + if (value < this._min) { + this._value = this._min; + } else if (value > this._max) { + this._value = this._max; + } else { + this._value = value; + } + + this.rawValue = (((this._value + Math.abs(this._min)) * 255) / (this._max - this._min)) + -128; + + if ("wrapper" in this) + this.wrapper.style.rotate = String((this.rawValue * 135) / 128) + "deg"; + } + get max() { + return this._max; + } + set max(value) { + if (isNaN(value)) return; + + this._max = value; + + if (this._value > this._max) { + this._value = this._max; + } + + this.rawValue = (((this._value + Math.abs(this._min)) * 255) / (this._max - this._min)) + -128; + + if ("wrapper" in this) + this.wrapper.style.rotate = String((this.rawValue * 135) / 128) + "deg"; + } +} + +customElements.define("knob-input", KnobElement); diff --git a/view/assets/scripts/main.js b/view/assets/scripts/main.js index c1f868e..030b14c 100644 --- a/view/assets/scripts/main.js +++ b/view/assets/scripts/main.js @@ -69,9 +69,15 @@ if (!("controller" in window)) { "play_pause": function (deckIndex) { sendControl("deck" + String(deckIndex) + ".play_pause"); }, + "sync": function (deckIndex) { + sendControl("deck" + String(deckIndex) + ".sync"); + }, "volume": function (deckIndex, value) { sendControl("deck" + String(deckIndex) + ".volume", value); }, + "eq": function (deckIndex, freq, value) { + sendControl("deck" + String(deckIndex) + ".eq_" + freq, value); + }, "speed": function (deckIndex, value) { sendControl("deck" + String(deckIndex) + ".speed", value); }, diff --git a/view/assets/stylesheets/style.css b/view/assets/stylesheets/style.css index f748427..2dd330a 100644 --- a/view/assets/stylesheets/style.css +++ b/view/assets/stylesheets/style.css @@ -4,6 +4,10 @@ background-color: rgba(8, 12, 16, 1.0); } +* { + user-select: none; +} + :root, body, main, #sections { width: 100%; height: 100%; @@ -18,12 +22,12 @@ button { min-height: 64px; } -#sections, .deck, #mixer, #volume, #volume-meters, .volume-meter, .deck-pads { +#sections, .deck, #mixer, #eq-knobs, #volume, #volume-meters, .volume-meter, .deck-pads { display: flex; justify-content: space-between; } -#sections, #volume, #volume-meters, .deck-pads { +#sections, #eq-knobs, #volume, #volume-meters, .deck-pads { flex-direction: row; } diff --git a/view/index.html b/view/index.html index 9448df6..37f69be 100644 --- a/view/index.html +++ b/view/index.html @@ -5,6 +5,7 @@ DJ Controller Emulator + @@ -30,15 +31,20 @@ +
-
+
- + + +
- + + +
@@ -77,6 +83,7 @@
+