Sunday, March 3, 2013

Comparing stored data

So last time when we left off we had brought serial data into supercollider from an arduino and then stored it in an ever evolving array.  Also, we noted that when that array had a zero in its last index, it was in alignment with the analog sensor placement--meaning index 0 represented the value found at analog sensor 0.

Here is the SC code for that:


(

SerialPort.listDevices;
~myPort = SerialPort("/dev/tty.usbserial-A600dSr9", baudrate: 9600, crtscts: true);
~myPort.close
SerialPort.closeAll;

~serialReceive.play;
~serialReceive.stop;

~pieceBegins = thisThread.seconds; //assign the start of the piece


~serialReceive = Task({  
       var serialArraySize = 7; 
       var serialArray = Array.newClear(serialArraySize); 
                inf.do({
                       if(~myPort.notNil, { 
                           ~incData = ~myPort.next; 
                          if(~incData.notNil, { 
                              ~incData.postln;
                              serialArray = serialArray.shift(-1); 
                              serialArray[serialArraySize - 1] = ~incData;  
                              if(serialArray[serialArraySize - 1]==0, {   
                                  serialArray.postln; 
                         });
                   });
           });
      0.1.wait;
     });
});

)


The next step is to store completed serial arrays in order to compare data from  consecutive arrays.  When I began working with these sensors I noticed that they can sometimes be quite sensitive and so I thought I would compare the average of three initial sensor values to the average of the subsequent three values in order to determine whether or not there was a breath.  The biggest problem with this approach was that it introduced a bit of a delay into the system so that breaths were not recognized in real time.  And so while I am still storing values from the six 'most recent' arrays, I am no longer taking the time to find the average values of the sensors for comparison.

Storing arrays in a 2D array:

The first step in storing arrays is to capture the relevant data in new array.  Here I fill a new array called ~newVal with the six values from the serialArray, and then post it:


~newValArray.fill(6, { arg i; serialArray[i]});
 "newVal is ".postln;
 ~newVal.postln;

In order to avoid nil related errors, first I check to make sure that the serialArray is notNil, so the complete code looks like this:

if((serialArray[0]).notNil && (serialArray[1]).notNil && (serialArray[2]).notNil && (serialArray[3]).notNil, { 


~newValArray.fill(6, { arg i; serialArray[i]});
 "newVal is ".postln;
 ~newVal.postln;


});

The next step is to store the next array that comes in.  In order to do this I essentially made a bucket brigade which passes the ~newVal array up the line until it eventually becomes ~oldVal5.  In the meantime, new arrays are filling up the chain.  So it ends up looking like this:


~curTime = thisThread.seconds - ~pieceBegins

