Live Looping

lightningkite's icon

Hello all,

I'm about to build a live audio looping patch with control from a midi foot controller. I was just wondering if there are any helpful objects members could tell me about or any information about how the patch is best to be structured. Also, if anyone knows of any examples on the web that would be extremely helpful. Just need a bit of information to get going really.

Thanks,

Rob

brendan mccloskey's icon

You don't mention what your level of experience with Max is, so forgive me if i assume no experience. For audio in/out you'll need [adc~], [dac~] and [gain~], for midi in, you'll need either [ctlin] and [notein~] or [midiin] and [midiparse]; for looping you'll need [groove~] or [play~] plus a [buffer~] to read from; you can record into a [buffer~] using [poke~] plus [count~] (i've attached an example of this). As far as specific patches, you could get 20 different version from this forum, each as viable as the other but achieving different things, so i won't include a full patch; you will have much more fun and learn much more building a looper that's perfect for you.

Max Patch
Copy patch and select New From Clipboard in Max.

fill a buffer example patch

Brendan

ps the object helpfiles have little examples of how to use them, and the MSP tutorials 13-18 are particularly relevant here; remember to let us all see your results

lightningkite's icon

Thanks Brendan, that patch is a real help. I am quite a novice, not a total beginner but no where near proficient yet. I'll be working my way through those tutorials tonight.

Is there any way to use the initial recording length (samples or ms) to determine the length of subsequent recordings. I'm still deciding whether to use a number of separate buffers (possibly 8 or 10) for the subsequent layers or whether to simply feed playback into my record element again. The latter would be simpler but has less control at the end.

Thanks again.

lightningkite's icon

Actually would it be possible for someone to look at my recording set up as I can seem to get anything to record into the buffer as of yet. I've cleaned up my patch and put a few notes in there to show whats going on. I can't figure out how to set up the record~ object. Followed the help file but it hasn't worked.

{
"patcher" : {
"fileversion" : 1,
"rect" : [ 0.0, 44.0, 628.0, 641.0 ],
"bglocked" : 0,
"defrect" : [ 0.0, 44.0, 628.0, 641.0 ],
"openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
"openinpresentation" : 0,
"default_fontsize" : 12.0,
"default_fontface" : 0,
"default_fontname" : "Arial",
"gridonopen" : 0,
"gridsize" : [ 15.0, 15.0 ],
"gridsnaponopen" : 0,
"toolbarvisible" : 1,
"boxanimatetime" : 200,
"imprint" : 0,
"enablehscroll" : 1,
"enablevscroll" : 1,
"devicewidth" : 0.0,
"boxes" : [ {
"box" : {
"maxclass" : "comment",
"text" : "compiled playback information for play~. once the sample has played through the buffer bangs and triggers the play~ again. looping",
"linecount" : 6,
"patching_rect" : [ 374.0, 370.0, 157.0, 89.0 ],
"id" : "obj-27",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 0
}

}
, {
"box" : {
"maxclass" : "comment",
"text" : "timer used to capture inital recording left and output as new buffer size for subsequent loops",
"linecount" : 4,
"patching_rect" : [ 455.0, 108.0, 160.0, 62.0 ],
"id" : "obj-25",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 0
}

}
, {
"box" : {
"maxclass" : "comment",
"text" : "sets initial buffer size",
"patching_rect" : [ 228.0, 46.0, 154.0, 20.0 ],
"id" : "obj-22",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 0
}

}
, {
"box" : {
"maxclass" : "comment",
"text" : "Record Start/Stop",
"patching_rect" : [ 99.0, 14.0, 107.0, 20.0 ],
"id" : "obj-18",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 0
}

}
, {
"box" : {
"maxclass" : "comment",
"text" : "BASIC LIVE LOOPER",
"patching_rect" : [ 283.0, 6.0, 150.0, 20.0 ],
"id" : "obj-16",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 0
}

}
, {
"box" : {
"maxclass" : "newobj",
"text" : "loadbang",
"patching_rect" : [ 169.0, 45.0, 60.0, 20.0 ],
"id" : "obj-29",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "bang" ]
}

}
, {
"box" : {
"maxclass" : "message",
"text" : "60000",
"patching_rect" : [ 203.0, 92.0, 44.0, 18.0 ],
"id" : "obj-21",
"fontname" : "Arial",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "" ]
}

}
, {
"box" : {
"maxclass" : "message",
"text" : "0",
"patching_rect" : [ 169.0, 92.0, 32.5, 18.0 ],
"id" : "obj-20",
"fontname" : "Arial",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "" ]
}

}
, {
"box" : {
"maxclass" : "newobj",
"text" : "loadmess 127",
"patching_rect" : [ 74.0, 233.0, 85.0, 20.0 ],
"id" : "obj-10",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "" ]
}

}
, {
"box" : {
"maxclass" : "meter~",
"patching_rect" : [ 289.0, 542.0, 34.0, 90.0 ],
"id" : "obj-50",
"numinlets" : 1,
"numoutlets" : 1,
"outlettype" : [ "float" ]
}

}
, {
"box" : {
"maxclass" : "message",
"text" : "3408",
"patching_rect" : [ 306.0, 402.0, 50.0, 18.0 ],
"id" : "obj-48",
"fontname" : "Arial",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "" ]
}

}
, {
"box" : {
"maxclass" : "newobj",
"text" : "pack",
"patching_rect" : [ 289.0, 438.0, 36.0, 20.0 ],
"id" : "obj-47",
"fontname" : "Arial",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "" ]
}

}
, {
"box" : {
"maxclass" : "message",
"text" : "3408",
"patching_rect" : [ 289.0, 321.0, 50.0, 18.0 ],
"id" : "obj-45",
"fontname" : "Arial",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "" ]
}

}
, {
"box" : {
"maxclass" : "newobj",
"text" : "prepend 0\,",
"patching_rect" : [ 289.0, 469.0, 71.0, 20.0 ],
"id" : "obj-35",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "" ]
}

}
, {
"box" : {
"maxclass" : "number",
"patching_rect" : [ 378.0, 167.0, 71.0, 20.0 ],
"id" : "obj-23",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 2,
"outlettype" : [ "int", "bang" ]
}

}
, {
"box" : {
"maxclass" : "newobj",
"text" : "select 0",
"patching_rect" : [ 433.0, 82.0, 52.0, 20.0 ],
"id" : "obj-3",
"fontname" : "Arial",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 2,
"outlettype" : [ "bang", "" ]
}

}
, {
"box" : {
"maxclass" : "newobj",
"text" : "select 1",
"patching_rect" : [ 378.0, 83.0, 52.0, 20.0 ],
"id" : "obj-2",
"fontname" : "Arial",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 2,
"outlettype" : [ "bang", "" ]
}

}
, {
"box" : {
"maxclass" : "newobj",
"text" : "timer",
"patching_rect" : [ 378.0, 124.0, 37.0, 20.0 ],
"id" : "obj-1",
"fontname" : "Arial",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 2,
"outlettype" : [ "float", "" ]
}

}
, {
"box" : {
"maxclass" : "newobj",
"text" : "prepend size",
"patching_rect" : [ 378.0, 234.0, 79.0, 20.0 ],
"id" : "obj-4",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "" ]
}

}
, {
"box" : {
"maxclass" : "newobj",
"text" : "play~ rec1 1",
"patching_rect" : [ 289.0, 504.0, 76.0, 20.0 ],
"id" : "obj-13",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "signal" ]
}

}
, {
"box" : {
"maxclass" : "toggle",
"patching_rect" : [ 134.0, 36.0, 20.0, 20.0 ],
"id" : "obj-12",
"numinlets" : 1,
"numoutlets" : 1,
"outlettype" : [ "int" ]
}

}
, {
"box" : {
"maxclass" : "newobj",
"text" : "record~ rec1 1",
"patching_rect" : [ 134.0, 125.0, 88.0, 20.0 ],
"id" : "obj-11",
"fontname" : "Arial",
"numinlets" : 3,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "signal" ]
}

}
, {
"box" : {
"maxclass" : "newobj",
"text" : "buffer~ rec1 60000 1",
"patching_rect" : [ 378.0, 270.0, 121.0, 20.0 ],
"id" : "obj-9",
"fontname" : "Arial",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 2,
"outlettype" : [ "float", "bang" ]
}

}
, {
"box" : {
"maxclass" : "meter~",
"patching_rect" : [ 36.0, 503.0, 21.0, 85.0 ],
"id" : "obj-8",
"numinlets" : 1,
"numoutlets" : 1,
"outlettype" : [ "float" ]
}

}
, {
"box" : {
"maxclass" : "ezdac~",
"patching_rect" : [ 101.0, 546.0, 45.0, 45.0 ],
"id" : "obj-7",
"numinlets" : 2,
"numoutlets" : 0
}

}
, {
"box" : {
"maxclass" : "gain~",
"patching_rect" : [ 48.0, 275.0, 108.0, 140.0 ],
"id" : "obj-6",
"numinlets" : 2,
"numoutlets" : 2,
"outlettype" : [ "signal", "int" ]
}

}
, {
"box" : {
"maxclass" : "ezadc~",
"patching_rect" : [ 48.0, 38.0, 45.0, 45.0 ],
"id" : "obj-5",
"numinlets" : 1,
"numoutlets" : 2,
"outlettype" : [ "signal", "signal" ]
}

}
],
"lines" : [ {
"patchline" : {
"source" : [ "obj-23", 0 ],
"destination" : [ "obj-48", 1 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-10", 0 ],
"destination" : [ "obj-6", 0 ],
"hidden" : 0,
"midpoints" : [ 83.5, 262.0, 57.5, 262.0 ]
}

}
, {
"patchline" : {
"source" : [ "obj-5", 0 ],
"destination" : [ "obj-6", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-13", 0 ],
"destination" : [ "obj-50", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-35", 0 ],
"destination" : [ "obj-13", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-47", 0 ],
"destination" : [ "obj-35", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-23", 0 ],
"destination" : [ "obj-45", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-23", 0 ],
"destination" : [ "obj-48", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-23", 0 ],
"destination" : [ "obj-45", 1 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-48", 0 ],
"destination" : [ "obj-47", 1 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-45", 0 ],
"destination" : [ "obj-47", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-13", 0 ],
"destination" : [ "obj-7", 0 ],
"hidden" : 0,
"midpoints" : [ 298.5, 535.0, 156.0, 535.0, 156.0, 531.0, 110.5, 531.0 ]
}

}
, {
"patchline" : {
"source" : [ "obj-13", 0 ],
"destination" : [ "obj-7", 1 ],
"hidden" : 0,
"midpoints" : [ 298.5, 528.0, 156.0, 528.0, 156.0, 543.0, 136.5, 543.0 ]
}

}
, {
"patchline" : {
"source" : [ "obj-4", 0 ],
"destination" : [ "obj-9", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-23", 0 ],
"destination" : [ "obj-4", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-12", 0 ],
"destination" : [ "obj-3", 0 ],
"hidden" : 0,
"midpoints" : [ 143.5, 73.0, 442.5, 73.0 ]
}

}
, {
"patchline" : {
"source" : [ "obj-12", 0 ],
"destination" : [ "obj-2", 0 ],
"hidden" : 0,
"midpoints" : [ 143.5, 73.0, 387.5, 73.0 ]
}

}
, {
"patchline" : {
"source" : [ "obj-1", 0 ],
"destination" : [ "obj-23", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-3", 0 ],
"destination" : [ "obj-1", 1 ],
"hidden" : 0,
"midpoints" : [ 442.5, 120.0, 405.5, 120.0 ]
}

}
, {
"patchline" : {
"source" : [ "obj-2", 0 ],
"destination" : [ "obj-1", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-12", 0 ],
"destination" : [ "obj-11", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-6", 0 ],
"destination" : [ "obj-8", 0 ],
"hidden" : 0,
"midpoints" : [ 57.5, 489.0, 45.5, 489.0 ]
}

}
, {
"patchline" : {
"source" : [ "obj-6", 0 ],
"destination" : [ "obj-7", 1 ],
"hidden" : 0,
"midpoints" : [ 57.5, 489.0, 136.5, 489.0 ]
}

}
, {
"patchline" : {
"source" : [ "obj-6", 0 ],
"destination" : [ "obj-7", 0 ],
"hidden" : 0,
"midpoints" : [ 57.5, 489.0, 110.5, 489.0 ]
}

}
, {
"patchline" : {
"source" : [ "obj-5", 0 ],
"destination" : [ "obj-11", 0 ],
"hidden" : 0,
"midpoints" : [ 57.5, 111.0, 143.5, 111.0 ]
}

}
, {
"patchline" : {
"source" : [ "obj-9", 1 ],
"destination" : [ "obj-48", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-9", 1 ],
"destination" : [ "obj-45", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-20", 0 ],
"destination" : [ "obj-11", 1 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-21", 0 ],
"destination" : [ "obj-11", 2 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-29", 0 ],
"destination" : [ "obj-20", 0 ],
"hidden" : 0,
"midpoints" : [ ]
}

}
, {
"patchline" : {
"source" : [ "obj-29", 0 ],
"destination" : [ "obj-21", 0 ],
"hidden" : 0,
"midpoints" : [ 178.5, 82.0, 212.5, 82.0 ]
}

}
]
}

}

Also, how do you post a patch as text in the smaller size like above? The 'Copy Compressed' option in Edit is greyed out for me. Thanks again.

lewis lepton's icon

hmm very strange about it being greyed out. it might be that you did not select [highlight] all the patch parts which need posting, maybe.
the copy compressed does not become active if nothing is selected.

mudang's icon

hi kite!

There are three little misunderstandings in your patch.
Here's why it doesnt work:

1. When sending a "size" message to the buffer~ it (unfortunately) looses its contents. But there isn't any need to resize the buffer. Just play the portion, that got recorded.

2. buffers~'s bang outlet only reports, when reading of a _soundfile_ is finished. That has nothing to do with playing the buffer.

3. play~ always needs an audio signal with the playback position. You probably forgot to insert a [line~] object. In the case of looping, I'd rather use a [phasor~], which will repeat the loop sample-accurate.

I've also added a [selector~] to mute the playback, while recording.

PS: For the "copy compressed" command you need to be in edit-mode and have the objects, you want to copy, selected.

Max Patch
Copy patch and select New From Clipboard in Max.

here you go:

lewis lepton's icon

oooh, actually. just had a thought balloon pop up to help. have you looked at mr. englefeldt's 'almost live' patch? that can be something to build upon for a live looper.

lightningkite's icon

Thanks so much Mudang. That's really helped me out. Think I'm getting to grips with this now. It works and I'm understanding it and building on it so that's great.

And yes you were right, I wasn't actually selecting my objects, simply the text. Lesson learned.

Thanks for the tip Lewis, where can I find that patch? Is it on this site?

brendan mccloskey's icon
lightningkite's icon

Thanks for the link Brenden. After looking at the way this one works which is to a set BPM I've decided to go that way instead.

Here's what I've constructed today. For some reason, when recording stops and playback (and looping) starts it's playing back from the middle of the sample instead of the beginning. It's probably a simple rookie mistake I've made. If anyone has a few seconds free to have a look I would be most grateful.

Max Patch
Copy patch and select New From Clipboard in Max.

Thanks.

Tim Lloyd's icon

It looks like you need to reset phasor~ to 0 every time your recording stops. To do that you just need to send a 0 to its right inlet.

lightningkite's icon

Thanks Tim, that's solved it. Excellent.

lightningkite's icon

I've finished it. Thanks to everyone.

Does anyone know how to save it as a standalone app?

Tim Lloyd's icon

There are many threads on the forum about the ins and outs of this etc. which you should look for.

Basically, you just go to the menu file/build collective/application, include the patcher you want to build into an app, and hit build. Then name it and make sure you select application (not collective) in the drop-down menu.

You can also add a picture to use as the application's icon if you click include file, choose a pic, and then in the build script, change the word "include" to "appicon", then finish the build process. This works for me on OSX, not sure about windows.

You'll want to search the forum further for info about how to exclude the max window from your app, and disallow resizing of the window etc......check out the thispatcher object.