Here's a script, adapteded from
qpaeq that comes with Pulseaudio:
# Source adapted from utils/qpaeq of PulseAudio
def connect(): # copied from qpaeq
if 'PULSE_DBUS_SERVER' in os.environ:
address = os.environ['PULSE_DBUS_SERVER']
bus = dbus.SessionBus() # Should be UserBus, but D-Bus doesn't implement that yet.
server_lookup = bus.get_object('org.PulseAudio1', '/org/pulseaudio/server_lookup1')
address = server_lookup.Get('org.PulseAudio.ServerLookup1', 'Address', dbus_interface='org.freedesktop.DBus.Properties')
except Exception as e:
sys.stderr.write('There was an error connecting to pulseaudio, '
'please make sure you have the pulseaudio dbus '
'module loaded, exiting...\n')
args = sys.argv[1:]
sinknum = args.pop(0);
sink = get_sink(sinknum);
sys.stderr.write('channels %d, sample rate: %f, filter sample rate: %f\n'%
(nchannels, sample_rate, filter_rate))
channel = int(args.pop(0));
preamp = float(args.pop(0));
freqs = ;
coeffs = ;
while len(args) > 0:
sys.stderr.write('Odd number of frequency/amplification arguments (%d)\n'%(len(freqs)*2+1))
freqs = list([int(round(x*filter_rate/sample_rate)) for x in freqs])
#sys.stderr.write("translated freqs: "+str(freqs)+'\n');
freqs = +freqs+[filter_rate//2];
coeffs = [coeffs]+coeffs+[coeffs[-1]];
#sys.stderr.write("proper freqs: "+str(freqs)+'\n');
# for some reason this fixes the types of the arguments to SeedFilter
# set the filter coefficients
First you want to load the equalizer module as well as the DBus protocol module:
pactl load-module module-dbus-protocol
pactl load-module module-equalizer-sink sink_name=... sink_master=...
pactl set-default-sink ...
Then make some noise, or music or whatever:
play -n synth pinknoise gain -10
Then call the script with the sink number, the channel index you want the equalizer to apply to, a preamp (scaling) factor, and a list of (frequency, coefficient) pairs. The frequency list doesn't have to be dense, since it is interpolated (see
pulseaudio/src/modules/module-equalizer-sink.c) to get the filter coefficients. If you specify the total number of channels as the channel index then the update applies to all channels. Use
pacmd list-sinks to get the sink number.
For example, if the sink number is $SINKNUM and it has 2 channels, then this resets the equalizer to all 1's, as you can verify by opening
./pulse-set-eq $SINKNUM 2 1.0 100 1.0
Here 100 Hz is arbitrary; the list needs to be not empty and can't start with zero. The frequencies must be between 0 and 32768 (the maximum for me, not sure if it is configurable, see the
filter_rate variable above - this is half that). The script uses the amplification coefficients of the smallest and largest frequencies specified on the command line, for the frequencies below and above these, respectively, so in this case the whole spectrum will be assigned an amplification rate of 1.0. At this setting, the equalizer will leave the signal unchanged (in theory at least).
Be careful not to damage your speakers, but for example you can play around by creating pink noise and setting the equalizer to a spike (at 100x amplification) at a single frequency (500Hz):
./pulse-set-eq $SINKNUM 2 1.0 499 0 500 100 501 0
When I do this, I can hear a discrete succession of tones of different random volumes, all at 500Hz, which I presume are belonging to successive DFT windows in the implementation of the filter. It is somewhat pleasant. Playing music through this filter is amusing. Such a sharp "bandpass" would not be possible using a normal graphical equalizer with no command-line version.
The following example makes a filter that goes up and down in frequency, a bit like a siren. Obviously you have to have some music playing or noise or something to hear the effect.
while true; do
for i in $(seq 500 10 1500) $(seq 1500 -10 500); do
./pulse-set-eq $SINKNUM $NCHAN 1 \
0 1 $(( $i - 300 )) 1 \
$i 5 $(( $i + 300 )) 1 \