Temperaments vs. Overtones
June 6, 2009
I’m building a string instrument that makes music with the strings’ harmonic tones as well as their fundamentals. And, for hard-to-explain reasons, it’s going to have more than 12 fundamental tones per octave. So I needed to figure out which tuning system has the best overlap between its equally-tempered fundamentals and their respective overtone sequences. This is exactly the kind of massive combinatorics problem I don’t know how to solve in my head. So I wrote some software to help me visualize it.
It’s written in SVG and Javascript. Firefox: definitely. IE: definitely not. I haven’t yet checked if Safari and Chrome support these features.
http://web.media.mit.edu/~hellyeah/crunch.svg
The + and – buttons* change the number of notes in the temperament. Each circle in the top row is a note in the selected equal temperament. Each row is an overtone series.
Hover over a note to see some information about it. Click on any note to see the distribution of notes that closely match simple musical intervals. Lighter green indicates a better match.
Enjoy!
* If clicking the buttons doesn’t seem to be working, try clicking their edges. And be aware that it takes a second or 2 to re-render the whole grid.
Super Lisa Bufano
June 6, 2009
Lisa Bufano is a superhero. One with fancy attachments! She spent a long weekend running all over NYC’s streets, bridges, and trains in her crazy cheetah legs, turning heads and fighting crime. And I tracked and documented her on my camera bike. It’s for the first of a series of video podcasts she’s producing about exploring her unique body.

Your best chance of keeping up with Lisa is through her website: http://www.lbufano.com
This video is poorly shot and from before she studied dance seriously. But I love it anyway.
Moving the Busycle
June 6, 2009
This was fun. One day Matthew Mazzotta asked me to help move the Busycle from an ancient warehouse in the Navy Yards to Cave Dave’s workshop/home in South Boston.
What is the Busycle? You’ll just have to see:
That thing is *so* not even hardly street legal. So we were concerned about police as well as high-speed mechanical mayhem. But the St. Patrick’s Day Parade had just happened in South Boston that day, which apparently changed the rules. The police seemed to think we were just some part of the celebration on our way home. So we somehow got a pass as long as they thought we were all drunk revelers rather than sober engineers and artists.
The Whirly-Bot
June 6, 2009
The Whirly-Bot is my favorite of Ensemble Robots’ musical bots. It’s 10 feet tall, 8 feet wide, and shakes like a turbine engine trying to lift off. It’s been said to sound like a chorus of angry angels or “kinda like sniffing a whole fistful of magic markers”.
It uses 7 layers of spinning corrugated plastic tubes to create a fully chromatic range of notes. Well, sort of. The 7 fundamental tones are tuned in 12-tone equal temperament. All other tones are created by spinning the tubes faster to create overtones. So all other tones are in the overtone series. It sounds quite unlike anything else.
The concept and design are mine. Its motor control system was designed and built by William Tremblay and its tubes were created, tuned and tweaked by Erik Nugent.

