Skip to content

Instantly share code, notes, and snippets.

@sevaa
Last active April 15, 2017 21:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sevaa/2d84822540bf1fc8b8deab255d6209eb to your computer and use it in GitHub Desktop.
Save sevaa/2d84822540bf1fc8b8deab255d6209eb to your computer and use it in GitHub Desktop.
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;
}
@sevaa
Copy link
Author

sevaa commented Nov 7, 2016

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