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