Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/mdev' into animatrix-gamma
Browse files Browse the repository at this point in the history
  • Loading branch information
netmindz committed Oct 21, 2023
2 parents 4713150 + 5672405 commit 8c3c9b4
Show file tree
Hide file tree
Showing 19 changed files with 4,658 additions and 4,520 deletions.
2 changes: 1 addition & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ lib_deps_S =


animartrix_build_flags = -D USERMOD_ANIMARTRIX ;; WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick
animartrix_lib_deps = https://github.com/netmindz/animartrix.git#f070fefc42febe2de3a2ab5d6d39e78bbc539702
animartrix_lib_deps = https://github.com/netmindz/animartrix.git#8fd0df3e0b006244d53eaf480c2720daa5a697aa

build_flags_M =
-D USERMOD_ARTIFX ; WLEDMM usermod - temporarily moved into "_M", due to problems in "_S" when compiling with -O2
Expand Down
4 changes: 2 additions & 2 deletions usermods/BH1750_v2/usermod_bh1750.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ class Usermod_BH1750 : public Usermod
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
device[F("name")] = serverDescription;
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
device[F("manufacturer")] = F("WLED");
device[F("model")] = F("FOSS");
device[F("manufacturer")] = F(WLED_BRAND); //WLEDMM + Moustachauve/Wled-Native
device[F("model")] = F(WLED_PRODUCT_NAME); //WLEDMM + Moustachauve/Wled-Native
device[F("sw_version")] = versionString;

String temp;
Expand Down
4 changes: 2 additions & 2 deletions usermods/BME280_v2/usermod_bme280.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ class UsermodBME280 : public Usermod
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
device[F("name")] = serverDescription;
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
device[F("manufacturer")] = F("WLED");
device[F("model")] = F("FOSS");
device[F("manufacturer")] = F(WLED_BRAND); //WLEDMM + Moustachauve/Wled-Native
device[F("model")] = F(WLED_PRODUCT_NAME); //WLEDMM + Moustachauve/Wled-Native
device[F("sw_version")] = versionString;

String temp;
Expand Down
4 changes: 2 additions & 2 deletions usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,8 @@ class PIRsensorSwitch : public Usermod
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
device[F("name")] = serverDescription;
device[F("ids")] = String(F("wled-sensor-")) + mqttClientID;
device[F("mf")] = "WLED";
device[F("mdl")] = F("FOSS");
device[F("mf")] = F(WLED_BRAND); //WLEDMM + Moustachauve/Wled-Native
device[F("mdl")] = F(WLED_PRODUCT_NAME); //WLEDMM + Moustachauve/Wled-Native
device[F("sw")] = versionString;

