xxxxxxxxxx
353
let isPlaying = false; // Checks if a sound is currently playing
let osc; // Oscillator for sound generation
var fft;
var particles = [];
let img;
// Dimensions for the music note image
let newWidth = 120; // Width of the image
let newHeight = 240; // Height of the image
let spacing = 40; // Distance between each staff line
let notes = ["E", "F", "G", "A", "B", "C", "D", "E2", "F2"]; // Note sequence
let noteHeights = {}; // Placeholder for mapping notes to y-positions
let currentNote = ""; // Current note being played
let previousNote = ""; // Previous note to avoid repetition
let points = 0; // Points for the game
let timeLeft = 60; // 1-minute timer
let gameOver = false; // To check if the game is over
let timer; // Timer interval
let pressedButtons = {}; // Tracks the state of each button to avoid repeated deductions
let isVisualizing = false; // to control visualization
// Loading the music note image
function preload() {
img = loadImage("music_note.png");
back_img = loadImage("back.png");
}
function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(DEGREES);
fft = new p5.FFT(); //for visualization P5 library
textSize(18);
rectMode(CENTER);
// Map each note to its corresponding heights
noteHeights = {
"E": height / 2 + 80,
"F": height / 2 + 60,
"G": height / 2 + 40,
"A": height / 2 + 20,
"B": height / 2,
"C": height / 2 - 20,
"D": height / 2 - 40,
"E2": height / 2 - 60,
"F2": height / 2 - 80,
};
// Initialize the oscillator
osc = new p5.Oscillator(); // Creates an oscillator for generating sound
osc.setType("sine"); // Sets the sound wave as sine
osc.start(); // Starts the oscillator
osc.amp(0); // To start with no volume
pickNextNote(); // Pick the first note
// Start the timer
timer = setInterval(() => {
if (timeLeft > 0) {
timeLeft--;
} else {
clearInterval(timer);
gameOver = true;
}
}, 1000);
img.resize(newWidth, newHeight); // Resize the image
img.loadPixels();
// Changing each pixel color for the music note image to change it from black to white
for (let i = 0; i < img.pixels.length; i += 4) {
img.pixels[i] = 255 - img.pixels[i]; // Red channel
img.pixels[i + 1] = 255 - img.pixels[i + 1]; // Green channel
img.pixels[i + 2] = 255 - img.pixels[i + 2]; // Blue channel
// Alpha channel (img.pixels[i + 3]) is left unchanged
}
img.updatePixels();
}
// particles class for visualization
class Particle {
constructor() {
this.pos = p5.Vector.random2D().mult(225);
this.vel = createVector(0, 0);
this.acc = this.pos.copy().mult(random(0.0001, 0.0001));
this.w = random(3, 5);
}
update() {
this.vel.add(this.acc);
this.pos.add(this.vel);
}
edges() {
if (
this.pos.x < -width / 2 ||
this.pos.x > width / 2 ||
this.pos.y < -height / 2 ||
this.pos.y > height / 2
) {
return true;
} else {
return false;
}
}
show() {
noStroke();
fill(255);
ellipse(this.pos.x, this.pos.y, this.w);
}
}
//for window size responsivness
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
function draw() {
background(0);
// Always draw the back button
image(back_img, 20, 20, 50, 50); // Draw the back image at (20, 20) with a size of 50x50
if (!serialActive) {
fill(255);
text("Press Space Bar to select Serial Port, Select USB", 20, 30); // Instructions for serial connection
} else if (gameOver) {
// Display "Game Over" and the total points
textSize(24);
fill(255);
textAlign(CENTER, CENTER);
text("Game Over!", width / 2, height / 2 - 40);
text(`Points: ${points}`, width / 2, height / 2);
// Draw the restart button
fill(100); // Gray button background
noStroke();
rect(width / 2, height / 2 + 60, 150, 50, 10); // Restart button
fill(255);
textSize(18);
text("Restart", width / 2, height / 2 + 60); // Restart text
} else {
if (isVisualizing) {
drawVisualization();
}
// Draw musical staff lines
stroke(255);
strokeWeight(1);
for (let i = -2; i <= 2; i++) {
line(0, height / 2 + i * spacing, width, height / 2 + i * spacing);
}
// Draw image
image(img, width / 5 - newWidth, height / 2 - newHeight / 2);
// Draw the current note
if (currentNote) {
let yPosition = noteHeights[currentNote]; // Get the y-position of the current note
noFill();
strokeWeight(5);
ellipse(width / 2, yPosition, 45, 35); // Draw the note as an ellipse
}
// Display the points and the timer
textSize(18);
noStroke();
fill(255);
text(`Points: ${points}`, 100, 30);
text(`Time Left: ${timeLeft}s`, width - 150, 30);
}
}
//to draw the visualization
function drawVisualization() {
push();
translate(width / 2, height / 2);
stroke(255);
noFill();
var wave = fft.waveform();
for (var t = -1; t <= 1; t += 2) {
beginShape();
for (var i = 0; i < 180; i += 0.5) {
var index = floor(map(i, 0, 180, 0, wave.length - 1));
var r = map(wave[index], -1, 1, 150, 300);
var x = r * sin(i) * t;
var y = r * cos(i);
vertex(x, y);
}
endShape();
}
// Draw particles
var p = new Particle();
particles.push(p);
for (var i = particles.length - 1; i >= 0; i--) {
if (!particles[i].edges()) {
particles[i].update();
particles[i].show();
} else {
particles.splice(i, 1);
}
}
pop();
}
function keyPressed() {
if (key == " ") {
if (getAudioContext().state !== 'running') {
getAudioContext().resume();
}
// Setup the serial communication when the space bar is pressed
setUpSerial();
}
}
// Function to handle data received from Arduino
function readSerial(data) {
if (data != null && !gameOver) {
// Split the incoming message from Arduino
let fromArduino = split(trim(data), ",");
let buttonIndex = int(fromArduino[0]); // Button index
if (buttonIndex >= 0 && buttonIndex < notes.length) {
let receivedNote = notes[buttonIndex];
checkNote(receivedNote, buttonIndex); // Check if the received note is correct
} else if (buttonIndex === -1) {
// Reset button states when no button is pressed
pressedButtons = {};
}
}
}
// Function to check if the received note is correct
function checkNote(receivedNote, buttonIndex) {
if (!pressedButtons[buttonIndex]) {
// Process only if the button was not previously pressed
pressedButtons[buttonIndex] = true;
if (receivedNote === currentNote) {
playSound(currentNote); // Play the note sound
points += 10; // Add points for correct answer
previousNote = currentNote; // Update the previous note
pickNextNote(); // Pick the next note
} else {
points -= 5; // Subtract points for wrong answer
}
}
}
// Function to pick the next note, avoiding repetition
function pickNextNote() {
let availableNotes = notes.filter((note) => note !== previousNote); // Filter out the previous note
currentNote = random(availableNotes); // Pick a random note
}
function playSound(note) {
let frequencies = {
E: 329.63,
F: 349.23,
G: 392.0,
A: 440.0,
B: 493.88,
C: 523.25,
D: 587.33,
E2: 659.25,
F2: 698.46,
}; // Frequencies for notes
let freq = frequencies[note]; // Get frequency based on the note
// Ensure audio context is active
if (getAudioContext().state !== 'running') {
getAudioContext().resume();
}
// Set oscillator frequency and volume
osc.freq(freq);
osc.amp(0.5, 0.1); // Smooth fade-in
isPlaying = true;
isVisualizing = true; // Start visualization
// Stop the sound after a short delay
setTimeout(() => {
stopSound();
}, 500);
}
// Function to stop the sound
function stopSound() {
if (isPlaying) {
osc.amp(0, 0.1); // Smooth fade-out
isPlaying = false;
isVisualizing = false; // Stop visualization
}
}
function mousePressed() {
// Check if the mouse is over the back button
let backButtonX = 20;
let backButtonY = 20;
let backButtonWidth = 50;
let backButtonHeight = 50;
if (
mouseX > backButtonX &&
mouseX < backButtonX + backButtonWidth &&
mouseY > backButtonY &&
mouseY < backButtonY + backButtonHeight
) {
// Redirect to index.html
window.location.href = "index.html";
}
// Check if the mouse is over the restart button (only if game over)
if (gameOver) {
let restartButtonX = width / 2 - 75; // Button x-position
let restartButtonY = height / 2 + 35; // Button y-position
let restartButtonWidth = 150;
let restartButtonHeight = 50;
if (
mouseX > restartButtonX &&
mouseX < restartButtonX + restartButtonWidth &&
mouseY > restartButtonY &&
mouseY < restartButtonY + restartButtonHeight
) {
restartGame(); // Restart the game
}
}
}
function restartGame() {
points = 0; // Reset points
timeLeft = 60; // Reset timer
gameOver = false; // Reset game over state
pressedButtons = {}; // Clear button states
pickNextNote(); // Pick a new random note
clearInterval(timer); // Clear the old timer
timer = setInterval(() => {
if (timeLeft > 0) {
timeLeft--;
} else {
clearInterval(timer);
gameOver = true;
}
}, 1000); // Restart the timer
}