edenx update

In the last few months I've had enough free time (i.e. less concerts to shoot) to resume work on edenx. Long before that I had converted most of Rosegarden's Rulers code. This wasn't a port, since I'm using CoreData structures, it meant that all the STL stuff had to be converted to CoreData calls.

For instance, this (full version) :

Event dummy("dummy", 0);
dummy.set(BarNumberProperty, n);

ReferenceSegment::iterator j = std::lower_bound(m_timeSigSegment.begin(), m_timeSigSegment.end(), &dummy, BarNumberComparator());
ReferenceSegment::iterator i = j;

if (i == m_timeSigSegment.end() || (*i)->get(BarNumberProperty) > n) {
if (i == m_timeSigSegment.begin())
i = m_timeSigSegment.end();
else
--i;
} else ++j; // j needs to point to following barline

becomes this (full version) :

NSManagedObjectContext* moc = [self managedObjectContext];
MyDocument* document = [[NSDocumentController sharedDocumentController] currentDocument];

NSEntityDescription *timeSignatureDescription = [NSEntityDescription entityForName:@"TimeSignature" inManagedObjectContext:moc];

NSFetchRequest *timeSignaturesRequest = [[[NSFetchRequest alloc] init] autorelease];
[timeSignaturesRequest setEntity:timeSignatureDescription];

// order by absolute time
//
[timeSignaturesRequest setSortDescriptors:[[document coreDataUtils] absoluteTimeSortDescriptorArray]];

NSError *error = nil;

NSArray *allTimeSigs = [moc executeFetchRequest:timeSignaturesRequest error:&error];
if (allTimeSigs != nil) {

if ([allTimeSigs count] > 0) {

// there are time signatures - find the one right after the bar n
//
BOOL (^checkBarNumberBlock)(id, NSUInteger, BOOL*) = ^ (id obj, NSUInteger idx, BOOL *stop) {
if ([[obj barNumber] intValue] > n) {
*stop = YES;
return YES;
}
return NO;
};

NSUInteger idxOfTimeSigAfterBarN = [allTimeSigs indexOfObjectPassingTest:checkBarNumberBlock];

More recently, I've improved the drawing of the segments on the canvas (and actually introduced the concepts of segments, which at first I had discarded, figuring that just using tracks as is for event container would be enough, but since Garage Band is apparently having those too, I changed my mind).

This led me to learn about Core Animation, which is a pretty darn cool framework. I'm a bit annoyed by the C-based API (mostly out of principle), but given the data structures in play, it's good enough. What is cool about Core Anim is the wealth of effects readily available, and that every change in a CALayer (say a resize) is, well, animated.

When I moved to OS X one of the things I liked was that UI changes never happen instantly, there's always a quick animation smoothing the change from one state to the other. I found this to be much easier on the eye, because you actually perceive the change and can follow what happens, rather than having to find your way in the UI again. I always wondered how this was done, if all apps had intricate code taking care of those changes. Well, turns out it's automagically handled by Core Animation.

For instance if you have a CALayer with a bounds rect of 100.0 witdth (all Core Anim coordinates are floats - another huge bonus), and set it to 200.0, simply setting

rectLayer.bounds = CGRectMake (0.0, 0.0, 200.0, rectHeight);

will result in a rectangle smoothly expanding from 100.0 to 200.0. This is the default behavior but it can be altered in any way you want (faster, slower, grouped with other anims, etc...). Likewise, adding a layer will not flash it instantly in view, but will fade it in.

So I tuned this for edenx. On this screencast I create several segments and resize them :



Finally, I've looked into how to display notes. At first I anticipated that I'd have to "manually" draw the notes through a bunch of Core Graphics commands. Then I remembered that Rosegarden was using Lilypond's note font. Things became much more interesting when I found about CTFontCreatePathForGlyph() : take a font glyph, get a CGPath, which you can draw, transform, etc... to your hearts content. That offered me a quick solution to the note drawing problem (although there are still some fine tuning to do).

Here's a short screencast :

So, to get back to edenx's dev status. I've just reached the point where I'm about to dive into layout code. Looking at RG's, it won't be easy. I think I'll write my own, very basic version instead. Then see about adding/removing notes, after which I may have something engaging enough for volunteers to join in.

One more thing I wanted to talk about. In Rosegarden, we were quickly confronted to the problem of letting know some part of the code that something has changed in data. For instance, if you have two editors opened on the same segment and make some changes in one of them, you need to tell the other editor about that change. Or you're recording and you want the notes to appear immediately in the editor. This led us to write a bunch of code following more or less the Observer Pattern, or to use the Qt signal/slots in some cases. But we never got around to make something generic that would be used in all the code. So I was happy to find that Cocoa has a standard solution for this problem, which are notifications. Yet more code I don't have to worry about, which is a good thing given how much time I can spend on this.