SuperCollider: A Class at LA Public School

Sunday October 16 16:21

I’m taking a class through the LA chapter of The Public School on the SuperCollider, like..application, I guess it is. It’s more of a programming environment for making and processing sound. Good fun stuff. I really want to invest more time and energy towards the audio project and this seemed like a good way to start on that goal.

Ezra Buchla is teaching it.

SuperCollider is a rather terse programming environment with a kinda curious set up that requires services/servers to actually run the programs you write — or that are interpreted. I’m making assumptions that this was done so as to allow a distributed model of processing when things get hairy or maybe its just a fetish of the network and its possibilities for elastically distributing processing. In any case, what I’m most interested in is being able to real-time process sound and a little less interested in generative sound synthesis.

I never thought (just without even checking) at how rich and sort of — overwhelmingly weedy the SuperCollider API was. I mean — there’s tons there. I wish that some embedded hardware-y stuff could actually consume/interpret it somehow so that I could have really portable sound processing capabilities. What I’d like is a way to do process sound but I don’t want to have to do it on a laptop or something — should be able to do it in something the size of a 1/4″ stereo audio jack or something.

Sunday October 16 16:26

Sunday October 16 14:19

But, to contradict myself, I may in fact be also somewhat interested in generative sound synthesis — making sounds with things, objects and algorithms. It’s on the @2012 list of things to be some kind of music maker of some sort and meeting up with Henry Newton-Dunn (who made BlockJam a precursor and prior art of Siftables/Sifteo by a good 6 or 7 years) at the AIGA Pivot conference a couple of days ago was fortuitous because I remembered that he was a DJ back there in Tokyo. We had some excited conversations about sound and audio and DJ’ng and software to do all that. I feel a collaboration in the near future!

In fact, here’s Henry himself — this is probably when we first met in, like..2005 in Tokyo. Some hepcat spot. Check out that Hi-Fi “Set” in the background!

Henry

LIS302DL. A 3 Axis Accelerometer

Tuesday July 14, 13.12.21

Ooooh. Those code jockeys in the Laboratory have been mucking about in the ol’ locker room, giving each other rat-tails, chucking firecrackers in the halls and having a good horsin’ around. Smoking cigarettes and drinking cheap booze. Everything. Stink bombs in the girl’s room. Whatever. It’s a regular Lord of the Flies fest on the electronics wing of the Near Future Laboratory. And, look what we found! Some pole dancin’ hardware porn! Step right up! Don’t crowd..

Wednesday July 15, 13.04.33

In reverent honor of my friends and chums who are holding forth with Sketching in Hardware 09 in London and for the solemn sadness I have for not being able to participate this year, I hereby drop some code and hardware science up on this piece of blog with a dozen or so lines of Arduinoness meant to articulate and instrumentalize the wonderful ST Micro LIS302DL 3 axis accelerometer, delivered here via the Sparkfun breakout board. Without further ado, but with plenty of firmware nakedness, here’s the sketch..*slug* this one’s for you, Sketchers..*sob*

#include

// TWI (I2C) sketch to communicate with the LIS302DL accelerometer
// Using the Wire library (created by Nicholas Zambetti)
// http://wiring.org.co/reference/libraries/Wire/index.html
// On the Arduino board, Analog In 4 is SDA, Analog In 5 is SCL
// These correspond to pin 27 (PC4/ADC4/SDA) and pin 28 (PC5/ADC5/SCL) on the Atmega8 and Atmega168
// The Wire class handles the TWI transactions, abstracting the nitty-gritty to make
// prototyping easy.

