TrentJohnny Yan
Published © GPL3+

UW-Makeathon: Laser Drums

Laser Cut Arduino Midi Drum Pads and Audio Waveform Visualizer

IntermediateWork in progress10 hours3,894
UW-Makeathon: Laser Drums

Things used in this project

Hardware components

Microphone Amplifier Breakout
Adafruit Microphone Amplifier Breakout
×1
Arduino Mega 2560
Arduino Mega 2560
×1
NeoPixel strip
NeoPixel strip
×1
5V 40A Power Supply
×1
Pro Micro - 5V/16MHz
SparkFun Pro Micro - 5V/16MHz
×1

Software apps and online services

Arduino IDE
Arduino IDE
Adobe Illustrator
FL Studio

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Laser cutter (generic)
Laser cutter (generic)
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Custom parts and enclosures

6x6 Box EVA Foam Pad

Drum Pad for the 6x6 Modules

6x6x2" Box

Laser Cut Box for Drum Module x7

7x7x2" Box

Laser Cut Box for Main Drum Module

6x6" Box EVA Foam Spacers

Foam Spacers for the 6x6 Modules

7x7 Box EVA Foam

EVA Foam Drum Pad and spacers for the 7x7 Module

Code

MusicVisualization

Arduino
Displays a music waveform to the NEOPixel display
#include <arduinoFFT.h>
#include <math.h>
#include <FastLED.h>
#include <LEDColumn.h>

#define SAMPLES 32
#define SAMPLING_FREQUENCY 10000
#define DATAPIN1 43
#define DATAPIN2 53
#define NUM_LEDS 300
#define AIN A7

CRGB leds1[NUM_LEDS];
CRGB leds2[NUM_LEDS];

arduinoFFT FFT = arduinoFFT();

unsigned int sampling_period_us;
unsigned long microseconds;

double vReal[SAMPLES];
double vImag[SAMPLES];
LEDColumn LEDColumns1[10];
LEDColumn LEDColumns2[10];
void setup()
{
  sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
  FastLED.addLeds<NEOPIXEL, DATAPIN1>(leds1, NUM_LEDS);
  FastLED.addLeds<NEOPIXEL, DATAPIN2>(leds2, NUM_LEDS);

  //sets up each LED Column
  for (int i = 0; i < 10; i++)
  {
    int startingLED = (i * 30);
    int evenOdd = (i % 2);
    LEDColumns1[i] = *(new LEDColumn(evenOdd));
    LEDColumns2[i] = *(new LEDColumn(evenOdd));
  }
  //make the bottom of every column light up
  for (int i = 0; i < 10; i++) {
    if (LEDColumns1[i].isUpsideDown()) {
      leds1[((30 * i) + 29)] = CRGB(0, 0, 100);
      leds2[((30 * i) + 29)] = CRGB(0, 0, 100);
    } else {
      leds1[(30 * i)] = CRGB(0, 0, 100);
      leds2[(30 * i)] = CRGB(0, 0, 100);
    }
  }
  FastLED.show();

}

void loop()
{
  /*SAMPLING*/
  for (int i = 0; i < SAMPLES; i++)
  {
    microseconds = micros();    //Overflows after around 70 minutes!

    vReal[i] = analogRead(AIN);
    vImag[i] = 0;

    while (micros() < (microseconds + sampling_period_us)) {
    }
  }
  /*FFT*/
  FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
  FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);

  //get the max value of the magnitude and pass it to LEDColumn to compute
  for (int i = 0; i < 20; i++) {
    double max = 0;
    for (int j = 0; j < (SAMPLES / 20); j++) {
      double currentReal = vReal[((SAMPLES / 20) * i + j)];

      if (currentReal > max) {
        max = currentReal;
      }
    }
    double computedMax = log(max * max * max);
    if (i < 10) {
      //the height of the LED columns must have increased
      if (LEDColumns1[i].computeHeight(computedMax) > 0) {

        if (LEDColumns1[i].isUpsideDown()) {
          leds1[(30 * i) + 29 - LEDColumns1[i].getHeight()] = CRGB(0, 0, 100);

        } else {
          leds1[(30 * i) + LEDColumns1[i].getHeight()] = CRGB(0, 0, 100);
        }
      } else {
        if (LEDColumns1[i].isUpsideDown()) {
          leds1[(30 * i) + 29 - LEDColumns1[i].getHeight() - 1] = CRGB::Black;
        } else {
          leds1[(30 * i) + LEDColumns1[i].getHeight() + 1] = CRGB::Black;
        }
      }
      FastLED.show();
    } else {
      if (LEDColumns2[i - 10].computeHeight(computedMax) > 0) {

        if (LEDColumns2[i - 10].isUpsideDown()) {
          leds2[(30 * (i - 10)) + 29 - LEDColumns2[i - 10].getHeight()] = CRGB(0, 0, 100);

        } else {
          leds2[(30 * (i - 10)) + LEDColumns2[i - 10].getHeight()] = CRGB(0, 0, 100);
        }
      } else {
        if (LEDColumns2[i - 10].isUpsideDown()) {
          leds2[(30 * (i - 10)) + 29 - LEDColumns2[i - 10].getHeight() - 1] = CRGB::Black;
        } else {
          leds2[(30 * (i - 10)) + LEDColumns2[i - 10].getHeight() + 1] = CRGB::Black;
        }
      }
      FastLED.show();
    }

  }
}

LEDColumn

C/C++
Class that controls data for each LED Column
#include "Arduino.h"
#include "LEDColumn.h"

LEDColumn::LEDColumn(void)
{
    //Fake Constructor to get around wierd type error
}
LEDColumn::LEDColumn(int evenOdd)
{
    //Real Constructor
    _Odd = evenOdd;
    height = 0;
}
boolean LEDColumn::isUpsideDown()
{
    return (_Odd == 1);
}
int LEDColumn::getHeight()
{
    return height;
}
// return the height

/**
 * calculate the new height of the columns based on whether it is incremented or decremented
 * return 1 if the height is incremented, -1 if it is decremented, or 0 if it stays the same
 */

int LEDColumn::computeHeight(double FFT_data)
{
    if (height < FFT_data)
    {
        if (height == 29)
        {
            return 0;
        }
        height++;
        return 1;
    }
    else
    {
        if (height == 0)
        {
            return 0;
        }
        height--;
        int guyo = -1;
        Serial.println(guyo);
        return guyo;
    }
}

LEDColumn Header file

C/C++
Header for the LEDColumn class.
#ifndef LEDColumn_h
#define LEDColumn_h

#include "Arduino.h"
class LEDColumn
{
  public:
  /* Constructor */
	  LEDColumn(void);
    LEDColumn(int evenOdd);
    int computeHeight(double FFT_DATA);
    int getHeight();
    boolean isUpsideDown();
  private:
    int height;
    int _Odd;

    
};

#endif

Credits

Trent

Trent

1 project • 0 followers
Johnny Yan

Johnny Yan

3 projects • 0 followers
Thanks to kosme (github), FastLED community, Evan Kale, and gurbrinder grewal.

Comments