Skip to content

Make SoundAnalyzer a template, remove Mic type; Use proper TFT_eSPI includes, fines; #746

Merged
davepl merged 14 commits into
mainfrom
ttgo
Aug 20, 2025
Merged

Make SoundAnalyzer a template, remove Mic type; Use proper TFT_eSPI includes, fines; #746
davepl merged 14 commits into
mainfrom
ttgo

Conversation

@davepl

@davepl davepl commented Aug 16, 2025

Copy link
Copy Markdown
Contributor

Gets rid of the runtime Mic information, makes it a templated argument.
Uses the proper setup headers instead of manual definitions in platformio.ini

  • I read the contribution guidelines in CONTRIBUTING.md.
  • I understand the BlinkenPerBit metric, and maximized it in this PR.
  • I selected main as the target branch.
  • All code herein is subjected to the license terms in COPYING.txt.

@davepl
davepl requested a review from Copilot August 16, 2025 00:01

This comment was marked as outdated.

@davepl
davepl requested a review from Copilot August 17, 2025 02:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull Request Overview

This PR converts the SoundAnalyzer from a runtime-configurable class to a template-based approach with compile-time microphone type selection. It also standardizes TFT_eSPI configuration to use proper setup headers instead of manual platformio.ini definitions.

  • Replaces runtime microphone type switching with compile-time template parameters
  • Updates TFT_eSPI configuration to use proper User_Setup includes
  • Refactors PeakData from custom class to std::array for better performance

