Similarity JukeBox @ ITP Winter Show

Showing my final at the ITP Winter Show was the most intense user testing process that taught me a lot about how to display my work and how to quickly explain what my project was about to the public.

If I had to do it all over again I would probably make the buttons on the interface have the play, next set, and last set symbols on them and move the fader down a little more. Although some people knew exactly how to use controls, there were a few that didn’t know where to start. Musically inclined people spent more time playing around with it and I was especially surprised to find that it was a big hit with high school students. I assumed that because a majority of the music referenced is from the 60’s and 70’s that they wouldn’t find it entertaining but I was wrong!

Laser cutting Illustrator File

I realized after putting my wood panel into the laser cutting machine that I needed to make the squares for the buttons double the size. I also added the symbols for the buttons underneath which could explain why they were so close to the fader. This was my first try at the laser cutting machine, and my first solo fabrication project – not too shabby! I tried using the same font that was on my cigar box but it looks like I should have adjusted the spacing of the font because it’s a little bit different. I used¬†Berthold Akzidenz Grotesk Bold and found it by uploading a picture of a word on the box to fontsgeek.com.

The final code:

P5.JS
let playbutton, stopbutton, nextbutton, backbutton;
let slider;
let fader = 0;
let songAFileNames =
['SweetLittleSixteen',
'HesSoFine',
'MaryJanesLastDance',
'BrownSugar',
'ObLaDiObLaDa',
'SpiritInTheSky',
'RunThroughTheJungle',
'PumpItUp',
'YouNeedLove',
'AmericanGirl',
'LadyMadonna',
'DontStopTilYouGetEnough',
'TwoPrinces',
'Mmmbop',
'IWontBackDown',
'Taurus',
'TheAirThatIBreathe',
//'PictureBook',
'TomorrowNeverKnows',
'UnderPressure',
'LittleMermaid'];
let songBFileNames =
['SurfinUSA',
'MySweetLord',
'DaniCalifornia',
'WalkAndTalkIt',
'WhyDontYouGetAJob',
'BabyDidABadBadThing',
'TheOldManDownTheRoad',
'GetOnYourBoots',
'WholeLottaLove',
'LastNight',
'WhatIGot',
'SteppinOut',
'SemiCharmedLife',
'Unpretty',
'StayWithMe',
'StairwayToHeaven',
'Creep',
//'SaladDays',
'LetForeverBe',
'IceIceBaby',
'Nude'];
let songAs = [];
let songBs = [];
let albumAs = [];
let albumBs = [];
let currentIndex = 0;
let serial;
let portName = '/dev/cu.usbmodem1411';
let hasStarted = false;
let isPlaying = false;
let volumeA = 1;
let volumeB = 1;
function preload() {
for (let i = 0; i < songAFileNames.length; i++) { songAs[i] = loadSound("assets/sounds/" + songAFileNames[i] + ".mp3"); songBs[i] = loadSound("assets/sounds/" + songBFileNames[i] + ".mp3"); albumAs[i] = loadImage("assets/images/" + songAFileNames[i] + ".jpg"); albumBs[i] = loadImage("assets/images/" + songBFileNames[i] + ".jpg"); } } function setup() { createCanvas(1334, 750); background(255); serial = new p5.SerialPort('172.16.250.39'); // make an instance of the serial library serial.open(portName); // open port serial.on('connected', function(){ console.log("CONNECTED"); background(15); }); serial.on('data', gotData); // declare serial data callback function } function draw() { //checkSliderAndUpdateSound(); if(hasStarted) checkFaderAndUpdateSound(); } function checkFaderAndUpdateSound() { console.log("checkFaderAndUpdateSound"); volumeA = map(fader, 0, 1023, 0, 1); volumeB = map(fader, 0, 1023, 1, 0); console.log(volumeA); songAs[currentIndex].setVolume(volumeA); songBs[currentIndex].setVolume(volumeB); tint(255,255); let xPos = width/2 - 750/2; image(albumBs[currentIndex], xPos, 0, 750, 750); tint(255, fader/4); image(albumAs[currentIndex], xPos, 0, 750, 750); } function playsound() { if (!songAs[currentIndex].isPlaying()) { songAs[currentIndex].loop(); songBs[currentIndex].loop(); } } function pausesound(){ songAs[currentIndex].pause(); songBs[currentIndex].pause(); } function stopsound() { songAs[currentIndex].stop(); songBs[currentIndex].stop(); } function nextsetsound() { stopsound(); currentIndex++; if (currentIndex > songAs.length - 1) {
currentIndex = 0;
}
playsound();
}
function lastsetsound() {
stopsound();
currentIndex--;
if (currentIndex < 0) { currentIndex = songAs.length - 1; } playsound(); } function gotData() { let arduinoInput = serial.readLine(); // read an ASCII-encoded string if(arduinoInput.length > 0) {
if (arduinoInput === 'P') {
if(isPlaying) {
pausesound();
isPlaying = false;
} else {
playsound();
isPlaying = true;
} hasStarted = true;
}
else if (arduinoInput === 'B') {
lastsetsound();
}
else if (arduinoInput === 'N') {
nextsetsound();
}
else {
if(arduinoInput == NaN) arduinoInput = 0;
fader = arduinoInput;
console.log(fader);
}
}
}

