2 finger metronome set up

OK… here we go! :slight_smile:
Since this rackspace still was in an experimental phase i had to do some “cleanup” first, so it took a bit longer…

This rackspace will do the following things:

  • load a specified midi file (has to be done in the script!)
  • do a remapping of particular midi tracks to a specified midi channel (has to be done in the script!)
  • step sequentially through the midi events of that file according to the quantization setting using two diffrent trigger controls

You can

  • set the quantization value with a knob widget
  • see the current position of the midi sequence
  • set the used note/key pair as stepping triggers or
  • use a pad widget as a stepping trigger (could be midi-mapped to a pedal or whatever)
  • use a pad widget to reset the sequence to start (could be midi mapped to a certain note or midi controller)
  • use a widget as “panic-button” (sends all notes off & resets midi)

This is the code of the script:

Var
MidiStepIn : MidiInBlock

cs : MidiSequence
trackCount, quantization, UStepKey : integer
   
padStep, padReset, lblPosition, knbQuant, btnPanic, knbUStepKey : widget


//user function to calculate the quantization from the knob widget's value
function SetQuantization(kVal : double) returns integer
var
exponent, quantValue : double
    exponent = ScaleRange(kVal, 0.0, 4.0)
    quantValue = Power (2.0 , exponent)
result = Round(quantValue)
SetWidgetLabel(knbQuant, result)
End
   
//user function to handle the MIDI-Stepping
function MidiStep()
var
notes : MidiMessage Array
aNote : MidiMessage
index : integer
trackIndex : integer
bar, beat, tick : integer

    bar = MidiSequence_GetCurrentBar(cs)
    beat = MidiSequence_GetCurrentBeat(cs)
    tick = MidiSequence_GetCurrentTick(cs)
    
    if MidiSequence_EndOfSong(cs) then
        MidiSequence_ResetToStart(cs)
    end
        
    SetWidgetLabel (lblPosition, "Pos: " + bar + " / " + beat + " / " + tick)
        
    MidiSequence_CollectEventsNow(cs); //fetch all events from current position

    notes = MidiSequence_GetCurrentEvents(cs, 3) // Get current events from track #3

    for index = 0; index < Size(notes); index = index + 1 do
        aNote = notes[index]
        SendNow(MidiStepIn, aNote)
    end                                  
End

initialization
    //load the midi file (!!! use a a"/" instead of "\" for path separator !!!)
   MidiSequence_LoadStandardMidiFile(cs, "D:/MIDI-files/The-Model.mid")
   
   trackCount = MidiSequence_GetTrackCount(cs) //get number of tracks of midi file
   Print(trackCount)
   quantization = SetQuantization (GetWidgetValue (knbQuant)) //get quantization value from widget
   MidiSequence_Quantize(cs, quantization ) // Quantization may be 1/2/4/8/16
   
   //map events coming from track 2 & 3 both to MIDI channel 1
   //change remapping to your needs -> add/remove according code lines
   MidiSequence_MapOutputChannel(cs, 2, 1)
   MidiSequence_MapOutputChannel(cs, 3, 1)
   UStepKey = ParamToMidi(GetWidgetValue (knbUStepKey))
end


// Use this as a finger clock -- pressing the trigger control (widget or two notes) behaves 
//like a manual clock to sequence through the MIDI events of the loaded file

//use pad widget as a trigger to step through the MIDI file
On WidgetValueChanged (pVal : double) from padStep
    if pVal > 0.6 then
        MidiStep()
    end            
End

//use the two trigger notes to step through the MIDI file (use knob widget to set the according notes)
On NoteEvent(m : NoteMessage) from MidiStepIn
    //if the note event is a trigger note, step forward
    if GetNoteNumber(m) == UStepKey or GetNoteNumber(m) == UStepKey-2 then
        if IsNoteOn(m) then
            MidiStep()
        end
    else //all other note will be played
        SendNow(MidiStepIn, m) //just send out note event
    end
End

//reset the midi sequence to start
On WidgetValueChanged (pVal : double) from padReset
var
bar, beat, tick : integer
    AllNotesOff(MidiStepIn)
    MidiSequence_ResetToStart(cs)
    
    bar = MidiSequence_GetCurrentBar(cs)
    beat = MidiSequence_GetCurrentBeat(cs)
    tick = MidiSequence_GetCurrentTick(cs)
        
    SetWidgetLabel (lblPosition, "Pos: " + bar + " / " + beat + " / " + tick)
End

//Panic button = all notes off & back to start
On WidgetValueChanged (pVal : double) from btnPanic
    if pVal > 0.6 then
        MidiSequence_ResetToStart(cs)
        Panic() // Stop any sounds that were being held
        AllNotesOff(MidiStepIn)
    end
End

//use knob to set quantization for sequence stepping
On WidgetValueChanged (kVal : double) from knbQuant
    quantization = SetQuantization (kVal)
    MidiSequence_Quantize(cs, quantization )
End

//use knob widget to set the Upper note/key used to step forward
On WidgetValueChanged (kVal : double) from knbUStepKey
    UStepKey = ParamToMidi(kVal)
    SetWidgetLabel (knbUStepKey, "" + NoteNumberToNoteName(ParamToMidi(kVal)-2) + "/" + NoteNumberToNoteName(ParamToMidi(kVal)))
End

And here is the gigfile for download:
midistepper.gig (115.8 KB)

I hope this helps somehow…
…and maybe, some day in the future, we might have some better options to do such things?!
However, have fun with it! May it be useful! :vulcan_salute:
Cheers, Erik

8 Likes