diff --git a/.gitignore b/.gitignore
index 9baecf6..981c569 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-compile.sh
libyammer.so
config.scm
*.swp
diff --git a/README.md b/README.md
index 843e9dd..461d1b4 100644
--- a/README.md
+++ b/README.md
@@ -6,17 +6,30 @@ Audio visualizer thing in guile scheme
`guile-sdl2`, `fftw`
## 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
-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
-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
-* `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
* `sample-factor`: Proportional to quality, may cause stuttering when >1.
* `fps`: Self explanatory
@@ -34,7 +47,6 @@ Below is a list of values and what they do
* `'exponential`: Simple (basic) exponential smoothing
* `moving-average-block-size`: Size of the moving average window. Higher is smoother.
* `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
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)
# Wishlist
-* Native PulseAudio and PipeWire sources
+* Native PipeWire, ALSA, JACK, sndio sources
* More smoothing modes (one that actually works would be nice)
* Average over `resolution`
* Offload some calculations to the GPU
+* Use more than one core lmao?
# Known issues
* 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
* The surface can't be transparent
+* PulseAudio can be reeaaaaally behind
diff --git a/compile.sh b/compile.sh
new file mode 100755
index 0000000..acfc21d
--- /dev/null
+++ b/compile.sh
@@ -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"
diff --git a/config.example.scm b/config.example.scm
index ed40889..e77fc36 100644
--- a/config.example.scm
+++ b/config.example.scm
@@ -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 sample-factor 2)
(define fps 30)
@@ -12,4 +17,3 @@
(define smoothing-mode 'none)
(define moving-average-block-size 25)
(define exponential-factor 0.5)
-(define exponential-init-factor 10)
diff --git a/yammer.c b/yammer.c
index 9f88527..f7b5de1 100644
--- a/yammer.c
+++ b/yammer.c
@@ -17,9 +17,9 @@
* Yammer. If not, see .
*
* @file yammer.c
- * @brief Discrete Fourier Transform wrapper
- * @details Wrapper around FFTW's real-to-complex (r2c) discrete fourier
- * transform
+ * @brief C library for Yammer
+ * @details Library that wraps around other C libraries for easier access in
+ * Guile.
*/
#include "yammer.h"
@@ -27,7 +27,8 @@
#include
#include
-#include
+
+#ifdef USE_FFTW
/**
* @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 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
diff --git a/yammer.h b/yammer.h
index 8efdc65..1a77c9d 100644
--- a/yammer.h
+++ b/yammer.h
@@ -23,9 +23,16 @@
#ifndef _YAMMER_H_
#define _YAMMER_H_
+#ifdef USE_FFTW
#include
-
fftw_plan init_plan(int vsize);
short int* do_dft(fftw_plan plan, short int* ins, int len);
+#endif // USE_FFTW
+
+#ifdef USE_PULSE
+#include
+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_
diff --git a/yammer.scm b/yammer.scm
index efbd9c2..70e973e 100755
--- a/yammer.scm
+++ b/yammer.scm
@@ -37,24 +37,31 @@
#:use-module (scheme base)
#:use-module (srfi srfi-1)
#:use-module (system foreign)
- #:use-module (yammer fftw))
+ #:use-module (yammer ffi))
-;; Add this dir to load path, for config
-(add-to-load-path ".")
-(load "config.scm")
+;; Add this config dir to load path
+(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 widely used constants in relation to config values
(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)
(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
(define window #f)
(define renderer #f)
-(define bv (make-bytevector sample-size))
+(define bv (make-list (/ sample-size 2) 0))
(define queue (make-q))
;; Turns a list lst into a list of pairs (chunks of 2)
@@ -75,7 +82,7 @@
;; Read from the MPD FIFO
(define (read-fifo fd)
(if (port-closed? fd)
- (set! fd (open-fifo fifo-file)))
+ (set! fd (open-fifo source)))
(define new-bv #vu8())
(define bytes-read 0)
(while (< bytes-read sample-size)
@@ -86,6 +93,17 @@
new-bv (get-bytevector-n
fd (- sample-size bytes-read))))
(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)
(if (>= (q-length queue) queue-size)
(set! bv (deq! queue))))
@@ -168,7 +186,7 @@
(do ((i 0 (1+ i)))
((>= i (length input)))
(if (= i 0)
- (set! ret (list (* exponential-init-factor (car input))))
+ (set! ret (list (car input)))
(set! ret
(append
ret
@@ -190,14 +208,18 @@
;; Initialize fps sleep to touch later
(set! sleep-future (future (usleep (round (* recip-fps 1000)))))
;; 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
;; Chunk the bytevector
- (set! plst (chunk2 (bytevector->sint-list
- bv (native-endianness) 2)))
+ (set! plst (chunk2 bv))
;; Read again
- (set! read-future (future (read-fifo fd)))
- (if fft?
+ (cond
+ ((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
(if (not (equal? smoothing-mode #f))
(draw ren
@@ -226,7 +248,13 @@
(touch sleep-future)))
;; 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
(set! window (make-window #:title "Yammer")) ;; Create window
@@ -238,6 +266,11 @@
(delete-renderer! renderer) ;; Free renderer on close
(close-window! window) ;; Close window
(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)))
diff --git a/yammer/ffi.scm b/yammer/ffi.scm
new file mode 100644
index 0000000..5d0c4dd
--- /dev/null
+++ b/yammer/ffi.scm
@@ -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 '*)))))
diff --git a/yammer/fftw.scm b/yammer/fftw.scm
deleted file mode 100644
index e58f072..0000000
--- a/yammer/fftw.scm
+++ /dev/null
@@ -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))