iamdjbones/ethervox-vocal-processor
0
1class EtherPlugin extends HTMLElement {2 constructor() {3 super();4 this.attachShadow({ mode: 'open' });5 this.shadowRoot.innerHTML = `6 <style>7 :host {8 display: block;9 max-width: 320px;10 background: linear-gradient(145deg, #083344, #0e7490);11 border-radius: 12px;12 padding: 20px;13 box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);14 font-family: 'Inter', sans-serif;15 }16 17 .plugin-container {18 display: flex;19 flex-direction: column;20 gap: 20px;21 }22 23 .title {24 color: #f97316;25 text-align: center;26 font-size: 24px;27 font-weight: 700;28 margin-bottom: 10px;29 text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);30 }31 32 .knob-container {33 display: flex;34 flex-direction: column;35 align-items: center;36 }37 38 .knob {39 width: 70px;40 height: 70px;41 border-radius: 50%;42 background: linear-gradient(145deg, #0f172a, #1e293b);43 position: relative;44 box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);45 margin-bottom: 10px;46 }47 48 .knob::after {49 content: '';50 position: absolute;51 top: 5px;52 left: 5px;53 right: 5px;54 bottom: 5px;55 background: linear-gradient(145deg, #1e293b, #0f172a);56 border-radius: 50%;57 box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5);58 }59 60 .knob-indicator {61 position: absolute;62 top: 10%;63 left: 50%;64 width: 3px;65 height: 20%;66 background: #f97316;67 transform-origin: bottom center;68 transform: translateX(-50%) rotate(0deg);69 z-index: 1;70 }71 72 .knob-label {73 color: #f8fafc;74 font-size: 14px;75 font-weight: 500;76 text-transform: uppercase;77 letter-spacing: 1px;78 }79 80 .knob-value {81 color: #f97316;82 font-size: 12px;83 font-weight: 600;84 margin-top: 4px;85 }86 </style>87 88 <div class="plugin-container">89 <div class="title">ETHER</div>90 91 <div class="knob-container">92 <div class="knob" id="ether">93 <div class="knob-indicator" id="ether-indicator"></div>94 </div>95 <div class="knob-label">Compression</div>96 <div class="knob-value" id="ether-value">50%</div>97 </div>98 99 <div class="knob-container">100 <div class="knob" id="gas">101 <div class="knob-indicator" id="gas-indicator"></div>102 </div>103 <div class="knob-label">Brightness</div>104 <div class="knob-value" id="gas-value">50%</div>105 </div>106 107 <div class="knob-container">108 <div class="knob" id="fire">109 <div class="knob-indicator" id="fire-indicator"></div>110 </div>111 <div class="knob-label">Body</div>112 <div class="knob-value" id="fire-value">50%</div>113 </div>114 </div>115 `;116 117 this.etherValue = 50;118 this.gasValue = 50;119 this.fireValue = 50;120 }121 122 connectedCallback() {123 this.setupKnob('ether', 'ether-indicator', 'ether-value', value => {124 this.etherValue = value;125 this.updateAudioProcessing();126 });127 128 this.setupKnob('gas', 'gas-indicator', 'gas-value', value => {129 this.gasValue = value;130 this.updateAudioProcessing();131 });132 133 this.setupKnob('fire', 'fire-indicator', 'fire-value', value => {134 this.fireValue = value;135 this.updateAudioProcessing();136 });137 }138 139 setupKnob(knobId, indicatorId, valueId, callback) {140 const knob = this.shadowRoot.getElementById(knobId);141 const indicator = this.shadowRoot.getElementById(indicatorId);142 const valueDisplay = this.shadowRoot.getElementById(valueId);143 let isDragging = false;144 145 const rotateKnob = (e, centerX, centerY) => {146 if (!isDragging) return;147 148 const rect = knob.getBoundingClientRect();149 const center = {150 x: centerX || rect.left + rect.width / 2,151 y: centerY || rect.top + rect.height / 2152 };153 154 const angle = Math.atan2(e.clientY - center.y, e.clientX - center.x) * (180 / Math.PI) + 90;155 const normalizedAngle = (angle < 0 ? angle + 360 : angle) % 360;156 const value = Math.min(100, Math.max(0, Math.round(normalizedAngle / 3.6)));157 158 indicator.style.transform = `translateX(-50%) rotate(${normalizedAngle}deg)`;159 valueDisplay.textContent = `${value}%`;160 callback(value);161 };162 163 knob.addEventListener('mousedown', (e) => {164 isDragging = true;165 rotateKnob(e);166 });167 168 window.addEventListener('mousemove', rotateKnob);169 window.addEventListener('mouseup', () => {170 isDragging = false;171 });172 173 knob.addEventListener('touchstart', (e) => {174 isDragging = true;175 const touch = e.touches[0];176 const rect = knob.getBoundingClientRect();177 rotateKnob(touch, rect.left + rect.width / 2, rect.top + rect.height / 2);178 e.preventDefault();179 });180 181 window.addEventListener('touchmove', (e) => {182 if (isDragging) {183 const touch = e.touches[0];184 const rect = knob.getBoundingClientRect();185 rotateKnob(touch, rect.left + rect.width / 2, rect.top + rect.height / 2);186 e.preventDefault();187 }188 });189 190 window.addEventListener('touchend', () => {191 isDragging = false;192 });193 }194 195 updateAudioProcessing() {196 // In a real VST plugin, this would interface with the audio processing code197 console.log(`Processing audio with Ether: ${this.etherValue}%, Gas: ${this.gasValue}%, Fire: ${this.fireValue}%`);198 }199}200 201customElements.define('ether-plugin', EtherPlugin);