Personalised Jpegs for emailed wedding invitations

Let’s say you want to invite a bunch of people to your wedding and your soon-to-be-spouse wants some nice graphic you can mail to folks that has their name in it. You can do this! This script works with Linux and should work with OS X. It will require a few changes to work with windows.

Install some nifty software

I’m using both imagemagick and OptiPNG, both of which I got through apt-get. Probably, the png step is overkill and you could go straight to jpeg.
I’m also using Inkscape, Python and LibreOffice, but you can use any spreadsheet you want.

Make the graphic template

  1. Find some nice border of some kind. I drew one and scanned it, but there are other options.
  2. Open your nice border in inkscape (or other svg editor).
  3. Put all the text you want into your graphic, with the font you want.
  4. In the part where you want their name, put GUEST_NAME
  5. Save your lovely creation as template.svg

Compile names and email addresses

In LibreOffice, open a new spreadsheet and make a bunch of entries for your guests.
The first column should be email addresses.
If you are NOT saving the images, then the next column can all say ‘invite’ for every single entry. If you want to save the jpegs (say to post them to facebook walls as well as emailing them), then give them a short name associated with the person. This will be a file name, so it should be all one word that starts with a letter and contains only letters, numbers and underscores.
The last column should be the name you want to appear in the invite. You know from making your template how much space you have for names, so keep that in mind, if you’re deciding to put in Reverend Doctor Julius Milliband Cameron III’s full name or not. Or you may need to go back and tweak your template.
Your speadsheet should look something like this:

foo@example.com jen Jennifer
bar@example.com ralph Ralph & Morris
dr_rev@example.com ju Dr Cameron

Save this spreadsheet as guests.csv

The script

Get ready

Save your template, your spreadsheet and the script (cut and past from below), all to the same folder on your system. Call the script doemail.py
You will need to modify this script a bit.
Put in your own text where you see the part that says YOUR OWN TEXT. You’ll see it asks for your own text twice. One of those times is plain text. The other one is HTML. the plain text one is just text. Don’t include any html tags. If you put in links, you just have to put in the link as plain text. In the HTML part, you can use a lot of markup, including <a href=”blah.com”>blahblah</a> tags and whatnot. You can do inline CSS, if you like, but it’s email, so keep it relatively simple. In both sections, don’t forget to include a link to your website. And if you are using an online form for RSVPs, link to that as well.
You’ll also need to put in your own email address, and your own password. If you are using gmail, you can use an application-specific password.
Finally, you will also need to put in the smpt server for your mail server. Near the top of the file, you’ll see lines for hotmail and gmail. Delete any that don’t apply to you. If you are using a different server, you’ll have to find out what to put there.
If you want to save the nice jpegs, say to also post them in facebook messages, then look for the line:

jpegname = "/tmp/"+name+".jpeg"

and change the ‘/tmp/’ part to another directory on your system.
You need to make the script executable. Open the terminal application and cd to the directory with the script, template, and spreadsheet. Type:

 chmod +x doemail.py

This will make the script executable as a program you can run. then, double check your spreadsheet is ok. Type:

less guests.csv

It should look like:

foo@example.com,jen,Jennifer
bar@example.com,ralph,Ralph & Morris
dr_rev@example.com,ju,Dr Cameron

If you see semicolons instead of commas, then you need to change your script to tell it that. Change

guestreader = csv.reader(csvfile)

to

guestreader = csv.reader(csvfile, delimiter=';')

To run the script, still from your terminal, type:

 ./doemail.py

The Actual script to cut and paste

#! /usr/bin/python

import smtplib
import re
import subprocess
import os
import cgi
import uuid
import csv

from subprocess import call
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text      import MIMEText
from email.mime.image     import MIMEImage
from email.header         import Header    



# me == my email address
me = "example@hotmail.co.uk"
password = "yourEmailPassword"
smpt_server = "smtp.live.com:587" #hotmail
smpt_server = "smtp.gmail.com:587" #gmail

