And I want presents!
That is all.
UPDATE 2006-07-23 23:30:00 PST
My parents have launched a denial of service attack on that Amazon.com Wish List by ordering the whole lot and shipping it to me. How on Earth am I supposed to figure out which one to read first?
Might I suggest some possible alternatives?
I've long needed a way to quantify my mounting frustration with Radio Shack. Today, as I trudged home from my local store empty handed, it came to me. My absolute measure of Radio Shack's fall from grace is fractions of a radio. That is, if one were to start with a clean slate, an empty desk with no tools or parts, what fraction of a radio could one construct using only that which one could purchase from Radio Shack?
The answer to that question seems to be a strictly decreasing function. Some of Radio Shack's recent tragic product discontinuations include: MOSFETs, LED detectors, their entire catalog of digital integrated circuits (except for TTL quad buffers for some reason), most of their analog integrated circuits, crystal oscillators, proto board, DTMF dialers (that was a long time ago, okay?)... the list goes on and on.
Today I walked to Radio Shack in search of a particular item: a single magnetic reed switch. I didn't see them. A helpful Radio Shack employee saw my plight (and my empty hands) and immediately concluded that I needed a cellphone. Though my wife would surely agree, this was presently not the case.
The woman looked up magnetic reed switches in the computer, and asked "Is this a security product?" I pondered for a moment and conceded that it could be used in a security system, though I had no intention of using it that way. Discontinued.
And though each time leave disappointed, I claim each time that this time I'm going to swear off Radio Shack for good, something gives me a glimmer of hope. Somewhere in every Radio Shack there is a high shelf, usually wedged in a corner of the store between UHF antenna mounts and soldering guns, above last year's model RC car. On that shelf are aged, leaking bottles of ferric chloride acid, used for etching printed circuit boards. If you ask a Radio Shack employee where it is, he will react with incomprehension, then flickering recognition, and finally disbelief. "You use that stuff?"
I never buy it there. If I did, they wouldn't restock it, and then I'd never go back.
Radio Shack. You've got questions, we've got mutherfucking ferric chloride.
I left the office around 3pm, and will not return for nine days. I feel like I'm on Spring Break.
How did I get nine days off? To start, the office closed early today in anticipation of the long weekend. I fully endorse this logic. The office will remain closed Monday and Tuesday, in celebration of Independence Day. I am a great supporter of freedom. I justified taking Wednesday off in the following way: "Its hard enough to be productive on Monday after a normal weekend. How can I be productive after a four day weekend?"
To understand how I rationalized Thursday away, you must understand that the office will once again close early next Friday. Santa Monica Yahoos are to pack up their cubicles, put stickers on their boxes, and make preparations to be moved over the weekend to our new offices. Everyone must be out by 12pm. From a certain perspective, this makes next weekend a three day weekend. And you know how hard it is to stay productive on the day before a three day weekend. There goes Thursday.
And, if I had the forethought to linger just a ilttle bit after the office closed this afternoon and pack up my spartan cubicle, then my presence next Friday will only slow everyone else down. And then there's that weekend...
My brother rightly points out that in a certain way I'm "begging the question." My logic is fundamentally flawed, and its natural extension suggests that I cannot possibly be productive on Monday, having just returned from a nine day weekend and should therefore stay home.
And though I'm forced to concede his point... Independence sure feels good.
At YMG, we have a lot of meetings. We have meetings to schedule meetings, which are cancelled and replaced by meetings to discuss the process by which we schedule meetings. New meetings are invariably scheduled to repeat by default; not to suggest that they will actually repeat at fixed intervals, but rather because finding a meeting room can be like a turf war, and it never hurts to plan ahead just in case.
We have face-to-face meetings, one-on-one meetings, phone meetings, video meetings. We have meetings conducted in every possible combination of those modes. We have video meetings with simultaneous phone meetings, because the microphones on the video conference machines are on the fritz.
Yesterday morning, I participated in an early morning telephone conference meeting using my cellphone on my way to work. I arrived just in time to rush to a video conferencing room to dial in for my weekly team meeting. The room on the other end of the connection was dark and empty. I was shortly informed via IM that our meeting had been postponed a half hour, and in the interim I should dial in to another phone conference meeting (which included all but one of the participants of the weekly team meeting). A half hour later, my teammates excused themselves from that meeting, and thirty seconds later appeared on my video conferencing screen. My boss showed up and we conducted our meeting. We got kicked out of the room thirty minutes hence, and my boss suggested that we continue our discussion over the phone (though he could sadly not attend, having another meeting to attend.) We continued our discussion over the phone (with the help of SubEthaEdit) until I had to excuse myself to have a lunch meeting with our principal architect. Our team resumed our meeting after lunch until we finished an hour later. My boss, meanwhile had finished his meeting, and since he is so rarely in LA he thought we should have a one-on-one meeting. We met over coffee until I had to excuse myself to attend the second part of the earlier phone conference meeting which our team meeting had so untimely interrupted. Get all that?
I spent the last thirty minutes of the work day at my desk, IM off, email closed, earplugs in my ears. I tried in vain to find a syntax error to correct; a line of code missing a semicolon; a minor bug to fix. Nothing.
It used to puzzle me that there are so few conference rooms, and a nonexistent conference room scheduling system, making room-squatting so prevalent. But today I realized the genius of this scheme. The conference room shortages are planned—supply is kept doggedly in check. This ensures that at all times, some fraction of the workforce is forced to be at their desks getting actual work done. Its brilliant. But some days it just doesn't work.
I'm trying to diagnose the MIDI output of my velocity poi base station. This base station receives normalized velocity telemetry via RF on two channels (418MHz and 433 MHz) and outputs MIDI data. In this case, I'm trying to examine the Control Change MIDI message (a number in the interval [0, 127]). Because there's noise in the system, I'm written hardware smoothing functions, and I wanted to understand the effect these smoothing functions have on my MIDI output---in real time. I wanted an "oscilloscope" for digital data.
And of course I discovered that the software I wanted already exists...
The GNU Graph program, part of the venerable plotutils package, can act as a "real-time filter" of data. I used darwinports to install plotutils and began poking around in GNU Graph. I discovered the following things from the documentation: in order to plot in real-time, the -x and -y (bounds) arguments must be specified, and data to graph is read from stdin.
My first naive attempt to plot data in real-time failed because pipes are inherently page buffered. In order to connect a program which is producing time-dependent data to graph via a pipe, this buffering must first be inhibited. But how?
One solution is to use the "unbuffer" program bundled with Expect. The other way (and the way I did it) is to use the Expect perl module. The important part here is to stty the terminal to raw (using Expect here seemed to be the easiest way to stty the terminal.)
The following code, then, plots data output by the Perl script in realtime:
#!/usr/bin/perl use Expect; use Time::HiRes qw(gettimeofday sleep);use constant {
DURATION => 30,
INCREMENT => 0.25,
};my $start = gettimeofday();
my $end = DURATION;my $exp = new Expect->new();
$exp->slave->stty(qw(raw));my $graph = $exp->spawn("graph -T X -x 0 $end -y 0 127");
open my $graph, "|graph -T X -x 0 $end -y 0 127");
for (my $i=0; $i<(DURATION*(1/INCREMENT)); $i++) {
my $val = rand(127);
print $graph gettimeofday()-$start ." $val\n";
sleep(0.25);
flush
}
In developing my velocity-aware poi, I needed to read acceleration telemetry from the serial port, and transmit MIDI packets from a virtual source to which other MIDI programs on my Mac could subscribe.
Apple's developer documentation is "sufficient," but there are a number of important concepts I had to understand first before I could begin coding.
CoreMIDI defines MIDI clients, which are actors that transmit and receive MIDI packets. These clients may own MIDIEndpoints: MIDI Sources and MIDI Destinations (abstracted by the generic MIDIEndpointRef).
The CoreMIDI documentation is quite easy to follow if you wish to create a MIDIClient, create a MIDISource, and transmit MIDI packets directly to a destination (identified by name.) However, in my limited MIDI experience, I do know that MIDI sources can generate MIDI packets and transmit them without a particular destination in mind. Other MIDI applications (in my case, Ableton Live) can enumerate the virtual and physical MIDI sources in the computer, and "subscribe" to the output of that MIDI Source.
Roughly, the methodology for accomplishing this (on the Source side (the side I was writing)) is to:
If that last step has you scratching your head, trust me you're not alone. The order to transmit MIDI from a MIDI Source is neither past-tense, nor related to "receiving." This functionality is documented in the CoreMIDI pdf, but it is far from clear. Perhaps Apple could have named this important method in a way that doesn't suggest its a callback function?
But no matter. I have created a virtual source and I am now successfully transcoding data read from a serial port to a virtual MIDI generator, and controlling Ableton Live using that signal. The output MIDI signal is a "Modulation Control Change" signal [176:1:val]. My efforts were rewarded by the sight of Ableton Live's volume slider under direct control of my wireless accelerometer poi.
Having met success in this endeavor, I'm inclined to screw it to hell and disintermediate the computer entirely. I originally chose to upload my data to the computer using serial because I wanted the flexibility to produce signals other than MIDI from the output of my sensor. However, after struggling with tcsetattr, ioctl, fcntl, and the arcane constants required to properly configure a serial port (especially an uncooperative Keyspan USB-Serial adaptor), I'm going to produce MIDI output directly from the microcontroller. I gain the flexibility of being able to hook it up to any MIDI host. And I realize now that configuring and using MIDI under CoreMIDI is not as difficult as I originally thought. I'll let my MIDI host controller do the work of injecting the data into my computer, and subscribe to that MIDI data like any other program.
And now, for your edification, the code. I've never worked with CoreServices before, and so I'm probably doing all sorts of things wrong here. There's idioms I don't know, and I couldn't find the CoreServices analog of strerror. Comment if you know this stuff better than I do, please!
#include "midi.h" #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <CoreMIDI/CoreMIDI.h>
MIDIEndpointRef *MIDIInit()
{
MIDIClientRef midi_client;
MIDIPortRef midi_port;
MIDIEndpointRef *midi_source;
midi_source = malloc(sizeof(MIDIEndpointRef));OSStatus err;
CFStringRef client_name_cf = CFStringCreateWithCString(NULL, "poi_client", kCFStringEncodingUTF8);
err = MIDIClientCreate(client_name_cf, NULL, NULL, &midi_client);
if (err != noErr) {
fprintf(stderr, "MIDIClientCreate had problems\n");
exit(1);
}CFStringRef port_name_cf = CFStringCreateWithCString(NULL, "poi_port", kCFStringEncodingUTF8);
err = MIDIOutputPortCreate(midi_client, port_name_cf, &midi_port);
if (err != noErr) {
fprintf(stderr, "MIDIOutputPortCreate had problems\n");
exit(1);
}CFStringRef source_name_cf = CFStringCreateWithCString(NULL, "poi", kCFStringEncodingUTF8);
err = MIDISourceCreate(midi_client, source_name_cf, midi_source);
if (err != noErr) {
fprintf(stderr, "MIDISourceCreate had problems\n");
exit(1);
}return midi_source;
}void MIDISendControl(MIDIEndpointRef *midi_source, unsigned char val)
{
MIDIPacketList pktlist;
MIDIPacket p;
Byte data[3];
p_head = MIDIPacketListInit(&pktlist);
data[0] = 176; // Control change
data[1] = 1; // Modulation
data[2] = val; // Value
MIDIPacketListAdd( &pktlist, sizeof(p), p_head, 0, 3, data);
MIDIReceived(*midi_source, &pktlist);
}