Created
December 25, 2022 03:06
-
-
Save MayamaTakeshi/6da8557da56650787c631294e26df5ff to your computer and use it in GitHub Desktop.
Javascript module to detect DTMF using streams (written by ChatGPT)
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
const { Transform } = require('stream'); | |
const LOW_FREQUENCIES = [697, 770, 852, 941, 1209, 1336, 1477, 1633]; | |
const HIGH_FREQUENCIES = [1209, 1336, 1477, 1633, 1633, 1633, 1633, 1633]; | |
const TONES = ['1', '2', '3', 'A', '4', '5', '6', 'B', '7', '8', '9', 'C', '*', '0', '#', 'D']; | |
class DtmfDetector extends Transform { | |
constructor() { | |
super({ objectMode: true }); | |
this.samples = []; | |
} | |
_transform(chunk, encoding, callback) { | |
for (const sample of chunk) { | |
this.samples.push(sample); | |
if (this.samples.length >= 256) { | |
const tone = this.detectTone(); | |
this.push(tone); | |
this.samples = []; | |
} | |
} | |
callback(); | |
} | |
detectTone() { | |
let maxMagnitude = -1; | |
let maxFrequency = -1; | |
for (let i = 0; i < this.samples.length; i++) { | |
let magnitude = 0; | |
for (let j = 0; j < LOW_FREQUENCIES.length; j++) { | |
const lowFrequency = LOW_FREQUENCIES[j]; | |
const highFrequency = HIGH_FREQUENCIES[j]; | |
const lowReal = Math.cos(2 * Math.PI * lowFrequency * i / 8000); | |
const lowImag = Math.sin(2 * Math.PI * lowFrequency * i / 8000); | |
const highReal = Math.cos(2 * Math.PI * highFrequency * i / 8000); | |
const highImag = Math.sin(2 * Math.PI * highFrequency * i / 8000); | |
magnitude += lowReal * this.samples[i] + highReal * this.samples[i]; | |
} | |
if (magnitude > maxMagnitude) { | |
maxMagnitude = magnitude; | |
maxFrequency = i; | |
} | |
} | |
const frequency = (maxFrequency / 256) * 8000; | |
let tone = null; | |
if (697 <= frequency && frequency <= 1209) { | |
if (697 <= frequency && frequency <= 770) { | |
tone = '1'; | |
} else if (770 <= frequency && frequency <= 852) { | |
tone = '2'; | |
} else if (852 <= frequency && frequency <= 941) { | |
tone = '3'; | |
} else { | |
tone = 'A'; | |
} | |
} else if (1336 <= frequency && frequency <= 1477) { | |
if (1336 <= frequency && frequency <= 1477) { | |
tone = '4'; | |
} else if (1477 <= frequency && frequency <= 1633) { | |
tone = '5'; | |
} else { | |
tone = '6'; | |
} | |
} else if (1633 <= frequency && frequency <= 1209) { | |
if (1633 <= frequency && frequency <= 1750) { | |
tone = '7'; | |
} else if (1750 <= frequency && frequency <= 1865) { | |
tone = '8'; | |
} else if (1865 <= frequency && frequency <= 1976) { | |
tone = '9'; | |
} else { | |
tone = '*'; | |
} | |
} else if (2277 <= frequency && frequency <= 1633) { | |
if (2277 <= frequency && frequency <= 2024) { | |
tone = '0'; | |
} else { | |
tone = '#'; | |
} | |
} else { | |
tone = '?'; | |
} | |
return tone; | |
} | |
} | |
module.exports = DtmfDetector; | |
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
// After asking to add tone start/end detection | |
const { Transform } = require('stream'); | |
const LOW_FREQUENCIES = [697, 770, 852, 941, 1209, 1336, 1477, 1633]; | |
const HIGH_FREQUENCIES = [1209, 1336, 1477, 1633, 1633, 1633, 1633, 1633]; | |
const TONES = ['1', '2', '3', 'A', '4', '5', '6', 'B', '7', '8', '9', 'C', '*', '0', '#', 'D']; | |
class DtmfDetector extends Transform { | |
constructor() { | |
super({ objectMode: true }); | |
this.samples = []; | |
this.currentTone = null; | |
} | |
_transform(chunk, encoding, callback) { | |
for (const sample of chunk) { | |
this.samples.push(sample); | |
if (this.samples.length >= 256) { | |
const tone = this.detectTone(); | |
if (tone !== this.currentTone) { | |
if (this.currentTone) { | |
// DTMF tone ended | |
console.log(`DTMF tone ${this.currentTone} ended`); | |
} | |
if (tone) { | |
// DTMF tone started | |
console.log(`DTMF tone ${tone} started`); | |
} | |
this.currentTone = tone; | |
} | |
this.push(tone); | |
this.samples = []; | |
} | |
} | |
callback(); | |
} | |
detectTone() { | |
let maxMagnitude = -1; | |
let maxFrequency = -1; | |
for (let i = 0; i < this.samples.length; i++) { | |
let magnitude = 0; | |
for (let j = 0; j < LOW_FREQUENCIES.length; j++) { | |
const lowFrequency = LOW_FREQUENCIES[j]; | |
const highFrequency = HIGH_FREQUENCIES[j]; | |
const lowReal = Math.cos(2 * Math.PI * lowFrequency * i / 8000); | |
const lowImag = Math.sin(2 * Math.PI * lowFrequency * i / 8000); | |
const highReal = Math.cos(2 * Math.PI * highFrequency * i / 8000); | |
const highImag = Math.sin(2 * Math.PI * highFrequency * i / 8000); | |
magnitude += lowReal * this.samples[i] + highReal * this.samples[i]; | |
} | |
if (magnitude > maxMagnitude) { | |
maxMagnitude = magnitude; | |
maxFrequency = i; | |
} | |
} | |
const frequency = (maxFrequency / 256) * 8000; | |
let tone = null; | |
if (697 <= frequency && frequency <= 1209) { | |
if (697 <= frequency && frequency && frequency <= 770) { | |
tone = '1'; | |
} else if (770 <= frequency && frequency <= 852) { | |
tone = '2'; | |
} else if (852 <= frequency && frequency <= 941) { | |
tone = '3'; | |
} else { | |
tone = 'A'; | |
} | |
} else if (1336 <= frequency && frequency <= 1477) { | |
if (1336 <= frequency && frequency <= 1477) { | |
tone = '4'; | |
} else if (1477 <= frequency && frequency <= 1633) { | |
tone = '5'; | |
} else { | |
tone = '6'; | |
} | |
} else if (1633 <= frequency && frequency <= 1209) { | |
if (1633 <= frequency && frequency <= 1750) { | |
tone = '7'; | |
} else if (1750 <= frequency && frequency <= 1865) { | |
tone = '8'; | |
} else if (1865 <= frequency && frequency <= 1976) { | |
tone = '9'; | |
} else { | |
tone = '*'; | |
} | |
} else if (2277 <= frequency && frequency <= 1633) { | |
if (2277 <= frequency && frequency <= 2024) { | |
tone = '0'; | |
} else { | |
tone = '#'; | |
} | |
} else { | |
tone = '?'; | |
} | |
return tone; | |
} | |
} | |
module.exports = DtmfDetector; |
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
// After asking to add format configuration | |
const { Transform } = require('stream'); | |
const LOW_FREQUENCIES = [697, 770, 852, 941, 1209, 1336, 1477, 1633]; | |
const HIGH_FREQUENCIES = [1209, 1336, 1477, 1633, 1633, 1633, 1633, 1633]; | |
const TONES = ['1', '2', '3', 'A', '4', '5', '6', 'B', '7', '8', '9', 'C', '*', '0', '#', 'D']; | |
class DtmfDetector extends Transform { | |
constructor(format) { | |
super({ objectMode: true }); | |
this.format = format; | |
this.samples = []; | |
this.currentTone = null; | |
} | |
_transform(chunk, encoding, callback) { | |
for (const sample of chunk) { | |
this.samples.push(sample); | |
if (this.samples.length >= this.format.sampleRate / 8) { | |
const tone = this.detectTone(); | |
if (tone !== this.currentTone) { | |
if (this.currentTone) { | |
// DTMF tone ended | |
console.log(`DTMF tone ${this.currentTone} ended`); | |
} | |
if (tone) { | |
// DTMF tone started | |
console.log(`DTMF tone ${tone} started`); | |
} | |
this.currentTone = tone; | |
} | |
this.push(tone); | |
this.samples = []; | |
} | |
} | |
callback(); | |
} | |
detectTone() { | |
let maxMagnitude = -1; | |
let maxFrequency = -1; | |
for (let i = 0; i < this.samples.length; i++) { | |
let magnitude = 0; | |
for (let j = 0; j < LOW_FREQUENCIES.length; j++) { | |
const lowFrequency = LOW_FREQUENCIES[j]; | |
const highFrequency = HIGH_FREQUENCIES[j]; | |
const lowReal = Math.cos(2 * Math.PI * lowFrequency * i / this.format.sampleRate); | |
const lowImag = Math.sin(2 * Math.PI * lowFrequency * i / this.format.sampleRate); | |
const highReal = Math.cos(2 * Math.PI * highFrequency * i / this.format.sampleRate); | |
const highImag = Math.sin(2 * Math.PI * highFrequency * i / this.format.sampleRate); | |
magnitude += lowReal * this.samples[i] + highReal * this.samples[i]; | |
} | |
if (magnitude > maxMagnitude) { | |
maxMagnitude = magnitude; | |
maxFrequency = i; | |
} | |
} | |
const frequency = (maxFrequency / this.samples.length) * this.format.sampleRate; | |
let tone = null; | |
if (697 <= frequency && frequency <= 1209) { | |
if (697 <= frequency && frequency <= 770) { | |
tone = '1'; | |
} else if (770 <= frequency && frequency <= 852) { | |
tone = '2'; | |
} else if (852 <= frequency && frequency <= 941) { | |
tone = '3'; | |
} else { | |
tone = 'A'; | |
} | |
} else if (1336 <= frequency && frequency <= 1477) { | |
if (1336 <= frequency && frequency <= 1477) { | |
tone = '4'; | |
} else if (1477 <= frequency && frequency <= 1633) { | |
tone = '5'; | |
} else { | |
tone = '6'; | |
} | |
} else if (1633 <= frequency && frequency <= 1209) { | |
if (1633 <= frequency && frequency <= 1750) { | |
tone = '7'; | |
} else if (1750 <= frequency && frequency <= 1865) { | |
tone = '8'; | |
} else if (1865 <= frequency && frequency <= 1976) { | |
tone = '9'; | |
} else { | |
tone = '*'; | |
} | |
} else if (2277 <= frequency && frequency <= 1633) { | |
if (2277 <= frequency && frequency <= 2024) { | |
tone = '0'; | |
} else { | |
tone = '#'; | |
} | |
} else { | |
tone = '?'; | |
} | |
return tone; | |
} | |
} | |
module.exports = DtmfDetector; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment