Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix mic distortion for Inmp441Max98357aFastLed #139

Merged
merged 2 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
Loading