xxxxxxxxxx
241
let freqVal = 0; // Frequency value received from Arduino
let isPlaying = false; // Checks if a sound is currently playing
let osc; // Oscillator for sound generation
let fft; // FFT object for audio visualization
let particles = []; // Array for particle visualization
let img, noteImg; // Back button image and music note image
// Notes and staff configuration
let notes = ["E", "F", "G", "A", "B", "C", "D", "E2", "F2"]; // Note sequence
let noteHeights = {}; // Map notes to staff positions
let pressedButtons = {}; // Tracks pressed button states
// Dimensions for the music note image
let newWidth = 120; // Note image width
let newHeight = 240; // Note image height
let spacing = 40; // Staff line spacing
const backButtonSize = 50; // Back button dimensions
function preload() {
img = loadImage("back.png"); // Load back button image
noteImg = loadImage("music_note.png"); // Load music note image
}
function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(DEGREES);
textSize(18);
rectMode(CENTER);
fft = new p5.FFT(); // Initialize FFT for visualization
// Resize and modify the music note image
noteImg.resize(newWidth, newHeight); // Resize the image
noteImg.loadPixels(); // Load pixels for manipulation
for (let i = 0; i < noteImg.pixels.length; i += 4) {
noteImg.pixels[i] = 255 - noteImg.pixels[i]; // Red channel
noteImg.pixels[i + 1] = 255 - noteImg.pixels[i + 1]; // Green channel
noteImg.pixels[i + 2] = 255 - noteImg.pixels[i + 2]; // Blue channel
// Alpha channel remains unchanged
}
noteImg.updatePixels(); // Update the pixel data
// Map each note to its corresponding staff position
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();
osc.setType("sine");
osc.start();
osc.amp(0); // Start with no volume
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
function draw() {
background(0);
// Draw back button
image(img, 20, 20, backButtonSize, backButtonSize);
if (!serialActive) {
fill(255);
text("Press Space Bar to select Serial Port", 20, 30);
} else {
drawStaff(); // Draw the musical staff
drawVisualization(); // Draw the audio visualization as a circle
drawNotes(); // Draw the current notes
drawNoteImage(); // Draw the music note image
}
}
function drawStaff() {
stroke(255);
strokeWeight(1);
for (let i = -2; i <= 2; i++) {
line(0, height / 2 + i * spacing, width, height / 2 + i * spacing); // Draw staff lines
}
}
function drawNotes() {
noFill();
strokeWeight(5);
for (let note in pressedButtons) {
if (pressedButtons[note]) {
let yPosition = noteHeights[note]; // Get y-position of the note
ellipse(width / 2, yPosition, 45, 35); // Draw note as an ellipse
}
}
}
function drawNoteImage() {
image(noteImg, width / 5 - newWidth, height / 2 - newHeight / 2); // Draw the note image
}
function drawVisualization() {
push();
translate(width / 2, height / 2); // Move origin to the center of the canvas
stroke(255);
noFill();
let wave = fft.waveform(); // Get the waveform data
// Draw the circular waveform
for (let t = -1; t <= 1; t += 2) {
beginShape();
for (let i = 0; i < 180; i += 0.5) {
let index = floor(map(i, 0, 180, 0, wave.length - 1));
let r = map(wave[index], -1, 1, 150, 300); // Map the waveform amplitude to radius
let x = r * sin(i) * t; // X-coordinate in polar form
let y = r * cos(i); // Y-coordinate in polar form
vertex(x, y); // Plot the point
}
endShape();
}
// Add particle effects
let p = new Particle();
particles.push(p);
for (let i = particles.length - 1; i >= 0; i--) {
if (!particles[i].edges()) {
particles[i].update();
particles[i].show();
} else {
particles.splice(i, 1);
}
}
pop();
}
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() {
return (
this.pos.x < -width / 2 ||
this.pos.x > width / 2 ||
this.pos.y < -height / 2 ||
this.pos.y > height / 2
);
}
show() {
noStroke();
fill(255);
ellipse(this.pos.x, this.pos.y, this.w);
}
}
function keyPressed() {
if (key === " ") {
if (getAudioContext().state !== "running") {
getAudioContext().resume();
}
setUpSerial();
}
}
function mousePressed() {
// Check if the mouse is over the back button
if (
mouseX > 20 &&
mouseX < 20 + backButtonSize &&
mouseY > 20 &&
mouseY < 20 + backButtonSize
) {
window.location.href = "index.html"; // Redirect to index.html
}
}
function readSerial(data) {
if (data != null) {
let fromArduino = split(trim(data), ",");
if (fromArduino.length == 1) {
let buttonIndex = int(fromArduino[0]); // Get button index
if (buttonIndex >= 0 && buttonIndex < notes.length) {
let receivedNote = notes[buttonIndex];
pressedButtons[receivedNote] = true; // Mark the button as pressed
playSound(receivedNote); // Play sound for the note
} else if (buttonIndex === -1) {
stopAllNotes(); // Stop all notes when no button is pressed
}
}
}
}
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,
};
let freq = frequencies[note];
osc.freq(freq);
osc.amp(0.5, 0.1); // Smooth fade-in
isPlaying = true;
}
function stopAllNotes() {
for (let note in pressedButtons) {
pressedButtons[note] = false; // Mark all notes as released
}
stopSound();
}
function stopSound() {
if (isPlaying) {
osc.amp(0, 0.1); // Smooth fade-out
isPlaying = false;
}
}