Gig Performer's "Built In" Looper! (And how to use it)

I have not found any way to do so. I switched to Enso Looper because of that.

I sort of found a workaround for that.

I added a new parameter to the scriptlet for turning overdubbing on or off. If overdub is off, then when pressing the Rec/Over/Play button it stops the player first and then emulates another press on the Rec/Over/Play button.

It doesn’t work awfully reliably though. Sometimes it works as intended, sometimes it just stops the player. It works better when the TH-U plugin window is open for some reason.

I haven’t had time to debug it properly yet, I’m not a frequent looper user, but I do plan to look into it and do some cleanup. Like, I have absolutely no use for 2 looper “channels”, so I want to completely remove that part of code, parameters I don’t need etc. Maybe I’ll also remove the overdub functionality completely as I don’t ever need it.

Anyway, here’s the modified code in case someone wants to take a look.

// Declare various kinds of parameters
var
   /*a : Continuous Parameter = 0.5 // A parameter that can range from 0.0 to 1.0 initialized to 0.5
   b : Subrange Parameter 1..16 = 4 // A parameter that can range from 1 to 16 initialized to 4
   c : Discrete Parameter "Yesterday", "Today", "Tomorrow" = "Today" // Discrete named parameters*/
   
   looper_1_recordplay ("Ch1 Record/Dub/Play") : Parameter = 0.0
   looper_1_stop ("Ch1 Stop"): Parameter = 0.0
   looper_1_undo ("Ch1 Undo"): Parameter = 0.0
   looper_1_clear ("Ch1 Clear"): Parameter = 0.0
   looper_1_recording ("Ch1 Recording"): Parameter = 0.0
   looper_1_playing ("Ch1 Playing"): Parameter = 0.0
   looper_1_memory ("Ch1 Memory"): Parameter = 0.0
   looper_1_state ("Ch1 State") : Discrete Parameter "Cleared", "Stopped", "Recording", "Overdubbing", "Playing" = "Cleared"
   looper_1_level ("Ch1 Level") : Parameter 1..128 = 113

   looper_2_recordplay ("Ch2 Record/Dub/Play") : Parameter = 0.0
   looper_2_stop ("Ch2 Stop"): Parameter = 0.0
   looper_2_undo ("Ch2 Undo"): Parameter = 0.0
   looper_2_clear ("Ch2 Clear"): Parameter = 0.0
   looper_2_recording ("Ch2 Recording"): Parameter = 0.0
   looper_2_playing ("Ch2 Playing"): Parameter = 0.0
   looper_2_memory ("Ch2 Memory"): Parameter = 0.0
   looper_2_state ("Ch2 State") : Discrete Parameter "Cleared", "Stopped", "Recording", "Overdubbing", "Playing" = "Cleared"
   looper_2_level ("Ch2 Level") : Parameter 1..128 = 113
   
   global_stop ("Stop All") : Parameter = 0.0
   global_clear ("Clear All") : Parameter = 0.0
   
   midi_learn ("MIDI Learn Mode") : Parameter = 0.0
   overdub_on ("Overdub On") : Parameter = 0.0
   
   ch1_rec_play_vel, ch2_rec_play_vel : integer = 0
   ch1_memory, ch2_memory : double = 0.0
   
   ch1_init, ch2_init : boolean = true

//   gPostOverdub : ExternalWidget

 
   
Function set_memory(action : string) returns double
var mem_state : double

    Select
        action == "Clear" do
            mem_state = 0.0
        action == "Set" do
            mem_state = 1.0
        
    End
    
    result = mem_state

End
   
Function set_channel_memory(channel : integer, action : string)

    Select
        channel == 1 do
            ch1_memory = set_memory(action)
        channel == 2 do
            ch2_memory = set_memory(action)      
    End

End
   
Function velocity_flipper(current_velocity : integer) returns integer
var new_velocity : integer

    if current_velocity > 64 then
        new_velocity = 1
    else
        new_velocity = 127
    end

    result = new_velocity


End

Function send_stop(channel : integer, velocity : integer)
var note : integer = 2
    
    if channel == 2 then
        note = note + 10
    end

    SendNow(MakeNoteMessage(note, velocity))

End
   
Function send_record_play(channel : integer)
var note : integer = 1
    msg : MidiMessage
    vel : integer
    msg_int : MidiMessage
    note_int : integer = 2
    vel_int : integer = 127
    
    Select
        channel == 1 do
            vel = velocity_flipper(ch1_rec_play_vel)
            note = note
        channel == 2 do
            vel = velocity_flipper(ch2_rec_play_vel)
            note = note + 10
    End
    msg = MakeNoteMessage(note, vel)
    msg_int = MakeNoteMessage (note_int, vel_int)
    If (looper_1_state == "Recording" or looper_1_state == "Playing" or looper_1_state == "Overdubbing") and overdub_on == 0.0 Then
        SendNow(msg_int)
        SendLater(msg, 2)
    Else
        SendNow(msg)
    End
End


Function send_clear(channel : integer, velocity : integer)
var note : integer = 3
    
    if channel == 2 then
        note = note + 10
    end

    SendNow(MakeNoteMessage(note, velocity))

End

Function send_undo(channel : integer, velocity : integer)
var note : integer = 4
    
    if channel == 2 then
        note = note + 10
    end

    SendNow(MakeNoteMessage(note, velocity))


End

Function check_other_channel(channel : integer, desired_state : string) returns string
/* Checks if the other channel is recording/overdubbing and sets channels accordingly

| Current State       |  Desired State      |   Other State     | New State        | Other New State    |
|---------------------|---------------------|-------------------|------------------|--------------------|
| Stopped             | Recording           |  Recording        | Overdubbing      | Playing            |
| Stopped             | Recording           |  Overdubbing      | Playing          | No Change          |
| Playing             | Overdubbing         |  Overdubbing      | Overdubbing      | Playing            |


*/
var new_state : string
    current_state : string
    other_state : string
    new_other_state : string

    Select
        channel == 1 do
            current_state = looper_1_state
            other_state = looper_2_state
        
        channel == 2 do
            current_state = looper_2_state
            other_state = looper_1_state
            
    End
    
    new_other_state = other_state
    
    if current_state == "Cleared" && desired_state == "Recording" then
        if other_state == "Recording" then
            new_state = "Overdubbing"    
            new_other_state = "Playing"
        elsif other_state == "Overdubbing" then
            new_state = "Playing"
        else
            new_state = desired_state
        end
    elsif current_state == "Playing" && desired_state == "Overdubbing" then
        if other_state == "Overdubbing" then
            new_other_state = "Playing"
        end
        new_state = desired_state
    else
        new_state = desired_state
    
    end

    Select
        channel == 1 do
            looper_2_state = new_other_state
        
        channel == 2 do
            looper_1_state = new_other_state    
    End

    
    result = new_state

End
   
Function looper_cleared(channel: integer, action : string) returns string
// Actions when looper is in a cleared (and stopped) state
var new_state : string = "Cleared"

    Select
        action == "RecordPlay" do
            new_state = check_other_channel(channel,"Recording")  // Set new state value
            //send_record_play(channel) // Send MIDI      
            set_channel_memory(channel, "Set")// Set Memory
    End


    result = new_state

End

Function looper_stopped(channel: integer, action : string) returns string
// Actions when looper is stopped but not cleared
var new_state : string = "Stopped"

    Select
        action == "RecordPlay" do
            new_state = check_other_channel(channel,"Playing") // set new state value
            //send_record_play(channel)// Send MIDI
        
        action == "Clear" do
            new_state = "Cleared"// set Cleared state
            //send_clear(channel)// Send MIDI
            set_channel_memory(channel, "Clear")// Update memory value
        
        action == "Undo" do  // Keeps same state
            //send_undo(channel)// Send MIDI
            
        
    End


    result = new_state

End


Function looper_recording(channel: integer, action : string) returns string
// Actions when looper is recording
var new_state : string = "Recording"

    Select
        action == "RecordPlay" do
            If overdub_on > 0.0 Then
                new_state = check_other_channel(channel,"Overdubbing")
                //send_record_play(channel)
            Else
                new_state = check_other_channel (channel, "Playing")
            End
        action == "Stop" do
            new_state = "Stopped"
            //send_stop(channel)
        
    End

    result = new_state

End

Function looper_overdubbing(channel: integer, action : string) returns string
// Actions when looper is overdubbing
var new_state : string = "Overdubbing"

    Select
        action == "RecordPlay" do
            new_state = check_other_channel(channel, "Playing")
            //send_record_play(channel)
        
        action == "Stop" do
            new_state = "Stopped"
            //send_stop(channel)
    
    End

    result = new_state

End


Function looper_playing(channel: integer, action : string) returns string
// Actions when looper is playing
var new_state : string = "Playing"

    Select
        action == "RecordPlay" do
            If overdub_on > 0.0 Then
                new_state = check_other_channel(channel,"Overdubbing")
                //send_record_play(channel)
            Else
                new_state = check_other_channel(channel, "Playing")
            End
        action == "Stop" do
            new_state = "Stopped"
            //send_stop(channel)
        
        action == "Undo" do
            //send_undo(channel)
        
        action == "clear" do
            
    
    End


    result = new_state