It was assembled in a raging week of all-nighters. Additional help came from Alicia Volpicelli, Peter Ford, and Emily Levin. It debuted at the Wired Magazine NextFest in NYC in 2006.
Make Bike Film Now
June 2, 2009
Have you ever wanted to capture the rare sense of grace and speed you only get from city biking?
I’ve been experimenting with my bike-cam. It’s so easy and fun I want everyone to do it! All together now – Just put a digital camera on a cheap tripod, and use some zip ties to strap the tripod and bicycle frame together in various compromising positions.
What you see here is a compilation of some sweet camera tests. This summer I hope to make some completely bicycle-based films. Maybe even a musical. Everyone on bikes: actors, cameras, crew. Daredevil traffic situations, breathless dialog, and a nice bicycle kiss at the end. So stay tuned.
iTunesCaster
June 2, 2009
Okay. This may be illegal. Or maybe not. Just don’t use it for illegal filesharing and I’m probably fine.
The iTunesCaster 0.1 turns your iTunes playlists into podcasts that can be easily shared with friends or the general public. Think of it as a nice way to make podcasts, not a way to share MP3s with your friends.
Its usage is still a little geeky at this point. You’ll need iTunes (a-doy), Apache HTTP server, and a newish Python interpreter. Macs will have all of that. Windows users will need to install all 3. For Linux users, are there any open-source music players that use the same XML library format as iTunes? If you know of one, drop me a line.
You don’t need to have those 3 programs on the same computer, so long as they can see each other through the filesystem.
Here is the code:
"""
++++++++++ iTunesCaster 0.1 [super alpha] ++++++++++
This script turns your iTunes playlists into podcasts.
Usage:
1. Create a playlist for a friend in iTunes
2. Run this script
3. Give friend the new podcast URL (see Setup below)
3a. Bask in gushing praise as your friend easily downloads selected files from your computer.
3b. Take a moment to reflect that this is totally illegal if done wrong.
Requirements:
1. Python 2.4.x or newer
2. mod_python 3.2 or newer
3. Apache HTTPD server 2.0.x or newer
4. iTunes 6 or newer
Setup:
0. first off, iTunes, Apache, and your music library must be visible to one another. They can be on the same machine or on separate network shares.
1. Update the values in the conf_d dictionary below.
set iTunesLib_filePath_str to the file path for your "iTunes Music Library.xml" file
set iTunesDir_filePath_str to the file path for your iTunes Music Folder
set TracksDir_urlPath_str to the URL path mapped to your iTunes Music Folder in Apache's httpd.conf file (see my httpd.conf example below)
set podcastDir_filePath_str to the file path where this script will write the podcast files. This path should be available via the Web. (see my httpd.conf example below)
2. run script, see new podcasts (XML files) appear in the folder named by podcastDir_filePath_str
3. Test it. Copy a podcast URL and subscribe to it in iTunes. If you can't download the files, check your paths in conf_d, httpd.conf, and in the podcast.
+++++++++ PERTINENT PART OF MY HTTPD.CONF +++++++++
# this is what makes this file work on my site. Note the correlation between the directories here and those in the script's configuration dictionary ("conf_d")
DocumentRoot C:server/websites/podcast.nervebox.com
ServerName podcast.nervebox.com
CustomLog logs/podcast_nervebox_com.log combined
DefaultType text/html
Options Indexes MultiViews
IndexIgnore . ..
AllowOverride None
Order allow,deny
Allow from all
AddHandler mod_python .py
PythonHandler server
PythonPath "[ (Put your own Python Path vals in here) ]"
PythonDebug On
Alias /mp3s "C:server/data/music"
Options Indexes MultiViews
IndexIgnore . ..
Order allow,deny
Allow from all
"""
import sys, os
from mod_python import apache
from xml.dom import minidom
def handler(req):
global tracks_d
global playlists_l
global req_global
global debugOut_str
global conf_d
req_global = req
debugOut_str = ""
conf_d = {
"iTunesLib_filePath_str":"C:Documents and SettingsAdministratorMy DocumentsMy MusiciTunesiTunes Music Library.xml",
"iTunesDir_filePath_str":"file://localhost/C:/server/data/music/",
"TracksDir_urlPath_str":"http://podcast.nervebox.com/mp3s/",
"podcastDir_filePath_str":"C:server/websites/podcast.nervebox.com/podcasts"
}
req.content_type="text/plain"
iTunesLib().parseMusicLibrary()
podcast().scanPlaylists()
req.write(debugOut_str)
return apache.OK
class iTunesLib:
def __init__(self):
global conf_d
self.XMLPath_str = conf_d["iTunesLib_filePath_str"]
self._tracks_d = {}
self._playlists_l = []
def parseMusicLibrary(self):
global tracks_d
global playlists_l
fields_l = ["Track ID","Name","Artist","Total Time","Date Modified","Location"]
itml_doc = minidom.parse(self.XMLPath_str)
plist_node = itml_doc.childNodes.item(1).childNodes.item(1)
for ni in range(plist_node.childNodes.length):
if plist_node.childNodes.item(ni).nodeName == "dict": # filter for the 1 dict (tracks) in plist
tracks_node = plist_node.childNodes.item(ni)
if plist_node.childNodes.item(ni).nodeName == "array": # filter for the 1 array (playlists) in plist
playlists_node = plist_node.childNodes.item(ni)
# get tracks
trackNodes_coll = tracks_node.getElementsByTagName("dict") # collect references to all track defs in an HTMLCollection
for ti in range(trackNodes_coll.length): # loop through all tracks
thisTrackData_coll = trackNodes_coll[ti].childNodes # reference to this track node
for ttdi in range(thisTrackData_coll.length): # loop through properties of each track
if thisTrackData_coll[ttdi].nodeName == "key":
if fields_l.count(thisTrackData_coll[ttdi].firstChild.data)>0:
if thisTrackData_coll[ttdi].firstChild.data== "Track ID": # should will first property of the track data. so we can set this here
trackID_str = str(thisTrackData_coll[ttdi+1].firstChild.data)
self._tracks_d[trackID_str] = {"Track ID":"", "Name":"", "Artist":"", "Total Time":"", "Date Modified":"", "Location":""}
try:
self._tracks_d[trackID_str][str(thisTrackData_coll[ttdi].firstChild.data)] = str(thisTrackData_coll[ttdi+1].firstChild.data)
except:
self._tracks_d[trackID_str][str(thisTrackData_coll[ttdi].firstChild.data)] = "bugginess: characters out of ascii range"
tracks_d = self._tracks_d
# get playlists
playlistNodes_coll = playlists_node.childNodes
for pni in range(playlistNodes_coll.length):
if playlistNodes_coll[pni].nodeName == "dict": # filter out textNodes
tempPlaylist_d = {"name":"","trackIDs":[]}
thisPlaylist_coll = playlistNodes_coll[pni].childNodes
for ppi in range(thisPlaylist_coll.length):
if thisPlaylist_coll[ppi].nodeName == "key" and thisPlaylist_coll[ppi].firstChild.data == "Name":
tempPlaylist_d["name"] = str(thisPlaylist_coll[ppi+1].firstChild.data)
if thisPlaylist_coll[ppi].nodeName == "array":
dicts_l = thisPlaylist_coll[ppi].getElementsByTagName("dict")
for di in range(dicts_l.length): # loop through the dicts in the playlist array
integer_node = dicts_l[di].getElementsByTagName("integer")[0]
tempPlaylist_d["trackIDs"].append(str(integer_node.firstChild.data))
self._playlists_l.append(tempPlaylist_d)
playlists_l = self._playlists_l
class podcast:
def __init__(self):
global conf_d
self.TracksDir_urlPath_str = conf_d["TracksDir_urlPath_str"]
self.iTunesDir_filePath_str = conf_d["iTunesDir_filePath_str"]
self.podcastDir_filePath_str = conf_d["podcastDir_filePath_str"]
def scanPlaylists(self):
global playlists_l
for pli in range(len(playlists_l)): # loop through playlists, making a podcast for each
self.makeFile(playlists_l[pli])
def makeFile(self, playlist_ref):
global debugOut_str
global tracks_d
impl = minidom.getDOMImplementation()# make podcast doc object
podcast_doc = impl.createDocument(None, "rss", None)
rss_node = podcast_doc.getElementsByTagName("rss").item(0)
self.setAttribute(rss_node, "xmlns:itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd")
self.setAttribute(rss_node, "version", "2.0")
# populate podcast doc object
channel_node = podcast_doc.createElement("channel")
rss_node.appendChild(channel_node)
self.addSimpleTag(podcast_doc,channel_node,"title",playlist_ref["name"])
self.addSimpleTag(podcast_doc,channel_node,"link","http://www.nervebox.com")
self.addSimpleTag(podcast_doc,channel_node,"description","Nervebox Experimental iTunes-podcast Bridge")
self.addSimpleTag(podcast_doc,channel_node,"itunes:summary","Nervebox Experimental iTunes-podcast Bridge")
self.addSimpleTag(podcast_doc,channel_node,"itunes:subtitle","Nervebox Experimental iTunes-podcast Bridge")
self.addSimpleTag(podcast_doc,channel_node,"language","en-us")
self.addSimpleTag(podcast_doc,channel_node,"lastBuildDate","Fri, 9 Dec 2005 09:00:00 EST") # date should be dynamic: date of script execution
self.addSimpleTag(podcast_doc,channel_node,"itunes:author","Nervebox Studio")
# add podcast items
for ii in range(len(playlist_ref["trackIDs"])):
if tracks_d.has_key(playlist_ref["trackIDs"][ii]): # omit playlist items that are not found among tracks
thisItem_node = podcast_doc.createElement("item")
thisTrack_l = tracks_d[playlist_ref["trackIDs"][ii]]
location_str = thisTrack_l["Location"].replace(self.iTunesDir_filePath_str,self.TracksDir_urlPath_str)
self.addSimpleTag(podcast_doc,thisItem_node,"title",thisTrack_l["Name"] + "-" + thisTrack_l["Artist"])
self.addSimpleTag(podcast_doc,thisItem_node,"pubDate",thisTrack_l["Date Modified"])
self.addSimpleTag(podcast_doc,thisItem_node,"itunes:author",thisTrack_l["Artist"])
self.addSimpleTag(podcast_doc,thisItem_node,"guid",location_str)
enclosure_node = self.addSimpleTag(podcast_doc,thisItem_node,"enclosure")
self.setAttribute(enclosure_node, "url", location_str)
self.setAttribute(enclosure_node, "length", "100000") # of course this is not the correct length. works fine anyway.
self.setAttribute(enclosure_node, "type", "audio/x-mp3")
channel_node.appendChild(thisItem_node)
# serialize podcast doc object to XML(!)
debugOut_str = debugOut_str + podcast_doc.toxml("utf-8") + "
"
# write XML to filesystem
podcastFilePath_str = self.podcastDir_filePath_str + playlist_ref["name"] + ".xml"
podcast_file = file(podcastFilePath_str, 'w')
podcast_file.write(podcast_doc.toxml("utf-8"))
podcast_file.close()
podcast_doc.unlink()
def setAttribute(self,target_node,name_str,val_str):
document = minidom.Document()# make podcast doc object
_att= document.createAttribute(name_str);
_att.value = val_str
target_node.attributes.setNamedItem(_att)
def addSimpleTag(self,doc_ref,parent_node,tagName_str,content_str=False):
newTag_node = doc_ref.createElement(tagName_str)
if content_str:
t_node = doc_ref.createTextNode(content_str)
newTag_node.appendChild(t_node)
parent_node.appendChild(newTag_node)
return newTag_node
Future development: (feel free to jump in here)
- Faster XML parser! It currently takes over a minute to parse my 4000-track iTunes library.
- Easy installers for Windows and MacOS
- On Windows, uses Python’s HTTP server if none is found.
- A fancy new desktop interface:
- shows user’s current IP address and URLs for the podcasts.
- allows user to select which playlists to convert to podcasts.
- runs as desktop program (with Tkinter) instead of requiring Apache and mod_python
Some of the dorkier among you might believe that this is a perfect place to use XSLT. Au Contraire! It’s true that this program simply transforms one flavor of XML file into a set of other-flavored XML files. But the logic required seems beyond any XSLT-fu that I possess. If you’d like to give it a hack, go right ahead. Tell me how it went.
Tallbike Rodeo
June 1, 2009

