diff --git a/.gitignore b/.gitignore index 58b805fe..2df424fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .DS_Store -node_modules/ \ No newline at end of file +node_modules/ +/.settings/ +/.project diff --git a/.project b/.project new file mode 100644 index 00000000..bae546d0 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + blinkstick-node + + + + + + org.eclipse.wst.validation.validationbuilder + + + + + + org.eclipse.wst.jsdt.core.jsNature + + diff --git a/.settings/.jsdtscope b/.settings/.jsdtscope new file mode 100644 index 00000000..d7991035 --- /dev/null +++ b/.settings/.jsdtscope @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/.settings/org.eclipse.wst.jsdt.ui.superType.container b/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100644 index 00000000..3bd5d0a4 --- /dev/null +++ b/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff --git a/.settings/org.eclipse.wst.jsdt.ui.superType.name b/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100644 index 00000000..05bd71b6 --- /dev/null +++ b/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window \ No newline at end of file diff --git a/examples/connect/connect.js b/examples/connect/connect.js index d0ad638a..b53f3f82 100644 --- a/examples/connect/connect.js +++ b/examples/connect/connect.js @@ -7,7 +7,7 @@ if (process.argv[2] == null) { console.log("Please supply access code as an argument"); - return; + process.exit(1); } var faye = require('faye'), diff --git a/examples/flex_stream/TODO.txt b/examples/flex_stream/TODO.txt new file mode 100644 index 00000000..bdb4b947 --- /dev/null +++ b/examples/flex_stream/TODO.txt @@ -0,0 +1,14 @@ +TODO +Add audio to notifiers +Multi-channel support +- Might require firmware changes to setColors() to send all channels in one call. +RGBA layers +Refactor as npm project +RGB Emulator Matrix REM (websockets - standalone or in parallel with BlinkStick) + +Make web UI prettier + + DONE + Specify width and height for matrices, setSize(W,H) getSize()==>WxH + - Sharp package's rasterized buffers already compatible with rasterized matrices + Add more notification images (s3d, vrip, twitter, etc) \ No newline at end of file diff --git a/examples/flex_stream/ambilight.js b/examples/flex_stream/ambilight.js new file mode 100644 index 00000000..8db21d1c --- /dev/null +++ b/examples/flex_stream/ambilight.js @@ -0,0 +1,39 @@ +//Ambient display (ambilight) based on flex_stream.js +//Real-time streaming of desktop to BlinkStick Flex and Pro +//User defined OnFrame() samples the desktop, and morphs scaled frames to BlinkStick +//Minimum Requirements: +//- Latest version of nodejs (tested with v8.9.3) +//- Latest blinkstick, screenshot-desktop and sharp npm packages (all cross-platform) +//For Windows, Linux and Mac + +module.exports = { + init: function() { + init(); + } +} + +const flex_stream = require("./flex_stream.js"); +const screenshot = require('screenshot-desktop'); //Available at npmjs.com +const sharp = require('sharp'); //Available at npmjs.com + + +//Stream scaled desktop (size x 1) to BlinkStick via async futures pipeline +function ambilight(){ + screenshot().then((img) => { + sharp(img).resize(flex_stream.getWidth(),flex_stream.getHeight()).ignoreAspectRatio().raw().toBuffer().then(data => { + flex_stream.setAlpha(0.1); + flex_stream.produceFrame(data); + }) + }); +} + +//Configure stream + +function init(){ + flex_stream.setSize(8,1); + flex_stream.setProducerFramerate(5); + flex_stream.setConsumerFramerate(60); + flex_stream.setOnFrame(ambilight); +} + +init(); diff --git a/examples/flex_stream/aurora.js b/examples/flex_stream/aurora.js new file mode 100644 index 00000000..06206fd7 --- /dev/null +++ b/examples/flex_stream/aurora.js @@ -0,0 +1,54 @@ +//Aurora Borealis ambience based on flex_stream.js +//For Windows, Linux and Mac + +module.exports = { + init: function() { + init(); + } +} + +const os = require("os"); +const flex_stream = require("./flex_stream.js"); + + +var frame = flex_stream.newFrame(); +function aurora() { + //Aurora + a = Math.random(); + flex_stream.setProducerFramerate(a*4+4); + flex_stream.setAlpha(.01+(a/200)); + + //Borealis + for (i=0; i.75) + { + r=0; g=0; b=0; + } + + if (Math.random()>.5) + { + frame[i*3+0] = Math.floor(r); //R + frame[i*3+1] = Math.floor(g); //G + frame[i*3+2] = Math.floor(b); //B + } + } + flex_stream.produceFrame(frame); +} + +//Configure stream + +function init(){ + flex_stream.setSize(8,1); + flex_stream.setProducerFramerate(60); + flex_stream.setConsumerFramerate(60); + flex_stream.setOnFrame(aurora); +} + +init(); + diff --git a/examples/flex_stream/cpu_meter.js b/examples/flex_stream/cpu_meter.js new file mode 100644 index 00000000..466ab49c --- /dev/null +++ b/examples/flex_stream/cpu_meter.js @@ -0,0 +1,79 @@ +//CPU load meter based on flex_stream.js +//User defined OnFrame() is a particle trail emitter to indicate CPU load. +//For Windows, Linux and Mac + +module.exports = { + init: function() { + init(); + } +} + +const os = require("os"); +const flex_stream = require("./flex_stream.js"); + +var framerate = 30; // Varies with CPU load + +var startMeasure = cpuLoad(); +var percentageCPU = 0; +var cpu_avg = 0; +var pos = 1; +var speed = 1; + +function cpuMeter() { + var endMeasure = cpuLoad(); + var idleDifference = endMeasure.idle - startMeasure.idle; + var totalDifference = endMeasure.total - startMeasure.total; + + startMeasure = endMeasure; + + if (totalDifference != 0) + percentageCPU = 100 - (100 * idleDifference / totalDifference); + + cpu_avg = (cpu_avg+percentageCPU)/2; + + //Vary the producer framerate by percentage CPU load (15 to 60 fps) + framerate = cpu_avg*0.45+15; + + //Bounce particle off edges of LED strip + if (pos<=0 || pos>=flex_stream.getSize()-1) + speed =-speed; + pos += speed; + + var frame = flex_stream.newFrame(); + //Vary particle colour by CPU load (green to amber to red) + frame[pos*3+0] = Math.floor(cpu_avg*2.5)+5; //R + frame[pos*3+1] = 100-Math.floor(cpu_avg); //G + frame[pos*3+2] = 2; //B + + flex_stream.setProducerFramerate(framerate); + flex_stream.setAlpha(0.25); + flex_stream.produceFrame(frame); +} + +//CPU load +function cpuLoad() { + var totalIdle = 0; + var totalTick = 0; + var cpus = os.cpus(); + + for(var i = 0, len = cpus.length; i < len; i++) { + var cpu = cpus[i]; + for(type in cpu.times) { + totalTick += cpu.times[type]; + } + totalIdle += cpu.times.idle; + } + return {idle: totalIdle / cpus.length, total: totalTick / cpus.length}; +} + +//Configure stream + +function init(){ + flex_stream.setSize(8,1); + flex_stream.setProducerFramerate(30); + flex_stream.setConsumerFramerate(60); + flex_stream.setOnFrame(cpuMeter); +} + +init(); + diff --git a/examples/flex_stream/favicon.ico b/examples/flex_stream/favicon.ico new file mode 100644 index 00000000..593a9ba7 Binary files /dev/null and b/examples/flex_stream/favicon.ico differ diff --git a/examples/flex_stream/fireplace.js b/examples/flex_stream/fireplace.js new file mode 100644 index 00000000..61f54000 --- /dev/null +++ b/examples/flex_stream/fireplace.js @@ -0,0 +1,47 @@ +//Fireplace ambience based on flex_stream.js +//For Windows, Linux and Mac + +const os = require("os"); +const flex_stream = require("./flex_stream.js"); + +module.exports = { + init: function() { + init(); + } +} + +var frame = flex_stream.newFrame(); + +function fireplace() { + + for (i=0; i0){ //Check if new frame available + var rgb = stream_buffer.shift(); + var grb = convert_grb(rgb); + currentFrame = grb; + } + if (currentFrame != null) + morphFrame(currentFrame); //Morph to the current frame +} + +//Morph current frame over composite frame +function morphFrame(current) +{ + if (composite == null || alpha == 1) + composite = current; //Initialize composite frame + + //Morph to the current frame with composite (additive alpha blending function) + if (alpha>0){ + for (var i = 0; i= getSize()+20) pos=0; + var frame = newFrame(); + if(pos < getSize()){ + frame[pos*3+0] = 255; //R + frame[pos*3+1] = 255; //G + frame[pos*3+2] = 255; //B + } + produceFrame(frame); +}; + +//Fade to Black used with fadeOut() +var fadeToBlack = function(){ + var frame = newFrame(); + produceFrame(frame); +}; + +function init(){ + setSize(8,1); + setProducerFramerate(20); + setConsumerFramerate(60); + setAlpha(0.05); + setOnFrame(signature); + +} + +if (device) +{ + init(); + producer(); + consumer(); +} diff --git a/examples/flex_stream/flex_stream_webserver.html b/examples/flex_stream/flex_stream_webserver.html new file mode 100644 index 00000000..2bf0a5bf --- /dev/null +++ b/examples/flex_stream/flex_stream_webserver.html @@ -0,0 +1,29 @@ + + + +Flex Stream Shaders + + + +

