Hi all! I want to capture microphone input with clojurescript.
I wonder if there are any existing examples. I have not found any yet.
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))
@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.
@tom.bowden @tkjone thanks!
Anything related to audio/video input will most likely be all about JavaScript, so I would search JS tutorials/resources and use CLJS interop.
Here is an example of using the speechsynthesis
API https://github.com/athomasoriginal/clojurescript-30/tree/master/23-speech-synthesis
I feel that demo might have a bug in it, but perhaps it can help you on your journey 🙏
https://github.com/athomasoriginal/clojurescript-30/tree/master/20-native-speech-recognition does speech rec
You can maybe write it "monad-ly" i guess. In clojure wrap everything in pure
and in clojurescript in Promise
it is fairly painful, but such is life
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?Or is goog.foobar
special syntactic sugar for js/goog.foobar
, while goog
itself isn't treated as js/goog
?
This is the issue: https://github.com/clj-kondo/clj-kondo/issues/1189
This is it maybe? https://github.com/clojure/clojurescript/blob/3fdaabedb1343f434b6cb4f75fa28e748f96eff0/src/main/clojure/cljs/analyzer.cljc#L2593
@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
Ah, so goog
is an alias, interesting.
thanks
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
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
so goog.global
will end up as goog.global
in the JS code, which exists provided by the goog/base.js
set.union
will also end up as set.union
which doesn't exist
the alias is only resolved if you use set/union
goog
is just an alias for itself so it happens to work there
which also works on goog
:
cljs.user=> (some? goog/global)
true
ah right
goog is an alias on itself... good to remember
What are people using to componentize their CSS these days?