-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
benjamin
committed
Apr 7, 2014
1 parent
e1c1ef4
commit 4707881
Showing
6 changed files
with
223 additions
and
2 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
goertzeljs | ||
goertzel.js | ||
========== | ||
|
||
A pure JavaScript implementation of the Goertzel algorithm. |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>Goertzel.js DTMF Demo</title> | ||
<script data-turbolinks-track="true" src="/assets/goertzel.js?body=1"></script> | ||
<script data-turbolinks-track="true" src="/assets/dtmf.js?body=1"></script> | ||
<script data-turbolinks-track="true" src="/assets/demo.js?body=1"></script> | ||
</head> | ||
<body> | ||
|
||
<div id="output"></div> | ||
|
||
</body> | ||
</html> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
if (!navigator.getUserMedia) | ||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || | ||
navigator.mozGetUserMedia || navigator.msGetUserMedia | ||
|
||
if (navigator.getUserMedia){ | ||
navigator.getUserMedia({audio:true}, success, function(e) { | ||
alert('Error capturing audio.') | ||
}) | ||
} else alert('getUserMedia not supported in this browser.') | ||
|
||
|
||
function success(e){ | ||
audioContext = window.AudioContext || window.webkitAudioContext | ||
context = new audioContext() | ||
volume = context.createGain() | ||
audioInput = context.createMediaStreamSource(e) | ||
audioInput.connect(volume) | ||
var bufferSize = 2048 | ||
recorder = context.createJavaScriptNode(bufferSize, 1, 1) | ||
var outputElement = document.querySelector('#output') | ||
var dtmf = new DTMF(context.sampleRate,5,0) | ||
dtmf.onDecode = function(value){ | ||
outputElement.innerHTML = outputElement.innerHTML + value | ||
} | ||
recorder.onaudioprocess = function(e){ | ||
var bin = e.inputBuffer.getChannelData (0) | ||
dtmf.processBin(bin) | ||
dtmf.refresh() | ||
} | ||
|
||
volume.connect (recorder) | ||
recorder.connect (context.destination) | ||
} | ||
|
||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
function DTMF(samplerate,decimation,threshold){ | ||
var self = this | ||
var samplerate = samplerate / decimation | ||
var decimation = decimation || 5 | ||
var threshold = threshold || 0.0002 | ||
var frequencyTable = { | ||
697: {1209: "1", 1336: "2", 1477: "3", 1633: "A"}, | ||
770: {1209: "4", 1336: "5", 1477: "6", 1633: "B"}, | ||
852: {1209: "7", 1336: "8", 1477: "9", 1633: "C"}, | ||
941: {1209: "*", 1336: "0", 1477: "#", 1633: "D"} | ||
} | ||
var lowFrequencies = [] | ||
for(var key in frequencyTable) lowFrequencies.push(parseInt(key)) | ||
var highFrequencies = [] | ||
for (var key in frequencyTable[lowFrequencies[0]]) highFrequencies.push(parseInt(key)) | ||
var allFrequencies = lowFrequencies.concat(highFrequencies) | ||
var frequencyData = { | ||
frequencyTable: frequencyTable, | ||
lowFrequencies: lowFrequencies, | ||
highFrequencies: highFrequencies, | ||
allFrequencies: allFrequencies | ||
} | ||
|
||
self.repeatCounter = 0 | ||
self.firstPreviousValue = "" | ||
self.goertzel = new Goertzel(frequencyData,samplerate,threshold) | ||
|
||
self.processBin = function(bin){ | ||
var value = "" | ||
var register = self.generateFrequencyRegister() | ||
// Downsample by decimation(choosing every Nth sample). | ||
for ( var i=0; i< bin.length; i+=decimation ) { | ||
floatSample = bin[i] * 32768 ; | ||
if ( floatSample > 32767 ) { | ||
floatSample = 32767 | ||
} else if (floatSample < -32786) { | ||
floatSample = -32768; | ||
} | ||
|
||
intSample = Math.round(floatSample) | ||
register.sample = intSample | ||
|
||
register = self.goertzel.getEnergyFromSample(register) | ||
value = self.goertzel.energyProfileToCharacter(register) | ||
} | ||
if (value == self.firstPreviousValue && value != undefined ){ | ||
self.repeatCounter+=1 | ||
if (self.repeatCounter == 4 && typeof this.onDecode === "function"){ | ||
// outputElement.innerHTML = outputElement.innerHTML + value | ||
setTimeout(this.onDecode(value), 1); | ||
} | ||
} else { | ||
self.repeatCounter = 0 | ||
self.firstPreviousValue = value | ||
} | ||
|
||
} | ||
|
||
self.generateFrequencyRegister = function(){ | ||
var register = { | ||
firstPrevious: {}, | ||
secondPrevious: {}, | ||
totalPower: {}, | ||
filterLength: {}, | ||
sample: 0, | ||
energies: {}, | ||
rememberSample: function(sample,frequency){ | ||
this.secondPrevious[frequency] = this.firstPrevious[frequency] | ||
this.firstPrevious[frequency] = sample | ||
} | ||
} | ||
for ( var i=0; i< allFrequencies.length; i++ ){ | ||
var frequency = allFrequencies[i] | ||
register.firstPrevious[frequency] = 0.0 | ||
register.secondPrevious[frequency] = 0.0 | ||
register.totalPower[frequency] = 0.0 | ||
register.filterLength[frequency] = 0.0 | ||
register.energies[frequency] = 0.0 | ||
} | ||
return register | ||
} | ||
|
||
self.refresh = function(){ | ||
self.goertzel.refresh() | ||
} | ||
|
||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
function Goertzel(frequencyData,samplerate,threshold){ | ||
var self = this | ||
self.threshold = threshold | ||
self.samplerate = samplerate | ||
self.frequencyTable = frequencyData.frequencyTable | ||
self.lowFrequencies = frequencyData.lowFrequencies | ||
self.highFrequencies = frequencyData.highFrequencies | ||
self.allFrequencies = frequencyData.allFrequencies | ||
self.firstPrevious = {} | ||
self.secondPrevious = {} | ||
self.totalPower = {} | ||
self.filterLength = {} | ||
self.coefficient = {} | ||
|
||
for ( var i=0; i< self.allFrequencies.length; i++ ){ | ||
var frequency = self.allFrequencies[i] | ||
normalizedFrequency = frequency / self.samplerate | ||
self.coefficient[frequency] = 2.0*Math.cos(2.0 * 3.14 * normalizedFrequency) | ||
} | ||
|
||
self.refresh = function(){ | ||
self.firstPrevious = {} | ||
self.secondPrevious = {} | ||
self.totalPower = {} | ||
self.filterLength = {} | ||
|
||
for ( var i=0; i< self.allFrequencies.length; i++ ){ | ||
var frequency = self.allFrequencies[i] | ||
self.firstPrevious[frequency] = 0.0 | ||
self.secondPrevious[frequency] = 0.0 | ||
self.totalPower[frequency] = 0.0 | ||
self.filterLength[frequency] = 0.0 | ||
} | ||
} | ||
|
||
self.energyProfileToCharacter = function(register){ | ||
var energies = register.energies | ||
var highFrequency = 0.0 | ||
var highFrequencyEngergy = 0.0 | ||
|
||
for (var i=0; i<self.highFrequencies.length; i++){ | ||
var f = self.highFrequencies[i] | ||
if (energies[f] > highFrequencyEngergy && energies[f] > self.threshold){ | ||
highFrequencyEngergy = energies[f] | ||
highFrequency = f | ||
} | ||
} | ||
|
||
var lowFrequency = 0.0 | ||
var lowFrequencyEnergy = 0.0 | ||
|
||
for (var i=0; i<self.lowFrequencies.length; i++){ | ||
var f = self.lowFrequencies[i] | ||
if (energies[f] > lowFrequencyEnergy && energies[f] > self.threshold){ | ||
lowFrequencyEnergy = energies[f] | ||
lowFrequency = f | ||
} | ||
} | ||
register = null | ||
delete register | ||
if (self.frequencyTable[lowFrequency] != undefined){ | ||
return self.frequencyTable[lowFrequency][highFrequency] || null | ||
} | ||
|
||
} | ||
|
||
self.getEnergyFromSample = function(register){ | ||
for ( var i=0; i< self.allFrequencies.length; i++ ){ | ||
var frequency = self.allFrequencies[i] | ||
sine = register.sample + (self.coefficient[frequency] * register.firstPrevious[frequency]) - register.secondPrevious[frequency] | ||
register.rememberSample(sine,frequency) | ||
register.filterLength[frequency]+=1 | ||
power = (register.secondPrevious[frequency]*register.secondPrevious[frequency]) + (register.firstPrevious[frequency]*register.firstPrevious[frequency]) - (self.coefficient[frequency]*register.firstPrevious[frequency]*register.secondPrevious[frequency]) | ||
register.totalPower[frequency]+=register.sample*register.sample | ||
if(register.totalPower[frequency] == 0){ | ||
register.totalPower[frequency] = 1 | ||
} | ||
register.energies[frequency] = power / register.totalPower[frequency] / register.filterLength[frequency] | ||
} | ||
return register | ||
} | ||
|
||
self.refresh() | ||
|
||
} |