Any C programmers wanna help convert code to convert between MIDI + CSV?
#41
@JRace
@TempodiBasic
@mnrvovrfc
@bplus
@Jack
@Kernelpanic
@DSMan195276
@Spriggsy

One of the challenges I am having, is knowing whether the code we're working with actually works. 
Converting a MIDI file to text, and back again doesn't seem to work, and I'm not sure if that's because the flavor of C the code is in, doesn't translate exactly to what the QB64's compiler expects, or what.
Maybe comparing it to other projects that do the same thing will help. 
I found a bunch in C and also VB6, Python, and other languages. 
I don't know if this will help anyone verify what the code should be doing, but it might help me!
Enjoy... 

VB6 code, module "midi.bas" contains a function "readMidiFile":
Advanced MIDI Programming / Utility Download zipped project file (82k)

Perl and C:
GitHub - robbie-cao / midicsv Download ZIP

haskell (for the brave):
GitHub - bburdette / ChordPredictor Download zipped project file (82k)

Outline of the MIDI file structure:
Outline of the Standard MIDI File Structure

GoLang:
GitHub - moutend / csv2midi Download zipped project file (82k)

MIDI disassembler in PureBasic:
Midi Disassembler - plus a toy - PureBasic Forums - English

MIDI compiler (in C):
GitHub - markc / midicomp Download zipped project file (82k)

VB6:
GitHub - crawfo / vba-midi-file Download zipped project file (82k)

Python:
GitHub - timwedde / py_midicsv Download zipped project file (82k)

More Python:
Python write_midifile Examples
Reply
#42
MID2ASC seems to be adding extra linefeeds.  Note below the lines which contain only " &00" or " &0c".
This example is from the MID2ASC output of Frank Zappa's "King Kong Itself", downloaded from the FreeMidi.org link you posted:
Code: (Select All)
BA    1  CR        0  TR  0  CH  1  ST &B0 &65
&00
BA    1  CR        0  TR  0  CH  1  ST &B0 &64
&00
BA    1  CR        0  TR  0  CH  1  ST &B0 &06
&0C
I took a quick look through the MID2ASC source code, but it is a compacted, uncommented mess.  Someone may have to step through it with a debugger to see where those linefeeds are being emitted.


ASC2MID doesn't seem to like those linefeeds.  If I remove the extra linefeeds and merge lines to look like this:
Code: (Select All)
BA    1  CR        0  TR  0  CH  1  ST &B0 &65 &00
BA    1  CR        0  TR  0  CH  1  ST &B0 &64 &00
BA    1  CR        0  TR  0  CH  1  ST &B0 &06 &0C
then ASC2MID will quietly produce a MIDI file from the ASCII file.
The MIDI file does not seem to be properly formed, though.  FFPlay complains and doesn't even try to play anything.  I do not currently have VLC set up, nor do I have any other means of listening to MIDI files.

I've compiled the programs with MinGW, Digital Mars, Open Watcom, Orange C, and Tiny C, all under Windows and all with no improvement.

The author's website (http://www.archduke.org/midi/index.html) mentions an unrelated program which was compiled & tested under Linux/Unix, so maybe these programs would work better under Linux?
Unfortunately, I am not currently set up to try compiling and testing under Linux.

Maybe it could work under Cygwin.  I tried compilation using my old, homebrew "portable" Cygwin setup, but GCC crashed badly, so no luck there.  (Well, my version of Cygwin is old.  I think I built it back in the WinXP days and don't think I've used it since.)

Anyway, this is all I can add ATM.   Undecided
Reply
#43
It should be relatively simple to convert the VBA MIDI file project as the language is extremely similar to QB. Only a few minor differences that I see.
Ask me about Windows API and maybe some Linux stuff
Reply
#44
Quote:One of the challenges I am having, is knowing whether the code we're working with actually works. 
Converting a MIDI file to text, and back again doesn't seem to work, and I'm not sure if that's because the flavor of C the code is in, doesn't translate exactly to what the QB64's compiler expects, or what.
Maybe comparing it to other projects that do the same thing will help. 
I found a bunch in C and also VB6, Python, and other languages. 
I don't know if this will help anyone verify what the code should be doing, but it might help me!
Enjoy... 


If I understand correctly, you want to convert a midi file to text, and then pass it to the C compiler in QB64. Wouldn't it be easier to add something to QB64 that could, for example, call up the media player?

I looked at your list of links again and saw something with VB6 and videos. I only have VB 5, VB 6 cannot be installed, but it also works with VB 5. I have now created a program like this.

I don't know if it helps. The source code:

Code: (Select All)
Option Explicit

Private Sub cmdAbspielen_Click()
  WindowsMediaPlayer1.URL = Text1.Text
End Sub

Private Sub cmdAnhalten_Click()
  WindowsMediaPlayer1.Close
End Sub

Private Sub cmdEnde_Click()
  End
End Sub

Private Sub cmdVideoeffenen_Click()

  Me.CommonDialog1.Filter = "WAV-Dateien (*.wav)|*.wav|" & "MP3-Dateien (*.mp3)|*.mp3|" & "MP4-Dateien (*.mp4)|*.mp4|" &   "Videodateien (*.avi)|*.avi|" & "MPEG-Dateien (*.mpg)|*.mpg|"
  CommonDialog1.ShowOpen
  Text1.Text = CommonDialog1.filename
End Sub

