Big refactor for pulseaudio addition
Signed-off-by: Skylar "The Cobra" Widulski <cobra@vern.cc>
This commit is contained in:
parent
48e99e057f
commit
ed890c469b
|
@ -1,4 +1,3 @@
|
||||||
compile.sh
|
|
||||||
libyammer.so
|
libyammer.so
|
||||||
config.scm
|
config.scm
|
||||||
*.swp
|
*.swp
|
||||||
|
|
26
README.md
26
README.md
|
@ -6,17 +6,30 @@ Audio visualizer thing in guile scheme
|
||||||
`guile-sdl2`, `fftw`
|
`guile-sdl2`, `fftw`
|
||||||
|
|
||||||
## Compiling the library
|
## Compiling the library
|
||||||
You need to compile the yammer.c file because it has wrappers around fftw functions.
|
You need to compile the yammer.c file because it has wrappers and stuff.
|
||||||
|
|
||||||
|
To disable FFTW, use `export NO_FFTW=1`
|
||||||
|
|
||||||
|
To disable PulseAudio, use `export NO_PULSE=1`
|
||||||
|
|
||||||
|
To enable debugging symbols, use `export DEBUG=1`
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
gcc -shared yammer.c -o libyammer.so -Wall -Wextra -Werror -fPIC -shared -lfftw3 -lm -O3 -ggdb
|
./compile.sh
|
||||||
```
|
```
|
||||||
|
You may need to adjust `PKG_CONFIG_PATH` if you use something like Guix System.
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
Copy `config.example.scm` to `config.scm`
|
Copy `config.example.scm` to `~/.config/yammer/config.scm`
|
||||||
|
|
||||||
Below is a list of values and what they do
|
Below is a list of values and what they do
|
||||||
|
|
||||||
* `fifo-file`: The MPD fifo
|
* `using-fftw?`: Was libyammer.so compiled using FFTW?
|
||||||
|
* `using-pulse?`: Was libyammer.so compiled using PulseAudio?
|
||||||
|
|
||||||
|
* `source`: The MPD fifo or PulseAudio source
|
||||||
|
* `source-type`: One of `'mpd` or `'pulse`
|
||||||
|
* `pulse-latency`: Latency to request from PulseAudio
|
||||||
* `bitrate`: The bitrate of the audio coming from the input
|
* `bitrate`: The bitrate of the audio coming from the input
|
||||||
* `sample-factor`: Proportional to quality, may cause stuttering when >1.
|
* `sample-factor`: Proportional to quality, may cause stuttering when >1.
|
||||||
* `fps`: Self explanatory
|
* `fps`: Self explanatory
|
||||||
|
@ -34,7 +47,6 @@ Below is a list of values and what they do
|
||||||
* `'exponential`: Simple (basic) exponential smoothing
|
* `'exponential`: Simple (basic) exponential smoothing
|
||||||
* `moving-average-block-size`: Size of the moving average window. Higher is smoother.
|
* `moving-average-block-size`: Size of the moving average window. Higher is smoother.
|
||||||
* `exponential-factor`: Exponential smoothing factor, lower is more smooth.
|
* `exponential-factor`: Exponential smoothing factor, lower is more smooth.
|
||||||
* `exponential-init-factor`: The first value of the exponentially smoothed list is multiplied by this
|
|
||||||
|
|
||||||
# Running
|
# Running
|
||||||
Either install libyammer.so and yammer.h to their respective systemwide locations, or do the following:
|
Either install libyammer.so and yammer.h to their respective systemwide locations, or do the following:
|
||||||
|
@ -63,12 +75,14 @@ guile -L . yammer.scm
|
||||||
![Moving Average FFT Mode](http://git.vern.cc/cobra/yammer/raw/branch/main/screenshots/moving-average-fft.png)
|
![Moving Average FFT Mode](http://git.vern.cc/cobra/yammer/raw/branch/main/screenshots/moving-average-fft.png)
|
||||||
|
|
||||||
# Wishlist
|
# Wishlist
|
||||||
* Native PulseAudio and PipeWire sources
|
* Native PipeWire, ALSA, JACK, sndio sources
|
||||||
* More smoothing modes (one that actually works would be nice)
|
* More smoothing modes (one that actually works would be nice)
|
||||||
* Average over `resolution`
|
* Average over `resolution`
|
||||||
* Offload some calculations to the GPU
|
* Offload some calculations to the GPU
|
||||||
|
* Use more than one core lmao?
|
||||||
|
|
||||||
# Known issues
|
# Known issues
|
||||||
* Sometimes the read gets misaligned and just becomes noise
|
* Sometimes the read gets misaligned and just becomes noise
|
||||||
* When MPD is paused or the program is sleeping for the next frame, the UI doesn't update so you can't exit
|
* When MPD is paused or the program is sleeping for the next frame, the UI doesn't update so you can't exit
|
||||||
* The surface can't be transparent
|
* The surface can't be transparent
|
||||||
|
* PulseAudio can be reeaaaaally behind
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
LDFLAGS="$(pkg-config --cflags --libs ${NO_FFTW:-fftw3} ${NO_PULSE:-libpulse-simple})"
|
||||||
|
CFLAGS="-Wall -Wextra -Werror -fPIC -O3 ${DEBUG:+-ggdb}"
|
||||||
|
|
||||||
|
gcc_invoke="gcc ${NO_FFTW:--DUSE_FFTW} ${NO_PULSE:--DUSE_PULSE} -shared yammer.c -o libyammer.so ${CFLAGS} -shared ${LDFLAGS}"
|
||||||
|
|
||||||
|
eval "$gcc_invoke"
|
|
@ -1,4 +1,9 @@
|
||||||
(define fifo-file "/tmp/mpd.fifo")
|
(define using-fftw? #t)
|
||||||
|
(define using-pulse? #t)
|
||||||
|
|
||||||
|
(define source "/tmp/mpd.fifo")
|
||||||
|
(define source-type 'mpd)
|
||||||
|
(define pulse-latency 10)
|
||||||
(define bitrate 44100)
|
(define bitrate 44100)
|
||||||
(define sample-factor 2)
|
(define sample-factor 2)
|
||||||
(define fps 30)
|
(define fps 30)
|
||||||
|
@ -12,4 +17,3 @@
|
||||||
(define smoothing-mode 'none)
|
(define smoothing-mode 'none)
|
||||||
(define moving-average-block-size 25)
|
(define moving-average-block-size 25)
|
||||||
(define exponential-factor 0.5)
|
(define exponential-factor 0.5)
|
||||||
(define exponential-init-factor 10)
|
|
||||||
|
|
40
yammer.c
40
yammer.c
|
@ -17,9 +17,9 @@
|
||||||
* Yammer. If not, see <https://www.gnu.org/licenses/>.
|
* Yammer. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
* @file yammer.c
|
* @file yammer.c
|
||||||
* @brief Discrete Fourier Transform wrapper
|
* @brief C library for Yammer
|
||||||
* @details Wrapper around FFTW's real-to-complex (r2c) discrete fourier
|
* @details Library that wraps around other C libraries for easier access in
|
||||||
* transform
|
* Guile.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "yammer.h"
|
#include "yammer.h"
|
||||||
|
@ -27,7 +27,8 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include <fftw3.h>
|
|
||||||
|
#ifdef USE_FFTW
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initializes the FFTW plan
|
* @brief Initializes the FFTW plan
|
||||||
|
@ -96,3 +97,34 @@ short int* do_dft(fftw_plan plan, short int* ins, int len) {
|
||||||
// Return the output array
|
// Return the output array
|
||||||
return outs;
|
return outs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // USE_FFTW
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_PULSE
|
||||||
|
|
||||||
|
pa_simple* make_pa_simple(unsigned int bitrate, char* dev, int latency) {
|
||||||
|
pa_sample_spec ss = {
|
||||||
|
.format = PA_SAMPLE_S16NE,
|
||||||
|
.channels = 2,
|
||||||
|
.rate = bitrate
|
||||||
|
};
|
||||||
|
|
||||||
|
pa_buffer_attr attr;
|
||||||
|
attr.maxlength = (uint32_t) -1;
|
||||||
|
attr.tlength = (uint32_t) -1;
|
||||||
|
attr.prebuf = (uint32_t) -1;
|
||||||
|
attr.minreq = (uint32_t) -1;
|
||||||
|
attr.fragsize = latency;
|
||||||
|
|
||||||
|
return pa_simple_new(NULL, "Yammer", PA_STREAM_RECORD, dev,
|
||||||
|
"Audio Visualizer", &ss, NULL, &attr, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
short int* read_from_pa(pa_simple* s, size_t bytes) {
|
||||||
|
short int* buf = malloc(sizeof(short int) * bytes);
|
||||||
|
pa_simple_read(s, buf, sizeof(short int) * bytes, NULL);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_PULSE
|
||||||
|
|
9
yammer.h
9
yammer.h
|
@ -23,9 +23,16 @@
|
||||||
#ifndef _YAMMER_H_
|
#ifndef _YAMMER_H_
|
||||||
#define _YAMMER_H_
|
#define _YAMMER_H_
|
||||||
|
|
||||||
|
#ifdef USE_FFTW
|
||||||
#include <fftw3.h>
|
#include <fftw3.h>
|
||||||
|
|
||||||
fftw_plan init_plan(int vsize);
|
fftw_plan init_plan(int vsize);
|
||||||
short int* do_dft(fftw_plan plan, short int* ins, int len);
|
short int* do_dft(fftw_plan plan, short int* ins, int len);
|
||||||
|
#endif // USE_FFTW
|
||||||
|
|
||||||
|
#ifdef USE_PULSE
|
||||||
|
#include <pulse/simple.h>
|
||||||
|
pa_simple* make_pa_simple(unsigned int bitrate, char* dev, int latency);
|
||||||
|
short int* read_from_pa(pa_simple* s, size_t bytes);
|
||||||
|
#endif // USE_PULSE
|
||||||
|
|
||||||
#endif // _YAMMER_H_
|
#endif // _YAMMER_H_
|
||||||
|
|
67
yammer.scm
67
yammer.scm
|
@ -37,24 +37,31 @@
|
||||||
#:use-module (scheme base)
|
#:use-module (scheme base)
|
||||||
#:use-module (srfi srfi-1)
|
#:use-module (srfi srfi-1)
|
||||||
#:use-module (system foreign)
|
#:use-module (system foreign)
|
||||||
#:use-module (yammer fftw))
|
#:use-module (yammer ffi))
|
||||||
|
|
||||||
;; Add this dir to load path, for config
|
;; Add this config dir to load path
|
||||||
(add-to-load-path ".")
|
(add-to-load-path
|
||||||
(load "config.scm")
|
(string-append
|
||||||
|
(if (getenv "XDG_CONFIG_HOME")
|
||||||
|
(getenv "XDG_CONFIG_HOME")
|
||||||
|
(string-append (getenv "HOME") "/.config"))
|
||||||
|
"/yammer"))
|
||||||
|
(load-from-path "config.scm")
|
||||||
|
|
||||||
;; Define widely used constants in relation to config values
|
;; Define widely used constants in relation to config values
|
||||||
(define recip-fps (/ 1 fps))
|
(define recip-fps (/ 1 fps))
|
||||||
(define sample-size (* 4 (round (* sample-factor (* (/ bitrate 4) recip-fps)))))
|
(define sample-size
|
||||||
|
(exact (* 4 (round (* sample-factor (* (/ bitrate 4) recip-fps))))))
|
||||||
|
|
||||||
;; Plan definition (if using fft)
|
;; Plan definition (if using fft)
|
||||||
(define plan #f)
|
(define plan #f)
|
||||||
(if fft (monitor (set! plan (init_plan (exact (/ sample-size 4))))))
|
(if (and fft using-fftw?)
|
||||||
|
(monitor (set! plan (init_plan (exact (/ sample-size 4))))))
|
||||||
|
|
||||||
;; Initialize more values
|
;; Initialize more values
|
||||||
(define window #f)
|
(define window #f)
|
||||||
(define renderer #f)
|
(define renderer #f)
|
||||||
(define bv (make-bytevector sample-size))
|
(define bv (make-list (/ sample-size 2) 0))
|
||||||
(define queue (make-q))
|
(define queue (make-q))
|
||||||
|
|
||||||
;; Turns a list lst into a list of pairs (chunks of 2)
|
;; Turns a list lst into a list of pairs (chunks of 2)
|
||||||
|
@ -75,7 +82,7 @@
|
||||||
;; Read from the MPD FIFO
|
;; Read from the MPD FIFO
|
||||||
(define (read-fifo fd)
|
(define (read-fifo fd)
|
||||||
(if (port-closed? fd)
|
(if (port-closed? fd)
|
||||||
(set! fd (open-fifo fifo-file)))
|
(set! fd (open-fifo source)))
|
||||||
(define new-bv #vu8())
|
(define new-bv #vu8())
|
||||||
(define bytes-read 0)
|
(define bytes-read 0)
|
||||||
(while (< bytes-read sample-size)
|
(while (< bytes-read sample-size)
|
||||||
|
@ -86,6 +93,17 @@
|
||||||
new-bv (get-bytevector-n
|
new-bv (get-bytevector-n
|
||||||
fd (- sample-size bytes-read))))
|
fd (- sample-size bytes-read))))
|
||||||
(set! bytes-read (bytevector-length new-bv)))
|
(set! bytes-read (bytevector-length new-bv)))
|
||||||
|
(enq! queue (bytevector->sint-list new-bv (native-endianness) 2))
|
||||||
|
(if (>= (q-length queue) queue-size)
|
||||||
|
(set! bv (deq! queue))))
|
||||||
|
|
||||||
|
(define (read-pa pa)
|
||||||
|
(define new-bv
|
||||||
|
(bytevector->sint-list
|
||||||
|
(pointer->bytevector
|
||||||
|
(read_from_pa pa (/ sample-size 2))
|
||||||
|
(/ sample-size 2) (sizeof int16) 's16)
|
||||||
|
(native-endianness) 2))
|
||||||
(enq! queue new-bv)
|
(enq! queue new-bv)
|
||||||
(if (>= (q-length queue) queue-size)
|
(if (>= (q-length queue) queue-size)
|
||||||
(set! bv (deq! queue))))
|
(set! bv (deq! queue))))
|
||||||
|
@ -168,7 +186,7 @@
|
||||||
(do ((i 0 (1+ i)))
|
(do ((i 0 (1+ i)))
|
||||||
((>= i (length input)))
|
((>= i (length input)))
|
||||||
(if (= i 0)
|
(if (= i 0)
|
||||||
(set! ret (list (* exponential-init-factor (car input))))
|
(set! ret (list (car input)))
|
||||||
(set! ret
|
(set! ret
|
||||||
(append
|
(append
|
||||||
ret
|
ret
|
||||||
|
@ -190,14 +208,18 @@
|
||||||
;; Initialize fps sleep to touch later
|
;; Initialize fps sleep to touch later
|
||||||
(set! sleep-future (future (usleep (round (* recip-fps 1000)))))
|
(set! sleep-future (future (usleep (round (* recip-fps 1000)))))
|
||||||
;; In the meantime, check if the read bytevector is the right size
|
;; In the meantime, check if the read bytevector is the right size
|
||||||
(if (= (bytevector-length bv) sample-size)
|
(if (= (length bv) (/ sample-size 2))
|
||||||
(begin
|
(begin
|
||||||
;; Chunk the bytevector
|
;; Chunk the bytevector
|
||||||
(set! plst (chunk2 (bytevector->sint-list
|
(set! plst (chunk2 bv))
|
||||||
bv (native-endianness) 2)))
|
|
||||||
;; Read again
|
;; Read again
|
||||||
(set! read-future (future (read-fifo fd)))
|
(cond
|
||||||
(if fft?
|
((eq? source-type 'mpd)
|
||||||
|
(set! read-future (future (read-fifo fd))))
|
||||||
|
((eq? source-type 'pulse)
|
||||||
|
(set! read-future (future (read-pa pa-simple)))))
|
||||||
|
|
||||||
|
(if (and fft? using-fftw?)
|
||||||
;; FFF smoothing enabled check
|
;; FFF smoothing enabled check
|
||||||
(if (not (equal? smoothing-mode #f))
|
(if (not (equal? smoothing-mode #f))
|
||||||
(draw ren
|
(draw ren
|
||||||
|
@ -226,7 +248,13 @@
|
||||||
(touch sleep-future)))
|
(touch sleep-future)))
|
||||||
|
|
||||||
;; Initially open the MPD FIFO
|
;; Initially open the MPD FIFO
|
||||||
(define fd (open-fifo fifo-file))
|
(define fd #f)
|
||||||
|
(define pa-simple #f)
|
||||||
|
(cond
|
||||||
|
((eq? source-type 'mpd)
|
||||||
|
(set! fd (open-fifo source)))
|
||||||
|
((eq? source-type 'pulse)
|
||||||
|
(set! pa-simple (make-pa-simple bitrate source pulse-latency))))
|
||||||
|
|
||||||
(sdl-init '(video events)) ;; Initialize SDL2
|
(sdl-init '(video events)) ;; Initialize SDL2
|
||||||
(set! window (make-window #:title "Yammer")) ;; Create window
|
(set! window (make-window #:title "Yammer")) ;; Create window
|
||||||
|
@ -238,6 +266,11 @@
|
||||||
(delete-renderer! renderer) ;; Free renderer on close
|
(delete-renderer! renderer) ;; Free renderer on close
|
||||||
(close-window! window) ;; Close window
|
(close-window! window) ;; Close window
|
||||||
(sdl-quit) ;; Quit SDL2
|
(sdl-quit) ;; Quit SDL2
|
||||||
(close-port fd) ;; Close MPD FIFO
|
(cond
|
||||||
|
((eq? source-type 'mpd)
|
||||||
|
(close-port fd))
|
||||||
|
((eq? source-type 'pulse)
|
||||||
|
(pa_simple_free pa-simple)))
|
||||||
|
|
||||||
(if fft (monitor (fftw_destroy_plan plan))) ;; Free FFT plan if FFT is enabled
|
;; Free FFT plan if FFT is enabled
|
||||||
|
(if (and fft using-fftw?) (monitor (fftw_destroy_plan plan)))
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
(define-module (yammer ffi)
|
||||||
|
#:use-module (system foreign)
|
||||||
|
#:export (FFTW_ESTIMATE
|
||||||
|
fftw_destroy_plan
|
||||||
|
init_plan
|
||||||
|
do-dft
|
||||||
|
make-pa-simple
|
||||||
|
read_from_pa
|
||||||
|
pa_simple_free))
|
||||||
|
|
||||||
|
(add-to-load-path
|
||||||
|
(string-append
|
||||||
|
(if (getenv "XDG_CONFIG_HOME")
|
||||||
|
(getenv "XDG_CONFIG_HOME")
|
||||||
|
(string-append (getenv "HOME") "/.config"))
|
||||||
|
"/yammer"))
|
||||||
|
(load-from-path "config.scm")
|
||||||
|
|
||||||
|
(define fftw #f)
|
||||||
|
(define pulseaudio #f)
|
||||||
|
(define libyammer (dynamic-link "libyammer"))
|
||||||
|
|
||||||
|
(define FFTW_ESTIMATE #f)
|
||||||
|
(define fftw_destroy_plan #f)
|
||||||
|
(define do_dft #f)
|
||||||
|
(define init_plan #f)
|
||||||
|
(define (do-dft plan lst len) #f)
|
||||||
|
|
||||||
|
(define make_pa_simple #f)
|
||||||
|
(define (make-pa-simple rate dev) #f)
|
||||||
|
(define read_from_pa #f)
|
||||||
|
(define pa_simple_free #f)
|
||||||
|
|
||||||
|
(if using-fftw?
|
||||||
|
(begin
|
||||||
|
(set! fftw (dynamic-link "libfftw3"))
|
||||||
|
(set! FFTW_ESTIMATE 48)
|
||||||
|
|
||||||
|
(set! fftw_destroy_plan
|
||||||
|
(pointer->procedure void
|
||||||
|
(dynamic-func "fftw_destroy_plan" fftw)
|
||||||
|
(list '*)))
|
||||||
|
|
||||||
|
(set! do_dft
|
||||||
|
(pointer->procedure '*
|
||||||
|
(dynamic-func "do_dft" libyammer)
|
||||||
|
(list '* '* int)))
|
||||||
|
|
||||||
|
(set! init_plan
|
||||||
|
(pointer->procedure '*
|
||||||
|
(dynamic-func "init_plan" libyammer)
|
||||||
|
(list int)))
|
||||||
|
|
||||||
|
(set! do-dft
|
||||||
|
(lambda (plan lst len)
|
||||||
|
(define out (do_dft plan (bytevector->pointer (list->s16vector lst)) len))
|
||||||
|
(pointer->bytevector out (floor/ len 2) (sizeof short) 's16)))))
|
||||||
|
|
||||||
|
(if using-pulse?
|
||||||
|
(begin
|
||||||
|
(set! pulseaudio (dynamic-link "libpulse-simple"))
|
||||||
|
|
||||||
|
(set! make_pa_simple
|
||||||
|
(pointer->procedure '*
|
||||||
|
(dynamic-func "make_pa_simple" libyammer)
|
||||||
|
(list unsigned-int '* int)))
|
||||||
|
|
||||||
|
(set! make-pa-simple
|
||||||
|
(lambda (rate dev latency)
|
||||||
|
(make_pa_simple rate (string->pointer dev latency))))
|
||||||
|
|
||||||
|
(set! read_from_pa
|
||||||
|
(pointer->procedure '*
|
||||||
|
(dynamic-func "read_from_pa" libyammer)
|
||||||
|
(list '* size_t)))
|
||||||
|
|
||||||
|
(set! pa_simple_free
|
||||||
|
(pointer->procedure void
|
||||||
|
(dynamic-func "pa_simple_free" pulseaudio)
|
||||||
|
(list '*)))))
|
|
@ -1,37 +0,0 @@
|
||||||
(define-module (yammer fftw)
|
|
||||||
#:use-module (system foreign)
|
|
||||||
#:export (FFTW_ESTIMATE
|
|
||||||
fftw_plan_dft_r2c_1d
|
|
||||||
fftw_destroy_plan ;))
|
|
||||||
init_plan
|
|
||||||
do-dft))
|
|
||||||
|
|
||||||
(define fftw (dynamic-link "libfftw3"))
|
|
||||||
|
|
||||||
(define FFTW_ESTIMATE 48)
|
|
||||||
|
|
||||||
(define fftw_plan_dft_r2c_1d
|
|
||||||
(pointer->procedure '*
|
|
||||||
(dynamic-func "fftw_plan_dft_r2c_1d" fftw)
|
|
||||||
(list int '* '* unsigned-int)))
|
|
||||||
|
|
||||||
(define fftw_destroy_plan
|
|
||||||
(pointer->procedure void
|
|
||||||
(dynamic-func "fftw_destroy_plan" fftw)
|
|
||||||
(list '*)))
|
|
||||||
|
|
||||||
(define libyammer (dynamic-link "libyammer"))
|
|
||||||
|
|
||||||
(define do_dft
|
|
||||||
(pointer->procedure '*
|
|
||||||
(dynamic-func "do_dft" libyammer)
|
|
||||||
(list '* '* int)))
|
|
||||||
|
|
||||||
(define init_plan
|
|
||||||
(pointer->procedure '*
|
|
||||||
(dynamic-func "init_plan" libyammer)
|
|
||||||
(list int)))
|
|
||||||
|
|
||||||
(define (do-dft plan lst len)
|
|
||||||
(define out (do_dft plan (bytevector->pointer (list->s16vector lst)) len))
|
|
||||||
(pointer->bytevector out (floor/ len 2) (sizeof short) 's16))
|
|
Loading…
Reference in New Issue