gokul/acoustic-guitar-tuner
This code defines the physical pin configuration and footprint layouts for the ATtiny1616 microcontroller and a matching tactile switch component, including their key hardware connection points, dimensions, and 3D models.
- Version
- 1.0.10
- License
- unset
- Stars
- 1
firmware/acoustic_tuner_attiny1616/pitch_detector.h
#pragma once
#include <stdint.h>
#include <math.h>
namespace tuner {
constexpr uint16_t sampleCount = 256;
constexpr uint16_t sampleRate = 8000;
constexpr uint16_t firstLag = 21;
constexpr uint16_t lastLag = 119;
// Input is signed, mean-removed, 10-bit ADC data. Fixed bounds keep SRAM use
// predictable on the ATtiny1616. Scores use the same normalization for peak
// selection and interpolation, unlike the earlier raw-correlation interpolator.
inline float estimate(const int16_t* samples) {
int32_t energy = 0;
for (uint16_t i = 0; i < sampleCount; ++i)
energy += (int32_t)samples[i] * samples[i];
if (energy < 1024) return 0;
float score[lastLag + 1] = {};
float strongest = 0;
for (uint16_t lag = firstLag; lag <= lastLag; ++lag) {
int32_t cross = 0, a2 = 0, b2 = 0;
for (uint16_t i = 0; i < sampleCount - lag; ++i) {
const int16_t a = samples[i], b = samples[i + lag];
cross += (int32_t)a * b;
a2 += (int32_t)a * a;
b2 += (int32_t)b * b;
}
score[lag] = a2 + b2 ? (2.0f * cross) / (a2 + b2) : 0;
if (score[lag] > strongest) strongest = score[lag];
}
if (strongest < 0.8f) return 0;
// Pick the earliest strong local peak, avoiding 2x/3x-period octave errors.
for (uint16_t lag = firstLag + 1; lag < lastLag; ++lag) {
const float left = score[lag - 1], mid = score[lag], right = score[lag + 1];
if (mid < 0.93f * strongest || mid <= left || mid < right) continue;
const float curvature = left - 2 * mid + right;
float offset = fabsf(curvature) > 0.000001f ? 0.5f * (left - right) / curvature : 0;
if (offset > 0.5f) offset = 0.5f;
if (offset < -0.5f) offset = -0.5f;
const float hz = sampleRate / (lag + offset);
return hz >= 70 && hz <= 360 ? hz : 0;
}
return 0;
}
}