summaryrefslogtreecommitdiff
path: root/src/wasp-soundboard.el
blob: a6db32e54111bd7999504b33e87e4192ed25b784 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
;;; wasp-soundboard --- On-stream soundboard -*- lexical-binding: t; -*-
;;; Commentary:
;;; Code:

(require 'wasp-utils)

(require 'f)

(defcustom w/sfx-play-process "wasp-sfx-play"
  "Name of process for playing audio with ffplay."
  :type '(string)
  :group 'wasp)

(defun w/sfx-glob (pat)
  "Find soundboard paths matching PAT."
  (let ((base (w/asset "soundboard")))
    (cond
      ((f-dir? (f-join base pat)) (f-entries (f-join base pat) #'f-file? t))
      (t (f-glob (s-concat pat "*") base)))))

(defun w/sfx-play (cands &optional filter)
  "Select and play a sound randomly from CANDS.
Optionally apply FILTER."
  (let ((path (w/pick-random cands)))
    (unless path (error "No matching clips"))
    (make-process
      :name w/sfx-play-process
      :buffer nil
      :command
      (print
      `( "ffplay" "-autoexit" "-nodisp"
         "-f" "lavfi"
         "-graph" ,(s-concat "amovie=" path (if (s-present? filter) (s-concat "," filter) ""))
         "dummy")))))

(cl-defstruct (w/sfx-state (:constructor w/make-sfx-state))
  cands
  stack
  filters)

(defun w/sfx-pop (st)
  "Pop an element from the stack of ST."
  (or (pop (w/sfx-state-stack st))
    (error "Stack underflow")))

(defun w/sfx-filter (st fil)
  "Add an FFMPEG audio filter given FIL in ST."
  (cond
    ((s-equals? fil "reverse") (push (cons "areverse" nil) (w/sfx-state-filters st)))
    ((s-equals? fil "tempo")
      (push (cons "atempo" (number-to-string (w/sfx-pop st)))
        (w/sfx-state-filters st)))
    (t
      (message "Unknown audio filter: %s" fil))))

(defun w/sfx-command (st cmd)
  "Evaluate CMD in the context of ST."
  (cond
    ((s-prefix? "!" cmd)
      (w/sfx-filter st (s-chop-prefix "!" cmd)))
    ((or (s-equals? cmd "0") (not (= 0 (string-to-number cmd))))
      (push (string-to-number cmd) (w/sfx-state-stack st)))
    (t
      (setf (w/sfx-state-cands st)
        (-concat (w/sfx-glob cmd) (w/sfx-state-cands st))))))

(defun w/sfx (cmds)
  "Evaluate the sound effect CMDS."
  (condition-case err
    (let ((st (w/make-sfx-state)))
      (--each (s-split " " cmds t)
        (message "Command: %s" it)
        (w/sfx-command st it))
      (w/sfx-play (w/sfx-state-cands st)
        (s-join ","
          (--map
            (s-concat (car it) (if (cdr it) (s-concat "=" (cdr it)) ""))
            (reverse (w/sfx-state-filters st)))))
      nil)
    (error err)))

(provide 'wasp-soundboard)
;;; wasp-soundboard.el ends here