OK, let's actually implement the "mean crossing" technique and see if it works. We'll start simple and just print the computed results to the serial monitor. Here's the complete code:

```# SPDX-FileCopyrightText: 2019 Carter Nelson for Adafruit Industries
#

import time
import array
import board
import audiobusio

#---| User Configuration |---------------------------
SAMPLERATE = 16000
SAMPLES = 1024
THRESHOLD = 100
MIN_DELTAS = 5
DELAY = 0.2
#----------------------------------------------------

# Create a buffer to record into
samples = array.array('H', [0] * SAMPLES)

# Setup the mic input
mic = audiobusio.PDMIn(board.MICROPHONE_CLOCK,
board.MICROPHONE_DATA,
sample_rate=SAMPLERATE,
bit_depth=16)

while True:
# Get raw mic data
mic.record(samples, SAMPLES)

# Compute DC offset (mean) and threshold level
mean = int(sum(samples) / len(samples) + 0.5)
threshold = mean + THRESHOLD

# Compute deltas between mean crossing points
# (this bit by Dan Halbert)
deltas = []
last_xing_point = None
crossed_threshold = False
for i in range(SAMPLES-1):
sample = samples[i]
if sample > threshold:
crossed_threshold = True
if crossed_threshold and sample < mean:
if last_xing_point:
deltas.append(i - last_xing_point)
last_xing_point = i
crossed_threshold = False

# Try again if not enough deltas
if len(deltas) < MIN_DELTAS:
continue

# Average the deltas
mean = sum(deltas) / len(deltas)

# Compute frequency
freq = SAMPLERATE / mean

print("crossings: {}  mean: {}  freq: {} ".format(len(deltas), mean, freq))

time.sleep(DELAY)
```

The part of the code that does the main work for the mean crossing estimation of frequency is this:

```# Compute deltas between mean crossing points
# (this bit by Dan Halbert)
deltas = []
last_xing_point = None
crossed_threshold = False
for i in range(SAMPLES-1):
sample = samples[i]
if sample > threshold:
crossed_threshold = True
if crossed_threshold and sample < mean:
if last_xing_point:
deltas.append(i - last_xing_point)
last_xing_point = i
crossed_threshold = False```

Compare that to the explanation in the previous section. You can see how it is looping over the entire mic SAMPLES, checking for crossed_threshold and storing all of the deltas.

Once that loop is complete, the actual frequency computation is straight forward. The deltas are averaged:

```# Average the deltas
mean = sum(deltas) / len(deltas)```

And the frequency (freq) is then just:

```# Compute frequency
freq = SAMPLERATE / mean```

## Tone Test

Here is a nice website that provides a pure tone generator we can use for testing:

Run the basic frequency example program above and watch the output on the serial monitor. Then use the website to generate some tones of various frequencies. Like this:

Here 190 is registering at about ~188:

Here 446 is registering at about ~442:

Here 1500 is registering at about ~1487:

The results are not exact, but pretty close. Not too bad for a technique this simple.

Now let's add some fun NeoPixel response based on the estimated frequency.

This guide was first published on Oct 22, 2019. It was last updated on Oct 22, 2019.