FlexTime 1.0b6

July 24th, 2006

The feedback I’ve gotten on FlexTime since releasing the last beta almost two weeks ago has been simply amazing. I really appreciate all the thoughtful comments people have made. While there has been some scrutinizing the limited feature set of this initial version, most of the feedback has focused on eliminating annoying bugs and making minor tweaks to improve the usability of the product.

I’m really happy with how things are progressing.

FlexTime 1.0b6 contains a number of fixes directly motivated and inspired by beta testers. Please note before downloading that the document format has changed in this release. To ease the transition, I’m including a “Document Upgrader” droplet that should take care of any old documents you have created with previous beta releases. BUT you should update all of your documents and never look back at older FlexTime releases. Please, do it for the children.

Update: Andy Lee found a heinous UI problem in 1.0b6 that prevented repeating cues from being set on new activities. Ouch! I’ve made a quick fix and upped the download to 1.0b7.

Click Here to download FlexTime 1.0b7.
Reminder: Be sure to drag your old documents to the upgrader droplet!

So now that we have the big scary document change out of the way, what else is in store with this release? Here are my notes:

- Revised About Box 
- Custom document icon
- Add "Repeat when finished" checkbox to main document window
- Add a "Remember Layout" menu item to save info in document:
	- Window frame
	- Table column order and sizes
- Scriptability
	- Added support for a "reset" command
	- Added scriptable access to the "running" attribute
		of a routine document
			- Caveat: cannot stop/resume from within a cue
			handler script at this time.
	- Added a "Display Message" command to give script access
		 to the text message mechanism
	- Early support for a special handler in AppleScript cues.
		HandleFlexTimeCue(theDocument, theActivity) gets called
		if exists within the cue script
- Fix a bug that prevented scripts from being chosen for Run Script
- Fix crashing bugs with "Speak Text" cues
- Fix a crash that could occur when a script alias was stale
- New documents now start out with a single default activity
- Open documents are now preserved across launches

Executive summary? Lots of bug fixes, a slightly cleaned up UI, and a controversial “Remember Layout” feature. Let me say now that this item is probably going to disappear before 1.0 final, so don’t get too used to it. The idea is that some FlexTime routines are suited to a particular UI layout and that layout should be remembered by the application. The question is whether the remembering should be automatic or explicit. I think I’ve been convinced that it should be automatic, and be saved in the user’s preferences instead of the document itself. If you’ve got strong feelings about this, now is the time to make your case.

So what’s left before it goes final? Mostly just documentation and finishing up the AppleScript support. Keep your eyes peeled for a final release within the next few weeks.

Iron Coder Three

July 23rd, 2006

The third Iron Coder concluded tonight. Mark Dalrymple emerged victorius! I’m really happy for Mark’s win because his entry was fun, and because it was clear he latched on to the opportunity to use the contest as an opportunity to really learn more about the Mac OS X APIs. I also have a lot of faith that Mark will be a good “chairman” for the next contest.

I came in 3rd or 4th, depending on how you do the math. My entry this time is called What Have You Done For Me Lately. It’s as a relatively snazzy (IMHO) application that gives the user a visual impression of how much time they’re spending in each application over a given period of time. Since the API for this contest was “CoreGraphics,” I used it as an excuse to play with patterns, which I use to fill the bar-graphs.

Download all the entries for the latest contest in a single DMG! Click here. (Thanks to Jon Wight for putting this together).

I take a different approach to Iron Coder than many of the contestants. There is a tendency to make “something cool” at the expense of refinement. These submissions are awesome! They make me laugh and stun me with their creativity. But I stubbornly cling to a different ideal. I use the event as an opportunity to play “what if a product’s entire cycle was 48 hours.” Now, I won’t go so far as to say my submission is bug-free, but I try to make it “release-ready.” That means I hold off adding features if the time limit doesn’t support it. I like the constraints that a 48 hour deadline gives me for producing a “relatively shippable product.” Instead of asking myself how much I can possibly cram into the submission, I ask myself how much can I make “user-ready” and try to accomplish that.

