Two mentions of Afflator

Posted on 2011-04-05 18:43:53

Thanks to Wylie Schwartz and Jo-Anne Green for their recent mentions of Afflator!


Afflator Video

Posted on 2011-03-31 07:25:48

Afflator from Nick Knouf on Vimeo.


Revealing Afflator

Posted on 2011-03-28 17:45:02

Open from 8AM to 5PM in the Experimental Gallery in Tjaden Hall until 1 April 2011. Reception on Thursday, 31 March 2011, from 5PM til 7PM.


Afflator almost there...

Posted on 2011-03-27 08:00:33

… with basic motor response to the voice and modes to grab your attention. (Nothing like doing things at the last minute.) “Mapping” is a difficult topic in and of itself, and Afflator is the first step towards building a framework that can be applicable across a number of different contexts. For much more on this issue, see Marc Downie’s excellent PhD dissertation, as well as his other work with The Open Ended Group.


How to Make 4-Bar Linkages Without a Mill, Laser Cutter, Bandsaw, Clamps, or a Drill Press...

Posted on 2011-03-24 22:38:54

… but you do have a drill, a jigsaw, and an exacto knife.

  1. Buy basswood of at least 1/4” thick.
  2. Use jigsaw to cut pieces to length cross-grain.
  3. Use exacto knife to cut pieces to width with-grain. This will not get you a perfect edge, and there will be some variations in width, but it’s better than cutting a finger off with your jigsaw as you try and cut it straight when your stock is only 3” wide.
  4. Use drill to make holes. Go slowly so that you don’t split the link.


Hand Moldable Plastic

Posted on 2011-03-24 07:09:11

Working with the hand moldable plastic that I’m using for the extrusions on the linkages.


Servo Control in Python

Posted on 2011-03-24 06:45:21

The class below does a basic job of controlling servo motors using the Pololu Micro Maestro servo controller board.

import serial
class PololuMicroMaestro(object):
    def __init__(self, port= "/dev/ttyACM0"):
        self.ser = serial.Serial(port = port)
    def setAngle(self, channel, angle):
        """Set the target angle of the servo.  This is converted into "quarter microseconds", i.e., the pulse width necessary to get to that angle (and thus it's between 1.0ms and 2.0ms in increments of 0.25us).  Whew!"""
        minAngle = 0.0
        maxAngle = 180.0
        # these numbers, in quarter microseconds, taken from the code here:
        minTarget = 256.0
        maxTarget = 13120.0
        scaledValue = int((angle / ((maxAngle - minAngle) / (maxTarget - minTarget))) + minTarget)
        commandByte = chr(0x84)
        channelByte = chr(channel)
        lowTargetByte = chr(scaledValue & 0x7F)
        highTargetByte = chr((scaledValue >> 7) & 0x7F)
        command = commandByte + channelByte + lowTargetByte + highTargetByte
    def setSpeed(self, channel, speed):
        """Set the speed of the given channel.  Speed is given in units of 0.25us / 10ms.  This means there is a range from 1 to 40000.  Getting a handle of what this _actually_ means in practice, in terms of the visual speed of the motors, will take a bit of work."""
        commandByte = chr(0x87)
        channelByte = chr(channel)
        lowTargetByte = chr(speed & 0x7F)
        highTargetByte = chr((speed >> 7) & 0x7F)
        command = commandByte + channelByte + lowTargetByte + highTargetByte
    def setAcceleration(self, channel, accel):
        """Set the acceleration of this channel.  Value should be between 1 and 255.  A setting of 0 removes the acceleration limit."""
        commandByte = chr(0x89)
        channelByte = chr(channel)
        lowTargetByte = chr(accel)
        highTargetByte = chr(0x00)
        command = commandByte + channelByte + lowTargetByte + highTargetByte
    def getPosition(self, channel):
        """Get the position of this servo.  Returned in units of us."""
        commandByte = chr(0x90)
        channelByte = chr(channel)
        command = commandByte + channelByte
        lowByte = ord(
        highByte = ord(
        highByte = highByte << 8
        position = highByte + lowByte
        return (position / 4.0)
    def goHome(self):
        """Set all servos to home position."""
    def close(self):


Mask construction

Posted on 2011-03-23 05:39:51

Some photos of the mask construction along with the tube, minus the necessary snaps to hold it shut.


Machine "Listening" in Supercollider

Posted on 2011-03-22 16:42:34

The code below calculates the MFCCs, spectral centroid, and spectral flatness for an input signal and sends the results over OSC. There’s one problem with it as written, which is that the MFCCs are not sent (or calculated) the first time you run the code, but if you stop it and start it again, they are calculated and sent properly. Still trying to figure that one out.

 s = Server.local.boot;
 // Setup address and buffer
 n = NetAddr("", 57130);
 b = Buffer.alloc(s, 1024, 1);
 // Setup control buses
 ~mfccBus = Bus.alloc(\control, s, 13);
 ~specCentroidBus = Bus.alloc(\control, s, 1);
 ~specFlatnessBus = Bus.alloc(\control, s, 1);
 SynthDef("listenSynth", {|name, buffer = 0, inputChannel = 0, specCentroidBusName = nil, specFlatnessBusName = nil, mfccBusName = nil|
     var in, fft, specCentroid, specFlatness, mfcc;
     in =;
     fft = FFT(buffer, in);
     specCentroid =;
     specFlatness =;
     mfcc =;
     // Output computed values to buses, mfcc);, specCentroid);, specFlatness);
 // Routine that sends the values over OSC
 // Note the "*" in the code for the MFCCs; this is needed to "flatten" the array into a series of numbers that Supercollider likes.
 r ={{
             n.sendMsg("/specCentroid", val);
             n.sendMsg("/specFlatness", val);
         ~mfccBus.getn(13, {|val| {n.sendMsg("/mfcc", *val);}.defer});
 // Create the synth
 ~listenSynth =\listenSynth, [\name, "creature", \buffer, b, \specCentroidBusName, ~specCentroidBus, \specFlatnessBusName, ~specFlatnessBus, \mfccBusName, ~mfccBus]);
 // Clear everything out


Creating an Ubuntu image for the Beagleboard

Posted on 2011-03-18 03:35:24

A wise man told me to install Ubuntu on the beagleboard, so I’m giving that a shot to see if I can fix the problems with jack that I’m experiencing in Angstrom. Follow the manual instructions using RootStock. I used the following command-line to create the image; if you’re on a fast ethernet connection, this should take no longer than 10-15 minutes to download and build:

sudo rootstock --fqdn omap --login ubuntu --password temppwd --imagesize 2G --seed build-essential,curl,wget,nano,linux-firmware,wireless-tools,usbutils --dist maverick --serial ttyO2 --components "main universe multiverse" --kernel-image

Change the “seed” line to include other packages as desired; I just installed them once the image was created. Follow the instructions on the wiki page to format your microsd card and make your images. Copy them as instructed to the card, unpack the image, and boot the beagleboard. You should be in. Notes on building supercollider shortly, if necessary.

Update: Unlike in Angstrom, in Ubuntu the built-in ethernet on the xM is available on interface usb1 and not usb0. This took longer than necessary to figure out. (See this thread )