【问题标题】:CoreMidi : logging received midi messages to an NSTextFieldCoreMidi : 将收到的 midi 消息记录到 NSTextField
【发布时间】:2012-01-09 21:57:38
【问题描述】:

对不起,我不会说英语(我正在使用谷歌翻译)。

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

我使用CoreMidi,我能够将应用程序连接到所需的输入并接收Midi消息(我可以使用NSLog打印它们)。我如何在NSTextField 中输出这些消息(与我在NSLog 中可以阅读的相同)?

我设置了一个属性,@synthesized 它并连接了 Interface Builder 中的 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);
}

提前感谢您提供任何可以帮助我的信息。

【问题讨论】:

  • 你能贴出你用来注册回调和回调函数本身的代码吗?
  • 非常感谢您阅读我 :) 添加代码(希望以正确的方式)

标签: xcode cocoa coremidi


【解决方案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() 函数 inside 您的 MyDocument.m 文件。这是完全不正确。如果您使用的是基于 Cocoa 文档的应用程序,则根本不应该更改 main() 函数,除非在极少数情况下。

相反,您应该在 NSDocument‑windowControllerDidLoadNib: 方法中进行所有 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

【讨论】:

  • 完美!!非常感谢你:D
  • 有效吗? MIDIGetNumberOfDevices 返回所有设备的数量(源和目标),但 MIDIGetSource 应该在 0..MIDIGetNumberOfSources-1 的范围内工作。你为什么还要调用 MIDIObjectGetProperties?
猜你喜欢
  • 2015-12-14
  • 2020-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-31
  • 2022-01-22
相关资源
最近更新 更多