A Squeezebox replacement based on the HiFiBerry DAC and the Raspberry Pi

This is not one of my own projects, but it is so cool that I want to show it here. This HiFiBerry DAC+ user has created a very cool Squeezebox replacement including a LCD display and infrared remote control. If you want to learn more, you can ask him about details in the HiFiBerry forum.

Pulseaudio on the Raspbery Pi

dacplus-rca-raspberry-600x600If you want to have different programs sending their output to the same sound card (like the HiFiBerry DAC+) on the Raspberry Pi you might run into problem. The I2S sound system allows only exclusive access to the sound subsystem. The solution for this and some other issues is PulseAudio. It creates an additional abstraction layer that allows different sound sources to use is simultaneously.

Installing Pulseaudio

unfortunately the Raspbian pulseaudio package does not include some resamplers. I was not able to use the “ffmpeg” resampler with it. Therefore I decided to build my own pulseaudio package. This also allows to use PulseAudio 5 instead of version 2 that it part of the Raspbian distribution.

Note that you need to install a lot of prerequisites before you start to configure and compile pulseaudio:

sudo apt-get install -y libltdl-dev libsamplerate0-dev libsndfile1-dev libglib2.0-dev libasound2-dev libavahi-client-dev libspeexdsp-dev liborc-0.4-dev libbluetooth-dev intltool libtdb-dev libssl-dev libudev-dev libjson0-dev bluez-firmware bluez-utils  libbluetooth-dev bluez-alsa libsbc-dev libcap-dev

This will install not only these packages, but also a lot of dependencies. Note that I did not install the X11 headers as Pulseaudio will run in systems mode.

Now you can get, extract, configure pulseaudio.

wget http://www.freedesktop.org/software/pulseaudio/releases/pulseaudio-5.0.tar.xz
unxz pulseaudio-5.0.tar.xz 
tar xvf pulseaudio-5.0.tar
cd pulseaudio-5.0
./configure

Now check the output of teh configuration process. It will look like this:

    prefix:                        /usr/local
    sysconfdir:                    ${prefix}/etc
    localstatedir:                 ${prefix}/var
    modlibexecdir:                 ${exec_prefix}/lib/pulse-5.0/modules
    System Runtime Path:           /usr/local/var/run/pulse
    System State Path:             /usr/local/var/lib/pulse
    System Config Path:            /usr/local/var/lib/pulse
    Compiler:                      gcc -std=gnu99
    CFLAGS:                        -g -O2 -Wall -W -Wextra -pipe -Wno-long-long -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing -Wwrite-strings -Wno-unused-parameter -ffast-math -fno-common -fdiagnostics-show-option
    CPPFLAGS:                      -DFASTPATH -D_FORTIFY_SOURCE=2
    LIBS:                          -lcap -lrt -ldl -lm 

    Enable X11:                    no
    Enable OSS Output:             yes
    Enable OSS Wrapper:            yes
    Enable EsounD:                 yes
    Enable Alsa:                   yes
    Enable CoreAudio:              no
    Enable Solaris:                no
    Enable WaveOut:                no
    Enable GLib 2.0:               yes
    Enable Gtk+ 3.0:               no
    Enable GConf:                  no
    Enable Avahi:                  yes
    Enable Jack:                   no
    Enable Async DNS:              no
    Enable LIRC:                   no
    Enable Xen PV driver:          no
    Enable D-Bus:                  yes
      Enable BlueZ 4:              yes
      Enable BlueZ 5:              yes
    Enable udev:                   yes
      Enable HAL->udev compat:     yes
    Enable systemd login:          no
    Enable systemd journal:        no
    Enable TCP Wrappers:           no
    Enable libsamplerate:          yes
    Enable IPv6:                   yes
    Enable OpenSSL (for Airtunes): yes
    Enable fftw:                   no
    Enable orc:                    yes
    Enable Adrian echo canceller:  yes
    Enable speex (resampler, AEC): yes
    Enable WebRTC echo canceller:  no
    Enable gcov coverage:          no
    Enable unit tests:             no
    Database
      tdb:                         yes
      gdbm:                        no
      simple database:             no

    System User:                   pulse
    System Group:                  pulse
    Access Group:                  pulse-access
    Enable per-user EsounD socket: yes
    Force preopen:                 no
    Preopened modules:             all

    Legacy Database Entry Support: yes

Make sure you have everything that you need compiled in. The alsa-module is the most important, but you will also need at least libsamplerate.

Now compile and install pulseaudio.

make
sudo make install

It will be installed to /usr/local. If you compile it at the Raspberry Pi, you have some time for a coffee. Crosscompiling on a PC is much faster, but also much more complicated, therefore I would not recommend this.

Module configuration

I would recommend to start with a minimal set of modules:

#!/usr/bin/pulseaudio -nF
load-module module-alsa-card
load-module module-native-protocol-unix auth-anonymous=1
load-module module-always-sink

Now start Pulseaudio:

/usr/local/bin/pulseaudio -vvv --system --disallow-exit --disallow-module-loading=1 --high-priority

Pulseaudio will run in foreground. This has to be changed later, but it makes debugging much easier.

Test

I usually use mplayer with some test .wav and .flac files like this:

pi@raspberrypi ~ $ mplayer ./test-44.wav 
MPlayer svn r34540 (Debian), built with gcc-4.6 (C) 2000-2012 MPlayer Team
mplayer: could not connect to socket
mplayer: No such file or directory
Failed to open LIRC support. You will not be able to use your remote control.

Playing ./test-44.wav.
libavformat version 53.21.1 (external)
Mismatching header version 53.19.0
Audio only file format detected.
Load subtitles in ./
==========================================================================
Opening audio decoder: [pcm] Uncompressed PCM audio decoder
AUDIO: 44100 Hz, 2 ch, s16le, 1411.2 kbit/100.00% (ratio: 176400->176400)
Selected audio codec: [pcm] afm: pcm (Uncompressed PCM)
==========================================================================
AO: [pulse] 44100Hz 2ch s16le (2 bytes per sample)
Video: no video
Starting playback...
A:  35.0 (34.9) of 60.0 (01:00.0)  3.9%

Have a look at the output. If Pulseaudio is working correctly, you will see the AO: [pulse] as output device.

Resampling

However there are some issues, the major one is resampling. To make sure that different sound sources can be mixed all have to be synchronized to a common sample rate. This is not a big problem if you use only 44.1 and 48kHz, but if you also want to playback high-resolution music you will run into trouble with the Raspberry Pi. What is the reason for this? The biggest issue is that the Raspberry Pi CPU is not very powerful and resampling is a relatively complex procedure.
If you read about this issue on several pages on the Internet, many people suggest to use the “trivial” resampling method. It does not need a lot of calculations and therefore works on the Raspberry Pi without a lot of problems. However how trivial is trivial? Have a look at this sine wave:

trivial-resampling

My audio analyzer shows almost 3% distortions! This is not a good method to use if you’re interested in high-quality audio.

Ok, let’s look at other resamplers. The “src-sinc-…” resamplers are too complex. Even the “src-sinc-fastest” is too complex for 192kHz material.

The “src-zero-order-hold” resampler performs as bad as “trivial”.

The two resamplers that perform quite well are “src-linear” and “ffmpeg”. While distortions are a bit less with “src-linear”, “ffmpeg” used less CPU. As distortions are still quite low with “ffmpeg” I recommend this for resampling.

Upsampling or downsampling?

Ok, we found a resampler that works. But what should be the target sample rate? Down to 44.1 or 48kHz or up to 192kHz? Some people will argue that frequencies above 20kHz are not audible and therefore 44.1kHz or 48kHz are enough. While I don’t want to discuss pros and cons of high-resolution audio format here, there are some other details to think about.

With a sound card like the HiFiBerry DAC+, the clocks are generated from the Raspberry Pi. This works quite well, but different sample rates provide better clocks to the DAC than others. The DAC shows the best performance at higher sample rates. Therefore the distortions will be lower with upsampling to 192kHz than with downsampling to 44.1kHz. There is also another effect: Playback of lower sample rates does not need a lot of processing. There is more processing possible than with 192kHz material. Playing back 192kHz material with its native sample rate does not need resampling.  This balances processing power between the playback software and Pulseaudio.

Configuration

My daemon.conf configuration file now looks like this:

resample-method =  ffmpeg
enable-remixing = no
enable-lfe-remixing = no
default-sample-format = s32le
default-sample-rate = 192000
alternate-sample-rate = 176000
default-sample-channels = 2

All other settings use the defaults.

Conclusion

Setting up Pulseaudio on the Raspberry Pi with support for high-resolution sound formats is not trivial. However by using the right sample rates and the right resampler, it will perform well even on the Raspberry Pi. With the configuration shown here Pulseaudio uses about 50% CPU when playing back 44.1, 48kHz and 96kHz material and 20% during playback of 192kHz streams.

A new speaker for the living room

Today I had some time again to work on the new speakers for the living room. Great sound is a must, but it also has to be accepted by everyone in the household. Therefore I can’t use speakers like the 10-34. I wasn’t sure about the color, so I build 2 versions: a green and a white one. I really like the green, but unfortunately it doesn’t really fit with the other furniture in the living room. Looks like I will go on with the white one. The surface is synthetic leather. This is a good material for all people who have problems to get a good finish with painting. It is relatively easy to use.

The speaker itself is a 3-way system. It will be powered by an HiFiBerry Amp.
grenn_white

Reading raw data from a Rigol oscilloscope

class-d-testModern oscilloscopes can be used for much more than  just displaying a waveform. With the some programming, you can use it as a logic analyzer, for jitter measurements or run use it as a spectrum analyzer. In a later article I will show how to do jitter measurements with a digital storage oscilloscope (DSO). You can argue that there are instruments that are more specialized for these use cases than an oscilloscope. You’re right. However if you don’t need the all the features and the superior performance of these instruments (like a logic analyzer or a spectrum analyzer), a DSO might be a cheap alternative. For some specific use cases it might be even superior.

Even relatively cheap scopes like a Rigol DS2072 can already sample data with rates of 2GSamples/s which means one sample every 0.5 nanoseconds. The resolution is usually limited to 8 bit, but the first scopes are already available with 12bit resolution. For many use cases, even 8 bit are more than enough.

The basic approach is to let the scope sample into its internal memory with the sample rate you need, then load the data to a PC and do all the post-processing on the PC. My Rigol scope has an internal memory of 56MSamples – that’s a lot.

The example code at the end of this picture shows how to do this with a Rigol DS2xx2 scope. The standard version of this scope has a memory of only 14MSamples. You can upgrade the memory with a license key. However, for most tasks even the 14MSamples are enough.

There are some things you have to do to get the data from the scope and use the full 56MSamples memory:

  • Timing is important
    I had quite some problems getting the program to work. In most cases I was sending new command to fast. If the scope still processes the last command, it ignores new commands.
    In worst case it is not even responding anymore.
    As a rule of thumb I use 100ms delay after every command and one second after some commands like “AUTO”, “RUN” or “STOP”. This has been found by trial-and-error.
  • Activate only one channel if you don’t need the second
    If you use both channels, the sample memory per channel is only the half.
  • Stop the recording
    With live display only 1400 samples are available.

One characteristic I don’t like with this specific Rigol oscilloscope: data transfers over the network are quite slow. I was able to transfer 110kSamples/s over TCP/IP. That means reading out the full memory of the scope takes over 8 minutes! That’s not really fast. If you don’t need all the samples, reduce the number of samples with the “:WAVeform:POINts” command.
Now some example code how to get the data. You will see, that some parts are missing. It should just give you an idea how to do it by yourself. I have programmed my own measurement toolkit. However the code still needs some cleanup, therefore I won’t publish it now. I plan to publish it later. Note that this is not a high-priority task, therefore I can’t say when it will be ready.

def readdata(instrument):
    global samples
    
    line=str(instrument.connection.read(12))
    
    if not line.startswith("#9"):
        raise Exception("unknown data format: {}.".format(line))

    samplecount=int(line[2:11])
    data=instrument.connection.read(samplecount)
    for d in data:
        samples.append(ord(d))
        
    lf=instrument.connection.readline()
    if (len(lf))>2:
        raise Exception("additional data after samples: {}".format(lf))
  
    
def read_from_scope():
    global samples
    global periods
    samples=[]
    
    ins=get_instrument("DS2202")
    print ins.identify()
    
    
    ins.send_command(":AUT")
    time.sleep(2)
    ins.send_command(":CHAN2:DISP 0")
    ins.mem_depth(points)
    ins.set_aquisition_type("HRES")
    time.sleep(2)
    ins.send_command(":CHANNEL1:BWLimit OFF")
    ins.send_command(":CHANNEL1:COUPLING DC")
    ins.send_command(":CHANNEL1:DISPLAY ON")
    
    ins.send_command(":RUN")
    time.sleep(1)
    ins.send_command(":ACQuire:MDEP {}".format(points))
    ins.send_command(":STOP")
    time.sleep(1)
    ins.send_command(":WAVeform:MODE RAW")
    ins.send_command(":WAVeform:POINts {}".format(points))
    ins.send_command(":WAVeform:FORMAT BYTE")
    ins.send_command(":WAVeform:SOURCE CHAN1")
    
    ins.send_command(":WAVeform:RESet")
    time.sleep(1)
    ins.send_command(":WAVeform:BEGIN")
    time.sleep(1)
    starttime=datetime.datetime.now()
    while True:
        stat=ins.send_command(":WAVeform:STATUS?").lower()
        if stat.startswith("idle"):
            ins.send_command(":WAVeform:DATA?",auto_read=False)
            readdata(ins)
            break
        elif stat.startswith("read,"):
            count=int(stat[5:])
            if count==0:
                break
            ins.send_command(":WAVeform:DATA?",auto_read=False)
            readdata(ins)
        else:
            print "unexpected return string"
            sys.exit(1)
    endtime=datetime.datetime.now()
    sec=(endtime-starttime).total_seconds()
    slen=len(samples)
    print("Retrieved {} samples in {} seconds ({}kSamples/s)".format(slen,sec,slen/sec/1000))

Remote controlling instruments using LXI

440px-LXI_Logo.svgA lot of modern lab equipment like power supplies, oscilloscopes, signal generators and many more, have an onboard Ethernet interface and an “LXI” logo. LXI stands for “LAN eXtensions for instruments” and replaces the old GPIB IEEE-488 parallel control bus. You can use this interface to control your instruments remotely from a PC. With LXI Class-A and Class-B devices you can even exchange trigger data over the network. But let’s stick today with the simplest version: Class-C devices. These interfaces provide only remote control features.  The good thing about Class-C: you will find this type even in hobbyist instruments. For example, even the mid-range Rigol instruments today have an LXI interface.

Often the manufacturers provide remote control software (usually only for Windows PCs), sometimes plugins for LabView. The quality of this software is sometimes ok, but often not very good.

However as LXI is an open standard, you don’t need the software of the device manufacturer. You can simply create your own. And if you’re usually working on the command line on Linux or MacOS, it is really simple to use it.

The LXI command protocol is a simple text protocol over a TCP connection. That means you can even use the simple “telnet” command to control your instruments remotely.

Let’s do an example with the DSA815. First, download the Programming Guide from Rigol. Unfortunately this one comes in CHM format which is used in Windows. We need an additional program to read it on other operating systems. On MacOS iChm does the job.

Now, connect to the instrument using the telnet command line tool. The port on Rigol instruments is 5555, which is easy to remember. With other vendors the port might be different, check your manuals.

$ telnet 192.168.1.126 5555
Trying 192.168.1.126...
Connected to 192.168.1.126.
Escape character is '^]'.
*IDN?
Rigol Technologies,DSA815,DSA8A154402661,00.01.07.00.01

Seems to work. The “*IDN?” command is a standard command that is implemented by all LXI instruments and returns the ID of the instrument.

Do you want more? Ok,  lets do a frequency response plot from 100 kHz to 10 MHz using the internal tracking generator.

# enable tracking generator output
:OUTP 1
# average 1000 measurements
:TRACE:AVER:COUN 1000
# frequency range from 0 to 10 MHz
:SENSe:FREQ:START 10000
:SENSe:FREQ:STOP 10000000

You can do similar things with oscilloscopes, multimeters and function generators. This allows very powerful automated measurements as you can control different instruments from a central console. Note that while the protocol is standardized, the commands are not. Therefore you have to check the manual from your vendor.

You want to do even cooler things? Have a look at this article from Ken Shirriff. I will also show how to to jitter measurements down to 1ns with a mid-range Rigol scope.

HP 8903B repair

I really like my HP8903B audio analyzer. While PC based systems can outperform it in some areas, I like the robustness and simplicity of this standalone device. However it failed with “Error 14″. The analyzer is almost 20 years old, this can happen.

The modular design makes debugging relatively easy. After removing the analog front-end card, the analyzer worked again. Ok, there is a problem with this card. Looking at the card there was no obvious problem. Further debugging showed the one rail of the analog power supply was out-of-spec 13V instead of 15V. Looking at this on the oscilloscope showed a huge amount of ripple. At this point it was obvious that at least a capacitor in the power supply had a problem. There are 3 huge capacitors in the power supply, 2 for the analog +/-15V and another one for the digital 12V and 5V. Checking these capacitors showed that one of them was defective. Have a look at these huge parts. I replaced them with more modern caps (the black ones in the picture):

capacitors

The replacement was done in a few minutes, as these are screw-in capacitors. There is no need to solder anything.

hp8903b-caps

Looking at the picture now I think, I should also clean the device ;-)

Was this the root cause of “Error 14″? Let’s power it on again:

hp8903-raspberry

Yes, it is working again – great. If you ever get this error with your HP8903, check the power supply first.

QuadVol PCB design files

quadvol-acryl-squareSome users asked why the QuadVol board is not on stock anymore. Unfortunately I don’t have much time to support users of this board, therefore I won’t produce it anymore. However, I release the PCB design files for this project under the Creative Commons Attribution-ShareAlike 4.0 International License. You can download them here: quadvol.zip

Creative Commons License
QuadVol by Daniel Matuschek is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Based on a work at http://www.crazy-audio.com/projects/quad-volume-control/.