Things I'll Forget In A Month Tech scratchpad and musings

25Feb/100

Toggling audio devices in Mac OS X

I frequently switch between my external computer speakers and my headphones. Repeatedly plugging and unplugging my headphones started wearing out my headphone jack, so I hooked up an iMic to a USB port to give myself a second headphone jack. This allows switching between them in software through the Sound preference pane, which is more convenient, but still takes too long. So the next step is automating the switch. This thread on Mac OS X Hints has people discussing various ways to do it. The easiest GUI way to do the switch on Snow Leopard is just option-clicking the menu bar volume control and selecting the new output device (on Leopard, you can do effectively the same by installing SoundSource).

I'd like to avoid using the mouse at all, of course. Someone got the sources to SoundSource and created a little command-line app called audiodevice that can do the switch without a GUI. Making progress, but that still requires executing a custom command to switch from the headphones to speakers or vice versa. As a final step, I threw together a quick script that uses audiodevice to toggle between the two without any arguments:

#!/usr/bin/perl -w

use strict;

my $device1 = 'Headphones'; # my speakers, plugged into my computer's jack
my $device2 = 'iMic USB audio system'; # my headphones, plugged into my iMic's jack

foreach (`audiodevice`) {
  if (/output: (.*)/) {
    if ($1 eq $device1) {
      system("audiodevice output '$device2'");
      exit;
    } elsif ($1 eq $device2) {
      system("audiodevice output '$device1'");
      exit;
    } else {
      print "Unrecognized output device '$1'.\n";
      exit;
    }
  }
}

print "Failed to find output device.\n";


I saved this script as audioswitch and added it to my LaunchBar index so I can call it from anywhere using only 4 or 5 keystrokes. Works exactly as I'd like, and you can modify it to toggle between any two devices by substituting the name of the devices (in the Output tab of the Sound preference pane) into the script.

UPDATE (3/11/10): I've fixed up the above script so that it automatically figures out what the two output devices are and switches between the two accordingly. This means that it'll work for switching between the iMic and either internal or external speakers. Also fixed a problem where system sounds would continue to play through the original device after switching and added a Growl notification with growlnotify.

#!/usr/bin/perl -w

use strict;

my $device1;
my $device2;

foreach (`audiodevice output list`) {
  chomp;
  if (defined $device1) {
    $device2 = $_;
    last;
  } else {
    $device1 = $_;
  }
}

foreach (`audiodevice`) {
  if (/output: (.*)/) {
    if ($1 eq $device1) {
      activate($device2);
      exit;
    } else {
      activate($device1);
      exit;
    }
  }
}

print "Failed to find output device.\n";

sub activate {
  my $device = shift;
  system("audiodevice output '$device'");
  system("audiodevice system '$device'");
  system("growlnotify -n audioswitch -m 'switching to $device'");
}

Filed under: Hacks, Mac No Comments
22Feb/100

Remote controlling an iSight

One of the neat things to do with a remote Mac is to control the built-in iSight remotely to snap photos. There are several commercial applications to do this, but we're going for the low-tech approach: taking pictures via command line over SSH. The free app iSightCapture is designed to do this, but hasn't been updated in awhile and doesn't work as-is on anything after 10.4.9 (including 10.5 and 10.6) due to new security restrictions. I put together a little script that basically wraps iSightCapture so that it works as designed on an up-to-date system.

You'll need to have iSightCapture installed and in your $PATH -- you can either do that by manually installing the binary at the above link or via MacPorts with 'sudo port install isightcapture'. Then create a new file in /usr/local/bin (or wherever) and put in the following Perl snippet:

#!/usr/bin/perl -w

use strict;

foreach (split(/\n/, `ps aux | grep loginwindow`)) {
  if (/^\w+\s+(\d+).+?loginwindow\.app/) {
    my $sudo = ($> == 0) ? '' : 'sudo ';
    exec("$sudo launchctl bsexec $1 isightcapture @ARGV");
    exit;
  }
}

print "Failed to find loginwindow process.\n";

I called the new binary 'snap', so at the command-line, I just type 'snap image.jpg' instead of 'isightcapture image.jpg'. The only real difference from isightcapture is that you need sudo -- that's an intentional Mac OS X security restriction that can't be circumvented (and probably shouldn't) as far as I know. All the iSightCapture command-line options should work as-is, so just refer to its documentation for those. The wrapper script is definitely a hack that might break with a future OS update, but has worked flawlessly for me on several different machines running a variety of Mac OS X versions.

Filed under: Hacks, Mac No Comments