wam-community
Version:
A collection of prebuilt Web Audio Modules ready for use
1,400 lines (1,392 loc) • 70.5 kB
JavaScript
/* *
*
* 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", {