MidiPlayer example help

I'm trying to figure out how this code works in the MidiPlayer example:

function playdate.update()
gfx.clear(gfx.kColorWhite)
gfx.setColor(gfx.kColorBlack)

for i=1,#active do
	local track = s:getTrackAtIndex(i)
	local n = track:getNotesActive(active[i])
	gfx.fillRect(400*(i-1)/#active, 240*(1-n/poly), 400/#active, 240)
end	

end

I don't understand how it knows what to draw....for the example the midi has 16 tracks. But how does this code know what the active thing to fill is? There's no iteration over all the notes or the current position in the sequence, etc.

Also, how would I go about getting the current note being played in track one? I thought something like:
local t = track:getNotes(s:getCurrentStep())
printTable(t)

But it doesn't show all the notes for the track I'm hearing, it's like it shows it randomly because the step jumps up more than the update.

1 Like

It doesn't need to iterate over all of the notes, because it just checks each track in the currently playing sequence s and polls for the currently active notes with getNotesActive():

for i=1,#active do

Runs an iteration on i for each item in active

local track = s:getTrackAtIndex(i)

Uses i to get each track in s, one at a time, stored in track

local n = track:getNotesActive(active[i])

Pulls the active notes out of the current track. (I'm not sure what the active[i] argument does, since it doesn't seem to be described in the manual... maybe it's for a depreciated parameter)

For your second question, I'm not totally sure why your method for checking the current notes based on the current step doesn't work how you expected. But, I suspect that the MIDI sequence processing may be running in parallel with the playdate.update() callback, so you don't always see the same number of MIDI ticks between frames (if you did, then graphical framerate would affect playback). I'm guessing that getNotesActive() still works in update() because it is designed to do so, though I'm not really sure if this is the case.

EDIT: It just occurred to me that getNotes() probably only gives the notes STARTED at a given step, while getNotesActive() will return any notes that are currently playing, which is dependent on note length and start position, as well as the current step.

EDIT: robamcclellan points out below that getNotesActive() only returns the number of active notes, not the notes themselves.

Try one of these:

  1. Use track:getNotesActive() instead, like in the example code (EDIT: only gives the number of notes, not the actual notes played)

  2. Instead of checking only the current step, try checking for the notes played since the last update() by also storing the previous step value, and doing this:

local t = track:getNotes(prevStep, s:getCurrentStep())

If there are notes that are in quick succession, but not simultaneous, in a project with a fast song/slow framerate, I think you might sometimes get more than one set of notes from this method. Another option is to store the notes played in EACH step passed since the last update(), by iterating over each step between prevStep and s:getCurrentStep().

To address your last remark, the step is going to increment based on the Tempo of the MIDI file, not based on the framerate of the game, so I think it's expected that the step increases faster than the number of update() calls.

1 Like

The problem I see, and why I'm getting confused:

printTable(active)
Returns this:
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
}

And local n = track:getNotesActive(active[i]) printTable(n)
returns the number of active notes, not what they actually are.

I'm scratching my head at this

Your idea made me think though, I switched to this:

local currentStep = s:getCurrentStep()
local notes = gameTrack:getNotes(lastStep, currentStep)
lastStep = currentStep

Now notes table will hold any notes played since the lastStep and the currentStep, and it works!

1 Like

You are right, getNotesActive() does indeed return only the number of notes, and not the notes themselves. I had overlooked that, I apologize.

I'm glad you have a working method now!

I will point out that regarding your above post, where you mentioned checking printTable(active), the active variable is just holding a count of the number of active tracks in the sequence s, which is why it is also just a list of numbers. The example code sets up active here:

for i=1,ntracks do
	local track = s:getTrackAtIndex(i)
	if track ~= nil then
		local n = track:getPolyphony(i)
		if n > 0 then active[#active+1] = i end       --Here!
		if n > poly then poly = n end
		print("track "..i.." has polyphony "..n)
	
		if i == 10 then
			track:setInstrument(druminst(n))
		else
			track:setInstrument(newinst(n))
		end
	end
end