'=======================
'DRUMMACHINE.BAS - v0.1c
'=======================
'QB64 Drum machine
'Make drum beats using 16 drum sounds.
'Coded by Dav for QB64, MAY/2020
'Follow this programs development here:
'https://www.qb64.org/forum/index.php?topic=2459.0
'===================================================================
'New in v0.1c...
'Added: Added program _TITLE and _ICON.
'Added: New logo with keyboard command help.
'Added: Shows big TEMPO (BMP) font at top left.
'Added: Shows green playing position line down grid.
' (replaced little white moving marker)
'Added: Give user notices when a Save/Load is done.
'Added: Instrument names can now be clicked on to mute/unmute.
'Added: Now shows on screen if metronome is ON/OFF
'Fixed: Changed keyboard input method. Much more responsive.
'
'===================================================================
''NOTE: You need "\drummachine-data\" and all of its data files!
'===================================================================
'=====
'ABOUT:
'=====
'This is a very basic drum machine with limited capabilities.
'With it you can program beats (limited to 2 bars of 4/4 now).
'It uses 16 real drum sounds to make a real sound drum beat.
'Drum patterns can be saved and loaded by file DRUMMACHINE.SAV.
'I just wanted to see if QB64 can handle something like this,
'and it looks like it can. So a more serious program can be tackled.
'The real drum sound samples were all found in public domain.
'==========
'HOW TO USE:
'==========
'The grid represents bars and notes that you can click on.
'Click on squares in a row to to add sound for that beat.
'Click an square again to turn it back off or change it's volume.
'A bright red square is loudest, a darker red is softer volume.
'Click on the instrument name on left screen to mute/unmute it.
'The following keys can be used as well:
'* +/- keys: Speeds up and slow down tempo (shown upper left)
'* SPACE key: Will pause and resume the playback.
'* ENTER: Resets current playback marker to the beginning.
'* M: Turns on/off metonome click sound (default is on)
'* C: Clears the playing grid (starts over).
'* L: Loads last saved grid pattern from DRUMMACHINE.SAV
'* S: Saves current grid pattern to DRUMMACHINE.SAV.
'* D: Enters drawing mode (fast filling of boxes.
' (While in it, enter ESC to exit drawing mode)
'* ESC: Exits program. DOES NOT ATUMATICALLY SAVE ON EXIT.
'======================================================================
'define playing grid and button deminsions
DIM SHARED row: row
= 16 'rows in playing grid DIM SHARED col: col
= 8 * 4 'columns in grid (8 beats, 4 boxes per beat) DIM SHARED size: size
= 30 ' pixel size of buttons DIM SHARED buttons: buttons
= row
* col
' total number of buttons on playing grid
'define button data
DIM SHARED buttonv
(row
* col
) ' num data for button DIM SHARED buttonx
(row
* col
), buttony
(row
* col
) 'top x/y cords of buttons DIM SHARED buttonx2
(row
* col
), buttony2
(row
* col
) ' bottom x/y cords of buttons
'define intrument name data
DIM SHARED instv
(row
* col
) 'value for is intrument on or off DIM SHARED instx
(row
* col
), insty
(row
* col
) 'top x/y cords of instr names
'share these sound files
DIM SHARED crash&
, crash2&
, ride&
, ride2&
, hhopen&
, hhclosed&
, snare&
, kick&
, tamb&
, gong2&
DIM SHARED tomhigh&
, tommid&
, tomlow&
, cowbell&
, shaker&
, gong&
, vibraslap&
, vibraslap2&
DIM SHARED clave&
, trianglelong&
, triangleshort&
'====================================================
'Set screen based on grid deminsions
'===================================================
'Load title top area.
'Load media sounds used
click&
= _SNDOPEN("drummachine-data\click.ogg") hhopen&
= _SNDOPEN("drummachine-data\hhopen.ogg") hhclosed&
= _SNDOPEN("drummachine-data\hhclosed.ogg") kick&
= _SNDOPEN("drummachine-data\kick.ogg") snare&
= _SNDOPEN("drummachine-data\snare.ogg") crash&
= _SNDOPEN("drummachine-data\crash.ogg") crash2&
= _SNDOPEN("drummachine-data\crash.ogg") ride&
= _SNDOPEN("drummachine-data\ride.ogg") ride2&
= _SNDOPEN("drummachine-data\ride.ogg") tomhigh&
= _SNDOPEN("drummachine-data\tomhigh.ogg") tommid&
= _SNDOPEN("drummachine-data\tommid.ogg") tomlow&
= _SNDOPEN("drummachine-data\tomlow.ogg") cowbell&
= _SNDOPEN("drummachine-data\cowbell.ogg") shaker&
= _SNDOPEN("drummachine-data\shaker.ogg") vibraslap&
= _SNDOPEN("drummachine-data\vibraslap.ogg") gong&
= _SNDOPEN("drummachine-data\gong.ogg") vibraslap2&
= _SNDOPEN("drummachine-data\vibraslap.ogg") gong2&
= _SNDOPEN("drummachine-data\gong.ogg") tamb&
= _SNDOPEN("drummachine-data\tamb.ogg") clave&
= _SNDOPEN("drummachine-data\clave.ogg") trianglelong&
= _SNDOPEN("drummachine-data\trianglelong.ogg") triangleshort&
= _SNDOPEN("drummachine-data\triangleshort.ogg")
'Load special number font images fro BMP display
num&
(t
) = _LOADIMAGE("drummachine-data\font\" + n$
+ ".png")
'===================================================
'Set program defaults
beats = 8 'number of beats in pattern
tempo = 80 'tempo of drum pattern
curbeat = 1 'start at bar 1
clickon = 1 'turn on click sound, on the beat
playing = 1 'Pattern is playing
firstlaunch = 1 'flag to know when program first launches
'=========================================================
START:
'========
'Init the grids button values (x/y, data)
bc = 1 'counter
x = (c * size) + 100: y = (r * size) + 100
buttonx(bc) = x - size: buttonx2(bc) = x ' generate x/y values
buttony(bc) = y - size: buttony2(bc) = y
buttonv(bc) = 0 'default button is OFF
bc = bc + 1
'Show metronome on/off
Metronome clickon
'Show the grid buttons
bc = 1
Show "drummachine-data\off.jpg", buttonx(bc), buttony(bc), 0
bc = bc + 1
'Draw beat lines, every 4 squares ...
bc = 1
LINE (buttonx
(bc
), buttony
(bc
))-(buttonx
(bc
), buttony
(bc
) + (size
* row
)), _RGB(128, 128, 0), B
bc = bc + 4
'====================================
'Init Instrument name data
instv(c) = 1 'all instruments on by default
bc = 1
instx(bc) = 0: insty(bc) = 70 + (bc * 30)
bc = bc + 1
'show the nstrument names
ShowInst c, instx(c), insty(c)
'==============================================
'Show current TEMPO (BMP) on upper left
'================================================
'If firstlaunch, load the saved pattern file
firstlaunch = 0
'============================================================
MAIN:
'=====
'Main Loop here
IF curbeat
= 1 THEN PlayBeat
(1) IF curbeat
= 1.25 THEN PlayBeat
(2) IF curbeat
= 1.50 THEN PlayBeat
(3) IF curbeat
= 1.75 THEN PlayBeat
(4) IF curbeat
= 2 THEN PlayBeat
(5) IF curbeat
= 2.25 THEN PlayBeat
(6) IF curbeat
= 2.50 THEN PlayBeat
(7) IF curbeat
= 2.75 THEN PlayBeat
(8) IF curbeat
= 3 THEN PlayBeat
(9) IF curbeat
= 3.25 THEN PlayBeat
(10) IF curbeat
= 3.50 THEN PlayBeat
(11) IF curbeat
= 3.75 THEN PlayBeat
(12) IF curbeat
= 4 THEN PlayBeat
(13) IF curbeat
= 4.25 THEN PlayBeat
(14) IF curbeat
= 4.50 THEN PlayBeat
(15) IF curbeat
= 4.75 THEN PlayBeat
(16) IF curbeat
= 5 THEN PlayBeat
(17) IF curbeat
= 5.25 THEN PlayBeat
(18) IF curbeat
= 5.50 THEN PlayBeat
(19) IF curbeat
= 5.75 THEN PlayBeat
(20) IF curbeat
= 6 THEN PlayBeat
(21) IF curbeat
= 6.25 THEN PlayBeat
(22) IF curbeat
= 6.50 THEN PlayBeat
(23) IF curbeat
= 6.75 THEN PlayBeat
(24) IF curbeat
= 7 THEN PlayBeat
(25) IF curbeat
= 7.25 THEN PlayBeat
(26) IF curbeat
= 7.50 THEN PlayBeat
(27) IF curbeat
= 7.75 THEN PlayBeat
(28) IF curbeat
= 8 THEN PlayBeat
(29) IF curbeat
= 8.25 THEN PlayBeat
(30) IF curbeat
= 8.50 THEN PlayBeat
(31) IF curbeat
= 8.75 THEN PlayBeat
(32)
curbeat = curbeat + .25
IF curbeat
> beats
+ .75 THEN curbeat
= 1
'Delay routine, based on TEMP
'still get user input while delaying....
'===============================
EndProgram:
'================================================
GetUserInput:
'============
'if left mouse button clicked
'see if a grid button was pressed
bx = buttonx(t): bx2 = buttonx2(t)
by = buttony(t): by2 = buttony2(t)
'If clicked on a grid button...
'change its value...
buttonv(t) = buttonv(t) + 1
IF buttonv
(t
) > 2 THEN buttonv
(t
) = 0 'LOCATE 1, 1: PRINT t 'for testing purposes...
CASE 0: Show
"drummachine-data\off.jpg", buttonx
(t
), buttony
(t
), 0 CASE 1: Show
"drummachine-data\hot.jpg", buttonx
(t
), buttony
(t
), 0 CASE 2: Show
"drummachine-data\med.jpg", buttonx
(t
), buttony
(t
), 0 buttonv(t) = 0
Show "drummachine-data\off.jpg", buttonx(t), buttony(t), 0
'see if a instrument name was pressed
bx = instx(t): bx2 = instx(t) + 100
by = insty(t): by2 = insty(t) + 30
'If clicked on an instrument name...
'change its value...
instv(t) = instv(t) + 1
IF instv
(t
) > 1 THEN instv
(t
) = 0 'redraw instrument name here
ShowInst t, instx(t), insty(t)
'wait until mouse button up to continue
'check is user made a keypress
CASE "D":
'd enters drawing mode 'see if a button was pressed
bx = buttonx(t): bx2 = buttonx2(t)
by = buttony(t): by2 = buttony2(t)
buttonv(t) = 1
Show "drummachine-data\hot.jpg", buttonx(t), buttony(t), 0
buttonv(t) = 0
Show "drummachine-data\off.jpg", buttonx(t), buttony(t), 0
CASE "C":
GOTO START
'C clears playing grid, starts over
CASE "S":
's = saves current pattern to file f$ = "drummachine-data\savingpattern.png"
f$ = "drummachine-data\loadingpattern.png"
CASE " " 'SPACE pauses and resumes playing playing = 0
playing = 1
CASE CHR$(13) 'ENTER resets marker to beginning 'erase all marker positions
CASE 1, 5, 9, 13, 17, 21, 25, 29 LINE (buttonx
(e
), buttony
(e
))-(buttonx
(e
), buttony
(e
) + (size
* row
)), _RGB(128, 128, 0), B
LINE (buttonx
(e
), buttony
(e
))-(buttonx
(e
), buttony
(e
) + (size
* row
)), _RGB(0, 0, 0), B
LINE (buttonx
(1), buttony
(1))-(buttonx
(1), buttony
(1) + (size
* row
)), _RGB(0, 255, 128), B
CASE "M" 'm = turn metronome click on/off clickon = 0
clickon = 1
Metronome clickon
CASE "+": tempo
= tempo
+ 1:
IF tempo
> 280 THEN tempo
= 280
CASE "-": tempo
= tempo
- 1:
IF tempo
< 40 THEN tempo
= 40
'=================================================================
loadfile: 'Loads pattern from save file
'=======
fil$ = "drummachine.sav"
CASE 0: Show
"drummachine-data\off.jpg", buttonx
(b
), buttony
(b
), 0 CASE 1: Show
"drummachine-data\hot.jpg", buttonx
(b
), buttony
(b
), 0 CASE 2: Show
"drummachine-data\med.jpg", buttonx
(b
), buttony
(b
), 0 'Show current TEMPO (BMP) on upper left
'print error message here
'========================================================
SUB Show
(nam$
, x
, y
, dly
) 'Loads & puts image filename img$ on screen at x,y
'dly is optional delay after putting image on screen
'SUB frees up image handle after loading file.
'===============================================
'This SUB loads the instrumet name image files
src$ = "drummachine-data\"
IF num
= 1 THEN n$
= pre$
+ "crash.png" IF num
= 2 THEN n$
= pre$
+ "ride.png" IF num
= 3 THEN n$
= pre$
+ "hhopen.png" IF num
= 4 THEN n$
= pre$
+ "hhclosed.png" IF num
= 5 THEN n$
= pre$
+ "tomhigh.png" IF num
= 6 THEN n$
= pre$
+ "tommid.png" IF num
= 7 THEN n$
= pre$
+ "tomlow.png" IF num
= 8 THEN n$
= pre$
+ "snare.png" IF num
= 9 THEN n$
= pre$
+ "kick.png" IF num
= 10 THEN n$
= pre$
+ "cowbell.png" IF num
= 11 THEN n$
= pre$
+ "shaker.png" IF num
= 12 THEN n$
= pre$
+ "tambourine.png" IF num
= 13 THEN n$
= pre$
+ "vibraslap.png" IF num
= 14 THEN n$
= pre$
+ "gong.png" IF num
= 15 THEN n$
= pre$
+ "triangle.png" IF num
= 16 THEN n$
= pre$
+ "clave.png"
fil$ = src$ + n$
'===============================================
'This SUB plays the sounds on current beat position.
'It also shows the playing marker moving across the grid
'erase previous marker pos displayed first, and redraw the 4 beat lines...
'erase the end playing position on grid
LINE (buttonx
(col
), buttony
(col
))-(buttonx
(col
), buttony
(col
) + (size
* row
)), _RGB(0, 0, 0), B
'erase the last beat position
LINE (buttonx
(num
- 1), buttony
(num
- 1))-(buttonx
(num
- 1), buttony
(num
- 1) + (size
* row
)), _RGB(0, 0, 0), B
'if last position was one of the 4 beat lines, redraw it
CASE 1, 5, 9, 13, 17, 21, 25, 29 LINE (buttonx
(num
- 1), buttony
(num
- 1))-(buttonx
(num
- 1), buttony
(num
- 1) + (size
* row
)), _RGB(128, 128, 0), B
'Show curreny marker position
'Now Draw current playing position beat line
LINE (buttonx
(num
), buttony
(num
))-(buttonx
(num
), buttony
(num
) + (size
* row
)), _RGB(0, 255, 128), B
'Play sounds on the current beat position, if inst not muted
'play crash
'play ride
'play hhopen
'play hhclosed
'play tom high
'play tom mid
'play tom low
'play snare
'play kick
'play cowbell
'play shaker
'play tambourine
'play vibraslap
'play gong
'play triangle
'play clave
SUB FPRINT
(x
, y
, xsize
, ysize
, num$
) LINE (x
, y
)-(x
+ 100, y
+ ysize
), _RGB(42, 42, 42), BF
_PUTIMAGE (x
, y
)-(x
+ xsize
, y
+ ysize
), num&
(n
) x = x + xsize: y = y + zsize
f$ = "drummachine-data\metron.png"
f$ = "drummachine-data\metroff.png"