Arduino
const int playButton = 2;
const int backButton = 3;
const int nextButton = 4;
int threshold = 2;
const int fader = A0;
int faderValue = 0;
int playButtonState = LOW;
int backButtonState = LOW;
int nextButtonState = LOW;
int prevFaderValue = 1;
int prevPlayButtonState;
int prevBackButtonState;
int prevNextButtonState;
void setup() {
pinMode(playButton, INPUT);
pinMode(backButton, INPUT);
pinMode(nextButton, INPUT);
Serial.begin(9600);
}
void loop() {
faderValue = analogRead(fader);
backButtonState = digitalRead(backButton);
playButtonState = digitalRead(playButton);
nextButtonState = digitalRead(nextButton);
if (playButtonState != prevPlayButtonState && playButtonState == HIGH) {
Serial.println('P');
}
if (backButtonState != prevBackButtonState && backButtonState == HIGH) {
Serial.println('B');
}
if (nextButtonState != prevNextButtonState && nextButtonState == HIGH) {
Serial.println('N');
}
if (abs(faderValue - prevFaderValue) > threshold) {
Serial.println(faderValue);
prevFaderValue = faderValue;
}
prevPlayButtonState = playButtonState;
prevBackButtonState = backButtonState;
prevNextButtonState = nextButtonState;
}

I’m shocked that in three months I was able to go from making an LED blink and a circle move on the screen to creating a jukebox which has been an idea of mine for quite some time. I can’t say that I love coding, but using code to create this project definitely made me appreciate the power of that language.

Finals Update #2: The coding is DONE!!!

HORRAY! The coding side of my project is finished! I have to give thanks where it’s deserved: Mimi, Tom, Mathura, Mithru thank you all so much for your help and support.