[Image: Video-mit-VB5-2022-09-01.jpg]
Reply
#45
(09-01-2022, 06:04 PM)Kernelpanic Wrote:
Quote:One of the challenges I am having, is knowing whether the code we're working with actually works. 
Converting a MIDI file to text, and back again doesn't seem to work, and I'm not sure if that's because the flavor of C the code is in, doesn't translate exactly to what the QB64's compiler expects, or what.
Maybe comparing it to other projects that do the same thing will help. 
I found a bunch in C and also VB6, Python, and other languages. 
I don't know if this will help anyone verify what the code should be doing, but it might help me!
Enjoy... 


If I understand correctly, you want to convert a midi file to text, and then pass it to the C compiler in QB64. Wouldn't it be easier to add something to QB64 that could, for example, call up the media player?

I looked at your list of links again and saw something with VB6 and videos. I only have VB 5, VB 6 cannot be installed, but it also works with VB 5. I have now created a program like this.

I don't know if it helps. The source code:

Code: (Select All)
Option Explicit

Private Sub cmdAbspielen_Click()
  WindowsMediaPlayer1.URL = Text1.Text
End Sub

Private Sub cmdAnhalten_Click()
  WindowsMediaPlayer1.Close
End Sub

Private Sub cmdEnde_Click()
  End
End Sub

Private Sub cmdVideoeffenen_Click()

  Me.CommonDialog1.Filter = "WAV-Dateien (*.wav)|*.wav|" & "MP3-Dateien (*.mp3)|*.mp3|" & "MP4-Dateien (*.mp4)|*.mp4|" &   "Videodateien (*.avi)|*.avi|" & "MPEG-Dateien (*.mpg)|*.mpg|"
  CommonDialog1.ShowOpen
  Text1.Text = CommonDialog1.filename
End Sub

[Image: Video-mit-VB5-2022-09-01.jpg]

Thanks for your reply. Let me explain one more time, since I seem to have confused a lot of people on what my goal is with this. 

I am ultimately looking to write some native QB64 functions that 

1. parse a MIDI file to translate it to a tab-delimited or CSV text file, which could then be easily loaded into Excel or Notepad++ or a custom QB64 editor, where the music can be edited or manipulated (or created from scratch). 

2. convert the MIDI text file to a real MIDI binary file, that can be played or edited by any MIDI compatible media player, sequencer, or DAW that can read MIDI files. 

3. ideally, it could take a MIDI file, translate it to text, and then translate the text back to MIDI, and the resulting MIDI will be exactly the same as the original MIDI file (maybe not byte for byte, but the music will play back the same and the file will be in the standard format)

I don't know the details of MIDI and don't really care to get too deep into it if I can help it (at least currently). I really just want to deal with a friendly text format that I can play with in Excel or my own QB64 programs, and make relatively simple songs in (maybe 1-4 instruments). 

This would be native cross-platform QB64 code, not C. 
The only reason C entered into this was I found existing programs written in C that translate between MIDI and CSV, and I am not a C programmer, and thought since QB64 comes with a C compiler that I might be able to use that to run the C code and play around with it, to understand what statements are doing so I can accurately translate it to QB64. 

With help from the friendly folks of the QB64 community, I was able to utilize QB64's built in C compiler to build working EXEs from the C code. So I can play with C - woohoo! Also there are other (non-MIDI) things I'm looking to do where I have example C code, so it will be similarly helpful for compiling and testing those in the future.

I don't really want to install a dedicated IDE for writing C programs, and being able to do some C with the existing QB64 installation appeals to me (no new installs!) Really, I'm even less interested in learning C than I am in learning MIDI, it's just a necessary evil since the code I found that does the MIDI parsing and translation was written in C! (Although I since found some VB6 code which, if it works, will surely be much much easier for me to work with, since I know and like old school VB6 and VBA,) 

The problem I had when I tested the MIDI<->CSV C code is that although I got it to run, it doesn't seem to be working for step 3 above, converting MIDI to text and back again. The re-converted MIDI file is messed up. That's where I left it, and since then folks have been discussing the finer points of C, but I have been busy and haven't gotten around to getting back into the details of translating it to QB64. Next I will look at the VB code and give that a try. 

I hope this explains everything. Thanks again...
Reply
#46
(09-01-2022, 06:58 PM)madscijr Wrote: :
2. convert the MIDI text file to a real MIDI binary file, that can be played or edited by any MIDI compatible media player, sequencer, or DAW that can read MIDI files. 
:
http://reaper.fm
This is as close as it could get between a MIDI file's representation of notes and CC's, and a MIDI event in this program. The song project file has extension RPP and is a text file. If you create a MIDI file within REAPER, save as RPP and look at the RPP file in a text editor, you'll notice close relations with a MIDI file. This information could be used to create a program that could at least inject MIDI text info into a DAW's project file. I've done that but I have to go look at my backups from three years ago and older LOL.

I have also written BASIC programs and Lua scripts that change other things into an RPP file, such as adding a track with a VST plug-in effect, or adding points to a volume envelope. Really exotic stuff for a musician into randomization schemes so his music isn't that great. Eh I'm a licensed user of v2 and v3, I like to support what I use and dislike "crippleware" music apps. I don't know the other half of how it is three versions later so check it out at your own risk...
Reply
#47
(09-02-2022, 12:29 AM)mnrvovrfc Wrote:
(09-01-2022, 06:58 PM)madscijr Wrote: :
2. convert the MIDI text file to a real MIDI binary file, that can be played or edited by any MIDI compatible media player, sequencer, or DAW that can read MIDI files. 
:
http://reaper.fm
This is as close as it could get between a MIDI file's representation of notes and CC's, and a MIDI event in this program. The song project file has extension RPP and is a text file. If you create a MIDI file within REAPER, save as RPP and look at the RPP file in a text editor, you'll notice close relations with a MIDI file. This information could be used to create a program that could at least inject MIDI text info into a DAW's project file. I've done that but I have to go look at my backups from three years ago and older LOL.

