Archive for the 'Software Development' Category

Over the past few days, I spent more time than should have been necessary trying to drag songs from iTunes to my application’s dock icon. There is already code out there to help folks handle a drag from iTunes to a custom NSView, but nothing has ever been said about handling a drag from iTunes to your application’s dock icon.

One might think that because you can drag a song out of iTunes into the Finder, and have the file copied there, that you can simply publish support for the public.audio UTI and everything will work fine. Of course, life is not so simple.

A drag out of iTunes puts a few different flavours of data onto the pasteboard—none of which appear to be natively accepted by the dock. The most intriguing of these data items is the one with the ‘itun’ OSType. It is an XML property list that can be stuffed into an NSDictionary and then read from—this is how folks currently access the song’s location in their drag handlers (see the code link above).

Now, in order to support dragging a song from iTunes to your application’s dock icon, life gets somewhat more complicated…

First of all, to handle a pasteboard drag to your application, you must expose a service in your application’s Info.plist. Check out Will Larson’s blog post about handling text drags for more information about how to do this. I started from this point.

One might make the immediate conclusion that you can simply add itun or CorePasteboardFlavorType 0x6974756E to your list of NSSendTypes in your service definition. I did, and I was wrong—neither of these two things will cause your dock icon to accept the drag.

So, after a lengthy discussion with some other developers, I determined a bittersweet workaround. In order for this to work properly, I need you to promise me that you will do exactly as I prescribe in order to accept iTunes dock drags. So, pay close attention.

Because NSSendTypes accepts a list of NSPboardTypes or UTIs, and not OSTypes, we will have to wrap the OSType in a UTI. Unfortunately, we can’t all go around wrapping itun in different UTIs, because this will not work. The first UTI to claim itun will win out over the others, and only one application will accept these dragged songs from iTunes.

So, the ideal situation would be to wait for Apple to expose a UTI from the iTunes Info.plist, but then we would all die holding our breath. Instead, I’m asking you to define this small chunk of the Info.plist for your application.


<key>UTImportedTypeDeclarations</key>
<array>
    <dict>
        <key>UTTypeConformsTo</key>
        <array>
            <string>public.data</string>
        </array>
        <key>UTTypeIdentifier</key>
        <string>org.liscio.itun</string>
        <key>UTTypeTagSpecification</key>
        <dict>
            <key>com.apple.ostype</key>
            <string>itun</string>
        </dict>
    </dict>
</array>

Yep, I’ve gone and named it after myself—org.liscio.itun. That way, you all know where it came from. I can’t go around writing into the public.* domain, or the com.apple.* domain, so I didn’t. Using org.* instead seemed more community-oriented. :)

After you’ve imported this type, add org.liscio.itun to your list of NSSendTypes, and you’re done. Your application should now accept song drops from iTunes. (See below for some troubleshooting tips.)

Note that you’re importing this type definition in your plist, and not exporting it. In fact, all my apps will be importing it as well. Nobody should really own it, as it doesn’t belong to any of us. If Apple ever does decide to add a UTI to wrap the ‘itun’ type, then we’ll all have to change our imported type definition accordingly. I’m OK with that, and it’s really a simple thing to fix…

I hope this helps you folks give your users a better experience in your apps, and I certainly don’t mind a “digital high-five” in your About box if it’s helped you as well. You can even drop me a line to let me know that you’re going to use it. This also serves the purpose in my potentially letting you know if Apple does actually add this UTI, and it’s time to remove my UTI from your plist. ;)

Troubleshooting

I don’t expect this to happen to most of you if you get it right from the get-go, but I figured I might as well add this just in case…

The above change to your Info.plist may not take effect right away. You might have to kick the services system using NSUpdateDynamicServices() (via Ruby or Python is best), quit iTunes, or use some magic incantation of lsregister at the terminal. Sometimes you might even have to reboot and try again. This is a part of life when you’re messing around with UTIs and services during development, unfortunately.

If you’re still having trouble, first try to make sure you can register a simple service using the NSStringPboardType, and that you get called in your app delegate first. Don’t even think of asking me for help until you’ve verified this… ;)

Update: 2009/02/24

I filed a radar, rdar://6616686 for this issue, so it’s tracked appropriately by Apple. I also added it to OpenRadar for all to see: http://openrdar.com/6616686

I’m pretty stoked about releasing TapeDeck 1.0.1 today. It contains some huge audio code overhaulin’ that I started about a week before we shipped 1.0…

