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.
Signed-off-by: Jacek Konieczny jajcus@jajcus.net --- 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.
Signed-off-by: Jacek Konieczny jajcus@jajcus.net --- 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);
Hi Jacek,
Thanks for the patch series.
On Sun, Feb 09, 2020 at 07:36:46PM +0100, Jacek Konieczny wrote:
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);
It looks like using snd_seq_ev_set_subs() might be a better fit here.
@@ -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);
Same here, obviously.
Also, in both patches, please try to match surrounding tab-style. I know it's already a mess. Or, if you prefer, I can fix it up and resend with your sign-off. Up to you.
Andrew
Hi Andrew,
Thank you for your review.
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);
It looks like using snd_seq_ev_set_subs() might be a better fit here.
Indeed. I missed this shortcut.
@@ -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);
Same here, obviously.
yes.
Also, in both patches, please try to match surrounding tab-style. I know it's already a mess.
It is. I looked at that once again and I give up. That mix of tabs and spaces in the original code makes not sense to me :-)
Or, if you prefer, I can fix it up and resend with your sign-off. Up to you.
That would be great. Will you change those snd_seq_ev_set_dest() to snd_seq_ev_set_subs() too?
Greets, Jacek