At last, I have eliminated (most of) the problems in my adjustable volume control script.
- The safety limit percentage value can now be correctly scrolled down to the biggest (integer) value greater than the volume setting's percentage (real) value.
- Changed the minimum and maximum safety limit positions to 1% and 100%, respectively.
- Fixed a problem which caused the foobar2000 volume to be set slightly above -100 dB instead of truly at -100 dB. Also, when the scrollwheel position is set to 100%, the volume bar's maximum volume position will also truly be at 0 dB.
- The displayed volume setting's percentage value is now properly rounded down.
- Fixed a problem which caused the script to show weird behavior when using the foobar2000 volume control.
However, please note the foobar2000 volume control COMPLETELY overrides the safety limit!
The safety limit is the ONLY reason why I wrote the script! The user of my script should :
- Follow the usual safety recommendations regarding potential loudness fluctuations in an audio track.
- Consider the use of ReplayGain, especially if playing a seqence of multiple, unequally loud, audio tracks.
- Hide the foobar2000 volume control, to prevent accidental overriding the safety limit of the script.
- Hide the foobar2000 status bar because it also has volume control.
- Either bypass the Windows mixer or be careful with its settings, etcetera, because the script ignores them (personally, I am using ASIO4ALL for this purpose because, for one reason or another, the ASIO component for foobar2000 works better for me than WASAPI, and I haven't yet tried Kernel Streaming).
- Not assign a hotkey, consisting of only a single key, to increase the volume. Keys can get stuck.
- Watch out with the mouse. Keep it away from children.
- Remember overall system security. Consider getting a separate PC for playback and storage of your music files. (personally, I am using just a cheap netbook that's very very silent in my listening room and, thanks to foobar2000 being so easy on the system resources, as well as thanks to the highly optimized USB device driver of my external DAC unit, the cheap computer has the processing power it takes for me to be able to play all my Hi Res FLAC files without hiccups.
I hope this helps anyone who might be looking to avoid the thermal noise typically caused by analogue volume control and / or the cost of adding a decent preamp.
Any suggestions to further improve my script are still always welcome.
var g_drag=0;
var sc1=1.01865;
var sc2=0.01865;
var p=25;
var pos=0;
var g_font=gdi.Font("Consolas",16,1);
var g_font2=gdi.Font("Consolas",18,1);
var g_textrender=gdi.CreateStyleTextRender();
var col1=RGB(205,205,205);
var col2=RGB(255,255,255);
function RGB(r,g,b){return(0xff000000|(r<<16)|(g<<8)|(b))}
function RGBA(r,g,b,a){return((a<<24)|(r<<16)|(g<<8)|(b))}
function on_paint(gr){
var ww=window.Width-1;
var wh=window.Height;
var v=fb.Volume;
pos=ww*sc1*Math.exp(0.04*v)-sc2*ww;
var pos2=pos/(p/100);
if(!g_drag) p=pos2>ww+1?Math.ceil(pos*100/ww): p;
pos=pos/(p/100);
gr.FillGradRect(0,0,pos,wh,0,RGB(40,42,57),RGB(p*2+55*g_drag,200-p*2+55*g_drag,Math.abs(100-p*2)+45*g_drag));
gr.FillGradRect(pos,0,ww-pos+1,wh,40,RGB(88,82,98),RGB(18,18,18));
gr.DrawLine(pos-1,0,pos-1,wh,2,RGB(55+p*2,200-p*2,Math.abs(50-p)));
gr.FillGradRect(0,wh/3,p/100*ww,wh/3,0,RGB(p*1.5,100-p/2,0),RGB(p+50,100-p,50-p/2));
gr.FillGradRect(p/100*ww,wh/3,(1-p/100)*ww+2,wh/3,350,RGB(68,74,82),RGB(48,48,48));
gr.DrawRect(-1,wh/3,ww+2,wh/3,1,RGB(55+p*2,200-p*2,Math.abs(50-p)));
gr.DrawLine(p/100*ww-2,wh/3+2,p/100*ww-2,wh*2/3-2,4,RGB(55+p*2,150-p*1.5,Math.abs(50-p)));
gr.SetTextRenderingHint(5);
var p2=-Math.ceil(-p*pos/ww);
var str_p=(p2<10?" ":p2<100?" ":"")+p2;
var vol=Math.ceil(Math.round(v*100)/100);
var voldec=""+Math.ceil(-Math.round((v+Math.ceil(-v)-1)*100)+100);
var str_vol=vol+"."+voldec.substr(1,2);
str_vol=v<-99.99?"-100.00":v>-0.01?" 0.00":" "+(vol>-10?" ":"")+(vol==0?"-":"")+str_vol;
var xr=ww-126;
var xl=-1;
gr.DrawString(str_p+" % "+str_vol+" dB",g_font2,RGB(0,0,0),xr+2,2,62,wh,0x11005000);
gr.DrawString((p<10?" ":p<100?" ":"")+p+" %",g_font,RGB(0,0,0),xl-2,2,62,wh,0x11005000);
gr.DrawString(str_p+" % "+str_vol+" dB",g_font2,g_drag?col2:col1,xr,0,62,wh,0x11005000);
gr.DrawString((p<10?" ":p<100?" ":"")+p+" %",g_font,g_drag?col2:col1,xl,0,62,wh,0x11005000);
}
function on_mouse_lbtn_down(x,y){
if (Math.abs(pos-x)<window.Width/75) g_drag=1;
window.Repaint();
}
function on_mouse_lbtn_up(x,y){
on_mouse_move(x,y);
g_drag=0;
window.Repaint();
}
function on_mouse_rbtn_down(x,y){
g_drag=0;
window.Repaint();
}
function on_mouse_rbtn_up(x,y){
g_drag=0;
window.Repaint();
}
function on_mouse_move(x,y){
var ww=window.Width-1;
if(g_drag){
//var xpos=x>ww?ww:x;
var xpos=x>ww?ww:x;
xpos=xpos*(p/100);
var v=(xpos+sc2*ww)/sc1/ww;
v=25*Math.log((v<0?0:v<1?v:1)+0.00001);
v=v>-0.01?0:v<-99.99?-100:v;
if (fb.Volume!=v)fb.Volume=v;
}
}
function on_mouse_wheel(delta){
if(!g_drag){
if(delta>0){
p=p+1;
p=(p>100)?100:p;
}else{
p=(pos<window.Width-1)?p-1:p;
p=(p<1)?1:p;
}
}
window.Repaint();
}
function on_volume_change(val){
window.Repaint();
}
window.MinHeight=32;
window.MinWidth=320;
fb.Volume=-50;