winealsa.drv uses ALSA sequencer interface to access MIDI ports. It is generally the preferred API for that in Linux, as it allows multiple applications access MIDI hardware at the same time and to route messages between software MIDI ports. The connections between the applications and hardware can be controlled within the app or by external tools.
Unfortunately wine uses the API in a way that won't allow other aplications to alter its MIDI connections. It disallows external changes to the connections and does not use the configured connection (subscriptions) to send outgoing messages (instead, it addresses the target device directly).
That has been already confusing, as e.g. https://bugs.winehq.org/show_bug.cgi?id=43725 shows.
Enabling other apps to alter wine MIDI connection has many use cases, like: - fixing Windows application that fail to select proper MIDI device - reverse-engineering Windows application controlling external MIDI hardware (recording SYSEX messages) - adding Linux application in the MIDI chain for processing MIDI streams before they hit the target device
Jacek Konieczny (2): winealsa.drv: allow external MIDI port connection changes winealsa.drv: send MIDI events to port subscribers
dlls/winealsa.drv/midi.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
That is generally the expected behaviour of any ALSA sequencer client. --- dlls/winealsa.drv/midi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/dlls/winealsa.drv/midi.c b/dlls/winealsa.drv/midi.c index 58b10bf666..ab14307917 100644 --- a/dlls/winealsa.drv/midi.c +++ b/dlls/winealsa.drv/midi.c @@ -255,15 +255,15 @@ static int midiOpenSeq(BOOL create_client) else TRACE("Outport port %d created successfully\n", port_out); #else - port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ, - SND_SEQ_PORT_TYPE_APPLICATION); + port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ|SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION); if (port_out < 0) TRACE("Unable to create output port\n"); else TRACE("Outport port %d created successfully\n", port_out);
- port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE, - SND_SEQ_PORT_TYPE_APPLICATION); + port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_READ|SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION); if (port_in < 0) TRACE("Unable to create input port\n"); else
This way changes to the port connections made by other applications will be honoured. --- dlls/winealsa.drv/midi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/dlls/winealsa.drv/midi.c b/dlls/winealsa.drv/midi.c index ab14307917..b5ffa6bf68 100644 --- a/dlls/winealsa.drv/midi.c +++ b/dlls/winealsa.drv/midi.c @@ -878,7 +878,7 @@ static DWORD modData(WORD wDevID, DWORD dwParam) snd_seq_ev_clear(&event); snd_seq_ev_set_direct(&event); snd_seq_ev_set_source(&event, port_out); - snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port); + snd_seq_ev_set_dest(&event, SND_SEQ_ADDRESS_SUBSCRIBERS, 0); switch (evt & 0xF0) { case MIDI_CMD_NOTE_OFF: @@ -1047,8 +1047,7 @@ static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize) snd_seq_ev_clear(&event); snd_seq_ev_set_direct(&event); snd_seq_ev_set_source(&event, port_out); - snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port); - TRACE("destination %d:%d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port); + snd_seq_ev_set_dest(&event, SND_SEQ_ADDRESS_SUBSCRIBERS, 0); snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData); EnterCriticalSection(&midiSeqLock); snd_seq_event_output_direct(midiSeq, &event);