Added more sliders and buttons, waterfall colors are now adjustable from the GUI.

This commit is contained in:
ha7ilm 2016-03-20 11:32:37 +01:00
parent 3c1d3b5b42
commit 06bd8b92aa
3 changed files with 266 additions and 144 deletions

View File

@ -1,7 +1,7 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<!-- <!--
This file is part of OpenWebRX, This file is part of OpenWebRX,
an open-source SDR receiver software with a web UI. an open-source SDR receiver software with a web UI.
Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu> Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
@ -31,8 +31,8 @@
var starting_mod = "%[START_MOD]"; var starting_mod = "%[START_MOD]";
var starting_offset_frequency = %[START_OFFSET_FREQ]; var starting_offset_frequency = %[START_OFFSET_FREQ];
var waterfall_colors=%[WATERFALL_COLORS]; var waterfall_colors=%[WATERFALL_COLORS];
var waterfall_min_level=%[WATERFALL_MIN_LEVEL]; var waterfall_min_level_default=%[WATERFALL_MIN_LEVEL];
var waterfall_max_level=%[WATERFALL_MAX_LEVEL]; var waterfall_max_level_default=%[WATERFALL_MAX_LEVEL];
</script> </script>
<script src="sdr.js"></script> <script src="sdr.js"></script>
<script src="openwebrx.js"></script> <script src="openwebrx.js"></script>
@ -42,8 +42,8 @@
<body onload="openwebrx_init();"> <body onload="openwebrx_init();">
<div id="webrx-page-container"> <div id="webrx-page-container">
<div id="webrx-top-container"> <div id="webrx-top-container">
<div id="webrx-top-photo-clip"> <div id="webrx-top-photo-clip">
<img src="gfx/openwebrx-top-photo.jpg" id="webrx-top-photo"/> <img src="gfx/openwebrx-top-photo.jpg" id="webrx-top-photo"/>
<div id="webrx-rx-photo-title">%[RX_PHOTO_TITLE]</div> <div id="webrx-rx-photo-title">%[RX_PHOTO_TITLE]</div>
<div id="webrx-rx-photo-desc">%[RX_PHOTO_DESC]</div> <div id="webrx-rx-photo-desc">%[RX_PHOTO_DESC]</div>
</div> </div>
@ -72,27 +72,43 @@
<div id="openwebrx-scale-container"> <div id="openwebrx-scale-container">
<canvas id="openwebrx-scale-canvas" width="0" height="0"></canvas> <canvas id="openwebrx-scale-canvas" width="0" height="0"></canvas>
</div> </div>
<div id="webrx-canvas-container"> <div id="webrx-canvas-container">
<div id="openwebrx-phantom-canvas"></div> <div id="openwebrx-phantom-canvas"></div>
<!-- add canvas here by javascript --> <!-- add canvas here by javascript -->
</div> </div>
<div id="openwebrx-panels-container"> <div id="openwebrx-panels-container">
<div class="openwebrx-panel" id="openwebrx-panel-receiver" data-panel-name="client-params" data-panel-pos="right" data-panel-order="0" data-panel-size="215,115"> <div class="openwebrx-panel" id="openwebrx-panel-receiver" data-panel-name="client-params" data-panel-pos="right" data-panel-order="0" data-panel-size="259,115">
<div id="webrx-actual-freq">---.--- MHz</div> <div id="webrx-actual-freq">---.--- MHz</div>
<div id="webrx-mouse-freq">---.--- MHz</div> <div id="webrx-mouse-freq">---.--- MHz</div>
<!--<div class="openwebrx-button" onclick="ws.send('SET mod=wfm');" >WFM</div>--> <!--<div class="openwebrx-button" onclick="ws.send('SET mod=wfm');" >WFM</div>-->
<div class="openwebrx-panel-line"> <div class="openwebrx-panel-line">
<div class="openwebrx-button" onclick="demodulator_analog_replace('nfm');">FM</div> <div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('nfm');">FM</div>
<div class="openwebrx-button" onclick="demodulator_analog_replace('am');">AM</div> <div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('am');">AM</div>
<div class="openwebrx-button" onclick="demodulator_analog_replace('lsb');">LSB</div> <div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('lsb');">LSB</div>
<div class="openwebrx-button" onclick="demodulator_analog_replace('usb');">USB</div> <div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('usb');">USB</div>
<div class="openwebrx-button" onclick="demodulator_analog_replace('cw');">CW</div> <div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('cw');">CW</div>
</div> </div>
<div class="openwebrx-panel-line"> <div class="openwebrx-panel-line">
<div id="openwebrx-mute-off" class="openwebrx-button" onclick="toggleMute()"><img src="gfx/openwebrx-speaker.png" id="openwebrx-mute-img"></div> <div title="Mute on/off" id="openwebrx-mute-off" class="openwebrx-button" onclick="toggleMute();"><img src="gfx/openwebrx-speaker.png" class="openwebrx-sliderbtn-img"></div>
<input id="openwebrx-panel-volume" type="range" min="0" max="150" value="50" step="1" onchange="updateVolume()" oninput="updateVolume()"> <input title="Volume" id="openwebrx-panel-volume" class="openwebrx-panel-slider" type="range" min="0" max="150" value="50" step="1" onchange="updateVolume()" oninput="updateVolume()">
<div title="Auto-adjust waterfall colors" id="openwebrx-waterfall-colors-auto" class="openwebrx-button" onclick="waterfall_measure_minmax_now=true;"><img src="gfx/openwebrx-waterfall-auto.png" class="openwebrx-sliderbtn-img"></div>
<input title="Waterfall minimum level" id="openwebrx-waterfall-color-min" class="openwebrx-panel-slider" type="range" min="-200" max="100" value="50" step="1" onchange="updateWaterfallColors(0);" oninput="updateVolume()">
</div>
<div class="openwebrx-panel-line">
<div title="Disable squelch" id="openwebrx-squelch-default" class="openwebrx-button" onclick="setSquelchDefault()"><img src="gfx/openwebrx-squelch-button.png" class="openwebrx-sliderbtn-img"></div>
<input title="Squelch" id="openwebrx-panel-squelch" class="openwebrx-panel-slider" type="range" min="0" max="100" value="0" step="1" onchange="updateVolume()" oninput="updateVolume()">
<div title="Set waterfall colors to default" id="openwebrx-waterfall-colors-default" class="openwebrx-button" onclick="waterfallColorsDefault()"><img src="gfx/openwebrx-waterfall-default.png" class="openwebrx-sliderbtn-img"></div>
<input title="Waterfall maximum level" id="openwebrx-waterfall-color-max" class="openwebrx-panel-slider" type="range" min="-200" max="100" value="50" step="1" onchange="updateWaterfallColors(1);" oninput="updateVolume()">
</div>
<div class="openwebrx-panel-line">
<div class="openwebrx-button openwebrx-square-button" onclick="zoomInOneStep();" title="Zoom in one step"> <img src="gfx/openwebrx-zoom-in.png" /></div>
<div class="openwebrx-button openwebrx-square-button" onclick="zoomOutOneStep();" title="Zoom out one step"> <img src="gfx/openwebrx-zoom-out.png" /></div>
<div class="openwebrx-button openwebrx-square-button" onclick="zoomInTotal();" title="Zoom in totally"><img src="gfx/openwebrx-zoom-in-total.png" /></div>
<div class="openwebrx-button openwebrx-square-button" onclick="zoomOutTotal();" title="Zoom out totally"><img src="gfx/openwebrx-zoom-out-total.png" /></div>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
/* /*
This file is part of OpenWebRX, This file is part of OpenWebRX,
an open-source SDR receiver software with a web UI. an open-source SDR receiver software with a web UI.
Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu> Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
@ -37,7 +37,6 @@ input[type=range]
{ {
-webkit-appearance: none; -webkit-appearance: none;
margin: 10px 0; margin: 10px 0;
width: 100%;
} }
input[type=range]:focus input[type=range]:focus
{ {
@ -242,7 +241,7 @@ input[type=range]:focus::-ms-fill-upper
position: absolute; position: absolute;
bottom: 0px; bottom: 0px;
left: 0px; left: 0px;
background-image:url(gfx/webrx-photo-gradient-corner.png); background-image:url(gfx/webrx-photo-gradient-corner.png);
width: 59px; width: 59px;
height: 92px; height: 92px;
@ -255,7 +254,7 @@ input[type=range]:focus::-ms-fill-upper
left: 59px; left: 59px;
right: 59px; right: 59px;
height: 92px; height: 92px;
background-image:url(gfx/webrx-photo-gradient-middle.png); background-image:url(gfx/webrx-photo-gradient-middle.png);
} }
#webrx-photo-gradient-right #webrx-photo-gradient-right
@ -263,7 +262,7 @@ input[type=range]:focus::-ms-fill-upper
position: absolute; position: absolute;
bottom: 0px; bottom: 0px;
right: 0px; right: 0px;
background-image:url(gfx/webrx-photo-gradient-corner.png); background-image:url(gfx/webrx-photo-gradient-corner.png);
width: 59px; width: 59px;
height: 92px; height: 92px;
-webkit-transform:scaleX(-1); -webkit-transform:scaleX(-1);
@ -456,7 +455,7 @@ input[type=range]:focus::-ms-fill-upper
background-color: #999999; background-color: #999999;
color: White; color: White;
z-index:9999; /*should be higher? z-index:9999; /*should be higher?
}*/ }*/
/* removed non-free fonts like that: */ /* removed non-free fonts like that: */
@ -484,7 +483,7 @@ input[type=range]:focus::-ms-fill-upper
padding: 0; padding: 0;
margin: 0; margin: 0;
line-height:22px; line-height:22px;
} }
#webrx-mouse-freq #webrx-mouse-freq
@ -497,6 +496,7 @@ input[type=range]:focus::-ms-fill-upper
margin-bottom: 5px; margin-bottom: 5px;
} }
.openwebrx-panel .openwebrx-panel
{ {
visibility: hidden; visibility: hidden;
@ -551,13 +551,72 @@ input[type=range]:focus::-ms-fill-upper
color: #FFFF50; color: #FFFF50;
} }
.openwebrx-button:active .openwebrx-button:active
{ {
background: #777777; background: #777777;
color: #FFFF50; color: #FFFF50;
} }
#openwebrx-client-log-title .openwebrx-demodulator-button
{
width: 35px;
height: 19px;
font-size: 12pt;
text-align: center;
}
.openwebrx-square-button img
{
height: 30px;
}
.openwebrx-round-button
{
margin-right: -2px;
width: 35px;
height: 35px;
border-radius: 25px;
}
.openwebrx-round-button img
{
height: 30px;
}
.openwebrx-round-button-small
{
margin-right: -3px;
width: 20px;
height: 20px;
border-radius: 25px;
}
.openwebrx-round-button-small img
{
height: 20px;
}
img.openwebrx-mirror-img
{
transform: scale(-1, 1);
}
.openwebrx-round-rightarrow img
{
position: relative;
left: 12px;
top: 3px;
}
.openwebrx-round-leftarrow img
{
position: relative;
left: 7px;
top: 3px;
}
#openwebrx-client-log-title
{ {
margin-bottom: 5px; margin-bottom: 5px;
font-weight: bold; font-weight: bold;
@ -626,7 +685,7 @@ input[type=range]:focus::-ms-fill-upper
display: table-cell; display: table-cell;
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
cursor:pointer; cursor:pointer;
} }
#openwebrx-main-buttons li:hover #openwebrx-main-buttons li:hover
@ -668,14 +727,14 @@ input[type=range]:focus::-ms-fill-upper
color: white; color: white;
} }
#openwebrx-panel-volume .openwebrx-panel-slider
{ {
position: relative; position: relative;
top: -2px; top: -2px;
width:170px; width:91px;
} }
#openwebrx-mute-img .openwebrx-sliderbtn-img
{ {
width: 14px; width: 14px;
position:relative; position:relative;

View File

@ -1,6 +1,6 @@
/* /*
This file is part of OpenWebRX, This file is part of OpenWebRX,
an open-source SDR receiver software with a web UI. an open-source SDR receiver software with a web UI.
Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu> Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
@ -58,12 +58,12 @@ var waterfall_timer;
something.fade_i=0; something.fade_i=0;
n_of_iters=time_ms/(1000/fps); n_of_iters=time_ms/(1000/fps);
change=(to-from)/(n_of_iters-1); change=(to-from)/(n_of_iters-1);
something.fade_timer=window.setInterval( something.fade_timer=window.setInterval(
function(){ function(){
if(something.fade_i++<n_of_iters) if(something.fade_i++<n_of_iters)
something.style.opacity=parseFloat(something.style.opacity)+change; something.style.opacity=parseFloat(something.style.opacity)+change;
else else
{something.style.opacity=to; window.clearInterval(something.fade_timer); } {something.style.opacity=to; window.clearInterval(something.fade_timer); }
},1000/fps); },1000/fps);
}*/ }*/
@ -115,12 +115,12 @@ function open_rx_photo()
function style_value(of_what,which) function style_value(of_what,which)
{ {
if(of_what.currentStyle) return of_what.currentStyle[which]; if(of_what.currentStyle) return of_what.currentStyle[which];
else if (window.getComputedStyle) return document.defaultView.getComputedStyle(of_what,null).getPropertyValue(which); else if (window.getComputedStyle) return document.defaultView.getComputedStyle(of_what,null).getPropertyValue(which);
} }
function updateVolume() function updateVolume()
{ {
volume = parseFloat(e("openwebrx-panel-volume").value) / 100; volume = parseFloat(e("openwebrx-panel-volume").value) / 100;
} }
function toggleMute() function toggleMute()
@ -137,15 +137,46 @@ function toggleMute()
e("openwebrx-mute-off").id="openwebrx-mute-on"; e("openwebrx-mute-off").id="openwebrx-mute-on";
e("openwebrx-mute-img").src="gfx/openwebrx-speaker-muted.png"; e("openwebrx-mute-img").src="gfx/openwebrx-speaker-muted.png";
e("openwebrx-panel-volume").disabled=true; e("openwebrx-panel-volume").disabled=true;
e("openwebrx-panel-volume").style.opacity=0.5; e("openwebrx-panel-volume").style.opacity=0.5;
volumeBeforeMute = e("openwebrx-panel-volume").value; volumeBeforeMute = e("openwebrx-panel-volume").value;
e("openwebrx-panel-volume").value=0; e("openwebrx-panel-volume").value=0;
} }
updateVolume(); updateVolume();
} }
function zoomInOneStep () { zoom_set(zoom_level+1); }
function zoomOutOneStep () { zoom_set(zoom_level-1); }
function zoomInTotal () { zoom_set(zoom_levels.length-1); }
function zoomOutTotal () { zoom_set(0); }
function setSquelchDefault() { e("openwebrx-panel-squelch").value=0; }
function updateWaterfallColors(which)
{
wfmax=e("openwebrx-waterfall-color-max");
wfmin=e("openwebrx-waterfall-color-min");
if(parseInt(wfmin.value)>=parseInt(wfmax.value))
{
if(!which) wfmin.value=(parseInt(wfmax.value)-1).toString();
else wfmax.value=(parseInt(wfmin.value)+1).toString();
}
waterfall_min_level=parseInt(wfmin.value);
waterfall_max_level=parseInt(wfmax.value);
}
function waterfallColorsDefault()
{
waterfall_min_level=waterfall_min_level_default;
waterfall_max_level=waterfall_max_level_default;
e("openwebrx-waterfall-color-min").value=waterfall_min_level.toString();
e("openwebrx-waterfall-color-max").value=waterfall_max_level.toString();
}
function waterfallColorsAuto()
{
e("openwebrx-waterfall-color-min").value=(waterfall_measure_minmax_min-20).toString();
e("openwebrx-waterfall-color-max").value=(waterfall_measure_minmax_max+30).toString();
updateWaterfallColors(0);
}
// ======================================================== // ========================================================
// ================= ANIMATION ROUTINES ================= // ================= ANIMATION ROUTINES =================
@ -165,8 +196,8 @@ function animate(object,style_name,unit,from,to,accel,time_ms,fps,to_exec)
if(object.anim_i++<n_of_iters) if(object.anim_i++<n_of_iters)
{ {
if(accel==1) object.style[style_name]=(parseFloat(object.style[style_name])+change).toString()+unit; if(accel==1) object.style[style_name]=(parseFloat(object.style[style_name])+change).toString()+unit;
else else
{ {
remain=parseFloat(object.style[style_name])-to; remain=parseFloat(object.style[style_name])-to;
if(Math.abs(remain)>9||unit!="px") new_val=(to+accel*remain); if(Math.abs(remain)>9||unit!="px") new_val=(to+accel*remain);
else {if(Math.abs(remain)<2) new_val=to; else {if(Math.abs(remain)<2) new_val=to;
@ -174,7 +205,7 @@ function animate(object,style_name,unit,from,to,accel,time_ms,fps,to_exec)
object.style[style_name]=new_val.toString()+unit; object.style[style_name]=new_val.toString()+unit;
} }
} }
else else
{object.style[style_name]=to.toString()+unit; window.clearInterval(object.anim_timer); delete object.anim_timer; } {object.style[style_name]=to.toString()+unit; window.clearInterval(object.anim_timer); delete object.anim_timer; }
if(to_exec!=0) to_exec(); if(to_exec!=0) to_exec();
},1000/fps); },1000/fps);
@ -209,7 +240,7 @@ function demod_envelope_draw(range, from, to, color, line)
// A "drag range" object is returned, containing information about the draggable areas of the envelope // A "drag range" object is returned, containing information about the draggable areas of the envelope
// (beginning, ending and the line showing the offset frequency). // (beginning, ending and the line showing the offset frequency).
if(typeof color == "undefined") color="#ffff00"; //yellow if(typeof color == "undefined") color="#ffff00"; //yellow
env_bounding_line_w=5; // env_bounding_line_w=5; //
env_att_w=5; // _______ ___env_h2 in px ___|_____ env_att_w=5; // _______ ___env_h2 in px ___|_____
env_h1=17; // _/| \_ ___env_h1 in px _/ |_ \_ env_h1=17; // _/| \_ ___env_h1 in px _/ |_ \_
env_h2=5; // |||env_att_line_w |_env_lineplus env_h2=5; // |||env_att_line_w |_env_lineplus
@ -219,11 +250,11 @@ function demod_envelope_draw(range, from, to, color, line)
from_px=scale_px_from_freq(from,range); from_px=scale_px_from_freq(from,range);
to_px=scale_px_from_freq(to,range); to_px=scale_px_from_freq(to,range);
if(to_px<from_px) /* swap'em */ { temp_px=to_px; to_px=from_px; from_px=temp_px; } if(to_px<from_px) /* swap'em */ { temp_px=to_px; to_px=from_px; from_px=temp_px; }
/*from_px-=env_bounding_line_w/2; /*from_px-=env_bounding_line_w/2;
to_px+=env_bounding_line_w/2;*/ to_px+=env_bounding_line_w/2;*/
from_px-=(env_att_w+env_bounding_line_w); from_px-=(env_att_w+env_bounding_line_w);
to_px+=(env_att_w+env_bounding_line_w); to_px+=(env_att_w+env_bounding_line_w);
// do drawing: // do drawing:
scale_ctx.lineWidth=3; scale_ctx.lineWidth=3;
scale_ctx.strokeStyle=color; scale_ctx.strokeStyle=color;
@ -247,7 +278,7 @@ function demod_envelope_draw(range, from, to, color, line)
scale_ctx.globalAlpha = 1; scale_ctx.globalAlpha = 1;
scale_ctx.stroke(); scale_ctx.stroke();
} }
if(typeof line != "undefined") // out of screen? if(typeof line != "undefined") // out of screen?
{ {
line_px=scale_px_from_freq(line,range); line_px=scale_px_from_freq(line,range);
if(!(line_px<0||line_px>window.innerWidth)) if(!(line_px<0||line_px>window.innerWidth))
@ -275,12 +306,12 @@ function demod_envelope_where_clicked(x, drag_ranges, key_modifiers)
if(drag_ranges.envelope_on_screen&&in_range(x,drag_ranges.whole_envelope)) return dr.pbs; if(drag_ranges.envelope_on_screen&&in_range(x,drag_ranges.whole_envelope)) return dr.pbs;
} }
if(drag_ranges.envelope_on_screen) if(drag_ranges.envelope_on_screen)
{ {
// For low and high cut: // For low and high cut:
if(in_range(x,drag_ranges.beginning)) return dr.beginning; if(in_range(x,drag_ranges.beginning)) return dr.beginning;
if(in_range(x,drag_ranges.ending)) return dr.ending; if(in_range(x,drag_ranges.ending)) return dr.ending;
// Last priority: having clicked anything else on the envelope, without holding the shift key // Last priority: having clicked anything else on the envelope, without holding the shift key
if(in_range(x,drag_ranges.whole_envelope)) return dr.anything_else; if(in_range(x,drag_ranges.whole_envelope)) return dr.anything_else;
} }
return dr.none; //User doesn't drag the envelope for this demodulator return dr.none; //User doesn't drag the envelope for this demodulator
} }
@ -304,7 +335,7 @@ demodulator.draggable_ranges={none: 0, beginning:1 /*from*/, ending: 2 /*to*/, a
// This can be used as a base for basic audio demodulators. // This can be used as a base for basic audio demodulators.
// It already supports most basic modulations used for ham radio and commercial services: AM/FM/LSB/USB // It already supports most basic modulations used for ham radio and commercial services: AM/FM/LSB/USB
demodulator_response_time=50; demodulator_response_time=50;
//in ms; if we don't limit the number of SETs sent to the server, audio will underrun (possibly output buffer is cleared on SETs in GNU Radio //in ms; if we don't limit the number of SETs sent to the server, audio will underrun (possibly output buffer is cleared on SETs in GNU Radio
function demodulator_default_analog(offset_frequency,subtype) function demodulator_default_analog(offset_frequency,subtype)
@ -318,7 +349,7 @@ function demodulator_default_analog(offset_frequency,subtype)
high_cut_limit: audio_context.sampleRate/2, high_cut_limit: audio_context.sampleRate/2,
low_cut_limit: -audio_context.sampleRate/2 low_cut_limit: -audio_context.sampleRate/2
}; };
//Subtypes only define some filter parameters and the mod string sent to server, //Subtypes only define some filter parameters and the mod string sent to server,
//so you may set these parameters in your custom child class. //so you may set these parameters in your custom child class.
//Why? As of demodulation is done on the server, difference is mainly on the server side. //Why? As of demodulation is done on the server, difference is mainly on the server side.
this.server_mod=subtype; this.server_mod=subtype;
@ -339,23 +370,23 @@ function demodulator_default_analog(offset_frequency,subtype)
this.low_cut=700; this.low_cut=700;
this.high_cut=900; this.high_cut=900;
this.server_mod="ssb"; this.server_mod="ssb";
} }
else if(subtype=="nfm") else if(subtype=="nfm")
{ {
this.low_cut=-4000; this.low_cut=-4000;
this.high_cut=4000; this.high_cut=4000;
} }
else if(subtype=="am") else if(subtype=="am")
{ {
this.low_cut=-4000; this.low_cut=-4000;
this.high_cut=4000; this.high_cut=4000;
} }
this.wait_for_timer=false; this.wait_for_timer=false;
this.set_after=false; this.set_after=false;
this.set=function() this.set=function()
{ //set() is a wrapper to call doset(), but it ensures that doset won't execute more frequently than demodulator_response_time. { //set() is a wrapper to call doset(), but it ensures that doset won't execute more frequently than demodulator_response_time.
if(!this.wait_for_timer) if(!this.wait_for_timer)
{ {
this.doset(false); this.doset(false);
this.set_after=false; this.set_after=false;
@ -384,7 +415,7 @@ function demodulator_default_analog(offset_frequency,subtype)
// for drawing the filter envelope above scale // for drawing the filter envelope above scale
this.envelope.parent=this; this.envelope.parent=this;
this.envelope.draw=function(visible_range) this.envelope.draw=function(visible_range)
{ {
this.visible_range=visible_range; this.visible_range=visible_range;
this.drag_ranges=demod_envelope_draw(range, this.drag_ranges=demod_envelope_draw(range,
@ -428,22 +459,22 @@ function demodulator_default_analog(offset_frequency,subtype)
//dragging any other parts of the filter envelope while holding Shift does emulate the PBS knob //dragging any other parts of the filter envelope while holding Shift does emulate the PBS knob
//(PassBand Shift) on radio equipment: PBS does move the whole passband without moving the offset //(PassBand Shift) on radio equipment: PBS does move the whole passband without moving the offset
//frequency. //frequency.
if(this.dragged_range==dr.beginning||this.dragged_range==dr.bfo||this.dragged_range==dr.pbs) if(this.dragged_range==dr.beginning||this.dragged_range==dr.bfo||this.dragged_range==dr.pbs)
{ {
//we don't let low_cut go beyond its limits //we don't let low_cut go beyond its limits
if((new_value=this.drag_origin.low_cut+minus*freq_change)<this.parent.filter.low_cut_limit) return true; if((new_value=this.drag_origin.low_cut+minus*freq_change)<this.parent.filter.low_cut_limit) return true;
//nor the filter passband be too small //nor the filter passband be too small
if(this.parent.high_cut-new_value<this.parent.filter.min_passband) return true; if(this.parent.high_cut-new_value<this.parent.filter.min_passband) return true;
//sanity check to prevent GNU Radio "firdes check failed: fa <= fb" //sanity check to prevent GNU Radio "firdes check failed: fa <= fb"
if(new_value>=this.parent.high_cut) return true; if(new_value>=this.parent.high_cut) return true;
this.parent.low_cut=new_value; this.parent.low_cut=new_value;
} }
if(this.dragged_range==dr.ending||this.dragged_range==dr.bfo||this.dragged_range==dr.pbs) if(this.dragged_range==dr.ending||this.dragged_range==dr.bfo||this.dragged_range==dr.pbs)
{ {
//we don't let high_cut go beyond its limits //we don't let high_cut go beyond its limits
if((new_value=this.drag_origin.high_cut+minus*freq_change)>this.parent.filter.high_cut_limit) return true; if((new_value=this.drag_origin.high_cut+minus*freq_change)>this.parent.filter.high_cut_limit) return true;
//nor the filter passband be too small //nor the filter passband be too small
if(new_value-this.parent.low_cut<this.parent.filter.min_passband) return true; if(new_value-this.parent.low_cut<this.parent.filter.min_passband) return true;
//sanity check to prevent GNU Radio "firdes check failed: fa <= fb" //sanity check to prevent GNU Radio "firdes check failed: fa <= fb"
if(new_value<=this.parent.low_cut) return true; if(new_value<=this.parent.low_cut) return true;
this.parent.high_cut=new_value; this.parent.high_cut=new_value;
@ -459,17 +490,17 @@ function demodulator_default_analog(offset_frequency,subtype)
mkenvelopes(this.visible_range); mkenvelopes(this.visible_range);
this.parent.set(); this.parent.set();
//will have to change this when changing to multi-demodulator mode: //will have to change this when changing to multi-demodulator mode:
e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",center_freq+this.parent.offset_frequency,1e6,4); e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",center_freq+this.parent.offset_frequency,1e6,4);
return true; return true;
}; };
this.envelope.drag_end=function(x) this.envelope.drag_end=function(x)
{ //in this demodulator we've already changed values in the drag_move() function so we shouldn't do too much here. { //in this demodulator we've already changed values in the drag_move() function so we shouldn't do too much here.
to_return=this.dragged_range!=demodulator.draggable_ranges.none; //this part is required for cliking anywhere on the scale to set offset to_return=this.dragged_range!=demodulator.draggable_ranges.none; //this part is required for cliking anywhere on the scale to set offset
this.dragged_range=demodulator.draggable_ranges.none; this.dragged_range=demodulator.draggable_ranges.none;
return to_return; return to_return;
}; };
} }
demodulator_default_analog.prototype=new demodulator(); demodulator_default_analog.prototype=new demodulator();
@ -496,9 +527,9 @@ function demodulator_add(what)
} }
function demodulator_analog_replace(subtype) function demodulator_analog_replace(subtype)
{ //this function should only exist until the multi-demodulator capability is added { //this function should only exist until the multi-demodulator capability is added
var temp_offset=0; var temp_offset=0;
if(demodulators.length) if(demodulators.length)
{ {
temp_offset=demodulators[0].offset_frequency; temp_offset=demodulators[0].offset_frequency;
demodulator_remove(0); demodulator_remove(0);
@ -525,7 +556,7 @@ var scale_canvas;
function scale_setup() function scale_setup()
{ {
e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",canvas_get_frequency(window.innerWidth/2),1e6,4); e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",canvas_get_frequency(window.innerWidth/2),1e6,4);
scale_canvas=e("openwebrx-scale-canvas"); scale_canvas=e("openwebrx-scale-canvas");
scale_ctx=scale_canvas.getContext("2d"); scale_ctx=scale_canvas.getContext("2d");
scale_canvas.addEventListener("mousedown", scale_canvas_mousedown, false); scale_canvas.addEventListener("mousedown", scale_canvas_mousedown, false);
scale_canvas.addEventListener("mousemove", scale_canvas_mousemove, false); scale_canvas.addEventListener("mousemove", scale_canvas_mousemove, false);
@ -563,7 +594,7 @@ function scale_offset_freq_from_px(x, visible_range)
function scale_canvas_mousemove(evt) function scale_canvas_mousemove(evt)
{ {
var event_handled; var event_handled;
if(scale_canvas_drag_params.mouse_down&&!scale_canvas_drag_params.drag&&Math.abs(evt.pageX-scale_canvas_drag_params.start_x)>canvas_drag_min_delta) if(scale_canvas_drag_params.mouse_down&&!scale_canvas_drag_params.drag&&Math.abs(evt.pageX-scale_canvas_drag_params.start_x)>canvas_drag_min_delta)
//we can use the main drag_min_delta thing of the main canvas //we can use the main drag_min_delta thing of the main canvas
{ {
scale_canvas_drag_params.drag=true; scale_canvas_drag_params.drag=true;
@ -577,7 +608,7 @@ function scale_canvas_mousemove(evt)
for (var i=0;i<demodulators.length;i++) event_handled|=demodulators[i].envelope.drag_move(evt.pageX); for (var i=0;i<demodulators.length;i++) event_handled|=demodulators[i].envelope.drag_move(evt.pageX);
if (!event_handled) demodulator_set_offset_frequency(0,scale_offset_freq_from_px(evt.pageX)); if (!event_handled) demodulator_set_offset_frequency(0,scale_offset_freq_from_px(evt.pageX));
} }
} }
function scale_canvas_end_drag(x) function scale_canvas_end_drag(x)
@ -681,13 +712,13 @@ var scale_min_space_bw_small_markers=7;
function get_scale_mark_spacing(range) function get_scale_mark_spacing(range)
{ {
out={}; out={};
fcalc=function(freq) fcalc=function(freq)
{ {
out.numlarge=(range.bw/freq); out.numlarge=(range.bw/freq);
out.large=canvas_container.clientWidth/out.numlarge; //distance between large markers (these have text) out.large=canvas_container.clientWidth/out.numlarge; //distance between large markers (these have text)
out.ratio=5; //(ratio-1) small markers exist per large marker out.ratio=5; //(ratio-1) small markers exist per large marker
out.small=out.large/out.ratio; //distance between small markers out.small=out.large/out.ratio; //distance between small markers
if(out.small<scale_min_space_bw_small_markers) return false; if(out.small<scale_min_space_bw_small_markers) return false;
if(out.small/2>=scale_min_space_bw_small_markers&&freq.toString()[0]!="5") {out.small/=2; out.ratio*=2; } if(out.small/2>=scale_min_space_bw_small_markers&&freq.toString()[0]!="5") {out.small/=2; out.ratio*=2; }
out.smallbw=freq/out.ratio; out.smallbw=freq/out.ratio;
return true; return true;
@ -725,11 +756,11 @@ function mkscale()
{ {
var x=scale_px_from_freq(marker_hz,range); var x=scale_px_from_freq(marker_hz,range);
if(x>window.innerWidth) break; if(x>window.innerWidth) break;
scale_ctx.beginPath(); scale_ctx.beginPath();
scale_ctx.moveTo(x, 22); scale_ctx.moveTo(x, 22);
if(marker_hz%spacing.params.large_marker_per_hz==0) if(marker_hz%spacing.params.large_marker_per_hz==0)
{ //large marker { //large marker
if(typeof first_large == "undefined") var first_large=marker_hz; if(typeof first_large == "undefined") var first_large=marker_hz;
last_large=marker_hz; last_large=marker_hz;
scale_ctx.lineWidth=3.5; scale_ctx.lineWidth=3.5;
scale_ctx.lineTo(x,22+11); scale_ctx.lineTo(x,22+11);
@ -742,16 +773,16 @@ function mkscale()
if(scale_px_from_freq(marker_hz+spacing.smallbw*spacing.ratio,range)-text_measured.width>=scale_min_space_bw_texts) if(scale_px_from_freq(marker_hz+spacing.smallbw*spacing.ratio,range)-text_measured.width>=scale_min_space_bw_texts)
{ //and if we have enough space to draw it correctly without clipping { //and if we have enough space to draw it correctly without clipping
scale_ctx.textAlign = "left"; scale_ctx.textAlign = "left";
scale_ctx.fillText(text_to_draw, 0, text_h_pos); scale_ctx.fillText(text_to_draw, 0, text_h_pos);
} }
} }
else if( zoom_level==0 && (range.end-spacing.smallbw*spacing.ratio<marker_hz) && (x>window.innerWidth-text_measured.width/2) ) else if( zoom_level==0 && (range.end-spacing.smallbw*spacing.ratio<marker_hz) && (x>window.innerWidth-text_measured.width/2) )
{ // if this is the last overall marker when zoomed out... and if it would be clipped off the screen... { // if this is the last overall marker when zoomed out... and if it would be clipped off the screen...
if(window.innerWidth-text_measured.width-scale_px_from_freq(marker_hz-spacing.smallbw*spacing.ratio,range)>=scale_min_space_bw_texts) if(window.innerWidth-text_measured.width-scale_px_from_freq(marker_hz-spacing.smallbw*spacing.ratio,range)>=scale_min_space_bw_texts)
{ //and if we have enough space to draw it correctly without clipping { //and if we have enough space to draw it correctly without clipping
scale_ctx.textAlign = "right"; scale_ctx.textAlign = "right";
scale_ctx.fillText(text_to_draw, window.innerWidth, text_h_pos); scale_ctx.fillText(text_to_draw, window.innerWidth, text_h_pos);
} }
} }
else scale_ctx.fillText(text_to_draw, x, text_h_pos); //draw text normally else scale_ctx.fillText(text_to_draw, x, text_h_pos); //draw text normally
} }
@ -791,7 +822,7 @@ function resize_scale()
function canvas_mouseover(evt) function canvas_mouseover(evt)
{ {
if(!waterfall_setup_done) return; if(!waterfall_setup_done) return;
//e("webrx-freq-show").style.visibility="visible"; //e("webrx-freq-show").style.visibility="visible";
} }
function canvas_mouseout(evt) function canvas_mouseout(evt)
@ -854,17 +885,17 @@ function canvas_mousemove(evt)
element.style.left=realX.toString()+"px";*/ element.style.left=realX.toString()+"px";*/
if(canvas_mouse_down) if(canvas_mouse_down)
{ {
if(!canvas_drag&&Math.abs(evt.pageX-canvas_drag_start_x)>canvas_drag_min_delta) if(!canvas_drag&&Math.abs(evt.pageX-canvas_drag_start_x)>canvas_drag_min_delta)
{ {
canvas_drag=true; canvas_drag=true;
canvas_container.style.cursor="move"; canvas_container.style.cursor="move";
} }
if(canvas_drag) if(canvas_drag)
{ {
var deltaX=canvas_drag_last_x-evt.pageX; var deltaX=canvas_drag_last_x-evt.pageX;
var deltaY=canvas_drag_last_y-evt.pageY; var deltaY=canvas_drag_last_y-evt.pageY;
//zoom_center_where=zoom_center_where_calc(evt.pageX); //zoom_center_where=zoom_center_where_calc(evt.pageX);
var dpx=range.hps*deltaX; var dpx=range.hps*deltaX;
if( if(
!(zoom_center_rel+dpx>(bandwidth/2-canvas_container.clientWidth*(1-zoom_center_where)*range.hps)) && !(zoom_center_rel+dpx>(bandwidth/2-canvas_container.clientWidth*(1-zoom_center_where)*range.hps)) &&
!(zoom_center_rel+dpx<-bandwidth/2+canvas_container.clientWidth*zoom_center_where*range.hps) !(zoom_center_rel+dpx<-bandwidth/2+canvas_container.clientWidth*zoom_center_where*range.hps)
@ -892,10 +923,10 @@ function canvas_mouseup(evt)
if(!waterfall_setup_done) return; if(!waterfall_setup_done) return;
relativeX=(evt.offsetX)?evt.offsetX:evt.layerX; relativeX=(evt.offsetX)?evt.offsetX:evt.layerX;
if(!canvas_drag) if(!canvas_drag)
{ {
//ws.send("SET offset_freq="+canvas_get_freq_offset(relativeX).toString()); //ws.send("SET offset_freq="+canvas_get_freq_offset(relativeX).toString());
demodulator_set_offset_frequency(0, canvas_get_freq_offset(relativeX)); demodulator_set_offset_frequency(0, canvas_get_freq_offset(relativeX));
e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",canvas_get_frequency(relativeX),1e6,4); e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",canvas_get_frequency(relativeX),1e6,4);
} }
else else
@ -928,7 +959,7 @@ function canvas_mousewheel(evt)
//console.log(dir); //console.log(dir);
//i/=120; //i/=120;
/*while (i--)*/ zoom_step(dir, relativeX, zoom_center_where_calc(evt.pageX)); /*while (i--)*/ zoom_step(dir, relativeX, zoom_center_where_calc(evt.pageX));
evt.preventDefault(); evt.preventDefault();
//evt.returnValue = false; //disable scrollbar move //evt.returnValue = false; //disable scrollbar move
} }
@ -961,12 +992,26 @@ function mkzoomlevels()
function zoom_step(out, where, onscreen) function zoom_step(out, where, onscreen)
{ {
if((out&&zoom_level==0)||(!out&&zoom_level>=zoom_levels_count-1)) return; if((out&&zoom_level==0)||(!out&&zoom_level>=zoom_levels_count-1)) return;
if(out) --zoom_level; if(out) --zoom_level;
else ++zoom_level; else ++zoom_level;
zoom_center_rel=canvas_get_freq_offset(where); zoom_center_rel=canvas_get_freq_offset(where);
//console.log("zoom_step || zlevel: "+zoom_level.toString()+" zlevel_val: "+zoom_levels[zoom_level].toString()+" zoom_center_rel: "+zoom_center_rel.toString()); //console.log("zoom_step || zlevel: "+zoom_level.toString()+" zlevel_val: "+zoom_levels[zoom_level].toString()+" zoom_center_rel: "+zoom_center_rel.toString());
zoom_center_where=onscreen; zoom_center_where=onscreen;
console.log(zoom_center_where, zoom_center_rel, where);
resize_canvases(true);
mkscale();
}
function zoom_set(level)
{
if(!(level>=0&&level<=zoom_levels.length-1)) return;
level=parseInt(level);
zoom_level = level;
//zoom_center_rel=canvas_get_freq_offset(-canvases[0].offsetLeft+canvas_container.clientWidth/2); //zoom to screen center instead of demod envelope
zoom_center_rel=demodulators[0].offset_frequency;
zoom_center_where=0.5+(zoom_center_rel/bandwidth); //this is a kind of hack
console.log(zoom_center_where, zoom_center_rel, -canvases[0].offsetLeft+canvas_container.clientWidth/2);
resize_canvases(true); resize_canvases(true);
mkscale(); mkscale();
} }
@ -977,7 +1022,7 @@ function zoom_calc()
var canvases_new_width=winsize*zoom_levels[zoom_level]; var canvases_new_width=winsize*zoom_levels[zoom_level];
zoom_offset_px=-((canvases_new_width*(0.5+zoom_center_rel/bandwidth))-(winsize*zoom_center_where)); zoom_offset_px=-((canvases_new_width*(0.5+zoom_center_rel/bandwidth))-(winsize*zoom_center_where));
if(zoom_offset_px>0) zoom_offset_px=0; if(zoom_offset_px>0) zoom_offset_px=0;
if(zoom_offset_px<winsize-canvases_new_width) if(zoom_offset_px<winsize-canvases_new_width)
zoom_offset_px=winsize-canvases_new_width; zoom_offset_px=winsize-canvases_new_width;
//console.log("zoom_calc || zopx:"+zoom_offset_px.toString()+ " maxoff:"+(winsize-canvases_new_width).toString()+" relval:"+(0.5+zoom_center_rel/bandwidth).toString() ); //console.log("zoom_calc || zopx:"+zoom_offset_px.toString()+ " maxoff:"+(winsize-canvases_new_width).toString()+" relval:"+(0.5+zoom_center_rel/bandwidth).toString() );
} }
@ -1001,9 +1046,9 @@ function audio_calculate_resampling(targetRate)
while(true) while(true)
{ {
audio_server_output_rate = Math.floor(targetRate / i); audio_server_output_rate = Math.floor(targetRate / i);
if(audio_server_output_rate < output_range_min) if(audio_server_output_rate < output_range_min)
{ {
audio_client_resampling_factor = audio_server_output_rate = 0; audio_client_resampling_factor = audio_server_output_rate = 0;
divlog("Your audio card sampling rate ("+targetRate.toString()+") is not supported.<br />Please change your operating system default settings in order to fix this.",1); divlog("Your audio card sampling rate ("+targetRate.toString()+") is not supported.<br />Please change your operating system default settings in order to fix this.",1);
} }
if(audio_server_output_rate >= output_range_min && audio_server_output_rate <= output_range_max) break; //okay, we're done if(audio_server_output_rate >= output_range_min && audio_server_output_rate <= output_range_max) break; //okay, we're done
@ -1029,7 +1074,7 @@ function on_ws_recv(evt)
{ {
var stringData=arrayBufferToString(evt.data); var stringData=arrayBufferToString(evt.data);
if(stringData.substring(0,16)=="CLIENT DE SERVER") divlog("Acknowledged WebSocket connection: "+stringData); if(stringData.substring(0,16)=="CLIENT DE SERVER") divlog("Acknowledged WebSocket connection: "+stringData);
} }
if(firstChars=="AUD") if(firstChars=="AUD")
{ {
@ -1048,7 +1093,7 @@ function on_ws_recv(evt)
else if(fft_compression="adpcm") else if(fft_compression="adpcm")
{ {
fft_codec.reset(); fft_codec.reset();
var waterfall_i16=fft_codec.decode(new Uint8Array(evt.data,4)); var waterfall_i16=fft_codec.decode(new Uint8Array(evt.data,4));
var waterfall_f32=new Float32Array(waterfall_i16.length-COMPRESS_FFT_PAD_N); var waterfall_f32=new Float32Array(waterfall_i16.length-COMPRESS_FFT_PAD_N);
for(var i=0;i<waterfall_i16.length;i++) waterfall_f32[i]=waterfall_i16[i+COMPRESS_FFT_PAD_N]/100; for(var i=0;i<waterfall_i16.length;i++) waterfall_f32[i]=waterfall_i16[i+COMPRESS_FFT_PAD_N]/100;
@ -1068,10 +1113,10 @@ function on_ws_recv(evt)
case "setup": case "setup":
waterfall_init(); waterfall_init();
audio_preinit(); audio_preinit();
break; break;
case "bandwidth": case "bandwidth":
bandwidth=parseInt(param[1]); bandwidth=parseInt(param[1]);
break; break;
case "center_freq": case "center_freq":
center_freq=parseInt(param[1]); //there was no ; and it was no problem... why? center_freq=parseInt(param[1]); //there was no ; and it was no problem... why?
break; break;
@ -1122,6 +1167,7 @@ function add_problem(what)
} }
waterfall_measure_minmax=false; waterfall_measure_minmax=false;
waterfall_measure_minmax_now=false;
waterfall_measure_minmax_min=1e100; waterfall_measure_minmax_min=1e100;
waterfall_measure_minmax_max=-1e100; waterfall_measure_minmax_max=-1e100;
@ -1139,13 +1185,14 @@ function waterfall_measure_minmax_print()
function waterfall_add_queue(what) function waterfall_add_queue(what)
{ {
if(waterfall_measure_minmax) waterfall_measure_minmax_do(what); if(waterfall_measure_minmax) waterfall_measure_minmax_do(what);
if(waterfall_measure_minmax_now) { waterfall_measure_minmax_do(what); waterfall_measure_minmax_now=false; waterfallColorsAuto(); }
waterfall_queue.push(what); waterfall_queue.push(what);
} }
function waterfall_dequeue() function waterfall_dequeue()
{ {
if(waterfall_queue.length) waterfall_add(waterfall_queue.shift()); if(waterfall_queue.length) waterfall_add(waterfall_queue.shift());
if(waterfall_queue.length>Math.max(fft_fps/2,20)) //in case of emergency if(waterfall_queue.length>Math.max(fft_fps/2,20)) //in case of emergency
{ {
console.log("waterfall queue length:", waterfall_queue.length); console.log("waterfall queue length:", waterfall_queue.length);
add_problem("fft overflow"); add_problem("fft overflow");
@ -1165,7 +1212,7 @@ function divlog(what, is_error)
{ {
is_error=!!is_error; is_error=!!is_error;
was_error |= is_error; was_error |= is_error;
if(is_error) if(is_error)
{ {
what="<span class=\"webrx-error\">"+what+"</span>"; what="<span class=\"webrx-error\">"+what+"</span>";
if(e("openwebrx-panel-log").openwebrxHidden) toggle_panel("openwebrx-panel-log"); //show panel if any error is present if(e("openwebrx-panel-log").openwebrxHidden) toggle_panel("openwebrx-panel-log"); //show panel if any error is present
@ -1213,7 +1260,7 @@ function gain_ff(gain_value,data) //great! solved clicking! will have to move to
function audio_prepare(data) function audio_prepare(data)
{ {
//audio_rebuffer.push(sdrjs.ConvertI16_F(data));//no resampling //audio_rebuffer.push(sdrjs.ConvertI16_F(data));//no resampling
//audio_rebuffer.push(audio_resampler.process(sdrjs.ConvertI16_F(data)));//resampling without ADPCM //audio_rebuffer.push(audio_resampler.process(sdrjs.ConvertI16_F(data)));//resampling without ADPCM
if(audio_compression=="none") if(audio_compression=="none")
@ -1223,7 +1270,7 @@ function audio_prepare(data)
else return; else return;
//console.log("prepare",data.length,audio_rebuffer.remaining()); //console.log("prepare",data.length,audio_rebuffer.remaining());
while(audio_rebuffer.remaining()) while(audio_rebuffer.remaining())
{ {
audio_prepared_buffers.push(audio_rebuffer.take()); audio_prepared_buffers.push(audio_rebuffer.take());
audio_buffer_current_count_debug++; audio_buffer_current_count_debug++;
@ -1236,7 +1283,7 @@ function audio_prepare_without_resampler(data)
{ {
audio_rebuffer.push(sdrjs.ConvertI16_F(data)); audio_rebuffer.push(sdrjs.ConvertI16_F(data));
console.log("prepare",data.length,audio_rebuffer.remaining()); console.log("prepare",data.length,audio_rebuffer.remaining());
while(audio_rebuffer.remaining()) while(audio_rebuffer.remaining())
{ {
audio_prepared_buffers.push(audio_rebuffer.take()); audio_prepared_buffers.push(audio_rebuffer.take());
audio_buffer_current_count_debug++; audio_buffer_current_count_debug++;
@ -1271,7 +1318,7 @@ function audio_prepare_old(data)
if(audio_last_output_offset==audio_buffer_size) dopush(); if(audio_last_output_offset==audio_buffer_size) dopush();
} }
else else
{ //array is larger than the remaining space in the output buffer { //array is larger than the remaining space in the output buffer
var copied=audio_buffer_size-audio_last_output_offset; var copied=audio_buffer_size-audio_last_output_offset;
var remain=data.length-copied; var remain=data.length-copied;
for(var i=0;i<audio_buffer_size-audio_last_output_offset;i++) //fill the remaining space in the output buffer for(var i=0;i<audio_buffer_size-audio_last_output_offset;i++) //fill the remaining space in the output buffer
@ -1320,12 +1367,12 @@ function audio_buffer_progressbar_update()
var text="buffer"; var text="buffer";
if(overrun) { text="overrun"; console.log("audio overrun, "+(++audio_overrun_cnt).toString()); } if(overrun) { text="overrun"; console.log("audio overrun, "+(++audio_overrun_cnt).toString()); }
if(underrun) { text="underrun"; console.log("audio underrun, "+(++audio_underrun_cnt).toString()); } if(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;
window.setTimeout(function(){audio_buffer_progressbar_update_disabled=false; audio_buffer_progressbar_update();},1000); window.setTimeout(function(){audio_buffer_progressbar_update_disabled=false; audio_buffer_progressbar_update();},1000);
} }
progressbar_set(e("openwebrx-bar-audio-buffer"),(underrun)?1:audio_buffer_value/1.5,"Audio "+text+" ["+(audio_buffer_value).toFixed(1)+" s]",overrun||underrun||audio_buffer_value<0.25); progressbar_set(e("openwebrx-bar-audio-buffer"),(underrun)?1:audio_buffer_value/1.5,"Audio "+text+" ["+(audio_buffer_value).toFixed(1)+" s]",overrun||underrun||audio_buffer_value<0.25);
} }
@ -1344,10 +1391,10 @@ function audio_flush()
} }
function audio_onprocess_notused(e) function audio_onprocess_notused(e)
{ {
//https://github.com/0xfe/experiments/blob/master/www/tone/js/sinewave.js //https://github.com/0xfe/experiments/blob/master/www/tone/js/sinewave.js
if(audio_received.length==0) if(audio_received.length==0)
{ add_problem("audio underrun"); return; } { add_problem("audio underrun"); return; }
output = e.outputBuffer.getChannelData(0); output = e.outputBuffer.getChannelData(0);
int_buffer = audio_received[0]; int_buffer = audio_received[0];
@ -1356,7 +1403,7 @@ function audio_onprocess_notused(e)
obi=0; //output buffer index obi=0; //output buffer index
debug_str="" debug_str=""
while(1) while(1)
{ {
if(int_buffer.length-audio_buffer_index>read_remain) if(int_buffer.length-audio_buffer_index>read_remain)
{ {
@ -1367,7 +1414,7 @@ function audio_onprocess_notused(e)
break; break;
} }
else else
{ {
for (i=audio_buffer_index; i<int_buffer.length; i++) for (i=audio_buffer_index; i<int_buffer.length; i++)
output[obi++] = int_buffer[i]/32768; output[obi++] = int_buffer[i]/32768;
read_remain-=(int_buffer.length-audio_buffer_index); read_remain-=(int_buffer.length-audio_buffer_index);
@ -1380,7 +1427,7 @@ function audio_onprocess_notused(e)
else*/ else*/
audio_received.splice(0,1); audio_received.splice(0,1);
//debug_str+="added remain, remain="+read_remain.toString()+" abi="+audio_buffer_index.toString()+" alen="+int_buffer.length.toString()+" i="+i.toString()+" arecva="+audio_received.length.toString()+" obi="+obi.toString()+"\n"; //debug_str+="added remain, remain="+read_remain.toString()+" abi="+audio_buffer_index.toString()+" alen="+int_buffer.length.toString()+" i="+i.toString()+" arecva="+audio_received.length.toString()+" obi="+obi.toString()+"\n";
audio_buffer_index = 0; audio_buffer_index = 0;
if(audio_received.length == 0 || read_remain == 0) return; if(audio_received.length == 0 || read_remain == 0) return;
int_buffer = audio_received[0]; int_buffer = audio_received[0];
} }
@ -1392,7 +1439,7 @@ function audio_onprocess_notused(e)
function audio_flush_notused() function audio_flush_notused()
{ {
if (audio_buffer_current_size>audio_buffer_maximal_length_sec*audio_context.sampleRate) if (audio_buffer_current_size>audio_buffer_maximal_length_sec*audio_context.sampleRate)
{ {
add_problem("audio overrun"); add_problem("audio overrun");
console.log("audio_flush() :: size: "+audio_buffer_current_size.toString()+" allowed: "+(audio_buffer_maximal_length_sec*audio_context.sampleRate).toString()); console.log("audio_flush() :: size: "+audio_buffer_current_size.toString()+" allowed: "+(audio_buffer_maximal_length_sec*audio_context.sampleRate).toString());
while (audio_buffer_current_size>audio_buffer_maximal_length_sec*audio_context.sampleRate*0.5) while (audio_buffer_current_size>audio_buffer_maximal_length_sec*audio_context.sampleRate*0.5)
@ -1410,7 +1457,7 @@ function webrx_set_param(what, value)
function parsehash() function parsehash()
{ {
if(h=window.location.hash) if(h=window.location.hash)
{ {
h.substring(1).split(",").forEach(function(x){ h.substring(1).split(",").forEach(function(x){
harr=x.split("="); harr=x.split("=");
@ -1418,28 +1465,28 @@ function parsehash()
if(harr[0]=="mod") starting_mod = harr[1]; if(harr[0]=="mod") starting_mod = harr[1];
if(harr[0]=="freq") { if(harr[0]=="freq") {
console.log(parseInt(harr[1])); console.log(parseInt(harr[1]));
console.log(center_freq); console.log(center_freq);
starting_offset_frequency = parseInt(harr[1])-center_freq; starting_offset_frequency = parseInt(harr[1])-center_freq;
} }
}); });
} }
} }
function audio_preinit() function audio_preinit()
{ {
try try
{ {
window.AudioContext = window.AudioContext||window.webkitAudioContext; window.AudioContext = window.AudioContext||window.webkitAudioContext;
audio_context = new AudioContext(); audio_context = new AudioContext();
} }
catch(e) catch(e)
{ {
divlog('Your browser does not support Web Audio API, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser.', 1); divlog('Your browser does not support Web Audio API, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser.', 1);
} }
//we send our setup packet //we send our setup packet
parsehash(); parsehash();
//needs audio_context.sampleRate to exist //needs audio_context.sampleRate to exist
@ -1465,7 +1512,7 @@ function audio_init()
audio_node = createjsnode_function(audio_buffer_size, 0, 1); audio_node = createjsnode_function(audio_buffer_size, 0, 1);
audio_node.onaudioprocess = audio_onprocess; audio_node.onaudioprocess = audio_onprocess;
audio_node.connect(audio_context.destination); audio_node.connect(audio_context.destination);
// --- Resampling --- // --- Resampling ---
//https://github.com/grantgalitz/XAudioJS/blob/master/XAudioServer.js //https://github.com/grantgalitz/XAudioJS/blob/master/XAudioServer.js
//audio_resampler = new Resampler(audio_received_sample_rate, audio_context.sampleRate, 1, audio_buffer_size, true); //audio_resampler = new Resampler(audio_received_sample_rate, audio_context.sampleRate, 1, audio_buffer_size, true);
//audio_input_buffer_size = audio_buffer_size*(audio_received_sample_rate/audio_context.sampleRate); //audio_input_buffer_size = audio_buffer_size*(audio_received_sample_rate/audio_context.sampleRate);
@ -1478,8 +1525,8 @@ function audio_init()
audio_source.buffer = buffer; audio_source.buffer = buffer;
audio_source.noteOn(0);*/ audio_source.noteOn(0);*/
demodulator_analog_replace(starting_mod); demodulator_analog_replace(starting_mod);
if(starting_offset_frequency) if(starting_offset_frequency)
{ {
demodulators[0].offset_frequency = starting_offset_frequency; demodulators[0].offset_frequency = starting_offset_frequency;
e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",center_freq+starting_offset_frequency,1e6,4); e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",center_freq+starting_offset_frequency,1e6,4);
demodulators[0].set(); demodulators[0].set();
@ -1499,11 +1546,11 @@ function audio_init()
function on_ws_closed() function on_ws_closed()
{ {
try try
{ {
audio_node.disconnect(); audio_node.disconnect();
} }
catch (dont_care) {} catch (dont_care) {}
divlog("WebSocket has closed unexpectedly. Please reload the page.", 1); divlog("WebSocket has closed unexpectedly. Please reload the page.", 1);
} }
function on_ws_error(event) function on_ws_error(event)
@ -1516,11 +1563,11 @@ String.prototype.startswith=function(str){ return this.indexOf(str) == 0; }; //h
function open_websocket() function open_websocket()
{ {
//if(ws_url.startswith("ws://localhost:")&&window.location.hostname!="127.0.0.1"&&window.location.hostname!="localhost") //if(ws_url.startswith("ws://localhost:")&&window.location.hostname!="127.0.0.1"&&window.location.hostname!="localhost")
//{ //{
//divlog("Server administrator should set <em>server_hostname</em> correctly, because it is left as <em>\"localhost\"</em>. Now guessing hostname from page URL.",1); //divlog("Server administrator should set <em>server_hostname</em> correctly, because it is left as <em>\"localhost\"</em>. Now guessing hostname from page URL.",1);
ws_url="ws://"+(window.location.origin.split("://")[1])+"/ws/"; //guess automatically -> now default behaviour ws_url="ws://"+(window.location.origin.split("://")[1])+"/ws/"; //guess automatically -> now default behaviour
//} //}
if (!("WebSocket" in window)) if (!("WebSocket" in window))
divlog("Your browser does not support WebSocket, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser."); divlog("Your browser does not support WebSocket, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser.");
ws = new WebSocket(ws_url+client_id); ws = new WebSocket(ws_url+client_id);
ws.onopen = on_ws_opened; ws.onopen = on_ws_opened;
@ -1566,15 +1613,15 @@ var canvas_container;
var canvas_phantom; var canvas_phantom;
function add_canvas() function add_canvas()
{ {
new_canvas = document.createElement("canvas"); new_canvas = document.createElement("canvas");
new_canvas.width=fft_size; new_canvas.width=fft_size;
new_canvas.height=canvas_default_height; new_canvas.height=canvas_default_height;
canvas_actual_line=canvas_default_height-1; canvas_actual_line=canvas_default_height-1;
new_canvas.style.width=(canvas_container.clientWidth*zoom_levels[zoom_level]).toString()+"px"; new_canvas.style.width=(canvas_container.clientWidth*zoom_levels[zoom_level]).toString()+"px";
new_canvas.style.left=zoom_offset_px.toString()+"px"; new_canvas.style.left=zoom_offset_px.toString()+"px";
new_canvas.style.height=canvas_default_height.toString()+"px"; new_canvas.style.height=canvas_default_height.toString()+"px";
new_canvas.openwebrx_top=(-canvas_default_height+1); new_canvas.openwebrx_top=(-canvas_default_height+1);
new_canvas.style.top=new_canvas.openwebrx_top.toString()+"px"; new_canvas.style.top=new_canvas.openwebrx_top.toString()+"px";
canvas_context = new_canvas.getContext("2d"); canvas_context = new_canvas.getContext("2d");
canvas_container.appendChild(new_canvas); canvas_container.appendChild(new_canvas);
@ -1608,7 +1655,7 @@ canvas_maxshift=0;
function shift_canvases() function shift_canvases()
{ {
canvases.forEach(function(p) canvases.forEach(function(p)
{ {
p.style.top=(p.openwebrx_top++).toString()+"px"; p.style.top=(p.openwebrx_top++).toString()+"px";
}); });
@ -1621,8 +1668,8 @@ function shift_canvases()
} }
else else
canvas_phantom.style.display="none"; canvas_phantom.style.display="none";
//canvas_container.style.height=(((canvases.length-1)*canvas_default_height)+(canvas_default_height-canvas_actual_line)).toString()+"px"; //canvas_container.style.height=(((canvases.length-1)*canvas_default_height)+(canvas_default_height-canvas_actual_line)).toString()+"px";
//canvas_container.style.height="100%"; //canvas_container.style.height="100%";
} }
@ -1634,7 +1681,7 @@ function resize_canvases(zoom)
zoom_calc(); zoom_calc();
new_width=(canvas_container.clientWidth*zoom_levels[zoom_level]).toString()+"px"; new_width=(canvas_container.clientWidth*zoom_levels[zoom_level]).toString()+"px";
var zoom_value=zoom_offset_px.toString()+"px"; var zoom_value=zoom_offset_px.toString()+"px";
canvases.forEach(function(p) canvases.forEach(function(p)
{ {
p.style.width=new_width; p.style.width=new_width;
p.style.left=zoom_value; p.style.left=zoom_value;
@ -1695,7 +1742,7 @@ function waterfall_add(data)
remain=pixel_per_point-(1-remain); remain=pixel_per_point-(1-remain);
} }
} }
} }
else else
{ //make line smaller (linear decimation, moving average) { //make line smaller (linear decimation, moving average)
@ -1708,7 +1755,7 @@ function waterfall_add(data)
{ {
if(remain>1) if(remain>1)
{ {
last_pixel+=data[i]; last_pixel+=data[i];
remain--; remain--;
} }
else else
@ -1722,8 +1769,8 @@ function waterfall_add(data)
} }
} }
//Add line to waterfall image //Add line to waterfall image
base=(h-1)*w*4; base=(h-1)*w*4;
for(x=0;x<w;x++) for(x=0;x<w;x++)
{ {
color=waterfall_mkcolor(scaled[x]); color=waterfall_mkcolor(scaled[x]);
@ -1731,7 +1778,7 @@ function waterfall_add(data)
waterfall_image.data[base+x*4+i] = ((color>>>0)>>((3-i)*8))&0xff; waterfall_image.data[base+x*4+i] = ((color>>>0)>>((3-i)*8))&0xff;
}*/ }*/
//Add line to waterfall image //Add line to waterfall image
oneline_image = canvas_context.createImageData(w,1); oneline_image = canvas_context.createImageData(w,1);
for(x=0;x<w;x++) for(x=0;x<w;x++)
{ {
@ -1740,12 +1787,12 @@ function waterfall_add(data)
oneline_image.data[x*4+i] = ((color>>>0)>>((3-i)*8))&0xff; oneline_image.data[x*4+i] = ((color>>>0)>>((3-i)*8))&0xff;
} }
//Draw image //Draw image
canvas_context.putImageData(oneline_image, 0, canvas_actual_line--); canvas_context.putImageData(oneline_image, 0, canvas_actual_line--);
shift_canvases(); shift_canvases();
if(canvas_actual_line<0) add_canvas(); if(canvas_actual_line<0) add_canvas();
//divlog("Drawn FFT"); //divlog("Drawn FFT");
} }
@ -1769,14 +1816,14 @@ function check_top_bar_congestion()
var rightmost=Math.max(rmf(wet),rmf(wed)); var rightmost=Math.max(rmf(wet),rmf(wed));
var tl=e("openwebrx-main-buttons"); var tl=e("openwebrx-main-buttons");
[wet, wed].map(function(what) { [wet, wed].map(function(what) {
if(rmf(what)>tl.offsetLeft-20) what.style.opacity=what.style.opacity="0"; if(rmf(what)>tl.offsetLeft-20) what.style.opacity=what.style.opacity="0";
else wet.style.opacity=wed.style.opacity="1"; else wet.style.opacity=wed.style.opacity="1";
}); });
} }
function openwebrx_resize() function openwebrx_resize()
{ {
resize_canvases(); resize_canvases();
resize_waterfall_container(true); resize_waterfall_container(true);
@ -1795,10 +1842,11 @@ function openwebrx_init()
//Synchronise volume with slider //Synchronise volume with slider
updateVolume(); updateVolume();
waterfallColorsDefault();
} }
/* /*
window.setInterval(function(){ window.setInterval(function(){
sum=0; sum=0;
for(i=0;i<audio_received.length;i++) for(i=0;i<audio_received.length;i++)
sum+=audio_received[i].length; sum+=audio_received[i].length;
@ -1833,15 +1881,15 @@ function debug_audio()
// ((audio_buffer_current_count_debug*audio_buffer_size)/audio_debug_time_taken).toFixed(1)+" sps output"; // ((audio_buffer_current_count_debug*audio_buffer_size)/audio_debug_time_taken).toFixed(1)+" sps output";
var audio_speed_value=audio_buffer_current_size_debug*kbps_mult/audio_debug_time_since_last_call; var audio_speed_value=audio_buffer_current_size_debug*kbps_mult/audio_debug_time_since_last_call;
progressbar_set(e("openwebrx-bar-audio-speed"),audio_speed_value/500000,"Audio stream ["+(audio_speed_value/1000).toFixed(0)+" kbps]",false); progressbar_set(e("openwebrx-bar-audio-speed"),audio_speed_value/500000,"Audio stream ["+(audio_speed_value/1000).toFixed(0)+" kbps]",false);
var audio_output_value=(audio_buffer_current_count_debug*audio_buffer_size)/audio_debug_time_taken; var audio_output_value=(audio_buffer_current_count_debug*audio_buffer_size)/audio_debug_time_taken;
progressbar_set(e("openwebrx-bar-audio-output"),audio_output_value/55000,"Audio output ["+(audio_output_value/1000).toFixed(1)+" ksps]",audio_output_value>55000||audio_output_value<10000); progressbar_set(e("openwebrx-bar-audio-output"),audio_output_value/55000,"Audio output ["+(audio_output_value/1000).toFixed(1)+" ksps]",audio_output_value>55000||audio_output_value<10000);
audio_buffer_progressbar_update(); audio_buffer_progressbar_update();
var network_speed_value=debug_ws_data_received/audio_debug_time_taken; var network_speed_value=debug_ws_data_received/audio_debug_time_taken;
progressbar_set(e("openwebrx-bar-network-speed"),network_speed_value*8/2000,"Network usage ["+(network_speed_value*8).toFixed(1)+" kbps]",false); progressbar_set(e("openwebrx-bar-network-speed"),network_speed_value*8/2000,"Network usage ["+(network_speed_value*8).toFixed(1)+" kbps]",false);
audio_buffer_current_size_debug=0; audio_buffer_current_size_debug=0;
@ -1858,10 +1906,10 @@ function pop_bottommost_panel(from)
{ {
min_order=parseInt(from[0].dataset.panelOrder); min_order=parseInt(from[0].dataset.panelOrder);
min_index=0; min_index=0;
for(i=0;i<from.length;i++) for(i=0;i<from.length;i++)
{ {
actual_order=parseInt(from[i].dataset.panelOrder); actual_order=parseInt(from[i].dataset.panelOrder);
if(actual_order<min_order) if(actual_order<min_order)
{ {
min_index=i; min_index=i;
min_order=actual_order; min_order=actual_order;
@ -1893,7 +1941,7 @@ function place_panels()
c=plist[i]; c=plist[i];
if(c.className=="openwebrx-panel") if(c.className=="openwebrx-panel")
{ {
if(c.openwebrxHidden) if(c.openwebrxHidden)
{ {
c.style.display="none"; c.style.display="none";
continue; continue;
@ -1907,7 +1955,7 @@ function place_panels()
//c.style.height=newSize[1]+"px"; //c.style.height=newSize[1]+"px";
if(!c.openwebrxPanelTransparent) c.style.margin=panel_margin.toString()+"px"; if(!c.openwebrxPanelTransparent) c.style.margin=panel_margin.toString()+"px";
else c.style.marginLeft=panel_margin.toString()+"px"; else c.style.marginLeft=panel_margin.toString()+"px";
c.openwebrxPanelWidth=parseInt(newSize[0]); c.openwebrxPanelWidth=parseInt(newSize[0]);
c.openwebrxPanelHeight=parseInt(newSize[1]); c.openwebrxPanelHeight=parseInt(newSize[1]);
} }
} }
@ -1938,7 +1986,7 @@ function progressbar_set(obj,val,text,over)
if (val>1) val=1; if (val>1) val=1;
var innerBar=null; var innerBar=null;
var innerText=null; var innerText=null;
for(var i=0;i<obj.children.length;i++) for(var i=0;i<obj.children.length;i++)
{ {
if(obj.children[i].className=="openwebrx-progressbar-text") innerText=obj.children[i]; if(obj.children[i].className=="openwebrx-progressbar-text") innerText=obj.children[i];
else if(obj.children[i].className=="openwebrx-progressbar-bar") innerBar=obj.children[i]; else if(obj.children[i].className=="openwebrx-progressbar-bar") innerBar=obj.children[i];
@ -1951,4 +1999,3 @@ function progressbar_set(obj,val,text,over)
if(innerText==null) return; if(innerText==null) return;
innerText.innerHTML=text; innerText.innerHTML=text;
} }