Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Converting a MIDI file to text (csv, tab-delimited, etc.) and back again?
#7
Actually the last answer wasn't fully complete, one of the routines was left empty with a comment "(MidiToText subroutine as provided previously)". 
Here is the full code with the missing routine added back in:

Code: (Select All)
' Requires file access permissions

DECLARE LIBRARY "winmm.dll"
    ' No winmm functions used, but keeping it in case you want to add MIDI playback
END DECLARE LIBRARY

TYPE MIDIHDR
    MThd$
    Length&
    Format%
    Tracks%
    Division%
END TYPE

TYPE MIDITRACK
    MTrk$
    Length&
    Events$
END TYPE

TYPE MIDIEVENT
    DeltaTime&
    Status%
    Data1%
    Data2%
END TYPE

FUNCTION ReadVariableLength& (data$, offset&)
    DIM value&, byte%
    value& = 0
    DO
        byte% = ASC(MID$(data$, offset&, 1))
        value& = (value& * 128) + (byte% AND 127)
        offset& = offset& + 1
    LOOP WHILE (byte% AND 128) <> 0
    ReadVariableLength& = value&
END FUNCTION

FUNCTION WriteVariableLength$ (value&)
    DIM bytes$(1 TO 4), numBytes%, i%
    IF value& = 0 THEN
        WriteVariableLength$ = CHR$(0)
        EXIT FUNCTION
    END IF

    numBytes% = 0
    DO
        numBytes% = numBytes% + 1
        bytes$(numBytes%) = CHR$(value& AND 127)
        value& = value& \ 128
    LOOP WHILE value& > 0

    FOR i% = numBytes% - 1 TO 1 STEP -1
        bytes$(i%) = CHR$(ASC(bytes$(i%)) OR 128)
    NEXT i%

    FOR i% = 1 TO numBytes%
        WriteVariableLength$ = WriteVariableLength$ + bytes$(i%)
    NEXT i%
END FUNCTION

SUB MidiToText (midiFilePath$, textFilePath$)
    DIM fileHandle&, fileSize&, midiData$, header AS MIDIHDR, tracks(1 TO 256) AS MIDITRACK, trackCount%, offset&, eventOffset&, event AS MIDIEVENT, textFileHandle&
    DIM eventData$

    fileHandle& = _OPEN(midiFilePath$, _OPENEXISTING, _OPENBINARY)
    IF fileHandle& = 0 THEN
        PRINT "Error opening MIDI file."
        EXIT SUB
    END IF

    fileSize& = LOF(fileHandle&)
    midiData$ = SPACE$(fileSize&)
    _GET fileHandle&, midiData$
    _CLOSE fileHandle&

    header.MThd$ = MID$(midiData$, 1, 4)
    header.Length& = (ASC(MID$(midiData$, 5, 1)) * 256 * 256 * 256) + (ASC(MID$(midiData$, 6, 1)) * 256 * 256) + (ASC(MID$(midiData$, 7, 1)) * 256) + ASC(MID$(midiData$, 8, 1))
    header.Format% = (ASC(MID$(midiData$, 9, 1)) * 256) + ASC(MID$(midiData$, 10, 1))
    header.Tracks% = (ASC(MID$(midiData$, 11, 1)) * 256) + ASC(MID$(midiData$, 12, 1))
    header.Division% = (ASC(MID$(midiData$, 13, 1)) * 256) + ASC(MID$(midiData$, 14, 1))

    offset& = 15
    FOR trackCount% = 1 TO header.Tracks%
        tracks(trackCount%).MTrk$ = MID$(midiData$, offset&, 4)
        tracks(trackCount%).Length& = (ASC(MID$(midiData$, offset& + 4, 1)) * 256 * 256 * 256) + (ASC(MID$(midiData$, offset& + 5, 1)) * 256 * 256) + (ASC(MID$(midiData$, offset& + 6, 1)) * 256) + ASC(MID$(midiData$, offset& + 7, 1))
        tracks(trackCount%).Events$ = MID$(midiData$, offset& + 8, tracks(trackCount%).Length&)
        offset& = offset& + 8 + tracks(trackCount%).Length&
    NEXT trackCount%

    textFileHandle& = _OPEN(textFilePath$, _OPENCREATE, _OPENOUTPUT)
    IF textFileHandle& = 0 THEN
        PRINT "Error creating text file."
        EXIT SUB
    END IF

    PRINT #textFileHandle&, "Track" + CHR$(9) + "DeltaTime" + CHR$(9) + "Status" + CHR$(9) + "Data1" + CHR$(9) + "Data2"

    FOR trackCount% = 1 TO header.Tracks%
        eventOffset& = 1
        DO WHILE eventOffset& <= LEN(tracks(trackCount%).Events$)
            event.DeltaTime& = ReadVariableLength&(tracks(trackCount%).Events$, eventOffset&)
            eventOffset& = eventOffset& + LEN(WriteVariableLength$(event.DeltaTime&))
            event.Status% = ASC(MID$(tracks(trackCount%).Events$, eventOffset&, 1))
            eventOffset& = eventOffset& + 1

            IF event.Status% < &HF0 THEN
                event.Data1% = ASC(MID$(tracks(trackCount%).Events$, eventOffset&, 1))
                event.Data2% = ASC(MID$(tracks(trackCount%).Events$, eventOffset& + 1, 1))
                eventOffset& = eventOffset& + 2
            ELSEIF event.Status% = &HFF THEN
                'Meta event: handle as needed. This simple version skips metadata.
                DIM metaType%, metaLength&
                metaType% = ASC(MID$(tracks(trackCount%).Events$, eventOffset&, 1))
                eventOffset& = eventOffset& +1
                metaLength& = ReadVariableLength&(tracks(trackCount%).Events$, eventOffset&)
                eventOffset& = eventOffset& + LEN(WriteVariableLength$(metaLength&)) + metaLength&
            ELSE
                event.Data1% = 0: event.Data2% = 0
                eventOffset& = eventOffset& + 1
            END IF
            PRINT #textFileHandle&, trackCount%; CHR$(9); event.DeltaTime&; CHR$(9); event.Status%; CHR$(9); event.Data1%; CHR$(9); event.Data2%
        LOOP
    NEXT trackCount%

    _CLOSE textFileHandle&
