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