summaryrefslogtreecommitdiff
path: root/src/wasp-audio.el
blob: eab8eb3660949921b74a6142963c9d88b8eaf489 (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
;;; wasp-audio --- On-stream audio input and output -*- lexical-binding: t; -*-
;;; Commentary:
;;; Code:

(require 'wasp-utils)

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

(defcustom w/transcribe-process "wasp-transcribe"
  "Name of process for transcribing speech using the Whisper API."
  :type '(string)
  :group 'wasp)

(defcustom w/transcribe-buffer " *wasp-transcribe*"
  "Name of buffer used to store transcription output."
  :type '(string)
  :group 'wasp)

(defcustom w/transcribe-error-buffer " *wasp-transcribe-error*"
  "Name of buffer used to store transcription errors."
  :type '(string)
  :group 'wasp)

(defcustom w/stream-transcribe-buffer " *wasp-fake-chat-transcribe*"
  "Name of buffer used to store stream transcription output."
  :type '(string)
  :group 'wasp)

(defcustom w/stream-transcribe-error-buffer " *wasp-fake-chat-transcribe-error*"
  "Name of buffer used to store fake chat transcription errors."
  :type '(string)
  :group 'wasp)

(defvar w/current-stream-transcribe-process nil)
(defvar w/last-stream-transcription "")
(defvar w/stream-keep-transcribing t)
(defvar w/stream-transcribe-voice-commands nil)

(defun w/tts (msg)
  "Use TTS to say MSG."
  (start-process "wasp-tts" nil "say" (w/tempfile "wasp-tts" msg)))

(defun w/play-audio (clip &optional k volume)
  "Play CLIP using mpv.
Call K when done.
If VOLUME is specified, use it to adjust the volume (100 is default)."
  (make-process
   :name w/play-audio-process
   :buffer nil
   :command
   (list
    "mpv" "--ao=alsa" "--no-video"
    (format "--volume=%s" (or volume 100))
    clip)
   :sentinel
   (lambda (_ _)
     (when k
       (funcall k)))))

(defun w/stop-all-audio ()
  "Stop all audio by killing mpv processes."
  (interactive)
  (start-process "pkill" nil "pkill" "mpv"))

(defun w/recorded-chatter-name? (user)
  "Return non-nil if we've recorded USER's name."
  (f-exists?
   (w/asset (s-concat "rats/users/" user ".wav"))))
(defun w/say-chatter-name (user &optional volume k)
  "Pronounce USER's name in using mpv.
Call K when done.
If VOLUME is specified, use it :)."
  (w/play-audio (w/asset (s-concat "rats/users/" user ".wav")) k volume))

(defun w/multipart-audio-helper (user rest &optional uservol clipvol)
  "Player all of the files in REST intercalated with saying USER's name.
Adjust volumes by USERVOL and CLIPVOL."
  (when (car rest)
    (w/play-audio
     (car rest)
     (when (cdr rest)
       (lambda ()
         (w/say-chatter-name
          user uservol
          (lambda () (w/multipart-audio-helper user (cdr rest) uservol clipvol)))))
     clipvol)))

(defun w/audio-rats-rats-we-are-the-rats (user)
  "Rats rats we are the rats.
Celebrating yet another birthday bash.
USER it's your birthday today."
  (let ((parts (--map (w/asset (format "rats/rats/rats%d.ogg" (+ it 1))) (-iota 3))))
    (w/multipart-audio-helper user parts 120 100)))
(defun w/audio-rambling-sub-thanks (user)
  "Say the thing about USER."
  (let ((parts (--map (w/asset (format "rats/new/part%d.wav" it)) (-iota 5))))
    (w/multipart-audio-helper user parts 120 150)))
(defun w/thank-sub (user)
  "Thank USER for their subscription user a randomized thanking technique."
  (let* ((thankers-named
          '(w/audio-rats-rats-we-are-the-rats
            w/audio-rambling-sub-thanks))
         (thankers-unnamed
          '((lambda (_) (w/play-audio (w/asset "rats/sam.wav") nil 90))
            (lambda (_) (w/play-audio (w/asset "rats/tyumici.mp3") nil 150))
            (lambda (_) (w/play-audio (w/asset "rats/abuffseagull.flac") nil 150))
            (lambda (_) (w/play-audio (w/asset "rats/unrecorded.wav") nil 150))
            ))
         (thanker
          (w/pick-random
           (-concat
            (when (w/recorded-chatter-name? user)
              thankers-named)
            thankers-unnamed))))
    (funcall thanker user)))

(defvar-local w/transcribe-callback nil)
(defun w/begin-transcribe (k)
  "Start recording audio to transcribe, passing the result to K."
  (let ((buf (generate-new-buffer w/transcribe-buffer)))
    (with-current-buffer buf
      (setq-local w/transcribe-callback k)
      (erase-buffer))
    (message "Transcribing...")
    (make-process
     :name w/transcribe-process
     :buffer buf
     :command (list "transcribe")
     :stderr (get-buffer-create w/transcribe-error-buffer)
     :sentinel
     (lambda (_ _)
       (with-current-buffer buf
         (funcall w/transcribe-callback (buffer-string)))))))
(defun w/end-transcribe ()
  "Finish recording transcription audio."
  (interactive)
  (message "End of transcription")
  (start-process "pkill" nil "pkill" "parecord")
  nil)

(defun w/handle-stream-transcribe ()
  "Start recording audio to transcribe."
  (unless w/current-stream-transcribe-process
    (with-current-buffer (get-buffer-create w/stream-transcribe-buffer)
      (erase-buffer))
    (setq
     w/current-stream-transcribe-process
     (make-process
      :name "fig-fake-chat-transcribe"
      :buffer (get-buffer-create w/stream-transcribe-buffer)
      :command (list "transcribe")
      :stderr (get-buffer-create w/stream-transcribe-error-buffer)
      :sentinel
      (lambda (_ _)
        (setq w/current-stream-transcribe-process nil)
        (with-current-buffer (get-buffer-create w/stream-transcribe-buffer)
          (w/daily-log (format "[VOICE]: %s" (buffer-string)))
          (setq w/last-stream-transcription (buffer-string))
          (--each w/stream-transcribe-voice-commands
            (when (s-contains? (car it) (s-downcase w/last-stream-transcription))
              (funcall (cdr it)))))
        (when w/stream-keep-transcribing
          (w/handle-stream-transcribe)))))))

(defun w/handle-stream-end-transcribe ()
  "Stop recording audio to transcribe."
  (when w/current-stream-transcribe-process
    (start-process "pkill" nil "pkill" "parecord")))

(defvar w/stream-transcribe-timer nil)
(defun w/run-stream-transcribe-timer ()
  "Run the fake chatter transcription timer."
  (when w/stream-transcribe-timer
    (cancel-timer w/stream-transcribe-timer))
  (w/handle-stream-end-transcribe)
  (setq
   w/stream-transcribe-timer
   (run-with-timer 10 nil #'w/run-stream-transcribe-timer)))

(defun w/start-stream-transcribe ()
  "Start transcribing speech for fake chatters."
  (interactive)
  (setq w/stream-keep-transcribing t)
  (w/handle-stream-transcribe))
(defun w/stop-stream-transcribe ()
  "Stop transcribing speech for fake chatters."
  (interactive)
  (setq w/stream-keep-transcribing nil)
  (w/handle-stream-end-transcribe))

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