TTimer understand how to use it.

Hello to everyones, am tryng to implement TTimer functions in my code, but i have some trouble, can anyone help me to do this ?

This is the link of the functions am talking about, VCV Rack API: rack::dsp::TTimer< T > Struct Template Reference.

My idea is to use getTime() to assign the actual time to a variable, then use reset() to reset this time , so in this manner i know te time passed from the start and the end of an event. but probably using it as-is is the wrong manner, can anyone help me to understand what is the righ manner ?

and didn’t understand well; the function process() is a function to add a time to the current time ?

many thanks to all who want to help me!!!

The timer is driven by process to accumulate time. It doesn’t call the os time functions. Call it from your Module::process override, passing args.sampleTime.

This means it has the granularity of the current sample rate. If you need more precision, system:: has functions that use the most accurate timing available on the OS.

many thanks for the reply, but i can’t understand how to apply your advice, am try to develop a function who emulate a peak detector (envelope follower) so i have this formula Vout=Vo * e^-(t/rc) and t mean the time passed from the istant when Vin< Vpeak to the time when the formulas was be computed, so i need to calculate a delta_t. for you what is the best solution to do this ?

Others here are far better qualified to answer DSP-related questions than I am, so I can’t answer the “best solution” part of your question. But in terms of TTimer usage:

The TTimer class is an extremely simple accumulator. process returns the elapsed time since the last reset. So reset it when Vin < Vpeak, and process returns the delta since that reset.

The deltaTime argument to TTimer::process is args.sampleTime passed to your module’s Module::process override.

Roughed out, the usage would be:

    void MyModule::process(const ProcessArgs& args)
    {
    ...
         if (vIn < vPeak) {
            this->envelopeTimer.reset();
        }
        float dt = this->envelopeTimer.process(args.sampleTime);
    ...
    }

I have no idea if the single-precision float sample time granularity is sufficient for your calculation (or how you are determining what Vpeak is), but you can try it and see what your results are and adjust from there.

am asking for “the best subjective solution” so i desnt pretend to have the best of all, i havn’t code alot with C++ so surely you will find a better solution than mine.

Anyway I tried to implement it, but I’m probably doing something wrong. envelopeTimer, is it a variable created by you or does it refer to the library function? and if envelopeTimer is a created variable, what is the manner to implement, and then, to initialize the timer ? and why you use “this->” , is it because you have write the code as a function?

If you are doing exponential decay from a peak you can use the derivative to just decay it and never call exp at all. That is since dv/dt = -v/rc you can instead do something like

V = v - v * decay V = std::max(v, in)

Where decay is probably 1/(rc sample rate)

No need to keep track of time. The decay will exp itself through this integrator approach.

Hope that helps.

By (rc samplerate) you mean rcsample time? Actually, the sampling rate is like a time reference because of the time passed between one sample and another, so effectively i can use it, but, can’t it be less precise than use an stopwatch?

By this->, I’m telling you that the variable is a member of your Module subclass. No initialization is needed – it has a default constructor. I suggest you spend a some time reading the source of other working modules to get familiar with how they’re structured. Fundamental is a pretty good place to start.

Of course baconpaul has shown you a much simpler and faster way to compute your values.

You had said you want to implement V = V0 e(-t/rc) right? So rc is the rc from your equation. and then it is just solving the integrated form of that. You convert time to samples using sample rate.

You can’t get a more accurate timer than once per sample since you are only called once per sample.

I’m not sure what you mean by a ‘stopwatch’ - if you mean system clock then it will be less accurate and less repeatable - especially in offline renders where a system clock would just be wrong. If you mean an accumulator then it is exactly the same thing.

rechecking the formula then I realized the obviousness of it, it was just that it wasn’t clear to me initially.

however, actually exploiting the sampling frequency could be the best solution, also to avoid overloading the processor unnecessarily. When I started researching this thing I found something that implemented the sampling frequency, but now I can no longer remember or find where was it talked about, do you have a link to pass me on?

aniways yes, with stopwathc i mean a time meter who can give me the length of a time period.

Your Module::process override is passed the sample rate (frequency) and sample time in the args structure:

	struct ProcessArgs {
		/** The current sample rate in Hz. */
		float sampleRate;
		/** The timestep of process() in seconds.
		Defined by `1 / sampleRate`.
		*/
		float sampleTime;
		/** Number of audio samples since the Engine's first sample. */
		int64_t frame;
	};

The TTimer class is a stopwatch (time accumulator) for samples. Typical stopwatches for code provide “real” time (modulo clock drift). As mentioned already in the thread, you want the computations in terms of samples, not real time.

yes i know and obviously a “realtime clock” so a continuous timeflow in digital domanin is impossible, but i think people can get time by sample frequency or by an internal clock function this is why initially am looking for a “direct time acumulator” than an accumulator based on sampling rate.

aniways this is the code i have write but probably there is a mistake, because when i turn the “sensitivity” knob the output signal change and start to decay slowly and it act like it doesn’t recive any signal. p.s: R have a range from 0 to 1000, is it the right way to implement getSampleRate() function ? (i have omitted the #include section).


void process(const ProcessArgs& args) override {

		float Vin = inputs[INPUT].getVoltage();
		float signal;
        float time_unit = APP->engine->getSampleRate();
		float Vout;
		
		float R = params[SENS].getValue();

	if(Vin > Ref){
			Ref = Vin;
			Vout = Vin;
			timer = 0;	
		}else{
			Vout = Ref * exp(-1*(timer/R));
            timer = timer + (1/time_unit);	
		}
                outputs[OUTPUT].setVoltage(Vout);
               }

The sample rate is passed in the args, so no need for APP->engine->getSampleRate() which carries the overhead of two indirections and a function call (that internally is another indirection) to get what is likely the identical value. Those multiple levels of indirection are likely to be cache misses. And if you read the comments on the code I posted which is the definition of the ProcessArgs reference passed to the function, you see that 1/sampleRate has already been computed for you (it’s args.sampleTime), so no reason to spend CPU computing that in your code either (1/timeUnit).

In fact, your code timer = timer + (1/time_unit) is what time = someTTimer.process(args.samleTime) computes, and your timer = 0 is exactly what someTTimer.reset() does.

So, you’ve just reinvented the TTimer class in inline code. Which means that (getting back to the title of this thread) that you do actually understand TTimer! :slight_smile:

all my code was born from the fact that I can’t implement TTimer correctly… but how you formulated it probably clarified my ideas (although I wouldn’t rely on it much, since I’m quite dumb)

I just tried to implement the functions you gave me, but it still gives me the error, “some” in "someTTimer… is it a placeholder or should it be written as reported?

then passing the TTimer functions (args.process()) as arguments in the same way gives me an error. To give you an idea I’ve attached the error messages.

src/peakdetector.cpp: In member function 'virtual void Peakdetector::process(const rack::engine::Module::ProcessArgs&)':
src/peakdetector.cpp:49:40: error: 'const struct rack::engine::Module::ProcessArgs' has no member named 'getSampleTime'; did you mean 'sampleTime'?
   49 |                 float time_unit = args.getSampleTime();
      |                                        ^~~~~~~~~~~~~
      |                                        sampleTime
src/peakdetector.cpp:71:32: error: 'someTTimer' was not declared in this scope
   71 |                         time = someTTimer.process(args.sampleTime)
      |                                ^~~~~~~~~~

To address these two errors:

the error at line 49: you should use float time_unit = args.sampleTime;

the error at line 71: you need to declare a variable of type TTimer called someTTimer and that variable declaration should be within your peakdetector class (or struct) but not within a function. You could declare it as: TTimer someTTimer;

I am not at my development environment, so I can’t 100% guarantee what I’m saying.

so to the line 49 i have to omit parentesis and to the line 71 i have to declare a variable someTTtimer what is type is TTtimer ? p.s. can i call the variable “xyzTTtimer” or am forced to call it someTTimer ?

My point was: you can write this entire thing without calling exp once if you just solve the integrated form

Store your current v as a state and use the derivative of v to update it

Exp is a very very expensive function compared to subtract and once you add polyphony is much harder to vectorize

Now i have understand the totaly of what you said. aniways, the response of a derivative function is a little bit different than an exponential decay, and in this case i prefeer to use the exponential, even because it’s a simple module with the only function to etract the envelope. what seems strange to me and what I can’t find a solution to is the fact that it seems to compute the function, but it doesn’t receive the audio.

line 49 you have to omit the parenthesis and you have to use the correct name which is sampleTime and not getSampleTime which is what you had in your error.

sampleTime is the name of a member variable of the ProcessArgs class, and getSampleTime is the name of a member function of the Engine class which is why one needs parenthesis and the other doesn’t

You do not need to call your TTimer variable someTTimer, you can call it xyzTimer or any other name.

Many thanks, can the TTimer variable simply be called xyz or TTimer must be added to the name because the compiler requires it?