'==========
'KEYS48.BAS v1.0
'==========
'A Playable/Recordable Keyboard instrument
'Coded by Dav for QB64, JAN/2021
'NOTE: Before compiling, check 'Output EXE to Source'
'-----
'ABOUT
'-----
'KEYS48 is a virtual instrument that you can play and
'and record little piano tunes with. Use mouse to click on
'and play notes. The program starts in freeplay mode, but
'you can record in real time as you play by pressing R.
'Record over your song several times to add more notes and
'make more complex patterns (called overdubbing).
'--------
'CONTROLS
'--------
'Press M fo a menu list of commands.
'Press R to record song and to overdub (add) notes to a song.
'Press U to Undo last recording (in case you make a mistake!)
'Press P to playback the recorded song currently in memory.
'Press C to clear (erase) recorded notes currently in memory.
'Press +/- keys to adjust playback and recording speed.
' Current speed value is shown at the top left.
'Press ENTER to go back to default speed of 100.
'Press ESC to stop recording playback of the song.
'NOTE: You can also play notes during song playback.
'Press S to Save your recording to .K46 file in songs dir.
' If the file already exists, it will be overwritten.
' If not the program will ask you for a song name.
' The .K48 extension will be added automatically.
'Press L to Load a saved song in the songs dir.
'Press D to Delete currently load song.
'
'=========================================================
'
'PRO TIP: Having trouble playing fast enough with mouse?
' Slow down Speed BEFORE recording, then raise it
' back up when playing song back.
'
'=========================================================
'
'NOTES: The directory 'keys' hold the keyboard sounds.
' The directory 'songs' holds all the .K48 songs.
' Both directories are required to use this program.
' They must be in the current EXE program path.
' Currently loaded song and speed is shown top left.
' Song speed setting is saved with the song file.
' Currently the max song length is about 6 minutes.
' You can change that with the MaxSongLength var.
'
'=========================================================
'
'Things planned in next version: Record speed changes
' (Add Page Up/Down +/- 5)
' Turn Overdub ON/Off
' Add a Metronome click
' Add render to .WAV file
' (will ask Petr for help!)
' Add more keyboard sounds
' Add Loop song playing
' pause, record position
' Seek position in song
'
'=========================================================
'Make sure keys directory is present first...
PRINT "ERROR: Needed 'keys' directory not found!"
'Make a songs directory if not found
'share note variable, playing flag, timer for all 48 keys
DIM SHARED n01flag
, n01timer
, n01&
, n02flag
, n02timer
, n02&
DIM SHARED n03flag
, n03timer
, n03&
, n04flag
, n04timer
, n04&
DIM SHARED n05flag
, n05timer
, n05&
, n06flag
, n06timer
, n06&
DIM SHARED n07flag
, n07timer
, n07&
, n08flag
, n08timer
, n08&
DIM SHARED n09flag
, n09timer
, n09&
, n10flag
, n10timer
, n10&
DIM SHARED n11flag
, n11timer
, n11&
, n12flag
, n12timer
, n12&
DIM SHARED n13flag
, n13timer
, n13&
, n14flag
, n14timer
, n14&
DIM SHARED n15flag
, n15timer
, n15&
, n16flag
, n16timer
, n16&
DIM SHARED n17flag
, n17timer
, n17&
, n18flag
, n18timer
, n18&
DIM SHARED n19flag
, n19timer
, n19&
, n20flag
, n20timer
, n20&
DIM SHARED n21flag
, n21timer
, n21&
, n22flag
, n22timer
, n22&
DIM SHARED n23flag
, n23timer
, n23&
, n24flag
, n24timer
, n24&
DIM SHARED n25flag
, n25timer
, n25&
, n26flag
, n26timer
, n26&
DIM SHARED n27flag
, n27timer
, n27&
, n28flag
, n28timer
, n28&
DIM SHARED n29flag
, n29timer
, n29&
, n30flag
, n30timer
, n30&
DIM SHARED n31flag
, n31timer
, n31&
, n32flag
, n32timer
, n32&
DIM SHARED n33flag
, n33timer
, n33&
, n34flag
, n34timer
, n34&
DIM SHARED n35flag
, n35timer
, n35&
, n36flag
, n36timer
, n36&
DIM SHARED n37flag
, n37timer
, n37&
, n38flag
, n38timer
, n38&
DIM SHARED n39flag
, n39timer
, n39&
, n40flag
, n40timer
, n40&
DIM SHARED n41flag
, n41timer
, n41&
, n42flag
, n42timer
, n42&
DIM SHARED n43flag
, n43timer
, n43&
, n44flag
, n44timer
, n44&
DIM SHARED n45flag
, n45timer
, n45&
, n46flag
, n46timer
, n46&
DIM SHARED n47flag
, n47timer
, n47&
, n48flag
, n48timer
, n48&
'Load/open all keys sound files
'Share/define these values - used often
DIM SHARED WhtOn&
, WhtOff&
, BlkOn&
, BlkOff&
, Blk&
'colors DIM SHARED speed
, MaxSongLength
, release
, header$
WhtOn&
= _RGB(179, 192, 211): WhtOff&
= _RGB(246, 255, 255) 'key colorsBlkOn&
= _RGB(48, 84, 107): BlkOff&
= _RGB(43, 56, 63): Blk&
= _RGB(23, 23, 23)release = .4 'how long to highlight notes when played
MaxSongLength = 500000 'max of 500k of song memory
speed = 100 'playback speed defaults to 100%
'========
MainLoop: 'I dont currently call this, but it's here if needed.
'========
GOSUB PlayKeys
'freeplay notes
'Get user input...
NoteData$ = "": song$ = ""
DrawBox:
LOCATE 15, 37:
PRINT " SONG MEMORY IS NOW CLEARED! "
GOSUB SaveScreen: DrawBox
NoteData$ = UndoData$
LOCATE 15, 37:
PRINT " UNDOING YOUR LAST RECORDING... ";
GOSUB SaveScreen: DrawBox
IF song$
<> "" THEN 'song already opened...just update song header$
= CHR$(27) + "K48V100" + CHR$(speed
)
'notedata here, song$ not made yet, so save new file
'strip out any path given (thanks, Steve!)
IF INSTR(1, song$
, ".") = 0 THEN song$
= song$
+ ".k48" header$
= CHR$(27) + "K48V100" + CHR$(speed
)
GOSUB SaveScreen: DrawBox
'strip out any path given (thanks, Steve!)
IF INSTR(1, tmpsong$
, ".") = 0 THEN tmpsong$
= tmpsong$
+ ".k48" header$ = "" 'file not big enough
header$
= INPUT$(9, 1) 'load header 'set saved speed
song$ = tmpsong$
GOSUB SaveScreen: DrawBox
DrawBox
NoteData$ = "": song$ = ""
speed
= speed
- 1:
IF speed
< 40 THEN speed
= 40
speed
= speed
+ 1:
IF speed
> 200 THEN speed
= 200
speed
= 100:
GOSUB UpdateInfo
_LIMIT speed
* 15 'this is the current delay/speed method....
'=========================================================
PlayKeys: 'Gets mouse input and plays keyboard
'=======
'=== play notes if clicked on one
'bottom row...
'top row...
done = 1 'make a mouse click flag
'=========================================================
record:
'=======
'Make copy of NoteData$ for undo purposes
UndoData$ = NoteData$
IF NoteData$
= "" THEN NoteData$
= "1" 'song recording marker note = 1
_LIMIT speed
* 15 'this is the current delay/speed method....
note$ = ""
'playback any previously recorded notes
'=== play notes if clicked on one
'top row...
'bottom row...
done = 1 'did a mouse click
MID$(NoteData$
, note
, 1) = note$
'add the note NoteData$ = NoteData$ + note$
NoteData$
= NoteData$
+ CHR$(255) 'else add empty space
note = note + 1
'=========================================================
playback:
'========
'If nothing recorded yet, don't try play
note = 1
_LIMIT speed
* 15 'this is the current delay/speed method....
note = note + 1
'if done playing, exit
GOSUB PlayKeys
'you can play notes too while song plays
speed = speed - 1
speed = speed + 1
IF speed
> 200 THEN speed
= 200 speed = 100
'=======================================================
NoteDisplay:
'===========
'if note playing flag is on, check its timer.
'If release time is past, note goes back to normal, flag off
n01flag = 0
LINE (4, 346)-(40, 434), WhtOff&
, BF
LINE (4, 434)-(63, 515), WhtOff&
, BF
n02timer
= 0:
LINE (44, 348)-(76, 429), BlkOff&
, BF
n03timer = 0
LINE (81, 346)-(109, 434), WhtOff&
, BF
LINE (68, 434)-(127, 515), WhtOff&
, BF
n04timer
= 0:
LINE (113, 348)-(146, 429), BlkOff&
, BF
n05timer = 0
LINE (150, 346)-(178, 434), WhtOff&
, BF
LINE (132, 434)-(191, 515), WhtOff&
, BF
n06timer
= 0:
LINE (183, 348)-(214, 429), BlkOff&
, BF
n07timer = 0
LINE (219, 346)-(255, 434), WhtOff&
, BF
LINE (196, 434)-(255, 515), WhtOff&
, BF
n08timer = 0
LINE (260, 346)-(295, 434), WhtOff&
, BF
LINE (260, 434)-(319, 515), WhtOff&
, BF
n09timer
= 0:
LINE (300, 348)-(332, 429), BlkOff&
, BF
n10timer = 0
LINE (337, 346)-(370, 434), WhtOff&
, BF
LINE (324, 434)-(383, 515), WhtOff&
, BF
n11timer
= 0:
LINE (375, 348)-(406, 429), BlkOff&
, BF
n12timer = 0
LINE (411, 346)-(447, 434), WhtOff&
, BF
LINE (387, 434)-(447, 515), WhtOff&
, BF
n13timer = 0
LINE (451, 346)-(487, 434), WhtOff&
, BF
LINE (451, 434)-(511, 515), WhtOff&
, BF
n14timer
= 0:
LINE (492, 348)-(523, 429), BlkOff&
, BF
n15timer = 0
LINE (528, 346)-(556, 434), WhtOff&
, BF
LINE (515, 434)-(575, 515), WhtOff&
, BF
n16timer
= 0:
LINE (560, 348)-(593, 429), BlkOff&
, BF
n17timer = 0
LINE (598, 346)-(625, 434), WhtOff&
, BF
LINE (579, 434)-(639, 515), WhtOff&
, BF
n18timer
= 0:
LINE (630, 348)-(663, 429), BlkOff&
, BF
n19timer = 0
LINE (667, 346)-(702, 434), WhtOff&
, BF
LINE (643, 434)-(702, 515), WhtOff&
, BF
n20timer = 0
LINE (707, 346)-(743, 434), WhtOff&
, BF
LINE (707, 434)-(766, 515), WhtOff&
, BF
n21timer
= 0:
LINE (747, 348)-(779, 429), BlkOff&
, BF
n22timer = 0
LINE (784, 346)-(817, 434), WhtOff&
, BF
LINE (771, 434)-(830, 515), WhtOff&
, BF
n23timer
= 0:
LINE (822, 348)-(854, 429), BlkOff&
, BF
n24timer = 0
LINE (858, 346)-(894, 434), WhtOff&
, BF
LINE (835, 434)-(894, 515), WhtOff&
, BF
n25timer = 0
LINE (5, 123)-(41, 210), WhtOff&
, BF
LINE (5, 211)-(64, 292), WhtOff&
, BF
n26timer
= 0:
LINE (45, 125)-(78, 207), BlkOff&
, BF
n27timer = 0
LINE (82, 123)-(110, 210), WhtOff&
, BF
LINE (69, 211)-(128, 292), WhtOff&
, BF
n28timer
= 0:
LINE (114, 125)-(147, 207), BlkOff&
, BF
n29timer = 0
LINE (151, 123)-(179, 210), WhtOff&
, BF
LINE (133, 211)-(192, 292), WhtOff&
, BF
n30timer
= 0:
LINE (184, 125)-(215, 207), BlkOff&
, BF
n31timer = 0
LINE (220, 123)-(256, 210), WhtOff&
, BF
LINE (197, 211)-(256, 292), WhtOff&
, BF
n32timer = 0
LINE (261, 123)-(296, 210), WhtOff&
, BF
LINE (261, 211)-(320, 292), WhtOff&
, BF
n33timer
= 0:
LINE (301, 125)-(332, 207), BlkOff&
, BF
n34timer = 0
LINE (338, 123)-(371, 210), WhtOff&
, BF
LINE (325, 211)-(384, 292), WhtOff&
, BF
n35timer
= 0:
LINE (376, 125)-(407, 207), BlkOff&
, BF
n36timer = 0
LINE (412, 123)-(448, 210), WhtOff&
, BF
LINE (388, 211)-(448, 292), WhtOff&
, BF
n37timer = 0
LINE (452, 123)-(488, 210), WhtOff&
, BF
LINE (452, 211)-(512, 292), WhtOff&
, BF
n38timer
= 0:
LINE (493, 125)-(525, 207), BlkOff&
, BF
n39timer = 0
LINE (529, 123)-(557, 210), WhtOff&
, BF
LINE (516, 211)-(576, 292), WhtOff&
, BF
n40timer
= 0:
LINE (561, 125)-(594, 207), BlkOff&
, BF
n41timer = 0
LINE (599, 123)-(626, 210), WhtOff&
, BF
LINE (580, 211)-(640, 292), WhtOff&
, BF
n42timer
= 0:
LINE (631, 125)-(664, 207), BlkOff&
, BF
n43timer = 0
LINE (668, 123)-(703, 210), WhtOff&
, BF
LINE (644, 211)-(703, 292), WhtOff&
, BF
n44timer = 0
LINE (708, 123)-(744, 210), WhtOff&
, BF
LINE (708, 211)-(767, 292), WhtOff&
, BF
n45timer
= 0:
LINE (748, 125)-(780, 207), BlkOff&
, BF
n46timer = 0
LINE (785, 123)-(818, 210), WhtOff&
, BF
LINE (772, 211)-(831, 292), WhtOff&
, BF
n47timer
= 0:
LINE (823, 125)-(855, 207), BlkOff&
, BF
n48timer = 0
LINE (859, 123)-(895, 210), WhtOff&
, BF
LINE (836, 211)-(895, 292), WhtOff&
, BF
'==========================================================
SaveScreen: 'save current screen image
'=========
_MEMCOPY scr1
, scr1.OFFSET
, scr1.SIZE
TO scr2
, scr2.OFFSET
'==========================================================
RestoreScreen: 'restores screen image saved
'============
_MEMCOPY scr2
, scr2.OFFSET
, scr2.SIZE
TO scr1
, scr1.OFFSET
'==========================================================
UpdateInfo: 'clears area and sets info at the top left
'=============
'==========================================================
'Plays the note$ sound, sets playing flag
n01flag
= 1: n01timer
= TIMER LINE (4, 346)-(40, 434), WhtOn&
, BF
LINE (4, 434)-(63, 515), WhtOn&
, BF
n02flag
= 1: n02timer
= TIMER LINE (44, 348)-(76, 429), BlkOn&
, BF
n03flag
= 1: n03timer
= TIMER LINE (81, 346)-(109, 434), WhtOn&
, BF
LINE (68, 434)-(127, 515), WhtOn&
, BF
n04flag
= 1: n04timer
= TIMER LINE (113, 348)-(146, 429), BlkOn&
, BF
n05flag
= 1: n05timer
= TIMER:
LINE (150, 346)-(178, 434), WhtOn&
, BF
LINE (132, 434)-(191, 515), WhtOn&
, BF
n06flag
= 1: n06timer
= TIMER LINE (183, 348)-(214, 429), BlkOn&
, BF
n07flag
= 1: n07timer
= TIMER LINE (219, 346)-(255, 434), WhtOn&
, BF
LINE (196, 434)-(255, 515), WhtOn&
, BF
n08flag
= 1: n08timer
= TIMER LINE (260, 346)-(295, 434), WhtOn&
, BF
LINE (260, 434)-(319, 515), WhtOn&
, BF
n09flag
= 1: n09timer
= TIMER LINE (300, 348)-(332, 429), BlkOn&
, BF
n10flag
= 1: n10timer
= TIMER LINE (337, 346)-(370, 434), WhtOn&
, BF
LINE (324, 434)-(383, 515), WhtOn&
, BF
n11flag
= 1: n11timer
= TIMER LINE (375, 348)-(406, 429), BlkOn&
, BF
n12flag
= 1: n12timer
= TIMER LINE (411, 346)-(447, 434), WhtOn&
, BF
LINE (387, 434)-(447, 515), WhtOn&
, BF
n13flag
= 1: n13timer
= TIMER LINE (451, 346)-(487, 434), WhtOn&
, BF
LINE (451, 434)-(511, 515), WhtOn&
, BF
n14flag
= 1: n14timer
= TIMER LINE (492, 348)-(523, 429), BlkOn&
, BF
n15flag
= 1: n15timer
= TIMER LINE (528, 346)-(556, 434), WhtOn&
, BF
LINE (515, 434)-(575, 515), WhtOn&
, BF
n16flag
= 1: n16timer
= TIMER LINE (560, 348)-(593, 429), BlkOn&
, BF
n17flag
= 1: n17timer
= TIMER LINE (598, 346)-(625, 434), WhtOn&
, BF
LINE (579, 434)-(639, 515), WhtOn&
, BF
n18flag
= 1: n18timer
= TIMER LINE (630, 348)-(663, 429), BlkOn&
, BF
n19flag
= 1: n19timer
= TIMER LINE (667, 346)-(702, 434), WhtOn&
, BF
LINE (643, 434)-(702, 515), WhtOn&
, BF
n20flag
= 1: n20timer
= TIMER LINE (707, 346)-(743, 434), WhtOn&
, BF
LINE (707, 434)-(766, 515), WhtOn&
, BF
n21flag
= 1: n21timer
= TIMER LINE (747, 348)-(779, 429), BlkOn&
, BF
n22flag
= 1: n22timer
= TIMER LINE (784, 346)-(817, 434), WhtOn&
, BF
LINE (771, 434)-(830, 515), WhtOn&
, BF
n23flag
= 1: n23timer
= TIMER LINE (822, 348)-(854, 429), BlkOn&
, BF
n24flag
= 1: n24timer
= TIMER LINE (858, 346)-(894, 434), WhtOn&
, BF
LINE (835, 434)-(894, 515), WhtOn&
, BF
n25flag
= 1: n25timer
= TIMER LINE (5, 123)-(41, 210), WhtOn&
, BF
LINE (5, 211)-(64, 292), WhtOn&
, BF
n26flag
= 1: n26timer
= TIMER LINE (45, 125)-(78, 207), BlkOn&
, BF
n27flag
= 1: n27timer
= TIMER LINE (82, 123)-(110, 210), WhtOn&
, BF
LINE (69, 211)-(128, 292), WhtOn&
, BF
n28flag
= 1: n28timer
= TIMER LINE (114, 125)-(147, 207), BlkOn&
, BF
n29flag
= 1: n29timer
= TIMER LINE (151, 123)-(179, 210), WhtOn&
, BF
LINE (133, 211)-(192, 292), WhtOn&
, BF
n30flag
= 1: n30timer
= TIMER LINE (184, 125)-(215, 207), BlkOn&
, BF
n31flag
= 1: n31timer
= TIMER LINE (220, 123)-(256, 210), WhtOn&
, BF
LINE (197, 211)-(256, 292), WhtOn&
, BF
n32flag
= 1: n32timer
= TIMER LINE (261, 123)-(296, 210), WhtOn&
, BF
LINE (261, 211)-(320, 292), WhtOn&
, BF
n33flag
= 1: n33timer
= TIMER LINE (301, 125)-(332, 207), BlkOn&
, BF
n34flag
= 1: n34timer
= TIMER LINE (338, 123)-(371, 210), WhtOn&
, BF
LINE (325, 211)-(384, 292), WhtOn&
, BF
n35flag
= 1: n35timer
= TIMER LINE (376, 125)-(407, 207), BlkOn&
, BF
n36flag
= 1: n36timer
= TIMER LINE (412, 123)-(448, 210), WhtOn&
, BF
LINE (388, 211)-(448, 292), WhtOn&
, BF
n37flag
= 1: n37timer
= TIMER LINE (452, 123)-(488, 210), WhtOn&
, BF
LINE (452, 211)-(512, 292), WhtOn&
, BF
n38flag
= 1: n38timer
= TIMER LINE (493, 125)-(525, 207), BlkOn&
, BF
n39flag
= 1: n39timer
= TIMER LINE (529, 123)-(557, 210), WhtOn&
, BF
LINE (516, 211)-(576, 292), WhtOn&
, BF
n40flag
= 1: n40timer
= TIMER LINE (561, 125)-(594, 207), BlkOn&
, BF
n41flag
= 1: n41timer
= TIMER LINE (599, 123)-(626, 210), WhtOn&
, BF
LINE (580, 211)-(640, 292), WhtOn&
, BF
n42flag
= 1: n42timer
= TIMER LINE (632, 125)-(664, 207), BlkOn&
, BF
n43flag
= 1: n43timer
= TIMER LINE (668, 123)-(703, 210), WhtOn&
, BF
LINE (644, 211)-(703, 292), WhtOn&
, BF
n44flag
= 1: n44timer
= TIMER LINE (708, 123)-(744, 210), WhtOn&
, BF
LINE (708, 211)-(767, 292), WhtOn&
, BF
n45flag
= 1: n45timer
= TIMER LINE (748, 125)-(780, 207), BlkOn&
, BF
n46flag
= 1: n46timer
= TIMER LINE (785, 123)-(818, 210), WhtOn&
, BF
LINE (772, 211)-(831, 292), WhtOn&
, BF
n47flag
= 1: n47timer
= TIMER LINE (823, 125)-(855, 207), BlkOn&
, BF
n48flag
= 1: n48timer
= TIMER LINE (859, 123)-(895, 210), WhtOn&
, BF
LINE (836, 211)-(895, 292), WhtOn&
, BF
'=======================================================
SUB DrawBox
'Draws the litte info box. Used often LINE (250, 200)-(650, 270), _RGB(70, 95, 114), BF
LINE (252, 202)-(648, 268), _RGB(255, 255, 255), B