clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
sova-soars-the-sora 2021-03-21T00:09:13.146900Z

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

sova-soars-the-sora 2021-03-21T00:29:12.147200Z

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

tomrbowden 2021-03-21T13:22:28.149Z

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))

tomrbowden 2021-03-21T13:39:41.149300Z

@sova 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.

sova-soars-the-sora 2021-03-21T16:05:26.149800Z

@tom.bowden @tkjone thanks!

raspasov 2021-03-21T01:37:19.147600Z

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

athomasoriginal 2021-03-21T02:23:02.147800Z

Here is an example of using the speechsynthesis API https://github.com/athomasoriginal/clojurescript-30/tree/master/23-speech-synthesis

athomasoriginal 2021-03-21T02:23:34.148Z

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

emccue 2021-03-21T04:46:57.148400Z

You can maybe write it "monad-ly" i guess. In clojure wrap everything in pure and in clojurescript in Promise

emccue 2021-03-21T04:47:09.148600Z

it is fairly painful, but such is life

borkdude 2021-03-21T16:14:50.151500Z

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?

borkdude 2021-03-21T16:16:47.152Z

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

borkdude 2021-03-21T16:18:10.152600Z

This is the issue: https://github.com/clj-kondo/clj-kondo/issues/1189

thheller 2021-03-21T16:30:33.155700Z

@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

borkdude 2021-03-21T16:33:07.156Z

Ah, so goog is an alias, interesting.

borkdude 2021-03-21T16:34:01.156400Z

thanks

borkdude 2021-03-21T16:41:49.157400Z

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

thheller 2021-03-21T16:42:49.158200Z

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

thheller 2021-03-21T16:43:05.158500Z

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

thheller 2021-03-21T16:43:23.159Z

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

thheller 2021-03-21T16:44:11.159700Z

the alias is only resolved if you use set/union

thheller 2021-03-21T16:44:22.160Z

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

borkdude 2021-03-21T16:44:35.160300Z

which also works on goog:

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

borkdude 2021-03-21T16:44:51.160500Z

ah right

borkdude 2021-03-21T16:45:03.160800Z

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

2021-03-21T23:53:21.162300Z

What are people using to componentize their CSS these days?