Follow carefully the instructions for cutting and pasting the text between the === lines into a separate file called "midi.txt".
All bug reports and suggestions for enhancements are welcome.
Code: Select all
;midi_text_player.pb
;written in PureBasic
;version 1.2
;updated on 12/15/07
;by chris319
;COPYRIGHT (C) 2007 Chris Clementson
;==========================================
CUT THE TEXT BETWEEN THIS LINE And THE Next === LINE BELOW And
PASTE INTO A FILE NAMED "midi.txt". THE PROGRAM WILL Read
THE TUNE FROM "midi.txt", Or YOU MAY CHANGE THE FILE NAME IN THE CODE.
THIS NIFTY LITTLE PROGRAM PLAYS MIDI NOTES FROM A TEXT SCRIPT.
YOU CAN SPECIFY ANY OF THE NOTES IN THE SCALE, THE OCTAVE OF THE
NOTE, THE DURATION OF NOTES And RESTS, And THE GENERAL MIDI
INSTRUMENT WHICH WILL BE USED To PLAY THE NOTE.
THERE MUST BE ONLY ONE NOTE, REST Or MEASURE DEMARCATOR (BAR) PER LINE.
EACH NOTE IS REPRESENTED BY A LETTER A - G. SHARPS ARE DENOTED With A
TRAILING "#" And FLATS ARE DENOTED BY A TRAILING "b", e.g. B-FLAT CAN
BE ENTERED As Bb Or A#. YOU MAY ALSO REPRESENT RESTS With THE LETTER "R"
And NO OCTAVE NUMBER. ALPHABETIC CHARACTERS MAY BE UPPER Or LOWER Case.
THERE MUST BE NO SPACES Or OTHER CHARACTERS.
AFTER EACH NOTE IS A NUMBER REPRESENTING THE OCTAVE, e.g. C4. AFTER THE
OCTAVE ARE ONE Or TWO LETTERS REPRESENTING THE DURATION OF THE NOTE.
If THE FIRST OF THE TWO CHARACTERS IS "D", THE NOTE THAT FOLLOWS IS A DOTTED NOTE.
THE NOTES ARE As FOLLOWS:
DW - DOTTED WHOLE NOTE
W - WHOLE NOTE
DH - DOTTED HALF NOTE
H - HALF NOTE
DQ - DOTTED QUARTER NOTE
Q - QUARTER NOTE
DE - DOTTED EIGHTH NOTE
E - EIGHTH NOTE
DS - DOTTED SIXTEENTH NOTE
S - SIXTEENTH NOTE NOTE
DT - DOTTED THIRTY-SECOND NOTE
T - THIRTY-SECOND NOTE
DF - DOTTED SIXTY-FOURTH NOTE
F - SIXTY-FOURTH NOTE
YOU MUST APPEND A NOTE DURATION To RESTS As WELL, e.g. A QUARTER REST WOULD BE
REPRESENTED As "RQ".
A NOTE MAY OPTIONALLY BE FOLLOWED BY A COMMA And A GENERAL MIDI INSTRUMENT
NUMBER. "C4Q,41" WOULD PLAY A QUARTER NOTE AT C4 IN THE VIOLIN VOICE. If THE
INSTRUMENT NUMBER IS LEFT OFF, THE LAST-PLAYED INSTRUMENT WILL BE USED.
THE BEGINNING OF YOUR SONG MUST BE DEMARCATED With THE WORD "START"
And THE End With "END". YOU MAY PLACE HEADER INFORMATION And NOTES ABOVE THE
WORD "START". YOU MAY ALSO DEMARCATE MEASURES IN YOUR TEXT FILE With THE "|"
CHARACTER. THIS IS MERELY A VISUAL AID And WILL BE DISREGARDED BY THE PLAYER.
BELOW IS THE SONG Data. BE SURE To INCLUDE THE WORDS "START" And "END".
start
c8q,10
c8q
g8q
g8q
|
a8q
a8q
g8h
|
f6q,74
f6q
e6q
e6q
|
d6q
d6q
c6h
rh
c6h,1
e6h
g6h
End
CUT And PASTE THE ABOVE TEXT (INCLUDING THIS LINE) To A FILE NAMED "MIDI.TXT".
;==========================================
;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
Global hMidiOut
bpm.f = 120
bps.f = bpm / 60
beat.f = (1 / bps) * 1000 ;IN MILLISECONDS
Global Dim Instrument(16)
Global channel, note, previous_note
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)
StopNote(channel, previous_note)
MidiOutMessage(hMidiOut, $90, channel, note , volume)
EndProcedure
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
instrument(1) = 57 ;TRUMPET
instrument(2) = 10 ;GLOCKENSPIEL
For ct = 1 To 2
SetInstrument(ct, instrument(ct))
Next
OpenFile(1, "midi.txt")
;LOOK For "START" To INDICATE START OF PLAYBACK
;ACCEPTABLE NOTATION
;a5q
;a#5q
;ab5q
;ab5dq
;ab5dq,10 -- INSTRUMENT = GLOCKENSPIEL
line_count = 0
inp$ = ""
While UCase(inp$) <> "START"
inp$ = ReadString(1)
line_count = line_count + 1
Wend
While Not Eof(1)
inp$ = ReadString(1)
line_count = line_count + 1
If inp$ = "" Or Left(inp$, 1) = "'": Goto blankline: EndIf ;BLANK LINE OR APOSTROPHE (COMMENT)
;Debug(inp$ + " " + Str(line_count))
;MEASURE DEMARCATOR
measure$ = "Measure " + " " + Right(inp$, Len(inp$) - 1)
If Left(inp$, 1) = "|"
If measure_beats.f > 0
Debug StrF(measure_beats)
;measure$ = measure$ + " " + Str(measure_beats)
;EndIf
measure_beats = 0
EndIf
Debug measure$
Goto nextnote
EndIf
If UCase(inp$) = "END": Debug StrF(measure_beats): End: EndIf
tmp$ = ""
ct = 1
While Mid(inp$, ct, 1) <> "," And ct <= Len(inp$)
tmp$ = tmp$ + Mid(inp$, ct, 1)
ct = ct + 1
Wend
ct = ct + 1
instrum$ = ""
While ct <= Len(inp$)
instrum$ = instrum$ + Mid(inp$, ct, 1)
ct = ct + 1
Wend
If Val(instrum$) > 0: instrument = Val(instrum$): EndIf
inp$ = tmp$
in2$ = Right(inp$, 2)
If UCase(Left(in2$, 1)) = "D"
in3$ = Right(inp$, 3)
octave = Val(Left(in3$, 1))
Else
octave = Val(Left(in2$, 1)) ;EXTRACT OCTAVE
EndIf
note$ = Left(inp$, 2)
dur$ = UCase(Right(inp$, 2))
If Right(note$, 1) <> "#" And Right(note$, 1) <> "b"
note$ = UCase(Left(note$, 1)) ;NATURAL
Else
note$ = UCase(Left(note$, 1)) + Right(note$, 1) ;SHARP OR FLAT
EndIf
If note$ = "C"
note = 0
ElseIf note$ = "C#" Or note$ = "Db"
note = 1
ElseIf note$ = "D"
note = 2
ElseIf note$ = "D#" Or note$ = "Eb"
note = 3
ElseIf note$ = "E"
note = 4
ElseIf note$ = "F"
note = 5
ElseIf note$ = "F#" Or note$ = "Gb"
note = 6
ElseIf note$ = "G"
note = 7
ElseIf note$ = "G#" Or note$ = "Ab"
note = 8
ElseIf note$ = "A"
note = 9
ElseIf note$ = "A#" Or note$ = "Bb"
note = 10
ElseIf note$ = "B"
note = 11
ElseIf note$ = "R" ;REST
note = 255
Else
MessageRequester("Error", "Invalid note on line " + Str(line_count)+":"+Chr(13)+note$, #MB_ICONERROR)
End
EndIf
note = (octave * 12) + note
Select Right(dur$, 1)
Case "Q"
dur.f = 1 ;QUARTER NOTE
Case "H"
dur = 2 ;HALF NOTE
Case "W"
dur = 4 ;WHOLE NOTE
Case "E"
dur = 0.5 ;EIGHTH NOTE
Case "S"
dur = 0.25 ;SIXTEENTH NOTE
Case "T"
dur = 0.125 ;THIRTY-SECOND NOTE
Case "F"
dur = 0.0625 ;SIXTY-FOURTH NOTE
Default
MessageRequester("Error", "Invalid note duration on line " + Str(line_count)+":"+Chr(13)+dur$, #MB_ICONERROR)
End
EndSelect
dotted$ = Left(dur$, 1)
If UCase(dotted$) = "D"
dur = dur * 1.5
ElseIf Asc(dotted$) > 57 And dotted$ <> "R"
MessageRequester("Error", "Invalid note duration on line " + Str(line_count)+":"+Chr(13)+dur$, #MB_ICONERROR)
EndIf
measure_beats = measure_beats + dur
If note < 255
For channel = 1 To 2
PlayNote(channel, note, 50)
Next
EndIf
Delay (beat * dur)
If note < 255
For channel = 1 To 2
StopNote(channel, note)
Next
;Else
; Delay(beat * dur)
EndIf
nextnote:
previous_note = note
blankline:
Wend
CloseFile(1)
midiOutClose_(@hMidiOut)
End