Forums > MaxMSP

live looping patch help

January 7, 2008 | 5:43 am

Hello, all.
I’m working on a live looping patch for a very specific use. As such, it doesn’t need to be very versatile. I’ve got a good deal of it planned out and feel confident that I can get it to work, however I’m not totally pleased with a particular mechanism of the looping.
Here’s the deal: a friend and I arranged a song by a rather popular electronic artist that we’re planning on performing in the very near future. There’s only two of us playing, and we’ve simplified/altered the arrangement enough that we can pull it off by recording and layering loops of ourselves. I’ve got a counter that goes to 16 (4 bars of 4) keeping track of the whole thing. I’ll be using qlist and a wii controller to move through each step of the arrangement. Basically, when I’m ready to play, I hit a button on the wii remote, and then next time the counter hits 1, it records whatever it is I’m doing. All this works fine so far. The next step is where it gets cumbersome. I’d like the patch to automatically stop recording and play back the loop it just recorded automatically once the counter hits 1 again. As of now I’m doing this with a messy array of gates and select objects. Anyone have any input on how you might efficiently accomplish such a thing? Thanks.


January 7, 2008 | 6:15 am

it depends partially on how you’ve set-up your patch (since there’s more than 1 way to record/playback in Max) but here’s some thoughts based on my personal experiences…

I use [record~] in conjunction with a [buffer~]. One of the handy features of this setup is that the recording automatically stops when the buffer is filled. So, I calculate ahead of time how long my buffer should be (for you, 16 beats at your bpm) then resize the buffer ahead of time. When I start recording at the top of my loop, it automatically stops at the end.

I drive all of my playback with a [phasor~] so i’m in msp signal land to keep timing accurate. I’ve got the phasor reading through my buffer with the [wave~] object. When I start my patch, the buffer is empty so no audio is heard. But as soon as my recording is finished the phasor starts a jumps to the top of my newly-recorded loop and I hear it right away.

Like I said before, there are many ways to tackle this issue. My older experiments in this area used [groove~] objects but everything started and stopped in Max bangs which makes it a bit more complex and less accurate. Since I’ve moved to the phasor~ solution i’ve been much happier.

Hope that helps. If you need more specific advice feel free to post a patch.

/dan

Quote: mopppish wrote on Sun, 06 January 2008 22:43
—————————————————-
> Here’s the deal: a friend and I arranged a song by a rather popular electronic artist that we’re planning on performing in the very near future. There’s only two of us playing, and we’ve simplified/altered the arrangement enough that we can pull it off by recording and layering loops of ourselves. I’ve got a counter that goes to 16 (4 bars of 4) keeping track of the whole thing. I’ll be using qlist and a wii controller to move through each step of the arrangement. Basically, when I’m ready to play, I hit a button on the wii remote, and then next time the counter hits 1, it records whatever it is I’m doing. All this works fine so far. The next step is where it gets cumbersome. I’d like the patch to automatically stop recording and play back the loop it just recorded automatically once the counter hits 1 again. As of now I’m doing this with a messy array of gates and select objects. Anyone have any input on how you might efficiently accomplish such a thing? Thanks.
—————————————————-


January 7, 2008 | 6:20 am

