diff --git a/README.md b/README.md index 4de59a8..79301c6 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ thanks to [anko](https://github.com/anko) for the gif, here is the [recipe]( htt - [From ALSA-loopback device (Tricky)](#from-alsa-loopback-device-tricky) - [From mpd's fifo output](#from-mpds-fifo-output) - [sndio](#sndio) + - [squeezelite](#squeezelite) - [Running via ssh](#running-via-ssh) - [Font notes](#font-notes) - [In ttys](#in-ttys) @@ -245,7 +246,15 @@ $ sndiod -dd -s default -m mon -s monitor $ AUDIODEVICE=snd/0.monitor cava ``` - +### squeezelite +[squeezelite](https://en.wikipedia.org/wiki/Squeezelite) is one of several software clients available for the Logitech Media Server. Squeezelite can export it's audio data as shared memory, which is what this input module uses. +Configure C.A.V.A. with the `--enable-shmem` option, then adapt your config: +``` +method = shmem +source = /squeezelite-AA:BB:CC:DD:EE:FF +``` +where `AA:BB:CC:DD:EE:FF` is squeezelite's MAC address (check the LMS Web GUI (Settings>Information) if unsure). +Note: squeezelite must be started with the `-v` flag to enable visualizer support. Running via ssh --------------- diff --git a/cava.c b/cava.c index f7727d2..7103bc2 100644 --- a/cava.c +++ b/cava.c @@ -63,6 +63,10 @@ #include "input/sndio.c" #endif +#ifdef SHMEM +#include "input/shmem.c" +#endif + #include #include "config.h" @@ -327,6 +331,9 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co #ifdef SNDIO strcat(supportedInput,", 'sndio'"); #endif + #ifdef SHMEM + strcat(supportedInput,", 'shmem'"); + #endif //fft: planning to rock fftw_complex outl[M / 2 + 1]; @@ -441,6 +448,13 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co } #endif + #ifdef SHMEM + if (p.im == 5) { + thr_id = pthread_create(&p_thread, NULL, input_shmem, (void*)&audio); + //audio.rate = 44100; + } + #endif + if (p.highcf > audio.rate / 2) { cleanup(); fprintf(stderr, diff --git a/config.c b/config.c index 050ee28..b886900 100644 --- a/config.c +++ b/config.c @@ -160,6 +160,13 @@ if (strcmp(inputMethod, "sndio") == 0) { return false; #endif } +if (strcmp(inputMethod, "shmem") == 0) { + p->im = 5; + #ifndef SHMEM + write_errorf(error, "cava was built without shmem support\n"); + return false; + #endif +} if (p->im == 0) { write_errorf(error, "input method '%s' is not supported, supported methods are: %s\n", inputMethod, supportedInput); @@ -480,6 +487,12 @@ if (strcmp(inputMethod, "sndio") == 0) { p->audio_source = (char *)iniparser_getstring(ini, "input:source", SIO_DEVANY); } #endif +#ifdef SHMEM +if (strcmp(inputMethod, "shmem") == 0) { + p->im = 5; + p->audio_source = (char *)iniparser_getstring(ini, "input:source", "/squeezelite-00:00:00:00:00:00"); +} +#endif return validate_config(supportedInput, params, error); //iniparser_freedict(ini); diff --git a/configure.ac b/configure.ac index a5eca4b..2f4602f 100644 --- a/configure.ac +++ b/configure.ac @@ -167,6 +167,24 @@ AS_IF([test "x$enable_legacy_iniparser" = "xyes"], [ CPPFLAGS="$CPPFLAGS -DLEGACYINIPARSER" ]) +dnl ############################ +dnl checking if shared memory input is enabled +dnl ############################ + +AC_ARG_ENABLE([shmem], + AS_HELP_STRING([--enable-shmem], + [enable shared memory input (e.g. for squeezelite)]) +) + +AS_IF([test "x$enable_shmem" = "xyes"], [ + dnl enabling shared memory input + AC_MSG_NOTICE([enabling shared memory input]) + CPPFLAGS="$CPPFLAGS -DSHMEM" + LIBS="$LIBS -lrt" +]) + + + dnl ############################ dnl Set font directory dnl ############################ diff --git a/example_files/config b/example_files/config index 91d52d7..14ddef5 100644 --- a/example_files/config +++ b/example_files/config @@ -37,7 +37,7 @@ [input] -# Audio capturing method. Possible methods are: 'pulse', 'alsa', 'fifo' or 'sndio'. +# Audio capturing method. Possible methods are: 'pulse', 'alsa', 'fifo', 'sndio' or 'shmem' # Defaults to 'pulse', 'alsa' or 'fifo', in that order, dependent on what support cava was built with. # # All input methods uses the same config variable 'source' @@ -48,6 +48,7 @@ # # For alsa 'source' will be the capture device. # For fifo 'source' will be the path to fifo-file. +# For shmem 'source' will be /squeezelite-AA:BB:CC:DD:EE:FF where 'AA:BB:CC:DD:EE:FF' will be squeezelite's MAC address ; method = pulse ; source = auto @@ -57,6 +58,8 @@ ; method = fifo ; source = /tmp/mpd.fifo +; method = shmem +; source = /squeezelite-AA:BB:CC:DD:EE:FF [output] diff --git a/input/shmem.c b/input/shmem.c new file mode 100644 index 0000000..ffaeacd --- /dev/null +++ b/input/shmem.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +typedef unsigned int u32_t; +typedef short s16_t; + + +// #define BUFSIZE 1024 +#define BUFSIZE 2048 + +int rc; + +#define VIS_BUF_SIZE 16384 +#define VB_OFFSET 8192+4096 + +typedef struct { + pthread_rwlock_t rwlock; + u32_t buf_size; + u32_t buf_index; + bool running; + u32_t rate; + time_t updated; + s16_t buffer[VIS_BUF_SIZE]; +} vis_t; + + +//input: SHMEM +void* input_shmem(void* data) +{ + struct audio_data *audio = (struct audio_data *)data; + vis_t *mmap_area; + int fd; /* file descriptor to mmaped area */ + int mmap_count = sizeof( vis_t); + int n = 0; + int i; + + printf("input_shmem: source: %s", audio->source); + + fd = shm_open(audio->source, O_RDWR, 0666); + + if (fd < 0 ) { + printf("Could not open source '%s': %s\n", audio->source, strerror( errno ) ); + exit(EXIT_FAILURE); + } else { + mmap_area = mmap(NULL, sizeof( vis_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if ((intptr_t)mmap_area == -1) { + printf("mmap failed - check if squeezelite is running with visualization enabled\n"); + exit(EXIT_FAILURE); + } + } + // printf("bufs: %u / run: %u / rate: %u\n",mmap_area->buf_size, mmap_area->running, mmap_area->rate); + audio->rate = mmap_area->rate; + + while (1) { + for (i = VB_OFFSET; i < BUFSIZE+VB_OFFSET; i += 2) { + if (audio->channels == 1) { + audio->audio_out_l[n] = (mmap_area->buffer[i] + mmap_area->buffer[i + 1]) / 2; + } else if (audio->channels == 2) { + audio->audio_out_l[n] = mmap_area->buffer[i]; + audio->audio_out_r[n] = mmap_area->buffer[i + 1]; + } + n++; + if (n == 2048 - 1) n = 0; + } + if (audio->terminate == 1) { + break; + } + } + + // cleanup + if ( fd > 0 ) { + if ( close( fd ) != 0 ) { + printf("Could not close file descriptor %d: %s", fd, strerror( errno ) ); + } + } else { + printf("Wrong file descriptor %d", fd ); + } + + if ( munmap( mmap_area, mmap_count ) != 0 ) { + printf("Could not munmap() area %p+%d. %s", mmap_area, mmap_count, strerror( errno ) ); + } + return 0; +}