with open('guests.csv') as csvfile:
    guestreader = csv.reader(csvfile)
    for row in guestreader:
        you = row[0]
        name = row[1]
        salutation = row[2]

        name = ''.join(name.split())



        text = "Dear " + salutation + ",nn  YOUR TEXT GOES HERE http://YOURWEBSITE.COM"


        html = """
<html>
  <head></head>
  <body>
    <p>
<a href="YOUR WEDDING WEBSITE">
        """
        html = html + "<img src="cid:{}@example.com" alt="[More Information]" /></a></p>n<p>Dear {},</p>".format(img['cid'], salutation)
        html = html + """
<p>YOUR TEXT GOES HERE</p>
</body></html>
    </p>
  </body>
</html>
        """


        print (salutation)

        # Create message container - the correct MIME type is multipart/alternative.
        msg = MIMEMultipart('related')
        text_msg = MIMEMultipart('alternative')
        msg['Subject'] = Header(u'Wedding Invitation', 'utf-8')
        msg['From'] = me
        msg['To'] = you



        # make the image

        filename = "/tmp/"+name+".svg"
        pngname = "/tmp/"+name+".png"
        jpegname = "/tmp/"+name+".jpeg"

        svg = open("template.svg");
        svgn = open(filename, 'w');
        lines = svg.read().split('n')
        for line in lines:
            line = re.sub('GUEST_NAME',re.sub('&', '&amp;', salutation), line)
            svgn.write(line)
        #endfor

        svg.close()
        svgn.close()

        os.system("inkscape -f " + filename + " -e " + pngname)
        os.system("optipng " + pngname);
        os.system("convert -compress JPEG -quality 87 " + pngname + " " + jpegname);

        os.system("rm " + filename)

    
        # attach the image to the email

        img = dict(title=u'Invitation', path=jpegname, cid=str(uuid.uuid4()))
                                                        #cid=name)
                                                        #cid=str(uuid.uuid4()))
        with open(img['path'], 'rb') as file:
            msg_img = MIMEImage(file.read(), name=os.path.basename(img['path']))
            #msg.attach(msg_img)
            msg_img.add_header('Content-ID', '<{}@example.com>'.format(img['cid']))
            msg_img.add_header('Content-Name', img['cid'])
            msg_img.add_header('X-Attachment-ID', '{}@example.com'.format(img['cid']))
            #msg_img.add_header('Content-Disposition', 'attachment', filename=os.path.basename(img['path']))
            msg_img.add_header('Content-Disposition', 'inline', filename=os.path.basename(img['path']))

        print('<{}>'.format(img['cid']))



        # Create the body of the message (a plain-text and an HTML version).



        # Record the MIME types of both parts - text/plain and text/html.
        part1 = MIMEText(text, 'plain')
        part2 = MIMEText(html, 'html')

        # Attach parts into message container.
        # According to RFC 2046, the last part of a multipart message, in this case
        # the HTML message, is best and preferred.
        text_msg.attach(part1)
        text_msg.attach(part2)
        msg.attach(text_msg)
        msg.attach(msg_img)


        # Send the message via local SMTP server.
        server = smtplib.SMTP(smpt_server)
        server.ehlo()
        server.starttls()
        server.login(me,password)
        # sendmail function takes 3 arguments: sender's address, recipient's address
        # and message to send - here it is sent as one string.
        server.sendmail(me, you, msg.as_string())
        server.close()
        #server.quit()

        os.system("rm " + pngname)

    #end for (going through rows in the database)
    csvfile.close()
#end with    

Kinect and OSC Human Interface Devices

To make up for the boring title of this post, lets’s start off with a video:

XYZ with Kinect a video by celesteh on Flickr.

This is a sneak preview of the system I wrote to play XYZ by Shelly Knotts. Her score calls for every player to make a drone that’s controllable by x, y, and z parameters of a gestural controller. For my controller, I’m using a kinect.

I’m using a little c++ program based on OpenNi and NITE to find my hand position and then sending out OSC messages with those coordinates. I’ve written a class for OSCHIDs in SuperCollider, which will automatically scale the values for me, based on the largest and smallest inputs it’s seen so far. In an actual performance, I would need to calibrate it by waving my arms around a bit before starting to play.

You can see that I’m selecting myself in a drop down menu as I start using those x, y and z values. If this had been a real performance, other players names would have been there also and there is a mechanism wherein we duel for controls of each other’s sounds!

We’re doing a sneak preview of this piece on campus on wednesday, which I’m not allowed to invite the public to (something about file regulations) but the proper premiere will be at NIME in Oslo, on Tuesday 31st May @ 9.00pm atChateau Neuf (Street address: Slemdalsveien 15). More information about the performance is available via BiLE’s blog.

The SuperCollider Code

I’ve blogged about this earlier, but have since updated WiiOSCClient.sc to be more immediately useful to people working with TouchOSC or OSCeleton or other weird OSC devices. I’ve also generated several helpfiles!
OSCHID allows one to describe single OSC devices and define “slots” for them.
Those are called OscSlots and are meant to be quite a lot like GeneralHIDSlots, except that OSCHIDs and their slots do not call actions while they are calibrating.
The OSC WiiMote class that uses DarWiinRemote OSC is still called WiiOSCClient and, as far as I recall, has not changed its API since I last posted.
Note that except for people using smart devices like iPhones or whatever, OSC HIDs require helper apps to actually talk to the WiiMote or the kinect. Speaking of which…