End   

Function set_recording_led(channel : integer, state: double)

    Select
        channel == 1 do
            looper_1_recording = state
        channel == 2 do
            looper_2_recording = state
    End

End

Function set_playing_led(channel : integer, state : double)

    Select
        channel == 1 do
            looper_1_playing = state
        channel == 2 do
            looper_2_playing = state
    End

End

Function set_memory_led(channel : integer)

    Select
        channel == 1 do
            looper_1_memory = ch1_memory
        channel == 2 do
            looper_2_memory = ch2_memory
    End

End

Function set_state_leds(channel : integer, new_state : string)

    Select
        new_state == "Cleared" do
            set_recording_led(channel, 0.0)
            set_playing_led(channel, 0.0)
            set_memory_led(channel)
        
        new_state == "Stopped" do
            set_recording_led(channel, 0.0)
            set_playing_led(channel, 0.0)
            set_memory_led(channel)
        
        new_state == "Recording" do
            set_recording_led(channel, 1.0)
            set_playing_led(channel, 0.0)
            set_memory_led(channel)
        
        new_state == "Overdubbing" do
            set_recording_led(channel, 1.0)
            set_playing_led(channel, 1.0)
            set_memory_led(channel)
            
        new_state == "Playing" do
            set_recording_led(channel, 0.0)
            set_playing_led(channel, 1.0)
            set_memory_led(channel)
        
        End

End
   
Function set_looper_state(channel : integer, current_state : string, action : string) returns string
/*
Move to a new looper state based on a button action. Actions are:

- Stop
- RecordPlay (Record/Overdub/Play)
- Clear
- Undo

*/
var new_state : string

    Select
        current_state == "Cleared" do
            new_state = looper_cleared(channel, action)
        current_state == "Stopped" do
            new_state = looper_stopped(channel, action)
        current_state == "Recording" do
            new_state = looper_recording(channel, action)
        current_state == "Overdubbing" do
            new_state = looper_overdubbing(channel, action)
        current_state == "Playing" do
            new_state = looper_playing(channel, action)
    End
    
    /*if current_state != new_state then
        set_state_leds(channel, new_state)
    end*/

    result = new_state

End

// ++++++++++++++++++++++++ INITIALISATION CALLBACK ++++++++++++++++++


    // Display a message at the bottom of the scriptlet window
Initialization
   SetDisplayMessage("Looper Control for Overloud THU - click info button for instructions")
   SetInfoMessage("Map the Record/Dub/Play, Stop, Clear, and Undo parameters to momentary widgets. Map Recording, Playing, Memory, and State to widgets as you like - these are read only controls.")
   
   ch1_init = true
   ch2_init = true
   
   /*looper_1_state = "Cleared"
   looper_1_memory = set_memory("Clear")
   set_state_leds(1, looper_1_state)
   
   looper_2_state = "Cleared"
   looper_2_memory = set_memory("Clear")
   set_state_leds(2, looper_2_state)*/
   
End  


// ++++++++++++++++++++++++ PARAMETER CALLBACKS ++++++++++++++++++

On ParameterValueChanged matching looper_1_recordplay

    if looper_1_recordplay > 0.5 then
        send_record_play(1)
        if midi_learn < 0.5 then
            looper_1_state = set_looper_state(1, looper_1_state, "RecordPlay")
        end
    end



End


On ParameterValueChanged matching looper_1_stop

    if looper_1_stop > 0.5 then
        send_stop(1, 127)
        if midi_learn < 0.5 then
            looper_1_state = set_looper_state(1, looper_1_state, "Stop")
    end
    else
        send_stop(1, 1)
    end



End

On ParameterValueChanged matching looper_1_clear

    if looper_1_clear > 0.5 then
        send_clear(1, 127)
        if midi_learn < 0.5 then
            looper_1_state = set_looper_state(1, looper_1_state, "Clear")
        end
    else
        send_clear(1, 1)
    end

End

On ParameterValueChanged matching looper_1_undo

    if looper_1_undo > 0.5 then
        send_undo(1, 127)
        if midi_learn < 0.5 then
            looper_1_state = set_looper_state(1, looper_1_state, "Undo")
        end
    else
        send_undo(1, 1)
    end



End

On ParameterValueChanged matching looper_2_recordplay

    if looper_2_recordplay > 0.5 then
        send_record_play(2)
        if midi_learn < 0.5 then
            looper_2_state = set_looper_state(2, looper_2_state, "RecordPlay")
        end
    end

