James,
Using liquidsoap, it's possible to output audio from each of your instances of op25 as separate "left" and "right" audio channels from the earphone (speaker) jack as well as a stereo mp3 stream to your private icecast server, or to a feed on BCFY.
Following
@boatbod's advice, you must first configure your rx.py command lines to output audio as PCM using separate UDP audio (ports) steams for input to liquidsoap.
Review
README-rpi3-liquidsoap in the
~/op25 folder for installation and configuration instructions. Additionally, its advisable to run
op25 (rx.py) and liquidsoap as services that will auto start at bootup of the Raspberry Pi.
Shown below are example op25.sh and op25.liq scripts to get you up and running. I'd suggest lowering the sampling rate for both
of your SDR's to something list
-S 1000000 to reduce CPU and USB bus cycles, and in view of the additional load incurred with the
addition of liquidsoap to to the mix.
Keep in mind that the 3.5 MM audio jack on the RPi is a 4-pole jack that carries composite video on one of the poles that will more
than likely introduce buzzing and noise when plugging in a headphone or amplified speakers with a 3-pole plug. You'll need an adapter
if your audio device doesn't already have a 4-pole plug.
(Example op25.sh)
./rx.py --args 'rtl=0' -N 'LNA:35' -S 2400000 -f 771.98125e6 -o 25000 -q 0 -T trunk_nfulton.tsv -2 -V -u 23450 2> stderr.2
./rx.py --args 'rtl=1' -N 'LNA:35' -S 2400000 -f 854.6125e6 -o 25000 -q 0 -T trunk_dekalb.tsv -2 -V -u 23460 2> stderr.2.1
(Example op25.liq)
#!/usr/bin/liquidsoap
# Example liquidsoap streaming from op25 to icecast
# (c) 2019, 2020 gnorbury@bondcar.com, wllmbecks@gmail.com
#
set("log.stdout", true)
set("log.file.path", "/home/pi/op25/op25/gr-op25_repeater/apps/liquidsoap.log")
set("log.file", true)
set("log.level", 3)
# Make the native sample rate compatible with op25
set("frame.audio.samplerate", 8000)
# Consider increasing the buffer value on slow systems such as RPi3. e.g. buffer=0.25
# Longer buffer results in less choppy audio but at the expense of increased latency.
# Left channel input source
#left = input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -u 23450 -x 2 -s")
#left = audio_to_stereo(left)
#left = stereo.pan(pan=1., left)
# Right channel input source
#right = input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -u 23460 -x 2 -s")
#right = audio_to_stereo(right)
#right = stereo.pan(pan=-1., right)
# OPTIONAL AUDIO SIGNAL PROCESSING BLOCKS
# Uncomment to enable
# High pass filter
left = filter.iir.butterworth.high(frequency = 200.0, order = 4, left)
right = filter.iir.butterworth.high(frequency = 200.0, order = 4, right)
# Low pass filter
left = filter.iir.butterworth.low(frequency = 3250.0, order = 4, left)
right = filter.iir.butterworth.low(frequency = 3250.0, order = 4, right)
# Normalization -- Adjust target gains independently to achieve left/right balance --
left = normalize(left, gain_max = 3.0, gain_min = -3.0, target = -16.0, threshold = -40.0)
right = normalize(right, gain_max = 3.0, gain_min = -3.0, target = -16.0, threshold = -40.0)
input = mksafe(add(normalize=false, [left,right]))
# LOCAL AUDIO OUTPUT
# Uncomment the line below to enable local sound
output.ao(input)
# ICECAST STREAMING
# Uncomment to enable output to an icecast server
# Change the "host", "password", and "mount" strings appropriately first!
# For metadata to work properly, the host address given here MUST MATCH the address in op25's meta.json file
#
output.icecast(%mp3(bitrate=16, samplerate=22050, stereo=true), description="op25", genre="Public Safety", url="", fallible=false, icy_metadata="false", host="localhost", port=8000, mount="op25", password="hackme", input)
Good luck,
Bill, WA8WG