Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSS input backend and general improved FreeBSD support #540

Merged
merged 2 commits into from Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 16 additions & 2 deletions Makefile.am
Expand Up @@ -7,7 +7,7 @@ cava_SOURCES = cava.c cavacore.c config.c input/common.c input/fifo.c input/shme
output/terminal_noncurses.c output/raw.c output/noritake.c
cava_CPPFLAGS = -DPACKAGE=\"$(PACKAGE)\" -DVERSION=\"$(VERSION)\" \
-D_POSIX_SOURCE -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE_EXTENDED \
-DFONTDIR=\"@FONT_DIR@\"
-DFONTDIR=\"@FONT_DIR@\" -DFONTFILE=\"@FONT_FILE@\"
cava_CFLAGS = -std=c99 -Wall -Wextra -Wno-unused-result -Wno-unknown-warning-option -Wno-maybe-uninitialized -Wno-vla-parameter

if OSX
Expand All @@ -16,7 +16,17 @@ if OSX
else
cava_LDADD = -lrt
cava_font_dir = @FONT_DIR@
cava_font__DATA = cava.psf
cava_font__DATA = @FONT_FILE@
endif

if FREEBSD
if CAVAFONT
CLEANFILES = cava.bdf cava.fnt

cava.fnt: ${srcdir}/cava.psf
${PSF2BDF} --fontname="-gnu-cava-medium-r-normal--16-160-75-75-c-80-iso10646-1" ${srcdir}/cava.psf cava.bdf
${VTFONTCVT} -o cava.fnt cava.bdf
endif
endif

if ALSA
Expand All @@ -39,6 +49,10 @@ if SNDIO
cava_SOURCES += input/sndio.c
endif

if OSS
cava_SOURCES += input/oss.c
endif

