New Voxglitch module: Minimal Looper

yes, but only 40 samples. Sure it’s “bad”, I just wanted to soften this because i don’t know if doing that 60 times per second makes a difference.

BTW: one could eliminate both problems I mentioned with a circular ring buffer. the module could dump each sample into it, the widget could pull it out… This would be much faster, and would be thread safe if the ring buffer is atomic.

Oh, I did not see that… I have to look closer…
Really good advice for the ring buffer though, I’m using dsp::RingBuffer in the Rack SDK all the time for sending messages from dsp to the GUI thread :+1:

1 Like

Great ideas! Each frame, I’m only pushing 1 value on to a std::deque …

std::deque<float> waveform_array;

This, in effect, acts like a ring buffer to my knowledge? It is shifting the array behind the scenes? Or is it more like a linked list? This is beyond my knowledge of C++ and any feedback is appreciated!

The widget is looping through the ring buffer to draw each line on the panel.

That’s really interesting about the different threads. I can probably fix that pretty easily. Thanks for pointing that out.

Just for completeness, here’s the code:

struct LooperWaveformDisplay : TransparentWidget
{
  Looper *module;
  std::deque<float> waveform_array;

  void draw(const DrawArgs &args) override
  {
    const auto vg = args.vg;

    // Save the drawing context to restore later
    nvgSave(vg);

    // Debugging code for draw area, which often has to be set experimentally
    /*
    nvgBeginPath(vg);
    nvgRect(vg, 0, 0, DRAW_AREA_WIDTH, DRAW_AREA_HEIGHT);
    nvgFillColor(vg, nvgRGBA(120, 20, 20, 100));
    nvgFill(vg);
    */

    if(module)
    {
      waveform_array.push_front(module->left_audio);

      if(waveform_array.size() > 40) waveform_array.pop_back();

      for (unsigned int i = 0; i < waveform_array.size(); i++)
      {
        nvgBeginPath(vg);
        nvgStrokeWidth(vg, 3);
        nvgStrokeColor(vg, nvgRGBA(97, 143, 170, 200));
        nvgMoveTo(vg, (DRAW_AREA_WIDTH / 2), i * 4.3);
        nvgLineTo(vg, (DRAW_AREA_WIDTH / 2) + (DRAW_AREA_WIDTH * waveform_array[i]), i * 4.3);
        nvgStroke(vg);
      }
    }

    nvgRestore(vg);
  }

It depends on the implementation but as far as I know you should consider the structures in std like std::deque not thread-safe.

yeah, in general two threads should not modify the same std container, although the docs will be more complete info. Yes, deque is more like what you want. I think it’s often implemented as a linked list of vectors so pushing individual items is probably slower than putting them in a ring buffer. But, again, this could all be premature optimization - don’t know if it matters.

1 Like

Just a quick note: Only the widget code is accessing the deque, not the main thread, so I’m not too worried about any mischief. :space_invader:

The widget runs on the main thread, the module runs on one of the audio threads. mischief will definitely happen. Whether you can tolerate that or not is up to you and what you are doing with it. if it will crash, don’t do it. If it will make your UI flicker every now and then, maybe it’s ok.

Oh… flickering UI! Ha ha ha. Yep, I’ve seen that for sure. Thanks for solving that mystery. Ok… I’ll swing back and revisit that portion of the code. Again, thanks for all of your help!

Quick update: I’ve patched a bug which was preventing samples from loading on Macs. The update has been submitted to the library and should roll out fairly soon.

Big fan of the WAV bank module!

1 Like