100200300400500600
  
 
 

JACK

Once upon a time I've made XBMC based HTPC... oh, sorry, you know that story, do you?

Well, next thing I found was that pulseaudio was not working (it is really poorly documented, and that time I didn't have a clue how to deal with it). So I removed pulse leaving XBMC on pure ALSA. Which was working perfectly till it became too limited for me and my audio preferences. This was a time I got acquainted with JACK.

Now I can handle easily and conveniently pulseaudio, after breaking my head through scattered across the internet information, but it is the subject for another story. Because first we need to bind together XBMC and JACK using...

Jack Audio - Alsa Bridge

Yes, that was already discussed many times, and the best and most detailed description I found here. I won't try to best that guy - he explained everything perfectly - so better read that article first to understand what you need to do in the first place. Here I will just capture my specifics.

Let configure alsa loopback device first. It is really straight-forward - just load the kernel module. You can look onto kernel module parameters by executing modinfo <modname> and since for our needs we just need one duplex loop device - adding following line to /etc/modules is absolutely sufficient for us:

snd-aloop id=JABridge pcm_substreams=2
and either rebooting (to find out which device number it will be registered with on normal boot) or just manually load module with command

# modprobe snd-aloop id=JABridge pcm_substreams=2

This way we're creating loopback ALSA device with two PCM substream, which we will organize into duplex ALSA endpoint.

There are several thing we need to consider while working with JACK in general and HTPC in particular, but definitely we need to have realtime, or at least lowlatency, kernel to deal with realtime lowdelay audio processing on Atom 230 with ION graphics. So don't delay it and replace your generic kernel with lowlatency/realtime one.

Now we can install jackd2, but hold on, there appeared another thing to consider. Let first configure alsa for our humble needs. I really am the only user on my HTPC, and the user set for autologin to XDM session for XBMC - so I do not care what is preconfigured in /etc/asound.conf. Really, I just commented out everything there (sed -i 's/^/#/g' /etc/asound.conf) and instead using my local .asoundrc in home, which looks now following way:

pcm.jab {
  type asym
  playback.pcm {
   type plug
   slave.pcm {
    type hw  
    card JABridge
    device 0
    subdevice 0
   }
  }
  capture.pcm {
   type plug  
   slave.pcm {
    type hw  
    card JABridge
    device 0
    subdevice 1
   }
  }
}
pcm.!default "jab"
pcm.analog {
  type hw
  card NVidia
  device 0
}

Simple. Of course I can shorten pcm.jab by packing all the hw nodes description just into one line, but I like it that way - easy to edit if I need to change something. And last analog section is merely for convenience and later use.

Now we're aproaching our first problem. NVidia ION is realy great solution - powerful and power efficient, with direct HDMI audio interface, well maintained binary driver. However audio implementation through HDA codec results into following ALSA device map:

$ aplay -l | grep card
card 0: JABridge [Loopback], device 0: Loopback PCM [Loopback PCM]
card 0: JABridge [Loopback], device 1: Loopback PCM [Loopback PCM]
card 1: NVidia [HDA NVidia], device 0: ALC662 rev1 Analog [ALC662 rev1 Analog]
card 1: NVidia [HDA NVidia], device 3: HDMI 0 [HDMI 0]

This ok until we want to use jackd as dbus session daemon instead of direct jackd/alsa_in|out execution. And we really want to use DBUS session - this is most reliable and convenient (and even more CPU effective) for our auto-launched session of the HTPC. I just was fiddling with this direct execution, which resulted into something like below commented part of my .xsessionrc file:

## Old standalone launch
## We're relying here on following content of the ~/.jackdrc:
## /usr/bin/jackd -R -d alsa -P hw:1,3 -C hw:1,0 -o2 -i2 -s
## which is used to autolauch jack daemon by the client as alsa_in below
#alsa_in -j jasrc -dhw:JABridge,1,0 -c2 &
#jack_wait -wt3
#jack_connect jasrc:capture_1 system:playback_1
#jack_connect jasrc:capture_2 system:playback_2
#jack_load netmanager

But it appeared really unstable, and alsa_in|out are a bit CPU hungry... So I started trying jack_control'ed DBUS daemon instead, which loads fine, and it's audioadapter is better candidate for alsa_in|out replacement. But. Current implementation is really raw - JACK was never designed and concieved as a session daemon, so this implementation sucks. Hence appeared my patched JACK2 vesrion where two problems are fixed:

  • You cannot attach jackdbus through jack_control to another device on a card
  • You cannot attach audioadapter to anything except card itself - no device, no subdevice

First issue preventing us using HDMI interface so we cannot stream sound to the A/V receiver (HDMI is device 3 on audio card) - only device 0 (analog line/headphone out) is aquired. And second is breaking apart alsa bridge (you cannot connect audioadapter to the subdevices) - audioadapter implementation is not using at all capture and playback parameters, only attaching to the raw card and its primary playback/capture devices.

Enhancement ticket 283 submitted on JACK's trac and accepted to the trunk. PPA will be mainteined till mainstream with patches is packaged by debian/ubuntu.

With patched version it's not a problem anymore, so we add following apt source
# echo 'deb http://ppa.launchpad.net/rufferson/snd/ubuntu precise main' > /etc/apt/sources.list.d/snd.list
and install patched jackd2, being ready to finalize session configuration.

Let first configure jackdbus with jack_control:

- Set ALSA as default driver for jack:
$ jack_control ds alsa
- Let be less restrictive for lazy clients - we're not recording studio
$ jack_control dps softmode True
- Set explicitly channel number. 
  HDA codec is surround capable - i.e. 6 channels, but we need stereo
$ jack_control dps inchannels 2
$ jack_control dps outchannels 2
- Now setting our primary (system) audio endpoints
$ jack_control dps device none
$ jack_control dps capture hw:1,0
$ jack_control dps playback hw:1,3
- This is sufficient for the server, now for adapter which will do the bridge
$ jack_control ips audioadapter device hw:0
$ jack_control ips audioadapter capture hw:0,1,0
$ jack_control ips audioadapter playback hw:0,1,1
- Now start everything together
$ jack_control start
$ jack_control iload audioadapter
- and in some other console start alsa_out for analog port
$ alsa_out -j analog -d hw:1,0 -c2
- make necessary port connections: bridge to system out
$ jack_connect audioadapter:capture_1 system:playback_1
$ jack_connect audioadapter:capture_2 system:playback_2
- bridge to analog line out
$ jack_connect audioadapter:capture_1 analog:playback_1
$ jack_connect audioadapter:capture_2 analog:playback_2
- and finally make a snapshot of the connections for later use
$ aj-snapshot .config/jack/default.ajs

DBUS daemon config is stored in .config/jack/conf.xml file, so of you spoiled config somehow just remove the file and it will reset to the default. Or stop jack (jack_control exit) and edit the file to remove malicious line.

Note here important thing - setting driver device parfameter (dps device) to none - this is what one part of patch is about - to ignore device when it is none.

Finally session file will be follwoing:

~/.xsessionrc:
pulseaudio -k
## New dbus launch
jack_control start
jack_control iload audioadapter
jack_control iload netmanager
jack_wait -wt3
alsa_out -j analog -danalog -c2 &
sleep 2
aj-snapshot -r .config/jack/default.ajs

Here we first stopping pulse, just in case someone left it running and it graped our alsa card. Then - starting jackdbus daemon, loading audioadapter, loading netmanager (I want to be able to reroute sound to my laptop sometimes for additional processing - like EQ, compressors, etc...), confirming everything is up and running, then - I also want to be able to use analog line out of HDA card and finally executing aj-snapshot to recover all the connections. That's it.

If you need more than one Jack-Audio-Bridge endpoints - increase number of pcm_substreams for snd-aloop module parameters, add endpoint definition to .asoundrc and instead of using jack_control load adapters directly with jack_load command - it still is not blocking and well behaving:

.asoundrc:
# Jack port 1 - 0 in (from sink), 1 out (to source)
pcm.jack1{ type asym
    capture.pcm { type plug slave.pcm "hw:JABridge,1,0" }
    playback.pcm { type plug slave.pcm "hw:JABridge,1,1" }
}
# Alsa port 2 - 2 sink, 3 source
pcm.jack2 { type asym
    playback.pcm { type plug slave.pcm "hw:JABridge,0,2" }
    capture.pcm { type plug slave.pcm "hw:JABridge,0,3" }
}
pcm.!default "jack1"
.xsessionrc:
...
## New dbus launch
jack_control start
#jack_control iload audioadapter
jack_load jab1 audioadapter -- '-dhw:0 -Chw:0,1,0 -Phw:0,1,1 -i2 -o2'
jack_load jab2 audioadapter -- '-dhw:0 -Chw:0,1,2 -Phw:0,1,3 -i2 -o2'
...

Oh, yes, if you wonder what happens with your jack daemon - go check .log/jack/jackdbus.log in your home.

Why bother? - Or ALSA JACK plugin

If you already read references you understood that alsa-jack plugin is not very convenient due to the nature of alsa - it creates ports only when someone needs to connect to them. This, with static nature of JACK connections renders alsa not very usefull - you need to reconnect the ports each time application plays something.

You can youse aj-snapshot in daemon restore mode - it will reconnect port whenever it becomes available. With XBMC - when process ID is almost static it is an apropriate workaround.

.asoundrc
pcm.jack0 { type jack
    playback_ports {
        0 system:playback_1
        1 system:playback_2
    }
}

With above configuration alsa will automatically connect playback ports to the system out when some app plays to the jack0 pcm.

Now we have two static app independednt ports jack1 and jack2 - which we can treat as a patchbay for dynamic apps and jack0 plugin for static apps (like XBMC or MPD).

With static aloop ports we can either connect jackd to them or even connect them to sound card with alsaloop -C hw:JABridge,1,0 -P analog -c2 if we have some problems with jackd.

But if we need real dynamic sound connection kit - this where pulseaudio comes in hand.

Wait, I mentioned laptop here?

Oh, yes, that part. It's almost the same - dbus, audioadapters, pulseaudio, netadapter (this is how we link laptop to the media center).

On thing however - on laptop I've faced with ugly kernel Oops with jack/netadapter/audioadapter chain while resuming from sleep. As such I've created following file in /etc/pm/sleep.d/

/etc/pm/sleep.d/05-jackctl 
#!/bin/bash

jackctl() {
    for u in `ls -1 /home`; do
	if [ -x /home/$u/.jack_control ]; then
	    su -l $u -c "/home/$u/.jack_control $1"
	fi
    done
}
case $1 in
    sleep|suspend)
	jackctl suspend
	;;
    resume|thaw)
	jackctl resume
	;;
    *)
	echo Wha?
