Home > Computer (HW and SW) > BEAGLEBONE BLACK: NAVIGATING THE AUDIO MAZE

BEAGLEBONE BLACK: NAVIGATING THE AUDIO MAZE

It is almost ironic that the quest for a “perfect audio appliance” which led us to the “simpler”, low power (the BBB consumes less than 5W) and light-weight world of embedded Linux, has also led us to the incredibly complex world of Linux audio.

Because of its long evolution, long list of requirements from both sound professionals and consumers, and open development environment, many audio subsystems have flourished, each having their own set of unique features, duplicating features from each other, providing interfaces to as many applications (even legacy applications) as possible and attempting to interfaces to each other.

Here is a depiction of Linux audio (circa 2008) [link]:

linuxaudio2

According to TuxRadar [link],

There’s a problem with the state of Linux audio, and it’s not that it doesn’t always work. The issue is that it’s overcomplicated. This soon becomes evident if you sit down with a piece of paper and try to draw the relationships between the technologies involved with taking audio from a music file to your speakers: the diagram soon turns into a plate of knotted spaghetti. This is a failure because there’s nothing intrinsically more complicated about audio than any other technology. It enters your Linux box at one point and leaves at another.

To and audio application, Linux audio provides several audio subsystems through which audio can be channeled: ALSA, PulseAudio, Jackd, OSS and others. An application can use anyone of these subsystems and in fact they do in order to provide wide appeal and compatibility.

Here is a very simplified and high diagram describing the different audio subsystems and their relationship [link]:

LinuxAudio

And application can literally take any route through the different components in order to arrive at the hardware. An application can use Alsa, routed through Jack and back to Alsa; or it can use PulseAudio to OSS or Alsa all the way. Audio applications such as MPD can use either ALSA, PureAudio or OSS in order to appeal to a wider audience and provide compatibility to new, old and in-between versions and flavors of Linux.

FOCUS ON ALSA

In our quest for bit-perfect audio, A reasonable approach to simplify the audio path is by minimizing the number of components through which the audio stream can possibly travel.

A practical way to do this is to direct the application to use a single audio subsystem. In our case, restricting to ALSA looks like a good option since it is already included in the Debian BBB release. (At least PulseAudio and Jack are are installed when installing MPD)

This site [link] also has a good overview of the different sound subsystems in Linux. A graphical description of ALSA is as follows:

alsa

The key message here is that ALSA looks like a “simple” interface between an audio application and the hardware and that there is a Mixer (Sound Mixing) outside the Kernel that CAN can potentially be bypassed. And indeed it can be bypassed:

A sound developer who wishes to output sound in their application can take any of the following routes with ALSA:

  • Output using ALSA API directly to ALSA’s Kernel API (when sound mixing is disabled)
  • Output using ALSA API to sound mixer, which outputs to ALSA’s Kernel API (when sound mixing is enabled)
  • Output using OSS version 3 API directly to ALSA’s Kernel API
  • Output using a wrapper API which outputs using any of the above 3 methods

A further simplification is to restrict the use of ALSA through the ALSA API (a more modern API) and avoid using the OSS v3 API (Legacy API). This may not be an issue as modern applications do not have a need to use the older OSS v3 interface.

MPD (MUSIC PLAYER DAEMON)

Since Volumio and RuneAudio are using MPD, It is a good idea to select MPD and figure out how it works. MPD simply runs in the background playing music from its playlist. Client programs communicate with MPD to manipulate playback, the playlist, and the database. [link]

Configuring MPD to use ALSA

The mpd configuration file is at /etc/mpd.conf. (The default configuration for mpd -as installed for debian, uses ALSA.)

The MPD documentation [link] indicates that it is capable of outputting to 15 plugins (ALSA, PulseAudio, OSS, etc). The configuration file allow the selection of the output plugin. Here is the example for selecting ALSA as the output of MPD:

To configure an audio output manually, add an audio_output block to mpd.conf:

audio_output {
    type "alsa"
    name "my ALSA device"
    device "hw:0"
}

Further, the configuration allows us to disable and bypass any mixing/resampling of the audio data. I’ve highlighted in red the important parameters.

The following table lists the audio_output options valid for all plugins:

Name Description
type The name of the plugin.
name The name of the audio output. It is visible to the client. Some plugins also use it internally, e.g. as a name registered in the PULSE server.
format Always open the audio output with the specified audio format (samplerate:bits:channels), regardless of the format of the input file. This is optional for most plugins. Any of the three attributes may be an asterisk to specify that this attribute should not be enforced, example: 48000:16:*. *:*:* is equal to not having a format specification.The following values are valid for bits: 8 (signed 8 bit integer samples), 16, 24 (signed 24 bit integer samples padded to 32 bit), 32 (signed 32 bit integer samples), f (32 bit floating point, -1.0 to 1.0). Note: /etc/mpd.conf states: “This setting will change all decoded audio to be converted to the specified format before being passed to the audio outputs.”  Therefore do not use this parameter.
enabled yes|no Specifies whether this audio output is enabled when MPD is started. By default, all audio outputs are enabled.
tags yes|no If set to “no”, then MPD will not send tags to this output. This is only useful for output plugins that can receive tags, for example the httpd output plugin.
always_on yes|no If set to “yes”, then MPD attempts to keep this audio output always open. This may be useful for streaming servers, when you don’t want to disconnect all listeners even when playback is accidentally stopped.
mixer_type hardware|software|none Specifies which mixer should be used for this audio output: the hardware mixer (available for ALSA, OSS and PulseAudio), the software mixer or no mixer (“none”). By default, the hardware mixer is used for devices which support it, and none for the others. Note: etc/mpd.conf states: This example will not allow MPD to touch the mixer at all and will disable all volume controls: mixer_type “disabled”
replay_gain_handler software|mixer|none Specifies how replay gain is applied. The default is “software”, which uses an internal software volume control. “mixer” uses the configured (hardware) mixer control. “none” disables replay gain on this audio output.

Additional ALSA specific  fields can be added according to this table (ALSA uses libasound)

Setting Description
device NAME Sets the device which should be used. This can be any valid ALSA device name. The default value is “default”, which makes libasound choose a device. It is recommended to use a “hw” or “plughw” device, because otherwise, libasound automatically enables “dmix”, which has major disadvantages (fixed sample rate, poor resampler, …).Note: the default device can be set in the ALSA configuration file. The default device can be set to “hw” type and thus bypassing “dmix” the ALSA mixer.
use_mmap yes|no If set to yes, then libasound will try to use memory mapped I/O. Here is an example using this parameter [link]: “yes” # Minor speed improvement, should work with all modern cards
buffer_time US Sets the device’s buffer time in microseconds. Don’t change unless you know what you’re doing. Note: etc/mpd.conf states: MPD Internal Buffering: ****** This setting adjusts the size of internal decoded audio buffering. Changing this may have undesired effects. Don’t change this if you don’t know what you are doing: audio_buffer_size “2048”. ******This setting controls the percentage of the buffer which is filled before beginning to play. Increasing this reduces the chance of audio file skipping at the cost of increased time prior to audio playback: buffer_before_play “10%”
period_time US Sets the device’s period time in microseconds. Don’t change unless you really know what you’re doing.
auto_resample yes|no If set to no, then libasound will not attempt to resample, handing the responsibility over to MPD. It is recommended to let MPD resample (with libsamplerate), because ALSA is quite poor at doing so.
auto_channels yes|no If set to no, then libasound will not attempt to convert between different channel numbers.
auto_format yes|no If set to no, then libasound will not attempt to convert between different sample formats (16 bit, 24 bit, floating point, …).
dsd_usb yes|no If set to yes, then DSD over USB according to the proposed standard by dCS and others is enabled. This wraps DSD samples in fake 24 bit PCM, and is understood by some DSD capable products, but may be harmful to other hardware. Therefore, the default is no and you can enable the option at your own risk.

Thus in the MPD configuration we can:

  1. Specify ALSA as output plugin
  2. Specify that no mixer should be used in audio output plugin
  3. Specify that no volume adjustment be applied
  4. Further disable “dmix” the ALSA mixer (#2 may do the same thing. But you never know…)
  5. Further disable resampling in the output (dmix?)
  6. Optionally enable DSD over USB

Here is a guide on setting up MPD that will illustrate some of the parameters discussed above [link].

Configuring sample rate converter in MPD

Now that we’ve taken cared of the output plugin configuration, we need to ensure that other MPD plugins do not do sample rater conversion and/or mixing.

MPD can use one of the following sample rate converter:

  • Internal (built in into MPD, low overhead, low quality)
  • Sox (provided by libsoxr [link])
  • Secret Rabbit Code (provided by libsamplerate [link])

Both libsoxr and libsamplerate can be compiled into MPD so they may be already available in the MPD package. After installing the package mpd in Debian/BBB further request for libsamplerate and libsoxr fails to locate these libraries, likely indicating that these have already been compiled into mpd (I am using “mpd” and “MPD” interchangeably).

SRC (libsamplerate) provides a small set of converters to allow quality to be traded off against computation cost. The current best converter provides a signal-to-noise ratio of 145dB with -3dB passband extending from DC to 96% of the theoretical best bandwidth for a given pair of input and output sample rates. [link] -The MPD documentation lists 97 db SNR [link]. Not sure how the two figures are related.

The MPD documentation says:

The setting samplerate_converter controls how MPD shall resample music.

If MPD has been compiled with libsamplerate support, this setting specifies the sample rate converter to use. Possible values can be found in the mpd.conf man page or the libsamplerate documentation. By default, this is setting is disabled.

Possible values:

Value Description
internal The internal resampler. Low CPU usage, but very poor quality.
soxr very high Use libsoxr with “Very High Quality” setting.
soxr high” or “soxr Use libsoxr with “High Quality” setting.
soxr medium Use libsoxr with “Medium Quality” setting.
soxr low Use libsoxr with “Low Quality” setting.
soxr quick Use libsoxr with “Quick” setting.
Best Sinc Interpolator” or “0 libsamplerate: Band limited sinc interpolation, best quality, 97dB SNR, 96% BW.
Medium Sinc Interpolator” or “1 libsamplerate: Band limited sinc interpolation, medium quality, 97dB SNR, 90% BW.
Fastest Sinc Interpolator” or “2 libsamplerate: Band limited sinc interpolation, fastest, 97dB SNR, 80% BW.
ZOH Sinc Interpolator” or “3 libsamplerate: Zero order hold interpolator, very fast, very poor quality with audible distortions.
Linear Interpolator” or “4 libsamplerate: Linear interpolator, very fast, poor quality.

It appears that there is no “off” setting, so that MPD will always attempt to resample audio in order to match the capabilities of the downstream components. There doesn’t seem to be a way to remove all the resamplers even by removing libsamplerate and libsoxr because there is always the internal resampler of MPD. In the case of the mpd package for debian, it is likely that libsamplerate and libsoxr have been compiled with mpd, so removal is not possible.

It is also reasonable to assume that if the “samplerate_converter” setting is not used in the config file (the default condition), a default sample rate conversion method would be applied.

We already have a way to disable “dmix” in ALSA which defaults to 48KHz, so there is no need for MPD to resample to 48KHz to meet the requirements of dmix. The only remaining sample rate requirement is the capability of the hardware. It is therefore reasonable to assume that if the hardware is capable to supporting the sample rate of the audio file, then no resampling would be performed.

CONFIGURING ALSA

According to these sites [link]. [link]

There are 3 configurations files that are read every time an ALSA device is opened:

  • Default configuration file (comes with ALSA and should not be changed): /usr/share/alsa/alsa.conf
  • System wide configuration file (optional): /etc/asound.conf
  • User configuration file (optional, residing in the users home directory): $HOME/.asoundrc

There is no need to write a configuration file since the default is always present. However, for our purpose we want to ensure that the data is sent to device without any mixing or resampling by ALSA

Recall from the ALSA-specific settings above:

It is recommended to use a “hw” or “plughw” device, because otherwise, libasound automatically enables “dmix”

Type=hw

Thus we wish to define a “hw” device. For our purpose we create a /etc/asound.conf file with the following entry:

pcm.!default {
	type hw
	card 0
}

The exclamation sign causes the previous definition of pcm.default to be overridden (so we don’t have to worry about the default configuration file /usr/share/alsa/alsa.conf).”card 0″ is the audio device we wish to use. It could be “card 1″ or other. See below to determine what to use. “type hw” is used as recommended.

If sample rate conversion is needed, we can use the “plug” type

Type=plug

Use the following entry in /etc/asound.conf

pcm.!default {
	type plug
        slave.pcm {
              type hw 
	      card 0
        }
}

 

EXPLORING THE AUDIO COMPONENTS IN DEBIAN/BBB (KNOWN TO ALSA)

I used this guide [link] to explore and identify the different ALSA components in the Debian distribution for BBB

The presence of /proc/asound directory indicates that ALSA is present:

procAsound

The file /proc/asound/cards tells you which sound cards are present in the system. Here is BBB without any external devices. It says there is a native sound device.

cards

The file /proc/asound/devices tells you the features of the cards. The native sound device is an audio playback device.

devices

The file /proc/asound/pcm gives us more detail on the “digital audio playback” (PCM). Here we find that the audio device is provided by the HDMI interface.

pcm

The simplest say to identify which cards are installed in the system is to use “aplay -l” [link]. This gives the same info as /proc/asound/cards and /proc/asound/pcm

aplay-l

Further information on the device capabilities can be obtained from the file /proc/asound/card0/pcm0p/sub0/hw_params. It requires that a track is being played. Notice we use “card0″ because the card has been identified as “card0″

hwparams

alsacap

alsacap serves to list the sampling rates, number of channels and sample formats supported by installed sound cards and their ALSA drivers.  This helps to find out which parameters do not require resampling (something to be avoided) and to troubleshoot ALSA problems [link]

However, the tool is only available as source so it needs to be compiled. Here is a sample output from here [link]

$ alsacap
*** Scanning for playback devices ***
Card 0, ID `SB', name `HDA ATI SB'
  Device 0, ID `VT1818S Analog', name `VT1818S Analog', 1 subdevices (1 available)
    2..8 channels, sampling rate 44100..192000 Hz
    Sample formats: S16_LE, S32_LE
      Subdevice 0, name `subdevice #0'
  Device 1, ID `VT1818S Digital', name `VT1818S Digital', 1 subdevices (1 available)
    2 channels, sampling rate 44100..192000 Hz
    Sample formats: S16_LE, S32_LE
      Subdevice 0, name `subdevice #0'

 

ADDING A USB AUDIO DEVICE

Unlike a PC, a reboot is required for the system to detect the presence of a USB audio device.

After connecting a TI PCM270x-based device (USB 1.1)

ibasso

aplay-lUSB

After connecting DIYINHK  XMOS-based device (USB 2.0)

DSC_1310

aplay-lUSB2

After connecting AMANERO board (USB2.0)

DSC02497

aplay-lUSB-amanero

As can be seen, these cards are all automatically recognized by BBB.

ENABLING I2S OUTPUT

diyaudio’s MIERO has developed a driver to enable I2S output in the BBB [link].

It requires that the HDMI output be disabled (since it shares the same lines with Audio I2S). In my installation, the line to disable HDMI is already there as a comment, so I just had to un-comment it and reboot.

With HDMI disabled (and no other device in the USB port) -no sound cards detected:

aplay-lNohdmi

Here are the instructions (copied from the site) to download and install the I2S driver for BBB/Debian. You can just copy each line and paste it in the PuTTY windows (paste by clicking the right mouse button.

download and extract driver
wget http://bbb.ieero.com/botic_driver_v3.tar.gz
tar xvzf botic_driver_v3.tar.gz

install driver (assumes official BBB Debian release)
cp -av botic_driver/firmware /lib/
cp -av botic_driver/modules.bbbdebian/* /lib/modules/

update module dependencies
depmod -a

load driver (Needed after every boot)
echo BB-BONE-BOTIC1 > /sys/devices/bone_capemgr.*/slots

There are 3 configurations for the driver
BB-BONE-BOTIC1 - this is for BBB without cape
- onboard clock (48fs rates only)
BB-BONE-BOTIC2
- onboard clock (48fs rates)
- external clock (44k1fs rates)
- external clock switch on UART1_TXD (pin configurable via DT)
BB-BONE-BOTIC3
- external clock (48fs rates)
- external clock (44k1fs rates)
- external clock switch on UART1_TXD (pin configurable via DT)

aplay -l shows the following

aplay-lBotic

The I2S pins are the following:

I2SPins

TESTING WITH APLAY

Rather than testing with MPD which might invoke its resampler by default, we will use aplay (alsa player) which does not have a resampler of its own. It is a lightweight player for alsa.

I compiled a collection of tracks from 44KHz to 384KHz

aplayAllWav

Type=hw: bypassing the mixer

The entry in /etc/asound.conf is as follows:

pcm.!default {
	type hw
	card 0
}

Using this configuration requires that the source file matches both the bit depth and sample rate supported by the hardware. For example, if using the BOTIC driver (which enables I2S output) which is set as 32bit device, you cannot play 16bit nor 24bit files. The error message indicates that only “S32_LE” (Signed 32 bit Little Endian) is supported:

aplayErr1

Type=plug, enabling the ALSA mixer

The entry in /etc/asound.conf is as follows:

pcm.!default {
	type plug
        slave.pcm {
              type hw 
	      card 0
        }
}

The result is that “dmix”, the alsa mixer is invoked. With a reputation of being bad, hopefully it does fine with bit-stuffing (e.g., converting 16 bit to 32 bit by appending zeros)

Testing the BOTIC driver we obtain the following results:

Playing a 44.1KHz file. We expect conversion to 48KHz because the native I2S in BBB only supports 48KHz and its family of sample rates. And indeed, this is what we find. The first screen shows playing the 44K file; the second screen shows the contents of /proc/asound/card0/pcm0p/sub0/hw_params showing that it is outputting 32-bit 48KHz. Thus we see dmix in action, doing sample rate convertion

aplayBotic44-1

aplayBotic44-2

Playing a 192KHz. We expect no sample rate conversion as this is natively supported by the BBB, but dmix will convert from 24 bit to 32 bit. Indeed, this is what we find. The first screen shows playing the 192K file; the second screen shows the contents of /proc/asound/card0/pcm0p/sub0/hw_params showing that it is outputting 32-bit 192KHz. We hope that dmix is just bit-stuffing…

aplayBotic192-1

aplayBotic192-2

If playing 176KHz, ALSA resamples to 192KHz.

REFERENCES

  • Automating the mpd configuration process for bit-perfect audio: [link]
  • Must read: bit-perfect audio in Linux: [link]
  • An example configuration for MPD and ALSA [link]
About these ads
  1. David Quayle
    2014/03/28 at 08:19

    Excellent thanks, although I do start to wonder if it’s really worth it, everything seems so fragmented and possibly a step backwards, at least at this stage.

    • BlgGear
      2014/03/28 at 17:22

      I share your feelings. At a point it was frustrating looking at the overwhelming amount of information all over the place. But once I was able to have a bit of understanding and then finally capable of enabling and disabling the low level mixer (dmix), it was worth the learning experience.
      I do believe that collectively, this would result in a step forward. At the minimum it is an order of magnitude cheaper than a computer :-)
      People seems to be a bit impatient in getting this to work. I hope it will take a while. It would be more fun :-)

  2. David Quayle
    2014/03/29 at 09:51

    I can wait, by the time I finish the things I’m building for my sister this will be a commercial product :). I resisted the urge to jump in so at this stage I’m just watching and learning. Unfortunately I don’t have the patients & stamina to do the research you do.

  3. 2014/03/29 at 19:08

    Another fantastic post! This syncs up perfectly with I’ve been trying to do with my Beaglebone https://twitter.com/foundatron/status/429279220603166720

  4. 2014/03/29 at 19:25

    Curious. Have you seen or used mpd-configure? https://github.com/ronalde/mpd-configure It’s a great commandline tool for configuring mpd. If nothing else, going through ronalde’s code really helped me understand how and what needs to be configured in alsa and mpd.

    • BlgGear
      2014/03/29 at 20:13

      Thanks for the tip. I haven’t come across this reference. There is so much information out there….
      This is very good info. Especially the articles from “la cocina…”

  5. 2014/04/04 at 00:52

    Thanks to the author for writing this elaborate page and to all for the compliments regarding my articles and scripts.

    I would like to point interested readers at the major improvements I made to the https://github.com/ronalde/mpd-configure/blob/master/mpd-configure script in the last few days.

    I also created a new script https://github.com/ronalde/mpd-configure/blob/master/tests/detect-alsa-output-capabilities.sh which serves as a more or less portable and modern replacement for `caps.c`. Since today, the detection and selection mechanisms of this new script are now incorporated into `mpd-configure`.

    See the accompanying article at http://lacocina.nl/detect-alsa-output-capabilities for some background and sample outputs.

    Kind regards,
    Ronald

    • BlgGear
      2014/04/04 at 03:00

      Certainly deserved.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 206 other followers