Flex Stream Shaders

+ +
+ +
+ PAUSE +
+ RESUME +
+ + \ No newline at end of file diff --git a/examples/flex_stream/flex_stream_webserver.js b/examples/flex_stream/flex_stream_webserver.js new file mode 100644 index 00000000..895ee0af --- /dev/null +++ b/examples/flex_stream/flex_stream_webserver.js @@ -0,0 +1,83 @@ +//Very simple server to switch between examples +//eg. http://localhost:5000 for the client UI +//eg. http://localhost:5000/?example=ambilight for a specific example +//Default is the flex stream signature + +const express = require('express'); +var path = require('path'); +const flex_stream = require("./flex_stream.js"); +const ambilight = require("./ambilight.js"); +const notifier = require("./notifier.js"); +const unicorn = require("./unicorn.js"); +const fireplace = require("./fireplace.js"); +const aurora = require("./aurora.js"); +const timesquare = require("./timesquare.js"); +const cpu_meter = require("./cpu_meter.js"); //This starts first + +var app = express() + +app.get('/', function (req, res) { + var shader = req.query.shader; + + + var ui = "/flex_stream_webserver.html"; + + switch(shader) { + case "cpu_meter": + cpu_meter.init(); + break; + case "notifier": + var filename = path.join(__dirname + "/" + req.query.filename); + notifier.init(filename, .3); // .3 secs + break; + case "aurora": + aurora.init(); + break; + case "unicorn": + unicorn.init(); + break; + case "fireplace": + fireplace.init(); + break; + case "ambilight": + ambilight.init(); + break; + case "timesquare": + timesquare.init(); + break; + case "stop": + flex_stream.stop(); + break; + case "start": + flex_stream.start(); + break; + case "crossFadeOn": + flex_stream.setCrossFade(true); + break; + case "crossFadeOff": + flex_stream.setCrossFade(false); + break; + case "clear": + flex_stream.fadeOut(); + break; + default: + flex_stream.init(); + example = "default" + } + + res.sendfile(path.join(__dirname + ui)); +}) + +app.get('/favicon.ico', function (req, res) { + res.sendfile(path.join(__dirname + '/favicon.ico')); +}) + +app.get('/img/flex_stream.png', function (req, res) { + res.sendfile(path.join(__dirname + '/img/flex_stream.png')); +}) + + +var port = process.env.PORT || 5000; + +app.listen(port, function() { +}); diff --git a/examples/flex_stream/img/flex_stream.jpg b/examples/flex_stream/img/flex_stream.jpg new file mode 100644 index 00000000..b782e8cf Binary files /dev/null and b/examples/flex_stream/img/flex_stream.jpg differ diff --git a/examples/flex_stream/img/flex_stream.png b/examples/flex_stream/img/flex_stream.png new file mode 100644 index 00000000..fa1d3632 Binary files /dev/null and b/examples/flex_stream/img/flex_stream.png differ diff --git a/examples/flex_stream/img/s3d.jpg b/examples/flex_stream/img/s3d.jpg new file mode 100644 index 00000000..c3840203 Binary files /dev/null and b/examples/flex_stream/img/s3d.jpg differ diff --git a/examples/flex_stream/img/twitter.jpg b/examples/flex_stream/img/twitter.jpg new file mode 100644 index 00000000..8351aa29 Binary files /dev/null and b/examples/flex_stream/img/twitter.jpg differ diff --git a/examples/flex_stream/img/vrip.jpg b/examples/flex_stream/img/vrip.jpg new file mode 100644 index 00000000..a0b6ec1f Binary files /dev/null and b/examples/flex_stream/img/vrip.jpg differ diff --git a/examples/flex_stream/notifier.js b/examples/flex_stream/notifier.js new file mode 100644 index 00000000..95064460 --- /dev/null +++ b/examples/flex_stream/notifier.js @@ -0,0 +1,60 @@ +//Image Notifiers +//User defined OnFrame() converts images, and morphs scaled frames to BlinkStick +//Minimum Requirements: +//- Latest version of nodejs (tested with v8.9.3) +//- Latest blinkstick, sharp npm packages (all cross-platform) +//For Windows, Linux and Mac + +module.exports = { + init: function(filename, sec) { + init(filename, sec); + } +} + +const flex_stream = require("./flex_stream.js"); +const sharp = require('sharp'); //Available at npmjs.com +var frame = null; +var notifying = false; +var num_frames = 0; //Default static image. + +//Stream scaled image (W x H) to BlinkStick +function notifier(){ + if (num_frames-- > 0) + { + flex_stream.setAlpha(.2); + flex_stream.produceFrame(frame); + } + else + { + flex_stream.restoreOnFrame(); + notifying = false; + } +} + +//Configure stream + +function init(filename, sec){ + + if (sec >= 0) + num_frames = sec*60; + + if(!notifying){ + notifying = true; + flex_stream.saveOnFrame(); + } + + //Scale image (W x H) + sharp(filename).resize(flex_stream.getWidth(),flex_stream.getHeight()).ignoreAspectRatio().raw().toBuffer().then(data => { + frame = data; + flex_stream.setSize(8,1); + flex_stream.setProducerFramerate(60); + flex_stream.setConsumerFramerate(60); + flex_stream.setOnFrame(notifier); + }); +} + +//Run from command line +if (!module.parent) + init(process.argv[2], process.argv[3]); + + diff --git a/examples/flex_stream/timesquare.js b/examples/flex_stream/timesquare.js new file mode 100644 index 00000000..08b2a2db --- /dev/null +++ b/examples/flex_stream/timesquare.js @@ -0,0 +1,54 @@ +//Times Square ambience based on flex_stream.js +//For Windows, Linux and Mac + +module.exports = { + init: function() { + init(); + } +} + +const os = require("os"); +const flex_stream = require("./flex_stream.js"); + +var frame = flex_stream.newFrame(); + +function timesquare() { + // Scrolling random patterns + var off = 0; + var amp = 150; + var r = Math.random()*amp+off; + var g = Math.random()*amp+off; + var b = Math.random()*amp+off; + var size = flex_stream.getSize(); + + //Shift() not supported for Uint8Arrays, so .. + for (i=1;i.5){ + r = 0; g = 0; b = 0; + } + + //Push() not supported for Uint8Arrays, so .. + frame[(size-1)*3+0] = Math.floor(r); //R + frame[(size-1)*3+1] = Math.floor(g); //G + frame[(size-1)*3+2] = Math.floor(b); //B + + flex_stream.setAlpha(0.1); + flex_stream.produceFrame(frame); +} + +//Configure stream + +function init(){ + flex_stream.setSize(8,1); + flex_stream.setProducerFramerate(8); + flex_stream.setConsumerFramerate(60); + flex_stream.setOnFrame(timesquare); +} + +init(); diff --git a/examples/flex_stream/unicorn.js b/examples/flex_stream/unicorn.js new file mode 100644 index 00000000..9a67367f --- /dev/null +++ b/examples/flex_stream/unicorn.js @@ -0,0 +1,43 @@ +//Aurora Borealis ambience based on flex_stream.js +//For Windows, Linux and Mac + +module.exports = { + init: function() { + init(); + } +} + +const os = require("os"); +const flex_stream = require("./flex_stream.js"); + +var frame = flex_stream.newFrame(); + +function unicorn() { + // Unicorn rainbow happy joy + var off = 0; + var amp = 150; + + for (i=0; i