Skip to content

Commit

Permalink
type cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
tcsullivan committed Apr 13, 2024
1 parent 0aa8db0 commit b6c80b0
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 29 deletions.
36 changes: 15 additions & 21 deletions noisemeter-device/spl-meter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
#define MIC_OFFSET_DB 0 // Default offset (sine-wave RMS vs. dBFS). Modify this value for linear calibration

// Customize these values from microphone datasheet
#define MIC_SENSITIVITY -26 // dBFS value expected at MIC_REF_DB (Sensitivity value from datasheet)
#define MIC_REF_DB 94.0 // Value at which point sensitivity is specified in datasheet (dB)
#define MIC_BITS 24 // valid number of bits in I2S data
#define MIC_SENSITIVITY -26.f // dBFS value expected at MIC_REF_DB (Sensitivity value from datasheet)
#define MIC_REF_DB 94.f // Value at which point sensitivity is specified in datasheet (dB)
#define MIC_BITS 24 // valid number of bits in I2S data
#define MIC_CONVERT(s) (s >> (SAMPLE_BITS - MIC_BITS))

//
Expand All @@ -32,13 +32,10 @@
#define DMA_BANKS 32

// Calculate reference amplitude value at compile time
constexpr double MIC_REF_AMPL = pow(10, double(MIC_SENSITIVITY) / 20) * ((1 << (MIC_BITS - 1)) - 1);
constexpr auto MIC_REF_AMPL = std::pow(10.f, MIC_SENSITIVITY / 20.f) * ((1 << (MIC_BITS - 1)) - 1);

void SPLMeter::initMicrophone() noexcept
{
// Setup I2S to sample mono channel for SAMPLE_RATE * SAMPLE_BITS
// NOTE: Recent update to Arduino_esp32 (1.0.2 -> 1.0.3)
// seems to have swapped ONLY_LEFT and ONLY_RIGHT channels
const i2s_config_t i2s_config = {
mode: i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
sample_rate: SAMPLE_RATE,
Expand Down Expand Up @@ -78,20 +75,20 @@ std::optional<float> SPLMeter::readMicrophoneData() noexcept
// Convert (including shifting) integer microphone values to floats,
// using the same buffer (assumed sample size is same as size of float),
// to save a bit of memory
auto int_samples = reinterpret_cast<SAMPLE_T*>(samples);

for (int i = 0; i < SAMPLES_SHORT; i++) samples[i] = MIC_CONVERT(int_samples[i]);
for (auto& s : samples)
s.f = MIC_CONVERT(s.i);

// Apply equalization and calculate Z-weighted sum of squares,
// writes filtered samples back to the same buffer.
const auto sum_sqr_SPL = MIC_EQUALIZER.filter(samples, samples, SAMPLES_SHORT);
auto fptr = &samples[0].f;
const auto sum_sqr_SPL = MIC_EQUALIZER.filter(fptr, fptr, samples.size());

// Apply weighting and calucate weigthed sum of squares
const auto sum_sqr_weighted = WEIGHTING.filter(samples, samples, SAMPLES_SHORT);
const auto sum_sqr_weighted = WEIGHTING.filter(fptr, fptr, samples.size());

// Calculate dB values relative to MIC_REF_AMPL and adjust for microphone reference
double short_RMS = sqrt(double(sum_sqr_SPL) / SAMPLES_SHORT);
double short_SPL_dB = MIC_OFFSET_DB + MIC_REF_DB + 20 * log10(short_RMS / MIC_REF_AMPL);
const auto short_RMS = std::sqrt(sum_sqr_SPL / samples.size());
const auto short_SPL_dB = MIC_OFFSET_DB + MIC_REF_DB + 20 * std::log10(short_RMS / MIC_REF_AMPL);

// In case of acoustic overload or below noise floor measurement, report infinty Leq value
if (short_SPL_dB > MIC_OVERLOAD_DB) {
Expand All @@ -102,15 +99,15 @@ std::optional<float> SPLMeter::readMicrophoneData() noexcept

// Accumulate Leq sum
Leq_sum_sqr += sum_sqr_weighted;
Leq_samples += SAMPLES_SHORT;
Leq_samples += samples.size();

// When we gather enough samples, calculate new Leq value
if (Leq_samples >= SAMPLE_RATE * LEQ_PERIOD) {
double Leq_RMS = sqrt(Leq_sum_sqr / Leq_samples);
const auto Leq_RMS = std::sqrt(Leq_sum_sqr / Leq_samples);
Leq_sum_sqr = 0;
Leq_samples = 0;

return MIC_OFFSET_DB + MIC_REF_DB + 20 * log10(Leq_RMS / MIC_REF_AMPL); // Leq dB
return MIC_OFFSET_DB + MIC_REF_DB + 20 * std::log10(Leq_RMS / MIC_REF_AMPL); // Leq dB
} else {
return {};
}
Expand All @@ -122,10 +119,7 @@ void SPLMeter::i2sRead()
//
// Data is moved from DMA buffers to our 'samples' buffer by the driver ISR
// and when there is requested ammount of data, task is unblocked
//
// Note: i2s_read does not care it is writing in float[] buffer, it will write
// integer values to the given address, as received from the hardware peripheral.
size_t bytes_read;
i2s_read(I2S_PORT, samples, sizeof(samples), &bytes_read, portMAX_DELAY);
i2s_read(I2S_PORT, samples.data(), samples.size() * sizeof(samples[0]), &bytes_read, portMAX_DELAY);
}

20 changes: 12 additions & 8 deletions noisemeter-device/spl-meter.h
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
#ifndef SPL_METER_H
#define SPL_METER_H

#include <array>
#include <cstdint>
#include <optional>

#define SAMPLE_RATE 48000 // Hz, fixed to design of IIR filters
#define SAMPLE_BITS 32 // bits
#define SAMPLE_T int32_t
#define SAMPLES_SHORT (SAMPLE_RATE / 8) // ~125ms

#define MIC_OVERLOAD_DB 120.0 // dB - Acoustic overload point
#define MIC_NOISE_DB 29 // dB - Noise floor

static_assert(sizeof(float) == sizeof(SAMPLE_T));
using SampleBuffer alignas(4) = float[SAMPLES_SHORT];
#define MIC_OVERLOAD_DB 120.f // dB - Acoustic overload point
#define MIC_NOISE_DB 29.f // dB - Noise floor

class SPLMeter
{
static_assert(sizeof(float) == sizeof(std::int32_t));
union sample_t {
std::int32_t i;
float f;
};

public:
// Prepares I2S Driver and mic config.
void initMicrophone() noexcept;
Expand All @@ -26,11 +29,12 @@ class SPLMeter

private:
// Buffer for block of samples
SampleBuffer samples;
alignas(4)
std::array<sample_t, SAMPLES_SHORT> samples;

// Sampling accumulators
unsigned Leq_samples = 0;
double Leq_sum_sqr = 0;
float Leq_sum_sqr = 0;

void i2sRead();
};
Expand Down

0 comments on commit b6c80b0

Please sign in to comment.