UNPKG

wam-community

Version:

A collection of prebuilt Web Audio Modules ready for use

1,400 lines (1,392 loc) 70.5 kB
/* * * * WebAudio-Controls is based on * webaudio-knob by Eiji Kitamura http://google.com/+agektmr * webaudio-slider by RYoya Kawai https://plus.google.com/108242669191458983485/posts * webaudio-switch by Keisuke Ai http://d.hatena.ne.jp/aike/ * Integrated and enhanced by g200kg http://www.g200kg.com/ * * Copyright 2013 Eiji Kitamura / Ryoya KAWAI / Keisuke Ai / g200kg(Tatsuya Shinyagaito) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * */ if (window.customElements) { let styles = document.createElement("style"); styles.innerHTML = `#webaudioctrl-context-menu { display: none; position: absolute; z-index: 10; padding: 0; width: 100px; color:#eee; background-color: #268; border: solid 1px #888; box-shadow: 1px 1px 2px #888; font-family: sans-serif; font-size: 11px; line-height:1.7em; text-align:center; cursor:pointer; color:#fff; list-style: none; } #webaudioctrl-context-menu.active { display: block; } .webaudioctrl-context-menu__item { display: block; margin: 0; padding: 0; color: #000; background-color:#eee; text-decoration: none; } .webaudioctrl-context-menu__title{ font-weight:bold; } .webaudioctrl-context-menu__item:last-child { margin-bottom: 0; } .webaudioctrl-context-menu__item:hover { background-color: #b8b8b8; } `; document.head.appendChild(styles); let midimenu = document.createElement("ul"); midimenu.id = "webaudioctrl-context-menu"; midimenu.innerHTML = `<li class="webaudioctrl-context-menu__title">MIDI Learn</li> <li class="webaudioctrl-context-menu__item" id="webaudioctrl-context-menu-learn" onclick="webAudioControlsMidiManager.contextMenuLearn()">Learn</li> <li class="webaudioctrl-context-menu__item" onclick="webAudioControlsMidiManager.contextMenuClear()">Clear</li> <li class="webaudioctrl-context-menu__item" onclick="webAudioControlsMidiManager.contextMenuClose()">Close</li> `; let opt = { useMidi: 0, midilearn: 0, mididump: 0, outline: 0, knobSrc: null, knobSprites: 0, knobWidth: 0, knobHeight: 0, knobDiameter: 64, knobColors: "#fff;#000;#000", sliderSrc: null, sliderKnobsrc: null, sliderWidth: 0, sliderHeight: 0, sliderKnobwidth: 0, sliderKnobheight: 0, sliderDitchlength: 0, sliderColors: "#bbb;#444;#fff", switchWidth: 0, switchHeight: 0, switchDiameter: 24, switchColors: "#e00;#000;#fcc", paramWidth: 32, paramHeight: 16, paramColors: "#fff;#000", }; if (window.WebAudioControlsOptions) Object.assign(opt, window.WebAudioControlsOptions); class WebAudioControlsWidget extends HTMLElement { constructor() { super(); this.addEventListener("keydown", this.keydown); this.addEventListener("mousedown", this.pointerdown, { passive: false }); this.addEventListener("touchstart", this.pointerdown, { passive: false }); this.addEventListener("wheel", this.wheel, { passive: false }); this.addEventListener("mouseover", this.pointerover); this.addEventListener("mouseout", this.pointerout); this.addEventListener("contextmenu", this.contextMenu); this.hover = this.drag = 0; document.body.appendChild(midimenu); this.basestyle = ` .webaudioctrl-tooltip{ display:inline-block; position:absolute; margin:0 -1000px; z-index: 999; background:#eee; color:#000; border:1px solid #666; border-radius:4px; padding:5px 10px; text-align:center; left:0; top:0; font-size:11px; opacity:0; visibility:hidden; } .webaudioctrl-tooltip:before{ content: ""; position: absolute; top: 100%; left: 50%; margin-left: -8px; border: 8px solid transparent; border-top: 8px solid #666; } .webaudioctrl-tooltip:after{ content: ""; position: absolute; top: 100%; left: 50%; margin-left: -6px; border: 6px solid transparent; border-top: 6px solid #eee; } `; } sendEvent(ev) { let event; event = document.createEvent("HTMLEvents"); event.initEvent(ev, false, true); this.dispatchEvent(event); } getAttr(n, def) { let v = this.getAttribute(n); if (v == "" || v == null) return def; switch (typeof def) { case "number": if (v == "true") return 1; v = +v; if (isNaN(v)) return 0; return v; } return v; } showtip(d) { function numformat(s, x) { let i = s.indexOf("%"); let c = [0, 0], type = 0, m = 0, r = "", j = i + 1; for (; j < s.length; ++j) { if ("dfxXs".indexOf(s[j]) >= 0) { type = s[j]; break; } if (s[j] == ".") m = 1; else c[m] = c[m] * 10 + parseInt(s[j]); } switch (type) { case "x": r = (x | 0).toString(16); break; case "X": r = (x | 0).toString(16).toUpperCase(); break; case "d": r = (x | 0).toString(); break; case "f": r = x.toFixed(c[1]); break; case "s": r = x.toString(); break; } if (c[0] > 0) r = (" " + r).slice(-c[0]); r = s.replace(/%.*[xXdfs]/, r); return r; } let s = this.tooltip; if (this.drag || this.hover) { if (this.valuetip) { if (s == null) s = `%.${this.digits}f`; else if (s.indexOf("%") < 0) s += ` : %.${this.digits}f`; } if (s) { this.ttframe.innerHTML = numformat(s, this.convValue); this.ttframe.style.display = "inline-block"; this.ttframe.style.width = "auto"; this.ttframe.style.height = "auto"; this.ttframe.style.transition = "opacity 0.5s " + d + "s,visibility 0.5s " + d + "s"; this.ttframe.style.opacity = 0.9; this.ttframe.style.visibility = "visible"; let rc = this.getBoundingClientRect(), rc2 = this.ttframe.getBoundingClientRect(), rc3 = document.documentElement.getBoundingClientRect(); this.ttframe.style.left = (rc.width - rc2.width) * 0.5 + 1000 + "px"; this.ttframe.style.top = -rc2.height - 8 + "px"; return; } } this.ttframe.style.transition = "opacity 0.1s " + d + "s,visibility 0.1s " + d + "s"; this.ttframe.style.opacity = 0; this.ttframe.style.visibility = "hidden"; } pointerover(e) { this.hover = 1; this.showtip(0.6); } pointerout(e) { this.hover = 0; this.showtip(0); } contextMenu(e) { if (window.webAudioControlsMidiManager && this.midilearn) { webAudioControlsMidiManager.contextMenuOpen(e, this); } e.preventDefault(); e.stopPropagation(); } setMidiController(channel, cc) { if (this.listeningToThisMidiController(channel, cc)) return; this.midiController = { channel: channel, cc: cc }; console.log("Added mapping for channel=" + channel + " cc=" + cc + " tooltip=" + this.tooltip); } listeningToThisMidiController(channel, cc) { const c = this.midiController; if ((c.channel === channel || c.channel < 0) && c.cc === cc) return true; return false; } processMidiEvent(event) { const channel = event.data[0] & 0xf; const controlNumber = event.data[1]; if (this.midiMode == "learn") { this.setMidiController(channel, controlNumber); webAudioControlsMidiManager.contextMenuClose(); this.midiMode = "normal"; } if (this.listeningToThisMidiController(channel, controlNumber)) { if (this.tagName == "WEBAUDIO-SWITCH") { switch (this.type) { case "toggle": if (event.data[2] >= 64) this.setValue(1 - this.value, true); break; case "kick": this.setValue(event.data[2] >= 64 ? 1 : 0); break; case "radio": let els = document.querySelectorAll("webaudio-switch[type='radio'][group='" + this.group + "']"); for (let i = 0; i < els.length; ++i) { if (els[i] == this) els[i].setValue(1); else els[i].setValue(0); } break; } } else { const val = this.min + ((this.max - this.min) * event.data[2]) / 127; this.setValue(val, true); } } } } try { customElements.define( "webaudio-knob", class WebAudioKnob extends WebAudioControlsWidget { constructor() { super(); } connectedCallback() { let root; // if(this.attachShadow) // root=this.attachShadow({mode: 'open'}); // else root = this; root.innerHTML = `<style> ${this.basestyle} webaudio-knob{ display:inline-block; position:relative; margin:0; padding:0; cursor:pointer; font-family: sans-serif; font-size: 11px; } .webaudio-knob-body{ display:inline-block; position:relative; z-index:1; margin:0; padding:0; } </style> <div class='webaudio-knob-body' tabindex='1' touch-action='none'></div><div class='webaudioctrl-tooltip'></div> `; this.elem = root.childNodes[2]; this.ttframe = root.childNodes[3]; this.enable = this.getAttr("enable", 1); this._src = this.getAttr("src", opt.knobSrc); if (!this.hasOwnProperty("src")) Object.defineProperty(this, "src", { get: () => { return this._src; }, set: (v) => { this._src = v; this.setupImage(); }, }); this._value = this.getAttr("value", 0); if (!this.hasOwnProperty("value")) Object.defineProperty(this, "value", { get: () => { return this._value; }, set: (v) => { this._value = v; this.redraw(); }, }); this.defvalue = this.getAttr("defvalue", 0); this._min = this.getAttr("min", 0); if (!this.hasOwnProperty("min")) Object.defineProperty(this, "min", { get: () => { return this._min; }, set: (v) => { this._min = +v; this.redraw(); }, }); this._max = this.getAttr("max", 100); if (!this.hasOwnProperty("max")) Object.defineProperty(this, "max", { get: () => { return this._max; }, set: (v) => { this._max = +v; this.redraw(); }, }); this._step = this.getAttr("step", 1); if (!this.hasOwnProperty("step")) Object.defineProperty(this, "step", { get: () => { return this._step; }, set: (v) => { this._step = +v; this.redraw(); }, }); this._sprites = this.getAttr("sprites", opt.knobSprites); if (!this.hasOwnProperty("sprites")) Object.defineProperty(this, "sprites", { get: () => { return this._sprites; }, set: (v) => { this._sprites = v; this.setupImage(); }, }); this._width = this.getAttr("width", opt.knobWidth); if (!this.hasOwnProperty("width")) Object.defineProperty(this, "width", { get: () => { return this._width; }, set: (v) => { this._width = v; this.setupImage(); }, }); this._height = this.getAttr("height", opt.knobHeight); if (!this.hasOwnProperty("height")) Object.defineProperty(this, "height", { get: () => { return this._height; }, set: (v) => { this._height = v; this.setupImage(); }, }); this._diameter = this.getAttr("diameter", opt.knobDiameter); if (!this.hasOwnProperty("diameter")) Object.defineProperty(this, "diameter", { get: () => { return this._diameter; }, set: (v) => { this._diameter = v; this.setupImage(); }, }); this._colors = this.getAttr("colors", opt.knobColors); if (!this.hasOwnProperty("colors")) Object.defineProperty(this, "colors", { get: () => { return this._colors; }, set: (v) => { this._colors = v; this.setupImage(); }, }); this.outline = this.getAttr("outline", opt.outline); this.sensitivity = this.getAttr("sensitivity", 1); this.valuetip = this.getAttr("valuetip", 1); this.tooltip = this.getAttr("tooltip", null); this.conv = this.getAttr("conv", null); if (this.conv) this.convValue = eval(this.conv)(this._value); else this.convValue = this._value; this.midilearn = this.getAttr("midilearn", opt.midilearn); this.midicc = this.getAttr("midicc", null); this.midiController = {}; this.midiMode = "normal"; if (this.midicc) { let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf("."))) - 1; let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(".") + 1)); this.setMidiController(ch, cc); } this.setupImage(); this.digits = 0; this.coltab = ["#e00", "#000", "#000"]; if (window.webAudioControlsMidiManager) // window.webAudioControlsMidiManager.updateWidgets(); window.webAudioControlsMidiManager.addWidget(this); } disconnectedCallback() {} setupImage() { this.kw = this.width || this.diameter; this.kh = this.height || this.diameter; if (!this.src) { if (this.colors) this.coltab = this.colors.split(";"); if (!this.coltab) this.coltab = ["#e00", "#000", "#000"]; let svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="6464" preserveAspectRatio="none"> <radialGradient id="gr" cx="30%" cy="30%"><stop offset="0%" stop-color="${this.coltab[2]}"/><stop offset="100%" stop-color="${this.coltab[1]}"/></radialGradient> <defs><circle id="B" cx="32" cy="32" r="30" fill="url(#gr)"/></defs> <defs><line id="K" x1="32" y1="28" x2="32" y2="7" stroke-linecap="round" stroke-width="6" stroke="${this.coltab[0]}"/></defs>`; for (let i = 0; i < 101; ++i) { svg += `<use xlink:href="#B" y="${64 * i}"/>`; svg += `<use xlink:href="#K" y="${64 * i}" transform="rotate(${(-135 + (270 * i) / 101).toFixed(2)},32,${ 64 * i + 32 })"/>`; } svg += "</svg>"; this.elem.style.backgroundImage = "url(data:image/svg+xml;base64," + btoa(svg) + ")"; // this.elem.style.backgroundSize = "100% 10100%"; this.elem.style.backgroundSize = `${this.kw}px ${this.kh * 101}px`; } else { this.elem.style.backgroundImage = "url(" + this.src + ")"; if (!this.sprites) this.elem.style.backgroundSize = "100% 100%"; else { // this.elem.style.backgroundSize = `100% ${(this.sprites+1)*100}%`; this.elem.style.backgroundSize = `${this.kw}px ${this.kh * (this.sprites + 1)}px`; } } this.elem.style.outline = this.outline ? "" : "none"; this.elem.style.width = this.kw + "px"; this.elem.style.height = this.kh + "px"; this.redraw(); } redraw() { this.digits = 0; if (this.step && this.step < 1) { for (let n = this.step; n < 1; n *= 10) ++this.digits; } if (this.value < this.min) { this.value = this.min; return; } if (this.value > this.max) { this.value = this.max; return; } let range = this.max - this.min; let style = this.elem.style; let sp = this.src ? this.sprites : 100; if (sp >= 1) { let offset = ((sp * (this.value - this.min)) / range) | 0; style.backgroundPosition = "0px " + -offset * this.kh + "px"; style.transform = "rotate(0deg)"; } else { let deg = 270 * ((this.value - this.min) / range - 0.5); style.backgroundPosition = "0px 0px"; style.transform = "rotate(" + deg + "deg)"; } } _setValue(v) { if (this.step) v = Math.round((v - this.min) / this.step) * this.step + this.min; this._value = Math.min(this.max, Math.max(this.min, v)); if (this._value != this.oldvalue) { this.oldvalue = this._value; if (this.conv) this.convValue = eval(this.conv)(this._value); else this.convValue = this._value; this.redraw(); this.showtip(0); return 1; } return 0; } setValue(v, f) { if (this._setValue(v) && f) this.sendEvent("input"), this.sendEvent("change"); } wheel(e) { let delta = (this.max - this.min) * 0.01; delta = e.deltaY > 0 ? -delta : delta; if (!e.shiftKey) delta *= 5; if (Math.abs(delta) < this.step) delta = delta > 0 ? +this.step : -this.step; this.setValue(+this.value + delta, true); e.preventDefault(); e.stopPropagation(); } pointerdown(ev) { if (!this.enable) return; let e = ev; if (ev.touches) { e = ev.changedTouches[0]; this.identifier = e.identifier; } else { if (e.buttons != 1 && e.button != 0) return; } this.elem.focus(); this.drag = 1; this.showtip(0); let pointermove = (ev) => { let e = ev; if (ev.touches) { for (let i = 0; i < ev.touches.length; ++i) { if (ev.touches[i].identifier == this.identifier) { e = ev.touches[i]; break; } } } if (this.lastShift !== e.shiftKey) { this.lastShift = e.shiftKey; this.startPosX = e.pageX; this.startPosY = e.pageY; this.startVal = this.value; } let offset = (this.startPosY - e.pageY - this.startPosX + e.pageX) * this.sensitivity; this._setValue( this.min + (((this.startVal + ((this.max - this.min) * offset) / ((e.shiftKey ? 4 : 1) * 128) - this.min) / this.step) | 0) * this.step ); this.sendEvent("input"); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }; let pointerup = (ev) => { let e = ev; if (ev.touches) { for (let i = 0; ; ) { if (ev.changedTouches[i].identifier == this.identifier) { break; } if (++i >= ev.changedTouches.length) return; } } this.drag = 0; this.showtip(0); this.startPosX = this.startPosY = null; window.removeEventListener("mousemove", pointermove); window.removeEventListener("touchmove", pointermove, { passive: false }); window.removeEventListener("mouseup", pointerup); window.removeEventListener("touchend", pointerup); window.removeEventListener("touchcancel", pointerup); document.body.removeEventListener("touchstart", preventScroll, { passive: false }); this.sendEvent("change"); }; let preventScroll = (e) => { e.preventDefault(); }; if (e.ctrlKey || e.metaKey) this.setValue(this.defvalue, true); else { this.startPosX = e.pageX; this.startPosY = e.pageY; this.startVal = this.value; window.addEventListener("mousemove", pointermove); window.addEventListener("touchmove", pointermove, { passive: false }); } window.addEventListener("mouseup", pointerup); window.addEventListener("touchend", pointerup); window.addEventListener("touchcancel", pointerup); document.body.addEventListener("touchstart", preventScroll, { passive: false }); ev.preventDefault(); ev.stopPropagation(); return false; } } ); } catch (error) { //console.log("webaudio-knob already defined"); } try { customElements.define( "webaudio-slider", class WebAudioSlider extends WebAudioControlsWidget { constructor() { super(); } connectedCallback() { let root; // if(this.attachShadow) // root=this.attachShadow({mode: 'open'}); // else root = this; root.innerHTML = `<style> ${this.basestyle} webaudio-slider{ display:inline-block; position:relative; margin:0; padding:0; font-family: sans-serif; font-size: 11px; cursor:pointer; } .webaudio-slider-body{ display:inline-block; position:relative; margin:0; padding:0; } .webaudio-slider-knob{ display:inline-block; position:absolute; margin:0; padding:0; } </style> <div class='webaudio-slider-body' tabindex='1' touch-action='none'><div class='webaudio-slider-knob' touch-action='none'></div></div><div class='webaudioctrl-tooltip'></div> `; this.elem = root.childNodes[2]; this.knob = this.elem.childNodes[0]; this.ttframe = root.childNodes[3]; this.enable = this.getAttr("enable", 1); this._src = this.getAttr("src", opt.sliderSrc); if (!this.hasOwnProperty("src")) Object.defineProperty(this, "src", { get: () => { return this._src; }, set: (v) => { this._src = v; this.setupImage(); }, }); this._knobsrc = this.getAttr("knobsrc", opt.sliderKnobsrc); if (!this.hasOwnProperty("knobsrc")) Object.defineProperty(this, "knobsrc", { get: () => { return this._knobsrc; }, set: (v) => { this._knobsrc = v; this.setupImage(); }, }); this._value = this.getAttr("value", 0); if (!this.hasOwnProperty("value")) Object.defineProperty(this, "value", { get: () => { return this._value; }, set: (v) => { this._value = v; this.redraw(); }, }); this.defvalue = this.getAttr("defvalue", 0); this._min = this.getAttr("min", 0); if (!this.hasOwnProperty("min")) Object.defineProperty(this, "min", { get: () => { return this._min; }, set: (v) => { this._min = v; this.redraw(); }, }); this._max = this.getAttr("max", 100); if (!this.hasOwnProperty("max")) Object.defineProperty(this, "max", { get: () => { return this._max; }, set: (v) => { this._max = v; this.redraw(); }, }); this._step = this.getAttr("step", 1); if (!this.hasOwnProperty("step")) Object.defineProperty(this, "step", { get: () => { return this._step; }, set: (v) => { this._step = v; this.redraw(); }, }); this._sprites = this.getAttr("sprites", 0); if (!this.hasOwnProperty("sprites")) Object.defineProperty(this, "sprites", { get: () => { return this._sprites; }, set: (v) => { this._sprites = v; this.setupImage(); }, }); this._direction = this.getAttr("direction", null); if (!this.hasOwnProperty("direction")) Object.defineProperty(this, "direction", { get: () => { return this._direction; }, set: (v) => { this._direction = v; this.setupImage(); }, }); this._width = this.getAttr("width", opt.sliderWidth); if (!this.hasOwnProperty("width")) Object.defineProperty(this, "width", { get: () => { return this._width; }, set: (v) => { this._width = v; this.setupImage(); }, }); this._height = this.getAttr("height", opt.sliderHeight); if (!this.hasOwnProperty("height")) Object.defineProperty(this, "height", { get: () => { return this._height; }, set: (v) => { this._height = v; this.setupImage(); }, }); if (this._direction == "horz") { if (this._width == 0) this._width = 128; if (this._height == 0) this._height = 24; } else { if (this._width == 0) this._width = 24; if (this._height == 0) this._height = 128; } this._knobwidth = this.getAttr("knobwidth", opt.sliderKnobwidth); Object.defineProperty(this, "knobwidth", { get: () => { return this._knobwidth; }, set: (v) => { this._knobwidth = v; this.setupImage(); }, }); this._knobheight = this.getAttr("knbheight", opt.sliderKnobheight); Object.defineProperty(this, "knobheight", { get: () => { return this._knobheight; }, set: (v) => { this._knobheight = v; this.setupImage(); }, }); this._ditchlength = this.getAttr("ditchlength", opt.sliderDitchlength); Object.defineProperty(this, "ditchlength", { get: () => { return this._ditchlength; }, set: (v) => { this._ditchlength = v; this.setupImage(); }, }); this._colors = this.getAttr("colors", opt.sliderColors); Object.defineProperty(this, "colors", { get: () => { return this._colors; }, set: (v) => { this._colors = v; this.setupImage(); }, }); this.outline = this.getAttr("outline", opt.outline); this.sensitivity = this.getAttr("sensitivity", 1); this.valuetip = this.getAttr("valuetip", 1); this.tooltip = this.getAttr("tooltip", null); this.conv = this.getAttr("conv", null); if (this.conv) this.convValue = eval(this.conv)(this._value); else this.convValue = this._value; this.midilearn = this.getAttr("midilearn", opt.midilearn); this.midicc = this.getAttr("midicc", null); this.midiController = {}; this.midiMode = "normal"; if (this.midicc) { let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf("."))) - 1; let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(".") + 1)); this.setMidiController(ch, cc); } this.setupImage(); this.digits = 0; if (window.webAudioControlsMidiManager) // window.webAudioControlsMidiManager.updateWidgets(); window.webAudioControlsMidiManager.addWidget(this); this.elem.onclick = (e) => { e.stopPropagation(); }; } disconnectedCallback() {} setupImage() { this.coltab = this.colors.split(";"); this.dr = this.direction; this.dlen = this.ditchlength; if (!this.width) { if (this.dr == "horz") this.width = 128; else this.width = 24; } if (!this.height) { if (this.dr == "horz") this.height = 24; else this.height = 128; } if (!this.dr) this.dr = this.width <= this.height ? "vert" : "horz"; if (this.dr == "vert") { if (!this.dlen) this.dlen = this.height - this.width; } else { if (!this.dlen) this.dlen = this.width - this.height; } this.knob.style.backgroundSize = "100% 100%"; this.elem.style.backgroundSize = "100% 100%"; this.elem.style.width = this.width + "px"; this.elem.style.height = this.height + "px"; this.kwidth = this.knobwidth || (this.dr == "horz" ? this.height : this.width); this.kheight = this.knobheight || (this.dr == "horz" ? this.height : this.width); this.knob.style.width = this.kwidth + "px"; this.knob.style.height = this.kheight + "px"; if (!this.src) { let r = Math.min(this.width, this.height) * 0.5; let svgbody = `<svg xmlns="http://www.w3.org/2000/svg" width="${this.width}" height="${ this.height }" preserveAspectRatio="none"> <rect x="1" y="1" rx="${r}" ry="${r}" width="${this.width - 2}" height="${this.height - 2}" fill="${ this.coltab[1] }"/></svg>`; this.elem.style.backgroundImage = "url(data:image/svg+xml;base64," + btoa(svgbody) + ")"; } else { this.elem.style.backgroundImage = "url(" + this.src + ")"; } if (!this.knobsrc) { let svgthumb = `<svg xmlns="http://www.w3.org/2000/svg" width="${this.kwidth}" height="${ this.kheight }" preserveAspectRatio="none"> <radialGradient id="gr" cx="30%" cy="30%"><stop offset="0%" stop-color="${ this.coltab[2] }"/><stop offset="100%" stop-color="${this.coltab[0]}"/></radialGradient> <rect x="2" y="2" width="${this.kwidth - 4}" height="${this.kheight - 4}" rx="${this.kwidth * 0.5}" ry="${ this.kheight * 0.5 }" fill="url(#gr)"/></svg>`; this.knob.style.backgroundImage = "url(data:image/svg+xml;base64," + btoa(svgthumb) + ")"; } else { this.knob.style.backgroundImage = "url(" + this.knobsrc + ")"; } this.elem.style.outline = this.outline ? "" : "none"; this.redraw(); } redraw() { this.digits = 0; if (this.step && this.step < 1) { for (let n = this.step; n < 1; n *= 10) ++this.digits; } if (this.value < this.min) { this.value = this.min; return; } if (this.value > this.max) { this.value = this.max; return; } let range = this.max - this.min; let style = this.knob.style; if (this.dr == "vert") { style.left = (this.width - this.kwidth) * 0.5 + "px"; style.top = (1 - (this.value - this.min) / range) * this.dlen + "px"; this.sensex = 0; this.sensey = 1; } else { style.top = (this.height - this.kheight) * 0.5 + "px"; style.left = ((this.value - this.min) / range) * this.dlen + "px"; this.sensex = 1; this.sensey = 0; } } _setValue(v) { v = Math.round((v - this.min) / this.step) * this.step + this.min; this._value = Math.min(this.max, Math.max(this.min, v)); if (this._value != this.oldvalue) { this.oldvalue = this._value; if (this.conv) this.convValue = eval(this.conv)(this._value); else this.convValue = this._value; this.redraw(); this.showtip(0); return 1; } return 0; } setValue(v, f) { if (this._setValue(v) && f) this.sendEvent("input"), this.sendEvent("change"); } wheel(e) { let delta = (this.max - this.min) * 0.01; delta = e.deltaY > 0 ? -delta : delta; if (!e.shiftKey) delta *= 5; if (Math.abs(delta) < this.step) delta = delta > 0 ? +this.step : -this.step; this.setValue(+this.value + delta, true); e.preventDefault(); e.stopPropagation(); this.redraw(); } pointerdown(ev) { if (!this.enable) return; let e = ev; if (ev.touches) { e = ev.changedTouches[0]; this.identifier = e.identifier; } else { if (e.buttons != 1 && e.button != 0) return; } this.elem.focus(); this.drag = 1; this.showtip(0); let pointermove = (ev) => { let e = ev; if (ev.touches) { for (let i = 0; i < ev.touches.length; ++i) { if (ev.touches[i].identifier == this.identifier) { e = ev.touches[i]; break; } } } if (this.lastShift !== e.shiftKey) { this.lastShift = e.shiftKey; this.startPosX = e.pageX; this.startPosY = e.pageY; this.startVal = this.value; } let offset = ((this.startPosY - e.pageY) * this.sensey - (this.startPosX - e.pageX) * this.sensex) * this.sensitivity; this._setValue( this.min + (((this.startVal + ((this.max - this.min) * offset) / ((e.shiftKey ? 4 : 1) * this.dlen) - this.min) / this.step) | 0) * this.step ); this.sendEvent("input"); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }; let pointerup = (ev) => { let e = ev; if (ev.touches) { for (let i = 0; ; ) { if (ev.changedTouches[i].identifier == this.identifier) { break; } if (++i >= ev.changedTouches.length) return; } } this.drag = 0; this.showtip(0); this.startPosX = this.startPosY = null; window.removeEventListener("mousemove", pointermove); window.removeEventListener("touchmove", pointermove, { passive: false }); window.removeEventListener("mouseup", pointerup); window.removeEventListener("touchend", pointerup); window.removeEventListener("touchcancel", pointerup); document.body.removeEventListener("touchstart", preventScroll, { passive: false }); this.sendEvent("change"); }; let preventScroll = (e) => { e.preventDefault(); }; if (e.touches) e = e.touches[0]; if (e.ctrlKey || e.metaKey) this.setValue(this.defvalue, true); else { this.startPosX = e.pageX; this.startPosY = e.pageY; this.startVal = this.value; window.addEventListener("mousemove", pointermove); window.addEventListener("touchmove", pointermove, { passive: false }); } window.addEventListener("mouseup", pointerup); window.addEventListener("touchend", pointerup); window.addEventListener("touchcancel", pointerup); document.body.addEventListener("touchstart", preventScroll, { passive: false }); e.preventDefault(); e.stopPropagation(); return false; } } ); } catch (error) { //console.log("webaudio-slider already defined"); } try { customElements.define( "webaudio-switch", class WebAudioSwitch extends WebAudioControlsWidget { constructor() { super(); } connectedCallback() { let root; // if(this.attachShadow) // root=this.attachShadow({mode: 'open'}); // else root = this; root.innerHTML = `<style> ${this.basestyle} webaudio-switch{ display:inline-block; margin:0; padding:0; font-family: sans-serif; font-size: 11px; cursor:pointer; } .webaudio-switch-body{ display:inline-block; margin:0; padding:0; } </style> <div class='webaudio-switch-body' tabindex='1' touch-action='none'><div class='webaudioctrl-tooltip'></div></div> `; this.elem = root.childNodes[2]; this.ttframe = this.elem.childNodes[0]; this.enable = this.getAttr("enable", 1); this._src = this.getAttr("src", null); if (!this.hasOwnProperty("src")) Object.defineProperty(this, "src", { get: () => { return this._src; }, set: (v) => { this._src = v; this.setupImage(); }, }); this._value = this.getAttr("value", 0); if (!this.hasOwnProperty("value")) Object.defineProperty(this, "value", { get: () => { return this._value; }, set: (v) => { this._value = v; this.redraw(); }, }); this.defvalue = this.getAttr("defvalue", 0); this.type = this.getAttr("type", "toggle"); this.group = this.getAttr("group", ""); this._width = this.getAttr("width", 0); if (!this.hasOwnProperty("width")) Object.defineProperty(this, "width", { get: () => { return this._width; }, set: (v) => { this._width = v; this.setupImage(); }, }); this._height = this.getAttr("height", 0); if (!this.hasOwnProperty("height")) Object.defineProperty(this, "height", { get: () => { return this._height; }, set: (v) => { this._height = v; this.setupImage(); }, }); this._diameter = this.getAttr("diameter", 0); if (!this.hasOwnProperty("diameter")) Object.defineProperty(this, "diameter", { get: () => { return this._diameter; }, set: (v) => { this._diameter = v; this.setupImage(); }, }); this.invert = this.getAttr("invert", 0); this._colors = this.getAttr("colors", opt.switchColors); if (!this.hasOwnProperty("colors")) Object.defineProperty(this, "colors", { get: () => { return this._colors; }, set: (v) => { this._colors = v; this.setupImage(); }, }); this.outline = this.getAttr("outline", opt.outline); this.valuetip = 0; this.tooltip = this.getAttr("tooltip", null); this.midilearn = this.getAttr("midilearn", opt.midilearn); this.midicc = this.getAttr("midicc", null); this.midiController = {}; this.midiMode = "normal"; if (this.midicc) { let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf("."))) - 1; let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(".") + 1)); this.setMidiController(ch, cc); } this.setupImage(); this.digits = 0; if (window.webAudioControlsMidiManager) // window.webAudioControlsMidiManager.updateWidgets(); window.webAudioControlsMidiManager.addWidget(this); this.elem.onclick = (e) => { e.stopPropagation(); }; } disconnectedCallback() {} setupImage() { let w = this.width || this.diameter || opt.switchWidth || opt.switchDiameter; let h = this.height || this.diameter || opt.switchHeight || opt.switchDiameter; if (!this.src) { this.coltab = this.colors.split(";"); let mm = Math.min(w, h); let svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${ h * 2 }" preserveAspectRatio="none"> <radialGradient id="gr" cx="30%" cy="30%"><stop offset="0%" stop-color="${ this.coltab[2] }"/><stop offset="100%" stop-color="${this.coltab[0]}"/></radialGradient> <rect x="${w * 0.05}" y="${h * 0.05}" width="${w * 0.9}" height="${h * 0.9}" rx="${mm * 0.1}" ry="${mm * 0.1}" fill="${ this.coltab[1] }"/> <rect x="${w * 0.05}" y="${h * 1.05}" width="${w * 0.9}" height="${h * 0.9}" rx="${mm * 0.1}" ry="${mm * 0.1}" fill="${ this.coltab[1] }"/> <circle cx="${w * 0.5}" cy="${h * 0.5}" r="${mm * 0.3}" stroke="${this.coltab[0]}" stroke-width="2"/> <circle cx="${w * 0.5}" cy="${h * 1.5}" r="${mm * 0.3}" stroke="${ this.coltab[0] }" stroke-width="2" fill="url(#gr)"/></svg>`; this.elem.style.backgroundImage = "url(data:image/svg+xml;base64," + btoa(svg) + ")"; this.elem.style.backgroundSize = "100% 200%"; } else { this.elem.style.backgroundImage = "url(" + this.src + ")"; if (!this.sprites) this.elem.style.backgroundSize = "100% 200%"; else this.elem.style.backgroundSize = `100% ${(this.sprites + 1) * 100}%`; } this.elem.style.width = w + "px"; this.elem.style.height = h + "px"; this.elem.style.outline = this.outline ? "" : "none"; this.redraw(); } redraw() { let style = this.elem.style; if (this.value ^ this.invert) style.backgroundPosition = "0px -100%"; else style.backgroundPosition = "0px 0px"; } setValue(v, f) { console.log(v, f); this.value = v; this.checked = !!v; if (this.value != this.oldvalue) { this.redraw(); this.showtip(0); if (f) { this.sendEvent("input"); this.sendEvent("change"); } this.oldvalue = this.value; } } pointerdown(ev) { if (!this.enable) return; let e = ev; if (ev.touches) { e = ev.changedTouches[0]; this.identifier = e.identifier; } else { if (e.buttons != 1 && e.button != 0) return; } this.elem.focus(); this.drag = 1; this.showtip(0); let pointermove = (e) => { e.preventDefault(); e.stopPropagation(); return false; }; let pointerup = (e) => { this.drag = 0; this.showtip(0); window.removeEventListener("mousemove", pointermove); window.removeEventListener("touchmove", pointermove, { passive: false }); window.removeEventListener("mouseup", pointerup); window.removeEventListener("touchend", pointerup); window.removeEventListener("touchcancel", pointerup); document.body.removeEventListener("touchstart", preventScroll, { passive: false }); if (this.type == "kick") { this.value = 0; this.checked = false; this.redraw(); this.sendEvent("change"); } this.sendEvent("click"); e.preventDefault(); e.stopPropagation(); }; let preventScroll = (e) => { e.preventDefault(); }; switch (this.type) { case "kick": this.setValue(1); this.sendEvent("change"); break; case "toggle": if (e.ctrlKey || e.metaKey) this.value = defvalue; else this.value = 1 - this.value; this.checked = !!this.value; this.sendEvent("change"); break; case "radio": let els = document.querySelectorAll("webaudio-switch[type='radio'][group='" + this.group + "']"); for (let i = 0; i < els.length; ++i) { if (els[i] == this) els[i].setValue(1); else els[i].setValue(0); } this.sendEvent("change"); break; } window.addEventListener("mouseup", pointerup); window.addEventListener("touchend", pointerup); window.addEventListener("touchcancel", pointerup); document.body.addEventListener("touchstart", preventScroll, { passive: false }); this.redraw(); e.preventDefault(); e.stopPropagation(); return false; } } ); } catch (error) { //console.log("webaudio-switch already defined"); } try { customElements.define( "webaudio-param", class WebAudioParam extends WebAudioControlsWidget { constructor() { super(); this.addEventListener("keydown", this.keydown); this.addEventListener("mousedown", this.pointerdown, { passive: false }); this.addEventListener("touchstart", this.pointerdown, { passive: false }); this.addEventListener("wheel", this.wheel); this.addEventListener("mouseover", this.pointerover); this.addEventListener("mouseout", this.pointerout); this.addEventListener("contextmenu", this.contextMenu); } connectedCallback() { let root; // if(this.attachShadow) // root=this.attachShadow({mode: 'open'}); // else root = this; root.innerHTML = `<style> ${this.basestyle} webaudio-param{ display:inline-block; user-select:none; margin:0; padding:0; font-family: sans-serif; font-size: 8px; cursor:pointer; } .webaudio-param-body{ display:inline-block; text-align:center; vertical-align:middle; position:relative; border:1px solid #888; background:none; border-radius:4px; margin:0; padding:0; font-family:sans-serif; font-size:11px; } </style> <input class='webaudio-param-body' value='0' tabindex='1' touch-action='none'/><div class='webaudioctrl-tooltip'></div> `; this.elem = root.childNodes[2]; this.ttframe = root.childNodes[3]; this.enable = this.getAttr("enable", 1); this._value = this.getAttr("value", 0); if (!this.hasOwnProperty("value")) Object.defineProperty(this, "value", { get: () => { return this._value; }, set: (v) => { this._value = v; this.redraw(); }, }); this.defvalue = this.getAttr("defvalue", 0); this._fontsize = this.getAttr("fontsize", 9); if (!this.hasOwnProperty("fontsize")) Object.defineProperty(this, "fontsize", { get: () => { return this._fontsize; }, set: (v) => { this._fontsize = v; this.setupImage(); }, }); this._src = this.getAttr("src", null); if (!this.hasOwnProperty("src")) Object.defineProperty(this, "src", { get: () => { return this._src; }, set: (v) => { this._src = v; this.setupImage(); }, }); this.link = this.getAttr("link", ""); this._width = this.getAttr("width", 32); if (!this.hasOwnProperty("width")) Object.defineProperty(this, "width", {