Just before we shipped TapeDeck, I noticed there was some utterly horrible skipping during audio playback. I attributed the skipping to a small jump in our CPU usage, right after adding the new Core Animation-powered HUD. It was so bad that TapeDeck was nearly unusable. This caused me to spend a 12h day rewriting our audio playback code (in a branch!).

The new code followed Apple’s recommendation for high-performance glitch-free audio playback. So I created a separate high-priority feeder thread reading chunks of a file’s audio data into a ring buffer that was consumed on the high-priority AU render thread.

I was about 80% done this rewrite when I realized that it was a different bug that caused this unbearable skipping to occur. I don’t recall the specifics, but I think I was re-seeking the file accidentally when some unintentional extra KVO updates got triggered. It was caused by the new HUD changes I checked in—are you surprised? :P

Content with the performance of the audio engine on my test systems (an 8-core Mac Pro, and a PowerBook G4 1.5), we shipped TapeDeck. Unfortunately, it still wasn’t perfect. I got reports that recent MacBook Pro machines were also experiencing the skipping, and some more heavy-duty testing on my PowerBook G4 was causing the skips to show up more often.

Well it was a good thing I kept the rewritten audio engine in a branch! I merged that branch with what we released in 1.0, and completed the last 20% of the rewrite. TapeDeck 1.0.1 contains this new, more efficient playback engine which should take up slightly less CPU time, and skip a lot less frequently (never say never!).

Unfortunately, I can’t say that the skips in playback are completely gone on my PowerBook G4 when you heavily load the system (I was able to get a few skips while doing compiles in Xcode), but they’re greatly reduced. On the other hand, I am unable to get TapeDeck 1.0.1 to skip on my wife’s MacBook Pro, even when pushing it pretty hard by browsing our very large collection of RAW photos in iPhoto—an operation that brought TapeDeck 1.0 to tears.

Anyway, this was the fix that required the largest effort in TapeDeck 1.0.1. I hope it solves the skipping problem completely for our users!

In late September 2007, Dan Sandler had started egging me on to develop a simple audio recorder. I was well within shipping mode for FuzzMeasure 3.0, and couldn’t do much more than talk about it at a high level.

In late November 2007—just before FuzzMeasure 3.0 shipped—Dan and I started talking more seriously about this simple audio recording application that just had to be written. Wrap up the Audio Queue Services API in Objective-C, add a great UI to aid in collecting the tapes, and it shouldn’t take very long at all, right? :)

By mid-December, Dan had some UI sketches which resemble what TapeDeck looks like today. There’s a ‘current’ tape in the player, some cassette recorder controls, and a list of tapes in a box. I had already built a prototype recorder by this point, complete with a tape, rotating spools, and an audio level meter. It looked nothing like the application we shipped. You could record audio straight to m4a files, and it worked fine. Effectively, at this stage, we were running Apple’s sample code with a UI on top.

Over the holidays, I spent a few days implementing the nuts and bolts of the early UI prototype. I wrote the UI a few times using Quartz, then Core Animation, then Quartz again. I probably wrote the bits that rendered the tape and its labels three times during this period.

In January, I had been busy with my job, and planning to leave (because FuzzMeasure was doing great, and I thought we had a real winner on our hands with TapeDeck). However, in every spare moment I could get, I would squeeze in fixes for FuzzMeasure, and code for TapeDeck. It was during this time that I decided I should get started on writing MP4 tags to the recorded files.

This was an extremely complicated undertaking, since Apple doesn’t provide an API to modify metadata on M4A files! So, I ended up writing an entire Objective-C library that is capable of parsing, and manipulating MP4 atoms (or boxes, depending on who you talk to), with the addition of the magic bits of metadata that work in iTunes. This was a very difficult piece of the TapeDeck puzzle, and it was fun to get working!

In early February, I left my job to work on SuperMegaUltraGroovy full-time. I spent some quality time with FuzzMeasure, cleaning up some bugs, and took a bit of a vacation from TapeDeck work.

By late February, Dan had sent me a mockup that looks very much like what we see today in the shipping version of TapeDeck. Initially, I wondered how on earth we would implement this–custom UI code, custom window drawing, etc. It seemed like a nightmare, but I put my head down and went for it.

Throughout March, Dan worked on our type logo for TapeDeck, and refined our plastic cassette rendering. I had the UI starting to look very much like the prototype at this point. Lots of work was done internally to support the box of tapes, and manage the collection with Core Animation–the UI that was written over the holidays was reworked again!

