High frequency pitch detection

twin_tone

Member
Hi all,

I'm trying to implement high frequency pitch tracking in a project but am having some issues.

I'm trying to make a device that transposes ultrasound into an audible range - basically a bat detector, but I'm not looking for bats. The issue is that after about 8000 Hz the tracking is pretty unreliable. It doesn't seem as though what it's reading is arbitrary - it's generally the correct value /2 or /3, so I'm guessing that somehow I'm getting subharmonics or something. I read in another thread that it was likely that the tracker in the library wouldn't work at high frequencies. I'd be grateful if anyone has any suggestions for how to do this. I suppose one option would be to use FFT and grab information from the loudest bin, but I want to be able to track the pitch continuously in order to resynthesize it.

I'm using an electret that's rated from 20 - 20,000 Hz and a SPW2430 MEMS mic, which performs a little better. But I'm assuming it's an algorithm more than a hardware issue.

Any help is super appreciated...
 
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2S i2s2; //xy=99,110
AudioSynthWaveform waveform2; //xy=127,309
AudioSynthWaveform waveform1; //xy=133,265
AudioFilterStateVariable filter1; //xy=316,109
AudioMixer4 mixer1; //xy=528,303
AudioEffectEnvelope envelope1; //xy=568,434
AudioAnalyzeNoteFrequency notefreq1; //xy=685,151
AudioOutputI2S i2s1; //xy=960,199
AudioConnection patchCord1(i2s2, 0, filter1, 0);
AudioConnection patchCord2(waveform2, 0, mixer1, 1);
AudioConnection patchCord3(waveform1, 0, mixer1, 0);
AudioConnection patchCord4(filter1, 2, notefreq1, 0);
AudioConnection patchCord5(mixer1, envelope1);
AudioConnection patchCord6(envelope1, 0, i2s1, 0);
AudioConnection patchCord7(envelope1, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1; //xy=222,428
// GUItool: end automatically generated code







int releaseVal = 1000;


void setup() {
Serial.begin(9600);
AudioMemory(30);
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);
sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
sgtl5000_1.micGain(36);
notefreq1.begin(.15);
delay(1000);

mixer1.gain(0, 0.7);
mixer1.gain(1, 0.7);

waveform1.begin(WAVEFORM_SINE);
waveform1.amplitude(0.75);
waveform1.frequency(400);


//waveform2.begin(WAVEFORM_SQUARE);
//waveform2.amplitude(0.75);
//waveform2.frequency(400);

envelope1.attack(10);
envelope1.hold(10);
envelope1.decay(25);
envelope1.sustain(0.4);
envelope1.release(releaseVal);
}

float note, prob;
float oldValue = 60;
float thresholdVal = 100;
float highVal = 1000;
bool triggered = false;