I have also written BASIC programs and Lua scripts that change other things into an RPP file, such as adding a track with a VST plug-in effect, or adding points to a volume envelope. Really exotic stuff for a musician into randomization schemes so his music isn't that great. Eh I'm a licensed user of v2 and v3, I like to support what I use and dislike "crippleware" music apps. I don't know the other half of how it is three versions later so check it out at your own risk...

How do you like Reaper? I have heard Reaper and FL Studio are good. I'm not too familiar with modern DAWs, for me simple tools work best for getting ideas down fast. I recorded with a cassette 4-track recorder back in the day, and am interested in trying modern a DAW. Can you recommend any with an easy learning curve, a small footprint on the PC, and preferably portable? Anyway, scripting for your DAW sounds pretty neat, if you want to pull out your code I would be interesting in giving it a look...
Reply
#48
I purposely created a MIDI file with four events, of different sizes, pitches and velocities so you could see more easily what is being wrapped into.

   
   
   
   
   

I'm sorry I couldn't do anything better than terminal "od" command to show the innards of the MIDI file. One thing to keep in mind is that in the RPP file, right after the "E" the "delta time" is stored in decimal, while everything in the screenshot of "od" output is in hexadecimal. Therefore the "delta time" is lost in the string of events in the listing by "od". The "delta time" is tricky, must be a sequence in which the very last byte of that sequence should be the only one not having the high-bit set, while the other bytes before it must be 128 or greater. The "delta time" determines the length and position of notes which is why things get complicated in a polyphonic or multi-track MIDI file.

The MIDI event is really comprised of four values: the "delta time", the event combined with the MIDI channel, the note pitch and finally the velocity. A MIDI note-on must be matched by a note-off later on in the sequence. It has to have the same lower-nibble for MIDI channel and the same MIDI note pitch so the processor knows which note to close. Usually the "delta time" is set to zero for MIDI note-on but is figured out for the note-off, while the velocity is usually set to zero for MIDI note-off.

Almost forgot to mention the MIDI "event" could be note-on, note-off, continuous controller (CC), pitch bend, aftertouch or system-exclusive. The latter is a whole new realm LOL. For example "90" in hex means MIDI note-on for channel 1, and "BB" means MIDI CC for channel 12.

Yes the DAW pictures are ugly... as I've said, it's an early version of REAPER, and it's on Fedora 36 MATE.  :/
Reply
#49
(09-02-2022, 05:01 PM)mnrvovrfc Wrote: I purposely created a MIDI file with four events, of different sizes, pitches and velocities so you could see more easily what is being wrapped into.







I'm sorry I couldn't do anything better than terminal "od" command to show the innards of the MIDI file. One thing to keep in mind is that in the RPP file, right after the "E" the "delta time" is stored in decimal, while everything in the screenshot of "od" output is in hexadecimal. Therefore the "delta time" is lost in the string of events in the listing by "od". The "delta time" is tricky, must be a sequence in which the very last byte of that sequence should be the only one not having the high-bit set, while the other bytes before it must be 128 or greater. The "delta time" determines the length and position of notes which is why things get complicated in a polyphonic or multi-track MIDI file.

The MIDI event is really comprised of four values: the "delta time", the event combined with the MIDI channel, the note pitch and finally the velocity. A MIDI note-on must be matched by a note-off later on in the sequence. It has to have the same lower-nibble for MIDI channel and the same MIDI note pitch so the processor knows which note to close. Usually the "delta time" is set to zero for MIDI note-on but is figured out for the note-off, while the velocity is usually set to zero for MIDI note-off.

Almost forgot to mention the MIDI "event" could be note-on, note-off, continuous controller (CC), pitch bend, aftertouch or system-exclusive. The latter is a whole new realm LOL. For example "90" in hex means MIDI note-on for channel 1, and "BB" means MIDI CC for channel 12.

Yes the DAW pictures are ugly... as I've said, it's an early version of REAPER, and it's on Fedora 36 MATE.  :/

Thanks for breaking it down into such simple terms. Some of that is insanely particular, like the last byte needing to have no high bit, and certain values in decimal and others in hex. Ugly! But I guess what a text translator does is smooth out that nonsense and make it simple & easier to interface with. 

So how do you like Reaper, as a musician?
Reply
#50
Hi QB64 friends
I have followed some links posted by MadSciJr
and I have found very interesting and reach of information the published work of Utah in VB6
moreover many links have talked about a unfoundable QMIDI.BAS written in Qbasic.

After different googled researches I got it...
Here it is and I post it in Codebox tool

Code: (Select All)
'538 lines (511 sloc)  20.5 KB

'QMIDI v3.0
'QBasic/QuickBASIC background MIDI player
'by Jesse Dorland (jessedorland@hotmail.com)
'Released to the Public Domain, March 1998
'
'See SAMPLE.BAS for a demo, and be sure to read the documentation before
'using QMIDI, even if you're familiar with previous versions..
'
'Acknowledgements:
'   Mike Huff      - author of the QuickBASIC MIDI code that QMIDI is based
'                    on.
'   Rick Elbers    - author of the QBasic Interrupt routine that was used in
'                    version 1.0.
'   Angelo Mottola - contributed the new Pause/Resume code.
'
'If you decide to use QMIDI, please tell me what you think of it and
'give me some ideas for improvements.  My e-mail address is at the top of
'the code and in the documentation.
'
'-Jesse
'
'DISCLAIMER
'----------
'If your computer suffers crashes, loss of data, or is taken over by
'smurfs as a result of using QMIDI, I won't be responsible for it.
'Reading the documentation should ensure safe operation, but I'm not
'making any guarantees.  You have been warned....

