Posted: Mon Dec 17, 2007 11:27 pm
I don't know anything about MIDI standards but this link might help.
http://www.midi.org/about-midi/dls/dls2spec.shtml
http://www.midi.org/about-midi/dls/dls2spec.shtml
http://www.purebasic.com
https://www.purebasic.fr/english/
Code: Select all
;USES TIMER TO PLAY MIDI EVENTS
;CREATED ON 12/15/2007
Global Dim ticks(10)
Global Dim note(10)
Global Dim volume(10)
Global Dim channel(10)
Global system_tick, midi_event
Global Dim Instrument(16)
Global Dim Is_MIDI(16)
Global channel
Global note, previous_note
Global hMidiOut
Procedure.l Roundoff(number.f) ;THE PROPER WAY TO ROUND A NUMBER
If number - Int(number) >= 0.5
ProcedureReturn number.f + 0.1
EndIf
ProcedureReturn number.f
EndProcedure
Procedure MidiOutMessage(hMidi,iStatus,iChannel,iData1,iData2)
dwMessage = iStatus | iChannel | (iData1 << 8 ) | (iData2 << 16)
ProcedureReturn midiOutShortMsg_(hMidi, dwMessage) ;
EndProcedure
Procedure SetInstrument(channel, instrument)
MidiOutMessage(hMidiOut, $C0, channel, instrument, 0)
EndProcedure
Procedure StopNote(channel, note)
MidiOutMessage(hMidiOut, $90, channel, Note, 0)
EndProcedure
Procedure PlayNote(channel, note, volume)
If Is_MIDI(channel) <> 0
StopNote(channel, previous_note)
MidiOutMessage(hMidiOut, $90, channel, note , volume)
Else
;PLAY WAV SAMPLE
EndIf
EndProcedure
Procedure tick() ;THIS ROUTINE IS INVOKED BY THE TIMER
While ticks(midi_event) = system_tick
PlayNote(channel(midi_event), note(midi_event), volume(midi_event))
;Debug(Str(midi_event) + " " + Str(system_tick))
midi_event = midi_event + 1
Wend
system_tick = system_tick + 1
EndProcedure
InitSound()
result = LoadSound(129, "C:\Programs\PureBasic 4.10\source\Flugelhorn_5C_Tuned.wav")
If result = 0: MessageRequester("Error", "Unable to open MIDI output.", #MB_ICONERROR): End: EndIf
result = LoadSound(130, "C:\Programs\PureBasic 4.10\source\Flugelhorn_5C_Tuned.wav")
If result = 0: MessageRequester("Error", "Unable to open MIDI output.", #MB_ICONERROR): End: EndIf
timesig$ = "4/4"
one_beat = Val(Right(timesig$, 1))
one_beat = one_beat / 4
bpm.f = 60
beat.f = (60 / bpm) * 1000 ;MILLISECONDS
Global timebase.f = (beat * 0.0625 * one_beat) / 2 ;ABLE TO RESOLVE DOTTED SIXTY-FOURTH NOTE
;SIXTY-FOURTH NOTE = 2 * timebase
int_timebase = Roundoff(timebase)
dwhole = 192 ;TICKS
whole = 128 ;TICKS
dhalf = 96 ;TICKS
half = 64 ;TICKS
dquarter = 48 ;TICKS
quarter = 32 ;TICKS
deighth = 24 ;TICKS
eighth = 16 ;TICKS
dsixteenth = 12 ;TICKS
sixteenth = 8 ;TICKS
dthirsec = 6 ;TICKS
thirsec = 4 ;TICKS
dsixfour = 3 ;TICKS
sixfour = 2 ;TICKS
system_tick = 1
;DATA FOR A COUPLE OF NOTES
ticks(1) = 20: note(1) = 66: volume(1) = 50: channel(1) = 1
ticks(2) = 20: note(2) = 66: volume(2) = 50: channel(2) = 2
ticks(3) = 20 + quarter: note(3) = 66: volume(3) = 0: channel(3) = 1
ticks(4) = 20 + quarter: note(4) = 66: volume(4) = 0: channel(4) = 2
ticks(5) = 20 + quarter: note(5) = 68: volume(5) = 50: channel(5) = 1
ticks(6) = 20 + quarter: note(6) = 68: volume(6) = 50: channel(6) = 2
ticks(7) = 20 + quarter + quarter: note(7) = 68: volume(7) = 0: channel(7) = 1
ticks(8) = 20 + quarter + quarter: note(8) = 68: volume(8) = 0: channel(8) = 2
midi.MIDIOUTCAPS
devices = midiOutGetNumDevs_()
For devnum = - 1 To devices - 1
If midiOutGetDevCaps_(devnum,@midi,SizeOf(MIDIOUTCAPS))=0
If midi\wVoices > 0
midiport=devnum
EndIf
EndIf
Next
*hMidiOut.l
If midiOutOpen_(@hMidiOut,midiport,0,0,0) <> #MMSYSERR_NOERROR
MessageRequester("Error", "Unable to open MIDI output.", #MB_ICONERROR)
End
EndIf
;SET INSTRUMENTS
instrument(1) = 65
instrument(2) = 10
For channel = 1 To 2
If instrument(channel) <= 128
SetInstrument(channel, instrument(channel))
Is_MIDI(channel) = 1
Else
Is_MIDI(channel) = 0
EndIf
Next
midi_event = 1
If OpenWindow(1,0,0,200,100,"MIDI Ticks",#PB_Window_SystemMenu|#PB_Window_ScreenCentered) = 0
MessageRequester("Error", "Unable to open window.", #MB_ICONERROR)
End
EndIf
;BEGIN MIDI PLAYBACK
tick_clock = SetTimer_(WindowID(1), 1, int_timebase, @tick())
Repeat
Window_Event = WaitWindowEvent()
Until Window_Event = #PB_Event_CloseWindow
midiOutClose_(@hMidiOut)
If tick_clock
KillTimer_(WindowID(1), tick_clock)
EndIf
FreeSound(129)
FreeSound(130)
End
Code: Select all
t,120
i1,57
i2,10
start
wt,100,c4
hn,100,c4
qr
Hn,100,a3C4e4,a5C6e6
end
I don't follow you. For anyone who knows basic music notation, the note pitches and durations are straightforward. Do you mean constants for the chords? That would fit well into the parser. Chords would be prepended with some symbol and then have something like a7s for A minor seventh suspended.superadnim wrote:Just an idea, why not support constants for the input files?, you can search&replace using a hash list or something similar, that way you don't have to memorize all the codes (doesn't have to be mandatory, you could still use the enumerations)
Code: Select all
ht,100,a4C5e5,a4C5e5
ht,100,a4C5e5,a4C5e5
hn,100,a4C5e5,a4C5e5
Code: Select all
Procedure StopNote(channel, note)
MidiOutMessage(hMidiOut, $90, channel, Note, 0)
EndProcedure
Code: Select all
;midi_text_player.pb
;written in PureBasic
;version 2.03
;USES TIMER TO PLAY MIDI EVENTS
;UPDATED ON 3/29/2008 BY chris319
;(C) 2007 - 2008 CHRIS CLEMENTSON
;N.B.: EACH NOTE IN A CHORD IS TREATED AS A SEPARATE EVENT
;128 voices
;1. Acoustic Grand Piano 65. Soprano Sax
;2. Bright Acoustic Piano 66. Alto Sax
;3. Electric Grand Piano 67. Tenor Sax
;4. Honky-tonk Piano 68. Baritone Sax
;5. Electric Piano 1 69. Oboe
;6. Electric Piano 2 70. English Horn
;7. Harpsichord 71. Bassoon
;8. Clavi 72. Clarinet
;9. Celesta 73. Piccolo
;10. Glockenspiel 74. Flute
;11. Music Box 75. Recorder
;12. Vibraphone 76. Pan Flute
;13. Marimba 77. Blown Bottle
;14. Xylophone 78. Shakuhachi
;15. Tubular Bells 79. Whistle
;16. Dulcimer 80. Ocarina
;17. Drawbar Organ 81. Lead 1 (square)
;18. Percussive Organ 82. Lead 2 (sawtooth)
;19. Rock Organ 83. Lead 3 (calliope)
;20. Church Organ 84. Lead 4 (chiff)
;21. Reed Organ 85. Lead 5 (charang)
;22. Accordion 86. Lead 6 (voice)
;23. Harmonica 87. Lead 7 (fifths)
;24. Tango Accordion 88. Lead 8 (bass + lead)
;25. Acoustic Guitar (nylon) 89. Pad 1 (new age)
;26. Acoustic Guitar (steel) 90. Pad 2 (warm)
;27. Electric Guitar (jazz) 91. Pad 3 (polysynth)
;28. Electric Guitar (clean) 92. Pad 4 (choir)
;29. Electric Guitar (muted) 93. Pad 5 (bowed)
;30. Overdriven Guitar 94. Pad 6 (metallic)
;31. Distortion Guitar 95. Pad 7 (halo)
;32. Guitar harmonics 96. Pad 8 (sweep)
;33. Acoustic Bass 97. FX 1 (rain)
;34. Electric Bass (finger) 98. FX 2 (soundtrack)
;35. Electric Bass (pick) 99. FX 3 (crystal)
;36. Fretless Bass 100. FX 4 (atmosphere)
;37. Slap Bass 1 101. FX 5 (brightness)
;38. Slap Bass 2 102. FX 6 (goblins)
;39. Synth Bass 1 103. FX 7 (echoes)
;40. Synth Bass 2 104. FX 8 (sci-fi)
;41. Violin 105. Sitar
;42. Viola 106. Banjo
;43. Cello 107. Shamisen
;44. Contrabass 108. Koto
;45. Tremolo Strings 109. Kalimba
;46. Pizzicato Strings 110. Bag pipe
;47. Orchestral Harp 111. Fiddle
;48. Timpani 112. Shanai
;49. String Ensemble 1 113. Tinkle Bell
;50. String Ensemble 2 114. Agogo
;51. SynthStrings 1 115. Steel Drums
;52. SynthStrings 2 116. Woodblock
;53. Choir Aahs 117. Taiko Drum
;54. Voice Oohs 118. Melodic Tom
;55. Synth Voice 119. Synth Drum
;56. Orchestra Hit 120. Reverse Cymbal
;57. Trumpet 121. Guitar Fret Noise
;58. Trombone 122. Breath Noise
;59. Tuba 123. Seashore
;60. Muted Trumpet 124. Bird Tweet
;61. French Horn 125. Telephone Ring
;62. Brass Section 126. Helicopter
;63. SynthBrass 1 127. Applause
;64. SynthBrass 2 128. Gunshot
#DWHOLE = 96 ;TICKS -- DOTTED WHOLE NOTE
#WHOLE = 64 ;TICKS -- WHOLE NOTE
#DHALF = 48 ;TICKS -- DOTTED HALF NOTE
#HALF = 32 ;TICKS -- HALF NOTE
#DQUARTER = 24 ;TICKS -- DOTTED QUARTER NOTE
#QUARTER = 16 ;TICKS -- QUARTER NOTE
#DEIGHTH = 12 ;TICKS -- DOTTED EIGHTH NOTE
#EIGHTH = 8 ;TICKS -- EIGHTH NOTE
#DSIXTEENTH = 6 ;TICKS -- DOTTED SIXTEENTH NOTE
#SIXTEENTH = 4 ;TICKS -- SIXTEENTH NOTE
#DTHIRSEC = 3 ;TICKS -- DOTTED THIRTY-SECOND NOTE
#THIRSEC = 2 ;TICKS -- THIRTY-SECOND NOTE
Global Dim velocity(32768)
Global Dim note(32768)
Global Dim channel(32768)
Global Dim start(32768)
Global Dim compiled_velocity(32768)
Global Dim compiled_note(32768)
Global Dim compiled_channel(32768)
Global Dim compiled_start(32768)
Global system_tick, midi_event
Global Dim instrument(16)
;Global Dim Is_MIDI(16)
Global channel, running_ticks, event_ct, line_count, header_count
Global previous_note, event_channels, bpm.f
Global hMidiOut, note_rest$
Procedure.l Roundoff(number.f) ;THE PROPER WAY TO ROUND A NUMBER
If number - Int(number) >= 0.5
ProcedureReturn number.f + 0.1
EndIf
ProcedureReturn number.f
EndProcedure
Procedure MidiOutMessage(hMidi,iStatus,iChannel,iData1,iData2)
dwMessage = iStatus | iChannel | (iData1 << 8 ) | (iData2 << 16)
ProcedureReturn midiOutShortMsg_(hMidi, dwMessage) ;
EndProcedure
Procedure SetInstrument(channel, instrument)
MidiOutMessage(hMidiOut, $C0, channel, instrument, 0)
EndProcedure
Procedure StopNote(channel, note)
MidiOutMessage(hMidiOut, $80, channel, Note, 0)
EndProcedure
Procedure PlayNote(channel, note, velocity)
MidiOutMessage(hMidiOut, $90, channel, note , velocity)
EndProcedure
Procedure Tick() ;THIS ROUTINE IS INVOKED BY THE TIMER
While compiled_start(midi_event) = system_tick
;If Is_MIDI(channel) <> 0
If compiled_velocity(midi_event) > 0
MidiOutMessage(hMidiOut, $90, compiled_channel(midi_event), compiled_note(midi_event), compiled_velocity(midi_event))
Else
MidiOutMessage(hMidiOut, $80, compiled_channel(midi_event), compiled_note(midi_event), 0)
EndIf
;EMBEDDED CODE TO PRINT NOTE DURATION IN SECONDS
;Global old_time.f
;If old_time > 0: Debug (ElapsedMilliseconds() - old_time) / 1000: EndIf
;If old_time = 0: old_time = ElapsedMilliseconds(): EndIf
;Else
;PLAY WAV SAMPLE
;EndIf
midi_event + 1
Wend
system_tick + 1
EndProcedure
Procedure Parse()
Dim ev$(1024)
event_ct = 0
running_ticks = 1
line_count = 1
While Eof(1) = 0
ev$(line_count) = ReadString(1) ;READ LINE FROM FILE
If Right(ev$(line_count), 1) = ","
t = Len(ev$(line_count))
While Mid(ev$(line_count), t, 1) = ","
t - 1
Wend
ev$(line_count) = Left(ev$(line_count), t)
EndIf
;Debug(ev$(line_count))
line_count + 1
Wend
line_count = 1
parse_loop:
If LCase(ev$(line_count)) <> "end"
;COUNT NUMBER OF CHANNELS IN EVENT, UP TO 16 CHANNELS
event_channels = 0
For ct = 3 To 18
If StringField(ev$(line_count),ct,",") <> ""
event_channels + 1
EndIf
Next
dur$ = StringField(ev$(line_count), 1, ",")
vel$ = StringField(ev$(line_count), 2, ",")
note_rest$ = LCase(Right(dur$, 1))
If note_rest$ <> "n" And note_rest$ <> "r" And note_rest$ <> "t"
MessageRequester("Error", "Missing note, rest or tie designation on line " + Str(line_count + header_count)+".", #MB_ICONERROR)
EndIf
Select Left(dur$, 1)
Case "w": duration = #WHOLE; - WHOLE NOTE
Case "h": duration = #HALF ; - HALF NOTE
Case "q": duration = #QUARTER ; - QUARTER NOTE
Case "e": duration = #EIGHTH ; - EIGHTH NOTE
Case "s": duration = #SIXTEENTH ; - SIXTEENTH NOTE NOTE
Case "t": duration = #THIRSEC ; - THIRTY-SECOND NOTE
; Case "f": duration = #SIXFOUR ; - SIXTY-FOURTH NOTE
Case "W": duration = #DWHOLE ;- DOTTED WHOLE NOTE
Case "H": duration = #DHALF ; - DOTTED HALF NOTE
Case "Q": duration = #DQUARTER ; - DOTTED QUARTER NOTE
Case "E": duration = #DEIGHTH ; - DOTTED EIGHTH NOTE
Case "S": duration = #DSIXTEENTH ; - DOTTED SIXTEENTH NOTE
Case "T": duration = #DTHIRSEC ; - DOTTED THIRTY-SECOND NOTE
; Case "F": duration = #DSIXFOUR ; - DOTTED SIXTY-FOURTH NOTE
Default
MessageRequester("Error", "Invalid note duration on line " + Str(line_count + header_count)+".", #MB_ICONERROR)
EndSelect
If Mid(ev$(line_count - 1), 2, 1) <> "t" ;: Debug ev$(line_count-1): End
For channel_ct = 1 To 16 ;event_channels
chord$ = StringField(ev$(line_count), channel_ct + 2, ",")
pos = 1
While pos < Len(chord$)
event_ct + 1
channel(event_ct) = channel_ct
If note_rest$ = "r" ;THIS IS A REST
velocity(event_ct) = 0
Else ;THIS IS A NOTE
velocity(event_ct) = Val(vel$)
EndIf
start(event_ct) = running_ticks
note$ = Mid(chord$, pos, 1)
;LOWERCASE FOR NATURALS AND UPPERCASE FOR SHARPS
Select note$
Case "c": temp_note = 0 ;C NATURAL
Case "C": temp_note = 1 ;C SHARP
Case "d": temp_note = 2 ;D NATURAL
Case "D": temp_note = 3 ;D SHARP
Case "e": temp_note = 4 ;E NATURAL
Case "f": temp_note = 5 ;F NATURAL
Case "F": temp_note = 6 ;F SHARP
Case "g": temp_note = 7 ;G NATURAL
Case "G": temp_note = 8 ;G SHARP
Case "a": temp_note = 9 ;A NATURAL
Case "A": temp_note = 10 ;A SHARP
Case "b": temp_note = 11 ;B NATURAL
Default
MessageRequester("Error", "Invalid note on line " + Str(line_count + header_count)+":"+Chr(13)+note$, #MB_ICONERROR)
EndSelect
If Mid(chord$, pos + 1, 1) = "-"
octave = 0
Else
octave = Val(Mid(chord$, pos + 1, 1)) + 1
EndIf
note(event_ct) = (octave * 12) + temp_note
If note_rest$ = "n" ;THIS IS A NOTE
;HERE WE CREATE AN EVENT TO STOP THE NOTE WHEN IT IS FINISHED PLAYING
event_ct + 1
channel(event_ct) = channel_ct
velocity(event_ct) = 0 ;SHUT OFF THE NOTE
note(event_ct) = (octave * 12) + temp_note
start(event_ct) = running_ticks + duration
ElseIf note_rest$ = "t" ;TIE TO NEXT NOTE
event_ct + 1
tie_ct = 0
tie_duration = 0
While Mid(ev$(line_count + tie_ct), 2, 1) = "t"
tie_dur$ = StringField(ev$(line_count + tie_ct), 1, ",")
Select Left(tie_dur$, 1)
Case "w": tie_duration + #WHOLE; - WHOLE NOTE
Case "h": tie_duration + #HALF ; - HALF NOTE
Case "q": tie_duration + #QUARTER ; - QUARTER NOTE
Case "e": tie_duration + #EIGHTH ; - EIGHTH NOTE
Case "s": tie_duration + #SIXTEENTH ; - SIXTEENTH NOTE NOTE
Case "t": tie_duration + #THIRSEC ; - THIRTY-SECOND NOTE
; Case "f": tie_duration + #SIXFOUR ; - SIXTY-FOURTH NOTE
Case "W": tie_duration + #DWHOLE ;- DOTTED WHOLE NOTE
Case "H": tie_duration + #DHALF ; - DOTTED HALF NOTE
Case "Q": tie_duration + #DQUARTER ; - DOTTED QUARTER NOTE
Case "E": tie_duration + #DEIGHTH ; - DOTTED EIGHTH NOTE
Case "S": tie_duration + #DSIXTEENTH ; - DOTTED SIXTEENTH NOTE
Case "T": tie_duration + #DTHIRSEC ; - DOTTED THIRTY-SECOND NOTE
; Case "F": tie_duration + #DSIXFOUR ; - DOTTED SIXTY-FOURTH NOTE
Default
MessageRequester("Error", "Invalid note duration on line " + Str(line_count + header_count), #MB_ICONERROR)
EndSelect
;HERE WE CREATE AN EVENT TO STOP THE NOTE WHEN IT IS FINISHED PLAYING
channel(event_ct) = channel_ct
velocity(event_ct) = 0 ;SHUT OFF THE NOTE
note(event_ct) = (octave * 12) + temp_note
start(event_ct) = running_ticks + duration + tie_duration
tie_ct + 1
Wend
EndIf
pos = pos + 2
Wend
Next ;END OF For event_channels
EndIf ; <> "t"
running_ticks + duration
line_count + 1
Goto parse_loop
EndIf
EndProcedure
Procedure Read_Header()
header_count = 0
While LCase(Left(st$,5)) <> "start"
st$ = LCase(ReadString(1))
If Right(st$, 1) = ","
t = Len(st$)
While Mid(st$, t, 1) = ","
t - 1
Wend
st$ = Left(st$, t)
EndIf
header_count + 1
;Debug st$
;End
Select Left(st$, 1)
Case "t": bpm = Val(StringField(st$,2,","))
Case "i"
inst$ = StringField(st$,1,",")
Select inst$
Case "i1": instrument(1) = Val(StringField(st$,2,","))
Case "i2": instrument(2) = Val(StringField(st$,2,","))
Case "i3": instrument(3) = Val(StringField(st$,2,","))
Case "i4": instrument(4) = Val(StringField(st$,2,","))
Case "i5": instrument(5) = Val(StringField(st$,2,","))
Case "i6": instrument(6) = Val(StringField(st$,2,","))
Case "i7": instrument(7) = Val(StringField(st$,2,","))
Case "i8": instrument(8) = Val(StringField(st$,2,","))
Case "i9": instrument(9) = Val(StringField(st$,2,","))
Case "i10": instrument(10) = Val(StringField(st$,2,","))
Case "i11": instrument(11) = Val(StringField(st$,2,","))
Case "i12": instrument(12) = Val(StringField(st$,2,","))
Case "i13": instrument(13) = Val(StringField(st$,2,","))
Case "i14": instrument(14) = Val(StringField(st$,2,","))
Case "i15": instrument(15) = Val(StringField(st$,2,","))
Case "i16": instrument(16) = Val(StringField(st$,2,","))
EndSelect
EndSelect
Wend
EndProcedure
Procedure Compile() ;SORT EVENTS BY START TIMES
index = 1
For ct2 = 1 To running_ticks
For ct1 = 1 To event_ct
If start(ct1) = ct2 ;And compiled_flag(ct1) = 0
compiled_velocity(index) = velocity(ct1)
compiled_note(index) = note(ct1)
compiled_channel(index) = channel(ct1)
compiled_start(index) = start(ct1)
index + 1
EndIf
Next
Next
EndProcedure
;InitSound()
;result = LoadSound(129, "C:\Programs\PureBasic 4.10\source\Flugelhorn_5C_Tuned.wav")
;If result = 0: MessageRequester("Error", "Unable to open MIDI output.", #MB_ICONERROR): End: EndIf
;result = LoadSound(130, "C:\Programs\PureBasic 4.10\source\Flugelhorn_5C_Tuned.wav")
;If result = 0: MessageRequester("Error", "Unable to open MIDI output.", #MB_ICONERROR): End: EndIf
;timesig$ = "4/4"
;one_beat = Val(Right(timesig$, 1))
;one_beat = one_beat / 4
;bpm.f = 60
;beat.f = 60000 / bpm
;timebase.f = (beat * 0.0625 * one_beat) / 2 ;ABLE TO RESOLVE DOTTED SIXTY-FOURTH NOTE
;SIXTY-FOURTH NOTE = 2 * timebase
;int_timebase = Roundoff(timebase)
;system_tick = 0
ReadFile(1, "tpir.csv")
read_header()
;timesig$ = "4/4"
;one_beat = Val(Right(timesig$, 1))
;one_beat = one_beat / 4
timebase.f = 3600 / bpm ;ABLE TO RESOLVE DOTTED SIXTY-FOURTH NOTE
;SIXTY-FOURTH NOTE = 2 * timebase
int_timebase = Roundoff(timebase)
system_tick = 0
Parse()
Compile()
midi.MIDIOUTCAPS
devices = midiOutGetNumDevs_()
For devnum = - 1 To devices - 1
If midiOutGetDevCaps_(devnum,@midi,SizeOf(MIDIOUTCAPS))=0
If midi\wVoices > 0
midiport=devnum
EndIf
EndIf
Next
*hMidiOut.l
If midiOutOpen_(@hMidiOut,midiport,0,0,0) <> #MMSYSERR_NOERROR
MessageRequester("Error", "Unable to open MIDI output.", #MB_ICONERROR)
End
EndIf
For channel = 1 To 16
;If instrument(channel) <= 128
SetInstrument(channel, instrument(channel))
; Is_MIDI(channel) = 1
;Else
; Is_MIDI(channel) = 0
;EndIf
Next
midi_event = 1
If OpenWindow(1,0,0,200,100,"MIDI Ticks",#PB_Window_SystemMenu|#PB_Window_ScreenCentered) = 0
MessageRequester("Error", "Unable to open window.", #MB_ICONERROR)
End
EndIf
;START TIMER BEGIN MIDI PLAYBACK
tick_clock = SetTimer_(WindowID(1), 1, int_timebase, @tick())
Repeat
Window_Event = WaitWindowEvent()
Until Window_Event = #PB_Event_CloseWindow
midiOutClose_(@hMidiOut)
If tick_clock
KillTimer_(WindowID(1), tick_clock)
EndIf
;FreeSound(129)
;FreeSound(130)
CloseFile(1)
End