zero crossing audio object for frequency detection

Status
Not open for further replies.

emmanuel63

Well-known member
Hello,

I want to mesure as fast as possible the frequency of a sine.
The audio library already provides objects for pitch tracking, but they have some latency due to buffering.
Since my signal is almost a pure sine, I would like to use a "zero crossing" method to estimate frequency.

I use a teensy 3.6 with audio shield.
I have already created very basic custom audio objects, but I can't figure out how to start for this one. For the moment, I was just able to create an object that indicates when the audio signal is either positive or negative.

If someone could give me some directions an ideas, that would help me a lot.
Emmanuel

Code:
#include "zero_pitch.h"

void zero_pitch::update(void) {
  int16_t *p, *end;
  audio_block_t *block;


  block = receiveReadOnly(0); //block reçoit l'adresse du paquet audio canal 0
  if (block == NULL) return;
  p = block->data;     
  end = p + AUDIO_BLOCK_SAMPLES; 
  while (p < end) {
    
    int16_t s = *p; 
    if (s < 0 && alternance == 1) {
      alternance = 0;
    }
    if (s > 0 && alternance == 0) {
      alternance = 1;
    }
    p++;
  }
  transmit(block);
  release(block);
}

bool zero_pitch::getAlternance() {
  return alternance;
}
 
Well you could time successive crossings, take the reciprocal to get a frequency estimate, and then digitally filter the result to smooth variations.

You'd be able to get _much_ better results if you interpolate between samples to estimate zero-crossing time to a fraction of a sample period.

If the waveform isn't symmetric you'd want to time between consecutive rising crossings, as the crossings won't be even in time.
 
Hi,
Thank you for your suggestions. Here is a first attempt :
Code:
#include "zero_pitch.h"

void zero_pitch::update(void) {
  int16_t *p;
  audio_block_t *block;
  block = receiveReadOnly(0);
  if (block == NULL) return;
  p = block->data;


  for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
    int16_t s = *p;

    if (s >= 0 && upward == 0) {
      upward = 1;                         //audio signal is going up
      period = i - prev_i;                //how many sample since last 0 crossing

      if (period > 0) {
        float freq = AUDIO_SAMPLE_RATE_EXACT / period;
        Serial.println(freq);  //for debugging
      }
      prev_i = i;
    }

    
    if (s < 0 && upward == 1) {
      upward = 0;                          //audio signal is going up
    }
    p++;
  }
  transmit(block);
  release(block);
}

My code is working but it is very poor and I am really not proud of it :-(
At least, it's fast.
It is working with a block of 128 samples. I need 256 samples blocks but I can't figure out the way to use 2 blocks in my code.
The way I track the zero crossings is also very weak.

A little help would be very appreciated...
Emmanuel
 
Why are you retransmitting the block? Wouldn't this make more sense either as a destination only?

To handle multiple blocks you need to store references to the blocks locally and remember to release them
when you finished using them. For 2 blocks you store the previous block only which is easiest:

Get the current block.
process the previous block and current block,
release the previous block, store ref to current block as prev block for next time.

Any NULL block pointers are treated as all zero.
 
You're right, I don't need to transmit the block. I will change that.

I have improved my code and it's working well now. Basically, here is my method :
- I first initialise a sample counter
- I track the zero crossings and read my sample counter at every crossing
- simple maths give me period and frequency

This is basically what you suggested first.
Doing this way, I don't need larger audio blocks for low frequency.

Emmanuel
 
Are you trying to measure an external signal? If so, for minimum latency you might consider using an external comparator like an LM393 and connect it's output to a digital input. You can use a potentiometer as a voltage divider for the comparison voltage then AC couple the input signal via a capacitor to the other comparator input. This will give you the ability to set a threshold for some noise immunity if you want to detect presence/absence of signal. But if you don't care then 2 same value resistors (e.g. 47k) can be used a half voltage divider for a straightforward zero crossing detector. Ideally you'd want to use 2 comparators (conveniently, you get 2 per LM393 8-pin package) where one has an adjustable offset for signal detection and the other a straight zero crossing detector. Then 2 pins to Teensy.

Hello,

I want to mesure as fast as possible the frequency of a sine.
The audio library already provides objects for pitch tracking, but they have some latency due to buffering.
Since my signal is almost a pure sine, I would like to use a "zero crossing" method to estimate frequency.

I use a teensy 3.6 with audio shield.
I have already created very basic custom audio objects, but I can't figure out how to start for this one. For the moment, I was just able to create an object that indicates when the audio signal is either positive or negative.

If someone could give me some directions an ideas, that would help me a lot.
Emmanuel

Code:
#include "zero_pitch.h"

void zero_pitch::update(void) {
  int16_t *p, *end;
  audio_block_t *block;


  block = receiveReadOnly(0); //block reçoit l'adresse du paquet audio canal 0
  if (block == NULL) return;
  p = block->data;     
  end = p + AUDIO_BLOCK_SAMPLES; 
  while (p < end) {
    
    int16_t s = *p; 
    if (s < 0 && alternance == 1) {
      alternance = 0;
    }
    if (s > 0 && alternance == 0) {
      alternance = 1;
    }
    p++;
  }
  transmit(block);
  release(block);
}

bool zero_pitch::getAlternance() {
  return alternance;
}
 
Thank you.
You are right, an external comparator would probably do the job.
I need to filter the audio signal before measuring frequency. I need to "slice" the signal into 4 bands, from 200 hz to 2000 hz approximatively. Low pass filter with high slope are required. Would you have a suggestion to that ?
Emmanuel
 
Thank you.
You are right, an external comparator would probably do the job.
I need to filter the audio signal before measuring frequency. I need to "slice" the signal into 4 bands, from 200 hz to 2000 hz approximatively. Low pass filter with high slope are required. Would you have a suggestion to that ?
Emmanuel

Do you mean band pass filters (at least for the intermediate ranges)? What are you trying to measure? How separated do you expect the 4 frequencies you are trying to measure are? It could be very difficult or easy depending on what it is.
 
Hi,
I use 4 low pass filters. All the filters are fed with the audio input signal.
first filter : fc = 300
second : fc = 500
third : 800
forth : 1200 (for example).

The output of the filters is routed to a pitch tracking sketch. This sketch is fast but requires strong pre-filtering (to remove harmonics). I found that 4 filters was enough to cover the range of audio signal. I use 4 biquad low pass filters. The concept is working good and I think I can achieve close to real-time pitch tracking.
Have a look here to see what I am working on :
https://youtu.be/N79Hcg-cOfI


I would like to try now an external approach with dedicated filtering circuits. I need to build a low pass filter with a hard slope.

Emmanuel
 
That looks really cool :) I'm doing a guitar synth project - polyphonic tracking. But all in the digital domain. So far I'm getting 10-80Hz latency depending on the note. You're a much better guitarist than me!

I'm not good enough with analogue design to dare to give you any advice with respect to designing audio high order filters. There's a lot that comes into it - choice of op-amps and passive components. But there seem to be a lot of knowledge out in the interwebs.
 
Polyphonic tracking... This is very challenging.
I had quite good result with the AccurateFFTInterpolator object. Here is a link :
https://forum.pjrc.com/threads/36358-A-New-Accurate-FFT-Interpolator-for-Frequency-Estimation

Even for monophonic pitch tracking, this object works very well.

For my project, I am focused on the flute. It's easier because the natural pitch range of the flute is high (from C4). So I can achieve low latency.

Thanks for your ideas. I will keep this thread active if I have success.
Emmanuel
 
Status
Not open for further replies.
Back
Top