Here is the final-ish p5.js code (it will include more songs)
let playbutton, stopbutton, nextbutton, backbutton;
let slider;
let fader = 0;
let songBFileNames = ['SpiritInTheSky', 'RunThroughTheJungle', 'HesSoFine','UnderPressure', 'YouNeedLove', 'AmericanGirl', 'SweetLittleSixteen','MaryJanesLastDance', 'BrownSugar', 'LadyMadonna','DontStopTilYouGetEnough', 'TwoPrinces', 'Mmmbop', 'PumpItUp','ObLaDiObLaDa', 'IWontBackDown', 'Taurus', 'TheAirThatIBreathe','PictureBook','TomorrowNeverKnows', 'LittleMermaid'];
let songAFileNames = ['BabyDidABadBadThing', 'TheOldManDownTheRoad', 'MySweetLord','IceIceBaby', 'WholeLottaLove', 'LastNight', 'SurfinUSA','DaniCalifornia', 'WalkAndTalkIt', 'WhatIGot','SteppinOut', 'SemiCharmedLife', 'Unpretty', 'GetOnYourBoots','WhyDontYouGetAJob', 'StayWithMe', 'StairwayToHeaven', 'Creep','SaladDays','LetForeverBe', 'Nude'];
let songAs = [];
let songBs = [];
let albumAs = [];
let albumBs = [];
let currentIndex = 0;
let serial;
let portName = '/dev/cu.usbmodem1421';
let hasStarted = false;
let isPlaying = false;
let volumeA = 1;
let volumeB = 1;
function preload() {
for (let i = 0; i < songAFileNames.length; i++) { songAs[i] = loadSound("assets/sounds/" + songAFileNames[i] + ".mp3"); songBs[i] = loadSound("assets/sounds/" + songBFileNames[i] + ".mp3"); albumAs[i] = loadImage("assets/images/" + songAFileNames[i] + ".jpg"); albumBs[i] = loadImage("assets/images/" + songBFileNames[i] + ".jpg"); } } function setup() { createCanvas(400, 400); background(255); serial = new p5.SerialPort('192.168.1.151'); // make an instance of the serial library serial.open(portName); // open port serial.on('connected', function(){ console.log("CONNECTED"); background(215); }); serial.on('data', gotData); // declare serial data callback function } function draw() { //checkSliderAndUpdateSound(); if(hasStarted) checkFaderAndUpdateSound(); } function checkFaderAndUpdateSound() { console.log("checkFaderAndUpdateSound"); volumeA = map(fader, 0, 1023, 0, 1); volumeB = map(fader, 0, 1023, 1, 0); console.log(volumeA); songAs[currentIndex].setVolume(volumeA); songBs[currentIndex].setVolume(volumeB); tint(255,255); image(albumBs[currentIndex], 55, 20); tint(255, fader/4); image(albumAs[currentIndex], 55, 20); } function playsound() { songAs[currentIndex].loop(); songBs[currentIndex].loop(); } function pausesound(){ songAs[currentIndex].pause(); songBs[currentIndex].pause(); } function stopsound() { songAs[currentIndex].stop(); songBs[currentIndex].stop(); } function nextsetsound() { stopsound(); currentIndex++; if (currentIndex > songAs.length - 1) {
currentIndex = 0;
}
playsound();
}
function lastsetsound() {
stopsound();
currentIndex--;
if (currentIndex < 0) { currentIndex = songAs.length - 1; } playsound(); } function gotData() { let arduinoInput = serial.readLine(); // read an ASCII-encoded string if(arduinoInput.length > 0) {
if (arduinoInput === 'P') {
if(isPlaying) {
pausesound();
isPlaying = false;
} else {
playsound();
isPlaying = true;
} hasStarted = true;
}
else if (arduinoInput === 'B') {
lastsetsound();
}
else if (arduinoInput === 'N') {
nextsetsound();
}
else {
if(arduinoInput == NaN) arduinoInput = 0;
fader = arduinoInput;
console.log(fader);
}
}
}

And the Arduino code:
const int playButton = 2;
const int backButton = 3;
const int nextButton = 4;
int threshold = 2;
const int fader = A0;
int faderValue = 0;
int playButtonState = LOW;
int backButtonState = LOW;
int nextButtonState = LOW;
int prevFaderValue = 1;
int prevPlayButtonState;
int prevBackButtonState;
int prevNextButtonState;
void setup() {
pinMode(playButton, INPUT);
pinMode(backButton, INPUT);
pinMode(nextButton, INPUT);
Serial.begin(9600);
}
void loop() {
faderValue = analogRead(fader);
backButtonState = digitalRead(backButton);
playButtonState = digitalRead(playButton);
nextButtonState = digitalRead(nextButton);
if (playButtonState != prevPlayButtonState && playButtonState == HIGH) {
Serial.println('P');
}
if (backButtonState != prevBackButtonState && backButtonState == HIGH) {
Serial.println('B');
}
if (nextButtonState != prevNextButtonState && nextButtonState == HIGH) {
Serial.println('N');
}
if (abs(faderValue - prevFaderValue) > threshold) {
Serial.println(faderValue);
prevFaderValue = faderValue;
}
prevPlayButtonState = playButtonState;
prevBackButtonState = backButtonState;
prevNextButtonState = nextButtonState;
}

Final proto from Marco Wylie on Vimeo.

Things I need to fix on the coding side:

  1. When streaming the p5.js sketch to the iphone, you have to press on the iphone’s screen in order to start the sketch, even after you press the physical Play button.
  2. When you start the sketch the album cover from the other side of the slider starts on the opposite side and then suddenly switches to the correct one and acts normally when you move the fader.

Similarity JukeBox

My song comparison box has finally started to come together. Although I haven’t figured out one section of code on the p5.js side that will let the physical fader control not only the sound but the album display, the rest of the functionality seems to be working.

What I hope to convey with this project is how similar two songs can be, using clips of the songs to emphasize exactly what sounds the same between the two.

I’ll be running the p5.js script through an old iphone¬† which will be fastened to the interface shown in the video below. All I need to do is figure out the following:

  1. How to get the songs to play in the order I wanted and not randomly after pressing the next button.
  2. How to map the tint function in p5.js so that the fader works with the album images as well as the song clips.
  3. How to properly fabricate everything so that it fits neatly inside the cigar box.

A p5.js functional sketch version of the project can be seen here.