Re: General CoreMIDI Newbie-Questions
Re: General CoreMIDI Newbie-Questions
- Subject: Re: General CoreMIDI Newbie-Questions
- From: Oliver Jaun <email@hidden>
- Date: Sun, 29 Dec 2002 01:04:35 +0100
Thanks a lot for your response. I know now that I have to do it
myself... :-(
I have to do some reading it seems... Well ok, I already have an idea:
I create an IPC Pipe. After sending the Sysex Message I do a read() on
the pipe. This will block until I read a response...let's say
"deviceInquiryResponse". Additionally I could use a select() system
call to specify a timeout. I guess that should work.
But I have yet another question...It's more a general C/C++ Question:
How can I pass function pointer of a non static function in C++? I'm
asking this regarding the MIDIResponseProc. I attached the source code
of my "application" (very short). Unfortunately I get the following
message if I try to compile it with
g++ -framework CoreAudio -framework CoreMIDI -framework CoreFoundation
-o scuba DataProvider.cpp
DataProvider.cpp:71: invalid use of member
`DataProvider::fileDescriptor' in
static member function
If MyReadProc wasn't static I would not get this message but then I
don't know how to pass a function pointer.
Thanks for your help.
Kind Regards
Oliver Jaun
#include <CoreAudio/HostTime.h>
#include <CoreMIDI/MIDIServices.h>
#include <CoreFoundation/CFRunLoop.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include "DataProvider.h"
DataProvider::DataProvider(int deviceId, int inPort, int outPort, int destination) {
_deviceId = deviceId;
_inPort = inPort;
_outPort = outPort;
_destination = destination;
if(pipe(fileDescriptor) < 0)
printf("pipe error\n");
MIDIClientRef client = NULL;
MIDIPortRef inputPortRef = NULL;
MIDIPortRef outputPortRef = NULL;
_destEndpointRef = MIDIGetDestination(_destination);
Byte buffer[1024];
MIDIClientCreate(CFSTR("Scuba"), NULL, NULL, &client);
MIDIInputPortCreate(client, CFSTR("Input port"),
&MyReadProc, NULL, &inputPortRef);
MIDIOutputPortCreate(client, CFSTR("Output port"), &outputPortRef);
MIDIEndpointRef src = MIDIGetSource(_inPort);
MIDIPortConnectSource(inputPortRef, src, NULL);
}
void DataProvider::getDeviceInquiryMessage() {
Byte data[] = { _SYSEX_MSG, 0x7E, _deviceId, 0x06, 0x01, _EOX };
MIDISysexSendRequest sysx = getSysexStruct(data, 6);
OSStatus status = MIDISendSysex(&sysx);
waitForResponse("deviceInquiryMessage");
printf("fertig\n");
}
void DataProvider::waitForResponse(char* commandName) {
// I'll use select() here later
read(fileDescriptor[0], _line, MAXLINE);
}
MIDISysexSendRequest DataProvider::getSysexStruct(Byte* data, int bytesToSend) {
MIDISysexSendRequest sysx;
sysx.destination = _destEndpointRef;
sysx.data = data;
sysx.bytesToSend = bytesToSend;
sysx.complete = false;
sysx.completionProc = NULL;
sysx.completionRefCon = NULL;
return sysx;
}
void DataProvider::MyReadProc(const MIDIPacketList *pktlist,
void *refCon, void *connRefCon) {
MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
for (unsigned int j = 0; j < pktlist->numPackets; ++j) {
for (int i = 0; i < packet->length; ++i) {
printf("X %c\n", packet->data[i], packet->data[i]);
}
packet = MIDIPacketNext(packet);
}
// later I'll send the respective command name
write(fileDescriptor[1], "blabla\n", 7);
}
int main(int argc, char *argv[]) {
DataProvider* dp = new DataProvider(0, 0, 0, 0);
dp->getDeviceInquiryMessage();
return 0;
}
#ifndef DataProvider_h
#define DataProvider_h
#define MAXLINE 10
class DataProvider {
public:
static const int _SYSEX_MSG = 0xF0;
static const int _EMU_ID = 0x18;
static const int _E4_ID = 0x21;
static const int _EDITOR_BYTE = 0x55;
static const int _EOX = 0xF7; // EOX = End of Sysex
// Commands
static const int _CMD_PARAMETER_MIN_MAX_DEFAULT = 0x04;
static const int _CMD_ASCII_PRESET_NAME = 0x05;
DataProvider(int deviceId, int inPort, int outPort, int destination);
void getDeviceInquiryMessage();
protected:
int _deviceId;
int _inPort;
int _outPort;
int _destination;
int fileDescriptor[2];
char _line[MAXLINE];
MIDIEndpointRef _destEndpointRef;
MIDISysexSendRequest getSysexStruct(Byte* data, int bytesToSend);
static void MyReadProc(
const MIDIPacketList *pktlist, void *refCon, void *connRefCon);
void waitForResponse(char* command);
};
#endif
On Saturday, December 28, 2002, at 10:30 PM, Kurt Revis wrote:
>
On Saturday, December 28, 2002, at 08:49 AM, Oliver Jaun wrote:
>
>
> It is not a problem to send a sysex message. But then how do I get
>
> the response? I know that I can specify a completionProc in the
>
> MIDISysexSendRequest struct. But then how do I wait for the answer?
>
>
The completionProc just tells you about the status of *sending* the
>
sysex message. It doesn't have anything to do with a response. In
>
general the CoreMIDI API provides one-way communication; it's up to
>
you to write code to manage a two-way conversation.
>
>
In order to receive MIDI data, you need to create a MIDI input port
>
and hook it up to one (or more) source endpoints. See
>
/Developer/Examples/CoreAudio/MIDI/SampleTools/Echo.cpp for a brief
>
example.
>
>
> DeviceInquiryMessage DataProvider::getDeviceInquiryMessage() {
>
>
>
> Byte data[] = { _SYSEX_MSG, 0x7E, _deviceId, 0x06, 0x01, _EOX };
>
> // create MIDISysexSendRequest struct here.
>
> OSStatus status = MIDISendSysex(&sysx);
>
>
>
> // wait for completion... but how? while(sysx.complete == false);
>
> ???
>
> // get the result... but how????
>
> // create DeviceInquiryMessage Object
>
>
>
> return DeviceInquiryMessage;
>
> }
>
>
>
>
One problem here is that this method must block forever, waiting for a
>
response. However, a response may never come. What if the MIDI device
>
is unplugged or turned off? These are both pretty common cases, and
>
it would be bad for your whole program to hang while it waits.
>
>
Basically, your program needs to implement a state machine. (Google
>
for "finite state machine", or look in a programming textbook, for
>
more details.) Your program would start with its state set to 'idle'.
>
When you send a sysex message, you would set the state to 'listening'.
>
Then, when you get a response, your MIDIReadProc will be called; you
>
should have it check the state, do the appropriate thing, and then
>
update the state. How you design this state machine is up to you.
>
>
Also, you will probably want to have a timeout. If no response happens
>
after a while (perhaps 1 second) then you should assume that something
>
has gone wrong with the MIDI device you're trying to talk to. Both
>
Carbon and Cocoa provide timers you can use.
>
>
You do *not* want to sit in a loop waiting for a flag to be set. This
>
will use a lot of CPU time for no benefit. Instead, think of things in
>
terms of events: when an event happens, some function in your program
>
will be called. (There are separate functions for receiving MIDI,
>
timeouts firing, and so on.) Each function should look at the current
>
state, figure out what to do, do it, and update the state.
>
>
I'm omitting a lot of details here, but I hope you get the general
>
idea.
>
>
--
>
Kurt Revis
>
email@hidden
>
_______________________________________________
>
coreaudio-api mailing list | email@hidden
>
Help/Unsubscribe/Archives:
>
http://www.lists.apple.com/mailman/listinfo/coreaudio-api
>
Do not post admin requests to the list. They will be ignored.
_______________________________________________
coreaudio-api mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/coreaudio-api
Do not post admin requests to the list. They will be ignored.