Home › Forums › Logelloop 6 (English spoken) › Dynamic playback/stretch control of a current Korpus looper
Tagged: "Looper speed settings by macro"
- This topic has 17 replies, 2 voices, and was last updated 3 weeks, 1 day ago by Pacocreative.
-
AuthorPosts
-
8 December 2024 at 14 h 57 min #6638PacocreativeParticipant
Hey,
I’d like to assign midi controllers to playback speed of FX tracks loopers sitting under Korpus.
While Korpus midi controls allow for navigating across loopers and triggering rec/play/fade etc…, I can’t seem to have access to playback speed and mode.
I understand I could control some some properties of each FX track’s looper directly (as shown on image below), but I’d prefer to have controls over the speed of a *currently selected* looper.1/ Do you plan on adding such controls in the future to the midi config UI? This would be handy
2/ I guess it’s doable via macros, but I’m a newbie. Could you suggest a way?Specifically, I’d like to have a way of controlling via midi the following properties of a *currently selected* looper:
⇀ Reverse
⇀ Freeze
⇀ Speed multiplier (midi CC 00-127 mapped to 1-x range)
⇀ Speed up/slowdown (x2 /2)
⇀ Speed reset
⇀ * Mode cycle (tape/extreme stretch etc…Thanks!
8 December 2024 at 16 h 24 min #6640PacocreativeParticipantWhat I’ve managed to do is the following:
CC11 is coming from an expression pedal and is 0-127 range.
1. Created a macro, for now it’s static FX target
InsertSendMessage fx1 1 HTSSpeed $midiv_cc1$
2. Made id available in the project
3. Assigned a macro midi listener for CC11
4. Added a macro triggered by midi CC11But it does not seem to work properly.
When I send CC11 it does not seem to trigger the macro, but sets the variable.
When I click “start” on macro, it sets the HTSSpeed to last changed variable.I’m not sure if that’s the right way of doing that.
When a macro is assigned to a midi trigger, I’d be expecting that the midi CC value be passed to the midi as a param.
I wasn’t able to find something like that in the docs, I only found the setting a listener path that I explored.Still need help with:
⇀ Accessing currently selected Korpus looper in the macro editor
⇀ Passing this looper address to send commands like HTSSpeed etc…
⇀ Properly capturing CC value in macro (assuming there’s a simpler way than a listener)
⇀ How to map $midiv_cc1$ (0-127) to speed range 1-50 for example (I’m having syntax issues)InsertSendMessage fx1 1 HTSSpeed $midiv_cc1$/127*10 Line: 1, Error in command: lexical error At position 44, separator needed after ", but / found instead!
Thanks
8 December 2024 at 17 h 09 min #6641PacocreativeParticipantFigured out the syntax thing, but still, I’m getting a very choppy and messy experience when using the expression pedal.
InsertSendMessage fx1 1 { $midiv_cc1$ / 127 * 100 }
It seems like the fact this macro exists, starts repeating messages and it comes into some midi feedback loop.
That’s something I’ll need help with in terms of the right approach.8 December 2024 at 17 h 35 min #6642PhilippeKeymasterHi,
There are plenty of things in your messages.
I can’t answer everything now.1/
the cc command is to trig the macro.
And yes you should go with a variable to get the cc value. As you’ve done.To scale the variable, you should use :
InsertSendMessage fx1 1 { scale($midiv_cc1$,0,127,0,1) }
for exampleYou will find those expression in the “references guide” (button at the bottom of the macro editor.
2/ You should load Korpus in the data plug-ins page in place of in a track as we made before.
I know there are no tutorials yet about this…
But Korpus is more a data plug-in, a new thing in Logelloop 6.3/ In the macro variable viewer page, you can filter “Korpus” and see the variable $Korpus_data1_rank1_Counter_state$ that reports the current Korpus group.
You can use this variable in you macro.And in the macro you should put :
InsertSendMessage fx[$Korpus_data1_rank1_Counter_state$] 1 SpeedFloat {scale(currentCaseOptionValueFloat(0), 0, 127, -20, 20)}assuming Korpus is in data 1 rank 1…
4/ you may organize a loop in your macro, like this :
DoRepeat
InsertSendMessage fx[$Korpus_data1_rank1_Counter_state$] 1 SpeedFloat {scale(currentCaseOptionValueFloat(0), 0, 127, -20, 20)}
Sleep 50
While trueThen, as soon as the macro is running, the speed will be updated every 50 ms.
Please, note that I didn’t test those macro, so maybe there are some typos…
This topic is interesting, I will answer more later, and perhaps I will make a tutos.
Best,
Philippe8 December 2024 at 17 h 50 min #6643PacocreativeParticipantThanks for your help on this. Very helpful.
I’m still not sure of a preferred approach:Approach 1
Use Macro Variable Listener to continuously update CC variable
Trigger macro once, but have it looping let’s say in 50ms as you suggestedApproach 2
Use Macro Variable Listener to continuously update CC variable
Use a Macro midi trigger on the same CC as the variable
Wrap the macro inDoOnce InsertSendMessage fx1 1 HTSSpeed { round ($midiv_cc1$ / 127 * 100) } EndDoOnce
I am not sure which one is a better way given the overall architecture.
Still, I’m having a performance issue due to some internal midi flooding.
When there’s no listener defined, my expression control works nice and clean:
I push the pedal, it immediately prints sweeping values to the console. The messages are clean and ordered:Midi input: ctlIn 30 7 16 "(value ctl channel)" Midi input: ctlIn 31 7 16 "(value ctl channel)" Midi input: ctlIn 33 7 16 "(value ctl channel)" Midi input: ctlIn 34 7 16 "(value ctl channel)" Midi input: ctlIn 35 7 16 "(value ctl channel)" Midi input: ctlIn 36 7 16 "(value ctl channel)" Midi input: ctlIn 37 7 16 "(value ctl channel)" Midi input: ctlIn 38 7 16 "(value ctl channel)"
When a listener is defined, it seems there’s a sort of feedback or flood triggered and the console is lagging for a few seconds processing queued messages.
You can see that the values start jumping erratically,Midi input: ctlIn 1 11 16 "(value ctl channel)" Midi input: ctlIn 67 11 16 "(value ctl channel)" Midi input: ctlIn 66 11 16 "(value ctl channel)" Midi input: ctlIn 69 11 16 "(value ctl channel)" Midi input: ctlIn 74 11 16 "(value ctl channel)" Midi input: ctlIn 2 11 16 "(value ctl channel)" Midi input: ctlIn 73 11 16 "(value ctl channel)" Midi input: ctlIn 47 11 16 "(value ctl channel)" Midi input: ctlIn 56 11 16 "(value ctl channel)"
Any ideas?
8 December 2024 at 18 h 00 min #6645PhilippeKeymasterI think the best approach should be something else.
I will think to it.For the midi issue, I wonder if this is the looper itsel taht is sending some midi value to the midi device and they come back to Logelloop… I don’t know.
8 December 2024 at 18 h 36 min #6646PacocreativeParticipantYeah, you’re right that none of these approaches is ideal.
The reason being there’s just one continuously stored “global” speed (coming from CC) that’s redistributed to each slot as we cycle through them.
If the macro is running in a loop, it’ll adjust the speed of each looper we jump to with the global value.
We could try working around it by implementing additional change listener in macro, but that’s cumbersome and not an ideal design anyways.I guess a more ideal approach would be to expose direct parameter in midi automations, as we have for stop/overdub etc… but I understand that takes effort on your side.
8 December 2024 at 18 h 58 min #6647PacocreativeParticipantHere’s my temporary solution:
1/ Unplugged midi-in cable from FCB1010, this prevents flooding and midi feedback.
But it seems that there’s something inside LL that does send midi signals out on the macro variable listeners.
In other midi commands configurations there’s a “Note Out” checkbox that prevents this, which I keep unticked.
Probably that’s something you may want to look into.2/ Following this fix:
– Set macro CC variable listener on CC11
– Set midi macro trigger on CC11
– write macro as DoOnceDoOnce Declare int $k_index$ = $Korpus_data1_rank1_Counter_state$ InsertSendMessage fx[$k_index$] 1 HTSSpeed {scale($midiv_cc1$, 0, 127, 100, 1)} EndDoOnce
I could not get the following to work:
InsertSendMessage fx[$k_index$] 1 HTSSpeed {scale(currentCaseOptionValueFloat(0), 0, 127, 100, 1)}
Are you trying to say that “$currentCaseOptionValueFloat(0)” in a midi-triggered macro should return the original midi CC value?
I haven’t found much documentation on using this. But it returns 0.0 float.8 December 2024 at 19 h 54 min #6648PhilippeKeymaster1/
If you put :
DoOnce
Declare int $k_index$ = $Korpus_data1_rank1_Counter_state$
InsertSendMessage fx[$k_index$] 1 HTSSpeed {scale($midiv_cc1$, 0, 127, 100, 1)}
EndDoOnceit is the same as you put
Declare int $k_index$ = $Korpus_data1_rank1_Counter_state$
InsertSendMessage fx[$k_index$] 1 HTSSpeed {scale($midiv_cc1$, 0, 127, 100, 1)}DoOnce is not useful.
2/
You should not trig the macro with your expression pedal, because you will trig to many time the macro and that will create some issues in Logelloop.
Please use a macro loop or something else instead.
3/
I made a mistake, when writing :DoRepeat
InsertSendMessage fx[$Korpus_data1_rank1_Counter_state$] 1 SpeedFloat {scale($midiv_cc1$, 0, 127, -20, 20)}
Sleep 50
While trueI wanted to write :
DoRepeat
InsertSendMessage fx[$Korpus_data1_rank1_Counter_state$] 1 SpeedFloat {scale(currentCaseOptionValueFloat(0), 0, 127, -20, 20)}
Sleep 50
While trueSorry
8 December 2024 at 20 h 31 min #6649PacocreativeParticipantMind explaining what “currentCaseOptionValueFloat(0)” refers to in this context?
And is there a way of achieving something like this:
$Looper_fx[$someindex$]_rank1_HTSFreeze_state$I’m currently using an ugly way:
IfThen { $k_index$ == 1 } Do Set $c_freeze$ = $Looper_fx1_rank1_HTSFreeze_state$
IfThen { $k_index$ == 2 } Do Set $c_freeze$ = $Looper_fx2_rank1_HTSFreeze_state$
IfThen { $k_index$ == 3 } Do Set $c_freeze$ = $Looper_fx3_rank1_HTSFreeze_state$
IfThen { $k_index$ == 4 } Do Set $c_freeze$ = $Looper_fx4_rank1_HTSFreeze_state$I know the array trick works at the end of the expression, but not to dynamically create a variable reference.
What language are we using for these macros in general? I’d prefer to save you from questions I can find answers to.Thank you so much.
8 December 2024 at 20 h 40 min #6650PhilippeKeymasterMind explaining what “currentCaseOptionValueFloat(0)” refers to in this context?
This is used in Matrix and Matrix arranger that are data plug-ins that use macro.
And is there a way of achieving something like this:
$Looper_fx[$someindex$]_rank1_HTSFreeze_state$Not currently, but yes, that would be great and it is on the to do list !
I’m currently using an ugly way:
IfThen { $k_index$ == 1 } Do Set $c_freeze$ = $Looper_fx1_rank1_HTSFreeze_state$
IfThen { $k_index$ == 2 } Do Set $c_freeze$ = $Looper_fx2_rank1_HTSFreeze_state$
IfThen { $k_index$ == 3 } Do Set $c_freeze$ = $Looper_fx3_rank1_HTSFreeze_state$
IfThen { $k_index$ == 4 } Do Set $c_freeze$ = $Looper_fx4_rank1_HTSFreeze_state$Not so ugly as the index solution isn’t crurrently working ! 😊
What language are we using for these macros in general? I’d prefer to save you from questions I can find answers to.
This a native language, but many things are the same as they would be in Java…
8 December 2024 at 23 h 30 min #6651PacocreativeParticipantLooks like I managed to implement most of the features I wanted for the modular looper via macros.
The only issue remaining is figuring out a better approach to handling continuous/dense midi messages coming from the pedal.
I did not use the looped macro, as it would mess up speed on counter change, unless I work on smart workarounds.
For now, the triggered macro in a non-looped version does not seem to choke the system nor cause any bad behavior.I’m sharing my macros below, in case someone finds them useful.
* All these macros assume Korpus running as a Data plugin on 1/1
* All these macros assume modular loopers running on fx channels 1-4
* All these macros add functionality for a *currently selected* looper in Korpus
* I use all of these macros mapped to midi CC commandsMacro#1 – ModLoop Play Mode
Switch current looper between Tape Mode and Extreme Time StretchDeclare int $k_index$ = $Korpus_data1_rank1_Counter_state$ Declare int $p_mode$ = 0 //PlayerMode 0 ⇀ Tape //PlayerMode 2 ⇀ Stretch IfThen { $k_index$ == 1 } Do Set $p_mode$ = $Looper_fx1_rank1_PlayerMode_state$ IfThen { $k_index$ == 2 } Do Set $p_mode$ = $Looper_fx2_rank1_PlayerMode_state$ IfThen { $k_index$ == 3 } Do Set $p_mode$ = $Looper_fx3_rank1_PlayerMode_state$ IfThen { $k_index$ == 4 } Do Set $p_mode$ = $Looper_fx4_rank1_PlayerMode_state$ InsertSendMessage fx[$k_index$] 1 PlayerMode { $p_mode$ == 0 ? 2 : 0 }
Macro#2 – ModLoop Reverse
Reverse current mod looper direction// Find current looper index Declare int $k_index$ = $Korpus_data1_rank1_Counter_state$ Declare int $p_mode$ = 0 Declare float $c_speed$ = 1 // Get current player mode // PlayerMode 0 ⇀ Tape // PlayerMode 2 ⇀ Stretch IfThen { $k_index$ == 1 } Do $p_mode$ = $Looper_fx1_rank1_PlayerMode_state$ IfThen { $k_index$ == 2 } Do $p_mode$ = $Looper_fx2_rank1_PlayerMode_state$ IfThen { $k_index$ == 3 } Do $p_mode$ = $Looper_fx3_rank1_PlayerMode_state$ IfThen { $k_index$ == 4 } Do $p_mode$ = $Looper_fx4_rank1_PlayerMode_state$ If {$p_mode$ != 2} // Only change direction if we're NOT in Extreme Stretch mode IfThen { $Korpus_data1_rank1_Counter_state$ == 1 } Do $c_speed$ = $Looper_fx1_rank1_SpeedFloat_state$ IfThen { $Korpus_data1_rank1_Counter_state$ == 2 } Do $c_speed$ = $Looper_fx2_rank1_SpeedFloat_state$ IfThen { $Korpus_data1_rank1_Counter_state$ == 3 } Do $c_speed$ = $Looper_fx3_rank1_SpeedFloat_state$ IfThen { $Korpus_data1_rank1_Counter_state$ == 4 } Do $c_speed$ = $Looper_fx4_rank1_SpeedFloat_state$ InsertSendMessage fx[$Korpus_data1_rank1_Counter_state$] 1 SpeedFloat {$c_speed$ * -1} EndIf
Macro#3 – ModLoop Speed Reset
Resets current looper speed to 1InsertSendMessage fx[$Korpus_data1_rank1_Counter_state$] 1 SpeedFloat 1
Macro#4 – ModLoop Speed Set
Set current mod looper speed to CC1 Variable.
Freezes on max slowdown in Extreme Stretch
# This macro assumes that speed is set via expression pedal (for example) and stored in CC variable #1
# Requires setting both macro trigger and midi CC listener
# It works in both modes – Extreme Stretch and standard Tape mode.
# In extreme Stretch mode, when reaching minimum speed, it will switch to freeze
# It’s NOT following Philippe’s recommendations, so use at your own risk// Find current looper index Declare int $k_index$ = $Korpus_data1_rank1_Counter_state$ Declare float $spd_min1$ = 0.25 // Edit this to set min speed in Standard modes Declare float $spd_max1$ = 4 // Edit this to set max speed in Extreme Stretch Mode Declare float $spd_max2$ = 25 // Edit this to set max speed in Extreme Stretch Mode Declare int $frz$ = 0 // Looper will freeze when reaching max speed Declare float $spd$ = 1 // Get current player mode // PlayerMode 0 ⇀ Tape // PlayerMode 2 ⇀ Stretch Declare int $p_mode$ = 0 IfThen { $k_index$ == 1 } Do $p_mode$ = $Looper_fx1_rank1_PlayerMode_state$ IfThen { $k_index$ == 2 } Do $p_mode$ = $Looper_fx2_rank1_PlayerMode_state$ IfThen { $k_index$ == 3 } Do $p_mode$ = $Looper_fx3_rank1_PlayerMode_state$ IfThen { $k_index$ == 4 } Do $p_mode$ = $Looper_fx4_rank1_PlayerMode_state$ // Depending on the mode set speeds If {$p_mode$ == 2} // Extreme Time Strech $spd$ = {scale($midiv_cc1$, 3, 126, $spd_max2$, 1)} IfThen {$spd$ > $spd_max2$ * 0.95} Do $frz$ = 1 InsertSendMessage fx[$k_index$] 1 HTSFreeze $frz$ InsertSendMessage fx[$k_index$] 1 HTSSpeed $spd$ Else // Tape or similar // Calculate target speed $spd$ = {scale($midiv_cc1$, 3, 126, $spd_min1$, $spd_max1$)} // Check current speed to see if it's reversed or not IfThen { $Korpus_data1_rank1_Counter_state$ == 1 } Do $c_speed$ = $Looper_fx1_rank1_SpeedFloat_state$ IfThen { $Korpus_data1_rank1_Counter_state$ == 2 } Do $c_speed$ = $Looper_fx2_rank1_SpeedFloat_state$ IfThen { $Korpus_data1_rank1_Counter_state$ == 3 } Do $c_speed$ = $Looper_fx3_rank1_SpeedFloat_state$ IfThen { $Korpus_data1_rank1_Counter_state$ == 4 } Do $c_speed$ = $Looper_fx4_rank1_SpeedFloat_state$ // Reverse if current speed was negative IfThen { $c_speed$ < 0 } Do $spd$ = { $spd$ * -1 } // Set Speed InsertSendMessage fx[$Korpus_data1_rank1_Counter_state$] 1 SpeedFloat $spd$ EndIf
9 December 2024 at 7 h 35 min #6656PhilippeKeymasterHi Patrick,
So, yesterday morning, you wrote :I guess it’s doable via macros, but I’m a newbie. Could you suggest a way?
And yesterday evening you posted four perfect macros…
Yes, I think these macros will be useful for many users.Thank you for the great work !
Best,
Philippe9 December 2024 at 12 h 14 min #6657PacocreativeParticipantMerci, c’est rien.
Despite being a ‘homemade’ tool, it’s a robustly designed platform, and incredibly versatile. That’s why it’s easy to learn.Moving forward, I’m thinking about handling some more automations via macros, and I wonder if the following architecture would make sense for more complex cases?
Have one central looped macro running at 25fps/20ms that would actually have access to all recently received midi signals, and it would process multiple behaviors dependent on many mini signals from one central program. I wonder if this makes sense in terms of optimizing performance, versus using multiple listeners and multiple loops? Or, rather LL is written in such way, that multiple looped macros are not a performance strain?
Wonder if exposing received midi in a more centralized way would be doable on your side?
I guess with 8 available listeners I’ll be soon out of slots 😉9 December 2024 at 18 h 50 min #6658PacocreativeParticipantHere’s a version 2 of the Speed Changer
# requires 2 expression pedals or similar signals on CC1 and CC2 variable
# CC2 adjusts speed in tape mode and in ETS mode
# CC1 allows to scrub through the sample in ETS freeze mode
# Triggered once, and working in a loop, so a proper implementation
# If looper is in freeze mode, CC1 allows to dynamically scrub through the buffer
$ if speed is lifted up from freeze, or is in any of the tape modes, it ignores CC1
# It does not mess up on looper change, as I implemented change listeners, so unless you start messing with CC1V CC2V, it does not automatically alter current looper settingsHell yeah!
// Find current looper index Declare int $k_index$ = $Korpus_data1_rank1_Counter_state$ Declare float $spd_min1$ = 0.25 // Edit this to set min speed in Standard modes Declare float $spd_max1$ = 4 // Edit this to set max speed in Extreme Stretch Mode Declare float $spd_max2$ = 25 // Edit this to set max speed in Extreme Stretch Mode Declare int $p_mode$ = 0 // Play mode Declare float $p_pos$ = 0 // ETS Freeze mode position scrubbing Declare float $p_pos_last$ = 0 Declare int $frz$ = 0 // Looper will freeze when reaching max speed Declare float $spd$ = 1 Declare float $bufsize$ = 0 // Buffer length in samples Declare float $midiv_cc1_last$ = $midiv_cc1$ Declare float $midiv_cc2_last$ = $midiv_cc2$ DoRepeat // Get current player mode // PlayerMode 0 ⇀ Tape // PlayerMode 2 ⇀ Stretch $k_index$ = $Korpus_data1_rank1_Counter_state$ IfThen { $k_index$ == 1 } Do $p_mode$ = $Looper_fx1_rank1_PlayerMode_state$ IfThen { $k_index$ == 2 } Do $p_mode$ = $Looper_fx2_rank1_PlayerMode_state$ IfThen { $k_index$ == 3 } Do $p_mode$ = $Looper_fx3_rank1_PlayerMode_state$ IfThen { $k_index$ == 4 } Do $p_mode$ = $Looper_fx4_rank1_PlayerMode_state$ IfThen { $k_index$ == 1 } Do $bufsize$ = $Looper_fx1_rank1_current_length$ IfThen { $k_index$ == 2 } Do $bufsize$ = $Looper_fx2_rank1_current_length$ IfThen { $k_index$ == 3 } Do $bufsize$ = $Looper_fx3_rank1_current_length$ IfThen { $k_index$ == 4 } Do $bufsize$ = $Looper_fx4_rank1_current_length$ // If { $p_mode$ == 2 and $midiv_cc1$ == $midiv_cc1_last$ } // Message "speed no change" // EndIf // Freeze player // Set speeds in ETS Mode If { $p_mode$ == 2 and $midiv_cc1$ != $midiv_cc1_last$ } $spd$ = {scale($midiv_cc1$, 3, 126, $spd_max2$, 1)} $frz$ = {$spd$ > ($spd_max2$ * 0.95) ? 1 : 0 } InsertSendMessage fx[$k_index$] 1 HTSSpeed $spd$ InsertSendMessage fx[$k_index$] 1 HTSFreeze $frz$ $midiv_cc1_last$ = $midiv_cc1$ EndIf // Store last position if playing If { $p_mode$ == 2 and $frz$ == 0 and $midiv_cc1$ != $midiv_cc1_last$} IfThen { $k_index$ == 1 } Do $p_pos_last$ = $Looper_fx1_rank1_Playing_head_position_samples$ IfThen { $k_index$ == 2 } Do $p_pos_last$ = $Looper_fx2_rank1_Playing_head_position_samples$ IfThen { $k_index$ == 3 } Do $p_pos_last$ = $Looper_fx3_rank1_Playing_head_position_samples$ IfThen { $k_index$ == 4 } Do $p_pos_last$ = $Looper_fx4_rank1_Playing_head_position_samples$ $midiv_cc2_last$ = $midiv_cc2$ EndIf // Position scrubbing via midiv CC2 only on pedal change If { $p_mode$ == 2 and $frz$ == 1 and $midiv_cc2$ != $midiv_cc2_last$} $p_pos$ = {scale($midiv_cc2$, 0, 127, 0, $bufsize$)} InsertController fx[$k_index$] 1 4 = $p_pos$ EndIf // Tape or similar If {$p_mode$ != 2 and $midiv_cc1$ != $midiv_cc1_last$} // Calculate target speed $spd$ = {scale($midiv_cc1$, 3, 126, $spd_min1$, $spd_max1$)} // Check current speed to see if it's reversed or not IfThen { $Korpus_data1_rank1_Counter_state$ == 1 } Do $c_speed$ = $Looper_fx1_rank1_SpeedFloat_state$ IfThen { $Korpus_data1_rank1_Counter_state$ == 2 } Do $c_speed$ = $Looper_fx2_rank1_SpeedFloat_state$ IfThen { $Korpus_data1_rank1_Counter_state$ == 3 } Do $c_speed$ = $Looper_fx3_rank1_SpeedFloat_state$ IfThen { $Korpus_data1_rank1_Counter_state$ == 4 } Do $c_speed$ = $Looper_fx4_rank1_SpeedFloat_state$ // Reverse if current speed was negative IfThen { $c_speed$ < 0 } Do $spd$ = { $spd$ * -1 } // Set Speed InsertSendMessage fx[$Korpus_data1_rank1_Counter_state$] 1 SpeedFloat $spd$ $midiv_cc1_last$ = $midiv_cc1$ EndIf Sleep 50 While true
-
AuthorPosts
- You must be logged in to reply to this topic.