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");
|
//DEBUGSR_PRINTLN("Checking for UDP Microphone Packet");
|
||||||
|
bool haveFreshData = false;
|
||||||
size_t packetSize = fftUdp.parsePacket();
|
size_t packetSize = fftUdp.parsePacket();
|
||||||
if (packetSize > 5) {
|
if (packetSize > 5) {
|
||||||
//DEBUGSR_PRINTLN("Received UDP Sync Packet");
|
//DEBUGSR_PRINTLN("Received UDP Sync Packet");
|
||||||
@ -774,8 +774,10 @@ class AudioReactive : public Usermod {
|
|||||||
FFT_Magnitude = my_magnitude;
|
FFT_Magnitude = my_magnitude;
|
||||||
FFT_MajorPeak = receivedPacket->FFT_MajorPeak;
|
FFT_MajorPeak = receivedPacket->FFT_MajorPeak;
|
||||||
//DEBUGSR_PRINTLN("Finished parsing UDP Sync Packet");
|
//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.
|
delay(100); // Give that poor microphone some time to setup.
|
||||||
switch (dmType) {
|
switch (dmType) {
|
||||||
case 1:
|
case 1:
|
||||||
DEBUGSR_PRINTLN(F("AS: Generic I2S Microphone."));
|
DEBUGSR_PRINT(F("AR: Generic I2S Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
||||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||||
delay(100);
|
delay(100);
|
||||||
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin);
|
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
DEBUGSR_PRINTLN(F("AS: ES7243 Microphone."));
|
DEBUGSR_PRINTLN(F("AR: ES7243 Microphone (right channel only)."));
|
||||||
audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE);
|
||||||
delay(100);
|
delay(100);
|
||||||
if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
DEBUGSR_PRINTLN(F("AS: SPH0645 Microphone"));
|
DEBUGSR_PRINT(F("AR: SPH0645 Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
||||||
audioSource = new SPH0654(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
audioSource = new SPH0654(SAMPLE_RATE, BLOCK_SIZE);
|
||||||
delay(100);
|
delay(100);
|
||||||
audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin);
|
audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
DEBUGSR_PRINTLN(F("AS: Generic I2S Microphone with Master Clock"));
|
DEBUGSR_PRINT(F("AR: Generic I2S Microphone with Master Clock - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
||||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||||
delay(100);
|
delay(100);
|
||||||
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
DEBUGSR_PRINTLN(F("AS: I2S PDM Microphone"));
|
DEBUGSR_PRINT(F("AR: I2S PDM Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
||||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||||
delay(100);
|
delay(100);
|
||||||
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin);
|
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin);
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
default:
|
default:
|
||||||
DEBUGSR_PRINTLN(F("AS: Analog Microphone."));
|
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
|
||||||
// we don't do the down-shift by 16bit any more
|
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||||
//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
|
|
||||||
delay(100);
|
delay(100);
|
||||||
if (audioSource) audioSource->initialize(audioPin);
|
if (audioSource) audioSource->initialize(audioPin);
|
||||||
break;
|
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 (!audioSource) enabled = false; // audio failed to initialise
|
||||||
if (enabled) onUpdateBegin(false); // create FFT task
|
if (enabled) onUpdateBegin(false); // create FFT task
|
||||||
if (enabled) disableSoundProcessing = false;
|
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;
|
initDone = true;
|
||||||
}
|
}
|
||||||
@ -1019,7 +1025,7 @@ class AudioReactive : public Usermod {
|
|||||||
|
|
||||||
// Begin UDP Microphone Sync
|
// Begin UDP Microphone Sync
|
||||||
if ((audioSyncEnabled & 0x02) && millis() - lastTime > delayMs) { // Only run the audio listener code if we're in Receive mode
|
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();
|
lastTime = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,19 +18,36 @@
|
|||||||
Until this configuration is moved to the webinterface
|
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
|
// 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
|
//#define I2S_USE_16BIT_SAMPLES // (experimental) define this to request 16bit - more efficient but possibly less compatible
|
||||||
#ifdef I2S_USE_16BIT_SAMPLES
|
#ifdef I2S_USE_16BIT_SAMPLES
|
||||||
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_16BIT
|
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_16BIT
|
||||||
#define I2S_datatype int16_t
|
#define I2S_datatype int16_t
|
||||||
|
#define I2S_unsigned_datatype uint16_t
|
||||||
#undef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
#undef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||||
#else
|
#else
|
||||||
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_32BIT
|
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_32BIT
|
||||||
#define I2S_datatype int32_t
|
#define I2S_datatype int32_t
|
||||||
|
#define I2S_unsigned_datatype uint32_t
|
||||||
#define I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
#define I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Interface class
|
/* Interface class
|
||||||
AudioSource serves as base class for all microphone types
|
AudioSource serves as base class for all microphone types
|
||||||
This enables accessing all microphones with one single interface
|
This enables accessing all microphones with one single interface
|
||||||
@ -65,15 +82,23 @@ class AudioSource {
|
|||||||
/* Get an up-to-date sample without DC offset */
|
/* Get an up-to-date sample without DC offset */
|
||||||
virtual int getSampleWithoutDCOffset() { return _sampleNoDCOffset; };
|
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:
|
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
|
// 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),
|
_sampleRate(sampleRate),
|
||||||
_blockSize(blockSize),
|
_blockSize(blockSize),
|
||||||
_sampleNoDCOffset(0),
|
_sampleNoDCOffset(0),
|
||||||
_dcOffset(0.0f),
|
_dcOffset(0.0f),
|
||||||
_shift(lshift),
|
|
||||||
_mask(mask),
|
|
||||||
_initialized(false)
|
_initialized(false)
|
||||||
{};
|
{};
|
||||||
|
|
||||||
@ -81,8 +106,6 @@ class AudioSource {
|
|||||||
int _blockSize; // I2S block size
|
int _blockSize; // I2S block size
|
||||||
volatile int _sampleNoDCOffset; // Up-to-date sample without DCOffset
|
volatile int _sampleNoDCOffset; // Up-to-date sample without DCOffset
|
||||||
float _dcOffset; // Rolling average DC offset
|
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
|
bool _initialized; // Gets set to true if initialization is successful
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -91,13 +114,13 @@ class AudioSource {
|
|||||||
*/
|
*/
|
||||||
class I2SSource : public AudioSource {
|
class I2SSource : public AudioSource {
|
||||||
public:
|
public:
|
||||||
I2SSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
I2SSource(int sampleRate, int blockSize) :
|
||||||
AudioSource(sampleRate, blockSize, lshift, mask) {
|
AudioSource(sampleRate, blockSize) {
|
||||||
_config = {
|
_config = {
|
||||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
|
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
|
||||||
.sample_rate = _sampleRate,
|
.sample_rate = _sampleRate,
|
||||||
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
|
.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)
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||||
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
|
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
|
||||||
#else
|
#else
|
||||||
@ -193,26 +216,16 @@ class I2SSource : public AudioSource {
|
|||||||
|
|
||||||
// Store samples in sample buffer and update DC offset
|
// Store samples in sample buffer and update DC offset
|
||||||
for (int i = 0; i < num_samples; i++) {
|
for (int i = 0; i < num_samples; i++) {
|
||||||
// pre-shift samples down to 16bit
|
newSamples[i] = postProcessSample(newSamples[i]); // perform postprocessing (needed for ADC samples)
|
||||||
#ifdef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
|
||||||
if (_shift != 0)
|
|
||||||
newSamples[i] >>= 16;
|
|
||||||
#endif
|
|
||||||
float currSample = 0.0f;
|
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
|
#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
|
#else
|
||||||
currSample = (float) newSamples[i];
|
currSample = (float) newSamples[i]; // 16bit input -> use as-is
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
buffer[i] = currSample;
|
buffer[i] = currSample;
|
||||||
_dcOffset = ((_dcOffset * 31) + currSample) / 32;
|
_dcOffset = ((_dcOffset * 31.0f) + currSample) / 32.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update no-DC sample
|
// Update no-DC sample
|
||||||
@ -275,8 +288,8 @@ class ES7243 : public I2SSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ES7243(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
ES7243(int sampleRate, int blockSize) :
|
||||||
I2SSource(sampleRate, blockSize, lshift, mask) {
|
I2SSource(sampleRate, blockSize) {
|
||||||
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
|
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -314,8 +327,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
class I2SAdcSource : public I2SSource {
|
class I2SAdcSource : public I2SSource {
|
||||||
public:
|
public:
|
||||||
I2SAdcSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
I2SAdcSource(int sampleRate, int blockSize) :
|
||||||
I2SSource(sampleRate, blockSize, lshift, mask) {
|
I2SSource(sampleRate, blockSize) {
|
||||||
_config = {
|
_config = {
|
||||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
|
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
|
||||||
.sample_rate = _sampleRate,
|
.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) {
|
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)) {
|
if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) {
|
||||||
return;
|
return;
|
||||||
@ -432,8 +448,8 @@ class I2SAdcSource : public I2SSource {
|
|||||||
*/
|
*/
|
||||||
class SPH0654 : public I2SSource {
|
class SPH0654 : public I2SSource {
|
||||||
public:
|
public:
|
||||||
SPH0654(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
SPH0654(int sampleRate, int blockSize) :
|
||||||
I2SSource(sampleRate, blockSize, lshift, mask)
|
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) {
|
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