Virtual Drum Kit Project
Page last updated 15 Dec 2017, by .
0
replies
Members
- Sterling Smith
- Karl Nicolaus
- Jingchi Yang
- Colin McRae
Parts List

- Mbed
- Sonar Modules (2)
- Speaker
- Fingerprint Scanner
- uSD card breakout board
- uSD card
- Class D Audio Amplifier
- LEDs
- Resistors
- SPST Switches
Circuit Board
- Printed circuit board schematic
- Printed circuit board with parts mounted
Demo Video
Source Code
Mbed Header File
#ifndef MAIN_H
#define MAIN_H
#include "ultrasonic.h"
#include "SDFileSystem.h"
#include "wave_player.h"
#include "GT511C3.hpp"
class Watchdog {
public:
// Load timeout value in watchdog timer and enable
void kick(float s) {
LPC_WDT->WDCLKSEL = 0x1; // Set CLK src to PCLK
uint32_t clk = SystemCoreClock / 16; // WD has a fixed /4 prescaler, PCLK default is /4
LPC_WDT->WDTC = s * (float)clk;
LPC_WDT->WDMOD = 0x3; // Enabled and Reset
kick();
}
// "kick" or "feed" the dog - reset the watchdog timer
// by writing this required bit pattern
void kick() {
LPC_WDT->WDFEED = 0xAA;
LPC_WDT->WDFEED = 0x55;
}
};
typedef struct drum {
Timer sampT;
Timer hitT;
float distance, distanceHold, distanceHold1; // from sensor
float dT, dD, dV; // delta values
float lastD, lastV; // remembered values
float newD, newV, newA; // filtered values
float avgV, v, v1, v2, v3; // remembered v values
float avgA, a, a1, a2, a3; // remembered accel values
float startHitD, hitDD, sumVs;
int validD, wasHit, canHit, countForHit;
unsigned char hitPacket[6];
} Drum;
// function setup
void RxInt(void); // usb serial interrupt handler
void Serial_Recieved(void); // processes usb serial data
void switchChange(void); // processes switch
void default_test(void); // plays a .wav and flashed LEDs
void dpot_test(void); // plays a .wav at different volume levels
void wav_test(void);
void SonarDist1(void); // operates drum 1 only
void SonarDist2(void); // operates drum 2 only
void SonarDistBoth(void); // operates both drums
void drumReset(Drum* drum); // resets all drum class variables
void dist_update1(int); // handler function to update drum 1 distance readings
void dist_update2(int); // handler function to update drum 2 distance readings
void dist_proc(Drum* drum, int); // process disance, velocity, and acceleration values
void hit_proc(Drum* drum, int); // determines if a drum hit has occurred
void hit_action(Drum* drum, int); // executes if a hit has occurred
void playSound(char* wav); // uses SD and .wav libraries
void writeSPI(int); // write to d pot
void playVolSet(int); // sets enumerated volume
void userSelect(void); // sets the global variable
// inputs
Serial pc(USBTX, USBRX);
DigitalIn modeSwitch(p25);
ultrasonic mu1(p21, p22, .005, 1, &dist_update1); //Set the trigger pin to p21 and the echo pin to p22
ultrasonic mu2(p24, p23, .005, 1, &dist_update2); //Set the trigger pin to p24 and the echo pin to p23
SDFileSystem sd(p5, p6, p7, p8, "sd"); // the pinout on the mbed Cool Components workshop board
GT511C3 finger(p28, p27); // Fingerprint scanner (_tx, _rx)
// outputs
DigitalOut myled1(LED1);
DigitalOut myled2(LED2);
DigitalOut myled3(LED3);
DigitalOut myled4(LED4);
DigitalOut hit1(p19);
DigitalOut hit2(p30);
DigitalOut hitLEDs[2] = {hit1, hit2};
DigitalOut recLED(p15);
AnalogOut DACout(p18);
SPI spi(p11, p12, p13); // mosi, miso, sclk. To write commands only, you do not need to connect p6(miso) to MCP4141.
DigitalOut cs(p14); // Chip select. Active low.
// globals
uint8_t volatile holder; // 1 byte
uint64_t volatile buffin; // 4 bytes
unsigned int volatile running = 0;
volatile bool guiConnected = false;
uint8_t volatile user;
Drum drum1;
Drum drum2;
int distance_1, distance_2; // from sensor
#define VGain 100000 // converts (mm/us) to 100x(m/s)
#define AGain 1000000 // converts 100x(m/s / us) to 100x(m/s /s)
// 1 hit ~250 ms
float alphaD;
float alphaV;
float alphaA;
wave_player waver(&DACout);
Timer totalTime;
uint8_t volHit;
int holdDT;
int sts;
// files for .wav playing
char play1[] = "/sd/wavfiles/DRUM1_trim.wav";
char play2[] = "/sd/wavfiles/DRUM2_trim.wav";
char play3[] = "/sd/wavfiles/DRUM3_trim.wav";
char play4[] = "/sd/wavfiles/DRUM4_trim.wav";
char play5[] = "/sd/wavfiles/DRUM5_trim.wav";
char play6[] = "/sd/wavfiles/DRUM6_trim.wav";
char play8[] = "/sd/wavfiles/DRUM8_trim.wav";
char play9[] = "/sd/wavfiles/DRUM9_trim.wav";
char *playWAV[8] = {play1, play2, play3, play4, play5, play6, play8, play9};
#endif
Mbed Main File
#include <mbed.h>
#include "main.h"
int main() {
// arbitrary setup
user = 1;
volHit = 2;
// Initialize FPS
sts = finger.Open();
if(sts ==0) {
finger.CmosLed(1);
}
// Initialize pc serial and interrupt
pc.baud(115200);
wait(1);
pc.attach(&Serial_Recieved, Serial::RxIrq); // interrupt for pcSerial input
modeSwitch.mode(PullUp);
// check WDT timeout condition
if ((LPC_WDT->WDMOD >> 2) & 1) {
pc.printf("WDT timeout occurred\n\r");
}
// setup a 10 second timeout on watchdog timer hardware
//wdt.kick(15.0);
//pc.printf("going\n\r");
playSound(playWAV[0]);
while(1) {
running = 1;
switchChange();
switch (buffin) {
case 0x0000640D : // d, enter
buffin = 0x00000000; // 4 byte reset
default_test();
break;
case 0x0000700D : // p, enter
buffin = 0x00000000; // 4 byte reset
dpot_test();
break;
case 0x0000730D : // s, enter
buffin = 0x00000000; // 4 byte reset
wav_test();
break;
case 0x0000310D : // 1, enter
buffin = 0x00000000; // 4 byte reset
SonarDist1();
break;
case 0x0000320D : // 2, enter
buffin = 0x00000000; // 4 byte reset
SonarDist2();
break;
case 0x0000330D : // 3, enter
buffin = 0x00000000; // 4 byte reset
SonarDistBoth();
break;
case 0x0000670D : // g, enter
buffin = 0x00000000; // 4 byte reset
guiConnected = true;
break;
}
}
}
void Serial_Recieved(void) {
//pc.printf("Serial recieved\n");
// pull in for hardware serial buffer and fill 3-byte software buffer
if (pc.readable()) {
holder = pc.getc(); // read bytes
pc.putc(holder);
buffin = buffin << 8;
buffin = buffin | holder;
}
// if ENTER is hit, set "running" to 0 to exit operating mode
if (holder == 0x0D) {
running = 0;
//pc.printf("\n\r");
}
// if ESC is hit, clear software buffer
if (holder == 0x1B) {
//pc.printf("clr\n\r");
buffin = 0x00000000; // 4 byte reset
}
}
void switchChange(void) {
// set mbed mode (setup or play)
if (!modeSwitch) {
recLED = 1;
myled4 = 0;
SonarDistBoth();
}
else {
recLED = 0;
myled4 = 1;
userSelect();
}
}
void default_test(void) {
//pc.printf("Default Test Mode\n");
writeSPI(96);
playSound(playWAV[0]);
while(running) {
hit1 = 1;
hit2 = 0;
wait(0.2);
hit1 = 0;
hit2 = 1;
wait(0.2);
}
hit1 = 0;
hit2 = 0;
}
void dpot_test(void) {
// 7 bit - 0 to 128
//pc.printf("dpot_test\n");
while(running) {
//myled1 = 1;
writeSPI(0);
playSound(playWAV[0]);
wait(1);
//myled1 = 0;
writeSPI(32);
playSound(playWAV[0]);
wait(1);
writeSPI(64);
playSound(playWAV[0]);
wait(1);
writeSPI(96);
playSound(playWAV[0]);
wait(1);
writeSPI(128);
playSound(playWAV[0]);
wait(1);
}
}
void wav_test(void) {
//pc.printf("wav test\n");
int k;
writeSPI(0);
while(running) {
for(k=0;k<8;k++) {
playSound(playWAV[k]);
wait(1);
}
}
}
void SonarDist1(void) {
drumReset(&drum1);
drumReset(&drum2);
totalTime.start();
mu2.pauseUpdates();
mu1.startUpdates();
while(running) {
mu1.checkDistance();
switchChange();
}
totalTime.stop();
totalTime.reset();
}
void SonarDist2(void) {
drumReset(&drum1);
drumReset(&drum2);
totalTime.start();
mu1.pauseUpdates();
mu2.startUpdates();
while(running) {
mu2.checkDistance();
switchChange();
}
totalTime.stop();
totalTime.reset();
}
void SonarDistBoth(void) {
drumReset(&drum1);
drumReset(&drum2);
totalTime.start();
mu1.startUpdates();
mu2.startUpdates();
while(running) {
if(modeSwitch) {
running = 0;
}
mu1.checkDistance();
mu2.checkDistance();
}
totalTime.stop();
totalTime.reset();
}
void drumReset(Drum* drum) {
drum->sampT.stop();
drum->sampT.reset();
drum->dT = 0; // dT between samples
drum->distance = 0;
drum->distanceHold1 = 0; // memory value for last raw distance
drum->distanceHold = 0; // memory value for last raw distance
drum->newD = 0; // filtered distance
drum->lastD = 0; // distance memory value
drum->startHitD = 0;
drum->hitDD = 0;
drum->sumVs = 0;
drum->newV = 0; // filtered velocity
drum->avgV = 0; // avg velocity
drum->lastV = 0; // velocity memory value
drum->v1 = 0; // velocity memory value
drum->v2 = 0; // velocity memory value
drum->v3 = 0; // velocity memory value
drum->newA = 0; // filtered acceleration
drum->avgA = 0;
drum->a1 = 0; // acceleration memory value
drum->a2 = 0; // acceleration memory value
}
void dist_update1(int distance_1) {
drum1.distanceHold = (float)distance_1;
dist_proc(&drum1, 0);
hit_proc(&drum1, 0);
}
void dist_update2(int distance_2) {
drum2.distanceHold = (float)distance_2;
dist_proc(&drum2, 1);
hit_proc(&drum2, 1);
}
void dist_proc(Drum* drum, int drumNum) {
// find valid data - timing can mess up filter
if (drum->sampT.read_us() > 1000000) {
drum->sampT.stop();
drum->sampT.reset();
}
if ( ((drum->distanceHold < 300) && (drum->distanceHold > 40)) && ((drum->distanceHold1 < 300) && (drum->distanceHold1 > 40))) {
drum->distanceHold1 = drum->distanceHold;
drum->distance = drum->distanceHold;
drum->validD = 1;
}
else {
drum->distanceHold1 = drum->distanceHold;
drum->distance = drum->distance;
drum->validD = 0;
//return;
}
// determine change in time
// determined in us: 1000000 us to s
drum->sampT.stop();
int holdDT = drum->sampT.read_us();
drum->dT = (float)holdDT;
drum->sampT.reset();
drum->sampT.start();
// determine change in distance
// determined in mm: 1000 mm to m
alphaD = drum->dT/(1000*250); // 1000 us per ms ; 1 hit width ~ 250 ms (higher = more smoothing)
drum->newD += alphaD * (drum->distance - drum->newD); // simple filter
//drum->newDist = drum->distance; // direct
drum->dD = drum->newD - drum->lastD;
// determine velocity, with scaling and averaging
if (drum->dT > 0) {
drum->v = -(drum->dD*VGain)/drum->dT;
}
else {
drum->v = 0;
}
alphaV = drum->dT/(1000*250); // 1000 us per ms ; 1 hit width ~ 250 ms (higher = more smoothing)
//drum->newV += alphaV * (drum->v - drum->newV); // simple filter
drum->newV = drum->v; // direct
//drum->avgV = (drum->newV+drum->v1+drum->v2+drum->v3)/4; // average
drum->avgV = drum->newV; // direct
drum->dV = drum->avgV - drum->lastV;
// determine acceleration, with scaling
if (drum->dT > 0) {
drum->a = (drum->dV*AGain)/drum->dT;
}
else {
drum->a = 0;
}
alphaA = drum->dT/(1000*250); // 1000 us per ms ; 1 hit width ~ 250 ms (higher = more smoothing)
//drum->newA += alphaA * (drum->a - drum->newA); // simple filter
drum->newA = drum->a; // direct
//drum->avgA = (drum->newA+drum->a1+drum->a2+drum->a3)/4; // average
drum->avgA = drum->newA; // direct
// update values for memory
drum->lastD = drum->newD;
drum->lastV = drum->avgV;
drum->a3 = drum->a2;
drum->a2 = drum->a1;
drum->a1 = drum->avgA;
drum->v3 = drum->v2;
drum->v2 = drum->v1;
drum->v1 = drum->avgV;
}
void hit_proc(Drum* drum, int drumNum) {
// find valid data
if (drum->validD == 0) {
return;
}
if (drum->avgV < -8 && drum->canHit == 0) {
drum->canHit = 1;
drum->countForHit = 0;
drum->v2 = 0;
drum->v1 = 0;
drum->startHitD = drum->newD;
drum->sumVs = drum->avgV;
drum->hitT.start();
}
if (drum->hitT.read_ms() > 350) {
drum->canHit = 0;
drum->countForHit = 0;
drum->sumVs = 0;
drum->hitT.stop();
drum->hitT.reset();
}
else if (drum->canHit != 1) {
drum->countForHit = 0;
drum->sumVs = 0;
drum->hitT.stop();
drum->hitT.reset();
}
else {
drum->sumVs += drum->avgV;
if (drum->newD < drum->startHitD) {
drum->startHitD = drum->newD;
}
}
if (drum->newD < 300 && drum->v2 < -3 && drum->avgV > 0.5 && drum->canHit == 1 && drum->hitT.read_ms() < 1000) {
hitLEDs[drumNum] = 1;
hit_action(drum, drumNum);
hitLEDs[drumNum] = 0;
drum->wasHit = 1;
drum->canHit = 0;
drum->hitT.reset();
}
else {
drum->wasHit = 0;
}
//pc.printf("%d; T: %d filtD: %f filtV: %f filtA: %f canHit: %d count: %d wasHit: %d dT: %d\r\n", drumNum, totalTime.read_ms(), drum->newD, drum->avgV, drum->avgA, drum->canHit, drum->countForHit, drum->wasHit, holdDT);
}
void hit_action(Drum* drum, int drumNum) {
int timeHold;
drum->hitT.stop();
holdDT = drum->hitT.read_ms();
drum->hitDD = drum->newD - drum->startHitD;
if (holdDT < 230) {
volHit = 3;
}
else {
volHit = 1;
}
playVolSet(volHit);
int k = (2*user)+drumNum;
if (guiConnected) {
timeHold = totalTime.read_ms();
drum->hitPacket[0] = user;
drum->hitPacket[1] = drumNum+1;
drum->hitPacket[2] = (((unsigned char)timeHold) & (0xFF0000)) >> 16;
drum->hitPacket[3] = (((unsigned char)timeHold) & (0x00FF00)) >> 8;
drum->hitPacket[4] = ((unsigned char)timeHold) & (0x0000FF);
drum->hitPacket[5] = volHit+1;
for(int i=0;i<6;i++) {
pc.putc(drum->hitPacket[i]);
//pc.printf("%x", drum->hitPacket[i]);
}
//pc.printf("%d\n\r", k);
}
playSound(playWAV[k]);
}
void playSound(char* wav) {
//open wav file
FILE *wave_file;
wave_file=fopen(wav,"r");
if(wave_file != NULL)
{
waver.play(wave_file);
fclose(wave_file);
return;
}
pc.printf("Could not open file for reading - %s\n\r", wav);
return;
}
void writeSPI(int n) { // 0 >= n <= 128
cs=1;
cs=0;
spi.write(0x00); // Address(volatile) 0000, write=00, data=00 = 0x00
spi.write(n); // Data
cs=1;
}
void playVolSet(int n) {
switch (n) {
case 3:
writeSPI(0); // loud
break;
case 2:
writeSPI(32); // mid high
break;
case 1:
writeSPI(64); // mid low
break;
case 0:
writeSPI(96); // low
break;
default:
writeSPI(128); // off
}
}
void userSelect(void) {
if (finger.IsPress() == 1) {
myled1 = 1;
while(finger.Capture(1) != 0) {
// loop until finger capture is good
}
user = finger.Identify() - 11;
if(user >= 0 && user <= 3) {
myled3 = 0;
} else {
myled3 = 1;
}
myled1 = 0;
finger.WaitPress(0);
wait(1);
}
}
C# GUI Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using AudioSwitcher.AudioApi.CoreAudio;
namespace VirtualDrums
{
public partial class Form1 : Form
{
int[][] song = new int[10][];
//song[0] = new int[4];
bool stopplay = false;
bool startrecord = false;
int count = 0;
private BackgroundWorker _worker = null;
private BackgroundWorker _record = null;
public Form1()
{
//Start application, begin reading drum playing
song[0] = new int[4];
song[1] = new int[4];
song[2] = new int[4];
song[3] = new int[4];
song[4] = new int[4];
song[5] = new int[4];
song[6] = new int[4];
song[7] = new int[4];
song[8] = new int[4];
song[9] = new int[4];
InitializeComponent();
serialPort1.Close();
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
//Display image test
ShowMyImage(@"C:\embedded_lab\VirtualDrums\Pictures\drum_1.bmp", 10, 10);
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//String data = serialPort1.ReadExisting();
//String data = serialPort1.ReadLine();
//Console.WriteLine(data);
int bytesize = serialPort1.BytesToRead;
Console.WriteLine("port read byte size: {0}", bytesize);
int[] hit = new int[] { 0, 0, 0, 0 };
while (startrecord && count < 10)
{
if (serialPort1.BytesToRead > 5)
{
Console.WriteLine("count: {0}",count);
byte[] data = new byte[serialPort1.BytesToRead];
//Console.WriteLine(serialPort1.BytesToRead);
serialPort1.Read(data, 0, serialPort1.BytesToRead);
int user = (int)data[0];
hit[0] = user;
Console.WriteLine(user);
song[count][0] = user;
Console.WriteLine(song[count][0]);
int drum = (int)data[1];
hit[1] = drum;
song[count][1] = hit[1];
Console.WriteLine(drum);
Console.WriteLine(song[count][1]);
int waitms = (65536 * (int)data[2] + 256 * (int)data[3] + (int)data[4]);
hit[2] = waitms;
song[count][2] = hit[2];
Console.WriteLine(waitms);
int volume = 15*(int)data[5];
hit[3] = volume;
Console.WriteLine(volume);
song[count][3] = hit[3];
//Console.WriteLine(song[i]);
Console.WriteLine("user{0} drum{1} ms{2} vol{3}", song[count][0], song[count][1], song[count][2], song[count][3]);
//playback(user, drum, waitms, volume);
count++;
}
}
if (count == 10) { Console.WriteLine("song(10 hits) recorded"); }
}
private void playsong(int[][] song)
{
for (int i = 0; i < song.Length; i++)
{
if (stopplay) break;
playhit(song[i][0], song[i][1], song[i][2], song[i][3]);
Console.WriteLine("playsong: {0}",i);
Console.WriteLine("user{0} drum{1} ms{2} vol{3}", song[i][0], song[i][1], song[i][2], song[i][3]);
}
Console.WriteLine("song is stopped.");
}
private void playhit(int user, int drum, int waitms, int volume)
{
CoreAudioDevice volumecontroller = new CoreAudioController().DefaultPlaybackDevice;
volumecontroller.Volume = volume;
switch (user)
{
case 1:
if (drum == 1)
{
System.Media.SoundPlayer player1 = new System.Media.SoundPlayer(@"c:\embedded_project\soundfile\DRUM1_trim.wav");
player1.PlaySync();
Console.WriteLine("soundfile 1");
}
else
{
System.Media.SoundPlayer player2 = new System.Media.SoundPlayer(@"c:\embedded_project\soundfile\DRUM2_trim.wav");
player2.PlaySync();
Console.WriteLine("soundfile 2");
}
break;
case 2:
if (drum == 1)
{
System.Media.SoundPlayer player3 = new System.Media.SoundPlayer(@"c:\embedded_project\soundfile\DRUM3_trim.wav");
player3.PlaySync();
Console.WriteLine("soundfile 3");
}
else
{
System.Media.SoundPlayer player4 = new System.Media.SoundPlayer(@"c:\embedded_project\soundfile\DRUM4_trim.wav");
player4.PlaySync();
Console.WriteLine("soundfile 4");
}
break;
case 3:
if (drum == 1)
{
System.Media.SoundPlayer player5 = new System.Media.SoundPlayer(@"c:\embedded_project\soundfile\DRUM5_trim.wav");
player5.PlaySync();
Console.WriteLine("soundfile 5");
}
else
{
System.Media.SoundPlayer player6 = new System.Media.SoundPlayer(@"c:\embedded_project\soundfile\DRUM6_trim.wav");
player6.PlaySync();
Console.WriteLine("soundfile 6");
}
break;
case 4:
if (drum == 1)
{
System.Media.SoundPlayer player7 = new System.Media.SoundPlayer(@"c:\embedded_project\soundfile\DRUM8_trim.wav");
player7.PlaySync();
Console.WriteLine("soundfile 7");
}
else
{
System.Media.SoundPlayer player8 = new System.Media.SoundPlayer(@"c:\embedded_project\soundfile\DRUM9_trim.wav");
player8.PlaySync();
Console.WriteLine("soundfile 8");
}
break;
}
Thread.Sleep(waitms);
}
//Function for displaying image
private Bitmap MyImage;
public void ShowMyImage(String fileToDisplay, int xSize, int ySize)
{
// Sets up an image object to be displayed.
if (MyImage != null)
{
MyImage.Dispose();
}
// Stretches the image to fit the pictureBox.
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
MyImage = new Bitmap(fileToDisplay);
//pictureBox1.ClientSize = new Size(xSize, ySize);
pictureBox1.Image = (Image)MyImage;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
//Code to play back recording
_worker = new BackgroundWorker();
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += new DoWorkEventHandler((state, args) =>
{
stopplay = false;
playsong(song);
});
_worker.RunWorkerAsync();
button1.Enabled = false;
button2.Enabled = true;
}
private void button2_Click(object sender, EventArgs e)
{
//Code to stop playback
button2.Enabled = false;
button1.Enabled = true;
_worker.CancelAsync();
stopplay = true;
startrecord = false;
for (int i = 0; i < 10; i++)
{
song[i][0] = 0;
song[i][1] = 0;
song[i][2] = 0;
song[i][3] = 0;
}
}
private void button3_Click(object sender, EventArgs e)
{
//code for record
_record = new BackgroundWorker();
_record.WorkerSupportsCancellation = true;
_record.DoWork += new DoWorkEventHandler((state, args) =>
{
startrecord = true;
count = 0;
});
_record.RunWorkerAsync();
button3.Enabled = false;
button5.Enabled = true;
}
private void pictureBox1_Click(object sender, EventArgs e)
{
}
private void button4_Click(object sender, EventArgs e)
{
//Code for changing profile via fingerprint scanner
label1.Text = "Hello, Colin!";
ShowMyImage(@"C:\embedded_lab\VirtualDrums\Pictures\drum_2.bmp", 10, 10);
}
private void button5_Click(object sender, EventArgs e)
{
button5.Enabled = false;
button3.Enabled = true;
_record.CancelAsync();
stopplay = true;
startrecord = false;
}
}
}
Please log in to post comments.
