DECEMBER 15, 2023
<aside> 📯 This project is an Audio Player Replica Music Machine that combines hardware and software components to create an interactive music-making system. The system allows users to control music playback, volume, and song selection through physical controls while providing a visual representation of the audio with an equalizer-like display.
</aside>
The code is designed for pre-recorded music. It plays music by triggering sampled audio files (MP3 format)
The code includes a visualizer that displays a series of bars that respond to the amplitude of the audio being played. These bars create an "equalizer-like" effect that visually represents the music.
const int joystickLPin = 14; // X-axis of the joystick
const int joystickUDPin = 32; // Y-axis of the joystick
const int buttonPin = 21; // Button pin
void setup() {
Serial.begin(9600);
pinMode(joystickLPin, INPUT);
pinMode(joystickUDPin, INPUT);
pinMode(buttonPin, INPUT_PULLUP);
}
void loop() {
int joyX = analogRead(joystickLPin);
int joyY = analogRead(joystickUDPin);
bool buttonState = digitalRead(buttonPin);
Serial.print(joyX);
Serial.print(",");
Serial.print(joyY);
Serial.print(",");
Serial.println(buttonState);
delay(100);
}
import processing.serial.*;
import processing.sound.*;
Serial myPort;
String val;
int joyX, joyY, prevJoyX;
final int defaultJoyX = 2300; // Fixed default X position
final int defaultJoyY = 2300; // Fixed default Y position
final int joyThreshold = 350; // Threshold for joystick movement
SoundFile[] melodies;
int currentMelodyIndex = 0;
float volumeLevel = 0.3;
final int songAdvanceSeconds = 5; // Time to jump forward in the song
boolean buttonState, prevButtonState = false;
String[] songNames = {"Für Elise", "Lakmé Act 1: Dôme Épais"};
Amplitude analyzer; // To analyze sound amplitude
void setup() {
size(800, 600);
// Initialize serial communication
try {
myPort = new Serial(this, "/dev/tty.SLAB_USBtoUART", 9600);
myPort.bufferUntil('\\n');
}
catch (Exception e) {
println("Error opening serial port.");
e.printStackTrace();
}
// Initialize the melodies array with the correct size
melodies = new SoundFile[2];
// Load melodies
melodies[0] = new SoundFile(this, "fur_elise.mp3");
melodies[1] = new SoundFile(this, "lakme_act1_dome_epais.mp3");
// Initialize the analyzer
analyzer = new Amplitude(this);
// Start playing the first melody
playMelody(currentMelodyIndex);
}
void draw() {
background(0);
if (myPort.available() > 0) {
val = myPort.readStringUntil('\\n');
if (val != null) {
processInput(val);
}
}
drawMediaPlayer();
drawVisualizer();
}
void processInput(String input) {
String[] vals = split(trim(input), ',');
if (vals.length == 3) {
joyX = int(vals[0]);
joyY = int(vals[1]);
buttonState = vals[2].equals("1");
processJoystick();
processButton();
}
}
void processJoystick() {
SoundFile currentMelody = melodies[currentMelodyIndex];
// Restart the song if joystick moved fully left
if (joyX < defaultJoyX - joyThreshold) {
currentMelody.cue(0);
currentMelody.play();
}
// Advance the song by a fixed time increment if joystick moved right
if (joyX > prevJoyX + 30 && joyX > defaultJoyX + joyThreshold) {
float newTime = currentMelody.position() + songAdvanceSeconds;
if (newTime < currentMelody.duration()) {
currentMelody.cue(newTime);
currentMelody.play();
}
}
prevJoyX = joyX;
// Adjust volume based on joystick up/down movement
if (abs(joyY - defaultJoyY) > joyThreshold) {
volumeLevel = map(joyY, 0, 4095, 0, 1);
volumeLevel = constrain(volumeLevel, 0, 1);
currentMelody.amp(volumeLevel);
}
}
void processButton() {
if (buttonState && !prevButtonState) {
currentMelodyIndex = (currentMelodyIndex + 1) % melodies.length;
playMelody(currentMelodyIndex);
}
prevButtonState = buttonState;
}
void playMelody(int index) {
for (int i = 0; i < melodies.length; i++) {
if (i == index) {
melodies[i].loop();
melodies[i].amp(volumeLevel);
analyzer.input(melodies[i]); // Change input for analyzer
} else {
melodies[i].stop();
}
}
}
void drawMediaPlayer() {
SoundFile playingMelody = melodies[currentMelodyIndex];
float progress = map(playingMelody.position(), 0, playingMelody.duration(), 0, width);
fill(50, 50, 200);
rect(0, height - 50, progress, 30);
fill(200, 50, 50);
rect(width - 60, height - 100, 30, map(volumeLevel, 0, 1, 0, -50));
fill(255);
text("Song: " + songNames[currentMelodyIndex], 10, height - 90);
text("Position: " + playingMelody.position(), 10, height - 70);
text("Volume: " + volumeLevel, width - 140, height - 70);
}
void drawVisualizer() {
float amplitude = analyzer.analyze();
int bars = 50; // Number of bars in the visualizer
for (int i = 0; i < bars; i++) {
float x = map(i, 0, bars, 0, width);
float h = amplitude * 500; // Scaling amplitude
fill(255, 100, 100);
rect(x, height / 2 - h / 2, width / bars - 2, h);
}
}
void stop() {
for (SoundFile melody : melodies) {
melody.stop();
}
myPort.stop();
super.stop();
}