Skip to content

Commit

Permalink
Merge pull request #139 from ProrokWielki/master
Browse files Browse the repository at this point in the history
Fix mic distortion for Inmp441Max98357aFastLed
  • Loading branch information
Romkabouter authored Mar 18, 2024
2 parents f423ff9 + 650ba2a commit c7c330a
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 51 deletions.
2 changes: 1 addition & 1 deletion PlatformIO/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ lib_deps =
https://github.com/marvinroger/async-mqtt-client.git
https://github.com/me-no-dev/AsyncTCP.git
https://github.com/knolleary/pubsubclient.git
https://github.com/bblanchon/ArduinoJson.git @ ^6.19.4
https://github.com/bblanchon/ArduinoJson.git
ESP Async WebServer
m5stack/M5Atom
fastled/FastLED
Expand Down
127 changes: 77 additions & 50 deletions PlatformIO/src/devices/Inmp441Max98357aFastLed.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,24 @@
#define NUM_LEDS 5
CRGB leds[NUM_LEDS];

// I2S pins on ESp32cam MIC
// GPIO2 <--> WS
// GPIO14 <--> SCK
// GPIO15 <--> SD
#define I2S_SCK 25
#define I2S_WS 32
#define I2S_SD 33
#define I2S_PORT I2S_NUM_0

// I2S pins on ESp32Cam Speakers
// GPIO16 <--> LRC
// GPIO13 <--> BCLK
// GPIO12 <--> DIN
#define I2S_LRC 12
#define I2S_BCLK 14
#define I2S_DIN 27

#define I2S_PORT_TX I2S_NUM_1

#define I2S_OUTPUT_SAMPLE_RATE (22050)
#define I2S_SAMPLE_RATE (16000)
#define I2S_SAMPLE_BITS (16)
#define I2S_READ_LEN 512
// LEDs
#define LED_FLASH 4

#define KEY1_GPIO GPIO_NUM_34
#define KEY_LISTEN KEY1_GPIO
// I2S pins & parameters for MIC
#define MIC_I2S_BCLK 25
#define MIC_I2S_FS 32
#define MIC_I2S_DIN 33
#define MIC_I2S_PORT I2S_NUM_0
#define MIC_SAMPLE_RATE (16000)
#define MIC_I2S_SAMPLE_BITS 32
#define MIC_I2S_SAMPLE_BYTES (MIC_I2S_SAMPLE_BITS / 8)
#define I2S_READ_LEN (256 * MIC_I2S_SAMPLE_BYTES)

// I2S pins & parameters for speaker
#define SPK_I2S_FS 12
#define SPK_I2S_BCLK 14
#define SPK_I2S_DOUT 27
#define SPK_I2S_PORT I2S_NUM_1
#define SPK_SAMPLE_RATE (22050)
#define SPK_I2S_SAMPLE_BITS 16
#define SPK_I2S_SAMPLE_BYTES (SPK_I2S_SAMPLE_BITS / 8)

class Inmp441Max98357aFastLED : public Device
{
Expand All @@ -51,6 +41,7 @@ class Inmp441Max98357aFastLED : public Device

void setWriteMode(int sampleRate, int bitDepth, int numChannels);
void writeAudio(uint8_t *data, size_t size, size_t *bytes_written);
void setGain(uint16_t gain);

int numAmpOutConfigurations()
{
Expand Down Expand Up @@ -79,6 +70,7 @@ class Inmp441Max98357aFastLED : public Device

private:
char *i2s_read_buff = (char *)calloc(I2S_READ_LEN, sizeof(char));
uint16_t m_gain;
long currentMillis, startMillis;
};

Expand All @@ -90,54 +82,69 @@ void Inmp441Max98357aFastLED::init()
// Speakers
i2s_config_t i2sConfig_tx = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
.sample_rate = I2S_OUTPUT_SAMPLE_RATE, // I2S_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // I2S_BITS_PER_SAMPLE_16BIT,
.sample_rate = SPK_SAMPLE_RATE, // I2S_SAMPLE_RATE,
.bits_per_sample = static_cast<i2s_bits_per_sample_t>(I2S_BITS_PER_SAMPLE_16BIT), // I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S),
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 2,
.dma_buf_len = 512,
.tx_desc_auto_clear = true,
};

i2s_pin_config_t pin_config_tx = {
.bck_io_num = I2S_BCLK, .ws_io_num = I2S_LRC, .data_out_num = I2S_DIN, .data_in_num = -1};
.bck_io_num = SPK_I2S_BCLK, .ws_io_num = SPK_I2S_FS, .data_out_num = SPK_I2S_DOUT, .data_in_num = -1};

err += i2s_driver_install(I2S_PORT_TX, &i2sConfig_tx, 0, NULL);
err += i2s_driver_install(SPK_I2S_PORT, &i2sConfig_tx, 0, NULL);
if (err != ESP_OK) {
Serial.printf("Failed installing headphone driver: %d\n", err);
while (true)
;
}

err += i2s_set_pin(I2S_PORT_TX, &pin_config_tx);
err += i2s_set_pin(SPK_I2S_PORT, &pin_config_tx);
if (err != ESP_OK) {
Serial.printf("Failed setting headphone pin: %d\n", err);
while (true)
;
}
Serial.println("I2S headphone driver installed.\n");

