Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamin committed Apr 7, 2014
1 parent e1c1ef4 commit 4707881
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 2 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2014 Ravenstine
Copyright (c) 2014 Benjamin Titcomb

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
2 changes: 1 addition & 1 deletion README.md
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.
14 changes: 14 additions & 0 deletions demo.html
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>
35 changes: 35 additions & 0 deletions demo.js
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)
}


87 changes: 87 additions & 0 deletions dtmf.js
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()
}

}
85 changes: 85 additions & 0 deletions goertzel.js
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()

}

0 comments on commit 4707881

Please sign in to comment.