Last active
April 3, 2021 06:31
-
-
Save Fordi/de824c6c72b70d1bc979b501b95bd0ef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<script> | |
window.AudioContext = window.AudioContext || window.webkitAudioContext; | |
var context = new AudioContext(); | |
function playSound(arr) { | |
var buf = new Float32Array(arr.length) | |
for (var i = 0; i < arr.length; i++) buf[i] = arr[i] | |
var buffer = context.createBuffer(1, buf.length, context.sampleRate) | |
buffer.copyToChannel(buf, 0) | |
var source = context.createBufferSource(); | |
source.buffer = buffer; | |
source.connect(context.destination); | |
source.start(0); | |
} | |
var generateSound = ({ | |
// Total Volume of the sound | |
volume, | |
// Length of the sound in seconds | |
length, | |
// How many tones to generate in parallel | |
steps, | |
// The interval between the lowest and highest tone | |
interval, | |
// Zero to full in what part of the whole sound | |
attack = 0.1, | |
// Full to zero in what part of the whole sound | |
decay = 0.5, | |
// How far to sweep the whole thing | |
sweepInterval = 3, | |
// How to ease the sweep (^1/x -> more r shaped; ^x -> more j shaped) | |
sweepOrder = 1, | |
}) => { | |
const arr = []; | |
// The set of tones | |
const tones = new Array(steps + 1).join('.').split('.').map((_, i) => 440 * Math.pow(Math.pow(2, interval / 12), i / steps)); | |
// Length of the sound in samples | |
const sampleLength = context.sampleRate * length; | |
// Base volume for a single tone | |
const baseVol = volume / tones.length; | |
for (var i = 0; i < sampleLength; i++) { | |
const sweep = Math.pow(2, Math.pow(i / sampleLength, sweepOrder) * sweepInterval/12); | |
const sampleVolume = ( | |
// Calculate the non-wave amplitude for all the tones in this sample... | |
// The base volume for one tone | |
baseVol | |
// Positional attack / decay (no one likes a "click" to start their tone) | |
* Math.min( | |
1, | |
i / (sampleLength * attack), | |
(sampleLength - i) / (sampleLength * decay) | |
) | |
); | |
arr[i] = tones.reduce((sum, tone, ti) => ( | |
sum | |
+ Math.cos(sweep * i / (context.sampleRate / tone / (Math.PI * 2))) * sampleVolume | |
), 0); | |
} | |
return arr; | |
}; | |
const playIt = () => { | |
playSound(generateSound({ | |
volume: parseFloat(document.getElementById('volume').value), | |
length: parseFloat(document.getElementById('length').value), | |
steps: parseFloat(document.getElementById('steps').value), | |
interval: parseFloat(document.getElementById('interval').value), | |
sweepInterval: parseFloat(document.getElementById('sweepInterval').value), | |
sweepOrder: parseFloat(document.getElementById('sweepOrder').value) | |
})); | |
}; | |
</script> | |
</head> | |
<body> | |
<label for="volume"> | |
Volume:<br /> | |
<input id="volume" type="number" min="0" max="1" step="0.1" value="1"/> | |
</label><hr /> | |
<label for="length"> | |
Length:<br /> | |
<input id="length" type="number" min="0" step="0.5" value="5" /> | |
seconds | |
</label><hr /> | |
<label for="steps"> | |
# of Tones:<br /> | |
<input id="steps" type="number" min="1" step="1" value="120" /> | |
</label><hr /> | |
<label for="interval"> | |
Interval between lowest and highest tone in semitones (float allowed):<br /> | |
<input id="interval" type="number" min="1" step="1" value="12" /> | |
</label><hr /> | |
<label for="sweepInterval"> | |
How far to sweep in semitones:<br /> | |
<input id="sweepInterval" type="number" min="0" step="1" value="0" /> | |
</label><hr /> | |
<label for="sweepOrder"> | |
How to ease the sweep; 1 is flat. >1 is more 'j' shaped. 0..1 is more 'r' shaped:<br /> | |
<input id="sweepOrder" type="number" min="0" step="0.1" value="1" /> | |
</label><hr /> | |
<button onclick="playIt()">Play</button><br /> | |
Depending on your config, this may take a moment. | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment