Skip to content

Instantly share code, notes, and snippets.

@gdassori
Created April 30, 2023 08:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gdassori/774beffaa3328a9cf7aa55098d8ff5f4 to your computer and use it in GitHub Desktop.
Save gdassori/774beffaa3328a9cf7aa55098d8ff5f4 to your computer and use it in GitHub Desktop.
GPT music Player
{
"channel1": [
440, "r250", 523.25, "r500", 659.25, "r1000", 880, 783.99, "r750", 880, 783.99, "r750",
880, "r500", 659.25, "r250", 783.99, "r500", 698.46, "r500", 659.25, "r500", 587.33, "r500", 523.25, "r500",
440, "r250", 523.25, "r500", 659.25, "r1000", 880, 783.99, "r750", 880, 783.99, "r750",
880, "r500", 659.25, "r250", 783.99, "r500", 698.46, "r500", 659.25, "r500", 587.33, "r500", 523.25, "r500",
440, "r250", 523.25, "r500", 659.25, "r1000", 880, 783.99, "r750", 880, 783.99, "r750",
880, "r500", 659.25, "r250", 783.99, "r500", 698.46, "r500", 659.25, "r500", 587.33,
523.25, "r250", 523.25, 587.33, 659.25, 783.99, 880, "r750", 783.99, 880,
783.99, "r750", 880, "r1000"
],
"channel2": [
"r250", 440, "r500", 440, "r750", 587.33, 523.25, "r750", 659.25, "r500", 783.99, "r250",
659.25, "r500", 523.25, "r750", 440, "r500", 440, "r250", 523.25, "r250", 587.33, "r500",
659.25, "r500", 783.99, "r250", 659.25, "r250", 523.25, "r500", 587.33, "r250", 523.25, "r250",
440, "r250", 523.25, "r500", 440, "r1000", 523.25, "r1000", 659.25, "r500", 783.99, "r250",
659.25, "r500", 523.25, "r750", 440, "r500", 440, "r250", 523.25, "r250", 587.33, "r500",
659.25, "r500", 783.99, "r250", 659.25, "r250", 523.25, "r500", 587.33, "r250", 523.25, "r250",
440, "r250", 523.25, "r500", 440, "r1000", 523.25, "r1000", 659.25, "r500", 783.99, "r250",
659.25, "r500", 523.25, "r750", 440, "r500", 440, "r250", 523.25, "r250", 587.33,
"r500", 659.25, "r500", 783.99, "r250", 659.25, "r250", 523.25, "r500", 587.33, "r250", 523.25,
"r250", "r1000"
]
}
<!DOCTYPE html>
<html>
<head>
<title>Custom Song Player (4 Channels)</title>
</head>
<body>
<h1>Custom Song Player (4 Channels)</h1>
<div class="channel">
<label for="songInput1">Channel 1:</label>
<input type="text" id="songInput1">
</div>
<div class="channel">
<label for="songInput2">Channel 2:</label>
<input type="text" id="songInput2">
</div>
<div class="channel">
<label for="songInput3">Channel 3:</label>
<input type="text" id="songInput3">
</div>
<div class="channel">
<label for="songInput4">Channel 4:</label>
<input type="text" id="songInput4">
</div>
<br>
<button onclick="importSong()">Import Song</button>
<br>
<button onclick="playSong()">Play</button>
<script>
function importSong() {
// create an input element and simulate a click on it to open the file browser
const input = document.createElement("input");
input.type = "file";
input.accept = "application/json";
input.onchange = function() {
// load the selected file and update the textboxes with the song data
const file = input.files[0];
const reader = new FileReader();
reader.onload = function(event) {
const songData = JSON.parse(event.target.result);
console.log("Song imported", songData);
// set the values of the textboxes for each channel, and clear the non-populated channels
for (let i = 1; i <= 4; i++) {
const channelName = `channel${i}`;
const channelInput = document.getElementById(`songInput${i}`);
if (channelName in songData) {
channelInput.value = songData[channelName].join(",");
} else {
channelInput.value = "";
}
}
};
reader.readAsText(file);
}
input.click();
}
async function playSong() {
// create an AudioContext object
const audioContext = new AudioContext();
console.log("AudioContext created");
// get the song input values from the textboxes and create an array of notes for each channel
const notes = [];
for (let i = 1; i <= 4; i++) {
const channelInput = document.getElementById(`songInput${i}`);
if (channelInput.value) {
notes[i - 1] = channelInput.value.split(",");
} else {
notes[i - 1] = [];
}
}
console.log("Notes:", notes);
// create audio nodes for each channel and connect each to the destination
const channelNodes = [];
for (let i = 0; i < notes.length; i++) {
const channelNode = audioContext.createGain();
channelNode.gain.value = 0.1;
channelNode.connect(audioContext.destination);
console.log("Channel", i + 1, "node created");
channelNodes.push(channelNode);
}
// play each note in sequence with a delay between each note, concurrently for all channels
await Promise.all(notes.map((channelNotes, channelIndex) => playNotes(audioContext, channelNotes, channelNodes[channelIndex])));
console.log("Song playback complete");
}
async function playNotes(audioContext, notes, channelNode) {
for (let index = 0; index < notes.length; index++) {
const note = notes[index];
if (note) {
if (note.startsWith("r")) {
// the note is a rest, so just wait for the duration of the rest
const duration = parseInt(note.substring(1));
console.log(`Rest for ${duration}ms on channel ${channelNode}`);
await new Promise(resolve => setTimeout(resolve, duration));
} else {
// the note is a musical note, so play it
const frequency = parseInt(note);
const duration = 250;
console.log(`Note ${frequency} played for ${duration}ms on channel ${channelNode}`);
const oscillator = audioContext.createOscillator();
oscillator.type = "square";
oscillator.frequency.value = frequency;
oscillator.connect(channelNode);
oscillator.start();
oscillator.stop(audioContext.currentTime + duration / 1000);
// wait for the duration of the note before playing the next one
await new Promise(resolve => setTimeout(resolve, duration));
}
}
}
}
</script>
</body>
</html>
Please generate a JSON song using the following contract:
The song data should be an object with the following properties:
channel1: An array of numbers and strings representing the notes for the first channel.
channel2: An array of numbers and strings representing the notes for the second channel.
channel3: An array of numbers and strings representing the notes for the third channel.
channel4: An array of numbers and strings representing the notes for the fourth channel.
Each channel should have at least one note, and each note should be a positive integer representing the frequency in Hertz or a string that starts with the letter 'r' followed by a duration in milliseconds for pauses. The duration of each note/pause should be 250 milliseconds, except for pause notes, in which the duration should be the specified pause duration.
The JSON object should be valid according to JSON syntax, and there should be no comments or extraneous characters in the file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment