would be a better approach to redefine MidiInput and MidiOutput to be part of a MidiDevice that can be subclassed and plugged at runtime with a MidiDeviceManager so more midi devices can be incorporated with different implementation ? just thorwing some ideas, i want to plug in jack midi support i need to differentiate it from the standard alsa sequencer handling… would be possible ?
i’ve initially made MidiOutput, but i can’t see anything changes in the midi flow:
class MidiOutputDevice
{
public:
MidiOutputDevice (MidiOutput* const midiOutput_,
snd_seq_t* const seqHandle_,
int const outputPort_)
:
midiOutput (midiOutput_),
seqHandle (seqHandle_),
outputPort (outputPort_)
{
jassert (seqHandle != 0 && midiOutput != 0);
}
~MidiOutputDevice()
{
snd_seq_close (seqHandle);
}
void sendMessageNow (const MidiMessage& message)
{
const int currentEventSize = message.getRawDataSize ();
snd_midi_event_t* midiParser;
if (snd_midi_event_new (currentEventSize, &midiParser) >= 0)
{
snd_seq_event_t event;
snd_seq_ev_clear (&event);
snd_midi_event_encode (midiParser, message.getRawData(), message.getRawDataSize (), &event);
snd_midi_event_reset_encode (midiParser);
snd_seq_ev_set_source (&event, outputPort);
snd_seq_ev_set_subs (&event);
snd_seq_ev_set_direct (&event);
snd_seq_event_output_direct (seqHandle, &event);
snd_midi_event_free (midiParser);
}
}
juce_UseDebuggingNewOperator
private:
MidiOutput* const midiOutput;
snd_seq_t* const seqHandle;
int outputPort;
};
static snd_seq_t* iterateOutputDevices (StringArray& deviceNamesFound, const int deviceIndexToOpen, int& portIndexOpened)
{
snd_seq_t* returnedHandle = 0;
snd_seq_t* seqHandle;
if (snd_seq_open (&seqHandle, "default", SND_SEQ_OPEN_OUTPUT, 0) == 0)
{
snd_seq_system_info_t* systemInfo;
snd_seq_client_info_t* clientInfo;
if (snd_seq_system_info_malloc (&systemInfo) == 0)
{
if (snd_seq_system_info (seqHandle, systemInfo) == 0
&& snd_seq_client_info_malloc (&clientInfo) == 0)
{
int numClients = snd_seq_system_info_get_cur_clients (systemInfo);
while (--numClients >= 0 && returnedHandle == 0)
{
if (snd_seq_query_next_client (seqHandle, clientInfo) == 0)
{
snd_seq_port_info_t* portInfo;
if (snd_seq_port_info_malloc (&portInfo) == 0)
{
int numPorts = snd_seq_client_info_get_num_ports (clientInfo);
const int client = snd_seq_client_info_get_client (clientInfo);
snd_seq_port_info_set_client (portInfo, client);
snd_seq_port_info_set_port (portInfo, -1);
while (--numPorts >= 0)
{
if (snd_seq_query_next_port (seqHandle, portInfo) == 0
&& (snd_seq_port_info_get_capability (portInfo) & SND_SEQ_PORT_CAP_WRITE) != 0)
{
deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo));
if (deviceNamesFound.size() == deviceIndexToOpen + 1)
{
const int sourcePort = snd_seq_port_info_get_port (portInfo);
const int sourceClient = snd_seq_client_info_get_client (clientInfo);
if (sourcePort != -1)
{
snd_seq_set_client_name (seqHandle, "Juce Midi Output");
const int portId
= snd_seq_create_simple_port (seqHandle, "Juce Midi Out Port",
SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
// SND_SEQ_PORT_TYPE_APPLICATION);
SND_SEQ_PORT_TYPE_MIDI_GENERIC);
snd_seq_connect_from (seqHandle, portId, sourceClient, sourcePort);
returnedHandle = seqHandle;
portIndexOpened = portId;
printf ("setup output port %s : %d -> %d \n", snd_seq_client_info_get_name (clientInfo), portId, sourcePort);
break;
}
}
}
}
snd_seq_port_info_free (portInfo);
}
}
}
snd_seq_client_info_free (clientInfo);
}
snd_seq_system_info_free (systemInfo);
}
if (returnedHandle == 0)
snd_seq_close (seqHandle);
}
return returnedHandle;
}
//==============================================================================
const StringArray MidiOutput::getDevices()
{
int portIndex = 0;
StringArray devices;
iterateOutputDevices (devices, -1, portIndex);
return devices;
}
int MidiOutput::getDefaultDeviceIndex()
{
return 0;
}
MidiOutput* MidiOutput::openDevice (int deviceIndex)
{
MidiOutput* newDevice = 0;
int portIndex = 0;
StringArray devices;
snd_seq_t* const handle = iterateOutputDevices (devices, deviceIndex, portIndex);
if (handle != 0)
{
newDevice = new MidiOutput ();
newDevice->internal = new MidiOutputDevice (newDevice, handle, portIndex);
}
return newDevice;
}
MidiOutput::MidiOutput() :
internal (0)
{
}
MidiOutput::~MidiOutput()
{
MidiOutputDevice* const device = (MidiOutputDevice*) internal;
delete device;
}
void MidiOutput::reset()
{
// @XXX : what i need to do here ?
}
bool MidiOutput::getVolume (float& leftVol, float& rightVol)
{
// @XXX : mmmh...
return false;
}
void MidiOutput::setVolume (float leftVol, float rightVol)
{
// @XXX : doh !
}
void MidiOutput::sendMessageNow (const MidiMessage& message)
{
((MidiOutputDevice*) internal)->sendMessageNow (message);
}
it enumerates correctly the possible writable devices as outputs, the connections are made without errors, but i still can’t receive any midi messages.
other thing. i’ve made the event to be sent in direct mode (no timestamp)
with snd_seq_event_output_direct. should i care of event timestamps ? but how could my application timestamp be understood by another application ?
just for trying if there are errors in encoding the bytes of the midi message, i’ve replaced the sendMessageNow function to be like:
void sendMessageNow (const MidiMessage& message)
{
snd_seq_event_t ev;
snd_seq_ev_clear(&ev);
snd_seq_ev_set_source(&ev, outputPort);
snd_seq_ev_set_subs(&ev);
snd_seq_ev_set_direct(&ev);
if (message.isNoteOnOrOff())
{
if (message.isNoteOn ())
ev.type = SND_SEQ_EVENT_NOTEON;
else if (message.isNoteOff ())
ev.type = SND_SEQ_EVENT_NOTEOFF;
ev.data.note.channel = message.getChannel ();
ev.data.note.note = message.getNoteNumber ();
ev.data.note.velocity = message.getVelocity ();
printf("Sending note on/off to port %d, ch=%d, note=%d, vel=%d, pid=%d\n",
outputPort, ev.data.note.channel, ev.data.note.note, ev.data.note.velocity, getpid());
}
else if (message.isAllNotesOff())
{
// @XXX how to handle this ?
}
else if (message.isAllSoundOff())
{
// @XXX how to handle this ?
}
else if (message.isPitchWheel ())
{
ev.type = SND_SEQ_EVENT_PITCHBEND;
ev.data.control.channel = message.getChannel ();
ev.data.control.value = message.getPitchWheelValue ();
printf("Sending pitchbend to port %d, ch=%d, value=%d\n",
outputPort, ev.data.control.channel, ev.data.control.value);
}
else if (message.isChannelPressure())
{
ev.type = SND_SEQ_EVENT_CHANPRESS;
ev.data.control.channel = message.getChannel ();
ev.data.control.param = 0;
ev.data.control.value = message.getChannelPressureValue ();
printf("Sending channel pressure to port %d, ch=%d, value=%d\n",
outputPort, ev.data.control.channel, ev.data.control.value);
}
else if (message.isAftertouch())
{
ev.type = SND_SEQ_EVENT_KEYPRESS
ev.data.note.channel = message.getChannel ();
ev.data.note.note = message.getNoteNumber ();
ev.data.note.velocity = message.getAfterTouchValue ();
ev.data.note.off_velocity = 0;
ev.data.note.duration = 0;
printf("Sending aftertouch to port %d, ch=%d, value=%d\n",
outputPort, ev.data.note.channel, ev.data.note.value);
}
else if (message.isController ())
{
ev.type = SND_SEQ_EVENT_CONTROLLER;
ev.data.control.channel = message.getChannel ();
ev.data.control.param = message.getControllerNumber ();
ev.data.control.value = message.getControllerValue ();
printf("Sending cc to port %d, ch=%d, cc=%d, value=%d\n",
outputPort, ev.data.control.channel, ev.data.control.param, ev.data.control.value);
}
else if (message.isProgramChange())
{
ev.type = SND_SEQ_EVENT_PGMCHANGE;
ev.data.control.channel = message.getChannel ();
ev.data.control.value = message.getProgramChangeNumber ();
printf("Sending program change to port %d, ch=%d, value=%d\n",
outputPort, ev.data.note.channel, ev.data.note.value);
}
snd_seq_event_output_direct(seqHandle, &ev);
but still i can’t receive any midi message. what could i’ve mistaken ?
whau ! i thought not but is working. looking with a midi monitor the correct messages are generated, even for sysex cc and aftertouch !
ok. now the juce application is shown in the jack audio midi connections (only for output port, the midi in not).
now the problem is another one: since i’m sending midi from juce demo, i’ve hacked the Synth to a hold a MidiOutput that sends midi messages on every audiocallback after the midibuffer get filled. but jucedemo is holding on the alsa device, so i can’t start jack daemon… aaargh. i need to finish JackAudioDevice as soon as we speak.
jules: will be possible to tweak the AudioDeviceManager (and its component selector) to let you select midi output also ?
groovy stuff, I’ll take a proper look at all this asap, just need to finish a couple of other things first…
Not sure about your MidiDeviceManager idea, I think it’s probably more useful just to stick it all into the AudioDeviceManager. I’ll have a think about that.