implement new way of measuring stats that allows arbitrary timeranges
This commit is contained in:
parent
fe08228204
commit
39120d9413
@ -30,6 +30,7 @@
|
|||||||
<script src="static/lib/BookmarkBar.js"></script>
|
<script src="static/lib/BookmarkBar.js"></script>
|
||||||
<script src="static/lib/AudioEngine.js"></script>
|
<script src="static/lib/AudioEngine.js"></script>
|
||||||
<script src="static/lib/ProgressBar.js"></script>
|
<script src="static/lib/ProgressBar.js"></script>
|
||||||
|
<script src="static/lib/Measurement.js"></script>
|
||||||
<link rel="stylesheet" type="text/css" href="static/lib/nanoscroller.css" />
|
<link rel="stylesheet" type="text/css" href="static/lib/nanoscroller.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="static/css/openwebrx.css" />
|
<link rel="stylesheet" type="text/css" href="static/css/openwebrx.css" />
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
@ -4,6 +4,7 @@ var useAudioWorklets = true;
|
|||||||
|
|
||||||
function AudioEngine(maxBufferLength, audioReporter) {
|
function AudioEngine(maxBufferLength, audioReporter) {
|
||||||
this.audioReporter = audioReporter;
|
this.audioReporter = audioReporter;
|
||||||
|
this.initStats();
|
||||||
this.resetStats();
|
this.resetStats();
|
||||||
var ctx = window.AudioContext || window.webkitAudioContext;
|
var ctx = window.AudioContext || window.webkitAudioContext;
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
@ -55,7 +56,12 @@ AudioEngine.prototype.start = function(callback) {
|
|||||||
me.audioNode.port.addEventListener('message', function(m){
|
me.audioNode.port.addEventListener('message', function(m){
|
||||||
var json = JSON.parse(m.data);
|
var json = JSON.parse(m.data);
|
||||||
if (typeof(json.buffersize) !== 'undefined') {
|
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();
|
me.audioNode.port.start();
|
||||||
@ -86,21 +92,23 @@ AudioEngine.prototype.start = function(callback) {
|
|||||||
var out = new Float32Array(bufferSize);
|
var out = new Float32Array(bufferSize);
|
||||||
while (me.audioBuffers.length) {
|
while (me.audioBuffers.length) {
|
||||||
var b = me.audioBuffers.shift();
|
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
|
// not enough space to fit all data, so splice and put back in the queue
|
||||||
if (newLength > bufferSize) {
|
if (total + b.length > bufferSize) {
|
||||||
var tokeep = b.subarray(0, bufferSize - total);
|
var spaceLeft = bufferSize - total;
|
||||||
|
var tokeep = b.subarray(0, spaceLeft);
|
||||||
out.set(tokeep, total);
|
out.set(tokeep, total);
|
||||||
var tobuffer = b.subarray(bufferSize - total, b.length);
|
var tobuffer = b.subarray(spaceLeft, b.length);
|
||||||
me.audioBuffers.unshift(tobuffer);
|
me.audioBuffers.unshift(tobuffer);
|
||||||
|
total += spaceLeft;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
out.set(b, total);
|
out.set(b, total);
|
||||||
|
total += b.length;
|
||||||
}
|
}
|
||||||
total = newLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
e.outputBuffer.copyToChannel(out, 0);
|
e.outputBuffer.copyToChannel(out, 0);
|
||||||
|
me.audioSamples.add(total);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,27 +132,36 @@ AudioEngine.prototype.isAllowed = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
AudioEngine.prototype.reportStats = function() {
|
AudioEngine.prototype.reportStats = function() {
|
||||||
var stats = {};
|
|
||||||
if (this.audioNode.port) {
|
if (this.audioNode.port) {
|
||||||
this.audioNode.port.postMessage(JSON.stringify({cmd:'getBuffers'}));
|
this.audioNode.port.postMessage(JSON.stringify({cmd:'getStats'}));
|
||||||
} else {
|
} 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
|
AudioEngine.prototype.initStats = function() {
|
||||||
this.stats.audioSamples = 0;
|
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() {
|
AudioEngine.prototype.resetStats = function() {
|
||||||
this.stats = {
|
this.audioBytes.reset();
|
||||||
startTime: new Date(),
|
this.audioSamples.reset();
|
||||||
audioBytes: 0,
|
|
||||||
audioSamples: 0
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AudioEngine.prototype.setupResampling = function() { //both at the server and the client
|
AudioEngine.prototype.setupResampling = function() { //both at the server and the client
|
||||||
@ -178,7 +195,7 @@ AudioEngine.prototype.getSampleRate = function() {
|
|||||||
|
|
||||||
AudioEngine.prototype.pushAudio = function(data) {
|
AudioEngine.prototype.pushAudio = function(data) {
|
||||||
if (!this.audioNode) return;
|
if (!this.audioNode) return;
|
||||||
this.stats.audioBytes += data.byteLength;
|
this.audioBytes.add(data.byteLength);
|
||||||
var buffer;
|
var buffer;
|
||||||
if (this.compression === "adpcm") {
|
if (this.compression === "adpcm") {
|
||||||
//resampling & ADPCM
|
//resampling & ADPCM
|
||||||
@ -187,7 +204,6 @@ AudioEngine.prototype.pushAudio = function(data) {
|
|||||||
buffer = new Int16Array(data);
|
buffer = new Int16Array(data);
|
||||||
}
|
}
|
||||||
buffer = this.resampler.process(sdrjs.ConvertI16_F(buffer));
|
buffer = this.resampler.process(sdrjs.ConvertI16_F(buffer));
|
||||||
this.stats.audioSamples += buffer.length;
|
|
||||||
if (this.audioNode.port) {
|
if (this.audioNode.port) {
|
||||||
// AudioWorklets supported
|
// AudioWorklets supported
|
||||||
this.audioNode.port.postMessage(buffer);
|
this.audioNode.port.postMessage(buffer);
|
||||||
|
@ -6,11 +6,12 @@ class OwrxAudioProcessor extends AudioWorkletProcessor {
|
|||||||
this.audioBuffer = new Float32Array(this.bufferSize);
|
this.audioBuffer = new Float32Array(this.bufferSize);
|
||||||
this.inPos = 0;
|
this.inPos = 0;
|
||||||
this.outPos = 0;
|
this.outPos = 0;
|
||||||
|
this.samplesProcessed = 0;
|
||||||
this.port.addEventListener('message', (m) => {
|
this.port.addEventListener('message', (m) => {
|
||||||
if (typeof(m.data) === 'string') {
|
if (typeof(m.data) === 'string') {
|
||||||
const json = JSON.parse(m.data);
|
const json = JSON.parse(m.data);
|
||||||
if (json.cmd && json.cmd === 'getBuffers') {
|
if (json.cmd && json.cmd === 'getStats') {
|
||||||
this.reportBuffers();
|
this.reportStats();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// the ringbuffer size is aligned to the output buffer size, which means that the input buffers might
|
// 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));
|
output.set(this.audioBuffer.subarray(this.outPos, this.outPos + 128));
|
||||||
});
|
});
|
||||||
this.outPos = (this.outPos + 128) % this.bufferSize;
|
this.outPos = (this.outPos + 128) % this.bufferSize;
|
||||||
|
this.samplesProcessed += 128;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
remaining() {
|
remaining() {
|
||||||
@ -44,8 +46,12 @@ class OwrxAudioProcessor extends AudioWorkletProcessor {
|
|||||||
if (mod >= 0) return mod;
|
if (mod >= 0) return mod;
|
||||||
return mod + this.bufferSize;
|
return mod + this.bufferSize;
|
||||||
}
|
}
|
||||||
reportBuffers() {
|
reportStats() {
|
||||||
this.port.postMessage(JSON.stringify({buffersize: this.remaining()}));
|
this.port.postMessage(JSON.stringify({
|
||||||
|
buffersize: this.remaining(),
|
||||||
|
samplesProcessed: this.samplesProcessed
|
||||||
|
}));
|
||||||
|
this.samplesProcessed = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
62
htdocs/lib/Measurement.js
Normal file
62
htdocs/lib/Measurement.js
Normal file
@ -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);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user