void loop() {
if (notefreq1.available()) {
note = notefreq1.read();
//if value has changed compared to previous time and the envelope is not currently on then turn the envelope on and set triggered to true
//if (abs(note-oldValue) > thresholdVal && triggered == false && note>highVal){
if (triggered == false && note>highVal){
//waveform1.frequency(note * 2);
envelope1.noteOn();
Serial.println("noteOn");
triggered = true;
}
//else if the value has changed enough and the envelope is still on turn it off
//else if (abs(note-oldValue) > thresholdVal && triggered == true && note<highVal){
else if (triggered == true && note<highVal){
envelope1.noteOff();
Serial.println("noteOff");
delay(releaseVal);
triggered = false;
//oldValue = note;
}
//in any other situation print the difference
else{
Serial.println(abs(note-oldValue) > thresholdVal);
}
oldValue = note;
prob = notefreq1.probability();


waveform1.frequency(note);
//waveform2.frequency(note);

Serial.printf("Note: %3.2f | Probability: %.2f\n", note, prob);


}
}
 
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2S i2s2; //xy=99,110
AudioSynthWaveform waveform2; //xy=127,309
AudioSynthWaveform waveform1; //xy=133,265
AudioFilterStateVariable filter1; //xy=316,109
AudioMixer4 mixer1; //xy=528,303
AudioEffectEnvelope envelope1; //xy=568,434
AudioAnalyzeNoteFrequency notefreq1; //xy=685,151
AudioOutputI2S i2s1; //xy=960,199
AudioConnection patchCord1(i2s2, 0, filter1, 0);
AudioConnection patchCord2(waveform2, 0, mixer1, 1);
AudioConnection patchCord3(waveform1, 0, mixer1, 0);
AudioConnection patchCord4(filter1, 2, notefreq1, 0);
AudioConnection patchCord5(mixer1, envelope1);
AudioConnection patchCord6(envelope1, 0, i2s1, 0);
AudioConnection patchCord7(envelope1, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1; //xy=222,428
// GUItool: end automatically generated code







int releaseVal = 1000;


void setup() {
	Serial.begin(9600);
	AudioMemory(30);
	sgtl5000_1.enable();
	sgtl5000_1.volume(0.5);
	sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
	sgtl5000_1.micGain(36);
	notefreq1.begin(.15);
	delay(1000);

	mixer1.gain(0, 0.7);
	mixer1.gain(1, 0.7);

	waveform1.begin(WAVEFORM_SINE);
	waveform1.amplitude(0.75);
	waveform1.frequency(400);


	//waveform2.begin(WAVEFORM_SQUARE);
	//waveform2.amplitude(0.75);
	//waveform2.frequency(400);

	envelope1.attack(10);
	envelope1.hold(10);
	envelope1.decay(25);
	envelope1.sustain(0.4);
	envelope1.release(releaseVal);
}

float note, prob;
float oldValue = 60;
float thresholdVal = 100;
float highVal = 1000;
bool triggered = false;

void loop() {
	if (notefreq1.available()) {
		note = notefreq1.read();
		//if value has changed compared to previous time and the envelope is not currently on then turn the envelope on and set triggered to true
		//if (abs(note-oldValue) > thresholdVal && triggered == false && note>highVal){
		if (triggered == false && note > highVal) {
			//waveform1.frequency(note * 2);
			envelope1.noteOn();
			Serial.println("noteOn");
			triggered = true;
		}
		//else if the value has changed enough and the envelope is still on turn it off
		//else if (abs(note-oldValue) > thresholdVal && triggered == true && note<highVal){
		else if (triggered == true && note < highVal) {
			envelope1.noteOff();
			Serial.println("noteOff");
			delay(releaseVal);
			triggered = false;
			//oldValue = note;
		}
		//in any other situation print the difference
		else {
			Serial.println(abs(note - oldValue) > thresholdVal);
		}
		oldValue = note;
		prob = notefreq1.probability();


		waveform1.frequency(note);
		//waveform2.frequency(note);

		Serial.printf("Note: %3.2f | Probability: %.2f\n", note, prob);


	}
}
If you include your code between code tags using the # button it makes your code so much more readable and you are more likely to get help with your problem.
Someone on this board has made a bat detector, so I am sure that there is valid information around.
Try searching for teensy bat detector.
 
Hi all,

I suppose one option would be to use FFT and grab information from the loudest bin, but I want to be able to track the pitch continuously in order to resynthesize it.

Perhaps you are looking for a software phase-locked loop (PLL) if you want precise real-time tracking?

Can you explain in more detail what you are trying to do, the nature of the signal, and how ultrasonics is involved
if the microphone only rated to 20kHz?

Pitch detection is often done by auto-correlation followed by spectral analysis, since the auto-correlation strongly
represents the fundamental of a complex waveform - but again this is a block-by-block process rather than sample-by-sample
real time - is that what you mean by "track the pitch continuously"?

PLLs are traditionally used to track a frequency signal buried in noise, there's a vast published literature on them (though mainly analog
rather than DSP).
 
Perhaps you are looking for a software phase-locked loop (PLL) if you want precise real-time tracking?

Can you explain in more detail what you are trying to do, the nature of the signal, and how ultrasonics is involved
if the microphone only rated to 20kHz?

Pitch detection is often done by auto-correlation followed by spectral analysis, since the auto-correlation strongly
represents the fundamental of a complex waveform - but again this is a block-by-block process rather than sample-by-sample
real time - is that what you mean by "track the pitch continuously"?

PLLs are traditionally used to track a frequency signal buried in noise, there's a vast published literature on them (though mainly analog
rather than DSP).

I'm trying transpose a signal that's around 16 kHz into an audible range, so the mics I'm working with should be adequate. The signal is really loud, so there shouldn't be much ambient interference. And I'm going to implement a highpass filter to make sure it's not tracking any lower pitches. And by real-time I just mean that it be able to continuously track the signal. In my limited understanding of FFT, I was imagining that you can only track bin by bin, so if the output is generating the pitch frequency, I figured it might end up being "quantized" or something.

Thanks - I'll look in to PLL.
 
Why not HPF, shift down to a lower frequency, then track that?

Do you actually mean transpose (divide frequency) or frequency shift down? The latter is a simple DSP stage (multiply by 15kHz sinusoid perhaps)

And by real-time I just mean that it be able to continuously track the signal
FFT can't do continuously, FFT processes block by block, so it will be time-quantized to the blocks if
nothing else.

As I mentioned PLL's are the best way to track a changing frequency signal continuously, but I don't know if
anyone's written a software PLL for the Audio lib.
 
Back
Top