changeset 2619:74e5d7a978ee

Merge branch 'vnext' into Dev_main
author Nicholas Jillings <nicholas.jillings@mail.bcu.ac.uk>
date Thu, 17 Nov 2016 13:08:26 +0000
parents cce1fb4009f2 (diff) fff464febd56 (current diff)
children 13a0b65143a6
files js/core.js
diffstat 4 files changed, 179 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/js/core.js	Thu Nov 17 13:08:00 2016 +0000
+++ b/js/core.js	Thu Nov 17 13:08:26 2016 +0000
@@ -1488,10 +1488,8 @@
 
     this.buffers = [];
     this.bufferObj = function () {
-        this.url = null;
+        var urls = [];
         this.buffer = null;
-        this.xmlRequest = new XMLHttpRequest();
-        this.xmlRequest.parent = this;
         this.users = [];
         this.progress = 0;
         this.status = 0;
@@ -1506,30 +1504,77 @@
                 }
             }
         };
-        this.getMedia = function (url) {
-            this.url = url;
-            this.xmlRequest.open('GET', this.url, true);
-            this.xmlRequest.responseType = 'arraybuffer';
+        this.setUrls = function (obj) {
+            // Obj must be an array of pairs:
+            // [{sampleRate, url}]
+            var localFs = audioContext.sampleRate,
+                list = [],
+                i;
+            for (i = 0; i < obj.length; i++) {
+                if (obj[i].sampleRate == localFs) {
+                    list.push(obj.splice(i, 1)[0]);
+                }
+            }
+            list = list.concat(obj);
+            urls = list;
+        };
+        this.hasUrl = function (checkUrl) {
+            var l = urls.length,
+                i;
+            for (i = 0; i < l; i++) {
+                if (urls[i].url == checkUrl) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        this.getMedia = function () {
+            var self = this;
+            var currentUrlIndex = 0;
 
-            var bufferObj = this;
+            function get(fqurl) {
+                return new Promise(function (resolve, reject) {
+                    var req = new XMLHttpRequest();
+                    req.open('GET', fqurl, true);
+                    req.responseType = 'arraybuffer';
+                    req.onload = function () {
+                        if (req.status == 200) {
+                            resolve(req.response);
+                        }
+                    };
+                    req.onerror = function () {
+                        reject(new Error(req.statusText));
+                    };
+
+                    req.addEventListener("progress", progressCallback.bind(self));
+                    req.send();
+                });
+            }
+
+            function getNextURL() {
+                currentUrlIndex++;
+                var self = this;
+                if (currentUrlIndex >= urls.length) {
+                    processError();
+                } else {
+                    return get(urls[currentUrlIndex].url).then(processAudio.bind(self)).catch(getNextURL.bind(self));
+                }
+            }
 
             // Create callback to decode the data asynchronously
-            this.xmlRequest.onloadend = function () {
-                // Use inbuilt WAVE decoder first
-                if (this.status == -1) {
-                    return;
-                }
-                var waveObj = new WAVE();
-                audioContext.decodeAudioData(bufferObj.xmlRequest.response, function (decodedData) {
-                    bufferObj.buffer = decodedData;
-                    bufferObj.status = 2;
-                    calculateLoudness(bufferObj, "I");
+            function processAudio(response) {
+                var self = this;
+                return audioContext.decodeAudioData(response, function (decodedData) {
+                    self.buffer = decodedData;
+                    self.status = 2;
+                    calculateLoudness(self, "I");
+                    return true;
                 }, function (e) {
                     var waveObj = new WAVE();
-                    if (waveObj.open(bufferObj.xmlRequest.response) == 0) {
-                        bufferObj.buffer = audioContext.createBuffer(waveObj.num_channels, waveObj.num_samples, waveObj.sample_rate);
+                    if (waveObj.open(response) == 0) {
+                        self.buffer = audioContext.createBuffer(waveObj.num_channels, waveObj.num_samples, waveObj.sample_rate);
                         for (var c = 0; c < waveObj.num_channels; c++) {
-                            var buffer_ptr = bufferObj.buffer.getChannelData(c);
+                            var buffer_ptr = self.buffer.getChannelData(c);
                             for (var n = 0; n < waveObj.num_samples; n++) {
                                 buffer_ptr[n] = waveObj.decoded_data[c][n];
                             }
@@ -1537,41 +1582,44 @@
 
                         delete waveObj;
                     }
-                    if (bufferObj.buffer != undefined) {
-                        bufferObj.status = 2;
-                        calculateLoudness(bufferObj, "I");
+                    if (self.buffer != undefined) {
+                        self.status = 2;
+                        calculateLoudness(self, "I");
+                        return true;
                     }
+                    return false;
                 });
-            };
+            }
 
             // Create callback for any error in loading
-            this.xmlRequest.onerror = function () {
-                this.parent.status = -1;
-                for (var i = 0; i < this.parent.users.length; i++) {
-                    this.parent.users[i].state = -1;
-                    if (this.parent.users[i].interfaceDOM != null) {
-                        this.parent.users[i].bufferLoaded(this);
+            function processError() {
+                this.status = -1;
+                for (var i = 0; i < this.users.length; i++) {
+                    this.users[i].state = -1;
+                    if (this.users[i].interfaceDOM != null) {
+                        this.users[i].bufferLoaded(this);
                     }
                 }
-                interfaceContext.lightbox.post("Error", "Could not load resource " + this.parent.url);
+                interfaceContext.lightbox.post("Error", "Could not load resource " + urls[currentUrlIndex].url);
             }
 
-            this.progress = 0;
-            this.progressCallback = function (event) {
+            function progressCallback(event) {
                 if (event.lengthComputable) {
-                    this.parent.progress = event.loaded / event.total;
-                    for (var i = 0; i < this.parent.users.length; i++) {
-                        if (this.parent.users[i].interfaceDOM != null) {
-                            if (typeof this.parent.users[i].interfaceDOM.updateLoading === "function") {
-                                this.parent.users[i].interfaceDOM.updateLoading(this.parent.progress * 100);
+                    this.progress = event.loaded / event.total;
+                    for (var i = 0; i < this.users.length; i++) {
+                        if (this.users[i].interfaceDOM != null) {
+                            if (typeof this.users[i].interfaceDOM.updateLoading === "function") {
+                                this.users[i].interfaceDOM.updateLoading(this.progress * 100);
                             }
                         }
                     }
                 }
             };
-            this.xmlRequest.addEventListener("progress", this.progressCallback);
+
+            this.progress = 0;
             this.status = 1;
-            this.xmlRequest.send();
+            currentUrlIndex = 0;
+            get(urls[0].url).then(processAudio.bind(self)).catch(getNextURL.bind(self));
         };
 
         this.registerAudioObject = function (audioObject) {
@@ -1648,14 +1696,25 @@
             var URL = page.hostURL + element.url;
             var buffer = null;
             for (var buffObj of this.buffers) {
-                if (URL == buffObj.url) {
+                if (buffObj.hasUrl(URL)) {
                     buffer = buffObj;
                     break;
                 }
             }
             if (buffer == null) {
                 buffer = new this.bufferObj();
-                buffer.getMedia(URL);
+                var urls = [{
+                    url: URL,
+                    sampleRate: element.sampleRate
+                }];
+                element.alternatives.forEach(function (e) {
+                    urls.push({
+                        url: e.url,
+                        sampleRate: e.sampleRate
+                    });
+                });
+                buffer.setUrls(urls);
+                buffer.getMedia();
                 this.buffers.push(buffer);
             }
         }
@@ -1725,7 +1784,7 @@
         var URL = testState.currentStateMap.hostURL + element.url;
         var buffer = null;
         for (var i = 0; i < this.buffers.length; i++) {
-            if (URL == this.buffers[i].url) {
+            if (this.buffers[i].hasUrl(URL)) {
                 buffer = this.buffers[i];
                 break;
             }
--- a/js/specification.js	Thu Nov 17 13:08:00 2016 +0000
+++ b/js/specification.js	Thu Nov 17 13:08:26 2016 +0000
@@ -781,6 +781,8 @@
             this.label = null;
             this.startTime = undefined;
             this.stopTime = undefined;
+            this.sampleRate = undefined;
+            this.alternatives = [];
             this.schema = specification.schema.getAllElementsByName('audioelement')[0];;
             this.parent = null;
             this.decode = function (parent, xml) {
@@ -800,6 +802,17 @@
                             break;
                     }
                 }
+                // Get the alternative nodes
+                var child = xml.firstElementChild;
+                while (child) {
+                    if (child.nodeName == "alternative") {
+                        this.alternatives.push({
+                            'url': child.getAttribute("url"),
+                            'sampleRate': child.getAttribute("sampleRate")
+                        });
+                    }
+                    child = child.nextElementSibling;
+                }
 
             };
             this.encode = function (root) {
@@ -814,6 +827,12 @@
                         eval("AENode.setAttribute('" + name + "',this." + name + ")");
                     }
                 }
+                this.alternatives.forEach(function (alt) {
+                    var node = root.createElement("alternative");
+                    node.setAttribute("url", alt.url);
+                    node.setAttribute("sampleRate", alt.sampleRate);
+                    AENode.appendChild(node);
+                });
                 return AENode;
             };
         };
--- a/test_create/test_core.js	Thu Nov 17 13:08:00 2016 +0000
+++ b/test_create/test_core.js	Thu Nov 17 13:08:26 2016 +0000
@@ -975,7 +975,7 @@
                 handleEvent: function (event) {
                     this.parent.scaleRoot.scales = [];
                     var protoScale = interfaceSpecs.getAllElementsByTagName('scaledefinitions')[0].getAllElementsByName(event.currentTarget.value)[0];
-                    var protoMarkers = protoScale.getElementsByTagName("scale");
+                    var protoMarkers = protoScale.getElementsByTagName("scalelabel");
                     for (var i = 0; i < protoMarkers.length; i++) {
                         var marker = {
                             position: protoMarkers[i].getAttribute("position"),
@@ -992,6 +992,22 @@
             optionHolder.className = 'node';
             optionHolder.id = 'popup-option-holder';
             this.content.appendChild(optionHolder);
+            this.addMarker = {
+                root: document.createElement("button"),
+                parent: this,
+                handleEvent: function () {
+                    var marker = {
+                        position: 0,
+                        text: "text"
+                    };
+                    this.parent.scaleRoot.scales.push(marker);
+                    var markerNode = new this.parent.buildMarkerNode(this.parent, marker);
+                    document.getElementById("popup-option-holder").appendChild(markerNode.root);
+                    this.parent.markerNodes.push(markerNode);
+                }
+            };
+            this.addMarker.root.textContent = "Add Marker";
+            this.addMarker.root.addEventListener("click", this.addMarker);
             this.generate = function (scaleRoot, parent) {
                 this.scaleRoot = scaleRoot;
                 this.parent = parent;
@@ -1007,23 +1023,6 @@
                     selectOption.textContent = scaleName;
                     this.preset.input.appendChild(selectOption);
                 }
-
-                this.addMarker = {
-                    root: document.createElement("button"),
-                    parent: this,
-                    handleEvent: function () {
-                        var marker = {
-                            position: 0,
-                            text: "text"
-                        };
-                        this.parent.scaleRoot.scales.push(marker);
-                        var markerNode = new this.parent.buildMarkerNode(this.parent, marker);
-                        document.getElementById("popup-option-holder").appendChild(markerNode.root);
-                        this.parent.markerNodes.push(markerNode);
-                    }
-                };
-                this.addMarker.root.textContent = "Add Marker";
-                this.addMarker.root.addEventListener("click", this.addMarker);
                 this.content.appendChild(this.addMarker.root);
 
                 // Create Marker List
@@ -1595,7 +1594,7 @@
                         this.parent.specification.title = event.currentTarget.value;
                     }
                 }
-                this.titleNode.label.textContent = "Axis Title:";
+                this.titleNode.label.textContent = "Presented Axis Title:";
                 this.titleNode.root.className = "node-children";
                 this.titleNode.root.appendChild(this.titleNode.label);
                 this.titleNode.root.appendChild(this.titleNode.input);
@@ -1603,6 +1602,24 @@
                 this.titleNode.input.value = this.specification.title;
                 this.children.push(this.titleNode);
                 this.childrenDOM.appendChild(this.titleNode.root);
+                // Set the interface-name attribute
+                this.axisName = {
+                    root: document.createElement("div"),
+                    label: document.createElement("span"),
+                    input: document.createElement("input"),
+                    parent: this,
+                    handleEvent: function (event) {
+                        this.parent.specification.name = event.currentTarget.value;
+                    }
+                }
+                this.axisName.label.textContent = "Saved Axis Name (no spaces):";
+                this.axisName.root.className = "node-children";
+                this.axisName.root.appendChild(this.axisName.label);
+                this.axisName.root.appendChild(this.axisName.input);
+                this.axisName.input.addEventListener("change", this.axisName, false);
+                this.axisName.input.value = this.specification.name;
+                this.children.push(this.axisName);
+                this.childrenDOM.appendChild(this.axisName.root);
             }
 
             // Put in the check / show options as individual children
--- a/xml/test-schema.xsd	Thu Nov 17 13:08:00 2016 +0000
+++ b/xml/test-schema.xsd	Thu Nov 17 13:08:26 2016 +0000
@@ -162,6 +162,20 @@
 
         <xs:element name="audioelement">
             <xs:complexType>
+                <xs:sequence>
+                    <xs:element name="alternative" minOccurs="0" maxOccurs="unbounded">
+                        <xs:complexType>
+                            <xs:attribute name="url" type="xs:anyURI" use="required" />
+                            <xs:attribute name="sampleRate" use="optional">
+                                <xs:simpleType>
+                                    <xs:restriction base="xs:decimal">
+                                        <xs:minInclusive value="1" />
+                                    </xs:restriction>
+                                </xs:simpleType>
+                            </xs:attribute>
+                        </xs:complexType>
+                    </xs:element>
+                </xs:sequence>
                 <xs:attribute ref="id" use="required" />
                 <xs:attribute name="url" type="xs:anyURI" use="required" />
                 <xs:attribute name="gain" type="xs:decimal" default="0" />
@@ -203,6 +217,13 @@
                         </xs:restriction>
                     </xs:simpleType>
                 </xs:attribute>
+                <xs:attribute name="sampleRate" use="optional">
+                    <xs:simpleType>
+                        <xs:restriction base="xs:decimal">
+                            <xs:minInclusive value="1" />
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:attribute>
             </xs:complexType>
         </xs:element>