Example clock multiplier code?

Hello everyone! Would someone have some code to share that does clock multiplication? In other words, it takes a clock input (processed using a dsp::SchmittTrigger) and outputs subdivisions of the input clock?

For some reason, my code almost does it, but can start to “swing” or “groove”.

Here’s my code:

#include <iostream>
#include <cmath> // for std::floor, std::abs

class ClockModifier
{
private:
  double time_between_clocks_ms = 0.0; // Time between incoming clock pulses in milliseconds
  int division_factor = 1;             // Clock division factor
  int multiplication_factor = 1;       // Clock multiplication factor

  double accumulated_time_ms = 0.0;
  double previous_trigger_output_time = 0.0;
  double previous_clock_time = 0.0;

  unsigned int clock_division_counter = 0;

public:

  // Called when a clock pulse happens
  void clockMultiply(float rate_index, double time_now)
  {
    multiplication_factor = rate_index;

    if (previous_clock_time == 0.0)
    {
      previous_clock_time = time_now;
      return;
    }

    time_between_clocks_ms = time_now - previous_clock_time;
    previous_clock_time = time_now;
  }

  // Should a clock pulse be emitted?
  bool process(double time_now)
  {
    accumulated_time_ms = time_now - previous_trigger_output_time;

    double target_time = time_between_clocks_ms / multiplication_factor;

    if (accumulated_time_ms >= target_time)
    {
      accumulated_time_ms -= target_time;
      previous_trigger_output_time = time_now;
      return true;
    }
    return false;
  }

  bool clockDivide(unsigned int clock_division)
  {
    if(clock_division == 0) clock_division = 1;

    clock_division_counter++;

    if (clock_division_counter >= clock_division)
    {
      clock_division_counter = 0;
      return true; // Emit a new clock pulse
    }
    else
    {
      return false; // Do not emit a new clock pulse
    }
  }

};

Thanks!

1 Like

You might find something useful in the source code for Bogaudio’s RGate module:

Thanks! Hmm… that code is pretty difficult to comprehend though.

Maybe there’s a clearer version out there?

This is pretty to the point. although I know you’ve blocked me in the past for not being nice enough. Oh, and it may have bugs in it - it’s very old. https://github.com/squinkylabs/SquinkyVCV-main/blob/master/sqsrc/clock/ClockMult.h

1 Like

To be honest, I prefer to use Brett’s method (I’m using it since 2017, for my first edition of KlokSpid module), by using doubles instead of floats, and discrete timer-related variables (instead of rack::dsp::ClockDivider class, because it uses float, this may cause timing / “phase” drifts, in particular during long runs, such dozens of minutes, or hours).

Of course, single precision takes less memory and run more quickly than double precision. In fact, It depends your needs.

Single precision float is perfect for voltages, but not for precise timed operations (IMHO).

1 Like

@Squinky Hey, it’s been a long time since I blocked you! Since then, I’ve gotten accustomed to your unusual ways of showing your love. :heart_eyes: :hugs:

It isn’t perfect, but here’s where I’m at so far with my clock modification class:

#include <iostream>
#include <cmath> // for std::floor, std::abs

class ClockModifier
{
private:

  double time_between_clocks_ms = 0.0; // Time between incoming clock pulses in milliseconds

  // Variables for clock multiplication
  double accumulated_time_ms = 0.0;
  double previous_clock_time = 0.0;
  int multiplication_factor = 1;

  // Variables for clock division
  unsigned int clock_division_counter = 0;

public:

  // Called when a in clock multiplication mode and when an incoming clock pulse is received
  void clockMultiply(float rate_index, double time_now)
  {
    multiplication_factor = rate_index;
    time_between_clocks_ms = time_now - previous_clock_time;
    previous_clock_time = time_now;
    accumulated_time_ms = 0.0;
  }

  // In the case of clock multiplication, this function is called every sample
  bool process(double delta_time_ms, double time_now)
  {
    accumulated_time_ms += delta_time_ms;

    double target_time = time_between_clocks_ms / (double) multiplication_factor;

    if (accumulated_time_ms >= target_time)
    {
      accumulated_time_ms = 0.0;
      return true;
    }
    return false;
  }