'**** START OF CODE ****

'Returns the amount of memory required to load a MIDI file
DECLARE FUNCTION BytesRequired& (Filename$)
'Detects whether the supplied sound drivers are loaded
DECLARE SUB DriversLoaded (SBMIDI%, SBSIM%)
'Internal functon used by DriversLoaded
DECLARE SUB GetIntVector (IntNum%, Segment%, Offset%)
'Loads and plays a file using SBSIM
DECLARE SUB LoadAndPlayLarge (Filename$)
'Loads and plays a file using SBMIDI
DECLARE SUB LoadAndPlayMIDI (Filename$, MIDISegment%, MIDIOffset%)
'Loads a file using SBSIM
DECLARE SUB LoadLarge (Filename$)
'Loads a file using SBMIDI
DECLARE SUB LoadMIDI (Filename$, MIDISegment%, MIDIOffset%)
'Returns the last error that occurred (if any)
DECLARE FUNCTION MIDIError$ ()
'Pauses a file that is currently playing
DECLARE SUB PauseMIDI ()
'Resumes playing a file after it has been paused
DECLARE SUB ResumeMIDI ()
'Plays a file loaded with LoadLarge
DECLARE SUB PlayLarge ()
'Plays a file loaded with LoadMIDI
DECLARE SUB PlayMIDI (MIDISegment%, MIDIOffset%)
'Stops any MIDI file that is currently playing.
DECLARE SUB StopMIDI ()
'Returns the amount of time a file has been playing
DECLARE FUNCTION TimeMIDI! ()

'The following code is REQUIRED in programs that use QMIDI in order for
'all features to work properly.

Dim Shared MIDI.PLAYTIME As Single
Dim Shared MIDI.ERROR As Integer
Dim Shared MIDI.LOADED As Integer
Dim Shared SBMIDI.INTERRUPT As Integer
Dim Shared SBSIM.INTERRUPT As Integer
MIDI.PLAYTIME = 0
MIDI.ERROR = 0
MIDI.LOADED = 0
SBMIDI.INTERRUPT = &H80
SBSIM.INTERRUPT = &H81

'Insert your own code beginning here.


'The data statements below are REQUIRED in programs that use the
'DriversLoaded routine.  Do not edit any of the data; it will cause
'the routine to return inaccurate information.
SBMIDIData:
Data 156,30,6,80,83,81,82,87,86,85,139,236,80,184,14,16,142,216,142,192,88,131,78,24,1,199,70,12,255,255,128,62
Data 68,1,0,117,59,198,6,68,1,1,251,252,11,219,120,21,129,251,13,0,115,37,131,102,24,254,209,227,255,151,40,0
Data 137,70,12,235,22,247,219,75,129,251,3,0,115,13,131,102,24,254,209,227,255,151,34,0,137,70,12,198,6,68,1,0
Data 93,94,95,90,89,91,88,7,31,157,207,156,250,30,6,80,184,14,16,142,216,142,192,161,145,1,1,6,28,0,114,6
Data 176,32,230,32,235,9,255,6,28,0,156,255,30,18,0,83,81,82,87,86,85,139,236,250,128,62,67,1,0,117,54,140
Data 22,32,0,137,38,30,0,140,216,142,208,188,66,1,198,6,67,1,1,251,252,131,62,133,1,0,116,10,128,62,21,3
Data 0,117,3,232,143,4,250,139,38,30,0,142,22,32,0,198,6,67,1,0,251,93,94,95,90,89,91,88,7,31,157,207
Data 30,6,80,83,81,82,87,86,85,156,184,14,16,142,216,142,192,228,96,10,192,120,18,60,83,117,14,180,2,205,22,36
SBSIMData:
Data 46,143,6,134,1,46,143,6,132,1,46,143,6,130,1,46,131,14,130,1,1,46,131,62,110,1,0,116,6,184,1,0
Data 233,193,0,128,255,5,119,25,10,255,116,27,81,80,51,192,138,207,128,225,127,249,211,208,46,35,6,126,1,88,89,117
Data 6,184,2,0,233,157,0,250,46,140,30,138,1,14,31,140,22,140,1,137,38,142,1,142,22,152,1,139,38,154,1,85
Data 139,236,86,87,6,83,163,144,1,137,22,148,1,137,14,146,1,180,98,205,33,137,30,150,1,180,80,140,203,205,33,199
Data 6,110,1,1,0,251,252,50,192,255,30,118,1,139,94,248,83,134,251,3,219,3,219,50,255,254,14,124,1,117,11,129
Data 6,152,1,0,0,198,6,124,1,20,139,243,91,50,255,30,7,255,156,176,1,114,5,131,38,130,1,254,250,199,6,110
Data 1,0,0,80,180,80,139,30,150,1,205,33,88,91,7,95,94,139,229,93,139,14,146,1,142,22,140,1,139,38,142,1
Data 142,30,138,1,46,255,54,130,1,46,255,54,132,1,46,255,54,134,1,207,85,139,236,131,236,10,137,70,248,137,94,250

