The entire state of a medianet node is collected in
/etc/medianet/config.json
. This file is being watched for changes by
mn_config.path (a systemd path
unit).
When a change in that file is detected, the mn_config_update
command is
triggered to automatically generate all necessary configuration files for
the features provided by the medianet distribution overlay.
You can provision or re-provision a running medianet node by dropping a new
config file in the proper place, waiting for the mn_config_update
background task to complete its job (you can watch its progress with sudo journalctl -f
), and rebooting.
In its current state, the config.json mechanism is a very thin layer of glue on top of systemd unit files to collect the configurations of multiple audio tools in a single place. It exposes the wildly different configuration mechanisms for all these tools. In the future, alternative services may be offered which hardcode most of the configuration and offer a more systematic and inituitive interface to config.json.
The config file is a serialized JSON object, which makes it accessible to automated deployment tools. It does however have a few differences compared to other common configuration file formats:
- You cannot use comments.
- You cannot use tab or other meta characters inside string literals (the JSON structure itself may well be formatted with tabs for better readability). Unfortunately, this precludes the use of multiline strings, which would sometimes improve readability.
- All keys are case sensitive.
The outer framework of top-level elements is as follows:
{
A short string describing what this particular setup does,
ideally a token without whitespaces and special chars,
shown on the login screen and available to scripts that include
/etc/medianet/config.inc
as ${CONFIG_PRODUCT}
:
"product" : "base_model",
A longer description of what this setup does, also shown on
the login screen and available as {CONFIG_DESCRIPTION}
:
"description" : "Example audio source with simple DSP chain, shairport-sync sink, zita-njbridge source and Icecast2 stream server",
This will be the actual hostname of this machine, so
[a-z][a-z0-9-]*
only and should not start with a number. It is
available as ${CONFIG_HOSTNME}
(or via the ubiquitous env
variable $HOSTNAME):
"hostname" : "mn-basic",
An arbitrarily chosen string that helps you remember the
physical whereabouts of this machine. Available as
${CONFIG_LOCATION}
:
"location" : "Mad Scientist Lab",
An arbitrarily chosen string that helps you keep track of
configuration changes in longer-term deployments. Available
as ${CONFIG_VERSION}
:
"version" : "2022-01-17",
The following keys control the low-level configuration of your Raspberry Pi as described in /boot/config.txt:
"bootConfig": {
dtparam and dtoverlay statements can occur multiple times, so they are handled as arrays of strings:
"dtparam" : [
"audio=off"
],
"dtoverlay" : [
"disable-wifi",
"disable-bt"
],
All other settings only occur a single time, so they are handled as simple key/value pairs:
"gpu_mem" : 128
},
You can use any command that is allowed in the Raspberry Pi config.txt file as a key/value pair in the
bootConfig
section.Caution: there is an implicit and not very well documented order to dtoverlay/dtparam commands, and the config.json parser may get this context-sensitivity wrong at times (see issue #108).
All features of a medianet system are systemd services. They are configured as follows:
"systemdUnits": [
{
"unit" : "mn_foo",
Some services also exist as
systemd templates
, which means they can be instatiated multiple times, for example asmn_mpv@player_1
. Check/medianet/overlay/usr/local/lib/system/system/
for services ending in@
.
This is redundant, because we do not currently handle
unit types other than service
in config.json, and the
option might go away in the future:
"type" : "service",
A 0/1 value to allow configured units to be turned off easily. Any unit that is not explicitly enabled in config.json is assumed off (i.e. deleting a unit object here will disable it):
"enabled" : 1,
JACK clients will honour the following option if the
underlying program supports setting the client name. If
it doesn't (such as with jackd itself, which always names
itself system
), it must be set to the actual value the
jack client uses, so that connection management works
correctly:
"jackName" : "foo"
The options mechanism is used for programs that can be
completely configured via the command line:
This is either the full set of command line options to the
underlying program, or a partial set with some settings
hardcoded into the corresponding service file. Check
/medianet/overlay/usr/lib/systemd/system/mn_*.service
for details.
"options" : "-k -zMagic --anticipate_user_needs"
For programs that read their configuration from a configuration file, the config mechanism is used. It consists of a simple array of strings, which will be written out into the corresponding config file by mn_config_update, which knows the name and location of said file:
"config" : [
"first line of config statements",
"second line of config statements"
],
JACK clients can optionally define inPorts
and outPorts
.
The ports thus defined are handled by the automatic
connection management. You cannot change the names via
this mechanism, port names have to match what the client
is actually providing. inPorts and outPorts are arrays of
objects. When the client name matches the jackName
, you
only need to list the relative port names after the colon
(see jack_lsp). If they don't, as is the case with
mod-host for example, you will have to provide a fully
qualified port name:
"inPorts" : [
{
"portName" : "left_input"
},
{
"portName" : "right_input"
}
],
outPorts can optionally specifiy the targetUnit
and targetPort
index (of that unit's list of inPorts) they want to be
connected to. The port index starts with 0. I regret that
choice now, but we're stuck with it for the time being:
"outPorts" : [
{
"portName" : "left_output"
"targetUnit": "mn_jackd"
"targetPort": 0
},
{
"portName" : "right_output"
"targetUnit": "mn_jackd"
"targetPort : 1
}
]
}
]
}
The automatic configuration updater (or a manual call to mn_config_update
)
will read the JACK connection graph fron the config.json
file and write each
service's incoming and outgoing connections to
/etc/systemd/system/foo.service.d/foo.service.connections
.
Each service performs
ExecStartPost=-/usr/local/bin/mn_connect foo.service --timeout 30 --interval 5
as part of its post-startup routine, which will attempt to connect all ports
as quickly as possible, and keep retrying to connect failed ports (e.g. to
clients who might still be starting up) every 5 seconds for a total of 30
seconds. The call is prepended with a -
(minus) sign, which means the
service will not fail even if some connection could not be made.
This behaviour has changed - previously, a failed JACK client could cause connection timeouts in others and force them to restart. The idea behind the new behaviour is to leave core services (such as
mn_mod-host
) running unhindered, even if a peripheral service (a frequent culprit wasmn_listen
due to an icecast2 issue) has failed.
This is part of the default configuration. Unfortunately, the Pi5 does not have a built-in mini jack output anymore, so it will break for Pi5 users.
{
"unit" : "mn_jackd",
"type" : "service",
"enabled" : 1,
"jackName": "system",
"options" : "-R -P40 -d alsa -d hw:Headphones -P -S -p 1024 -n 2 -r 48000 -i 0 -o 2",
"inPorts": [
{
"portName" : "playback_1"
},
{
"portName" : "playback_2"
}
],
"outPorts": [
]
}
This set of command line options makes jackd run with realtime permissions
(-R
) at a realtime priority -P
of 40, i.e. below that of the kernel's
tasklet handlers, so it cannot starve the audio device. In some
circumstances, e.g. under heavy network load when running alongside an MJPEG
stream receiver, it may be necessary to raise this above the tasklet
priority.
We use the ALSA backend and the hardware device "Headphones" without any
software adaptor layer (hw:
). This is highly efficient, but it means that
the jackd settings must match the hardware capabilities exactly.
Since the PWM device provides only an output, we select playback-only mode
(-P
). The device expects 16 bit integers, so we set the -S
(shorts)
option.
The PWM device is a little bit tricky with respect to timing - a generally
stable setting is a period size of 1024 frames (-p
) and two periods per
buffer (-n
). At this setting, it has no trouble coping with a samle rate
of 48000 Hz (-r
).
Due to a minor bug in jackd, the audio device will only open successfully if
we additionally specify zero input channels and two output channels (-i
and -o
), although this should be implied by -P
already.
This setup will enable 7.1 surround playback via the HDMI port to a sufficiently equipped A/V home cinema amplifier or a HDMI audio extractor.
It has been tested on a Raspberry Pi 4B running KODI. It should also run on other Pi boards, and shorter period sizes (at least down to 512) should be possible.
For 5.1 or stereo-only playback, you could limit the number of output channels to 6 (
-o 6
) or 2, and and use fewerinPorts
, but the savings in CPU load are usually negligible.
For a complete surround setup using KODI, see the KODI example below.
{
"unit" : "mn_jackd",
"type" : "service",
"enabled" : 1,
"jackName": "system",
"options" : "-R -P40 -d alsa -d hdmi:vc4hdmi0 -P -S -p 1024 -n 2 -r 48000 -i 0 -o 8"
"inPorts": [
{
"portName" : "playback_1"
},
{
"portName" : "playback_2"
},
{
"portName" : "playback_3"
},
{
"portName" : "playback_4"
},
{
"portName" : "playback_5"
},
{
"portName" : "playback_6"
},
{
"portName" : "playback_7"
},
{
"portName" : "playback_8"
}
],
"outPorts": [
]
}
This setting will only work if an audio-capable HDMI sink is connected to the Pi. Ways to force-hotplug the hardware to always provide this device and to enable up to 8 channel playback for surround sound applications are currently under investigation.
For an alternative approach, check out the KODI section, which uses
zita-j2a
for the job and has performed reliably in a few installations.
To enable the driver for this card, add
bootConfig : [
dtoverlay : [
"hifiberry-dacplusadc"
]
]
{
"unit" : "mn_jackd",
"type" : "service",
"enabled" : 1,
"jackName": "system",
"options" : "-R -t 4500 -P 40 -dalsa -dhw:sndrpihifiberry -D -r48000 -p512 -n2 -zs",
"inPorts": [
{
"portName" : "playback_1"
},
{
"portName" : "playback_2"
}
],
"outPorts": [
{
"portName" : "capture_1",
"targetUnit" : "mn_mod-host",
"targetPort" : 0
},
{
"portName" : "capture_2",
"targetUnit" : "mn_mod-host",
"targetPort" : 1
}
]
}
If you need lower latency, it is possible to run Hifiberry cards at -p 128
without any problems.
The JACK settings generally remain the same. Just add the correct overlay as
per the Hifiberry documentation, and for devices without inputs, use -P
("playback only") instead of -D
("duplex") in JACK options, and leave the
inPorts
array empty.
mod-host provides an easy way to run a DSP chain of LV2 plugins on the Pi.
Its setup is slightly tricky because each plugin running in mod-host
presents as a separate JACK client called effect_N
, where N is an index set
during plugin instantiation.
Not all mod-host client ports need to be declared as inPorts or outPorts - it is usually more convenient to only declare the entrance and exit ports, and treat inter-plugin connections as "private" to mod-host. This example puts a 12x10 matrix mixer at the beginning of the plugin chain, so that multiple sources can be mixed together at different levels. The actual DSP chain consists of a parametric equalizer, a simple speaker management plugin for delay and gain compensation, and a limiter to prevent overloading of the analog output stage.
Only a few of the matrix inputs and the two limiter outputs are declared as
external ports (and will be handled by the connection management system),
the others are private to mod-host and are handled by connect
statements
in the config
section.
The config section also contains some default settings of plugin parameters.
If parameters are changed at runtime, the mn_modsave
/mn_modconf
mechanism will ensure their persistence.
The medianet distribution comes with a simple web GUI to control the settings of plugins running in mod-host. It is accessible via http://$HOSTNAME/medianet/DSP and provided by the lv2rdf2html package.
The GUI is machine-generated, but not dynamically: after changing the plugin configuration of mn_mod-host, you will have to re-run
$~> /medianet/sbin/mn_build lv2rdf2html
The generated plugin GUI will automatically be deployed to the web server docroot.
{
"unit" : "mn_mod-host",
"type" : "service",
"enabled" : 1,
"jackName": "mod-host",
"options" : "",
"inPorts" : [
{
"portName" : "effect_0:in1"
},
{
"portName" : "effect_0:in2"
},
{
"portName" : "effect_0:in3"
},
{
"portName" : "effect_0:in4"
},
{
"portName" : "effect_0:in5"
},
{
"portName" : "effect_0:in6"
}
],
"outPorts": [
{
"portName" : "effect_9:outL",
"targetUnit" : "mn_jackd",
"targetPort" : 0
},{
"portName" : "effect_9:outR",
"targetUnit" : "mn_jackd",
"targetPort" : 1
}
],
"config": [
"add http://gareus.org/oss/lv2/matrixmixer#i12o10 0",
"add http://gareus.org/oss/lv2/fil4#stereo 1",
"add http://stackingdwarves.net/lv2/sm#stereo 4",
"add http://gareus.org/oss/lv2/dpl#stereo 9",
"connect effect_0:out1 effect_1:inL",
"connect effect_0:out2 effect_1:inR",
"connect effect_1:outL effect_4:inL",
"connect effect_1:outR effect_4:inR",
"connect effect_4:outL effect_9:inL",
"connect effect_4:outR effect_9:inR",
"param_set 0 mix_1_1 1",
"param_set 0 mix_1_3 1",
"param_set 0 mix_2_2 1",
"param_set 0 mix_2_4 1",
"param_set 0 mix_3_1 1",
"param_set 0 mix_3_3 1",
"param_set 0 mix 4_2 1",
"param_set 0 mix_4_4 1",
"param_set 0 mix_5_1 1",
"param_set 0 mix_5_3 1",
"param_set 0 mix_5_5 0",
"param_set 0 mix_6_2 1",
"param_set 0 mix_6_4 1",
"param_set 0 mix_6_6 0",
"param_set 0 mix_7_7 0",
"param_set 0 mix_8_8 0",
"param_set 0 mix_9_9 0",
"param_set 0 mix_10_10 0",
"param_set 9 gain -2",
"param_set 9 threshold -2"
]
}
The zita-njbridge package will enable you to stream very low-latency, uncompressed audio between hosts running JACK with unsynchronised sample clocks, using very-high-quality dynamic resampling.
It is usually not a good idea to run the JACK graph on the built-in PWM device - its timing stability is not very good and will lead to unsatisfactory zita-njbridge performance, as it will have to compensate for both the deviations at the remote and the local end. Use a physical sound card if at all possible.
This snippet will send audio from the local JACK graph onto an administratively-scoped IPv4 multicast group, where it can be picked up by an arbitrary number of receivers on the local network.
Be careful: dumb switches that do not support IGMP snooping will just duplicate multicast traffic to all ports, no matter whether the client behind that ports wants it or not. Using more than a few multicast streams under such conditions will quickly degrate network performance.
If your network addresses are stable and known in advance, and you only want to send audio to one receiver, it's better to use a normal unicast IPv4 address.
Stick to port 30000 - mn_config_update
will automatically open that port in
the firewall when it detects an active zita-njbridge service.
For latency and debugging hints, consult the
zita-njbridge
man page.
{
"unit" : "mn_zita-j2n",
"type" : "service",
"enabled" : 1,
"jackName": "zita-j2n",
"options" : "--chan 2 239.192.17.65 30000 medianet0",
"inPorts": [
{
"portName" : "in_1"
},
{
"portName" : "in_2"
}
]
}
This snippet will receive audio from an administratively-scoped IPv4 multicast group and make it available to the local JACK graph. Because the zita-n2j client has to perform dynamic resampling, its CPU load is non-negligible. An additional buffer setting of 20 ms is conservative and may be reduced to 10 or even 0 on a dedicated network without other traffic.
If you stick to the default port of 30000, mn_config_update will automatically punch a hole in the firewall for you.
{
"unit" : "mn_zita-n2j",
"type" : "service",
"enabled" : 0,
"jackName": "zita-n2j",
"options" : "--chan 1,2 --buff 20 239.0.0.1 30000 medianet0",
"inPorts" : [],
"outPorts": [
{
"portName" : "out_1",
"targetUnit" : "mn_mod-host",
"targetPort" : 0
},
{
"portName" : "out_2",
"targetUnit" : "mn_mod-host",
"targetPort" : 1
}
]
}
Thanks to Mike Brady and his helpful attitude towards accepting a JACK backend to shairport-sync, AirPlay and Airplay2 audio can now be fully integrated into a medianet audio system.
You do not need an Apple device to use this audio source - a number of free Android apps are available to stream AirPlay, and it is also supported by PulseAudio with its RAOP sink and source.
{
"unit" : "mn_shairport-sync",
"type" : "service",
"enabled" : 1,
"jackName": "shairport-sync",
"inPorts" : [],
"outPorts": [
{
"portName" : "out_L",
"targetUnit" : "mn_mod-host",
"targetPort" : 0
},
{
"portName" : "out_R",
"targetUnit" : "mn_mod-host",
"targetPort" : 1
}
],
"config" : [
"general = {",
" name = \"[mn] %h\";",
" interpolation = \"soxr\";",
" output_backend = \"jack\";",
" drift_tolerance_in_seconds = 0.015;",
" ignore_volume_control = \"no\";",
" interface = \"medianet0\";",
"}",
"jack = {",
" soxr_resample_quality = \"very high\"",
"}",
"sessioncontrol = {",
"// run_this_before_play_begins = \"/usr/local/bin/mn_disconnect zita-n2j.service\";",
"// run_this_after_play_ends = \"/usr/local/bin/mn_connect zita-n2j.service\";",
"}",
"diagnostics = {",
" statistics = \"no\";",
"}"
]
}
With two Raspberry Pi 4B and a cheap HDMI USB2 grabber, it is possible
to create a low-cost HDMI over IP extender with reasonable, although not
perfect, quality. Video is grabbed by a gstreamer
chain and forwarded
as quickly as possible, and received without buffer or resynchronisation
by another gstreamer chain on the sink.
The audio is transmitted independently via zita-njbridge
and resampled
to the sink's JACK clock.
Again, you can opt for multicasting, which is easy to configure and will allow multiple sinks without extra costs, but it will rapidly congest your network if you have switches that do not do IGMP snooping. Alternatively, you can insert a (fixed) IPv4 address.
mn_hdmi-[tx|rx] will also accept a local hostname (which will be looked up and replaced by an IP address before calling gstreamer). This needs to be implemented for zita-njbridge as well.
{
"unit" : "mn_hdmi-tx",
"type" : "service",
"enabled" : 1,
"options" : "/dev/video0 239.192.17.43 29999"
},
{
"unit" : "mn_zita-j2n@hdmi",
"type" : "service",
"enabled" : 1,
"jackName": "hdmi_sender",
"options" : "--chan 2 239.192.17.44 30000 medianet0",
"inPorts": [
{
"portName" : "in_1"
},
{
"portName" : "in_2"
}
]
}
{
"unit" : "mn_hdmi-rx",
"type" : "service",
"enabled" : 1,
"options" : "239.192.17.43 29999"
},
{
"unit" : "mn_zita-n2j@hdmi",
"type" : "service",
"enabled" : 1,
"jackName": "zita-n2j",
"options" : "--chan 1,2 --buff 30 239.192.17.44 30000 me
"inPorts" : [],
"outPorts": [
{
"portName" : "out_1",
"targetUnit" : "mn_mod-host",
"targetPort" : 4
},
{
"portName" : "out_2",
"targetUnit" : "mn_mod-host",
"targetPort" : 5
}
]
}
If you are using the HDMI output for media display, you may want to prevent your audience from seeing system messages or the Linux command prompt:
{
"unit" : "mn_autostart_root@clear_console",
"type" : "service",
"enabled" : 1,
"options" : "/usr/local/bin/mn_console --blank"
}
To see the console again, issue sudo mn_console --unblank
. You might also
have to hit a key locally and/or kill the agetty process to get back to the
initial view.
This snippet will help you integrate KODI into your medianet setup. It is
assumed that you have a 5.1 amplifier that is connected via HDMI, or (in my
case), an HDMI audio extractor connected to an active 5.1 speaker set.
The JACK server can run in dummy mode or on any other sound device, such as
the built-in mini-jack via the PWM device. zita-j2a
will resample your
audio to play nicely over the HDMI output.
The channel order is L, R, C, Sub, SL, SR. We add a stereo EQ plugin each for L/R and SL/SR, and a mono EQ for center and sub. Loudspeaker management plugins are used to delay the surround speakers by 10 ms so that they don't hit before the screen ones, and the center is delayed by 0.6 ms because in my setup it sits slightly in front of the screen.
The settings below will add about 10 ms of latency, around a quarter frame for cinema content and well below tolerance thresholds. If it bothers you, you can adjust it via the settings icon in the KODI player. Remember to make it the default for all media.
{
"unit" : "mn_jackd",
"type" : "service",
"enabled" : 1,
"jackName": "system",
"options" : "-R -P40 -d alsa -d hdmi:vc4hdmi0 -P -S -p 1024 -n 2 -r 48000 -i 0 -o 8",
"inPorts": [
{
"portName" : "playback_1"
},
{
"portName" : "playback_2"
},
{
"portName" : "playback_3"
},
{
"portName" : "playback_4"
},
{
"portName" : "playback_5"
},
{
"portName" : "playback_6"
},
{
"portName" : "playback_7"
},
{
"portName" : "playback_8"
}
],
"outPorts": [
]
},
{
"unit" : "mn_kodi",
"type" : "service",
"enabled" : 1
},
{
"unit" : "mn_mod-host",
"type" : "service",
"enabled" : 1,
"jackName": "mod-host",
"options" : "",
"inPorts" : [
{
"portName" : "effect_0:in1"
},
{
"portName" : "effect_0:in2"
},
{
"portName" : "effect_0:in3"
},
{
"portName" : "effect_0:in4"
},
{
"portName" : "effect_0:in5"
},
{
"portName" : "effect_0:in6"
},
{
"portName" : "effect_0:in7"
},
{
"portName" : "effect_0:in8"
},
{
"portName" : "effect_0:in9"
},
{
"portName" : "effect_0:in10"
},
{
"portName" : "effect_0:in11"
},
{
"portName" : "effect_0:in12"
}
],
"outPorts": [
{
"portName" : "effect_19:outL",
"targetUnit" : "mn_jackd",
"targetPort" : 0
},{
"portName" : "effect_19:outR",
"targetUnit" : "mn_jackd",
"targetPort" : 1
},{
"portName" : "effect_39:out",
"targetUnit" : "mn_jackd",
"targetPort" : 3
},{
"portName" : "effect_49:out",
"targetUnit" : "mn_jackd",
"targetPort" : 2
},{
"portName" : "effect_59:outL",
"targetUnit" : "mn_jackd",
"targetPort" : 4
},{
"portName" : "effect_59:outR",
"targetUnit" : "mn_jackd",
"targetPort" : 5
},{
"portName" : "effect_0:out9",
"targetUnit" : "mn_listen",
"targetPort" : 0
},{
"portName" : "effect_0:out10",
"targetUnit" : "mn_listen",
"targetPort" : 1
}
],
"config": [
"add http://gareus.org/oss/lv2/matrixmixer#i12o10 0",
"add http://gareus.org/oss/lv2/fil4#stereo 11",
"add http://stackingdwarves.net/lv2/sm#stereo 14",
"add http://gareus.org/oss/lv2/dpl#stereo 19",
"add http://gareus.org/oss/lv2/fil4#mono 31",
"add http://stackingdwarves.net/lv2/sm#stereo 34",
"add http://gareus.org/oss/lv2/dpl#mono 39",
"add http://gareus.org/oss/lv2/fil4#mono 41",
"add http://gareus.org/oss/lv2/dpl#mono 49",
"add http://gareus.org/oss/lv2/fil4#stereo 51",
"add http://stackingdwarves.net/lv2/sm#stereo 54",
"add http://gareus.org/oss/lv2/dpl#stereo 59",
"connect effect_0:out1 effect_11:inL",
"connect effect_0:out2 effect_11:inR",
"connect effect_11:outL effect_14:inL",
"connect effect_11:outR effect_14:inR",
"connect effect_14:outL effect_19:inL",
"connect effect_14:outR effect_19:inR",
"connect effect_0:out3 effect_31:in",
"connect effect_0:out4 effect_41:in",
"connect effect_31:out effect_34:inL",
"connect effect_41:out effect_34:inR",
"connect effect_34:outL effect_39:in",
"connect effect_34:outR effect_49:in",
"connect effect_0:out5 effect_51:inL",
"connect effect_0:out6 effect_51:inR",
"connect effect_51:outL effect_54:inL",
"connect effect_51:outR effect_54:inR",
"connect effect_54:outL effect_59:inL",
"connect effect_54:outR effect_59:inR",
"param_set 0 mix_1_1 1",
"param_set 0 mix_2_2 1",
"param_set 0 mix_3_3 1",
"param_set 0 mix_4_4 1",
"param_set 0 mix_5_5 1",
"param_set 0 mix_6_6 1",
"param_set 0 mix_7_7 0",
"param_set 0 mix_8_8 0",
"param_set 0 mix_9_9 0",
"param_set 0 mix_10_10 0",
"param_set 14 gain -20.0000",
"param_set 34 gain -10.0000",
"param_set 34 attL -10.0000",
"param_set 34 delayL 0.6000",
"param_set 34 attR -6.0000",
"param_set 34 delayR 0.6000",
"param_set 54 gain -20.0000",
"param_set 54 delayL 10.0000",
"param_set 54 delayR 10.0000",
"param_set 79 gain -2",
"param_set 79 threshold -2",
"param_set 99 gain -2",
"param_set 99 threshold -2",
"param_set 109 gain -2",
"param_set 109 threshold -2",
"param_set 119 gain -2",
"param_set 119 threshold -2"
]
},
{
"unit" : "mn_shairport-sync",
"type" : "service",
"enabled" : 1,
"jackName": "shairport-sync",
"inPorts" : [],
"outPorts": [
{
"portName" : "out_L",
"targetUnit" : "mn_mod-host",
"targetPort" : 8
},
{
"portName" : "out_R",
"targetUnit" : "mn_mod-host",
"targetPort" : 9
}
],
"config" : [
"general = {",
" name = \"[mn] %h\";",
" interpolation = \"soxr\";",
" output_backend = \"jack\";",
" drift_tolerance_in_seconds = 0.015;",
" ignore_volume_control = \"no\";",
" volume_control_profile = \"dasl_tapered\";",
" interface = \"medianet0\";",
"}",
"jack = {",
" soxr_resample_quality = \"very high\"",
"}",
"sessioncontrol = {",
"// run_this_before_play_begins = \"/usr/local/bin/mn_disconnect zita-n2j.service\";",
"// run_this_after_play_ends = \"/usr/local/bin/mn_connect zita-n2j.service\";",
"}",
"diagnostics = {",
" statistics = \"no\";",
" log_verbosity = 0",
"}"
]
}
Since KODI does not support JACK natively, the signal flow is a bit convoluted:
- The
mn_kodi.service
file will force ALSA output by settingKODI_AE_SINK=ALSA
. - In the KODI system settings under AUDIO, you have to choose the
KODI to JACK 5.1 sink
. - In
/home/medianet/, an
.asoundrc` file provides the matching endpoints:
pcm.jack_kodi {
type jack
playback_ports {
0 effect_0:in1
1 effect_0:in2
2 effect_0:in5
3 effect_0:in6
4 effect_0:in3
5 effect_0:in4
}
capture_ports {
}
hint {
show {
@func refer
name defaults.namehint.basic
}
description "KODI to JACK 5.1 sink"
}
}
ctl.jack_kodi {
type hw
card vc4hdmi0
}