SoundMaker / index.html
eaglelandsonce's picture
Update index.html
156d059 verified
Raw
History Blame Contribute Delete
4.19 kB
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Double Jeopardy Sound Maker</title>
<style>
body {
font-family: Arial, sans-serif;
background: #111;
color: white;
text-align: center;
padding: 20px;
}
input {
margin: 5px;
padding: 5px;
}
button {
margin-top: 10px;
padding: 10px 20px;
font-size: 16px;
background: gold;
border: none;
cursor: pointer;
}
button:hover {
background: orange;
}
</style>
</head>
<body>
<h1>🎶 Double Jeopardy Sound Maker</h1>
<p>Pick your fanfare notes and durations, then click Play or Download!</p>
<div>
<label>Note 1 (Hz): <input id="note1" type="number" value="440"></label>
<label>Duration (ms): <input id="dur1" type="number" value="300"></label>
</div>
<div>
<label>Note 2 (Hz): <input id="note2" type="number" value="660"></label>
<label>Duration (ms): <input id="dur2" type="number" value="300"></label>
</div>
<div>
<label>Note 3 (Hz): <input id="note3" type="number" value="880"></label>
<label>Duration (ms): <input id="dur3" type="number" value="300"></label>
</div>
<button onclick="playFanfare()">▶ Play Fanfare</button>
<button onclick="downloadMP3()">💾 Download MP3</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lamejs/1.2.0/lame.min.js"></script>
<script>
function generateAudioBuffer(notes) {
const sampleRate = 44100;
const audioCtx = new OfflineAudioContext(1, sampleRate * 5, sampleRate);
let time = 0;
notes.forEach(([freq, dur]) => {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'square';
osc.frequency.setValueAtTime(freq, time);
gain.gain.setValueAtTime(0.3, time);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(time);
osc.stop(time + dur / 1000);
time += dur / 1000 + 0.1; // gap between notes
});
return audioCtx.startRendering();
}
function playFanfare() {
const notes = getNotes();
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
let time = audioCtx.currentTime;
notes.forEach(([freq, dur]) => {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'square';
osc.frequency.setValueAtTime(freq, time);
gain.gain.setValueAtTime(0.3, time);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(time);
osc.stop(time + dur / 1000);
time += dur / 1000 + 0.1;
});
}
function getNotes() {
return [
[parseFloat(document.getElementById('note1').value), parseInt(document.getElementById('dur1').value)],
[parseFloat(document.getElementById('note2').value), parseInt(document.getElementById('dur2').value)],
[parseFloat(document.getElementById('note3').value), parseInt(document.getElementById('dur3').value)]
];
}
async function downloadMP3() {
const notes = getNotes();
const renderedBuffer = await generateAudioBuffer(notes);
const channelData = renderedBuffer.getChannelData(0);
const mp3encoder = new lamejs.Mp3Encoder(1, renderedBuffer.sampleRate, 128);
const samples = new Int16Array(channelData.length);
for (let i = 0; i < channelData.length; i++) {
samples[i] = channelData[i] * 32767.5;
}
const mp3Data = [];
const chunkSize = 1152;
for (let i = 0; i < samples.length; i += chunkSize) {
const sampleChunk = samples.subarray(i, i + chunkSize);
const mp3buf = mp3encoder.encodeBuffer(sampleChunk);
if (mp3buf.length > 0) mp3Data.push(mp3buf);
}
const endBuf = mp3encoder.flush();
if (endBuf.length > 0) mp3Data.push(endBuf);
const blob = new Blob(mp3Data, { type: 'audio/mp3' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'daily-double.mp3';
a.click();
}
</script>
</body>
</html>