// We've got two accelerometers connected. You configure the address of each one
// by some wiring
int address_1 = 0x1C; // SDO on the LIS302DL connected to GND makes it at I2C address 0x1C
int address_2 = 0x1D; // SDO/MISO on the LIS302DL connected to VCC makes it at I2C address 0x1D
void setup()
{
  // we'll use the serial port to spit out our data
  Serial.begin(9600);

  byte tmp;

  Wire.begin(); // join i2c bus (address optional for master)


  // Read from the "WHO_AM_I" register of the LIS302DL and see if it is at
  // the expected address.
  // If it is not, spit out a useful message on the serial port. We'll get
  // erroneous data from querying this address, though.
  Wire.beginTransmission(address_1);
  Wire.send(0x0F);
  Wire.endTransmission();
  Wire.requestFrom(address_1,1);
  while(Wire.available()) {
   tmp = Wire.receive();
   if(tmp != 0x3B) {
     Serial.print("Problem! Can't find device at address "); Serial.println(address_1, HEX);
     delay(1000);
   } else {
  // configure the device's CTRL_REG1 register to initialize it
  Wire.beginTransmission(address_1);
  Wire.send(0x20); // CTRL_REG1 (20h)
  Wire.send(B01000111); // Nominal data rate, Device in active mode, +/- 2g scale, self-tests disabled, all axis's enabled
  Wire.endTransmission();
   }
  }


  // Read from the "WHO_AM_I" register of the second LIS302DL and see if it is at
  // the expected address.
  // If it is not, spit out a useful message on the serial port. We'll get
  // erroneous data from querying this address, though.
  Wire.beginTransmission(address_2);
  Wire.send(0x0F);
  Wire.endTransmission();
  Wire.requestFrom(address_2,1);
  while(Wire.available()) {
   tmp = Wire.receive();
   if(tmp != 0x3B) {
     Serial.print("Problem! Can't find device at address "); Serial.println(address_2, HEX);
     delay(1000);
   } else {

  // configure the device's CTRL_REG1 register to initialize it
  Wire.beginTransmission(address_2);
  Wire.send(0x20); // CTRL_REG1 (20h)
  Wire.send(B01000111); // Nominal data rate, Device in active mode, +/- 2g scale, self-tests disabled, all axis's enabled
  Wire.endTransmission();
   }
  }
}

void loop()
{
  Serial.print("1:"); read_acceleration(address_1);
  Serial.print("2:"); read_acceleration(address_2);
}

void read_acceleration(int address) {
  byte z_val_l, z_val_h, x_val_l, x_val_h, y_val_l, y_val_h;
  int z_val, x_val, y_val;
  Wire.beginTransmission(address);
 // Now do a transfer reading one byte from the LIS302DL
 // This data will be the contents of register 0x29, which is OUT_X
  Wire.send(0x29);
  Wire.endTransmission();
 Wire.requestFrom(address, 1);
  while(Wire.available())
 {
   x_val = Wire.receive();
 }
 // This data will be the contents of register 0x2B which is OUT_Y
 Wire.beginTransmission(address);
 Wire.send(0x2B);
 Wire.endTransmission();
 Wire.requestFrom(address, 1);
 while(Wire.available())
 {
    y_val = Wire.receive();
 }

 // This data will be the contents of register 0x2D, which is OUT_Z
 Wire.beginTransmission(address);
 Wire.send(0x2D);
 Wire.endTransmission();
 Wire.requestFrom(address, 1);
 while(Wire.available())
 {
    z_val = Wire.receive();
 }

 // I want values that run from {-X to 0} and {0 to +X}, so a little bit math goes on here...
 if(bit_is_set(x_val, 7)) {
   x_val &= B01111111;
   x_val = -1*x_val + 128;
   x_val *= -1;
 }

if(bit_is_set(y_val, 7)) {
   y_val &= B01111111;
   y_val = 128 - y_val;
   y_val *= -1;
 }

 if(bit_is_set(z_val, 7)) {
   z_val &= B01111111;
   z_val = 128 - z_val;
   z_val *= -1;
 }

 Serial.print(x_val); Serial.print(":");Serial.print(y_val); Serial.print(":"); Serial.println(z_val);
}

Tuesday July 14, 13.09.59

Wednesday July 15, 14.37.44

What’s going on here? Well, straightforward silliness and hardware gafflin’. Two accelerometers on the I2C bus just cause. It looks like this is as many as you can have on there, unless you do some shenanigans or create a second bus with some cleverness.

The hardware spec on the device (Which. You. Should. Read.) explains that, if we’re going to talk to these things using the I2C bus we need to wire CS to the high end of the logic rail, so VCC. This tells the device that we’ll be using I2C protocol. Then we need to address the device. If we connect the MISO (master in, slave out) pin to GND, then the address will be 0x1C. If we connect the MISO pin to VCC, then the address will be 0x1D. So, MISO, in the I2C configuration, controls the least significant bit of the address. This way, without further mucking about, we can have two accelerometers on the bus, which is probably one more than most situations demand, but just in case.

If I were to connect more than two, I would probably go ahead and use the three-wire protocol and have one microcontroller pin per accelerometer dedicated for chip-select (CS). Fortunately, this device supports three-wire protocols, or the SPI protocol.