//----- Microphone
/*
- the INMP441 MEMS microphone has a specified sensitivity of -26 dBFS @ 1 Pa,
or 120 dB SPL @ FS.
- Even a loud speech signal won't exceed 80 dB, so we "waste" the top 40 dB
of dynamic range.
- The microphone sends 24 bit samples (which is not to say it has a true
24 bit dynamic range).
- If we read 16 bit samples (nominal 96 dB dynamic range), and waste the top
40 dB, then we get a ~56 dB dynamic range speech signal.
- Might bet better to read 24 bit samples, and amplify by 40 dB ?
- in my experiments, a x20 gain (26 dB) will result in a 50% FS signal, i.e.
not clipping, with a loud voice at 30cm distance
*/
i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = I2S_SAMPLE_RATE,
.bits_per_sample = i2s_bits_per_sample_t(I2S_SAMPLE_BITS),
.sample_rate = MIC_SAMPLE_RATE,
.bits_per_sample = static_cast<i2s_bits_per_sample_t>(MIC_I2S_SAMPLE_BITS),
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = 0,
.dma_buf_count = 2,
.dma_buf_len = 512,
.dma_buf_len = I2S_READ_LEN,
.use_apll = 1};

i2s_pin_config_t pin_config = {.bck_io_num = I2S_SCK, .ws_io_num = I2S_WS, .data_out_num = -1, .data_in_num = I2S_SD};
i2s_pin_config_t pin_config = {
.bck_io_num = MIC_I2S_BCLK, .ws_io_num = MIC_I2S_FS, .data_out_num = -1, .data_in_num = MIC_I2S_DIN};

err += i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
err += i2s_driver_install(MIC_I2S_PORT, &i2s_config, 0, NULL);
if (err != ESP_OK) {
Serial.printf("Failed installing mic driver: %d\n", err);
while (true)
;
}

err += i2s_set_pin(I2S_PORT, &pin_config);
err += i2s_set_pin(MIC_I2S_PORT, &pin_config);
if (err != ESP_OK) {
Serial.printf("Failed setting mic pin: %d\n", err);
while (true)
Expand Down Expand Up @@ -207,31 +214,51 @@ void Inmp441Max98357aFastLED::animateBlinking(StateColors colors)
void Inmp441Max98357aFastLED::setWriteMode(int sampleRate, int bitDepth, int numChannels)
{
if (sampleRate > 0) {
i2s_set_clk(I2S_PORT_TX, sampleRate, static_cast<i2s_bits_per_sample_t>(bitDepth),
i2s_set_clk(SPK_I2S_PORT, sampleRate, static_cast<i2s_bits_per_sample_t>(bitDepth),
static_cast<i2s_channel_t>(numChannels));
}
}

void Inmp441Max98357aFastLED::writeAudio(uint8_t *data, size_t size, size_t *bytes_written)
{
i2s_write(I2S_PORT_TX, data, size, bytes_written, portMAX_DELAY);
i2s_write(SPK_I2S_PORT, data, size, bytes_written, portMAX_DELAY);
}

bool Inmp441Max98357aFastLED::readAudio(uint8_t *data, size_t size)
{
size_t samples_requested = size / sizeof(int16_t);
size_t bytes_requested = MIC_I2S_SAMPLE_BYTES * samples_requested;
size_t bytes_read;
i2s_read(I2S_PORT, (void *)i2s_read_buff, size, &bytes_read, portMAX_DELAY);
uint32_t j = 0;
uint32_t dac_value = 0;
for (int i = 0; i < size; i += 2) {
dac_value = ((((uint16_t)(i2s_read_buff[i + 1] & 0xff) << 8) | ((i2s_read_buff[i + 0]))));
data[j++] = 0;
data[j++] = dac_value * 256 / 2048;
// we skip every other sample, so read 2x desired # of samples
i2s_read(MIC_I2S_PORT, (void *)i2s_read_buff, bytes_requested, &bytes_read, portMAX_DELAY);

int16_t *from = (int16_t *)i2s_read_buff;
int16_t *to = (int16_t *)data;
size_t nsamples = bytes_read / MIC_I2S_SAMPLE_BYTES;
size_t i;
int32_t y;
/*
according to https://www.esp32.com/viewtopic.php?t=11023 , incoming samples
are in flipped order, i.e. 1,0,3,2,5,4,... instead of 0,1,2,3,4,5,...
As of ESP-IF 4, this is not true anymore. However, reading from the
microphone as int16_t appears to repeat every sample twice, so we read
as int32_t instead, and ignore the 16 LSBs
*/
for (i = 0; i < nsamples; i++) {
y = from[1]; // read 16 MSB of the 32 bit value
y *= m_gain;
*to++ = y;
from += 2;
}
return true;
}

void Inmp441Max98357aFastLED::updateBrightness(int brightness)
{
FastLED.setBrightness(brightness);
}

void Inmp441Max98357aFastLED::setGain(uint16_t gain)
{
m_gain = gain;
}

0 comments on commit c7c330a

Please sign in to comment.