diff --git a/light-programs/LightController.js b/light-programs/LightController.js index 03b7a58..1b11c18 100644 --- a/light-programs/LightController.js +++ b/light-programs/LightController.js @@ -2,11 +2,14 @@ const Geometry = require('./Geometry') const _ = require('lodash'); const programNames = [ - "PROGRAM_Main", + "shapes", + "musicFrequencyDot", + "frequencyActivation", + "musicVolumeDot", // "PROGRAM_Transition", // "PROGRAM_Triangulo", + "PROGRAM_Main", "rays", - "shapes", "stripe-patterns", "sound-waves", "radial", @@ -19,13 +22,11 @@ const programNames = [ "circles", "musicFlow", "rainbow", - "musicVolumeDot", "stars", "debugSetup", "debugShapes", "all-off", "all-white", - "leap-test" ] const Emitter = require('events') @@ -145,8 +146,9 @@ module.exports = class LightController { } } - updateLeds(leds) { - lightsSampleEmitter.emit('lights', leds) - this.setLightsCbk(leds) + updateLeds(rgbaLeds) { + const rgbLeds = _.map(rgbaLeds, rgba => rgba.slice(0, 3)); + lightsSampleEmitter.emit('lights', rgbLeds) + this.setLightsCbk(rgbLeds) } } diff --git a/light-programs/base-programs/SoundBasedFunction.js b/light-programs/base-programs/SoundBasedFunction.js index 338815b..cd0f9b5 100644 --- a/light-programs/base-programs/SoundBasedFunction.js +++ b/light-programs/base-programs/SoundBasedFunction.js @@ -6,7 +6,19 @@ const soundEmitter = require("../../sound-broadcast") // Fake sound wave with random let lastRandom = 0; let realSound = 0; -let currentAudioFrame = null; + + +let lastFrameData = { + centroid: 0, + rms: 0, + spectralBands: {bass: {energy: 0}}, + filteredBands: {bass: {energy: 0, rms: 25}}, + movingStats: {rms: {value: 0, normalizedValue: 0}}, + spectralCentroid: {bin: 100} +}; + +let currentAudioFrame = lastFrameData; + let fakingSoundInterval = 0; let t = 0; function startFakeSound(){ @@ -15,18 +27,52 @@ function startFakeSound(){ // Magic formula to simulate song audio volume change? realSound = Math.min(1, Math.max(0, Math.pow(Math.random(), 2)*0.2+realSound*0.7+Math.sin(t*7)/10+Math.sin(t/3)/10)); t += (25/1000) + + lastFrameData = {rms: realSound, centroid: 50} }, 25) } // After 1sec without mic sound, fake wave let fakeSoundTimeout = setTimeout(startFakeSound, 1000) -soundEmitter.on('processedaudioframe', frame => { +let absolutefft = _.range(0,512).map(()=> 0); +let maxabsolutefft = _.range(0,512).map(()=> 0); + +let medianVolume11 = _.map(_.range(11), () => 0) +let averageVolume = 0; +let averageRelativeVolume = 0; +let averageVolumeSmoothed = 0; +let averageVolumeSmoothedSlow = 0; +let averageRelativeVolumeSmoothed = 0; +let medianVolume = 0 +let maxVolume = 0; + +soundEmitter.on('processedaudioframe', (frame) => { + let {center: lastFrame} = frame; realSound = frame.center.rms; - currentAudioFrame = frame; + realSound = lastFrame.rms; + clearTimeout(fakeSoundTimeout) clearInterval(fakingSoundInterval) fakeSoundTimeout = setTimeout(startFakeSound, 1000) + + currentAudioFrame = frame; + lastFrameData = lastFrame + + _.each(maxabsolutefft, (v, i) => maxabsolutefft[i] = Math.max(maxabsolutefft[i]*0.99,lastFrame.absolutefft[i])); + _.each(absolutefft, (v, i) => absolutefft[i] = absolutefft[i]*0.5+0.5*lastFrame.absolutefft[i]); + + averageVolume = realSound; + averageVolumeSmoothed = (averageVolume + 2 * averageVolumeSmoothed) / 3 + averageVolumeSmoothedSlow = (averageVolume + 20 * averageVolumeSmoothedSlow) / 21 + + medianVolume11.push(averageRelativeVolume) + medianVolume11 = medianVolume11.slice(1) + medianVolume = _.sortBy(medianVolume11)[5] + + maxVolume = (Math.max(maxVolume, averageVolume)*500+averageVolume)/501; + averageRelativeVolume = averageVolume / (maxVolume || 1) + averageRelativeVolumeSmoothed = averageVolumeSmoothed / (maxVolume || 1) }) // @@ -39,34 +85,8 @@ soundEmitter.on('processedaudioframe', frame => { // fakeSoundTimeout = setTimeout(startFakeSound, 1000) // }) -let averageVolume = 0; -let averageRelativeVolume = 0; -let averageVolumeSmoothed = 0; -let averageVolumeSmoothedSlow = 0; -let averageRelativeVolumeSmoothed = 0; -let medianVolume15 = _.map(_.range(15), () => 0) -let medianVolume = 0 let lastTime = new Date(); -let maxVolume = 0; let processInterval = setTimeout(function computeSoundStats() { - // calculate average - averageVolume = realSound; - averageVolumeSmoothed = (averageVolume + 2 * averageVolumeSmoothed) / 3 - averageVolumeSmoothedSlow = (averageVolume + 20 * averageVolumeSmoothedSlow) / 21 - - // Plot - // self.plotEnergyHistogram(self); - - - // self.bassesAverageVolume = getAverageVolume(array, 32); - maxVolume = (Math.max(maxVolume, averageVolume)*300+averageVolume)/301; - averageRelativeVolume = averageVolume / (maxVolume || 1) - averageRelativeVolumeSmoothed = averageVolumeSmoothed / (maxVolume || 1) - - medianVolume15.push(averageRelativeVolume) - medianVolume15 = medianVolume15.slice(1) - medianVolume = _.sortBy(medianVolume15)[7] - soundEmitter.emit('volume', {level: averageRelativeVolume, max: maxVolume}) // console.log("Last audio: " + (new Date() - lastTime) + "ms "+self.averageVolume) @@ -87,7 +107,7 @@ module.exports = class SoundBasedFunction extends TimeTickedFunction{ this.averageRelativeVolume = averageRelativeVolume; this.averageVolumeSmoothed = averageVolumeSmoothed; this.averageVolumeSmoothedSlow = averageVolumeSmoothedSlow; - this.medianVolume15 = medianVolume15 + this.medianVolume11 = medianVolume11 this.medianVolume = medianVolume let self = this; @@ -102,9 +122,14 @@ module.exports = class SoundBasedFunction extends TimeTickedFunction{ self.averageRelativeVolume = averageRelativeVolume self.averageRelativeVolumeSmoothed = averageRelativeVolumeSmoothed - self.medianVolume15 = medianVolume15 + self.medianVolume11 = medianVolume11 self.medianVolume = medianVolume + self.centroid = lastFrameData.centroid; + self.lastFrame = lastFrameData; + self.absolutefft = absolutefft; + self.maxabsolutefft = maxabsolutefft; + self.processInterval = setTimeout(updateValues, 1000/self.config.fps); }, 1000/self.config.fps); diff --git a/light-programs/programs/frequencyActivation.js b/light-programs/programs/frequencyActivation.js new file mode 100644 index 0000000..8d77997 --- /dev/null +++ b/light-programs/programs/frequencyActivation.js @@ -0,0 +1,51 @@ +const SoundBasedFunction = require("./../base-programs/SoundBasedFunction"); +const ColorUtils = require("./../utils/ColorUtils"); + +module.exports = class MusicVolumeDot extends SoundBasedFunction{ + constructor(config, leds) { + super(config, leds); + } + + start(config, draw, done){ + this.lastVolume = new Array(this.numberOfLeds+1).join('0').split('').map(() => [0,0,0]); + this.time = 0; + this.maxVolume = 0; + + super.start(config, draw, done) + } + + // Override parent method + drawFrame(draw, done){ + this.time += this.config.speed; + + let size = this.config.zoom; + if(this.absolutefft) { + for (let i = 0; i < this.numberOfLeds; i++) { + let pos = Math.floor((i % 150)/size); + let vol = this.config.multiplier * this.absolutefft[pos+5]/10; + + let newVal = ColorUtils.HSVtoRGB(vol, 1, Math.min(vol, 1)); + + this.lastVolume[i] = newVal; + } + } + + draw(this.lastVolume); + done(); + } + + static presets(){ + return { + } + } + + // Override and extend config Schema + static configSchema(){ + let res = super.configSchema(); + res.multiplier = {type: Number, min: 0, max: 2, step: 0.01, default: 1}; + res.zoom = {type: Number, min: 1, max: 32, step: 1, default: 4}; + res.numberOfOnLeds = {type: Number, min: 1, max: 100, step: 1, default: 40}; + res.cutThreshold = {type: Number, min: 0, max: 1, step: 0.01, default: 0.45}; + return res; + } +} \ No newline at end of file diff --git a/light-programs/programs/musicFrequencyDot.js b/light-programs/programs/musicFrequencyDot.js new file mode 100644 index 0000000..3b182d0 --- /dev/null +++ b/light-programs/programs/musicFrequencyDot.js @@ -0,0 +1,87 @@ +const SoundBasedFunction = require("./../base-programs/SoundBasedFunction"); +const ColorUtils = require("./../utils/ColorUtils"); + +module.exports = class MusicVolumeDot extends SoundBasedFunction{ + constructor(config, leds) { + super(config, leds); + } + + start(config, draw, done){ + this.lastVolume = new Array(this.numberOfLeds+1).join('0').split('').map(() => [0,0,0]); + this.time = 0; + this.maxVolume = 0; + + this.maxCentroid = 140; + this.minCentroid = 139; + + super.start(config, draw, done) + } + + // Override parent method + drawFrame(draw, done){ + this.time += this.config.speed; + + // let vol = this.averageRelativeVolume*this.config.multiplier; + let vol = this.lastFrame.spectralBands.bass.energy/100*this.config.multiplier; + + // Como las luces tenues son MUY fuertes igual, a partir de cierto valor "las bajamos" + if(vol < this.config.cutThreshold){ + vol = 0; + } else { + vol = (vol - this.config.cutThreshold) / (1-this.config.cutThreshold) + } + + // let centroidHue = (this.lastFrame.spectralCentroid.bin - this.minCentroid) / (this.maxCentroid - this.minCentroid); + // this.maxCentroid = Math.max(this.lastFrame.spectralCentroid.bin, this.maxCentroid) + // this.minCentroid = Math.min(this.lastFrame.spectralCentroid.bin, this.minCentroid) + if(this.lastFrame.filteredBands) { + let bass = this.lastFrame.filteredBands.bass.movingStats.rms.normalizedValue; + let r = Math.round(255 * bass * bass * 0.5); + let mid = this.lastFrame.filteredBands.mid.movingStats.rms.normalizedValue; + let g = Math.round(255 * mid * mid * 4); + let high = this.lastFrame.filteredBands.high.movingStats.rms.normalizedValue; + let b = Math.round(255 * high * high * 4); + + + let width = Math.round((this.numberOfLeds / this.config.numberOfOnLeds)); + + for (let i = 0; i < this.numberOfLeds; i += 1) { + let rms = this.lastFrame.movingStats.rms.normalizedValue; + let explosion = Math.ceil(this.config.multiplier * rms * rms * rms * width / 3) + if (Math.abs((i % width - width/2)) < explosion) { + this.lastVolume[i] = [r, g, b]; + } else { + this.lastVolume[i] = [0, 0, 0]; + } + + // if (i % width === 0) { + // this.lastVolume[i] = [r, 0, 0]; + // } else if (i % width === Math.floor(width/3)) { + // this.lastVolume[i + Math.floor(width/3)] = [0, g, 0]; + // } else if (i % width === Math.floor(2*width/3)) { + // this.lastVolume[i + 2 * Math.floor(width/3)] = [0, 0, b]; + // } else { + // this.lastVolume[i] = [0, 0, 0]; + // } + + } + } + + draw(this.lastVolume); + done(); + } + + static presets(){ + return { + } + } + + // Override and extend config Schema + static configSchema(){ + let res = super.configSchema(); + res.multiplier = {type: Number, min: 0, max: 2, step: 0.01, default: 1}; + res.numberOfOnLeds = {type: Number, min: 1, max: 100, step: 1, default: 40}; + res.cutThreshold = {type: Number, min: 0, max: 1, step: 0.01, default: 0.45}; + return res; + } +} \ No newline at end of file diff --git a/light-programs/programs/musicVolumeDot.js b/light-programs/programs/musicVolumeDot.js index e4e078e..0a33f32 100644 --- a/light-programs/programs/musicVolumeDot.js +++ b/light-programs/programs/musicVolumeDot.js @@ -18,7 +18,8 @@ module.exports = class MusicVolumeDot extends SoundBasedFunction{ drawFrame(draw, done){ this.time += this.config.speed; - let vol = this.averageRelativeVolume*this.config.multiplier; + // let vol = this.averageRelativeVolume*this.config.multiplier; + let vol = this.currentAudioFrame.center.movingStats.rms.normalizedValue*this.config.multiplier; // Como las luces tenues son MUY fuertes igual, a partir de cierto valor "las bajamos" if(vol < this.config.cutThreshold){ diff --git a/mic/mic.js b/mic/mic.js index 5090d60..4a4373a 100644 --- a/mic/mic.js +++ b/mic/mic.js @@ -9,9 +9,9 @@ var mic = function mic(options) { options = options || {}; var that = {}; var endian = osEndianness == "BE"? "big" : "little"; - var bitwidth = 32; - var encoding = 'floating-point'; - var rate = that._sampleRate = options.rate || 44100; + var outputBitwidth = 32; + var outputEncoding = 'floating-point'; + var outputRate = that._sampleRate = options.rate || 44100; var channels = that._channels = options.channels || 1; if (channels != 1) { // TODO: stereo support. @@ -21,7 +21,7 @@ var mic = function mic(options) { var exitOnSilence = options.exitOnSilence || 0; var fileType = options.fileType || 'raw'; var frameSize = options.frameSize || 512; - var bufferSize = frameSize * channels * bitwidth / 8; + var bufferSize = frameSize * channels * outputBitwidth / 8; var debug = options.debug || false; var format, formatEndian, formatEncoding; var audioProcess = null; @@ -36,28 +36,43 @@ var mic = function mic(options) { } // Setup format variable for arecord call - if(encoding === 'unsigned-integer') { + if(outputEncoding === 'unsigned-integer') { formatEncoding = 'U'; } else { formatEncoding = 'S'; } - format = formatEncoding + bitwidth + '_' + osEndianness; + format = formatEncoding + outputBitwidth + '_' + osEndianness; that.start = function start() { if(audioProcess === null) { if(isWindows){ - var params = ['-b', bitwidth, '--endian', endian, - '-c', channels, '-r', rate, '-e', encoding, - '-t' , 'waveaudio', 'default', '-p', '--buffer', bufferSize, '-V', - '-V']; + var params = [ + // Parameters for input (-t wave audio) + // '-b', '16', + // '--endian', endian, + '-c', channels, + // '-r', '44100', + // '-e', 'signed-integer', + '-t' , 'waveaudio', 'default', + + // Parameters for output (- means pipe it) + '-b', outputBitwidth, + '--endian', endian, + '-c', channels, + '-r', outputRate, + '-e', outputEncoding, + '--buffer', bufferSize/8, + '-t' , 'raw', + '-' + ]; audioProcess = spawn('sox', params, audioProcessOptions) console.log(params.join(" ")) } else if(isMac){ - let params = ['-b', bitwidth, '--endian', endian, - '-c', channels, '-r', rate, '-e', encoding, + let params = ['-b', outputBitwidth, '--endian', endian, + '-c', channels, '-r', outputRate, '-e', outputEncoding, '-t', fileType, '--buffer', bufferSize, '-']; console.log("rec", params.join(' ')) @@ -65,7 +80,7 @@ var mic = function mic(options) { } else { // TODO: fix this branch, no idea about the args for this program. - let params = ['-c', channels, '-r', rate, '-f', + let params = ['-c', channels, '-r', outputRate, '-f', format, '-D', device, '-B', '100000']; console.log("arecord", params.join(' ')) diff --git a/public/lightsSimulator.js b/public/lightsSimulator.js index 499abac..b9990b5 100644 --- a/public/lightsSimulator.js +++ b/public/lightsSimulator.js @@ -13,6 +13,9 @@ class LightsSimulator extends React.Component { this.lastFrameTime = performance.now(); this.lastFPS = 0; this.frameCount = 0; + + this.onVisibilityChange = this.onVisibilityChange.bind(this); + this.onFocusChange = this.onFocusChange.bind(this); } turnOnSimulation() { @@ -30,22 +33,48 @@ class LightsSimulator extends React.Component { socket.emit('stopSamplingLights', layout => {}); } + decodeLedsColorsFromString(encodedLights) { + let bytes = Uint8Array.from(atob(encodedLights), c => c.charCodeAt(0)); + + let byLed = new Array(bytes.length / 3); + for (let i = 0; i < bytes.length / 3; i += 1) { + byLed[i] = [bytes[i * 3], bytes[i * 3 + 1], bytes[i * 3 + 2]]; + } + return byLed; + } + + onVisibilityChange() { + if (document.hidden && this.state.renderingEnabled) { + this.turnOffSimulation(); + } else if (!document.hidden && this.state.renderingEnabled) { + this.turnOnSimulation(); + } + } + + onFocusChange() { + debugger; + if (!document.hasFocus() && this.state.renderingEnabled) { + this.turnOffSimulation(); + } else if (document.hasFocus() && this.state.renderingEnabled) { + this.turnOnSimulation(); + } + } + componentDidMount() { - socket.on('lightsSample', lights => { + socket.on('lightsSample', encodedLights => { + const lights = this.decodeLedsColorsFromString(encodedLights); console.log("Lights data"); this.drawCanvas(lights); }); - document.addEventListener('visibilitychange', () => { - if (document.hidden && this.state.renderingEnabled) { - this.turnOffSimulation(); - } else if (!document.hidden && this.state.renderingEnabled) { - this.turnOnSimulation(); - } - }); + document.addEventListener('visibilitychange', this.onVisibilityChange); + document.onblur = this.onFocusChange; + document.onfocus = this.onFocusChange; } componentWillUnmount() { + document.removeEventListener('visibilitychange', this.onVisibilityChange); + // document.removeEventListener('blur', this.onFocusChange); //this.stopCurrent() } diff --git a/public/lightsSimulator.js.map b/public/lightsSimulator.js.map index a6f9b29..def8392 100644 --- a/public/lightsSimulator.js.map +++ b/public/lightsSimulator.js.map @@ -1 +1 @@ -{"version":3,"sources":["lightsSimulator.jsx"],"names":[],"mappings":"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","file":"lightsSimulator.js","sourcesContent":["if (!window.socket) {\n window.socket = io();\n}\n\nclass LightsSimulator extends React.Component {\n constructor() {\n super(...arguments)\n\n this.state = {\n renderingEnabled: false\n }\n\n this.lastFrameTime = performance.now();\n this.lastFPS = 0;\n this.frameCount = 0;\n }\n\n turnOnSimulation() {\n socket.emit('startSamplingLights', (layout) => {\n this.geometryX = layout.geometry.x\n this.geometryY = layout.geometry.y\n this.minX = _.min(this.geometryX)\n this.minY = _.min(this.geometryY)\n this.maxX = _.max(this.geometryX)\n this.maxY = _.max(this.geometryY)\n });\n }\n\n turnOffSimulation() {\n socket.emit('stopSamplingLights', (layout) => {});\n }\n\n componentDidMount() {\n socket.on('lightsSample', (lights) => {\n console.log(\"Lights data\")\n this.drawCanvas(lights)\n });\n\n document.addEventListener('visibilitychange', () =>{\n if(document.hidden && this.state.renderingEnabled) {\n this.turnOffSimulation();\n } else if (!document.hidden && this.state.renderingEnabled) {\n this.turnOnSimulation();\n }\n })\n }\n\n componentWillUnmount() {\n //this.stopCurrent()\n }\n\n componentDidUpdate(oldProps, oldState) {\n if (oldState.func !== this.state.func) {\n //this.startCurrent()\n }\n }\n\n drawCanvas(lights) {\n const drawStartTime = performance.now();\n\n const leds = this.geometryX.length;\n const ctx = this.refs.canvas.getContext('2d');\n\n ctx.globalCompositeOperation = 'source-over';\n ctx.fillStyle = 'black';\n ctx.fillRect(0, 0, this.props.width, this.props.height);\n\n ctx.globalCompositeOperation = 'lighter';\n\n if (this.state.renderingEnabled) {\n const X = this.geometryX;\n const Y = this.geometryY;\n\n for (let i = 0; i < leds; i++) {\n const [r, g, b] = lights[i];\n\n let SCALE = 4;\n const x = X[i]*SCALE + 5*SCALE;\n const y = Y[i]*SCALE + 5*SCALE;\n\n let power = r + g + b;\n if (power < 0) power = 0;\n\n let lightRadius = (40 + (r + g + b) / (255 * 3) * 80) * 1;\n\n let m = 2;\n if (power < 200) {\n m = 4;\n } else if (power < 100) {\n m = 8;\n } else if (power < 50) {\n m = 16;\n }\n let [or, og, ob] = [r * m, g * m, b * m];\n if (or > 255) or = 255;\n if (og > 255) og = 255;\n if (ob > 255) ob = 255;\n\n ctx.beginPath();\n\n lightRadius = lightRadius / 24;\n ctx.fillStyle = `rgba(${or}, ${og}, ${ob}, 1)`;\n\n // let gradient = ctx.createRadialGradient(x, y, 0, x, y, lightRadius)\n // gradient.addColorStop(0, `rgba(${or}, ${og}, ${ob}, 1)`)\n // // gradient.addColorStop(0.065, `rgba(${or}, ${og}, ${ob}, 1)`)\n // gradient.addColorStop(0.25, `rgba(${r}, ${g}, ${b}, 1)`)\n // // gradient.addColorStop(0.25, `rgba(${r}, ${g}, ${b}, 0.5)`)\n // gradient.addColorStop(0.5, `rgba(${r}, ${g}, ${b}, 0.25)`)\n // // gradient.addColorStop(1, `rgba(${0}, ${0}, ${0}, 1)`)\n // gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`)\n // ctx.fillStyle = gradient\n\n\n ctx.arc(x, y, lightRadius, Math.PI * 2, false);\n ctx.fill();\n }\n\n this.frameCount++;\n\n let drawMilliseconds = performance.now() - drawStartTime;\n let timeSinceLastFPS = performance.now() - this.lastFrameTime;\n if (timeSinceLastFPS > 100) {\n this.lastFPS = 1000 * this.frameCount / timeSinceLastFPS;\n this.frameCount = 0;\n this.lastFrameTime = performance.now();\n }\n\n ctx.fillStyle = 'white';\n ctx.font = \"12px sans-serif\";\n\n ctx.fillText(`Sim overhead FPS: ${Math.floor(1000/drawMilliseconds)}`, 10, 40);\n ctx.fillText(`FPS: ${this.lastFPS.toFixed(1)}`, 10, 20);\n }\n }\n\n __changeSelection() {\n if(this.state.renderingEnabled) {\n this.turnOffSimulation();\n } else {\n this.turnOnSimulation();\n }\n this.setState({renderingEnabled: !this.state.renderingEnabled});\n }\n\n render() {\n return
\n
\n \n
\n \n
\n }\n}"]} \ No newline at end of file +{"version":3,"sources":["lightsSimulator.jsx"],"names":[],"mappings":"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","file":"lightsSimulator.js","sourcesContent":["if (!window.socket) {\n window.socket = io();\n}\n\nclass LightsSimulator extends React.Component {\n constructor() {\n super(...arguments)\n\n this.state = {\n renderingEnabled: false\n }\n\n this.lastFrameTime = performance.now();\n this.lastFPS = 0;\n this.frameCount = 0;\n\n this.onVisibilityChange = this.onVisibilityChange.bind(this);\n this.onFocusChange = this.onFocusChange.bind(this);\n }\n\n turnOnSimulation() {\n socket.emit('startSamplingLights', (layout) => {\n this.geometryX = layout.geometry.x\n this.geometryY = layout.geometry.y\n this.minX = _.min(this.geometryX)\n this.minY = _.min(this.geometryY)\n this.maxX = _.max(this.geometryX)\n this.maxY = _.max(this.geometryY)\n });\n }\n\n turnOffSimulation() {\n socket.emit('stopSamplingLights', (layout) => {});\n }\n\n decodeLedsColorsFromString(encodedLights) {\n let bytes = Uint8Array.from(atob(encodedLights), c => c.charCodeAt(0))\n\n let byLed = new Array(bytes.length / 3);\n for (let i = 0; i < bytes.length/3; i += 1) {\n byLed[i] = [bytes[i*3], bytes[i*3 + 1], bytes[i*3 + 2]];\n }\n return byLed;\n }\n\n onVisibilityChange() {\n if(document.hidden && this.state.renderingEnabled) {\n this.turnOffSimulation();\n } else if (!document.hidden && this.state.renderingEnabled) {\n this.turnOnSimulation();\n }\n }\n\n onFocusChange() {\n debugger;\n if(!document.hasFocus() && this.state.renderingEnabled) {\n this.turnOffSimulation();\n } else if (document.hasFocus() && this.state.renderingEnabled) {\n this.turnOnSimulation();\n }\n }\n\n componentDidMount() {\n socket.on('lightsSample', (encodedLights) => {\n const lights = this.decodeLedsColorsFromString(encodedLights)\n console.log(\"Lights data\")\n this.drawCanvas(lights)\n });\n\n document.addEventListener('visibilitychange', this.onVisibilityChange);\n document.onblur = this.onFocusChange;\n document.onfocus = this.onFocusChange;\n }\n\n componentWillUnmount() {\n document.removeEventListener('visibilitychange', this.onVisibilityChange);\n // document.removeEventListener('blur', this.onFocusChange);\n //this.stopCurrent()\n }\n\n componentDidUpdate(oldProps, oldState) {\n if (oldState.func !== this.state.func) {\n //this.startCurrent()\n }\n }\n\n drawCanvas(lights) {\n const drawStartTime = performance.now();\n\n const leds = this.geometryX.length;\n const ctx = this.refs.canvas.getContext('2d');\n\n ctx.globalCompositeOperation = 'source-over';\n ctx.fillStyle = 'black';\n ctx.fillRect(0, 0, this.props.width, this.props.height);\n\n ctx.globalCompositeOperation = 'lighter';\n\n if (this.state.renderingEnabled) {\n const X = this.geometryX;\n const Y = this.geometryY;\n\n for (let i = 0; i < leds; i++) {\n const [r, g, b] = lights[i];\n\n let SCALE = 4;\n const x = X[i]*SCALE + 5*SCALE;\n const y = Y[i]*SCALE + 5*SCALE;\n\n let power = r + g + b;\n if (power < 0) power = 0;\n\n let lightRadius = (40 + (r + g + b) / (255 * 3) * 80) * 1;\n\n let m = 2;\n if (power < 200) {\n m = 4;\n } else if (power < 100) {\n m = 8;\n } else if (power < 50) {\n m = 16;\n }\n let [or, og, ob] = [r * m, g * m, b * m];\n if (or > 255) or = 255;\n if (og > 255) og = 255;\n if (ob > 255) ob = 255;\n\n ctx.beginPath();\n\n lightRadius = lightRadius / 24;\n ctx.fillStyle = `rgba(${or}, ${og}, ${ob}, 1)`;\n\n // let gradient = ctx.createRadialGradient(x, y, 0, x, y, lightRadius)\n // gradient.addColorStop(0, `rgba(${or}, ${og}, ${ob}, 1)`)\n // // gradient.addColorStop(0.065, `rgba(${or}, ${og}, ${ob}, 1)`)\n // gradient.addColorStop(0.25, `rgba(${r}, ${g}, ${b}, 1)`)\n // // gradient.addColorStop(0.25, `rgba(${r}, ${g}, ${b}, 0.5)`)\n // gradient.addColorStop(0.5, `rgba(${r}, ${g}, ${b}, 0.25)`)\n // // gradient.addColorStop(1, `rgba(${0}, ${0}, ${0}, 1)`)\n // gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`)\n // ctx.fillStyle = gradient\n\n\n ctx.arc(x, y, lightRadius, Math.PI * 2, false);\n ctx.fill();\n }\n\n this.frameCount++;\n\n let drawMilliseconds = performance.now() - drawStartTime;\n let timeSinceLastFPS = performance.now() - this.lastFrameTime;\n if (timeSinceLastFPS > 100) {\n this.lastFPS = 1000 * this.frameCount / timeSinceLastFPS;\n this.frameCount = 0;\n this.lastFrameTime = performance.now();\n }\n\n ctx.fillStyle = 'white';\n ctx.font = \"12px sans-serif\";\n\n ctx.fillText(`Sim overhead FPS: ${Math.floor(1000/drawMilliseconds)}`, 10, 40);\n ctx.fillText(`FPS: ${this.lastFPS.toFixed(1)}`, 10, 20);\n }\n }\n\n __changeSelection() {\n if(this.state.renderingEnabled) {\n this.turnOffSimulation();\n } else {\n this.turnOnSimulation();\n }\n this.setState({renderingEnabled: !this.state.renderingEnabled});\n }\n\n render() {\n return
\n
\n \n
\n \n
\n }\n}"]} \ No newline at end of file diff --git a/public/lightsSimulator.jsx b/public/lightsSimulator.jsx index a4bd19d..df0cb0a 100644 --- a/public/lightsSimulator.jsx +++ b/public/lightsSimulator.jsx @@ -13,6 +13,9 @@ class LightsSimulator extends React.Component { this.lastFrameTime = performance.now(); this.lastFPS = 0; this.frameCount = 0; + + this.onVisibilityChange = this.onVisibilityChange.bind(this); + this.onFocusChange = this.onFocusChange.bind(this); } turnOnSimulation() { @@ -30,22 +33,48 @@ class LightsSimulator extends React.Component { socket.emit('stopSamplingLights', (layout) => {}); } + decodeLedsColorsFromString(encodedLights) { + let bytes = Uint8Array.from(atob(encodedLights), c => c.charCodeAt(0)) + + let byLed = new Array(bytes.length / 3); + for (let i = 0; i < bytes.length/3; i += 1) { + byLed[i] = [bytes[i*3], bytes[i*3 + 1], bytes[i*3 + 2]]; + } + return byLed; + } + + onVisibilityChange() { + if(document.hidden && this.state.renderingEnabled) { + this.turnOffSimulation(); + } else if (!document.hidden && this.state.renderingEnabled) { + this.turnOnSimulation(); + } + } + + onFocusChange() { + debugger; + if(!document.hasFocus() && this.state.renderingEnabled) { + this.turnOffSimulation(); + } else if (document.hasFocus() && this.state.renderingEnabled) { + this.turnOnSimulation(); + } + } + componentDidMount() { - socket.on('lightsSample', (lights) => { + socket.on('lightsSample', (encodedLights) => { + const lights = this.decodeLedsColorsFromString(encodedLights) console.log("Lights data") this.drawCanvas(lights) }); - document.addEventListener('visibilitychange', () =>{ - if(document.hidden && this.state.renderingEnabled) { - this.turnOffSimulation(); - } else if (!document.hidden && this.state.renderingEnabled) { - this.turnOnSimulation(); - } - }) + document.addEventListener('visibilitychange', this.onVisibilityChange); + document.onblur = this.onFocusChange; + document.onfocus = this.onFocusChange; } componentWillUnmount() { + document.removeEventListener('visibilitychange', this.onVisibilityChange); + // document.removeEventListener('blur', this.onFocusChange); //this.stopCurrent() } diff --git a/public/microphoneViewer.js b/public/microphoneViewer.js index 6d360ef..83465aa 100644 --- a/public/microphoneViewer.js +++ b/public/microphoneViewer.js @@ -31,7 +31,8 @@ class MicrophoneViewer extends React.Component { socket.on('micViewerReady', this._initializeState.bind(this)); socket.on('micSample', samples => { - _.each(samples, ({ level, max }) => this.plotEnergyHistogram(level, max)); + // _.each(samples, ({level, max}) => this.plotEnergyHistogram(level, max)) + _.each(samples, sample => this.plotPerBandHistogram(sample)); }); this.createHistogramCanvas(); @@ -62,10 +63,12 @@ class MicrophoneViewer extends React.Component { plotEnergyHistogram(level, max) { let histogram = document.getElementById("music"); - let h = Math.round(level * 50); + level = level / 100; + let HEIGHT = this.canvasCtx.canvas.height; + let h = Math.round(level * HEIGHT); this.canvasCtx.fillStyle = `hsl(${Math.round((1 - h / 50 % 1 + 0) * 255)}, ${50}%, ${50}%)`; // this.canvasCtx.fillStyle = `#ff5500`; - this.canvasCtx.fillRect(300, 50 - h, 2, h); + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, 50 - h, 2, h); // Move all left let imageData = this.canvasCtx.getImageData(2, 0, this.canvasCtx.canvas.width - 1, this.canvasCtx.canvas.height); this.canvasCtx.putImageData(imageData, 0, 0); @@ -75,9 +78,70 @@ class MicrophoneViewer extends React.Component { this.canvasCtx.fillStyle = 'white'; this.canvasCtx.font = "12px monospace"; this.canvasCtx.clearRect(this.canvasCtx.canvas.width - 100, 0, 100, this.canvasCtx.canvas.height); - this.canvasCtx.fillText(`MAX Vol: ${Math.round(max * 100)}`, 310, 15); + this.canvasCtx.fillText(`MAX Vol: ${Math.round(max * 100)}`, this.canvasCtx.canvas.width - 90, 15); // this.canvasCtx.fillText(` Vol: ${Math.round(this.averageVolume*100)}`, 310, 30); - this.canvasCtx.fillText(`REL Vol: ${Math.round(level * 100)}`, 310, 45); + this.canvasCtx.fillText(`REL Vol: ${Math.round(level)}`, this.canvasCtx.canvas.width - 90, 45); + } + + plotPerBandHistogram({ bass, mid, high, onsetbass, onsetmid, onsethigh }) { + let histogram = document.getElementById("music"); + let r = Math.round(bass * 255); + let g = Math.round(mid * 255); + let b = Math.round(high * 255); + let max = 0; + let level = Math.min(1, (r + g + b) / (3 * 255)); + + // let w = 2; + // let HEIGHT = this.canvasCtx.canvas.height; + // let h = Math.round(level * HEIGHT); + // this.canvasCtx.fillStyle = `rgb(${r}, ${g}, ${b})`; + // this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h, w, h); + + // + let MIN = 30; + let w = 2; + let HEIGHT = this.canvasCtx.canvas.height / 3; + let h = Math.round(level * HEIGHT); + this.canvasCtx.globalCompositeOperation = "screen"; + this.canvasCtx.fillStyle = `rgba(${Math.max(MIN, r)}, ${0}, ${0})`; + h = Math.round(r / 255 * HEIGHT); + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h, w, h); + if (onsetbass) { + this.canvasCtx.fillStyle = `#fff`; + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT, w, 5); + } + + h = Math.round(g / 255 * HEIGHT); + this.canvasCtx.fillStyle = `rgba(${0}, ${Math.max(MIN, g)}, ${0})`; + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h + HEIGHT, w, h); + if (onsetmid) { + this.canvasCtx.fillStyle = `#fff`; + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT + HEIGHT, w, 5); + } + + h = Math.round(b / 255 * HEIGHT); + this.canvasCtx.fillStyle = `rgba(${0}, ${0}, ${Math.max(MIN, b)})`; + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h + HEIGHT * 2, w, h); + if (onsethigh) { + this.canvasCtx.fillStyle = `#fff`; + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT + HEIGHT * 2 - 5, w, 5); + } + // + this.canvasCtx.globalCompositeOperation = "source-over"; + + // this.canvasCtx.fillStyle = `#ff5500`; + // Move all left + let imageData = this.canvasCtx.getImageData(w, 0, this.canvasCtx.canvas.width - 1, this.canvasCtx.canvas.height); + this.canvasCtx.putImageData(imageData, 0, 0); + // now clear the right-most pixels: + this.canvasCtx.clearRect(this.canvasCtx.canvas.width - w, 0, w, this.canvasCtx.canvas.height); + + this.canvasCtx.fillStyle = 'white'; + this.canvasCtx.font = "12px monospace"; + this.canvasCtx.clearRect(this.canvasCtx.canvas.width - 100, 0, 100, this.canvasCtx.canvas.height); + this.canvasCtx.fillText(`MAX Vol: ${Math.round(max * 100)}`, this.canvasCtx.canvas.width - 90, 15); + // this.canvasCtx.fillText(` Vol: ${Math.round(this.averageVolume*100)}`, 310, 30); + this.canvasCtx.fillText(`REL Vol: ${Math.round(level * 100)}`, this.canvasCtx.canvas.width - 90, 45); } turnOn() { @@ -95,7 +159,7 @@ class MicrophoneViewer extends React.Component { { className: 'mic-client' }, React.createElement( 'canvas', - { id: 'music', width: '400', height: '50' }, + { id: 'music', width: '800', height: '300' }, 'a' ) ); diff --git a/public/microphoneViewer.js.map b/public/microphoneViewer.js.map index 8df8398..3b4542e 100644 --- a/public/microphoneViewer.js.map +++ b/public/microphoneViewer.js.map @@ -1 +1 @@ -{"version":3,"sources":["microphoneViewer.jsx"],"names":[],"mappings":"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","file":"microphoneViewer.js","sourcesContent":["if (!window.socket) {\n window.socket = io();\n}\n\nclass MicrophoneViewer extends React.Component {\n constructor() {\n super(...arguments)\n\n this.state = {\n micOn: true,\n connected: false,\n }\n }\n\n _initializeState(state) {\n this.setState({\n connected: true,\n })\n }\n\n _stateChange(state) {\n this.setState({\n selected: state.currentProgramName,\n currentConfig: state.currentConfig,\n remoteChange: true\n })\n console.log(state)\n }\n\n componentDidMount() {\n socket.on('micViewerReady', this._initializeState.bind(this));\n\n socket.on('micSample', samples => {\n _.each(samples, ({level, max}) => this.plotEnergyHistogram(level, max))\n });\n\n this.createHistogramCanvas()\n }\n\n createHistogramCanvas() {\n let c = document.getElementById(\"music\");\n this.canvasCtx = c.getContext(\"2d\");\n this.canvasCtx.clearRect(0, 0, this.canvasCtx.canvas.width, this.canvasCtx.canvas.height);\n this.frame = 0;\n }\n\n componentWillUnmount() {\n //this.stopCurrent()\n }\n\n componentDidUpdate(oldProps, oldState) {\n if (oldState.func !== this.state.func) {\n //this.startCurrent()\n }\n }\n\n handleProgramClick(key, ev) {\n ev.preventDefault()\n this.setCurrentProgram(key)\n }\n\n plotEnergyHistogram(level, max) {\n let histogram = document.getElementById(\"music\");\n\n let h = Math.round(level * 50);\n this.canvasCtx.fillStyle = `hsl(${Math.round((1 - h/50 % 1 + 0)*255)}, ${50}%, ${50}%)`;\n // this.canvasCtx.fillStyle = `#ff5500`;\n this.canvasCtx.fillRect(300, 50 - h, 2, h);\n // Move all left\n let imageData = this.canvasCtx.getImageData(2, 0, this.canvasCtx.canvas.width - 1, this.canvasCtx.canvas.height);\n this.canvasCtx.putImageData(imageData, 0, 0);\n // now clear the right-most pixels:\n this.canvasCtx.clearRect(this.canvasCtx.canvas.width - 2, 0, 2, this.canvasCtx.canvas.height);\n\n this.canvasCtx.fillStyle = 'white'\n this.canvasCtx.font = \"12px monospace\";\n this.canvasCtx.clearRect(this.canvasCtx.canvas.width - 100, 0, 100, this.canvasCtx.canvas.height);\n this.canvasCtx.fillText(`MAX Vol: ${Math.round(max*100)}`, 310, 15);\n // this.canvasCtx.fillText(` Vol: ${Math.round(this.averageVolume*100)}`, 310, 30);\n this.canvasCtx.fillText(`REL Vol: ${Math.round(level*100)}`, 310, 45);\n }\n\n turnOn(){\n this.setState({micOn: true});\n alert(\"No se duerme\")\n }\n\n turnOff(){\n this.setState({micOn: false})\n }\n\n render() {\n return
\n a\n
\n }\n}"]} \ No newline at end of file +{"version":3,"sources":["microphoneViewer.jsx"],"names":[],"mappings":"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","file":"microphoneViewer.js","sourcesContent":["if (!window.socket) {\n window.socket = io();\n}\n\nclass MicrophoneViewer extends React.Component {\n constructor() {\n super(...arguments)\n\n this.state = {\n micOn: true,\n connected: false,\n }\n }\n\n _initializeState(state) {\n this.setState({\n connected: true,\n })\n }\n\n _stateChange(state) {\n this.setState({\n selected: state.currentProgramName,\n currentConfig: state.currentConfig,\n remoteChange: true\n })\n console.log(state)\n }\n\n componentDidMount() {\n socket.on('micViewerReady', this._initializeState.bind(this));\n\n socket.on('micSample', samples => {\n // _.each(samples, ({level, max}) => this.plotEnergyHistogram(level, max))\n _.each(samples, sample => this.plotPerBandHistogram(sample))\n });\n\n this.createHistogramCanvas()\n }\n\n createHistogramCanvas() {\n let c = document.getElementById(\"music\");\n this.canvasCtx = c.getContext(\"2d\");\n this.canvasCtx.clearRect(0, 0, this.canvasCtx.canvas.width, this.canvasCtx.canvas.height);\n this.frame = 0;\n }\n\n componentWillUnmount() {\n //this.stopCurrent()\n }\n\n componentDidUpdate(oldProps, oldState) {\n if (oldState.func !== this.state.func) {\n //this.startCurrent()\n }\n }\n\n handleProgramClick(key, ev) {\n ev.preventDefault()\n this.setCurrentProgram(key)\n }\n\n plotEnergyHistogram(level, max) {\n let histogram = document.getElementById(\"music\");\n\n level = level /100;\n let HEIGHT = this.canvasCtx.canvas.height;\n let h = Math.round(level * HEIGHT);\n this.canvasCtx.fillStyle = `hsl(${Math.round((1 - h/50 % 1 + 0)*255)}, ${50}%, ${50}%)`;\n // this.canvasCtx.fillStyle = `#ff5500`;\n this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, 50 - h, 2, h);\n // Move all left\n let imageData = this.canvasCtx.getImageData(2, 0, this.canvasCtx.canvas.width - 1, this.canvasCtx.canvas.height);\n this.canvasCtx.putImageData(imageData, 0, 0);\n // now clear the right-most pixels:\n this.canvasCtx.clearRect(this.canvasCtx.canvas.width - 2, 0, 2, this.canvasCtx.canvas.height);\n\n this.canvasCtx.fillStyle = 'white'\n this.canvasCtx.font = \"12px monospace\";\n this.canvasCtx.clearRect(this.canvasCtx.canvas.width - 100, 0, 100, this.canvasCtx.canvas.height);\n this.canvasCtx.fillText(`MAX Vol: ${Math.round(max*100)}`, this.canvasCtx.canvas.width - 90, 15);\n // this.canvasCtx.fillText(` Vol: ${Math.round(this.averageVolume*100)}`, 310, 30);\n this.canvasCtx.fillText(`REL Vol: ${Math.round(level)}`, this.canvasCtx.canvas.width - 90, 45);\n }\n\n plotPerBandHistogram({bass, mid, high, onsetbass, onsetmid, onsethigh}) {\n let histogram = document.getElementById(\"music\");\n let r = Math.round(bass*255);\n let g = Math.round(mid*255);\n let b = Math.round(high*255);\n let max = 0;\n let level = Math.min(1, (r+g+b)/(3*255));\n\n\n\n // let w = 2;\n // let HEIGHT = this.canvasCtx.canvas.height;\n // let h = Math.round(level * HEIGHT);\n // this.canvasCtx.fillStyle = `rgb(${r}, ${g}, ${b})`;\n // this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h, w, h);\n\n //\n let MIN = 30;\n let w = 2;\n let HEIGHT = this.canvasCtx.canvas.height / 3;\n let h = Math.round(level * HEIGHT);\n this.canvasCtx.globalCompositeOperation = \"screen\";\n this.canvasCtx.fillStyle = `rgba(${Math.max(MIN,r)}, ${0}, ${0})`;\n h = Math.round((r/255) * HEIGHT);\n this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h, w, h);\n if(onsetbass) {\n this.canvasCtx.fillStyle = `#fff`;\n this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT, w, 5);\n }\n\n h = Math.round((g/255) * HEIGHT);\n this.canvasCtx.fillStyle = `rgba(${0}, ${Math.max(MIN,g)}, ${0})`;\n this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h +HEIGHT, w, h);\n if(onsetmid) {\n this.canvasCtx.fillStyle = `#fff`;\n this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT + HEIGHT, w, 5);\n }\n\n h = Math.round((b/255) * HEIGHT);\n this.canvasCtx.fillStyle = `rgba(${0}, ${0}, ${Math.max(MIN,b)})`;\n this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h+HEIGHT*2, w, h);\n if(onsethigh) {\n this.canvasCtx.fillStyle = `#fff`;\n this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT + HEIGHT*2-5, w, 5);\n }\n //\n this.canvasCtx.globalCompositeOperation = \"source-over\";\n\n\n\n // this.canvasCtx.fillStyle = `#ff5500`;\n // Move all left\n let imageData = this.canvasCtx.getImageData(w, 0, this.canvasCtx.canvas.width - 1, this.canvasCtx.canvas.height);\n this.canvasCtx.putImageData(imageData, 0, 0);\n // now clear the right-most pixels:\n this.canvasCtx.clearRect(this.canvasCtx.canvas.width - w, 0, w, this.canvasCtx.canvas.height);\n\n this.canvasCtx.fillStyle = 'white'\n this.canvasCtx.font = \"12px monospace\";\n this.canvasCtx.clearRect(this.canvasCtx.canvas.width - 100, 0, 100, this.canvasCtx.canvas.height);\n this.canvasCtx.fillText(`MAX Vol: ${Math.round(max*100)}`, this.canvasCtx.canvas.width - 90, 15);\n // this.canvasCtx.fillText(` Vol: ${Math.round(this.averageVolume*100)}`, 310, 30);\n this.canvasCtx.fillText(`REL Vol: ${Math.round(level*100)}`, this.canvasCtx.canvas.width - 90, 45);\n }\n\n turnOn(){\n this.setState({micOn: true});\n alert(\"No se duerme\")\n }\n\n turnOff(){\n this.setState({micOn: false})\n }\n\n render() {\n return
\n a\n
\n }\n}"]} \ No newline at end of file diff --git a/public/microphoneViewer.jsx b/public/microphoneViewer.jsx index e3a72bf..96021aa 100644 --- a/public/microphoneViewer.jsx +++ b/public/microphoneViewer.jsx @@ -31,7 +31,8 @@ class MicrophoneViewer extends React.Component { socket.on('micViewerReady', this._initializeState.bind(this)); socket.on('micSample', samples => { - _.each(samples, ({level, max}) => this.plotEnergyHistogram(level, max)) + // _.each(samples, ({level, max}) => this.plotEnergyHistogram(level, max)) + _.each(samples, sample => this.plotPerBandHistogram(sample)) }); this.createHistogramCanvas() @@ -62,10 +63,12 @@ class MicrophoneViewer extends React.Component { plotEnergyHistogram(level, max) { let histogram = document.getElementById("music"); - let h = Math.round(level * 50); + level = level /100; + let HEIGHT = this.canvasCtx.canvas.height; + let h = Math.round(level * HEIGHT); this.canvasCtx.fillStyle = `hsl(${Math.round((1 - h/50 % 1 + 0)*255)}, ${50}%, ${50}%)`; // this.canvasCtx.fillStyle = `#ff5500`; - this.canvasCtx.fillRect(300, 50 - h, 2, h); + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, 50 - h, 2, h); // Move all left let imageData = this.canvasCtx.getImageData(2, 0, this.canvasCtx.canvas.width - 1, this.canvasCtx.canvas.height); this.canvasCtx.putImageData(imageData, 0, 0); @@ -75,9 +78,74 @@ class MicrophoneViewer extends React.Component { this.canvasCtx.fillStyle = 'white' this.canvasCtx.font = "12px monospace"; this.canvasCtx.clearRect(this.canvasCtx.canvas.width - 100, 0, 100, this.canvasCtx.canvas.height); - this.canvasCtx.fillText(`MAX Vol: ${Math.round(max*100)}`, 310, 15); + this.canvasCtx.fillText(`MAX Vol: ${Math.round(max*100)}`, this.canvasCtx.canvas.width - 90, 15); // this.canvasCtx.fillText(` Vol: ${Math.round(this.averageVolume*100)}`, 310, 30); - this.canvasCtx.fillText(`REL Vol: ${Math.round(level*100)}`, 310, 45); + this.canvasCtx.fillText(`REL Vol: ${Math.round(level)}`, this.canvasCtx.canvas.width - 90, 45); + } + + plotPerBandHistogram({bass, mid, high, onsetbass, onsetmid, onsethigh}) { + let histogram = document.getElementById("music"); + let r = Math.round(bass*255); + let g = Math.round(mid*255); + let b = Math.round(high*255); + let max = 0; + let level = Math.min(1, (r+g+b)/(3*255)); + + + + // let w = 2; + // let HEIGHT = this.canvasCtx.canvas.height; + // let h = Math.round(level * HEIGHT); + // this.canvasCtx.fillStyle = `rgb(${r}, ${g}, ${b})`; + // this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h, w, h); + + // + let MIN = 30; + let w = 2; + let HEIGHT = this.canvasCtx.canvas.height / 3; + let h = Math.round(level * HEIGHT); + this.canvasCtx.globalCompositeOperation = "screen"; + this.canvasCtx.fillStyle = `rgba(${Math.max(MIN,r)}, ${0}, ${0})`; + h = Math.round((r/255) * HEIGHT); + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h, w, h); + if(onsetbass) { + this.canvasCtx.fillStyle = `#fff`; + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT, w, 5); + } + + h = Math.round((g/255) * HEIGHT); + this.canvasCtx.fillStyle = `rgba(${0}, ${Math.max(MIN,g)}, ${0})`; + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h +HEIGHT, w, h); + if(onsetmid) { + this.canvasCtx.fillStyle = `#fff`; + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT + HEIGHT, w, 5); + } + + h = Math.round((b/255) * HEIGHT); + this.canvasCtx.fillStyle = `rgba(${0}, ${0}, ${Math.max(MIN,b)})`; + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT - h+HEIGHT*2, w, h); + if(onsethigh) { + this.canvasCtx.fillStyle = `#fff`; + this.canvasCtx.fillRect(this.canvasCtx.canvas.width - 100, HEIGHT + HEIGHT*2-5, w, 5); + } + // + this.canvasCtx.globalCompositeOperation = "source-over"; + + + + // this.canvasCtx.fillStyle = `#ff5500`; + // Move all left + let imageData = this.canvasCtx.getImageData(w, 0, this.canvasCtx.canvas.width - 1, this.canvasCtx.canvas.height); + this.canvasCtx.putImageData(imageData, 0, 0); + // now clear the right-most pixels: + this.canvasCtx.clearRect(this.canvasCtx.canvas.width - w, 0, w, this.canvasCtx.canvas.height); + + this.canvasCtx.fillStyle = 'white' + this.canvasCtx.font = "12px monospace"; + this.canvasCtx.clearRect(this.canvasCtx.canvas.width - 100, 0, 100, this.canvasCtx.canvas.height); + this.canvasCtx.fillText(`MAX Vol: ${Math.round(max*100)}`, this.canvasCtx.canvas.width - 90, 15); + // this.canvasCtx.fillText(` Vol: ${Math.round(this.averageVolume*100)}`, 310, 30); + this.canvasCtx.fillText(`REL Vol: ${Math.round(level*100)}`, this.canvasCtx.canvas.width - 90, 45); } turnOn(){ @@ -91,7 +159,7 @@ class MicrophoneViewer extends React.Component { render() { return
- a + a
} } \ No newline at end of file diff --git a/public/test.html b/public/test.html index 19731f7..45388c8 100644 --- a/public/test.html +++ b/public/test.html @@ -37,8 +37,8 @@ #music { background-color: #333; - height: 100px; - width: 400px; + /*height: 100px;*/ + /*width: 400px;*/ } .state { diff --git a/public/warro.html b/public/warro.html index 02e205f..15ee769 100644 --- a/public/warro.html +++ b/public/warro.html @@ -51,7 +51,7 @@ #music { background-color: #333; - width: 400px; + /*width: 400px;*/ } .state { diff --git a/server.js b/server.js index b646a1a..83c3ba3 100644 --- a/server.js +++ b/server.js @@ -1,17 +1,25 @@ const _ = require('lodash'); - const fs = require('fs'); -// const privateKey = fs.readFileSync('sslcert/server.key', 'utf8'); -// const certificate = fs.readFileSync('sslcert/server.crt', 'utf8'); -// const credentials = {key: privateKey, cert: certificate}; - const express = require('express'); +const { Buffer } = require('buffer'); + +require('./volume-broadcaster') +let soundBroadcast = require("./sound-broadcast"); + const app = express(); const http = require('http').createServer(app); + +// // HTTPS +// const privateKey = fs.readFileSync('sslcert/server.key', 'utf8'); +// const certificate = fs.readFileSync('sslcert/server.crt', 'utf8'); +// const credentials = {key: privateKey, cert: certificate}; // const httpsServer = require('https').createServer(credentials, app); -require('./volume-broadcaster') +const lightsToByteString = (ledsColorArray) => { + let bytes = _.flatten(ledsColorArray); + return Buffer.from(bytes).toString('base64'); +} exports.createRemoteControl = function(lightProgram, deviceMultiplexer) { app.use(express.static('public')) @@ -34,17 +42,42 @@ exports.createRemoteControl = function(lightProgram, deviceMultiplexer) { const io = require('socket.io').listen(http); - let lastVolumes = []; + let lastRawVolumes = []; + let lastBands = []; let flushVolume = _.throttle(() => { io.volatile.emit('micSample', lastVolumes) lastVolumes = []; - }, 100) + }, 30) + + let avg = 3; + + let last = new Date(); + + soundBroadcast.on('processedaudioframe', (frame) => { + let timeSinceLastFrame = new Date() - last; + if(timeSinceLastFrame > 50) { + console.log(`SOUND DROPPING FRAMES: Last processedaudioframe frame: ${timeSinceLastFrame}ms ago`.red) + } + last = new Date() - require("./sound-broadcast").on('volume', volData => { - lastVolumes.push(volData); - flushVolume(); + let {center: {filteredBands, movingStats: {rms: {normalizedValue}}}} = frame; + lastRawVolumes.push({... _.mapValues(filteredBands, b => b.movingStats.rms.normalizedValue), all: normalizedValue}); + + if(lastRawVolumes.length >= avg) { + let avgLastVolumes = { + bass: _.sum(_.map(lastRawVolumes, 'bass'))/avg, + mid: _.sum(_.map(lastRawVolumes, 'mid'))/avg, + high: _.sum(_.map(lastRawVolumes, 'high'))/avg, + all: _.sum(_.map(lastRawVolumes, 'all'))/avg + } + + lastVolumes.push(avgLastVolumes); + flushVolume(); + + lastRawVolumes.shift(); + } }) io.on('connection', (socket) => { @@ -57,24 +90,26 @@ exports.createRemoteControl = function(lightProgram, deviceMultiplexer) { }) socket.on('startSamplingLights', (ack) => { + console.log('[ON] Web client sampling lights data'.green) simulating = true - console.log('Client requested startSamplingLights') ack(lightProgram.layout) }) + socket.on('stopSamplingLights', () => { - console.log('Client stoppedSamplingLights') - return simulating = false; + console.log('[OFF] Web client stopped sampling lights'.gray) + simulating = false; }) socket.on('restartProgram', () => lightProgram.restart()) let lightsCbk = lights => { if(simulating) { - socket.volatile.emit('lightsSample', lights) + let encodedColors = lightsToByteString(lights); + socket.volatile.emit('lightsSample', encodedColors) } } - console.log("Remote control connnected") + console.log("[ON] Remote control connnected".green) lightProgram.onLights(lightsCbk) // socket.on('reconnect', function () { @@ -93,7 +128,7 @@ exports.createRemoteControl = function(lightProgram, deviceMultiplexer) { }) socket.on('disconnect', function () { - console.log("Remote control DISCONNNECTED") + console.log("[OFF] Remote control DISCONNNECTED".gray) lightProgram.removeOnLights(lightsCbk) });