In early April, I had started playing with the speaker grilles. In order to really get the look of a cassette recorder right, there had to be speakers above the area where the tape sits. We had a few mockups with the speakers sitting somewhere between the tape and the controls, but once I drew up the grille pattern you see today, we had a winner. When the tape box was closed, TapeDeck really looked like a cassette recorder. Also, adding height meant the user could see more tapes at a glance, which was a nice end result.

By mid April, TapeDeck looked very much like TapeDeck 1.0 does. We were tweaking all sorts of the UI, and finishing off the functionality. The Design Awards deadline was announced, and I openly wondered if we could ship on time. I figured we might as well go for it, because external schedule pressure is always effective to maximize my use of time. :)

During this period, I got to the part where we wanted to implement seeking and FFWD/REW functionality. This is where my use of Audio Queue Services had to end. We wanted our tapes to play very quickly when seeking through the tape (in both directions!), which meant we had to do some trickier things within the audio stream. So, I took a week to rewrite the audio playback engine so that we could support full FFWD/REW functionality, as well as the accurate seeking of the audio stream.

I also decided that we really had to render an accurate image of the tape in the album art of each m4a file. This was an interesting thing to pull off using Core Animation, but the results were amazing when it was done.

In late April, TapeDeck began its beta. Tons of feedback came in, and we actually added the pause functionality in response to user feedback. I was coding at an amazing level of efficiency during this time.

Dan sent a mockup of a new HUD layout (recording and playback controls) to me on Friday, May 3rd, and I didn’t receive it until I woke up on Saturday, May 4. When I went to bed that same night, I had implemented the entire thing from the ground-up using Core Animation, borrowing bits of code from the old HUD. A great deal of this incredible implementation speed is due to Dan’s amazingly detailed mockups. He had effectively written a full specification with clear instructions about how each component would behave, alongside the detailed drawings.

In the last stages of the beta, tons of little bug fixes went in, and smaller aspects of the UI were cleaned up. Dan quickly put together our launch website, and threw a little easter egg into TapeDeck. We were smoking towards completion, and by May 8th I felt we’d done the impossible - we made it to 1.0!

I sent a copy to a few key folks in the media, and they all seemed to really like it! From there, the launch has just been incredible. We’re getting a lot of really positive feedback about TapeDeck, and we’re very proud of what we’ve done. You will find a tally of our media coverage in Dan’s blog post about TapeDeck 1.0.

Introduction

Improving FuzzMeasure to take advantage of Leopard under the hood was an exercise in patience and self-discipline. When I first learned about Objective-C 2.0, I wanted to just leap in and change every single accessor to use properties and turn for into for-each loops.

However, I didn’t have the time to do this, and I was smarter than that — mucking around in code that already works isn’t necessary. That said, there are benefits to ripping out code when you have the opportunity — less code means less bugs!. Also, converting to Objective-C 2.0 buys you other benefits behind the scenes, such as enhanced thread safety, proper memory management, faster enumeration, etc.

How does the one-man shop deal with balancing this lack of time with a mounting pile of important, but not time-sensitive, work? You page it in!

Read the rest of this entry »

I went to WWDC this year, and really enjoyed my time there. I got a lot of code written, a handful of questions answered, some new contacts at Apple, and a whole lot of motivation to keep innovating with FuzzMeasure. Sure, there were the keynotes, sessions, announcements, etc., but I found the real benefit was in the interactivity.

I met a lot of folks that I’ve been speaking to for years online, and some that I’ve never otherwise interacted with before, which added a whole other dimension of awesome to my trip. You can gain a whole lot just from attending the various parties and chatting with people in a laid-back setting over a beer (or a whole lot of fantastic tequila…).

On the more official front, you really do gain a lot from speaking directly with Apple’s engineers. Banging your head against a wall trying to figure out why an API isn’t quite doing what it’s supposed to can be very frustrating. Thus, having an engineer crack his laptop, check the source code, and tell you what’s going on under the hood is simply priceless. The labs are totally where it’s at.

If I could have done anything differently, I would have been far more prepared with questions and issues in advance. I found myself trying to create as many problems as I could while at the show so I had some more tricky issues to get sorted out as I hit them. That said, I think I got along just fine with the list of questions I encountered over the years, and I got a lot of names and email addresses to fire questions to in the future.

So, to those of you using FuzzMeasure, you have a lot to look forward to.