if(~curTime > 5.0, { 

     ~oldVal5 = Array.newClear(6);  
     ~oldVal5 = ~oldVal4;

     ~oldVal4 = Array.newClear(6);
     ~oldVal4 = ~oldVal3;

     ~oldVal3 = Array.newClear(6);
     ~oldVal3 = ~oldVal2;

     ~oldVal2 = Array.newClear(6);
     ~oldVal2 = ~oldVal1;

     ~oldVal1 = Array.newClear(6);
     ~oldVal1 = ~oldVal

     if(~newVal.notNil, {
         ~oldVal = Array.newClear(6);
         ~oldVal = ~newVal
       });

     if((serialArray[0]).notNil 
        && (serialArray[1]).notNil 
        && (serialArray[2]).notNil 
        && (serialArray[3]).notNil, { 

           ~newVal= Array.fill(6, { arg i; serialArray[i]});
           "newVal is ".postln;
           ~newVal.postln;
});


Note that it's a bit counterintuitive...everything moves from the bottom newVal up to the top oldVal5.

Finally,  all of these values are stored in a 2D array:


~array = [~oldVal5, ~oldVal4, ~oldVal3, ~oldVal2, ~oldVal1, ~oldVal, ~newVal];

Again, the main purpose of storing so many arrays (actually 7, total) was so that I could compare the average sensor values to avoid reading in a lot of 'noise'.  After I discovered that this was creating an awkward delay, instead of rewriting all of the code that dealt with the averages, I put a temporary bandaid on the situation by swapping out the arrays in my 2D array:


~array = [~oldVal, ~oldVal, ~oldVal, ~newVal, ~newVal, ~newVal, ~newVal];

This way when I go to take the 'averages' I can do it right away because the arrays stored are really only the most recent arrays.  

Here I get the 'averages' (again, will most likely be getting rid of this step):

//gets averages of 3 values for each sensor, and rounds

//get the average of the first sensor value in the OLDEST three arrays
if((~array[0][0]).notNil 
   && (~array[1][0]).notNil 
   && (~array[2][0]).notNil, { //avoid nil-related errors
           ~sensorOLD0
                 [~array[0][0], ~array[1][0], ~array[2][0]].mean; 
                 //round that number to whole number
                  ~sensorOLD0 = ~sensorOLD0.round; 
                 //and post it
                  ("sensorOLD0 is " ++ ~sensorOLD0).postln;
});

//and again...
if((~array[0][1]).notNil && (~array[1][1]).notNil && (~array[2][1]).notNil, {
~sensorOLD1 = [~array[0][1], ~array[1][1], ~array[2][1]].mean; 
~sensorOLD1 = ~sensorOLD1.round; 
("sensorOLD1 is " ++ ~sensorOLD1).postln;
});

if((~array[0][2]).notNil && (~array[1][2]).notNil && (~array[2][2]).notNil, {
~sensorOLD2 = [~array[0][2], ~array[1][2], ~array[2][2]].mean; 
~sensorOLD2 = ~sensorOLD2.round; 
//("sensorOLD2 is " ++ ~sensorOLD2).postln;
});

if((~array[0][3]).notNil && (~array[1][3]).notNil && (~array[2][3]).notNil, {
~sensorOLD3 = [~array[0][3], ~array[1][3], ~array[2][3]].mean;
~sensorOLD3 = ~sensorOLD3.round; 
//("sensorOLD3 is " ++ ~sensorOLD3).postln;
});

if((~array[0][4]).notNil && (~array[1][4]).notNil && (~array[2][4]).notNil, {
~sensorOLD4 = [~array[0][4], ~array[1][4], ~array[2][4]].mean; 
~sensorOLD4 = ~sensorOLD4.round; 
//("sensorOLD4 is " ++ ~sensorOLD4).postln;
});

if((~array[0][5]).notNil && (~array[1][5]).notNil && (~array[2][5]).notNil, {
~sensorOLD5 = [~array[0][5], ~array[1][5], ~array[2][5]].mean; 
~sensorOLD5 = ~sensorOLD5.round; 
//("sensorOLD5 is " ++ ~sensorOLD5).postln;
});



 //get the average of the first sensor value in the NEWEST three arrays
if((~array[3][0]).notNil 
      && (~array[4][0]).notNil 
      && (~array[5][0]).notNil, { 
             ~sensorNEW0 = [~array[3][0], ~array[4][0], ~array[5][0]].mean;
             //round that number to whole number
             ~sensorNEW0 = ~sensorNEW0.round;
             //and post it
             ("sensorNEW0 is " ++ ~sensorNEW0).postln;
});

//...and again
if((~array[3][1]).notNil && (~array[4][1]).notNil && (~array[5][1]).notNil, {
~sensorNEW1 = [~array[3][1], ~array[4][1], ~array[5][1]].mean;
~sensorNEW1 = ~sensorNEW1.round;
//("sensorNEW1 is " ++ ~sensorNEW1).postln;
});

if((~array[3][2]).notNil && (~array[4][2]).notNil && (~array[5][2]).notNil, {
~sensorNEW2 = [~array[3][2], ~array[4][2], ~array[5][2]].mean;
~sensorNEW2 = ~sensorNEW2.round;
//("sensorNEW2 is " ++ ~sensorNEW2).postln;
});

if((~array[3][3]).notNil && (~array[4][3]).notNil && (~array[5][3]).notNil, {
~sensorNEW3 = [~array[3][3], ~array[4][3], ~array[5][3]].mean;
~sensorNEW3 = ~sensorNEW3.round;
//("sensorNEW3 is " ++ ~sensorNEW3).postln;
});

if((~array[3][4]).notNil && (~array[4][4]).notNil && (~array[5][4]).notNil, {
~sensorNEW4 = [~array[3][4], ~array[4][4], ~array[5][4]].mean;
~sensorNEW4 = ~sensorNEW4.round;
//("sensorNEW4 is " ++ ~sensorNEW4).postln;
});

if((~array[3][5]).notNil && (~array[4][5]).notNil && (~array[5][5]).notNil, {
~sensorNEW5 = [~array[3][5], ~array[4][5], ~array[5][5]].mean;
~sensorNEW5 = ~sensorNEW5.round;
//("sensorNEW5 is " ++ ~sensorNEW5).postln;
});



I then take the information about the newest and oldest values and place that in a series of short, two-index arrays:

if((~sensorOLD0.notNil) && (~sensorNEW0.notNil), { //make an array of 2 values, for comparison's sake: the old (averaged & rounded) value and the new (averaged & rounded) value
~compareSensor0 = [~sensorOLD0, ~sensorNEW0];
//("compare sensor 0 = " ++ ~compareSensor0).postln;
});

if((~sensorOLD1.notNil) && (~sensorNEW1.notNil), {//do this for each stretch sensor
~compareSensor1 = [~sensorOLD1, ~sensorNEW1];
//("compare sensor 1 = " ++ ~compareSensor1).postln;
});

if((~sensorOLD2.notNil) && (~sensorNEW2.notNil), {
~compareSensor2 = [~sensorOLD2, ~sensorNEW2];
//("compare sensor 2 = " ++ ~compareSensor2).postln;
});

if((~sensorOLD3.notNil) && (~sensorNEW3.notNil), {
~compareSensor3 = [~sensorOLD3, ~sensorNEW3];
//("compare sensor 3 = " ++ ~compareSensor3).postln;
});

if((~sensorOLD4.notNil) && (~sensorNEW4.notNil), {
~compareSensor4 = [~sensorOLD4, ~sensorNEW4];
//("compare sensor 4 = " ++ ~compareSensor4).postln;
});

if((~sensorOLD5.notNil) && (~sensorNEW5.notNil), {
~compareSensor5 = [~sensorOLD5, ~sensorNEW5];
//("compare sensor 5 = " ++ ~compareSensor5).postln;
});


Finally, for each of these small arrays, I test to see whether the new value is greater than, less than or equal to the old value (indicating, respectively, an inspiration, expiration or no change.

if(~compareSensor0[0] < ~compareSensor0[1], { //if the old value is less than the new value
   ~count0 = 1; //return a 1 to indicate an inspiration
   //subtract the old value from the new value in the array to see depth:
   ~depthAtZeroInspiration = ~compareSensor0[1] - ~compareSensor0[0]; 
   //"the depth of the inspiration at sensor 0 is ".post;
   //~depthAtZeroInspiration.postln;

}, { 

//if the old value is NOT less than the new value, check to see if the old value is > the new value.

if(~compareSensor0[0] > ~compareSensor0[1], {//if the old value is greater than the new value
    ~count0 = -1;//return -1 to show an exhalation
    //depth of exhalation. Note that this will return a NEGATIVE NUMBER:
    ~depthAtZeroExhalation = ~compareSensor0[1] - ~compareSensor0[0]; 
    //"the depth of the exhalation at sensor 0 is ".post;
    //~depthAtZeroExhalation.postln;

}, {

//otherwise, if the sensors show no change, return a zero to indicate no 
      ~count0 = 0;
      ~depthAtZeroInspiration = 0; breath
      ~depthAtZeroExhalation = 0;
      //"depth at zero = 0".postln});

      }); 
});





As previously mentioned: the code immediately above is repeated for each ~compareSensor array, #0-5, and is part of an infinite loop reading in and testing these values.  Whether or not there is a breath is determined in a separate loop that considers the data coming in from all of the sensors, collectively.  I'll save that for my next post...

Thursday, February 28, 2013

Storing data in arrays

Yesterday I posted about how my respiratory-musical interface gathers information from the analog sensors (sent via the lilypad) and read in through supercollider.

Today I will post how these values are stored for comparison.

For starters, recall that this code read the serial values in from the lilypad:

(

~serialReceive = Task({  //create a new task to get serial data

   inf.do({
        if(~myPort.notNil, { //if the serial port is notNil
                ~incData = ~myPort.next; //then assign the new incoming serial value to incData
                if(~incData.notNil, { //if the values coming in are notNil
                    ~incData.postln;//post them

                    });
          });
         0.1.wait;
      });
});

)

This next code adds on to this basic template.  First we need to add variables (inside the task, but outside the inf loop) for the array where we will store the data:


//6 sensors...1 zero 'message' to say that the array is over
var serialArraySize = 7; 


//make a new array (with space for serialArraySize number of values) for incoming serial data
var serialArray = Array.newClear(serialArraySize); 



Next, inside the inf loop, we will need to tell the serial array to constantly shift values to the left so that new values can replace the old as they are read in:


//make the serial array constantly shift to receive new data
serialArray = serialArray.shift(-1); 


And then we will need to tell the array what values it should storing:
//put the new values into the serial array
serialArray[serialArraySize - 1] = ~incData


Finally, we need to figure out when the analog values coming into the array are in the right position, so that the value for sensor 0 is in index 0 and sensor 1 is in index 1 and so on.  Remember we did this by making the array have one extra index that we fill with a message (a zero) that we can then use to tell our code that the array is complete.  

if(serialArray[serialArraySize - 1]==0, { 
       serialArray.postln; //post the serial array
    });




And we'll stop here to see this altogether:




(

~serialReceive = Task({  //create a new task to get serial data
 
        var serialArraySize = 7; 
        var serialArray = Array.newClear(serialArraySize); 

               inf.do({
                        if(~myPort.notNil, { //if the serial port is notNil
                                ~incData = ~myPort.next; //then assign the new incoming serial value to incData
                                 if(~incData.notNil, { //if the values coming in are notNil
                                        ~incData.postln;//post them

                                        //make the serial array constantly shift to receive new data
                                         serialArray = serialArray.shift(-1);
                                         //put the new values into the serial array
                                         serialArray[serialArraySize - 1] = ~incData;  

                                                 if(serialArray[serialArraySize - 1]==0, { 
                                                         //if sc receives the 0, the flag or message coming in from the arduino             
                                                         //and it is placed at the end of the serial array,
                         
                                                         serialArray.postln; //post the serial array

                           });
                 });
            0.1.wait;
           });
    });

)

The next step is to store these completed arrays as they come in so that it's possible to compare old sensor values with new ones to tell if there has been a breath.  Note that the time between an old value and a new one is *very* small--I've been setting it at tenth of a second or less.  

Wednesday, February 27, 2013

Breathing in Banff


For the past seven weeks I have been working on a new version of the Respiratory-Musical Interface in Banff, AB, as part of the Winter Creative Residency.  Somewhat belatedly, I wanted to return to posting updates about the project.  There is quite a lot to catch up with so I will try to post a very short code excerpt every day from now until I leave, rather than trying to get EVERYTHING into a single post.

*Apologies for the lack of formatting in the code...blogger seems to make that disappear when I copy and paste.  I'll look for a solution when I have a chance!

Anyway, for starters, here is the LilyPad Arduino code:


int message = 0;
int numSensors = 6; //number of analog sensors used
int sensorVal;
int sensorIn = 0;

void setup()
{
  Serial.begin(9600);
}

void loop()
{

  for(int a=0; a<numSensors; a++) //loop through sensors 0 through 5
  {
    sensorVal = analogRead(a); //read sensor values at a
Serial.write(sensorVal/4); //IMPORTANT - USE Serial.write NOT serial.print or SC will have a bit of a FIT
  }

  Serial.write(message); //send message so SC can distinguish between serial values
// delay(175);
 delay(500);
}



The 'message' variable is there to help the SC side of the equation distinguish between the values coming in for the various pins from the LilyPad.  When it gets the message (a zero), it knows that the subsequent value it receives will be from analog pin 0.  (After receiving the zero it puts the next 7 values into an array in which the last index is the 0 message.)


Now, moving to SuperCollider, the most important part of the code below is assigned to the ~myPort variable, which tells SC where to find the serial values. SerialPort.listDevices justs lets you find the name of the proper device.



SerialPort.listDevices;
~myPort = SerialPort("/dev/tty.usbserial-A600dSr9", baudrate: 9600, crtscts: true);
~myPort.close
SerialPort.closeAll;

~pieceBegins = thisThread.seconds; //assign the start of the piece



After assigning the start of the piece, the next step is to actually read in some values from the analog sensors. The code below reads in data from the serialPort, ~myPort, assuming it is notNil, and then posts it.




(

~serialReceive = Task({  //create a new task to get serial data

   inf.do({
        if(~myPort.notNil, { //if the serial port is notNil
                ~incData = ~myPort.next; //then assign the new incoming serial value to incData
                if(~incData.notNil, { //if the values coming in are notNil
                    ~incData.postln;//post them

                    });
          });
         0.1.wait;
      });
});

)

~serialReceive.play;
~serialReceive.stop;

Tomorrow I will post the code that captures this data in an array, and perhaps the code that enables a comparison between old and new values from the sensors.