LADSPA plugin programming for Linux audio crossovers

Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.
Member
Joined 2007
Paid Member
Using a virtual loopback card (via snd-aloop), I got MPD to feed ecasound via ALSA! ...and it seems to not resample... That's the good news. :D

The bad news is that system overhead has gone up considerably, and both ecasound plus MPD are drawing a higher %CPU than when joined with a pipe. ...enough to cause gaps in the sound above 48kHz. :mad:

Lots of experimenting needed. I wonder if some of the buffers or the loopback driver need optimization, etc. I am using single-buffering (-z:nodb) in ecasound.

As always, any and all suggestions will help a great deal!

Frank
 
Tweaking ecasound's buffer and no-realtime parameters brought it under control. Unfortunately, I find that MPD is auto resampling everything to 44.1 - need to fix that.

Just to clarify, WHAT was brought under control exactly? Do you mean that after you changed the buffer and other settings in ecasound the CPU utilization was "brought under control" meaning its less now? If so, can you recall what the CPU utilization was before and after your tweak?

Keep in mind that resampling, depending on how that is done, can be relatively CPU intensive. Let us know if you can get MPD to not resample and other things that you try. Others may benefit from your experiences.
 
Member
Joined 2007
Paid Member
My mpd.conf file is 350 lines of README and 50 lines of options! But it turns out that both MPD and ecasound have logic to decide when to and when not to resample. ...trying to get my head around the options, and that means a lot of trial and error. For example, MPD uses only 3% CPU to read a 96/24 file and dump it onto ALSA. But fire up ecasound as the final destination, and MPD uses 60% CPU - both situations with the same mpd.conf!

Turns out that ecasound is equally capricious. Changing from 'realtime' to 'non-realtime' presets (-B:nonrt) and raising the buffer (-b:4096) eliminated some under run problems. So, I'm just working through this the slow way. And will take one more stab at type LADSPA plugins in ALSA. But it seems to me that some ALSA resources must be missing. If I load a fresh copy of ALSA I step way back. In any case, I'm happy to share insights as they arise. ...hope I don't have to wait for a quad-core beagle bone! :cool:
 
My mpd.conf file is 350 lines of README and 50 lines of options!

Yet those options are quite crucial.

But it turns out that both MPD and ecasound have logic to decide when to and when not to resample

Of course they do, depending on the capabilities reported to them by the chain further down the line.


For example, MPD uses only 3% CPU to read a 96/24 file and dump it onto ALSA. But fire up ecasound as the final destination, and MPD uses 60% CPU - both situations with the same mpd.conf!

Having the same mpd.conf for the A) and B) means you have ecasound configured in the alsa chain.

OK, let's look at the mpd alsa output code.

This line master/mpd.git asks the alsa-lib library for samplerate which the alsa device can consume, nearest to the samplerate of the source. This value is stored back to the AudioFormat struct master/mpd.git

And here master/mpd.git mpd decides whether resampling is required or not - i.e. whether the output device is capable of consuming the source samplerate

Now the question is - is your alsa device configured in .asoundrc/asound.conf/aloop-recorded-by-ecasound capable of the 96kHz samplerate?

Clearly for hw:X it is = low load due to no resampling.

And For ecasound configured somewhere in the chain (where? - again you did not provide any info) it is not. My 2 cents aloop (which works for all samplerates) is recorded by ecasound in samplerate different to the 96kHz, thus the "aloop" soundcard reporting only this samplerate as playback capability further up the stream to mpd.

You can look at /proc/asound/YOUR_LOOPBACK_CARD/..../hw_params to see what samplerate the loopback "soundcard" uses at the moment (https://bbs.archlinux.org/viewtopic.php?id=133610 ).

BTW the libsox sample conversion available in recent mpd master/mpd.git - should be pretty CPU thrifty Using libsoxr-lsr in Alsa Rate Plugin with LD_PRELOAD | Blog IVITERA a.s. . Which samplerate conversion method do you use in mpd? My 2 cents the cpu-demanding libsamplerate . We are back at the lack of useful information...
 
Member
Joined 2007
Paid Member
I just returned from a brief out-of-town trip that sidetracked my efforts, and am delighted to read the helpful comments from @phofman, above. I truly appreciate the generosity! :):):) This makes the handshaking among programs much more understandable.

