Making MIDI Player template playing MIDI files

Concerning playing MIDI files there is an example of script as a good start: readMidiFile - HALion 7.1.30

However I’d like to change the line

midiSequence = File("c:/temp/C Major Scale.mid")

to a piece of code that would load a MIDI file using built-in MIDI Player template. My steps were the following:

  1. I added a midi module called MIDI Player
  2. Created an empty Macro Page
  3. Drag and drop there the MIDI Player template
  4. Input “HALion” in the Product field and “MIDI Files” in the Load From field (according to this: MIDI Player - HALion 7.1.30)

But after that I’ve encountered a couple problems:

  1. The load drop-down menu is not clickable (in the demonstration mode). It’s just not reacting
  2. How can I connect the midiSequence variable to the path of the actually loaded file in MIDI Player? I suggest it must deal with either @PhraseSelectPopup or @Filename from here (MIDI Player - HALion 7.1.30), but what is the exact way?

MIDI Player has a parameter called MidiSequenceData

You could use that to read the midi events.

mp = this.parent:getMidiModule("MIDI Player")
state = mp:getParameter("ActiveState")

print(state)

if state == 0 then
	midi = mp:getParameter("MidiSequenceData")
else
	midi = mp:getParameter("State" .. state .. ".MidiData")
end

print(midi)

if midi.tracks then
	for i, event in ipairs(midi.tracks[1].events) do
		print(event)
	end
end

Thank you, misohoza! Could please clarify me several things?

  1. In Parameter List the ActiveState can be either Off or from 1 to 8, depending which Variation button is activated. However I see that print(state) always gives 0, doesn’t matter if MIDI Player On or Off and which variation is selected (when MIDI Player is On).

  2. Why “State” .. state .. “.MidiData” combination is used when the state is not 0? I understand that “else” condition is for the state when MIDI Player is On. So why MidiSequenceData is not working in this case?

  3. I noticed that State7 is the last subpage in Parameter List for MIDI Player. Where is State8 gone for Variation 8? Is it a bug? Because if Variation 8 is activated, the script above gives error, being not able to read the sub-parameter “State8.MidiData”

When you check the parameter list there are 2 parameters. One is Active which is on/off parameter and then you have ActiveState which is an integer parameter ranging from 1 to 8. This should reflect the currently selected variation. However this is a bit misleading because when you want to use this parameter in your script you need to use values 0 to 7. You can easily test this yourself using getParameter or setParameter.

When the first variation is selected the ActiveState should be 0. The parameter that contains the midi data is called MidiSequenceData. Parameters specific to the first variation are in the main list together with the other global parameters for the midi player module.

Parameters specific for other variations are grouped under folders: State1, State2….

So to access the midi data of the second variation you need to get the parameter State1.MidiData. Don’t know why it’s called differently for the other variations. I’m going by what’s listed in the parameters list and some trial and error.

Trying to get State8.MidiData will give you error because there is no such thing. First variation is 0, last one is State7.

1 Like

Now it’s all clear! Thanks a lot!

The only thing I’m struggling now is the pointing my custom folder with midi-files to the MIDI Player. According to the dev resource custom midi-files “must be added to the folder ./MyProductName/Sub Presets/MIDI Files inside the VST Sound”.

And so far I have such file structure:

MIDI-Player-v1\
    macropages\
    samples\
    sub presets\
    MIDI Files\
         my-midi.mid
MIDI-Player-v1.vstpreset

So MyProductName is MIDI-Player-v1 and my-midi.mid resides inside “MIDI-Player-v1” folder, inside “sub presets” folder and inside “MIDI Files” folder. But I suspect that I get something wrong.

While you are working locally try copying them to your Documents folder

Documents\Steinberg\MIDI-Player-v1\Sub Presets\MIDI Files

Yes, that worked! Do you think it’s possible to connect a custom button with MIDI-Player so that it would load in it a particular midi-file from the subpresets folder? Rather then loading from a native popup menu…

So far I had an unsuccessful attempt by trying the following idea:

defineParameter(“midi_1”, nil, 0, 0, 1, 1, function() midi_1_changed() end)
defineParameter(“midi_2”, nil, 0, 0, 1, 1, function() midi_2_changed() end)
defineParameter(“midi_3”, nil, 0, 0, 1, 1, function() midi_3_changed() end)

function midi_1_changed()
  if(modules[2]:getParameter(“midi_1”)==1) then
    modules[2]:setParameter(“midi_2”, 0)
    modules[2]:setParameter(“midi_3”, 0)
    modules[1]:setParameter(“Filename”, “midi_1”)
  end
end

function midi_2_changed()
  if(modules[2]:getParameter(“midi_2”)==1) then
    modules[2]:setParameter(“midi_1”, 0)
    modules[2]:setParameter(“midi_3”, 0)
    modules[1]:setParameter(“Filename”, “midi_2”)
  end
end

function midi_3_changed()
  if(modules[2]:getParameter(“midi_3”)==1) then
    modules[2]:setParameter(“midi_1”, 0)
    modules[2]:setParameter(“midi_2”, 0)
    modules[1]:setParameter(“Filename”, “midi_3”)
  end
end

modules[1] is factory MIDI Player, modules[2] is the Lua Script above. Also I connected Parameters midi_1, midi_2 and midi_3 to three buttons on the MacroPage.

Visually it works, but the actual loading is not happening. Is it possible at all without opening a built-in popup file browser?

I don’t think you can do that easily if at all. Setting the Filename parameter alone won’t do much. It’s not full path to a midi file and I guess it’s rather a songname which you can get from the midi sequence table (midi.songname)

I tried using readMidiFile and then use it to set the MidiSequenceData parameter. While it did load something the ppq positions were completely messed up. A 2 bar loop that should contain 8 beats (quarters) was a thousand times longer. Don’t know why. If that worked correctly then you just need to adjust the LoopEnd parameter and possibly the Filename

I’ve tried this idea as well and noticed that if I set “MidiSequenceData” parameter to what I’ve got with readMidiFile() then the ppqPosition in MidiSequenceData changes its scale. Rather than having 1 beat equal to 1 tick () it turns to 1 beat = 9600 ticks.

Moreover ppq turns from a floating number to an integer one. And thus putting something like event.ppqPosition/9600 after readMidiFile() but before setParameter(“MidiSequenceData”, readMidiFile()) didn’t work as all ppqPositions changed to integers like 0,1,2,3 etc

Maybe you have any enlightenment of how the 9600 ticks upscale could be fixed? Tanks!

I succeeded in loading midi-file via GUI buttons. The general idea is the following

1) Read a midi file:
midiSequence = readMidiFile(root_path .. selected_mf .. ".mid ")

2) Scale its PPQ positions right away by dividing them by 20:

for i, event in ipairs(midiSequence.tracks[1].events) do
  event.ppqPosition=event.ppqPosition/20
end 

3) After that send the midiSequence to MidiSequenceData parameter of the factory MIDI Player :

modules[2]:setParameter("MidiSequenceData",midiSequence) 

The only feature that’s not working for the present is Drag&Drop, when accomplishing it exactly from the MacroPage (I added factory the MIDI Player template to my MacroPage and connected all its available parameters to the ones related in MIDI Player module, like Swing and Velocity knobs, Play button, etc). To the contrary, if I drag a midifile out of MIDI Player module itself (an icon with DIN-plug), it all works. Meanwhile the dragging area on the MacroPage is not active and doesn’t react at all to the dragging.

Do you have any suggestion of how this could be fix ed?

P.S. If loading midifile in a standard way, via drop-down menu, the Drag&Drop from the MacroPage works perfectly…