esac

and added following file to my home

.jack_control 
#!/bin/bash
[ "x$_" = "x/bin/sh" ] && exec $0
# vim: ts=4 et:
SESSION=.jack_state
AJSNAP=.aj.snap
stop() {
  if jack_lsp >/dev/null 2>&1 ; then
    jack_control stop
    jack_control exit
    killall -9 jackdbus >/dev/null 2>&1
    killall -9 jackd >/dev/null 2>&1
  fi
  true
}
suspend() {
  if jack_lsp >/dev/null 2>&1 ; then
    aj-snapshot -f $AJSNAP
    jack_unload jack1 && echo jack1 >> $SESSION
    jack_unload jack2 && echo jack2 >> $SESSION
    jack_unload netadapter && echo netadapter >> $SESSION
    pulseaudio -k && echo pulse >> $SESSION
    jack_control ds dummy
    jack_control sm
  fi
  true
}
resume() {
  if jack_lsp >/dev/null 2>&1 ; then
    jack_control ds alsa
    jack_control sm
    [ -f $SESSION ] && \
	[ "`grep -v jack1 $SESSION`" = "`cat $SESSION`" ] || \
	jack_load jack1 audioadapter -- \
		'-dhw:JABridge -Chw:JABridge,1,0 -Phw:JABridge,1,1 -i2 -o2'
    [ -f $SESSION ] && \
	[ "`grep -v jack2 $SESSION`" = "`cat $SESSION`" ] || \
	jack_load jack2 audioadapter -- \
		'-dhw:JABridge -Chw:JABridge,1,2 -Phw:JABridge,1,3 -i2 -o2'
    [ -f $SESSION ] && \
	[ "'`grep -v netadapter $SESSION`'" = "'`cat $SESSION`'" ] || \
	jack_load netadapter
    [ -f $SESSION ] && grep -q pulse $SESSION && \
		pulseaudio --start --log-target=syslog
    rm -f $SESSION
    if [ -f $AJSNAP ]; then
      aj-snapshot -r $AJSNAP
      rm -f $AJSNAP
    else
      aj-snapshot -r .jack_snapshot
    fi
  fi
}
start() {
  jack_lsp >/dev/null 2>&1 || jack_control start
  jack_control ds alsa
  jack_control sm
  rm -f $SESSION
  rm -f $AJSNAP
  resume
}
case $1 in
    sleep|suspend)
	suspend
    ;;
    thaw|resume)
	resume
    ;;
    stop)
	stop
    ;;
    *)
	stop
	start
esac
Fri Jun 15 11:43:12 2012 Upd.: Sat Feb 9 14:47:33 2013
 
 
© ruff 2011