Discussion:
[SoX-users] implementing loudspeaker crossovers using Sox + LADSPA plugins
Charlie Laub
2015-06-24 20:12:35 UTC
Permalink
I have developed a couple of useful LADSPA plugins that are intended to
implement a variety IIR filters and delay so that the user can implement a
loudspeaker crossover on their PC. I have gotten this to work using ecasound
under Linux, however, since Sox can run under a broader range of O/Ss and
can also run LADSPA plugins I am wondering if the same thing can be done
with Sox.



A detailed web page about how to use ecasound and LADSPA plugins to create a
loudspeaker crossover is presented here:

http://rtaylor.sites.tru.ca/2013/06/25/digital-crossovereq-with-open-source-
software-howto/



One of the keys to making this work has to do with routing. Each channel of
the input (usually 2 channel audio) is duplicated into "branches", one
branch per driver of the loudspeaker system. Then a separate LADSPA filter
chain is run on each branch. Finally the branches are recombined back into
one multichannel audio stream (one channel per loudspeaker driver) and sent
to the output device of choice. In my version of this, there is also one
multichannel LADSPA plugin that the re-combined audio stream is run through
that will implement fractional delay on each channel.



I read through the Sox manual, but it's not clear to me how to do the
routing (splitting and recombining of channels) and processing of each
channel with a its own chain of LADSPA plugins. Can someone give me a little
hand holding towards this goal with some examples?



Also, with ecasound, a chain of several LADSPA plugins can be specified in a
text file. The text file can then be called instead of having to list all of
the LADSPA plugins explicitly, and this seems to tidy up the command line.
Can this sort of thing be done in Sox as well?



If any of this is not clear, please let me know and I will provide more
detail or concrete examples. The link above is very good at presenting how
the audio stream should be processed, and is better than me trying to ramble
on and on.



Thanks for any help while I give Sox a try. I think that Sox would be a very
popular tool for this kind of thing, at least in the DIY loudspeaker
community.



-Charlie
Charlie Laub
2015-06-26 03:46:10 UTC
Permalink
I still need help with this problem. How can I take a 2 channel file, split
it into more channels, run several different LADSPA plugins on EACH channel
separately, then output all channels together?



I did not find the necessary functionality in the Sox documentation as far
as I can tell. If anyone knows if this is possible, PLEASE post about it.
Thanks.



-Charlie





From: Charlie Laub [mailto:***@sbcglobal.net]
Sent: Wednesday, June 24, 2015 1:13 PM
To: sox-***@lists.sourceforge.net
Subject: [SoX-users] implementing loudspeaker crossovers using Sox + LADSPA
plugins



I have developed a couple of useful LADSPA plugins that are intended to
implement a variety IIR filters and delay so that the user can implement a
loudspeaker crossover on their PC. I have gotten this to work using ecasound
under Linux, however, since Sox can run under a broader range of O/Ss and
can also run LADSPA plugins I am wondering if the same thing can be done
with Sox.



A detailed web page about how to use ecasound and LADSPA plugins to create a
loudspeaker crossover is presented here:

http://rtaylor.sites.tru.ca/2013/06/25/digital-crossovereq-with-open-source-
software-howto/



One of the keys to making this work has to do with routing. Each channel of
the input (usually 2 channel audio) is duplicated into "branches", one
branch per driver of the loudspeaker system. Then a separate LADSPA filter
chain is run on each branch. Finally the branches are recombined back into
one multichannel audio stream (one channel per loudspeaker driver) and sent
to the output device of choice. In my version of this, there is also one
multichannel LADSPA plugin that the re-combined audio stream is run through
that will implement fractional delay on each channel.



I read through the Sox manual, but it's not clear to me how to do the
routing (splitting and recombining of channels) and processing of each
channel with a its own chain of LADSPA plugins. Can someone give me a little
hand holding towards this goal with some examples?



Also, with ecasound, a chain of several LADSPA plugins can be specified in a
text file. The text file can then be called instead of having to list all of
the LADSPA plugins explicitly, and this seems to tidy up the command line.
Can this sort of thing be done in Sox as well?



If any of this is not clear, please let me know and I will provide more
detail or concrete examples. The link above is very good at presenting how
the audio stream should be processed, and is better than me trying to ramble
on and on.



Thanks for any help while I give Sox a try. I think that Sox would be a very
popular tool for this kind of thing, at least in the DIY loudspeaker
community.



-Charlie



_____

No virus found in this message.
Checked by AVG - www.avg.com <http://www.avg.com>
Version: 2015.0.5961 / Virus Database: 4365/10087 - Release Date: 06/23/15
Eric Wong
2015-06-27 12:27:11 UTC
Permalink
sox can take other commands (especially sox command) as inputs using
"|command"

So you would use remix and "--combine merge" as appropriate

Probably something like the following for Linkwitz-Riley crossover
(which is two Butterworth crossovers in series)

# crossover frequency of 1500 Hz
XO=1500
sox -M \
"|sox i.wav -p remix 1v1 highpass $XO highpass $XO $HI_FX1" \
"|sox i.wav -p remix 1v1 lowpass $XO lowpass $XO $LO_FX1" \
"|sox i.wav -p remix 2v1 highpass $XO highpass $XO $HI_FX2" \
"|sox i.wav -p remix 2v1 lowpass $XO lowpass $XO $LO_FX2" \
output.wav \
remix -m 1,2 3,4

Note: totally untested.

First, we use "-M" (same as --combine merge) since we'll be merging
in the main sox process.

The next four lines with "|sox ..." sets up the inputs, each of each
makes a mono channel from an independently running sox process.
The use of "-p" means each of these sub-commands will output in the
sox internal data format for the main sox instance to read.

Two high-frequency channels, two low-frequency channels. Then you have
the output.wav as the destination.

HI_FX1 will be the effects you want to affect the high frequencies in
the left channel, LO_FX1 are the effects you want on the low frequencies
in the left channel. Ditto for *_FX2 placeholders in the right channel.

Finally, the "remix" effect (along with the -M as the first arg)
combines the first two channels (1=hi1,2=lo1) into the left channel, and
3=hi2,4=lo2. We use -m with remix to avoid power adjustments when
recombining two channels (because the crossover makes it unnecessary for
the recombining to overload)

Hopefully this makes sense.

Please be patient with us, most of us are not watching this list 24/7
and only have time for this on weekends (and not much at that).
francolargo
2016-06-22 17:19:19 UTC
Permalink
Greetings Eric,

I'm part of a small group that is interested in DSP using ARMhf boards like
RPi and BBB. Sox has been a great tool for simpler tasks, but now I would
like to try the objective of the original poster, Charlie. I'm using SoX to
process a stream from a 2-channel ALSA 'recording' source and send filtered
output to a different 2-channel ALSA plug. ...it works great like this:

sox --buffer 1024 -r 48000 -c 2 -t alsa hw:1,0 -t alsa plug:filter1 gain -h
fir /usr/filter/fir2peak48mild.txt

This also works with LADSPA filters like this:

sox --buffer 1024 -r 48000 -c 2 -t alsa hw:1,0 -t alsa plug:filter1 ladspa
ACDf 21 1 0 240 0.707 0 0 ladspa ACDf 21 1 0 240 0.707 0 0

However, I'm stuck when I try to split clone the streaming source using the
Post by Eric Wong
sox -M \
"|sox i.wav -p remix 1v1 highpass $XO highpass $XO $HI_FX1" \
"|sox i.wav -p remix 1v1 lowpass $XO lowpass $XO $LO_FX1" \
"|sox i.wav -p remix 2v1 highpass $XO highpass $XO $HI_FX2" \
"|sox i.wav -p remix 2v1 lowpass $XO lowpass $XO $LO_FX2" \
output.wav \
remix -m 1,2 3,4
Inserted into the above, this:
“|sox -t alsa hw:1,0 -p remix 1v1 ladspa ACDf 21 1 0 240 0.707 0 0 ladspa
ACDf 21 1 0 240 0.707 0 0” \

produces three of these alerts:
"sox FAIL formats: can't open input `hw:1,0': snd_pcm_open error: Device or
resource busy"

Other attempts to specify and clone the inputs all failed as well:
"sox FAIL sox: Not enough input filenames specified"

If you have any advice on getting SoX to clone an ALSA streaming source
(e.g., a stereo signal from 'hw:1,0') for input into multiple effect chains,
it would be much appreciated. As well, any additional advice in correctly
defining each sub-stream for ALSA output would undoubtedly help. Output
could go via hardware (hw:0,0), or via user space into an ALSA plug. Either
way, SoX offers advantages in the ability to combine a multitude of useful
effects. [Think: a digital-level "miniDSP" using a RPi...] We just need to
better understand channel creation and management for continuous streaming.

Many thanks in advance!

Frank




--
View this message in context: http://sox.10957.n7.nabble.com/implementing-loudspeaker-crossovers-using-Sox-LADSPA-plugins-tp5464p5712.html
Sent from the SoX mailing list archive at Nabble.com.
Erich Eckner
2016-06-22 19:12:45 UTC
Permalink
Hi Frank,

i think, sox can't duplicate audio without "buffering" in a file.
However, (in linux) there are other commands, which can copy the data,
e.g. "tee".
I think fiddling around with named pipes and tee, you should be able to
do the trick:

mkfifo sound
mkfifo sound_copy
sox -M "|sox sound -p remix 1 $left_channel_effects" "|sox sound_copy -p
remix 2 $right_channel_effects" $output $effects_to_both

and then in a separate terminal/thread:

sox $stereo_input -p | tee sound > sound_copy

Hmm, I just tried it and it doesn't work, also

sox $stereo_input -p | tee sound sound_copy > /dev/null

as second command doesn't work. Probably some buffering issue (feeding
the pipes from two separate sox commands works)?
Maybe someone else knows how to make this idea work?