At the end of 48 hours (with time out for sleep, family, and social commitments), what I submit is not exactly product material, but something I can proudly attach the Red Sweater brand to.

The Case of the Missing Check

July 20th, 2006

Things are getting ever-nearer with FlexTime, but as is the case with every project of relative complexity, the number of niggling details expands in proportion to the number of days of development ostensibly remaining.

Today I spent a lot longer than I wish I had working on a confounding bug report with my NSToolbar implementation: the contextual menu doesn’t update to reflect the changed state. If you control or right-click in any standard Cocoa application’s toolbar, you’ll see a contextual menu containing common configuration options. The indicate the enabled state of these options, a check mark is supposed to appear next to them. This is what the menu looks like in FlexTime 1.0b5:

No check marks! Obviously the icons are small, and I’m seeing “Icon and Text.” Something is definitely wrong here. But I’m not responsible for managing this menu. Who is?

If you guessed NSToolbar, don’t feel stupid. I guessed that too, but we’re wrong. My first reaction in debugging this problem was to search the documentation for NSToolbar to see if there are any funny delegate methods I’m supposed to implement. NSToolbar has nary a mention at all of even the existence of this contextual menu! Before getting too deep, I tried a few web searches, to see if anybody else had already run into the problem. I found this from 2001 describing an identical problem. And this from last year following up on it. But where is the fix?

The next phase in my debugging process was to start hacking out code until things work. I figured there must be something funny in my delegate methods, so I just unhooked the delegate and let ‘er rip. Result? An empty toolbar with exactly the same problem.

The Xcode developer tools ship with a source code example, Simple Toolbar (local link). I was running out of patience so I decided to attack the problem from the complete opposite direction. Instead of asking “why doesn’t my menu work right?” I would ask “why do all the other ones work right?” Who is responsible for setting those damn check marks, anyway?

From the SimpleToolbar project, I first launched and confirmed that the check marks show up as expected. Then, I “Paused” execution to get access to the gdb console, and set a breakpoint:

(gdb) b -[NSMenuItem setState:]
Breakpoint 6 at 0x9373b5f8
(gdb)

Somebody is setting those checkmarks, and unless they’re doing it in an extremely unsanctioned way, I’ll catch them at NSMenuItem’s setState: method. The only problem is everytime I “continue,” it reactivates the application and causes my breakpoint to be hit at a spot where I don’t care about it. Once the breakpoint hits I’m stuck back in the debugger and never get a chance to right-click in the toolbar! This is overcome with a simple conditional on the breakpoint. It turns out that it’s just one menu item that is always getting set when I activate the app, so I configure gdb to skip it in that case:

Breakpoint 6 at 0x9373b5f8
(gdb) po $r3
<MenuItem: 0x36b660 Untitled>
(gdb) cond 1 $r3!=0x36b660
(gdb) c

Now I’m able to right-click and provoke the anticipated action. I’m interrupted again by the debugger, this time pointing at one of my items of interest:

Breakpoint 6 at 0x9373b5f8
(gdb) po $r3
<MenuItem: 0x3ab160 Icon & Text>
(gdb) bt
#0 0x9373b5f8 in -[NSMenuItem setState:] ()
#1 0x9382cf90 in -[NSWindow validateUserInterfaceItem:] ()
#2 0x9382ca04 in -[NSWindow validateMenuItem:] ()
#3 0x937e931c in -[NSMenu _enableItems] ()
#4 0x937e72d4 in AppKitMenuEventHandler ()

Note: you don’t have to use commands like “bt” at this point, since Xcode’s debugger is mimicking the same thing in the UI, but once I get started in the console I tend to stay there for a while.

