diff --git a/resources/public/index.html b/resources/public/index.html index a62658c..ca98f9d 100644 --- a/resources/public/index.html +++ b/resources/public/index.html @@ -10,7 +10,9 @@
- - + + + + diff --git a/resources/public/js/audioRecorder.js b/resources/public/js/audioRecorder.js new file mode 100644 index 0000000..47b1945 --- /dev/null +++ b/resources/public/js/audioRecorder.js @@ -0,0 +1,65 @@ +(function(window) { + var AUDIO_RECORDER_WORKER = 'js/audioRecorderWorker.js'; + var AudioRecorder = function(source, cfg) { + this.consumers = []; + var config = cfg || {}; + var errorCallback = config.errorCallback || function() {}; + var inputBufferLength = config.inputBufferLength || 4096; + var outputBufferLength = config.outputBufferLength || 4000; + this.context = source.context; + this.node = this.context.createScriptProcessor(inputBufferLength); + var worker = new Worker(config.worker || AUDIO_RECORDER_WORKER); + worker.postMessage({ + command: 'init', + config: { + sampleRate: this.context.sampleRate, + outputBufferLength: outputBufferLength, + outputSampleRate: (config.outputSampleRate || 16000) + } + }); + var recording = false; + this.node.onaudioprocess = function(e) { + if (!recording) return; + worker.postMessage({ + command: 'record', + buffer: [ + e.inputBuffer.getChannelData(0), + e.inputBuffer.getChannelData(1) + ] + }); + }; + this.start = function(data) { + this.consumers.forEach(function(consumer, y, z) { + consumer.postMessage({ command: 'start', data: data }); + recording = true; + return true; + }); + recording = true; + return (this.consumers.length > 0); + }; + this.stop = function() { + if (recording) { + this.consumers.forEach(function(consumer, y, z) { + consumer.postMessage({ command: 'stop' }); + }); + recording = false; + } + worker.postMessage({ command: 'clear' }); + }; + this.cancel = function() { + this.stop(); + }; + myClosure = this; + worker.onmessage = function(e) { + if (e.data.error && (e.data.error == "silent")) errorCallback("silent"); + if ((e.data.command == 'newBuffer') && recording) { + myClosure.consumers.forEach(function(consumer, y, z) { + consumer.postMessage({ command: 'process', data: e.data.data }); + }); + } + }; + source.connect(this.node); + this.node.connect(this.context.destination); + }; + window.AudioRecorder = AudioRecorder; +})(window); diff --git a/resources/public/js/audioRecorderWorker.js b/resources/public/js/audioRecorderWorker.js new file mode 100644 index 0000000..7d0e932 --- /dev/null +++ b/resources/public/js/audioRecorderWorker.js @@ -0,0 +1,59 @@ +var recBuffers = [], + outputSampleRate = 16000, + inSampleRate; + +this.onmessage = function(e){ + switch(e.data.command){ + case 'init': + init(e.data.config); + break; + case 'record': + record(e.data.buffer); + break; + case 'clear': + clear(); + break; + } +}; + +function init(config){ + inSampleRate = config.sampleRate; + outputBufferLength = config.outputBufferLength; + outputSampleRate = config.outputSampleRate || outputSampleRate; +} + +function record(inputBuffer){ + var isSilent = true; + for (var i = 0 ; i < inputBuffer[0].length ; i++) { + recBuffers.push((inputBuffer[0][i] + inputBuffer[1][i]) * 16383.0); + } + while(recBuffers.length * outputSampleRate / inSampleRate > outputBufferLength) { + var result = new Int16Array(outputBufferLength); + var bin = 0, + num = 0, + indexIn = 0, + indexOut = 0; + while(indexIn < outputBufferLength) { + bin = 0; + num = 0; + while(indexOut < Math.min(recBuffers.length, (indexIn + 1) * inSampleRate / outputSampleRate)) { + bin += recBuffers[indexOut]; + num += 1; + indexOut++; + } + result[indexIn] = bin / num; + if(isSilent && (result[indexIn] != 0)) isSilent = false; + indexIn++; + } + var output = {}; + output.command = 'newBuffer'; + output.data = result; + if (isSilent) output.error = "silent"; + this.postMessage(output); + recBuffers = recBuffers.slice(indexOut); + } +} + +function clear(){ + recBuffers = []; +} diff --git a/src/sliva/core.cljs b/src/sliva/core.cljs index 911a38e..fa30a1a 100644 --- a/src/sliva/core.cljs +++ b/src/sliva/core.cljs @@ -1,10 +1,11 @@ (ns sliva.core - (:require - [secretary.core :as secretary :refer-macros [defroute]] + (:require [secretary.core :as secretary :refer-macros [defroute]] [reagent.core :as reagent] + [sliva.socket :refer [websocket-init click-test]] [sliva.data :refer [appstate]] [sliva.routes :refer [app-routes]] - [sliva.pages.hub :refer [hub]]) + [sliva.pages.hub :refer [hub]] + [sliva.pages.visual :refer [visual]]) (:require-macros [cljs.core.async.macros :refer [go go-loop]])) (enable-console-print!) @@ -13,20 +14,20 @@ ;; Page switching (defmulti current-page #(@appstate :page)) -(defmethod current-page :hub [] hub) - -;;(defn on-js-reload [] -;; (stop-render) -;; (start-render) -;; (websocket-init)) +(defmethod current-page :hub [] [hub]) +(defmethod current-page :visual [] [visual]) ;; On figwheel reload do this (reload routes, render currently active page) (defn init-app [] (app-routes) + (console.log "App (re)init!") (reagent/render [current-page] (.getElementById js/document "container"))) ;; Vstopna tocka (aset js/document "onreadystatechange" (fn [] (if (= (.-readyState js/document) "complete") - (init-app)))) + (do + (websocket-init) + (click-test) + (init-app))))) diff --git a/src/sliva/data.cljs b/src/sliva/data.cljs index b05a044..ad16803 100644 --- a/src/sliva/data.cljs +++ b/src/sliva/data.cljs @@ -5,6 +5,26 @@ ;; App state atom (def appstate (reagent/atom - {:clients []})) - -(def vtic (atom (chan))) + {:clients [] + :vtic (chan) + :vizual {:animiraj false + ;;;;; ☭☭☭☭☭☭☭☭☭☭☭☭☭☭ ;;;;;; + ;; ☭☭☭☭ Parametri razni ☭☭☭☭ ;; + ;;;;; ☭☭☭☭☭☭☭☭☭☭☭☭☭☭ ;;;;;; + :odmik-kamere 100 + :rotacija-kamere 1 + :bg-barva 0x000000 + :FOV 140 + :lik-sirina 2 + :obj-limit 1000 + :objekti [] + :stevec 0 + :rotacija-x 0.006 + :rotacija-y 0.001 + :rotacija-z 0.003 + :zamik-barve 0.0000666 + :zacetna-barva 0.333 + :saturacija 1 + :svetlost 0.4 + :w-diff 0.5 + :gostota-obj 2}})) diff --git a/src/sliva/gfx.cljs b/src/sliva/gfx.cljs index df74713..0db7edc 100644 --- a/src/sliva/gfx.cljs +++ b/src/sliva/gfx.cljs @@ -1,37 +1,20 @@ -(ns sliva.gfx) +(ns sliva.gfx + (:require [sliva.data :refer [appstate]])) -;;;;; ☭☭☭☭☭☭☭☭☭☭☭☭☭☭ ;;;;;; -;; ☭☭☭☭ Parametri razni ☭☭☭☭ ;; -;;;;; ☭☭☭☭☭☭☭☭☭☭☭☭☭☭ ;;;;;; +(defn get-param [param] + (get-in @appstate [:vizual param])) -(def odmik-kamere 100) -(def rotacija-kamere 1) -(def FOV 140) -(def lik-sirina 2) -(def obj-limit 1000) - -(def objekti (atom [])) -(def stevec (atom 0)) - -(def rotacija-x 0.006) -(def rotacija-y 0.001) -(def rotacija-z 0.003) - -(def zamik-barve 0.0000666) -(def zacetna-barva 0.333) -(def saturacija 1) -(def svetlost 0.4) - -(def w-diff 0.5) -(def gostota-obj 2) +(defn set-param [param val] + (swap! appstate assoc-in [:vizual param] val)) ;; Inicializacija ;; - (def scena (THREE.Scene.)) +(def kamera (THREE.PerspectiveCamera. + (get-param :FOV) + (/ (.-innerWidth js/window) + (.-innerHeight js/window)) 0.1 2000)) -(def kamera (THREE.PerspectiveCamera. FOV (/ (.-innerWidth js/window) - (.-innerHeight js/window)) 0.1 2000)) -(aset kamera "position" "z" odmik-kamere) +(aset kamera "position" "z" (get-param :odmik-kamere)) (aset kamera "aspect" (/ (.-innerWidth js/window) (.-innerHeight js/window))) (.updateProjectionMatrix kamera) @@ -40,7 +23,7 @@ (.setSize izris (.-innerWidth js/window) (.-innerHeight js/window)) ;; Crno ozadje -(.setClearColor izris 0x000000 1) +(.setClearColor izris (get-param :bg-barva) 1) (defn dodaj-obj [sirina] (let [barva (js/THREE.Color.) @@ -51,7 +34,11 @@ -zamik (* -1 zamik)] ;; Nastavi barvo novega objekta - (.setHSL barva (* @stevec zacetna-barva) saturacija svetlost) + (.setHSL barva + (* (get-param :stevec) + (get-param :zacetna-barva)) + (get-param :saturacija) + (get-param :svetlost)) (aset mat "color" barva) ;; Kvadratek (za nov objekt) @@ -61,55 +48,47 @@ (.push koti (js/THREE.Vector3. zamik 0 0) (js/THREE.Vector3. 0 -zamik 0)) (let [obj (js/THREE.Line. geo mat (.-LineSegments js/THREE)) - novi-objekti (conj (take obj-limit @objekti) + novi-objekti (conj (take (get-param :obj-limit) (get-param :objekti)) obj)] (.add scena obj) ;; pucaj sceno ane - (reset! objekti novi-objekti)))) + (set-param :objekti novi-objekti)))) (defn obj-anim [obj] - (.rotateY obj rotacija-y) - (.rotateZ obj rotacija-z) - (.rotateX obj rotacija-x) + (.rotateY obj (get-param :rotacija-y)) + (.rotateZ obj (get-param :rotacija-z)) + (.rotateX obj (get-param :rotacija-x)) (let [new-scale (+ (aget obj "scale" "x") - w-diff)] + (get-param :w-diff))] (aset obj "scale" "x" new-scale) (aset obj "scale" "y" new-scale) (aset obj "scale" "z" new-scale)) - (.offsetHSL (aget obj "material" "color") zamik-barve 0 0)) + (.offsetHSL (aget obj "material" "color") (get-param :zamik-barve) 0 0)) (defn cam-rotate [] - (.translateX kamera rotacija-kamere) - (.translateZ kamera (- odmik-kamere - (.sqrt js/Math (+ (.pow js/Math odmik-kamere 2) - (.pow js/Math rotacija-kamere 2))))) - (.lookAt kamera scena.position)) + (.translateX kamera (get-param :rotacija-kamere)) + (.translateZ kamera (- (get-param :odmik-kamere) + (.sqrt js/Math (+ (.pow js/Math (get-param :odmik-kamere) 2) + (.pow js/Math (get-param :rotacija-kamere) 2))))) + (.lookAt kamera (.-position scena))) (defn render [] - (.requestAnimationFrame js/window render) + (if (get-in @appstate [:vizual :animiraj]) + (.requestAnimationFrame js/window render)) - (reset! stevec (inc @stevec)) + (set-param :stevec (inc (get-param :stevec))) - (if (= 0 (rem @stevec gostota-obj)) - (dodaj-obj lik-sirina)) + (if (= 0 (rem (get-param :stevec) (get-param :gostota-obj))) + (dodaj-obj (get-param :lik-sirina))) (.render izris scena kamera) - (run! obj-anim @objekti) + (run! obj-anim (get-param :objekti)) (cam-rotate)) -(defn start-render [] - (let [container (.getElementById js/document "container")] - (.appendChild container (.-domElement izris)))) - -(defn stop-render [] - (let [container (.getElementById js/document "container")] - (.removeChild container (.-firstChild container)))) - - ;; Hendlaj risajz (defn on-window-resize[] (let [sirina (.-innerWidth js/window) @@ -117,4 +96,15 @@ (aset kamera "aspect" (/ sirina visina)) (.updateProjectionMatrix kamera) (.setSize izris sirina visina))) -(.addEventListener js/window "resize" on-window-resize false) + +(defn start-render [container] + (.appendChild container (.-domElement izris)) + (swap! appstate assoc-in [:vizual :animiraj] true) + (.addEventListener js/window "resize" on-window-resize false) + (render)) + +(defn stop-render [container] + (swap! appstate assoc-in [:vizual :animiraj] false) + (.removeEventListener js/window "resize" on-window-resize false) + (.removeChild container (.-firstChild container))) + diff --git a/src/sliva/pages/hub.cljs b/src/sliva/pages/hub.cljs index 1af75da..dcf2318 100644 --- a/src/sliva/pages/hub.cljs +++ b/src/sliva/pages/hub.cljs @@ -1,9 +1,6 @@ (ns sliva.pages.hub - (:require [sliva.socket :refer [websocket-init click-test]] - [sliva.data :refer [appstate]])) - -(websocket-init) -(click-test) + (:require [sliva.data :refer [appstate]] + [sliva.pages.navigation :refer [navigation]])) (defn hub [] [:div @@ -12,10 +9,14 @@ [:thead [:tr [:th "Client ID"] - [:th "Something else?"]]] + [:th "status"]]] [:tbody - (map (fn [cid] - [:tr {:key cid} - [:td cid] - [:td "connected"]]) - (keys (:clients @appstate)))]]]) + (let [clients (:clients @appstate)] + (if (empty? clients) + [:tr [:td "No"] [:td "clients"]] + (->> clients + (map-indexed (fn [idx cid] + [:tr {:key idx} + [:td cid] + [:td "connected"]])))))]] + (navigation)]) diff --git a/src/sliva/pages/navigation.cljs b/src/sliva/pages/navigation.cljs new file mode 100644 index 0000000..7e4650a --- /dev/null +++ b/src/sliva/pages/navigation.cljs @@ -0,0 +1,8 @@ +(ns sliva.pages.navigation) + +(defn navigation [] + [:div + [:h2 "Navigation"] + [:ul + [:li [:a {:href "#/hub"} "Hub"]] + [:li [:a {:href "#/visual"} "Visual"]]]]) diff --git a/src/sliva/pages/visual.cljs b/src/sliva/pages/visual.cljs new file mode 100644 index 0000000..959cb26 --- /dev/null +++ b/src/sliva/pages/visual.cljs @@ -0,0 +1,15 @@ +(ns sliva.pages.visual + (:require [reagent.core :as reagent] + [sliva.gfx :refer [start-render stop-render]] + [sliva.pages.navigation :refer [navigation]])) + +(defn visual [] + (reagent/create-class + {:display-name "vizual-render-plac" + :reagent-render (fn [] [:div + [:div {:ref "vizual"}] + [navigation]]) + :component-did-mount (fn [this] (start-render (.. this -refs -vizual))) + :component-will-unmount (fn [this] + (console.log "unmounting?") + (stop-render (.. this -refs -vizual)))})) diff --git a/src/sliva/routes.cljs b/src/sliva/routes.cljs index e0d86a7..b89cfb8 100644 --- a/src/sliva/routes.cljs +++ b/src/sliva/routes.cljs @@ -14,7 +14,10 @@ (.setEnabled true))) (defn app-routes [] - (secretary/set-config! :prefix "#☭") - (defroute "/" [] - (swap! appstate assoc :page :hub)) + ;;(secretary/set-config! :prefix "#☭") + (secretary/set-config! :prefix "#") + (defroute "/" [] (swap! appstate assoc :page :hub)) + (defroute "/hub" [] (swap! appstate assoc :page :hub)) + (defroute "/visual" [] (swap! appstate assoc :page :visual)) + (defroute "/test" [] (console.log "test!")) (hook-browser-navigation!)) diff --git a/src/sliva/server/handlers.clj b/src/sliva/server/handlers.clj index 7a6ee84..633abc9 100644 --- a/src/sliva/server/handlers.clj +++ b/src/sliva/server/handlers.clj @@ -1,5 +1,6 @@ (ns sliva.server.handlers - (:require [compojure.core :refer [defroutes GET]] + (:require [clojure.java.shell :refer [sh]] + [compojure.core :refer [defroutes GET]] [compojure.route :refer [resources]] [clojure.string :as str] [clojure.core.async :refer [chan ! put! close! go go-loop]] @@ -21,7 +22,7 @@ (go (let [{:keys [message]} (! ws-ch (str "Hello " cid)) + (>! ws-ch (str "hello:" cid)) (swap! clients assoc cid ws-ch) (send-all "open" cid) (go-loop [] @@ -31,7 +32,11 @@ (println "command" cmd "(" args ")") (condp = cmd "ping" (>! ws-ch "pong") - - "Unknown command :/")) (keys @clients) + (println "DEBUG: msg ignored: " cmd))) (keys @clients) (recur)))))))) ;;(close! ws-ch))))) + +;;test +;;(while true +;; (let [return (sh "sudo" "bash" "-c" "iwlist scan | grep ESSID | sed 's/[[:space:]]\\+ESSID://' | sed 's/\"//g' | uniq")] +;; (println "GOT NETWORKS!" (:out return))) diff --git a/src/sliva/socket.cljs b/src/sliva/socket.cljs index 0439517..462bd84 100644 --- a/src/sliva/socket.cljs +++ b/src/sliva/socket.cljs @@ -1,35 +1,38 @@ (ns sliva.socket (:require [chord.client :refer [ws-ch]] + [clojure.string :as str] [cljs.core.async :refer [! put! close!]] - [sliva.data :refer [vtic]]) + [sliva.data :refer [appstate]]) (:require-macros [cljs.core.async.macros :refer [go go-loop]])) -;; Zacetek ;; -;;(aset js/document "onreadystatechange" -;; (fn [] -;; (if (= (.-readyState js/document) "complete") -;; (do -;; (start-render) -;; (render) -;; (websocket-init))))) +(defn handle-message [msg-str] + (let [[msg & args] (str/split msg-str #":")] + (console.info "msg" msg "args" args) + (condp = msg + "pong" (console.log "hura, server se odziva") + "open" (swap! appstate assoc :clients (conj (:clients @appstate) (first args))) + (console.info "msg ingored: " msg)))) (defn websocket-init [] (console.log "init webscoket") - (go (let [{:keys [ws-channel error]} (! ws-channel "Hello server") + (swap! appstate assoc :vtic ws-channel) + (>! ws-channel "hello:server") - ;; (go-loop [] - ;; (let [{:keys [message]} (! @vtic "ping"))))) + (.addEventListener + js/window + "click" + (fn [] + (let [vtic (:vtic @appstate)] + (console.log "klik") + (put! vtic "ping")))))