Skip to content

Conversation

DedeHai
Copy link
Collaborator

@DedeHai DedeHai commented Jun 26, 2025

  • Added ESP DSP FFT option for all ESP32 variants
    • ESP32 and S3 still use ArduinoFFT by default because DSP FFT uses ~13k extra flash
    • S2 and C3 use DSP FFT with integer math by default
  • Added integer math for handling sample filter and FFT bin post-processing
  • useBandPassFilter applied to all mic inputs: tested with INMP441 and sine-tones from speaker -> reduces aliasing to base bands and highest frequency bands and comes at minimal processing cost
  • changed default windowing from Flat Top to Blackman-Harris for cleaner frequency peaks

On the C3 the DSP FFT saves 3k of flash but uses 6k of heap, probably for FFT internal buffers.
On the S2 the DSP FFT uses 2.2k extra flash, did not check heap but most likely the same as on C3

Sampling time and FFT time on S2 and C3 are now just barely higher than on ESP32 or S3. Using the DSP FFT on ESP32 or S3 uses the highly optimized versions (roughly 2x as fast as Arduino FFT but at a high extra flash cost and probably also heap).

Some speed test results:

ESP32 Speed test with 512 samples:
Arduino FFT (float): 0.9ms
DSP FFT (float): 0.49ms
DSP FFT (16bit integer): 1.4ms

ESP32 S3 Speed test with 512 samples:
Arduino FFT (float): 0.90ms
DSP FFT (float): 0.51ms (0.68ms if using ANSI C version)
DSP FFT (16bit integer): 1.1ms

ESP32 S2 Speed test with 512 samples:
Arduino FFT (float): 8.5ms
DSP FFT (float): 7.2ms
DSP FFT (16bit integer): 1.3ms

ESP32 C3 Speed test with 512 samples:
Arduino FFT (float): 22.0ms
DSP FFT (float): 19.1ms
DSP FFT (16bit integer): 1.4ms

@softhack007 I moved the scaling of float version (/16.0) up into the FFT code as I did not see any reason it is not done there. Any issue with that?

I tested all versions with a simulated set of sine waves on all platforms, ArduinoFFT and DSP FFT produce identical results, integer version is of course slightly less accurate but produces very comparable results. I also compared ArduinoFFT and integer FFT side-by-side using PS GEQ 1D: while the ArduinoFFT sometimes picks up on low-volume sounds, the integer FFT is much more responsive thanks to faster processing. In general, all variants behave almost the same.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added support for multiple FFT processing backends, including floating point and integer FFT variants, with improved windowing options for sharper frequency detection.
    • Enabled audioreactive features for the ESP32-C3 development board.
  • Improvements

    • Enhanced audio sample handling to support both floating point and integer data types, improving compatibility and performance across different devices.
    • Improved bandpass filter logic and default settings for microphone inputs.
    • Updated audio source sample retrieval to use a generic buffer type, enabling flexible data handling.
  • Bug Fixes

    • Fixed compatibility issues with ESP32-C3 by updating conditional compilation checks.

@DedeHai DedeHai requested review from netmindz and softhack007 June 26, 2025 21:11
Copy link
Contributor

coderabbitai bot commented Jun 26, 2025

Walkthrough

The changes implement multiple FFT processing backends (ArduinoFFT, ESP-IDF DSP, and integer FFT) for the audioreactive usermod, with conditional compilation to select the appropriate variant. Buffer management, windowing, and filtering are adapted for each type. The audio sample acquisition interface is generalized to support both floating-point and integer sample buffers. The platformio.ini configuration is updated to enable the audioreactive usermod for the ESP32-C3 environment.

Changes

Files/Paths Change Summary
platformio.ini Added custom_usermods = audioreactive to [env:esp32c3dev] to enable the audioreactive usermod.
usermods/audioreactive/audio_reactive.cpp Refactored to support multiple FFT variants (ArduinoFFT, ESP-IDF DSP, integer FFT); updated buffer management, windowing, and filtering; added macros for FFT selection; changed filter defaults; introduced FFT_PREFER_EXACT_PEAKS macro; overloaded runMicFilter.
usermods/audioreactive/audio_source.h Updated getSamples method signatures to use FFTsampleType* instead of float*; removed exclusion of CONFIG_IDF_TARGET_ESP32C3 from conditional compilation; adjusted I2S sample processing logic for integer FFT variant.

Suggested reviewers

  • softhack007

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e032cf7 and 1cc8adf.

