Lightweight RTP stereo speaker dual mic satellite setup with AEC

So with 3 Pis 2x Pi3A+ and 1x Pi4B+2gb if you want AEC and playing media is important to you.
Guess you could do the same with a 2xPi0 and 1x Pi3 but for me ‘barge-in’ on media playback is such a common need its sort of essential.
Sattelite RPC audio is extremely light AEC is not and needs a PI3 minimum and you can actually hear a slight improvement on the Pi4 so even with a Pi3 its just on the cusp of things.

Choose your version of RTP be it Roc, Pulseaudio or whatever currently I have become a snapcast fan.
Roc is a good alternative also.

https://roc-project.github.io/roc/docs/about_project/overview.html

But we are going to head to snapcast.

We will go for the latest release so head to.

No surprises here on the server install snapserver and on the 2x satellites install snapclient.
sudo dpkg -i snapclient_0.x.x_armhf.deb and follow with sudo apt-get -f install just to pull in the missing dependencies.
Dunno why its like that must be some reason but works fine.

Scroll down to https://github.com/badaix/snapcast/blob/master/doc/player_setup.md#alsa
As we will set up the server to work with simple ALSA sound.

/etc/asound.conf

pcm.!default {
    type plug
    slave.pcm rate48000Hz
}

pcm.rate48000Hz {
    type rate
    slave {
        pcm writeFile # Direct to the plugin which will write to a file
        format S16_LE
        rate 48000
    }
}

pcm.writeFile {
    type file
    slave.pcm null
    file "/tmp/snapfifo"
    format "raw"
}

You can connect to various devices and setup multiple streams that can become the basis of various different groups playing different streams of multi-channel audio.
But for now that it as snapclient plays to the default sound device and if you aplay on the server then due to the inbuilt avahi you should get audio on your satelites.

You can go on google play and search for snapcast and there is a pretty nifty app for controlling your snapcast server and clients remotely.

Have to say this is why I like snapcast as its really simple.
Currently though you will have a stereo pair of satelites playing the full stream which isn’t very stereo and there unfortunately doesn’t seem to be channel support but probably because you do this by setting the channel volume on the device.
Use alsamixer set 1 to 100% left volume and the other to 100% right volume as snapclient just controls the master volume.
alsactl store to make persistent over reboots.

So thats its for stereo server to twin satellite playback be it Pi0 or Pi3.

So now we are going to do the reverse for the mics.
Install snapserver on the satellites and snapclient on the server.

If your going to use VAD or KWS to trigger mic transmission that is something for later but for now just going to concentrate on a base snapcast set up.
We are just going to pipe the mic into the snapcast fifo with

arecord (format) (device) > /tmp/snapfifo

on the cli snapserver -h will give you a quick overview of all the cli options for running a server.

On our server because we are doing things backwards for the mics we need to run 2 snapclients.
This time we don’t want auto configure via avahi we want to point to the satellite of choice.
So snapclient -h to view the cli options.

snapclient -h, --host, arg server hostname or ip address, -s, --soundcard arg (=default) index or name of the pcm device
snapclient has the similar method a aplay -L so snapclient -l, --list, list PCM devices.

This is where you might be thinking hold on this is going to set up a sink and also 2?! As you do need to run 2 client instances to each server.

What we are going to do is use a alsa snd-aloop device play into one side of the loopback device and the other side will be available as a mic source.

So sudo modprobe snd-aloop

Then aplay -l will not bring back the following as you will just have a single card.

pi@raspberrypi:~ $ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Loopback_1 [Loopback], device 0: Loopback PCM [Loopback PCM]
  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 0: Loopback_1 [Loopback], device 1: Loopback PCM [Loopback PCM]
  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 1: Loopback [Loopback], device 0: Loopback PCM [Loopback PCM]
  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 1: Loopback [Loopback], device 1: Loopback PCM [Loopback PCM]
  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: Loopback_2 [Loopback], device 0: Loopback PCM [Loopback PCM]
  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: Loopback_2 [Loopback], device 1: Loopback PCM [Loopback PCM]
  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 3: Loopback_3 [Loopback], device 0: Loopback PCM [Loopback PCM]
  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 3: Loopback_3 [Loopback], device 1: Loopback PCM [Loopback PCM]
  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 4: Loopback_4 [Loopback], device 0: Loopback PCM [Loopback PCM]
  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 4: Loopback_4 [Loopback], device 1: Loopback PCM [Loopback PCM]
  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

But if you wanted to go crazy with mics everywhere you can load multiple cards by setting options in a file such as /etc/modprobe.d/snd-aloop.conf

options snd-aloop enable=1,1,1,1,1

Will create 5 and so you don’t have to run modprobe each time in /etc/modules just add
snd-aloop and reboot to test.

You can add some pcms to /etc/asound.conf to make things a little more clear.

defaults.namehint.showall on
defaults.namehint.extended on

pcm.!default {
    type plug
    slave.pcm rate48000Hz
}

pcm.rate48000Hz {
    type rate
    slave {
        pcm writeFile # Direct to the plugin which will write to a file
        format S16_LE
        rate 48000
    }
}

pcm.writeFile {
    type file
    slave.pcm null
    file "/tmp/snapfifo"
    format "raw"
}

pcm.mic1 {
 type plug
 slave {
 pcm "hw:0,1,0"
 }
 hint {
		description "Mic1 loopback 0,1,0"
	}
	}

pcm.mic2 {
 type plug
 slave {
 pcm "hw:0,1,1"
 }
 hint {
                description "Mic2 loopback 0,1,1"
        }
        }

Now you should have 2 mic sources on the oppisite side of the loopback “hw:0,0,0” & “hw:0,0,1”

The choice is yours from now as with asla you might want to combine or route into a sum.
But thats the basic setup.
If you choose pi3 then on each satelite you run AEC as with.

And start creating a wide array microphone system with aec.

But you should have a server with sateliite audio that runs as if just local alsa devices.

I haven’t tested running AEC but presume all the clock drift thing comes into play but webrtc might be interesting as then the Pi0 might be an option satelite side and AEC runs on the server.