Setting up Campcaster Station on a server without a sound card

This English translation is by Peter Whitehead from the French original by Philippe Drouot at Openradio.info. The article can be found at this address:

http://www.openradio.info/spip/spip.php?article2

1. Problem

The objective is to use Campcaster Station for programming a web radio station – not only from the user's station but also from a server housed in a data center, so that programming from different geographical locations can be collectively broadcast. Streaming sound from the Campcaster scheduler without using an audio card?

Most hosted servers don’t integrate an audio card. A sound card isn’t expensive but it is usually impossible to add one to your server (no access to the machine and/or no PCI slots).

The Campcaster solution requires the ALSA system to function. We don’t want to hear the sound directly on the server, but instead want to send it to a streaming server using Darkice as an intermediary. The idea is therefore to send the sound from the Campcaster scheduler directly to Darkice without passing through the sound card. ALSA offers the imitation/artificial audio peripheral snd-dummy but it redirects the sound to /dev/null which doesn’t get us very far.

Proposed solution: Interconnect Campcaster and Darkice via the audio server Jackit

The audio server Jackit offers a pefectly functional imitation/artificial dummy interface, which allows you to connect audio applications between themselves without passing through a sound card.

2. Installing JackIt, Darkice and flux routing ALSA to Jack

Requirements :

Use a datacenter server installed under Debian or Ubuntu and install the campcaster-libs and campcaster-station packages (see the article Launching a webradio for installation). Use an IceCast 2 server that is installed and operational (on the same server or a different one).

2.1. Installing the audio server JackIt

sudo apt-get install jackd libjack0.100.0-0

Please note that if you encounter difficulties when interconnecting the Campcaster scheduler and the Darkice streamer, it might be a good idea to install the Jackit server-control graphic interface qjackctl.

2.2. Installing Darkice with Jack support (and mp3 format)

To deal with the non-free format mp3 and Jack support, you must compile darkice yourself.

To deal with the mp3 format: add the multiverse repository for Ubuntu or the debian-multimedia.org repository for Debian (deb http://www.debian-multimedia.org sid main).

sudo apt-get install liblame0 liblame-dev libvorbis-dev libasound2-dev

We have also added the packages: libvorbis-dev and libasound2-dev to support ogg and alsa.

Development library for Jack support:

sudo apt-get install libjack0.100.0-dev

Downloading darkice sources:

wget http://ovh.dl.sourceforge.net/sourceforge/darkice/darkice-0.17.1.tar.gz
tar xvf darkice-0.17.1.tar.gz 
cd darkice-0.17.1

Compiling Darkice: If you have never compiled on your server, start with:

sudo apt-get build-essential

Before compiling Darkice, we are going to modify a line of source code! The name of the Jack output is the form darkice-pid (pid being the process number); which isn’t very practical because it means we have to change the name of the jack port each time we launch or the alsa flux will be rerouted.

We modify the file src/JackDspSource.cpp and replace line 189:

snprintf(client_name, 255, "darkice-%d", getpid());

with:

snprintf(client_name, 255, "darkice");

We can now compile and install Darkice:

./configure --with-lame --with-vorbis --with-alsa --with-jack --prefix=/usr/local/darkice 
make
make install

Darkice is now installed in /usr/local/darkice and deals with the ogg (—with-vorbis), the mp3 (—with-lame) and supports ALSA and Jackit.

2.3. Routing the ALSA input flux of the jackit audio-server

Installation of additional ALSA plug-ins before disposing of the module libasound_module_pcm_jack.so :

sudo apt-get install libasound2-plugins

Creating the virtual peripheral ALSA jackplug is done by creating the file /etc/asound.conf as below:

pcm.jackplug {
    type plug
    slave { pcm "jack" }
}
pcm.jack {
    type jack
    playback_ports {
        0 darkice:left
        1 darkice:right
   }
}

3. Configuration of Campcaster Scheduler and DarkIce

Configuring Campcaster Scheduler The configuration of the Campcaster scheduler is done by editing the file /opt/campcaster/etc/campcaster-scheduler.xml and by providing information to the audio peripheral by:

    <audioPlayer>
        <gstreamerPlayer audioDevice = "jackplug" />
    </audioPlayer>

We are clarifying here that the peripheral is not an audio card type hw:0,0 but the ALSA jackplug virtual peripheral created earlier. Don’t forget to relaunch the Campcaster scheduler by the command /etc/init.d/campcaster-scheduler restart

Configuring Darkice Start by copying the configuration file example in the Campcaster configuration directory.

cp /usr/local/darkice/etc/darkice.cfg /opt/campcaster/etc/

Then modify the configuration file /opt/campcaster/etc/darkice.cfg as required.

* In the section [general], you must replace duration = 60 by duration = 0 if not you’ll go mad (darkice stops automatically after 60 seconds in the default configuration).

* In the section [input], instruct the peripheral used by replacing /dev/dsp with jack. In this same section, set the sampling frequency: typically 44100 Hz for CD quality. The frequency should be identical to that used in the launch parameters of jackd (see Launching section).

* Delete all the following sections except the section[icecast2-0]

* In the section [icecast2-0], input the information allowing the sending of the flux to the IceCast 2 server: server, port, password and mountPoint. Make sure the information is in the format (format = mp3 or format = vorbis) and the (bitrate).

4. Launching JackIt, DarkIce and Campcaster Scheduler

4.1 Test: To test that everything works, you can launch two different consoles as a superuser:

sudo jackstart -vv -R -d dummy -r 44100 -C 0 -P 0

and

/usr/local/darkice/bin/darkice -c /opt/campcaster/etc/darkice.cfg

And check the functioning of the station by playing the programming in a navigator with a url of the type http://domain.tld/campcaster/ and listening to your IceCast flux.

4.2 Automate the launch of the audio server Jackit and the Darkice streamer:

We have created launch scripts /etc/init.d/jackd and /etc/init.d/darkice as follows:

In /etc/init.d/jackd :

#! /bin/sh
set -e
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="Jackit audio server"
NAME=jackd
DAEMON=/usr/bin/$NAME
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0
#
#       Function that starts the daemon/service.
#
d_start() {
        start-stop-daemon --start --quiet --background --make-pidfile \
                --pidfile $PIDFILE --exec $DAEMON -- -vv -R -d dummy -r 44100 -C 0 -P 0 &> /dev/null \
                || echo -n " already running"
}
#
#       Function that stops the daemon/service.
#
d_stop() {
        kill `cat $PIDFILE` \
                || echo -n " not running"
}
#
#       Function that sends a SIGHUP to the daemon/service.
#
d_reload() {
        start-stop-daemon --stop --quiet --pidfile $PIDFILE \
                --name $NAME --signal 1
}
case "$1" in
  start)
        echo -n "Starting $DESC: $NAME"
        d_start
        echo "."
        ;;
  stop)
        echo -n "Stopping $DESC: $NAME"
        d_stop
        echo "."
        ;;
  #reload)
        #
        #       If the daemon can reload its configuration without
        #       restarting (for example, when it is sent a SIGHUP),
        #       then implement that here.
        #
        #       If the daemon responds to changes in its config file
        #       directly anyway, make this an "exit 0".
        #
        # echo -n "Reloading $DESC configuration..."
        # d_reload
        # echo "done."
  #;;
  restart|force-reload)
        #
        #       If the "reload" option is implemented, move the "force-reload"
        #       option to the "reload" entry above. If not, "force-reload" is
        #       just the same as "restart".
        #
        echo -n "Restarting $DESC: $NAME"
        d_stop
        # One second might not be time enough for a daemon to stop,
        # if this happens, d_start will fail (and dpkg will break if
        # the package is being upgraded). Change the timeout if needed
        # be, or change d_stop to have start-stop-daemon use --retry.
        # Notice that using --retry slows down the shutdown process somewhat.
        sleep 1
        d_start
        echo "."
        ;;
  *)
        echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
        exit 3
        ;;
esac
exit 0

In /etc/init.d/darkice :

#! /bin/sh
set -e
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="Darkice"
NAME=darkice
DAEMON=/usr/local/darkice/bin/$NAME
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0
#
#       Function that starts the daemon/service.
#
d_start() {
        start-stop-daemon --start --quiet --background --make-pidfile \
                --pidfile $PIDFILE --exec $DAEMON -- -c /opt/campcaster/etc/darkice.cfg -v 0 &> /dev/null \
                || echo -n "already running"
}
#
#       Function that stops the daemon/service.
#
d_stop() {
        start-stop-daemon --stop --quiet --pidfile $PIDFILE \
                --name $NAME \
                || echo -n " not running"
}
#
#       Function that sends a SIGHUP to the daemon/service.
#
d_reload() {
        start-stop-daemon --stop --quiet --pidfile $PIDFILE \
                --name $NAME --signal 1
}
case "$1" in
  start)
        echo -n "Starting $DESC: $NAME"
        d_start
        echo "."
        ;;
  stop)
        echo -n "Stopping $DESC: $NAME"
        d_stop
        echo "."
        ;;
  #reload)
        #
        #       If the daemon can reload its configuration without
        #       restarting (for example, when it is sent a SIGHUP),
        #       then implement that here.
        #
        #       If the daemon responds to changes in its config file
        #       directly anyway, make this an "exit 0".
        #
        # echo -n "Reloading $DESC configuration..."
        # d_reload
        # echo "done."
  #;;
  restart|force-reload)
        #
        #       If the "reload" option is implemented, move the "force-reload"
        #       option to the "reload" entry above. If not, "force-reload" is
        #       just the same as "restart".
        #
        echo -n "Restarting $DESC: $NAME"
        d_stop
        # One second might not be time enough for a daemon to stop,
        # if this happens, d_start will fail (and dpkg will break if
        # the package is being upgraded). Change the timeout if needed
        # be, or change d_stop to have start-stop-daemon use --retry.
        # Notice that using --retry slows down the shutdown process somewhat.
        sleep 1
        d_start
        echo "."
        ;;
  *)
        echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
        exit 3
        ;;
esac
exit 0

To check that it works, you only need to make the two scripts executable (chmod +x) then launch them:

/etc/init.d/jackd start
/etc/init.d/darkice start

Beware, the audio server JackIt must always be launched with the Darkice streamer. To make sure that the daemons are automatically launched when starting the machine, you only need to do:

update-rc.d jackd defaults 85
update-rc.d darkice defaults 86

The priorities 85 and 86 are purely arbitrary. You must observe the fact that these figures are available, that they are lower than the launch priority of Campcaster scheduler, and that jackd precedes darkice! Add ls /etc/rc2.d/ to the preliminary commands update-rc.d

5. Conclusion

By redirecting the ALSA flux from Campcaster Scheduler into the input of the Jackit audio-server and by connecting with the Darkice streamer at the output, we are able to work on the programming of a webradio from a server that doesn’t have a sound card. There are probably other solutions. Don’t hesitate to have your say in the campcaster-dev forum or mailing list.