So what have we learned by now? NSToolbar is not responsible for setting those menu item states, NSWindow is! It makes a sort of sick sense, if you consider the overlapping responsibilities of the toolbar and the window. After all, the window is what you “set” a toolbar on. The window has to know how to collapse and expand it. It’s a sick little behind-the-scenes game that fortunately we don’t have to see much of.

So why isn’t validateUserInterfaceItem: called in my app? I set a breakpoint on the method of NSWindow and it never triggers. Hmm, suspicious indeed. But when I, on a whim, search for the method in my sources, my memory is thankfully jostled. Of ourse! FlexTime uses a subclass of NSWindow, and within it, overrides validateUserInterfaceItem! It’s been a while since I did this, so it’s not fresh in my mind. This is what the method looks like:

// MENU Validation
- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
{
	BOOL validItem = YES;

	// We use validation to keep the state of NSMenu targets up to date
	SEL thisSelection = [anItem action];
	if ((thisSelection == @selector(undo:)) ||
		(thisSelection == @selector(redo:)))
	{
		validItem = (mUndoDisabled == NO);
	}
	return validItem;
}

At some point I figured out that if I override NSWindow’s menu validation, I get a chance to disable undo and redo. I wanted to do this because it turns out to be very inconvenient timing for a user to suddenly try to “undo” during the middle of a FlexTime routine playback. So what’s wrong with the above method? It cuts NSWindow out of the loop.

Simply adding a “[super validateUserInterfaceItem:anItem]” call at the top of the method does the trick functionally, but it leaves me with a bad taste in my mouth. Specifically, a bad compiler warning. The method is defined as the sole member of the formal NSUserInterfaceValidations protocol, but NSWindow isn’t declared as conforming to that protocol. So I’m stuck in a tough situation. I know my superclass implements this method – I saw it in gdb! I also know that if it doesn’t get its implementation called, my functionality suffers. I decide to code defensively and plan for the best while expecting the worst. If the superclass responds to the method in question, we’ll cast it as being protocol compliant, to satisfy the compiler:

// Let the standard NSWindow validation take place
if ([super respondsToSelector:@selector(validateUserInterfaceItem:)] == YES)
{
	validItem = [(id )super validateUserInterfaceItem:anItem];
}

Now, even in the unlikely event that Apple completely changes the way they handle this, we’ll probably still be safe. Added bonus? No compiler warnings.

But more importantly, the change works! My check marks now appear as expected. One more bug, laboriously down the drain.

How did this bug happen? Several factors played into the mistake on my part. Ultimately it came to an underestimation on my part of the method’s significance to NSWindow. Since NSWindow is not documented as using the method, it was a “lucky break” for me that implementing it in my subclass got me the short-term results I was seeking. But alarm bells should have been going off. I’m overriding a method in a standard system class and seeing interesting results. That means the system must have good reason to call it. I will be much more careful in the future when subclassing and overriding system classes.

The other problems here were with naming convention and habitual expectations. It’s important to remember that, although validateUserInterfaceItem’s main purpose is to enable or disable a part of the UI, its secondary purpose is to allow clients arbitrary access to the UI item. If it was called “validateAndMuckWithUserInterfaceItem,” I might have paused a second or two and thought more carefully about the implications of overriding it. Worse, the typical scenarios where this method is implemented are those where the system has set aside some number of UI elements as being “your responsibility.” I’m not in the habit of needing to defer to the system about whether my UI elements are enabled or not, but in this case by becoming the system, I acquired a whole new set of responsibilities.

Update: Chris Liscio pointed out that the behavior of validateUserInterfaceItem: is to bounce up the responder chain until somebody says “YES” to an item. So another failure here is based no my misunderstanding of the method. It’s well documented, but I never paid close attention:

To validate a control, the application calls validateUserInterfaceItem: for each item in the responder chain, starting with the first responder. If no responder returns YES, the item is disabled.

