Fork me on GitHub
#clojurescript
<
2021-03-21
>
sova-soars-the-sora00:03:13

Hi all! I want to capture microphone input with clojurescript.

sova-soars-the-sora00:03:12

I wonder if there are any existing examples. I have not found any yet.

raspasov01:03:19

Anything related to audio/video input will most likely be all about JavaScript, so I would search JS tutorials/resources and use CLJS interop.

athomasoriginal02:03:34

I feel that demo might have a bug in it, but perhaps it can help you on your journey 🙏

tomrbowden13:03:28

I've created a minimal example, based on MDN's MediaStream Recording API: https://developer.mozilla.org/en-US/docs/Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API

(ns web-audio.app.core
  (:require [reagent.core :as r]
            [reagent.dom :as rdom]))

(defn log [x]
  (.log js/console x))

(defonce chunks (r/atom []))

(defn app []
  (let [nav (.-mediaDevices js/navigator)
        gum (.-getUserMedia nav)
        cs  (clj->js {:audio true})
        rec-ref (atom nil)
        stp-ref (atom nil)
        _ (if gum
            (-> (.getUserMedia nav cs)
                (.then (fn [stream]
                         (let [mr (js/MediaRecorder. stream)
                               rh #(do (.start mr)
                                       (log (.-state mr))
                                       (log "recorder started"))
                               sh #(do (.stop mr)
                                       (log (.-state mr))
                                       (log "recorder stopped"))
                               stop-mr (fn []
                                         (let [aud (.createElement js/document "audio")
                                               _ (.setAttribute aud "controls" "")
                                               _ (set! (.-controls aud) true)
                                               _ (.appendChild (.-body js/document) aud)
                                               attr (clj->js {"type" "audio/ogg; codecs=opus"})
                                               blob (js/Blob. @chunks attr)
                                               audio-url (.createObjectURL (.-URL js/window) blob)]
                                           (log "media recorder stopped")
                                           (reset! chunks [])
                                           (set! (.-src aud) audio-url)))
                               _ (set! (.-ondataavailable mr)
                                       (fn [e]
                                         (swap! chunks conj (.-data e))))]
                           (set! (.-onclick @rec-ref) rh)
                           (set! (.-onclick @stp-ref) sh)
                           (set! (.-onstop mr) stop-mr))))
                (.catch (fn [err] (log err))))
            (log "getUserMedia not supported on your browser!"))]
    (fn []
      [:div
       [:h1 "Web Audio Recording"]
       [:button {:ref #(reset! rec-ref %)} "Record"]
       [:button {:ref #(reset! stp-ref %)} "Stop"]])))

(defn render []
  (rdom/render [app] (.getElementById js/document "root")))

(defn ^:export main []
  (render))

(defn ^:dev/after-load reload! []
  (render))

tomrbowden13:03:41

@U3ES97LAC From the example above, it's just a lot of JS interop, setting up a few event listeners manually (ie. not via React) since they need to be embedded in the Promise returned by getUserMedia. You may find it simpler to use a library such as https://github.com/closeio/mic-recorder-to-mp3 , which I've used before and like.

borkdude16:03:50

I am trying to support the syntax foo.bar.baz for JS objects stored in vars (don't exist at runtime, I know) or locals, in clj-kondo.

cljs.user=> (def foo #js {:foo #js {:bar 1}})
#'cljs.user/foo
cljs.user=> foo.bar
nil
cljs.user=> foo.foo.bar
1
I run into some issues with goog as a special case:
cljs.user=> (some? goog.global)
true
cljs.user=> (some? goog)
                   ^
WARNING: Use of undeclared Var cljs.user/goog at line 1
false
What is goog, why can you do goog.global, but not goog instead of js/goog? Is there a special list of goog.{global, ...} things that are supported whereas other goog.foobars are not supported?

borkdude16:03:47

Or is goog.foobar special syntactic sugar for js/goog.foobar, while goog itself isn't treated as js/goog?

thheller16:03:33

@borkdude symbols with dots are somewhat of a weird case in the compiler. goog is the root object of the closure library provided by the goog/base.js file. it is implicit and always exists as a namespace alias

borkdude16:03:07

Ah, so goog is an alias, interesting.

borkdude16:03:49

so if goog is an alias, why can I do goog.global but not:

cljs.user=> (require '[clojure.set :as set])
nil
cljs.user=> set.union
Execution error (ReferenceError) at (<cljs repl>:1).
Can't find variable: set

thheller16:03:49

well as I said it is weird and buggy if you ask me but the basic logic for symbols with a . is to just treat them as regular JS lookups

thheller16:03:05

so goog.global will end up as goog.global in the JS code, which exists provided by the goog/base.js

thheller16:03:23

set.union will also end up as set.union which doesn't exist

thheller16:03:11

the alias is only resolved if you use set/union

thheller16:03:22

goog is just an alias for itself so it happens to work there

borkdude16:03:35

which also works on goog:

cljs.user=> (some? goog/global)
true

borkdude16:03:03

goog is an alias on itself... good to remember

jaide23:03:21

What are people using to componentize their CSS these days?