END SUB

SUB TextToMidi (textFilePath$, midiFilePath$)
    DIM fileHandle&, textFileHandle&, line$, parts$(), header AS MIDIHDR, tracks(1 TO 256) AS MIDITRACK, trackCount%, offset&, deltaTime&, status%, data1%, data2%, midiData$, trackData$
    DIM trackLength&

    textFileHandle& = _OPEN(textFilePath$, _OPENEXISTING, _OPENINPUT)
    IF textFileHandle& = 0 THEN
        PRINT "Error opening text file."
        EXIT SUB
    END IF

    header.MThd$ = "MThd"
    header.Length& = 6
    header.Format% = 0
    header.Tracks% = 1
    header.Division% = 480

    midiData$ = header.MThd$ + CHR$(0) + CHR$(0) + CHR$(0) + CHR$(header.Length&) + CHR$(0) + CHR$(header.Format%) + CHR$(0) + CHR$(header.Tracks%) + CHR$(0) + CHR$(header.Division%)

    tracks(1).MTrk$ = "MTrk"
    trackData$ = ""
    LINE INPUT #textFileHandle&, line$ ' Skip header line

    DO WHILE NOT EOF(textFileHandle&)
        LINE INPUT #textFileHandle&, line$
        parts$() = SPLIT$(line$, CHR$(9))
        deltaTime& = VAL(parts$(1))
        status% = VAL(parts$(2))
        data1% = VAL(parts$(3))
        data2% = VAL(parts$(4))

        trackData$ = trackData$ + WriteVariableLength$(deltaTime&) + CHR$(status%)
        IF status% < &HF0 THEN
            trackData$ = trackData$ + CHR$(data1%) + CHR$(data2%)
        END IF
    LOOP

    trackData$ = trackData$ + CHR$(0) + CHR$(&HFF) + CHR$(&H2F) + CHR$(0) ' End of track meta event

    trackLength& = LEN(trackData$)
    midiData$ = midiData$ + tracks(1).MTrk$ + CHR$((trackLength& \ &H1000000) AND &HFF) + CHR$((trackLength& \ &H10000) AND &HFF) + CHR$((trackLength& \ &H100) AND &HFF) + CHR$(trackLength& AND &HFF) + trackData$

    fileHandle& = _OPEN(midiFilePath$, _OPENCREATE, _OPENBINARY)
    IF fileHandle& = 0 THEN
        PRINT "Error creating MIDI file."
        EXIT SUB
    END IF

    _PUT fileHandle&, midiData$
    _CLOSE fileHandle&
    _CLOSE textFileHandle&
END SUB

' Example Usage
DIM midiIn$, textOut$, midiOut$

midiIn$ = "C:\test.mid" ' Replace with your input MIDI file
textOut$ = "C:\test.txt" ' Replace with your output text file
midiOut$ = "C:\test_converted.mid" ' Replace with your output MIDI file

MidiToText midiIn$, textOut$
TextToMidi textOut$, midiOut$

PRINT "MIDI conversion complete."

END
Reply


Messages In This Thread
RE: Converting a MIDI file to text (csv, tab-delimited, etc.) and back again? - by madscijr - 03-11-2025, 08:52 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  Download the file from the internet and extract the .zip file quickbasic 6 686 10-25-2025, 01:39 AM
Last Post: madscijr
  Be warned, I'm back! Unseen Machine 6 1,130 06-13-2025, 05:07 PM
Last Post: Unseen Machine
  Well after a month it's back to the drawing board. Pete 12 1,855 05-15-2025, 11:00 PM
Last Post: Kernelpanic
  Don't look now developers, but you've got a pat on your back! Pete 7 1,314 12-15-2024, 09:59 AM
Last Post: TempodiBasic
Thumbs Up He is come back! Kernelpanic 16 3,053 09-28-2022, 10:47 PM
Last Post: Kernelpanic

Forum Jump:


Users browsing this thread: 1 Guest(s)