This means my typical approach to implementing the method needs to change. I have assumed that everything would be enabled unless I said otherwise, when in fact the opposite is true. I should insist that everything I don’t recognize gets a “NO” result from the validation method, as a general rule.

Don’t Speak Twice, It’s All Right

July 17th, 2006

Andy Lee sent me a bunch of excellent feedback about FlexTime, and let me know about a strange, 100% reproducible crashing bug. If you configure FlexTime such that both the ending cue of one activity and the starting cue of the one that follows are “Speak Text” cues, then the application crashes.

First thought: damn I’m glad I put a beta out. Second thought: good lord, what I have done!?

Unfortunately, the bug is not in my code. I was able to reproduce the problem quite easily with the simplest of command line tools:

#import <Carbon/Carbon.h>
main() { Str255 string1 = "\\pHello"; Str255 string2 = "\\pHello Again";
SpeakString(string1); SpeakString(string2); }

You may not have realized that it was quite so simple to accomplish spoken text on a Mac. Unfortunately, the simplicity is deceptive, since compiling and running the above tool results in a nasty crash:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000000
0x0020da94 in MTBEAudioUnitSoundOutput::BufferComplete ()
(gdb) bt
#0  0x0020da94 in MTBEAudioUnitSoundOutput::BufferComplete ()
#1  0x7006e9bc in AUScheduledSoundPlayerEntry ()
#2  0x700097c8 in DefaultOutputAUEntry ()
#3  0x700049d0 in DefaultOutputAUEntry ()
#4  0x700da1a8 in dyld_stub__keymgr_get_and_lock_processwide_ptr ()
#5  0x90bd9d24 in CallComponent ()
#6  0x942647a0 in AudioUnitUninitialize ()
#7  0x94178368 in AudioUnitNodeInfo::Uninitialize ()
#8  0x94178300 in AudioUnitGraph::Uninitialize ()
#9  0x941786a4 in AudioUnitGraph::Dispose ()
#10 0x941785f0 in DisposeAUGraph ()
#11 0x0020e0a8 in MTBEAudioUnitSoundOutput::~MTBEAudioUnitSoundOutput ()
#12 0x0020a78c in SpeechChannelManager::~SpeechChannelManager ()
#13 0x002215f4 in SECloseSpeechChannel ()
#14 0x91997974 in KillSpeechChannel ()
#15 0x91995088 in KillPrivateChannels ()
#16 0x919969d8 in SpeakString ()
#17 0x00002cec in main ()

SpeakString has been around for a long time. Long before Mac OS X and long before CoreAudio, where the crash appears to be happening. I would guess it didn’t used to crash, but when it was ported to Mac OS X, something got overlooked and now it leads to whammy land.

OK, so I how do I work around the problem? It is clearly related to attempting to speak text while some text is already speaking. Maybe if I could coddle the Speech Manager a little bit, I could prevent it from crashing.

From the Speech Synthesis Manager Reference documentation for SpeakString, we see that the behavior for overlapping speech is (supposed to be) very well defined:

“If SpeakString is called while a prior string is still being spoken, the sound currently being synthesized is interrupted immediately. Conversion of the new text into speech is then begun. If you pass a zero-length string (or, in C, a null pointer) to SpeakString, the Speech Synthesis Manager stops any speech previously being synthesized by SpeakString without generating additional speech. If your application uses SpeakString, it is often a good idea to stop any speech in progress whenever your application receives a suspend event. Calling SpeakString with a zero-length string has no effect on speech channels other than the one managed internally by the Speech Synthesis Manager for the SpeakString function.)”

Translation: what we’re doing is supposed to work. But maybe by overdoing it we can achieve the desired goal. If Mac OS X falls down on the “interrupting immediately” behavior, perhaps we can manually stop any previous sound to help it keep its bearings. According to the documentation, calling “SpeakString(NULL)” should effectively cancel playback. Unfortunately, injecting it into my simple crash case changes nothing. Worse, when I add it to my live application, I observe a new failure path. The text “pure virtual method called” is printed to the console, with the following backtrace:

