diff --git a/instruments/Bastl_Drum/model.yml b/instruments/Bastl_Drum/model.yml new file mode 100644 index 0000000..4114647 --- /dev/null +++ b/instruments/Bastl_Drum/model.yml @@ -0,0 +1,48 @@ +name: Bastl Drum +slug: Bastl_Drum +description: The drum without buttons, not that drums usually have button but +params: +- drum_mod +- drum +- pitch +- decay +- pitch_mod +- tempo_mod +- tempo +sockets: +- decay_3 +- decay_2 +- decay_1 +- noise_3 +- noises_2 +- noises_1 +- clk_3 +- clk_2 +- clk_1 +- lfo_3 +- lfo_2 +- lfo_1 +- drums_2 +- drums_1 +- pattern_3 +- pattern_2 +- pattern_1 +- i/o_right +- i/o_left +- trig_tempo_mod_3 +- trig_tempo_mod_2 +- trig_tempo_mod_1 +- trig_pitch_mod_3 +- trig_pitch_mod_2 +- trig_pitch_mod_1 +- trig_drum_mod_3 +- trig_drum_mod_2 +- trig_drum_mod_1 +- mode_minus +- mode_plus +- clk_in_2 +- clk_in_1 +- trig_in_2 +- trig_in_1 +- feed_2 +- feed_1 diff --git a/instruments/Bastl_Drum/panel.svg b/instruments/Bastl_Drum/panel.svg new file mode 100644 index 0000000..2c048ed --- /dev/null +++ b/instruments/Bastl_Drum/panel.svg @@ -0,0 +1,175 @@ + diff --git a/instruments/Bastl_Drum/patches/Looo.yml b/instruments/Bastl_Drum/patches/Looo.yml new file mode 100644 index 0000000..2de9b12 --- /dev/null +++ b/instruments/Bastl_Drum/patches/Looo.yml @@ -0,0 +1,25 @@ +cables: '' +date: '2022-06-26' +decay: '50' +description: test +drum: '50' +drum_mod: '82' +name: Looo +pitch: '50' +pitch_mod: '50' +slug: Looo +tempo: '50' +tempo_mod: '50' diff --git a/instruments/Bastl_Drum/patches/test.yml b/instruments/Bastl_Drum/patches/test.yml new file mode 100644 index 0000000..af8458f --- /dev/null +++ b/instruments/Bastl_Drum/patches/test.yml @@ -0,0 +1,49 @@ +date: "2022-06-28" +decay: "50" +description: toast +drum: "50" +drum_mod: "50" +name: test +pitch: "50" +pitch_mod: "50" +slug: test +tempo: "50" +tempo_mod: "50" +cables: '' diff --git a/instruments/Bastl_Kastle_v1.5/model.yml b/instruments/Bastl_Kastle_v1.5/model.yml new file mode 100644 index 0000000..e53efd8 --- /dev/null +++ b/instruments/Bastl_Kastle_v1.5/model.yml @@ -0,0 +1,48 @@ +name: Bastl Kastle v1.5 +slug: Bastl_Kastle_v1.5 +description: The one we know +params: + - pitch_mod + - osc_pitch + - timbre_mod + - waveshape + - osc_timbre + - rate_mod + - lfo_rate +sockets: + - rate_mod_1 + - rate_mod_2 + - rate_mod_3 + - triangle_lfo_1 + - triangle_lfo_2 + - triangle_lfo_3 + - step_generator_1 + - step_generator_2 + - step_generator_3 + - square_lfo_1 + - square_lfo_2 + - square_lfo_3 + - osc_out_1 + - osc_out_2 + - osc_out_3 + - timbre_mod_1 + - timbre_mod_2 + - timbre_mod_3 + - mode_static_minus + - mode_static_plus + - lfo_reset_1 + - lfo_reset_2 + - bit_in_1 + - bit_in_2 + - left_input + - right_input + - mode_minus + - mode_plus + - secondary_osc_out_1 + - secondary_osc_out_2 + - pitch_mode_1 + - pitch_mode_2 + - pitch_mode_3 + - waveshape_1 + - waveshape_2 + - waveshape_3 diff --git a/instruments/Bastl_Kastle_v1.5/panel.svg b/instruments/Bastl_Kastle_v1.5/panel.svg new file mode 100644 index 0000000..61fbd4f --- /dev/null +++ b/instruments/Bastl_Kastle_v1.5/panel.svg @@ -0,0 +1,174 @@ + diff --git a/instruments/Kastle_Drum/model.yml b/instruments/Kastle_Drum/model.yml deleted file mode 100644 index 2372dcf..0000000 --- a/instruments/Kastle_Drum/model.yml +++ /dev/null @@ -1,8 +0,0 @@ -description: ye the drum -name: Kastle Drum -slug: Kastle_Drum - -params: -- drum -- noise -- tempo \ No newline at end of file diff --git a/instruments/Pic_nic_synthetyzer/patches/daily pic nic.yml b/instruments/Pic_nic_synthetyzer/patches/daily pic nic.yml deleted file mode 100644 index 3a65515..0000000 --- a/instruments/Pic_nic_synthetyzer/patches/daily pic nic.yml +++ /dev/null @@ -1,13 +0,0 @@ -celery: 'no' -'cherry tomatos': '3' -mayo: a lot -salad: sure -bread: 'yes' -date: '2022-06-13' -description: 'is a picnicco ' -ginger: 'yes' -input: Hunger -name: daily pic nic -noddles: sweet pot -output: Our stomachs -routing: new york hotl diff --git a/instruments/Sample_Demo_Dinner/model.yml b/instruments/Sample_Demo_Dinner/model.yml new file mode 100644 index 0000000..2c3a96d --- /dev/null +++ b/instruments/Sample_Demo_Dinner/model.yml @@ -0,0 +1,23 @@ +name: Sample Demo Dinner +slug: Sample_Demo_Dinner +description: A test panel just for fun +params: +- soup +- goat_cheese +- pasta +- tequila +- rice_rolls +- lemon +- salt +- shot_rate +- thai_curry +- linzen +- kip +- leek +- stuffed_peppers +- pizza +- kikkerverten +sockets: +- lunch_3 +- breakfast +- dinner_2 diff --git a/instruments/Sample_Demo_Dinner/panel.svg b/instruments/Sample_Demo_Dinner/panel.svg new file mode 100644 index 0000000..4370563 --- /dev/null +++ b/instruments/Sample_Demo_Dinner/panel.svg @@ -0,0 +1,134 @@ + diff --git a/instruments/Simple_sample_synth/model.yml b/instruments/Simple_sample_synth/model.yml deleted file mode 100644 index 921ce47..0000000 --- a/instruments/Simple_sample_synth/model.yml +++ /dev/null @@ -1,6 +0,0 @@ -description: SSS snake synth for demo -name: Simple sample synth -params: -- sample -- hold -slug: Simple_sample_synth diff --git a/instruments/Simple_sample_synth/panel.svg b/instruments/Simple_sample_synth/panel.svg deleted file mode 100644 index adf981b..0000000 --- a/instruments/Simple_sample_synth/panel.svg +++ /dev/null @@ -1,99 +0,0 @@ - diff --git a/instruments/Simple_sample_synth/patches/4.yml b/instruments/Simple_sample_synth/patches/4.yml deleted file mode 100644 index 82f00b3..0000000 --- a/instruments/Simple_sample_synth/patches/4.yml +++ /dev/null @@ -1,8 +0,0 @@ -date: '2022-06-08' -description: gg -hold: g -input: d -name: '4' -output: s -routing: g -sample: r diff --git a/instruments/Simple_sample_synth/patches/A test patch just the first one.yml b/instruments/Simple_sample_synth/patches/A test patch just the first one.yml deleted file mode 100644 index 76d9fa2..0000000 --- a/instruments/Simple_sample_synth/patches/A test patch just the first one.yml +++ /dev/null @@ -1,8 +0,0 @@ -date: '2022-06-13' -description: A nice patch to see if itsworki -hold: eeeeee -input: us -name: A test patch just the first one -output: them -routing: us----->them -sample: quak diff --git a/instruments/Simple_sample_synth/patches/The_second_patch.yml b/instruments/Simple_sample_synth/patches/The_second_patch.yml deleted file mode 100644 index fd9949e..0000000 --- a/instruments/Simple_sample_synth/patches/The_second_patch.yml +++ /dev/null @@ -1,8 +0,0 @@ -date: '2022-06-13' -description: ahah eheh ahah -hold: ggg -input: v -name: The second patch -output: j -routing: v2 ---> j -sample: efff diff --git a/instruments/Simple_sample_synth/patches/third.yml b/instruments/Simple_sample_synth/patches/third.yml deleted file mode 100644 index 289925a..0000000 --- a/instruments/Simple_sample_synth/patches/third.yml +++ /dev/null @@ -1,8 +0,0 @@ -date: '2022-06-13' -description: finally picnic -hold: wine -input: buffeto -name: third -output: us -routing: us -----> buffettp -sample: bread diff --git a/instruments/moog/model.yml b/instruments/moog/model.yml deleted file mode 100644 index f429919..0000000 --- a/instruments/moog/model.yml +++ /dev/null @@ -1,8 +0,0 @@ -description: the classy moog -name: moog -params: -- VCO -- LFO -- VCF -- VCA -slug: moog diff --git a/instruments/moog/panel.svg b/instruments/moog/panel.svg deleted file mode 100644 index adf981b..0000000 --- a/instruments/moog/panel.svg +++ /dev/null @@ -1,99 +0,0 @@ - diff --git a/instruments/moog/patches/my_first_analog_patch.yml b/instruments/moog/patches/my_first_analog_patch.yml deleted file mode 100644 index e343673..0000000 --- a/instruments/moog/patches/my_first_analog_patch.yml +++ /dev/null @@ -1,10 +0,0 @@ -LFO: '321' -VCA: '234' -VCF: '43' -VCO: '12' -date: '2022-07-01' -description: wow i spent 3000 in this cannot believie it -input: '43' -name: my first analog patch -output: '2' -routing: '432' diff --git a/instruments/perepepepepe/model.yml b/instruments/perepepepepe/model.yml deleted file mode 100644 index a31ce3d..0000000 --- a/instruments/perepepepepe/model.yml +++ /dev/null @@ -1 +0,0 @@ -name: Watersand synth amazing 2 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index ff8b2d2..aa60c37 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,10 @@ click==8.1.3 +colorama==0.4.5 Flask==2.1.2 importlib-metadata==4.11.4 itsdangerous==2.1.2 Jinja2==3.1.2 MarkupSafe==2.1.1 +PyYAML==6.0 Werkzeug==2.1.2 zipp==3.8.0 diff --git a/static/css/home.css b/static/css/home.css index 9e907d7..42e7b64 100644 --- a/static/css/home.css +++ b/static/css/home.css @@ -29,7 +29,7 @@ svg { position: absolute; top: 0; left: 0; - border: 1px solid currentColor; + /* border: 1px solid currentColor; */ width: 100%; height: 100%; } diff --git a/static/css/panel.css b/static/css/panel.css new file mode 100644 index 0000000..c6c3eb1 --- /dev/null +++ b/static/css/panel.css @@ -0,0 +1,5 @@ +#panel-container { + position: relative; + width: auto; + height: auto; +} diff --git a/static/css/patch.css b/static/css/patch.css index 6a1d7f6..f3e9580 100644 --- a/static/css/patch.css +++ b/static/css/patch.css @@ -1,61 +1,60 @@ .instrument { - max-height: 100vh; - max-width: 100vw; - object-fit: contain; - margin: 0 auto; - display: block; + max-height: 100vh; + max-width: 100vw; + object-fit: contain; + margin: 0 auto; + display: block; + position: relative; } .samples { - width: 100%; - padding: 0; + width: 100%; + padding: 0; } .samples li { - display: flex; - width: 100%; - font-size: 32px; - padding: 8px; + display: flex; + width: 100%; + font-size: 32px; + padding: 8px; } .samples li > * { - flex-shrink: 0; + flex-shrink: 0; } .samples li:nth-child(even) { - background:#efefef; - } + background: #efefef; +} -.samples li button{ - background-color: white; - border: 1px solid currentColor; - padding: 4px; - width: 32px; - height: 32px; - display: inline-block; +.samples li button { + background-color: white; + border: 1px solid currentColor; + padding: 4px; + width: 32px; + height: 32px; + display: inline-block; } -.samples li button svg{ - width: 100%; - height: 100%; +.samples li button svg { + width: 100%; + height: 100%; } .download { - margin-left: auto; - margin-right: 16px; + margin-left: auto; + margin-right: 16px; } .samples .description { - margin: 0 8px; - flex-shrink: 1; - + margin: 0 8px; + flex-shrink: 1; } -.print svg{ - background-color: white; - border: 1px solid currentColor; - padding: 4px; - width: 32px; - height: 32px; - display: inline-block; +.print svg { + background-color: white; + border: 1px solid currentColor; + padding: 4px; + width: 32px; + height: 32px; + display: inline-block; } - diff --git a/static/js/input-knobs.js b/static/js/input-knobs.js new file mode 100644 index 0000000..e4486f9 --- /dev/null +++ b/static/js/input-knobs.js @@ -0,0 +1,375 @@ +window.addEventListener("load", () => { + let op = window.inputKnobsOptions || {}; + op.knobWidth = op.knobWidth || op.knobDiameter || 64; + op.knobHeight = op.knobHeight || op.knobDiameter || 64; + op.sliderWidth = op.sliderWidth || op.sliderDiameter || 128; + op.sliderHeight = op.sliderHeight || op.sliderDiameter || 20; + op.switchWidth = op.switchWidth || op.switchDiameter || 24; + op.switchHeight = op.switchHeight || op.switchDiameter || 24; + op.fgcolor = op.fgcolor || "#fff"; + op.bgcolor = op.bgcolor || "#000"; + op.knobMode = op.knobMode || "linear"; + op.sliderMode = op.sliderMode || "relative"; + let styles = document.createElement("style"); + styles.innerHTML = `input[type=range].input-knob,input[type=range].input-slider{ + -webkit-appearance:none; + -moz-appearance:none; + border:none; + box-sizing:border-box; + overflow:hidden; + background-repeat:no-repeat; + background-size:100% 100%; + background-position:0px 0%; + background-color:transparent; + touch-action:none; +} +input[type=range].input-knob{ + width:${op.knobWidth}px; height:${op.knobHeight}px; +} +input[type=range].input-slider{ + width:${op.sliderWidth}px; height:${op.sliderHeight}px; +} +input[type=range].input-knob::-webkit-slider-thumb,input[type=range].input-slider::-webkit-slider-thumb{ + -webkit-appearance:none; + opacity:0; +} +input[type=range].input-knob::-moz-range-thumb,input[type=range].input-slider::-moz-range-thumb{ + -moz-appearance:none; + height:0; + border:none; +} +input[type=range].input-knob::-moz-range-track,input[type=range].input-slider::-moz-range-track{ + -moz-appearance:none; + height:0; + border:none; +} +input[type=checkbox].input-switch,input[type=radio].input-switch { + width:${op.switchWidth}px; + height:${op.switchHeight}px; + -webkit-appearance:none; + -moz-appearance:none; + background-size:100% 200%; + background-position:0% 0%; + background-repeat:no-repeat; + border:none; + border-radius:0; + background-color:transparent; +} +input[type=checkbox].input-switch:checked,input[type=radio].input-switch:checked { + background-position:0% 100%; +}`; + document.head.appendChild(styles); + let makeKnobFrames = (fr, fg, bg) => { + let r = `"; + }; + let makeHSliderFrames = (fr, fg, bg, w, h) => { + let r = `"; + }; + let makeVSliderFrames = (fr, fg, bg, w, h) => { + let r = `"; + }; + let initSwitches = (el) => { + let w, h, d, fg, bg; + if (el.inputKnobs) return; + el.inputKnobs = {}; + el.refresh = () => { + let src = el.getAttribute("data-src"); + d = +el.getAttribute("data-diameter"); + let st = document.defaultView.getComputedStyle(el, null); + w = parseFloat(el.getAttribute("data-width") || d || st.width); + h = parseFloat(el.getAttribute("data-height") || d || st.height); + bg = el.getAttribute("data-bgcolor") || op.bgcolor; + fg = el.getAttribute("data-fgcolor") || op.fgcolor; + el.style.width = w + "px"; + el.style.height = h + "px"; + if (src) el.style.backgroundImage = "url(" + src + ")"; + else { + let minwh = Math.min(w, h); + let svg = ``; + el.style.backgroundImage = "url(data:image/svg+xml;base64," + btoa(svg) + ")"; + } + }; + el.refresh(); + }; + let initKnobs = (el) => { + let w, h, d, fg, bg; + if (el.inputKnobs) { + el.redraw(); + return; + } + let ik = (el.inputKnobs = {}); + el.refresh = () => { + d = +el.getAttribute("data-diameter"); + let st = document.defaultView.getComputedStyle(el, null); + w = parseFloat(el.getAttribute("data-width") || d || st.width); + h = parseFloat(el.getAttribute("data-height") || d || st.height); + bg = el.getAttribute("data-bgcolor") || op.bgcolor; + fg = el.getAttribute("data-fgcolor") || op.fgcolor; + ik.sensex = ik.sensey = 200; + if (el.className.indexOf("input-knob") >= 0) ik.itype = "k"; + else { + if (w >= h) { + ik.itype = "h"; + ik.sensex = w - h; + ik.sensey = Infinity; + el.style.backgroundSize = "auto 100%"; + } else { + ik.itype = "v"; + ik.sensex = Infinity; + ik.sensey = h - w; + el.style.backgroundSize = "100% auto"; + } + } + el.style.width = w + "px"; + el.style.height = h + "px"; + ik.frameheight = h; + let src = el.getAttribute("data-src"); + if (src) { + el.style.backgroundImage = `url(${src})`; + let sp = +el.getAttribute("data-sprites"); + if (sp) ik.sprites = sp; + else ik.sprites = 0; + if (ik.sprites >= 1) el.style.backgroundSize = `100% ${(ik.sprites + 1) * 100}%`; + else if (ik.itype != "k") { + el.style.backgroundColor = bg; + el.style.borderRadius = Math.min(w, h) * 0.25 + "px"; + } + } else { + let svg; + switch (ik.itype) { + case "k": + svg = makeKnobFrames(101, fg, bg); + break; + case "h": + svg = makeHSliderFrames(101, fg, bg, w, h); + break; + case "v": + svg = makeVSliderFrames(101, fg, bg, w, h); + break; + } + ik.sprites = 100; + el.style.backgroundImage = "url(data:image/svg+xml;base64," + btoa(svg) + ")"; + el.style.backgroundSize = `100% ${(ik.sprites + 1) * 100}%`; + } + ik.valrange = { + min: +el.min, + max: el.max == "" ? 100 : +el.max, + step: el.step == "" ? 1 : +el.step, + }; + el.redraw(true); + }; + el.setValue = (v) => { + v = + Math.round((v - ik.valrange.min) / ik.valrange.step) * ik.valrange.step + + ik.valrange.min; + if (v < ik.valrange.min) v = ik.valrange.min; + if (v > ik.valrange.max) v = ik.valrange.max; + el.value = v; + if (el.value != ik.oldvalue) { + el.setAttribute("value", el.value); + el.redraw(); + let event = document.createEvent("HTMLEvents"); + event.initEvent("input", false, true); + el.dispatchEvent(event); + ik.oldvalue = el.value; + } + }; + ik.pointerdown = (ev) => { + el.focus(); + const evorg = ev; + if (ev.touches) ev = ev.touches[0]; + let rc = el.getBoundingClientRect(); + let cx = (rc.left + rc.right) * 0.5, + cy = (rc.top + rc.bottom) * 0.5; + let dx = ev.clientX, + dy = ev.clientY; + let da = Math.atan2(ev.clientX - cx, cy - ev.clientY); + if (ik.itype == "k" && op.knobMode == "circularabs") { + dv = + ik.valrange.min + + ((da / Math.PI) * 0.75 + 0.5) * (ik.valrange.max - ik.valrange.min); + el.setValue(dv); + } + if (ik.itype != "k" && op.sliderMode == "abs") { + dv = + (ik.valrange.min + ik.valrange.max) * 0.5 + + ((dx - cx) / ik.sensex - (dy - cy) / ik.sensey) * + (ik.valrange.max - ik.valrange.min); + el.setValue(dv); + } + ik.dragfrom = { + x: ev.clientX, + y: ev.clientY, + a: Math.atan2(ev.clientX - cx, cy - ev.clientY), + v: +el.value, + }; + document.addEventListener("mousemove", ik.pointermove); + document.addEventListener("mouseup", ik.pointerup); + document.addEventListener("touchmove", ik.pointermove); + document.addEventListener("touchend", ik.pointerup); + document.addEventListener("touchcancel", ik.pointerup); + document.addEventListener("touchstart", ik.preventScroll); + evorg.preventDefault(); + evorg.stopPropagation(); + }; + ik.pointermove = (ev) => { + let dv; + let rc = el.getBoundingClientRect(); + let cx = (rc.left + rc.right) * 0.5, + cy = (rc.top + rc.bottom) * 0.5; + if (ev.touches) ev = ev.touches[0]; + let dx = ev.clientX - ik.dragfrom.x, + dy = ev.clientY - ik.dragfrom.y; + let da = Math.atan2(ev.clientX - cx, cy - ev.clientY); + switch (ik.itype) { + case "k": + switch (op.knobMode) { + case "linear": + dv = + (dx / ik.sensex - dy / ik.sensey) * + (ik.valrange.max - ik.valrange.min); + if (ev.shiftKey) dv *= 0.2; + el.setValue(ik.dragfrom.v + dv); + break; + case "circularabs": + if (!ev.shiftKey) { + dv = + ik.valrange.min + + ((da / Math.PI) * 0.75 + 0.5) * + (ik.valrange.max - ik.valrange.min); + el.setValue(dv); + break; + } + case "circularrel": + if (da > ik.dragfrom.a + Math.PI) da -= Math.PI * 2; + if (da < ik.dragfrom.a - Math.PI) da += Math.PI * 2; + da -= ik.dragfrom.a; + dv = (da / Math.PI / 1.5) * (ik.valrange.max - ik.valrange.min); + if (ev.shiftKey) dv *= 0.2; + el.setValue(ik.dragfrom.v + dv); + } + break; + case "h": + case "v": + dv = (dx / ik.sensex - dy / ik.sensey) * (ik.valrange.max - ik.valrange.min); + if (ev.shiftKey) dv *= 0.2; + el.setValue(ik.dragfrom.v + dv); + break; + } + }; + ik.pointerup = () => { + document.removeEventListener("mousemove", ik.pointermove); + document.removeEventListener("touchmove", ik.pointermove); + document.removeEventListener("mouseup", ik.pointerup); + document.removeEventListener("touchend", ik.pointerup); + document.removeEventListener("touchcancel", ik.pointerup); + document.removeEventListener("touchstart", ik.preventScroll); + let event = document.createEvent("HTMLEvents"); + event.initEvent("change", false, true); + el.dispatchEvent(event); + }; + ik.preventScroll = (ev) => { + ev.preventDefault(); + }; + ik.keydown = () => { + el.redraw(); + }; + ik.wheel = (ev) => { + let delta = ev.deltaY > 0 ? -ik.valrange.step : ik.valrange.step; + if (!ev.shiftKey) delta *= 5; + el.setValue(+el.value + delta); + ev.preventDefault(); + ev.stopPropagation(); + }; + el.redraw = (f) => { + if (f || ik.valueold != el.value) { + let v = (el.value - ik.valrange.min) / (ik.valrange.max - ik.valrange.min); + if (ik.sprites >= 1) + el.style.backgroundPosition = + "0px " + -((v * ik.sprites) | 0) * ik.frameheight + "px"; + else { + switch (ik.itype) { + case "k": + el.style.transform = "rotate(" + (270 * v - 135) + "deg)"; + break; + case "h": + el.style.backgroundPosition = (w - h) * v + "px 0px"; + break; + case "v": + el.style.backgroundPosition = "0px " + (h - w) * (1 - v) + "px"; + break; + } + } + ik.valueold = el.value; + } + }; + el.refresh(); + el.redraw(true); + el.addEventListener("keydown", ik.keydown); + el.addEventListener("mousedown", ik.pointerdown); + el.addEventListener("touchstart", ik.pointerdown); + el.addEventListener("wheel", ik.wheel); + }; + let refreshque = () => { + let elem = document.querySelectorAll("input.input-knob,input.input-slider"); + for (let i = 0; i < elem.length; ++i) procque.push([initKnobs, elem[i]]); + elem = document.querySelectorAll( + "input[type=checkbox].input-switch,input[type=radio].input-switch" + ); + for (let i = 0; i < elem.length; ++i) { + procque.push([initSwitches, elem[i]]); + } + }; + let procque = []; + refreshque(); + setInterval(() => { + for (let i = 0; procque.length > 0 && i < 8; ++i) { + let q = procque.shift(); + q[0](q[1]); + } + if (procque.length <= 0) refreshque(); + }, 50); +}); diff --git a/static/js/new-panel.js b/static/js/new-panel.js new file mode 100644 index 0000000..70ea355 --- /dev/null +++ b/static/js/new-panel.js @@ -0,0 +1,29 @@ +const container = document.querySelector("#panel-container"); +const file = document.querySelector("input[name=panel]"); +const form = document.querySelector("form"); + +const submit = document.querySelector("input[type=submit]"); +let panel; + +form.addEventListener("submit", (e) => { + e.preventDefault(); + form.querySelector("[name=params]").value = JSON.stringify(panel.model.params); + form.querySelector("[name=sockets]").value = JSON.stringify(panel.model.sockets); + form.submit(); +}); + +file.addEventListener("change", (e) => { + let file = e.target.files[0]; + + if (file) { + var reader = new FileReader(); + reader.readAsText(file, "UTF-8"); + reader.onload = function (evt) { + let svg = evt.target.result; + panel = new Panel(svg, container); + }; + reader.onerror = function (evt) { + console.log("error reading file"); + }; + } +}); diff --git a/static/js/new-patch.js b/static/js/new-patch.js new file mode 100644 index 0000000..ce25e5d --- /dev/null +++ b/static/js/new-patch.js @@ -0,0 +1,12 @@ +const form = document.querySelector("form"); + +const container = document.querySelector("#panel-container"); +const svg = container.querySelector("svg").outerHTML; + +let panel = new Panel(svg, container); + +form.addEventListener("submit", (e) => { + e.preventDefault(); + form.querySelector("[name=cables]").value = container.querySelector(".cables").outerHTML; + form.submit(); +}); diff --git a/static/js/panel.js b/static/js/panel.js new file mode 100644 index 0000000..766a299 --- /dev/null +++ b/static/js/panel.js @@ -0,0 +1,298 @@ +class Cable { + start = ""; + end = ""; + strPath = ""; + color = ""; + strokeWidth = 10; + bufferSize = 20; + buffer = []; + path = null; + + constructor(start) { + this.start = start; + this.randomColor(); + } + + randomColor() { + const hue = Math.floor(Math.random() * 360); + const saturation = Math.floor(50 + Math.random() * (50 + 1)) + "%"; + const lightness = "75%"; + this.color = "hsl(" + hue + ", " + saturation + ", " + lightness + ")"; + } + + createCable(position) { + this.path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + + this.path.setAttribute("fill", "none"); + + this.path.setAttribute("stroke", this.color); + this.path.setAttribute("stroke-width", this.strokeWidth); + this.path.setAttribute("stroke-linecap", "round"); + + this.buffer = []; + this.appendToBuffer(position); + this.strPath = "M" + position.x + " " + position.y; + this.path.setAttribute("d", this.strPath); + } + + appendToBuffer(position) { + this.buffer.push(position); + while (this.buffer.length > this.bufferSize) { + this.buffer.shift(); + } + } + + getAveragePoint(offset) { + // Calculate the average point, starting at offset in the buffer + let len = this.buffer.length; + if (len % 2 === 1 || len >= this.bufferSize) { + let totalX = 0; + let totalY = 0; + let position, i; + let count = 0; + for (i = offset; i < len; i++) { + count++; + position = this.buffer[i]; + totalX += position.x; + totalY += position.y; + } + return { + x: totalX / count, + y: totalY / count, + }; + } + return null; + } + + updatePath() { + let position = this.getAveragePoint(0); + if (position) { + // Get the smoothed part of the path that will not change + this.strPath += " L" + position.x + " " + position.y; + + // Get the last part of the path (close to the current mouse position) + // This part will change if the mouse moves again + var tmpPath = ""; + for (var offset = 2; offset < this.buffer.length; offset += 2) { + position = this.getAveragePoint(offset); + tmpPath += " L" + position.x + " " + position.y; + } + this.path.setAttribute("d", this.strPath + tmpPath); + } + } +} + +class Panel { + width = 0; + height = 0; + + svg = null; + container = null; + containerBoundingClient = null; + + model = {}; + params = []; + sockets = []; + + cable = null; + cables = null; + + constructor(svg, container, debug = false) { + this.svg = this.htmlToElement(svg); + this.container = container; + this.container.style.position = "relative"; + this.containerRect = container.getBoundingClientRect(); + this.debug = debug; + + while (container.firstChild) { + container.removeChild(container.lastChild); + } + container.appendChild(this.svg); + + this.setSize(); + this.createParams(); + this.createSockets(); + this.createCables(); + + this.svg.addEventListener("mousedown", (e) => this.startCable(e)); + this.svg.addEventListener("mousemove", (e) => this.drawCable(e)); + this.svg.addEventListener("mouseup", (e) => this.endCable(e)); + // this.svg.addEventListener("mouseleave", (e) => this.endCable(e)); + } + + htmlToElement(string) { + var template = document.createElement("template"); + string = string.trim(); // Never return a text node of whitespace as the result + template.innerHTML = string; + return template.content.firstChild; + } + + setSize() { + this.width = this.svg.getAttribute("width"); + this.height = this.svg.getAttribute("height"); + this.container.style.width = this.width + "px"; + this.container.style.height = this.height + "px"; + } + + createParams() { + this.params = this.svg.querySelectorAll("#params [fill='#FF0000']"); + this.model.params = []; + + for (const param of this.params) { + let rect = param.getBoundingClientRect(); + + let control = document.createElement("input"); + control.setAttribute("type", "range"); + control.setAttribute("data-width", rect.width); + control.setAttribute("data-height", rect.height); + + control.setAttribute("data-fgcolor", "white"); + control.classList.add("input-knob"); + control.style.position = "absolute"; + control.style.left = rect.left - this.containerRect.left + "px"; + control.style.top = rect.top - this.containerRect.top + "px"; + + control.setAttribute("name", param.id); + + this.container.appendChild(control); + + this.model.params.push(param.id); + + if (this.debug) { + let label = document.createElement("label"); + label.setAttribute("for", param.id); + label.innerHTML = param.id; + + label.style.position = "absolute"; + label.style.left = rect.left - this.containerRect.left + "px"; + label.style.top = rect.top + rect.height + "px"; + label.style.fontSize = "1rem"; + label.style.backgroundColor = "red"; + label.style.color = "white"; + + this.container.appendChild(label); + } + } + let group = this.svg.querySelector("#params"); + if (group) group.style.display = "none"; + } + + createSockets() { + this.sockets = this.svg.querySelectorAll("#sockets [fill='#00FF00']"); + this.model.sockets = []; + for (const socket of this.sockets) { + let rect = socket.getBoundingClientRect(); + + let input = document.createElement("input"); + input.setAttribute("name", socket.id); + input.classList.add("socket"); + + input.style.position = "absolute"; + input.style.left = rect.left - this.containerRect.left + "px"; + input.style.top = rect.top - this.containerRect.top + "px"; + input.style.width = rect.width + "px"; + input.style.height = rect.height + "px"; + + input.style.border = "none"; + input.style.background = "none"; + input.style.opacity = 0; + + if (this.debug) { + input.style.border = "1px solid #00FF00"; + input.style.opacity = 1; + } + + input.style.cursor = "alias"; + + input.addEventListener("mouseup", (e) => this.endCable(e)); + + input.addEventListener("mousedown", (e) => { + e.preventDefault(); + this.startCable(e); + return false; + }); + + this.container.appendChild(input); + + this.model.sockets.push(socket.id); + } + + let group = this.svg.querySelector("#sockets"); + if (group) group.style.display = "none"; + } + + createCables() { + let cables = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + cables.classList.add("cables"); + cables.setAttribute("width", this.width); + cables.setAttribute("height", this.height); + cables.setAttribute("viewBox", `0 0 ${this.width} ${this.height}`); + cables.setAttribute("fill", "none"); + cables.style.pointerEvents = "none"; + cables.style.position = "absolute"; + cables.style.left = 0; + cables.style.top = 0; + cables.style.width = this.width; + cables.style.height = this.height; + + this.cables = cables; + this.container.appendChild(this.cables); + } + + startCable(event) { + let position = this.getMousePosition(event); + let socket = event.target; + + if (socket && socket.classList.contains("socket")) { + this.cable = new Cable(socket.getAttribute("name")); + this.cable.createCable(position); + let cable = this.cable.path; + cable.style.pointerEvents = "stroke"; + + cable.addEventListener("click", (e) => { + console.log(e); + cable.remove(); + }); + + this.cables.appendChild(this.cable.path); + + for (const input of this.container.querySelectorAll(".input-knob")) { + input.style.pointerEvents = "none"; + } + } + } + + drawCable(event) { + if (this.cable) { + this.cable.appendToBuffer(this.getMousePosition(event)); + this.cable.updatePath(); + } + } + + endCable(event) { + if (this.cable) { + let socket = event.target; + + if (socket && socket.classList.contains("socket")) { + this.cable.end = socket.getAttribute("name"); + + // TODO: log cable + } else { + this.cable.path.remove(); + } + + for (const input of this.container.querySelectorAll(".input-knob")) { + input.style.pointerEvents = "all"; + } + + this.cable = null; + } + } + + getMousePosition(e) { + return { + x: e.pageX - this.containerRect.left, + y: e.pageY - this.containerRect.top, + }; + } +} diff --git a/templates/add_instrument.html b/templates/add_instrument.html index a8daa71..ec45200 100644 --- a/templates/add_instrument.html +++ b/templates/add_instrument.html @@ -2,20 +2,31 @@
- + - + + + + + + +Description skjdflkajsdkfLabore aliqua ut sit consequat veniam ut do ad nulla.Ad labore mollit ut cillum cillum est. In ex nostrud esse sit fugiat elit qui. Enim do laboris irure in sint deserunt irure culpa ad officia eu est non. Officia reprehenderit labore excepteur non. Exercitation aliqua commodo exercitation quis deserunt exercitation esse eiusmod excepteur sint consectetur fugiat nisi dolore. Eiusmod adipisicing aliqua veniam eu ullamco anim duis labore commodo cupidatat voluptate nisi mollit. Tempor fugiat in enim culpa ut veniam veniam.
++ Description skjdflkajsdkfLabore aliqua ut sit consequat veniam ut do ad + nulla.Ad labore mollit ut cillum cillum est. In ex nostrud esse sit fugiat + elit qui. Enim do laboris irure in sint deserunt irure culpa ad officia eu + est non. Officia reprehenderit labore excepteur non. Exercitation aliqua + commodo exercitation quis deserunt exercitation esse eiusmod excepteur sint + consectetur fugiat nisi dolore. Eiusmod adipisicing aliqua veniam eu ullamco + anim duis labore commodo cupidatat voluptate nisi mollit. Tempor fugiat in + enim culpa ut veniam veniam. +
Description
Description
Description
Description