Gabriel InojosaDaniel Law
Published © GPL3+

Spotify API Synesthesia Metronome

Webhook project involving the Spotify API that takes a specific track and alternates colours at each measure like a "metronome".

BeginnerWork in progress5.5 hours371
Spotify API Synesthesia Metronome

Things used in this project

Hardware components

Argon
Particle Argon
×1
RGB Diffused Common Cathode
RGB Diffused Common Cathode
×1
Resistor 330 ohm
Resistor 330 ohm
×3
Resistor 220 ohm
Resistor 220 ohm
×1

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE
*Webhook Integration setup required.
Arduino IDE
Arduino IDE
To listen to the Serial monitor of the device. Can be alternated with TeraTerm.

Story

Read more

Schematics

Visualizer Schematics

I just put it together on Fritzing to be easier to see than on a photo.

Code

Visualizer Code on the build.particle.io IDE

C/C++
Just copy and paste it to the Particle Web IDE. I had to cut it too short because my original plan also took up too much storage.
/*
* Copyright © 2020-2021 Gabriel Inojosa
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.

* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <https://www.gnu.org/licenses></https:>.

*/

// This #include statement was automatically added by the Particle IDE.
#include <ArduinoJson.h>
#include <stdlib.h>

const int bluePin = D3;
const int redPin = D4;
const int greenPin = D5;

const char *colourCycle[13] = {"red","blue","bright green", "magenta", "yellow", "cyan", "white", "maroon", "olive", "green", "violet", "teal","navy"};

const size_t CAPACITY = JSON_ARRAY_SIZE(3);

float tempo; // tempo of the song in bpm 
float timeSignature; // Beats per measure of the song. 
float tempoInSeconds;
float secondsPerMeasure;
float millisPerMeasure = 1000;

void setup() {
  Serial.begin(9600);
  pinMode(bluePin, OUTPUT);
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  
   // Subscribe to the integration response event
  Particle.subscribe("hook-response/requestAudioAnalysis", multiValueHandler, MY_DEVICES);
  // Get some data
  String data = String(10);
  // Trigger the integration
  Particle.publish("requestAudioAnalysis", data, PRIVATE);
  // Wait 60 seconds
  delay(60000);
  
}

void loop() {
    /* This portion was cut out as I was unable to figure out moving the local variables on the multiValueHandler to the loop method without using iostream.h that took too much space. Take a look.
   for (int i = 0; i < sizeof(colourCycle)/sizeof(colourCycle[0]); i++){
        flashColour(colourCycle[i]);
        Serial.println(colourCycle[i]);
        delay(millisPerMeasure);
        
        //Consider time signature and tempo
        float tempoInSeconds = tempo/60;
        
        
        
        
        
    } */
} 

void multiValueHandler(const char *event, const char *data) {
  const char* json = data;
  
  String dataStr = String(data);
  char strBuffer[10] = "";
  dataStr.toCharArray(strBuffer, 10);
  
  String tempoString = strtok(strBuffer, ",");
  
  Serial.println("Data returned: " + dataStr);
  Serial.println("Tempo: " + tempoString);
  
  String timeSignatureString = strtok(NULL, ",");
  Serial.println("Time Signature: " + timeSignatureString);
  
  tempo = atof(tempoString);
  timeSignature = atof(timeSignatureString); //Converts the parsed strings to floats
  
  tempoInSeconds = tempo/60;
  secondsPerMeasure = timeSignature/tempoInSeconds;
  millisPerMeasure = secondsPerMeasure*1000;
  
  Serial.print("millisPerMeasure: ");
  Serial.print(millisPerMeasure);
  Serial.print("\n");
  // I might be cutting off lines 98-106. This was the original plan to parse the arrays of bars and sections given by the returned JSON file to consistently change the given variables over time.
  //However, that was proven to be too time consuming for the project. I might return to this on my own time.
  StaticJsonDocument<CAPACITY> doc;
  
  deserializeJson(doc, json);
  
  JsonObject main = doc["main"];
  JsonArray sections = doc.to<JsonArray>();
  for(JsonVariant v : sections) {
    Serial.println(v.as<int>());
  }
  // I just copied and pasted here from the loop() function it works 
  while (true){
    for (int i = 0; i < sizeof(colourCycle)/sizeof(colourCycle[0]); i++){
        flashColour(colourCycle[i]);
        Serial.println(colourCycle[i]);
        delay(millisPerMeasure);
    }     
  }
  
}
          
  

void flashColour(String colourName) {
    if (colourName.equals("red")){
        analogWrite(bluePin, 0);
        analogWrite(greenPin, 0);
        digitalWrite(redPin, HIGH); 
    }
    else if (colourName.equals("blue")){
        analogWrite(bluePin, 255);
        analogWrite(greenPin, 0);
        analogWrite(redPin, 255); 
    }
    else if (colourName.equals("bright green")){
        analogWrite(bluePin, 0);
        analogWrite(greenPin, 255);
        analogWrite(redPin, 0); 
    }
    else if (colourName.equals("magenta")){
        analogWrite(bluePin, 255);
        analogWrite(greenPin, 0);
        analogWrite(redPin, 255); 
    }
    else if (colourName.equals("yellow")){
        analogWrite(bluePin, 0);
        analogWrite(greenPin, 255);
        analogWrite(redPin, 255); 
    }
    else if (colourName.equals("cyan")){
        analogWrite(bluePin, 255);
        analogWrite(greenPin, 255);
        analogWrite(redPin, 0); 
    }
    else if (colourName.equals("white")){
        analogWrite(bluePin, 255);
        analogWrite(greenPin, 255);
        analogWrite(redPin, 255); 
    }
    else if (colourName.equals("silver")){
        analogWrite(bluePin, 170);
        analogWrite(greenPin, 170);
        analogWrite(redPin, 170); 
    }
    else if (colourName.equals("grey")){
        analogWrite(bluePin, 128);
        analogWrite(greenPin, 128);
        analogWrite(redPin, 128); 
    }
    else if (colourName.equals("maroon")){
        analogWrite(bluePin, 0);
        analogWrite(greenPin, 0);
        analogWrite(redPin, 128); 
    }
    else if (colourName.equals("olive")){
        analogWrite(bluePin, 0);
        analogWrite(greenPin, 128);
        analogWrite(redPin, 128); 
    }
    else if (colourName.equals("green")){
        analogWrite(bluePin, 0);
        analogWrite(greenPin, 128);
        analogWrite(redPin, 0); 
    }
    else if (colourName.equals("violet")){
        analogWrite(bluePin, 128);
        analogWrite(greenPin, 0);
        analogWrite(redPin, 128); 
    }
    else if (colourName.equals("teal")){
        analogWrite(bluePin, 128);
        analogWrite(greenPin, 128);
        analogWrite(redPin, 0); 
    }
    else if (colourName.equals("navy")){
        analogWrite(bluePin, 128);
        analogWrite(greenPin, 0);
        analogWrite(redPin, 0); 
    }
}

Spotify API Request JSON

JSON
This is supposed to be on the webhook integration in the Particle IDE on console.particle.io Replace the {PUT YOUR AUTHORIZATION TOKEN HERE} with the authorization token from the Spotify API. Replace it every hour.

The specific track ID 5OkKOkdVTKFrYi6GWXkMzR is for Dean Martin's Volare. If want to change the track ID you can search for it on Spotify and paste it there.
{
    "event": "requestAudioAnalysis",
    "url": "https://api.spotify.com/v1/audio-analysis/5OkKOkdVTKFrYi6GWXkMzR",
    "requestType": "GET",
    "noDefaults": false,
    "rejectUnauthorized": true,
    "responseTemplate": "{{#track}}{{tempo}}{{/track}},{{#track}}{{time_signature}}{{/track}}",
    "headers": {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": "Bearer {PUT YOUR AUTHORIZATION TOKEN HERE}"
    }
}

Credits

Gabriel Inojosa
3 projects • 3 followers
Contact
Daniel Law
46 projects • 9 followers
Teacher. Maker. Citizen of the planet.
Contact

Comments

Please log in or sign up to comment.