2012-01-09 45 views
2

對不起,我不會說英語(我正在使用Google翻譯)。CoreMidi:將收到的midi消息記錄到NSTextField

我是Xcode的新手。我試圖編寫一個應用程序,可以收聽收到的MIDI信息並在NSTextField中顯示它們(就像MIDI顯示器一樣)。

我使用CoreMidi,我可以將應用程序連接到所需的輸入並接收Midi消息(我可以使用NSLog打印它們)。如何在NSTextField中輸出這些信息(我可以在NSLog中讀到的信息相同)?

我設置了一個屬性,@synthesize d it並連接了Interface Builder中的NSTextField,但是從midi回調函數我無法訪問它(它說「Undeclared」)。

下面的代碼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); 
} 

預先感謝您的任何信息,可以幫助我。

+0

你能後您使用註冊回調和回調函數本身的代碼? – 2012-01-10 02:11:40

+0

非常感謝您閱讀我:) 添加代碼(希望以正確的方式) – 8003130124464 2012-01-10 08:38:31

回答

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()函數內部MyDocument.m文件中。那是完全和完全不正確。如果您使用的是基於Cocoa文檔的應用程序,則除非極少數情況下,您不應該更改main()函數。

相反,您應該在‑windowControllerDidLoadNib:方法NSDocument中執行所有MIDI設置,當文檔的窗口已加載並且插口已保證準備好時調用此方法。

事情是這樣的:

@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 
+0

它是完美的! 謝謝sooooo much:D – 8003130124464 2012-01-11 19:52:51

+0

它工作? MIDIGetNumberOfDevices返回所有設備(源和目標)的數量,但MIDIGetSource應在0..MIDIGetNumberOfSources-1範圍內工作。 無論如何,你爲什麼要調用MIDIObjectGetProperties? – 2014-02-20 11:38:52