Introduction: Facial Music Visualizer

This instructable is a work in progress, photos and videos to come soon!

My class is entering projects into the Pensacola Mini Maker's Fair. Working off the prompt of creating a sensor reactive wearable, I started this project with the intent of visualizing sound through LED strips modeled as a facial accessory.

In my research for LEDs, I discovered the beautiful Mini Skinny Neopixel strands from Adafruit. For my project, I used the strand with 60 LEDs per meter, however, you could purchase a strand with more or fewer LEDs per meter depending on your budget.

I am very inspired by LED projects that react to music and I wanted to find a way to fuse the concept of music reacting LEDs with the insane ways my friends in the Shanghai club kid scene dress as well as using the mini skinny neopixel strands.

That being said, I'm going to show you how to make an LED sound-reactive Headpiece conceptualized for your next rave, festival, or concert.

Supplies

  • Microphone sensor with adjustable gain and pins for out, ground, and VCC (I bought the DAOKI 5PCS High Sensitivity Sound Microphone Sensor off of amazon)

  • LED Mini Skinny Neopixel Strip (2-meter strip to account for any errors)

  • Arduino UNO

  • 330 Ohm Resistor

  • 1000uF Capacitor

  • Breakaway Male Headers

  • A PCB

  • 9volt Battery

  • DC battery jack

  • Braided cable management coil

  • Some sort of chest rig to attach the Arduino, battery, and sensor onto. ( I used a posture harness and sewed a small pouch onto it.)

  • A small box to house the Arduino and battery

  • Thin silver metal headbands (I ordered a cheap pack of 10 from amazon)

  • Silicon self-fusing heat-resistant tape.

  • Heat shrink tubing

  • Hot Glue Gun

  • Heat gun

  • Soldering iron

For testing, you'll also want to have a breadboard, some M/M jumper wires, and two alligator clip double-ended jumper wires.

Step 1: Layout Your Bread Board

Follow the fritzing diagram for the connections, but feel free to rearrange your parts on the breadboard as you please.

check out adafruits guide to testing and wiring neopixels: https://learn.adafruit.com/adafruit-neopixel-uberg...

Your neopixels should connect the black ground wire to the ground pin on the Arduino and the red power wire to the 5V pin on the Arduino. The white wire is your Dout(Data out) and will connect through your 330 Ohm resistor to pin 6 on the Arduino board.

Connect the negative end of your capacitor to ground in line with your neopixel's ground and the positive end inline with your power wire of the neopixels powerline.

Your microphone sensor will have three pins on it reading "OUT, GND, VCC". Connect the OUT pin to the A1 pin on your Arduino, the VCC to the power line on the breadboard, and the GND to ground either on the Arduino or on the ground line of the breadboard.

Step 2: Upload and Adjust Your Code

Now that you have everything connected to your Arduino, It's time to upload your code and adjust some variables to your project.

Remember that your microphone sensor has a potentiometer that will adjust your gain, therefore, adjusting how much sound it actually picks up (this will come in handy if you notice your neopixels are not lighting up as much as you want them to.)

The code has instructions within it on what each variable controls. (Code provided by Sparkfun and requires neopixel library)

I have made my own adjustments to the original code

Once code is uploaded and adjusted to your preferred number of LEDs, I recommend playing around with some variables to alter the colors, brightness, and patterns you can come out with! All the time making notes of what you change or having a second copy of the original code so if you want to change it back you can! Play some music to see what kind of playback you get with your neopixels!

text of the code here:

//Program by Michael Bartlett

//Libraries #include //Library to simplify interacting with the LED strand #ifdef __AVR__ #include //Includes the library for power reduction registers if your chip supports them. #endif //More info: http://www.nongnu.org/avr-libc/user-manual/group_...

//Constants (change these as necessary) #define LED_PIN 6 //Pin for the pixel strand. Does not have to be analog. #define LED_TOTAL 48 //Change this to the number of LEDs in your strand. #define LED_HALF LED_TOTAL/2 #define AUDIO_PIN A0 //Pin for the envelope of the sound detector #define KNOB_PIN A1 //Pin for the trimpot 10K

////////// // These values either need to be remembered from the last pass of loop() or // need to be accessed by several functions in one pass, so they need to be global.

Adafruit_NeoPixel strand = Adafruit_NeoPixel(LED_TOTAL, LED_PIN, NEO_GRB + NEO_KHZ800); //LED strand objetc

uint16_t gradient = 0; //Used to iterate and loop through each color palette gradually

uint8_t volume = 0; //Holds the volume level read from the sound detector. uint8_t last = 0; //Holds the value of volume from the previous loop() pass.

float maxVol = 20; //Holds the largest volume recorded thus far to proportionally adjust the visual's responsiveness. float knob = 1023.0; //Holds the percentage of how twisted the trimpot is. Used for adjusting the max brightness. float avgVol = 0; //Holds the "average" volume-level to proportionally adjust the visual experience. float avgBump = 0; //Holds the "average" volume-change to trigger a "bump."

bool bump = false; //Used to pass if there was a "bump" in volume

//////////

//////////

void setup() { //Like it's named, this gets ran before any other function.

Serial.begin(9600); //Sets data rate for serial data transmission.

strand.begin(); //Initialize the LED strand object. strand.show(); //Show a blank strand, just to get the LED's ready for use. }

void loop() { //This is where the magic happens. This loop produces each frame of the visual. volume = analogRead(AUDIO_PIN); //Record the volume level from the sound detector knob = analogRead(KNOB_PIN) / 1023.0; //Record how far the trimpot is twisted avgVol = (avgVol + volume) / 2.0; //Take our "average" of volumes.

//monotoring readings //String StrUno = "this is the volume: "; //String StrDos = volume ; //String StrTres = "this is the avg volume: "; //String StrQ = avgVol; //String StrC = (StrUno + StrDos); //Serial.println(StrC); //Sets a threshold for volume. // In practice I've found noise can get up to 15, so if it's lower, the visual thinks it's silent. // Also if the volume is less than average volume / 2 (essentially an average with 0), it's considered silent. if (volume < avgVol / 2.0 || volume < 15) volume = 0;

//If the current volume is larger than the loudest value recorded, overwrite if (volume > maxVol) maxVol = volume;

//This is where "gradient" is reset to prevent overflow. if (gradient > 1529) {

gradient %= 1530;

//Everytime a palette gets completed is a good time to readjust "maxVol," just in case // the song gets quieter; we also don't want to lose brightness intensity permanently // because of one stray loud sound. maxVol = (maxVol + volume) / 2.0; }

//If there is a decent change in volume since the last pass, average it into "avgBump" if (volume - last > avgVol - last && avgVol - last > 0) avgBump = (avgBump + (volume - last)) / 2.0;

//if there is a notable change in volume, trigger a "bump" bump = (volume - last) > avgBump;

Pulse(); //Calls the visual to be displayed with the globals as they are.

gradient++; //Increments gradient

last = volume; //Records current volume for next pass

delay(40); //Paces visuals so they aren't too fast to be enjoyable }

//////////

//////////

//PULSE //Pulse from center of the strand void Pulse() {

fade(0.75); //Listed below, this function simply dims the colors a little bit each pass of loop()

//Advances the gradient to the next noticeable color if there is a "bump" if (bump) gradient += 124;

//If it's silent, we want the fade effect to take over, hence this if-statement if (volume > 0) { uint32_t col = Rainbow(gradient); //Our retrieved 32-bit color

//These variables determine where to start and end the pulse since it starts from the middle of the strand. // The quantities are stored in variables so they only have to be computed once. int start = LED_HALF - (LED_HALF * (volume / maxVol)); int finish = LED_HALF + (LED_HALF * (volume / maxVol)) + strand.numPixels() % 19; //Listed above, LED_HALF is simply half the number of LEDs on your strand. ↑ this part adjusts for an odd quantity.

for (int i = start; i < finish; i++) {

//"damp" creates the fade effect of being dimmer the farther the pixel is from the center of the strand. // It returns a value between 0 and 1 that peaks at 1 at the center of the strand and 0 at the ends. float damp = float( ((finish - start) / 2.0) - abs((i - start) - ((finish - start) / 2.0)) ) / float((finish - start) / 12.0);

//Sets the each pixel on the strand to the appropriate color and intensity // strand.Color() takes 3 values between 0 & 255, and returns a 32-bit integer. // Notice "knob" affecting the brightness, as in the rest of the visuals. // Also notice split() being used to get the red, green, and blue values. strand.setPixelColor(i, strand.Color( split(col, 0) * pow(damp, 2.0) * knob, split(col, 1) * pow(damp, 2.0) * knob, split(col, 2) * pow(damp, 2.0) * knob )); } //Sets the max brightness of all LEDs. If it's loud, it's brighter. // "knob" was not used here because it occasionally caused minor errors in color display. Original brightness 255 strand.setBrightness(40.0 * pow(volume / maxVol, 2)); }

//This command actually shows the lights. If you make a new visualization, don't forget this! strand.show(); }

//Fades lights by multiplying them by a value between 0 and 1 each pass of loop(). void fade(float damper) {

//"damper" must be between 0 and 1, or else you'll end up brightening the lights or doing nothing. if (damper >= 1) damper = 0.99;

for (int i = 0; i < strand.numPixels(); i++) {

//Retrieve the color at the current position. uint32_t col = (strand.getPixelColor(i)) ? strand.getPixelColor(i) : strand.Color(0, 0, 0);

//If it's black, you can't fade that any further. if (col == 0) continue;

float colors[3]; //Array of the three RGB values

//Multiply each value by "damper" for (int j = 0; j < 3; j++) colors[j] = split(col, j) * damper;

//Set the dampened colors back to their spot. strand.setPixelColor(i, strand.Color(colors[0] , colors[1], colors[2])); } }

uint8_t split(uint32_t color, uint8_t i ) {

//0 = Red, 1 = Green, 2 = Blue

if (i == 0) return color >> 16; if (i == 1) return color >> 8; if (i == 2) return color >> 0; return -1; }

//This function simply take a value and returns a gradient color // in the form of an unsigned 32-bit integer

//The gradient returns a different, changing color for each multiple of 255 // This is because the max value of any of the 3 LEDs is 255, so it's // an intuitive cutoff for the next color to start appearing. // Gradients should also loop back to their starting color so there's no jumps in color.

uint32_t Rainbow(unsigned int i) { if (i > 1529) return Rainbow(i % 1530); if (i > 1274) return strand.Color(255, 0, 255 - (i % 255)); //violet -> red if (i > 1019) return strand.Color((i % 255), 0, 255); //blue -> violet if (i > 764) return strand.Color(0, 255 - (i % 255), 255); //aqua -> blue if (i > 509) return strand.Color(0, 255, (i % 255)); //green -> aqua if (i > 255) return strand.Color(255 - (i % 255), 255, 0); //yellow -> green return strand.Color(255, i, 0); //red -> yellow }

//////////

Step 3: Let's Build

For the Arduino:

- Make a shield for your Arduino with balanced male headers, making sure to plug [ins in required pins on the Arduino. Make sure the shield has an extended width for some extra space for soldered connections and wires.

- Solder on your wires for the sensor and other hardware pieces to appropriate pins on the shield, using stranded wire. Make sure to give yourself a lot of extra length for the opposite connection. (you can always cut it shorter as needed)

For the LED Armature:

- Align your headbands on your head or the head of a mannequin with one coming over the front center of your head, one coming over the center of the head and behind the ears, and the third horizontal on the head and resting the ends on your temples.

- Once you have a comfortable alignment, mark your placement on the headbands with the silicon tape so you can remove the headbands from your head. Use the silicon tape to wrap in an X pattern to fasten each touching point of the headbands.

Cutting and soldering LEDs: this is where things get a little tricky. I made a branched solder so that my neopixels could go off into several directions along the armature. MAKE SURE ALL YOUR NEOPIXELS ARE IN THE RIGHT DIRECTION!! there is a small arrow pattern along the strand that indicates the flow of electricity. Just be sure you keep the arrows flowing out.

- Measure out how many LEDs you want on the central longitude headband leading to the central meeting point of all the headbands.

- Using stranded wire, create a tree-like wire contraction for the ground, power, and data line. The easiest way I found to do this is to take a short strand of wire and strip both ends of it, Keep one end bare and to the other end create a twisted connection with three other wires. Then solder the twisted connections and insulate it with electrical tape. Solder the bare end to the corresponding connections.

- I found the most secure connection was twisting the stranded wire tight and threading it through the connection holes on the neopixel strand.

-Measure, cut, and solder the rest of your neopixels to fit on your armature exactly as you want to.

- Use the silicon tape on each opening and wrap the silicon tape around certain connections on the armature.

- Use hot-glue to secure all of your solders.

For the Microphone connection:

- Cut the male end off of a set of three different color, male to female jumper cable. solder those connections with the corresponding wires for the microphone connection. (This will make it easier for you to replace your microphone sensor when needed)

For Arduino holder:

- Using a box with about twice the length of the Arduino and shield (plus room for a 9V battery), customize it to fix the back of your brace with an opening for your wires. ( for this I just found a small O.P.I nail polish box, taped up the bottom, and cut open the top vertically.)

- Sew a small pouch to hold the Arduino box onto the central part of the back brace.

- Attach the wired microphone sensor to the top of the shoulder on the back brace.

Finishing touches:
- Use braided cable management to conceal and consolidate the wires and seal it with heat shrink.

Step 4: Wear It and Have Fun