sábado, 2 de junho de 2012

Entendendo e Manipulando o Código


No penúltimo post, apresentamos os códigos usados no Arduíno e no Processing para a captação de frequências, sem contúdo uma explicação mais aprofundada dos códigos, isso por se tratar do nosso primeiro teste bem sucedido com ele.

Depois disso pudemos estudar melhor o código e manipulá-lo de acordo com nosso propósito. Um detalhe importante, e já falado no post anterior, é que para que o código funcione é preciso que se adicione a biblioteca ffft na pasta libraries do Arduíno, a qual existe um link no post anterior.

A primeira coisa que descobrimos é que o código do Arduíno não funciona com qualquer versão de software, mas sim com a versão Arduíno 0022, quanto ao Processing funciona bem a comunicação com a versão mais atual: Processing 1.5.1.
Depois descobrimos que o software tem duas opções para a divisão de frêquencias: a primeira divide o espectro de aproximadamente 19200Hz por 64 faixas de 300Hz e a segunda divide o espectro de aproximadamente 9600Hz por 64 faixas de 150Hz. Essas duas opções limitam nosso campo de ação, com elas não podemos captar frequências especificas de notas musicais, mas sim intervalos nos quais estas frequências estão inseridas, pela segunda opção apresentar um intervalo menor e portanto uma definição maior optamos por ela.

É importante observar que nosso projeto sofreu portanto uma modificação de objetivo, nosso experimento não reconhecerá mais frequências de notas musicais específicas, e sim intervalos de frequências, para cada intervalo o Arduíno acionará uma luz. Teremos nesse experimento sete intervalos de frequências e portanto usaremos apenas 7 das 64 barras ou intervalos de frequências geradas pela biblioteca, essas barras começarão a captar a frequência aproximadamente de 400Hz até 1000Hz.

Para que o Arduíno identifique a frequência predominante foi preciso criar uma função que acha o intervalo de frequência de maior intensidade dentre os 7 escolhidos. Código abaixo:

int Maior(uint16_t lista[]){
int maior = 7;
for(int i=7;i<14;i++){
  if(lista[i]>lista[maior])maior=i;
  }
  if(lista[maior]<15)maior=0;
  return maior;
}

Como ainda não compramos todos os Leds, utilizamos um LED RGB que utiliza a porta 3 para vermelho, 4 para negativo, 5 para verde e 6 para Azul. E para controlá-lo, criamos uma função que recebe os três paramêtros de intersidade 0-255 para cade um:


void RGB(int r, int g, int b){
  digitalWrite(4, HIGH);
  analogWrite(3,r);
  analogWrite(5,g);
  analogWrite(6,b);
}

E outra que a utilizará, esta recebe apenas um valor de 1-7 o qual acionara uma cor pre-definida para o RGB.

void Cor(int n){
switch(n){
  case 1: RGB(255,0,0);break;
  case 2: RGB(0,255,0);break;
  case 3: RGB(0,0,255);break;
  case 4: RGB(255,255,0);break;
  case 5: RGB(255,0,255);break;
  case 6: RGB(150,255,10);break;
  case 7: RGB(255,255,255);break;
  default: RGB(0,0,0);
  }
}

Abaixo está o código comentado do Arduíno.




/*
This Example acquire analog signal from A0 of Arduino, and Serial out to Processing application to visualize.
Tested with preamplified audio data. Take a look at http://www.youtube.com/watch?v=drYWullBWcI

Analog signal is captured at 9.6 KHz, 64 spectrum bands each 150Hz which can be change from adcInit()
Load the this file to Arduio, run Processing application.

Original Fixed point FFT library is from ELM Chan, http://elm-chan.org/works/akilcd/report_e.html
Ported to the library and demo codes are from AMurchick http://arduino.cc/forum/index.php/topic,37751.0.html
Processing code is from boolscott http://boolscott.wordpress.com/2010/02/04/arduino-processing-analogue-bar-graph-2/
*/


#include <stdint.h>
#include <ffft.h>



#define  IR_AUDIO  0 // ADC canal para capturar


volatile  byte  position = 0;
volatile  long  zero = 0;

int16_t capture[FFT_N];            /* buffer capturador de onde */
complex_t bfly_buff[FFT_N];        /* FFT buffer */
uint16_t spektrum[FFT_N/2];        /* saida do espectro do buffer  */
int minimo = 10;


void RGB(int r, int g, int b){//Contralador RBG
  digitalWrite(4, HIGH);
  analogWrite(3,r);
  analogWrite(5,g);
  analogWrite(6,b);
}

void Cor(int n){//Função que define 7 cores para n de 1-7
switch(n){
  case 1: RGB(255,0,0);break;
  case 2: RGB(0,255,0);break;
  case 3: RGB(0,0,255);break;
  case 4: RGB(255,255,0);break;
  case 5: RGB(255,0,255);break;
  case 6: RGB(150,255,10);break;
  case 7: RGB(255,255,255);break;
  default: RGB(0,0,0);
  }
}

int Maior(uint16_t lista[]){//Função que define intervalo de frequência de maior intensidade
int maior = 7;
for(int i=7;i<14;i++){
  if(lista[i]>lista[maior])maior=i;
  }
  if(lista[maior]<15)maior=0;
  return maior;
}

void setup()
{
  Serial.begin(57600);
  adcInit();
  adcCalb();
  establishContact();  // manda um byte para estabelecer contato até o processing responder
  for(int i=4;i<11;i++)pinMode(i,OUTPUT);
}

void loop()
{
  if (position == FFT_N)
  {
    fft_input(capture, bfly_buff);
    fft_execute(bfly_buff);
    fft_output(bfly_buff, spektrum);

    for (byte i = 7; i < 14; i++){
      Serial.print(spektrum[i],BYTE);
    }//manda dados para o processing
   position = 0;
    int maior = Maior(spektrum);
    Cor(maior-6);//pede a função Cor() acender a cor maior-6
  }
}

void establishContact() {
 while (Serial.available() <= 0) {
      Serial.print('A', BYTE);   // manda um A maiusculo
      delay(300);
  }
}

// free running ADC fills capture buffer
ISR(ADC_vect)
{
  if (position >= FFT_N)
    return;
 
  capture[position] = ADC + zero;
  if (capture[position] == -1 || capture[position] == 1)
    capture[position] = 0;

  position++;
}
void adcInit(){
  /*  REFS0 : VCC use as a ref, IR_AUDIO : channel selection, ADEN : ADC Enable, ADSC : ADC Start, ADATE : ADC Auto Trigger Enable, ADIE : ADC Interrupt Enable,  ADPS : ADC Prescaler  */
  // free running ADC mode, f = ( 16MHz / prescaler ) / 13 cycles per conversion
  ADMUX = _BV(REFS0) | IR_AUDIO; // | _BV(ADLAR);
 //ADCSRA = _BV(ADSC) | _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1); //pre-escala 64 : 19231 Hz - 300Hz por 64 divisões
  ADCSRA = _BV(ADSC) | _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // pre-escala 128 : 9615 Hz - 150 Hz porr 64 divisões, melhor para maioria das músicas(mais definição)
  sei();
}
void adcCalb(){
  Serial.println("Start to calc zero");
  long midl = 0;
  // get 2 meashurment at 2 sec
  // on ADC input must be NO SIGNAL!!!
  for (byte i = 0; i < 2; i++)
  {
    position = 0;
    delay(100);
    midl += capture[0];
    delay(900);
  }
  zero = -midl/2;
  Serial.println("Done.");
}


Quanto ao código do processing modificamos o código de barras para 7, ao invés das 64 do post anterior, contúdo é preciso que você modifique o campo da porta, pois ela lê a porta que o arduíno está utilizando, para saber a porta que seu arduíno está utilizando vá em tools-> Serial Port. No nosso caso utilizamos a porta "COM5"

// Feel Free to edit these variables ///////////////////////////
String  xLabel = "Frequency";
String  yLabel = "Values";
String  Heading = "Arduino FFT";
String  URL = "01/02/2010";
float Vcc = 255.0;    // the measured voltage of your usb
int NumOfVertDivisions=5;      // dark gray
int NumOfVertSubDivisions=10;  // light gray


int NumOfBars=7;    // Numero de barras/ Deve ser o mesmo que o arduíno envia.
                    // since you should change what the arduino sends
                   

// if these are changed, backgroung image has problems
// a plain background solves the problem
int ScreenWidth = 800, ScreenHeight=600;
/////////////////////////////////////////////////////////

//  Serial port stuff ///////////////////////
import processing.serial.*;
Serial myPort;       
boolean firstContact = false;
int[] serialInArray = new int[NumOfBars];
int serialCount = 0;
///////////////////////////////////////////////

int LeftMargin=100;
int RightMArgin=80;
int TextGap=50;
int GraphYposition=80;
float BarPercent = 0.4;

int value;

PFont font;
PImage bg;

int temp;
float yRatio = 0.58;
int BarGap, BarWidth, DivisounsWidth;
int[] bars = new int[NumOfBars];

void setup(){

  bg = loadImage("BG.jpg");

  /// NB SETTINGS ////////////////////////////////////////////////////////
  myPort = new Serial(this,"COM5",57600);
  ////////////////////////////////////////////////////////////////////////

  DivisounsWidth = (ScreenWidth-LeftMargin-RightMArgin)/(NumOfBars);
  BarWidth = int(BarPercent*float(DivisounsWidth));
  BarGap = DivisounsWidth - BarWidth;

  size(ScreenWidth,ScreenHeight);
  font = createFont("Arial",12);

  textAlign(CENTER);
  textFont(font);
}

void draw(){

  //background(bg);     // My one used a background image, I've
  background(250);      // commented it out and put a plain colour

  //  Headings();           // Displays bar width, Bar gap or any variable.
  Axis();
  Labels();
  PrintBars();
 // Line();
//  Dots();
}




// Send Recieve data //
void serialEvent(Serial myPort) {

  // read a byte from the serial port:
  int inByte = myPort.read();

  if (firstContact == false) {
    if (inByte == 'A') {
      myPort.clear();          // clear the serial port buffer
      firstContact = true;     // you've had first contact from the microcontroller
      myPort.write('A');       // ask for more
    }
  }
  else {
    // Add the latest byte from the serial port to array:
    serialInArray[serialCount] = inByte;
    serialCount++;

    // If we have 6 bytes:
    if (serialCount > NumOfBars -1 ) {

for (int x=0;x<NumOfBars;x++){
   
  bars[x] = int (yRatio*(ScreenHeight)*(serialInArray[x]/256.0));

}


      // Send a capital A to request new sensor readings:
      myPort.write('A');
      // Reset serialCount:
      serialCount = 0;
    }
  }
}

/////// Display any variables for testing here//////////////
void Headings(){
  fill(0 );
  text("BarWidth",50,TextGap );  
  text("BarGap",250,TextGap ); 
  text("DivisounsWidth",450,TextGap );
  text(BarWidth,100,TextGap );   
  text(BarGap,300,TextGap );   
  text(DivisounsWidth,520,TextGap );
}


void PrintBars(){

  int c=0;
  for (int i=0;i<NumOfBars;i++){
  
    fill((0xe4+c),(255-bars[i]+c),(0x1a+c));
    stroke(90);
    rect(i*DivisounsWidth+LeftMargin,   ScreenHeight-GraphYposition,   BarWidth,   -bars[i]);
    fill(0x2e,0x2a,0x2a);
//    text(float(bars[i])/(yRatio*(ScreenHeight))*Vcc,   i*DivisounsWidth+LeftMargin+BarWidth/2,   ScreenHeight-bars[i]-5-GraphYposition );
//    text("A",   i*DivisounsWidth+LeftMargin+BarWidth/2 -5,   ScreenHeight-GraphYposition+20 );
//    text(i,   i*DivisounsWidth+LeftMargin+BarWidth/2 +5,   ScreenHeight-GraphYposition+20 );
  }
}

void Axis(){

  strokeWeight(1);
  stroke(220);
  for(float x=0;x<=NumOfVertSubDivisions;x++){

    int bars=(ScreenHeight-GraphYposition)-int(yRatio*(ScreenHeight)*(x/NumOfVertSubDivisions));
    line(LeftMargin-15,bars,ScreenWidth-RightMArgin-DivisounsWidth+50,bars);
  }
  strokeWeight(1);
  stroke(180);
  for(float x=0;x<=NumOfVertDivisions;x++){

    int bars=(ScreenHeight-GraphYposition)-int(yRatio*(ScreenHeight)*(x/NumOfVertDivisions));
    line(LeftMargin-15,bars,ScreenWidth-RightMArgin-DivisounsWidth+50,bars);
  }
  strokeWeight(2);
  stroke(90);
  line(LeftMargin-15, ScreenHeight-GraphYposition+2, ScreenWidth-RightMArgin-DivisounsWidth+50, ScreenHeight-GraphYposition+2);
  line(LeftMargin-15,ScreenHeight-GraphYposition+2,LeftMargin-15,GraphYposition+80);
  strokeWeight(1);
}

void Labels(){
  textFont(font,18);
  fill(50);
  rotate(radians(-90));
  text(yLabel,-ScreenHeight/2,LeftMargin-45);
  textFont(font,10);
  for(float x=0;x<=NumOfVertDivisions;x++){

    int bars=(ScreenHeight-GraphYposition)-int(yRatio*(ScreenHeight)*(x/NumOfVertDivisions));
    text(round(x),-bars,LeftMargin-20);
  }

  textFont(font,18);
  rotate(radians(90)); 
  text(xLabel,LeftMargin+(ScreenWidth-LeftMargin-RightMArgin-50)/2,ScreenHeight-GraphYposition+40);
  textFont(font,24);
  fill(50);
  text(Heading,LeftMargin+(ScreenWidth-LeftMargin-RightMArgin-50)/2,70);
  textFont(font);

  fill(150);
  text(URL,ScreenWidth-RightMArgin-40,ScreenHeight-15);
  textFont(font);

}

Abaixo estão os dois vídeos do teste:




quarta-feira, 9 de maio de 2012

Estudo de Timbre e Onda Sonora


Onda
Em física, uma onda é uma perturbação oscilante de alguma grandeza física no espaço e periódica no tempo. A oscilação espacial é caracterizada pelo comprimento de onda e o tempo decorrido para uma oscilação é medido pelo período da onda, que é o inverso da sua frequência.
Ondas podem ser descritas usando um número de variáveis, incluindo: frequência, comprimento de onda, amplitude e período.
A amplitude de uma onda é a medida da magnitude de um distúrbio em um meio durante um ciclo de onda. Por exemplo, ondas em uma corda têm sua amplitude expressada como uma distância (metros), ondas de som como pressão (pascals) e ondas eletromagnéticas como a amplitude de um campo elétrico (volts por metro). A amplitude pode ser constante (neste caso a onda é uma onda contínua), ou pode variar com tempo e/ou posição. A forma desta variação é o envelope da onda.
O período é o tempo(T) de um ciclo completo de uma oscilação de uma onda. A frequência (F) é período dividido por uma unidade de tempo (exemplo: um segundo), e é expressa em hertz.
As frequências de vibração de uma corda de violino, por exemplo, variam com o comprimento dela e com as suas características (material, tensão, espessura), que determinam a velocidade de propagação das ondas. (http://pt.wikipedia.org/wiki/Onda)
As ondas podem ser classificadas de três modos. A seguir são descritas as classificações correspondentes às ondas sonoras (http://ww2.unime.it/weblab/awardarchivio/ondulatoria/ondas.htm#Revendo os conceitos iniciais).
1-Quanto à natureza
2- Quanto à direção de propagação
3- Quanto à direção de vibração
Ondas sonoras são:
1- Mecânicas, ou seja, precisam de um meio material para se propagar (não se propagam no vácuo).
2- Tridimensionais, ou seja, se propagam em todas as direções.
3- Longitudinais, ou seja, suas vibrações coincidem com a direção de propagação.
Chamamos de som grave, aquele que é emitido por uma fonte sonora que vibra com baixa freqüência e som agudo, o que vibra com uma alta freqüência. Para entender melhor basta perceber a diferença entre a voz masculina (grave) e a voz feminina (agudo). Essa caracterização em relação à freqüência de um som é chamada de altura.
Quando um som possui uma grande quantidade de energia por unidade de tempo e a onda sonora possui uma grande amplitude, dizemos que o som possui uma grande intensidade. Falando de forma mais clara, a intensidade está relacionada ao volume do som. Essa intensidade é medida em dB (decibéis), onde se estabeleceu que ao som de menor intensidade que o ser humanos fosse capaz de escutar seria atribuído o valor de 0 dB e o de maior intensidade, de 120 dB.
Timbre
Timbre é a característica sonora que nos permite distinguir sons de uma mesma freqüência, porém emitidos por fontes sonoras conhecidas, permitindo-nos identificar o emissor do som (http://www.mundoeducacao.com.br/fisica/ondas-sonoras.htm).
Em música, chama-se timbre à característica sonora que nos permite distinguir se sons de mesma frequência foram produzidos por fontes sonoras conhecidas e que nos permite diferenciá-las. Quando ouvimos, por exemplo uma nota tocada por um piano e a mesma nota (uma nota com a mesma altura) produzida por um violino, podemos imediatamente identificar os dois sons como tendo a mesma freqüência, mas com características sonoras muito distintas. O que nos permite diferenciar os dois sons é o timbre instrumental. De forma simplificada podemos considerar que o timbre é como a impressão digital sonora de um instrumento ou a qualidade de vibração vocal.
O Lá central do piano possui a freqüência de 440Hz. A nota equivalente produzida por um violino possui a mesma freqüência. O que permite ao ouvido diferenciar os dois sons e identificar sua fonte é a forma da onda e seu envelope sonoro (http://pt.wikipedia.org/wiki/Timbre).
Forma de onda
Quando uma corda, uma membrana, um tubo ou qualquer outro objeto capaz de produzir sons entra em vibração, uma série de ondas senoidais é produzida. Além da frequência fundamental, que define a nota, várias freqüências harmônicas também soam. O primeiro harmônico de qualquer nota tem o dobro de sua freqüência; o segundo harmônico tem o triplo de sua freqüência e assim por diante. Qualquer corpo em vibração produz dezenas de freqüências harmônicas que soam simultaneamente à nota fundamental. No entanto o ouvido humano não é capaz de ouvir os harmônicos com freqüencia muito alta (maior que 20000Hz). Além disso, devido às características de cada instrumento ou da forma como a nota foi obtida, alguns dos harmônicos audíveis possuem amplitude diferente de um instrumento para outro.
Se somarmos a amplitude da freqüência fundamental às amplitudes dos harmônicos, a forma de onda resultante não é mais senoidal, mas sim uma onda irregular cheia de cristas e vales. Como a combinação exata de amplitudes depende das características de cada instrumento, suas formas de onda também são muito distintas entre si. Veja os exemplos abaixo:

Forma de onda produzida por uma flauta


Forma de onda produzida por um xilofone. Note que no início da nota (ataque), a onda possui muito mais harmônicos, que se devem à batida pela baqueta. Depois disso, a forma de onda é resultado somente da vibração da madeira

Envelope sonoro
Não é só a forma de onda que define que um som é produzido por determinado instrumento, mas também a forma como o som se inicia, se mantém e termina ao longo do tempo. Esta característica é chamada envelope sonoro ou envoltória sonora. Ainda que as formas de onda de dois instrumentos sejam muito parecidas, ainda poderíamos distingui-las pelo seu envelope. O envelope é composto basicamente de quatro momentos: Ataque, decaimento, sustentação e relaxamento(http://pt.wikipedia.org/wiki/Timbre).

A imagem acima mostra o envelope característico de três instrumentos. O primeiro é de uma tabla, espécie de tambor da Índia. Veja como o som surge quase instantaneamente após a percussão da pele pelas mãos do executante e como cada nota tem uma duração muito curta. A segunda onda mostra três notas produzidas por uma trompa. Aqui a nota se inicia com um aumento mais gradual de intensidade, sofre um pequeno decaimento após o início da nota e dura todo o tempo em que o trompista mantém o sopro, desaparecendo de forma bastante rápida ao final das notas. O terceiro exemplo mostra uma longa nota produzida por uma flauta. O som surge muito suavemente, mantém-se com amplitude quase constante e depois desaparece lentamente. Vejamos com mais detalhes cada um dos momentos presentes nestes exemplos.
Ataque: é o início de cada nota musical. Em um instrumento de corda tocado com arco, o som surge e aumenta lentamente de intensidade, assim como no exemplo da flauta. Se a mesma corda for percutida o som surgirá muito rapidamente e com intensidade alta. Dependendo do instrumento, o ataque pode durar de alguns centésimos de segundo até mais de um segundo.
Decaimento: em alguns instrumentos, após o ataque o som sofre um decaimento de intensidade antes de se estabilizar. Em um instrumento de sopro, por exemplo, isso pode se dever à força inicial necessária para colocar a palheta em vibração, após o que a força para manter a nota soando é menor. Normalmente dura apenas de alguns centésimos a menos de um décimo de segundo. Nos exemplos mostrados, o decaimento é claramente perceptível nas notas da tabla.
Sustentação: corresponde ao tempo de duração da nota musical. Na maior parte dos instrumentos este tempo pode ser controlado pelo executante. Durante este tempo a intensidade é mantida no mesmo nível, como as notas da trompa e da flauta na imagem. Alguns instrumentos (principalmente os de percussão) não permitem controlar este tempo. Em alguns casos o som nem chega a se sustentar e o decaimento inicial já leva o som diretamente ao seu desaparecimento, como na tabla.
Relaxamento: final da nota, quando a intensidade sonora diminui até desaparecer completamente. Pode ser muito brusco, como em um instrumento de sopro, quando o instrumentista corta o fluxo de ar, ou muito lento, como em um gongo ou um piano com o pedal de sustentação acionado. Na imagem acima, a nota da flauta tem um final suave devido à reverberação da sala onde a música foi executada, que fez o som permanecer ainda por um tempo, mesmo após o término do sopro.
A combinação entre os tempos de ataque, decaimento, sustentação e relaxamento é tão importante para permitir reconhecer o timbre de um instrumento, que em alguns casos usa-se um sintetizador ou sampler para alterar estes tempos e criar timbres totalmente novos a partir do som de instrumentos conhecidos (http://pt.wikipedia.org/wiki/Timbre).

terça-feira, 8 de maio de 2012

Captação de Frequência

Para fazermos a captação de frequência, usamos um código já disponível no blog http://labduino.blogspot.com.br/search/label/FFT%20com%20o%20Arduino, lá você encontrará uma pasta zipada com dois códigos e uma biblioteca e um slide mostrando sua utilização.
Além do arduino, também foi usado o programa Processing para exibir as frequências captadas:


Códigos usados para o experimento:

Processing:

// Feel Free to edit these variables ///////////////////////////
String  xLabel = "Frequency";
String  yLabel = "Values";
String  Heading = "Arduino FFT";
String  URL = "01/02/2010";
float Vcc = 255.0;    // the measured voltage of your usb 
int NumOfVertDivisions=5;      // dark gray
int NumOfVertSubDivisions=10;  // light gray

int NumOfBars=64;    // you can choose the number of bars, but it can cause issues  
                    // since you should change what the arduino sends
                    
// if these are changed, backgroung image has problems 
// a plain background solves the problem
int ScreenWidth = 800, ScreenHeight=600;
/////////////////////////////////////////////////////////

//  Serial port stuff ///////////////////////
import processing.serial.*;
Serial myPort;        
boolean firstContact = false; 
int[] serialInArray = new int[NumOfBars];
int serialCount = 0;
///////////////////////////////////////////////

int LeftMargin=100;
int RightMArgin=80;
int TextGap=50;
int GraphYposition=80; 
float BarPercent = 0.4;

int value;

PFont font;
PImage bg;

int temp;
float yRatio = 0.58;
int BarGap, BarWidth, DivisounsWidth;
int[] bars = new int[NumOfBars];

void setup(){

  bg = loadImage("BG.jpg"); 

  /// NB SETTINGS ////////////////////////////////////////////////////////
  myPort = new Serial(this, "COM10", 57600); 
  ////////////////////////////////////////////////////////////////////////

  DivisounsWidth = (ScreenWidth-LeftMargin-RightMArgin)/(NumOfBars); 
  BarWidth = int(BarPercent*float(DivisounsWidth));
  BarGap = DivisounsWidth - BarWidth;

  size(ScreenWidth,ScreenHeight);
  font = createFont("Arial",12);

  textAlign(CENTER);
  textFont(font);
}

void draw(){

//  background(bg);     // My one used a background image, I've 
  background(250);      // commented it out and put a plain colour 

  //  Headings();           // Displays bar width, Bar gap or any variable. 
  Axis();
  Labels();
  PrintBars();
//  Line();
//  Dots(); 
}

// Send Recieve data //
void serialEvent(Serial myPort) {

  // read a byte from the serial port:
  int inByte = myPort.read();

  if (firstContact == false) {
    if (inByte == 'A') { 
      myPort.clear();          // clear the serial port buffer
      firstContact = true;     // you've had first contact from the microcontroller
      myPort.write('A');       // ask for more
    } 
  } 
  else {
    // Add the latest byte from the serial port to array:
    serialInArray[serialCount] = inByte;
    serialCount++;

    // If we have 6 bytes:
    if (serialCount > NumOfBars -1 ) {

for (int x=0;x<NumOfBars;x++){
    
  bars[x] = int (yRatio*(ScreenHeight)*(serialInArray[x]/256.0));

}

      // Send a capital A to request new sensor readings:
      myPort.write('A');
      // Reset serialCount:
      serialCount = 0;
    }
  }
}

/////// Display any variables for testing here//////////////
void Headings(){
  fill(0 );
  text("BarWidth",50,TextGap );   
  text("BarGap",250,TextGap );  
  text("DivisounsWidth",450,TextGap );
  text(BarWidth,100,TextGap );    
  text(BarGap,300,TextGap );    
  text(DivisounsWidth,520,TextGap );
}

void PrintBars(){ 

  int c=0;
  for (int i=0;i<NumOfBars;i++){
   
    fill((0xe4+c),(255-bars[i]+c),(0x1a+c));
    stroke(90);
    rect(i*DivisounsWidth+LeftMargin,   ScreenHeight-GraphYposition,   BarWidth,   -bars[i]);
    fill(0x2e,0x2a,0x2a);
//    text(float(bars[i])/(yRatio*(ScreenHeight))*Vcc,   i*DivisounsWidth+LeftMargin+BarWidth/2,   ScreenHeight-bars[i]-5-GraphYposition );
//    text("A",   i*DivisounsWidth+LeftMargin+BarWidth/2 -5,   ScreenHeight-GraphYposition+20 );
//    text(i,   i*DivisounsWidth+LeftMargin+BarWidth/2 +5,   ScreenHeight-GraphYposition+20 );
  }
}

void Axis(){

  strokeWeight(1);
  stroke(220);
  for(float x=0;x<=NumOfVertSubDivisions;x++){

    int bars=(ScreenHeight-GraphYposition)-int(yRatio*(ScreenHeight)*(x/NumOfVertSubDivisions));
    line(LeftMargin-15,bars,ScreenWidth-RightMArgin-DivisounsWidth+50,bars);
  }
  strokeWeight(1);
  stroke(180);
  for(float x=0;x<=NumOfVertDivisions;x++){

    int bars=(ScreenHeight-GraphYposition)-int(yRatio*(ScreenHeight)*(x/NumOfVertDivisions));
    line(LeftMargin-15,bars,ScreenWidth-RightMArgin-DivisounsWidth+50,bars);
  }
  strokeWeight(2);
  stroke(90);
  line(LeftMargin-15, ScreenHeight-GraphYposition+2, ScreenWidth-RightMArgin-DivisounsWidth+50, ScreenHeight-GraphYposition+2);
  line(LeftMargin-15,ScreenHeight-GraphYposition+2,LeftMargin-15,GraphYposition+80);
  strokeWeight(1);
}

void Labels(){
  textFont(font,18);
  fill(50);
  rotate(radians(-90));
  text(yLabel,-ScreenHeight/2,LeftMargin-45);
  textFont(font,10);
  for(float x=0;x<=NumOfVertDivisions;x++){

    int bars=(ScreenHeight-GraphYposition)-int(yRatio*(ScreenHeight)*(x/NumOfVertDivisions));
    text(round(x),-bars,LeftMargin-20);
  }

  textFont(font,18);
  rotate(radians(90));  
  text(xLabel,LeftMargin+(ScreenWidth-LeftMargin-RightMArgin-50)/2,ScreenHeight-GraphYposition+40);
  textFont(font,24);
  fill(50);
  text(Heading,LeftMargin+(ScreenWidth-LeftMargin-RightMArgin-50)/2,70);
  textFont(font);

  fill(150);
  text(URL,ScreenWidth-RightMArgin-40,ScreenHeight-15);
  textFont(font);

}

Arduino:

/*
This Example acquire analog signal from A0 of Arduino, and Serial out to Processing application to visualize.
Tested with preamplified audio data. Take a look at http://www.youtube.com/watch?v=drYWullBWcI

Analog signal is captured at 9.6 KHz, 64 spectrum bands each 150Hz which can be change from adcInit()
Load the this file to Arduio, run Processing application.

Original Fixed point FFT library is from ELM Chan, http://elm-chan.org/works/akilcd/report_e.html
Ported to the library and demo codes are from AMurchick http://arduino.cc/forum/index.php/topic,37751.0.html
Processing code is from boolscott http://boolscott.wordpress.com/2010/02/04/arduino-processing-analogue-bar-graph-2/
*/

#include <stdint.h>
#include <ffft.h>

#define  IR_AUDIO  0 // ADC channel to capture

volatile  byte  position = 0;
volatile  long  zero = 0;

int16_t capture[FFT_N]; /* Wave captureing buffer */
complex_t bfly_buff[FFT_N]; /* FFT buffer */
uint16_t spektrum[FFT_N/2]; /* Spectrum output buffer */

void setup()
{
  Serial.begin(57600);
  adcInit();
  adcCalb();
  establishContact();  // send a byte to establish contact until Processing respon
}

void loop()
{
  if (position == FFT_N)
  {
    fft_input(capture, bfly_buff);
    fft_execute(bfly_buff);
    fft_output(bfly_buff, spektrum);

    for (byte i = 0; i < 64; i++){
      Serial.print(spektrum[i],BYTE);
    }
   position = 0;
  }
}

void establishContact() {
 while (Serial.available() <= 0) {
      Serial.print('A', BYTE);   // send a capital A
      delay(300);
  }
}

// free running ADC fills capture buffer
ISR(ADC_vect)
{
  if (position >= FFT_N)
    return;
  
  capture[position] = ADC + zero;
  if (capture[position] == -1 || capture[position] == 1)
    capture[position] = 0;

  position++;
}
void adcInit(){
  /*  REFS0 : VCC use as a ref, IR_AUDIO : channel selection, ADEN : ADC Enable, ADSC : ADC Start, ADATE : ADC Auto Trigger Enable, ADIE : ADC Interrupt Enable,  ADPS : ADC Prescaler  */
  // free running ADC mode, f = ( 16MHz / prescaler ) / 13 cycles per conversion 
  ADMUX = _BV(REFS0) | IR_AUDIO; // | _BV(ADLAR); 
//  ADCSRA = _BV(ADSC) | _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1) //prescaler 64 : 19231 Hz - 300Hz per 64 divisions
  ADCSRA = _BV(ADSC) | _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // prescaler 128 : 9615 Hz - 150 Hz per 64 divisions, better for most music
  sei();
}
void adcCalb(){
  Serial.println("Start to calc zero");
  long midl = 0;
  // get 2 meashurment at 2 sec
  // on ADC input must be NO SIGNAL!!!
  for (byte i = 0; i < 2; i++)
  {
    position = 0;
    delay(100);
    midl += capture[0];
    delay(900);
  }
  zero = -midl/2;
  Serial.println("Done.");
}




sábado, 5 de maio de 2012

Teste de captação com microfone

Teste de captação com o microfone





Neste primeiro teste, captamos os dados lidos pelo microfone com o Arduíno, que representam na verdade a tensão, lida em um intervalo decimal de 0 à 1023. A foto acima mostra o esquema do circuito e a de baixo mostra os detalhes da ligação dos fios. Onde o fio vermelho está ligado ao 5V, o preto ao terra e azul a porta analógica A0.



Depois de montado o circuito, partimos para a implementação do código, abaixo está o código e sua imagem no software Arduíno e sua respectiva saída pelo serial monitor quando falamos ao microfone.

(Código Arduíno: Teste de captação de som)

(Saída no Serial Monitor)