The Kinect Code

Compiling / Installing

This code is, frankly, a complete mess and this should be considered pre-alpha. I’m only sharing it because I’m hoping somebody knows how to add support to change the tilt or how to package this as a proper Mac Application. And because I like to share. As far as I know, this code should be cross-platform, but I make no promises at all.
First, there are dependencies. You have to install a lot of crap: SensorKinect, OpenNi and NITE. Find instructions here or here.
Then you need to install the OSC library. Everybody normally uses packosc because it’s easy and stuff…. except it was segfaulting for me, so bugger that. Go install libOSC++.
Ok, now you can download my source code: OscHand.zip. (Isn’t that a clever name? Anyway…) Go to your NITE folder and look for a subfolder called Samples. You need to put this into that folder. Then, go to the terminal and get into the directory and type: make. God willing and the floodwaters don’t rise, it should compile and put an executable file into the ../Bin directory.
You need to invoke the program from the terminal, so cd over to Bin and type ./OscHand and it should work.

Using

This program needs an XML file which is lurking a few directories below in ../../Data/Sample-Tracking.xml. If you leave everything where it is in Bin, you don’t need to specify anything, but if you want to move stuff around, you need to provide the path to this XML file as the first argument on the command line.
The program generates some OSC messages which are /hand/x , /hand/y and /hand/z, all of which are followed by a single floating point number. It does not bundle things together because I couldn’t get oscpack to work, so this is what it is. By default, it sends these to port 57120, because that is the port I most want to use. Theoretically, if you give it a -p followed by a number for the second and third arguments, it will set to the port that you want. Because I have not made this as lovely as possible, you MUST specify the XML file path before you specify the port number. (As this is an easy fix, it’s high on my todo list, but it’s not happening this week.)
There are some keyboard options you can do in the window while the program is running. Typing s turns smoothing on or off. Unless you’re doing very small gestures, you probably want smoothing on.
If you want to adjust the tilt, you’re SOL, as I have been unable to solve this problem. If you also download libfreenect, you can write a little program to aim the thing, which you will then have to quit before you can use this program. Which is just awesome. There are some Processing sketches which can also be used for aiming.
You should be able to figure out how to use this in SuperCollider with the classes above, but here’s a wee bit of example code to get you started:




 k = OSCHID.new.spec_((
  ax: OscSlot(realtive, '/hand/x'),
  ay: OscSlot(realtive, '/hand/y'),
  az: OscSlot(realtive, '/hand/z')
  ));

 // wave your arms a bit to calibrate

 k.calibrate = false;

 k.setAction(ax, { |val|  val.value.postln});

And more teaser

You can see the GUIs of a few other BiLE Tools in the video at the top, including the Chat client and a shared stopwatch. There’s also a network API. I’m going to do a big code release in the fall, so stay tuned.

TuningLib

Yesterday, I added a new Quark to Supercollider, called TuningLib. It requires a recent version of MathLib, one with the Bessel.sc file included. There are several classes in the new Quark, all realted to tuning.

Stuff from Jascha

Scala – This class is based on the SCL class from Jascha Narveson, but updated so it’s a subclass of the newer Tuning class. It opens Scala Files, which means you can use the large and interesting scala file library of thousands of tunings.
Key – Jascha’s SCL file also did a bunch of other interesting tuning-related things that the newer Tuning class does not, so I put these features in Key. It tracks your key changes and can interpolate between a given frequency or tuning ratio and the current active Scale.

Dissonance Curves

DissonanceCurve – is, I think, the most interesting part of the TuningLib. It generates Tunings, on the fly, for a given timbre. Give it your spectrum as lists of frequencies and amplitudes, or as a FFT buffer or as the specs for an FM tone, and it makes two different scales.
The first kind of scale it makes is the sort described by Bill Sethares. If you want to see the generated curve, you can plot it. Or you can get a Tuning from it. Or, you can get a scale made up of the n most consonant Tuning ratios. This is used in the second section of my piece Blakes 9
The other sort of tuning it does is based on a similar idea, but using the classic Just Intonation notions of consonance. Like with Sethares’ algorithm, every partial of a timbre’s spectrum is compared against every partial of the proposed tuning. It calculates the ratio between the frequencies. This could be 3/2, for example, or 115/114 or any whole number ratio. The numerator and denominator of that ratio are summed. In just intonation, smaller numbers are considered more consonant, so the smaller the sum, the more consonant the ratio. (This sum is related to Clarence Barlow‘s ideas of ‘digestibility.’) Then, the resultant sum is scaled by the amplitude of the quieter of the two partials. So if they are 3/2 and one has an amplitude of 0.2 and the other of 0.1, the result will be 0.5 ( = (3 + 2) * 0.1). This process repeats for every partial, and the results for each are summed, giving the level of dissonance (or digestibility) of the proposed tuning.
After computing the relative dissonance of all 1200 possible tunings in an octave, the next step is to figure out which ones to select as members of a scale. For this, the algorithm uses a moving window of n potential tunings. For a given tuning, if it is the most consonant of the n/2 tunings below it and the n/2 tunings above it, then it gets added to the Tuning returned by digestibleTuning.
I don’t have any sound examples for this usage yet, but I’m working on some. I don’t know of any pieces by anybody else using this algorithm either, but I’m sure I’m not the first person to think of it. If you know of any prior work using this idea, please leave a comment.

