CoreMidi:将收到的midi消息记录到NSTextField

时间:2012-01-09 21:57:38

标签: xcode cocoa coremidi

抱歉,我不会说英语(我正在使用Google翻译)。

我对Xcode很新。我正在尝试编写一个可以收听收到的midi消息并在NSTextField中显示它们的应用程序(就像midi监视器一样)。

我使用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 个答案:

答案 0 :(得分:5)

您遇到的主要问题是您假设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()文件中似乎有<{1}}函数。那 完全错误 。如果您使用的是基于Cocoa文档的应用程序,则除非在极少数情况下,否则不应更改MyDocument.m函数。

相反,您应该使用main() ‑windowControllerDidLoadNib:方法完成所有MIDI设置,该方法在加载文档窗口并保证插槽就绪时调用。

这样的事情:

NSDocument