Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 2 of 2
  1. #1
    New to the CF scene
    Join Date
    Apr 2014
    Posts
    1
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Want to send EEG data from Processing to Max Msp using OSC

    Hey people,

    i am completely new to any kind of coding and i thought i would come to you with this question hoping that you can give me some help. I have built a EEG to measure electrical activity across the scalp. I want to get this data in max msp to turn it into a piece of music for my final project at uni. The schematic for this EEG came with software which makes sense of the signal coming into the microphone port, filters, and seperates the data into its relevant bands. I would like to find send the frequency and amplitude of each band of brainwaves (alpha, beta etc) to max msp as a number. I am having trouble finding where on the processing code i would send the data from and how i would do it, any attempt made so far has made the sketch unable to run... Any suggestions or help will be much appreciated. Attatched is the code for processing.

    /* Reading and Visualizing EEG Data
    * by Christian Henry.

    * 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 bad data
    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();
    }

  • #2
    Supreme Master coder! Philip M's Avatar
    Join Date
    Jun 2002
    Location
    London, England
    Posts
    18,243
    Thanks
    203
    Thanked 2,555 Times in 2,533 Posts
    This is the JavaScript forum. Java and Javascript are entirely different programming languages, in spite of the confusingly similar names. Rather like Austria and Australia! Ask a mod to move this thread to the right forum.

    All the code given in this post has been tested and is intended to address the question asked.
    Unless stated otherwise it is not just a demonstration.


  •  

    Tags for this Thread

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •