style: auto-fix linting issues
This commit is contained in:
parent
7c568030af
commit
a308d380b9
2 changed files with 3990 additions and 2835 deletions
464
bc-demo.html
464
bc-demo.html
|
|
@ -1,241 +1,245 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>Butterchurn Demo Fixed</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/normalize.css/normalize.css" />
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Butterchurn Demo Fixed</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://unpkg.com/normalize.css/normalize.css" />
|
||||
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
background: black;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
#mainWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#audioSelectWrapper,
|
||||
#presetControls {
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
#presetSelect {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
#presetCycleLength {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="mainWrapper">
|
||||
<div id="audioSelectWrapper">
|
||||
<button id="localFileBut">Load local files</button>
|
||||
<button id="micSelect">Use Mic</button>
|
||||
</div>
|
||||
|
||||
<div id="presetControls">
|
||||
<label>Preset: <select id="presetSelect"></select></label>
|
||||
<label>Cycle: <input type="checkbox" id="presetCycle" checked></label>
|
||||
<label>Seconds: <input type="number" id="presetCycleLength" value="15" min="1"></label>
|
||||
<label>Random: <input type="checkbox" id="presetRandom" checked></label>
|
||||
</div>
|
||||
|
||||
<canvas id="canvas" width="800" height="600"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Load Butterchurn and presets -->
|
||||
<script type="module">
|
||||
import butterchurn from 'https://unpkg.com/butterchurn@3.0.0-beta.5/dist/butterchurn.js';
|
||||
|
||||
let audioContext = null;
|
||||
let visualizer = null;
|
||||
let sourceNode = null;
|
||||
let delayedAudible = null;
|
||||
let cycleInterval = null;
|
||||
|
||||
let presets = {};
|
||||
let presetKeys = [];
|
||||
let presetIndexHist = [];
|
||||
let presetIndex = 0;
|
||||
let presetCycle = true;
|
||||
let presetCycleLength = 15000;
|
||||
let presetRandom = true;
|
||||
|
||||
const canvas = document.getElementById('canvas');
|
||||
|
||||
function startRenderer() {
|
||||
requestAnimationFrame(startRenderer);
|
||||
visualizer.render();
|
||||
}
|
||||
|
||||
function connectToAudioAnalyzer(node) {
|
||||
if (delayedAudible) delayedAudible.disconnect();
|
||||
delayedAudible = audioContext.createDelay();
|
||||
delayedAudible.delayTime.value = 0.26;
|
||||
node.connect(delayedAudible);
|
||||
delayedAudible.connect(audioContext.destination);
|
||||
visualizer.connectAudio(delayedAudible);
|
||||
}
|
||||
|
||||
function playBufferSource(buffer) {
|
||||
if (sourceNode) sourceNode.disconnect();
|
||||
sourceNode = audioContext.createBufferSource();
|
||||
sourceNode.buffer = buffer;
|
||||
connectToAudioAnalyzer(sourceNode);
|
||||
sourceNode.start();
|
||||
}
|
||||
|
||||
function loadLocalFiles(files, index = 0) {
|
||||
audioContext.resume();
|
||||
const reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
audioContext.decodeAudioData(e.target.result, buffer => {
|
||||
playBufferSource(buffer);
|
||||
setTimeout(() => {
|
||||
if (files.length > index + 1) loadLocalFiles(files, index + 1);
|
||||
}, buffer.duration * 1000);
|
||||
});
|
||||
};
|
||||
reader.readAsArrayBuffer(files[index]);
|
||||
}
|
||||
|
||||
function connectMicAudio(stream) {
|
||||
audioContext.resume();
|
||||
const micSource = audioContext.createMediaStreamSource(stream);
|
||||
const gainNode = audioContext.createGain();
|
||||
gainNode.gain.value = 1.25;
|
||||
micSource.connect(gainNode);
|
||||
visualizer.connectAudio(gainNode);
|
||||
startRenderer();
|
||||
}
|
||||
|
||||
function nextPreset(blendTime = 5.7) {
|
||||
presetIndexHist.push(presetIndex);
|
||||
if (presetRandom) presetIndex = Math.floor(Math.random() * presetKeys.length);
|
||||
else presetIndex = (presetIndex + 1) % presetKeys.length;
|
||||
visualizer.loadPreset(presets[presetKeys[presetIndex]], blendTime);
|
||||
document.getElementById('presetSelect').value = presetIndex;
|
||||
}
|
||||
|
||||
function prevPreset(blendTime = 5.7) {
|
||||
if (presetIndexHist.length > 0) presetIndex = presetIndexHist.pop();
|
||||
else presetIndex = ((presetIndex - 1) + presetKeys.length) % presetKeys.length;
|
||||
visualizer.loadPreset(presets[presetKeys[presetIndex]], blendTime);
|
||||
document.getElementById('presetSelect').value = presetIndex;
|
||||
}
|
||||
|
||||
function restartCycleInterval() {
|
||||
if (cycleInterval) clearInterval(cycleInterval);
|
||||
if (presetCycle) cycleInterval = setInterval(() => nextPreset(2.7), presetCycleLength);
|
||||
}
|
||||
|
||||
function initPlayer() {
|
||||
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
|
||||
// Load presets from window globals
|
||||
presets = { ...(window.base?.default || {}), ...(window.extra?.default || {}) };
|
||||
presets = Object.fromEntries(
|
||||
Object.entries(presets).sort(([a], [b]) => a.toLowerCase().localeCompare(b.toLowerCase()))
|
||||
);
|
||||
presetKeys = Object.keys(presets);
|
||||
presetIndex = Math.floor(Math.random() * presetKeys.length);
|
||||
|
||||
// Populate preset select
|
||||
const presetSelect = document.getElementById('presetSelect');
|
||||
presetKeys.forEach((k, i) => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = i;
|
||||
opt.textContent = k.length > 60 ? k.substring(0, 60) + '…' : k;
|
||||
presetSelect.appendChild(opt);
|
||||
});
|
||||
|
||||
visualizer = butterchurn.createVisualizer(audioContext, canvas, {
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
pixelRatio: window.devicePixelRatio || 1,
|
||||
textureRatio: 1
|
||||
});
|
||||
|
||||
nextPreset(0);
|
||||
startRenderer();
|
||||
restartCycleInterval();
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
document.getElementById('localFileBut').onclick = () => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'audio/*';
|
||||
input.multiple = true;
|
||||
input.onchange = e => loadLocalFiles(e.target.files);
|
||||
input.click();
|
||||
};
|
||||
|
||||
document.getElementById('micSelect').onclick = async () => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
connectMicAudio(stream);
|
||||
} catch (err) {
|
||||
console.error('Microphone error', err);
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
background: black;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('presetSelect').onchange = () => {
|
||||
presetIndexHist.push(presetIndex);
|
||||
presetIndex = parseInt(document.getElementById('presetSelect').value);
|
||||
visualizer.loadPreset(presets[presetKeys[presetIndex]], 5.7);
|
||||
};
|
||||
#mainWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
document.getElementById('presetCycle').onchange = e => {
|
||||
presetCycle = e.target.checked;
|
||||
restartCycleInterval();
|
||||
};
|
||||
#canvas {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
document.getElementById('presetCycleLength').onchange = e => {
|
||||
presetCycleLength = parseInt(e.target.value) * 1000;
|
||||
restartCycleInterval();
|
||||
};
|
||||
#audioSelectWrapper,
|
||||
#presetControls {
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
document.getElementById('presetRandom').onchange = e => {
|
||||
presetRandom = e.target.checked;
|
||||
};
|
||||
#presetSelect {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
// Start on first user click (required for autoplay policies)
|
||||
document.body.addEventListener('click', () => {
|
||||
if (!audioContext) initPlayer();
|
||||
}, { once: true });
|
||||
</script>
|
||||
#presetCycleLength {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
<!-- Load Butterchurn presets as global scripts -->
|
||||
<script src="https://unpkg.com/butterchurn-presets@3.0.0-beta.4/dist/base.min.js"></script>
|
||||
<script src="https://unpkg.com/butterchurn-presets@3.0.0-beta.4/dist/extra.min.js"></script>
|
||||
button {
|
||||
cursor: pointer;
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
</body>
|
||||
<body>
|
||||
<div id="mainWrapper">
|
||||
<div id="audioSelectWrapper">
|
||||
<button id="localFileBut">Load local files</button>
|
||||
<button id="micSelect">Use Mic</button>
|
||||
</div>
|
||||
|
||||
</html>
|
||||
<div id="presetControls">
|
||||
<label
|
||||
>Preset:
|
||||
<select id="presetSelect"></select
|
||||
></label>
|
||||
<label>Cycle: <input type="checkbox" id="presetCycle" checked /></label>
|
||||
<label>Seconds: <input type="number" id="presetCycleLength" value="15" min="1" /></label>
|
||||
<label>Random: <input type="checkbox" id="presetRandom" checked /></label>
|
||||
</div>
|
||||
|
||||
<canvas id="canvas" width="800" height="600"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Load Butterchurn and presets -->
|
||||
<script type="module">
|
||||
import butterchurn from 'https://unpkg.com/butterchurn@3.0.0-beta.5/dist/butterchurn.js';
|
||||
|
||||
let audioContext = null;
|
||||
let visualizer = null;
|
||||
let sourceNode = null;
|
||||
let delayedAudible = null;
|
||||
let cycleInterval = null;
|
||||
|
||||
let presets = {};
|
||||
let presetKeys = [];
|
||||
let presetIndexHist = [];
|
||||
let presetIndex = 0;
|
||||
let presetCycle = true;
|
||||
let presetCycleLength = 15000;
|
||||
let presetRandom = true;
|
||||
|
||||
const canvas = document.getElementById('canvas');
|
||||
|
||||
function startRenderer() {
|
||||
requestAnimationFrame(startRenderer);
|
||||
visualizer.render();
|
||||
}
|
||||
|
||||
function connectToAudioAnalyzer(node) {
|
||||
if (delayedAudible) delayedAudible.disconnect();
|
||||
delayedAudible = audioContext.createDelay();
|
||||
delayedAudible.delayTime.value = 0.26;
|
||||
node.connect(delayedAudible);
|
||||
delayedAudible.connect(audioContext.destination);
|
||||
visualizer.connectAudio(delayedAudible);
|
||||
}
|
||||
|
||||
function playBufferSource(buffer) {
|
||||
if (sourceNode) sourceNode.disconnect();
|
||||
sourceNode = audioContext.createBufferSource();
|
||||
sourceNode.buffer = buffer;
|
||||
connectToAudioAnalyzer(sourceNode);
|
||||
sourceNode.start();
|
||||
}
|
||||
|
||||
function loadLocalFiles(files, index = 0) {
|
||||
audioContext.resume();
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
audioContext.decodeAudioData(e.target.result, (buffer) => {
|
||||
playBufferSource(buffer);
|
||||
setTimeout(() => {
|
||||
if (files.length > index + 1) loadLocalFiles(files, index + 1);
|
||||
}, buffer.duration * 1000);
|
||||
});
|
||||
};
|
||||
reader.readAsArrayBuffer(files[index]);
|
||||
}
|
||||
|
||||
function connectMicAudio(stream) {
|
||||
audioContext.resume();
|
||||
const micSource = audioContext.createMediaStreamSource(stream);
|
||||
const gainNode = audioContext.createGain();
|
||||
gainNode.gain.value = 1.25;
|
||||
micSource.connect(gainNode);
|
||||
visualizer.connectAudio(gainNode);
|
||||
startRenderer();
|
||||
}
|
||||
|
||||
function nextPreset(blendTime = 5.7) {
|
||||
presetIndexHist.push(presetIndex);
|
||||
if (presetRandom) presetIndex = Math.floor(Math.random() * presetKeys.length);
|
||||
else presetIndex = (presetIndex + 1) % presetKeys.length;
|
||||
visualizer.loadPreset(presets[presetKeys[presetIndex]], blendTime);
|
||||
document.getElementById('presetSelect').value = presetIndex;
|
||||
}
|
||||
|
||||
function prevPreset(blendTime = 5.7) {
|
||||
if (presetIndexHist.length > 0) presetIndex = presetIndexHist.pop();
|
||||
else presetIndex = (presetIndex - 1 + presetKeys.length) % presetKeys.length;
|
||||
visualizer.loadPreset(presets[presetKeys[presetIndex]], blendTime);
|
||||
document.getElementById('presetSelect').value = presetIndex;
|
||||
}
|
||||
|
||||
function restartCycleInterval() {
|
||||
if (cycleInterval) clearInterval(cycleInterval);
|
||||
if (presetCycle) cycleInterval = setInterval(() => nextPreset(2.7), presetCycleLength);
|
||||
}
|
||||
|
||||
function initPlayer() {
|
||||
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
|
||||
// Load presets from window globals
|
||||
presets = { ...(window.base?.default || {}), ...(window.extra?.default || {}) };
|
||||
presets = Object.fromEntries(
|
||||
Object.entries(presets).sort(([a], [b]) => a.toLowerCase().localeCompare(b.toLowerCase()))
|
||||
);
|
||||
presetKeys = Object.keys(presets);
|
||||
presetIndex = Math.floor(Math.random() * presetKeys.length);
|
||||
|
||||
// Populate preset select
|
||||
const presetSelect = document.getElementById('presetSelect');
|
||||
presetKeys.forEach((k, i) => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = i;
|
||||
opt.textContent = k.length > 60 ? k.substring(0, 60) + '…' : k;
|
||||
presetSelect.appendChild(opt);
|
||||
});
|
||||
|
||||
visualizer = butterchurn.createVisualizer(audioContext, canvas, {
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
pixelRatio: window.devicePixelRatio || 1,
|
||||
textureRatio: 1,
|
||||
});
|
||||
|
||||
nextPreset(0);
|
||||
startRenderer();
|
||||
restartCycleInterval();
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
document.getElementById('localFileBut').onclick = () => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'audio/*';
|
||||
input.multiple = true;
|
||||
input.onchange = (e) => loadLocalFiles(e.target.files);
|
||||
input.click();
|
||||
};
|
||||
|
||||
document.getElementById('micSelect').onclick = async () => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
connectMicAudio(stream);
|
||||
} catch (err) {
|
||||
console.error('Microphone error', err);
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('presetSelect').onchange = () => {
|
||||
presetIndexHist.push(presetIndex);
|
||||
presetIndex = parseInt(document.getElementById('presetSelect').value);
|
||||
visualizer.loadPreset(presets[presetKeys[presetIndex]], 5.7);
|
||||
};
|
||||
|
||||
document.getElementById('presetCycle').onchange = (e) => {
|
||||
presetCycle = e.target.checked;
|
||||
restartCycleInterval();
|
||||
};
|
||||
|
||||
document.getElementById('presetCycleLength').onchange = (e) => {
|
||||
presetCycleLength = parseInt(e.target.value) * 1000;
|
||||
restartCycleInterval();
|
||||
};
|
||||
|
||||
document.getElementById('presetRandom').onchange = (e) => {
|
||||
presetRandom = e.target.checked;
|
||||
};
|
||||
|
||||
// Start on first user click (required for autoplay policies)
|
||||
document.body.addEventListener(
|
||||
'click',
|
||||
() => {
|
||||
if (!audioContext) initPlayer();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<!-- Load Butterchurn presets as global scripts -->
|
||||
<script src="https://unpkg.com/butterchurn-presets@3.0.0-beta.4/dist/base.min.js"></script>
|
||||
<script src="https://unpkg.com/butterchurn-presets@3.0.0-beta.4/dist/extra.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
6361
index.html
6361
index.html
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue