kopia lustrzana https://github.com/jameshball/osci-render
Use modified version of dood.al oscilloscope for visualiser
rodzic
ea406551f8
commit
0e62c7667a
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 8.9 KiB |
|
@ -0,0 +1,662 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<style>
|
||||
audio { width: 100%; }
|
||||
body{font: 12px Courier, Monospace;}
|
||||
canvas{margin-right: 10px;}
|
||||
table {
|
||||
border-spacing:0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body bgcolor="silver" text="black" autocomplete="off" style="margin: 0px;">
|
||||
|
||||
<script>
|
||||
var controls=
|
||||
{
|
||||
swapXY : false,
|
||||
sweepOn : false,
|
||||
sweepMsDiv : 1,
|
||||
sweepTriggerValue : 0,
|
||||
signalGeneratorOn : false,
|
||||
mainGain : 0.0,
|
||||
exposureStops : 0.0,
|
||||
audioVolume : 1.0,
|
||||
hue : 125,
|
||||
freezeImage: false,
|
||||
disableFilter: false,
|
||||
aValue : 1.0,
|
||||
aExponent : 0.0,
|
||||
bValue : 1.0,
|
||||
bExponent :0.0,
|
||||
invertXY : false,
|
||||
grid : true,
|
||||
persistence : 0,
|
||||
xExpression : "sin(2*PI*a*t)*cos(2*PI*b*t)",
|
||||
yExpression : "cos(2*PI*a*t)*cos(2*PI*b*t)",
|
||||
}
|
||||
|
||||
Number.prototype.toFixedMinus = function(k)
|
||||
{
|
||||
if (this<0) return this.toFixed(k);
|
||||
//else return '\xa0'+this.toFixed(k);
|
||||
else return '+'+this.toFixed(k);
|
||||
}
|
||||
|
||||
var toggleVisible = function(string)
|
||||
{
|
||||
var element = document.getElementById(string);
|
||||
console.log(element.style.display);
|
||||
if (element.style.display == "none") element.style.display="block";
|
||||
else element.style.display = "none";
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
|
||||
<td valign="top" style="padding: 0px;">
|
||||
<canvas id="crtCanvas" width="800" height="800"></canvas>
|
||||
<b id="clicktostart" style="position: absolute; z-index: 1; left: 100px; top: 50px; width:200px; height:40px; font-size:18px; color: rgb(116, 187, 116);">
|
||||
[CLICK TO START]
|
||||
</b>
|
||||
</td>
|
||||
<td width="360" valign="top">
|
||||
|
||||
<br>
|
||||
|
||||
<b id="title" style="font-size:26px"> XXY OSCILLOSCOPE </b> <b id="samplerate"></b>
|
||||
|
||||
<br>
|
||||
|
||||
<hr noshade>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td> <!--TOP LEFT CONTROL-->
|
||||
<table>
|
||||
<tr><td align="center">Gain</td></tr>
|
||||
<tr><td><input id="mainGain" type="range" width="200" min="-1" max="4" value=0.0 step=0.05
|
||||
oninput="controls.mainGain=mainGain.value; mainGainOutput.value=parseFloat(mainGain.value).toFixedMinus(2)+' '"></td>
|
||||
<td> <output id="mainGainOutput">+0.00</td>
|
||||
</tr></table>
|
||||
</td>
|
||||
|
||||
<td> <!--TOP RIGHT CONTROL-->
|
||||
<table>
|
||||
<tr><td align="center">Intensity</td></tr>
|
||||
<tr><td><input id="exposure" type="range" width="200" min="-2" max="2" value=0.0 step=0.1
|
||||
oninput="controls.exposureStops=this.value; exposureOutput.value=parseFloat(this.value).toFixedMinus(1)"></td>
|
||||
<td> <output id="exposureOutput">+0.0</td>
|
||||
</tr></table>
|
||||
</td>
|
||||
|
||||
<tr>
|
||||
<td> <!--BOTTOM LEFT CONTROL-->
|
||||
<table>
|
||||
<tr><td align="center">Audio volume</td></tr>
|
||||
<tr><td><input id="audioVolume" type="range" width="200" min="0" max="1" value=1.0 step=0.01
|
||||
oninput="controls.audioVolume=this.value; audioVolumeOutput.value=parseFloat(this.value).toFixed(2)"></td>
|
||||
<td> <output id="audioVolumeOutput">1.00</td>
|
||||
</tr></table>
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td> <!--BOTTOM RIGHT CONTROL-->
|
||||
<table>
|
||||
<tr><td>  
|
||||
<input id="swapXY" type="checkbox" onchange="controls.swapXY=this.checked"> Swap x / y
|
||||
</td></tr>
|
||||
<tr><td>  
|
||||
<input id="invertXY" type="checkbox" onchange="controls.invertXY=this.checked"> Invert x and y
|
||||
</td></tr>
|
||||
</table><div align="right">
|
||||
<a href="javascript:toggleVisible('displayNotes'); toggleVisible('generatorNotes'); toggleVisible('micNotes');">(notes)</a>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<div id="displayNotes" style="display:none">
|
||||
<p><i>Version 0.96 (May 2022). Made
|
||||
by <a href="http://venuspatrol.nfshost.com">Neil Thapen</a>.<br>
|
||||
Line-drawing code adapted from
|
||||
<a href="https://github.com/m1el/woscope">woscope</a> by e1ml.<br>
|
||||
Thanks to e1ml and ompuco for inspiration.</i>
|
||||
</div>
|
||||
|
||||
<hr noshade>
|
||||
|
||||
<p><b style="font-size:18px">
|
||||
<input id="sweepCheckbox" type="checkbox" onchange="controls.sweepOn=this.checked"> SWEEP</b>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<table>
|
||||
<tr><td align="center">Trigger value</td></tr>
|
||||
<tr><td><input id="trigger" type="range" width="200" min="-1" max="1" value=0.0 step=0.001
|
||||
oninput="controls.sweepTriggerValue=0.5*this.value*Math.abs(this.value); triggerOutput.value=parseFloat(controls.sweepTriggerValue).toFixedMinus(2)+' '"></td>
|
||||
<td> <output id="triggerOutput">+0.00</td>
|
||||
</tr></table>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<table>
|
||||
<tr><td align="center">Milliseconds/div</td></tr>
|
||||
<tr><td><input id="msDiv" type="range" width="200" min="0" max="7" value=2 step=1
|
||||
oninput="controls.sweepMsDiv=Math.pow(2, this.value-2); msDivOutput.value = controls.sweepMsDiv"></td>
|
||||
<td> <output id="msDivOutput">1</td>
|
||||
</tr></table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<hr noshade>
|
||||
|
||||
<p><b style="font-size:18px">
|
||||
<input id="generatorCheckbox" type="checkbox" onchange="controls.signalGeneratorOn=this.checked"> SIGNAL GENERATOR</b>
|
||||
|
||||
<p> x = <input type="text" size="37" id="xInput" value = ""
|
||||
onkeydown = "if (event.keyCode == 13) {UI.compile(); xNote.value='';}"
|
||||
oninput = "if (this.value != controls.xExpression) xNote.value='X'; else xNote.value='';"/>
|
||||
<output id="xNote"> </output><br>
|
||||
|
||||
 y = <input type="text" size="37" id="yInput" value = ""
|
||||
onkeydown = "if (event.keyCode == 13) {UI.compile(); yNote.value='';}"
|
||||
oninput = "if (this.value != controls.yExpression) yNote.value='X'; else yNote.value='';"/>
|
||||
<output id="yNote"> </output><br>
|
||||
|
||||
|
||||
<table border="0">
|
||||
<tr>
|
||||
<td width="155"></td> <td width="45"></td><td width="155"></td> <td width="45"></td>
|
||||
</tr><tr>
|
||||
<td align="right">Parameter a</td> <td></td>
|
||||
<td><input id="aExponent" type="range" style="width:90%" min="0" max="3" value=0 step=1
|
||||
oninput="controls.aExponent=this.value; aExponentOutput.value=[' x1',' x10','x100','x1000'][this.value]"></td>
|
||||
<td> <output id="aExponentOutput"> x1</td>
|
||||
</tr><tr>
|
||||
<td colspan="3"><input id="aValue" type="range" style="width:95%" min="0.5" max="5.00" value=1.0 step=0.02
|
||||
oninput="controls.aValue=this.value; aValueOutput.value=parseFloat(this.value).toFixed(2)"><br></td>
|
||||
<td> <output id="aValueOutput">1.00</td>
|
||||
</tr>
|
||||
<tr><td height="5"></td></tr>
|
||||
<tr>
|
||||
<td align="right">Parameter b</td> <td></td>
|
||||
<td><input id="bExponent" type="range" style="width:90%" min="0" max="3" value=0 step=1
|
||||
oninput="controls.bExponent=this.value; bExponentOutput.value=[' x1',' x10','x100','x1000'][this.value]"></td>
|
||||
<td> <output id="bExponentOutput"> x1</td>
|
||||
</tr><tr>
|
||||
<td colspan="3"><input id="bValue" type="range" style="width:95%" min="0.5" max="5.00" value=1.0 step=0.02
|
||||
oninput="controls.bValue=this.value; bValueOutput.value=parseFloat(this.value).toFixed(2)"></td>
|
||||
<td> <output id="bValueOutput">1.00</td></tr>
|
||||
</table>
|
||||
|
||||
<div id="generatorNotes" style="display:none">
|
||||
<p><i>Use javascript expressions, e.g. 'Math.tan(t)'.<br>
|
||||
t is the time, n is the number of samples.</i>
|
||||
</div>
|
||||
|
||||
<hr noshade>
|
||||
|
||||
<p><b style="font-size:18px">
|
||||
<input type="checkbox" id="micCheckbox"
|
||||
onchange="if (this.checked) AudioSystem.tryToGetMicrophone(); else AudioSystem.disconnectMicrophone()"> MICROPHONE</b>
|
||||
<output id="microphoneOutput">
|
||||
|
||||
|
||||
<div id="micNotes" style="display:none"><i>
|
||||
Set "Audio Volume" to zero to avoid feedback.<br>
|
||||
Stereo input may not work in some browsers.<br>
|
||||
<p>To get audio from another program,
|
||||
you can either physically connect your audio output to your audio input,
|
||||
or use third party software.
|
||||
E.g. <a href="http://vb-audio.pagesperso-orange.fr/Cable/">VB-CABLE</a> on Windows,
|
||||
or <a href="https://github.com/mattingalls/Soundflower">Soundflower</a> with
|
||||
<a href="https://github.com/mLupine/SoundflowerBed">SoundflowerBed</a> on Mac.
|
||||
</i></div>
|
||||
|
||||
<hr noshade>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width=200><b style="font-size:18px"> PLAY FILE<b></td>
|
||||
<td width=200><input id="audioFile" type="file" accept="audio/*" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p><audio id="audioElement" controls></audio>
|
||||
|
||||
<script>
|
||||
var file;
|
||||
audioFile.onchange = function()
|
||||
{
|
||||
if (file) URL.revokeObjectURL(file)
|
||||
var files = this.files;
|
||||
file = URL.createObjectURL(files[0]);
|
||||
audioElement.src = file;
|
||||
audioElement.play();
|
||||
};
|
||||
</script>
|
||||
|
||||
<hr noshade>
|
||||
|
||||
|
||||
<table><tr>
|
||||
|
||||
<td>
|
||||
<table>
|
||||
<tr><td align="center">Hue</td></tr>
|
||||
<tr><td><input id="hue" type="range" width="200" min="0" max="359" value=125 step=1
|
||||
oninput="controls.hue=this.value; hueOutput.value=this.value"></td>
|
||||
<td width=35> <output id="hueOutput">125</td>
|
||||
</tr>
|
||||
<tr><td align="center">Persistence</td></tr>
|
||||
<tr><td><input id="persistence" type="range" width="200" min="-1" max="1" value=0 step=0.01
|
||||
oninput="controls.persistence=this.value; persistenceOutput.value=parseFloat(this.value).toFixedMinus(1)"></td>
|
||||
<td width=35> <output id="persistenceOutput">0.00</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<table>
|
||||
<tr><td>
|
||||
  <input id="freeze" type="checkbox" onchange="controls.freezeImage=this.checked"> Freeze image
|
||||
</td></tr>
|
||||
<tr><td>
|
||||
  <input id="disableFilter" type="checkbox" onchange="controls.disableFilter=this.checked"> Disable upsampling
|
||||
</td></tr>
|
||||
<tr><td>
|
||||
  <input id="hideGrid" type="checkbox"
|
||||
onchange="controls.grid=!controls.grid; if (Render) Render.screenTexture = Render.loadTexture('noise.jpg');"> Hide graticule
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
|
||||
</tr></table>
|
||||
|
||||
|
||||
<input id="urlText" type="text" size="28" style="margin-top:5px" onclick="Controls.generateUrl()"
|
||||
value=" export current settings as a URL">
|
||||
 <a href="javascript:restoreDefaults();">[reset all]</a>
|
||||
|
||||
<script>
|
||||
|
||||
var Controls = {
|
||||
generateUrl : function()
|
||||
{
|
||||
var locationString = location.toString();
|
||||
var site = locationString.split('#')[0];
|
||||
//var jsonText = JSON.stringify(getControlsArray());
|
||||
//jsonText = jsonText.replace(/"/g, 'Q');
|
||||
//var hm = encodeURI(jsonText);
|
||||
var text = this.getControlsArray().toString();
|
||||
var hm = encodeURI(text);
|
||||
urlText.value = site+'#'+hm;
|
||||
urlText.select();
|
||||
},
|
||||
|
||||
getControlsArray : function()
|
||||
{
|
||||
var a = [];
|
||||
a.push(mainGain.value);
|
||||
a.push(exposure.value);
|
||||
//a.push(audioVolume.value);
|
||||
a.push(0+swapXY.checked);
|
||||
a.push(0+invertXY.checked);
|
||||
a.push(0+sweepCheckbox.checked);
|
||||
a.push(trigger.value);
|
||||
a.push(msDiv.value);
|
||||
a.push(0+generatorCheckbox.checked);
|
||||
a.push(this.encodeString(xInput.value));
|
||||
a.push(this.encodeString(yInput.value));
|
||||
a.push(aExponent.value);
|
||||
a.push(aValue.value);
|
||||
a.push(bExponent.value);
|
||||
a.push(bValue.value);
|
||||
// don't try to record microphone status
|
||||
a.push(hue.value);
|
||||
a.push(persistence.value);
|
||||
a.push(0+disableFilter.checked);
|
||||
a.push(0+hideGrid.checked);
|
||||
return a;
|
||||
},
|
||||
|
||||
setupControls : function()
|
||||
{
|
||||
var locationString = location.toString();
|
||||
if (!(locationString.includes('#'))) return;
|
||||
var hash = locationString.split('#')[1];
|
||||
var arrayString = decodeURI(hash);
|
||||
var a = arrayString.split(',');
|
||||
this.setupSlider(mainGain, a.shift());
|
||||
this.setupSlider(exposure, a.shift());
|
||||
//this.setupSlider(audioVolume, a.shift());
|
||||
this.setupSlider(audioVolume, "0");
|
||||
this.setupCheckbox(swapXY, a.shift());
|
||||
this.setupCheckbox(invertXY, a.shift());
|
||||
this.setupCheckbox(sweepCheckbox, a.shift());
|
||||
this.setupSlider(trigger, a.shift());
|
||||
this.setupSlider(msDiv, a.shift());
|
||||
this.setupCheckbox(generatorCheckbox, a.shift());
|
||||
this.setupString(xInput, a.shift());
|
||||
this.setupString(yInput, a.shift());
|
||||
this.setupSlider(aExponent, a.shift());
|
||||
this.setupSlider(aValue, a.shift());
|
||||
this.setupSlider(bExponent, a.shift());
|
||||
this.setupSlider(bValue, a.shift());
|
||||
this.setupSlider(hue, a.shift());
|
||||
this.setupSlider(persistence, a.shift());
|
||||
this.setupCheckbox(disableFilter, a.shift());
|
||||
this.setupCheckbox(hideGrid, a.shift());
|
||||
UI.compile();
|
||||
},
|
||||
|
||||
encodeString : function(s)
|
||||
{
|
||||
s=s.replace(/ /g,"");
|
||||
s=s.replace(/,/g,";");
|
||||
return s;
|
||||
},
|
||||
|
||||
decodeString : function(s)
|
||||
{
|
||||
s=s.replace(/;/,",");
|
||||
return s;
|
||||
},
|
||||
|
||||
setupSlider : function(slider, s)
|
||||
{
|
||||
slider.value = parseFloat(s);
|
||||
slider.oninput();
|
||||
},
|
||||
|
||||
setupCheckbox : function(checkbox, s)
|
||||
{
|
||||
checkbox.checked = parseInt(s);
|
||||
checkbox.onchange();
|
||||
},
|
||||
|
||||
setupString : function(inp, s)
|
||||
{
|
||||
inp.value = this.decodeString(s);
|
||||
},
|
||||
|
||||
restoreDefaults : function()
|
||||
{
|
||||
var locationString = location.toString();
|
||||
var site = locationString.split('#')[0];
|
||||
location = site;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div id="extraNotes" style="display:none">
|
||||
</div>
|
||||
|
||||
|
||||
<!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -->
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<script id="vertex" type="x-shader">
|
||||
attribute vec2 vertexPosition;
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(vertexPosition, 0.0, 1.0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="fragment" type="x-shader">
|
||||
precision highp float;
|
||||
uniform vec4 colour;
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = colour;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- The Gaussian line-drawing code, the next two shaders, is adapted
|
||||
from woscope by e1ml : https://github.com/m1el/woscope -->
|
||||
|
||||
<script id="gaussianVertex" type="x-shader">
|
||||
#define EPS 1E-6
|
||||
uniform float uInvert;
|
||||
uniform float uSize;
|
||||
uniform float uNEdges;
|
||||
uniform float uFadeAmount;
|
||||
uniform float uIntensity;
|
||||
uniform float uGain;
|
||||
attribute vec2 aStart, aEnd;
|
||||
attribute float aIdx;
|
||||
varying vec4 uvl;
|
||||
varying vec2 vTexCoord;
|
||||
varying float vLen;
|
||||
varying float vSize;
|
||||
void main () {
|
||||
float tang;
|
||||
vec2 current;
|
||||
// All points in quad contain the same data:
|
||||
// segment start point and segment end point.
|
||||
// We determine point position using its index.
|
||||
float idx = mod(aIdx,4.0);
|
||||
|
||||
// `dir` vector is storing the normalized difference
|
||||
// between end and start
|
||||
vec2 dir = (aEnd-aStart)*uGain;
|
||||
uvl.z = length(dir);
|
||||
|
||||
if (uvl.z > EPS)
|
||||
{
|
||||
dir = dir / uvl.z;
|
||||
vSize = 0.006/pow(uvl.z,0.08);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the segment is too short, just draw a square
|
||||
dir = vec2(1.0, 0.0);
|
||||
vSize = 0.006/pow(EPS,0.08);
|
||||
}
|
||||
|
||||
vSize = uSize;
|
||||
vec2 norm = vec2(-dir.y, dir.x);
|
||||
|
||||
if (idx >= 2.0) {
|
||||
current = aEnd*uGain;
|
||||
tang = 1.0;
|
||||
uvl.x = -vSize;
|
||||
} else {
|
||||
current = aStart*uGain;
|
||||
tang = -1.0;
|
||||
uvl.x = uvl.z + vSize;
|
||||
}
|
||||
// `side` corresponds to shift to the "right" or "left"
|
||||
float side = (mod(idx, 2.0)-0.5)*2.0;
|
||||
uvl.y = side * vSize;
|
||||
|
||||
uvl.w = uIntensity*mix(1.0-uFadeAmount, 1.0, floor(aIdx / 4.0 + 0.5)/uNEdges);
|
||||
|
||||
vec4 pos = vec4((current+(tang*dir+norm*side)*vSize)*uInvert,0.0,1.0);
|
||||
gl_Position = pos;
|
||||
vTexCoord = 0.5*pos.xy+0.5;
|
||||
//float seed = floor(aIdx/4.0);
|
||||
//seed = mod(sin(seed*seed), 7.0);
|
||||
//if (mod(seed/2.0, 1.0)<0.5) gl_Position = vec4(10.0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="gaussianFragment" type="x-shader">
|
||||
#define EPS 1E-6
|
||||
#define TAU 6.283185307179586
|
||||
#define TAUR 2.5066282746310002
|
||||
#define SQRT2 1.4142135623730951
|
||||
precision highp float;
|
||||
uniform float uSize;
|
||||
uniform float uIntensity;
|
||||
uniform sampler2D uScreen;
|
||||
varying float vSize;
|
||||
varying vec4 uvl;
|
||||
varying vec2 vTexCoord;
|
||||
|
||||
// A standard gaussian function, used for weighting samples
|
||||
float gaussian(float x, float sigma)
|
||||
{
|
||||
return exp(-(x * x) / (2.0 * sigma * sigma)) / (TAUR * sigma);
|
||||
}
|
||||
|
||||
// This approximates the error function, needed for the gaussian integral
|
||||
float erf(float x)
|
||||
{
|
||||
float s = sign(x), a = abs(x);
|
||||
x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
|
||||
x *= x;
|
||||
return s - s / (x * x);
|
||||
}
|
||||
|
||||
void main (void)
|
||||
{
|
||||
float len = uvl.z;
|
||||
vec2 xy = uvl.xy;
|
||||
float brightness;
|
||||
|
||||
float sigma = vSize/5.0;
|
||||
if (len < EPS)
|
||||
{
|
||||
// If the beam segment is too short, just calculate intensity at the position.
|
||||
brightness = gaussian(length(xy), sigma);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, use analytical integral for accumulated intensity.
|
||||
brightness = erf(xy.x/SQRT2/sigma) - erf((xy.x-len)/SQRT2/sigma);
|
||||
brightness *= exp(-xy.y*xy.y/(2.0*sigma*sigma))/2.0/len;
|
||||
}
|
||||
|
||||
brightness *= uvl.w;
|
||||
gl_FragColor = 2.0 * texture2D(uScreen, vTexCoord) * brightness;
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="texturedVertex" type="x-shader">
|
||||
precision highp float;
|
||||
attribute vec2 aPos;
|
||||
varying vec2 vTexCoord;
|
||||
void main (void)
|
||||
{
|
||||
gl_Position = vec4(aPos, 0.0, 1.0);
|
||||
vTexCoord = (0.5*aPos+0.5);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="texturedVertexWithResize" type="x-shader">
|
||||
precision highp float;
|
||||
attribute vec2 aPos;
|
||||
varying vec2 vTexCoord;
|
||||
uniform float uResizeForCanvas;
|
||||
void main (void)
|
||||
{
|
||||
gl_Position = vec4(aPos, 0.0, 1.0);
|
||||
vTexCoord = (0.5*aPos+0.5)*uResizeForCanvas;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="texturedFragment" type="x-shader">
|
||||
precision highp float;
|
||||
uniform sampler2D uTexture0;
|
||||
varying vec2 vTexCoord;
|
||||
void main (void)
|
||||
{
|
||||
gl_FragColor = texture2D(uTexture0, vTexCoord);
|
||||
gl_FragColor.a= 1.0;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="blurFragment" type="x-shader">
|
||||
precision highp float;
|
||||
uniform sampler2D uTexture0;
|
||||
uniform vec2 uOffset;
|
||||
varying vec2 vTexCoord;
|
||||
void main (void)
|
||||
{
|
||||
vec4 sum = vec4(0.0);
|
||||
sum += texture2D(uTexture0, vTexCoord - uOffset*8.0) * 0.000078;
|
||||
sum += texture2D(uTexture0, vTexCoord - uOffset*7.0) * 0.000489;
|
||||
sum += texture2D(uTexture0, vTexCoord - uOffset*6.0) * 0.002403;
|
||||
sum += texture2D(uTexture0, vTexCoord - uOffset*5.0) * 0.009245;
|
||||
sum += texture2D(uTexture0, vTexCoord - uOffset*4.0) * 0.027835;
|
||||
sum += texture2D(uTexture0, vTexCoord - uOffset*3.0) * 0.065592;
|
||||
sum += texture2D(uTexture0, vTexCoord - uOffset*2.0) * 0.12098;
|
||||
sum += texture2D(uTexture0, vTexCoord - uOffset*1.0) * 0.17467;
|
||||
sum += texture2D(uTexture0, vTexCoord + uOffset*0.0) * 0.19742;
|
||||
sum += texture2D(uTexture0, vTexCoord + uOffset*1.0) * 0.17467;
|
||||
sum += texture2D(uTexture0, vTexCoord + uOffset*2.0) * 0.12098;
|
||||
sum += texture2D(uTexture0, vTexCoord + uOffset*3.0) * 0.065592;
|
||||
sum += texture2D(uTexture0, vTexCoord + uOffset*4.0) * 0.027835;
|
||||
sum += texture2D(uTexture0, vTexCoord + uOffset*5.0) * 0.009245;
|
||||
sum += texture2D(uTexture0, vTexCoord + uOffset*6.0) * 0.002403;
|
||||
sum += texture2D(uTexture0, vTexCoord + uOffset*7.0) * 0.000489;
|
||||
sum += texture2D(uTexture0, vTexCoord + uOffset*8.0) * 0.000078;
|
||||
gl_FragColor = sum;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="outputVertex" type="x-shader">
|
||||
precision highp float;
|
||||
attribute vec2 aPos;
|
||||
varying vec2 vTexCoord;
|
||||
varying vec2 vTexCoordCanvas;
|
||||
uniform float uResizeForCanvas;
|
||||
void main (void)
|
||||
{
|
||||
gl_Position = vec4(aPos, 0.0, 1.0);
|
||||
vTexCoord = (0.5*aPos+0.5);
|
||||
vTexCoordCanvas = vTexCoord*uResizeForCanvas;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="outputFragment" type="x-shader">
|
||||
precision highp float;
|
||||
uniform sampler2D uTexture0; //line
|
||||
uniform sampler2D uTexture1; //tight glow
|
||||
uniform sampler2D uTexture2; //big glow
|
||||
uniform sampler2D uTexture3; //screen
|
||||
uniform float uExposure;
|
||||
uniform vec3 uColour;
|
||||
varying vec2 vTexCoord;
|
||||
varying vec2 vTexCoordCanvas;
|
||||
void main (void)
|
||||
{
|
||||
vec4 line = texture2D(uTexture0, vTexCoordCanvas);
|
||||
// r components have grid; g components do not.
|
||||
vec4 screen = texture2D(uTexture3, vTexCoord);
|
||||
vec4 tightGlow = texture2D(uTexture1, vTexCoord);
|
||||
vec4 scatter = texture2D(uTexture2, vTexCoord)+0.35;
|
||||
float light = line.r + 1.5*screen.g*screen.g*tightGlow.r;
|
||||
light += 0.4*scatter.g * (2.0+1.0*screen.g + 0.5*screen.r);
|
||||
float tlight = 1.0-pow(2.0, -uExposure*light);
|
||||
float tlight2 = tlight*tlight*tlight;
|
||||
gl_FragColor.rgb = mix(uColour, vec3(1.0), 0.3+tlight2*tlight2*0.5)*tlight;
|
||||
gl_FragColor.a= 1.0;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="oscilloscope.js"></script>
|
|
@ -0,0 +1,942 @@
|
|||
|
||||
var AudioSystem =
|
||||
{
|
||||
microphoneActive : false,
|
||||
|
||||
init : function (bufferSize)
|
||||
{
|
||||
window.AudioContext = window.AudioContext||window.webkitAudioContext;
|
||||
this.audioContext = new window.AudioContext();
|
||||
this.sampleRate = this.audioContext.sampleRate;
|
||||
this.bufferSize = bufferSize;
|
||||
this.timePerSample = 1/this.sampleRate;
|
||||
this.oldXSamples = new Float32Array(this.bufferSize);
|
||||
this.oldYSamples = new Float32Array(this.bufferSize);
|
||||
this.smoothedXSamples = new Float32Array(Filter.nSmoothedSamples);
|
||||
this.smoothedYSamples = new Float32Array(Filter.nSmoothedSamples);
|
||||
|
||||
if (!(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia))
|
||||
{
|
||||
microphoneOutput.value = " unavailable in this browser";
|
||||
}
|
||||
},
|
||||
|
||||
startSound : function()
|
||||
{
|
||||
var audioElement = document.getElementById("audioElement");
|
||||
this.source = this.audioContext.createMediaElementSource(audioElement);
|
||||
this.audioVolumeNode = this.audioContext.createGain();
|
||||
|
||||
this.generator = this.audioContext.createScriptProcessor(this.bufferSize, 0, 2);
|
||||
this.generator.onaudioprocess = SignalGenerator.generate;
|
||||
|
||||
this.scopeNode = this.audioContext.createScriptProcessor(this.bufferSize, 2, 2);
|
||||
this.scopeNode.onaudioprocess = doScriptProcessor;
|
||||
this.source.connect(this.scopeNode);
|
||||
this.generator.connect(this.scopeNode);
|
||||
|
||||
this.scopeNode.connect(this.audioVolumeNode);
|
||||
this.audioVolumeNode.connect(this.audioContext.destination);
|
||||
},
|
||||
|
||||
tryToGetMicrophone : function()
|
||||
{
|
||||
if (this.microphoneActive)
|
||||
{
|
||||
AudioSystem.microphone.connect(AudioSystem.scopeNode);
|
||||
audioVolume.value = 0.0;
|
||||
audioVolume.oninput();
|
||||
return;
|
||||
}
|
||||
|
||||
var constraints = {audio: { mandatory: { echoCancellation: false }}};
|
||||
//var constraints = {audio: {echoCancellation: false} };
|
||||
navigator.getUserMedia = navigator.getUserMedia ||
|
||||
navigator.webkitGetUserMedia ||
|
||||
navigator.mozGetUserMedia;
|
||||
if (navigator.getUserMedia)
|
||||
{
|
||||
navigator.getUserMedia(constraints, onStream, function(){micCheckbox.checked = false;});
|
||||
}
|
||||
else
|
||||
{
|
||||
micCheckbox.checked = false;
|
||||
}
|
||||
},
|
||||
|
||||
disconnectMicrophone : function()
|
||||
{
|
||||
if (this.microphone) this.microphone.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
onStream = function(stream)
|
||||
{
|
||||
AudioSystem.microphoneActive = true;
|
||||
AudioSystem.microphone = AudioSystem.audioContext.createMediaStreamSource(stream);
|
||||
AudioSystem.microphone.connect(AudioSystem.scopeNode);
|
||||
|
||||
audioVolume.value = 0.0;
|
||||
audioVolume.oninput();
|
||||
};
|
||||
|
||||
var SignalGenerator =
|
||||
{
|
||||
oldA : 1.0,
|
||||
oldB : 1.0,
|
||||
timeInSamples : 0,
|
||||
|
||||
generate : function(event)
|
||||
{
|
||||
var xOut = event.outputBuffer.getChannelData(0);
|
||||
var yOut = event.outputBuffer.getChannelData(1);
|
||||
var newA = controls.aValue * Math.pow(10.0, controls.aExponent);
|
||||
var newB = controls.bValue * Math.pow(10.0, controls.bExponent);
|
||||
var oldA = SignalGenerator.oldA;
|
||||
var oldB = SignalGenerator.oldB;
|
||||
var PI = Math.PI;
|
||||
var cos = Math.cos;
|
||||
var sin = Math.sin;
|
||||
var xFunc = eval("(function xFunc(){return "+controls.xExpression+";})");
|
||||
var yFunc = eval("(function yFunc(){return "+controls.yExpression+";})");
|
||||
var bufferSize = AudioSystem.bufferSize;
|
||||
var timeInSamples = SignalGenerator.timeInSamples;
|
||||
var sampleRate = AudioSystem.sampleRate;
|
||||
var x = 0.0;
|
||||
var y = 0.0;
|
||||
if (!controls.signalGeneratorOn)
|
||||
{
|
||||
for (var i=0; i<bufferSize; i++)
|
||||
{
|
||||
xOut[i] = 0;
|
||||
yOut[i] = 0;
|
||||
}
|
||||
}
|
||||
else if ((newA == oldA) && (newB == oldB))
|
||||
{
|
||||
var n = timeInSamples;
|
||||
for (var i=0; i<bufferSize; i++)
|
||||
{
|
||||
var t = n/sampleRate;
|
||||
var a = newA;
|
||||
var b = newB;
|
||||
x = xFunc();
|
||||
y = yFunc();
|
||||
xOut[i] = x;
|
||||
yOut[i] = y;
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var n = timeInSamples;
|
||||
for (var i=0; i<bufferSize; i++)
|
||||
{
|
||||
var t = n/sampleRate;
|
||||
|
||||
var a = oldA;
|
||||
var b = oldB;
|
||||
var oldX = xFunc();
|
||||
var oldY = yFunc();
|
||||
a = newA;
|
||||
b = newB;
|
||||
var newX = xFunc();
|
||||
var newY = yFunc();
|
||||
var alpha_z = i/bufferSize;
|
||||
x = oldX*(1.0-alpha_z)+newX*alpha_z;
|
||||
y = oldY*(1.0-alpha_z)+newY*alpha_z;
|
||||
|
||||
xOut[i] = x;
|
||||
yOut[i] = y;
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
SignalGenerator.timeInSamples += AudioSystem.bufferSize;
|
||||
SignalGenerator.oldA = newA;
|
||||
SignalGenerator.oldB = newB;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var Filter =
|
||||
{
|
||||
lanczosTweak : 1.5,
|
||||
|
||||
init : function(bufferSize, a, steps)
|
||||
{
|
||||
this.bufferSize = bufferSize;
|
||||
this.a = a;
|
||||
this.steps = steps;
|
||||
this.radius = a * steps;
|
||||
this.nSmoothedSamples = this.bufferSize*this.steps + 1;
|
||||
this.allSamples = new Float32Array(2*this.bufferSize);
|
||||
|
||||
this.createLanczosKernel();
|
||||
},
|
||||
|
||||
|
||||
generateSmoothedSamples : function (oldSamples, samples, smoothedSamples)
|
||||
{
|
||||
//this.createLanczosKernel();
|
||||
var bufferSize = this.bufferSize;
|
||||
var allSamples = this.allSamples;
|
||||
var nSmoothedSamples = this.nSmoothedSamples;
|
||||
var a = this.a;
|
||||
var steps = this.steps;
|
||||
var K = this.K;
|
||||
|
||||
for (var i=0; i<bufferSize; i++)
|
||||
{
|
||||
allSamples[i] = oldSamples[i];
|
||||
allSamples[bufferSize+i] = samples[i];
|
||||
}
|
||||
|
||||
/*for (var s= -a+1; s<a; s++)
|
||||
{
|
||||
for (var r=0; r<steps; r++)
|
||||
{
|
||||
if (r==0 && !(s==0)) continue;
|
||||
var kernelPosition = -r+s*steps;
|
||||
if (kernelPosition<0) k = K[-kernelPosition];
|
||||
else k = K[kernelPosition];
|
||||
|
||||
var i = r;
|
||||
var pStart = bufferSize - 2*a + s;
|
||||
var pEnd = pStart + bufferSize;
|
||||
for (var p=pStart; p<pEnd; p++)
|
||||
{
|
||||
smoothedSamples[i] += k * allSamples[p];
|
||||
i += steps;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
var pStart = bufferSize - 2*a;
|
||||
var pEnd = pStart + bufferSize;
|
||||
var i = 0;
|
||||
for (var position=pStart; position<pEnd; position++)
|
||||
{
|
||||
smoothedSamples[i] = allSamples[position];
|
||||
i += 1;
|
||||
for (var r=1; r<steps; r++)
|
||||
{
|
||||
var smoothedSample = 0;
|
||||
for (var s= -a+1; s<a; s++)
|
||||
{
|
||||
var sample = allSamples[position+s];
|
||||
var kernelPosition = -r+s*steps;
|
||||
if (kernelPosition<0) smoothedSample += sample * K[-kernelPosition];
|
||||
else smoothedSample += sample * K[kernelPosition];
|
||||
}
|
||||
smoothedSamples[i] = smoothedSample;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
smoothedSamples[nSmoothedSamples-1] = allSamples[2*bufferSize-2*a];
|
||||
},
|
||||
|
||||
createLanczosKernel : function ()
|
||||
{
|
||||
this.K = new Float32Array(this.radius);
|
||||
this.K[0] = 1;
|
||||
for (var i =1; i<this.radius; i++)
|
||||
{
|
||||
var piX = (Math.PI * i) / this.steps;
|
||||
var sinc = Math.sin(piX)/piX;
|
||||
var window = this.a * Math.sin(piX/this.a) / piX;
|
||||
this.K[i] = sinc*Math.pow(window, this.lanczosTweak);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var UI =
|
||||
{
|
||||
sidebarWidth : 360,
|
||||
|
||||
init : function()
|
||||
{
|
||||
var kHzText = (AudioSystem.sampleRate/1000).toFixed(1)+"kHz";
|
||||
document.getElementById("samplerate").innerHTML=kHzText;
|
||||
mainGain.oninput();
|
||||
trigger.oninput();
|
||||
this.xInput = document.getElementById("xInput");
|
||||
this.yInput = document.getElementById("yInput");
|
||||
this.xInput.value = controls.xExpression;
|
||||
this.yInput.value = controls.yExpression;
|
||||
},
|
||||
|
||||
compile : function() //doesn't compile anything anymore
|
||||
{
|
||||
controls.xExpression = this.xInput.value;
|
||||
controls.yExpression = this.yInput.value;
|
||||
}
|
||||
}
|
||||
|
||||
var Render =
|
||||
{
|
||||
debug : 0,
|
||||
|
||||
init : function()
|
||||
{
|
||||
this.canvas = document.getElementById("crtCanvas");
|
||||
this.onResize();
|
||||
window.onresize = this.onResize;
|
||||
window.gl = this.canvas.getContext("webgl", {preserveDrawingBuffer: true}, { alpha: false } );
|
||||
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendEquation( gl.FUNC_ADD );
|
||||
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.colorMask(true, true, true, true);
|
||||
var ext1 = gl.getExtension('OES_texture_float');
|
||||
var ext2 = gl.getExtension('OES_texture_float_linear');
|
||||
//this.ext = gl.getExtension('OES_texture_half_float');
|
||||
//this.ext2 = gl.getExtension('OES_texture_half_float_linear');
|
||||
this.fadeAmount = 0.2*AudioSystem.bufferSize/512;
|
||||
|
||||
|
||||
this.fullScreenQuad = new Float32Array([
|
||||
-1, 1, 1, 1, 1,-1, // Triangle 1
|
||||
-1, 1, 1,-1, -1,-1 // Triangle 2
|
||||
]);
|
||||
|
||||
this.simpleShader = this.createShader("vertex","fragment");
|
||||
this.simpleShader.vertexPosition = gl.getAttribLocation(this.simpleShader, "vertexPosition");
|
||||
this.simpleShader.colour = gl.getUniformLocation(this.simpleShader, "colour");
|
||||
|
||||
this.lineShader = this.createShader("gaussianVertex","gaussianFragment");
|
||||
this.lineShader.aStart = gl.getAttribLocation(this.lineShader, "aStart");
|
||||
this.lineShader.aEnd = gl.getAttribLocation(this.lineShader, "aEnd");
|
||||
this.lineShader.aIdx = gl.getAttribLocation(this.lineShader, "aIdx");
|
||||
this.lineShader.uGain = gl.getUniformLocation(this.lineShader, "uGain");
|
||||
this.lineShader.uSize = gl.getUniformLocation(this.lineShader, "uSize");
|
||||
this.lineShader.uInvert = gl.getUniformLocation(this.lineShader, "uInvert");
|
||||
this.lineShader.uIntensity = gl.getUniformLocation(this.lineShader, "uIntensity");
|
||||
this.lineShader.uNEdges = gl.getUniformLocation(this.lineShader, "uNEdges");
|
||||
this.lineShader.uFadeAmount = gl.getUniformLocation(this.lineShader, "uFadeAmount");
|
||||
this.lineShader.uScreen = gl.getUniformLocation(this.lineShader, "uScreen");
|
||||
|
||||
this.outputShader = this.createShader("outputVertex","outputFragment");
|
||||
this.outputShader.aPos = gl.getAttribLocation(this.outputShader, "aPos");
|
||||
this.outputShader.uTexture0 = gl.getUniformLocation(this.outputShader, "uTexture0");
|
||||
this.outputShader.uTexture1 = gl.getUniformLocation(this.outputShader, "uTexture1");
|
||||
this.outputShader.uTexture2 = gl.getUniformLocation(this.outputShader, "uTexture2");
|
||||
this.outputShader.uTexture3 = gl.getUniformLocation(this.outputShader, "uTexture3");
|
||||
this.outputShader.uExposure = gl.getUniformLocation(this.outputShader, "uExposure");
|
||||
this.outputShader.uColour = gl.getUniformLocation(this.outputShader, "uColour");
|
||||
this.outputShader.uResizeForCanvas = gl.getUniformLocation(this.outputShader, "uResizeForCanvas");
|
||||
|
||||
this.texturedShader = this.createShader("texturedVertexWithResize","texturedFragment");
|
||||
this.texturedShader.aPos = gl.getAttribLocation(this.texturedShader, "aPos");
|
||||
this.texturedShader.uTexture0 = gl.getUniformLocation(this.texturedShader, "uTexture0");
|
||||
this.texturedShader.uResizeForCanvas = gl.getUniformLocation(this.texturedShader, "uResizeForCanvas");
|
||||
|
||||
this.blurShader = this.createShader("texturedVertex","blurFragment");
|
||||
this.blurShader.aPos = gl.getAttribLocation(this.blurShader, "aPos");
|
||||
this.blurShader.uTexture0 = gl.getUniformLocation(this.blurShader, "uTexture0");
|
||||
this.blurShader.uOffset = gl.getUniformLocation(this.blurShader, "uOffset");
|
||||
|
||||
this.vertexBuffer = gl.createBuffer();
|
||||
this.setupTextures();
|
||||
},
|
||||
|
||||
setupArrays : function(nPoints)
|
||||
{
|
||||
this.nPoints = nPoints;
|
||||
this.nEdges = this.nPoints-1;
|
||||
|
||||
this.quadIndexBuffer = gl.createBuffer();
|
||||
var indices = new Float32Array(4*this.nEdges);
|
||||
for (var i=0; i<indices.length; i++)
|
||||
{
|
||||
indices[i] = i;
|
||||
}
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadIndexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, indices, gl.STATIC_DRAW);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||||
|
||||
this.vertexIndexBuffer = gl.createBuffer();
|
||||
var len = this.nEdges * 2 * 3,
|
||||
indices = new Uint16Array(len);
|
||||
for (var i = 0, pos = 0; i < len;)
|
||||
{
|
||||
indices[i++] = pos;
|
||||
indices[i++] = pos + 2;
|
||||
indices[i++] = pos + 1;
|
||||
indices[i++] = pos + 1;
|
||||
indices[i++] = pos + 2;
|
||||
indices[i++] = pos + 3;
|
||||
pos += 4;
|
||||
}
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.vertexIndexBuffer);
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
|
||||
|
||||
|
||||
this.scratchVertices = new Float32Array(8*nPoints);
|
||||
},
|
||||
|
||||
setupTextures : function()
|
||||
{
|
||||
this.frameBuffer = gl.createFramebuffer();
|
||||
this.lineTexture = this.makeTexture(1024, 1024);
|
||||
this.onResize();
|
||||
this.blur1Texture = this.makeTexture(256, 256);
|
||||
this.blur2Texture = this.makeTexture(256, 256);
|
||||
this.blur3Texture = this.makeTexture(32, 32);
|
||||
this.blur4Texture = this.makeTexture(32, 32);
|
||||
this.screenTexture = this.loadTexture('noise.jpg');
|
||||
},
|
||||
|
||||
onResize : function()
|
||||
{
|
||||
var windowWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
|
||||
var windowHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0)
|
||||
var canvasSize = Math.min(windowHeight, windowWidth);
|
||||
Render.canvas.width = canvasSize;
|
||||
Render.canvas.height = canvasSize;
|
||||
if (Render.lineTexture)
|
||||
{
|
||||
var renderSize = Math.min(Math.max(canvasSize, 128), 1024);
|
||||
Render.lineTexture.width = renderSize;
|
||||
Render.lineTexture.height = renderSize;
|
||||
//testOutputElement.value = windowHeight;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
drawLineTexture : function(xPoints, yPoints)
|
||||
{
|
||||
this.fadeAmount = Math.pow(0.5, controls.persistence)*0.2*AudioSystem.bufferSize/512 ;
|
||||
this.activateTargetTexture(this.lineTexture);
|
||||
this.fade();
|
||||
//gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
this.drawLine(xPoints, yPoints);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.targetTexture);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
},
|
||||
|
||||
drawCRT : function()
|
||||
{
|
||||
this.setNormalBlending();
|
||||
|
||||
this.activateTargetTexture(this.blur1Texture);
|
||||
this.setShader(this.texturedShader);
|
||||
gl.uniform1f(this.texturedShader.uResizeForCanvas, this.lineTexture.width/1024);
|
||||
this.drawTexture(this.lineTexture);
|
||||
|
||||
//horizontal blur 256x256
|
||||
this.activateTargetTexture(this.blur2Texture);
|
||||
this.setShader(this.blurShader);
|
||||
gl.uniform2fv(this.blurShader.uOffset, [1.0/256.0, 0.0]);
|
||||
this.drawTexture(this.blur1Texture);
|
||||
|
||||
//vertical blur 256x256
|
||||
this.activateTargetTexture(this.blur1Texture);
|
||||
//this.setShader(this.blurShader);
|
||||
gl.uniform2fv(this.blurShader.uOffset, [0.0, 1.0/256.0]);
|
||||
this.drawTexture(this.blur2Texture);
|
||||
|
||||
//preserve blur1 for later
|
||||
this.activateTargetTexture(this.blur3Texture);
|
||||
this.setShader(this.texturedShader);
|
||||
gl.uniform1f(this.texturedShader.uResizeForCanvas, 1.0);
|
||||
this.drawTexture(this.blur1Texture);
|
||||
|
||||
//horizontal blur 64x64
|
||||
this.activateTargetTexture(this.blur4Texture);
|
||||
this.setShader(this.blurShader);
|
||||
gl.uniform2fv(this.blurShader.uOffset, [1.0/32.0, 1.0/60.0]);
|
||||
this.drawTexture(this.blur3Texture);
|
||||
|
||||
//vertical blur 64x64
|
||||
this.activateTargetTexture(this.blur3Texture);
|
||||
//this.setShader(this.blurShader);
|
||||
gl.uniform2fv(this.blurShader.uOffset, [-1.0/60.0, 1.0/32.0]);
|
||||
this.drawTexture(this.blur4Texture);
|
||||
|
||||
this.activateTargetTexture(null);
|
||||
this.setShader(this.outputShader);
|
||||
var brightness = Math.pow(2, controls.exposureStops-2.0);
|
||||
//if (controls.disableFilter) brightness *= Filter.steps;
|
||||
gl.uniform1f(this.outputShader.uExposure, brightness);
|
||||
gl.uniform1f(this.outputShader.uResizeForCanvas, this.lineTexture.width/1024);
|
||||
var colour = this.getColourFromHue(controls.hue);
|
||||
gl.uniform3fv(this.outputShader.uColour, colour);
|
||||
this.drawTexture(this.lineTexture, this.blur1Texture, this.blur3Texture, this.screenTexture);
|
||||
},
|
||||
|
||||
getColourFromHue : function(hue)
|
||||
{
|
||||
var alpha = (hue/120.0) % 1.0;
|
||||
var start = Math.sqrt(1.0-alpha);
|
||||
var end = Math.sqrt(alpha);
|
||||
var colour;
|
||||
if (hue<120) colour = [start, end, 0.0];
|
||||
else if (hue<240) colour = [0.0, start, end];
|
||||
else colour = [end, 0.0, start];
|
||||
return colour;
|
||||
},
|
||||
|
||||
activateTargetTexture : function(texture)
|
||||
{
|
||||
if (texture)
|
||||
{
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
||||
gl.viewport(0, 0, texture.width, texture.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
this.targetTexture = texture;
|
||||
},
|
||||
|
||||
setShader : function(program)
|
||||
{
|
||||
this.program = program;
|
||||
gl.useProgram(program);
|
||||
},
|
||||
|
||||
drawTexture : function(texture0, texture1, texture2, texture3)
|
||||
{
|
||||
//gl.useProgram(this.program);
|
||||
gl.enableVertexAttribArray(this.program.aPos);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture0);
|
||||
gl.uniform1i(this.program.uTexture0, 0);
|
||||
|
||||
if (texture1)
|
||||
{
|
||||
gl.activeTexture(gl.TEXTURE1);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture1);
|
||||
gl.uniform1i(this.program.uTexture1, 1);
|
||||
}
|
||||
|
||||
if (texture2)
|
||||
{
|
||||
gl.activeTexture(gl.TEXTURE2);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture2);
|
||||
gl.uniform1i(this.program.uTexture2, 2);
|
||||
}
|
||||
|
||||
if (texture3)
|
||||
{
|
||||
gl.activeTexture(gl.TEXTURE3);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture3);
|
||||
gl.uniform1i(this.program.uTexture3, 3);
|
||||
}
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, this.fullScreenQuad, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(this.program.aPos, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||||
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
gl.disableVertexAttribArray(this.program.aPos);
|
||||
|
||||
if (this.targetTexture)
|
||||
{
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.targetTexture);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
}
|
||||
},
|
||||
|
||||
drawLine : function(xPoints, yPoints)
|
||||
{
|
||||
this.setAdditiveBlending();
|
||||
|
||||
var scratchVertices = this.scratchVertices;
|
||||
//this.totalLength = 0;
|
||||
var nPoints = xPoints.length;
|
||||
for (var i=0; i<nPoints; i++)
|
||||
{
|
||||
var p = i*8;
|
||||
scratchVertices[p]=scratchVertices[p+2]=scratchVertices[p+4]=scratchVertices[p+6]=xPoints[i];
|
||||
scratchVertices[p+1]=scratchVertices[p+3]=scratchVertices[p+5]=scratchVertices[p+7]=yPoints[i];
|
||||
/*if (i>0)
|
||||
{
|
||||
var xDelta = xPoints[i]-xPoints[i-1];
|
||||
if (xDelta<0) xDelta = -xDelta;
|
||||
var yDelta = yPoints[i]-yPoints[i-1];
|
||||
if (yDelta<0) yDelta = -yDelta;
|
||||
this.totalLength += xDelta + yDelta;
|
||||
}*/
|
||||
}
|
||||
//testOutputElement.value = this.totalLength;
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, scratchVertices, gl.STATIC_DRAW);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||||
|
||||
var program = this.lineShader;
|
||||
gl.useProgram(program);
|
||||
gl.enableVertexAttribArray(program.aStart);
|
||||
gl.enableVertexAttribArray(program.aEnd);
|
||||
gl.enableVertexAttribArray(program.aIdx);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
||||
gl.vertexAttribPointer(program.aStart, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.vertexAttribPointer(program.aEnd, 2, gl.FLOAT, false, 0, 8*4);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadIndexBuffer);
|
||||
gl.vertexAttribPointer(program.aIdx, 1, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.screenTexture);
|
||||
gl.uniform1i(program.uScreen, 0);
|
||||
|
||||
gl.uniform1f(program.uSize, 0.015);
|
||||
gl.uniform1f(program.uGain, Math.pow(2.0,controls.mainGain)*450/512);
|
||||
if (controls.invertXY) gl.uniform1f(program.uInvert, -1.0);
|
||||
else gl.uniform1f(program.uInvert, 1.0);
|
||||
if (controls.disableFilter) gl.uniform1f(program.uIntensity, 0.005*(Filter.steps+1.5));
|
||||
// +1.5 needed above for some reason for the brightness to match
|
||||
else gl.uniform1f(program.uIntensity, 0.005);
|
||||
gl.uniform1f(program.uFadeAmount, this.fadeAmount);
|
||||
gl.uniform1f(program.uNEdges, this.nEdges);
|
||||
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.vertexIndexBuffer);
|
||||
var nEdgesThisTime = (xPoints.length-1);
|
||||
|
||||
/*if (this.totalLength > 300)
|
||||
{
|
||||
nEdgesThisTime *= 300/this.totalLength;
|
||||
nEdgesThisTime = Math.floor(nEdgesThisTime);
|
||||
}*/
|
||||
|
||||
gl.drawElements(gl.TRIANGLES, nEdgesThisTime * 6, gl.UNSIGNED_SHORT, 0);
|
||||
|
||||
gl.disableVertexAttribArray(program.aStart);
|
||||
gl.disableVertexAttribArray(program.aEnd);
|
||||
gl.disableVertexAttribArray(program.aIdx);
|
||||
},
|
||||
|
||||
fade : function(alpha)
|
||||
{
|
||||
this.setNormalBlending();
|
||||
|
||||
var program = this.simpleShader;
|
||||
gl.useProgram(program);
|
||||
gl.enableVertexAttribArray(program.vertexPosition);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, this.fullScreenQuad, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(program.vertexPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||||
gl.uniform4fv(program.colour, [0.0, 0.0, 0.0, this.fadeAmount]);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
gl.disableVertexAttribArray(program.vertexPosition);
|
||||
},
|
||||
|
||||
loadTexture : function(src)
|
||||
{
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
// Fill with grey pixel, as placeholder until loaded
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
|
||||
new Uint8Array([128, 128, 128, 255]));
|
||||
// Asynchronously load an image
|
||||
var image = new Image();
|
||||
image.crossOrigin = "anonymous";
|
||||
image.src = src;
|
||||
image.addEventListener('load', function()
|
||||
{
|
||||
// Now that the image has loaded make copy it to the texture.
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
//hardcoded:
|
||||
texture.width = texture.height = 512;
|
||||
if (controls.grid) Render.drawGrid(texture);
|
||||
});
|
||||
return texture;
|
||||
},
|
||||
|
||||
drawGrid : function(texture)
|
||||
{
|
||||
this.activateTargetTexture(texture);
|
||||
this.setNormalBlending();
|
||||
this.setShader(this.simpleShader);
|
||||
gl.colorMask(true, false, false, true);
|
||||
|
||||
var data = [];
|
||||
|
||||
for (var i=0; i<11; i++)
|
||||
{
|
||||
var step = 45;
|
||||
var s = i*step;
|
||||
data.splice(0,0, 0, s, 10*step, s);
|
||||
data.splice(0,0, s, 0, s, 10*step);
|
||||
if (i!=0 && i!=10)
|
||||
{
|
||||
for (var j=0; j<51; j++)
|
||||
{
|
||||
t = j*step/5;
|
||||
if (i!=5)
|
||||
{
|
||||
data.splice(0,0, t, s-2, t, s+1);
|
||||
data.splice(0,0, s-2, t, s+1, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.splice(0,0, t, s-5, t, s+4);
|
||||
data.splice(0,0, s-5, t, s+4, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var j=0; j<51; j++)
|
||||
{
|
||||
var t = j*step/5;
|
||||
if (t%5 == 0) continue;
|
||||
data.splice(0,0, t-2, 2.5*step, t+2, 2.5*step);
|
||||
data.splice(0,0, t-2, 7.5*step, t+2, 7.5*step);
|
||||
}
|
||||
|
||||
|
||||
var vertices = new Float32Array(data);
|
||||
for (var i=0; i<data.length; i++)
|
||||
{
|
||||
vertices[i]=(vertices[i]+31)/256-1;
|
||||
}
|
||||
|
||||
|
||||
gl.enableVertexAttribArray(this.program.vertexPosition);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(this.program.vertexPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||||
gl.uniform4fv(this.program.colour, [0.01, 0.1, 0.01, 1.0]);
|
||||
|
||||
gl.lineWidth(1.0);
|
||||
gl.drawArrays(gl.LINES, 0, vertices.length/2);
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.targetTexture);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.colorMask(true, true, true, true);
|
||||
},
|
||||
|
||||
makeTexture : function(width, height)
|
||||
{
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, null);
|
||||
//gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, Render.ext.HALF_FLOAT_OES, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
texture.width = width;
|
||||
texture.height = height;
|
||||
return texture;
|
||||
},
|
||||
|
||||
xactivateTargetTexture : function(ctx, texture)
|
||||
{
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, ctx.renderBuffer);
|
||||
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, ctx.frameBuffer.width, ctx.frameBuffer.height);
|
||||
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
||||
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, ctx.renderBuffer);
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
||||
},
|
||||
|
||||
drawSimpleLine : function(xSamples, ySamples, colour)
|
||||
{
|
||||
var nVertices = xSamples.length;
|
||||
var vertices = new Float32Array(2*nVertices);
|
||||
for (var i=0; i<nVertices; i++)
|
||||
{
|
||||
vertices[2*i] = xSamples[i];
|
||||
vertices[2*i+1] = ySamples[i];
|
||||
}
|
||||
|
||||
this.setAdditiveBlending();
|
||||
|
||||
var program = this.simpleShader;
|
||||
gl.useProgram(program);
|
||||
gl.enableVertexAttribArray(program.vertexPosition);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(program.vertexPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||||
if (colour=="green") gl.uniform4fv(program.colour, [0.01, 0.1, 0.01, 1.0]);
|
||||
else if (colour == "red") gl.uniform4fv(program.colour, [0.1, 0.01, 0.01, 1.0]);
|
||||
|
||||
gl.lineWidth(3.0);
|
||||
gl.drawArrays(gl.LINE_STRIP, 0, nVertices);
|
||||
},
|
||||
|
||||
setAdditiveBlending : function()
|
||||
{
|
||||
//gl.blendEquation( gl.FUNC_ADD );
|
||||
gl.blendFunc(gl.ONE, gl.ONE);
|
||||
},
|
||||
|
||||
setNormalBlending : function()
|
||||
{
|
||||
//gl.blendEquation( gl.FUNC_ADD );
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
},
|
||||
|
||||
createShader : function(vsTag, fsTag)
|
||||
{
|
||||
if (!this.supportsWebGl())
|
||||
{
|
||||
throw new Error('createShader: no WebGL context');
|
||||
}
|
||||
|
||||
var vsSource = document.getElementById(vsTag).firstChild.nodeValue;
|
||||
var fsSource = document.getElementById(fsTag).firstChild.nodeValue;
|
||||
|
||||
var vs = gl.createShader(gl.VERTEX_SHADER);
|
||||
gl.shaderSource(vs, vsSource);
|
||||
gl.compileShader(vs);
|
||||
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS))
|
||||
{
|
||||
var infoLog = gl.getShaderInfoLog(vs);
|
||||
gl.deleteShader(vs);
|
||||
throw new Error('createShader, vertex shader compilation:\n' + infoLog);
|
||||
}
|
||||
|
||||
var fs = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
gl.shaderSource(fs, fsSource);
|
||||
gl.compileShader(fs);
|
||||
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS))
|
||||
{
|
||||
var infoLog = gl.getShaderInfoLog(fs);
|
||||
gl.deleteShader(vs);
|
||||
gl.deleteShader(fs);
|
||||
throw new Error('createShader, fragment shader compilation:\n' + infoLog);
|
||||
}
|
||||
|
||||
var program = gl.createProgram();
|
||||
|
||||
gl.attachShader(program, vs);
|
||||
gl.deleteShader(vs);
|
||||
|
||||
gl.attachShader(program, fs);
|
||||
gl.deleteShader(fs);
|
||||
|
||||
gl.linkProgram(program);
|
||||
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
||||
{
|
||||
var infoLog = gl.getProgramInfoLog(program);
|
||||
gl.deleteProgram(program);
|
||||
throw new Error('createShader, linking:\n' + infoLog);
|
||||
}
|
||||
|
||||
return program;
|
||||
},
|
||||
|
||||
supportsWebGl : function()
|
||||
{
|
||||
// from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/webgl.js
|
||||
var canvas = document.createElement('canvas'),
|
||||
supports = 'probablySupportsContext' in canvas ? 'probablySupportsContext' : 'supportsContext';
|
||||
if (supports in canvas)
|
||||
{
|
||||
return canvas[supports]('webgl') || canvas[supports]('experimental-webgl');
|
||||
}
|
||||
return 'WebGLRenderingContext' in window;
|
||||
}
|
||||
}
|
||||
|
||||
var sweepPosition = -1;
|
||||
var belowTrigger = false;
|
||||
|
||||
function doScriptProcessor(event)
|
||||
{
|
||||
var xSamplesRaw = event.inputBuffer.getChannelData(0);
|
||||
var ySamplesRaw = event.inputBuffer.getChannelData(1);
|
||||
var xOut = event.outputBuffer.getChannelData(0);
|
||||
var yOut = event.outputBuffer.getChannelData(1);
|
||||
|
||||
var length = xSamplesRaw.length;
|
||||
for (var i=0; i<length; i++)
|
||||
{
|
||||
xSamples[i] = xSamplesRaw[i];// + (Math.random()-0.5)*controls.noise/2000;
|
||||
ySamples[i] = ySamplesRaw[i];// + (Math.random()-0.5)*controls.noise/2000;
|
||||
}
|
||||
|
||||
if (controls.sweepOn)
|
||||
{
|
||||
var gain = Math.pow(2.0,controls.mainGain);
|
||||
var sweepMinTime = controls.sweepMsDiv*10/1000;
|
||||
var triggerValue = controls.sweepTriggerValue;
|
||||
for (var i=0; i<length; i++)
|
||||
{
|
||||
xSamples[i] = sweepPosition / gain;
|
||||
sweepPosition += 2*AudioSystem.timePerSample/sweepMinTime;
|
||||
if (sweepPosition > 1.1 && belowTrigger && ySamples[i]>=triggerValue)
|
||||
sweepPosition =-1.3;
|
||||
belowTrigger = ySamples[i]<triggerValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!controls.freezeImage)
|
||||
{
|
||||
if (!controls.disableFilter)
|
||||
{
|
||||
Filter.generateSmoothedSamples(AudioSystem.oldXSamples, xSamples, AudioSystem.smoothedXSamples);
|
||||
Filter.generateSmoothedSamples(AudioSystem.oldYSamples, ySamples, AudioSystem.smoothedYSamples);
|
||||
|
||||
if (!controls.swapXY) Render.drawLineTexture(AudioSystem.smoothedXSamples, AudioSystem.smoothedYSamples);
|
||||
else Render.drawLineTexture(AudioSystem.smoothedYSamples, AudioSystem.smoothedXSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!controls.swapXY) Render.drawLineTexture(xSamples, ySamples);
|
||||
else Render.drawLineTexture(ySamples, xSamples);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i<length; i++)
|
||||
{
|
||||
AudioSystem.oldXSamples[i] = xSamples[i];
|
||||
AudioSystem.oldYSamples[i] = ySamples[i];
|
||||
xOut[i] = xSamplesRaw[i];
|
||||
yOut[i] = ySamplesRaw[i];
|
||||
}
|
||||
|
||||
AudioSystem.audioVolumeNode.gain.value = controls.audioVolume;
|
||||
}
|
||||
|
||||
function drawCRTFrame(timeStamp)
|
||||
{
|
||||
Render.drawCRT();
|
||||
requestAnimationFrame(drawCRTFrame);
|
||||
}
|
||||
|
||||
var xSamples = new Float32Array(1024);
|
||||
var ySamples = new Float32Array(1024);
|
||||
UI.init();
|
||||
Render.init();
|
||||
|
||||
document.onclick = function(){ // quick fix to get around autoplay rules, May 2022
|
||||
document.onclick = null;
|
||||
document.getElementById("clicktostart").remove();
|
||||
//Filter.init(512, 10, 4);
|
||||
Filter.init(1024, 8, 6);
|
||||
AudioSystem.init(1024);
|
||||
Render.setupArrays(Filter.nSmoothedSamples);
|
||||
AudioSystem.startSound();
|
||||
requestAnimationFrame(drawCRTFrame);
|
||||
Controls.setupControls();
|
||||
};
|
|
@ -5,6 +5,9 @@ VisualiserComponent::VisualiserComponent(int numChannels, OscirenderAudioProcess
|
|||
resetBuffer();
|
||||
startTimerHz(60);
|
||||
startThread();
|
||||
|
||||
addAndMakeVisible(browser);
|
||||
browser.goToURL(juce::WebBrowserComponent::getResourceProviderRoot() + "oscilloscope.html");
|
||||
|
||||
setFullScreen(false);
|
||||
|
||||
|
@ -92,20 +95,20 @@ void VisualiserComponent::setColours(juce::Colour bk, juce::Colour fg) {
|
|||
}
|
||||
|
||||
void VisualiserComponent::paint(juce::Graphics& g) {
|
||||
g.setColour(backgroundColour);
|
||||
g.fillRoundedRectangle(getLocalBounds().toFloat(), OscirenderLookAndFeel::RECT_RADIUS);
|
||||
|
||||
auto r = getLocalBounds().toFloat();
|
||||
auto minDim = juce::jmin(r.getWidth(), r.getHeight());
|
||||
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType scope(lock);
|
||||
if (buffer.size() > 0) {
|
||||
g.setColour(waveformColour);
|
||||
paintXY(g, r.withSizeKeepingCentre(minDim, minDim));
|
||||
}
|
||||
}
|
||||
|
||||
// g.setColour(backgroundColour);
|
||||
// g.fillRoundedRectangle(getLocalBounds().toFloat(), OscirenderLookAndFeel::RECT_RADIUS);
|
||||
//
|
||||
// auto r = getLocalBounds().toFloat();
|
||||
// auto minDim = juce::jmin(r.getWidth(), r.getHeight());
|
||||
//
|
||||
// {
|
||||
// juce::CriticalSection::ScopedLockType scope(lock);
|
||||
// if (buffer.size() > 0) {
|
||||
// g.setColour(waveformColour);
|
||||
// paintXY(g, r.withSizeKeepingCentre(minDim, minDim));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
if (!active) {
|
||||
// add translucent layer
|
||||
g.setColour(juce::Colours::black.withAlpha(0.5f));
|
||||
|
@ -264,6 +267,7 @@ void VisualiserComponent::resetBuffer() {
|
|||
}
|
||||
|
||||
void VisualiserComponent::resized() {
|
||||
browser.setBounds(getLocalBounds());
|
||||
auto area = getLocalBounds();
|
||||
area.removeFromBottom(5);
|
||||
auto buttonRow = area.removeFromBottom(25);
|
||||
|
|
|
@ -66,6 +66,31 @@ private:
|
|||
SvgButton popOutButton{ "popOut", BinaryData::open_in_new_svg, juce::Colours::white, juce::Colours::white };
|
||||
SvgButton settingsButton{ "settings", BinaryData::cog_svg, juce::Colours::white, juce::Colours::white };
|
||||
|
||||
juce::WebBrowserComponent::ResourceProvider provider = [](const juce::String& path) {
|
||||
juce::String mimeType;
|
||||
if (path.endsWith(".html")) {
|
||||
mimeType = "text/html";
|
||||
} else if (path.endsWith(".jpg")) {
|
||||
mimeType = "image/jpeg";
|
||||
} else if (path.endsWith(".js")) {
|
||||
mimeType = "text/javascript";
|
||||
}
|
||||
std::vector<std::byte> data;
|
||||
int size;
|
||||
const char* file = BinaryData::getNamedResource(path.substring(1).replaceCharacter('.', '_').toRawUTF8(), size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
data.push_back((std::byte) file[i]);
|
||||
}
|
||||
juce::WebBrowserComponent::Resource resource = { data, mimeType };
|
||||
return resource;
|
||||
};
|
||||
|
||||
juce::WebBrowserComponent browser = juce::WebBrowserComponent(
|
||||
juce::WebBrowserComponent::Options()
|
||||
.withNativeIntegrationEnabled()
|
||||
.withResourceProvider(provider)
|
||||
);
|
||||
|
||||
std::vector<float> tempBuffer;
|
||||
int precision = 4;
|
||||
|
||||
|
|
|
@ -22,6 +22,12 @@
|
|||
<GROUP id="{D084D1B2-6804-34C3-C831-231A00F4103A}" name="models">
|
||||
<FILE id="LbviBq" name="cube.obj" compile="0" resource="1" file="Resources/models/cube.obj"/>
|
||||
</GROUP>
|
||||
<GROUP id="{F3C16D02-63B4-E3DA-7498-901173C37D6C}" name="oscilloscope">
|
||||
<FILE id="dNtZYs" name="noise.jpg" compile="0" resource="1" file="Resources/oscilloscope/noise.jpg"/>
|
||||
<FILE id="YPMnjq" name="oscilloscope.html" compile="0" resource="1"
|
||||
file="Resources/oscilloscope/oscilloscope.html"/>
|
||||
<FILE id="BeXHj7" name="oscilloscope.js" compile="0" resource="1" file="Resources/oscilloscope/oscilloscope.js"/>
|
||||
</GROUP>
|
||||
<GROUP id="{82BCD6F1-A8BF-F30B-5587-81EE70168883}" name="svg">
|
||||
<FILE id="rl17ZK" name="cog.svg" compile="0" resource="1" file="Resources/svg/cog.svg"/>
|
||||
<FILE id="sDajXu" name="delete.svg" compile="0" resource="1" file="Resources/svg/delete.svg"/>
|
||||
|
|
Ładowanie…
Reference in New Issue