抱歉,我不会说英语(我正在使用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);
}
提前感谢您提供有助于我的任何信息。
答案 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