The Boston/Cambridge area has a preponderance of artists, engineers, and just plain freaks who build things. What happens if you put ‘em all in a pot and stir ‘em up? I wanted to find out. And a Tallbike Rodeo seemed like a perfect juncture of art, engineering, and daredevil badass-ness.
The idea lounged around in the back of my head for a while. But it became a reality when Lisa Monrose from the Museum of Science took it on as a project. She got people working and even wrangled some grant money and sponsorship.
What we didn’t know was that all the tallbike freaks already knew each other through SCUL. And there was a lot of suspicion among them that we were trying to co-opt their home-grown culture to promote some corporate agenda. Heavens forefend!

Eventually we met with Fleet Admiral Skunk of SCUL who turned out to be an awesome and genuinely sweet guy. He saw that we were just working to promote creativity and ingenuity through bicycle culture. And he could totally get behind that. So we had Skunk to thank for the ten tallbikes and riders who showed up for the big day!

So anyway …. races, obstacles, and a demolition derby — all on tallbikes. What could possibly go wrong? Enjoy the photos. Many were taken by Monica Parker-James.
Beelzabuggy!
June 1, 2009
The Beelzabuggy was my first Web robot back in 1999. I’m not sure it’s relevant here. But you never forget your first.

It has a camera and microphone and was controlled by a Web server. So visitors to the site could drive it around and explore Nervebox Studio from a cat’s-eye view.
It was my first project with Java, threads, sockets, serial ports, and microcontroller programming. And it handed me my own butt every day until it was finally done.
I eventually retired the little guy because of short battery life, privacy concerns, and just getting caught up in other projects. I might be willing to give him somebody who wanted to give him some geeky lovin’ and let him run free again.
NerveMail
June 1, 2009
I just want to claim dibs: I may have written the very first rich and useful AJAX application.