📒 Files selected for processing (1)
  • usermods/audioreactive/audio_reactive.cpp (16 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: DedeHai
PR: wled/WLED#4750
File: usermods/audioreactive/audio_reactive.cpp:311-321
Timestamp: 2025-06-27T06:04:44.622Z
Learning: In WLED's AudioReactive usermod, FFT buffer memory (valFFT, windowFFT) is allocated once during initialization and never deallocated, following typical embedded system design where core functionality buffers persist for the application lifetime. Raw buffer pointers for aligned allocations don't need to be tracked in the current implementation since deallocation is never performed.
usermods/audioreactive/audio_reactive.cpp (4)

<retrieved_learning>
Learnt from: DedeHai
PR: #4750
File: usermods/audioreactive/audio_reactive.cpp:311-321
Timestamp: 2025-06-27T06:04:44.622Z
Learning: In WLED's AudioReactive usermod, FFT buffer memory (valFFT, windowFFT) is allocated once during initialization and never deallocated, following typical embedded system design where core functionality buffers persist for the application lifetime. Raw buffer pointers for aligned allocations don't need to be tracked in the current implementation since deallocation is never performed.
</retrieved_learning>

<retrieved_learning>
Learnt from: blazoncek
PR: #4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize)) is unnecessary and could be problematic, as it bypasses proper size verification.
</retrieved_learning>

<retrieved_learning>
Learnt from: netmindz
PR: #4728
File: wled00/FX.h:378-378
Timestamp: 2025-06-15T09:59:52.720Z
Learning: In WLED's FX.h, MODE_COUNT represents the highest FX_MODE_ ID + 1, not the total count of FX_MODE_ definitions. This is because some effects may have been removed but their IDs are not reused, creating gaps in the numbering sequence. MODE_COUNT is used for array bounds and range checking.
</retrieved_learning>

<retrieved_learning>
Learnt from: netmindz
PR: #4728
File: wled00/FX.h:378-378
Timestamp: 2025-06-15T09:59:52.720Z
Learning: In WLED's FX.h, MODE_COUNT represents the highest FX_MODE_ ID + 1, not the total count of FX_MODE_ definitions. This is because some effects may have been removed but their IDs are not reused, creating gaps in the numbering sequence. MODE_COUNT is used for array bounds and range checking.
</retrieved_learning>

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32dev_V4)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp32dev)
🔇 Additional comments (12)
usermods/audioreactive/audio_reactive.cpp (12)

27-27: LGTM: Clear macro definition for windowing preference.

The FFT_PREFER_EXACT_PEAKS macro provides clear control over windowing function selection with good inline documentation.


29-46: Excellent documentation of FFT variants and their trade-offs.

The comprehensive comment block clearly explains the three FFT variants, their performance characteristics, and usage patterns. This is valuable for maintainability and developer understanding.


149-166: Well-designed type abstraction for FFT variants.

The type aliases and macro abstractions (FFTsampleType, FFTmathType, FFTabs) provide clean separation between float and integer FFT implementations. This follows good abstraction principles.


289-343: FFT buffer allocation and initialization appears correct.

The three different FFT initialization paths are properly implemented with appropriate error handling. The aligned memory allocation for ESP-IDF DSP FFT is correctly implemented, and the conversion from float to int16_t window function is properly done.

Based on retrieved learnings, the memory allocation pattern follows the embedded system design where buffers persist for the application lifetime, so not tracking raw pointers for deallocation is acceptable.


377-379: Good separation of filtering flags with clear documentation.

The distinction between useBandPassFilter and useMicFilter is well-documented and provides appropriate control over different filtering stages.


397-476: Complex but well-structured FFT processing implementation.

The three-way conditional compilation for different FFT variants is extensive but well-organized. Each path handles the specific requirements of its FFT type appropriately:

  • ArduinoFFT path uses the library's built-in methods
  • ESP-IDF DSP float path implements manual DC removal, windowing, and magnitude calculation
  • Integer FFT path uses fixed-point arithmetic throughout

The code correctly handles the different data types and scaling requirements for each variant.


579-644: Well-implemented dual filtering functions for float and integer types.

The overloaded runMicFilter functions provide equivalent filtering logic for both floating-point and fixed-point arithmetic. The fixed-point implementation correctly uses appropriate bit shifting and maintains precision. The parameter constants are well-documented with their cutoff frequencies.


732-732: Update peak detection to use unified FFT buffer.

The peak detection correctly references valFFT[binNum] instead of the old vReal array, maintaining consistency with the unified buffer approach.


1377-1379: Appropriate default filter settings based on hardware capabilities.

The default settings for useBandPassFilter and useMicFilter are sensible, with filtering enabled by default to improve audio quality for most microphone inputs.


1413-1413: Logical filter disabling for line-in sources.

Disabling useMicFilter for I2S with Master Clock (typically used for line-in) is appropriate since line-in sources generally don't need the same noise filtering as microphones.


1429-1429: Consistent filter disabling for ES8388 source.

Similar to the I2S Master Clock case, disabling useMicFilter for ES8388 is appropriate as this is typically a line-in audio source.


126-147: FFT variant macros verified—logic is correct.

The conditional compilation in usermods/audioreactive/audio_reactive.cpp (lines 126-147) properly:

  • Defines UM_AUDIOREACTIVE_USE_ARDUINO_FFT for ESP32/S3 (and for ESP-IDF < 4.4.0).
  • Includes the ArduinoFFT library and undefines integer FFT when that macro is set.
  • Otherwise includes the ESP-IDF DSP FFT and defines UM_AUDIOREACTIVE_USE_INTEGER_FFT for ESP32-S2/C3.

A repository-wide search shows no other conflicting definitions of UM_AUDIOREACTIVE_USE_*(ARDUINO_FFT|ESPDSP_FFT|INTEGER_FFT).

(Optional) To improve maintainability, you might merge the two separate #if blocks that set UM_AUDIOREACTIVE_USE_ARDUINO_FFT or add a comment clarifying their precedence.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
usermods/audioreactive/audio_source.h (1)

710-710: Critical: Method signature mismatch with base class.

The getSamples method in I2SAdcSource still uses float* while the base class AudioSource now uses void*. This signature mismatch means the method won't properly override the virtual function, causing potential compilation errors or runtime issues.

Apply this fix to match the base class signature:

-    void getSamples(float *buffer, uint16_t num_samples) {
+    void getSamples(void *buffer, uint16_t num_samples) {
+      float *_buffer = static_cast<float*>(buffer);

Then update line 725 to use _buffer instead of buffer:

-        I2SSource::getSamples(buffer, num_samples);
+        I2SSource::getSamples(_buffer, num_samples);
🧹 Nitpick comments (3)
usermods/audioreactive/audio_reactive.cpp (3)

371-373: Consider using separate buffers for clarity

While the current approach works, reusing valFFT as an int16_t buffer when it's declared as float* relies on type punning. Consider using a union or separate buffers for better type safety.


663-663: Remove redundant semicolon

-sampleBuffer[i] = highFilteredSample_fp - (lowfilt_fp >> 15);;
+sampleBuffer[i] = highFilteredSample_fp - (lowfilt_fp >> 15);

540-540: Fix comment formatting

-// bins frequency  range
+// bins frequency range
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 66869f8 and ab69c5a.

📒 Files selected for processing (3)
  • platformio.ini (1 hunks)
  • usermods/audioreactive/audio_reactive.cpp (14 hunks)
  • usermods/audioreactive/audio_source.h (4 hunks)
🧰 Additional context used
🧠 Learnings (3)
platformio.ini (2)
Learnt from: blazoncek
PR: wled/WLED#4482
File: wled00/udp.cpp:147-149
Timestamp: 2025-02-19T12:43:34.199Z
Learning: ESP8266 and ESP32 platforms have different maximum segment name lengths in WLED, which can cause truncation when syncing segment names between devices. This platform difference affects the user experience when using the segment name sync feature.
Learnt from: willmmiles
PR: wled/WLED#4623
File: usermods/word-clock-matrix/word-clock-matrix.cpp:332-332
Timestamp: 2025-03-29T01:22:54.617Z
Learning: In the WLED project, hardcoded usermod IDs (like 500 in the WordClockMatrix's getId() method) are intentionally used as part of a strategy to avoid modifying core code. There are plans to remove numeric usermod IDs entirely in the future.
usermods/audioreactive/audio_source.h (1)
Learnt from: blazoncek
PR: wled/WLED#4482
File: wled00/udp.cpp:147-149
Timestamp: 2025-02-19T12:43:34.199Z
Learning: ESP8266 and ESP32 platforms have different maximum segment name lengths in WLED, which can cause truncation when syncing segment names between devices. This platform difference affects the user experience when using the segment name sync feature.
usermods/audioreactive/audio_reactive.cpp (2)
Learnt from: netmindz
PR: wled/WLED#4728
File: wled00/FX.h:378-378
Timestamp: 2025-06-15T09:59:52.720Z
Learning: In WLED's FX.h, MODE_COUNT represents the highest FX_MODE_ ID + 1, not the total count of FX_MODE_ definitions. This is because some effects may have been removed but their IDs are not reused, creating gaps in the numbering sequence. MODE_COUNT is used for array bounds and range checking.
Learnt from: netmindz
PR: wled/WLED#4728
File: wled00/FX.h:378-378
Timestamp: 2025-06-15T09:59:52.720Z
Learning: In WLED's FX.h, MODE_COUNT represents the highest FX_MODE_ ID + 1, not the total count of FX_MODE_ definitions. This is because some effects may have been removed but their IDs are not reused, creating gaps in the numbering sequence. MODE_COUNT is used for array bounds and range checking.
⏰ Context from checks skipped due to timeout of 90000ms (16)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (esp32dev_V4)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
🔇 Additional comments (11)
platformio.ini (1)

537-537: LGTM! Configuration aligns with PR objectives.

Adding the audioreactive usermod to the ESP32-C3 build configuration is consistent with the PR's goal of enabling DSP FFT with integer math for C3 variants by default.

usermods/audioreactive/audio_source.h (3)

25-25: Correct removal of C3 from exclusion list.

The removal of CONFIG_IDF_TARGET_ESP32C3 from the conditional compilation aligns with the PR objectives to support C3 variants with DSP FFT integer math implementation.


336-367: Implementation correctly handles dual buffer types.

The conditional compilation properly splits the logic between float and integer FFT processing paths. The integer path optimization (no scaling, just bit shifting) is appropriate for performance-critical single-core MCUs like C3.

Note: The comment at line 358 mentions that scaling is not used for integer FFT to save ~0.4ms on C3, which is a reasonable performance optimization.


137-137: Breaking API change requires careful verification.

Changing the virtual method signature from float* to void* is a breaking change that affects all derived classes. While this enables flexibility for different buffer types, ensure all implementations are updated.

Run the following script to verify all AudioSource implementations have updated signatures:

#!/bin/bash
# Description: Find all AudioSource derived classes and check their getSamples signatures

# Search for classes that inherit from AudioSource
ast-grep --pattern 'class $_ : public AudioSource {
  $$$
}'

# Search for getSamples method implementations
rg -A 2 "getSamples\s*\(" --type cpp
usermods/audioreactive/audio_reactive.cpp (7)

27-68: Well-structured FFT variant selection with clear documentation

The conditional compilation logic and documentation effectively explain the FFT backend choices and their tradeoffs. The automatic selection of appropriate FFT implementations based on the target MCU is a good design decision.


70-86: Appropriate buffer management for different FFT variants

Good use of conditional typing and 16-byte alignment for SIMD optimization. The separation of real/imaginary buffers for ArduinoFFT vs interleaved format for DSP FFT is handled correctly.


217-221: Good function overloading for different sample types

The overloaded runMicFilter functions properly support both float and integer FFT paths.


275-289: Correct scaling between integer and float FFT results

The scaling factor of 32 properly compensates for the different magnitude scales between integer and float FFT implementations.


1399-1399: Appropriate band-pass filter configuration

Good decision to enable the band-pass filter by default for noise reduction while allowing high-quality sources like ES8388 to bypass it.

Also applies to: 1448-1448


29-41: Excellent performance optimization strategy

The automatic selection of integer FFT for S2/C3 (no FPU) and float FFT for ESP32/S3 (with FPU) provides optimal performance for each platform as evidenced by the timing measurements in the PR description.


489-489: ```shell
#!/bin/bash

Locate wled.h

wd=$(fd -e h wled.h)
echo "wled.h found at: $wd"

Check if wled.h includes fcn_declare.h

grep -R "#include.*fcn_declare.h" -n "$wd" || echo "fcn_declare.h not included in wled.h"

Check for sqrt32_bw declaration directly in wled.h

grep -R "sqrt32_bw" -n "$wd" || echo "sqrt32_bw not directly declared in wled.h"


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

now also compiles on ESP8266
@blazoncek
Copy link
Collaborator

I would suggest you use a brilliant idea by @TripleWhy to define custom type instead of many #ifdefs.

WLED/wled00/FX.cpp

Lines 1973 to 1993 in 66869f8

#ifdef ESP8266
using mathType = int32_t;
using wideMathType = int64_t;
using angleType = unsigned;
constexpr mathType sInt16Scale = 0x7FFF;
constexpr mathType maxAngle = 0x8000;
constexpr mathType staticRotationScale = 256;
constexpr mathType animatedRotationScale = 1;
constexpr int16_t (*sinFunction)(uint16_t) = &sin16_t;
constexpr int16_t (*cosFunction)(uint16_t) = &cos16_t;
#else
using mathType = float;
using wideMathType = float;
using angleType = float;
constexpr mathType sInt16Scale = 1.0f;
constexpr mathType maxAngle = M_PI / 256.0;
constexpr mathType staticRotationScale = 1.0f;
constexpr mathType animatedRotationScale = M_TWOPI / double(0xFFFF);
constexpr float (*sinFunction)(float) = &sin_t;
constexpr float (*cosFunction)(float) = &cos_t;
#endif

@DedeHai
Copy link
Collaborator Author

DedeHai commented Jun 27, 2025

I initially wanted to do that but then decided to do it the classic way for easier debugging. I will check if and see if that approach results in cleaner code.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Jun 28, 2025

I took a look at that approach and in some places it would work but IMHO it does not make the code cleaner or simpler. In most places different function calls are used depending on int/float so all those functions also need an alias, making the code harder to read as it gets obfuscated.

…pplication

post FFT band pass and IIR applied to samples are now separated: I found in testing that applying the sample filter helps with aliasing into base-bands, there is no need to hard-cut the lowest frequencies after FFT.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
usermods/audioreactive/audio_reactive.cpp (1)

503-507: Buffer clearing correctly handles different data types.

The memset operations now properly account for the actual buffer data type, using sizeof(float) for float buffers and sizeof(int16_t) for integer buffers. This addresses the buffer size mismatch issue identified in previous reviews.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 90637eb and f8b1af0.

📒 Files selected for processing (1)
  • usermods/audioreactive/audio_reactive.cpp (16 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: DedeHai
PR: wled/WLED#4750
File: usermods/audioreactive/audio_reactive.cpp:311-321
Timestamp: 2025-06-27T06:04:44.622Z
Learning: In WLED's AudioReactive usermod, FFT buffer memory (valFFT, windowFFT) is allocated once during initialization and never deallocated, following typical embedded system design where core functionality buffers persist for the application lifetime. Raw buffer pointers for aligned allocations don't need to be tracked in the current implementation since deallocation is never performed.
usermods/audioreactive/audio_reactive.cpp (2)

<retrieved_learning>
Learnt from: DedeHai
PR: #4750
File: usermods/audioreactive/audio_reactive.cpp:311-321
Timestamp: 2025-06-27T06:04:44.622Z
Learning: In WLED's AudioReactive usermod, FFT buffer memory (valFFT, windowFFT) is allocated once during initialization and never deallocated, following typical embedded system design where core functionality buffers persist for the application lifetime. Raw buffer pointers for aligned allocations don't need to be tracked in the current implementation since deallocation is never performed.
</retrieved_learning>

<retrieved_learning>
Learnt from: blazoncek
PR: #4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize)) is unnecessary and could be problematic, as it bypasses proper size verification.
</retrieved_learning>

⏰ Context from checks skipped due to timeout of 90000ms (19)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32dev_V4)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
🔇 Additional comments (10)
usermods/audioreactive/audio_reactive.cpp (10)

27-27: Good addition for improved FFT accuracy.

The FFT_PREFER_EXACT_PEAKS macro enables Blackman-Harris windowing for sharper frequency peaks and reduced spectral leakage, which is beneficial for audio analysis.


29-46: Well-documented FFT variant selection strategy.

The comprehensive comments clearly explain the trade-offs between different FFT implementations and the automatic selection logic based on ESP32 variants. The default choices are sensible: ArduinoFFT for ESP32/S3 (with FPU) and integer DSP FFT for S2/C3 (without FPU).


148-163: Type-safe buffer declarations with conditional compilation.

The buffer type declarations correctly adapt to the selected FFT variant. The use of int16_t* for integer FFT and float* for floating-point variants is appropriate.


214-218: Function overloading provides clean type safety.

The overloaded runMicFilter functions for both float* and int16_t* buffers provide type safety while maintaining a consistent interface. This is better than using void pointers or type casting.


273-285: Scaling factor in fftAddAvg requires validation.

The integer version uses a scaling factor of 32 to match float values. Verify this scaling is mathematically correct across the entire processing chain.

The comment mentions "scaling value between float and int is 512, float version is scaled down by 16" but uses a factor of 32. Please confirm this scaling relationship is accurate for maintaining equivalent results between FFT variants.


1401-1403: Filter initialization logic is well-designed.

The initialization of useBandPassFilter (false) and useMicFilter (true by default) provides sensible defaults. The useMicFilter helps reduce noise floor and aliasing, which is beneficial for most microphone inputs.


1437-1437: Appropriate filter disabling for line-in sources.

Disabling useMicFilter for I2S with Master Clock (typically used for line-in) is correct since line-in sources generally don't need the microphone-specific filtering.


1453-1453: Consistent filter handling for ES8388 source.

Disabling useMicFilter for ES8388 source maintains consistency with other line-in type sources that don't require microphone-specific filtering.


126-146: I’d like to inspect the actual FFT‐execution block in audio_reactive.cpp to confirm that the dsp calls are correctly wrapped and that there is no unreachable or missing path. Can you run:

sed -n '120,180p' usermods/audioreactive/audio_reactive.cpp

and share those lines?


604-668: runMicFilter integer implementation verified

The fixed-point version correctly mirrors the floating-point logic:

  • BETA1_FP (55706) and BETA2_FP ((65536 − 55706)/2 = 4915) exactly encode β₁=0.85 and β₂=(1–β₁)/2 in 16.16 format, with the >>16 shift restoring the filtered sample to original units.
  • ALPHA_FP (737) matches α=0.0225 in 17.15 format, with the >>15 shift correctly converting back to sample units.
  • Worst-case intermediate products (≤≈2.147×10⁹) and low-pass accumulator (≤≈1.122×10⁹) stay within signed 32-bit range, avoiding overflow.

No changes required.

// get a fresh batch of samples from I2S
if (audioSource) audioSource->getSamples(vReal, samplesFFT);
memset(vImag, 0, samplesFFT * sizeof(float)); // set imaginary parts to 0
if (audioSource) audioSource->getSamples(valFFT, samplesFFT); // note: valFFT is used as a int16_t buffer on C3 and S2, could optimize RAM use by only allocating half the size (but makes code harder to read)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify buffer type compatibility in getSamples call.

The comment mentions that valFFT is used as an int16_t buffer on C3/S2 but the function signature suggests void*. Ensure the AudioSource implementation handles this correctly.


🏁 Script executed:

#!/bin/bash
# Check AudioSource getSamples method signature and implementation
ast-grep --pattern 'getSamples($_, $_) {
  $$$
}'

# Also check the header file
rg "getSamples" usermods/audioreactive/audio_source.h -A 3 -B 3

Length of output: 1265


🏁 Script executed:

#!/bin/bash
# Search for valFFT declaration and allocation in audio_reactive.cpp
rg "valFFT" -n -C 5 usermods/audioreactive/audio_reactive.cpp

Length of output: 11977


🏁 Script executed:

#!/bin/bash
# Locate I2SSource class definition and its getSamples signature
rg -n "class I2SSource" -C5 usermods/audioreactive/audio_source.h
rg -n "I2SSource::getSamples" -C5 usermods/audioreactive/audio_source.*

Length of output: 1083


🏁 Script executed:

#!/bin/bash
# Locate audioSource declaration and initialization in audio_reactive.cpp
rg -n "audioSource" -C5 usermods/audioreactive/audio_reactive.cpp

Length of output: 8473


🏁 Script executed:

#!/bin/bash
# Print context around the inline getSamples implementations in audio_source.h
sed -n '680,760p' usermods/audioreactive/audio_source.h

Length of output: 3711


Ensure getSamples buffer type matches valFFT under integer‐FFT mode

There’s a mismatch when UM_AUDIOREACTIVE_USE_INTEGER_FFT is enabled:
– In audio_reactive.cpp (line 370) you pass an int16_t* (valFFT) into
audioSource->getSamples(valFFT, samplesFFT).
– In usermods/audioreactive/audio_source.h, the analog‐ADC override of
getSamples(void*, uint16_t) always does
float* _buffer = static_cast<float*>(buffer);
(around line 700), so it assumes buffer is a float*.

Action items:
• Verify each AudioSource::getSamples override handles an int16_t* buffer when integer FFT is in use.
• Either:
– Change valFFT to remain a float* (and convert to integers before DSP), or
– Add an #ifdef UM_AUDIOREACTIVE_USE_INTEGER_FFT path in each getSamples override that casts to int16_t* and delivers raw 16-bit samples.
• Confirm no overruns or miscasts occur on C3/S2 targets.

🤖 Prompt for AI Agents
In usermods/audioreactive/audio_reactive.cpp at line 370, the buffer valFFT is
passed as int16_t* to getSamples, but the getSamples overrides in audio_source.h
assume a float* buffer, causing a type mismatch under
UM_AUDIOREACTIVE_USE_INTEGER_FFT. To fix this, review each
AudioSource::getSamples override and add conditional compilation (#ifdef
UM_AUDIOREACTIVE_USE_INTEGER_FFT) to handle int16_t* buffers correctly by
casting and providing raw 16-bit samples. Alternatively, keep valFFT as float*
and convert to int16_t after DSP. Ensure no buffer overruns or invalid casts
occur on C3/S2 targets during this adjustment.

better anti-aliasing at minimal loss of base frequency.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
usermods/audioreactive/audio_reactive.cpp (2)

503-507: Duplicate comment: Buffer clearing fixed correctly.

The memset calls now correctly account for different buffer types, resolving the previously identified buffer size mismatch issue.


370-370: Duplicate comment: Buffer type mismatch in getSamples call still exists.

This issue was previously identified but remains unresolved. When UM_AUDIOREACTIVE_USE_INTEGER_FFT is enabled, valFFT is int16_t* but getSamples implementations in audio_source.h expect float* buffers.

🧹 Nitpick comments (2)
usermods/audioreactive/audio_reactive.cpp (2)

309-348: Memory allocation complexity increases maintenance burden.

The three different memory allocation strategies for each FFT variant significantly increase code complexity. While the aligned allocation for DSP variants is necessary for SIMD performance, the branching logic makes the code harder to maintain.

Consider extracting allocation logic into separate functions:

+static bool allocateArduinoFFTBuffers() {
+  if (valFFT == nullptr) valFFT = (float*) calloc(sizeof(float), samplesFFT);
+  if (vImag == nullptr) vImag = (float*) calloc(sizeof(float), samplesFFT);
+  return (valFFT != nullptr) && (vImag != nullptr);
+}

+static bool allocateFloatDSPBuffers() {
+  // Float DSP allocation logic here
+}

+static bool allocateIntegerDSPBuffers() {
+  // Integer DSP allocation logic here
+}

1405-1457: Filter initialization logic is sensible but could be more explicit.

The initialization of useBandPassFilter and useMicFilter based on audio source type is logical, but the conditions could be more clearly documented. Specifically, why ES8388 and I2S with Master Clock sources skip filtering.

Add clarifying comments:

-useMicFilter = false; // I2S with Master Clock is mostly used for line-in, skip sample filtering
+useMicFilter = false; // I2S with Master Clock is typically used for line-in audio sources that don't need microphone-specific noise filtering
-useMicFilter = false;
+useMicFilter = false; // ES8388 provides its own built-in filtering, additional software filtering is unnecessary
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8b1af0 and 3915b1b.

📒 Files selected for processing (1)
  • usermods/audioreactive/audio_reactive.cpp (16 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: DedeHai
PR: wled/WLED#4750
File: usermods/audioreactive/audio_reactive.cpp:311-321
Timestamp: 2025-06-27T06:04:44.622Z
Learning: In WLED's AudioReactive usermod, FFT buffer memory (valFFT, windowFFT) is allocated once during initialization and never deallocated, following typical embedded system design where core functionality buffers persist for the application lifetime. Raw buffer pointers for aligned allocations don't need to be tracked in the current implementation since deallocation is never performed.
usermods/audioreactive/audio_reactive.cpp (2)

<retrieved_learning>
Learnt from: DedeHai
PR: #4750
File: usermods/audioreactive/audio_reactive.cpp:311-321
Timestamp: 2025-06-27T06:04:44.622Z
Learning: In WLED's AudioReactive usermod, FFT buffer memory (valFFT, windowFFT) is allocated once during initialization and never deallocated, following typical embedded system design where core functionality buffers persist for the application lifetime. Raw buffer pointers for aligned allocations don't need to be tracked in the current implementation since deallocation is never performed.
</retrieved_learning>

<retrieved_learning>
Learnt from: blazoncek
PR: #4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize)) is unnecessary and could be problematic, as it bypasses proper size verification.
</retrieved_learning>

⏰ Context from checks skipped due to timeout of 90000ms (20)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (esp32dev_V4)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
  • GitHub Check: wled_build / Test cdata.js
🔇 Additional comments (4)
usermods/audioreactive/audio_reactive.cpp (4)

27-45: Well-documented FFT variant selection strategy.

The comprehensive documentation and conditional compilation approach for selecting FFT variants based on platform capabilities is well-designed. The performance trade-offs between accuracy and speed are clearly explained.


411-496: FFT processing logic correctly handles three variants.

The three distinct FFT processing paths are well-implemented with appropriate type handling, DC offset removal, windowing, and magnitude calculation for each variant. The integer FFT path includes proper fixed-point arithmetic.


604-672: Excellent fixed-point arithmetic implementation for integer filtering.

The integer version of runMicFilter properly implements equivalent filtering logic using fixed-point arithmetic. The scaling factors and bit shifts are well-documented and mathematically correct.


126-163: Type consistency verified—no mismatches found

All valFFT usages are correctly guarded by the corresponding preprocessor checks:

  • When UM_AUDIOREACTIVE_USE_INTEGER_FFT is defined, valFFT is an int16_t* and only integer operations (e.g., abs, integer sums, bit‐shifts) are performed.
  • When it’s not defined (including the Arduino‐FFT and float DSP paths), valFFT is a float* and only floating-point operations (e.g., fabsf, sqrtf, division by 16.0f) are used.

No type safety issues remain.

Comment on lines 272 to 286
static float fftAddAvg(int from, int to) {
#if !defined(UM_AUDIOREACTIVE_USE_INTEGER_FFT)
float result = 0.0f;
for (int i = from; i <= to; i++) {
result += vReal[i];
result += valFFT[i];
}
return result / float(to - from + 1);
#else
int32_t result = 0;
for (int i = from; i <= to; i++) {
result += valFFT[i];
}
result *= 32; // scale result to match float values. note: scaling value between float and int is 512, float version is scaled down by 16
#endif
return float(result) / float(to - from + 1); // return average as float
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Integer FFT scaling logic needs verification.

The scaling factor of 32 for integer results (line 283) appears to be a magic number. The comment mentions "scaling value between float and int is 512, float version is scaled down by 16", suggesting 512/16 = 32, but this needs validation.


🏁 Script executed:

#!/bin/bash
# Search for scaling factor documentation or related constants
rg -n -A3 -B3 "(512|scaling)" usermods/audioreactive/audio_reactive.cpp

Length of output: 7274


Extract FFT integer‐to‐float scaling into named constants

The integer FFT path today uses a hard-coded 32 to compensate for a 512× scale (512/16 = 32) when averaging. Let’s clarify and centralize this logic:

• In usermods/audioreactive/audio_reactive.cpp (near the top or in an anonymous namespace), add:

constexpr int FFT_INT_SCALE          = 512;               // int→float magnitude scale
constexpr int FFT_FLOAT_DOWNSCALE    = 16;                // float path downscaling
constexpr int FFT_INT_AVG_SCALE      = FFT_INT_SCALE / FFT_FLOAT_DOWNSCALE; // 512/16

• Replace line 283:

-  result *= 32; // scale result to match float values. note: scaling value between float and int is 512, float version is scaled down by 16
+  result *= FFT_INT_AVG_SCALE; // scale summed ints to float magnitude (512/16)

• Update line 488 similarly:

-          FFT_Magnitude_int = valFFT[i] * 512; // scale to match raw float value
+          FFT_Magnitude_int = valFFT[i] * FFT_INT_SCALE;  // scale to match float magnitudes

This removes “magic” numbers, makes the 512/16 relationship explicit, and improves maintainability.

🤖 Prompt for AI Agents
In usermods/audioreactive/audio_reactive.cpp around lines 272 to 286, replace
the hard-coded integer scaling factor 32 with named constants for clarity and
maintainability. Define constexpr int constants near the top or in an anonymous
namespace: FFT_INT_SCALE as 512, FFT_FLOAT_DOWNSCALE as 16, and
FFT_INT_AVG_SCALE as their quotient (32). Then replace the literal 32 on line
283 with FFT_INT_AVG_SCALE. Also, apply the same replacement on line 488 to
remove magic numbers and clarify the scaling logic.

- moved scaling of FFT values into fftAddAvg() to use ferwer operations
- added "using" for math types, removing some ifdefs and duplications
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
usermods/audioreactive/audio_reactive.cpp (1)

365-365: Buffer type compatibility issue persists.

The valFFT buffer passed to getSamples() can be either float* or int16_t* depending on the FFT variant, but the AudioSource implementations may expect a specific type. This creates a type safety issue that should be resolved by either:

  1. Adding conditional compilation in getSamples() implementations to handle both types
  2. Keeping valFFT as float* and converting to integers after sampling
🧹 Nitpick comments (1)
usermods/audioreactive/audio_reactive.cpp (1)

278-278: Extract scaling factors into named constants.

The hardcoded scaling factor 32 (and 512 on line 468) should be extracted into named constants to improve maintainability and make the scaling relationships explicit.

+// FFT scaling constants for integer/float conversion
+constexpr int32_t FFT_INT_SCALE = 512;           // int→float magnitude scale  
+constexpr int32_t FFT_FLOAT_DOWNSCALE = 16;      // float path downscaling
+constexpr int32_t FFT_INT_AVG_SCALE = FFT_INT_SCALE / FFT_FLOAT_DOWNSCALE; // 32

-  result *= 32; // scale result to match float values. note: raw scaling value between float and int is 512, float version is scaled down by 16
+  result *= FFT_INT_AVG_SCALE; // scale result to match float values

Apply similar changes to line 468.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3915b1b and e032cf7.

📒 Files selected for processing (2)
  • usermods/audioreactive/audio_reactive.cpp (16 hunks)
  • usermods/audioreactive/audio_source.h (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • usermods/audioreactive/audio_source.h
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: DedeHai
PR: wled/WLED#4750
File: usermods/audioreactive/audio_reactive.cpp:311-321
Timestamp: 2025-06-27T06:04:44.622Z
Learning: In WLED's AudioReactive usermod, FFT buffer memory (valFFT, windowFFT) is allocated once during initialization and never deallocated, following typical embedded system design where core functionality buffers persist for the application lifetime. Raw buffer pointers for aligned allocations don't need to be tracked in the current implementation since deallocation is never performed.
usermods/audioreactive/audio_reactive.cpp (4)

<retrieved_learning>
Learnt from: DedeHai
PR: #4750
File: usermods/audioreactive/audio_reactive.cpp:311-321
Timestamp: 2025-06-27T06:04:44.622Z
Learning: In WLED's AudioReactive usermod, FFT buffer memory (valFFT, windowFFT) is allocated once during initialization and never deallocated, following typical embedded system design where core functionality buffers persist for the application lifetime. Raw buffer pointers for aligned allocations don't need to be tracked in the current implementation since deallocation is never performed.
</retrieved_learning>

<retrieved_learning>
Learnt from: blazoncek
PR: #4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize)) is unnecessary and could be problematic, as it bypasses proper size verification.
</retrieved_learning>

<retrieved_learning>
Learnt from: netmindz
PR: #4728
File: wled00/FX.h:378-378
Timestamp: 2025-06-15T09:59:52.720Z
Learning: In WLED's FX.h, MODE_COUNT represents the highest FX_MODE_ ID + 1, not the total count of FX_MODE_ definitions. This is because some effects may have been removed but their IDs are not reused, creating gaps in the numbering sequence. MODE_COUNT is used for array bounds and range checking.
</retrieved_learning>

<retrieved_learning>
Learnt from: netmindz
PR: #4728
File: wled00/FX.h:378-378
Timestamp: 2025-06-15T09:59:52.720Z
Learning: In WLED's FX.h, MODE_COUNT represents the highest FX_MODE_ ID + 1, not the total count of FX_MODE_ definitions. This is because some effects may have been removed but their IDs are not reused, creating gaps in the numbering sequence. MODE_COUNT is used for array bounds and range checking.
</retrieved_learning>

⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
🔇 Additional comments (3)
usermods/audioreactive/audio_reactive.cpp (3)

27-46: Excellent documentation and clear macro definition.

The comprehensive comments explaining the three FFT variants (ArduinoFFT, ESP-IDF DSP float, ESP-IDF DSP integer) provide valuable context for understanding the conditional compilation strategy. The performance trade-offs and target-specific defaults are well documented.


149-165: Well-designed type abstraction for FFT variants.

The typedef approach with FFTsampleType and FFTmathType cleanly abstracts the different data types used across FFT variants. This is much more maintainable than scattered conditional compilation throughout the codebase.


612-643: Excellent fixed-point filter implementation.

The integer version of runMicFilter() correctly translates the floating-point band-pass filter mathematics to fixed-point arithmetic. The choice of 17.15 and 16.16 fixed-point formats provides appropriate precision while optimizing performance on targets without FPU (ESP32-S2, C3).

@netmindz
Copy link
Member

Personally I would prefer if this change was against the MM version as version of audioreactive in this repo is out of date, so adding this to what in effect is downstream for this usermod will make it even harder to bring in the newer MM changes

@DedeHai
Copy link
Collaborator Author

DedeHai commented Jun 28, 2025

Now that the code is running and tested, I could port it to MM quite easily. But isnt MM still using IDF 3?
I also briefly looked at MM code and it is quite a bit more advanced. Is bringing it to AC even feasible at this point without manually merging? It has the "fastpath" for example.
Is bringing current MM AR version to AC something I should look at?

@netmindz
Copy link
Member

netmindz commented Jun 28, 2025

Now that the code is running and tested, I could port it to MM quite easily.

Yeah I hoped that having done it once, then doing again hopefully wasn't going to too hard

But isnt MM still using IDF 3?

I'm in the process of dropping any V3 builds, we have been focusing on the V4 builds for a long time
MoonModules#244 I just need to think if we delete the non-v4 envs rather than having a duplicate of regular builds being v4 as well as the v4 ones, given we have been recommending people use the V4 envs to then remove them feels odd, but if we are now looking towards V5 then not sure what is going to be least confusing

I also briefly looked at MM code and it is quite a bit more advanced.

Yeah is roughly 18 months newer

Is bringing it to AC even feasible at this point without manually merging? It has the "fastpath" for example. Is bringing current MM AR version to AC something I should look at?

So I asked @softhack007 about just bringing in the MM version to (as was) AC, but Frank felt there were some experimental features that were perhaps a bit much for vanilla WLED

I would be keen to see everything on V4 and ESP-DSP and the vanilla WLED version basically based off WLED-MM but with the experimental stuff stripped out, it's something that really needs discussion with @softhack007 and @troyhacks

@troyhacks
Copy link
Contributor

troyhacks commented Jun 28, 2025

I would be keen to see everything on V4 and ESP-DSP and the vanilla WLED version basically based off WLED-MM but with the experimental stuff stripped out, it's something that really needs discussion with @softhack007 and @troyhacks

I also think that once we simplify and drop a bunch of the legacy things (ESP8266 for example) that the firmware size should improve - or at least that's my hope.

Also pushing towards V5 means a bunch of things get MUCH simpler to implement - like all the new RISC-V boards, easier I2S/FFT, maybe easier I2C+I2S audio codec support, etc - but I'm feeling like all this will also add some bloat to the firmware size.

So the net outcome may be "about the same." 😁

@DedeHai
Copy link
Collaborator Author

DedeHai commented Jun 28, 2025

Sounds like a good plan but also sounds like its not going to happen soon. I would love to see AR as a default on C3 in 0.16.

@netmindz netmindz added keep This issue will never become stale/closed automatically usermod usermod related labels Jul 6, 2025
@netmindz
Copy link
Member

netmindz commented Jul 6, 2025

For the moment, if we could focus on MoonModules#248 and then we can take a call at that stage if it's easier to take the MM version and strip/hide anything too experimental for the version here or finalize this "temporary" solution based on the changes made to MM.

I really don't want to try and unpick any optimizations that might happen here when we do pull over the MM version

@netmindz netmindz added the external Not part of WLED itself - an external plugin/remote etc. label Jul 6, 2025
@DedeHai
Copy link
Collaborator Author

DedeHai commented Jul 6, 2025

Yes, MM version supersedes this PR, I would like to keep this as a draft just to have a working AC version in case cherry picking MM results in trouble.

@blazoncek
Copy link
Collaborator

I took the effort back in 2022 to re-code soundreactive into usermod. It took me less than two weeks (with a bit more polishing afterwards).
It can't be that difficult to move MM AR code into AC AR even though it is like a Gordian knot. 😉 That is, if one wants to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
external Not part of WLED itself - an external plugin/remote etc. keep This issue will never become stale/closed automatically usermod usermod related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants