grapheqd stands for graphical equalizer daemon. It displays the frequency spectrum of an audio signal via its HTML5 webpage or ASCII based telnet interface. grapheqd runs as a daemon on either Linux with OpenWRT in mind, or FreeBSD. Thus, it uses either ALSA or OSS, choosen at compile time. As FFT computation might require a lot CPU resources on systems without mathematical coprocessor, grapheqd can operate in a client/server mode, where a remote instance on a more powerful system does not use an actual sound device, but connects to a grapheqd instance running on a system with limited CPU power. In such scenarios, you should point your web browser or telnet client only to the remote instance. grapheqd supports PCM signals with one or two channels (read: mono or stereo) and 16 bits per channel. Your audio device must be capable of sampling at 44100 or 48000 Hz. You need a modern web browser with support for JavaScript, CSS3, and websockets in order to access the web interface.
Back in the 80ies and 90ies hifis and stereos came with a graphical equalizer as a separate, physical device, or could be equipped with one. You don't easily find one of these nowadays, at least not for the consumer market. I have never found any use of actually adjusting the audio signal, but always liked the fluorescent dot matrix displays or LCDs. grapheqd tries to reassemble this.
The web interface runs on port 8083 by default:
Point your telnet client to port 8083 as well, and simply enter a c followed by Enter to access the ASCII interface in color mode:
If your terminal does not support colors and/or escape sequences, press m followed by Enter:
$ ./grapheqd -h
grapheqd version 7
PCM driver: OSS
Usage:
grapheqd [-d] [-e <program>] [-l <address[:port]>] [-p <pidfile>]
[-r <address[:port]>] [-s <soundcard>] [-u <user>]
grapheqd -h
-d run in foreground, and log to stdout/stderr, do not
detach from terminal, do not log to syslog
-e <program> read PCM data from this program's standard output;
the name of the soundcard is passed as first
commandline parameter to <program>;
if the second commandline parameter is present and
has a value of "stderr", i.e. if grapheqd has been
started with -d, then <program> can write to
standard error safely;
otherwise, error logging must be done via syslog,
e.g. by calling logger;
cannot be used in conjunction with either option -c
or -r
-l <address[:port]> listen on this address and port; a maximum of 16
addresses may be specified; port defaults to 8083;
default: 0.0.0.0:8083
-p <pidfile> daemonize and save pid to this file; no default, pid
gets not written to any file unless <pidfile> is given
-r <address[:port]> connect to another grapheqd running at this address and
port; <port> defaults to 8083; cannot be used in
conjunction with either option -e or -s
-s <soundcard> read PCM from this soundcard; default: /dev/dsp0;
cannot be used in conjunction with option -r; a
program given via parameter -e takes precedence
-u <user> switch to this user; no default, run as invoking user
-h show this help ;-)
-
KISS FFT by Mark Borgerding to convert PCM data from time to frequency domain
By default, the grapheqd build process expects a copy of KISS FFT in the directory ../kissfft. You can specify another directory by passing KISSFFT=/some/other/directory to make. To fetch a copy of KISS FFT, issue this from within the grapheqd directory:
git clone https://github.com/mborgerding/kissfft.git ../kissfft
Or simply call
make
. -
On Linux: ALSA library and headers to access your audio device and to read PCM data
Your Linux distro should have these.
-
On FreeBSD: OSS library and headers to access your audio device and to read PCM data
FreeBSD 4 and newer should have these. However, I just tested with FreeBSD 12.
-
OpenSSL crypto library and headers for SHA1 hashing of the Sec-WebSocket-Accept header when switching protocols
Your Linux distro should have these as well.
-
(G)libc system library and headers with support for libm for mathematical routines and pthreads for threads, mutexes, and conditions. Any other libc might do as well, but has not been tested yet.
-
C compiler. I tested with GCC version 10.1.0, and Clang/LLVM 8.0.1 and 10.0.0.
For cross compiling you can pass CC to make, e.g.
make CC=mips-openwrt-linux-gcc
-
On Linux: GNU make or any other compatible make.
-
On FreeBSD: default /usr/bin/make or any other compatible make.
-
Either clone this repository:
git clone https://github.com/felixjogris/grapheqd.git
or grab an official release from https://ogris.de/grapheqd/
-
If you haven't fetched KISS FFT yet, do it now:
git clone https://github.com/mborgerding/kissfft.git ../kissfft
-
Call
make
. It will use Makefile or GNUmakefile on FreeBSD or Linux, respectively, and fetch KISS FFT automatically if you haven't done it, and will compile everything. -
Optionally, if you want systemd integration, call make with USE_SYSTEMD=1
-
You now have grapheqd in the current directory. Either call it directly, copy it somewhere, or run
sudo make install
, which will place it to /usr/local/sbin. Then either add it to your RC init or systemd configuration, or use the provided scriptsgrapheqd.service
,grapheqd.openrc
, orgrapheqd.sh
, whichmake install
has copied to/lib/systemd/system
,/etc/init.d
, or/usr/local/etc/rc.d
, respectively. -
Starting with version 5, the RC script
/usr/local/etc/rc.d/grapheqd
on FreeBSD supports multiple profiles, e.g. one might have this in/etc/rc.conf
:grapheqd_enable="YES" grapheqd_profiles="scard remote" grapheqd_scard_soundcard="/dev/dsp1" grapheqd_scard_username="audiouser" grapheqd_remote_address="0.0.0.0:8084" grapheqd_remote_raddress="somehost:8083"
You can start each instance via
service grapheqd start scard
andservice grapheqd start remote
, respectively. If you omit the profile name, the RC script operates on every configured and enabled instance, e.g.service grapheqd stop
.
Create a directory package/grapheqd inside your copy of the OpenWRT source tree, and download https://ogris.de/grapheqd/openwrt/Makefile to that directory. Now run make menuconfig
, and under Multimedia select grapheqd. Optionally, select kmod-usb-audio under Kernel modules -> Sound Support. Then build OpenWRT as usual, e.g. by calling make
.
openwrt/Makefile is a copy of that Makefile.
-
By default, grapheqd reads PCM data from a soundcard found on the local system. On Linux, ALSA device hw:0 is used if not overridden by commandline option
-s
. On FreeBSD, the default soundcard is /dev/dsp0. -
You can also connect to another instance of grapheqd running on a remote host:
grapheqd -c <remote host>
-
Starting with version 5, grapheqd can start an external program via commandline option
-e
and read PCM data from the standard output of that program. See ffmpeg2grapheqd.sh for further details. -
You can also place ffmpeg2grapheqd.sh (or any similiar program) on a remote host and make it accessible via inetd, e.g. on FreeBSD:
-
Place ffmpeg2grapheqd.sh to /usr/local/libexec, which
make install
does by default. -
Add this to /etc/inetd.conf:
mmcc stream tcp nowait root /usr/local/libexec/ffmpeg2grapheqd.sh ffmpeg2grapheqd.sh
-
(Re-)start inetd:
service inetd restart
-
Start grapheqd with
-c <remote host> -r mmcc
If grapheqd runs as user who is not allowed to access local devices or is otherwise prohibited from running the external program, you can also connect to a local instance of inetd if it has been configured as shown in aboves inetd.conf snippet. Simply connect to localhost:
grapheqd -c localhost -r mmcc
-
Per channel, grapheqd passes 4096 16 bit audio samples (mono or stereo) at a time to FFT, and pushes calculated linear frequency data to all connected clients, thus resulting in roughly 11 or 12 updates per second if your hardware supports sampling at 44100 or 48000 Hz, respectively. The web interface adjusts the labels under each band (vertical bar) to match its frequency range. If only mono audio is available, then the FFT is called just once per loop, while the output data is duplicated. If no client is connected, then no PCM data is read and FFT does not burn CPU cycles unnecessarily.
The internal web serving routines (to put it nicely) use static and predetermined buffers where possible. printf() and family are not used in the hot code paths and loops.