Cheers,
Erich
Post by francolargo
Greetings Eric,
I'm part of a small group that is interested in DSP using ARMhf boards like
RPi and BBB. Sox has been a great tool for simpler tasks, but now I would
like to try the objective of the original poster, Charlie. I'm using SoX to
process a stream from a 2-channel ALSA 'recording' source and send filtered
sox --buffer 1024 -r 48000 -c 2 -t alsa hw:1,0 -t alsa plug:filter1 gain -h
fir /usr/filter/fir2peak48mild.txt
sox --buffer 1024 -r 48000 -c 2 -t alsa hw:1,0 -t alsa plug:filter1 ladspa
ACDf 21 1 0 240 0.707 0 0 ladspa ACDf 21 1 0 240 0.707 0 0
However, I'm stuck when I try to split clone the streaming source using the
Post by Eric Wong
sox -M \
"|sox i.wav -p remix 1v1 highpass $XO highpass $XO $HI_FX1" \
"|sox i.wav -p remix 1v1 lowpass $XO lowpass $XO $LO_FX1" \
"|sox i.wav -p remix 2v1 highpass $XO highpass $XO $HI_FX2" \
"|sox i.wav -p remix 2v1 lowpass $XO lowpass $XO $LO_FX2" \
output.wav \
remix -m 1,2 3,4
“|sox -t alsa hw:1,0 -p remix 1v1 ladspa ACDf 21 1 0 240 0.707 0 0 ladspa
ACDf 21 1 0 240 0.707 0 0” \
"sox FAIL formats: can't open input `hw:1,0': snd_pcm_open error: Device or
resource busy"
"sox FAIL sox: Not enough input filenames specified"
If you have any advice on getting SoX to clone an ALSA streaming source
(e.g., a stereo signal from 'hw:1,0') for input into multiple effect chains,
it would be much appreciated. As well, any additional advice in correctly
defining each sub-stream for ALSA output would undoubtedly help. Output
could go via hardware (hw:0,0), or via user space into an ALSA plug. Either
way, SoX offers advantages in the ability to combine a multitude of useful
effects. [Think: a digital-level "miniDSP" using a RPi...] We just need to
better understand channel creation and management for continuous streaming.
Many thanks in advance!
Frank
--
View this message in context: http://sox.10957.n7.nabble.com/implementing-loudspeaker-crossovers-using-Sox-LADSPA-plugins-tp5464p5712.html
Sent from the SoX mailing list archive at Nabble.com.
------------------------------------------------------------------------------
Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San
Francisco, CA to explore cutting-edge tech and listen to tech luminaries
present their vision of the future. This family event has something for
everyone, including kids. Get more information and register today.
http://sdm.link/attshape
_______________________________________________
Sox-users mailing list
https://lists.sourceforge.net/lists/listinfo/sox-users
Doug Lee
2016-06-22 19:42:13 UTC
Permalink
I'm not sure I read this thoroughly enough to have it right, but...

I think the biggest inevitable problem here is synchronization: If you're trying to make two processes send
sound to devices in real time at the same time, you will generally not be able to be sure they are in perfect
sync, resulting in echo effects, inaccurate sense of audio location of sounds in the room, etc. Even if you
could get SoX to send sound two ways simultaneously from a single process, there's no guarantee that the two
devices being sent to would be in perfect sync either.

I'm inferring from the subject of this thread, which refers to speaker crossover implementation, that the goal
is real-time simultaneous output on two devices from a single input stream.

On Wed, Jun 22, 2016 at 09:12:45PM +0200, Erich Eckner wrote:
Hi Frank,

i think, sox can't duplicate audio without "buffering" in a file.
However, (in linux) there are other commands, which can copy the data,
e.g. "tee".
I think fiddling around with named pipes and tee, you should be able to
do the trick:

mkfifo sound
mkfifo sound_copy
sox -M "|sox sound -p remix 1 $left_channel_effects" "|sox sound_copy -p
remix 2 $right_channel_effects" $output $effects_to_both

and then in a separate terminal/thread:

sox $stereo_input -p | tee sound > sound_copy

Hmm, I just tried it and it doesn't work, also

sox $stereo_input -p | tee sound sound_copy > /dev/null

as second command doesn't work. Probably some buffering issue (feeding
the pipes from two separate sox commands works)?
Maybe someone else knows how to make this idea work?