Tuning Tables

Lattice – This is based on some tuning methods that Ellen Fullman showed me a few years ago. Based on the numbers you feed it, which should be an array of 2 and then odd numbers, it generates a tuning table. for [2, 5, 3, 7, 9], it creates:

 1/1  5/4  3/2  7/4  9/8
 8/5  1/1  6/5  7/5  9/5
 4/3  5/3  1/1  7/6  3/2
 8/7  10/7 12/7 1/1  9/7
 16/9 10/9 4/3  14/9 1/1

You can use this class to navigate around in your generated table. For otonality, adjacent fractions are horizontal neighbors, so they share a denominator. For utonality, neighbors are on the vertical axis, so they have the same numerator. Three neighboring ratios make up a triad. You can walk around the table, so that you’re playing a triad, and then pick a member f that triad to be a pivot. Then, create a new triad on the other axis that contains your pivot as one of the members.
For example, one possible walk around the table, starting at 0,0 would be [1/1, 5/4, 3/2], [5/4, 1/1, 5/3], [3/2, 4/3, 5/3], [8/5, 4/3, 8/7], [8/7, 9/7, 1/1] etc. As you can (hopefully) see, the table wraps around at the edges.
I’ve done several pieces using this class, usually initializing it with odd numbers up to 21. Two examples are Beep and Bell Tolls

Undocumented

There is also a class FMSpectrum that will compute the spectrum for a FM tone if given the carrier frequency, the modulation frequency and depth (in Hz). I would like to also add in a class to calculate the spectrum of phase-modulated signals, but I don’t have the formula for this. If you know it (or where to find it), leave a comment!

Some Source Code

Yesterday, I posted some links to a supercollider class, BufferTool and it’s helpfile. I thought maybe I should also post an example of using the class.
I wrote one piece, “Rush to Excuse,” in 2004 that uses most of the features of the class. Program notes are posted at my podcast. And, The code is below. It requires two audio files, geneva-rush.wav and limbaugh-dog.aiff You will need to modify the source code to point at your local copy of those files.
The piece chops up the dog file into evenly sized pieces and finds the average pitch for each of them. It also finds phrases in Rush Limbaugh’s speech and intersperses his phrases with the shorter grains. This piece is several years old, but I think it’s a good example to post because the code is all cleaned up to be in my MA thesis. Also, listening to this for the first time in a few years makes me feel really happy. Americans finally seem to agree that torture is bad! Yay! (Oh alas, that it was ever a conversation.)

(

 // first run this section

 var sdef, buf;
 
 
 // a callback function for loading pitches
  
 c = {

  var callback, array, count;

    array = g.grains;
    count = g.grains.size - 1;
     
    callback = { 
   
     var next, failed;
      
     failed = true;
      
     {failed == true}. while ({
    
      (count > 0 ). if ({
    
       count = count -1;
       next = array.at(count);
    
       (next.notNil).if ({
        next.findPitch(action: callback);
        failed = false;
       }, {
        // this is bad. 
        "failed".postln;   
        failed = true;
       });
      }, { 
       // we've run out of grains, so we must have succeeded
       failed = false;
       "pitch finding finished".postln;
      });
     });
    };
   
   };

 
 // buffers can take a callback function for when they finish loading
 // so when the buffer loads, we create the BufferTools and then
 // analyze them
  
   buf = Buffer.read(s, "sounds/pundits/limbaugh-dog.aiff", action: {
  
    g = BufferTool.grain(s, buf);
    h = BufferTool.grain(s, buf);
    "buffers read!".postln;
   
  g.calc_grains_num(600, g.dur);
    g.grains.last.findPitch(action: c.value);
    h.prepareWords(0.35, 8000, true, 4000);
 
   });
 
   i = BufferTool.open(s, "sounds/pundits/geneva-rush.wav");

  
   sdef = SynthDef(marimba, {arg out=0, freq, dur, amp = 1, pan = 0;
  
   var ring, noiseEnv, noise, panner, totalEnv;
   noise = WhiteNoise.ar(1);
   noiseEnv = EnvGen.kr(Env.triangle(0.001, 1));
     ring = Ringz.ar(noise * noiseEnv, freq, dur*5, amp);
     totalEnv = EnvGen.kr(Env.linen(0.01, dur*5, 2, 1), doneAction:2);
     panner = Pan2.ar(ring * totalEnv * amp, pan, 1);
     Out.ar(out, panner);
    }).writeDefFile;
   sdef.load(s);
   sdef.send(s);

   SynthDescLib.global.read;  // pbinds and buffers act strangely if this line is omitted

)

// wait for: pitch finding finished

(

 // this section runs the piece

 var end_grains, doOwnCopy;
 
 end_grains = g.grains.copyRange(g.grains.size - 20, g.grains.size);
 

 // for some reason, Array.copyRange blows up
 // this is better anyway because it creates copies of
 // the array elements

 // also:  why not stress test the garbage collector?
 
 doOwnCopy = { arg arr, start = 0, end = 10, inc = 1;
 
  var new_arr, index;
  
  new_arr = [];
  index = start.ceil;
  end = end.floor;
  
  {(index < end) && (index < arr.size)}. while ({
  
   new_arr = new_arr.add(arr.at(index).copy);
   index = index + inc;
  });
  
  new_arr;
 };

 
 
 Pseq( [


  // The introduction just plays the pitches of the last 20 grains
  
  Pbind(
 
   instrument, marimba,
   amp,   0.4,
   pan,   0,
   
   grain,   Pseq(end_grains, 1),
   
   [freq, dur],
      Pfunc({ arg event;
     
       var grain;
      
       grain = event.at(grain);
       [ grain.pitch, grain.dur];
      })
  ),
  Pbind(
  
   grain, Prout({
   
      var length, loop, num_words, loop_size, max, grains, filler, size,
       grain;
      
      length = 600;
      loop = 5;
      num_words = h.grains.size;
      loop_size = num_words / loop;
      filler = 0.6;
      size = (g.grains.size * filler).floor;
      
      
      grains = g.grains.reverse.copy;

      // then play it straight through with buffer and pitches

      {grains.size > 0} . while ({
        
       //"pop".postln;
       grain = grains.pop;
       (grain.notNil).if({
        grain.yield;
       });
      });
      
      
      loop.do ({ arg index;
      
       "looping".postln;
      

       // mix up some pitched even sizes grains with phrases

       max = ((index +2) * loop_size).floor;
       (max > num_words). if ({ max = num_words});
       
       grains = 
         //g.grains.scramble.copyRange(0, size) ++
         doOwnCopy.value(g.grains.scramble, 0, size) ++
         //h.grains.copyRange((index * loop_size).ceil, max);
         doOwnCopy.value(h.grains, (index * loop_size).ceil, max);
         
       

       // start calculating for the next pass through the loop
       
       length = (length / 1.5).floor;
       g.calc_grains_num(length, g.dur);
       g.grains.last.findPitch(action: c.value);
       
       grains = grains.scramble;
       

       // ok, play them
       
       {grains.size > 0} . while ({
        
        //"pop".postln;
        grain = grains.pop;
        (grain.notNil).if({
         grain.yield;
        });
       });
      });
      
      i.yield;
      "end".postln;
     }),
   [bufnum, dur, grainDur, startFrame, freq, instrument], 
    Pfunc({arg event;
    
     // oddly, i find it easier to figure out the grain in one step
     // and extract data from it in another step
     
     // this gets all the data you might need
    
     var grain, dur, pitch;
     
     grain = event.at(grain);
     dur = grain.dur - 0.002;
     
     pitch = grain.pitch;
     
     (pitch == nil).if ({
      pitch = 0;
     });
     
     [
      grain.bufnum,
      dur,
      grain.dur,
      grain.startFrame,
      pitch,
      grain.synthDefName
     ];
     
    }),
      
      
   amp,   0.6,
   pan,   0,
   xPan,   0,
   yPan,   0,
   rate,   1,
   
   
   twoinsts, Pfunc({ arg event;
       
     // so how DO you play two different synths in a Pbind
     // step 1: figure out all the data you need for both
     // step 2: give that a synthDef that will get invoked no matter what
     // step 3: duplicate the event generated by the Pbind and tell it to play
       
       var evt, pitch;
       
       pitch = event.at(freq);
       
       (pitch.notNil). if ({

        // the pitches below 20 Hz do cool things to the 
        // speakers, but they're not really pitches,
        // so screw 'em
        
        (pitch > 20). if ({
         evt = event.copy;
         evt.put(instrument, marimba);
         evt.put(amp, 0.4);
         evt.play;
         true;
        }, {
         false;
        })
       }, {
        // don't let a nil pitch cause the Pbind to halt
        event.put(freq, rest);
        false;
       });
      })
        
  )      
      
       
 ], 1).play
)
 

