Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Generating a simple musical tone purely from JavaScript
function GenerateTone(frequency, duration, volume, rate)
{
if (!volume)
volume = 30000;
if (!rate)
rate = 8000;
var nSamples = rate * duration,
i, w = (2 * Math.PI * frequency) / rate,
wav = new ArrayBuffer(44 + nSamples*2);
(new Int32Array(wav, 0, 11)).set(
[0x46464952, 36 + nSamples*2, 0x45564157,
0x20746d66, 16, 0x10001, rate, rate*2, 0x100002,
0x61746164, nSamples*2], 0);
var samples = new Int16Array(wav, 44, nSamples);
for (i = 0; i < nSamples; i++)
samples[i] = volume * Math.sin(w * i);
return "data:audio/wav;base64," + ToBase64(wav);
}
function ToBase64(arrayBuffer)
{
var s = "", a, b, c, d, chunk;
var alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var bytes = new Uint8Array(arrayBuffer)
var byteLength = bytes.byteLength;
var byteRemainder = byteLength % 3;
var mainLength = byteLength - byteRemainder;
for (var i = 0; i < mainLength; i += 3)
{
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
a = (chunk & 16515072) >> 18;
b = (chunk & 258048) >> 12;
c = (chunk & 4032) >> 6;
d = chunk & 63;
s += alpha[a] + alpha[b] + alpha[c] + alpha[d];
}
if (byteRemainder == 1)
{
chunk = bytes[mainLength];
a = (chunk & 252) >> 2;
b = (chunk & 3) << 4;
s += alpha[a] + alpha[b] + "==";
}
else if (byteRemainder == 2)
{
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
a = (chunk & 64512) >> 10;
b = (chunk & 1008) >> 4;
c = (chunk & 15) << 2;
s += alpha[a] + alpha[b] + alpha[c] + "=";
}
return s;
}
Owner

sevaa commented Nov 7, 2016 edited

Today's snippet: generating a simple musical tone purely from JavaScript. I saw some online samples for doing it on a server (e. g. in PHP) and sending to the client, but that's a waste of bandwidth.

The ingredients are twofold:

  • An element with a source that's populated from a data: URI
  • An ArrayBuffer object with WAV data inside

There's a concise description of the WAV format here. My implementation limits the format to 16-bit mono PCM.

The GenerateTone() function returns a data: URI with a WAV file in it. The parameters are:

  • frequency in Hz
  • duration in seconds
  • volume - max is 32768, min is 0
  • rate - in samples per second, default 8000 is enough for simple beeps

That's it. All that remains is feeding that data to an element. Assuming you have an <audio id="MyAudio"> element on the page:

var audio = document.getElementById("MyAudio");
audio.src = GenerateTone(349.23, 0.5); //The F note
audio.play(); 

The ToBase64 function here is one of many JavaScript-only implementations of the Base64 format. If your project already has one, feel free to substitute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment