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.