This code is under a Creative Commons Share Music License

BufferTool

A while back, I wrote some code and put it in a class called BufferTool. It’s useful for granulation. Any number of BufferTools may point at a single Buffer. Each of them knows it’s own startFrame, endFrame and duration. Each one also can hold an array of other BufferTools which are divisions of itself. Each one may also know it’s own SynthDef for playback and it’s own amplitude. You can mix and match arrays of them.
You can give them rules for how to subdivide, like a set duration of each grain, a range of allowable durations or even an array of allowed duration lengths. Or, it can detect pauses in itself and subdivide according to them. It can calculate the fundamental pitch of itself.
I want to release this as a quark, but first I’d like it if some other people used it a bit. The class file is BufferTool.sc, and there’s a helpfile and a quark file.
Leave comments with feedback, if you’d like.

Gigs!

I’ve mentioned that I’m playing soon in Austria (12 July in Lintz). The information is here. Note that the concert is open to anybody at all who wants to attend, whether they are participating in the rest of the conference or not.

I think playing at hacker / open source meetings is a good idea. I’m looking into using Perl to talk to the SC server, just so I can submit a proposal to a Perl conference. Of course, I spent some time as a professional perl scripter and I once wrote a web server in it for fun. My first-ever SuperCollider project had quite a bit of perl in it, as SC3 was really very alpha at the time. so it’s not just a cynical ploy to get to play at a geeky gathering.
Also, leftist political conferences are a good fit. I’ve got American political music, for sure. I keep thinking about doing more of it, but my ability to manipulate non-English text is lower. I think I will do such a piece for the /etc conference, as I recorded a friend of mine speaking in French about feminism. (Do you want to be in the piece? Send me a recording, in any format, of yourself, speaking in any language, answering the question: “Are you a feminist? Why or why not?”) Anyway, there’s certainly plenty of American material to work with, alas.
So if you, dear reader, are doing some sort of conference about open source or hacking or programming languages or leftist organizing or any related field, you should drop me a line. Depending on the location, I may be able to cover my own travel expenses. I have no technical needs, aside from electricity and a sound system. If the venue is café-sized and/or in Holland, I can even provide my own sound system.
Hm, if only I had a few pieces in Esperanto . . ..

Quick JJiCalc Tutorial

Launching JJiCalc

After you download the software to your computer, you can run it by
double-clicking on JJiCalc.jar. Or, if you have a command prompt,
you can type “java -jar JJiCalc.jar” .

Opening Files

Once the software is launched, click on the file menu and select open.
A dialog box will come up listing files and directories on your computer.
This box may look different from ones that you are used to. On OS X, the
box will list files in the root directory. If you want to get to your
home directory, click on the Users folder, then on your directory name.
Go to the directory where you put the software. In the JJiCalc folder,
there is a folder called tuning. Go there and open the file called
old_grandad.jic
Eight tuning ratios will appear in the top of the tuning table. The
title “Old Grandad” will appear in the title bar. (If you want to change
the name, just type in the title field.) On the top right hand side
of the application window are some buttons. One of them is called
“Comments.” If you click on comments, you can view the comments made about
the scale.

Lattice

Click the button marked “Lattice” to see the tuning lattice for the scale.
A new window will pop up, which shows ratios connected by lines. If you
want to hear a tuning ratio, you can click on it in the lattice. Click
on 3/2. The box around the ratio will turn gray and you should hear the
sound of the ratio being played. If you don’t hear anything and you’ve made
sure that sounds are working on your computer, you may need to update
your Java libraries, especially Swing.
If you click again on the ratio, it will stop playing. You can play
any number of ratios at the same time as you would like. Click on 3/2,
5/4 and 1/1.

Pop-up Menus

You can also play ratios directly from the tuning table. If you have a
multi-button mouse, right click on the numerator or denominator of one of
the ratios. If you are on a macintosh with a single-button mouse, option
click on the numerator or denominator. A pop-up menu will appear with
the options “Enable Sound”, “Freeze this Cell”, and “Clear this Cell”. To
hear the ratio, select “Enable Sound.” To stop hearing the ratio, right
or control click again and select “Disable Sound.”
Frozen cells can’t be played. Also, they can’t be cleared and will not
be sorted. To freeze a cell, right or control click on the cell for the
pop-up menu. To defrost a cell, do the same thing again, but select
“Defrost this Cell”. To erase the contents of a cell and remove a ratio
from the table, select “Clear this Cell”.

