This project is my personal favourite. For one , I learned something about music and the science used to process it. And also , through this project I was able to revisit and apply a lot of concepts that I had learned during the last semester but thought were useless. I listened to Queen's 'We Will Rock You' , a hundred times or so while working on this project. So much so that I can no longer tolerate it.
That aside , this project used a lot of components that I will each visit separately in this tutorial.
Feeding Audio into the Arduino
1.Sound is an analog signal that is stored and processed digitally in computers. We use an Arduino's inbuilt ADC ( analog to digital converter) to read an audio signal. The ADC is built for a 0-5V range of values, that is , it can convert analog signals in this range into a corresponding digital value between 0 and 1023.
Music output from an MP3 or smart phones,however, will be in milli-volts. This raises the issue of not being able to use the entire resolution of the ADC. Thus , the input signal will have to be amplified. You can use an operational amplifier IC or use a simple transistor to do this. The circuit is very simple , you will just need a couple of capacitors and resistors. I however was too lazy to do this. I managed by maxing the volume from my laptop but that is not the proper solution.
Music output from an MP3 or smart phones,however, will be in milli-volts. This raises the issue of not being able to use the entire resolution of the ADC. Thus , the input signal will have to be amplified. You can use an operational amplifier IC or use a simple transistor to do this. The circuit is very simple , you will just need a couple of capacitors and resistors. I however was too lazy to do this. I managed by maxing the volume from my laptop but that is not the proper solution.
2. An audio signal can have both positive and negative values of voltage.However , your tiny micro-controller's ADC is made for only positive value. You will have to make an offset circuit for that. What this offsetting circuit does is that it shifts the entire signal such that the bias value become the origin. For example if you provide a bias of 2.5 V , the signal value will oscillate about this value.
Here is how it is made :
The 22uF capacitor with the 10K resistor make a High Pass Filter. A high pass filter allows frequencies only above a certain value to pass. This cutoff frequency is given by f=1/2*pi*R*C.
For this arrangement we see the value 0.7 Hz. Thus , the DC component is removed from the incoming signal.
The two 10K resistors make a voltage divider, creating a bias of 2.5V. This is connected to a 1K pull up resistor and a coupling capacitor , to reduce noise. This also makes a low pass filter.
3. This is fed into the ADC. At analog pin 0 or whichever you might choose.
Sampling and Processing
1. We need to sample this input signal. Sampling mean recording amplitudes at regular intervals. This is how analog signals are read by computers. The issue is the analogRead() function only allows sampling a 8Khz which is very poor for Audio. Typical sampling rates for audio is 40Khz. However all hope is not lost. There is a way to change the ADC sampling rate by setting values for the four MUX that control the ADC.
Check this another blog post to understand how it is done :
http://yaab-arduino.blogspot.in/2015/02/fast-sampling-from-analog-input.html
2. That done , now we need to compute the FFT (Fast Fourier Transform) on the signal. What this does is that it converts the signal from time domain to frequency domain. What this means is that you can get amplitudes of different frequency ranges in your sound signal. Typical Bass frequency ranges from 50-200Hz. It is this that we are interested in. All drum kicks , snares and rhythm information is contained here. There are various optimizations of the FFT available for the arduino. The one I used is developed by Open Music Labs and can be downloaded from the link below :
http://wiki.openmusiclabs.com/wiki/ArduinoFFT
You will have to read a bit from their website to get a better grasp of using this library.
3.Now with the FFT done , we look for peaks/ increase in amplitudes in the lower frequency ranges. that is when a beat happens.Once we have that , rest is easy. Control Lights or Motors.
These might be useful :
http://dpeckett.com/beat-detection-on-the-arduino
http://www.arduinoos.com/2010/10/fast-fourier-transform-fft/
Finally here is the Video of my project in action :
The code :
#define LIN_OUT8 1 // use the Lin_OUT8 output function
#define FFT_N 16
#include <Servo.h>
#include <FFT.h> // include the library
int led=13;
Servo servo;
Servo servo1;// create servo object to control a servo
int j=1,l=1; //for changing direction
int sel=1; //variable to select one of the two servos
int pos =0;
int pos1=0;// variable to store the servo position
int k=0;
int flag=1;
int sum=0;
int sam=60;
void setup() {
Serial.begin(115200); // use the serial port
TIMSK0 = 0; // turn off timer0 for lower jitter
ADCSRA = 0xe5; // set the adc to free running mode
ADMUX = 0x40; // use adc0
DIDR0 = 0x01;
pinMode(led, OUTPUT); // turn off the digital input for adc0
servo.attach(6);
servo1.attach(10);
}
void loop() {
while(1) { // reduces jitter
cli(); // UDRE interrupt slows this way down on arduino1.0
for (int i = 0 ; i < 32 ; i += 2) { // save 256 samples
while(!(ADCSRA & 0x10)); // wait for adc to be ready
ADCSRA = 0xf5; // restart adc
byte m = ADCL; // fetch adc data
byte j = ADCH;
int k = (j << 8) | m; // form into an int
k -= 0x0200; // form into a signed int
k <<= 6; // form into a 16b signed int
fft_input[i] = k; // put real data into even bins
fft_input[i+1] = 0; // set odd bins to 0
}
fft_window(); // window the data for better frequency response
fft_reorder(); // reorder the data before doing the fft
fft_run(); // process the data in the fft
fft_mag_lin8(); // take the output of the fft
sei();
//for(int i=0;i<7;i++) // print FFT results
//{
//Serial.print(fft_lin_out8[i]);
//Serial.print(" ");
//}
//Serial.println();
k++;
sum=sum+fft_lin_out8[0];
servo.write(pos); // tell servo to go to position in variable 'pos'
servo1.write(pos1);
if(k==sam)
{
k=0;
sum=sum/sam;
Serial.println(sum);
if (sum>10)
{
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
sel=-sel;
if(flag==1) // Dance sequencce
{ if(sel==1)
{
pos=pos+ (j*60);
j=(-j);
}
pos1=pos1+(l*40);
l=-l;
}
flag=0;
}
else
{
digitalWrite(led, LOW);
flag=1;
}
sum=0;
}
}
}
Here is how it is made :
The 22uF capacitor with the 10K resistor make a High Pass Filter. A high pass filter allows frequencies only above a certain value to pass. This cutoff frequency is given by f=1/2*pi*R*C.
For this arrangement we see the value 0.7 Hz. Thus , the DC component is removed from the incoming signal.
The two 10K resistors make a voltage divider, creating a bias of 2.5V. This is connected to a 1K pull up resistor and a coupling capacitor , to reduce noise. This also makes a low pass filter.
3. This is fed into the ADC. At analog pin 0 or whichever you might choose.
Sampling and Processing
1. We need to sample this input signal. Sampling mean recording amplitudes at regular intervals. This is how analog signals are read by computers. The issue is the analogRead() function only allows sampling a 8Khz which is very poor for Audio. Typical sampling rates for audio is 40Khz. However all hope is not lost. There is a way to change the ADC sampling rate by setting values for the four MUX that control the ADC.
Check this another blog post to understand how it is done :
http://yaab-arduino.blogspot.in/2015/02/fast-sampling-from-analog-input.html
2. That done , now we need to compute the FFT (Fast Fourier Transform) on the signal. What this does is that it converts the signal from time domain to frequency domain. What this means is that you can get amplitudes of different frequency ranges in your sound signal. Typical Bass frequency ranges from 50-200Hz. It is this that we are interested in. All drum kicks , snares and rhythm information is contained here. There are various optimizations of the FFT available for the arduino. The one I used is developed by Open Music Labs and can be downloaded from the link below :
http://wiki.openmusiclabs.com/wiki/ArduinoFFT
You will have to read a bit from their website to get a better grasp of using this library.
3.Now with the FFT done , we look for peaks/ increase in amplitudes in the lower frequency ranges. that is when a beat happens.Once we have that , rest is easy. Control Lights or Motors.
These might be useful :
http://dpeckett.com/beat-detection-on-the-arduino
http://www.arduinoos.com/2010/10/fast-fourier-transform-fft/
Finally here is the Video of my project in action :
The code :
#define LIN_OUT8 1 // use the Lin_OUT8 output function
#define FFT_N 16
#include <Servo.h>
#include <FFT.h> // include the library
int led=13;
Servo servo;
Servo servo1;// create servo object to control a servo
int j=1,l=1; //for changing direction
int sel=1; //variable to select one of the two servos
int pos =0;
int pos1=0;// variable to store the servo position
int k=0;
int flag=1;
int sum=0;
int sam=60;
void setup() {
Serial.begin(115200); // use the serial port
TIMSK0 = 0; // turn off timer0 for lower jitter
ADCSRA = 0xe5; // set the adc to free running mode
ADMUX = 0x40; // use adc0
DIDR0 = 0x01;
pinMode(led, OUTPUT); // turn off the digital input for adc0
servo.attach(6);
servo1.attach(10);
}
void loop() {
while(1) { // reduces jitter
cli(); // UDRE interrupt slows this way down on arduino1.0
for (int i = 0 ; i < 32 ; i += 2) { // save 256 samples
while(!(ADCSRA & 0x10)); // wait for adc to be ready
ADCSRA = 0xf5; // restart adc
byte m = ADCL; // fetch adc data
byte j = ADCH;
int k = (j << 8) | m; // form into an int
k -= 0x0200; // form into a signed int
k <<= 6; // form into a 16b signed int
fft_input[i] = k; // put real data into even bins
fft_input[i+1] = 0; // set odd bins to 0
}
fft_window(); // window the data for better frequency response
fft_reorder(); // reorder the data before doing the fft
fft_run(); // process the data in the fft
fft_mag_lin8(); // take the output of the fft
sei();
//for(int i=0;i<7;i++) // print FFT results
//{
//Serial.print(fft_lin_out8[i]);
//Serial.print(" ");
//}
//Serial.println();
k++;
sum=sum+fft_lin_out8[0];
servo.write(pos); // tell servo to go to position in variable 'pos'
servo1.write(pos1);
if(k==sam)
{
k=0;
sum=sum/sam;
Serial.println(sum);
if (sum>10)
{
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
sel=-sel;
if(flag==1) // Dance sequencce
{ if(sel==1)
{
pos=pos+ (j*60);
j=(-j);
}
pos1=pos1+(l*40);
l=-l;
}
flag=0;
}
else
{
digitalWrite(led, LOW);
flag=1;
}
sum=0;
}
}
}
No comments:
Post a Comment