(gdb) x/s $r4
0x52c1ad8 <_zn24mtmbraisedsinecrossfader7scoeffse +5916>:
"pure virtual method called\n" (gdb) bt #0 0x90014ac0 in write () #1 0x052b2814 in MTFEClone::VisitCommand () #2 0x05287604 in MTBEWorker::WorkLoop () #3 0x052866f4 in MTBEWorkerStartMPTask () #4 0x90bc1900 in PrivateMPEntryPoint () #5 0x9002bc28 in _pthread_body ()

Well, this can is getting wormier and wormier. It is starting to look like I won’t be able to take advantage of the ease and simplicity of SpeakString. Ten years ago, sure. But in 2006 SpeakString es muy sucky. It’s probably time to start looking at the more advanced speech API, where I’m responsible for managing my own speech channels. With responsibility also comes (we hope) the ability to save ourselves from certain doom.

But let’s say I just need to stick with SpeakString, because I have a demo in 5 minutes, or users are just screaming bloody murder about this bug. There is a crude workaround that takes all the asynchronous fun out of speech, but also prevents the crash. By explicitly waiting for the Speech Manager to be done with any previous speech, I can prevent it from maiming itself:

#import 
main() { Str255 string1 = "\\pHello"; Str255 string2 = "\\pHello Again";
while (SpeechBusy()) { ; } SpeakString(string1); while (SpeechBusy()) { ; } SpeakString(string2); while (SpeechBusy()) { ; } }

This also “works” in FlexTime, for some definition of “working.” But it can cause hideous stalls in the playback UI, since I’m blocking there for an indeterminate length of time. Passable in a beta release, but not acceptable for a finished product.

Sigh. I’m going to have to do real work. But you don’t have to. RSSafeSpeaker is a simple singleton class designed to make worry-free overlapping speech easy for the Cocoa programmer. Instead of trying to manage a number of open speech channels, this class takes the approach that it’s “good enough” to just allocate and deallocate a channel for every speech made. Obviously for some purposes this will not be suitable, and you’ll want to manage a pool of open channels. For the “everyday, get this done easily” use though, I hope you’ll find this class handy. Rewriting our previous example using RSSafeSpeaker:

NSString* string1 = @"Hello";
NSString* string2 = @"Hello Again";

[[RSSafeSpeaker sharedInstance] speakString:string1]; [[RSSafeSpeaker sharedInstance] speakString:string2];

No crashes! And I get to use NSString. Everything is better. This is a good example of a situation where the shortcomings of Apple’s API caused me grief and made me go to a lot of extra work. But it’s also an example of such a situation where the extra work won’t be for naught. It’s a good idea for me to use the “deeper” speech APIs, because it’s inevitable that I’ll want to have finer control over the playback effects in my application. It was just a lot easier to choose “SpeakString” as the quickest solution. If anything else persnickety comes up, I’ll be in an excellent position to respond quickly and effectively. All in all, time well spent!

Oh, and in case anybody was worried, I did report the crashing bug to Apple (rdar://problem/4633582).

Update: Oh man, don’t I feel like a dork! I somehow missed the presence of NSSpeechSynthesizer, altogether. Thanks to Jim Correia for pointing it out to me via email. It does seem to work, and doesn’t crash. Of course, now that I’ve got the infrastructure in place, I might as well keep using it, since it will ultimately give me more control over the playback options. But NSSpeechSynthesizer does seem a better choice for most purposes.

It looks like each NSSpeechSynthesizer corresponds with a “speech channel,” so if you actually want to overlap voices (instead of just causing the previous speech to be canceled), you’d need to allocate multiple speech synths (similarly to how my RSSafeSpeaker allocates a speech channel for each request).

Thanks again to Jim for sharing this! I am embarrassed to have overlooked it…