if(($ACT == 'edit' || $ACT == 'preview') && $INFO['editable']){ ?> } else { ?> } ?>
jackdmp control scripts.
Recent versions of Jack2 aka. jackdmp can be controlled via Dbus and most interestingly, jackdmp allows the backend-driver to be replaced dynamically even while jackd is running!
There's two main use-cases that motivated me:
I hardly ever re-boot my laptop and I keep jackd running anytime 1). So far I use Qjackctl's Dbus interface to start/stop jackd during suspend; disadvantage is that it terminates all existing jack sessions; it's not a big deal since most apps can simply re-connect but if jackd can be kept running: why not do so?
I did the switch: jackd here is now jackdmp.. while audio-applications are ignorant about the change and everything there works just like it was before with jack1, suspend-resume and qjackctl is now somewhat broken.
qjackctl (v0.3.6.22) sometimes gets stuck when switching backends; this is a major bummer since I like it's connection patchbay. patchage
(v0.4.4) suffers a similar issue. It does not update the ports correctly.
Anyway those problems can be fixed in future releases (and were in qjackctl 0.3.6.24).
But I hit another little snag: Calling jack_control sm
- which is needed to switch the backend - drops existing connections to system:* i/o ports. OK. The ports may be different in some setups but they're not here: switching between two stereo cards.
Long story short, here are two shell-scripts. The first to switch between different sound-cards retaining connections. and a second to take care of switching to the dummy-driver and back during suspend/resume cycles. They're rather pragmatic hacks, but good enough to get started.
Installation steps:
myjackctl.sh
script to some folder and make it executable (chmod +x myjackctl.sh
).90myjackctl.sh
script to /etc/pm/sleep.d
or a similar folder where it gets executed as hook during suspend/resume90myjackctl.sh
and set the path to myjackctl.sh
~/.myjackctl
file (example below) which allows to override the settings when resuming the system. By default the parameters used before suspend are restored.myjackctl.sh alsa hw:0 1024 48000 3
sudo etc/pm/sleep.d/90jack2ctl.sh suspend
→ qjackctl is disconnected and jackdmp-dummy audiodriver is loaded.sudo etc/pm/sleep.d/90jack2ctl.sh resume
→ previous audio settings are restored and qjackctl reconnected.pm-suspend
(or activate your computer's suspend-button aka close-lid) to test a real suspend/resume cycle.
~/bin/myjackctl.sh
:
#!/bin/sh DSESSIONBUS=`ls -t ~/.dbus/session-bus/* | head -n1` if [ -n "$DSESSIONBUS" -a -f "$DSESSIONBUS" ]; then . $DSESSIONBUS export DBUS_SESSION_BUS_ADDRESS fi function jackctl { dbus-send --session --print-reply --dest=org.jackaudio.service \ /org/jackaudio/Controller \ org.jackaudio.JackControl.$1 } function jackcfg { dbus-send --session --print-reply --dest=org.jackaudio.service \ /org/jackaudio/Controller \ org.jackaudio.Configure.SetParameterValue \ array:string:$1,$2 \ variant:$3:"$4" >/dev/null } function jackget { dbus-send --session --print-reply --dest=org.jackaudio.service \ /org/jackaudio/Controller \ org.jackaudio.Configure.GetParameterValue \ array:string:$1,$2 } function jackports { dbus-send --session --print-reply --dest=org.jackaudio.service \ /org/jackaudio/Controller \ org.jackaudio.JackPatchbay.GetAllPorts \ | awk '/string "(.*)"/{match($0, /string "(.*)"/, a); printf "\"%s\"\n", a[1]; }' } function jackconnections { dbus-send --session --print-reply --dest=org.jackaudio.service \ /org/jackaudio/Controller \ org.jackaudio.JackPatchbay.GetGraph \ uint64:0 \ | awk 'BEGIN{i=0;} /^ array/{i++;} /^ }/{if (i==2) printf "\n";} /string "(.*)"/ {if (i==2) {match($0, /string "(.*)"/, a); printf "string:\"%s\" ", a[1]; } }' } function connect_ports { eval `echo \ dbus-send \ --session \ --print-reply \ --dest=org.jackaudio.service \ /org/jackaudio/Controller \ org.jackaudio.JackPatchbay.ConnectPortsByName \ $@` > /dev/null } function switch2 { CONNECTIONS=$(jackconnections) # TODO: remember device name of old interface jackctl SwitchMaster > /dev/null jack_wait -w -t 5 > /dev/null # TODO: get device name of new interface # TODO restore connections to physical ports: 'system' and similar phyical IDs!!) CONNECTIONS=$(echo "$CONNECTIONS" | grep '"system"') # TODO Map connections e.g. # replace string:"system" string:"playback_1" -> string:"ffado" string:"playback_7" # replace string:"system" string:"playback_2" -> string:"ffado" string:"playback_8" IFS=$'\n' for line in $CONNECTIONS; do #echo "connect ${line}" connect_ports ${line} done } function start { DRIVER=${1-"alsa"} DEVICE=${2-"hw:0"} if [ "$DRIVER" == "dummy" ];then PNUMP=${3-"2"} PNUMC=${4-"2"} PERIOD=0 RATE=0 NPERIODS=0 MIDI="" else PERIOD=${3-"1024"} RATE=${4-"48000"} NPERIODS=${5-"3"} MIDI=${6-"none"} fi jackcfg engine driver string "$DRIVER" test "$DRIVER" == "dummy" -o -z "$DEVICE"|| jackcfg driver device string "$DEVICE" test "$DRIVER" == "dummy" -a -n "$PNUMP" && jackcfg driver playback uint32 "$PNUMP" test "$DRIVER" == "dummy" -a -n "$PNUMC" && jackcfg driver capture uint32 "$PNUMC" test $RATE -eq 0 || jackcfg driver rate uint32 $RATE test $PERIOD -eq 0 || jackcfg driver period uint32 $PERIOD test $NPERIODS -eq 0 -o "$DRIVER" == "dummy" || \ jackcfg driver nperiods uint32 $NPERIODS test "$DRIVER" == "dummy" -o -n "$MIDI" || jackcfg driver midi-driver string "$MIDI" jackctl IsStarted | grep "boolean false" >/dev/null && ( jackcfg engine realtime boolean true jackcfg engine realtime-priority int32 70 jackcfg engine sync boolean true jackctl StartServer > /dev/null ) } if [ "$1" == "config" ];then jackget engine driver | tail -n1 | awk '{printf "SNDDRV=%s\n",$3;}' jackget driver device | tail -n1 | awk '{printf "SNDDEV=%s\n",$3;}' #echo "PNUMC=\"`jack_lsp | grep system:capture | wc -l`\"" echo "PNUMC=\"`jackports | grep system:capture | wc -l`\"" #echo "PNUMP=\"`jack_lsp | grep system:playback | wc -l`\"" echo "PNUMP=\"`jackports | grep system:playback | wc -l`\"" exit fi #dbus-send --system /org/rncbc/qjackctl org.rncbc.qjackctl.stop start $@ switch2 #test "$1" == "dummy" || \ # dbus-send --system /org/rncbc/qjackctl org.rncbc.qjackctl.start
/etc/pm/sleep.d/90myjackctl.sh
:
#!/bin/bash # # this is: /etc/pm/sleep.d/90myjackctl.sh # a system suspend/resume hook script invoked by pm-utils # # on suspend it switches a running jackdmp instance's backend to 'dummy' # in order to make jackd survive the suspend/resume cycle and it restores # previous settings on resume. # see https://rg42.org/wiki/jack2contol for more information # MYJACKCTL=/usr/local/bin/myjackctl.sh CONFIGDIR=/var/cache/pm-utils/ mkdir -p $CONFIGDIR case $1 in hibernate|suspend) for JPID in $(pidof jackdbus);do JUSER=`ps -wp $JPID -o user h` su -l $JUSER -c "$MYJACKCTL config"> $CONFIGDIR/qjackctl_$JUSER test -r $CONFIGDIR/qjackctl_$JUSER && . $CONFIGDIR/qjackctl_$JUSER su -l $JUSER -c "$MYJACKCTL dummy $PNUMP $PNUMC" >/dev/null done ;; thaw|resume) (sleep 6 /etc/init.d/rtirq start >/dev/null; \ for JPID in $(pidof jackdbus);do POSTSCRIPT="" JUSER=`ps -wp $JPID -o user h` SNDDEV="hw:0" SNDDRV="alsa" test -r $CONFIGDIR/qjackctl_$JUSER && . $CONFIGDIR/qjackctl_$JUSER eval FOO_HOME=~$JUSER test -r ${FOO_HOME}/.myjackctl && . ${FOO_HOME}/.myjackctl su -l $JUSER -c "$MYJACKCTL $SNDDRV $SNDDEV" >/dev/null test -n "$POSTSCRIPT" && su -l $JUSER -c "$POSTSCRIPT" done )& ;; *) ;; esac
~/.myjackctl
: optional
# This is a POSIX shell fragment # # it is sourced from /etc/pm/sleep.d/90myjackctl.sh as $HOME/.myjackctl # and allows to override $SNDDRV and $SNDDEV variables which are # used to configure the resumed jackdmp server. # by default the previously running config - saved on suspend - is used # # This script also provides for addidional hooks. # do not use saved setup but rather check which cards are connected: SNDDRV="alsa"; SNDDEV="hw:0"; POSTSCRIPT="" grep UA25 /proc/asound/cards >/dev/null && SNDDEV="hw:1" # change qjackctl's preset accordingly: PRESET="" if [ "${SNDDEV:0:4}" == "hw:1" ]; then PRESET="ua25-1024"; fi if [ "${SNDDEV:0:4}" == "hw:0" ]; then PRESET="int-1024"; fi test -n "${PRESET}" && \ dbus-send --system /org/rncbc/qjackctl \ org.rncbc.qjackctl.preset string:$PRESET
myjackctl.sh does intentionally not use jack_control
. The reason for this is that calling jack_control
from a pm-suspend hook cancels the suspend and the system returns without sleeping.
I have no idea why this happens, but simply using dbus-send works. (addendum: I experience similar behaviour when using which myjackctl.sh
in the pm hook script instead of hardcoding its path.)
The DSESSIONBUS
environment variable at the top of myjackctl.sh
is required to allow the script to be run in a setuid (su
) session.
What's left ToDo?
rtirq
before it calls it; also use rtctl
or similar.