Microsoft Office 2008 for Mac is finished. They’re pressing CDs now and will begin shipping in mid-January. I’ve been looking forward to this release for a while, mainly because everyone else in the universe uses Office, so if I want to (successfully) view their documents, and have them view mine, I need to use Office. I tried Apple’s new iWork suite, and it’s nice, but it had a lot of trouble with the Office documents I tried to open. The current Office suite for Mac is Office 2004, and only exists for PowerPC processors, so on all the new Intel-based Macs (like ours), it runs in slow PowerPC emulation mode. I did order Office 2008, mainly because of the too-good-to-refuse deal Microsoft ran the day after thanksgiving (buy Office 2004 now, get $100 off, and get a free upgrade to 2008 in January), which resulted in me getting Office 2008 for $25 plus shipping.
Anyway, the real point of this post is to share how sad I am at the Frankenstein user-interface of the new Office… a half-MacOS interface with a half-Vista interface glued on. This on the same day that I read an article about glow-in-the-dark cats being bred by inserting jellyfish genes into their DNA. I don’t think the cats or the software are better off. And what’s with that floppy-disk “save” icon?? Has anyone even seen a floppy disk in the last 8 years? What makes this more amusing is that the Office team did change the floppy disk icon as compared to the previous version of Office for Mac, and compared to the latest Windows Office: the new floppy icon is anatomically correct, whereas the previous versions had the aluminum access door on the wrong side as compared to a real disk. Well, all I can say is I’m glad they got that fixed!
I’ve been trying to work on my MP3 collection’s ID3 metadata, and in the past weeks have gone through close to a dozen different ID3 tagging utilities and libraries, before finally finding a solution I’m happy with, so I thought I’d summarize my findings here. Going into all my crazy requirements would take forever, but a few key ones were:
The underlying vision behind this is my non-standard workflow. I rip all my CDs into FLAC (Free Lossless Audio Codec) format using the Max CD ripper/encoder application for Mac OSX. These FLAC files are my masters, and once ripped, the CDs go into a storage cabinet and (hopefully) are never seen again. Once I’m happy with the metadata in the FLAC files, I kick off a script that converts them to MP3, and copies the metadata over (including some custom mappings).
After lots of trial and error, and finding utilities and libraries that were missing one feature or another, I finally settled on using TagLib via its Perl interface. Unfortunately, I couldn’t find any examples of code using this interface, so had to struggle through the terse docs and fall back on lots of experimentation. Below is some of the code I came up with… hopefully it’ll help someone else with this library some day…
Example 1: Printing all the ID3v2.4 tags in an MP3 file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #!/usr/bin/perl -w # # Just print out all the ID3v2.4 tags in the MP3 file # use Audio::TagLib; use warnings; use strict; if ($#ARGV != 0) { print "Usage: mp3-tagslist.pl mp3file\n"; exit; } my $mp3fn = $ARGV[0]; my $mp3 = Audio::TagLib::MPEG::File->new($mp3fn); my $mp3tag = $mp3->ID3v2Tag(1); my $list = $mp3tag->frameList(); my $iter = $list->begin(); for (my $i = 0; $i < $list->size(); $i++) { my $id = $iter->data()->frameID(); my $iter2 = $id->begin(); my $idstr = ""; for (my $j = 0; $j < $id->size(); $j++) { $idstr .= $$iter2; $iter2++; } my $data = $iter->data()->toString()->toCString(); print "$idstr = $data\n"; $iter++; } |
Here’s what the output looks like:
<10 home->bin> id3v24view.pl 02.\ Busy\ Child.mp3
TIT2 = Busy Child
TPE1 = The Crystal Method
TALB = Vegas
COMM = Ripped with EAC 0.95 beta 2
TRCK = 2
TSOP = Crystal Method
TXXX = [MFPLAYLIST] Dance,Mike,NewWave,Popular,Techno,
Example 2: Converting Vorbis tags (the common ones, and a few custom ones) from FLAC files to the corresponding ID3v2.4 tags in MP3 files
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | #!/usr/bin/perl -w # # Copy all our tags from a FLAC file to an MP3 file. # use Audio::TagLib; use warnings; use strict; if ($#ARGV != 1) { print "Usage: flac2mp3-tags.pl flacfile mp3file\n"; exit; } my $flacfn = $ARGV[0]; my $mp3fn = $ARGV[1]; my $flac = Audio::TagLib::FLAC::File->new($flacfn); my $mp3 = Audio::TagLib::MPEG::File->new($mp3fn); my $flactag = $flac->xiphComment(0); $mp3->strip(); my $mp3tag = $mp3->ID3v2Tag(1); my $mp3tag1 = $mp3->ID3v1Tag(1); $mp3tag->setTitle($flactag->title()); $mp3tag->setArtist($flactag->artist()); $mp3tag->setAlbum($flactag->album()); $mp3tag->setComment($flactag->comment()); $mp3tag->setGenre($flactag->genre()); $mp3tag->setYear($flactag->year()); $mp3tag->setTrack($flactag->track()); $mp3tag1->setTitle($flactag->title()); $mp3tag1->setArtist($flactag->artist()); $mp3tag1->setAlbum($flactag->album()); $mp3tag1->setComment($flactag->comment()); $mp3tag1->setGenre($flactag->genre()); $mp3tag1->setYear($flactag->year()); $mp3tag1->setTrack($flactag->track()); # Add some custom fields my $field; # Performer sort order my $str = Audio::TagLib::String->new("ARTISTSORT"); if ($flactag->fieldListMap()->contains($str)) { my $text = $flactag->fieldListMap()->getItem($str)->toString(); my $bv = Audio::TagLib::ByteVector->new("TSOP"); $field = Audio::TagLib::ID3v2::TextIdentificationFrame->new($bv, "Latin1"); $field->setText($text); $mp3tag->addFrame($field); } # Custom text entries for the TXXX fields foreach my $tagname ( ("DATEADDED", "MFPLAYLIST") ) { $str = Audio::TagLib::String->new($tagname) if ($flactag->fieldListMap()->contains($str)) { my $text = $flactag->fieldListMap()->getItem($str)->toString(); $field = Audio::TagLib::ID3v2::UserTextIdentificationFrame->new("Latin1"); $field->setDescription(Audio::TagLib::String->new($tagname)); $field->setText($text); $mp3tag->addFrame($field); } } $mp3->save(); |
I’ve been writing a lot more Javascript recently, and have occasionally run into variable and function naming conflicts, where I pick a name, not realizing that some other Javascript library that I’m using already has a function or variable with that name. This leads to a lot of troublesome, unnecessary debugging.
For a while, I used the old-fashioned technique of prefacing all my variable and function names with something fairly unique, like my initials. So instead of a variable named “i” I would have “mdf_i”. Messy, and ugly, and more than half the time I would forget to do it.
Doing a Google search for “javascript namespace” brought back quite a few results of the type:
After looking through the suggestions, I’ve started using Dustin Diaz’s solution. Here’s a quick example script I put together that shows how it works, using a new object “mdf” that contains all my code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | var mdf = function() { // This can be accessed only from methods inside mdf var vtest1 = 'vtest1'; // This can be accessed only from methods inside mdf function ftest1() { alert('inside ftest1'); } // Public methods are part of the returned object: return { // mdf.ftest2() can be accessed from the main javascript // scope or from another mdf method ftest2 : function() { alert('inside ftest2'); }, // To execute a public method of mdf, specify the mdf object ftest3 : function() { mdf.ftest2(); }, // To access a private function or variable, use it normally ftest4 : function() { ftest1(); alert(vtest1); } }; }(); alert(vtest1); // This will fail ftest1(); // This will fail mdf.ftest4(); // This will succeed |
Time to celebrate! At long last, Google has added IMAP support to Gmail. This means that instead of having to use Gmail’s web interface, I can finally use Apple’s fine Mail.app program to read my mail on both my Macs, and the mail will stay in sync between the two.
When I signed up for Gmail a year or so ago, I checked for IMAP support and saw it was missing. I checked again about 4 months ago and there was nothing. Just for fun, I did a search today for “gmail imap” and got some hits! Coincidentally, IMAP support was just announced yesterday, and is being rolled out to users over the next couple days. Many people report that it’s working for their accounts already, though it’s still missing from mine. That’s OK, I’ll try tomorrow.
While doing some testing a few days ago, I discovered that my web site was completely failing to load in Internet Explorer. I think the problem started a couple weeks ago when I upgraded WordPress to version 2.3. It seemed to be a bad Javascript interaction between WP2.3 and one of the plug-ins I was using, NextGEN Gallery. Disabling NextGEN Gallery resolved the problem. Now I am looking for a new way to insert images into my posts, but at least the page works again!
Well, NextGEN Gallery had nothing to do with it. Microsoft’s Script Debugger was pointing the finger at my “accordion” Javascript used for the right-hand sidebar. I troubleshot that for while, and eventually removed it entirely, but was still getting errors. The root cause ended up being the “intelligent” visual editor in WordPress. When I wrote the blog entry about Iggy, I copied and pasted the story into the blog from my Gmail e-mail window. Well, it appears that the e-mail wasn’t plain-text as it looked, but had gobs of embedded Javascript in it. If you use Gmail, you can right-click on your message text, and choose “This Frame -> View Frame Source” to see the mess. When I copied and pasted from Gmail into WordPress, the visual editor in WordPress displayed the text just fine, hiding from me the fact that there was lots of Javascript embedded within the text — Javascript that won’t function correctly outside of a Gmail window.
I’ve tried year after year to move to a visual HTML editor for web site work, but this has convinced me that it’s just not feasible. Time to go disable the editor and switch back to editing raw HTML. On the bright side, at least I can re-enable the NextGEN image gallery plug-in!