As this thread seems now to include 'filter implementation' in addition to 'filter design', let me reference the kernel I am using. It is the work of DIYer 'Miero' (from Prague!) who has collaborated with hardware designers as detailed in this DIYaudiothread, also on this webpage, this 'beginner's guide', and at this Debian patch repository.

Let me now add detail so we can better address the problem(s). For my mpd.conf, I edited out most of the comment lines and copied it here (unused output options remain for reference).

Code:
music_directory		"/data"
playlist_directory		"/var/lib/mpd/playlists"
db_file			"/var/lib/mpd/tag_cache"
log_file			"/var/log/mpd/mpd.log"
pid_file			"/run/mpd/pid"
state_file			"/var/lib/mpd/state"
sticker_file                   "/var/lib/mpd/sticker.sql"
user				"mpd"
bind_to_address		"localhost"
input {
plugin "curl"
}

########  example audio output configurations ########
audio_output {
type		"alsa"
name            "Loopback"
device          "hw:1,0"
auto_channels	"no"
auto_resample   "no"    # parameter found in online example - not working
dsd_usb         "no"
dsd_native      "yes"
dsd_native_type "3"
priority        "FIFO:32"
format         "*:32:2"         # is '2' the # of mono channels?
period_time     "1"
mixer_type      "hardware"
#	mixer_device	"default"	# optional
#	mixer_control	"PCM"		# optional
#	mixer_index	"0"		# optional
}
#
# An example of an OSS output:

#audio_output {
#	type		"oss"
#	name		"My OSS Device"
#	device		"/dev/dsp"	# optional
#	mixer_type      "hardware"      # optional
#	mixer_device	"/dev/mixer"	# optional
#	mixer_control	"PCM"		# optional
#}
#
# An example of a shout output (for streaming to Icecast):
#
#audio_output {
#	type		"shout"
#	encoding	"ogg"			# optional
#	name		"My Shout Stream"
#	host		"localhost"
#	port		"8000"
#	mount		"/mpd.ogg"
#	password	"hackme"
#	quality		"5.0"
#	bitrate		"128"
#	format		"44100:16:1"
#	protocol	"icecast2"		# optional
#	user		"source"		# optional
#	description	"My Stream Description"	# optional
#	url             "http://example.com"    # optional
#	genre		"jazz"			# optional
#	public		"no"			# optional
#	timeout		"2"			# optional
#	mixer_type      "software"              # optional
#}
#
# An example of a recorder output:
#
#audio_output {
#       type            "recorder"
#       name            "My recorder"
#       encoder         "vorbis"                # optional, vorbis or lame
#       path            "/var/lib/mpd/recorder/mpd.ogg"
##      quality         "5.0"                   # do not define if bitrate is defined
#       bitrate         "128"                   # do not define if quality is defined
#       format          "44100:16:1"
#}
#
# An example of a httpd output (built-in HTTP streaming server):
#
#audio_output {
#	type		"httpd"
#	name		"My HTTP Stream"
#	encoder		"vorbis"		# optional, vorbis or lame
#	port		"8000"
#	bind_to_address "0.0.0.0"               # optional, IPv4 or IPv6
#	quality		"5.0"			# do not define if bitrate is defined
#	bitrate		"128"			# do not define if quality is defined
#	format		"44100:16:1"
#	max_clients     "0"                     # optional 0=no limit
#}
#
# An example of a pulseaudio output (streaming to a remote pulseaudio server)
# Please see README.Debian if you want mpd to play through the pulseaudio
# daemon started as part of your graphical desktop session!
#
#audio_output {
#	type		"pulse"
#	name		"My Pulse Output"
#	server		"remote_server"		# optional
#	sink		"remote_server_sink"	# optional
#}
#
# An example of a winmm output (Windows multimedia API).
#
#audio_output {
#	type		"winmm"
#	name		"My WinMM output"
#	device		"Digital Audio (S/PDIF) (High Definition Audio Device)" # optional
#		or
#	device		"0"		# optional
#	mixer_type	"hardware"	# optional
#}
#
# An example of an openal output.
#
#audio_output {
#	type		"openal"
#	name		"My OpenAL output"
#	device		"Digital Audio (S/PDIF) (High Definition Audio Device)" # optional
#}
#
## Example "pipe" output:
#
#audio_output {
#	type		"pipe"
#	name		"crossover/eq"
#        format		"*:32:2"
#        mixer_type 	"software"
#        auto_channels   "no"
#	command		"ecasound..."   # reliably starts from this command
#}
#	name		"my pipe"
#	command		"ecasound..."   # reliably starts from this command
## Or if you want to use AudioCompress
#	command		"AudioCompress -m | aplay -f cd 2>/dev/null"
## Or to send raw PCM stream through PCM:
#	command		"nc example.org 8765"
#	format		"44100:16:2"
#}
#
## An example of a null output (for no audio output):
#
#audio_output {
#	type		"null"
#	name		"My Null Output"
#	mixer_type      "none"                  # optional
#}
#
# If MPD has been compiled with libsamplerate support, this setting specifies
# the sample rate converter to use.  Possible values can be found in the
# mpd.conf man page or the libsamplerate documentation. By default, this is
# setting is disabled.
#
#samplerate_converter		"Fastest Sinc Interpolator"
#
###############################################################################

filesystem_charset		"UTF-8"

id3v1_encoding			"UTF-8"

realtime_option {
memlock                 "yes"
stack_reserve           "1024"
heap_reserve            "10240"
main_priority           "OTHER:0"
player_priority         "FIFO:32"
decoder_priority        "FIFO:31"
update_priority         "OTHER:0"
}


Having the same mpd.conf for the A) and B) means you have ecasound configured in the alsa chain.

Unfortunately, then, ecasound seems to be at the heart of the problem. In MPD.conf, the signal can be 'padded' to 32 bits and the frequency can be ignored by placing an asterisk in the format line. Ecasound is different - it defaults to 44.1 kHz 2ch if either no format is specified or if any of the "-F:" parameters are not specified.

Here is the ecasound filter chain I was using:
Code:
 ecasound  -q -z:nodb -z:nointbuf -B:nonrt -b:1024 -x -a:pre -i:alsahw,1,1 -o:loop,1 -a:mid,woofer -i:loop,1 -a:woofer -pn:f_woofer -chorder:0,1,0,2 -a:mid -pn:f_mid -chorder:1,0,2,0 -a:woofer,mid -f:32,4,44100 -o:alsahw,0,0
I did try an '*' in the frequency spot of the -f:... parameter and it didn't work.


You can look at /proc/asound/YOUR_LOOPBACK_CARD/..../hw_params to see what samplerate the loopback "soundcard" uses at the moment (https://bbs.archlinux.org/viewtopic.php?id=133610 ).
Yes, the Loopback virtual 'card' always reports 44.1kHz!

There may be other possible parameters, but I found no suitable configuration for the ./usr/share/ecasound/ecasoundrc file:
Code:
 # settings decided at build time
ecasound-version = 2.9.0
resource-directory = /usr/share/ecasound
resource-file-genosc-envelopes = generic_oscillators
resource-file-effect-presets = effect_presets
ladspa-plugin-directory = /usr/lib/ladspa

# settings that affect creation of chainsetups (examples)
#midi-device = rawmidi,/dev/midi
#default-output = autodetect
#default-audio-format = s16_le,2,44100,i
#default-to-precise-sample-rates = false                     
default-mix-mode = sum
#bmode-defaults-nonrt = 1024,false,50,false,100000,true
#bmode-defaults-rt = 1024,true,50,true,100000,true
#bmode-defaults-rtlowlatency = 256,true,50,true,100000,false

# commands for launching external programs
#ext-cmd-text-editor = nano
#ext-cmd-text-editor-use-getenv = true
#ext-cmd-wave-editor = ecawave
#ext-cmd-mp3-input = mpg123 --stereo -r %s -b 0 -q -s -k %o %f
#ext-cmd-mp3-output = lame -b %B -s %S --little-endian -S - %f
#ext-cmd-ogg-input = ogg123 -d raw -o byteorder:%E --file=- %f
#ext-cmd-ogg-output = oggenc -b %B --raw --raw-bits=%b --raw-chan=%c --raw-rate=%s --raw-endianness 0 --output=%f -
#ext-cmd-mikmod = mikmod -d stdout -o 16s -q -f %s -p 0 --noloops %f
#ext-cmd-timidity = timidity -Or1S -id -s %s -o - %f
#ext-cmd-flac-input = flac -d -c %f
#ext-cmd-flac-output = flac -o %f -f --force-raw-format --channels=%c --bps=%b --sample-rate=%s --sign=%I --endian=%E -
#ext-cmd-aac-input = faad -w -b 1 -f 2 -d %f
#ext-cmd-aac-output = faac -P -o %f -R %s -B %b -C %c -
The 'default to precise sample rates' reportedly only works on OSS output - it has no effect with ALSA. In order to get 4 channels out, it is necessary to include -f:32,4,[samplerate] in the ecasound chain setup. For example: ecasound -q -z:nodb -z:nointbuf -B:nonrt -b:1024 -x -a:pre -i:alsahw,1,1 -o:loop,1 -a:mid,woofer -i:loop,1 -a:woofer -pn:f_woofer -chorder:0,1,0,2 -a:mid -pn:f_mid -chorder:1,0,2,0 -a:woofer,mid -f:32,4,44100 -o:alsahw,0,0

...where f_woofer and f_mid are filter specs in /usr/share/ecasound/effect_presets

If I leave out the sample rate like... -f:32,4,* (...works in MPD!) it defaults to 32:2:44100 and there is no "work-around" to specifying 4 channels in the -f: spec ahead of the -o:alsahw,0,0 output spec. But the great thing about ecasound is that it actually works with the Botic kernel (versus type-LADSPA plugs in ALSA :headbash:). I can fight with ALSA plugins some more - trial-and-error!!!

BTW the libsox sample conversion available in recent mpd master/mpd.git - should be pretty CPU thrifty Using libsoxr-lsr in Alsa Rate Plugin with LD_PRELOAD | Blog IVITERA a.s. . Which samplerate conversion method do you use in mpd? My 2 cents the cpu-demanding libsamplerate . We are back at the lack of useful information...

You are right - libsamplerate. :p SoxResampler might be a partial fix, but if it is always active it would partially defeat the point of the system's clock-switching kernel.

Would it be reasonable to tweak the ecasound source code so that it doesn't down-sample unless necessary, and only by integers of frequency to minimize CPU load and maintain a cleaner signal? I compliment the authors on their good work, but clearly this is a different use scenario than they originally conceived. If, for example, SoxResampling was only triggered if the crossover filters would overload the CPU... That would be a practical solution! ...way out of my league - though perhaps with a great deal of study... ?!?! :D

Thanks again for the critical thought and tutorials!

Frank
 
Last edited:
From the Ecasound man pages at:
ecasound man pages
If no '-f' option is specified, or some of the argument fields are left empty (e.g. '-f:,2,44100'), ecasound will use default values. These default values are defined in ecasoundrc configuration file. See ecasoundrc(5) manual page.
So, this indicates that you should leave arguments empty, not use an asterisk, to get the default. How to set the default? Read on...

The ecasoundrc man page can be found at:
ecasoundrc man pages
ecasoundrc is the configuration file for ecasound.

You may need to create this file, just like with .asoundrc, in order create a default format. As instructed in the description section of the man pages, do this in your home directory:
Any user-specific modification should be done to $HOME/.ecasound/ecasoundrc, not to the global resource file.

Then I believe you can override parts of the format and leave the rate specifier blank to get the default rate or by specifying one. If the input rate is anything else, sample rate conversion will take place. In either case, ecasound seems to need to have the output rate hardcoded as far as I can tell.

I hope I have spared you from more of this: :headbash:
 
Member
Joined 2007
Paid Member
You may need to create this file, just like with .asoundrc, in order create a default format. As instructed in the description section of the man pages, do this in your home directory:


Then I believe you can override parts of the format and leave the rate specifier blank to get the default rate or by specifying one. If the input rate is anything else, sample rate conversion will take place. In either case, ecasound seems to need to have the output rate hardcoded as far as I can tell.

Great comment - I guess I haven't exhausted all of the possibilities. I did try defining a default -f: with 4 channels in ecasoundrc, but then MPC couldn't use it as an output for 2 channel music. I really just want ecasound to mirror all of the attributes of the signal it is getting. I read somewhere that if any parameter of the -f: spec was missing, default would be used for that parameter. in the case of sample rate, I don't want that. I just want 4 channels instead of two with the operating frequency untouched. BUT ... I read this a number of times and guess I didn't take it in context: "Note that ecasound opens out files by default in update mode. Unless option '-x' (overwrite outputs) option is given, audio parameters of an existing audio file take preference over the params set with '-f'." :eek: I have had an -x in my filter chains thinking it meant something else, and the Richard Taylor tutorial used it as well. Still, when a source is not at default frequency, would ecasound use the default frequency for the filters and then upsample the output back to the input frequency? ...will tinker some more! :D
 
I think that you do not want to use the -x option in your case. I think you want ecasound to take on the sample rate and number of channels from the input file. You will need the -f option on the output to specify the number of channels. Using -f:,4 may work, or you may need to use -f:32,4 to explicitly force 32 bit processing since IIRC 24 bit processing is not supported but can be accommodated by 32 bit. Let us know what happens!
 
Member
Joined 2007
Paid Member
That gives sound in fits and starts while ecasound crashes. MPD cpu use is 60%. Something is 'wonky' here. There must be some state files or something that are preventing me from replicating my initial results with mpd/ecasound. For example, when I was messing with piped output I took the sample rate out of the mix and got white noise. After restoring the .conf files and rebooting - white noise...
 
Last edited:
How does ecasound in the aloop capture chain know the samplerate of your to-be played track? It has to open the capturing soundcard at one specific samplerate at start.

As I said before, I would try the alsa file plugin with pipe output. (starting with the pipe character). You will have all the parameters available for constructing your command line (samplerate, channel count, sample format). It will offer mpd all possible samplerates, mpd will pick the requested one (hence no resampling) and upon opening the devices ecasound will be started with parameters of the actual playback.

http://www.diyaudio.com/forums/pc-based/93315-linux-audio-way-go-85.html#post1705320

http://www.diyaudio.com/forums/pc-based/93315-linux-audio-way-go-86.html#post1707310
 
Member
Joined 2007
Paid Member
one possible source of confusion I have is understanding data flow between a source and a slave.pcm. For example,

pcm.filters {
type LADSPA
slave.pcm "hw:1,1"
channels 4
path "/usr/lib/ladspa"
plugins { # listed sequentially

My understanding is that "hw:1,1" will be the data source for the filters, and the slave named "filters" will be available to another plugin for further processing or to redirect to output at hw:0,0. Or do I have it backwards and 'filters' is the input side?
 
Think of the configuration as wrappers. In your example the alsa device named "hw:1,1" is wrapped with LADSPA plugin, yielding the alsa device named "filters".

In playback the samples are passed from the outer-most layer (opened by the playback application) down to the inner-most layer, usually hw:XX - the actual soundcard driver.

In capture mode the flow of samples is backwards - from the inner-most layer (soundcard) to the outer-most layer (the recording application).

The stream of samples itself carries no additional information (in fact it is no stream, plugins just pass position/size of a memory region to be read from/written to). Samplerate, channels count, sample format etc. are setup/negotiated as part of opening the slave device.
 
Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.