Cents, Hertz, Fret Position

In the middle of the bottom of the application, there is a section of
buttons labelled “View As.” Click the one labelled “ET +-Cents”. This
will calculate the closest Equally Tempered pitches, plus or minus the
cents needed to get your tuning. In the gray boxes below each ratio,
you should see a postive or negative number indicating the cents to add
or subtract, with the closest ET pitch below that. The JJiCalc assumes
that 1/1 is C0, so therefore 5/4 is E0 -13.7 cents.
To calculate Hertz, click the button marked Hertz in the “View As”
section. 1/1 defaults to 440. If you would like to use a different
base frequency, on the bottom right is a section called “base frequency.”
Type in what frequency you would like in the text area marked “1/1 freq”.
Then, click the button “Update Base”. If you play the ratios, they will
sound at the new frequencies. The displayed Hertz will not change,
however, until you click the “Hertz” button again in the “View As” area.
If you click the button “fret pos”, it will calculate fret positions
for you. The top number is the integer part of the fret position and the
bottom number is the decimal section. Below 3/2, there is 0. in the top
box and 3333 in the bottom box. Thus, for a string 1 meter long,
the fret position for 3/2 would be at 0.3333 meters.
You can change the string length by typing it in the text area labelled
“Str. Len.” and then clicking the button labelled “string len”. This will
cause your fret positions to recompute. There is a known bug: The
recomputed numbers will be incorrect.
After you change the String
Length, click the “fret pos” button again to figure out the correct fret
positions.

Adding New Ratios

Go to an empty table cell and (left) click in the numerator box. type
15 and then in the denominator box, type 16. This adds a 15/16 minor
second. If you click the lattice button, you will see your new
fraction in the lattice.
The JJiCalc automatically reduces your fractions for you. In another empty
cell, enter in 32/30. As soon as you click off of the cell, it will
reduce to 16/15. There is an option under the Configuration menu to turn
this reduction off. This is a known bug: Your fractions will always
reduce.

The Configuration Menu

One thing that works in the configuration menu is changing the wave
form setting. You can hear your fractions played as sine waves,
square waves or sawtooth waves.

Sorting

all of our tunings are in scale order except for the new one, 16/15.
On the right is a button marked “Sort”. Click it to but the ratios
in order from smallest to largest.
All of the things in the gray boxes below the ratios will clear when
you hit sort.

Saving

When you save your file, all of the data including the 1/1 frequency,
the title, the comments, the ratios and the stuff written in the gray
boxes below the ratios all get saved. Saving a tuning gives you the same
kind of dialog box you got when you openned old_grandad.jic. If you
type in the name of a file that already exists, a box will appear to
ask if you’re sure you want to overwrite the old file.

Other Formats

If you don’t want to save anyhting but the title, the comments and the
ratios (but not the stuff in the gray boxes), go to the File menu, then
look in the Export submenu and select “Ratios only.”
JJiCalc also supports the Scala file format. To save your tuning as a
Scala file, select “Scala File” under the Export menu. There is a very
large set of tunings in the Scala format that you can download from http://www.xs4all.nl/~huygensf/doc/scales.zip. To open
these tunings, select “Scala File” from the import menu. Not all Scala
tunings are Just Intoned. If the Scala tuning that you open contains
some non-just tunings, JJiCalc will approximate them as fractions.

Moo themes

Ok, so I was looking at modifying the source code of SuperCollider to make network connections . . . what a nightmare! So instead, there will be a perl program, that SC will execute that will handle all of the text stuff and the network stuff and everything that supercollider isn’t good at. so the perl thing will do everything but make sound. (Note: maybe someone ought to add sound capablities to Perl. An OSC lib, maybe?)
Players will have the options of attaching arrays to themselves caled themes. So you would need to create a property on yourself called a theme and then put stuff in it. This theme will look *cough* just like a ring tone on a Nokia phone. Some of you have experience with cell phone ring composers and will be able to hear your theme on a phone before comiting it to yourself. I just wrote a ring tone. It goes 4d1 4a1 4b1 2c2 32d2 32#f2 8a2 8f2 8f2 16d2 16f2. It’s pretty jazzy at the end.

Anatomy of a Nokia Composition

the first number is the note duration. It is the denonominator of a fraction whose numerator is 1. So smaller numbers are faster. In musical terms, a 1 is equivalent to a whole note, 2 a half note, 4 a quarter note, 8 and 8th note, etc.
Then comes an optional # and then the note name. There are no flats. but that’s ok because in equal temperment Bb = A#. but since it’s backwards, in this case it equals #a.
The last number indicates the octave. A low number is a low octave. A d2 is an octave higher than a d1.

