Porting QIRX to Linux/Mono

An Experiment

The DAB version has been ported to Linux Debian 9, 64-Bit. The result is a working solution, its properties shortly described here. The goal was to find out whether it is possible to run the DAB version of QIRX on Linux/Mono with the same Windows codebase. This could reached only partly, mainly due to the incompatiblity of the C# files automatically produced by the Windows Forms Designer. Under Mono, these showed often unacceptable behaviour in their original form.

  • Basics

    Binary compatibility. The .NET assemblies produced with the Windows compiler run on Mono. However, the sources also have been ported to Linux, because of the testing and debugging necessities.

    Development IDE. Monodevelop V7.4 (build 1035) has been used as the C# IDE. After having been updated to its latest version it run very stable. The version installable from the Debian package manager crashed reliably and should not be used. It is possible to directly import Visual Studio Solutions and Projects into Monodevelop, this being a great feature. Debugging is fairly comfortable, although everything seems to go a little slower than on Windows. However, this might have been due to using a Remote Desktop Windows-Linux environment, working very well.

    Mono Upgrade. The Mono offered in the Debian package manager was outdated as well. The upgrade to the version 5.10 has been far from being a smooth experience. It took quite a while to find out how to update and export the necessary public keys for the update procedure to work.

    C/C++ Libraries. The libraries necessary for the Viterbi and Reed-Solomon error correction and the MP2 audio decoding could be compiled and linked without any problems. Consisting of only a few files, they were compiled and linked "by hand". On the other hand, the libfaad library responisble for the AAC audio decoding consists of many C-files. Although this library is contained in the Debian distribution, it was built from the identical files having been used on Windows. The selected CodeLite IDE has been doing the job very well.

    Not using the built-in libfaad had the reason that QIRX reliably crashed when it came to AAC decoding. An incompatibility with the libfaad used on Windows has been suspected. However, this was not the case. The culprit was an incompatibility between the Windows and the Linux (gcc) compiler with respect to the "long" data type . The former treats it - also in 64-Bit systems - as 32-Bit number, the latter as 64-Bit number. After a P/Invoke structure used by the "NeAACDecode" function had got a platform-specific change, everything worked as expected.

  • Restrictions

    No spectra. The MS-Chart available in .Net Windows does not work in Mono. Although the namespaces are all available in Monodevelop, the functionality is not, important functions throw a NotImplementedException.

    No Linux audio. NAudio does not work under Linux, as is well-known. Although a C# binding exists for the popular PortAudio , it was abandoned after learning from the examples that the blocking mode would not work. The (normally used) alternative with callback functions was not tried because the solution with P/Invoke callbacks seemed very complicated. No C# examples were available.
    As a consequence, the Audio .wav recorder is not available.

    As a replacement, the solution to stream the audio via UDP broadcast was selected. It is a well-functioning and very simple work-around.

    General GUI. Another source of incompatibility is the general Windows Forms behaviour. Not everything works like in the original on Windows. Resizing, Anchoring, Transparent backgrounds of bitmaps are examples of incompatiblities. These are not nice, but tolerable for the more experimental purpose followed here. However, in a production environment one had to find different solutions.

  • Platform Specifics:

    Conditional Compilation. The only parts of conditional compilation are the selections of the P/Invoke C++ DLLs (Shared Objects in Linux) in the C# code. Although Mono describes a runtime solution for the problem, this path was not followed. Instead, all P/Invoke interfaces were concentrated in one single project, and the switch between the Windows ".dll" and the Linux ".so" files is made with a conditional compile. The library names must be present at compile-time.

    P/Invoke. Amazingly, with one exception the interfacing between Mono and native C code worked very smoothly. Linux specifics must of course be observed, in particular the search algorithm for .so shared libraries. The .so is NOT found when present in the path where the "exe" resides, in contrast to Windows.
    As described above, one source of grief has been the fact that the Windows C-compiler treats the "long" data type as 32-Bit, whereas the gcc compiler treats it as 64-Bit. Forgetting about this fact can easily result in many hours of troubleshooting.

    Runtime Platform Distinction. During runtime, platform-specific distinctions must be made in a number of code locations, often due to GUI incompatibilities. As the platform detection is very easy, this has been no principal source of trouble, it just caused a lot of testing.

  • Performance:

    The performance is disappointing. On an i7-equipped notebook (about 5 years old), the DAB services with the highest bit rates of 144kbit/s cannot be decoded within the necessary 96ms time frame, whereas the very same machine running under Windows7 has no problem decoding those services. The following pictures may give a hint for the reason. In both cases, Linux and Windows, the rtl_tcp I/Q data server was running on another machine.

    QIRX running under Linux/Mono

    The red box in the QIRX window (right side) shows that the CPU is not able to decode within 96ms the hr2-service being transmitted with 144kbit/s. It needs more than 100ms resulting in a stuttering audio output. On the left hand side of the picture the output of the Linux "top"-command is shown, with the option to indicate the load of all available CPUs (top part of left side). The notebook is a quad-core with hyperthreading, showing 8 CPUs.
    The table clearly shows that almost only Cpu7 contributes to the overall load, leaving all other cores almost idling. As a consequence, although there is more than sufficient processing power left, it remains unused. The overall load is shown as 14.2% in the lower table.
    The identical QIRX software doing exactly the same under Win7 on the same machine (dual-boot) is shown in the next picture.

    QIRX running under Windows 7

    Again, on the right hand side is the QIRX GUI. The 95.4ms of the DAB Frame decoding time shows that there is no time problem decoding the service.
    On the left hand side of the picture the output of Sysinternals' Process Explorer is shown. The overall load caused by QIRX is shown in the lower part and is 8.5%. The main difference is the upper part titled "System Information". It shows on a per CPU basis the load of the system. Although not only showing the QIRX activities, the load caused by the remaining processes is almost zero. Hovering over the graphs indicates on all of them that the load is caused by qirx. This picture shows that the load is distributed almost evenly among the used processors, leading to a much more economic usage of the system.
  • Future:

    The future and usability in production environments etc. of Mono, .NET Core and the like is not clear. A more or less comprehensive overview of the situation might be found on Stackoverflow.