Trying in vain to get a decent Swing functionality in Ableton (or, alternatively, auto-groove-apply) which is applied AUTOMATICALLY on live recorded clips
So I'm tearing my hair out at Ableton Live's total lack of standard Swing (aka "Shuffle") feature. And since what I do is live looping stuff (see https://twitch.tv/zapandersson if you are a bored) this needs to happen automatically the moment the clips has been recorded. There is no time for manual interaction. It has to work just like swing/shuffle on any other sequencer I've ever used..... I can't fathom how to do it, and it seems such a glaring omission in Ableton Live....!!
After a while I found the live_set.swing_amount property, but even though this is documented as applying both to record quantize and calls to the quantize function, the former seems not to be true. I've managed to make a max patch (with some javascript) to set the property, but nothing happens when I record.
So I've tried to figure out how to observe the creation / recording of a clip, and then quantize it myself from code (or even better - apply a groove).
But I'm lost ... I can't get it to work.... help?
Auto-applying a groove is such a common request, I'm surprised such a Max for Live patch doesn't already exist.
Or.... what obvious thing am I missing?
Help?
/Z
(P.S. I'm a software engineer, so it's really the unfamiliarity with how to talk to the live objects and get messages from them I seem to be missing, is there a good starter tutorial for using the javascript stuff inside Max for Live to Get Stuff Done(tm)) :)
Hi Zap, I want to put this feature into a device maybe with more live looping hacks. Another fail seems to be that "Record Quantization" is always applied at 100% which doesn't allow to keep some of your own groove if you apply it. And I can't find anything in the API docs to assign / apply grooves to clips programmatically.
Here is a Javascript tutorial about modifying clip notes. It explains the basics and shows good object-oriented programming.
http://compusition.com/writings/js-live-api
But surely, someone has already done this? It's one of the most extreme oversigts in Live - that you can't get shuffle in the record quantize (or have a default groove assigned, would work too - even better actually)?
Please tell me some Smart Guy or Girl has done this?
/Z
If I could figure out how to observe the end of recording in a clip (which I havn't figure out yet) I could call "quantize" on it..... but.... how?
:(
I have the end of recording detection working but I want it to work with existing clips which are overdubbed, too. My plan is to take a snapshot of the notes when recording/overdub starts and ends. Then diff out the new notes and only apply quantization to them. When you only need this for new recorded clips you could go with swing_amount and clip.quantize when recording stops. cheers
You have a code snippet/example for observing end-of-recording?
Zap, I tried to extract the important part. It starts at the level of a clipslot (test with first slot on first track).
Okay this is super frustrating. I have something that ALMOST works.
I couldn't make it work using only Max for Live's nodes, so I resorted to some Javascript. And it DID work. However, the moment I've used it for a minute or two, Abeltons UI slugs down insanely. Like.... right clicking something takes 10 second to make the menu pop up.
Check this live stream I did where I was supposed to demo my success - but it ended up mostly a failure:
https://www.twitch.tv/videos/743564308
At the start I manage to record something with my auto-swing-apply script. But you can even tell that when I punch the buttons to trigger clips, the lights changing on the buttons is behind by several secodns (sorry I seem to have independently screwed up the audio/video sync).
At first I thought it was observing things that broke, but after doing the stream, it turns out if I took that out, and only left the code that applied swing to the clip under the cursor when I turned a knob, that STILL caused the sluggishness. So just triggering three/four quantizations from the knob, and Ableton grinds to a halt.
So after this failed-but-almost-successful result I tried yet again to replicate what I did in JavaScript with max-for-live nodes, and I managed to make the "apply swing to the selected clip if you turn a knob" to work WITHOUT the sluggishness. SUCCESS ... except.... *anything* I try to trigger that manually, I *always* run into the problem where it says "You can't do this from a notification, you must defer the message" and no matter how much crap I defer, it still says it.
I'm about to pull my hair out. It is working - yet not. ARGH :(
(At 14 minutes into that stream I have a bit of a rant about how broken this is in Abelton)
Hi Zap, yes I can understand your rant. I don't want to to promise to much before it's fully tested but I have something working for new and overdubbed clips. Although, I need to check how it scales to a 16+ tracks session. We need to connect.
Awesome, if you can make it work.
What I did in mine, was only care about the "live_set view detail_clip".
That may not be enough for everyone (e.g. people maybe recording multiple things at once) but for my use case it would be enough.
Here is one thing I did that works.
Basically, it gives you a "swing knob". Record something, and then touch the knob (ideally by binding a MIDI knob to it), and it will quantize it with that swing level. You can even turn the knob and hear the swing change.
I can't for the life of me have it trigger on the recording ending tho. I have a Jacascript snippet that can detect recording ending - nice - but having it trigger this has been a huge no-go for me.... :(
inlets = 3;
var liveObject;
// Observe detail_clip
function bang()
{
post("Listener installed");
post();
liveObject = new LiveAPI(cb, "live_set view");
liveObject.property = "detail_clip";
}
var doReact = 1;
function msg_int(i)
{
doReact = i;
}
function msg_float(f)
{
var set = new LiveAPI("live_set");
set.set("swing_amount", f);
}
// Flag if the last time we ran was recording
var was_recorded = 0;
function cb(arx)
{
post("in callback");
var t = new LiveAPI("live_set view detail_clip");
var s = new LiveAPI("live_set");
post("In callbackerer");
if (t.id > 0 && doReact)
{
if (t.get("is_recording") == 1)
{
post("Recording start");
post();
was_recorded = 1;
}
else
{
if (was_recorded == 1)
{
was_recorded = 0;
post("End-of-recording trigger fired");
post();
outlet(0, 1/*s.get("swing_amount") * 127.0 */);
}
}
}
}
/Z