Cheers,
Erich
Post by francolargo
Greetings Eric,
I'm part of a small group that is interested in DSP using ARMhf boards like
RPi and BBB. Sox has been a great tool for simpler tasks, but now I would
like to try the objective of the original poster, Charlie. I'm using SoX to
process a stream from a 2-channel ALSA 'recording' source and send filtered
sox --buffer 1024 -r 48000 -c 2 -t alsa hw:1,0 -t alsa plug:filter1 gain -h
fir /usr/filter/fir2peak48mild.txt
sox --buffer 1024 -r 48000 -c 2 -t alsa hw:1,0 -t alsa plug:filter1 ladspa
ACDf 21 1 0 240 0.707 0 0 ladspa ACDf 21 1 0 240 0.707 0 0
However, I'm stuck when I try to split clone the streaming source using the
Post by Eric Wong
sox -M \
"|sox i.wav -p remix 1v1 highpass $XO highpass $XO $HI_FX1" \
"|sox i.wav -p remix 1v1 lowpass $XO lowpass $XO $LO_FX1" \
"|sox i.wav -p remix 2v1 highpass $XO highpass $XO $HI_FX2" \
"|sox i.wav -p remix 2v1 lowpass $XO lowpass $XO $LO_FX2" \
output.wav \
remix -m 1,2 3,4
???|sox -t alsa hw:1,0 -p remix 1v1 ladspa ACDf 21 1 0 240 0.707 0 0 ladspa
ACDf 21 1 0 240 0.707 0 0??? \
"sox FAIL formats: can't open input `hw:1,0': snd_pcm_open error: Device or
resource busy"
"sox FAIL sox: Not enough input filenames specified"
If you have any advice on getting SoX to clone an ALSA streaming source
(e.g., a stereo signal from 'hw:1,0') for input into multiple effect chains,
it would be much appreciated. As well, any additional advice in correctly
defining each sub-stream for ALSA output would undoubtedly help. Output
could go via hardware (hw:0,0), or via user space into an ALSA plug. Either
way, SoX offers advantages in the ability to combine a multitude of useful
effects. [Think: a digital-level "miniDSP" using a RPi...] We just need to
better understand channel creation and management for continuous streaming.
Many thanks in advance!
Frank
--
Doug Lee ***@dlee.org http://www.dlee.org
SSB BART Group ***@ssbbartgroup.com http://www.ssbbartgroup.com
No one alive is beyond hope; every second of life is a chance.
(08/29/02)
francolargo
2016-06-22 19:47:24 UTC
Permalink
I appreciate your thoughts on this, Doug and Erich!

I'm testing on a Beaglebone Black running Debian Stretch.

I found a ridiculously simple solution to the input sharing issue, at least
in terms of the availability of a signal to SoX. By changing SoX input from
'hw:1,0' to 'dsnoop:1,0' ALSA does not lock up the source.

So, this runs:
sox --buffer 1024 -r 48000 -c 2 -t alsa dsnoop:1,0 -t alsa plug:filter1
ladspa ACDf 21 1 0 240 0.707 0 0 ladspa ACDf 21 1 0 240 0.707 0 0

But unfortunately this does not, giving the attached result:
sox -V3 -M \
“| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 21 1 0 240 0.707 0 0
ladspa ACDf 21 1 0 240 0.707 0 0” \
“| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 22 1 0 240 0.707 0 0
ladspa ACDf 22 1 0 240 0.707 0 0” \
“| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 21 1 0 240 0.707 0 0
ladspa ACDf 21 1 0 240 0.707 0 0” \
“| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 22 1 0 240 0.707 0 0
ladspa ACDf 22 1 0 240 0.707 0 0” \
-t alsa plug:testout4 remix -m 1,2 3,4

sox: SoX v14.4.1
sox FAIL sox: Not enough input filenames specified

sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]
sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]
sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]
sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]

Doug, if we got as far as having unsynchronized signals, there are delay
filters in the ACDf LADSPA filter set and also most crossover paths can be
made (nearly) symmetrical anyway. :)

TIA for any and all ideas!

Frank





--
View this message in context: http://sox.10957.n7.nabble.com/implementing-loudspeaker-crossovers-using-Sox-LADSPA-plugins-tp5464p5715.html
Sent from the SoX mailing list archive at Nabble.com.
Doug Lee
2016-06-22 21:16:00 UTC
Permalink
You'll probably get close then, but I find that sync among processes changes over time based on machine load,
I/O delays, etc.

Cool discovery on how to share the input. I don't know ALSA.

On Wed, Jun 22, 2016 at 12:47:24PM -0700, francolargo wrote:
I appreciate your thoughts on this, Doug and Erich!

I'm testing on a Beaglebone Black running Debian Stretch.

I found a ridiculously simple solution to the input sharing issue, at least
in terms of the availability of a signal to SoX. By changing SoX input from
'hw:1,0' to 'dsnoop:1,0' ALSA does not lock up the source.

So, this runs:
sox --buffer 1024 -r 48000 -c 2 -t alsa dsnoop:1,0 -t alsa plug:filter1
ladspa ACDf 21 1 0 240 0.707 0 0 ladspa ACDf 21 1 0 240 0.707 0 0

But unfortunately this does not, giving the attached result:
sox -V3 -M \
???| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 21 1 0 240 0.707 0 0
ladspa ACDf 21 1 0 240 0.707 0 0??? \
???| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 22 1 0 240 0.707 0 0
ladspa ACDf 22 1 0 240 0.707 0 0??? \
???| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 21 1 0 240 0.707 0 0
ladspa ACDf 21 1 0 240 0.707 0 0??? \
???| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 22 1 0 240 0.707 0 0
ladspa ACDf 22 1 0 240 0.707 0 0??? \
-t alsa plug:testout4 remix -m 1,2 3,4

sox: SoX v14.4.1
sox FAIL sox: Not enough input filenames specified

sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]
sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]
sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]
sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]

Doug, if we got as far as having unsynchronized signals, there are delay
filters in the ACDf LADSPA filter set and also most crossover paths can be
made (nearly) symmetrical anyway. :)

TIA for any and all ideas!

Frank





--
View this message in context: http://sox.10957.n7.nabble.com/implementing-loudspeaker-crossovers-using-Sox-LADSPA-plugins-tp5464p5715.html
Sent from the SoX mailing list archive at Nabble.com.

------------------------------------------------------------------------------
Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San
Francisco, CA to explore cutting-edge tech and listen to tech luminaries
present their vision of the future. This family event has something for
everyone, including kids. Get more information and register today.
http://sdm.link/attshape
_______________________________________________
Sox-users mailing list
Sox-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sox-users
--
Doug Lee ***@dlee.org http://www.dlee.org
SSB BART Group ***@ssbbartgroup.com http://www.ssbbartgroup.com
There is more freedom in knowing how to handle pain than in knowing
how to avoid it. (4/29/01)
Måns Rullgård
2016-06-23 09:35:52 UTC
Permalink
Post by Doug Lee
You'll probably get close then, but I find that sync among processes
changes over time based on machine load, I/O delays, etc.
That's not a problem as long as all the streams keep up within the
buffer constraints. The hard part is synchronising the playback
devices. ALSA provides some aids for this, but it ultimately depends on
hardware capabilities, and even then it's tricky to get right. Of
course, if the playback devices don't all use the same clock, you're
screwed no matter what.

If the output is done through a single multi-channel device, none of
this is an issue.
--
Måns Rullgård
Eric Wong
2016-06-23 10:36:13 UTC
Permalink
Post by francolargo
I appreciate your thoughts on this, Doug and Erich!
I'm testing on a Beaglebone Black running Debian Stretch.
I found a ridiculously simple solution to the input sharing issue, at least
in terms of the availability of a signal to SoX. By changing SoX input from
'hw:1,0' to 'dsnoop:1,0' ALSA does not lock up the source.
Good to know! (I'm not too familiar with non-basic-playback ALSA stuff)
Post by francolargo
sox --buffer 1024 -r 48000 -c 2 -t alsa dsnoop:1,0 -t alsa plug:filter1
ladspa ACDf 21 1 0 240 0.707 0 0 ladspa ACDf 21 1 0 240 0.707 0 0
Alright...
Post by francolargo
sox -V3 -M \
“| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 21 1 0 240 0.707 0 0
ladspa ACDf 21 1 0 240 0.707 0 0” \
“| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 22 1 0 240 0.707 0 0
ladspa ACDf 22 1 0 240 0.707 0 0” \
“| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 21 1 0 240 0.707 0 0
ladspa ACDf 21 1 0 240 0.707 0 0” \
“| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 22 1 0 240 0.707 0 0
ladspa ACDf 22 1 0 240 0.707 0 0” \
-t alsa plug:testout4 remix -m 1,2 3,4
Your email shows “smart quotes”, is that from your email client?
If not and the smart quotes are in your original script,
it could be screwing up parsing at the shell level.
They should be a normal double quote: "
And in the above case, a single-quote (') works, too.
Post by francolargo
sox: SoX v14.4.1
sox FAIL sox: Not enough input filenames specified
sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]
sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]
sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]
sox FAIL ladspa: usage: MODULE [PLUGIN] [ARGUMENT...]
francolargo
2016-06-23 13:38:36 UTC
Permalink
Eric, thanks so much for your attention to this. Good catch on the type of
quote! I changed them to single quotes and also took out the '\' separaters
because greater-thans (>) crept in. And... sound happened!

But we're not quite there on collecting the separate streams. The ALSA plug
listed for output (pcm.testout4) contains 4 channels but I only get sound on
two of them and it seems to be the whole signal. I must let this rest until
tonight, but wanted to a) report the progress and, b) show you the screen
log.

Hopefully we're close. Once a 2-way crossover works, then I want to expand
to a 3-way. ...eventually...

Cheers!

Frank

terminal log:

***@beaglebone:/# export
LADSPA_PATH=$LADSPA_PATH:/usr/local/lib/ladspa:/usr/lib/ladspa

***@beaglebone:/# chrt -f 45 sox -V3 -M '| sox -t alsa dsnoop:1,0 -p remix
1v1 ladspa ACDf 21 1 0 240 0.707 0 0 ladspa ACDf 21 1 0 240 0.707 0 0' '|
sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 22 1 0 240 0.707 0 0 ladspa
ACDf 22 1 0 240 0.707 0 0' '| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa
ACDf 21 1 0 240 0.707 0 0 ladspa ACDf 21 1 0 240 0.707 0 0' '| sox -t alsa
dsnoop:1,0 -p remix 2v1 ladspa ACDf 22 1 0 240 0.707 0 0 ladspa ACDf 22 1 0
240 0.707 0 0' -t alsa plug:testout4 remix -m 1,2 3,4
sox: SoX v14.4.1

Input File : 'dsnoop:1,0' (alsa)
Channels : 2
Sample Rate : 48000
Precision : 16-bit
Sample Encoding: 16-bit Signed Integer PCM

In:0.00% 00:00:00.09 [00:00:00.00] Out:0 [ | ] Clip:1
sox INFO formats: detected file format type `sox'

Input File : 'dsnoop:1,0' (alsa)
Channels : 2
Sample Rate : 48000
Precision : 16-bit
Sample Encoding: 16-bit Signed Integer PCM

In:0.00% 00:00:00.09 [00:00:00.00] Out:0 [ | ] Clip:0
sox INFO formats: detected file format type `sox'

Input File : 'dsnoop:1,0' (alsa)
Channels : 2
Sample Rate : 48000
Precision : 16-bit
Sample Encoding: 16-bit Signed Integer PCM

In:0.00% 00:00:00.09 [00:00:00.00] Out:0 [ | ] Clip:0
sox INFO formats: detected file format type `sox'

Input File : 'dsnoop:1,0' (alsa)
Channels : 2
Sample Rate : 48000
Precision : 16-bit
Sample Encoding: 16-bit Signed Integer PCM

In:0.00% 00:00:00.09 [00:00:00.00] Out:0 [ | ] Clip:0
sox INFO formats: detected file format type `sox'

Input File : '| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 21 1 0
240 0.707 0 0 ladspa ACDf 21 1 0 240 0.707 0 0' (sox)
Channels : 1
Sample Rate : 48000
Precision : 32-bit
Sample Encoding: 32-bit Signed Integer PCM
Endian Type : little
Reverse Nibbles: no
Reverse Bits : no
Comment : 'Processed by SoX'


Input File : '| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 22 1 0
240 0.707 0 0 ladspa ACDf 22 1 0 240 0.707 0 0' (sox)
Channels : 1
Sample Rate : 48000
Precision : 32-bit
Sample Encoding: 32-bit Signed Integer PCM
Endian Type : little
Reverse Nibbles: no
Reverse Bits : no
Comment : 'Processed by SoX'


Input File : '| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 21 1 0
240 0.707 0 0 ladspa ACDf 21 1 0 240 0.707 0 0' (sox)
Channels : 1
Sample Rate : 48000
Precision : 32-bit
Sample Encoding: 32-bit Signed Integer PCM
Endian Type : little
Reverse Nibbles: no
Reverse Bits : no
Comment : 'Processed by SoX'


Input File : '| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 22 1 0
240 0.707 0 0 ladspa ACDf 22 1 0 240 0.707 0 0' (sox)
Channels : 1
Sample Rate : 48000
Precision : 32-bit
Sample Encoding: 32-bit Signed Integer PCM
Endian Type : little
Reverse Nibbles: no
Reverse Bits : no
Comment : 'Processed by SoX'


Output File : 'plug:testout4' (alsa)
Channels : 2
Sample Rate : 48000
Precision : 32-bit
Sample Encoding: 32-bit Signed Integer PCM
Endian Type : little
Reverse Nibbles: no
Reverse Bits : no

sox INFO sox: effects chain: input 48000Hz 4 channels
sox INFO sox: effects chain: remix 48000Hz 2 channels
sox INFO sox: effects chain: output 48000Hz 2 channels
In:0.00% 00:00:00.26 [00:00:00.00] Out:10.2k [-=====|=====-] Hd:0.0 Clip:2
sox WARN alsa: under-run
In:0.00% 00:00:00.60 [00:00:00.00] Out:28.7k [======|======] Hd:0.0 Clip:1
sox WARN alsa: over-run
In:0.00% 00:03:23.78 [00:00:00.00] Out:9.78M [ =====|===== ] Hd:4.0 Clip:52

[system ran happily from here - then ^C issued]

sox WARN ladspa: ladspa clipped 11 samples; decrease volume?
sox WARN ladspa: ladspa clipped 41 samples; decrease volume?
Aborted.
In:0.00% 00:03:23.78 [00:00:00.00] Out:9.78M [ =====|===== ] Hd:0.0 Clip:60
sox WARN ladspa: ladspa clipped 12 samples; decrease volume?
sox WARN ladspa: ladspa clipped 48 samples; decrease volume?
Aborted.
In:0.00% 00:03:23.61 [00:00:00.00] Out:9.77M [ | ] Hd:3.7 Clip:0
Aborted.
In:0.00% 00:03:23.52 [00:00:00.00] Out:9.77M [ -====|==== ] Hd:0.3 Clip:878
sox WARN remix: remix clipped 878 samples; decrease volume?
Aborted.
In:0.00% 00:03:23.86 [00:00:00.00] Out:9.79M [ | ] Hd:3.8 Clip:0
Aborted.
***@beaglebone:/#




--
View this message in context: http://sox.10957.n7.nabble.com/implementing-loudspeaker-crossovers-using-Sox-LADSPA-plugins-tp5464p5720.html
Sent from the SoX mailing list archive at Nabble.com.
francolargo
2016-06-23 15:05:05 UTC
Permalink
Quick update:

Curiosity got the better of me so I did a quick experiment - I removed the
'remix' statement.
Now the screen report is:

...
sox INFO sox: effects chain: input 48000Hz 4 channels
sox INFO sox: effects chain: output 48000Hz 4 channels

And, I hear four channels that seem appropriately filtered. Currently they
don't go to the correct drivers but I can re-route them in ALSA.

However, clearly, the bass lines are delayed relative to the mids/highs.
Any thoughts on re-synching (as 'remix' seemed to do previously)?

TIA,

Frank



--
View this message in context: http://sox.10957.n7.nabble.com/implementing-loudspeaker-crossovers-using-Sox-LADSPA-plugins-tp5464p5721.html
Sent from the SoX mailing list archive at Nabble.com.
francolargo
2016-06-23 19:42:23 UTC
Permalink
Curiosity got the better of me and I ran a couple more tests.

Conclusions:
1) "-t alsa hw:0,0" works fine for 4 channel output - no ALSA plug needed
(though it doesn't hurt).
2) Adding 'delay' to each process seems seamless. How much delay?
Increments of 8192 samples sound darn close... Note that very little
competition for CPU is anticipated because the Beaglebone Black is a
dedicated music player.

Question: How would one precisely find the size of the delay added by each
of the first 3 SoX filter processes?

What is playing now:

chrt -f 45 sox -M \
'| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 22 1 0 640 0.707 0 0
delay 24576s' \
'| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 21 1 0 640 0.707 0 0
delay 16384s' \
'| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 22 1 0 640 0.707 0 0
delay 8192s' \
'| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 21 1 0 640 0.707 0 0' \
-t alsa hw:0,0

'top' for the above command:

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
709 root -46 0 8752 5040 4072 S 3.6 1.0 0:43.95 sox
711 root -46 0 8784 4880 3880 S 3.6 1.0 0:47.52 sox
713 root -46 0 8816 4980 3948 S 3.6 1.0 0:46.96 sox
707 root -46 0 8624 4864 3948 S 3.3 1.0 0:39.44 sox
705 root -46 0 7396 3840 3120 S 3.0 0.8 0:34.05 sox



--
View this message in context: http://sox.10957.n7.nabble.com/implementing-loudspeaker-crossovers-using-Sox-LADSPA-plugins-tp5464p5722.html
Sent from the SoX mailing list archive at Nabble.com.
Eric Wong
2016-06-24 08:11:43 UTC
Permalink
Post by francolargo
Curiosity got the better of me and I ran a couple more tests.
1) "-t alsa hw:0,0" works fine for 4 channel output - no ALSA plug needed
(though it doesn't hurt).
2) Adding 'delay' to each process seems seamless. How much delay?
Increments of 8192 samples sound darn close... Note that very little
competition for CPU is anticipated because the Beaglebone Black is a
dedicated music player.
Question: How would one precisely find the size of the delay added by each
of the first 3 SoX filter processes?
The default pipe size is 65536 in the Linux kernel and
current versions of sox do not change that, so you could
use

I'm also unsure of the overhead in the .sox ('-p') data stream.
When I care about delay from the pipe,
I use something like FMT='-t s32 -c2 -r48000'
and use $FMT (no quotes) where you would use '-p' to avoid the
metadata overhead.

However, you may not need to care about delay...
Post by francolargo
chrt -f 45 sox -M \
'| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 22 1 0 640 0.707 0 0
delay 24576s' \
'| sox -t alsa dsnoop:1,0 -p remix 1v1 ladspa ACDf 21 1 0 640 0.707 0 0
delay 16384s' \
'| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 22 1 0 640 0.707 0 0
delay 8192s' \
'| sox -t alsa dsnoop:1,0 -p remix 2v1 ladspa ACDf 21 1 0 640 0.707 0 0' \
-t alsa hw:0,0
Am I correct dsnoop acts like a UDP multicast channel?
(and you have 4 subscribers in the above case)

If so, perhaps try an expanded version with using the extra FIFO
in my response to Erich. I had two channels, and one extra FIFO
for the previous example. With 4 channels, it would require 3
intermediate FIFOs for a total of seven FIFOs.

The following probably works for you:
#!/bin/sh
mkfifo llo.sox
mkfifo rlo.sox
mkfifo lhi.sox
mkfifo rhi.sox
mkfifo split1.fifo
mkfifo split2.fifo
mkfifo split3.fifo

# One dsnoop subscriber feeds multiple pipes:
(sox -t alsa dsnoop:1,0 -p | tee split1.fifo | cat >llo.sox) &

# Notice a pattern? You could add more FIFOs into this chain :)
(tee <split1.fifo split2.fifo | cat >lhi.sox) &
(tee <split2.fifo split3.fifo | cat >rlo.sox) &

# "sox -M" actually reads from this, first
(cat <split3.fifo >rhi.sox) &

# You shouldn't have to worry about synchronization/delays here,
# either. "sox -M" will block on reading (which is why we
# used so many background processes above) and they're all
# fed from the common "sox -t alsa dsnoop:1,0 -p" process
sox -M \
"|sox llo.sox -p remix 1v1 ladspa ACDf 22 1 0 640 0.707 0 0" \
"|sox lhi.sox -p remix 1v1 ladspa ACDf 21 1 0 640 0.707 0 0" \
"|sox rlo.sox -p remix 2v1 ladspa ACDf 22 1 0 640 0.707 0 0" \
"|sox rhi.sox -p remix 2v1 ladspa ACDf 21 1 0 640 0.707 0 0" \
-t alsa hw:0,0
wait # for backgrounded processes to exit
--
EW
Eric Wong
2016-06-24 08:14:51 UTC
Permalink
Post by Eric Wong
The default pipe size is 65536 in the Linux kernel and
current versions of sox do not change that, so you could
use
... that size based on the sample size and rate to calculate
the delay incurred by the pipe.

(sorry, incomplete sentence :x, pipes may not drop data, but my
brain does at this hour :)
Måns Rullgård
2016-06-24 09:51:04 UTC
Permalink
Post by Eric Wong
Post by Eric Wong
The default pipe size is 65536 in the Linux kernel and
current versions of sox do not change that, so you could
use
... that size based on the sample size and rate to calculate
the delay incurred by the pipe.
The absolute delay is unimportant here. It's the relative delay between
channels that matters.
--
Måns Rullgård
francolargo
2016-06-26 14:14:22 UTC
Permalink
Many thanks for your ideas and help, Eric!

I am learning about Linux only for the purposes of my audio hobby, so your
last ideas will take some time to digest. One of the attractions of SoX is
the ease with which different genres of effects can be combined into a
stream. SoX is efficient and simple to use in cases where the input sample
frequency is fixed - like USB input from a fixed frequency appliance.
However, in my own setup I use a clock-switching Linux kernel for the
'audiophile material' so it is never resampled regardless of it's native
frequency. For now I will focus on scripting a detector for a file's native
sample rate so that any FIR filtering is done correctly. [I'm only barely
fluent in Python :P] Once that works I will revisit SoX to come up with
different command lines for each source sample rate. So, timeout for now on
the delay question and again, thanks for your ideas.

Frank



--
View this message in context: http://sox.10957.n7.nabble.com/implementing-loudspeaker-crossovers-using-Sox-LADSPA-plugins-tp5464p5727.html
Sent from the SoX mailing list archive at Nabble.com.

Eric Wong
2016-06-23 10:47:36 UTC
Permalink
Post by Erich Eckner
Hi Frank,
i think, sox can't duplicate audio without "buffering" in a file.
However, (in linux) there are other commands, which can copy the data,
e.g. "tee".
I think fiddling around with named pipes and tee, you should be able to
mkfifo sound
mkfifo sound_copy
sox -M "|sox sound -p remix 1 $left_channel_effects" "|sox sound_copy -p
remix 2 $right_channel_effects" $output $effects_to_both
sox $stereo_input -p | tee sound > sound_copy
Hmm, I just tried it and it doesn't work, also
sox $stereo_input -p | tee sound sound_copy > /dev/null
as second command doesn't work. Probably some buffering issue (feeding
the pipes from two separate sox commands works)?
Maybe someone else knows how to make this idea work?
The annoying thing about FIFOs is opening them for either
reading or writing blocks the process until the other end
also opens for reading or writing.

So whenever you do "tee FIFO1 >FIFO2" or "tee FIFO1 FIFO2",
you're generally screwed :) At the very least, you would need
"tee FIFO1 | cat >FIFO2" to open in parallel.

On Linux, you can "strace -p $PID" and watch processes get
stuck like this.

But reading is still order-dependent on the "sox -M" process
when writing two FIFOs like that, and tee does not write in
parallel.

I create an intermediate split.fifo to workaround this problem:

#!/bin/sh
INPUT=/my/favorite/song.flac # in stereo :)
mkfifo l.sox
mkfifo r.sox
mkfifo split.fifo # the key intermediate buffer
(sox $INPUT -p | tee split.fifo | cat >l.sox) &
(cat split.fifo >r.sox) &

# The above ensures r.sox and l.sox are opened in
# parallel and also get written to in parallel;
# something tee alone cannot do.

# This means the read end can go about doing its thing:
sox -M "|sox l.sox -p remix 1" "|sox r.sox -p remix 2" -p | play -p

---
In contrast, the following is order-dependent,
since 'sox -M" will read from the r.sox process
before the l.sox one.

#!/bin/sh
INPUT=/my/favorite/song.flac
mkfifo l.sox
mkfifo r.sox

# The order in the pipeline when we write to FIFOs matters.
# (sox $INPUT -p | tee l.sox | cat >r.sox) & # broken!
(sox $INPUT -p | tee r.sox | cat >l.sox) & # works!

# With strace, I saw "sox -M" tried reading from the
# second (r.sox) process before reading from the l.sox process.
# Which is why the "tee" above writes to r.sox, first:
sox -M "|sox l.sox -p remix 1" "|sox r.sox -p remix 2" -p | play -p
---

But yeah, the ordering-dependency makes it non-intuitive.
Loading...