Midi parser (not player)

About
After realizing most solutions to this is to either port a pure lua midi parser to roblox or use a incredibly old script, which is often tedious and mostly a pain in the bum. So I decided to make one myself, for anyone to use, this midi parser turns raw data from Http service to a table that shouldnt be hard to read.

Some Important notes
there are some or a lot of status bytes and meta events i have not implemented yet (laziness!!), but if you need to it shouldnt be that hard.
to do so add a module under the StatusBytes folder in which it should be named after the Hex or decimal of the status byte you want to add in the module it should include this template

return function(self)
   local Byte = self:ReadBytes(1) --or self:ReadBytes()
   local RawText = self:ReadRaw(Step) --or nil = 1 step
end

I am aware that richie0866 has already made one that looks better but his code is pretty chunky.
Lastly, as the title said there is no midi player included

Example code

local Data = Http:GetAsync(URL)
local ParseMidi = require(...)

local Midi = ParseMidi.new(Data)
local Result: ParseMidi.Result = Midi:Start()
Result.RawStack --all the messages and meta events ordered by when they show up
Result.Tracks[1] -- chunk 1 with all the meta events and messages
Should I make the midi player?
  • Yes
  • No

0 voters

12 Likes

ig thats enough for me to at least try, so heres some visualizations
image

1 Like

although im not sure how to actually get those sounds, it would be great if someone point me in the correct direction

Can you show the script you used to achieve this? Really nice module though, I’m sure it will be helpful for some people.

1 Like

its a bit messy though,

local function visualizeNotes(TrackData)
	local Delta = 0
	for _, EventData in ipairs(TrackData) do
		local IsNoteOn = EventData.Name == "NoteOn"
		
		if IsNoteOn then
			local NoteNumber = EventData.Data.NoteNumber
			local NoteVelocity = EventData.NoteVelocity
			local DeltaTimeInTicks = EventData.DeltaTime

			local Seconds = (DeltaTimeInTicks / Result.Division) * (60 / 90) --tempo is 90 derrived from 60000000 / 666666
			
			local Part = Instance.new("Part")
			Part.Size = Vector3.new(Seconds * 10, 1, 1) --regularly it would be too small
			Part.Anchored = true
			Part.Position = Vector3.new(Delta + (Part.Size.X / 2), NoteNumber, 0)
			Part.Parent = workspace
			
			Delta += Seconds * 10
		end
	end
end

visualizeNotes(Result.Tracks[1])
1 Like

what’s a midi parser

2 Likes

A module that turns raw binary midi files to readable ones just like https:JsonDecode

1 Like

Quick update

  • Added Duration property to each event
  • Added 2 properties to each events, Duration and start time

Great progress today, the visualizer fully works, the timing should be precise

in the image the notes are stretched to show the note duration and stuff
roblox:


website

1 Like

LuaMidi from luarocks is a pure lua midi parser I think you should look at and try to compare with it (performance, etc). You can use midi2score() function and just plug in the returned value of HttpService:GetAsync(mid file link). I have my own midi player that utlizes that module.

3 Likes

The sound ids provided definitely helps, thanks. as of now the progress is looking great

Nice, I recommend you try mine and see how your midi sounds with my player :smiley:

1 Like

Will do, also upon benchmarking on a 100kb file the pure lua one is indeed faster by around 7 times, but i would say theyre both negligible as the time is within the milisecond range 1ms and 7ms, although the pure lua one is faster by a lot, it isnt in readability :sob:. One major bottleneck i think is opting to use lua’s string.unpack rather than some bit manipulation (maybe in the future).

So, uh i got the rest working i just need the assets which i do not know where to get

Look up the MIDI channels, it should provide you a basic outline of which channels are your tuned percussion, untuned percussion, synth etc. Channel 9 is usually the percussion.

I have a big dictionary of instruments in my project: https://github.com/vxsqi/MIDI-player/blob/main/instruments.lua

According to this MIDI Program Change message | RecordingBlogs percussion is on channel 9 (according to a comment this often differs from how you count, from zero or one). I have seen your Instruments file but since im no audio engineer idk how to use it or something. Btw whats the formula to get Pitch? Also I have found this neat website with all the assets Samples by MIDI Instrument.

When I find the motivation ill rework the entire think, hopefully it wouldnt be that messy.

function notetopitch(note, offset)
	return ((440 / 32) * math.pow(2, ((note + offset) / 12)) / 440)*8
end

Im guessing Note is NoteNumber and the offset?

Yep. Offset can just be 0 if you don’t want a offset. Best used for octave control.

btw in the pure lua module, midi2opus for note on messeges what are 2-5?