CoreMidi: регистрация полученных миди-сообщений в NSTextField

Извините, я не говорю по-английски (я использую Google Translate).

Я очень новичок в Xcode. Я пытаюсь написать приложение, которое может прослушивать полученные миди-сообщения и показывать их в NSTextField (так же, как миди-монитор).

я использую CoreMidi и я могу подключить приложение к нужному входу и получать сообщения Midi (я могу распечатать их, используя NSLog). Как я могу вывести это сообщение (те же, которые я могу прочитать в NSLog) в NSTextField?

Я установил свойство, @synthesizeСделайте это и подключите NSTextField в Интерфейсном Разработчике, но из функции обратного вызова midi я не могу получить к ней доступ (она говорит "Необъявленный").

Вот код в MyDocument.h

@property (retain,nonatomic) IBOutlet NSTextField *test_messages;

Вот код в MyDocument.m

@synthesize test_messages;

void midiInputCallback (const MIDIPacketList *list, void *procRef, void *srcRef) {   
id POOL = [[NSAutoreleasePool alloc] init];
UInt16 nBytes;
NSString *ric;
const MIDIPacket *packet = &list->packet[0];
for (unsigned int i = 0; i < list->numPackets; i++) {
    nBytes = packet->length;
    UInt16 iByte, size;

    iByte = 0;
    while (iByte < nBytes) {
        size = 0;
        unsigned char status = packet->data[iByte];
        if (status < 0xC0) {
            size = 3;
        } else if (status < 0xE0) {
            size = 2;
        } else if (status < 0xF0) {
            size = 3;
        } else if (status < 0xF3) {
            size = 3;
        } else if (status == 0xF3) {
            size = 2;
        } else {
            size = 1;
        }

        switch (status & 0xF0) {
            case 0x80:
                ric = @"Note Off";
                break;

            case 0x90:
                ric = @"Note On";
                break;

            case 0xA0:
                ric = @"Aftertouch";
                break;

            case 0xB0:
                ric = @"Control change";
                break;

            case 0xC0:
                ric = @"Program Change";
                break;

            case 0xD0:
                ric = @"Channel Pressure";
                break;

            case 0xE0:
                ric = @"Pitch Wheel";
                break;

            default:
                ric = @"Unk";
                break;
        }
        //TEST HERE
        [test_messages setStringValue:@"TEST TEST"]; //THIS GET "test_messages undeclared (first use in this function)"
        iByte += size;
    }
    packet = MIDIPacketNext(packet);
}
[POOL release];
}

int main(int argc, char *argv[]) {
MIDIClientRef midiClient;
MIDIEndpointRef src; 

OSStatus result;


result = MIDIClientCreate(CFSTR("MIDI client"), NULL, NULL, &midiClient);
if (result != noErr) {
    NSLog(@"Errore : %s - %s",
          GetMacOSStatusErrorString(result), 
          GetMacOSStatusCommentString(result));
    return 0;
}

result = MIDIDestinationCreate(midiClient, CFSTR("Porta virtuale"), midiInputCallback, NULL, &src);
if (result != noErr ) {
    NSLog(@"Errore : %s - %s",
          GetMacOSStatusErrorString(result), 
          GetMacOSStatusCommentString(result));
    return 0;   
}

MIDIPortRef inputPort;
result = MIDIInputPortCreate(midiClient, CFSTR("Input"), midiInputCallback, NULL, &inputPort);

ItemCount numOfDevices = MIDIGetNumberOfDevices();

for (int i = 0; i < numOfDevices; i++) {
    MIDIDeviceRef midiDevice = MIDIGetDevice(i);
    NSDictionary *midiProperties;

    MIDIObjectGetProperties(midiDevice, (CFPropertyListRef *)&midiProperties, YES);
    MIDIEndpointRef src = MIDIGetSource(i);
    MIDIPortConnectSource(inputPort, src, NULL);
}

return NSApplicationMain(argc, (const char **) argv);
}

Заранее благодарю за любую информацию, которая может мне помочь.

1 ответ

Решение

Основная проблема, с которой вы столкнулись, заключается в том, что вы предполагаете, что функция обратного вызова MIDI "знает" о вашем MyDocument класс и может получить доступ к его свойствам. К сожалению, это не так. Функции C не имеют внутренней информации о состоянии, единственный способ передать информацию в функцию - передать ее в качестве аргумента.

Это то, что все void* refCon аргументы есть в документации. refCon является общим указателем, который вы можете использовать, чтобы передать ссылку на какой-то другой объект в вашу функцию.

Например, документы показывают подпись для MIDIInputPortCreate() функционировать следующим образом:

extern OSStatus MIDIInputPortCreate(
    MIDIClientRef client, 
    CFStringRef portName, 
    MIDIReadProc readProc, 
    void *refCon, 
    MIDIPortRef *outPort ); 

В вашем конкретном случае вы должны передать ссылку на ваш MyDocument объект как refCon параметр. В настоящее время вы проходите NULL,

MIDIPortRef inputPort;
result = MIDIInputPortCreate(midiClient, 
    CFSTR("Input"), 
    midiInputCallback, 
    myDocument,  //note the change
    &inputPort);

Затем в вашем обратном вызове вы можете получить доступ к объекту документа и, следовательно, к его свойствам:

void midiInputCallback (const MIDIPacketList *list, void *procRef, void *srcRef) 
{
    //do some MIDI stuff here

    //get a reference to your document by casting the void* pointer
    MyDocument* myDocument = (MyDocument*)procRef;
    //log the message to the text field
    [myDocument.test_messages setStringValue:@"TEST TEST"];
}

Так и должно быть. Тем не менее, в вашем коде выше у вас, кажется, есть main() функционировать внутри вашего MyDocument.m файл. Это совершенно и совершенно неверно. Если вы используете приложение на основе документов Какао, вы не должны изменять main() функционировать вообще, за исключением очень редких обстоятельств.

Вместо этого вы должны выполнить все настройки MIDI в ‑windowControllerDidLoadNib: метод NSDocument, который вызывается, когда окно документа загружено и выходы гарантированно будут готовы.

Что-то вроде этого:

@implementation MyDocument
@synthesize test_messages;

void midiInputCallback (const MIDIPacketList *list, void *procRef, void *srcRef) 
{
    //do some MIDI stuff here

    //get a reference to your document by casting the void* pointer
    MyDocument* myDocument = (MyDocument*)procRef;
    //log the message to the text field
    [myDocument.test_messages setStringValue:@"TEST TEST"];
}    

‑ (void)windowControllerDidLoadNib:(NSWindowController*)windowController
{
    //set up midi input
    MIDIClientRef midiClient;
    MIDIEndpointRef src; 

    OSStatus result;

    result = MIDIClientCreate(CFSTR("MIDI client"), NULL, NULL, &midiClient);
    if (result != noErr) {
        NSLog(@"Errore : %s - %s",
              GetMacOSStatusErrorString(result), 
              GetMacOSStatusCommentString(result));
        return 0;
    }

    //note the use of "self" to send the reference to this document object
    result = MIDIDestinationCreate(midiClient, CFSTR("Porta virtuale"), midiInputCallback, self, &src);
    if (result != noErr ) {
        NSLog(@"Errore : %s - %s",
              GetMacOSStatusErrorString(result), 
              GetMacOSStatusCommentString(result));
        return 0;   
    }

    MIDIPortRef inputPort;
    //and again here
    result = MIDIInputPortCreate(midiClient, CFSTR("Input"), midiInputCallback, self, &inputPort);

    ItemCount numOfDevices = MIDIGetNumberOfDevices();

    for (int i = 0; i < numOfDevices; i++) {
        MIDIDeviceRef midiDevice = MIDIGetDevice(i);
        NSDictionary *midiProperties;

        MIDIObjectGetProperties(midiDevice, (CFPropertyListRef *)&midiProperties, YES);
        MIDIEndpointRef src = MIDIGetSource(i);
        MIDIPortConnectSource(inputPort, src, NULL);
    }
}

@end
Другие вопросы по тегам