Add a freeze button

I am looking for some code lines that allow to ‘freeze’ the pattern.
Ideally also store that ‘snapshot’ so it can later be recalled/scrolled with a rotary switch.
Anyone ideas?
Thanks,
Jacob

Something similar was done for stop motion, check out https://github.com/thijsc/dragonblaze

The pattern or concept could be modified to do what you are looking for.

Combine that with the new UI controls in the beta/preview version for v3.21/v3.22 and you could make a pause, play, next, previous kind of tool for stepping through time.

Thanks a lot Wizard, this goes however too deep for my limited coding skills. I asked Thijs for some help, but so far no reply.

I wouldn’t ask him about it, I only pointed to that repo as an example and starting place. Ask here on the forums! If someone can help they will!

I’ll help myself (unless someone beats me to it) now that I know you are stuck, once I have a bit of spare time, hopefully over the next few days.

Wonderful, thanks a lot

One sort of hack you can do to freeze is just disconnect the data line coming from the pb. Obviously this doesn’t do all of what you want it to do, but it might be useful for others to know.

1 Like

There isn’t a “one size fits all” solution, but if a pattern generates its activity using the time() function, this might work:

/////////////////////////////////////////////////////////////////////////////////
//
//  Pattern code goes here.  Replace "time()" calls with "tick()".
//

//  Demo pattern is @jeff's KITT from https://forum.electromage.com/t/kitt-without-arrays/1219
var tailPct = .6 // length of the tail in 0..1
var t1, shift = 1 - tailPct

export function beforeRender() { t1 = tick(.05) }

function pulse(x) {
  var halfsaw = triangle(x - t1) * square(x - t1, .5)
  return max(0, (halfsaw - shift) / tailPct)
}

export function render(index) {
  pct = index / pixelCount / 2
  v = pulse(pct) + pulse(-pct)
  hsv(0, 1, v * v * v)
}

/////////////////////////////////////////////////////////////////////////////////
//
//  Drop-in replacement for the built-in time() function to enable pausing and scrolling.
//

//  Whether or not the pattern is paused.
export var paused = false; export function sliderPaused(v) { paused = floor(v); }
//  The position of a rotary encoder for scrubbing through past pattern frames.
export var position = 1; export function sliderEncoderPosition(v) { position = v; } 
//  You'll need to provide your own code here to read the rotary encoder position.
export function getEncoderPosition() { return position; }


//  A ring buffer of previous frame time() values.
var maxHistory = pixelCount;  // or whatever, up to the maximum array size.
var tickHistory = array(maxHistory); var bufferSize = 0; var bufferCurrent = 0;
function tick(v) {
  if (!paused) {
    //  Adjust the ring buffer pointers.
    bufferCurrent = (bufferCurrent + 1) % maxHistory;
    if (bufferCurrent > bufferSize) { bufferSize = bufferCurrent; }
    //  Save and return the current timer value.
    tickHistory[bufferCurrent] = time(v);
    return tickHistory[bufferCurrent];
  }
  else {
    //  Return a historical timer value.
    var bufferScroll = (bufferCurrent + floor(bufferSize * getEncoderPosition())) % maxHistory;
    return tickHistory[bufferScroll];
  }
}

A similar approach can work for other types of generative patterns, if the state variables are small enough.

1 Like