'BytesRequired - Returns the amount of memory needed to store a file.
Function BytesRequired& (Filename$)
    'Open the file.
    FF% = FreeFile
    Open Filename$ For Binary As #FF%
    'Store the length of the file.
    FileLen& = LOF(FF%)
    'Close the file.
    Close FF%
    'If the length of the file is 0, assume it does not exist and delete it.
    If FileLen& = 0 Then Kill Filename$
    'Return the length of the file as the number of bytes required.
    BytesRequired& = FileLen&
End Function

DefInt A-Z
Sub DriversLoaded (SBMIDI%, SBSIM%) Static

    'Check the interrupt handlers for int 80h-89h to see if they contain
    'program code from either SBSIM or SBMIDI.  Only those 10 interrupts
    'are checked because chances are slim that either driver will be loaded
    'at int 8Ah or higher, and they will never load themselves below
    '80h.

    For I% = &H80 To &H8A
        'Get the address of the interrupt handler.
        GetIntVector I%, Segment%, Offset%
        'If the address is null, then the interrupt is not in use, and can be
        'skipped.
        If Segment% = 0 And Offset% = 0 Then GoTo Skip:

        'Point to the segment of the interrupt handler.
        Def Seg = Segment%
        Restore SBMIDIData:
        For J% = 0 To 255
            'Read a byte of the SBMIDI program code that has been previously
            'saved.
            Read Byte1%
            'Read a byte of code from the current interrupt handler.
            Byte2% = Peek(Offset% + J%)
            'Do they match?  If so, we may have found SBMIDI!
            If Byte1% = Byte2% Then
                MatchSBMIDI% = 1
                'If not, this interrupt is definitely not occupied by SBMIDI.
            Else
                Select Case J%
                    Case Is = 14, 15, 113, 114, 235, 236
                    Case Else
                        MatchSBMIDI% = 0
                        Exit For
                End Select
            End If
        Next J%
        'If there's a match, set SBMIDI% to the current interrupt.
        If MatchSBMIDI% Then SBMIDI% = I%
        Restore SBSIMData:
        For J% = 0 To 255
            'Read a byte of the SBSIM program code that has been previously
            'saved.
            Read Byte1%
            'Read a byte of code from the current interrupt handler.
            Byte2% = Peek(Offset% + J%)
            'If the values match, SBSIM may be loaded at this interrupt.
            If Byte1% = Byte2% Then
                MatchSBSIM% = 1
                'If not, then it isn't.
            Else
                MatchSBSIM% = 0
                Exit For
            End If
        Next J%
        'If this interrupt handler is a match, set SBSIM% to the current
        'interrupt number.
        If MatchSBSIM% Then SBSIM% = I%

        'If both SBMIDI% and SBSIM% have been detected, there's no need to
        'check the rest of the interrupt handling routines.
        If MatchSBMIDI% And MatchSBSIM% Then Exit For
        Skip:
    Next I%
End Sub

Sub GetIntVector (IntNum%, Segment%, Offset%) Static
    'If the code hasn't been loaded already, do it now.
    If GetIntVCodeLoaded% = 0 Then
        asm$ = asm$ + Chr$(&H55)
        asm$ = asm$ + Chr$(&H89) + Chr$(&HE5)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&HA)
        asm$ = asm$ + Chr$(&H8A) + Chr$(&H7)
        asm$ = asm$ + Chr$(&HB4) + Chr$(&H35)
        asm$ = asm$ + Chr$(&HCD) + Chr$(&H21)
        asm$ = asm$ + Chr$(&H8C) + Chr$(&HC1)
        asm$ = asm$ + Chr$(&H89) + Chr$(&HDA)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H8)
        asm$ = asm$ + Chr$(&H89) + Chr$(&HF)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H6)
        asm$ = asm$ + Chr$(&H89) + Chr$(&H17)
        asm$ = asm$ + Chr$(&H5D)
        asm$ = asm$ + Chr$(&HCB)
        asm$ = asm$ + Chr$(&H34) + Chr$(&H0)
        asm$ = asm$ + Chr$(&H60)
        asm$ = asm$ + Chr$(&H23) + Chr$(&H0)
        GetIntVCodeLoaded% = 1
    End If
    'Execute the code
    Def Seg = VarSeg(asm$)
    Call Absolute(IntNum%, Segment%, Offset%, SAdd(asm$))
End Sub

