diff --git a/htdocs/index.html b/htdocs/index.html
index 1751cc7..985196b 100644
--- a/htdocs/index.html
+++ b/htdocs/index.html
@@ -30,6 +30,7 @@
+
diff --git a/htdocs/lib/AudioEngine.js b/htdocs/lib/AudioEngine.js
index 0445dd3..5860b2e 100644
--- a/htdocs/lib/AudioEngine.js
+++ b/htdocs/lib/AudioEngine.js
@@ -4,6 +4,7 @@ var useAudioWorklets = true;
function AudioEngine(maxBufferLength, audioReporter) {
this.audioReporter = audioReporter;
+ this.initStats();
this.resetStats();
var ctx = window.AudioContext || window.webkitAudioContext;
if (!ctx) {
@@ -55,7 +56,12 @@ AudioEngine.prototype.start = function(callback) {
me.audioNode.port.addEventListener('message', function(m){
var json = JSON.parse(m.data);
if (typeof(json.buffersize) !== 'undefined') {
- me.audioReporter(json);
+ me.audioReporter({
+ buffersize: json.buffersize
+ });
+ }
+ if (typeof(json.samplesProcessed) !== 'undefined') {
+ me.audioSamples.add(json.samplesProcessed);
}
});
me.audioNode.port.start();
@@ -86,21 +92,23 @@ AudioEngine.prototype.start = function(callback) {
var out = new Float32Array(bufferSize);
while (me.audioBuffers.length) {
var b = me.audioBuffers.shift();
- var newLength = total + b.length;
// not enough space to fit all data, so splice and put back in the queue
- if (newLength > bufferSize) {
- var tokeep = b.subarray(0, bufferSize - total);
+ if (total + b.length > bufferSize) {
+ var spaceLeft = bufferSize - total;
+ var tokeep = b.subarray(0, spaceLeft);
out.set(tokeep, total);
- var tobuffer = b.subarray(bufferSize - total, b.length);
+ var tobuffer = b.subarray(spaceLeft, b.length);
me.audioBuffers.unshift(tobuffer);
+ total += spaceLeft;
break;
} else {
out.set(b, total);
+ total += b.length;
}
- total = newLength;
}
e.outputBuffer.copyToChannel(out, 0);
+ me.audioSamples.add(total);
}
@@ -124,27 +132,36 @@ AudioEngine.prototype.isAllowed = function() {
};
AudioEngine.prototype.reportStats = function() {
- var stats = {};
if (this.audioNode.port) {
- this.audioNode.port.postMessage(JSON.stringify({cmd:'getBuffers'}));
+ this.audioNode.port.postMessage(JSON.stringify({cmd:'getStats'}));
} else {
- stats.buffersize = this.getBuffersize();
+ this.audioReporter({
+ buffersize: this.getBuffersize()
+ });
}
- stats.audioRate = this.stats.audioSamples;
- var elapsed = new Date() - this.stats.startTime;
- stats.audioByteRate = this.stats.audioBytes * 1000 / elapsed;
- this.audioReporter(stats);
+};
- // sample rate is just measuring the last seconds
- this.stats.audioSamples = 0;
+AudioEngine.prototype.initStats = function() {
+ var me = this;
+ var buildReporter = function(key) {
+ return function(v){
+ var report = {};
+ report[key] = v;
+ me.audioReporter(report);
+ }
+
+ };
+
+ this.audioBytes = new Measurement();
+ this.audioBytes.report(10000, 1000, buildReporter('audioByteRate'));
+
+ this.audioSamples = new Measurement();
+ this.audioSamples.report(10000, 1000, buildReporter('audioRate'));
};
AudioEngine.prototype.resetStats = function() {
- this.stats = {
- startTime: new Date(),
- audioBytes: 0,
- audioSamples: 0
- };
+ this.audioBytes.reset();
+ this.audioSamples.reset();
};
AudioEngine.prototype.setupResampling = function() { //both at the server and the client
@@ -178,7 +195,7 @@ AudioEngine.prototype.getSampleRate = function() {
AudioEngine.prototype.pushAudio = function(data) {
if (!this.audioNode) return;
- this.stats.audioBytes += data.byteLength;
+ this.audioBytes.add(data.byteLength);
var buffer;
if (this.compression === "adpcm") {
//resampling & ADPCM
@@ -187,7 +204,6 @@ AudioEngine.prototype.pushAudio = function(data) {
buffer = new Int16Array(data);
}
buffer = this.resampler.process(sdrjs.ConvertI16_F(buffer));
- this.stats.audioSamples += buffer.length;
if (this.audioNode.port) {
// AudioWorklets supported
this.audioNode.port.postMessage(buffer);
diff --git a/htdocs/lib/AudioProcessor.js b/htdocs/lib/AudioProcessor.js
index 900e56f..7ac76c5 100644
--- a/htdocs/lib/AudioProcessor.js
+++ b/htdocs/lib/AudioProcessor.js
@@ -6,11 +6,12 @@ class OwrxAudioProcessor extends AudioWorkletProcessor {
this.audioBuffer = new Float32Array(this.bufferSize);
this.inPos = 0;
this.outPos = 0;
+ this.samplesProcessed = 0;
this.port.addEventListener('message', (m) => {
if (typeof(m.data) === 'string') {
const json = JSON.parse(m.data);
- if (json.cmd && json.cmd === 'getBuffers') {
- this.reportBuffers();
+ if (json.cmd && json.cmd === 'getStats') {
+ this.reportStats();
}
} else {
// the ringbuffer size is aligned to the output buffer size, which means that the input buffers might
@@ -37,6 +38,7 @@ class OwrxAudioProcessor extends AudioWorkletProcessor {
output.set(this.audioBuffer.subarray(this.outPos, this.outPos + 128));
});
this.outPos = (this.outPos + 128) % this.bufferSize;
+ this.samplesProcessed += 128;
return true;
}
remaining() {
@@ -44,8 +46,12 @@ class OwrxAudioProcessor extends AudioWorkletProcessor {
if (mod >= 0) return mod;
return mod + this.bufferSize;
}
- reportBuffers() {
- this.port.postMessage(JSON.stringify({buffersize: this.remaining()}));
+ reportStats() {
+ this.port.postMessage(JSON.stringify({
+ buffersize: this.remaining(),
+ samplesProcessed: this.samplesProcessed
+ }));
+ this.samplesProcessed = 0;
}
}
diff --git a/htdocs/lib/Measurement.js b/htdocs/lib/Measurement.js
new file mode 100644
index 0000000..018739c
--- /dev/null
+++ b/htdocs/lib/Measurement.js
@@ -0,0 +1,62 @@
+function Measurement() {
+ this.reset();
+};
+
+Measurement.prototype.add = function(v) {
+ this.value += v;
+};
+
+Measurement.prototype.getValue = function() {
+ return this.value;
+};
+
+Measurement.prototype.getElapsed = function() {
+ return new Date() - this.start;
+};
+
+Measurement.prototype.getRate = function() {
+ return this.getValue() / this.getElapsed();
+};
+
+Measurement.prototype.reset = function() {
+ this.value = 0;
+ this.start = new Date();
+};
+
+Measurement.prototype.report = function(range, interval, callback) {
+ return new Reporter(this, range, interval, callback);
+}
+
+function Reporter(measurement, range, interval, callback) {
+ this.measurement = measurement;
+ this.range = range;
+ this.samples = [];
+ this.callback = callback;
+ this.interval = setInterval(this.report.bind(this), interval);
+};
+
+Reporter.prototype.sample = function(){
+ this.samples.push({
+ timestamp: new Date(),
+ value: this.measurement.getValue()
+ });
+};
+
+Reporter.prototype.report = function(){
+ this.sample();
+ var now = new Date();
+ var minDate = now.getTime() - this.range;
+ this.samples = this.samples.filter(function(s) {
+ return s.timestamp.getTime() > minDate;
+ });
+ this.samples.sort(function(a, b) {
+ return a.timestamp - b.timestamp;
+ });
+ var oldest = this.samples[0];
+ var newest = this.samples[this.samples.length -1];
+ var elapsed = newest.timestamp - oldest.timestamp;
+ if (elapsed <= 0) return;
+ var accumulated = newest.value - oldest.value;
+ // we want rate per second, but our time is in milliseconds... compensate by 1000
+ this.callback(accumulated * 1000 / elapsed);
+};
\ No newline at end of file