Thanks for the reply.
I should have added that I’m driving the counter object with tempo. This was done so that one of us could hear a "click track" in headphones when recording the first loop so that the timing would be solid as it built up.
I’m only now finding out about timing issues outside of the signal domain… :(
Although, now that I think of it, there were times that I thought it might be an issue (uneven clicks).
I will probably post a patch once I’ve cleaned it up a bit. Right now it’s a conceptual-stage-ish mess.



jml
January 7, 2008 | 6:23 am

what dan has said about signal-rate is *accurate*.

for now, here’s some info re: control-rate:
counter has a flag output for when it returns to its max/min values. please consult the help file for this…

you’ll want to find a solution to end the recording via the recording module- for example, if you know how long the recording will be, then you delay/record/etc for that amount of time and there could be a notification given at the end of that.

here’s a shot in the dark:
#P window setfont "Sans Serif" 9.;
#P window linecount 2;
#P comment 148 192 140 196617 < amount of times 16 has been counted fully;
#P button 20 337 15 0;
#P window linecount 1;
#P newex 45 365 31 196617 t 1 b;
#P message 20 243 14 196617 0;
#P toggle 45 46 15 0;
#P newex 45 73 58 196617 metro 100;
#P newex 45 210 47 196617 sel 2;
#P window linecount 2;
#P comment 63 327 86 196617 indicates you’ve reached the "end";
#P window linecount 1;
#P comment 123 244 140 196617 < notification of your trigger;
#P button 109 241 15 0;
#P button 45 103 48 0;
#P comment 63 314 86 196617 < output of event;
#P number 111 192 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P newex 45 283 50 196617 del 1000;
#N counter 0 1 16;
#X flags 0 0;
#P newobj 45 163 77 196617 counter 0 1 16;
#P comment 103 285 68 196617 < time of rec;
#P connect 9 0 12 0;
#P connect 2 0 14 0;
#P fasten 13 0 11 0 50 398 274 398 274 36 50 36;
#P fasten 12 0 11 0 25 268 13 268 13 43 50 43;
#P connect 11 0 10 0;
#P connect 10 0 5 0;
#P connect 5 0 1 0;
#P connect 1 3 9 0;
#P connect 9 0 2 0;
#P connect 2 0 13 0;
#P fasten 13 1 1 2 71 387 265 387 265 156 82 156;
#P connect 9 0 6 0;
#P connect 1 3 3 0;
#P window clipboard copycount 16;

please post an example for more help.

jl


January 10, 2008 | 10:40 pm

OK! I’ve got a good start now. I finally found the rate~ object. I will post an example once I get a few more things in place. In the meantime, any reasons to use play~ vs. wavetable~ (driven by phasor~)? Just curious. Using play~ for now.
Thanks!


January 10, 2008 | 10:42 pm

Sorry, I meant to say wave~ (or I suppose index~).


January 10, 2008 | 10:58 pm

I made a really simple loop machine using a tapin/tapout delay with a
feedback of 1 (no higher than 1!), and gates to control input/output. Simply
putting a signal into it will put it into an infinite loop, looping at the
specified delay time.
I had four tapin/tapout loops, with a gate~ selecting where the audio signal
was being sent. You also have the advantage of being able to send a clear
message to tapin and that loop is wiped.

On 10/01/2008, Eric Sheffield wrote:
>
>
> OK! I’ve got a good start now. I finally found the rate~ object. I will
> post an example once I get a few more things in place. In the meantime, any
> reasons to use play~ vs. wavetable~ (driven by phasor~)? Just
> curious. Using play~ for now.
> Thanks!
>


January 10, 2008 | 11:11 pm

Hi ive been wondering about connecting external devices to max msp. how do i do this using the serial object such as using a usb voip phone?

Conor Higgins

Get the next generation of Free Windows Live Services

http://get.live.com


January 10, 2008 | 11:18 pm

OK, how’s this?

#P window setfont "Sans Serif" 9.;
#P window linecount 1;
#P newex 273 379 48 196617 loadbang;
#P user ezadc~ 265 270 309 303 0;
#P message 273 398 79 196617 set firstsound1;
#P user ezdac~ 471 354 515 387 0;
#P message 356 306 14 196617 1;
#P message 411 335 14 196617 0;
#P button 390 335 15 0;
#P newex 356 333 29 196617 gate;
#P newex 356 356 101 196617 record~ firstsound1;
#P newex 460 314 90 196617 play~ firstsound1;
#P message 183 207 30 196617 read;
#P message 91 207 30 196617 read;
#P newex 91 224 91 196617 buffer~ pulse 375;
#P newex 183 224 116 196617 buffer~ downbeat 3000;
#P user waveform~ 273 415 200 74 3 9;
#W mode select;
#W mouseoutput continuous;
#W unit ms;
#W grid 1000.;
#W ticks 0;
#W labels 1;
#W vlabels 0;
#W vticks 1;
#W bpm 120. 4.;
#W frgb 33 0 0;
#W brgb 60 178 173;
#W rgb2 0 95 255;
#W rgb3 0 0 0;
#W rgb4 0 0 0;
#W rgb5 190 137 255;
#W rgb6 100 100 100;
#W rgb7 100 100 100;
#N vpatcher 179 244 410 454;
#P window setfont "Sans Serif" 9.;
#P message 60 140 14 196617 1;
#N comlet 1 out (rec on);
#P outlet 60 157 15 0;
#N comlet sig in (from rate);
#P inlet 60 43 15 0;
#P button 60 123 15 0;
#P window linecount 1;
#P newex 60 102 34 196617 edge~;
#P newex 60 81 43 196617 ==~ -1.;
#P newex 60 60 101 196617 change~;
#B color 5;
#P comment 76 124 24 196617 dec;
#P connect 5 0 1 0;
#P connect 1 0 2 0;
#P connect 2 0 3 0;
#P connect 3 0 4 0;
#P connect 4 0 7 0;
#P connect 7 0 6 0;
#P pop;
#P newobj 375 314 60 196617 p detective;
#P newex 549 243 56 196617 *~ 12000;
#P newex 474 243 50 196617 *~ 6000;
#P newex 396 223 50 196617 *~ 3000;
#P newex 301 224 65 196617 rate~ 0.125;
#P newex 549 224 44 196617 rate~ 4;
#P newex 474 224 44 196617 rate~ 2;
#P newex 396 130 106 196617 phasor~ 0.333333;
#P connect 13 0 19 0;
#P connect 4 0 7 0;
#P connect 4 0 13 0;
#P connect 22 0 20 0;
#P fasten 17 0 15 0 416 352 350 352 350 330 361 330;
#P connect 21 0 14 0;
#P connect 20 0 8 0;
#P connect 15 0 14 0;
#P fasten 15 0 16 0 361 352 387 352 387 332 395 332;
#P connect 18 0 15 0;
#P fasten 16 0 17 0 395 352 407 352 407 332 416 332;
#P connect 7 0 15 1;
#P connect 11 0 10 0;
#P connect 12 0 9 0;
#P connect 0 0 3 0;
#P connect 0 0 4 0;
#P connect 1 0 5 0;
#P connect 0 0 1 0;
#P connect 2 0 6 0;
#P connect 0 0 2 0;
#P window clipboard copycount 23;

This would account for one "track" of our arrangement.
The buffers "pulse" and "downbeat" provide our metronome (quarter notes with a louder downbeat on the first of every group of eight), which will be in headphones to make sure we know where "one" is. I will load samples into these buffers ahead of time.
The 1 above the gate is my way of saying "OK, I’m ready to play on the next ‘one’ (beginning of the loop)". In practice, this would be activated with a wii remote while I’m standing behind whatever instrument it is I play next. Record~ is activated the next time the phasor~ resets (done from within the "detective" subpatch). The gate also feeds back into itself, so that as soon as the record message passes through, the gate is shut to prevent any further recording. The play~ object should automatically line up for the next "one" of the pattern.
The different rates are to accommodate different loop lengths (8 beat, 16 beat, and 32 beat).
Any thoughts?
Thanks a million!


January 10, 2008 | 11:22 pm

Sorry, me again.
I should add that the buffers will be play/record enabled by stepping through a qlist as per the arrangement we’ve come up with (once all tracks are accounted for).


January 11, 2008 | 4:15 am

A couple of thoughts on your patch…

- You need to create a [buffer~] called "firstsound1". Both the record~ and play~ objects need to reference an existing buffer.

- I’m confused by some of the msp math that you’re doing. I take it that your loop time is 3 seconds – and that’s why you’re multiplying by 3000? I guess this answers one of the questions of the advantage of using wave~ over play~. wave~ accepts a signal that goes from 0. to 1. to drive playback (which is exactly what you’ll get out of the phasor directly). One advantage of that is it makes your patch more reusable. This patch will work for loops that are 3 seconds exactly, but it won’t adapt to other lengths without a lot of legwork on your part.

- I wouldn’t recommend leaving the order of operations to chance coming out of that gate. I’d always recommend using a trigger object so that you can make sure that you start the recording first, then close the gate. It might not cause any problems in this particular case, but in larger patches this kind of questionable order of operations can cause pesky bugs.

Below is a simple example patch that I pulled out of a larger project I’m working on. It demonstrates a method of recording loops in real-time using the wave~ object for playback. One advantage of this patch is that it dynamically resizes the loop length based on tempo, number of measures, and number of beats per measure…. which makes it more useful to me in a variety of contexts. When you toggle the "Record Arm", it will automatically start recording at the top of the loop, stop at the end of the loop, and instantly start to playback.

Hope this clears things up a little. Let me know if you have any questions.

/dan

max v2;
#N vpatcher 128 44 981 686;
#P window setfont "Sans Serif" 9.;
#P comment 30 52 100 196617 dlehrich@gmail.com;
#P toggle 270 352 25 0;
#P user ezadc~ 411 499 455 532 0;
#P user ezdac~ 698 424 742 457 0;
#P newex 706 367 101 196617 wave~ MyRecording;
#P window setfont "Sans Serif" 10.;
#P newex 342 224 231 196618 expr 1000/((60000 / $f1) * ($i2 * $i3));
#P newex 342 247 128 196618 phasor~ 0.;
#P window linecount 2;
#P comment 369 190 110 196618 Calculate phasor frequency;
#P window setfont "Sans Serif" 9.;
#P window linecount 1;
#P newex 364 577 107 196617 record~ MyRecording;
#P objectname $0Record;
#P newex 342 440 32 196617 t 0 1;
#P newex 342 386 32 196617 t 0 b;
#P newex 342 415 32 196617 gate;
#P newex 342 355 36 196617 edge~;
#N vpatcher 447 147 1047 547;
#N comlet (sig) == 1 at top of loop;
#P outlet 238 284 15 0;
#N comlet Phaser in;
#P inlet 238 155 15 0;
#P window setfont "Sans Serif" 9.;
#P newex 238 246 39 196617 >~ 0.5;
#P newex 238 223 27 196617 !-~;
#P newex 255 199 58 196617 delay~ 1 1;
#P connect 3 0 1 0;
#P connect 1 0 2 0;
#P connect 2 0 4 0;
#P connect 3 0 0 0;
#P connect 0 0 1 1;
#P pop;
#P newobj 342 333 87 196617 p LoopEdgeDetect;
#P window linecount 2;
#P comment 433 334 102 196617 Put out an impulse at the top of the loop;
#P comment 377 383 235 196617 when the gate is open (record enabled) , the trigger will start the recording and then close the gate;
#P window linecount 1;
#P message 14 439 43 196617 size $1;
#P newex 14 485 105 196617 buffer~ MyRecording;
#P window linecount 2;
#P comment 72 402 161 196617 Calculate the length of the buffer and send it out if it changes;
#P window linecount 1;
#P comment 456 89 94 196617 Beats per measure;
#P comment 391 89 51 196617 Measures;
#P number 466 106 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P number 399 106 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P flonum 342 106 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P newex 14 408 52 196617 change 0.;
#P newex 342 156 125 196617 pak 80. 4 4;
#P newex 14 384 146 196617 expr (60000/$f1)*($i2*$i3);
#P comment 335 89 38 196617 Tempo;
#P window setfont "Sans Serif" 12.;
#P window linecount 2;
#P comment 350 39 191 196620 Set these first to calculate loop length:;
#P user panel 326 27 231 114;
#X brgb 191 191 191;
#X frgb 0 0 0;
#X border 1;
#X rounded 0;
#X shadow 0;
#X done;
#P window setfont "Sans Serif" 9.;
#P comment 442 543 141 196617 Length of buffer comes in to set the record end point;
#P comment 379 435 132 196617 First start recording , then set "record arm" back to 0;
#P window setfont "Sans Serif" 18.;
#P window linecount 1;
#P comment 215 312 119 196626 Record Arm:;
#P user panel 214 291 120 101;
#X brgb 191 191 191;
#X frgb 0 0 0;
#X border 1;
#X rounded 0;
#X shadow 0;
#X done;
#P window setfont "Sans Serif" 9.;
#P comment 31 36 100 196617 by Dan Lehrich;
#P connect 9 0 8 0;
#P connect 8 0 10 0;
#P connect 10 0 18 0;
#P connect 18 0 17 0;
#P fasten 25 0 33 0 347 475 262 475 262 341 275 341;
#P connect 11 0 9 0;
#P connect 9 0 29 0;
#P connect 29 0 28 0;
#P connect 28 0 21 0;
#P connect 21 0 22 0;
#P connect 22 0 24 0;
#P connect 33 0 23 0;
#P connect 24 0 23 0;
#P connect 23 0 25 0;
#P connect 24 1 23 1;
#P connect 32 0 26 0;
#P connect 25 1 26 0;
#P connect 12 0 9 1;
#P connect 13 0 9 2;
#P connect 10 0 26 2;
#P connect 30 0 31 0;
#P connect 28 0 30 0;
#P connect 30 0 31 1;
#P pop;

Quote: mopppish wrote on Thu, 10 January 2008 16:18
—————————————————-
> OK, how’s this?
>
> #P window setfont "Sans Serif" 9.;
> #P window linecount 1;
> #P newex 273 379 48 196617 loadbang;
> #P user ezadc~ 265 270 309 303 0;
> #P message 273 398 79 196617 set firstsound1;
> #P user ezdac~ 471 354 515 387 0;
> #P message 356 306 14 196617 1;
> #P message 411 335 14 196617 0;
> #P button 390 335 15 0;
> #P newex 356 333 29 196617 gate;
> #P newex 356 356 101 196617 record~ firstsound1;
> #P newex 460 314 90 196617 play~ firstsound1;
> #P message 183 207 30 196617 read;
> #P message 91 207 30 196617 read;
> #P newex 91 224 91 196617 buffer~ pulse 375;
> #P newex 183 224 116 196617 buffer~ downbeat 3000;
> #P user waveform~ 273 415 200 74 3 9;
> #W mode select;
> #W mouseoutput continuous;
> #W unit ms;
> #W grid 1000.;
> #W ticks 0;
> #W labels 1;
> #W vlabels 0;
> #W vticks 1;
> #W bpm 120. 4.;
> #W frgb 33 0 0;
> #W brgb 60 178 173;
> #W rgb2 0 95 255;
> #W rgb3 0 0 0;
> #W rgb4 0 0 0;
> #W rgb5 190 137 255;
> #W rgb6 100 100 100;
> #W rgb7 100 100 100;
> #N vpatcher 179 244 410 454;
> #P window setfont "Sans Serif" 9.;
> #P message 60 140 14 196617 1;
> #N comlet 1 out (rec on);
> #P outlet 60 157 15 0;
> #N comlet sig in (from rate);
> #P inlet 60 43 15 0;
> #P button 60 123 15 0;
> #P window linecount 1;
> #P newex 60 102 34 196617 edge~;
> #P newex 60 81 43 196617 ==~ -1.;
> #P newex 60 60 101 196617 change~;
> #B color 5;
> #P comment 76 124 24 196617 dec;
> #P connect 5 0 1 0;
> #P connect 1 0 2 0;
> #P connect 2 0 3 0;
> #P connect 3 0 4 0;
> #P connect 4 0 7 0;
> #P connect 7 0 6 0;
> #P pop;
> #P newobj 375 314 60 196617 p detective;
> #P newex 549 243 56 196617 *~ 12000;
> #P newex 474 243 50 196617 *~ 6000;
> #P newex 396 223 50 196617 *~ 3000;
> #P newex 301 224 65 196617 rate~ 0.125;
> #P newex 549 224 44 196617 rate~ 4;
> #P newex 474 224 44 196617 rate~ 2;
> #P newex 396 130 106 196617 phasor~ 0.333333;
> #P connect 13 0 19 0;
> #P connect 4 0 7 0;
> #P connect 4 0 13 0;
> #P connect 22 0 20 0;
> #P fasten 17 0 15 0 416 352 350 352 350 330 361 330;
> #P connect 21 0 14 0;
> #P connect 20 0 8 0;
> #P connect 15 0 14 0;
> #P fasten 15 0 16 0 361 352 387 352 387 332 395 332;
> #P connect 18 0 15 0;
> #P fasten 16 0 17 0 395 352 407 352 407 332 416 332;
> #P connect 7 0 15 1;
> #P connect 11 0 10 0;
> #P connect 12 0 9 0;
> #P connect 0 0 3 0;
> #P connect 0 0 4 0;
> #P connect 1 0 5 0;
> #P connect 0 0 1 0;
> #P connect 2 0 6 0;
> #P connect 0 0 2 0;
> #P window clipboard copycount 23;
>
> This would account for one "track" of our arrangement.
> The buffers "pulse" and "downbeat" provide our metronome (quarter notes with a louder downbeat on the first of every group of eight), which will be in headphones to make sure we know where "one" is. I will load samples into these buffers ahead of time.
> The 1 above the gate is my way of saying "OK, I’m ready to play on the next ‘one’ (beginning of the loop)". In practice, this would be activated with a wii remote while I’m standing behind whatever instrument it is I play next. Record~ is activated the next time the phasor~ resets (done from within the "detective" subpatch). The gate also feeds back into itself, so that as soon as the record message passes through, the gate is shut to prevent any further recording. The play~ object should automatically line up for the next "one" of the pattern.
> The different rates are to accommodate different loop lengths (8 beat, 16 beat, and 32 beat).
> Any thoughts?
> Thanks a million!
—————————————————-


January 12, 2008 | 12:32 am

Whoops! The buffers were on a different part of the patch- I just forgot to copy and paste them.
The MSP path is indeed because the loops are 3 seconds (or 6 or 12) long. That’s what 8 beats (or 16 or 32) of 160bpm comes out to exactly.
I didn’t realize that wave~ only required 0-1, so I may give that a try. I was worried about the very specific buffer sizes as we may end up changing the tempo slightly down the road.
Thanks a million for the help!


Viewing 12 posts - 1 through 12 (of 12 total)