playing midi on windows is easy; windows has a built-in synth: microsoft gs wavetable synth, so you can play a midi file by opening it with windows media player; it often sounds garbage, but at least you can hear something; likewise, you can plug in a midi keyboard and start a foolproof e-piano software, bam, your music is there;

the same job is more complicated on linux; you need to understand a few concepts and combine several programs to turn midi into sound;

linux sound system

i believe you can google a few good articles about the linux sound system; but here i just want to keep things at their simplest: alsa and fluidsynth;

alsa

alsa stands for advanced linux sound architecture; it provides linux sound card drivers and an api to use them; therefore, an application can make sound using alsa library; one can think of alsa as the root of all sounds on linux;

fluidsynth

with alsa, playing audio file is a matter of reading data from the file and sending them to alsa, which will then drive the sound card; but a midi file does not contain audio data; as you can imagine, we need a program to convert midi data into audio data: this is called a synth;

a widely-used synth on linux is fluidsynth; it has a gui named qsynth;

play a midi file

with alsa and fluidsynth, we can play a midi file; if you dont have a midi file at hand, freemidi is a good place to get one; wlog, lets name it a.mid;

to play this file, run:

$ fluidsynth -a alsa FluidR3_GM.sf2 a.mid

FluidR3_GM.sf2 is a soundfont file; a soundfont is basically a bank of sounds arranged by midi notes; this specific soundfont comes from fluidsynth itself; if you dont like those sounds, you can search online and try another one;

play a midi keyboard: fluidsynth

we can also play a midi keyboard with alsa and fluidsynth; now we need to run fluidsynth in server mode and route midi data from keyboard to it;

first, we find the address of the midi keyboard using alsa utility amidi:

$ amidi -l
Dir Device    Name
IO  hw:1,0,0  WTF-MIDI-Keyboard

second, we start fluidsynth server with the midi keyboard device address:

$ fluidsynth -s -a alsa -m alsa_raw -o midi.alsa.device=hw:1,0,0 FluidR3_GM.sf2

play a midi keyboard: qsynth

we can do the same using qsynth, a gui front-end of fluidsynth;

  1. click Setup...;

  2. change settings:

    • midi settings:

      qsynth-0.jpg

    • audio settings:

      qsynth-1.jpg

    • qsynth should already come up with a default soundfont; you can change this in Soundfonts tab;

  3. click Restart to apply changes;

if playing midi file and midi keyboard are all you want, you can stop reading now; below we will cover some additional topics;

alsa sequencer

in the above example, we were using alsa_raw as the midi driver to get input from the midi keyboard; we need to specify the keyboard address when starting the synth so that it knows where to fetch midi data; this static configuration is inflexible: what if we have multiple midi keyboards? what if midi keyboards come and go?

alsa sequencer comes to solve this problem; it can send midi events between clients; this helps build a midi patch-bay; it features a subscription model so that multiple clients can send data to the same application at the same time, and the routing can be changed dynamically;

here is an example of playing midi keyboard using alsa sequencer:

  1. start fluidsynth in server mode, using midi driver alsa_seq:

    $ fluidsynth -s -a alsa -m alsa_seq FluidR3_GM.sf2
    
  2. find the port number of the synth:

    $ aconnect -o
    client 128: 'FLUID Synth (12345)' [type=user,pid=12345]
        0 'Synth input port (12345:0)'
    
  3. find the port number of the midi keyboard:

    $ aconnect -i
    client 20: 'WTF-MIDI-Keyboard' [type=kernel,card=1]
        0 'WTF-MIDI-Keyboard MIDI 1'
    
  4. connect midi keyboard (sender) to synth (receiver):

    $ aconnect 20:0 128:0
    

    if you have multiple midi keyboards you can connect them all to the same synth; for example:

    $ aconnect 21:0 128:0
    $ aconnect 22:0 128:0
    $ aconnect 23:0 128:0
    
  5. the midi keyboard should work now;

  6. to check connected pairs:

    $ aconnect -l
    

    to disconnect a pair:

    $ aconnect -d 20:0 128:0
    

as you can see, alsa sequencer supports dynamic configuration very well; in the above example we were routing data from a midi keyboard to a synth, but we can route data from any sender to any receiver, such as between two applications;

jack

many linux sound articles will mention jack: what is it?

jack is a sound server providing real-time, low-latency connections for audio and midi data between applications that support its api; it can use alsa as the back-end for hardware communication, and therefore sits at a higher level than alsa in the linux audio stack;

since jack can route both audio and midi data, there are jack-audio and jack-midi; jack-midi and alsa sequencer do similar jobs, but jack-midi has better timing and can often be used as replacement for alsa sequencer;

our jack example is still about playing a midi keyboard:

  1. start jack:

    $ jackd -d alsa -r 44100 -p 128 -X raw
    

    -r sets sample rate; -p sets period; -X sets which alsa midi system to provide access to; it has 3 options:

    • none:

      this is the default option when you dont add -X in the command line; in this case jack doesnt use alsa midi devices; this means you cant connect to physical midi ports and cant route data to or from them;

      since we want to read midi data from keyboard, we cant use this option;

    • raw:

      jack converts raw alsa midi devices into jack midi ports; this often means jack exclusively owns these midi devices, and you cannot connect to them from other applications;

    • seq:

      jack converts alsa sequencer ports into jack midi ports; the alsa sequencer ports are still usable as well as the jack midi ports;

    in short, we recommend using -X raw, and will explain the difference between -X raw and -X seq in following examples;

  2. start fluidsynth using jack for both midi and audio:

    $ fluidsynth -s -a jack -m jack -j FluidR3_GM.sf2
    

    -a sets audio driver; -m sets midi driver; -j attempts to connect jack outputs to physical ports, so we dont have to do it manually;

  3. find jack midi ports that need to be connected:

    we use jack_lsp to list jack ports, with aliases and properties:

    $ jack_lsp -Ap
    ...
    system:midi_capture_2
       alsa_pcm:WTF-MIDI-Keyboard/midi_playback_1
       WTF-MIDI-Keyboard:midi/playback_1
            properties: output,physical,terminal,
    system:midi_playback_2
       alsa_pcm:WTF-MIDI-Keyboard/midi_capture_1
       WTF-MIDI-Keyboard:midi/capture_1
            properties: input,physical,terminal,
    ...
    fluidsynth:left
            properties: output,
    fluidsynth:right
            properties: output,
    fluidsynth:midi_00
            properties: input,terminal,
    ...
    

    from this list, it is clear that the pair to connect is:

    (system:midi_capture_2, fluidsynth:midi_00)
    
  4. connect sender to receiver:

    $ jack_connect system:midi_capture_2 fluidsynth:midi_00
    

this completes the steps and our keyboard should work now;

raw vs seq

to show the difference between -X raw and -X seq, we use this script to run a comparison:

$ jackd -d alsa -r 44100 -p 128 -X [raw|seq]
$ fluidsynth -s -a jack -m alsa_seq -j FluidR3_GM.sf2

this script starts a fluidsynth instance exposing alsa sequencer midi ports; we then try to connect our keyboard to this fluidsynth instance, using either jack ports or alsa sequencer ports (port names and numbers may vary);

  • case raw:

    we cannot connect using jack ports, because the fluidsynth doesnt have an input jack port:

    • the fluidsynth is using alsa_seq driver, which creates alsa sequencer ports, not jack ports;

    • jack with -X raw creates jack ports for physical alsa midi devices, not alsa sequencer ports;

    we can only try to connect using alsa sequencer ports:

    $ aconnect 20:0 128:0
    Connection failed (Resource temporarily unavailable)
    

    this is because jack with -X raw (exclusively) grabs the physical alsa midi devices, making them unavailable to other connections;

  • case seq:

    connection using jack ports works:

    $ jack_connect system:midi_capture_2 system:midi_playback_4
    

    because jack creates jack ports for fluidsynth mapped to its alsa sequencer ports;

    connection using alsa sequencer ports also works:

    $ aconnect 20:0 129:0
    

    because the fluidsynth alsa sequencer ports are still working there;

question: if -X seq works when -X raw doesnt, why is -X raw recommended?

answer: because the jack midi developers have a dream where it is the normal case for jack to exclusively control the sound system; in general, it is recommended to grant exclusive access to the audio system to one program, minimizing interference from other similar programs; alsa sequencer is one of those; another example is pulseaudio; pulseaudio is actually not bad, but is not designed for low latency audio; in fact we recommend you kill pulseaudio while running jack, but jack seems to be auto suspending pulseaudio already;

qjackctl

jack has a gui front-end qjackctl, which is able to control jack server and manage connections; it features a graphical patch-bay that allows easy rewire;

we recommend install qjackctl-0.6.0 or above; there is a subtle client name bug in qjackctl-0.5.5 that can make graphical wiring unfunctioning;

using qjackctl, we can make midi keyboard work with jack easily:

  1. run qjackctl;

    qjackctl-0.jpg

  2. click Setup... and change settings;

    qjackctl-1.jpg

  3. click Start to start jack server;

  4. start fluidsynth or qsynth, setting both midi and audio driver to jack;

  5. click Graph to wire jack ports;

    qjackctl arranges jack ports into clients; to route midi or audio data from one port to another, you simply click the source port and drag it to the destination port; by default, jack midi ports are red and jack audio ports are green; the patch-bay graph clearly shows the wiring;

    qjackctl-2.jpg

a client name bug

in qjackctl-0.5.5, a qsynth client can be shown as two nodes with the same name; when users try to connect a system midi-out port to a qsynth midi-in port by dragging, qjackctl will find the wrong node and cancels the wiring;

the bug may or may not exist in other versions; however, in qjackctl-0.6.0 it doesnt show up because there is only one qsynth node;

fedora 29 users: download qjackctl-0.6.0-1.fc29.x86_64.rpm;

troubleshooting

jack fails to create realtime thread during start

  1. add yourself to audio group:

    $ usermod -a -G audio {username}
    
  2. create or edit /etc/security/limits.d/95-jack.conf:

    @audio      - rtprio    95
    @audio      - memlock   unlimited
    
    @jackuser   - rtprio    95
    @jackuser   - memlock   unlimited
    
  3. reboot for these changes to take effect;

vmpk doesnt work with jack

no, it doesnt work with jack any more; it once did, but then at some version it switched to a different library which doesnt support jack; this message is from one of its developers; i forgot where i read this, though;