How to send data from Processing to Max Msp

Hi guys,

I am at a bit of a loss as to how to send a float from processing to max, i have read the various but limited instructions that i have found online but have not offered much help.

I have built an EEG from Instructables, and it came with a processing sketch which separates the data into the different brainwave categories (alpha, beta etc…). All i want to do is be able to send the data of these six bands into six different flonums in Max so i can process them. Does anyone have any ideas?

I assume it will be to enter “link.declareInlet(“xPosition”);” in the part of the code where it says “//Draw un-averaged frequency bands of signal.”

But i am new to processing and fairly new to Max and cant work out where i would put it and how i would get Max to recognise it using Maxlink.

I know im probably asking a lot, but i will be grateful for any type of feedback.

Thanks

Heres the code:

 * Reads in EEG data through the microphone input of the 
 * computer, then displays the signal in time, its frequency
 * components, and then averages of frequencies that estimate
 * the concentration of different brain waves.
 *
 * For reference, the frequency bars are ordered/classified as
 * follows:
 *
 * 1 - blue -------- delta
 * 2 - blue/purple - theta
 * 3 - purple ------ alpha 
 * 4 - purple/red -- low beta
 * 5 - dark red ---- mid beta
 * 6 - red --------- high beta
 * 
 * This sketch will measure all brain waves, from 0 - 30 Hz. It does
 * best, however, measuring alpha waves across the occipital lobe.
 * To view this function, play the program, click the window to
 * make sure its in "focus", and hit the "a" key to bandpass the alpha
 * waves only. The alpha wave bar is the 3rd one (purple), and should
 * increase in height by 2-3x when you close your eyes and relax
 * (you'll see it for a second or two after you open your eyes, before it
 * averages back down).
 * /

/* One issue: when taking the FFT of the data, it seems as if
the frequency bands have a bandwidth of 1.33 instead of 1, as 
60Hz noise peaks out at band 45. This is worked around by using
the scaleFreq parameter, which is used frequently. */

import ddf.minim.*;
import ddf.minim.signals.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;

//Important constants that may need to be changed.
float timeScale = 50; //scales the amplitude of time-domain data, can be changed
static float normalScale = 50;
static float alphaScale = 100;
static int freqAvgScale = 50; //does same for averages of frequency data
static int alphaCenter = 12;
static int alphaBandwidth = 2; //really bandwidth divided by 2
static int betaCenter = 24;
static int betaBandwidth = 2;

//Variables used to store data functions/effects.
Minim minim;
AudioInput in;
FFT fft;
NotchFilter notch;
LowPassSP lpSP;
LowPassFS lpFS;
BandPass betaFilter;
BandPass alphaFilter;

//Constants mainly used for scaling the data to readable sizes.
int windowLength = 840;
int windowHeight = 500;
int FFTheight;
float scaling[] = {.00202,.002449/2,.0075502/2,.00589,.008864,.01777};
float offset[] = {0,0,0,0,0,0};
float amplify[] = {1,1,1,1,1,1};
float maximum[] = {0,0,0,0,0,0};
float scaledMaximum[] = {0,0,0,0,0,0};
int FFTrectWidth = 6;
float scaleFreq = 1.33f;
float timeDomainAverage = 0;

//Variables used to handle bada 
int cutoffHeight = 200; //frequency height to throw out "bad data" for averaging after
float absoluteCutoff = 1.5;
boolean absoluteBadDataFlag; //data that is bad because it's way too far out of our desired range --
                             // ex: shaking your head for a second
boolean averageBadDataFlag;  //data that's bad because it spikes too far outside of the average for 
                             //that second -- 
                             // ex: blinking your eyes for a split second

//Constants used to create a running average of the data.
float[][] averages;
int averageLength = 200; //averages about the last 5 seconds worth of data
int averageBins = 6; //we have 6 types of brain waves
int counter = 0;

void setup()
{
  //initialize array of averages for running average calculation
  averages = new float[averageBins][averageLength];
  for (int i = 0; i < averageBins; i++){
    for (int j = 0; j < averageLength; j++){
      averages[i][j] = 0;
    }
  }

  //set some drawing parameters
  windowLength = 840;
  windowHeight = 500;
  FFTheight = windowHeight - 200;

  size(windowLength, windowHeight, P2D);

  //initialize minim, as well as some filters
  minim = new Minim(this);
  minim.debugOn();
  notch = new NotchFilter(60, 10, 32768);
  lpSP = new LowPassSP(40, 32768);
  lpFS = new LowPassFS(60, 32768);
  betaFilter = new BandPass(betaCenter/scaleFreq,betaBandwidth/scaleFreq,32768);
  alphaFilter = new BandPass(alphaCenter/scaleFreq,alphaBandwidth/scaleFreq,32768);

  // get a line in from Minim, default bit depth is 16
  in = minim.getLineIn(Minim.MONO, 8192*4);
  in.addEffect(notch);

  //initialize FFT
  fft = new FFT(in.bufferSize(), in.bufferSize());
  fft.window(FFT.HAMMING);
  rectMode(CORNERS);
  println(fft.getBandWidth());
}

void draw()
{
  /*badDataFlag handles any "artifacts" we may pick up while recording the data.
  Artifacts are essentially imperfections in the data recording -- they can come
  from muscle movements, blinking, anything that disturbs the electrodes. If the 
  program encounters a set of data that spikes out of a reasonable window 
  (controlled by the variable cutoffHeight), it won't consider that data
  when computing the running average.
  */
  absoluteBadDataFlag = false;
  averageBadDataFlag = false;

  fft.forward(in.mix); //compute FFT
  background(0); //make sure the background color is black
  stroke(255);   //and that time data is drawn in white

  line(0,100,windowLength,100); //line separating time and frequency data

  drawSignalData();

  //check for spikes relative to other data
  for (int i = 0; i < windowLength - 1; i++){
    if (abs(in.left.get((i+1)*round(in.bufferSize()/windowLength))) > timeDomainAverage*4)
      averageBadDataFlag = true;
  }

  displayText();

  displayFreqAverages();

  counter++;
}

//Calls function to zoom into average bars when you right click on the average bars.
//Scales depending on position of click.
void mousePressed(){
  if (mouseButton == RIGHT && mouseY > FFTheight){
    scaleAverage(floor(mouseX/(windowLength/averageBins)), windowHeight - mouseY);
  }
}

//Zooms into average bars to show small fluctuations better. New range (from original) is 
//from position clicked on to a maximum height such that the previously logged maximum fills 75% of the 
//current bar.
void scaleAverage(int bin, float pos){
  offset[bin] += pos/amplify[bin];
  println(offset[bin]);
  amplify[bin] *= 200/(1.33f*(scaledMaximum[bin] - pos));
  println(amplify[bin]);
  maximum[bin] = 0;
  scaledMaximum[bin] = 0;
}

//Hitting number keys will reset that bin of averages, hitting "`" will wipe them all.
void keyPressed(){
  if (key == '1' || key == '2' || key == '3' || key == '4' || key == '5' || key == '6'){
    char data[] = {key};
    String str1 = new String(data);
    int keyNum = Integer.parseInt(str1);
    keyNum -= 1; //shift down 1 since array is zero-indexed
    offset[keyNum] = 0;
    amplify[keyNum] *= 1;
    maximum[keyNum] = 0;
    scaledMaximum[keyNum] = 0;
  }

  if (key == '`'){
    for (int i = 0; i < offset.length; i++){
      offset[i] = 0;
      amplify[i] = 1;
      maximum[i] = 0;
      scaledMaximum[i] = 0;
    }
  }
  if (key == 'w'){
    fft.window(FFT.HAMMING);
  }
  if (key == 'e'){
    fft.window(FFT.NONE);
  }
  if (key == 'a'){
    toggleEffect(alphaFilter);
  }
  if (key == 'b'){
    toggleEffect(betaFilter);
  }
}

//Toggle effects such as notch or lowpass filter, not being used at
//the moment, though.
void toggleEffect(AudioEffect effect){
  if(in.hasEffect(effect)){
    in.removeEffect(effect);
    timeScale = normalScale;
  }
  else{
    in.addEffect(effect);
    timeScale = alphaScale;
  }
}

//Draw the signal in time and frequency.
void drawSignalData(){
  timeDomainAverage = 0;
  for(int i = 0; i < windowLength - 1; i++)
    {
      stroke(255,255,255);
      //data that fills our window is normalized to +-1, so we want to throw out
      //sets that have data that exceed this by the factor absoluteCutoff
      if (abs(in.left.get(i*round(in.bufferSize()/windowLength)))*timeScale/normalScale > .95){
          absoluteBadDataFlag = true;
          fill(250,250,250);
          stroke(150,150,150);
        }
      //Draw the time domain signal.
      line(i, 50 + in.left.get(i*round(in.bufferSize()/windowLength))*timeScale, 
           i+1, 50 + in.left.get((i+1)*round(in.bufferSize()/windowLength))*timeScale);

      timeDomainAverage += abs(in.left.get(i*round(in.bufferSize()/windowLength)));

      //Draw un-averaged frequency bands of signal.
      if (i < (windowLength - 1)/2){
        //set colors for each type of brain wave
          if (i <= round(3/scaleFreq)){             
            fill(0,0,250);        //delta
            stroke(25,0,225);
          }
          if (i >= round(4/scaleFreq) && i <= round((alphaCenter - alphaBandwidth)/scaleFreq)-1){
            fill(50,0,200);       //theta
            stroke(75,0,175);
          }
          if (i >= round((alphaCenter - alphaBandwidth)/scaleFreq) && 
          i <= round((alphaCenter + alphaBandwidth)/scaleFreq)){  
            fill(100,0,150);      //alpha
            stroke(125,0,125);
          }
          if (i >= round((alphaCenter + alphaBandwidth)/scaleFreq)+1 && 
          i <= round((betaCenter-betaBandwidth)/scaleFreq)-1){ 
            fill(150,0,100);      //low beta
            stroke(175,0,75);
          }
          if (i >= round((betaCenter - betaBandwidth)/scaleFreq) && 
          i <= round((betaCenter + betaBandwidth)/scaleFreq)){ 
            fill(200,0,50);       //midrange beta
            stroke(225,0,25);
          }
          if (i >= round((betaCenter + betaBandwidth)/scaleFreq)+1 && i <= round(30/scaleFreq)){ 
            fill(250,0,0);        //high beta
            stroke(255,0,10);
          }
          if (i >= round(32/scaleFreq)){
            fill(240,240,240);    //rest of stuff, mainly noise
            stroke(200,200,200);
          }
          if (i == round(60/scaleFreq)){
            fill(200,200,200);    //color 60 Hz a different tone of grey,
            stroke(150,150,150);  //to see how much noise is in data
          }
        //draw the actual frequency bars
        rect(FFTrectWidth*i, FFTheight, FFTrectWidth*(i+1), FFTheight - fft.getBand(i)/10);
      }
    }
  //divide the average by how many time points we have
  timeDomainAverage = timeDomainAverage / (windowLength - 1);
}