Tuesday July 14, 13.17.11

The Arduino code example above does some simple preambling — initializing the two devices after making sure they are there. Then it just loops forever, reading accelerometer data from each of the three axes of each one, doing a little simple bitwise arithmetic to make the data from a negative value for negative g (upside down in most situations) to positive g (right side up, in most situations). The initialization stage sets the accelerometer range — that is, the max/min values it will read — to +/- 2g. (The device will support +/- 8g according to the specifications.)

There are some cool additional features that I don’t play with, including some interrupts that can be triggered if the device falls suddenly, or if it is “clicked/tapped” or “double-clicked/double-tapped”, which is kinda cool, I guess. If you can come up with a non-gratuitous scenario. Which is probably harder than it sounds. But, even in your gratuitous-I-double-click-my-glass-of-Porto-to-signal-the-waiter-I-need-more-Porto the device will save you the hassle of trying to do this sort of interaction semantics in firmware and get you back to finishing what you were doing in the first place.

Why do I blog this? Notes on the integration of hardware to firmware to ideas. This time with a “new” accelerometer that has some pretty neat features. After this, we’ll be going to paper-pulp and line drawings for a bit folks.

GPX to DXF – Drawing GPS Tracks

Sunday March 29, 13.25.20

Lines and arrows and splines point the way. Taken in a parking lot near the beaches at Santa Monica.

It has not been a quiet couple of days here in the Laboratory. Lots of gear and glassware about. Goggles, bunsen burners and all that sort of thing. And the report draft was just finished with Nicolas Nova, which occupied many early mornings. We almost spilled an organic, but toxic material on the draft which cause a collective gasp, but it was pulled out of the way, just in time before irrevocable damage was done. We’re writing with ink and pen these days, which feels so much more angelic and respectful, but are, through incidents like this, reminded that such may have nostalgic integrity, but it is also quite delicate and precious.

In the midst of all that was the need to translate a GPS track from GPX format to DXF. This was harder than I thought it would be, at least after poking around the Google. There are some tools that’ll do format conversions and so forth, but they were way more expensive than I thought was reasonable, seeing as we’re not making precious objects. It’s basically a translation of one connected graph format to another.

Okay, so — it was time to think about making our own tools, which took 10 times less time than the original set of Google searches, mostly because of the recently discovered ancient treasure of a Java library our buddy Tom Carden wrote back in the Precambian of the Age of the Network — somewhere’s around 1996 or some such…

