Skip to content

Instantly share code, notes, and snippets.

@drslump
Last active August 29, 2015 14:16
Show Gist options
  • Save drslump/51d6a8a039c8a6d334b3 to your computer and use it in GitHub Desktop.
Save drslump/51d6a8a039c8a6d334b3 to your computer and use it in GitHub Desktop.
dtmf
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<button onclick="ringUK()">UK!</button>
<button onclick="ringEU()">Europe + Argentina!</button>
<button onclick="stop()">STOP!</button>
<hr/>
<p>
<input type="text" id="phone" />
<button onclick="playDtmf()">play</button>
</p>
<p>
<button onclick="dtmf('1')">1</button>
<button onclick="dtmf('2')">2</button>
<button onclick="dtmf('3')">3</button>
</p>
<p>
<button onclick="dtmf('4')">4</button>
<button onclick="dtmf('5')">5</button>
<button onclick="dtmf('6')">6</button>
</p>
<p>
<button onclick="dtmf('7')">7</button>
<button onclick="dtmf('8')">8</button>
<button onclick="dtmf('9')">9</button>
</p>
<p>
<button onclick="dtmf('*')">*</button>
<button onclick="dtmf('0')">0</button>
<button onclick="dtmf('#')">#</button>
</p>
<hr />
</body>
</html>
var VOLUME = 0.7;
var EUR_TONE = 425;
var EUR_DURATION = 1;
var EUR_SILENCE = 4;
var DTMF_DURATION = 0.15;
var ctx = new AudioContext();
var gain = ctx.createGain();
gain.gain.value = VOLUME;
// Since we generate mono audio we need to merge it in both channels!
var merger = ctx.createChannelMerger();
merger.connect(ctx.destination);
gain.connect(merger);
gain.connect(merger);
function oscillate (hz, start, duration, gainNode) {
var osc = ctx.createOscillator();
osc.type = 'sine';
osc.frequency.value = hz;
//osc.detune.value = Math.pow(2, 1/12) * 10;
osc.connect(gainNode);
var now = ctx.currentTime;
osc.start(now + start);
osc.stop(now + start + duration);
return osc;
}
var ringNodes = [];
window.ringUK = function () {
// Enable global audio output
gain.gain.setValueAtTime(VOLUME, ctx.currentTime);
// Create a gain node specific for the ring tone
var ring = ctx.createGain();
ringNodes.push(ring);
// Connect the ringing gain to the global one
ring.connect(gain);
// Start the oscillators
ringNodes.push(
oscillate(400, 0, 100 * (0.4 + 0.2 + 0.4 + 2.0), ring)
);
ringNodes.push(
oscillate(450, 0, 100 * (0.4 + 0.2 + 0.4 + 2.0), ring)
);
// Setup the gain node to make the rbt sequence
var now = ctx.currentTime;
var gv = ring.gain;
for (var i=0; i < 100; i++ ) {
// Fade in
gv.setValueAtTime(0, now);
gv.linearRampToValueAtTime(1, now + 0.02);
// Fade out
gv.setValueAtTime(1, now + (0.4) - 0.02);
gv.linearRampToValueAtTime(0, now + (0.4));
// Fade in
gv.setValueAtTime(0, now + (0.4 + 0.2));
gv.linearRampToValueAtTime(1, now + (0.4 + 0.2) + 0.02);
// Fade out
gv.setValueAtTime(1, now + (0.4 + 0.2 + 0.4) - 0.02);
gv.linearRampToValueAtTime(0, now + (0.4 + 0.2 + 0.4));
now += (0.4 + 0.2 + 0.4 + 2.0);
}
};
window.ringEU = function () {
// Enable global audio output
gain.gain.setValueAtTime(VOLUME, ctx.currentTime);
// Create a gain node specific for the ring tone
var ring = ctx.createGain();
ringNodes.push(ring);
// Connect the oscillator gain to the global one
ring.connect(gain);
// Start the oscillator with the tone
var osc = oscillate(EUR_TONE, 0, 100 * (EUR_DURATION + EUR_SILENCE), ring);
ringNodes.push(osc);
// Setup the gain node to make the rbt sequence
var now = ctx.currentTime;
var gv = ring.gain;
for (var i=0; i < 100; i++) {
// Fade in
gv.setValueAtTime(0, now);
gv.linearRampToValueAtTime(1, now + 0.02);
// Fade out
gv.setValueAtTime(1, now + EUR_DURATION - 0.02);
gv.linearRampToValueAtTime(0, now + EUR_DURATION);
now = now + EUR_DURATION + EUR_SILENCE;
}
};
window.stop = function () {
while (ringNodes.length) {
var node = ringNodes.pop();
node.disconnect();
// In case it's an oscillators and hasn't stopped yet
try { node.stop(); } catch (e) {}
}
//gain.gain.setValueAtTime(0, ctx.currentTime);
gain.gain.setValueAtTime(VOLUME, ctx.currentTime);
gain.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.15);
};
var hi = [ 1209, 1336, 1477, 1633 ];
var lo = [ 697, 770, 852, 941 ];
var digits = '123a456b789c*0#d'.split('');
var tones = {};
for (var i = 0; i < lo.length; i++) {
for (var j = 0; j < hi.length; j++) {
var lb = digits.shift();
tones[lb] = tones[lb.toUpperCase()] = [ lo[i], hi[j] ];
}
}
window.dtmf = function (digit, delay, duration) {
delay = delay || 0;
duration = duration || DTMF_DURATION;
// Use a small fade out to avoid the nasty click sound at the end
//gain.gain.value = VOLUME;
var now = ctx.currentTime + delay;
gain.gain.setValueAtTime(VOLUME, now);
gain.gain.setValueAtTime(VOLUME, now + duration - 0.02);
gain.gain.linearRampToValueAtTime(0, now + duration);
oscillate(tones[digit][0], delay, duration, gain);
oscillate(tones[digit][1], delay, duration, gain);
};
window.playDtmf = function () {
var phone = document.getElementById('phone').value;
var delay = 0;
var duration = 0.090;
for (var i=0; i < phone.length; i++) {
var digit = phone[i].toUpperCase();
if (!tones[digit]) continue;
dtmf(digit, delay, duration);
delay += duration + 0.002;
}
}
function makeSlider (title, name, min, max, step) {
var body = document.body;
var span = document.createElement('span');
var slider = document.createElement('input');
slider.type = 'range';
slider.min = min;
slider.max = max;
slider.step = step;
slider.value = '' + window[name];
slider.addEventListener('change', function () {
window[name] = parseFloat(this.value, 10);
span.innerHTML = '(' + window[name] + ')';
});
slider.addEventListener('mousemove', function () {
window[name] = parseFloat(this.value, 10);
span.innerHTML = '(' + window[name] + ')';
});
slider.addEventListener('keyup', function () {
window[name] = parseFloat(this.value, 10);
span.innerHTML = '(' + window[name] + ')';
});
span.innerHTML = '(' + window[name] + ')';
var p = document.createElement('p');
p.appendChild(document.createTextNode(title));
p.appendChild(slider);
p.appendChild(span);
body.appendChild(p);
}
makeSlider('Volume', 'VOLUME', 0, 1, 0.1);
makeSlider('DTMF duration', 'DTMF_DURATION', 0.05, 1, 0.05);
makeSlider('Europe tone freq.', 'EUR_TONE', 100, 1000, 5);
makeSlider('Europe tone duration', 'EUR_DURATION', 0.2, 2, 0.1);
makeSlider('Europe silence duration', 'EUR_SILENCE', 0.2, 6, 0.2);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment