Some improvements for I2S Line-In

- I2S DAC devices utilize the complete sample range [-32767 ... 32768], while I2S microphones typically deliver  samples in [-2047... 2047]. Down-scaling Line-in samples ensures that filters and AGC operate optimally.
- use aPLL clock source for I2S with MCLK
- experimental: I2S slave mode (however could not get it to work ...)
This commit is contained in:
Frank
2022-11-07 15:34:42 +01:00
parent 46ee25b46b
commit 4bd35b780e
3 changed files with 44 additions and 15 deletions

View File

@@ -662,7 +662,7 @@ class AudioReactive : public Usermod {
Serial.print("micReal:"); Serial.print(micDataReal); Serial.print("\t");
Serial.print("volumeSmth:"); Serial.print(volumeSmth); Serial.print("\t");
//Serial.print("volumeRaw:"); Serial.print(volumeRaw); Serial.print("\t");
//Serial.print("DC_Level:"); Serial.print(micLev); Serial.print("\t");
Serial.print("DC_Level:"); Serial.print(micLev); Serial.print("\t");
//Serial.print("sampleAgc:"); Serial.print(sampleAgc); Serial.print("\t");
//Serial.print("sampleAvg:"); Serial.print(sampleAvg); Serial.print("\t");
//Serial.print("sampleReal:"); Serial.print(sampleReal); Serial.print("\t");
@@ -1107,8 +1107,9 @@ class AudioReactive : public Usermod {
}
// Reset I2S peripheral for good measure
i2s_driver_uninstall(I2S_NUM_0);
i2s_driver_uninstall(I2S_NUM_0); // E (696) I2S: i2s_driver_uninstall(2006): I2S port 0 has not installed
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
delay(100);
periph_module_reset(PERIPH_I2S0_MODULE); // not possible on -C3
#endif
delay(100); // Give that poor microphone some time to setup.
@@ -1140,7 +1141,8 @@ class AudioReactive : public Usermod {
break;
case 4:
DEBUGSR_PRINT(F("AR: Generic I2S Microphone with Master Clock - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE);
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, true, 1.0f/16.0f);
//audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, false, 1.0f/16.0f); // I2S SLAVE mode - does not work, unfortunately
delay(100);
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
break;

View File

@@ -138,28 +138,33 @@ class AudioSource {
virtual I2S_datatype postProcessSample(I2S_datatype sample_in) {return(sample_in);} // default method can be overriden by instances (ADC) that need sample postprocessing
// Private constructor, to make sure it is not callable except from derived classes
AudioSource(SRate_t sampleRate, int blockSize) :
AudioSource(SRate_t sampleRate, int blockSize, bool i2sMaster = true, float sampleScale = 1.0f) :
_sampleRate(sampleRate),
_blockSize(blockSize),
_initialized(false)
_initialized(false),
_i2sMaster(i2sMaster),
_sampleScale(sampleScale)
{};
SRate_t _sampleRate; // Microphone sampling rate
int _blockSize; // I2S block size
bool _initialized; // Gets set to true if initialization is successful
bool _i2sMaster; // when false, ESP32 will be in I2S SLAVE mode (for devices that only operate in MASTER mode). Only workds in newer IDF >= 4.4.x
float _sampleScale; // pre-scaling factor for I2S samples
};
/* Basic I2S microphone source
All functions are marked virtual, so derived classes can replace them
WARNING: i2sMaster = false is experimental, and most likely will not work
*/
class I2SSource : public AudioSource {
public:
I2SSource(SRate_t sampleRate, int blockSize) :
AudioSource(sampleRate, blockSize) {
I2SSource(SRate_t sampleRate, int blockSize, bool i2sMaster=true, float sampleScale = 1.0f) :
AudioSource(sampleRate, blockSize, i2sMaster, sampleScale) {
_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
.mode = i2sMaster ? i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX) : i2s_mode_t(I2S_MODE_SLAVE | I2S_MODE_RX),
.sample_rate = _sampleRate,
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
.bits_per_sample = I2S_SAMPLE_RESOLUTION, // slave mode: may help to set this to 96000, as the other side (master) controls sample rates
.channel_format = I2S_MIC_CHANNEL,
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
@@ -168,6 +173,7 @@ class I2SSource : public AudioSource {
.dma_buf_count = 8,
.dma_buf_len = _blockSize,
.use_apll = 0,
//.fixed_mclk = 0,
.bits_per_chan = I2S_data_size,
#else
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
@@ -203,6 +209,21 @@ class I2SSource : public AudioSource {
#endif
}
if (mclkPin != I2S_PIN_NO_CHANGE) {
_config.use_apll = true; // experimental - use aPLL clock source to improve sampling quality, and to avoid glitches.
// //_config.fixed_mclk = 512 * _sampleRate;
// //_config.fixed_mclk = 256 * _sampleRate;
}
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
if (ESP.getChipRevision() == 0) _config.use_apll = false; // APLL is broken on ESP32 revision 0
#endif
if (_i2sMaster == false) {
DEBUG_PRINTLN(F("AR: Warning - i2S SLAVE mode is experimental!"));
if ((_config.mode & I2S_MODE_MASTER) != 0)
DEBUG_PRINTLN("AR: (oops) I2S SLAVE mode requested but not configured!");
}
// Reserve the master clock pin if provided
_mclkPin = mclkPin;
if (mclkPin != I2S_PIN_NO_CHANGE) {
@@ -222,13 +243,20 @@ class I2SSource : public AudioSource {
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
DEBUGSR_PRINTF("AR: Failed to install i2s driver: %d\n", err);
return;
}
DEBUGSR_PRINTF("AR: I2S#0 driver %s aPLL; fixed_mclk=%d.\n", _config.use_apll? "uses":"without", _config.fixed_mclk);
DEBUGSR_PRINTF("AR: Sample scaling factor = %6.4f\n", _sampleScale);
if(_config.mode & I2S_MODE_MASTER)
DEBUGSR_PRINTLN(F("AR: I2S#0 driver installed in MASTER mode."));
else
DEBUGSR_PRINTLN(F("AR: I2S#0 driver installed in SLAVE mode."));
err = i2s_set_pin(I2S_NUM_0, &_pinConfig);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to set i2s pin config: %d\n", err);
DEBUGSR_PRINTF("AR: Failed to set i2s pin config: %d\n", err);
i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver
return;
}
@@ -236,7 +264,7 @@ class I2SSource : public AudioSource {
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
err = i2s_set_clk(I2S_NUM_0, _sampleRate, I2S_SAMPLE_RESOLUTION, I2S_CHANNEL_MONO); // set bit clocks. Also takes care of MCLK routing if needed.
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to configure i2s clocks: %d\n", err);
DEBUGSR_PRINTF("AR: Failed to configure i2s clocks: %d\n", err);
i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver
return;
}
@@ -288,8 +316,7 @@ class I2SSource : public AudioSource {
currSample = (float) newSamples[i]; // 16bit input -> use as-is
#endif
buffer[i] = currSample;
//buffer[i] *= 0.6f; // (ICS-43434): compensate for higher sensitivity (reduce by 2db)
buffer[i] *= _sampleScale; // scale samples
}
}
}

View File

@@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2210281
#define VERSION 2211071
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG