Making Mplay.jl work under Linux

Here’s how I got Mplay.jl to work under Linux (Ubuntu 20.04).

Mplay.jl writes to a “raw midi device”, and Ubuntu does not have those on by default.
To create a “virtual midi device” I do

$ sudo modprobe snd-virmidi snd_index=1

On my system they show up like this:

$ ls  /dev/snd/midi*
/dev/snd/midiC3D0  /dev/snd/midiC3D1  /dev/snd/midiC3D2  /dev/snd/midiC3D3

The number “3” in “midiC3D0” is important, and depends on the number of physical soundcards on the system.

We also need a software synthesizer to turn the midi code into a sound signal. I use fluidsynth together with the FluidR3 GM soundfonts. On Ubuntu I get them like this (we will need alsa-utils also):

$ sudo apt install fluidsynth fluid-soundfont-gm alsa-utils

Then I can run fluidsynth in “server mode” (it blocks the terminal):

$ fluidsynth --server --audio-driver=alsa -o audio.alsa.device=hw:0 /usr/share/sounds/sf2/FluidR3_GM.sf2
>

Here the device “hw:0” is the soundcard on the motherboard, so the sound will come out of the headphones I plugged in the jack on the front of the computer.

Now we need to connect fluidsynth to the the virtual midi device. This is done using aconnect from alsa-utils.

First we need an overview of our system:

$ aconnect -l
client 0: 'System' [type=kernel]
    0 'Timer           '
    1 'Announce        '
client 14: 'Midi Through' [type=kernel]
    0 'Midi Through Port-0'
client 28: 'Virtual Raw MIDI 3-0' [type=kernel,card=3]
    0 'VirMIDI 3-0     '
client 29: 'Virtual Raw MIDI 3-1' [type=kernel,card=3]
    0 'VirMIDI 3-1     '
client 30: 'Virtual Raw MIDI 3-2' [type=kernel,card=3]
    0 'VirMIDI 3-2     '
client 31: 'Virtual Raw MIDI 3-3' [type=kernel,card=3]
    0 'VirMIDI 3-3     '
client 128: 'FLUID Synth (5104)' [type=user,pid=5104]
    0 'Synth input port (5104:0)'

My virtual midi devices are clients 28 to 31 and fluidsynth is client 128 (these numbers might be different on other systems).

Then I can connect the output of client 28 to the input of client 128 like this:

$ aconnect 28:0 128:0

The result should look like this:

$ aconnect -l
client 0: 'System' [type=kernel]
    0 'Timer           '
    1 'Announce        '
client 14: 'Midi Through' [type=kernel]
    0 'Midi Through Port-0'
client 28: 'Virtual Raw MIDI 3-0' [type=kernel,card=3]
    0 'VirMIDI 3-0     '
	Connecting To: 128:0
client 29: 'Virtual Raw MIDI 3-1' [type=kernel,card=3]
    0 'VirMIDI 3-1     '
client 30: 'Virtual Raw MIDI 3-2' [type=kernel,card=3]
    0 'VirMIDI 3-2     '
client 31: 'Virtual Raw MIDI 3-3' [type=kernel,card=3]
    0 'VirMIDI 3-3     '
client 128: 'FLUID Synth (5104)' [type=user,pid=5104]
    0 'Synth input port (5104:0)'
	Connected From: 28:0

Now we are ready to start Mplay.jl using the helper script in the src directory:

Mplay.jl/src$ bash Mplay.sh --device="hw:3,0" song.midi

Here the number “3” in “hw:3,0” is the same “3” as in /dev/snd/midiC3D0

With a bit of luck, your song will now play.

If not, try reconnecting the midi device and fluidsynth by doing

$ aconnect -x
$ aconnect 28:0 128:0

The graphical tool aconnectgui gives a graphical version of aconnect -l

I hope this can help others.

I’ve used these 2 midi tutorials:

There a lots of free midi file son the internet, eg here: https://freemidi.org/

3 Likes

Thanks for the info. I tried the same with timidity and the system did no longer complain about open/write errors - but I couldn’t hear anything.

I tried also with timidity is server mode (timidity -iA).

At first I got got no sound, but after playing a midifile directly timidity song.midi then it worked when retrying the server.

The current master branch seems to work fine on Linux. The problem on my Linux box was that simple: the Timidity++ output was muted. I also fixed the console interface - the terminal settings were not reset correctly when leaving Mplay.

May be someone could test the GUI - IMO it should work. But my Ubuntu VM seems to have problems with the OpenGL software rasterization (although similar demos work).

Thanks @jheinen for updating and also for updating the README.

I tested the --gui option.

On current master I get this error:

ERROR: LoadError: could not load library "/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries/libGL.dylib"
/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries/libGL.dylib.so: cannot open shared object file: No such file or directory

This is easily solved by this patch:

diff --git a/src/OpenGL.jl b/src/OpenGL.jl
index 0d9e63d..964d458 100644
--- a/src/OpenGL.jl
+++ b/src/OpenGL.jl
@@ -1,6 +1,7 @@
 module OpenGL
 
-const libGL = Sys.KERNEL == :NT ? "C:/Windows/System32/opengl32.dll" : "/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries/libGL.dylib"
+const libGL = Sys.KERNEL == :NT ? "C:/Windows/System32/opengl32.dll" :
+              Sys.KERNEL == :Linux ? "/usr/lib/x86_64-linux-gnu/libGL.so" : "/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries/libGL.dylib"
 
 const GL_LINES = 0x0001
 export GL_LINES

However, with this patch, I get this error

bash src/Mplay.sh --device="hw:3,0" --gui song.midi
ALSA lib rawmidi.c:281:(snd_rawmidi_open_noupdate) Unknown RawMidi 
midi: cannot open MIDI output

Which is strange, as this command plays the song (with the text interface):

bash src/Mplay.sh --device="hw:3,0" song.midi

Not sure how that come.

This patch fixed the midi device error:

diff --git a/src/Mplay.jl b/src/Mplay.jl
index cc2d042..c2e18bd 100644
--- a/src/Mplay.jl
+++ b/src/Mplay.jl
@@ -318,7 +318,7 @@ export mplay
 function main()
     if length(ARGS) > 0
         path = ARGS[1]
-        device = length(ARGS) > 1 ? ARGS[2] : ""
+        device = length(ARGS) > 1 ? ARGS[2] : haskey(ENV, "MIDI_DEVICE")
         mplay(path, device)
     end
 end

But now it fails like this:

ERROR: LoadError: MethodError: no method matching unsafe_convert(::Type{Ptr{UInt8}}, ::Bool)
Closest candidates are:
  unsafe_convert(::Type{Ptr{UInt8}}, ::Symbol) at pointer.jl:57
  unsafe_convert(::Type{Ptr{UInt8}}, ::String) at pointer.jl:59
  unsafe_convert(::Type{T}, ::T) where T<:Ptr at essentials.jl:391

I guess I need to look closer at the differences between tui.jl and Mplay.jl