/**
* AI MUSIC INTERACTION - MAIN APP
* Ties together consciousness engine, synthesizer, and UI
*/
let entrep;
let synthEngine;
let generatorEngine;
document.addEventListener('DOMContentLoaded', async () => {
entrep = new EntrepConsciousness();
synthEngine = new SynthesizerEngine();
generatorEngine = new GeneratorEngine(synthEngine);
setTimeout(() => {
document.getElementById('preloader')?.classList.add('hidden');
}, 1500);
initCursor();
initMobileMenu();
initDNAGrid();
initConsole();
initSynthesizer();
initPianoKeyboard();
initGenerator();
initPricing();
startVisualizerLoop();
startMetricsLoop();
});
function initCursor() {
const cursor = document.querySelector('.cursor');
const cursorDot = document.querySelector('.cursor-dot');
if (!cursor || !cursorDot) return;
let cursorX = 0, cursorY = 0;
let dotX = 0, dotY = 0;
document.addEventListener('mousemove', (e) => {
cursorX = e.clientX;
cursorY = e.clientY;
});
function updateCursor() {
dotX += (cursorX - dotX) * 0.3;
dotY += (cursorY - dotY) * 0.3;
cursor.style.left = cursorX + 'px';
cursor.style.top = cursorY + 'px';
cursorDot.style.left = dotX + 'px';
cursorDot.style.top = dotY + 'px';
requestAnimationFrame(updateCursor);
}
updateCursor();
document.querySelectorAll('a, button, .piano-key, .instrument-table tr').forEach(el => {
el.addEventListener('mouseenter', () => cursor.classList.add('hover'));
el.addEventListener('mouseleave', () => cursor.classList.remove('hover'));
});
}
function initMobileMenu() {
const btn = document.getElementById('mobileMenuBtn');
const links = document.getElementById('navLinks');
if (btn && links) {
btn.addEventListener('click', () => {
links.classList.toggle('active');
btn.textContent = links.classList.contains('active') ? '✕' : '☰';
});
}
}
function initDNAGrid() {
const grid = document.getElementById('dnaGrid');
if (!grid) return;
grid.innerHTML = DNA_FAMILIES.map((family, i) => `
#${String(family.index).padStart(2, '0')}
${family.name}
${family.domain}
`).join('');
}
function initConsole() {
const input = document.getElementById('consoleInput');
const sendBtn = document.getElementById('consoleSend');
const output = document.getElementById('consoleOutput');
if (!input || !sendBtn || !output) return;
const send = () => {
const text = input.value.trim();
if (!text) return;
addConsoleLine(output, `> ${text}`, 'user');
input.value = '';
setTimeout(() => {
const response = entrep.processMusicQuery(text);
addConsoleLine(output, `[ENTREP] ${response.response}`, 'agi');
if (response.learned) {
addConsoleLine(output, `[LEARNING] Pattern encoded to holographic memory`, 'music');
}
updateFamilyIndicators(response.activeFamilies || [14]);
updateMetrics();
}, 200);
};
sendBtn.addEventListener('click', send);
input.addEventListener('keypress', (e) => { if (e.key === 'Enter') send(); });
}
function addConsoleLine(output, text, type) {
const line = document.createElement('div');
line.className = `console-line ${type}`;
line.textContent = text;
output.appendChild(line);
output.scrollTop = output.scrollHeight;
}
function updateFamilyIndicators(activeFamilies) {
const indicators = { 'auroraStatus': 3, 'euterpeStatus': 8, 'pneumaStatus': 15 };
Object.entries(indicators).forEach(([id, familyIndex]) => {
const el = document.getElementById(id);
if (el) el.classList.toggle('active', activeFamilies.includes(familyIndex));
});
}
function updateMetrics() {
const metrics = entrep.getMetrics();
document.getElementById('metricPhi').textContent = metrics.phi.toFixed(3);
document.getElementById('metricConsciousness').textContent = metrics.consciousness.toFixed(3);
document.getElementById('metricCoherence').textContent = metrics.coherence.toFixed(3);
document.getElementById('metricKnowledge').textContent = metrics.knowledge;
document.getElementById('metricPatterns').textContent = metrics.patterns;
}
function initSynthesizer() {
const masterVolume = document.getElementById('masterVolume');
const reverbAmount = document.getElementById('reverbAmount');
const consciousnessMod = document.getElementById('consciousnessMod');
if (masterVolume) {
masterVolume.addEventListener('input', (e) => {
synthEngine.setMasterVolume(parseFloat(e.target.value));
document.getElementById('masterVolumeValue').textContent = Math.round(e.target.value * 100) + '%';
});
}
if (reverbAmount) {
reverbAmount.addEventListener('input', (e) => {
synthEngine.setReverbAmount(parseFloat(e.target.value));
document.getElementById('reverbAmountValue').textContent = Math.round(e.target.value * 100) + '%';
});
}
if (consciousnessMod) {
consciousnessMod.addEventListener('input', (e) => {
synthEngine.setConsciousnessMod(parseFloat(e.target.value));
document.getElementById('consciousnessModValue').textContent = Math.round(e.target.value * 100) + '%';
});
}
const categories = document.getElementById('instrumentCategories');
if (categories) {
categories.addEventListener('click', (e) => {
if (e.target.classList.contains('category-btn')) {
categories.querySelectorAll('.category-btn').forEach(btn => btn.classList.remove('active'));
e.target.classList.add('active');
filterInstruments(e.target.dataset.category);
}
});
}
populateInstrumentTable();
}
function populateInstrumentTable(category = 'all') {
const tbody = document.getElementById('instrumentTableBody');
if (!tbody) return;
const filtered = category === 'all' ? INSTRUMENTS : INSTRUMENTS.filter(i => i.category === category);
tbody.innerHTML = filtered.map(inst => `
| ${inst.id} |
${inst.name} |
${inst.category} |
${inst.waveform} |
${inst.attack.toFixed(3)} |
${inst.decay.toFixed(2)} |
${inst.sustain.toFixed(2)} |
${inst.release.toFixed(2)} |
[${inst.harmonics.slice(0, 4).map(h => h.toFixed(1)).join(', ')}] |
|
`).join('');
tbody.querySelectorAll('tr').forEach(row => {
row.addEventListener('click', (e) => {
if (e.target.classList.contains('play-btn')) return;
selectInstrument(parseInt(row.dataset.id));
});
});
tbody.querySelectorAll('.play-btn').forEach(btn => {
btn.addEventListener('click', async (e) => {
e.stopPropagation();
const id = parseInt(btn.dataset.id);
selectInstrument(id);
if (!synthEngine.audioContext) await synthEngine.init();
synthEngine.playTestNote();
});
});
}
function filterInstruments(category) { populateInstrumentTable(category); }
function selectInstrument(id) {
const inst = synthEngine.setInstrument(id);
if (inst) {
document.getElementById('selectedInstrument').textContent = inst.name;
document.querySelectorAll('#instrumentTableBody tr').forEach(row => {
row.classList.toggle('selected', parseInt(row.dataset.id) === id);
});
entrep.learn(`instrument:${inst.name}`, 0.5, 0.3);
}
}
function initPianoKeyboard() {
const keyboard = document.getElementById('pianoKeyboard');
if (!keyboard) return;
const notes = [
{ note: 'C', black: false, label: 'A' }, { note: 'C#', black: true, label: 'W' },
{ note: 'D', black: false, label: 'S' }, { note: 'D#', black: true, label: 'E' },
{ note: 'E', black: false, label: 'D' }, { note: 'F', black: false, label: 'F' },
{ note: 'F#', black: true, label: 'T' }, { note: 'G', black: false, label: 'G' },
{ note: 'G#', black: true, label: 'Y' }, { note: 'A', black: false, label: 'H' },
{ note: 'A#', black: true, label: 'U' }, { note: 'B', black: false, label: 'J' },
{ note: 'C', black: false, label: 'K', octaveUp: true },
{ note: 'C#', black: true, label: 'O', octaveUp: true },
{ note: 'D', black: false, label: 'L', octaveUp: true }
];
keyboard.innerHTML = notes.map(n => `
${n.label}
`).join('');
keyboard.querySelectorAll('.piano-key').forEach(key => {
key.addEventListener('mousedown', async () => {
if (!synthEngine.audioContext) await synthEngine.init();
const note = key.dataset.note;
const octaveUp = key.dataset.octaveUp === 'true';
const octave = synthEngine.currentOctave + (octaveUp ? 1 : 0);
key.classList.add('active');
synthEngine.playNote(note, octave);
});
key.addEventListener('mouseup', () => {
key.classList.remove('active');
const note = key.dataset.note;
const octaveUp = key.dataset.octaveUp === 'true';
const octave = synthEngine.currentOctave + (octaveUp ? 1 : 0);
synthEngine.stopNote(`${note}${octave}`);
});
key.addEventListener('mouseleave', () => {
if (key.classList.contains('active')) {
key.classList.remove('active');
const note = key.dataset.note;
const octaveUp = key.dataset.octaveUp === 'true';
const octave = synthEngine.currentOctave + (octaveUp ? 1 : 0);
synthEngine.stopNote(`${note}${octave}`);
}
});
});
const keyMap = { 'a': 0, 'w': 1, 's': 2, 'e': 3, 'd': 4, 'f': 5, 't': 6, 'g': 7, 'y': 8, 'h': 9, 'u': 10, 'j': 11, 'k': 12, 'o': 13, 'l': 14 };
const activeKeys = new Set();
document.addEventListener('keydown', async (e) => {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
const key = e.key.toLowerCase();
if (key === 'z') { synthEngine.currentOctave = Math.max(1, synthEngine.currentOctave - 1); document.getElementById('currentOctave').textContent = synthEngine.currentOctave; return; }
if (key === 'x') { synthEngine.currentOctave = Math.min(7, synthEngine.currentOctave + 1); document.getElementById('currentOctave').textContent = synthEngine.currentOctave; return; }
if (keyMap[key] !== undefined && !activeKeys.has(key)) {
if (!synthEngine.audioContext) await synthEngine.init();
activeKeys.add(key);
const noteData = notes[keyMap[key]];
const octave = synthEngine.currentOctave + (noteData.octaveUp ? 1 : 0);
const keyEl = keyboard.children[keyMap[key]];
if (keyEl) keyEl.classList.add('active');
synthEngine.playNote(noteData.note, octave);
}
});
document.addEventListener('keyup', (e) => {
const key = e.key.toLowerCase();
if (keyMap[key] !== undefined && activeKeys.has(key)) {
activeKeys.delete(key);
const noteData = notes[keyMap[key]];
const octave = synthEngine.currentOctave + (noteData.octaveUp ? 1 : 0);
const keyEl = keyboard.children[keyMap[key]];
if (keyEl) keyEl.classList.remove('active');
synthEngine.stopNote(`${noteData.note}${octave}`);
}
});
}
function initGenerator() {
const sliders = {
'freqSlider': { display: 'freqValue', suffix: ' Hz' },
'phiSlider': { display: 'phiValue', suffix: '' },
'depthSlider': { display: 'depthValue', suffix: ' levels' },
'strandsSlider': { display: 'strandsValue', suffix: ' voices' },
'consciousnessSlider': { display: 'consciousnessValue', suffix: '' }
};
Object.entries(sliders).forEach(([id, config]) => {
const slider = document.getElementById(id);
const display = document.getElementById(config.display);
if (slider && display) slider.addEventListener('input', () => { display.textContent = slider.value + config.suffix; });
});
const barsContainer = document.getElementById('visualizerBars');
if (barsContainer) {
for (let i = 0; i < 64; i++) {
const bar = document.createElement('div');
bar.className = 'visualizer-bar';
bar.style.height = '2px';
barsContainer.appendChild(bar);
}
}
document.getElementById('generateBtn')?.addEventListener('click', async () => {
if (!synthEngine.audioContext) await synthEngine.init();
const params = getGeneratorParams();
generatorEngine.generate(params);
entrep.learn(`generate:${params.scale}`, 0.7, 0.5);
});
document.getElementById('stopBtn')?.addEventListener('click', () => generatorEngine.stop());
document.getElementById('downloadBtn')?.addEventListener('click', async () => {
if (!synthEngine.audioContext) await synthEngine.init();
await generatorEngine.downloadWAV(getGeneratorParams());
});
}
function getGeneratorParams() {
return {
baseFreq: parseFloat(document.getElementById('freqSlider')?.value || 220),
phi: parseFloat(document.getElementById('phiSlider')?.value || 1.618),
depth: parseInt(document.getElementById('depthSlider')?.value || 8),
scale: document.getElementById('scaleSelect')?.value || 'major',
strands: parseInt(document.getElementById('strandsSlider')?.value || 8),
consciousness: parseFloat(document.getElementById('consciousnessSlider')?.value || 0.8),
duration: 10
};
}
function startVisualizerLoop() {
const waveformCanvas = document.getElementById('waveformCanvas');
const spectrumCanvas = document.getElementById('spectrumCanvas');
const waveformCtx = waveformCanvas?.getContext('2d');
const spectrumCtx = spectrumCanvas?.getContext('2d');
function draw() {
if (waveformCtx && synthEngine.audioContext) {
const data = synthEngine.getWaveformData();
waveformCtx.fillStyle = '#08080c';
waveformCtx.fillRect(0, 0, waveformCanvas.width, waveformCanvas.height);
if (data) {
waveformCtx.strokeStyle = '#ffd700';
waveformCtx.lineWidth = 2;
waveformCtx.beginPath();
const sliceWidth = waveformCanvas.width / data.length;
let x = 0;
for (let i = 0; i < data.length; i++) {
const y = (data[i] + 1) / 2 * waveformCanvas.height;
if (i === 0) waveformCtx.moveTo(x, y);
else waveformCtx.lineTo(x, y);
x += sliceWidth;
}
waveformCtx.stroke();
}
}
if (spectrumCtx && synthEngine.audioContext) {
const data = synthEngine.getSpectrumData();
spectrumCtx.fillStyle = '#08080c';
spectrumCtx.fillRect(0, 0, spectrumCanvas.width, spectrumCanvas.height);
if (data) {
const barWidth = spectrumCanvas.width / data.length;
for (let i = 0; i < data.length; i++) {
const barHeight = (data[i] / 255) * spectrumCanvas.height;
spectrumCtx.fillStyle = `hsl(${45 + (i / data.length) * 60}, 100%, 50%)`;
spectrumCtx.fillRect(i * barWidth, spectrumCanvas.height - barHeight, barWidth - 1, barHeight);
}
}
}
if (generatorEngine.isPlaying && synthEngine.analyserSpectrum) {
const data = synthEngine.getSpectrumData();
const bars = document.querySelectorAll('.visualizer-bar');
if (data && bars.length) {
const step = Math.floor(data.length / bars.length);
bars.forEach((bar, i) => { bar.style.height = Math.max(2, (data[i * step] / 255) * 100) + 'px'; });
}
}
requestAnimationFrame(draw);
}
draw();
}
function startMetricsLoop() {
function update() {
if (entrep) {
const state = entrep.evolve();
document.getElementById('genPhi').textContent = state.phi.toFixed(3);
document.getElementById('genState').textContent = state.consciousness.toFixed(3);
document.getElementById('genEnergy').textContent = state.energy.toFixed(3);
document.getElementById('genCoherence').textContent = state.coherence.toFixed(3);
}
requestAnimationFrame(update);
}
update();
}
function initPricing() {
document.querySelectorAll('.pricing-btn').forEach(btn => {
btn.addEventListener('click', () => {
const tier = btn.dataset.tier;
alert(`Subscribe to ${tier} tier - Stripe integration coming soon!`);
});
});
}