From 8fd0256a43c6f9b4d4623e372d83fbb712ed8787 Mon Sep 17 00:00:00 2001
From: stelian56
Date: Fri, 20 Feb 2015 17:08:02 -0500
Subject: [PATCH] Euler angles-based grid
---
bmconfig.json | 11 +-
bmserver.py => bodymusic.py | 21 +-
css/bm.css | 4 -
css/bodymusic.css | 75 +++
html/bm.html | 26 -
html/bodymusic.html | 11 +
html/play.html | 63 ---
js/bm.js | 77 ---
js/bodymusic.js | 600 ++++++++++++++++++++
log/2015-02-14.1.log | 1042 -----------------------------------
log/2015-02-20.91.log | 837 ++++++++++++++++++++++++++++
11 files changed, 1538 insertions(+), 1229 deletions(-)
rename bmserver.py => bodymusic.py (96%)
delete mode 100644 css/bm.css
create mode 100644 css/bodymusic.css
delete mode 100644 html/bm.html
create mode 100644 html/bodymusic.html
delete mode 100644 html/play.html
delete mode 100644 js/bm.js
create mode 100644 js/bodymusic.js
delete mode 100644 log/2015-02-14.1.log
create mode 100644 log/2015-02-20.91.log
diff --git a/bmconfig.json b/bmconfig.json
index 01cea8e..c26a5fc 100644
--- a/bmconfig.json
+++ b/bmconfig.json
@@ -4,14 +4,9 @@
"maxUpdateRate": 100,
"readings": [
{
- "name": "quat",
- "command": "getTaredOrientationAsQuaternion",
- "components": ["q0", "q1", "q2", "q3"]
- },
- {
- "name": "acc",
- "command": "getCorrectedAccelerometerVector",
- "components": ["x", "y", "z"]
+ "name": "angles",
+ "command": "getTaredOrientationAsEulerAngles",
+ "components": ["pitch", "yaw", "roll"]
}
]
},
diff --git a/bmserver.py b/bodymusic.py
similarity index 96%
rename from bmserver.py
rename to bodymusic.py
index 30ec432..e117940 100644
--- a/bmserver.py
+++ b/bodymusic.py
@@ -16,7 +16,6 @@
config_file_name = "bmconfig.json"
log_dir = "log"
-log_file_regex = re.compile(".+\.(.+)\.log")
class BMHttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def __init__(self, on_start, on_stop, on_playback, *args):
@@ -165,15 +164,19 @@ def __init__(self, config, update_rate, on_sensor_data, stop_event):
self.sensors = None
def create_log_file(self):
- file_names = glob.glob("%s/*.log" % log_dir)
+ today = datetime.date.today()
+ file_names = glob.glob("%s/%s.*.log" % (log_dir, today))
file_count = len(file_names)
- suffix = "1"
+ suffix = 1
if file_count > 0:
- file_names.sort()
- last_file_name = file_names.pop()
- match = log_file_regex.match(last_file_name)
- suffix = int(match.group(1)) + 1
- file_path = "%s/%s.%s.log" % (log_dir, datetime.date.today(), suffix)
+ regex = re.compile(".+\.(.+)\.log")
+ suffixes = []
+ for file_name in file_names:
+ match = regex.match(file_name)
+ suffixes.append(int(match.group(1)))
+ suffixes.sort()
+ suffix = suffixes.pop() + 1
+ file_path = "%s/%s.%s.log" % (log_dir, today, suffix)
dir_name = os.path.dirname(file_path)
if not os.path.exists(dir_name):
os.makedirs(dir_name)
@@ -183,7 +186,6 @@ def init(self):
self.log_file = self.create_log_file()
if not self.log_file:
return False
- print "Started logging to %s" % self.log_file.name
device_list = api.getComPorts(filter = api.TSS_FIND_DNG|api.TSS_FIND_WL)
for device_port in device_list:
com_port, device_name, device_type = device_port
@@ -231,6 +233,7 @@ def init(self):
eval(expression)
sensor.startStreaming()
print "Started sensors at update rate %dHz" % self.update_rate
+ print "Started logging to %s" % self.log_file.name
return True
def stop(self):
diff --git a/css/bm.css b/css/bm.css
deleted file mode 100644
index 5222bf8..0000000
--- a/css/bm.css
+++ /dev/null
@@ -1,4 +0,0 @@
-.update_rate {
- width: 60px;
-}
-
\ No newline at end of file
diff --git a/css/bodymusic.css b/css/bodymusic.css
new file mode 100644
index 0000000..be97902
--- /dev/null
+++ b/css/bodymusic.css
@@ -0,0 +1,75 @@
+body {
+margin: 10px;
+}
+.bodymusic_ribbon {
+width: 100%;
+margin-top: 10px;
+margin-bottom: 10px;
+}
+.bodymusic_legendicon {
+display: inline-block;
+width: 15px;
+height: 15px;
+margin-left: 15px;
+}
+.bodymusic_legendlabel {
+margin-left: 3px;
+}
+.bodymusic_staff {
+position: relative;
+width: 100%;
+border: 1px solid black;
+}
+.bodymusic_plot {
+position: relative;
+width: 100%;
+margin: -1px 0 0 0;
+border: 1px solid black;
+z-index: 1;
+}
+.bodymusic_veil {
+position: absolute;
+top: 0;
+left: 0;
+right: 0;
+height: 100%;
+margin: 0;
+background-color: black;
+opacity: 0.3;
+}
+.bodymusic_tooltip {
+position: absolute;
+display: none;
+padding: 5px;
+background-color: #ffffcc;
+border: 1px solid black;
+z-index: 2;
+}
+svg {
+width: 100%;
+height: 100%;
+}
+line {
+display: none;
+}
+circle {
+display: none;
+}
+ellipse {
+display: none;
+}
+text {
+font-size: 0.9em;
+}
+.bodymusic_updaterate {
+ margin-left: 5px;
+ margin-right: 10px;
+ width: 60px;
+}
+.bodymusic_logfile {
+ margin-left: 10px;
+ margin-right: 10px;
+}
+.bodymusic_button {
+ margin-right: 10px;
+}
\ No newline at end of file
diff --git a/html/bm.html b/html/bm.html
deleted file mode 100644
index 45a3e9f..0000000
--- a/html/bm.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-Body Music
-
-
-
-
-Update rate (Hz):
-
-
-
-
-
-
-
-
-
-
diff --git a/html/bodymusic.html b/html/bodymusic.html
new file mode 100644
index 0000000..9945d23
--- /dev/null
+++ b/html/bodymusic.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+Body Music
+
+
+
+
+
diff --git a/html/play.html b/html/play.html
deleted file mode 100644
index 6b4b292..0000000
--- a/html/play.html
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/js/bm.js b/js/bm.js
deleted file mode 100644
index 9a977f4..0000000
--- a/js/bm.js
+++ /dev/null
@@ -1,77 +0,0 @@
-var bm = (function() {
-
- var websocketPort = 8081;
- var websocket;
-
- var startWebSocket = function() {
- if (!websocket || websocket.readyState != WebSocket.OPEN) {
- websocket = new WebSocket("ws://localhost:" + websocketPort);
- websocket.onmessage = function(event) {
- var sensorData = JSON.parse(event.data);
- console.log(JSON.stringify(sensorData, null, 0));
- };
- }
- };
-
- var stop = function() {
- var request = new XMLHttpRequest();
- request.open("GET", "/?command=stop", true);
- request.send();
- };
-
- var start = function() {
- startWebSocket();
- var updateRateString = $("#updateRate").val();
- var updateRate = parseInt(updateRateString);
- var query = "/?command=start";
- if (updateRate) {
- query += "&updateRate=" + updateRate;
- }
- var request = new XMLHttpRequest();
- request.open("GET", query, true);
- request.send();
- };
-
- var playback = function() {
- startWebSocket();
- var logFile = $("#logFiles").val();
- if (logFile) {
- var query = "/?command=playback";
- query += "&logFile=" + logFile;
- }
- var request = new XMLHttpRequest();
- request.open("GET", query, true);
- request.send();
- };
-
- var parseInputs = function() {
- $("#stop").click(stop);
- $("#start").click(start);
- $("#playback").click(playback);
- };
-
- var getLogFiles = function() {
- var request = new XMLHttpRequest();
- request.onload = function() {
- $.each(request.responseText.split(","), function() {
- $("#logFiles").append($("").text(this));
- });
- };
- request.open("GET", "/?command=getLogFiles", true);
- request.send();
- }
-
- var init = function() {
- getLogFiles();
- parseInputs();
- startWebSocket();
- };
-
- return {
- init: init
- };
-})();
-
-$(document).ready(function() {
- bm.init();
-});
diff --git a/js/bodymusic.js b/js/bodymusic.js
new file mode 100644
index 0000000..639c796
--- /dev/null
+++ b/js/bodymusic.js
@@ -0,0 +1,600 @@
+var bmprocessor = (function() {
+
+ var angleAssignment = { yaw: "group", roll: "row", pitch: "column" };
+ var angleRanges = { yaw: [-Math.PI, Math.PI], roll: [-Math.PI, Math.PI],
+ pitch: [-0.4*Math.PI, 0.4*Math.PI] };
+ var nodeCounts = { group: 8, row: 8, column: 8 };
+ var overlap = 0.1;
+ var currentNode;
+
+ var grid = [
+ "MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY",
+ "MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY",
+ "MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY",
+ "MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY",
+ "MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY",
+ "MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY",
+ "MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY",
+ "MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY","MNQRTUXY"
+ ];
+
+ var update = function(data) {
+ var node = {};
+ var groupIndex, rowIndex, columnIndex;
+ var nodeChanged = !currentNode && true;
+ $.each(data.angles, function(key, value) {
+ var assignment = angleAssignment[key];
+ var nodeCount = nodeCounts[assignment];
+ if (nodeCount > 1) {
+ var angleRange = angleRanges[key];
+ var angleMin = angleRange[0];
+ var angleMax = angleRange[1];
+ if (value < angleMin || value > angleMax) {
+ nodeChanged = false;
+ return false;
+ }
+ var gap = (angleMax - angleMin)/(nodeCount - 1);
+ var nodeIndex = Math.round((value - angleMin)/gap);
+ node[assignment] = nodeIndex;
+ if (!nodeChanged) {
+ var currentNodeIndex = currentNode[assignment];
+ if (Math.abs(value - (angleMin + currentNodeIndex*gap)) > 0.5*gap*(1 + 2*overlap)) {
+ nodeChanged = true;
+ }
+ }
+ }
+ });
+ if (nodeChanged) {
+ var pitch = grid[node.row*nodeCounts.row + node.group][node.column];
+ bmplayer.update(pitch);
+ currentNode = node;
+// console.log(JSON.stringify(node, null) + " " + pitch);
+ }
+ };
+
+ var stop = function() {
+ currentNode = null;
+ }
+
+ return {
+ update: update,
+ stop: stop
+ };
+})();
+
+var bmplayer = (function() {
+
+ var pitches = {
+ "A": {name: "C3"},
+ "B": {name: "C#3"},
+ "C": {name: "D3"},
+ "D": {name: "D#3"},
+ "E": {name: "E3"},
+ "F": {name: "F3"},
+ "G": {name: "F#3"},
+ "H": {name: "G3"},
+ "I": {name: "G#3"},
+ "J": {name: "A3"},
+ "K": {name: "A#3"},
+ "L": {name: "B3"},
+ "M": {name: "C4"},
+ "N": {name: "C#4"},
+ "O": {name: "D4"},
+ "P": {name: "D#4"},
+ "Q": {name: "E4"},
+ "R": {name: "F4"},
+ "S": {name: "F#4"},
+ "T": {name: "G4"},
+ "U": {name: "G#4"},
+ "V": {name: "A4", frequency: 440},
+ "W": {name: "A#4"},
+ "X": {name: "B4"},
+ "Y": {name: "C5"}
+ };
+ var refPitch = "V";
+ $.each(pitches, function(key) {
+ var offset = key.charCodeAt(0) - refPitch.charCodeAt(0);
+ if (offset != 0) {
+ this.frequency = pitches[refPitch].frequency*Math.pow(2, offset/12);
+ }
+ });
+
+ var contextClass = window.AudioContext || window.webkitAudioContext;
+ var context = new contextClass();
+ var attack = 0.001
+ var release = 0.5;
+ var spacing = 0.25;
+ var checkInterval = 0.025;
+ var lookahead = 0.05;
+ var lastNote;
+ var nextPitch;
+ var timer;
+
+ var update = function(pitch) {
+ nextPitch = pitch;
+ }
+
+ var scheduleNote = function(pitch, time) {
+ if (lastNote) {
+ lastNote.stop(time);
+ }
+ var note = context.createOscillator();
+ var frequency = pitches[pitch].frequency;
+ note.frequency.setValueAtTime(frequency, time);
+ var envelope = context.createGain();
+ envelope.gain.setValueAtTime(0, time);
+ envelope.gain.setTargetAtTime(1, time, attack);
+ envelope.gain.setTargetAtTime(0, time + attack, release);
+ note.connect(envelope);
+ envelope.connect(context.destination);
+ note.start(time);
+ lastNote = note;
+// console.log(nextPitch);
+ };
+
+ var start = function() {
+ timer = setInterval(function() {
+ if (nextPitch) {
+ var currentTime = context.currentTime;
+ var tick = Math.floor((currentTime + lookahead)/spacing);
+ var time = tick*spacing;
+ if (Math.floor(currentTime < time)) {
+ scheduleNote(nextPitch, time);
+ nextPitch = null;
+ }
+ }
+ }, checkInterval*1000);
+ };
+
+ var stop = function() {
+ if (timer) {
+ clearInterval(timer);
+ }
+ };
+
+ return {
+ start: start,
+ update: update,
+ stop: stop
+ };
+})();
+
+var defaultUpdateRate = 20;
+
+var bmplotter = (function() {
+
+ var plotProps = [
+ {
+ title: "Pitch",
+ sensorId: 0,
+ maxValue: 0.5*Math.PI,
+ key: "angles",
+ series: [
+ {key: "pitch", name: "pitch", color: "magenta"}
+ ],
+ scale: 1,
+ unit: ""
+ },
+ {
+ title: "Yaw",
+ sensorId: 0,
+ maxValue: Math.PI,
+ key: "angles",
+ series: [
+ {key: "yaw", name: "yaw", color: "green"}
+ ],
+ scale: 1,
+ unit: ""
+ },
+ {
+ title: "Roll",
+ sensorId: 0,
+ maxValue: Math.PI,
+ key: "angles",
+ series: [
+ {key: "roll", name: "roll", color: "blue"}
+ ],
+ scale: 1,
+ unit: ""
+ }
+ ];
+ var plotUpdateInterval = 1;
+ var plotStep = 2;
+
+ var plotHeight = 300 / plotProps.length;
+ var plotMargin = 10;
+ var lineWidth = 2;
+ var markerRadius = 4;
+ var bodyRect;
+ var plots = [];
+ var clock = new (function() {
+ this.tick = 0;
+ })();
+
+ var createRibbon = function() {
+ bodyRect = document.body.getBoundingClientRect();
+ var ribbon = $("", {
+ class: "bodymusic_ribbon"
+ }).appendTo("body");
+ $.each(plotProps, function() {
+ $.each(this.series, function() {
+ var iconDiv = $("", {
+ class: "bodymusic_legendicon"
+ }).appendTo(ribbon);
+ iconSvg = $(document.createElementNS("http://www.w3.org/2000/svg", "svg"))
+ .appendTo(iconDiv);
+ $(document.createElementNS("http://www.w3.org/2000/svg", "line")).attr({
+ x1: 0,
+ x2: 20,
+ y1: 10,
+ y2: 10,
+ stroke: this.color,
+ "stroke-width": lineWidth*2
+ }).css("display", "inline").appendTo(iconSvg);
+ $("", {
+ class: "bodymusic_legendlabel"
+ }).appendTo(ribbon).html(this.name);
+ });
+ });
+ };
+
+ var createPlots = function() {
+ var strokeIndex;
+ $.each(plotProps, function() {
+ var thisPlotProps = this;
+ var $plotElement;
+ var plot;
+ var $svg;
+ var veil;
+ var series = [];
+ var plotWidth;
+
+ var createCanvas = function() {
+ $plotElement = $("", {
+ class: "bodymusic_plot"
+ }).appendTo("body");
+ plotWidth = $plotElement.width();
+ $plotElement.height(plotHeight);
+ $svg = $(document.createElementNS("http://www.w3.org/2000/svg", "svg"))
+ .appendTo($plotElement);
+ };
+
+ var createVeil = function() {
+ veil = document.createElement("div");
+ $(veil).attr({
+ class: "bodymusic_veil"
+ }).appendTo($plotElement);
+ };
+
+ var createAxis = function() {
+ var axis = document.createElementNS("http://www.w3.org/2000/svg", "line");
+ $(axis).attr({
+ x1: 0,
+ x2: plotWidth,
+ y1: plotHeight/2,
+ y2: plotHeight/2,
+ stroke: "gray",
+ "stroke-dasharray": "5,3"
+ }).appendTo($svg);
+ axis.style.display = "inline";
+ };
+
+ var createLabels = function() {
+ var zeroLabel = document.createElementNS("http://www.w3.org/2000/svg", "text");
+ $(maxLabel).attr({
+ x: 5,
+ y: plotHeight/2 - 5
+ }).appendTo($svg);
+ var maxLabel = document.createElementNS("http://www.w3.org/2000/svg", "text");
+ $(maxLabel).attr({
+ x: 5,
+ y: 15
+ }).appendTo($svg);
+ maxLabel.textContent = (thisPlotProps.maxValue * thisPlotProps.scale).toFixed(0) +
+ " " + thisPlotProps.unit;
+ var minLabel = document.createElementNS("http://www.w3.org/2000/svg", "text");
+ $(minLabel).attr({
+ x: 5,
+ y: plotHeight - 5
+ }).appendTo($svg);
+ minLabel.textContent = (-thisPlotProps.maxValue * thisPlotProps.scale).toFixed(0) +
+ " " + thisPlotProps.unit;
+ var plotLabel = document.createElementNS("http://www.w3.org/2000/svg", "text");
+ $(plotLabel).attr({
+ x: 50,
+ y: 15
+ }).appendTo($svg);
+ plotLabel.textContent = thisPlotProps.title;
+ };
+
+ var createPlot = function() {
+ var yBase = plotHeight/2;
+ plot = {
+ key: thisPlotProps.key,
+ id: thisPlotProps.sensorId,
+ yBase: yBase,
+ yScale: yBase/thisPlotProps.maxValue,
+ veil: veil,
+ series: series,
+ currentStrokeIndex: -1,
+ history: []
+ };
+ plot.strokeCount = Math.floor((plotWidth - 2*plotMargin)/plotStep);
+ plots.push(plot);
+ };
+
+ var createSlider = function() {
+ var slider = document.createElementNS("http://www.w3.org/2000/svg", "line");
+ $(slider).attr({
+ x1: 0,
+ x2: 0,
+ y1: 0,
+ y2: plotHeight,
+ stroke: "gray"
+ }).appendTo($svg);
+ var tooltip = document.createElement("div");
+ $(tooltip).attr({
+ class: "bodymusic_tooltip"
+ }).appendTo("body");
+ slider.tooltip = tooltip;
+ plot.slider = slider;
+ $plotElement.mouseover(function() {
+ slider.style.display = "inline";
+ $plotElement.css("cursor", "none");
+ });
+ $plotElement.mouseout(function() {
+ slider.style.display = "none";
+ slider.tooltip.style.display = "none";
+ });
+ $plotElement.mousemove(function(event) {
+ var eventX = event.clientX - bodyRect.left;
+ var strokeIndex = Math.floor((eventX - plotMargin) / plotStep);
+ var history = plot.history;
+ if (strokeIndex > -1 && strokeIndex < history.length) {
+ $(slider).attr({
+ x1: eventX,
+ x2: eventX
+ });
+ var tooltipText = "";
+ var data = history[strokeIndex];
+ var plotKey = plot.key;
+ tooltipText = "Time: " + data.time.toFixed(2) + " sec";
+ $.each(plot.series, function() {
+ var value = data[plotKey][this.key] * thisPlotProps.scale;
+ tooltipText += "
" + this.name + ": " +
+ value.toFixed(5) + " " + thisPlotProps.unit;
+ });
+ tooltip.style.display = "inline-block";
+ tooltip.style.left = eventX - 30 + "px";
+ tooltip.style.top = event.clientY + 10 + "px";
+ }
+ else {
+ tooltip.style.display = "none";
+ }
+ tooltip.innerHTML = tooltipText;
+ });
+ };
+
+
+ var createSeries = function() {
+ $.each(thisPlotProps.series, function() {
+ var color = this.color;
+ var strokes = [];
+ var marker = document.createElementNS("http://www.w3.org/2000/svg", "circle");
+ var thisSeries = {
+ key: this.key,
+ name: this.name,
+ strokes: strokes,
+ marker: marker
+ };
+ series.push(thisSeries);
+ for (strokeIndex = 0; strokeIndex < plot.strokeCount; strokeIndex++) {
+ var stroke = document.createElementNS("http://www.w3.org/2000/svg", "line");
+ $(stroke).attr({
+ x1: plotMargin + strokeIndex*plotStep,
+ x2: plotMargin + (strokeIndex + 1)*plotStep,
+ y1: plotHeight/2,
+ y2: plotHeight/2,
+ stroke: color,
+ "stroke-width": lineWidth,
+ "stroke-linecap": "round"
+ }).appendTo($svg);
+ strokes.push(stroke);
+ }
+ $(marker).attr({
+ cx: 0,
+ cy: 0,
+ r: markerRadius,
+ stroke: color,
+ fill: color
+ }).appendTo($svg);
+ });
+ };
+
+ createCanvas();
+ createVeil();
+ createAxis();
+ createLabels();
+ createPlot();
+ createSlider();
+ createSeries();
+ });
+ };
+
+ var updatePlots = function(data) {
+ $.each(plots, function() {
+ var plot = this;
+ if (plot.sensorId = data.id) {
+ if (plot.currentStrokeIndex < plot.strokeCount - 1) {
+ plot.currentStrokeIndex++;
+ }
+ else {
+ plot.currentStrokeIndex = 0;
+ }
+ var x = plotMargin + (plot.currentStrokeIndex + 1)*plotStep;
+ var thisPlotProps = plotProps[plot.key];
+ var ys = data[plot.key];
+ if (ys) {
+ $.each(plot.series, function() {
+ var series = this;
+ var y = ys[series.key];
+ var strokes = series.strokes;
+ var marker = series.marker;
+ var yScaled = plot.yBase - y*plot.yScale;
+ var stroke = strokes[plot.currentStrokeIndex];
+ stroke.setAttribute("y2", yScaled);
+ marker.setAttribute("cx", x);
+ marker.setAttribute("cy", yScaled);
+ if (plot.currentStrokeIndex > 0) {
+ var prevY = strokes[plot.currentStrokeIndex - 1].getAttribute("y2");
+ stroke.setAttribute("y1", prevY);
+ stroke.style.display = "inline";
+ marker.style.display = "inline";
+ }
+ });
+ }
+ var history = plot.history;
+ if (plot.currentStrokeIndex < history.length) {
+ history[plot.currentStrokeIndex] = data;
+ }
+ else {
+ history.push(data);
+ }
+ plot.veil.style.left = x + "px";
+ }
+ });
+ };
+
+ var update = function(data) {
+ if (clock.tick++ % plotUpdateInterval == 0) {
+ updatePlots(data);
+ }
+ };
+
+ var init = function() {
+ createRibbon();
+ createPlots();
+ };
+
+ return {
+ init: init,
+ update: update
+ };
+})();
+
+var bmconsole = (function() {
+
+ var updateRateInput;
+ var logFileInput;
+ var websocketPort = 8081;
+ var websocket;
+
+ var startWebSocket = function() {
+ if (!websocket || websocket.readyState != WebSocket.OPEN) {
+ websocket = new WebSocket("ws://localhost:" + websocketPort);
+ websocket.onmessage = function(event) {
+ var data = JSON.parse(event.data);
+ bmprocessor.update(data);
+ bmplotter.update(data);
+ };
+ }
+ };
+
+ var stop = function() {
+ var request = new XMLHttpRequest();
+ request.open("GET", "/?command=stop", true);
+ request.send();
+ bmplayer.stop();
+ bmprocessor.stop();
+ };
+
+ var start = function() {
+ bmplayer.start();
+ startWebSocket();
+ var updateRateString = updateRateInput.val();
+ var updateRate = parseInt(updateRateString);
+ var query = "/?command=start";
+ if (updateRate) {
+ query += "&updateRate=" + updateRate;
+ }
+ var request = new XMLHttpRequest();
+ request.open("GET", query, true);
+ request.send();
+ };
+
+ var playback = function() {
+ startWebSocket();
+ var logFile = logFileInput.val();
+ if (logFile) {
+ var query = "/?command=playback";
+ query += "&logFile=" + logFile;
+ }
+ var request = new XMLHttpRequest();
+ request.open("GET", query, true);
+ request.send();
+ };
+
+ var getLogFiles = function() {
+ var request = new XMLHttpRequest();
+ request.onload = function() {
+ $.each(request.responseText.split(","), function() {
+ logFileInput.append($("").text(this));
+ });
+ };
+ request.open("GET", "/?command=getLogFiles", true);
+ request.send();
+ };
+
+ var init = function() {
+ var stopButton = $(" ").attr({
+ type: "button",
+ class: "bodymusic_button",
+ value: "Stop"
+ }).appendTo($("body"));
+ stopButton.click(function() {
+ stop();
+ });
+ $("Update rate (Hz):").appendTo($("body"));
+ updateRateInput = $("").attr({
+ type: "text",
+ class: "bodymusic_updaterate",
+ value: defaultUpdateRate,
+ list: "updateRates"
+ }).appendTo($("body"));
+ var dataList = $("").attr({
+ id:"updateRates"
+ }).appendTo($("body"));
+ $.each([1, 2, 4, 10, 20, 50, 100], function() {
+ $('