Midi2lua Today

midi2lua does not magically create sound. It gives you events. You still need a soundfont or a synthesizer (like Soloud, FMOD, or Roblox’s Sound objects) to turn those note pitches into audio. Think of it as the sheet music, not the orchestra.

Because writing a raw binary MIDI parser in Lua from scratch can be complex, the most "helpful" workflow for many developers is a two-step process:

However, if you want a pure Lua solution for simple Type 0 MIDI files (single track), here is a lightweight parser snippet.

Even with a great tool, midi2lua conversions can fail silently. Here is what to watch for.

When creating custom charts for rhythm games that use Lua (such as modded versions of Friday Night Funkin’ using Psych Engine), you need to convert chart files (.mid or .json) into game-readable scripts. midi2lua allows modders to compose complex rhythms in FL Studio or Reaper, then instantly convert those note timings into Lua tables that the game engine polls every frame.

Using a standard CLI tool (assuming midi2lua.py):

python midi2lua.py my_song.mid -o my_song.lua

Flags to look for:

Overview

Why convert MIDI to Lua

Core concepts

Typical Lua event structure

Parsing MIDI — methodical steps

Tick → second conversion (algorithm)

  • Implementation note: compute delta ticks per segment then convert to seconds using (us_per_quarter / 1e6) / TPQN.
  • Design choices to consider

    Example: Minimal parser outline (pseudocode) midi2lua

    Example Lua output (small excerpt)

    -- midi2lua output: events in seconds
    events = 
       t = 0.000, type = "note", ch = 1, note = 60, vel = 100, dur = 0.5 ,
       t = 0.500, type = "note", ch = 1, note = 64, vel = 110, dur = 0.5 ,
       t = 1.000, type = "cc",   ch = 1, cc = 1, val = 64 ,
    -- simple player: advance time and trigger events
    function play(clock_time, synth)
      -- synth must implement note_on(ch,note,vel) and note_off(...)
      for _, e in ipairs(events) do
        if not e.played and clock_time >= e.t then
          if e.type == "note" then
            synth:note_on(e.ch, e.note, e.vel)
            -- schedule note_off at e.t + e.dur (engine-dependent)
          elseif e.type == "cc" then
            synth:cc(e.ch, e.cc, e.val)
          end
          e.played = true
        end
      end
    end
    

    Practical example: tempo changes and conversion

    Optimizations

    Toolchains and libraries (recommended approach)

    Sample conversion workflow (practical)

    Debugging tips

    Use cases

    Further extensions

    If you want, I can:


    A single Lua file that returns a table like:

    -- Generated by midi2lua
    return 
      format = 1,
      ticks_per_beat = 480,
      tracks = 
         -- Track 0: Piano
          events = 
             tick = 0,    type = "note_on",  channel = 0, note = 60, velocity = 100 ,
             tick = 120,  type = "note_off", channel = 0, note = 60, velocity = 64 ,
             tick = 240,  type = "tempo",    bpm = 120 ,
            -- etc.
    

    Optionally, the converter can:

    midi2lua is a utility (or script) that parses a standard MIDI file (.mid) and outputs a Lua table representation of its musical data. This allows developers to embed procedural music playback, note-accurate event triggering, or rhythm-based game logic directly into Lua environments (e.g., LÖVE2D, Roblox, Defold, PICO-8, or custom embedded systems).

    The output is self-contained Lua code – no external MIDI parser or real-time MIDI playback required at runtime.