Music Editor
by Ben Gillbanks
HTML
<h2>Music Editor</h2>
<label>Tempo (ms per note):</label>
<input id="tempo" type="number" value="100">
<label>Note Length:</label>
<input id="noteLength" type="number" value="80">
<br><br>
<label>Music Length (notes):</label>
<input id="channelLength" type="number" value="16" step="4">
<div id="tabs"></div>
<button id="addChannel">Add Channel</button>
<div id="channel-content"></div>
<button id="generate">Generate Song Text</button>
<pre id="output"></pre>
CSS
body {
font-family: sans-serif;
}
table {
border-collapse: collapse;
margin-top: 5px;
font-size: 10px;
}
td, th {
border: 1px solid #ddd;
text-align: center;
padding: 0;
line-height: 0;
}
input[type=radio] {
width: 10px;
height: 10px;
margin:0;
padding: 0;
}
.channel {
margin-bottom: 20px;
}
JavaScript
// Define note characters.
const noteChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const numRows = noteChars.length;
let numColumns = Number(document.getElementById('channelLength').value) || 16;
let channelCount = 0;
const tabsContainer = document.getElementById('tabs');
const channelContentContainer = document.getElementById('channel-content');
// Create a channel grid.
function createChannel(channelIndex) {
const channelContainer = document.createElement('div');
channelContainer.className = 'channel';
channelContainer.dataset.channel = channelIndex;
// Only show the first channel.
if (channelIndex !== 0) {
channelContainer.style.display = 'none';
}
// Instrument selector.
const instrumentLabel = document.createElement('span');
instrumentLabel.textContent = 'Channel ' + (channelIndex + 1) + ' Instrument: ';
const instrumentSelect = document.createElement('select');
instrumentSelect.id = 'instrument-' + channelIndex;
["0", "1", "2", "3", "4", "5", "6", "7"].forEach(inst => {
const option = document.createElement('option');
option.value = inst;
option.textContent = inst;
instrumentSelect.appendChild(option);
});
channelContainer.appendChild(instrumentLabel);
channelContainer.appendChild(instrumentSelect);
channelContainer.appendChild(document.createElement('br'));
// Create table grid with radio buttons.
const table = document.createElement('table');
for (let r = 0; r < numRows; r++) {
const tr = document.createElement('tr');
// Note label cell.
const labelTd = document.createElement('td');
labelTd.textContent = noteChars[r];
tr.appendChild(labelTd);
// Create cells for each time column.
for (let c = 0; c < numColumns; c++) {
const td = document.createElement('td');
const radio = document.createElement('input');
radio.type = 'radio';
// Group by channel and column.
radio.name = `radio-ch-${channelIndex}-col-${c}`;
radio.dataset.row =...