2020
2121#include < cmath>
2222
23+ #if defined(USE_MIC_IM64D130A)
2324/* * Sample size time duration to use for Leq calculation (seconds). */
2425static constexpr auto LEQ_PERIOD = 1 .f;
25- /* * Number of samples to use for a Leq decibel calculation. */
26- static constexpr auto SAMPLES_LEQ = SPLMeter::SAMPLE_RATE * LEQ_PERIOD ;
2726/* * Specifies the type of weighting to use for decibel calculation: dBA, dBC, or None/Z. */
2827static constexpr auto & WEIGHTING = A_weighting;
2928/* * Specifies the microphone's equalization filter. See pre-defined filters or set to 'None'. */
@@ -41,6 +40,19 @@ static constexpr auto MIC_OVERLOAD_DB = 130.f;
4140static constexpr auto MIC_NOISE_DB = 30 .f;
4241/* * Linear calibration offset to apply to calculated decibel values. */
4342static constexpr auto MIC_OFFSET_DB = 0 .f;
43+ #elif defined(USE_MIC_SPH0645)
44+ static constexpr auto LEQ_PERIOD = 1 .f;
45+ static constexpr auto & WEIGHTING = A_weighting;
46+ static constexpr auto & MIC_EQUALIZER = SPH0645LM4H_B_RB ;
47+ static constexpr auto MIC_BITS = 18u ;
48+ static constexpr auto MIC_SENSITIVITY = -26 .f;
49+ static constexpr auto MIC_REF_DB = 94 .f;
50+ static constexpr auto MIC_OVERLOAD_DB = 120 .f;
51+ static constexpr auto MIC_NOISE_DB = 29 .f;
52+ static constexpr auto MIC_OFFSET_DB = 0 .f;
53+ #else
54+ #error "Unsupported microphone! Use either SPH0645 or IM64D130A."
55+ #endif
4456
4557/* * Reference amplitude level for the microphone. */
4658static constexpr auto MIC_REF_AMPL = std::pow(10 .f, MIC_SENSITIVITY / 20 .f) * ((1 << (MIC_BITS - 1 )) - 1 );
@@ -52,9 +64,19 @@ void SPLMeter::initMicrophone() noexcept
5264 /* Setp 1: Determine the I2S channel configuration and allocate RX channel only
5365 * The default configuration can be generated by the helper macro,
5466 * but note that PDM channel can only be registered on I2S_NUM_0 */
55- i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG (I2S_PORT , I2S_ROLE_MASTER );
67+ i2s_chan_config_t rx_chan_cfg = {
68+ .id = I2S_PORT ,
69+ .role = I2S_ROLE_MASTER ,
70+ .dma_desc_num = 15 ,
71+ .dma_frame_num = 400 ,
72+ .auto_clear_after_cb = false ,
73+ .auto_clear_before_cb = false ,
74+ .allow_pd = false ,
75+ .intr_priority = 0 ,
76+ };
5677 ESP_ERROR_CHECK (i2s_new_channel (&rx_chan_cfg, nullptr , &rx_chan));
5778
79+ #if defined(USE_MIC_IM64D130A)
5880 /* Step 2: Setting the configurations of PDM RX mode and initialize the RX channel
5981 * The slot configuration and clock configuration can be generated by the macros
6082 * These two helper macros is defined in 'i2s_pdm.h' which can only be used in PDM RX mode.
@@ -71,6 +93,26 @@ void SPLMeter::initMicrophone() noexcept
7193 };
7294
7395 ESP_ERROR_CHECK (i2s_channel_init_pdm_rx_mode (rx_chan, &pdm_rx_cfg));
96+ #elif defined(USE_MIC_SPH0645)
97+ i2s_std_config_t std_rx_cfg = {
98+ .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG (SAMPLE_RATE ),
99+ .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG (I2S_DATA_BIT_WIDTH_32BIT , I2S_SLOT_MODE_MONO ),
100+ .gpio_cfg = {
101+ .mclk = I2S_GPIO_UNUSED ,
102+ .bclk = I2S_SCK ,
103+ .ws = I2S_WS ,
104+ .dout = I2S_GPIO_UNUSED ,
105+ .din = I2S_SD ,
106+ .invert_flags = {
107+ .mclk_inv = false ,
108+ .bclk_inv = false ,
109+ .ws_inv = false ,
110+ },
111+ },
112+ };
113+
114+ ESP_ERROR_CHECK (i2s_channel_init_std_mode (rx_chan, &std_rx_cfg));
115+ #endif
74116
75117 /* Step 3: Enable the rx channels before reading data */
76118 ESP_ERROR_CHECK (i2s_channel_enable (rx_chan));
@@ -79,26 +121,40 @@ void SPLMeter::initMicrophone() noexcept
79121
80122std::optional<float > SPLMeter::readMicrophoneData () noexcept
81123{
82- i2sRead ();
124+ const auto bread = i2sRead ();
83125
126+ #if defined(USE_MIC_IM64D130A)
84127 // I2S samples are 16-bit integers stored into samples's memory.
85128 // These need to be expanded into 32-bit floats for processing.
86129 // Iterate in reverse to avoid overwriting data.
87- auto src = reinterpret_cast <const int16_t *>(samples.data ()) + samples.size ();
88- auto dst = samples.data () + samples.size ();
89- for (int i = 0 ; i < samples.size (); i++)
130+ const auto sread = bread / sizeof (int16_t );
131+ auto src = reinterpret_cast <const int16_t *>(samples.data ()) + sread;
132+ auto dst = samples.data () + sread;
133+ for (int i = 0 ; i < sread; i++)
90134 *--dst = *--src;
135+ #elif defined(USE_MIC_SPH0645)
136+ // Convert (including shifting) integer microphone values to floats,
137+ // using the same buffer (assumed sample size is same as size of float),
138+ // to save a bit of memory
139+ const auto sread = bread / sizeof (int32_t ) / 2 ;
140+ auto src = reinterpret_cast <const int32_t *>(samples.data ());
141+ auto dst = samples.data ();
142+ for (int i = 0 ; i < sread; i++) {
143+ const auto conv = src[i * 2 ] >> ((sizeof (int32_t ) * 8u ) - MIC_BITS );
144+ dst[i] = conv;
145+ }
146+ #endif
91147
92148 // Apply equalization and calculate Z-weighted sum of squares,
93149 // writes filtered samples back to the same buffer.
94150 const auto fptr = samples.data ();
95- const auto sum_sqr_SPL = MIC_EQUALIZER .filter (fptr, fptr, samples. size () );
151+ const auto sum_sqr_SPL = MIC_EQUALIZER .filter (fptr, fptr, sread );
96152
97153 // Apply weighting and calucate weigthed sum of squares
98- const auto sum_sqr_weighted = WEIGHTING .filter (fptr, fptr, samples. size () );
154+ const auto sum_sqr_weighted = WEIGHTING .filter (fptr, fptr, sread );
99155
100156 // Calculate dB values relative to MIC_REF_AMPL and adjust for microphone reference
101- const auto short_RMS = std::sqrt (sum_sqr_SPL / samples. size () );
157+ const auto short_RMS = std::sqrt (sum_sqr_SPL / sread );
102158 const auto short_SPL_dB = MIC_OFFSET_DB + MIC_REF_DB + 20 * std::log10 (short_RMS / MIC_REF_AMPL );
103159
104160 // In case of acoustic overload or below noise floor measurement, report infinty Leq value
@@ -110,7 +166,7 @@ std::optional<float> SPLMeter::readMicrophoneData() noexcept
110166
111167 // Accumulate Leq sum
112168 Leq_sum_sqr += sum_sqr_weighted;
113- Leq_samples += samples. size () ;
169+ Leq_samples += sread ;
114170
115171 // When we gather enough samples, calculate new Leq value
116172 if (Leq_samples >= SAMPLE_RATE * LEQ_PERIOD ) {
@@ -124,14 +180,20 @@ std::optional<float> SPLMeter::readMicrophoneData() noexcept
124180 }
125181}
126182
127- void SPLMeter::i2sRead () noexcept
183+ size_t SPLMeter::i2sRead () noexcept
128184{
129185 // Block and wait for microphone values from I2S
130186 //
131187 // Data is moved from DMA buffers to our 'samples' buffer by the driver ISR
132188 // and when there is requested ammount of data, task is unblocked
133189 size_t bread;
134- (void )bread;
190+
191+ #if defined(USE_MIC_IM64D130A)
135192 i2s_channel_read (i2s_handle, samples.data (), samples.size () * sizeof (int16_t ), &bread, portMAX_DELAY);
193+ #elif defined(USE_MIC_SPH0645)
194+ i2s_channel_read (i2s_handle, samples.data (), samples.size () * sizeof (int32_t ), &bread, portMAX_DELAY);
195+ #endif
196+
197+ return bread;
136198}
137199
0 commit comments