  // Simple clock division
  bool clockDivide(unsigned int clock_division)
  {
    if(clock_division == 0) clock_division = 1;

    clock_division_counter++;

    if (clock_division_counter >= clock_division)
    {
      clock_division_counter = 0;
      return true; // Emit a new clock pulse
    }
    else
    {
      return false; // Do not emit a new clock pulse
    }
  }

};

A few problems with it are:

  1. It doesn’t help the developer manage a transition between clock division and multiplication.
  2. It would be nice if you could feed it a CV and have it switch automatically between division and multiplication so you don’t need to handle that in your main code.
2 Likes

Hi Brett,

It would be nice if you could feed it a CV and have it switch automatically between division and multiplication so you don’t need to handle that in your main code.

Fortunately, my in-development new clocking module (named KlokSpid MkII) can pick pre-built divider/multiplier from table (they’re exactly 86 pre-defined factors in table, from /16384 to x128 - please see below…) by any free assignable CV source. KlokSpid MkII module embeds 8 versatile CV inputs, so one can be assigned to do this task.

Applied voltage on CV input can be either constant, or may vary in realtime (e.g. by LFO source).

Here’s using CV4 input as “CV Ratio” selector, for output 5:

image

List of available modifier ratios:

On right image, GATE, START, STOP and OFF menu items aren’t ratios, but utility modifiers.

I don’t use class for clock (old-school programming :smiley:), but it works like a charm! Free ratio isn’t allowed, only ratios indicated above, it’s a “developer choice”… :wink:

Partial inspiration from ALM Pamela’s PRO Workout real module (I’ve added some missing factors in original hardware).

2 Likes

That’s fantastic looking! Beautiful work! :star2: My clock multiplier code is a feature included in a more complex module that I’m working on. Can you share – How do you handle the transition between clock division and clock multiplication? Do they all share the same routine, or do you swap between two strategies, like I do?

I’m pretty old-school myself. I still often prefer good old arrays to vectors! :smile:

1 Like

Thank you, Brett for compliment, be sure it’s really appreciated! :ok_hand: :star:

The source code of OhmerPrems is closed (it’s a commercial plugin) despite KlokSpid MkII module and its expander, KX module, are free for everyone, without any form of limitation/restriction. However, you’ll can find closest example from my 2017 KlokSpid (Ohmer plugin)

