AudioSource improvements (work in progress)
-new methods: getType(), isInitailized(), postProcessSample() - allow users to compile for RIGHT audio channel (-D I2S_USE_RIGHT_CHANNEL) - better handling in case audio input driver failed to initialize - removed some unneeded code and unneeded parameters
This commit is contained in:
parent
924073424f
commit
5e6532959b
@ -728,11 +728,11 @@ class AudioReactive : public Usermod {
|
||||
}
|
||||
|
||||
|
||||
void receiveAudioData()
|
||||
bool receiveAudioData() // check & process new data. return TRUE in case that new audio data was received.
|
||||
{
|
||||
if (!udpSyncConnected) return;
|
||||
if (!udpSyncConnected) return false;
|
||||
//DEBUGSR_PRINTLN("Checking for UDP Microphone Packet");
|
||||
|
||||
bool haveFreshData = false;
|
||||
size_t packetSize = fftUdp.parsePacket();
|
||||
if (packetSize > 5) {
|
||||
//DEBUGSR_PRINTLN("Received UDP Sync Packet");
|
||||
@ -774,8 +774,10 @@ class AudioReactive : public Usermod {
|
||||
FFT_Magnitude = my_magnitude;
|
||||
FFT_MajorPeak = receivedPacket->FFT_MajorPeak;
|
||||
//DEBUGSR_PRINTLN("Finished parsing UDP Sync Packet");
|
||||
haveFreshData = true;
|
||||
}
|
||||
}
|
||||
return haveFreshData;
|
||||
}
|
||||
|
||||
|
||||
@ -822,50 +824,54 @@ class AudioReactive : public Usermod {
|
||||
delay(100); // Give that poor microphone some time to setup.
|
||||
switch (dmType) {
|
||||
case 1:
|
||||
DEBUGSR_PRINTLN(F("AS: Generic I2S Microphone."));
|
||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
||||
DEBUGSR_PRINT(F("AR: Generic I2S Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||
delay(100);
|
||||
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||
break;
|
||||
case 2:
|
||||
DEBUGSR_PRINTLN(F("AS: ES7243 Microphone."));
|
||||
audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
||||
DEBUGSR_PRINTLN(F("AR: ES7243 Microphone (right channel only)."));
|
||||
audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE);
|
||||
delay(100);
|
||||
if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
||||
break;
|
||||
case 3:
|
||||
DEBUGSR_PRINTLN(F("AS: SPH0645 Microphone"));
|
||||
audioSource = new SPH0654(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
||||
DEBUGSR_PRINT(F("AR: SPH0645 Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
||||
audioSource = new SPH0654(SAMPLE_RATE, BLOCK_SIZE);
|
||||
delay(100);
|
||||
audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||
break;
|
||||
case 4:
|
||||
DEBUGSR_PRINTLN(F("AS: Generic I2S Microphone with Master Clock"));
|
||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
||||
DEBUGSR_PRINT(F("AR: Generic I2S Microphone with Master Clock - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||
delay(100);
|
||||
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
||||
break;
|
||||
case 5:
|
||||
DEBUGSR_PRINTLN(F("AS: I2S PDM Microphone"));
|
||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
||||
DEBUGSR_PRINT(F("AR: I2S PDM Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||
delay(100);
|
||||
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin);
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
DEBUGSR_PRINTLN(F("AS: Analog Microphone."));
|
||||
// we don't do the down-shift by 16bit any more
|
||||
//audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE, -4, 0x0FFF); // request upscaling to 16bit - still produces too much noise
|
||||
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0x0FFF); // keep at 12bit - less noise
|
||||
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
|
||||
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||
delay(100);
|
||||
if (audioSource) audioSource->initialize(audioPin);
|
||||
break;
|
||||
}
|
||||
delay(250); // give mictophone enough time to initialise
|
||||
delay(250); // give microphone enough time to initialise
|
||||
|
||||
if (!audioSource) enabled = false; // audio failed to initialise
|
||||
if (enabled) onUpdateBegin(false); // create FFT task
|
||||
if (enabled) disableSoundProcessing = false;
|
||||
if (!audioSource) enabled = false; // audio failed to initialise
|
||||
if (enabled) onUpdateBegin(false); // create FFT task
|
||||
if (FFT_Task == nullptr) enabled = false; // FFT task creation failed
|
||||
if (enabled) disableSoundProcessing = false; // all good - enable audio processing
|
||||
|
||||
if((!audioSource) || (!audioSource->isInitialized())) { // audio source failed to initialize. Still stay "enabled", as there might be input arriving via UDP Sound Sync
|
||||
DEBUGSR_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings."));
|
||||
disableSoundProcessing = true;
|
||||
}
|
||||
|
||||
initDone = true;
|
||||
}
|
||||
@ -1019,7 +1025,7 @@ class AudioReactive : public Usermod {
|
||||
|
||||
// Begin UDP Microphone Sync
|
||||
if ((audioSyncEnabled & 0x02) && millis() - lastTime > delayMs) { // Only run the audio listener code if we're in Receive mode
|
||||
receiveAudioData();
|
||||
(void) receiveAudioData(); // ToDo: use return value for something meaningfull
|
||||
lastTime = millis();
|
||||
}
|
||||
|
||||
|
@ -18,19 +18,36 @@
|
||||
Until this configuration is moved to the webinterface
|
||||
*/
|
||||
|
||||
// if you have problems to get your microphone work on the left channel, uncomment the following line
|
||||
//#define I2S_USE_RIGHT_CHANNEL // (experimental) define this to use right channel (digital mics only)
|
||||
#ifdef I2S_USE_RIGHT_CHANNEL
|
||||
#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_RIGHT
|
||||
#define I2S_MIC_CHANNEL_TEXT "right channel only."
|
||||
#else
|
||||
#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_LEFT
|
||||
#define I2S_MIC_CHANNEL_TEXT "left channel only."
|
||||
#endif
|
||||
|
||||
// Uncomment the line below to utilize ADC1 _exclusively_ for I2S sound input.
|
||||
// benefit: analog mic inputs will be sampled contiously -> better response times and less "glitches"
|
||||
// WARNING: this option WILL lock-up your device in case that any other analogRead() operation is performed;
|
||||
// for example if you want to read "analog buttons"
|
||||
//#define I2S_GRAB_ADC1_COMPLETELY // (experimental) continously sample analog ADC microphone. WARNING will cause analogRead() lock-up
|
||||
|
||||
// data type requested from the I2S driver - currently we always use 32bit
|
||||
//#define I2S_USE_16BIT_SAMPLES // (experimental) define this to request 16bit - more efficient but possibly less compatible
|
||||
#ifdef I2S_USE_16BIT_SAMPLES
|
||||
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_16BIT
|
||||
#define I2S_datatype int16_t
|
||||
#define I2S_unsigned_datatype uint16_t
|
||||
#undef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||
#else
|
||||
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_32BIT
|
||||
#define I2S_datatype int32_t
|
||||
#define I2S_unsigned_datatype uint32_t
|
||||
#define I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||
#endif
|
||||
|
||||
|
||||
/* Interface class
|
||||
AudioSource serves as base class for all microphone types
|
||||
This enables accessing all microphones with one single interface
|
||||
@ -65,15 +82,23 @@ class AudioSource {
|
||||
/* Get an up-to-date sample without DC offset */
|
||||
virtual int getSampleWithoutDCOffset() { return _sampleNoDCOffset; };
|
||||
|
||||
/* check if the audio source driver was initialized successfully */
|
||||
virtual bool isInitialized(void) {return(_initialized);}
|
||||
|
||||
/* identify Audiosource type - I2S-ADC or I2S-digital */
|
||||
typedef enum{Type_unknown=0, Type_I2SAdc=1, Type_I2SDigital=2} AudioSourceType;
|
||||
virtual AudioSourceType getType(void) {return(Type_I2SDigital);} // default is "I2S digital source" - ADC type overrides this method
|
||||
|
||||
protected:
|
||||
/* Post-process audio sample - currently on needed for I2SAdcSource*/
|
||||
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(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||
AudioSource(int sampleRate, int blockSize) :
|
||||
_sampleRate(sampleRate),
|
||||
_blockSize(blockSize),
|
||||
_sampleNoDCOffset(0),
|
||||
_dcOffset(0.0f),
|
||||
_shift(lshift),
|
||||
_mask(mask),
|
||||
_initialized(false)
|
||||
{};
|
||||
|
||||
@ -81,8 +106,6 @@ class AudioSource {
|
||||
int _blockSize; // I2S block size
|
||||
volatile int _sampleNoDCOffset; // Up-to-date sample without DCOffset
|
||||
float _dcOffset; // Rolling average DC offset
|
||||
int16_t _shift; // Shift obtained samples to the right (positive) or left(negative) by this amount
|
||||
uint32_t _mask; // Bitmask for sample data after shifting. Bitmask 0X0FFF means that we need to convert 12bit ADC samples from unsigned to signed
|
||||
bool _initialized; // Gets set to true if initialization is successful
|
||||
};
|
||||
|
||||
@ -91,13 +114,13 @@ class AudioSource {
|
||||
*/
|
||||
class I2SSource : public AudioSource {
|
||||
public:
|
||||
I2SSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||
AudioSource(sampleRate, blockSize, lshift, mask) {
|
||||
I2SSource(int sampleRate, int blockSize) :
|
||||
AudioSource(sampleRate, blockSize) {
|
||||
_config = {
|
||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
|
||||
.sample_rate = _sampleRate,
|
||||
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
|
||||
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||
.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),
|
||||
#else
|
||||
@ -193,26 +216,16 @@ class I2SSource : public AudioSource {
|
||||
|
||||
// Store samples in sample buffer and update DC offset
|
||||
for (int i = 0; i < num_samples; i++) {
|
||||
// pre-shift samples down to 16bit
|
||||
#ifdef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||
if (_shift != 0)
|
||||
newSamples[i] >>= 16;
|
||||
#endif
|
||||
newSamples[i] = postProcessSample(newSamples[i]); // perform postprocessing (needed for ADC samples)
|
||||
|
||||
float currSample = 0.0f;
|
||||
if(_shift > 0)
|
||||
currSample = (float) (newSamples[i] >> _shift);
|
||||
else {
|
||||
if(_shift < 0)
|
||||
currSample = (float) (newSamples[i] << (- _shift)); // need to "pump up" 12bit ADC to full 16bit as delivered by other digital mics
|
||||
else
|
||||
#ifdef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||
currSample = (float) newSamples[i] / 65536.0f; // _shift == 0 -> use the chance to keep lower 16bits
|
||||
currSample = (float) newSamples[i] / 65536.0f; // 32bit input -> 16bit; keeping lower 16bits as decimal places
|
||||
#else
|
||||
currSample = (float) newSamples[i];
|
||||
currSample = (float) newSamples[i]; // 16bit input -> use as-is
|
||||
#endif
|
||||
}
|
||||
buffer[i] = currSample;
|
||||
_dcOffset = ((_dcOffset * 31) + currSample) / 32;
|
||||
_dcOffset = ((_dcOffset * 31.0f) + currSample) / 32.0f;
|
||||
}
|
||||
|
||||
// Update no-DC sample
|
||||
@ -275,8 +288,8 @@ class ES7243 : public I2SSource {
|
||||
}
|
||||
|
||||
public:
|
||||
ES7243(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||
I2SSource(sampleRate, blockSize, lshift, mask) {
|
||||
ES7243(int sampleRate, int blockSize) :
|
||||
I2SSource(sampleRate, blockSize) {
|
||||
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
|
||||
};
|
||||
|
||||
@ -314,8 +327,8 @@ public:
|
||||
*/
|
||||
class I2SAdcSource : public I2SSource {
|
||||
public:
|
||||
I2SAdcSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||
I2SSource(sampleRate, blockSize, lshift, mask) {
|
||||
I2SAdcSource(int sampleRate, int blockSize) :
|
||||
I2SSource(sampleRate, blockSize) {
|
||||
_config = {
|
||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
|
||||
.sample_rate = _sampleRate,
|
||||
@ -332,6 +345,9 @@ class I2SAdcSource : public I2SSource {
|
||||
};
|
||||
}
|
||||
|
||||
/* identify Audiosource type - I2S-ADC*/
|
||||
AudioSourceType getType(void) {return(Type_I2SAdc);}
|
||||
|
||||
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) {
|
||||
return;
|
||||
@ -432,8 +448,8 @@ class I2SAdcSource : public I2SSource {
|
||||
*/
|
||||
class SPH0654 : public I2SSource {
|
||||
public:
|
||||
SPH0654(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||
I2SSource(sampleRate, blockSize, lshift, mask)
|
||||
SPH0654(int sampleRate, int blockSize) :
|
||||
I2SSource(sampleRate, blockSize)
|
||||
{}
|
||||
|
||||
void initialize(uint8_t i2swsPin, uint8_t i2ssdPin, uint8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
|
Loading…
Reference in New Issue
Block a user