Documentation for Recitation 7: Neopixel Music Visualization

In this recitation, we used the NeoPixel Led strip to create light animations for our chosen.

Task #1: Test the NeoPixel

The first step is to test whether our NeoPixel and coding worked. Wiring up the components as the schematic, we then downloaded the Arduino library needed for controlling the NeoPixel. schematic

Library

Library

Then, we sent the FastLED/Blink code via Arduino IDE to ensure everything worked perfectly.

Task #2: Use your computer to light up NeoPixels

After testing, we downloaded the SerialRecord library into the Arduino IDE.

And we coded the Arduino to be in the “data-receiving” mode so that the Arduino could receive data later from Processing and light up specific LEDs accordingly. 

/* This is a code example for Arduino, to be used on Recitation 7
  You need to have  installed SerialRecord and FastLED libraries.
  It requires NeoPixel WS2812 at pin 3
  Interaction Lab
  IMA NYU Shanghai
  2022 Fall
*/
 
#include "SerialRecord.h"
#include 

#define NUM_LEDS 60  // How many leds in your strip?
#define DATA_PIN 3   // Which pin are you connecting Arduino to Data In?
CRGB leds[NUM_LEDS];

// Change this number to the number of values you want to receive
SerialRecord reader(4);

void setup() {
 Serial.begin(9600);
 FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);  // Initialize 
 FastLED.setBrightness(10); // BEWARE: external power for full (255)
 //further info at https://learn.adafruit.com/adafruit-neopixel-uberguide/powering-neopixels
}


void loop() {
 if (reader.read()) {
   int n = reader[0];
   int r = reader[1];
   int g = reader[2];
   int b = reader[3];

   leds[reader[0]] = CRGB(reader[1], reader[2], reader[3]);  //  Prepare the color information using CRGB( Red, Green, Blue
   FastLED.show();  //  Pass the information of color to the LED
 }
}

Here, I typed into some values to check whether NeoPixel could be changed.

Then, we launched Processing and coded it in the same format as we did in the Arduino IDE so that it could function perfectly. 

/* This is a code example for Processing, to be used on Recitation 7
  You need to have installed the SerialRecord library.
  
  Interaction Lab
  IMA NYU Shanghai
  2022 Fall
*/

import processing.serial.*;
import osteele.processing.SerialRecord.*;

Serial serialPort;
SerialRecord serialRecord;

int W;         //width of the tiles
int NUM = 60;  //amount of pixels
int[] r = new int[NUM]; //red of each tile
int[] g = new int[NUM]; //red of each tile
int[] b = new int[NUM]; //red of each tile

void setup() {
  size(600, 200);
  W = width/NUM;


  // You can use this syntax and change COM3 for your serial port
  // printArray(Serial.list());
  // serialPort = new Serial(this, "COM3", 9600);
  // in MacOS it looks like "/dev/cu.usbmodem1101"
  //or you can try to use this instead:
  
  String serialPortName = SerialUtils.findArduinoPort();
  serialPort = new Serial(this, serialPortName, 9600);
  serialRecord = new SerialRecord(this, serialPort, 4);
  serialRecord.logToCanvas(false);
  rectMode(CENTER);
}

void draw() {
  background(0);
  for (int i=0; i<NUM; i ++) {
    fill(r[i], g[i], b[i]);
    rect(i * W + W/2, height/2, 10, 10);
  }

 if (mousePressed == true) {
    int n = floor(constrain(mouseX/W , 0, NUM-1));

    r[n] = floor(random(255));
    g[n] = floor(random(255));
    b[n] = floor(random(255));

    serialRecord.values[0] = n;     // which pixel we change (0-59)
    serialRecord.values[1] = r[n];  // how much red (0-255)
    serialRecord.values[2] = g[n];  // how much green (0-255)
    serialRecord.values[3] = b[n];  // how much blue (0-255)
    serialRecord.send();            // send it!
  }
}

However, this is only a raw sketch or a code prototype. To test the code, we modified the code so that we could use the mouse to control the light. This step is fundamental for setting the music as the determinant of the lights later. 

/* This is a code example for Processing, to be used on Recitation 7
  You need to have installed the SerialRecord library.
  
  Interaction Lab
  IMA NYU Shanghai
  2022 Fall
*/

import processing.serial.*;
import osteele.processing.SerialRecord.*;

Serial serialPort;
SerialRecord serialRecord;

int W;         //width of the tiles
int NUM = 60;  //amount of pixels
int[] r = new int[NUM]; //red of each tile
int[] g = new int[NUM]; //red of each tile
int[] b = new int[NUM]; //red of each tile

void setup() {
  size(600, 200);
  W = width/NUM;


  // You can use this syntax and change COM3 for your serial port
  // printArray(Serial.list());
  // serialPort = new Serial(this, "COM3", 9600);
  // in MacOS it looks like "/dev/cu.usbmodem1101"
  //or you can try to use this instead:
  
  String serialPortName = SerialUtils.findArduinoPort();
  serialPort = new Serial(this, serialPortName, 9600);
  serialRecord = new SerialRecord(this, serialPort, 4);
  serialRecord.logToCanvas(false);
  rectMode(CENTER);
}

void draw() {
  background(0);
  for (int i=0; i<NUM; i ++) {
    fill(r[i], g[i], b[i]);
    rect(i * W + W/2, height/2, 10, 10);
  }

 if (mousePressed == true) {
    int n = floor(constrain(mouseX/W , 0, NUM-1));

    r[n] = floor(random(255));
    g[n] = floor(random(255));
    b[n] = floor(random(255));

    serialRecord.values[0] = n;     // which pixel we change (0-59)
    serialRecord.values[1] = r[n];  // how much red (0-255)
    serialRecord.values[2] = g[n];  // how much green (0-255)
    serialRecord.values[3] = b[n];  // how much blue (0-255)
    serialRecord.send();            // send it!
  }

}

Task #3: Add Music!
Now, it is the final step that makes the little program dynamic and fascinating. What we did first was to download a set of codes that will analyze the volume of a piece of the audio and transform it to a variable that later can be used to create the visual effects of the lights.

import processing.sound.*;

SoundFile sample;
Amplitude analysis;

void setup() {
  size(640, 480);
 
  // load and play a sound file in a loop
  sample = new SoundFile(this, "beat.aiff");
  sample.loop();

  // create the Amplitude analysis object
  analysis = new Amplitude(this);
  // analyze the playing sound file
  analysis.input(sample);
}

void draw() {
  println(analysis.analyze());
  background(125, 255, 125);
  noStroke();
  fill(255, 0, 150);

  // analyze the audio for its volume level
  float volume = analysis.analyze();

  // volume is a number between 0.0 and 1.0
  // map the volume value to a useful scale
  float diameter = map(volume, 0, 1, 0, width);
  // draw a circle based on the microphone amplitude (volume)
  circle(width/2, height/2, diameter);
}

Then I replaced the audio with one of my favorite songs called “This Hell” by Rina Sawayama. I asked Professor Haider to help me modify some parts of the code so that the NeoPixel could respond to the song and generate dynamic visual effects.

import processing.sound.*;
SoundFile sample;
Amplitude analysis;

import processing.serial.*;
import osteele.processing.SerialRecord.*;
Serial serialPort;
SerialRecord serialRecord;
int W;         //width of the tiles
int NUM = 60;  //amount of pixels
int[] r = new int[NUM]; //red of each tile
int[] g = new int[NUM]; //red of each tile
int[] b = new int[NUM]; //red of each tile
int lastN;

void setup() {
  size(640, 480);
  W = width/NUM;
  // load and play a sound file in a loop
  sample = new SoundFile(this, "Rina Sawayama - This Hell.mp3");
  sample.loop();

  // create the Amplitude analysis object
  analysis = new Amplitude(this);
  // analyze the playing sound file
  analysis.input(sample);

  String serialPortName = SerialUtils.findArduinoPort();
  serialPort = new Serial(this, serialPortName, 9600);
  serialRecord = new SerialRecord(this, serialPort, 4);
  serialRecord.logToCanvas(false);
}

void draw() {
  println(analysis.analyze());
  background(255, 171, 225);
  noStroke();
  fill(255, 230, 247);

  // analyze the audio for its volume level
  float volume = analysis.analyze();

  // volume is a number between 0.0 and 1.0
  // map the volume value to a useful scale
  float diameter = map(volume, 0, 1, 0, width);
  // draw a circle based on the microphone amplitude (volume)
  circle(width/2, height/2, diameter);

  int n = floor(constrain(volume*55, 0, NUM-1));

  r[n] = floor(random(255));
  g[n] = floor(random(255));
  b[n] = floor(random(255));

  serialRecord.values[0] = lastN;     // which pixel we change (0-59)
  serialRecord.values[1] = 0;  // how much red (0-255)
  serialRecord.values[2] = 0;  // how much green (0-255)
  serialRecord.values[3] = 0;  // how much blue (0-255)
  serialRecord.send();            // send it!
  serialRecord.values[0] = n;     // which pixel we change (0-59)
  serialRecord.values[1] = r[n];  // how much red (0-255)
  serialRecord.values[2] = g[n];  // how much green (0-255)
  serialRecord.values[3] = b[n];  // how much blue (0-255)
  serialRecord.send();
  lastN=n;// send it!
}

Look! I successfully visualized my favorite song!

I also asked Professor Haider to help me add time as a variable to change the visual effect along with the song, but due to the limited time, I did not make it happen during the recitation. I will post my code here, though, for future reference.

import processing.sound.*;
SoundFile sample;
Amplitude analysis;

import processing.serial.*;
import osteele.processing.SerialRecord.*;
Serial serialPort;
SerialRecord serialRecord;
int W;         //width of the tiles
int NUM = 60;  //amount of pixels
int[] r = new int[NUM]; //red of each tile
int[] g = new int[NUM]; //red of each tile
int[] b = new int[NUM]; //red of each tile
int lastN;
void setup() {
  size(640, 480);
  W = width/NUM;
  // load and play a sound file in a loop
  sample = new SoundFile(this, "Rina Sawayama - This Hell.mp3");
  sample.loop();

  // create the Amplitude analysis object
  analysis = new Amplitude(this);
  // analyze the playing sound file
  analysis.input(sample);

  String serialPortName = SerialUtils.findArduinoPort();
  serialPort = new Serial(this, serialPortName, 9600);
  serialRecord = new SerialRecord(this, serialPort, 4);
  serialRecord.logToCanvas(false);
}

void draw() {
  println(analysis.analyze());

  long t = millis();
  if (t < 1500) { background(155); } else if ((t>=1500) && t<3000) {
    background(255);
  } else {
    background(55);
  }

  //background(255, 171, 225);
  noStroke();
  fill(255, 230, 247);

  // analyze the audio for its volume level
  float volume = analysis.analyze();

  // volume is a number between 0.0 and 1.0
  // map the volume value to a useful scale
  float diameter = map(volume, 0, 1, 0, width);
  // draw a circle based on the microphone amplitude (volume)

  circle(width/2, height/2, diameter);

  int n = floor(constrain(volume*55, 0, NUM-1));

//  r[n] = floor(random(255));
//  g[n] = floor(random(255));
//  b[n] = floor(random(255));

  long t2 = millis();
  if (t2 < 5000) {
    r[n] = 255;
    g[n] = 0;
    b[n] = 0;
  } else  {
    r[n] = 0;
    g[n] = 0;
    b[n] = 255;
  }


  serialRecord.values[0] = lastN;     // which pixel we change (0-59)
  serialRecord.values[1] = 0;  // how much red (0-255)
  serialRecord.values[2] = 0;  // how much green (0-255)
  serialRecord.values[3] = 0;  // how much blue (0-255)
  serialRecord.send();            // send it!
  serialRecord.values[0] = n;     // which pixel we change (0-59)
  serialRecord.values[1] = r[n];  // how much red (0-255)
  serialRecord.values[2] = g[n];  // how much green (0-255)
  serialRecord.values[3] = b[n];  // how much blue (0-255)
  serialRecord.send();
  lastN=n;// send it!
}

Leave a Reply

Your email address will not be published. Required fields are marked *