I'm using node.js on a Raspberry Pi to steer some instruments. I would like to have a mic listening for a specific signal, say a tone at 500 Hz, and trigger an event when it is heard.
Having looked at multiple node.js libraries, node-core-audio () is the one that es closest, but it fails on pilation.
Can anybody remend a good way to do this?
I'm using node.js on a Raspberry Pi to steer some instruments. I would like to have a mic listening for a specific signal, say a tone at 500 Hz, and trigger an event when it is heard.
Having looked at multiple node.js libraries, node-core-audio (https://www.npmjs./package/node-core-audio) is the one that es closest, but it fails on pilation.
Can anybody remend a good way to do this?
Share Improve this question asked Mar 23, 2015 at 16:34 MortenMorten 752 silver badges5 bronze badges 4- 1 Is your problem how to get access to the audio stream, or how to detect a tone of a given frequency in the stream? These are two quite different things. – cfh Commented Mar 23, 2015 at 16:36
- It is how to access the stream. Worst case I'll implement a simple FFT myself. But for now it is simply a nightmare finding a suitable audio library. – Morten Commented Mar 23, 2015 at 16:38
- There is a project to add the full Web Audio API to node.js. It doesn't look like it's far enough along to support your use case, but maybe you could contribute to it. – aldel Commented Mar 23, 2015 at 16:57
- 1 Using an FFT to detect a single tone is overkill - just use Goertzel. – Paul R Commented Mar 23, 2015 at 17:14
2 Answers
Reset to default 6Just use some parts of the Fourier transform for the frequency you are interested in.
Scalar multiply your input signal with two 500 Hz tones shifted one quarter of a wavelength and with a total root mean square (RMS) of one, which means that you scale the vector with n^-½.
var sampleSize = 2000;
var sampleRate = 44100; // Or whatever in use (Hz)
var tone = 500; // tone to detect in Hz
var sin500Hz = Array(sampleSize);
var cos500Hz = Array(sampleSize);
for (var i = 0; i < sampleSize; i++) {
sin500Hz[i] = Math.sin(2*Math.PI*tone/sampleRate*i)/Math.sqrt(sampleSize);
cos500Hz[i] = Math.cos(2*Math.PI*tone/sampleRate*i)/Math.sqrt(sampleSize);
}
Scalar multiply the signal input with the two vectors.
function findTone(inputSamples) {
var amplitudeSin = 0;
var amplitudeCos = 0;
for (var i = 0; i < sampleSize; i++) {
amplitudeSin += inputSamples[i]*sin500Hz[i];
amplitudeCos += inputSamples[i]*cos500Hz[i];
}
return Math.sqrt(amplitudeSin*amplitudeSin + amplitudeCos*amplitudeCos);
}
You may want to pare this value with the total amplitude of the signal (take the RMS of all samples in the interval) otherwise noise can also be detected with findTone().
function noiseLevel(inputSamples) {
var power = 0;
var average = 0;
for (var i = 0; i < sampleSize; i++) {
average += inputSamples[i];
}
average /= sampleSize;
for (var i = 0; i < sampleSize; i++) {
power += Math.pow(inputSamples[i] - average, 2);
}
return Math.sqrt(power);
}
So the detection you want is probably some minimum value of the quota findTone(inputSamples)/noiseLevel(inputSamples).
If you use too many samples the algorithm will be very precise and maybe more precise than you wish if you have a biased or noisy 500 Hz input signal. Then a band pass filter is an alternative.
It is a waste of CPU cycles to use FFT if you only want one or some few parts of a spectrum and FFT neither allows arbitrary frequencies nor arbitrary sampling sets.
Just a heads up, I've e across this question as well, but couldn't find an answer. I went ahead and made 2 NPM modules, 1 that wraps "sndpeek" (an opensource audio analyzing library) and another one that detects tones with that wrapped module; https://www.npmjs./package/whistle-detection
Its too bad that I can't get the sndpeek application to work on my raspberry pi, but these node apps works great on my Linux (ubuntu 15.04) and Windows 7 machine.