DefSng A-Z
Sub LoadAndPlayLarge (Filename$) Static
    'See if an extension was supplied, and if not, add one.
    If InStr(Filename$, ".") = 0 Then Filename$ = Filename$ + ".MID"
    'The sound driver needs an ASCIIZ string (a string that ends with
    'character 0) for a file name.
    If Right$(Filename$, 1) <> Chr$(0) Then Filename$ = Filename$ + Chr$(0)
    'Initialize the MIDI driver and load the file in memory
    If LoadCodeLoaded% = 0 Then
        Lasm$ = ""
        Lasm$ = Lasm$ + Chr$(&H55)
        Lasm$ = Lasm$ + Chr$(&H89) + Chr$(&HE5)
        Lasm$ = Lasm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H8)
        Lasm$ = Lasm$ + Chr$(&H8B) + Chr$(&H7)
        Lasm$ = Lasm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H6)
        Lasm$ = Lasm$ + Chr$(&H8B) + Chr$(&H17)
        Lasm$ = Lasm$ + Chr$(&HBB) + Chr$(&H0) + Chr$(&H5)
        Lasm$ = Lasm$ + Chr$(&HCD) + Chr$(SBSIM.INTERRUPT)
        Lasm$ = Lasm$ + Chr$(&H5D)
        Lasm$ = Lasm$ + Chr$(&HCB)
        LoadCodeLoaded% = 1
    End If
    Def Seg = VarSeg(Lasm$)
    Offset% = SAdd(Lasm$)
    Call Absolute(SAdd(Filename$), VarSeg(Filename$), Offset%)

    'Start the music!!
    If PlayCodeLoaded% = 0 Then
        Pasm$ = ""
        Pasm$ = Pasm$ + Chr$(&HB8) + Chr$(&H1) + Chr$(&H0)
        Pasm$ = Pasm$ + Chr$(&HBA) + Chr$(&H1) + Chr$(&H0)
        Pasm$ = Pasm$ + Chr$(&HBB) + Chr$(&H1) + Chr$(&H5)
        Pasm$ = Pasm$ + Chr$(&HCD) + Chr$(SBSIM.INTERRUPT)
        Pasm$ = Pasm$ + Chr$(&HCB)
        PlayCodeLoaded% = 1
    End If
    Def Seg = VarSeg(Pasm$)
    Offset% = SAdd(Pasm$)
    Call Absolute(Offset%)
    MIDI.LOADED = 1
    MIDI.PLAYTIME = Timer
End Sub

'LoadAndPlayMIDI - Loads a MIDI file into memory and plays it
Sub LoadAndPlayMIDI (Filename$, MIDISegment%, MIDIOffset%)
    'See if an extension was supplied, and if not, add one.
    If InStr(Filename$, ".") = 0 Then Filename$ = Filename$ + ".MID"
    'Open the file
    FF% = FreeFile
    Open Filename$ For Binary As #FF%
    FileLen& = LOF(FF%)
    Close #FF%
    'If the file is empty, delete it and exit now.
    If FileLen& = 0 Then Kill Filename$: MIDI.ERROR = 1: Exit Sub
    'If the file is too large, exit now.
    If FileLen& > 65536 Then MIDI.ERROR = 2: Exit Sub
    'Make the filename an ASCIIZ string.
    Filename$ = Filename$ + Chr$(0)
    'Check if the MIDI loading code has already been loaded;
    'if not, do it now.
    If LoadCodeLoaded% = 0 Then
        asm1$ = asm1$ + Chr$(&H1E)
        asm1$ = asm1$ + Chr$(&H55)
        asm1$ = asm1$ + Chr$(&H89) + Chr$(&HE5)
        asm1$ = asm1$ + Chr$(&HB8) + Chr$(&H0) + Chr$(&H3D)
        asm1$ = asm1$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&HE)
        asm1$ = asm1$ + Chr$(&H8B) + Chr$(&H17)
        asm1$ = asm1$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H10)
        asm1$ = asm1$ + Chr$(&H8E) + Chr$(&H1F)
        asm1$ = asm1$ + Chr$(&HCD) + Chr$(&H21)
        asm1$ = asm1$ + Chr$(&H89) + Chr$(&HC6)
        asm1$ = asm1$ + Chr$(&HB4) + Chr$(&H3F)
        asm1$ = asm1$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H8)
        asm1$ = asm1$ + Chr$(&H8B) + Chr$(&HF)
        asm1$ = asm1$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&HA)
        asm1$ = asm1$ + Chr$(&H8B) + Chr$(&H17)
        asm1$ = asm1$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&HC)
        asm1$ = asm1$ + Chr$(&H8E) + Chr$(&H1F)
        asm1$ = asm1$ + Chr$(&H89) + Chr$(&HF3)
        asm1$ = asm1$ + Chr$(&HCD) + Chr$(&H21)
        asm1$ = asm1$ + Chr$(&HB4) + Chr$(&H3E)
        asm1$ = asm1$ + Chr$(&HCD) + Chr$(&H21)
        asm1$ = asm1$ + Chr$(&H5D)
        asm1$ = asm1$ + Chr$(&H1F)
        asm1$ = asm1$ + Chr$(&HCA) + Chr$(&HA) + Chr$(&H0)
        LoadCodeLoaded% = 1
    End If
    'Call the assembly language routine.
    Def Seg = VarSeg(asm1$)
    Call Absolute(VarSeg(Filename$), SAdd(Filename$), MIDISegment%, MIDIOffset%, &HFFFF, SAdd(asm1$))
    'Check to see if the MIDI playing code has previously been loaded.
    'If not, load it now.
    If PlayCodeLoaded% = 0 Then
        'Load the machine codes into a string.
        asm2$ = asm2$ + Chr$(&H55)
        asm2$ = asm2$ + Chr$(&H89) + Chr$(&HE5)
        asm2$ = asm2$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H8)
        asm2$ = asm2$ + Chr$(&H8B) + Chr$(&H17)
        asm2$ = asm2$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H6)
        asm2$ = asm2$ + Chr$(&H8B) + Chr$(&H7)
        asm2$ = asm2$ + Chr$(&HBB) + Chr$(&H4) + Chr$(&H0)
        asm2$ = asm2$ + Chr$(&HCD) + Chr$(SBMIDI.INTERRUPT)
        asm2$ = asm2$ + Chr$(&HBB) + Chr$(&H5) + Chr$(&H0)
        asm2$ = asm2$ + Chr$(&HCD) + Chr$(SBMIDI.INTERRUPT)
        asm2$ = asm2$ + Chr$(&H5D)
        asm2$ = asm2$ + Chr$(&HCA) + Chr$(&H4) + Chr$(&H0)
        'Indicate that the code has been loaded.
        PlayCodeLoaded% = 1
    End If
    'Call the machine language routine to play the music.
    Def Seg = VarSeg(asm2$)
    Call Absolute(MIDISegment%, MIDIOffset%, SAdd(asm2$))
    'Start the MIDI timer.
    MIDI.PLAYTIME = Timer