Reviewed Changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
include/soundanalyzer.h Major template refactor - converts SoundAnalyzer to template class with compile-time microphone parameters
src/audio.cpp Updates to use new templated SoundAnalyzer type and increases max FPS from 45 to 60
src/main.cpp Removes global SoundAnalyzer declaration (now handled in audio.cpp)
src/network.cpp Updates remote peak data handling to use new PeakData array interface
src/drawing.cpp Updates VU meter logic to use new IsRemoteAudioActive() method
include/screen.h Adds conditional TFT_eSPI setup includes for TTGO devices
include/globals.h Adds custom std::sum implementation and TFT_eSPI setup logic
platformio.ini Extensive updates to TFT configuration flags and project-specific settings
src/screen.cpp Screen rendering improvements including FPS throttling and display threshold adjustments
src/effects.cpp Removes duplicate DEFAULT_EFFECT_INTERVAL definition
src/deviceconfig.cpp Fixes missing brace in conditional block
include/effects/strip/musiceffect.h Updates to use array access syntax for PeakData
include/effects/strip/fireeffect.h Updates to use new std::sum function
include/effects/matrix/spectrumeffects.h Updates to use new IsRemoteAudioActive() method

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread include/globals.h
// sum helper
// Provides std::sum for containers/ranges and iterator pairs.
// Note: Adding to namespace std is a deliberate convenience for this project.
namespace std {

Copilot AI Aug 17, 2025

Copy link

Choose a reason for hiding this comment

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

Adding functions to the std namespace is undefined behavior according to the C++ standard. Consider using a custom namespace like 'nds' or 'nightdriver' instead.

Suggested change
namespace std {
// Note: Adding to namespace std is undefined behavior. Use a custom namespace instead.
namespace nightdriver {

Copilot uses AI. Check for mistakes.

This comment was marked as resolved.

Comment thread include/soundanalyzer.h Outdated
Comment thread platformio.ini Outdated
Comment thread src/screen.cpp Outdated
Comment thread include/soundanalyzer.h
Comment thread platformio.ini Outdated
davepl and others added 2 commits August 16, 2025 19:21
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@davepl
davepl requested a review from rbergen August 17, 2025 02:25

@robertlipe robertlipe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm unqualified to comment on the signal processing side, but just reviewing the normal programmer-ry stuff, there are some things I'd like you to at least reconsider, but LGTM.

Comment thread include/effects/strip/fireeffect.h Outdated
auto begin = &heat[i * CellsPerLED];
auto end = begin + CellsPerLED;
int sum = std::accumulate(begin, end, 0);
int sum = std::sum(begin, end);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think you're going backward. ::sum was proposed and never ratified in favof of ::accumulate, I think. cppreference.com, which I trust highly, lists accumulate (used correctly before) and partial_sum

Comment thread include/globals.h Outdated

// sum helper
// Provides std::sum for containers/ranges and iterator pairs.
// Note: Adding to namespace std is a deliberate convenience for this project.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think you've recreated accumulate()

I wonder if our ancient c++ has some bit of C++20 goodness that you wanted that just isn't there yet. Buzz me.

Comment thread include/globals.h
#define DISABLE_ALL_LIBRARY_WARNINGS 1
// If the project provides its own TFT_eSPI setup via USER_SETUP_LOADED
// and build_flags, do not include the TTGO default user setup to avoid
// macro redefinition warnings.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we please keep this stuff out of globals? Plop it into screen.cc or something of smaller scope.

Comment thread include/screen.h
#endif

#if USE_TFTSPI
#if TTGO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same file, different scope, different manifest. Bleeah.

Comment thread include/soundanalyzer.h
// Mesmerizer (default) tuning
static constexpr AudioInputParams kParamsMesmerizer{
inline constexpr AudioInputParams kParamsMesmerizer{
4.0f, // windowPowerCorrection

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Optional, but the readability of these blocks would be enhanced using designated initializers.
...kParamsMesmerizer{
.windowPowerCorrection = 4.0f,
.energyNoiseAdept = 0.02f,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I agree. In this case the code can just show what the documentation is explaining, with the benefit of having the compiler watching.

Comment thread platformio.ini
-DTFT_BL=45
-DTFT_BACKLIGHT_ON=HIGH
-DTFT_RGB_ORDER=TFT_RGB
-DTFT_RGB_ORDER=TFT_RGB

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Trailing Whitesapce.

Comment thread platformio.ini
build_flags = -DDEMO=1
-DEFFECTS_DEMO=1
-DPROJECT_NAME="\"M5Demo\""
-DMATRIX_WIDTH=144*5+38

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please describe the odd geometry here.

Comment thread platformio.ini
hard_reset
--no-stub

board_build.flash_mode = dio

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If it's the correct board, you shouldn't have to specify these params as they'll be provided by the board definition.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the issue here is that esp32dev covers a lot of actual devices, which may have variations with settings like this (I remember having to explicitly deviate from a board's flash memory type in another specific case before.)

That said, the ttgo env has been in here for a long time, so it is interesting why this is necessary "all of a sudden", now.

Comment thread src/network.cpp
PeakData peaks((float *)(payloadData.get() + STANDARD_DATA_HEADER_SIZE));
g_Analyzer.SetPeakData(peaks);
const float* pFloats = reinterpret_cast<const float*>(payloadData.get() + STANDARD_DATA_HEADER_SIZE);
PeakData peaks{};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this to narrow the window of them chanaging while you're looking at them? Do we need a better queueing/notification/locking scheme?

Comment thread src/screen.cpp Outdated

constexpr float kMaxFPS = 60.0f;
const auto targetDelay = PERIOD_FROM_FREQ(kMaxFPS) * MILLIS_PER_SECOND / MICROS_PER_SECOND;
delay(max(1.0, targetDelay - (millis() - lastFrame)));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we think about having this, our renderer, and anything else that's too busy spinning just park on a semaphore that's only signalled every FPS times per second so we don't have this kind of synchronization in multiple threads?

Possible downside: stampeding elephants. If we have multiple such threads and we pop the sema to start them all at the "same" time, will they compete for resources and negatively affect the entire system from advancing? (Maybe it was called something different in Win kernels, but in UNIX land, that was the name when everything was sleeping on one resource, a wakeup would be issued, but everything-1 processes would have to then immediately go back to sleep because the resoure was taken by the other things competing for that same resource: "stampeding elephants".

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think one issue with this approach is that we'd be pinning the FPS to one number, where we actually want different framerates in different places - and our opinions about what the "correct" FPS at a particular place is develops over time.

I also think the "sync to FPS per thread" approach is not so messy that we need to actively avoid it.

@davepl

davepl commented Aug 17, 2025 via email

Copy link
Copy Markdown
Contributor Author

@robertlipe

robertlipe commented Aug 17, 2025 via email

Copy link
Copy Markdown
Collaborator

@rbergen rbergen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Summary: it's not technically broken, but I really, really don't like the std::sum thing.

Comment thread include/globals.h
// sum helper
// Provides std::sum for containers/ranges and iterator pairs.
// Note: Adding to namespace std is a deliberate convenience for this project.
namespace std {

This comment was marked as resolved.

Comment thread include/globals.h
Comment thread include/soundanalyzer.h
// Mesmerizer (default) tuning
static constexpr AudioInputParams kParamsMesmerizer{
inline constexpr AudioInputParams kParamsMesmerizer{
4.0f, // windowPowerCorrection

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I agree. In this case the code can just show what the documentation is explaining, with the benefit of having the compiler watching.

Comment thread include/soundanalyzer.h
free(_vPeaks);
free(_livePeaks);
// Stop I2S if it was started
#if !USE_M5 && (ELECROW || USE_I2S_AUDIO || !defined(USE_I2S_AUDIO))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It's the syntax one would use if they want to say "don't do the following if USE_I2S_AUDIO is explicitly set to 0". I.e. default to it being 1 when undefined.

Comment thread platformio.ini
hard_reset
--no-stub

board_build.flash_mode = dio

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the issue here is that esp32dev covers a lot of actual devices, which may have variations with settings like this (I remember having to explicitly deviate from a board's flash memory type in another specific case before.)

That said, the ttgo env has been in here for a long time, so it is interesting why this is necessary "all of a sudden", now.

Comment thread src/screen.cpp Outdated

constexpr float kMaxFPS = 60.0f;
const auto targetDelay = PERIOD_FROM_FREQ(kMaxFPS) * MILLIS_PER_SECOND / MICROS_PER_SECOND;
delay(max(1.0, targetDelay - (millis() - lastFrame)));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think one issue with this approach is that we'd be pinning the FPS to one number, where we actually want different framerates in different places - and our opinions about what the "correct" FPS at a particular place is develops over time.

I also think the "sync to FPS per thread" approach is not so messy that we need to actively avoid it.

@robertlipe

robertlipe commented Aug 17, 2025 via email

Copy link
Copy Markdown
Collaborator

@rbergen

rbergen commented Aug 17, 2025

Copy link
Copy Markdown
Collaborator

It is, technically, broken to use a reserved namespace. That's why they're reserved. But, we agree that it's icky, so let's please just not.

I used Dave's usual standard for "technically broken" in cases like these, which is "it doesn't compile". ;) But yeah, you and I agree this is very disagreeable, which I think is the main point.

About the question I asked about the "why", I've now seen the answer from @davepl (I really need to remember to scroll down the PR page to look at email exchanges before I comment...), and IMHO that's not enough to hack into the std namespace. One of my own cosmetic objections to using std stuff is that it often makes the code that uses them more verbose - and to me less intuitive - than tailor-made, self-baked alternatives, but I accept that sticking to the standard when possible has benefits that far outweigh that objection - no matter how cleverly the AI-generated code has been put together.

I don't think the "this is shorter when calling" argument warrants deviating from the standard in this case, let alone extending it. And I agree that there is an actual benefit of showing intent by explicitly setting the init value, even in non-string cases.

This is why we're on 2b instead of 2a, even.

I'm sure the -std build flag refers to 2a?

Will that specific aspect of ranges work? Maybe.

I think you knew very well that it doesn't. :) I added another comment on my earlier one. Those approaches (one of which I label as a form of over-engineering myself) I have confirmed to compile.

Again/still, the main thing is that I strongly believe the whole (std::)sum() thing shouldn't exist at all in the first place.

It's the syntax one would use if they want to say "don't do the following if USE_I2S_AUDIO is explicitly set to 0". I.e. default to it being 1 when undefined.

But we've tried to avoid that, right? Since I just invented the USE_i2s_audio path a week ago (and you even called out that I, alone, had boards using it that i hadn't submitted yet) we should be able to NOT have a super strange case here. Let's try to make it less odd.

When you boil it down, this is basically a shorthand for:

#ifndef USE_I2S_AUDIO
    #define USE_I2S_AUDIO 1
#endif

#if !USE_M5 && (ELECROW || USE_I2S_AUDIO)

I agree that code is easier to follow, and more in line with our usual approach.

@davepl

davepl commented Aug 17, 2025 via email

Copy link
Copy Markdown
Contributor Author

@rbergen

rbergen commented Aug 17, 2025

Copy link
Copy Markdown
Collaborator

There's no point teaching something to someone who doesn't want to be taught. ;) It's clear that you've considered the input and made a decision.

Conventions are meant to make things predictable when people collaborate, and according to C++ conventions you indeed really shouldn't add stuff to std::. But you can; the compiler will allow it. Which makes it a case of "proceed at your own peril", in the end.

Comment thread include/globals.h
Comment on lines +900 to +904
template <typename Range>
inline auto accumulate(const Range& r)
-> std::remove_cv_t<std::remove_reference_t<decltype(*std::begin(r))>>
{
using T = std::remove_cv_t<std::remove_reference_t<decltype(*std::begin(r))>>;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

std::remove_cv_t<std::remove_reference_t<decltype(*std::begin(r))>> can be replaced by the more concise std::decay_t<decltype(*std::begin(r))>.

You may have to #include <type_traits> to make it compile.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@davepl
davepl merged commit 7fed301 into main Aug 20, 2025
94 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants