joerg-krause writes

I am playing a radio station on a device which is running both, the Songcast Sender and Receiver. MPD is configured to output to a fifo (/tmp/mpdfifo). The Songcast Receiver is configured to output to ALSA:

mpd --no-daemon --stderr --verbose
upmpdcli -c /etc/upmpdcli.conf
{Digital-Multiro} /bin/sh /usr/share/upmpdcli/src_scripts/Digital-Multiroom -f Multiroom
/usr/bin/mpd2sc -f /tmp/mpdfifo -A 44100:16:2:1 -a 0 -u 2e89eaed-8b91-4724-c74a-0023b1e3c315 -n Multiroom -p
/usr/bin/sc2mpd -d -u ohz://239.255.255.250:51972/2e89eaed-8b91-4724-c74a-0023b1e3c315 -c /etc/upmpdcli.conf

Both MPD and mpd2sc audio formats are configured to 44100:16:2.

When using Songcast the quality of the audio stream contains much more static noise compared to playing the stream on the same device using aplay -f cd /tmp/mpdfifo.

Note, that no sample rate converter (NONE) is enabled in sc2mpd.

This is the log:

:4:mpd2src/openaudio.cpp:90::Audioparams: freq 44100 bits 16 chans 2
:4:mpd2src/fiforeader.cpp:53::FifoReader::open: blocking: 0
:4:mpd2src/mpd2sc.cpp:420::sample rate:        44100
:4:mpd2src/mpd2sc.cpp:421::sample size:        2
:4:mpd2src/mpd2sc.cpp:422::channels:           2
:4:mpd2src/mpd2sc.cpp:134::bytes per packet: 1764
:4:mpd2src/mpd2sc.cpp:135::frames per packet: 441
:4:mpd2src/mpd2sc.cpp:136::usec per packet:   10000
  adapter=0x1dfbbf0, iServer=0xb6e19840
:4:mpd2src/mpd2sc.cpp:160::PcmSender::Start: using timers
:3:sc2src/sc2mpd.cpp:558::scmpdcli: using subnet 192.168.178.0
:4:sc2src/alsadirect.cpp:373::src_cvt_type. conf string [NONE]
:4:sc2src/alsadirect.cpp:701::audioEater: alsadirect. Will use converter type -1
:4:sc2src/sc2mpd.cpp:179::=== STARTED ====
:4:sc2src/sc2mpd.cpp:188::=== CONNECTED ====
:4:sc2src/sc2mpd.cpp:376::OhmRcvDrv::Process:trk: TRACK SEQ 2 URI /tmp/mpdfifo METADATA
:4:sc2src/sc2mpd.cpp:383::OhmRcvDrv::Process:meta: METATEXT SEQUENCE 1 METATEXT PcmSender repeated play
LATENCY 50
:4:sc2src/sc2mpd.cpp:193::=== PLAYING ====
:4:sc2src/alsadirect.cpp:785::alsaEater: playing
:4:sc2src/alsadirect.cpp:131::alsawriter: alsa init
:4:sc2src/alsadirect.cpp:188::Alsa: periodsmin 0 periodsmax 2048 bsminsz 8 bsmaxsz 16384 prminsz 8 prmaxsz 2048
:4:sc2src/alsadirect.cpp:198::Alsa: set buffer_time_near: asked 200000 got 200000
:4:sc2src/alsadirect.cpp:207::Alsa: set_period_time_near: asked 50000 got 40000
:4:sc2src/alsadirect.cpp:212::Alsa: bufferframes 8820 periodframes 1764
:4:sc2src/sc2mpd.cpp:219::OhmRcvDrv::Process:audio: samplerate 44100 bitdepth 16 channels 2 samples 441 Halted ? 0

AFAIK, Songcast is a PCM sender and the receiver is passing the audio data as is to ALSA, right? Why mighty the output quality be reduced?

medoc92 writes

I really don’t know what may be happening. The NONE code is not well tested of course (I don’t think that the option is even documented).

Maybe you could have a look at the driver queue size with a change like the following: if it’s not reasonably stable (grows or shrinks), this might be an indication of the direction to look into.

----` diff --git a/sc2src/alsadirect.cpp b/sc2src/alsadirect.cpp index 4757f08..a339f77 100644 --- a/sc2src/alsadirect.cpp + b/sc2src/alsadirect.cpp @@ -727,6 +727,11 @@ static void *audioEater(void *cls) goto done; } } else { + static int64_t cnt; + if ((cnt %100)==0) { + int qs = alsaqueue.qsize() * tsk- >frames() + alsadelay(); + LOGDEB("QS: " << qs << endl); + } convert_to16le(tsk); }

----`

medoc92 writes

Testing on my intel pc, the queue grows very slowly (maybe 500 bytes/minute), so I guess that it’s going to cause problems at some point, but not right now…

medoc92 writes

So in my case the queue grows, until nothing works, because there is currently no code to discard buffers.

joerg-krause writes

I run a test for about an hour on two Receivers:

For both Receivers, the queue size is slowly increasing: about 1000 bytes per hour.

joerg-krause writes

This is a test audio file I recorded by writing the audio buffer to a file right after this line: https://github.com/medoc92/sc2mpd/blob/master/sc2src/sc2mpd.cpp#L348

When importing the file with Audacity (import as raw file) you can clearly hear the noises. Note, that the noise also occurs with SRC_LINEAR.

joerg-krause writes

I also edited the Sender side to output the audio data to a file right after this line: https://github.com/medoc92/sc2mpd/blob/master/mpd2src/mpd2sc.cpp#L261

The noise is already on the Sender! I wonder why?

medoc92 writes

This sounds a little like it was converted to dithered 8bit at some point, I have no idea why.

joerg-krause writes

I will investigate further tomorrow. Maybe you can point me to the function where the mpd fifo is read?

medoc92 writes

it’s in mpd2src/fiforeader.cpp, the function which actually reads is data()

joerg-krause writes

Thanks!

It has something to do with swapping the audio data. The ALSA audio eater is configured with BO_HOST. As my device is Little Endian, swapping is activated in the Receiver here: https://github.com/medoc92/sc2mpd/blob/master/sc2src/sc2mpd.cpp#L340.

That’s why I activated also swapping in the Sender by running mpd2sc with -A 44100:16:2:1. However, this introduces the static noise.

For debugging of the fiforeader, I am writing the audio data before swapping and after swapping to separate files:

    write (g_fd_before, m_tmpbuf, size);
    if (m_needswap)
        swapSamples(m_tmpbuf, bytesPerSample(), m_tmpbufsize / bytesPerSample());
    write (g_fd_after, m_tmpbuf, size);

The audio file before the swapping is fine, whereas the audio file after the swapping contains the noise. So, there is something odd about swapSamples(). The parameters for swapSamples() are bytesPerSamp=2 and scount=882. This results in swab() being called, which is a system function.

Looking at the [implementation](https://git.musl-libc.org/cgit/musl/tree/src/string/swab.c) of swab() in musl shows that for swab(src, dest) the parameters src and dest should not be pointing to the same memory.

There is another question: If the audio file is already fine before the swapping, why do the swapping at all? If I hard-code needswap to false and disable the swapping in the Sender everything works fine.

EDIT: I guess swapping is required as the audio content of an AudioMessage is always PCM big endian encoded, as defined by the Songcast protocol.

medoc92 writes

Jörg Krause writes: > Thanks! > > It has something to do with swapping the audio data. The ALSA audio eater is > configured with BO_HOST. As my device is Little Endian, swapping is activated > in the Receiver here: https://github.com/medoc92/sc2mpd/blob/master/sc2src/ > sc2mpd.cpp#L340. > > That’s why I activated also swapping in the Sender by running mpd2sc with -A > 44100:16:2:1. However, this introduces the static noise. > > For debugging of the fiforeader, I am writing the audio data before swapping > and after swapping to separate files: > > write (g_fd_before, m_tmpbuf, size); > if (m_needswap) > swapSamples(m_tmpbuf, bytesPerSample(), m_tmpbufsize / bytesPerSample()); > write (g_fd_after, m_tmpbuf, size); > > The audio file before the swapping is fine, whereas the audio file after > the swapping contains the noise. So, there is something odd about > swapSamples(). The parameters for swapSamples() are bytesPerSamp=2 and > scount=882. This results in swab() being called, which is a system > function. > > Looking at the implementation of swab() in musl shows that for swab(src, > dest) the parameters src and dest should not be pointing to the same > memory.

That’s the bug, I guess that it ends up zeroing the low byte (the audio shows 8 bit quantization steps). It should be in the man page really !

I pushed a version which should swap in place allright.

> There is another question: If the audio file is already fine before the
> swapping, why do the swapping at all? If I hard-code needswap to false and
> disable the swapping in the Sender everything works fine.

Because Songcast data is always MSB first. Receivers expect it this way, there is no flag to say "this is LSB first".

jf

joerg-krause writes

I can confirm that commit 5f89eacb14b05251c8d83795c642e7dbf108eaa0 fixes the issue.

Many thanks for the fixing this so fast!