End Sub

Sub LoadLarge (Filename$) Static
    'See if an extension was supplied, and if not, add one.
    If InStr(Filename$, ".") = 0 Then Filename$ = Filename$ + ".MID"
    'The sound driver needs an ASCIIZ string (a string that ends with
    'character 0) for a file name.
    If Right$(Filename$, 1) <> Chr$(0) Then Filename$ = Filename$ + Chr$(0)
    'Initialize the MIDI driver and load the file in memory
    If CodeLoaded% = 0 Then
        asm$ = ""
        asm$ = asm$ + Chr$(&H55)
        asm$ = asm$ + Chr$(&H89) + Chr$(&HE5)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H8)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H7)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H6)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H17)
        asm$ = asm$ + Chr$(&HBB) + Chr$(&H0) + Chr$(&H5)
        asm$ = asm$ + Chr$(&HCD) + Chr$(SBSIM.INTERRUPT)
        asm$ = asm$ + Chr$(&H5D)
        asm$ = asm$ + Chr$(&HCB)
        CodeLoaded% = 1
    End If
    Def Seg = VarSeg(asm$)
    Offset% = SAdd(asm$)
    Call Absolute(SAdd(Filename$), VarSeg(Filename$), Offset%)
    MIDI.LOADED = 1
End Sub

'LoadMIDI - loads a MIDI file into memory
Sub LoadMIDI (Filename$, MIDISegment%, MIDIOffset%) Static
    'See if an extension was supplied, and if not, add one.
    If InStr(Filename$, ".") = 0 Then Filename$ = Filename$ + ".MID"
    'Open the file
    FF% = FreeFile
    Open Filename$ For Binary As #FF%
    FileLen& = LOF(FF%)
    Close #FF%
    'If the file is empty, delete it and exit now.
    If FileLen& = 0 Then Kill Filename$: MIDI.ERROR = 1: Exit Sub
    'If the file is too large, exit now.
    If FileLen& > 65536 Then MIDI.ERROR = 2: Exit Sub
    'Make the filename an ASCIIZ string.
    Filename$ = Filename$ + Chr$(0)
    'Check if the assembly language code has already been loaded;
    'if not, do it now.
    If CodeLoaded% = 0 Then
        asm$ = asm$ + Chr$(&H1E)
        asm$ = asm$ + Chr$(&H55)
        asm$ = asm$ + Chr$(&H89) + Chr$(&HE5)
        asm$ = asm$ + Chr$(&HB8) + Chr$(&H0) + Chr$(&H3D)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&HE)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H17)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H10)
        asm$ = asm$ + Chr$(&H8E) + Chr$(&H1F)
        asm$ = asm$ + Chr$(&HCD) + Chr$(&H21)
        asm$ = asm$ + Chr$(&H89) + Chr$(&HC6)
        asm$ = asm$ + Chr$(&HB4) + Chr$(&H3F)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H8)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&HF)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&HA)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H17)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&HC)
        asm$ = asm$ + Chr$(&H8E) + Chr$(&H1F)
        asm$ = asm$ + Chr$(&H89) + Chr$(&HF3)
        asm$ = asm$ + Chr$(&HCD) + Chr$(&H21)
        asm$ = asm$ + Chr$(&HB4) + Chr$(&H3E)
        asm$ = asm$ + Chr$(&HCD) + Chr$(&H21)
        asm$ = asm$ + Chr$(&H5D)
        asm$ = asm$ + Chr$(&H1F)
        asm$ = asm$ + Chr$(&HCA) + Chr$(&HA) + Chr$(&H0)
        CodeLoaded% = 1
    End If
    'Call the assembly language routine.
    Def Seg = VarSeg(asm$)
    Call Absolute(VarSeg(Filename$), SAdd(Filename$), MIDISegment%, MIDIOffset%, &HFFFF, SAdd(asm$))
End Sub

Function MIDIError$
    Select Case MIDI.ERROR
        Case 0: MIDIError$ = "NO ERROR"
        Case 1: MIDIError$ = "FILE CONTAINS NO DATA"
        Case 2: MIDIError$ = "FILE IS TOO LARGE"
        Case 3: MIDIError$ = "NO MIDI FILE PLAYING"
        Case Else: MIDIError$ = "UNKNOWN ERROR"
    End Select
End Function

'PauseMIDI - Pauses a MIDI file that is currently playing
Sub PauseMIDI
    'If no MIDI file is playing, exit now
    If MIDI.PLAYTIME = 0 Then Exit Sub
    'Pause the music.
    If CodeLoaded% = 0 Then
        asm$ = ""
        asm$ = asm$ + Chr$(&HB8) + Chr$(&H1) + Chr$(&H0)
        asm$ = asm$ + Chr$(&HBA) + Chr$(&H1) + Chr$(&H0)
        asm$ = asm$ + Chr$(&HBB) + Chr$(&H3) + Chr$(&H5)
        asm$ = asm$ + Chr$(&HCD) + Chr$(SBSIM.INTERRUPT)
        asm$ = asm$ + Chr$(&HCB)
        CodeLoaded% = 1
    End If
    Def Seg = VarSeg(asm$)
    Offset% = SAdd(asm$)
    Call Absolute(Offset%)
    'Save the number of seconds the MIDI file has been playing.
    PAUSED = TimeMIDI!
    'If it hasn't been playing long enough for TimeMIDI! to return
    'a value greater than 0, change PAUSED to a tiny positive value.
    If PAUSED = 0! Then PAUSED = .00001
    'Indicate that the file has stopped playing.
    MIDI.PLAYTIME = 0
