'=========================================================================== ' Subject: MIDI PLAYER (FOR TYPE 0) Date: 07-26-98 (18:56) ' Author: Luke Erren Code: QB, QBasic, PDS ' Origin: errenvep@worldonline.nl Packet: SOUND.ABC '=========================================================================== ' MIDI Player (for type 0) ' (C) Luke Erren 1998 ' errenvep@worldonline.nl DECLARE SUB MIDIPitch (Kanaal%, Bottom%, Top%) DECLARE SUB MidiNoteOFF (Note%, Volume%, Kanaal%, MidiPoort%) DECLARE SUB MidiNoot (Noot%, Volume%, Kanaal%, MidiPoort%, Pause%) DECLARE SUB MIDIInstrument (Kanaal%, MidiPoort%, Instrument%) DECLARE SUB MidiReset (MidiPoort%) DECLARE SUB MidiInit (MidiPoort%) DECLARE SUB MidiNote (Note%, Volume%, Kanaal%, MidiPoort%) DECLARE FUNCTION Instrumentname$ (Nmbr%) DECLARE FUNCTION NextNumber! () DECLARE FUNCTION Controlname$ (Nmr!) DECLARE FUNCTION Nibble$ (Cr$, l!) DECLARE FUNCTION Nibble2Number! (ch$) DECLARE FUNCTION InstrumentNaam$ (Nummer!) DECLARE FUNCTION NoteName$ (Nmbr%) DECLARE FUNCTION LeesTimeSigUitBest$ () DECLARE FUNCTION LeesTempoUitBest! () DECLARE FUNCTION LeesTekstUitBest$ () DECLARE FUNCTION VierByteLengte! () DECLARE FUNCTION TweeByteLengte! () DECLARE FUNCTION ReadVarLen! () DIM SHARED Instrname(127) AS STRING DIM Header AS STRING * 4 DIM TweeByte AS STRING * 2 DIM VierByte AS STRING * 4 DIM FileType AS STRING * 2 DIM l AS INTEGER DIM SHARED a AS STRING * 1 Tijd = TIMER: FOR i = 1 TO 100000: NEXT i: Wl = 2 / (TIMER - Tijd) DIM InLen(16) AS INTEGER IF LEN(ENVIRON$("BLASTER")) THEN FOR Length% = 1 TO LEN(ENVIRON$("BLASTER")) IF MID$(ENVIRON$("BLASTER"), Length%, 1) = "P" THEN MidiPoort% = VAL("&H" + MID$(ENVIRON$("BLASTER"), Length% + 1, 3)) NEXT END IF IF NOT MidiPoort% THEN MidiPoort% = &H330 MidiInit (MidiPoort%) MidiReset (MidiPoort%) CLS PRINT " MIDI Player for type 0 " PRINT " (C) 1998 Luke Erren " PRINT PRINT "Adjust speed with '-' and '+'" PRINT FOR i = 0 TO 127: READ Instrname(i): NEXT i LINE INPUT "Midi file : "; File$: IF File$ = "" THEN STOP IF INSTR(File$, ".") = 0 THEN File$ = File$ + ".MID" WIDTH 80, 50: CLS OPEN File$ FOR BINARY AS #1 GET 1, , Header PRINT File$ IF Header <> "MThd" THEN PRINT "Not a valid MIDI file": STOP GET 1, , VierByte GET 1, , FileType IF ASC(RIGHT$(FileType, 1)) = 0 THEN PRINT "Single trax" ELSE PRINT "Multy trax, this file type is not supported" END IF Ttrx = TweeByteLengte: PRINT "Aantal trax ="; Ttrx PRINT "Tempo = "; TweeByteLengte Tempo = 120 FOR i = 1 TO Ttrx GET 1, , Header PRINT Header IF Header = "MTrk" THEN TrkLengte = VierByteLengte + LOC(1) WHILE (LOC(1) < TrkLengte) AND Stoppen = 0 x$ = INKEY$: IF x$ = CHR$(27) THEN Stoppen = 1 IF x$ = "-" THEN Wl = Wl + .5 IF x$ = "=" OR x$ = "+" THEN Wl = Wl - .5 t = ReadVarLen FOR Wacht = 1 TO t * (1.5 / Tempo) * 10000 * Wl: NEXT Wacht: REM Timer TotalTime! = TotalTime! + t LOCATE 40, 1: PRINT USING "Song pointer : ###,###,###"; TotalTime! GET 1, , a IF a = CHR$(255) THEN GET #1, , a SELECT CASE ASC(a) CASE 0: PRINT "Sequence Number : "; LeesTekstUitBest$ CASE 1: PRINT "Text event : "; LeesTekstUitBest$ CASE 2: PRINT "Copyright : "; LeesTekstUitBest$ CASE 3: PRINT "Track name : "; LeesTekstUitBest$ CASE 4: PRINT "Instrument name : "; LeesTekstUitBest$ CASE 5: PRINT "Lyric : "; LeesTekstUitBest$ CASE 6: PRINT "Marker : "; LeesTekstUitBest$ CASE 7: PRINT "Cue point : "; LeesTekstUitBest$ CASE 8: PRINT "Unknown text 8 : "; LeesTekstUitBest$ CASE 9: PRINT "Unknown text 9 : "; LeesTekstUitBest$ CASE 10: PRINT "Unknown text 10 : "; LeesTekstUitBest$ CASE 11: PRINT "Unknown text 11 : "; LeesTekstUitBest$ CASE 12: PRINT "Unknown text 12 : "; LeesTekstUitBest$ CASE 13: PRINT "Unknown text 13 : "; LeesTekstUitBest$ CASE 14: PRINT "Unknown text 14 : "; LeesTekstUitBest$ CASE 15: PRINT "Unknown text 15 : "; LeesTekstUitBest$ CASE 32: PRINT "MIDI ch. Prefix.. ": a$ = LeesTekstUitBest$: REM <====== What is this ? CASE 47: PRINT "End of track ": GET #1, , a CASE 81: Tempo = (60000000 / LeesTempoUitBest): PRINT USING "Set tempo : #### BPM"; Tempo CASE 84: PRINT "SMPTE Offset : "; : t$ = LeesTekstUitBest$: PRINT ASC(LEFT$(t$, 1)); " "; ASC(MID$(t$, 2, 1)) CASE 88: PRINT "Time signature "; LeesTimeSigUitBest$ CASE 89: PRINT "Key signature"; LeesTekstUitBest$ CASE 127: PRINT "Sequencer-specific Meta Event"; LeesTekstUitBest$ CASE ELSE: PRINT "Unknown command "; ASC(a): t$ = LeesTekstUitBest$ END SELECT ELSE IF HEX$(ASC(a)) = "F0" OR HEX$(ASC(a)) = "F7" THEN t$ = LeesTekstUitBest ELSE IF Nibble$(a, 1) = "8" THEN Kanaal% = Nibble2Number(Nibble$(a, 0)) Note% = NextNumber Volume% = NextNumber ' PRINT "Note off Chn.:"; Kanaal%; NoteName(Note%), " Velocent : "; Volume% CALL MidiNoteOFF(Note%, Volume%, Kanaal%, MidiPoort%) LOCATE Kanaal% + 20, 28: PRINT " " InLen(Kanaal%) = 0 END IF IF Nibble$(a, 1) = "9" THEN Kanaal% = Nibble2Number(Nibble$(a, 0)) Note% = NextNumber Volume% = NextNumber ' PRINT "Note on Chn.:"; Kanaal%; NoteName(Note%), " Velocent : "; Volume% LOCATE Kanaal% + 20, 28: PRINT NoteName(Note%); " " CALL MidiNote(Note%, Volume%, Kanaal%, MidiPoort%) InLen(Kanaal%) = 40 END IF IF Nibble$(a, 1) = "A" THEN PRINT "Key after-touch "; Nibble2Number(Nibble$(a, 0)); NoteName(NextNumber); " Velocent : "; NextNumber IF Nibble$(a, 1) = "B" THEN Kanaal% = Nibble2Number(Nibble$(a, 0)) Instrument% = NextNumber NewVal% = NextNumber ' PRINT "Control change "; Kanaal%; Controlname(Instrument%); " New Value"; NextNumber END IF IF Nibble$(a, 1) = "C" THEN Kanaal% = Nibble2Number(Nibble$(a, 0)) Instrument% = NextNumber ' PRINT "Program change "; Kanaal%; " New program number :"; Instrname(Instrument%) LOCATE Kanaal% + 20, 1: PRINT Kanaal% + 1; Instrname(Instrument%) MIDIInstrument Kanaal%, MidiPoort%, Instrument% END IF IF Nibble$(a, 1) = "D" THEN PRINT "Channel after-touch"; Nibble2Number(Nibble$(a, 0)); " Channel : "; NextNumber IF Nibble$(a, 1) = "E" THEN Kanaal% = Nibble2Number(Nibble$(a, 0)) Bottom% = NextNumber Top% = NextNumber ' PRINT "Pitch wheel change "; Kanaal%; " Bottom : "; Bottom%; " Top : "; Top% MIDIPitch Kanaal%, Bottom%, Top% END IF END IF END IF FOR i = 0 TO 15 IF InLen(i) = 0 THEN InLen(i) = 1 Bar$ = STRING$(INT(InLen(i)), 22) + STRING$(40 - INT(InLen(i)), 32) LOCATE i + 20, 33: COLOR 2 PRINT MID$(Bar$, 2, 29); : COLOR 4 PRINT RIGHT$(Bar$, 10); COLOR 7 InLen(i) = InLen(i) - 1 NEXT i WEND END IF NEXT i CLOSE #1 MidiReset MidiPoort% FOR i% = 0 TO 15 FOR J% = 0 TO 120 MidiNoteOFF J%, 0, i%, MidiPoort% NEXT J% NEXT i% Instrumentname: DATA "Acoustic Grand","Bright Acoustic","Electric Grand" DATA "Honky-Tonk","Electric Piano 1","Electric Piano 2" DATA "Harpsichord","Clav","Celesta" DATA "Glockenspiel","Music Box","Vibraphone" DATA "Marimba","Xylophone","Tubular Bells" DATA "Dulcimer","Drawbar Organ","Percussive Organ" DATA "Rock Organ","Church Organ","Reed Organ" DATA "Accoridan","Harmonica","Tango Accordian" DATA "Acoustic Guitar(nylon)","Acoustic Guitar(steel)","Electric Guitar(jazz)" DATA "Electric Guitar(clean)","Electric Guitar(muted)","Overdriven Guitar" DATA "Distortion Guitar","Guitar Harmonics","Acoustic Bass" DATA "Electric Bass(finger)","Electric Bass(pick)","Fretless Bass" DATA "Slap Bass 1","Slap Bass 2","Synth Bass 1" DATA "Synth Bass 2","Violin","Viola" DATA "Cello","Contrabass","Tremolo Strings" DATA "Pizzicato Strings","Orchestral Strings","Timpani" DATA "String Ensemble 1","String Ensemble 2","SynthStrings 1" DATA "SynthStrings 2","Choir Aahs","Voice Oohs" DATA "Synth Voice","Orchestra Hit","Trumpet" DATA "Trombone","Tuba","Muted Trumpet" DATA "French Horn","Brass Section","SynthBrass 1" DATA "SynthBrass 2","Soprano Sax","Alto Sax" DATA "Tenor Sax","Baritone Sax","Oboe" DATA "English Horn","Bassoon","Clarinet" DATA "Piccolo","Flute","Recorder" DATA "Pan Flute","Blown Bottle","Skakuhachi" DATA "Whistle","Ocarina","Lead 1 (square)" DATA "Lead 2 (sawtooth)","Lead 3 (calliope)","Lead 4 (chiff)" DATA "Lead 5 (charang)","Lead 6 (voice)","Lead 7 (fifths)" DATA "Lead 8 (bass+lead)","Pad 1 (new age)","Pad 2 (warm)" DATA "Pad 3 (polysynth)","Pad 4 (choir)","Pad 5 (bowed)" DATA "Pad 6 (metallic)","Pad 7 (halo)","Pad 8 (sweep)" DATA "FX 1 (rain)","FX 2 (soundtrack)","FX 3 (crystal)" DATA "FX 4 (atmosphere)","FX 5 (brightness)","FX 6 (goblins)" DATA "FX 7 (echoes)","FX 8 (sci-fi)","Sitar" DATA "Banjo","Shamisen","Koto" DATA "Kalimba","Bagpipe","Fiddle" DATA "Shanai","Tinkle Bell","Agogo" DATA "Steel Drums","Woodblock","Taiko Drum" DATA "Melodic Tom","Synth Drum","Reverse Cymbal" DATA "Guitar Fret Noise","Breath Noise","Seashore" DATA "Bird Tweet","Telephone Ring","Helicopter" DATA "Applause","Gunshot","Unknown instrument" FUNCTION Controlname$ (Nmr) SELECT CASE Nmr CASE 1: Controlname$ = "Modulation" CASE 7: Controlname$ = "Main volume" CASE 10: Controlname$ = "Pan" CASE 11: Controlname$ = "Expression" CASE 64: Controlname$ = "Sustain" CASE 121: Controlname$ = "Reset all controllers" CASE 123: Controlname$ = "All notes off" CASE ELSE: Controlname$ = "Unknown " + STR$(Nmr) END SELECT END FUNCTION FUNCTION LeesTekstUitBest$ Lengte = ReadVarLen FOR tt = 1 TO Lengte GET #1, , a Temp$ = Temp$ + a NEXT tt LeesTekstUitBest$ = Temp$ END FUNCTION FUNCTION LeesTempoUitBest DIM Temp AS LONG GET #1, , a IF a = CHR$(3) THEN FOR i = 1 TO 3 GET #1, , a Temp = (Temp * 256) + ASC(a) NEXT i END IF LeesTempoUitBest = Temp END FUNCTION FUNCTION LeesTimeSigUitBest$ ' Temp$ = LeesTekstUitBest$ GET 1, , a GET 1, , a: T1 = ASC(a) GET 1, , a: T2 = ASC(a) GET 1, , a GET 1, , a LeesTimeSigUitBest$ = STR$(T1) + " /" + STR$(2 ^ T2) END FUNCTION SUB MidiInit (MidiPoort%) FOR Temp = 255 TO 0 STEP -1 OUT MidiPoort% + 1, Temp + 2 NEXT Temp END SUB SUB MIDIInstrument (Kanaal%, MidiPoort%, Instrument%) OUT MidiPoort%, &HC0 + Kanaal% OUT MidiPoort%, Instrument% END SUB SUB MidiNoot (Noot%, Volume%, Kanaal%, MidiPoort%, Pause%) OUT MidiPoort%, &H90 + Kanaal% OUT MidiPoort%, Noot% OUT MidiPoort%, Volume% FOR Temp = 1 TO Pause%: NEXT Temp OUT MidiPoort%, &H90 + Kanaal% OUT MidiPoort%, Noot% OUT MidiPoort%, 0 END SUB SUB MidiNote (Note%, Volume%, Kanaal%, MidiPoort%) OUT MidiPoort%, &H90 + Kanaal% OUT MidiPoort%, Note% OUT MidiPoort%, Volume% END SUB SUB MidiNoteOFF (Note%, Volume%, Kanaal%, MidiPoort%) OUT MidiPoort%, &H80 + Kanaal% OUT MidiPoort%, Note% OUT MidiPoort%, Volume% END SUB SUB MIDIPitch (Kanaal%, Bottom%, Top%) OUT MidiPoort%, &HE0 + Kanaal% OUT MidiPoort%, Bottom% OUT MidiPoort%, Top% END SUB SUB MidiReset (MidiPoort%) OUT (MidiPoort%), &HFF END SUB FUNCTION NextNumber GET #1, , a NextNumber = ASC(a) END FUNCTION FUNCTION Nibble$ (Cr$, l) IF l = 1 THEN Nibble$ = LEFT$(HEX$(ASC(Cr$) AND 240), 1) ELSE Nibble$ = RIGHT$(HEX$(ASC(Cr$) AND 15), 1) END IF END FUNCTION FUNCTION Nibble2Number (ch$) SELECT CASE ch$ CASE "0": Nibble2Number = 0 CASE "1": Nibble2Number = 1 CASE "2": Nibble2Number = 2 CASE "3": Nibble2Number = 3 CASE "4": Nibble2Number = 4 CASE "5": Nibble2Number = 5 CASE "6": Nibble2Number = 6 CASE "7": Nibble2Number = 7 CASE "8": Nibble2Number = 8 CASE "9": Nibble2Number = 9 CASE "A": Nibble2Number = 10 CASE "B": Nibble2Number = 11 CASE "C": Nibble2Number = 12 CASE "D": Nibble2Number = 13 CASE "E": Nibble2Number = 14 CASE "F": Nibble2Number = 15 END SELECT END FUNCTION FUNCTION NoteName$ (Nmbr%) Note = Nmbr% MOD 12 SELECT CASE Note CASE 0: Temp$ = "C" CASE 1: Temp$ = "C#" CASE 2: Temp$ = "D" CASE 3: Temp$ = "D#" CASE 4: Temp$ = "E" CASE 5: Temp$ = "F" CASE 6: Temp$ = "F#" CASE 7: Temp$ = "G" CASE 8: Temp$ = "G#" CASE 9: Temp$ = "A" CASE 10: Temp$ = "A#" CASE 11: Temp$ = "B" END SELECT Octave = (Nmbr% - Note) / 12 NoteName$ = Temp$ + STR$(Octave) END FUNCTION FUNCTION ReadVarLen GET #1, , a Value = ASC(a) IF (Value AND 128) THEN Value = (Value AND 127) DO GET #1, , a Value2 = ASC(a) Value = (Value * (2 ^ 7)) + (Value2 AND 127) LOOP WHILE (Value2 AND 128) END IF ReadVarLen = Value END FUNCTION FUNCTION TweeByteLengte GET #1, , a t = ASC(a) * 2 ^ 8 GET #1, , a TweeByteLengte = t + ASC(a) END FUNCTION FUNCTION VierByteLengte GET #1, , a t = ASC(a) * 2 ^ 8 GET #1, , a t = (ASC(a) + t) * 2 ^ 8 GET #1, , a t = (ASC(a) + t) * 2 ^ 8 GET #1, , a VierByteLengte = t + ASC(a) END FUNCTION