SanderVocke / winmm_midi_rename

WinMM wrapper which enables rule-based modification of MIDI device properties.
GNU General Public License v3.0
6 stars 1 forks source link

Pioneer DJ controllers and Rekordbox #4

Open SanderVocke opened 1 month ago

SanderVocke commented 1 month ago

Creating this issue because I have had two people come forward with unsuccessful attempts to connect DJ controllers to Rekordbox on Wine in Linux using this wrapper. Let's discuss here.

SanderVocke commented 1 month ago

@elvetemedve reported using the following config for a DDJ-FLX10 after using the wrapper to determine the correct values on Windows:

{
    "log": "midi_rename.log",
    "popup": true,
    "rules": [
        {
          "match_name": "DDJ-FLX10 - DDJ-FLX10 MIDI.+",
          "match_direction": "out",
          "replace_name": "DDJ-FLX10",
          "replace_man_id": 65535,
          "replace_prod_id": 65535,
          "replace_channel_mask": 65535,
          "replace_driver_version": 274,
          "replace_technology": 1
        },
        {
          "match_name": "DDJ-FLX10 - DDJ-FLX10 MIDI.+",
          "match_direction": "in",
          "replace_name": "DDJ-FLX10",
          "replace_man_id": 65535,
          "replace_prod_id": 65535,
          "replace_driver_version": 274
        }
    ]
}

With the following result:

Request for output device capabilities:   name: DDJ-FLX10 - DDJ-FLX10 MIDI 1
  man id: 255
  prod id: 1
  driver version: 1
  technology: 1
  voices: 0
  notes: 0
  channel mask: 65535
  support: 0

--> Matched a replace rule. Returning:   name: DDJ-FLX10
  man id: 65535
  prod id: 65535
  driver version: 274
  technology: 1
  voices: 0
  notes: 0
  channel mask: 65535
  support: 0

Request for input device capabilities:   name: DDJ-FLX10 - DDJ-FLX10 MIDI 1
  man id: 255
  prod id: 1
  driver version: 1

--> Matched a replace rule. Returning:   name: DDJ-FLX10
  man id: 65535
  prod id: 65535
  driver version: 274

... But not recognized in Rekordbox.

I got a very similar experience in a private message, in that case it was a DDJ-RB.

SanderVocke commented 1 month ago

To me, there are a couple of options of what could be going wrong:

  1. Maybe there is still something wrong in the values configured in the configuration. It seems suspicious that on Windows, we always get man id and prod id of 65535 - it could be a bug. A next step could be to go on Windows and find a tool which can also inspect these properties independently. If such a tool also would report 65535 on Windows, we know we are doing the correct thing here and we can rule out this potential issue.
  2. Maybe there is still some other property missing in the wrapper. It seems unlikely to me - the properties supported, as far as I know, completely cover all device properties that the WinMM midi API has. You could verify this independently: https://learn.microsoft.com/en-us/windows/win32/api/mmeapi/
  3. Maybe the Rekordbox software is talking to the controller using more than just MIDI. After all, it is a USB device. If it turns out that Rekordbox verifies the device with some non-MIDI communication, this wrapper tool is not going to help. It will be difficult to get around in that case, because AFAIK, WINE does not support USB drivers in the Windows side.

One thing I would suggest is to install MidiView (https://hautetechnique.com/midi/midiview/) both in Windows, and in WINE on Linux. Use it to monitor the MIDI communication when the device is connected. Check if the kinds of messages being sent and received are the same in both environments.

In my own use-case (a Joue Play MIDI controller and app), the wrapper worked fine, but of course the application could be doing less checks.

I myself am happy to support by e.g. fixing bugs if it turns out that, for example, the man id / prod id thing is somehow broken. But I am not willing to actively help analyze communications or something of that nature - I would rely on input from you.

Good luck, I hope it ends up working!

SanderVocke commented 1 month ago

There is one level deeper you could go in analyzing the differences in Windows and WINE: use something like API monitor (http://www.rohitab.com/apimonitor). You could track exactly what Windows Multimedia API calls the Rekordbox application is making, including the exact values returned by Windows or WINE to the application.

Maybe this would reveal some difference in behavior between WINE and Windows that this wrapper could solve.

SanderVocke commented 1 month ago

One last suggestion: I read that the DDJ-FLX10 also supports Serato DJ Pro. Maybe install a free trial of that in WINE and see if it picks up the device? Just as a sanity-check, you never know.

SanderVocke commented 1 month ago

... And another addition. I found a blog post of someone in 2019 achieving the connection with DDJ-SX2. I don't know which WINE version and which Rekordbox version was used. https://www.erhan.es/blog/using-pioneer-controller-with-rekordbox-in-linux/

He essentially does the same thing as this wrapper library does, but instead of using a wrapper DLL he modified WINE itself to do the same name re-mapping. He was able to get it working by only remapping the name, not the IDs.

Please have a detailed look at what he did, and maybe you can replicate. Who knows, maybe you should only remap the name and not the IDs?

SanderVocke commented 1 month ago

In any case, I would appreciate if you let me know here if/when your attempts are successful. Maybe someone else will benefit.

elvetemedve commented 1 month ago

@SanderVocke Thank you for sharing your view and the suggestions.

I explored some of these options. The API Monitor was the one that provided the most details:

The DJ controller is the device ID 3 and the name is "DDJ-FLX10". All midiInMessage() call failed. Screenshot from 2024-07-22 00-00-39

This shows that both midiInMessage() and midiOutMessage() failed, but with different return value. Screenshot from 2024-07-22 00-05-02

Device ID 1 is the virtual device "PipeWire-RT-Event input". Screenshot from 2024-07-22 00-05-57

SanderVocke commented 1 month ago

That's interesting info, and also taught me something I didn't know yet about the MIDI API on Windows.

Looking in the docs of midi*Message() (see e.g. https://learn.microsoft.com/en-us/windows/win32/api/mmeapi/nf-mmeapi-midiinmessage), apparently these functions are used not to send MIDI messages, but to send messages (queries) to the driver.

the message is the second argument (2061 in your screenshots). Digging a bit further, the following is the list of queries mentioned in the MSDN docs above:

#define DRV_QUERYDEVNODE (DRV_RESERVED + 2)
#define DRV_QUERYMAPPABLE (DRV_RESERVED + 5)
#define DRV_QUERYMODULE (DRV_RESERVED + 9)
#define DRV_PNPINSTALL (DRV_RESERVED + 11)
#define DRV_QUERYDEVICEINTERFACE (DRV_RESERVED + 12)
#define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13)
#define DRV_QUERYSTRINGID (DRV_RESERVED + 14)
#define DRV_QUERYSTRINGIDSIZE (DRV_RESERVED + 15)
#define DRV_QUERYIDFROMSTRINGID (DRV_RESERVED + 16)
#define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17)
#define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18)

where DRV_RESERVED is 0x800 (so 2048).

That would suggest that Rekordbox is querying for DRV_QUERYDEVICEINTERFACESIZE, which is usually the first step before querying DRV_QUERYDEVICEINTERFACE to get a driver-level device name.

In other words, looks like Rekordbox is using this advanced method to query the device name, instead of just relying on the device properties we have spoofed. If we are lucky, this is just a final check before really starting to talk to the device. And these calls could be spoofed by this wrapper just like we are spoofing the other calls.

I would suggest:

No guarantees that this will work, but at least it is not a dead end.

SanderVocke commented 1 month ago

One more thing, does the API monitor tool support exporting/importing a logged session? If so, could you record the behavior on Windows and share it?

elvetemedve commented 1 month ago

Looking in the docs of midi*Message() (see e.g. https://learn.microsoft.com/en-us/windows/win32/api/mmeapi/nf-mmeapi-midiinmessage), apparently these functions are used not to send MIDI messages, but to send messages (queries) to the driver.

You are right. I didn't pay attention to this detail. Thanks for noticing. It looks like before talking to the device it has to call midi*Open() first.


Can you have a close look on Windows with the API monitor, and see what calls to midi*Message are made exactly?

As far as I see midi*Message() functions are called in pairs, first with 2061 as message, secondly 2060. Based on your observation, these must be the DRV_QUERYDEVICEINTERFACESIZE and DRV_QUERYDEVICEINTERFACE messages. That must be the way Rekordbox obtains the device name.


No guarantees that this will work, but at least it is not a dead end.

Yeah, there is hope fortunately. Though I noticed that these function pairs are called multiple times until the midi*Open() call takes place. So there could be a loop, or callback that is waiting until a condition is met (the open question is what are these conditions and will these be fulfilled under Wine).


Combining these two steps, I could then add extra features to be able to spoof these queries.

If you can put the effort into that, it would be very nice.


One more thing, does the API monitor tool support exporting/importing a logged session? If so, could you record the behavior on Windows and share it?

Sure, I've saved the capture of function calls. You should be able to open and inspect it after extracting the zip file: midi-api-capture.zip

SanderVocke commented 1 month ago

Nice. I will get back to you some time later this week with an update to log these calls.

SanderVocke commented 1 month ago

In the meantime, you can already use this tool I found (midienum.exe) to check and compare the device interface name on WINE vs. Windows:

https://matthewvaneerde.wordpress.com/2012/09/21/enumerating-midi-devices/

SanderVocke commented 1 month ago

You can have a try with this modified wrapper:

release-dll.zip

It should now also log the queries to DRV_QUERYDEVICEINTERFACE(...) in the log file.

You can add an entry to your replace rules: "replace_interface_name", to match the Windows name in WINE.

I didn't test this myself, sorry, so it may not work correctly. Otherwise, it would be good to also have a look with the midi enum tool above.

elvetemedve commented 1 month ago

Thank you for the updated version.

I've checked out the interface name by both methods on Windows 10 and got the same text:

\\?\usb#vid_2b73&pid_0041&mi_03#9&2f46a9a1&0&0003#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\global

Based on that I've updated the config file: midi_rename_config.json

Unfortunately it does not load the rules properly ("# of rules loaded 0"). image

Could you have a look what might be wrong there, please?

SanderVocke commented 1 month ago

Sure, I'll have a look. I really should have tested it myself first. I can't get around to it until Monday though.

I am curious, what is the interface name when running with wine?

elvetemedve commented 1 month ago

Possibly I made a mistake somewhere, because now I got different info in the popup for the same config JSON: Screenshot from 2024-07-29 00-44-11


Sure, I'll have a look. I really should have tested it myself first. I can't get around to it until Monday though.

I am curious, what is the interface name when running with wine?

The interface name is just a number under Wine. For example --> Transparently queried the device interface with result: Queried device interface size for input. Native result: 1448556300 midi_rename.log

SanderVocke commented 1 month ago

It could be that the interface isn't always exactly the same. But it wouldn't be hard to imagine that Rekordbox does some basic check on it so that it looks "Windows-like" and not just a number like WINE.

Hang in there - I don't have a lot of time this week but I will get around to fixing the config bug at some point.

elvetemedve commented 1 month ago

It could be that the interface isn't always exactly the same. But it wouldn't be hard to imagine that Rekordbox does some basic check on it so that it looks "Windows-like" and not just a number like WINE.

Actually Rekordbox needs to recognize the controller for two purposes:

  1. To load the device specific midi mapping. Maybe the device name as text is good enough for this.
  2. Certain paid features are unlocked when a Pioneer controller is connected. I can imagine they do some trickery because of that to prevent getting those features for free by simply faking the name.

Hang in there - I don't have a lot of time this week but I will get around to fixing the config bug at some point.

Sure, thanks for the update.

SanderVocke commented 1 month ago

Actually Rekordbox needs to recognize the controller for two purposes:

I agree, I just meant that maybe it doesn't use only this interface name for device recognition. It could be doing some checks on the interface name and then combining that with the Midi device name.

It is a bit unclear to me, but I think that the interface name contains information both about the device and about the Midi hardware and/or driver used to connect to it.

SanderVocke commented 3 weeks ago

release-dll(2).zip Thanks for waiting, I got around to it. Bug with loading rules should be fixed (along with strange characters that were printed in the message box), although I am still not able right now to test this with a real MIDI device.

Can you try again with this DLL and the same config file?

Also one remark about --> Transparently queried the device interface with result: Queried device interface size for input. Native result: 1448556300: notice that it says "interface size", not "interface name". It first asked how long the name is. So it is supposed to be a number - although 1448556300 is a strange answer.

Anyway, please have a try and next time if something is wrong I will be able to respond faster.

elvetemedve commented 3 weeks ago

No worries. Thanks for your effort to keep investigating this.

Can you try again with this DLL and the same config file?

I tested it and the rules are loaded successfully this time. However faking the Windows API call still does not look right to me. In the log I see empty string as device name (--> Transparently queried the device interface with result:), while API Monitor still captured midiInMessage(*, 2061, *) calls returning MMYSYERR_INVALIDHANDLE, and of course Rekordbox does not recognize the MIDI controller.

notice that it says "interface size", not "interface name".

My bad, sorry about that. I add the full log here.

midi_rename.log

Do you have any other idea what went wrong?

SanderVocke commented 2 weeks ago

Sorry for my delayed response. I guess responding quickly didn't really happen.

Do you also have logs from the API monitor (both on Windows and on WINE)?

elvetemedve commented 1 week ago

No worries. 🙂

Sure, I will do, but can't until the middle of next week.

elvetemedve commented 3 days ago

Here are the logs captured under both Windows and Linux: logs.zip

SanderVocke commented 2 days ago

Thanks! This is very informative. The API monitor gives a lot of info, although not all - unfortunately, arguments passed as pointers to the system layer have invisible values to the API monitor.

But it seems you are right about the MMYSYERR_INVALIDHANDLE. For some reason, making these driver-level calls to the Wine implementation returns errors and empty device names. I am not sure if that is some kind of bug - it could simply be that these features are not supported in Wine. I had a quick look at the Wine source code but couldn't track it down.

We don't necessarily need to know the exact problem though. We are trying to spoof the device name anyway. I think we can just return a success code to Rekordbox and the spoofed name, even if the actual system gives error codes. To Rekordbox it would look like a successful call.

I'll go ahead and implement that and you can try it out.

SanderVocke commented 2 days ago

OK, I added a new release on the front page (1.4.1). This one will return MMSYSERR_NOERROR to Rekordbox, even if Wine gives an MSMSYSERR_INVALHANDLE.

I would expect that this will at least trigger Rekordbox to then query the device name next. Let's see whether it accepts the result and moves on to actual MIDI communication.

Can you give it a try and report back with the new API monitor log?

elvetemedve commented 2 days ago

Thanks.

It made me thinking that the Midi implementation in Wine must be incomplete. The related wiki page is from 2016, which tells me that it's not actively developed.

I've tried out the new release. Unfortunately I don't see the name in the log or MMSYSERR_NOERROR in API Monitor. It looks like the rule does not match in handle_QUERYDEVICEINTERFACESIZE(), because the text starting with "_Matched a replace rule. Returning MMSYSERRNOERROR with size" is not captured in the log.

See logs.zip