let tsPerBar = 32; let nBars = 2; let tssPerStep = 4; let isDragging = false; let action = null; let player; let isPlaying = false; let ts = 0; let lastTime = 0; document.addEventListener("DOMContentLoaded", function () { setupGridListeners(); // Clear grid on button click const clearButton = document.getElementById('clearButton'); clearButton.addEventListener('click', clearGrid); loadGrid(); setupVisualizer(); setupPlayer(); setupGenerationListeners(); }); function setupGridListeners() { const grid = document.getElementById('grid'); const rows = grid.querySelectorAll('tr'); rows.forEach((row, i) => { const cells = row.querySelectorAll('td'); cells.forEach((cell, j) => { setupCellListeners(cell); }); }); document.addEventListener('mouseup', function () { isDragging = false; }); } function setupCellListeners(cell) { cell.addEventListener('dragstart', function (e) { e.preventDefault(); }); cell.addEventListener('mousedown', function (e) { if (e.button !== 0) return; e.preventDefault(); isDragging = true; // Determine action based on initial cell's state action = cell.classList.contains('activated') ? 'deactivate' : 'activate'; applyAction(cell, action); }); cell.addEventListener('mouseover', function () { if (isDragging) applyAction(cell, action); }); cell.addEventListener('mouseup', function () { isDragging = false; // End dragging once mouse is released }); } function applyAction(cell, action) { if (action === 'activate') { cell.classList.remove('deactivated'); cell.classList.add('activated'); } else { cell.classList.add('deactivated'); cell.classList.remove('activated'); } } function clearGrid() { const grid = document.getElementById('grid'); const cells = grid.querySelectorAll('td'); cells.forEach(cell => { cell.classList.add('deactivated'); cell.classList.remove('activated'); }); } function loadGrid() { fetch('/load_grid') .then(response => response.json()) .then(data => applyConfiguration(data[0])) .catch(error => console.error('Error fetching grid:', error)); } function applyConfiguration(config) { const grid = document.getElementById('grid'); const rows = grid.querySelectorAll('tr'); for (let i = 0; i < rows.length && i < config.length; i++) { const cells = rows[i].querySelectorAll('td'); for (let j = 0; j < cells.length && j < config[i].length; j++) { const cell = cells[j]; if (config[i][j] === true) { cell.classList.add('activated'); cell.classList.remove('deactivated'); } else { cell.classList.add('deactivated'); cell.classList.remove('activated'); } } } } function setupPlayer() { player = document.getElementById('midiplayer'); player.loop = true; player.addEventListener('start', function () { isPlaying = true; lastTime = -1; ts = 0; update(); }); player.addEventListener('stop', function (event) { if (event.detail.finished) isPlaying = false; }); } function update() { if (!isPlaying) return; currentTime = player.currentTime; dur = player.duration; if (currentTime != lastTime) { tsTime = dur / (nBars * tsPerBar); ts = Math.min(nBars * tsPerBar - 1, currentTime / tsTime); ts = tssPerStep * Math.floor(ts / tssPerStep); pianorollStep(ts); } lastTime = currentTime; requestAnimationFrame(update); } function pianorollStep(ts) { const grid = document.getElementById('grid'); const rows = grid.querySelectorAll('tr'); // Clear previously darkened cells const previouslyDarkened = document.querySelectorAll('.darkened'); previouslyDarkened.forEach(cell => cell.classList.remove('darkened')); for (let row of rows) { const cell = row.cells[(ts % tsPerBar) + 1]; if (cell) { cell.classList.add('darkened'); } } } function setupVisualizer() { const visualizer = document.getElementById('midivisualizer'); visualizer.config = { noteHeight: 5, pixelsPerTimeStep: 100, minPitch: 40, maxPitch: 80 }; } const emptyMessages = [ "\"Hello Darkness, my old friend...\"", ] function setupGenerationListeners() { const generateButton = document.getElementById('generate'); const keepTonality = document.getElementById('keepTonality'); const useGrid = document.getElementById('useGrid'); generateButton.addEventListener('click', async function () { generateButton.disabled = true; const config = getGridConfiguration(); const emptyMessage = document.getElementById('emptyMessage'); if (!config) { const r = Math.floor(Math.random() * emptyMessages.length); emptyMessage.textContent = emptyMessages[r]; generateButton.disabled = false; emptyMessage.style.display = 'block'; return; } else { emptyMessage.textContent = ''; emptyMessage.style.display = 'none'; } sendJSONToBackend(config).then(response => { console.log(response.message); }); const shouldKeepTonality = keepTonality.checked; let shouldUseGrid; if (useGrid != null) { shouldUseGrid = useGrid.checked; } else { shouldUseGrid = true; } try { const response = await fetch('/generate_midi', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ keepTonality: shouldKeepTonality, useGrid: shouldUseGrid }) }); if (response.ok) { const data = await response.json(); const filename = data.filename; reinitializePlayer(filename); } else { console.error('Failed to generate MIDI.'); } } catch (error) { console.error('Error:', error); } finally { generateButton.disabled = false; } }); } async function sendJSONToBackend(data) { const response = await fetch('/save_json', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return response.json(); } function reinitializePlayer(filename) { const player = document.getElementById('midiplayer'); const visualizer = document.getElementById('midivisualizer'); const staticUrl = document.body.getAttribute('static-url'); // Reset properties player.currentTime = 0; player.noteSequence = null; // Load the new MIDI file path = staticUrl + '0/' + filename; player.src = path; // Reload the player player.reload(); visualizer.src = path; visualizer.noteSequence = null; // Reload the player and visualizer player.reload(); visualizer.reload(); visualizer.clearActiveNotes(); visualizer.redraw(); } function getGridConfiguration() { const grid = document.getElementById('grid'); const rows = grid.querySelectorAll('tr'); let isEmpty = true; const config = []; rows.forEach(row => { const cells = Array.from(row.querySelectorAll('td')); const rowData = cells.map(cell => { if (cell.classList.contains('activated')) { isEmpty = false; return 1; } return 0; }); config.push(rowData); }); return isEmpty ? null : [config]; }