From: Ralph Ronnquist Date: Sat, 29 Apr 2023 02:54:09 +0000 (+1000) Subject: some cleanup X-Git-Url: https://git.rrq.au/?a=commitdiff_plain;h=98563e2eb6f57ac720234fffa6b14a84a31004c5;p=rrq%2Fnewlisp%2Falsa-dispatcher.git some cleanup --- diff --git a/alsa-dispatcher.lsp b/alsa-dispatcher.lsp index deb557c..df8863b 100755 --- a/alsa-dispatcher.lsp +++ b/alsa-dispatcher.lsp @@ -9,35 +9,35 @@ (signal 2 (fn (x) (exit 0))) (signal 15 (fn (x) (exit 0))) +; Optionally print output to stderr and optionally exit with code (define (die N) (when (args) (write-line 2 (join (map string (args)) " "))) (when (number? N) (exit N))) -(define (enlist X) (if (list? X) X (list X))) +; Return non-nil for comment line (starts with # or is blank) +(define (comment? LINE) (regex "^\\s*(#|$)" LINE 0)) -(define (comment? LINE) (and (regex "^\\s*(#|$)" LINE 0) true)) +; Return list of space-separated words on a line +(define (words LINE) + (parse (trim LINE) "\\s+" 0)) -(define (first-word LINE) (and (regex "^\\s*(\\S+)" LINE 0) $1)) - -(define (prog1 X) X) - -(define (read-config-line LINE) - (map (fn (x) (if (regex "^([^=]+)=(.*)" x) (list $1 $2) x)) - (map trim (find-all "([^, ]+)" LINE $1 0)))) +; Read the configuration file and return it as list of plugs or the default +(define (read-config FILE) + (or + (if (read-file FILE) (map words (clean comment? (parse $it "\n")))) + '(("plughw")) ; the default priority list + )) ;; ############################################################ ;; Load Configuration (~/.alsa-dispatcher) ; Format: one-liners for each option, ignoring comment lines starting ; with # and blank lines. (constant - 'HOME (env "HOME") - 'CONFIG (format "%s/.alsa-dispatcher" HOME) - 'CFGLINES (if (read-file CONFIG) (clean comment? (parse $it "\n" )) - '("bt,latency=1000" "plughw")) - 'CFGMAP (map read-config-line CFGLINES) - 'PCM-LIST (map first-word CFGLINES) + 'CFGMAP (read-config (format "%s/.alsa-dispatcher" (env "HOME"))) + 'PCM-LIST (map first CFGMAP) ) +; Return value of configuration setting KEY for plug PCM, or DEFAULT. (define (cfg-lookup PCM KEY DEFAULT) (if (if (assoc PCM CFGMAP) (lookup KEY $it)) (read-expr $it) DEFAULT)) @@ -117,25 +117,22 @@ "long" ; snd_pcm_uframes_t size ) -;; Open a PCM by name +;; Open a PCM by name. Returns list of (NAME PCM) or nil. (define (open-pcm NAME) - (snd_pcm_open NAME SND_PCM_STREAM_PLAYBACK SND_PCM_MODE_BLOCK)) + (if (snd_pcm_open NAME SND_PCM_STREAM_PLAYBACK SND_PCM_MODE_BLOCK) + (list NAME $it))) -;; Setup PCM +;; Setup PCM. Preset format. access, channels, rate and soft resample. +;; Configurable latency. (define (setup-pcm PCM NAME) - (snd_pcm_set_params PCM - (cfg-lookup NAME "format" SND_PCM_FORMAT_S16_LE) - SND_PCM_ACCESS_RW_INTERLEAVED - 2 ; channels - 48000 ; rate - 1 ; soft resample (0/1) - (cfg-lookup NAME "latency" 100000) ; (microseconds) - )) + (let ((LATENCY (cfg-lookup NAME "latency" 100000))) + (snd_pcm_set_params PCM SND_PCM_FORMAT_S16_LE SND_PCM_ACCESS_RW_INTERLEAVED + 2 48000 1 LATENCY ))) ;; ############################################################ ; The main program -;; redirect stdout/err to /dev/null +; redirect stdout/err to /dev/null (flagged for debugging purposes) (when true (let ((REDIR (open "/dev/null" "append"))) (when (< REDIR) (exit 1)) @@ -145,27 +142,29 @@ )) ; find the first usable PCM -(setf PCM (unless (dolist (N PCM-LIST (open-pcm (setf NAME N)))) - (die 1 "No PCM available"))) +(map set '(NAME PCM) (unless (dolist (N PCM-LIST (open-pcm N))) + (die 1 "No PCM available"))) ; configure the PCM (when (!= (setf E (setup-pcm PCM NAME))) (snd_pcm_close PCM) (die 1 "setup pcm" E)) -(setf N 0 x 0 E 0 i 0) - -(while (> (or (setf N (read 0 BUFFER 1000000)) 0)) - (while (> N) +; channel stdin audio onto selected pcm +(while (> (or (setf N (read 0 BUFFER 2000000)) 0)) + (while (> N) + ; N is in bytes while snd_pcm_writei counts "frames" of 4 bytes (when (< (setf E (snd_pcm_writei PCM BUFFER (/ N 4)))) (snd_pcm_close PCM) - (die 1 "writing" E N)) + (die 1 "writing failed")) (setf E (* E 4)) (setf BUFFER (E BUFFER)) (dec N E) ) ) +; let the pcm complete its output (snd_pcm_drain PCM) + (snd_pcm_close PCM) (exit 0)