This library provides enough functionality to read a GPX track and draw it on screen. The Processing.org DXF library can spit that drawing from the screen out as a saved DXF. So, that basically solves the problem. The DXF files that come out are flat lines that can then be serviced by other software to do other things. (There also exists this Kabeja library for consuming DXF and creating DOM models, which we’ll save for another day.

My lead toward the Carden code was found here on the Processing.org forums, where I found enough of a simple code snippet to get me out and through the chiseled hole in the brick wall I had hit.

Also to note is the small comment in the small simple bit of code that can cobble together many separate GPX files (tracks from a GPS) into one larger one, which can be quite convenient.

Why do I blog this? Mostly for my own recollection and notes as to how things are done. It’s been enough time in the jungle of small, utility challenges that, when on another project inevitably in the future, some small task I need to perform smells familiar — but, why? One gets the feeling — I’ve had to do this before? What project was it? How did I do it? Playing in the geo/map-making/cartography space has all these little formats and translation steps that are a bit zany to wrangle. Jotting a post with a bit of a reminder helps. Te bigger “why do I blog this?” has to do with using real-world GPS tracks as a basis for constructing other things — the input is movement in the world, an effort to figure out how a map might look that inverted the assumptions about static geographies and fluid movement, so that the ground moved and the things that moved became static. *shrug*


import processing.dxf.*;

// Based on Tom Carden's code and GPX library available at
// http://www.processing.org/hacks/hacks:gpx
// Press "R" and your track gets saved as a DXF file which
// you can use in lots of other things..

import tomc.gpx.*;

GPX gpx;

GPXTrack track;
GPXTrackSeg trackSeg;
String trackName;

double minLat, maxLat;
double minLon, maxLon;
double minEle, maxEle;
boolean record = false;

final static int SEPARATOR = 200;
String filename, filepath;

// I collapse lots of individual GPX files into one larger file
// using the free gpsbabel.
// At the command prompt (the GUI editions don't have enough features
// to do this) you'll do something like this:

/************

/Applications/GPSBabel+-1.3.5/gpsbabel -i gpx -f 20090321.gpx 
-f 20090401.gpx -f 20090402.gpx -f 20090403.gpx 
  -x transform,wpt=trk,del 
  -x radius,distance=5,lat=34.0236,lon=-118.4319,nosort 
  -x transform,trk=wpt,del  
  -o gpx -F foo.gpx

The "-x" filters do a couple of things.
The first -x filter turns the tracks into waypoints to work around
an issue that gpsbabel has with the "radius" filter
The second -x filters the output only to points that are within a
5 mile radius of the specified lat/lon, which is useful if you want
to limit the range of data you draw.
The final -x filter turns the waypoints back into tracks, which is what we want
Finally, we output as GPX formatted data and
write the whole thing to the file called foo.gpx.dxf


*************/

void setup()
{
  // Yep, hardcoded path to the GPX file we'll process
  filepath = "/Users/julian/Desktop/GPS Tracks/foo.gpx";
  // We'll use the name of the file for our DXF output, with the ".dxf" extension added
  filename = (new File(filepath)).getName();
  size(800, 800, P2D);
  gpx = new GPX(this);

  // you can load a file or a URL evidently..
  gpx.parse(filepath);

  // Find scope of track file so we can scale our drawing
  minLat = 2000; minLon = 2000; minEle = 100000;
  maxLon = -1000; maxLat = -1000;

  println("track count "+gpx.getTrackCount());
  for(int j=0; j < gpx.getTrackCount(); j++) {
  track = gpx.getTrack(j);
  println("track size "+track.size());
  for(int k=0; k<track.size(); k++) {
     trackSeg = track.getTrackSeg(k);
     println("track seg size "+trackSeg.size());
  for (int i = 0; i < trackSeg.size(); i++)
  {

    GPXPoint pt = trackSeg.getPoint(i);
    if (pt.lat < minLat)
    {
      minLat = pt.lat;
    }
    if (pt.lon < minLon)
    {
      minLon = pt.lon;
    }
    if (pt.ele  maxLat)
    {
      maxLat = pt.lat;
    }
    if (pt.lon > maxLon)
    {
      maxLon = pt.lon;
    }
    if (pt.ele > maxEle)
    {
      maxEle = pt.ele;
    }
  }
  }
  }
println("Lat: " + minLat + " to " + maxLat);
println("Lon: " + minLon + " to " + maxLon);
println("Ele: " + minEle + " to " + maxEle);
}

boolean hasDrawn = false;

void draw()
{
  if(record == true) {
    beginRaw(DXF, filename+".dxf");
    hasDrawn = false;
  }
  if(hasDrawn == false) {
  background(255);
  //stroke(#FF0000);
  //line(0, SEPARATOR, width, SEPARATOR);

  double distance = 0;

 for(int j=0; j < gpx.getTrackCount(); j++) {
  track = gpx.getTrack(j);
  //println("track size "+track.size());
  for(int k=0; k<track.size(); k++) {
     trackSeg = track.getTrackSeg(k);
     //println("track seg size "+trackSeg.size());
       GPXPoint prevPt = trackSeg.getPoint(0);
      PVector prevPos = GetPosition(prevPt);
      for (int i = 1; i < trackSeg.size(); i++)
      {
       GPXPoint pt = trackSeg.getPoint(i);

    // Show track
    PVector pos = GetPosition(pt);
    stroke(#FF8800);
    line(prevPos.x, prevPos.y, pos.x, pos.y);
    prevPos = pos;
  }
  }
 }
  }
  if(record == true) {
    endRaw();
    record = false; // stop recording to the file
    println("done writing "+filename+".dxf");
  }
  hasDrawn = true;
}

void keyPressed() {
  if (key == 'R' || key == 'r') {
    record = true;
  }
}

PVector GetElevation(int n, GPXPoint pt)
{
  return new PVector(
      map(n, 0, trackSeg.size(), 10, width - 10),
      map((float) pt.ele, (float) minEle, (float) maxEle, SEPARATOR - 10, 10)
  );
}

PVector GetPosition(GPXPoint pt)
{
  return new PVector(
      map((float) pt.lon, (float) minLon, (float) maxLon, 10, width - 10),
      map((float) pt.lat, (float) minLat, (float) maxLat, SEPARATOR + 10, height - 10)
  );
}