I’m working seriously on it, I’d like many features are working fine prior to publish a beta. The most heavy part is the “menu system” (invoked every 32 DSP frames, behind a if (args.frame % 32) { condition), either by “touchscreen” and/or continuous encoder+buttons, menu calculations (only when required) mainly to save CPU usage.

Input voltages, expander, output voltages, light management, are plain DSP processing!

Clock multiplication and division shares the same routine, factors for division are 1 / x numbers (/2 is 0.5 factor, x2 is 2.), all as double precision (important for /9091, as example). Except “BPM CV” (where the frequency/tempo is given by 2Hz/V), the CLK input jack (internal, or from KX expander) needs to receive two consecutive pulses to establish the new frequency. While “syncing” (the transport LED is blinking fast, red color), the module continues to work by using “last-known BPM/frequency” strategy during the degraded situation! (IMO, it’s better than nothing).

It’s funny because also I prefer to use arrays instead of vectors, too! :smiley: despite vectors can be resized. KlokSpid MkII is, for me, the exception, because each wavetable (8), are loaded in vectors.

Yep, I’m discovering the world of vectors.

Hi Bret. I’d like to say the division is pretty simple and the multiplication is a bit more tricky.

  1. For division of 2, you simply skip every other trigger.
  2. For multiplication you have to generate you own triggers, so you’re basically running your own clock source internally, and re-syncing it to the incoming triggers.

I had to do this in “Prism/Prince of Perception” since the “End of Cycle” trigger can be divided and multiplied. I’ll include the relevant pieces here.

First of all it’s not 100% exactly your situation, (since Prism/Prince can be either externally clocked or internally clocked (the delay-time is the period of the clock)) but you should be able to lift the pieces that are relevant to you.

When Prism/Prince is in “clocked”-mode, the delay_seconds is calculated by this class:

struct ClockToTime
{
	int _clock_counter = 0;
	bool _clock_state = false;

	float _period_s = 1.0f;
	float _history_0 = 1.0f;
	float _history_1 = 1.0f;

	float update_clock(float clock_state, float sample_time)
	{
		 // pick values that works with both uni- and bi-polar lfo
        const float k_trig_lo = 1.0f;
        const float k_trig_hi = 2.0f; // to be able to use metros headphone output, where 1/4 goes to 5 and the other goes to 2.5

        ++_clock_counter;
        if ( _clock_state )
        {
            if ( clock_state < k_trig_lo )
            {
                _clock_state = false;
            }
        }
        else
        {
            if ( clock_state > k_trig_hi )
            {
                _clock_state = true;
                float period_s = _clock_counter * sample_time;

                // box filter
                _period_s = (_history_0 + _history_1 + period_s) / 3.0f;

                // rotate
                _history_0 = _history_1;
                _history_1 = period_s;

                // restart clock
                _clock_counter = 0;
            }
        }

        return _period_s;
	}
};

The members related to generate the multiplied/divided triggers are:

	int _eoc_ttl = 0;
	int _eoc_ttl_skip_counter = 0; // skip counter

	int _eoc_samples_since_last_fake_eoc = 0; // count up
	int _eoc_samples_since_last_fake_eoc_limit = 0; // limit, recalc sometimes

	int _eoc_skip_mul_number = 0; // 1 to 16, negative is skip, positive is mul, (0 is nop)

The _eoc_ttl is a counter (ttl == time-to-live) for when the out-trigger-signal is high.

When the parameters (delay_seconds) change I calculate _eoc_samples_since_last_fake_eoc_limit :

		if (_eoc_skip_mul_number>0)
		{
			float mul = 1.f + _eoc_skip_mul_number;
			float seconds = delay_seconds / mul;
			_eoc_samples_since_last_fake_eoc_limit = 1 + (int)(seconds * common._last_sr);
		}

Every time the “end-of-cycle” is reached (in your case, a clock-input-trigger is received) I do this:

	void trigger_eoc(const Common& common)
	{
		if (_eoc_skip_mul_number < 0)
		{
			// division-mode (more like skip-mode)
			if (_eoc_ttl_skip_counter>=-_eoc_skip_mul_number)
			{
				_eoc_ttl_skip_counter = 0;
				_eoc_ttl = common._trigger_ttl;
			}
			else
			{
				++_eoc_ttl_skip_counter;
			}
			return;
		}

		if (_eoc_skip_mul_number > 0)
		{
			_eoc_samples_since_last_fake_eoc = 0;
		}

		// regular mode
		_eoc_ttl = common._trigger_ttl;
	}

Every sample I generate new multiplied triggers (I call the m “fake”), and set the output based on the _eoc_ttl :

		// handle multiply-EOC signal
		if (_eoc_skip_mul_number>0)
		{
			++_eoc_samples_since_last_fake_eoc;
			if (_eoc_samples_since_last_fake_eoc >= _eoc_samples_since_last_fake_eoc_limit)
			{
				// reset counter, fixme maybe fixedpoint
				_eoc_samples_since_last_fake_eoc = 0;

				// trigger again...
				_eoc_ttl = common._trigger_ttl;
			}
		}

		// handle EOC
		if (_eoc_ttl > 0)
		{
			--_eoc_ttl;
			module.outputs[EOC_OUTPUT].setVoltage(10.0f);
		}
		else
		{
			module.outputs[EOC_OUTPUT].setVoltage(0.0f);
		}

It ends up being pretty straightforward, but many opportunities for mistakes though.

Hope this helps! Jonas

1 Like

My goodness–this looks great!

1 Like

Thank you! but I’m working hard on this module, at the moment what you see on screen captures are working fine, but they’re many stuffs remaining in development.

1 Like

I really like Graham Wakefield and Gregory Taylor’s phasor division/multiplication algorithm from their book Generating Sound & Organizing Time. I have it implemented in the PhasorDivMult class here:

Specifically, the modulatedSync function is used to make sure that the output phasor remains in sync with the input phasor. Now, you probably aren’t using phasors, but a chunk of the code might be useful for mapping it over to timers:

const float speedScale = multiplier/divider;
const float speedScaleDifference = std::abs((lastSpeedScale - speedScale)/(lastSpeedScale + speedScale));
if(speedScaleDifference > threshold) waitingToSync = true;
lastSpeedScale = speedScale;

So what this is doing is checking for leaps in the combined multiplication/division value. When it clears some proportional threshold, it triggers a boolean waitingToSync. If that’s true:

    bool inputReset = resetDetector.detectSimpleReset(_normalizedPhasorIn);

    float resetPhase = trunc(roundedPhaseDiff) + scaledPhase; 

    float selectedPhase;
    if(inputReset && waitingToSync)
    {
        selectedPhase = resetPhase;
        waitingToSync = false;
    } 

We have a reset detector, but that’s for a phasor. You can just use a Schmitt Trigger to check if the input clock resets. Here, we’re resyncing it to the phase of the incoming phasor, but you can just reset the phase/timer to 0 to match the input clock.

2 Likes

I thought for sure that I had posted my final clock modifier class, but I don’t seem to see it. Anyhow, here it is for reference!

#include <iostream>
#include <cmath> // for std::floor, std::abs
#include <deque>

class ClockModifier
{
private:

  double time_between_clocks_ms = 0.0; // Time between incoming clock pulses in milliseconds

  std::deque<double> timer_targets;

  // Variables for clock multiplication
  double previous_clock_time = 0.0;

  // Variables for clock division
  unsigned int clock_division_counter = 0;

  // Variable for rate factor
  float rate_factor = 1.0f;

public:

  bool clock(double time_now, float rate_factor)
  {
    if(previous_clock_time == 0.0)
    {
      previous_clock_time = time_now;
    }
    else
    {
      time_between_clocks_ms = time_now - previous_clock_time;
      previous_clock_time = time_now;
    }

    this->rate_factor = rate_factor;

    if(rate_factor < 0)
    {
      return clockDivide(static_cast<unsigned int>(std::floor(rate_factor * -1.0f)));
    }

    clockMultiply(static_cast<unsigned int>(std::floor(rate_factor)), time_now);
    return(true);
  }

  // I only call the process function if I'm doing clock multiplication
  bool process(double time_now)
  {
    if(timer_targets.empty()) return false;

    if(time_now >= timer_targets.front())  // Use front() instead of top()
    {
      timer_targets.pop_front();  // Use pop_front() instead of pop()
      return true;
    }
    else
    {
      return false;
    }
  }

  void clockMultiply(unsigned int clock_multiplication, double time_now)
  {
    // Clear the timer_targets deque
    timer_targets.clear();

    // Fill the timer_targets deque with the time between clocks divided by the rate factor
    double interval = time_between_clocks_ms / rate_factor;
    for(int i = 0; i < (rate_factor - 1); i++)
    {
      timer_targets.push_back(interval * (i + 1) + time_now);  // Use push_back() instead of push()
    }
  }

  // Simple clock division
  bool clockDivide(unsigned int clock_division)
  {
    if(clock_division == 0) clock_division = 1;

    clock_division_counter++;

    if (clock_division_counter >= clock_division)
    {
      clock_division_counter = 0;
      return true; // Emit a new clock pulse
    }
    else
    {
      return false; // Do not emit a new clock pulse
    }
  }

};

2 Likes

That looks very nice! I notice you are pushing and poping from a deque while processing. Unless you are very careful that can call malloc from the audio thread, and cause poping and clicking.

It’s one of the things I talk about in my ancient paper: https://github.com/squinkylabs/Demo/blob/main/docs/efficient-plugins.md

4 Likes

Looking very nice!

1 Like

I have to chime in every time @Squinky posts this link: it’s essential reading for anyone interested in Rack development or, frankly, audio software engineering in general. I wish there were a book’s worth of it.

Nice code @clone45! I have a vague sense that one way to avoid malloc() would be to build the required parts of a deque interface on top of a fixed-size circular array (ring) and see if there’s a way to gracefully handle overflow in this application (overflow being for a ring the Ouroborous situation where the ends touch). Some of the guarantees provided by the very general C++ deque implementation are probably not needed here.

2 Likes

Thanks. Yeah, some kind of simple ring buffer would be just the ticket here. Since it’s only accessed by one thread it doesn’t need to be “atomic” of anything.

1 Like