'------------------------------------------------------------------- ' ' Q U I C K B A S I C ' ' ÛÛÛ ÛÛÛ ÛÛÛÛÛ ÛÛÛÛ ÛÛÛÛÛ ' Û Û Û Û Û Û Û Û ' Û Û Û Û Û Û Û ' Û Û Û ÛÛÛÛÛ ÛÛÛÛ ÛÛÛÛÛ ' ' QBMIDI(TM) Library Sample Programs ' ' Q B M - D E M 2 . B A S ' ' M U S I C G E N E R A T O R ' ' S H A R E W A R E V E R S I O N 1 . 0 ' ' Developed by: ' AskUs! Technology Specialists ' PO Box 737 ' Bountiful, UT 84011-0737 ' '------------------------------------------------------------------- DECLARE SUB GetKeypress (AsciiValueOfKey%, WaitUntilKeyIsPressed%) DECLARE SUB DispTextLeftJust (Row%, Col%, Text$, Attribute%) DECLARE SUB DispTextCenter (Row%, Text$, Attribute%) DECLARE SUB DispNumbLeftJust (Row%, Col%, Number%, Attribute%) DEFINT A-Z 'Set all variables to integer unless specified CONST False = 0, True = NOT False 'Set true and false constants (usable in all subs) CONST Bright = 15, Normal = 7 'Set colors to use (also usable in all subs) CONST EscapeKey = 27, SpaceBar = 32 'Set values to check for when keys are pressed CONST UpArrow = -72, DownArrow = -80 CONST LeftArrow = -75, RightArrow = -77 CONST HomeKey = -71, EndKey = -79 CONST PageUp = -73, PageDown = -81 CONST JKey = 74, NKey = 78, PKey = 80 CONST RKey = 82, TKey = 84, VKey = 86 CONST SKey = 83, Minus = 45, Plus = 43 DIM Note(1 TO 6), Text$(1 TO 7) 'Make small array for notes and functions that are displayed Text$(1) = " Jump Method " 'Set Functions to be displayed Text$(2) = " Random Pause " Text$(3) = " Patch Number " Text$(4) = " Tempo % " Text$(5) = " Step Per Jump " Text$(6) = " Notes/Chord " Text$(7) = " Velocity " '------------------------------------------------------------------- ' See if a midi unit is installed in system '------------------------------------------------------------------- CALL SeeIfMPUExists(Found) 'See if unit exists in system IF Found = False THEN 'If MPU controller not found then error message and exit PRINT "Midi Controller not found or not responding." END END IF CALL ResetMpu 'Reset just in case some other program left if modified CALL SetDataInStopMode 'Minimize intelligence provided by MPU 401 or equivalent CALL OmniModeOn 'Set all midi data going to all midi channels CALL SetToPolyMode 'Set multivoice on '------------------------------------------------------------------- ' Display titles and a border '------------------------------------------------------------------- GOSUB DisplayBorderAndTitles 'Call local SUB to clear screen and display border and initial screen of text '------------------------------------------------------------------- ' Set defaults - at initial runtime for starting point '------------------------------------------------------------------- JumpMethod = False 'Random Method (True) or Drunk (False) RandomPause = False 'Insert Random pauses enabled on True PatchNumber = 64 'Sounds like a good number to start with Tempo = 50 'Set tempo to 100, moderate- May need to be changed when compiled JumpStep = 5 'Jump by fives when in drunken mode NotesPerChord = 2 'Play one note at a time to start Velocity = 64 'Moderate strike velocity GOSUB RedisplaySettings 'Show them on screen for the first time Selected = 1 'Which item is selected for editing GOSUB UpdateSelectedItem 'Show selected item '------------------------------------------------------------------- ' Check to see if they wish to continue '------------------------------------------------------------------- Text$ = "Press any key to begin or Esc to exit" Row = 22 CALL DispTextCenter(Row, Text$, Normal) 'Call local sub to display centered text WaitUntilKeyIsPressed = True CALL GetKeypress(Keypress, WaitUntilKeyIsPressed) 'Keypress contains ASCII value of key, -ASCII if extended key IF Keypress = 27 THEN CLS : END 'Exit program if escape key was kit '------------------------------------------------------------------- ' Change message at bottom '------------------------------------------------------------------- Text$ = " Press Space to Pause, Esc to stop " CALL DispTextCenter(22, Text$, Normal) '------------------------------------------------------------------- ' Change patch to default patch before sending any notes '------------------------------------------------------------------- CALL ChangePatchTo(PatchNumber) 'Call QBMIDI function '------------------------------------------------------------------- ' Start loop to play notes based on Random numbers and constraints ' given. Allow user to change constraints while running (listening). '------------------------------------------------------------------- RANDOMIZE TIMER 'Setup random number generator seed using internal clock DO '------------------------------------------------------------------- ' If pause hasn't been turned on then go play new notes, otherwise skip and just look for keypresses '------------------------------------------------------------------- IF PauseOn = 0 THEN '------------------------------------------------------------------- 'Select New Starting Note based on Jump Method selected 'where Random is truly random and drunk simulates a drunk stagger 'moving from side to side based on Random number given '------------------------------------------------------------------- IF JumpMethod THEN 'Random method StartingNote = (RND * 60) + 36 'Select random Starting note between 36 and 96 ELSE 'Drunk method SeedNote = (RND * 99) + 1 'Get seed between 1 and 100 IF SeedNote > 50 THEN 'If over fifty then jump up by Jump Step increment StartingNote = StartingNote + JumpStep 'Set Starting note up by Jump step IF StartingNote > 90 THEN StartingNote = 90 'If too high, then set at 80 ELSE StartingNote = StartingNote - JumpStep 'Jump down by JumpStep increment IF StartingNote < 42 THEN StartingNote = 42 'If too low, set bottom at 42 END IF END IF '------------------------------------------------------------------- ' Get ready to play next selected notes by truning off the ones ' currently playing - if any '------------------------------------------------------------------- i = 0 'Set counter to zero DO 'Start loop i = i + 1 'Increment counter IF Note(i) THEN 'If not was on, turn it off CALL PlayNote(Note(i), 0) 'Call PlayNote with velocity of zero ELSE 'Until Note(i)=0 EXIT DO 'Exit sub END IF Note(i) = 0 'Clear the Note to 0 LOOP UNTIL i = 6 'Loop until all six have been turned off '------------------------------------------------------------------- ' Set values for notes to play or leave at zero if not to play '------------------------------------------------------------------- Note(1) = StartingNote 'Set center point note IF NotesPerChord > 1 THEN Note(2) = StartingNote + 4 'If two notes are to be played, then calc second note IF NotesPerChord > 2 THEN Note(3) = StartingNote - 5 'If three then ... same IF NotesPerChord > 3 THEN Note(4) = StartingNote + 7 'And so on .. IF NotesPerChord > 4 THEN Note(5) = StartingNote + 12 IF NotesPerChord > 5 THEN Note(6) = StartingNote - 12 '------------------------------------------------------------------- ' Play the actual notes until you find a 0 then exit do loop '------------------------------------------------------------------- i = 0 'Set counter to zero DO 'Start loop i = i + 1 'Increment counter IF Note(i) THEN 'If a note value exists then play it CALL PlayNote(Note(i), Velocity) 'Call the play routine ELSE 'Until no more notes are found EXIT DO 'Exit loop early if less than 6 notes are to be played END IF LOOP UNTIL i = 6 'Exit if all six have been played ELSE 'If already paused IF RandomPause THEN 'If Ok to use random pause then PauseOn = PauseOn + 1 'If already paused increment counter IF PauseOn > LengthOfPause THEN 'If Time limit exceeded then PauseOn = 0 'Turn off pause flag GOSUB ClearPauseMessage 'Clear message off screen and continue LengthOfPause = 0 'Clear length of pause so we know to reset it later END IF END IF END IF '------------------------------------------------------------------- ' If Random Pauses allowed then calculate pause delay and time to next pause ' or increment counter while waiting for next pause '------------------------------------------------------------------- IF RandomPause THEN 'If random pause OK IF LengthOfPause = 0 THEN 'And length has not been set ChordsToPlayBeforePause = RND * 70 + 5 'Set number of chords to play before pausing LengthOfPause = RND * 75 'Set length of pause ChordsPlayed = 0 'Set chords played counter to zero (for when we need it) END IF IF PauseOn = 0 THEN 'If pause is not on then ChordsPlayed = ChordsPlayed + 1 'Increment chord counter IF ChordsPlayed > ChordsToPlayBeforePause THEN 'and check against number of chords to play before pause PauseOn = 1 'And if equal, set pause to on GOSUB DisplayPauseMessage 'Display pause message END IF END IF END IF '------------------------------------------------------------------- ' Wait for correct time with regards to tempo setting, checking KB for valid keypresses '------------------------------------------------------------------- Time = 0 'Clear time counter DO 'Start timing delay loop Time = Time + 1 'Increment time counter CALL GetKeypress(Keypress, False) 'See if key has been pressed IF Keypress THEN GOSUB HandleKeypress 'If so then gosub to check it IF Time > Tempo * 2 THEN EXIT DO 'Check if enough time has passed '------------------------------------------------------------------- ' While waiting - look at the midi port for data being sent back to us ' from the MPU and from devices attached .. ie keyboards, etc. ' ' For this example, we'll look for patch changes and reflect them ' on screen. ' ' The CALL ReceiveMidiData(Value) routine returns a -1 if no ' data is coming in from MIDI port, and the actual values if ' data is present. See the Midi 1.0 specification for a full ' list of commands returned from devices connected. Many devices ' send only the command change once until functions are changed. ' Meaning, if you change the patch from the keyboard, you'll get ' a 192 as the command then the number as the patch pressed. If you ' press another patch, it skips sending the 192 since you're already ' in that mode. Pressing a note on the keyboard sends a 144 then the ' note and then velocity. Each subsequent note will not be preceeded ' by a 144 unless another command occurred inbetween (for example ' a patch change). ' ' Some opular commands returned are (See your MIDI spec for more): ' Value = 192 Patch Change, followed by patch number ' Value = 144 Note on/off, followed by Note and velocity ' Value = 176 Portamento On/off ' 'Suggestion: ' Insert a print statement to view what's happening when you ' select functions or play from your Keyboard '------------------------------------------------------------------- CALL ReceiveMidiData(Value) 'Look at data coming in from Midi IF Value <> -1 THEN 'If -1, no data is present IF PatchChangeOn THEN 'If Previous data was patchchange then this data is patch number so make the change IF Value < 127 THEN 'If not a command then it's still a patch PatchNumber = Value 'Set patch to Value received GOSUB RedisplaySettings 'Show them on screen for the first time END IF END IF IF Value > 127 THEN 'If value >127 then command was received IF Value = 192 THEN 'If 192 then a patch change came in PatchChangeOn = True 'So set Patch change on so next value read will be read above ELSE 'Otherwise ignore PatchChangeOn = False END IF END IF END IF LOOP 'Loop until time passes LOOP 'Loop for next note END 'Should never get to this end ChangeKeyToRightArrow: '------------------------------------------------------------------- ' If item is already selected then control jumps here to change the key pressed to right arrow ' before entering handle Keypress routine. This lets you press the key letter and increment the value '------------------------------------------------------------------- Keypress = RightArrow HandleKeypress: '------------------------------------------------------------------- ' Handle Keypressed or ignore if not value '------------------------------------------------------------------- SELECT CASE Keypress CASE RightArrow, Plus SELECT CASE Selected CASE 1 'JumpMethod IF JumpMethod = False THEN JumpMethod = True ELSE JumpMethod = False CASE 2 'Random Pause IF RandomPause = True THEN RandomPause = False ELSE RandomPause = True CASE 3 'Patch Number PatchNumber = PatchNumber + 1 IF PatchNumber > 127 THEN PatchNumber = 0 CALL ChangePatchTo(PatchNumber) CASE 4 'Tempo Tempo = Tempo - 5 IF Tempo < 20 THEN Tempo = 20 CASE 5 'Jump per step JumpStep = JumpStep + 1 IF JumpStep > 12 THEN JumpStep = 12 CASE 6 'Notes/Chord NotesPerChord = NotesPerChord + 1 IF NotesPerChord > 6 THEN NotesPerChord = 6 CASE 7 'Velocity Velocity = Velocity + 2 IF Velocity > 127 THEN Velocity = 127 END SELECT GOSUB RedisplaySettings 'Show them on screen for the first time CASE LeftArrow, Minus SELECT CASE Selected CASE 1 'JumpMethod IF JumpMethod = False THEN JumpMethod = True ELSE JumpMethod = False CASE 2 'Random Pause IF RandomPause = True THEN RandomPause = False ELSE RandomPause = True CASE 3 'Patch Number PatchNumber = PatchNumber - 1 IF PatchNumber < 0 THEN PatchNumber = 127 CALL ChangePatchTo(PatchNumber) CASE 4 'Tempo Tempo = Tempo + 5 IF Tempo > 500 THEN Tempo = 500 CASE 5 'Jump per step JumpStep = JumpStep - 1 IF JumpStep < 1 THEN JumpStep = 1 CASE 6 'Notes/Chord NotesPerChord = NotesPerChord - 1 IF NotesPerChord < 1 THEN NotesPerChord = 1 CASE 7 'Velocity Velocity = Velocity - 2 IF Velocity < 1 THEN Velocity = 1 END SELECT GOSUB RedisplaySettings 'Show them on screen for the first time CASE SpaceBar IF PauseOn THEN PauseOn = 0 GOSUB ClearPauseMessage 'Clear the message on screen ELSE PauseOn = 1 GOSUB DisplayPauseMessage 'Display message END IF RETURN CASE EscapeKey 'Check for keypress GOTO EndNow CASE JKey IF Selected = 1 THEN GOTO ChangeKeyToRightArrow 'If already selected then change keypress to right arrow allowing them to increment value Selected = 1 'Otherwise set selected item at 1 CASE RKey IF Selected = 2 THEN GOTO ChangeKeyToRightArrow 'Ditto Selected = 2 CASE PKey IF Selected = 3 THEN GOTO ChangeKeyToRightArrow Selected = 3 CASE TKey IF Selected = 4 THEN GOTO ChangeKeyToRightArrow Selected = 4 CASE SKey IF Selected = 5 THEN GOTO ChangeKeyToRightArrow Selected = 5 CASE NKey IF Selected = 6 THEN GOTO ChangeKeyToRightArrow Selected = 6 CASE VKey IF Selected = 7 THEN GOTO ChangeKeyToRightArrow Selected = 7 CASE UpArrow Selected = Selected - 1: IF Selected = 0 THEN Selected = 7 CASE DownArrow Selected = Selected + 1: IF Selected = 8 THEN Selected = 1 CASE PageUp, HomeKey Selected = 1 CASE PageDown, EndKey Selected = 7 END SELECT GOSUB UpdateSelectedItem 'Redraw items and highlight selected item RETURN RedisplaySettings: '------------------------------------------------------------------- ' Update settings on screen '------------------------------------------------------------------- 'Note: Col and Offset were already set in Borders routine IF JumpMethod THEN Text$ = "Random" ELSE Text$ = "Drunk " CALL DispTextLeftJust(14, Col + Offset, Text$, Bright) IF RandomPause THEN Text$ = "On " ELSE Text$ = "Off" CALL DispTextLeftJust(15, Col + Offset, Text$, Bright) CALL DispNumbLeftJust(16, Col + Offset, PatchNumber, Bright) T1 = (Tempo - 50) * 100 'Calc Tempo in Percent T1 = T1 / 450 T1 = 100 - T1 CALL DispNumbLeftJust(17, Col + Offset, T1, Bright) CALL DispNumbLeftJust(18, Col + Offset, JumpStep, Bright) CALL DispNumbLeftJust(19, Col + Offset, NotesPerChord, Bright) CALL DispNumbLeftJust(20, Col + Offset, Velocity, Bright) RETURN ClearPauseMessage: '------------------------------------------------------------------- ' Display Paused messages '------------------------------------------------------------------- CALL DispTextLeftJust(15, Col + Offset + 12, " ", Normal) Pause = 0 LengthOfPause = 0 Text$ = " Press Space to Pause, Esc to stop " CALL DispTextCenter(22, Text$, Normal) RETURN DisplayPauseMessage: '------------------------------------------------------------------- ' Clear Paused messages and redisplay what to do at bottom '------------------------------------------------------------------- COLOR 0, Normal 'Set to Reverse momentairly CALL DispTextLeftJust(15, Col + Offset + 12, " Paused ", 0) 'Display as black COLOR Normal, 0 'Restore to normal Text$ = " Press Space to Continue, Esc to stop " CALL DispTextCenter(22, Text$, Normal) RETURN DisplayBorderAndTitles: '------------------------------------------------------------------- ' Borders and titles are used just once, so we've put them down ' here and Gosub once to get things started. This could have been ' placed in a SUB function and called instead, but we like to only ' put routines called often in Subs. '------------------------------------------------------------------- SCREEN 0 'Set text screen CLS 0 'Clear screen to black Row = 1 'Which Row Col = 1 'Which Column Text$ = "É" + STRING$(77, 205) + "»" 'Text to be displayed CALL DispTextLeftJust(Row, Col, Text$, Normal) 'Call local SUB that displays text left justified Text$ = "º" FOR Row = 2 TO 24 CALL DispTextLeftJust(Row, 1, Text$, Normal) CALL DispTextLeftJust(Row, 79, Text$, Normal) NEXT Row Row = 24 Text$ = "È" + STRING$(77, 205) + "¼" CALL DispTextLeftJust(Row, Col, Text$, Normal) Row = 1 Text$ = "  QBMIDI(TM) DEMO - MUSIC GENERATOR  " Attribute = Bright CALL DispTextCenter(Row, Text$, Attribute) Text$ = " (C) 1990 AskUs! Technology Specialists, Box 737, Bountiful, UT 84011 " 'Bottom title CALL DispTextCenter(25, Text$, Normal) Text$ = "This demonstration program uses the QBMIDI library to provide access" CALL DispTextCenter(4, Text$, Normal) Text$ = "your MIDI instruments via an MPU401 or compatible controller. Change" CALL DispTextCenter(5, Text$, Normal) Text$ = "the parameters below to hear different sound combinations. " CALL DispTextCenter(6, Text$, Normal) Text$ = "Use the Cursor Up/Down keys or Key letter to select function" CALL DispTextCenter(8, Text$, Normal) Text$ = "Right/Left Cursor to change value, space to pause/re-start" CALL DispTextCenter(9, Text$, Normal) Col = 27 Offset = 17 Text$ = "Function" CALL DispTextLeftJust(12, Col, Text$, Normal) Text$ = "Setting" CALL DispTextLeftJust(12, Col + Offset, Text$, Normal) Text$ = "-----------" CALL DispTextLeftJust(13, Col, Text$, Normal) Text$ = "-------" CALL DispTextLeftJust(13, Col + Offset, Text$, Normal) UpdateSelectedItem: '------------------------------------------------------------------- ' Redray items and highlight the one that Selected = '------------------------------------------------------------------- Item = 0 StartRow = 13 DO Item = Item + 1 IF Item = Selected THEN Attribute = 0 COLOR 0, Normal CALL DispTextLeftJust(StartRow + Item, Col - 1, Text$(Item), 0) COLOR Normal, 0 ELSE CALL DispTextLeftJust(StartRow + Item, Col - 1, Text$(Item), Normal) CALL DispTextLeftJust(StartRow + Item, Col - 1, LEFT$(Text$(Item), 2), Bright) END IF LOOP UNTIL Item = 7 RETURN EndNow: '------------------------------------------------------------------- ' End the program, but shut off all notes before leaving '------------------------------------------------------------------- CALL AllNotesOff 'Don't exit without shutting off all notes (it shuts off immediately, unlike Velocity=0 that uses normal decay value of patch) CLS 'Clear screen just to keep things tidy END SUB DispNumbLeftJust (Row, Col, Number, Attribute) STATIC LOCATE Row, Col - 1 COLOR Attribute PRINT Number; " "; COLOR Normal END SUB SUB DispTextCenter (Row, Text$, Attribute) STATIC LOCATE Row, 40 - LEN(Text$) / 2 COLOR Attribute PRINT Text$; COLOR Normal END SUB SUB DispTextLeftJust (Row, Col, Text$, Attribute) STATIC LOCATE Row, Col COLOR Attribute PRINT Text$; COLOR Normal END SUB SUB GetKeypress (AsciiValueOfKey, WaitUntilKeyIsPressed) AsciiValueOfKey = 0 'Clear variable ExtendedKey = 0 'Turn off extended key flag (used to show that function key was pressed) IF WaitUntilKeyIsPressed THEN DO: LOOP UNTIL INKEY$ = "" 'Clear out keys that may be sitting in buffer DO 'Loop until a key is pressed Key$ = INKEY$ LOOP WHILE Key$ = "" ELSE Key$ = INKEY$ WaitUntilKeyIsPressed = 0 IF LEN(Key$) = 0 THEN EXIT SUB END IF IF LEN(Key$) > 1 THEN 'Check for extended key (F1, Cursor, etc.) by checking for length variable >1 Key$ = RIGHT$(Key$, 1) 'If extended then get right character in variable ExtendedKey = -1 'Set flag to show extended key was pressed END IF AsciiValueOfKey = (ASC(UCASE$(Key$))) 'Place ASCII value in variable to be returned IF ExtendedKey THEN 'If extended then convert to minus AsciiValueOfKey = -AsciiValueOfKey 'Convert to minus number to show extended key END IF END SUB