Question

I'm creating a piano app (for OSX) that has an onscreen keyboard that displays what the user is playing on their synth keyboard. Everything is connected using the CoreMIDI framework. I have created some customs buttons by subclassing NSButton in a class called PianoKey. The class can be seen below:

#import "PianoKey.h"

@implementation PianoKey

//updates the core animation layer whenever it needs to be changed
- (BOOL)wantsUpdateLayer {
    return YES;
}

- (void)updateLayer {
    //tells you whether or not the button is pressed
    if ([self.cell isHighlighted]) {
        NSLog(@"BUTTON PRESSED");
        self.layer.contents = [NSImage imageNamed:@"buttonpressed.png"];
    }
    else {
        NSLog(@"BUTTON NOT PRESSED");
        self.layer.contents = [NSImage imageNamed:@"button.png"];
    }
}

@end

The "PianoKeys" are created programmatically. When unpressed, the piano keys are blue and when they are pressed they are pink (just temp colours). The colour switch works fine if I click the buttons on screen, but they are not changing when I try to play through my MIDI keyboard.

Some things to note: 1) The MIDI keyboard works 2) I am successfully getting MIDI data from the keyboard.

a) This MIDI data is passed to a callback function called midiInputCallback as seen below: [from class AppController.m]

void midiInputCallback(const MIDIPacketList *list, void *procRef, void *srcRef) {
    PianoKey *button = (__bridge PianoKey*)procRef;

    UInt16 nBytes;
    const MIDIPacket *packet = &list->packet[0]; //gets first packet in list

    for(unsigned int i = 0; i < list->numPackets; i++) {
        nBytes = packet->length; //number of bytes in a packet

        handleMIDIStatus(packet, button);
        packet = MIDIPacketNext(packet);
    }
}

The object button is a reference to the button that I've created programatically as defined in AppController.h as:

@property PianoKey *button; //yes, this is synthesized in AppController.m

b) The callback function calls a bunch of functions that handle the MIDI data. If a note has been detected as being played, this is where I set my custom button to be highlighted...this can be seen in the function below:

void updateKeyboardButtonAfterKeyPressed(PianoKey *button, int key, bool keyOn) {
    if(keyOn)
        [button highlight:YES];
    else
        [button highlight:NO];

    [button updateLayer];

}

[button updateLayer] calls my updateLayer() method from PianoKey, but it doesn't change the image. Is there something I'm missing? It seems as if the view or window is not being updated, even though my button says that it is "highlighted". Please help! Thanks in advance :)

Was it helpful?

Solution

Just a guess (but a good one...;-) Is the callback function being called on the main thread? If not, that could be the problem - at leas that is the way it works in iOS: the UI (screen drawing) is only updated in the main thread.

Put a log statement or breakpoint in the callback to see if it is getting called. If it is, then the problem is due to this thread issue.


Update:

So tell the button to update itself - but on the main thread (I think I have the syntaxt right - at least for iOS - OSX may vary)

[button performSelectorOnMainThread:@selector(updateLayer) 
                         withObject:nil 
                      waitUntilDone:FALSE];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top