Ubuntu Linux Streamer: Enabling High Quality Audio on Linux ?

DAC buffers are tiny and that is not the cause of noise. If there are 10 post men coming at the same time vs 1 post man coming at 1 hr intervals matters. In the former the raspberry pi has to boost up and increase its clocks to avoid a buffer underrun. Everytime a cpu changes states by boosting up, it is extra voltage being applied to the CPU and as a result extra noise on all the bus lines. This can be heard if your system and dac are resolving enough.

This is a measurable phenomenon - noise at output is higher when cpu is under heavy load. I work for a cpu manufacturer and we do these measurements to check if the noise at the output is acceptable or not. Not sure how much of this is done on a raspberry pi but it will be easy to measure.
who do you work for.
 
DAC buffers are tiny and that is not the cause of noise. If there are 10 post men coming at the same time vs 1 post man coming at 1 hr intervals matters.
10 postmen knocking at ALSA's door just cannot happen. The primary ALSA function for writing audio data to a DAC (Digital-to-Analog Converter) is snd_pcm_writei(). The "i" in the name indicates that it is for interleaved access, which is the most common data format for playback. This function will simply block and hold the other postmen at the door till the first postman has completed his delivery. Before calling this function, one must first open and configure the PCM (Pulse Code Modulation) device using a series of other libasound functions, but which is not important here. Additionally underrun is avoided by having preallocated buffer in the ALSA sound system itself. You can find the size by looking at hw_params. e.g. here is my dac having a buffer size of 44100 samples (which is quite generous) and a period size that is 1/4th of the buffer size. So the dac can processs 11025 samples in a single shot and will have to process the buffer 4 times before the buffer becomes empty. But By that time, the buffer would have again got filled with new samples. This buffer is a ring buffer as explained later. The below is for my DSD dac connected to a PI4 that also has the onboard PI2AES card (buggy implementation BTW but highly praised by audiophiles).

PI2AES:(pi) /home/pi >cat /proc/asound/card1/pcm0p/sub0/hw_params
access: RW_INTERLEAVED
format: DSD_U32_BE
subformat: STD
channels: 2
rate: 88200 (88200/1)
period_size: 11025
buffer_size: 44100

Here is a good animation of how this happens


A sound card has a hardware buffer that stores playback samples. When the buffer is sufficiently full, it generates an interrupt. The kernel sound driver then uses direct memory access (DMA) to transfer samples to an application buffer in memory. Similarly, for playback, another application buffer is transferred from memory to the sound card's hardware buffer using DMA.

These hardware buffers are ring buffers, meaning the data wraps back to the start when the end of the buffer is reached. A pointer is maintained to keep track of the current positions in both the hardware buffer and the application buffer. Outside of the kernel, only the application buffer is of interest, so from here on we discuss only the application buffer. A program like gentoo player will not deal with this buffer. Very few programs have the ability to teak this (mpd being one of them where you can tweak the buffer size, period time, etc in /etc/mpd.conf).

Now let us talk about application buffer maintained by ALSA. So not only do we have buffer inside the hardware, we have a buffer maintained by ALSA too and this can be very large. How to find that? How to set that? You just need to set prealloc and look at prealloc_max for the maximum buffer size that you can set.

List of Sound Devices (DACS and Transports)

HiFiBerry:(pi) /proc/asound/card3/pcm0p/sub0 >aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: vc4hdmi [vc4-hdmi], device 0: MAI PCM i2s-hifi-0 [MAI PCM i2s-hifi-0]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: Headphones [bcm2835 Headphones], device 0: bcm2835 Headphones [bcm2835 Headphones]
Subdevices: 8/8
Subdevice #0: subdevice #0
Subdevice #1: subdevice #1
Subdevice #2: subdevice #2
Subdevice #3: subdevice #3
Subdevice #4: subdevice #4
Subdevice #5: subdevice #5
Subdevice #6: subdevice #6
Subdevice #7: subdevice #7
card 2: One [ASUS Xonar Essence One], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 3: sndrpihifiberry [snd_rpi_hifiberry_digi], device 0: HiFiBerry Digi+ Pro HiFi wm8804-spdif-0 [HiFiBerry Digi+ Pro HiFi wm8804
-spdif-0]
Subdevices: 1/1
Subdevice #0: subdevice #0

List of Preallocated ALSA buffers
HiFiBerry:(pi) /proc/asound/card3/pcm0p/sub0 >ls -ld /proc/asound/card*/pcm0p/sub0/prealloc*
-rw-r--r-- 1 root root 0 Oct 4 11:59 /proc/asound/card0/pcm0p/sub0/prealloc
-r--r--r-- 1 root root 0 Oct 4 11:50 /proc/asound/card0/pcm0p/sub0/prealloc_max
-rw-r--r-- 1 root root 0 Oct 4 11:59 /proc/asound/card1/pcm0p/sub0/prealloc
-r--r--r-- 1 root root 0 Oct 4 11:50 /proc/asound/card1/pcm0p/sub0/prealloc_max
-rw-r--r-- 1 root root 0 Oct 4 10:58 /proc/asound/card3/pcm0p/sub0/prealloc
-r--r--r-- 1 root root 0 Oct 4 10:58 /proc/asound/card3/pcm0p/sub0/prealloc_max

Size of ALSA Buffers
HiFiBerry:(pi) /proc/asound/card3/pcm0p/sub0 >cat /proc/asound/card*/pcm0p/sub0/prealloc*
512
18014398509481983
128
128
512
18014398509481983

The size of the buffer can be programmed by ALSA library calls. The buffer can be quite large, and transferring it in one operation could result in unacceptable delays, called latency. To solve this, ALSA splits the buffer up into a series of periods (called fragments in OSS/Free) and transfers the data in units of a period.

A period stores frames, each of which contains the samples captured at one point in time. For a stereo device, the frame would contain samples for two channels. Figure 1 illustrates the breakdown of a buffer into periods, frames and samples with some hypothetical values. Here, left and right channel information is stored alternately within a frame; this is called interleaved mode. A non-interleaved mode, where all the sample data for one channel is stored followed by the data for the next channel, also is supported.

When a sound device is active, data is transferred continuously between the hardware and application buffers. In the case of data capture (recording), if the application does not read the data in the buffer rapidly enough, the circular buffer is overwritten with new data. The resulting data loss is known as overrun. During playback, if the application does not pass data into the buffer quickly enough, it becomes starved for data, resulting in an error called underrun. The ALSA documentation sometimes refers to both of these conditions using the term XRUN. Properly designed applications can minimize XRUN and recover if it occurs. My favourite application mpd simply inserts silence when underrun happens (this is when the application is very slow to fill up the buffers in time. Using puny devices like RPI2/RPI3, underrun is more likely to occurr when you are using the same system for many things. e.g. install one of those distros volumio, etc with tons of php code and a web server and you play DSD. When underrun does occur, the snd_pcm_writei() will return EPIPE. e.g. code
/*
* This example reads standard from input and writes to
* the default PCM device for 5 seconds of data.
*
* Use the newer ALSA API
*/
#define ALSA_PCM_NEW_HW_PARAMS_API #include <alsa/asoundlib.h>
int
main(int argc, char **argv, char **envp)
{
long loops;
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;
char *buffer; /* Open PCM device for playback. */
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
exit(1);
} /* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(&params); /* Fill it in with default values. */
snd_pcm_hw_params_any(handle, params); /* Set the desired hardware parameters. *//* Interleaved mode */
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); /* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); /* Two channels (stereo) */
snd_pcm_hw_params_set_channels(handle, params, 2); /* 44100 bits/second sampling rate (CD quality) */
val = 44100;
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); /* Set period size to 32 frames. */
frames = 32;
snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); /* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
exit(1);
} /* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(params, &frames, &dir);
size = frames * 4; /* 2 bytes/sample, 2 channels */
buffer = (char *) malloc(size); /* We want to loop for 5 seconds */
snd_pcm_hw_params_get_period_time(params, &val, &dir); /* 5 seconds in microseconds divided by * period time */
loops = 5000000 / val;
while (loops > 0) {
loops--;
rc = read(0, buffer, size);
if (rc == 0) {
fprintf(stderr, "end of file on input\n");
break;
} else
if (rc != size) {
fprintf(stderr, "short read: read %d bytes\n", rc);
}
rc = snd_pcm_writei(handle, buffer, frames);
if (rc == -EPIPE) { /* EPIPE means underrun */
fprintf(stderr, "underrun occurred\n");
snd_pcm_prepare(handle);
} else
if (rc < 0) {
fprintf(stderr, "error from writei: %s\n", snd_strerror(rc));
} else
if (rc != (int) frames) {
fprintf(stderr, "short write, write %d frames\n", rc);
}
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer);
return 0;
}
In the previous code, the PCM streams were operating in blocking mode, that is, the calls would not return until the data had been transferred. In an interactive event-driven application, this situation could lock up the application for unacceptably long periods of time. ALSA allows opening a stream in nonblocking mode where the read and write functions return immediately. If data transfers are pending and the calls cannot be processed, ALSA returns an error code of EBUSY.

Many graphical applications use callbacks to handle events. ALSA supports opening a PCM stream in asynchronous mode. This allows registering a callback function to be called when a period of sample data has been transferred.

The snd_pcm_readi and snd_pcm_writei calls used here are similar to the Linux read and write system calls. The letter i indicates that the frames are interleaved; corresponding functions exist for non-interleaved mode. Many devices under Linux also support the mmap system call, which maps them into memory where they can be manipulated with pointers. Finally, ALSA supports opening a PCM channel in mmap mode, which allows efficient zero copy access to sound data.
 
Last edited:
Ubuntu is for timepass linux wannabies.

Such a blasphemous sentiment sir I am offended 😂 Ubuntu and its derivatives/flavours being my primary OS over the years. Just a reminder one of these days I would like to drop by your place and observe first hand your linux-related endeavours maybe on a weekend if you are okay with it 😁 plus general chit-chat to compare notes on our linux history.
 
Such a blasphemous sentiment sir I am offended 😂 Ubuntu and its derivatives/flavours being my primary OS over the years. Just a reminder one of these days I would like to drop by your place and observe first hand your linux-related endeavours maybe on a weekend if you are okay with it 😁 plus general chit-chat to compare notes on our linux history.
You are welcome any day. BTW Ubuntu is the most popular Linux distro
1759672236359.png
 
Ubuntu is the most popular Linux distro

The reason I started using Ubuntu was to check out the "wobbly windows" effect way back when, but then I stuck with it over the years mainly because it's easiest to find solutions online to issues I faced or some configurations or HOWTO guides etc. so yes that validates the "newbie friendly distro for linux wannabes" idea 😅

I do have others installed like Tumbleweed (and have previously tried Arch/Gentoo etc) and most recently KDE-linux (see https://kde.org/linux) but always stuck with Ubuntu based distros in the end for it being mainstream and, well, it "just works" for the most part.

But all this going off topic for this thread :) we can chat whenever I can drop by (thank you btw) hopefully before this year ends.
 
Join WhatsApp Channel to get HiFiMART.com Offers & Deals delivered to your smartphone!
Back
Top