Back in 2001, years before GMail, or Firefox, or even the coining of the term AJAX, I built a webmail system that looked and *worked* like a desktop client. There were no pages, just a client program (in JavaScript) that managed a rich GUI and dynamically loaded data and libraries. It had some interesting features like server push and an error reporting system stored JavaScript exception data in cookies so errors could be reported in the event of a crash. It was also a pretty mature mail client.

It worked more of less the same way Google Docs or other application-in-a-browser systems work. Except this was 2001 and the only browser capable of handling it was Mozilla 0.9x.
After endless tweaking I finally shared this in 2002:
http://www.mozillazine.org/talkback.html?article=2716
What seems obvious now – that rich clients are as good an idea on the Web as they are on the desktop – took more than a paragraph to explain in 2002. And in 2002, at the bottom of the crash, that last thing anyone wanted to hear about was “a new technology that was going to revolutionize the Web”.

So, while AOL offered me a job developing it for them in Mountain View, I never directly turned it into money. There certainly weren’t any companies looking out for this technology they didn’t yet know existed. The release of Google’s GMail had the simultaneous effect of making everybody get it and also making NerveMail far less relevant.
I’ve extended that codebase and still use it to build great things that can’t be built with Dojo or EXT. And I believe I was there before anyone. But I never got any props or cash. So I used to be a little touchy about it.
Just staking my claim.