//Give user textual information on data being thrown out and filter's we have active.
void displayText(){
  //show user when data is being thrown out
  text("absoluteBadDataFlag = " + absoluteBadDataFlag, windowLength - 200, 120);
  if (absoluteBadDataFlag == true)
  {
    println("absoluteBadDataFlag = " + absoluteBadDataFlag);
    println(counter);
  }
  text("averageBadDataFlag = " + averageBadDataFlag, windowLength - 200, 140);
  if (averageBadDataFlag == true)
  {
    println("averageBadDataFlag = " + averageBadDataFlag);
    println(counter);
  }

  //and when a filter is being applied to the data
  text("alpha filter is " + in.hasEffect(alphaFilter),
    windowLength - 200, 160);
  text("beta filter is " + in.hasEffect(betaFilter),
    windowLength - 200, 180);
}

//Compute and display averages for each brain wave for the past ~5 seconds.
void displayFreqAverages(){
  //show averages of alpha, beta, etc. waves
  for (int i = 0; i < 6; i++){
    float avg = 0; //raw data for amplitude of section of frequency
    int lowFreq = 0;
    int hiFreq = 0;

    //Set custom frequency ranges to be averaged. 
    if(i == 0){
      lowFreq = 0;
      hiFreq = 3;
      fill(0,0,250);
      stroke(25,0,225);
    }
    if(i == 1){
      lowFreq = 3;
      hiFreq = 7;
      fill(50,0,200);
      stroke(75,0,175);
    }
    if(i == 2){
      lowFreq = alphaCenter - alphaBandwidth;
      hiFreq = alphaCenter + alphaBandwidth;
      fill(100,0,150);
      stroke(125,0,125);
    }
    if(i == 3){
      lowFreq = 12;
      hiFreq = 15;
      fill(150,0,100);
      stroke(175,0,75);
    }
    if(i == 4){
      lowFreq = betaCenter - betaBandwidth;
      hiFreq = betaCenter + betaBandwidth;
      fill(200,0,50);
      stroke(225,0,25);
    }
    if(i == 5){
      lowFreq = 20;
      hiFreq = 30;
      fill(250,0,0);
      stroke(255,0,10);
    }

    //Convert frequencies we want to the actual FFT bands. Because of our
    //FFT parameters, these happen to be equal (each band has a 1 Hz width).
    int lowBound = fft.freqToIndex(lowFreq);
    int hiBound = fft.freqToIndex(hiFreq);

    //Scale the band number, because of the issue outlined at very beginning of
    //program.
    lowBound = round(lowBound/scaleFreq);
    hiBound = round(hiBound/scaleFreq);

    //get average for frequencies in range
    for (int j = lowBound; j <= hiBound; j++){
      avg += fft.getBand(j);
      }
    avg /= (hiBound - lowBound + 1);

    // Scale the bars so that it fits our window a little better.
    for (int k = 0; k < 6; k++)
    {
      if (i == k)
      {
        avg *= scaling[i]*freqAvgScale;
      }
    }

    //update our array for the moving average (only if our data is "good")
    if (absoluteBadDataFlag == false && averageBadDataFlag == false){
      averages[i][counter%averageLength] = avg;
    }

    //calculate the running average for each frequency range
    float sum = 0;
    for (int k = 0; k < averageLength; k++){
      sum += averages[i][k];
    }
    sum = sum / averageLength;

    //draw averaged/smoothed frequency ranges
    rect(i*width/6, height, (i+1)*width/6, height - (sum-offset[i])*amplify[i]);
  }
}

// always close Minim audio classes when you are done with them
void stop()
{
  in.close();
  minim.stop();
  super.stop();
}

Answers

Sign In or Register to comment.