if NCURSES
cava_SOURCES += output/terminal_ncurses.c
endif
Expand Down
75 changes: 75 additions & 0 deletions README.md
Expand Up @@ -21,6 +21,7 @@ by [Karl Stavestrand](mailto:karl@stavestrand.no)
- [ALSA](#alsa)
- [MPD](#mpd)
- [sndio](#sndio)
- [OSS](#oss)
- [squeezelite](#squeezelite)
- [macOS](#macos-1)
- [Windows](#windows)
Expand Down Expand Up @@ -87,6 +88,10 @@ For better a better visual experience ncurses is also recomended.

All the requirements can be installed easily in all major distros:

FreeBSD

pkg install autoconf-archive autotools fftw3 iniparser pkgconf psftools sdl2 sndio

Debian/Ubuntu:

sudo apt install build-essential libfftw3-dev libasound2-dev libncursesw5-dev libpulse-dev libtool automake autoconf-archive libiniparser-dev libsdl2-2.0-0 libsdl2-dev libpipewire-0.3-dev pkgconf
Expand Down Expand Up @@ -182,6 +187,10 @@ Or you can change `PREFIX`, for example:
All distro specific instalation sources might be out of date. Please check version before reporting any issues here.


#### FreeBSD

pkg install cava

#### openSUSE

Tumbleweed users have cava in their repo. They can just use:
Expand Down Expand Up @@ -329,6 +338,72 @@ $ sndiod -dd -s default -m mon -s monitor
$ AUDIODEVICE=snd/0.monitor cava
```

### OSS

The audio system used on FreeBSD is the Open Sound System (OSS).
The following example demonstrates how to setup CAVA for OSS on FreeBSD:

```sh
$ cat /dev/sndstat
Installed devices:
pcm0: <Realtek ALC1220 (Rear Analog)> (play/rec) default
pcm1: <Realtek ALC1220 (Front Analog Mic)> (rec)
pcm2: <USB audio> (play/rec)
No devices installed from userspace.
```

The system has three `pcm` sound devices, `pcm0`, `pcm1` and `pcm2`. `pcm0` corresponds to the analog
output jack on the rear, in which external stereo speakers are plugged in, and the analog input jack,
in which one could plug in a microphone. Because it encapsulates both, output and input, it is marked
as `play/rec`. It is also set as the `default` sound device. `pcm1` corresponds to another analog input
jack for a mic on the front side and is marked `rec`. A USB headset which an integrated mic is plugged
in an USB port and the system has created the `pcm2` sound device with `play/rec` capabilities for
it.

In general for every `pcmX` device there is a corresponding `/dev/dspX` audio device. In this example
there are `/dev/dsp0`, `/dev/dsp1` and `/dev/dsp2` (the system creates them when needed, they are not
listet via `ls /dev` if they are currently not in use). The system also creates an implicit `/dev/dsp`,
which acts like a symlink to the `default` audio device, in this example to `/dev/dsp0`.

Now in order to visualize the mic input in CAVA, the `source` value in the configuration file must
be set to the corresponding audio device, i.e.
```sh
[input]
source = /dev/dsp # or /dev/dsp0 for which /dev/dsp is a symlink in this example
```
(which is already the default for CAVA) for the `pcm0` mic on the rear, or
```sh
[input]
source = /dev/dsp1
```
for the `pcm1` mic on the front, or
```sh
[input]
source = /dev/dsp2
```
for the `pcm2` mic on the USB headset.

OSS can't record the outgoing audio on its own, i.e. the sounds from a music player or a browser which
play on the external stereo speakers through `/dev/dsp0` are not visualized in CAVA. A solution is
to use Virtual OSS. It can create virtual audio devices from existing audio devices and from which
the played back audio can be fed into CAVA:
```sh
$ doas pkg install virtual_oss
$ doas virtual_oss -Q0 -C2 -c2 -r48000 -b16 -s2048 -P /dev/dsp0 -R /dev/null -w vdsp.wav -t vdsp.ctl -T /dev/sndstat -l dsp

$ cat /dev/sndstat
Installed devices:
pcm0: <Realtek ALC1220 (Rear Analog)> (play/rec) default
pcm1: <Realtek ALC1220 (Front Analog Mic)> (rec)
pcm2: <USB audio> (play/rec)
Installed devices from userspace:
dsp: <Virtual OSS> (play/rec)
```
It created a virtual device `dsp` from `/dev/dsp0`. Now the audio is visualized in CAVA with the default
`source = /dev/dsp` in the configuration file. Virtual OSS can be configured and started as a service
on FreeBSD.


### squeezelite
[squeezelite](https://en.wikipedia.org/wiki/Squeezelite) is one of several software clients available for the Logitech Media Server. Squeezelite can export its audio data as shared memory, which is what this input module uses.
Just adapt your [config](#configuration):
Expand Down
30 changes: 26 additions & 4 deletions cava.c
Expand Up @@ -70,6 +70,7 @@

#include "input/alsa.h"
#include "input/fifo.h"
#include "input/oss.h"
#include "input/pipewire.h"
#include "input/portaudio.h"
#include "input/pulse.h"
Expand Down Expand Up @@ -299,18 +300,30 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co
if (strncmp(ttyname(0), "/dev/ttys", 9) == 0)
inAtty = 0;
if (inAtty) {
#ifdef CAVAFONT
// checking if cava psf font is installed in FONTDIR
FILE *font_file;
font_file = fopen(FONTDIR "/cava.psf", "r");
font_file = fopen(FONTDIR "/" FONTFILE, "r");
if (font_file) {
fclose(font_file);
system("setfont " FONTDIR "/cava.psf >/dev/null 2>&1");
#ifdef __FreeBSD__
system("vidcontrol -f " FONTDIR "/" FONTFILE " >/dev/null 2>&1");
#else
system("setfont " FONTDIR "/" FONTFILE " >/dev/null 2>&1");
#endif
} else {
// if not it might still be available, we dont know, must try
system("setfont cava.psf >/dev/null 2>&1");
#ifdef __FreeBSD__
system("vidcontrol -f " FONTFILE " >/dev/null 2>&1");
#else
system("setfont " FONTFILE " >/dev/null 2>&1");
#endif
}
#endif // CAVAFONT
#ifndef __FreeBSD__
if (p.disable_blanking)
system("setterm -blank 0");
#endif
}

// We use unicode block characters to draw the bars and
Expand Down Expand Up @@ -348,6 +361,7 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co
audio.cava_in = (double *)malloc(audio.cava_buffer_size * sizeof(double));
memset(audio.cava_in, 0, sizeof(int) * audio.cava_buffer_size);

audio.threadparams = 0; // most input threads don't adjust the parameters
audio.terminate = 0;

debug("starting audio thread\n");
Expand Down Expand Up @@ -403,6 +417,14 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co
audio.rate = 44100;
thr_id = pthread_create(&p_thread, NULL, input_sndio, (void *)&audio);
break;
#endif
#ifdef OSS
case INPUT_OSS:
audio.format = p.samplebits;
audio.rate = p.samplerate;
audio.threadparams = 1; // OSS can adjust parameters
thr_id = pthread_create(&p_thread, NULL, input_oss, (void *)&audio);
break;
#endif
case INPUT_SHMEM:
audio.format = 16;
Expand Down Expand Up @@ -440,7 +462,7 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co
nanosleep(&timeout_timer, NULL);
#endif
pthread_mutex_lock(&audio.lock);
if (audio.format != -1 && audio.rate != 0)
if ((audio.threadparams == 0) && (audio.format != -1) && (audio.rate != 0))
break;

pthread_mutex_unlock(&audio.lock);
Expand Down
12 changes: 9 additions & 3 deletions config.c
Expand Up @@ -45,19 +45,20 @@ const char *default_shader_name[NUMBER_OF_SHADERS] = {"northern_lights.frag", "p
double smoothDef[5] = {1, 1, 1, 1, 1};

enum input_method default_methods[] = {
INPUT_FIFO, INPUT_PORTAUDIO, INPUT_ALSA, INPUT_PULSE, INPUT_PIPEWIRE, INPUT_WINSCAP,
INPUT_FIFO, INPUT_PORTAUDIO, INPUT_ALSA, INPUT_PULSE,
INPUT_PIPEWIRE, INPUT_WINSCAP, INPUT_SNDIO, INPUT_OSS,
};

char *outputMethod, *orientation, *channels, *xaxisScale, *monoOption, *fragmentShader,
*vertexShader;

const char *input_method_names[] = {
"fifo", "portaudio", "pipewire", "alsa", "pulse", "sndio", "shmem", "winscap",
"fifo", "portaudio", "pipewire", "alsa", "pulse", "sndio", "oss", "shmem", "winscap",
};

const bool has_input_method[] = {
HAS_FIFO, /** Always have at least FIFO and shmem input. */
HAS_PORTAUDIO, HAS_PIPEWIRE, HAS_ALSA, HAS_PULSE, HAS_SNDIO, HAS_SHMEM, HAS_WINSCAP,
HAS_PORTAUDIO, HAS_PIPEWIRE, HAS_ALSA, HAS_PULSE, HAS_SNDIO, HAS_OSS, HAS_SHMEM, HAS_WINSCAP,
};

enum input_method input_method_by_name(const char *str) {
Expand Down Expand Up @@ -712,6 +713,11 @@ bool load_config(char configPath[PATH_MAX], struct config_params *p, bool colors
case INPUT_SNDIO:
p->audio_source = strdup(iniparser_getstring(ini, "input:source", SIO_DEVANY));
break;
#endif
#ifdef OSS
case INPUT_OSS:
p->audio_source = strdup(iniparser_getstring(ini, "input:source", "/dev/dsp"));
break;
#endif
case INPUT_SHMEM:
p->audio_source =
Expand Down
7 changes: 7 additions & 0 deletions config.h
Expand Up @@ -37,6 +37,12 @@
#define HAS_SNDIO false
#endif

#ifdef OSS
#define HAS_OSS true
#else
#define HAS_OSS false
#endif

#ifdef _MSC_VER
#define HAS_WINSCAP true
#define SDL true
Expand All @@ -59,6 +65,7 @@ enum input_method {
INPUT_ALSA,
INPUT_PULSE,
INPUT_SNDIO,
INPUT_OSS,
INPUT_SHMEM,
INPUT_WINSCAP,
INPUT_MAX,
Expand Down