I wonder if the Timer::startTimer should not gain an extra parameter.
Imaging that you have the following 3 timers, that are not started at the same exact moment (t is the time since program start):
t = 0ms Component A calls startTimer(10)
t = 1ms Component B calls startTimer(10)
t = 3ms Component C calls startTimer(20)
then at:
t = 10ms A::timerCallback() is called
t = 11ms B::timerCallback() is called
t = 20ms A::timerCallback() is called
t = 21ms B::timerCallback() is called
t = 23ms C::timerCallback() is called
t = 30ms A::timerCallback() is called
etc…
So if you have many Timer objects, even if all their period are round numbers such as 10ms, you will end up with a TimerThreads that wakes up every millisecond, instead of waking up every hundredth of second. In the end, the juce TimerThread takes a not so insignificant amount of cpu.
I think in most cases (not all, but most), we do not really care about the precise timing of events, and we would be happy with this:
t = 10ms A::timerCallback() is called
t = 20ms A::timerCallback() is called
t = 20ms B::timerCallback() is called
t = 30ms A::timerCallback() is called
t = 30ms B::timerCallback() is called
t = 30ms C::timerCallback() is called
t = 40ms A::timerCallback() is called
etc…
That could be an extra “bool syncedTimer” argument to startTimer for example.
that’s actually something i’ve been wondering about myself. as you say: what’s the point in everything having individual timers when it’s not so important how precise the updates come anyway? i have a bit of a plan how i wanna get around that in the future. i haven’t tried it yet but my idea is a bit like this. i wanna let the pluginEditor inherit from Timer, set it to some really good fps most people wouldn’t argue having to be any faster for anything, like 60, but then let no component have its own timer, but just some class, that i still have to write, which can distribute those frames to smaller framerates while getting called by the “base” timer
Interesting thread. You might also try something like this:
class SyncedTimer : public juce::Timer
{
public:
class AddAndRemoveTimer : public juce::Timer
{
public:
int freq = 100;
AddAndRemoveTimer()
{
startTimer(freq);
}
std::vector<std::pair<juce::Timer*, int>> timersToStart;
void timerCallback() override
{
for (const auto [timer, freq] : timersToStart)
timer->startTimer(freq);
timersToStart.clear();
}
};
AddAndRemoveTimer& getAddAndRemoveTimerSingleton() const
{
static auto s = AddAndRemoveTimer();
return s;
}
void syncStartTimer(int freq)
{
jassert(getAddAndRemoveTimerSingleton().freq % freq == 0);
getAddAndRemoveTimerSingleton().timersToStart.push_back({ this, freq });
}
};
This is just a proof of concept and I haven’t even tested it. But something like this might work? Of course there’s an evil singleton in there, but the advantage is that you wouldn’t have to worry about creation arguments, just like with juce::Timer.
I don’t think so, MultiTimer seems pretty simple, it just holds an array of Timer objects, and they won’t be synchronized unless they are started simultaneously.
Accuracy of Timer is like 10-20ms or even worse dependent on what’s currently going on, so synchronising many “regular” timers seems even impossible…