xxxxxxxxxx
366
// p5.js drum machine
// incorporating elements of tone.js library
// setting global variables
let beat = 0;
let value = 0;
let cells = [];
// amount of steps or beats in one musical line
let nSteps = 8; // number of columns / loop (x-axis)
let currentStep = 0;
let nSamples = 4; // number of sounds / kit (y-axis)
// button for play/pause, slider for controlling BPM / speed
let playButton;
let slider;
// other settings
let a = 40; // cell height variable
let b = 25; // cell width variable
let gridWidth, gridHeight, cellWidth, cellHeight; // initializing values for grid
let colors = ["orange", "pink", "yellow", "cyan"]; // array of colors
// sample kits/sounds
let rock; // kit 1
let trap; // kit 2
let percussion; // kit 3
let chooseKit; // variable for choosing drum kit
let drumNames = ["kick", "snare", "hat", "open"];
// associate different sounds with each kit
//Tone.Player is a way to load and play back an audio file
// three sets of beat sounds ----------------------------
rock = new Tone.Players({
kick: "kick1.wav",
snare: "snare1.wav",
hat: "hat1.wav",
open: "open1.wav",
});
// to be replaced with different kit sounds //
trap = new Tone.Players({
kick: "kick2.wav",
snare: "snare2.wav",
hat: "hat2.wav",
open: "open2.wav",
});
percussion = new Tone.Players({
kick: "bongo1.wav",
snare: "Cowbell1.wav",
hat: "ride3.wav",
open: "bells3.wav",
});
// ----------------------------
// toMaster function connects the output to the context's destination node
rock.toMaster();
trap.toMaster();
percussion.toMaster();
// transport is a timekeeper function in tone.js
//8th means 8th notes
Tone.Transport.scheduleRepeat(onBeat, "8n");
// preloading images
function preload() {
logo = loadImage("roland.png");
real = loadImage("real.png");
based = loadImage("based.png");
instructions = loadImage("instructions.png");
label = loadImage("label.png");
p5drum = loadImage("p5drum.png");
speed = loadImage("speed.png");
}
// slider for bpm
function setup() {
slider = createSlider(0, 200, 100);
slider.position(300, 400);
slider.style("width", "200px");
// drop down menu to select kit
sel = createSelect();
sel.position(350, 430);
sel.size(100, 50);
sel.style("font-family", "Fredoka One");
sel.style("font-size", "20px");
sel.style("border-width", "0px"); // get rid of border width
sel.style("border-radius", "20px");
sel.selected("rock");
sel.option("rock");
sel.option("trap");
sel.option("percussion");
sel.changed(mySelectEvent);
chooseKit = sel.value();
// initialize cell
// OFF = 0, ON = 1
for (let sample = 0; sample < nSamples; sample++) {
cells[sample] = [];
for (let step = 0; step < nSteps; step++) {
cells[sample][step] = 0;
}
}
// play button
playButton = createButton("PLAY");
playButton.position(300, 500);
playButton.mouseClicked(togglePlay);
playButton.size(200, 100);
playButton.style("font-family", "Fredoka One");
playButton.style("font-size", "48px");
playButton.style("border-width", "0px"); // get rid of border/stroke
playButton.style("border-radius", "20px");
// grid
createCanvas(820, 800);
gridWidth = width - 2 * b;
gridHeight = height / 2 - 2 * a;
cellWidth = gridWidth / nSteps;
cellHeight = gridHeight / nSamples;
gray = color(178, 178, 188);
}
function onBeat(time) {
let velocity = 0.5;
currentStep = beat % nSteps;
for (let sample = 0; sample < nSamples; sample++) {
if (cells[sample][currentStep]) {
let k, k2, k3;
if (chooseKit == "rock") {
k = rock.get(drumNames[sample]);
k.start(time);
} else if (chooseKit == "trap") {
k2 = trap.get(drumNames[sample]);
k2.start(time);
} else if (chooseKit == "percussion") {
k3 = percussion.get(drumNames[sample]);
k3.start(time);
}
}
}
beat++;
}
class drawGrid {
constructor(
nSteps,
cells,
colors,
cellWidth,
cellHeight,
gridWidth,
gridHeight,
beat,
a,
b,
width
) {
this.nSteps = nSteps;
this.cells = cells;
this.colors = colors;
this.cellWidth = cellWidth;
this.cellHeight = cellHeight;
this.gridWidth = gridWidth;
this.gridHeight = gridHeight;
this.beat = beat;
this.a = a;
this.b = b;
this.width = width;
}
// fillCells() {
// for (let step = 0; step < this.nSteps; step++) {
// for (let sample = 0; sample < this.nSamples; sample++) {
// if (this.cells[sample][step] == 1) {
// fill(this.colors[sample]);
// rect(
// this.b + step * this.cellWidth,
// this.a + sample * this.cellHeight,
// this.cellWidth,
// this.cellHeight
// );
// }
// }
// }
// }
drawHorizontals() {
// horizontal lines
for (let i = 0; i <= this.nSamples; i++) {
let y = this.a + i * this.cellHeight;
let right = this.width - this.b;
stroke(gray);
line(this.b, y, right, y);
}
}
// vertical lines
drawVerticals(){
for (let i = 0; i <= this.nSteps; i++) {
stroke(gray);
line(this.b + i * this.cellWidth, this.a, this.b + i * this.cellWidth, this.a + this.gridHeight);
}
}
highlightCells(){
// highlight the cell that is playing
for (let i = 0; i <= this.nSteps; i++) {
let step = (this.beat - 1) % this.nSteps;
if (i == step && Tone.Transport.state == "started") {
fill(0, 128, 128, 80); // color to highlight column
noStroke();
rect(b + i * this.cellWidth, this.a, this.cellWidth, this.gridHeight);
}
}
}
}
function draw() {
background("black");
// load in all the logos, etc
//image(logo, 225, 4, 180, 35); // mid top logo
//image(real, 235, 520, 380, 205); // drum logo
//image(based, 235, 740, 380, 40);
//image(instructions, 235, 360, 360, 175);
//image(label, 1, 50, 70, 315); // labels
//image(p5drum, 385, 7, 320, 35); // top right, project title
//image(speed, 300, 377, 200, 22); // bpm label
textSize(24);
fill("orange");
text("BPM", 230, 420);
textFont("Fredoka One");
// setting up the slider
let bpmMax = slider.value();
// use Tone.Transport...
Tone.Transport.bpm.value = bpmMax;
fill(value);
// fill the cells in grid
for (let step = 0; step < nSteps; step++) {
for (let sample = 0; sample < nSamples; sample++) {
if (cells[sample][step] == 1) {
fill(colors[sample]);
rect(
b + step * cellWidth,
a + sample * cellHeight,
cellWidth,
cellHeight
);
}
}
}
// call draw grid here
let newGrid = new drawGrid(nSteps,
cells,
colors,
cellWidth,
cellHeight,
gridWidth,
gridHeight,
beat,
a,
b,
820)
//newGrid.fillCells()
newGrid.drawHorizontals()
newGrid.drawVerticals()
newGrid.highlightCells()
// fill the cells in grid
// for (let step = 0; step < nSteps; step++) {
// for (let sample = 0; sample < nSamples; sample++) {
// if (cells[sample][step] == 1) {
// fill(colors[sample]);
// rect(
// b + step * cellWidth,
// a + sample * cellHeight,
// cellWidth,
// cellHeight
// );
// }
// }
// }
// // turn into grid class?
// // horizontal lines
// for (let i = 0; i <= nSamples; i++) {
// let y = a + i * cellHeight;
// right = width - b;
// line(b, y, right, y);
// }
// // vertical lines
// for (let i = 0; i <= nSteps; i++) {
// right = width - b;
// stroke(gray);
// line(b + i * cellWidth, a, b + i * cellWidth, a + gridHeight);
// // highlight the cell that is playing
// let step = (beat - 1) % nSteps;
// if (i == step && Tone.Transport.state == "started") {
// fill(0,128,128, 80); // color to highlight column
// noStroke();
// rect(b + i * cellWidth, a, cellWidth, gridHeight);
// }
// }
}
function mousePressed() {
// make sure the mouse is in the grid
if (
b < mouseX &&
mouseX < b + gridWidth &&
a < mouseY &&
mouseY < a + gridHeight
) {
// margins
let x = mouseX - b;
let y = mouseY - a;
// which cell is clicked
let i = floor(x / cellWidth);
let j = floor(y / cellHeight);
// cell on/off switch
cells[j][i] = !cells[j][i];
}
}
// PLAY / PAUSE button
function togglePlay() {
if (Tone.Transport.state == "started") {
Tone.Transport.stop();
playButton.html("PLAY");
} else {
if (rock.loaded && trap.loaded) {
Tone.Transport.start();
playButton.html("PAUSE");
}
}
}
// select the kits mySelectEvent is a function in p5.js
function mySelectEvent() {
if (sel.value() == "rock") {
chooseKit = "rock";
} else if (sel.value() == "trap") {
chooseKit = "trap";
} else if (sel.value() == "percussion") {
chooseKit = "percussion";
}
// say which kit is selected and playing in console
console.log("playing kit: " + chooseKit);
}