End Sub

Sub PlayLarge Static
    'If no MIDI file is loaded, exit now
    If MIDI.LOADED = 0 Then Exit Sub
    'Start the music!!
    If CodeLoaded% = 0 Then
        asm$ = ""
        asm$ = asm$ + Chr$(&HB8) + Chr$(&H1) + Chr$(&H0)
        asm$ = asm$ + Chr$(&HBA) + Chr$(&H1) + Chr$(&H0)
        asm$ = asm$ + Chr$(&HBB) + Chr$(&H1) + Chr$(&H5)
        asm$ = asm$ + Chr$(&HCD) + Chr$(SBSIM.INTERRUPT)
        asm$ = asm$ + Chr$(&HCB)
        CodeLoaded% = 1
    End If
    Def Seg = VarSeg(asm$)
    Offset% = SAdd(asm$)
    Call Absolute(Offset%)
    MIDI.PLAYTIME = Timer
End Sub

'PlayMIDI - Begins playing a MIDI file in the background.
Sub PlayMIDI (MIDISegment%, MIDIOffset%) Static
    'Check to see if the MIDI playing code has previously been loaded.
    'If not, load it now.
    If CodeLoaded% = 0 Then
        'Load the machine codes into a string.
        asm$ = asm$ + Chr$(&H55)
        asm$ = asm$ + Chr$(&H89) + Chr$(&HE5)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H8)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H17)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H5E) + Chr$(&H6)
        asm$ = asm$ + Chr$(&H8B) + Chr$(&H7)
        asm$ = asm$ + Chr$(&HBB) + Chr$(&H4) + Chr$(&H0)
        asm$ = asm$ + Chr$(&HCD) + Chr$(&H80)
        asm$ = asm$ + Chr$(&HBB) + Chr$(&H5) + Chr$(&H0)
        asm$ = asm$ + Chr$(&HCD) + Chr$(&H80)
        asm$ = asm$ + Chr$(&H5D)
        asm$ = asm$ + Chr$(&HCA) + Chr$(&H4) + Chr$(&H0)
        'Indicate that the code has been loaded.
        CodeLoaded% = 1
    End If
    'Call the machine language routine to play the music.
    Def Seg = VarSeg(asm$)
    Offset% = SAdd(asm$)
    Call Absolute(MIDISegment%, MIDIOffset%, Offset%)
    'Start the MIDI timer.
    MIDI.PLAYTIME = Timer
End Sub

'ResumeMIDI - Starts playing a MIDI file after it has been paused
Sub ResumeMIDI
    'If no MIDI file is paused, exit now
    If PAUSED = 0! Then Exit Sub
    'Resume playing.
    If CodeLoaded% = 0 Then
        asm$ = ""
        asm$ = asm$ + Chr$(&HB8) + Chr$(&H1) + Chr$(&H0)
        asm$ = asm$ + Chr$(&HBA) + Chr$(&H1) + Chr$(&H0)
        asm$ = asm$ + Chr$(&HBB) + Chr$(&H4) + Chr$(&H5)
        asm$ = asm$ + Chr$(&HCD) + Chr$(SBSIM.INTERRUPT)
        asm$ = asm$ + Chr$(&HCB)
        CodeLoaded% = 1
    End If
    Def Seg = VarSeg(asm$)
    Offset% = SAdd(asm$)
    Call Absolute(Offset%)
    MIDI.PLAYTIME = Timer - PAUSED
    PAUSED = 0!
End Sub

'StopMIDI - Stops playing MIDI file
Sub StopMIDI
    'Stop the music!!
    asm$ = asm$ + Chr$(&HBB) + Chr$(&H4) + Chr$(&H0)
    asm$ = asm$ + Chr$(&HCD) + Chr$(SBMIDI.INTERRUPT)
    asm$ = asm$ + Chr$(&HCB)
    'These next commented lines are for using the converted code.
    Def Seg = VarSeg(asm$)
    Offset% = SAdd(asm$)
    Call Absolute(Offset%)
    'No MIDI file is playing, so reset the timer
    MIDI.PLAYTIME = 0
End Sub

Function TimeMIDI!
    'If a MIDI file is paused, lock the current playing time
    If PAUSED > 0! Then
        TimeMIDI! = PAUSED
        'If a MIDI file is playing, carry out the timing routine
    ElseIf MIDI.PLAYTIME Then
        'Get the current time
        CurrentTime! = Timer
        'If midnight has come since the MIDI file started playing, change
        'CurrentTime! accordingly
        If CurrentTime! - MIDI.PLAYTIME < 0 Then
            CurrentTime! = 86400 + CurrentTime!
        End If
        'Get the final result
        TimeMIDI! = CurrentTime! - MIDI.PLAYTIME
    Else
        MIDI.ERROR = 3
    End If
End Function

you can got it from here or if you prefer from https://github.com/creationix/basic-game.../QMIDI.BAS

If you copy and paste the code into QB64 IDE you get this error tha I don't know how to solve to try to run the code.
Any suggestions?


Attached Files Image(s)
   
Reply




Users browsing this thread: 30 Guest(s)