Erhan

IT-Security Engineer

Using Pioneer Controller with Rekordbox in Linux

Posted in DJ on Jan. 7, 2020

Table of Contents

Introduction to the Problem

In the previous article, we already got Rekordbox to run with Wine in Linux. But what we really want is to use Rekordbox with our Pioneer DJ controller (e.g. DDJ-SR, DDJ-SX2, etc). The DJ controller is basically a MIDI device and a USB soundcard. We can connect it to our system and it is also detected but we can not control Rekordbox with the controller itself. In this article, we will show how we can solve this problem.

How to detect MIDI controllers in Linux

In linux we can list all connected MIDI devices with the amidi utility. It reads from and writes to ALSA RawMIDI ports. By using amidi -l we can list all connected MIDI devices.

% aplay -l
midiIn devices: 1
-- 0: PIONEER DDJ-SR MIDI 1 --
    Device ID: 0
    Manufacturer identifier: 255
    Product identifier: 1
    Driver version: 0.1
    Product name: PIONEER DDJ-SR MIDI 1
    Support: 0x0
midiInMessage(DRV_QUERYDEVICEINTERFACESIZE) failed: mmr     = 0x00000005
midiOut devices: 1
[...]

Also we can see the devices information this way:

% cat /proc/asound/seq/clients
Client 24 : "PIONEER DDJ-SR" [Kernel] Port 0 : "PIONEER DDJ-SR MIDI 1" (RWeX)

Comparing this output with windows, we can see that the appeneded "MIDI 1" string at the end of the product name is not visible in Windows. But the proper product name is stored somewhere as we can see.

How to detect MIDI Controller in Windows

So how can a Windows application detect or enumerate MIDI devices? There are two handy functions for this:

  • midiInGetDevCaps: Retrieves the capabilities of a given MIDI input device and places this information in the MIDIINCAPS structure.
  • midiOutGetDevCaps: Retrieves the capabilities of a given MIDI output device and places this information in the MIDIOUTCAPS structure.

The MIDICINCAPS structure looks like this:

typedef struct {
  WORD      wMid;
  WORD      wPid;
  MMVERSION vDriverVersion;
  TCHAR     szPname[MAXPNAMELEN];
  DWORD     dwSupport;
} MIDIINCAPS;

The interesting part for us is szPname which is the "Product name in a null-terminated string". More information can be found here and here.

John Haskey has developed a tiny Windows application ([here[(http://haskey.com/midi/)) which enumartes MIDI devices this way and which we can use to understand how WINE sees our MIDI devices.

Hypothesis

I assume that Rekordbox detects the controller by product name. In Windows it shows up as "PIONEER DDJ-SR" but in Linux it is "PIONEER DDJ-SR MIDI 1". If we can remove the "MIDI 1" part from the product name, Rekordbox should be able to detect and use the controller again.

Solution

There are two approaches I thought about how to solve this problem. We can either change this naming convention on Linux side so that Linux shows already the right product name from the beginning or we can change how Wine shows the product name in Windows applications.

I did the second approach but I have gathered some information for the first one as well, which I will list here for completeness.

The alsa-lib has some raw MIDI functions which are interesting in this case. snd_rawmidi_info_get_name gets the rawmidi hardware driver name but there is no equivalent snd_rawmidi_info_set_name function.

But there are other interesting functions like snd_seq_set_client_name() and snd_seq_set_client_info() which may be used to set the name. I was not able to create a working proof of concept for this. Tell me if you are successful!

So let's get to our working solution and the path of how it was found.

Changing MIDI product name in WINE

Our goal is to change what WINE sees as the product name of the MIDI devices so that other Windows applications see the same change.

First we have to get Rekordbox running in Wine. Please check my other article for this.

The we run Rekordbox the following way to get more verbose debug information:

WINEDEBUG=warn+mci,+mcimidi,+mmsys,+winmm,+midi wine rekordbox.exe &> ~/winemidi.log

The following line was particulary interesting which contains the wrong name received from Alsa:

002a:trace:midi:ALSA_AddMidiPort MidiIn [0] name='L"PIONEER DDJ-SR MIDI 1"' support=0

I wandered from midiInGetDevCapsA() in dlls/winealsa.drv/winmm.c in the Wine sourcecode to dlls/winealsa.drv/midi.c. midi.c was actually the right file where we could solve the problem. I changed some places in ALSA_AddMidiPort() to change the device name removing the "MIDI 1" part at the end and added some debug messages to see if it really happened (GitHub).

002a:trace:midi:ALSA_AddMidiPort IN **with mod**  (24:PIONEER     DDJ-SR:kernel:0:PIONEER DDJ-SR MIDI 1:90002)
002a:trace:midi:ALSA_AddMidiPort **Changing PIONEER name.**
MidiIn [0]       name='L"PIONEER DDJ-SR"' support=0
    ALSA info: midi dev-type=90002, capa=0

To verify if this change is visible with Wine in Windows applications, we can test the previously mentioned midienum application:

% wine midienum.exe
midiIn devices: 1
-- 0: PIONEER DDJ-SR --
    Device ID: 0
    Manufacturer identifier: 255
    Product identifier: 1
    Driver version: 0.1
    Product name: PIONEER DDJ-SR
    Support: 0x0
midiInMessage(DRV_QUERYDEVICEINTERFACESIZE) failed: mmr = 0x00000005
midiOut devices: 1
-- 0: PIONEER DDJ-SR MIDI 1 --
    Device ID: 0
    Manufacturer identifier: 255
    Product identifier: 1
    Driver version: 0.1
    Product name: PIONEER DDJ-SR MIDI 1
    Technology: 1 (MOD_MIDIPORT)
    Voices: 0
    Notes: 0
    Channel mask: 0xffff
    Support: 0x0
midiOutMessage(DRV_QUERYDEVICEINTERFACESIZE) failed: mmr = 0x00000008
002a:trace:winmm:DllMain 0x7e9a0000 0x0 0x1

This was not working at the beginning because it only change the IN devices name but not for the output. Changing the output in a similar way worked and the controller was detected in Rekordbox.

Demin Dmitriy had a better idea and found a cleaner solution. He modified the part where it gets the name and uses the client name instead of the port name:

lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);

with

lstrcpynA(midiPortName, snd_seq_client_info_get_name(cinfo), MAXPNAMELEN);

See GitHub for more information.

Thanks again Demin!

I still have the wine-staging package in ArchLinux installed. To add these changes I downloaded the sourcecode and build Wine as described here. Additionally I build for 32-bit as well (see here) so that I could verify the changes with midienum.exe but this is not mandatory as Rekordbox is x64.

During compiling you should run make -j10 to run multiple recipies at the same time and speed up the process. When everything is compiled you have to copy two files to the right locations:

# cp winealsa.drv.fake /usr/lib/wine/fakedlls/winealsa.drv
# cp winealsa.drv.so /usr/lib/wine/winealsa.drv.so

Additionally I copied 32-bit version but you don't have to:

# cp winealsa.drv.so /usr/lib32/wine/winealsa.drv.so

If you do not want to compile Wine yourself, you can download those two files here.

Then we run Rekordbox as described in the previous article and voila.

Rekordbox detects the controller

If you have problems with getting the sound through the controller, check with pavucontrol or your favorite audio tool, if Rekordbox uses the controller devices and that the controller device is chosen in the Rekordbox preferences.

I hope you guys liked this article and have fun using Rekordbox in Linux.

Using the DDJ-SX2 in Linux

Ideas for the future

  • Give non-Pioneer controllers a Pioneer product name
  • Check what happens, if we use DDJ-200 as product name ;)
  • Translate midi commands at input and output to use any controller with Rekordbox