Last active
August 29, 2015 14:16
-
-
Save drslump/51d6a8a039c8a6d334b3 to your computer and use it in GitHub Desktop.
dtmf
This file contains hidden or 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"> | |
<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> |
This file contains hidden or 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
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