diff --git a/noisemeter-device/spl-meter.cpp b/noisemeter-device/spl-meter.cpp index c554d53..324dd57 100644 --- a/noisemeter-device/spl-meter.cpp +++ b/noisemeter-device/spl-meter.cpp @@ -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)) // @@ -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, @@ -78,20 +75,20 @@ std::optional 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(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) { @@ -102,15 +99,15 @@ std::optional 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 {}; } @@ -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); } diff --git a/noisemeter-device/spl-meter.h b/noisemeter-device/spl-meter.h index 1356746..cdb35b7 100644 --- a/noisemeter-device/spl-meter.h +++ b/noisemeter-device/spl-meter.h @@ -1,22 +1,25 @@ #ifndef SPL_METER_H #define SPL_METER_H +#include #include #include #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; @@ -26,11 +29,12 @@ class SPLMeter private: // Buffer for block of samples - SampleBuffer samples; + alignas(4) + std::array samples; // Sampling accumulators unsigned Leq_samples = 0; - double Leq_sum_sqr = 0; + float Leq_sum_sqr = 0; void i2sRead(); };