sprintf_P(buf, PSTR("homeassistant/binary_sensor/%s/config"), uid);
Expand Down
43 changes: 39 additions & 4 deletions usermods/audioreactive/audio_reactive.h
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ class AudioReactive : public Usermod {
float sampleRaw; // 04 Bytes - either "sampleRaw" or "rawSampleAgc" depending on soundAgc setting
float sampleSmth; // 04 Bytes - either "sampleAvg" or "sampleAgc" depending on soundAgc setting
uint8_t samplePeak; // 01 Bytes - 0 no peak; >=1 peak detected. In future, this will also provide peak Magnitude
uint8_t reserved1; // 01 Bytes - for future extensions - not used yet
uint8_t frameCounter; // 01 Bytes - track duplicate/out of order packets
uint8_t fftResult[16]; // 16 Bytes
float FFT_Magnitude; // 04 Bytes
float FFT_MajorPeak; // 04 Bytes
Expand Down Expand Up @@ -1457,16 +1457,19 @@ class AudioReactive : public Usermod {
void transmitAudioData()
{
if (!udpSyncConnected) return;
static uint8_t frameCounter = 0;
//DEBUGSR_PRINTLN("Transmitting UDP Mic Packet");

audioSyncPacket transmitData;
memset(reinterpret_cast<void *>(&transmitData), 0, sizeof(transmitData)); // make sure that the packet - including "invisible" padding bytes added by the compiler - is fully initialized

strncpy_P(transmitData.header, PSTR(UDP_SYNC_HEADER), 6);
// transmit samples that were not modified by limitSampleDynamics()
transmitData.sampleRaw = (soundAgc) ? rawSampleAgc: sampleRaw;
transmitData.sampleSmth = (soundAgc) ? sampleAgc : sampleAvg;
transmitData.samplePeak = udpSamplePeak ? 1:0;
udpSamplePeak = false; // Reset udpSamplePeak after we've transmitted it
transmitData.reserved1 = 0;
transmitData.frameCounter = frameCounter;

for (int i = 0; i < NUM_GEQ_CHANNELS; i++) {
transmitData.fftResult[i] = (uint8_t)constrain(fftResult[i], 0, 254);
Expand All @@ -1479,7 +1482,8 @@ class AudioReactive : public Usermod {
fftUdp.write(reinterpret_cast<uint8_t *>(&transmitData), sizeof(transmitData));
fftUdp.endPacket();
}
return;

frameCounter++;
} // transmitAudioData()
#endif
static bool isValidUdpSyncVersion(const char *header) {
Expand All @@ -1491,6 +1495,16 @@ class AudioReactive : public Usermod {

void decodeAudioData(int packetSize, uint8_t *fftBuff) {
audioSyncPacket *receivedPacket = reinterpret_cast<audioSyncPacket*>(fftBuff);

static uint8_t lastFrameCounter = 0;
if(receivedPacket->frameCounter <= lastFrameCounter && receivedPacket->frameCounter != 0) { // TODO: might need extra checks here
DEBUGSR_PRINTF("Skipping audio frame out of order or duplicated - %u vs %u\n", lastFrameCounter, receivedPacket->frameCounter);
return;
}
else {
lastFrameCounter = receivedPacket->frameCounter;
}

// update samples for effects
volumeSmth = fmaxf(receivedPacket->sampleSmth, 0.0f);
volumeRaw = fmaxf(receivedPacket->sampleRaw, 0.0f);
Expand Down Expand Up @@ -1739,6 +1753,23 @@ class AudioReactive : public Usermod {
if (i2c_sda >= 0) sdaPin = -1; // -1 = use global
if (i2c_scl >= 0) sclPin = -1;

if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
break;
case 7:
#ifdef use_wm8978_mic
DEBUGSR_PRINTLN(F("AR: WM8978 Source (Mic)"));
#else
DEBUGSR_PRINTLN(F("AR: WM8978 Source (Line-In)"));
#endif
audioSource = new WM8978Source(SAMPLE_RATE, BLOCK_SIZE, 1.0f);
//useInputFilter = 0; // to disable low-cut software filtering and restore previous behaviour
delay(100);
// WLEDMM align global pins
if ((sdaPin >= 0) && (i2c_sda < 0)) i2c_sda = sdaPin; // copy usermod prefs into globals (if globals not defined)
if ((sclPin >= 0) && (i2c_scl < 0)) i2c_scl = sclPin;
if (i2c_sda >= 0) sdaPin = -1; // -1 = use global
if (i2c_scl >= 0) sclPin = -1;

if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
break;

Expand Down Expand Up @@ -2548,7 +2579,11 @@ class AudioReactive : public Usermod {
#else
oappend(SET_F("addOption(dd,'ES8388 ☾',6);"));
#endif

#if SR_DMTYPE==7
oappend(SET_F("addOption(dd,'WM8978 ☾ (⎌)',7);"));
#else
oappend(SET_F("addOption(dd,'WM8978 ☾',7);"));
#endif
#ifdef SR_SQUELCH
oappend(SET_F("addInfo('AudioReactive:config:squelch',1,'<i>&#9100; ")); oappendi(SR_SQUELCH); oappend("</i>');"); // 0 is field type, 1 is actual field
#endif
Expand Down
91 changes: 91 additions & 0 deletions usermods/audioreactive/audio_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,97 @@ class ES8388Source : public I2SSource {

};

class WM8978Source : public I2SSource {
private:
// I2C initialization functions for WM8978
void _wm8978I2cBegin() {
Wire.setClock(400000);
}

void _wm8978I2cWrite(uint8_t reg, uint16_t val) {
#ifndef WM8978_ADDR
#define WM8978_ADDR 0x1A
#endif
char buf[2];
buf[0] = (reg << 1) | ((val >> 8) & 0X01);
buf[1] = val & 0XFF;
Wire.beginTransmission(WM8978_ADDR);
Wire.write((const uint8_t*)buf, 2);
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
if (i2cErr != 0) {
DEBUGSR_PRINTF("AR: WM8978 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, WM8978_ADDR, reg, val);
}
}

void _wm8978InitAdc() {
// https://www.mouser.com/datasheet/2/76/WM8978_v4.5-1141768.pdf
// Sets ADC to around what AudioReactive expects, and loops line-in to line-out/headphone for monitoring.
// Registries are decimal, settings are 9-bit binary as that's how everything is listed in the docs
// ...which makes it easier to reference the docs.
//
_wm8978I2cBegin();

_wm8978I2cWrite( 0,0b000000000); // Reset all settings
_wm8978I2cWrite( 1,0b000001011); // Power Management 1 - power off most things
_wm8978I2cWrite( 2,0b110110011); // Power Management 2 - enable output and amp stages (amps may lift signal but it works better on the ADCs)
_wm8978I2cWrite( 3,0b000001100); // Power Management 3 - enable L&R output mixers
_wm8978I2cWrite( 4,0b001010000); // Audio Interface - standard I2S, 24-bit
_wm8978I2cWrite( 5,0b000000001); // Loopback Enable
_wm8978I2cWrite( 6,0b000000000); // Clock generation control - use external mclk
_wm8978I2cWrite( 7,0b000000100); // Sets sample rate to ~24kHz (only used for internal calculations, not I2S)
_wm8978I2cWrite(14,0b010001000); // 128x ADC oversampling - high pass filter disabled as it kills the bass response
_wm8978I2cWrite(43,0b000110000); // Mute signal paths we don't use
_wm8978I2cWrite(44,0b000000000); // Disconnect microphones
_wm8978I2cWrite(45,0b111000000); // Mute signal paths we don't use
_wm8978I2cWrite(46,0b111000000); // Mute signal paths we don't use
_wm8978I2cWrite(47,0b001000000); // 0dB gain on left line-in
_wm8978I2cWrite(48,0b001000000); // 0dB gain on right line-in
_wm8978I2cWrite(49,0b000000010); // Mixer thermal shutdown enable
_wm8978I2cWrite(50,0b000010110); // Output mixer enable only left bypass at 0dB gain
_wm8978I2cWrite(51,0b000010110); // Output mixer enable only right bypass at 0dB gain
_wm8978I2cWrite(52,0b110111001); // Left line-out enabled at 0dB gain
_wm8978I2cWrite(53,0b110111001); // Right line-out enabled at 0db gain
_wm8978I2cWrite(54,0b001000000); // Mute left speaker output
_wm8978I2cWrite(55,0b101000000); // Mute right speaker output

}

public:
WM8978Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
};

void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
DEBUGSR_PRINTLN("WM8978Source:: initialize();");

// if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
// ERRORSR_PRINTF("\nAR: invalid I2S WM8978 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
// return;
// }
// BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
// Workaround: Set I2C pins here, which will also set them globally.
// Bug also exists in ES7243.
if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
ERRORSR_PRINTF("\nAR: invalid WM8978 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
return;
}
if (!pinManager.joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
return;
}

// First route mclk, then configure ADC over I2C, then configure I2S
_wm8978InitAdc();
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
}

void deinitialize() {
I2SSource::deinitialize();
}

};

#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
#if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC)
#warning this MCU does not support analog sound input
Expand Down
Loading

0 comments on commit 8c3c9b4

Please sign in to comment.