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 # # SPDX-License-Identifier: MIT 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
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.
Text editor powered by tinymce.