kill client-side early rebuffering, improving the latency

This commit is contained in:
Jakob Ketterl 2019-10-18 21:13:48 +02:00
parent 93d4e629d1
commit 0b2c457030
3 changed files with 37 additions and 57 deletions

View File

@ -181,11 +181,6 @@ sdrs = {
# ==== Misc settings ==== # ==== Misc settings ====
client_audio_buffer_size = 5
# increasing client_audio_buffer_size will:
# - also increase the latency
# - decrease the chance of audio underruns
iq_port_range = [ iq_port_range = [
4950, 4950,
4960, 4960,

View File

@ -1115,7 +1115,6 @@ function on_ws_recv(evt) {
window.starting_mod = config['start_mod']; window.starting_mod = config['start_mod'];
window.starting_offset_frequency = config['start_offset_freq']; window.starting_offset_frequency = config['start_offset_freq'];
window.audio_buffering_fill_to = config['client_audio_buffer_size'];
bandwidth = config['samp_rate']; bandwidth = config['samp_rate'];
center_freq = config['center_freq'] + config['lfo_offset']; center_freq = config['center_freq'] + config['lfo_offset'];
fft_size = config['fft_size']; fft_size = config['fft_size'];
@ -1258,7 +1257,7 @@ function on_ws_recv(evt) {
audio_prepare(audio_data); audio_prepare(audio_data);
audio_buffer_current_size_debug += audio_data.length; audio_buffer_current_size_debug += audio_data.length;
audio_buffer_all_size_debug += audio_data.length; audio_buffer_all_size_debug += audio_data.length;
if (!(ios || is_chrome) && (audio_initialized === 0 && audio_prepared_buffers.length > audio_buffering_fill_to)) audio_init(); if (!(ios || is_chrome) && (audio_initialized === 0)) audio_init();
break; break;
case 3: case 3:
// secondary FFT // secondary FFT
@ -1536,12 +1535,8 @@ var audio_buffer_maximal_length_sec = 3; //actual number of samples are calculat
var audio_buffer_decrease_to_on_overrun_sec = 2.2; var audio_buffer_decrease_to_on_overrun_sec = 2.2;
var audio_flush_interval_ms = 500; //the interval in which audio_flush() is called var audio_flush_interval_ms = 500; //the interval in which audio_flush() is called
var audio_prepared_buffers = Array(); var audio_buffers = [];
var audio_rebuffer;
var audio_last_output_buffer; var audio_last_output_buffer;
var audio_buffering = false;
//var audio_buffering_fill_to=4; //on audio underrun we wait until this n*audio_buffer_size samples are present
//tnx to the hint from HA3FLT, now we have about half the response time! (original value: 10)
function gain_ff(gain_value, data) //great! solved clicking! will have to move to sdr.js function gain_ff(gain_value, data) //great! solved clicking! will have to move to sdr.js
{ {
@ -1551,24 +1546,12 @@ function gain_ff(gain_value, data) //great! solved clicking! will have to move t
} }
function audio_prepare(data) { function audio_prepare(data) {
var buffer = data;
//audio_rebuffer.push(sdrjs.ConvertI16_F(data));//no resampling if (audio_compression === "adpcm") {
//audio_rebuffer.push(audio_resampler.process(sdrjs.ConvertI16_F(data)));//resampling without ADPCM //resampling & ADPCM
if (audio_compression === "none") buffer = audio_codec.decode(buffer);
audio_rebuffer.push(audio_resampler.process(gain_ff(volume, sdrjs.ConvertI16_F(data))));//resampling without ADPCM
else if (audio_compression === "adpcm")
audio_rebuffer.push(audio_resampler.process(gain_ff(volume, sdrjs.ConvertI16_F(audio_codec.decode(data))))); //resampling & ADPCM
else return;
//console.log("prepare",data.length,audio_rebuffer.remaining());
while (audio_rebuffer.remaining()) {
audio_prepared_buffers.push(audio_rebuffer.take());
audio_buffer_current_count_debug++;
}
if (audio_buffering && audio_prepared_buffers.length > audio_buffering_fill_to) {
console.log("buffers now: " + audio_prepared_buffers.length.toString());
audio_buffering = false;
} }
audio_buffers.push(audio_resampler.process(gain_ff(volume, sdrjs.ConvertI16_F(buffer))));
} }
if (!AudioBuffer.prototype.copyToChannel) { //Chrome 36 does not have it, Firefox does if (!AudioBuffer.prototype.copyToChannel) { //Chrome 36 does not have it, Firefox does
@ -1579,45 +1562,50 @@ if (!AudioBuffer.prototype.copyToChannel) { //Chrome 36 does not have it, Firefo
} }
} }
var silence = new Float32Array(4096);
function audio_onprocess(e) { function audio_onprocess(e) {
if (audio_buffering) { var total = 0;
e.outputBuffer.copyToChannel(silence, 0); var out = new Float32Array(audio_buffer_size);
return; while (audio_buffers.length) {
} var b = audio_buffers.shift();
if (audio_prepared_buffers.length === 0) { var newLength = total + b.length;
audio_buffer_progressbar_update(); // not enough space to fit all data, so splice and put back in the queue
audio_buffering = true; if (newLength > audio_buffer_size) {
e.outputBuffer.copyToChannel(silence, 0); var tokeep = b.slice(0, audio_buffer_size - total);
out.set(tokeep, total);
var tobuffer = b.slice(audio_buffer_size - total, b.length);
audio_buffers.unshift(tobuffer);
break;
} else { } else {
var buf = audio_prepared_buffers.shift(); out.set(b, total);
e.outputBuffer.copyToChannel(buf, 0);
} }
total = newLength;
}
e.outputBuffer.copyToChannel(out, 0);
} }
var audio_buffer_progressbar_update_disabled = false; var audio_buffer_progressbar_update_disabled = false;
var audio_buffer_total_average_level = 0; var audio_buffer_total_average_level = 0;
var audio_buffer_total_average_level_length = 0; var audio_buffer_total_average_level_length = 0;
var audio_overrun_cnt = 0;
var audio_underrun_cnt = 0; function audio_buffers_total_length() {
return audio_buffers.map(function(b){ return b.length; }).reduce(function(a, b){ return a + b; }, 0);
}
function audio_buffer_progressbar_update() { function audio_buffer_progressbar_update() {
if (audio_buffer_progressbar_update_disabled) return; if (audio_buffer_progressbar_update_disabled) return;
var audio_buffer_value = (audio_prepared_buffers.length * audio_buffer_size) / audio_context.sampleRate; var audio_buffer_value = audio_buffers_total_length() / audio_context.sampleRate;
audio_buffer_total_average_level_length++; audio_buffer_total_average_level_length++;
audio_buffer_total_average_level = (audio_buffer_total_average_level * ((audio_buffer_total_average_level_length - 1) / audio_buffer_total_average_level_length)) + (audio_buffer_value / audio_buffer_total_average_level_length); audio_buffer_total_average_level = (audio_buffer_total_average_level * ((audio_buffer_total_average_level_length - 1) / audio_buffer_total_average_level_length)) + (audio_buffer_value / audio_buffer_total_average_level_length);
var overrun = audio_buffer_value > audio_buffer_maximal_length_sec; var overrun = audio_buffer_value > audio_buffer_maximal_length_sec;
var underrun = audio_prepared_buffers.length === 0; var underrun = audio_buffers.length === 0;
var text = "buffer"; var text = "buffer";
if (overrun) { if (overrun) {
text = "overrun"; text = "overrun";
console.log("audio overrun, " + (++audio_overrun_cnt).toString());
} }
if (underrun) { if (underrun) {
text = "underrun"; text = "underrun";
console.log("audio underrun, " + (++audio_underrun_cnt).toString());
} }
if (overrun || underrun) { if (overrun || underrun) {
audio_buffer_progressbar_update_disabled = true; audio_buffer_progressbar_update_disabled = true;
@ -1633,12 +1621,12 @@ function audio_buffer_progressbar_update() {
function audio_flush() { function audio_flush() {
var flushed = false; var flushed = false;
var we_have_more_than = function (sec) { var we_have_more_than = function (sec) {
return sec * audio_context.sampleRate < audio_prepared_buffers.length * audio_buffer_size; return sec * audio_context.sampleRate < audio_buffers_total_length();
}; };
if (we_have_more_than(audio_buffer_maximal_length_sec)) while (we_have_more_than(audio_buffer_decrease_to_on_overrun_sec)) { if (we_have_more_than(audio_buffer_maximal_length_sec)) while (we_have_more_than(audio_buffer_decrease_to_on_overrun_sec)) {
if (!flushed) audio_buffer_progressbar_update(); if (!flushed) audio_buffer_progressbar_update();
flushed = true; flushed = true;
audio_prepared_buffers.shift(); audio_buffers.shift();
} }
} }
@ -1690,13 +1678,11 @@ function audio_preinit() {
else if (audio_context.sampleRate > 44100 * 4) else if (audio_context.sampleRate > 44100 * 4)
audio_buffer_size = 4096 * 4; audio_buffer_size = 4096 * 4;
if (!audio_rebuffer) {
audio_rebuffer = new sdrjs.Rebuffer(audio_buffer_size, sdrjs.REBUFFER_FIXED);
audio_last_output_buffer = new Float32Array(audio_buffer_size);
//we send our setup packet //we send our setup packet
// TODO this should be moved to another stage of initialization
parsehash(); parsehash();
if (!audio_resampler) {
audio_calculate_resampling(audio_context.sampleRate); audio_calculate_resampling(audio_context.sampleRate);
audio_resampler = new sdrjs.RationalResamplerFF(audio_client_resampling_factor, 1); audio_resampler = new sdrjs.RationalResamplerFF(audio_client_resampling_factor, 1);
} }

View File

@ -65,7 +65,6 @@ class OpenWebRxReceiverClient(Client):
"fft_compression", "fft_compression",
"max_clients", "max_clients",
"start_mod", "start_mod",
"client_audio_buffer_size",
"start_freq", "start_freq",
"center_freq", "center_freq",
"mathbox_waterfall_colors", "mathbox_waterfall_colors",