End


On ParameterValueChanged matching looper_2_stop

    if looper_2_stop > 0.5 then
        send_stop(2, 127)
        if midi_learn < 0.5 then
            looper_2_state = set_looper_state(2, looper_2_state, "Stop")
        end
    else
        send_stop(2, 1)
    end



End

On ParameterValueChanged matching looper_2_clear
    
    if looper_2_clear > 0.5 then
        send_clear(2, 127)
        if midi_learn < 0.5 then
            looper_2_state = set_looper_state(2, looper_2_state, "Clear")
        end
    else
        send_clear(2, 1)
    end



End

On ParameterValueChanged matching looper_2_undo

    if looper_2_undo > 0.5 then
        send_undo(2, 127)
        if midi_learn < 0.5 then
            looper_2_state = set_looper_state(2, looper_2_state, "Undo")
        end
    else
        send_undo(2, 1)
    end



End


On ParameterValueChanged matching looper_1_state

    set_state_leds(1, looper_1_state)

End

On ParameterValueChanged matching looper_2_state

    set_state_leds(2, looper_2_state)

End


On ParameterValueChanged matching looper_1_level

    SendNow(MakeControlChangeMessage(5, looper_1_level))

End

On ParameterValueChanged matching looper_2_level

    SendNow(MakeControlChangeMessage(15, looper_2_level))

End

On ParameterValueChanged matching global_stop

    if global_stop > 0.5 then
        SendNow(MakeNoteMessage(7, 127))
        if midi_learn < 0.5 then
            looper_1_state = set_looper_state(1, looper_1_state, "Stop")
            looper_2_state = set_looper_state(2, looper_2_state, "Stop")
        end
    end

End

On ParameterValueChanged matching global_clear

    if global_clear > 0.5 then
        SendNow(MakeNoteMessage(7, 127))
        if midi_learn < 0.5 then
            looper_1_state = set_looper_state(1, looper_1_state, "Stop")
            looper_1_state = set_looper_state(1, looper_1_state, "Clear")
            looper_2_state = set_looper_state(2, looper_2_state, "Stop")
            looper_2_state = set_looper_state(2, looper_2_state, "Clear")
        end

    end

End


That thingie is completely mysterious to me, I couldn’t even figure out how that trigger button works. Sometimes it goes to recording mode but doesn’t record, sometimes it does other strange things.

1 Like

Wow, that is a lot of work on the THU looper!

Regarding Enso, it only works properly for me if Gig Performer is already set to Play mode. If you hit the trigger button without activating Play, Enso starts blinking (as if it were recording) but doesn’t actually work.

Because of that, I mapped both actions (GP Play and Enso Trigger) to a group.

See if that works for you!

Yeah, that was the first thing I tried, it still does strange things with that trigger, so I dumped it

Finally figured out how to turn the damn overdub mode off and on.

Hi there,

I have issues, finally i solved the problem with the vst version, cause i don’t have the vst3 version

But i still have nothing that goes to the looper, everything seems to work in panel view, record, overdub etc.. but in thu nothing happens…is there something i need to do in thu?

@speed12 is the author of this gig file, maybe he’ll give some info :slight_smile:

Hello.

I’m new to GP and came across this script to control the looper. I use a Nektar Pacer footswitch. I opened one of the stock GP Guitar rigs (for noobs) and played around with the MIDI learn and was able to use my expression pedals and foot switches to turn off various effects and get the master volume and swells to work, i.e. the Pacer is communicating with GP.

I downloaded and loaded up the script files to control the TH-U plugin. First, I noticed the plugin CC mapping is in the range of 50, i.e 49 is chan 1 rec/play/stop, etc. I could not determine how to change this in the plug. I did however change my Pacer mapping and use the learn feature in the edit window for the plug panel created by @speed12. So I push the Nektar pedal and I see the corresponding panel blinking in the GUI on GP. I can see that the TH-U plug notes MIDI is there because it momentarily “blinks” at the bottom of the plugin. However, nothing happens at all? I feel I’m close here but something is still not communicating. Any help from the user base is appreciated.

Thank you.

Jed

As an update. I was able to get it working using EDM’s workaround to force the script to recognize the VST version of the plugin, which appears the default that installs with GP.

So, is there a way to save this updated script into the script folder to be accessed by other rack spaces? I copy/pasted to another gig file and it worked but would rather right click and load from the User bank.

Thx,

Jer

save it as a preset

I couldn’t figure out how to do that. When I right click the midi plugin it doesn’t give that option.