Garunteed support changes

You will be able to use any number, not just a power of 2, to denote duration, but it will still be in denominator format. therefore a starting number of 3 will be meaningful (in musical terms, it would be a half note triplet, since our notation isn’t so good at divisions by 3). You will be able to specify b for flat or # for sharp. Equal temperment may not be supported. Because an optional ‘b’ means flat, all the note names must be in upper case. and the final integer specifyin octave will be optional (if you don’t specify, then the program will pick an octave and your theme may get moved around, from high to low). you can pick an arbitrarily large or small octave number. the low threshold of human hearing is around 20 Hz, so octave 0 will be down there someplace. the high threshold of human hearing is around 20000 Hz, so figure out what octave number that is and that’s you upper lmit. Extremely high pitched sounds are likely to be played at a much lower volume.

yeah, yeah, ringtones

I’m not crazy about cell phone, or their ringtones, but I have to admit that this has cross-fertalization possibilities. Some phones allow you to specify different rings for different people in your addressbook. Also, some websites have the ring tones for different songs up on them. My phone plays the Indiana Jones theme song, for example. there are Ives-ian possibilities lurking danegerously around.
telnet happy.xkey.com 3333

DarkStar Sierra: Come in, Come in

Don’t ask me what this subject line is. It has something to do with an angry airplane.
JJiCalc now saves and opens files. It’s very exciting. Right click (or control-click) on the link and select “save link as” or something similar to that: Tar Gzipped or .sit (Mac!!). These links are compiled only. If you want source, you need to go to Source Forge and then sign up to join the project. Ok, you can donload source without joining. but if you want source, then you ought to want to join. There’s no real webpage for the project yet. I need to figure out how to do that. on the one hand, it seems like it’s more useful to fix saving and loading files. on the other hand, nobody can sign up to do thigns and fix other stuff if I don’t make the page.
Speaking of saving and loading files, I was unable to salvage any of the tuning files that came with the original source. I need tunings! Chromaic tunings. Utonal tunings. Otonal tunings! 7-limit tunings! Dorian tunings! Pythagorian tunings! More tunings that you can shake a stick at!
You (yes you!) could be a contributor. Even if you can’t code! Just download the program and then go find some tunings. Eneter the tunings into the program and save them. Then, if you want to join the project, you can check them in. If you don’t want to figure out how to join the project, you can mail your tuning files to me and I’ll add them with a project with a note that you (yes you!) are the fab person who inputted the tuning. I don’t actually have very much tuning knowledge. Very little, actually. Fixing loading and saving files is not increasing my knowledge of tuning, only of file I/O. So, it would be good if people with tuning knowledge did the tuning files part.
tonight is mitch night. I should go practice some bass quitar. reveal yourself to me, i am tacet blue. In the time I spent typing this post, I listened to the evil three songs at least twice, probably three times. How long will it take you to read it?

Java Beans

I’ve submitted a request to SourceForge.net
to have them host CVS stuff for the Just Intonation Calculator. They should reply within th
e next couple of days.
By Friday, it would be nice if the JiCalc would save and open it’s own files and also tab-dili
neated spreadsheet files. There’s some file IO code in it already, but it doesn’t work. Chand s
ays I should use some code form Enterprise Java Beans or something. I thought they just used a m
atter replicator to make coffee in Star Trek. I’m pretty sure that I saw Kirk make a mixed drink
that way on some episode.
Someplace, I have a book on Java Beans that I once read. I remember seeing Java Beans not as
pieces of code, but as a spiritual approach to code. Objects that could save themselves! Hallel
uija! They could load themselves too. In a way, any object that contains data could be thought
of as a bean, and since every object contains at least some kind of data, than every object is so
mehow a bean, which makes the whole idea of beans meaningless, since it’s just a way of thinking
of every object. Obviously, I didn’t get it. More importantly, I’d like to be able to deal with
files by Friday. Is file IO part of an “is a” relationship, or a “has a” relationship? Should
I add more code right to the TuningTable object, or should I have a seperate File IO class? Shou
ld I go read the OOP chapter of my programming languages book, or will some OOP programmer take p
ity on me and clue me in?
Mills offered a class on databases, but I didn’t take it, since it sounded boring. I think ma
ny of the programming questions that I’ve had in the years since would have been answered if I ha
d taken that class. Most of my questions have been about data storage and retrieval. I also zon
ed out on statistics in high school. And music theory in college. Alas. I’m going to try to so
ak up as much knowledge as possible in grad skool and let life sort out important vs unimportant
material, since my guesses about what will or will not turn out to be useful are often wrong. So
I say now, while I have zero study habits. If only, like a bean, I could come up with a way to
save myself.