Copying/clearing SDRAM between separate Audio Buffers

Hi everyone!

My last two posts have been to do with a looper that I’m working on, and this one is no different! Thanks to @Takumi_Ogata especially for the help in the past, hoping I might be able to tempt you back for some more assistance?

The looper sketch has grown considerably over the last year or so, but the issue I’m facing at the moment is specific to clearing Audio Buffers. As you’ll see below, the first loop is recorded into bufL, and then we have a distinction to make. A toggle switch corresponds to the bools stack and flatten. if(stack), all subsequent loops are recorded to bufR. if(flatten), mixBuffer() sends the previously recorded bufR loop to bufL, and clears it to accept new audio.

if(rec && first){
  WriteBuffer_L(in, i);
}
if(rec && !first) {
  rectime++;
  if(stack) {
  WriteBuffer_R(in, i);
  full = true;
  }
  if(flatten) {
    if(!cont) {
      mixBuffer(posL, posR);
    }
      WriteBuffer_R(in, i);
  }
  if(undo){
    for (int i = 0; i < mod; i++) {
      bufR[i] = 0;
      bufU[i] = 0;
        }
    undo = false;
    full = false;
      }
}

With the three called functions being

  void mixBuffer(int posR, int posL) {
    bufL[posL] = bufL[posL] + bufR[posR];
    bufR[posR] = 0;
  }

  void WriteBuffer_R(float **in, size_t i) {
  bufR[posR] = bufR[posR] + in[0][i];
  }

void WriteBuffer_L(float **in, size_t i) {

  bufL[posL] = bufL[posL] + in[0][i];
  if (first) {
    len++;
  }
}

This is all working as intended, but we’ve identified an issue in flatten when the incoming audio is shorter than the total buffer length. If, for example, mod is set so that the buffer length is 30-seconds, and I record a 5-second loop, only 5-seconds of the previous loop in bufR is overwritten and sent to bufL. When this is eventually summed to Mono, you can’t tell, but it ends up breaking things when undo is involved. Speaking of, undo is obviously using the traditional method of clearing bufR (and bufU, but that’s a separate thing…), but when I try that method to clear bufR in mixBuffer(), the Daisy completely freezes up, and I get a horrible noise on the output…

I hope all of that makes sense! If there’s anything I need to clarify, or any other parts of the code that would help with diagnosing this issue, please let me know.

Cheers!

-Oliver

Hello Oliver! Great to hear from you again and glad that you have been continuing to work on the project.

I wanted to clarify the issue you’re running into. So the main issue is when you record a loop that’s shorter than the buffer length? Specifically, when you hit undo for that short loop, it also undo the entire buffer length worth of loop? So for a 30 second buffer length and you record a 5 second loop, hitting undo will also undo 25 second of the loop that was recorded previous to the 5 second loop? I hope I’m understanding this right.

So it sounds like your overall question is…“How should I go about making a looper in which each loop can be a different length?”

Thanks Takumi!

Correct, the main issue is in recording a loop that’s shorter than the buffer length. This presents an issue with Undo, as you described, but is complicated further by a feature that I’ve implemented (the “Stack” and “Flatten” modes. I’m currently working with three Audio Buffers, bufL (or “Loop” buffer), bufR (or “Record” buffer), and bufU (or “Undo” buffer). bufL and bufR are on the Left and Right Outputs respectively for testing, though the final project is summed to Mono (and bufU is unheard, which I’ll explain a bit later).

From reset, the first Loop is recorded directly to bufL (setting mod, and internally we call this our “sacred” loop). From there, the “Stack”/“Flatten” functionality comes into play.

In “Stack” mode, all subsequent overdubs are recorded to the bufR, stacked on top of one another. If we have recorded three overdubs into bufR, Undo will remove all three, leaving us with the “sacred” loop in bufL.

In “Flatten” mode, once an overdub has been recorded to bufR, all subsequent overdubs will still write to bufR, but the previous audio is instead added to bufL, preserving it from being removed by Undo. This way, only the most recent overdub can be removed. I accomplish this here

  void mixBuffer(int posR, int posL) {
    bufL[posL] = bufL[posL] + bufR[posR];
    bufR[posR] = 0;
  }

This is where the issue with recording as shorter loop comes in. Let’s say, for example, the buffer length is 30 seconds, and I have a 30-second “sacred” loop in bufL, as well as a 30-second overdub in bufR. If I record a 5-second loop, the first 5-seconds of the previous overdub will print to bufL, and bufR will contain the new 5-second loop as well as the remaining 25-seconds of the previous (as you surmised).

In Mono, this is a non-issue by itself, but when you Undo bufR, the 5-seconds of the previous overdub that was printed to bufL remains as an “artifact”. My main efforts in tackling this (unsuccessfully) so far, have been to “force” all overdubs to be the full buffer length (by writing “nothing” to the remaining length of bufR), or changing my mixBuffer() function to copy and clear the loop immediately, rather than how it is currently working (sequentially maybe?).

Being able to record different length loops could be a solution to this problem…

I hope that makes things a bit more clear!

Cheers,

-Oliver

Oh, for what it’s worth, the Undo functionality works similarly to the “Flatten” mode, in that the “Undo Action” (a 1-second footswitch hold) copies bufR to bufU, and clears bufR, done here

if(undo) {
    bufU[posU] = bufU[posU] + bufR[posR];
    bufR[posR] = bufR[posR] * 0;
    full = false;
}
if(!undo && !first){
  bufR[posR] = bufR[posR] + bufU[posU];
  bufU[posU] = bufU[posU] * 0;
  full = true;
  }
  }

This block lived below the modulos in if(play){}. The reverse action allows for a “redo” option. This method is working well, but if there was a way not to have to use a portion of the Audio buffer this way, that’d be great too! :sweat_smile:

Finally, I have a block that clears bufR and bufU is there is Audio in bufU, and the user wants to record something fresh to bufR here

if(undo){
    for (int i = 0; i < mod; i++) {
      bufR[i] = 0;
      bufU[i] = 0;
        }
    undo = false;
    full = false;
      }

This is obviously the “proper” way to clear a buffer, but when I try this in mixBuffer(), everything falls apart… Just thought I’d add this, in case it happens to help :slightly_smiling_face:

Do you have a variable for the length of previously recorded loop that can be undone?
For example, if you only record for 5 second (in our 30 second buffer length example), is there a variable that says that your previous loop that you can undo is 5 seconds long?
I think something like that could be used in your code to say that you only need to undo 5 seconds of the previous loop or something along that line so that rest of 25 second of some other loop is not interfered. I may still not be comprehending your issue 100% :man_bowing:

Sorry Takumi, I don’t think I’ve managed to explain properly. The issue is more to do with how bufR is copied to bufL during record (though in mono, it isn’t apparent until the Undo). I made a little diagram that should hopefully explain what I think it happening.

Loop_issue

Does this help to clarify? BufR is being copied to bufL as a new overdub is being written into bufR, so if the new overdub is shorter than mod (let’s say, 5 seconds of a total 30 seconds), 5 seconds of the previous bufR is copied to bufL, and remains there if/when bufR is cleared.

I hope that makes sense. Please let me know if you’d like to look at any specific parts of the sketch!

Cheers,

-Oliver :slightly_smiling_face: