/home/user1/NetBeansProjects/HTML5 White Noise/public_html/index.html
  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4   <meta charset="UTF-8">
  5   <title>Noise Generator with Peak Hold Spectrum</title>
  6   <style>
  7     body { font-family: sans-serif; background: #222; color: #eee; }
  8     button, label, input[type="range"] { margin: 0.5em 0.5em 0.5em 0; }
  9     #spectrum { background: #111; display: block; margin-top: 10px; border-radius: 6px; }
 10     .controls { margin-bottom: 8px; }
 11     .slider-label { display: inline-block; width: 110px; }
 12   </style>
 13 </head>
 14 <body>
 15   <h2>Noise Generator</h2>
 16   <div class="controls">
 17     <label><input type="radio" name="noiseType" id="whiteNoiseRadio" value="white" checked> White Noise</label>
 18     <label><input type="radio" name="noiseType" id="pinkNoiseRadio" value="pink"> Pink Noise</label>
 19     <label><input type="radio" name="noiseType" id="brownNoiseRadio" value="brown"> Brown Noise</label>
 20   </div>
 21   <div class="controls">
 22     <button id="startNoise">Start Noise</button>
 23     <button id="stopNoise" disabled>Stop Noise</button>
 24   </div>
 25   <div class="controls">
 26     <label for="volumeSlider">Volume:</label>
 27     <input type="range" id="volumeSlider" min="0" max="1" step="0.01" value="0.5">
 28   </div>
 29   <div class="controls">
 30     <span class="slider-label">Canvas Width:</span>
 31     <input type="range" id="canvasWidth" min="200" max="800" value="400">
 32     <span id="canvasWidthValue">400</span>px
 33     <br>
 34     <span class="slider-label">Canvas Height:</span>
 35     <input type="range" id="canvasHeight" min="100" max="400" value="160">
 36     <span id="canvasHeightValue">160</span>px
 37   </div>
 38   <div class="controls">
 39     <label><input type="checkbox" id="peakHoldCheckbox"> Peak Hold (Maximum with Decay)</label>
 40   </div>
 41   <canvas id="spectrum" width="400" height="160"></canvas>
 42   <script>
 43     let audioCtx = null;
 44     let processor = null;
 45     let gainNode = null;
 46     let analyser = null;
 47     let animationId = null;
 48 
 49     // Peak hold state
 50     let peakHoldEnabled = false;
 51     let peakValues = [];
 52     const PEAK_DECAY_PER_FRAME = 0.5; // dB per frame
 53 
 54     function getSelectedNoiseType() {
 55       if (document.getElementById('pinkNoiseRadio').checked) return 'pink';
 56       if (document.getElementById('brownNoiseRadio').checked) return 'brown';
 57       return 'white';
 58     }
 59 
 60     function startNoise() {
 61       if (audioCtx) return;
 62 
 63       audioCtx = new (window.AudioContext || window.webkitAudioContext)();
 64       gainNode = audioCtx.createGain();
 65       analyser = audioCtx.createAnalyser();
 66       analyser.fftSize = 2048;
 67       analyser.smoothingTimeConstant = 0;
 68 
 69       const volume = parseFloat(document.getElementById('volumeSlider').value);
 70       gainNode.gain.value = volume;
 71 
 72       // Create ScriptProcessorNode for real-time noise generation
 73       processor = audioCtx.createScriptProcessor(1024, 1, 1);
 74 
 75       // Initialize noise state variables
 76       let pink_b = [0, 0, 0, 0, 0, 0, 0];
 77       let brown_last = 0.0;
 78 
 79       processor.onaudioprocess = function(e) {
 80         const output = e.outputBuffer.getChannelData(0);
 81         const type = getSelectedNoiseType();
 82         for (let i = 0; i < output.length; i++) {
 83           let white = Math.random() * 2 - 1;
 84           if (type === 'white') {
 85             output[i] = white;
 86           } else if (type === 'pink') {
 87             // Paul Kellet's refined pink noise filter
 88             pink_b[0] = 0.99886 * pink_b[0] + white * 0.0555179;
 89             pink_b[1] = 0.99332 * pink_b[1] + white * 0.0750759;
 90             pink_b[2] = 0.96900 * pink_b[2] + white * 0.1538520;
 91             pink_b[3] = 0.86650 * pink_b[3] + white * 0.3104856;
 92             pink_b[4] = 0.55000 * pink_b[4] + white * 0.5329522;
 93             pink_b[5] = -0.7616 * pink_b[5] - white * 0.0168980;
 94             output[i] = pink_b[0] + pink_b[1] + pink_b[2] + pink_b[3] + pink_b[4] + pink_b[5] + pink_b[6] + white * 0.5362;
 95             output[i] *= 0.11;
 96             pink_b[6] = white * 0.115926;
 97           } else if (type === 'brown') {
 98             // Brownian noise via integration
 99             brown_last = (brown_last + (0.02 * white)) / 1.02;
100             output[i] = brown_last * 3.5;
101           }
102         }
103       };
104 
105       processor.connect(gainNode);
106       gainNode.connect(analyser);
107       analyser.connect(audioCtx.destination);
108 
109       // Initialize peak hold array
110       peakValues = new Array(analyser.frequencyBinCount).fill(-100);
111 
112       document.getElementById('startNoise').disabled = true;
113       document.getElementById('stopNoise').disabled = false;
114 
115       drawSpectrum();
116     }
117 
118     function stopNoise() {
119       if (processor) {
120         processor.disconnect();
121         processor.onaudioprocess = null;
122         processor = null;
123       }
124       if (gainNode) {
125         gainNode.disconnect();
126         gainNode = null;
127       }
128       if (analyser) {
129         analyser.disconnect();
130         analyser = null;
131       }
132       if (audioCtx) {
133         audioCtx.close();
134         audioCtx = null;
135       }
136       document.getElementById('startNoise').disabled = false;
137       document.getElementById('stopNoise').disabled = true;
138 
139       if (animationId) {
140         cancelAnimationFrame(animationId);
141         animationId = null;
142       }
143       // Clear spectrum display
144       const canvas = document.getElementById('spectrum');
145       const ctx = canvas.getContext('2d');
146       ctx.clearRect(0, 0, canvas.width, canvas.height);
147     }
148 
149     function setVolume(value) {
150       if (gainNode) {
151         gainNode.gain.value = parseFloat(value);
152       }
153     }
154 
155     function drawSpectrum() {
156   if (!analyser) return;
157   const canvas = document.getElementById('spectrum');
158   const ctx = canvas.getContext('2d');
159   const bufferLength = analyser.frequencyBinCount;
160   const dataArray = new Float32Array(bufferLength);
161 
162   analyser.getFloatFrequencyData(dataArray);
163 
164   ctx.clearRect(0, 0, canvas.width, canvas.height);
165 
166   // Draw grid and labels
167   drawGridAndLabels(ctx, canvas, analyser);
168 
169   if (!peakHoldEnabled) {
170     // Draw spectrum (dB) in green only if peak hold is off
171     ctx.strokeStyle = 'lime';
172     ctx.lineWidth = 2;
173     ctx.beginPath();
174     for (let i = 0; i < bufferLength; i++) {
175       let db = dataArray[i];
176       if (db < -100) db = -100;
177       if (db > 0) db = 0;
178       const x = (i / (bufferLength - 1)) * canvas.width;
179       const y = ((-db) / 100) * canvas.height;
180       if (i === 0) ctx.moveTo(x, y);
181       else ctx.lineTo(x, y);
182     }
183     ctx.stroke();
184   }
185 
186   if (peakHoldEnabled) {
187     // Draw peak hold (red) only if peak hold is on
188     ctx.strokeStyle = 'red';
189     ctx.lineWidth = 1.5;
190     ctx.beginPath();
191     for (let i = 0; i < bufferLength; i++) {
192       let db = dataArray[i];
193       if (db < -100) db = -100;
194       if (db > 0) db = 0;
195       // Update peak value
196       if (db > peakValues[i]) {
197         peakValues[i] = db;
198       } else {
199         peakValues[i] -= PEAK_DECAY_PER_FRAME;
200         if (peakValues[i] < -100) peakValues[i] = -100;
201       }
202       const x = (i / (bufferLength - 1)) * canvas.width;
203       const y = ((-peakValues[i]) / 100) * canvas.height;
204       if (i === 0) ctx.moveTo(x, y);
205       else ctx.lineTo(x, y);
206     }
207     ctx.stroke();
208   }
209 
210   animationId = requestAnimationFrame(drawSpectrum);
211 }
212 
213     function drawGridAndLabels(ctx, canvas, analyser) {
214       ctx.save();
215       ctx.strokeStyle = '#444';
216       ctx.fillStyle = '#888';
217       ctx.lineWidth = 1;
218       ctx.font = '11px sans-serif';
219       ctx.textAlign = 'center';
220 
221       // Horizontal grid/labels (frequency)
222       const freqs = [0, 5000, 10000, 15000, 20000];
223       const sampleRate = audioCtx ? audioCtx.sampleRate : 44100;
224       const nyquist = sampleRate / 2;
225       for (let i = 0; i < freqs.length; i++) {
226         const freq = freqs[i];
227         const x = (freq / nyquist) * canvas.width;
228         ctx.beginPath();
229         ctx.moveTo(x, 0);
230         ctx.lineTo(x, canvas.height);
231         ctx.stroke();
232         ctx.fillText(freq === 0 ? '0' : (freq/1000) + 'k', x, canvas.height - 2);
233       }
234 
235       // Vertical grid/labels (dB)
236       ctx.textAlign = 'right';
237       const dbTicks = [0, -20, -40, -60, -80, -100];
238       for (let i = 0; i < dbTicks.length; i++) {
239         const db = dbTicks[i];
240         const y = ((-db) / 100) * canvas.height;
241         ctx.beginPath();
242         ctx.moveTo(0, y);
243         ctx.lineTo(canvas.width, y);
244         ctx.stroke();
245         ctx.fillText(db + ' dB', canvas.width - 2, y - 2);
246       }
247       ctx.restore();
248     }
249 
250     // Canvas size slider logic and peak hold checkbox
251     window.addEventListener('load', function() {
252       document.getElementById('startNoise').addEventListener('click', startNoise);
253       document.getElementById('stopNoise').addEventListener('click', stopNoise);
254       document.getElementById('volumeSlider').addEventListener('input', function(e) {
255         setVolume(e.target.value);
256       });
257 
258       const canvas = document.getElementById('spectrum');
259       const widthSlider = document.getElementById('canvasWidth');
260       const heightSlider = document.getElementById('canvasHeight');
261       const widthValue = document.getElementById('canvasWidthValue');
262       const heightValue = document.getElementById('canvasHeightValue');
263       const peakHoldCheckbox = document.getElementById('peakHoldCheckbox');
264 
265       widthSlider.addEventListener('input', function() {
266         canvas.width = widthSlider.value;
267         widthValue.textContent = widthSlider.value;
268         if (analyser) drawSpectrum();
269       });
270       heightSlider.addEventListener('input', function() {
271         canvas.height = heightSlider.value;
272         heightValue.textContent = heightSlider.value;
273         if (analyser) drawSpectrum();
274       });
275       peakHoldCheckbox.addEventListener('change', function() {
276         peakHoldEnabled = peakHoldCheckbox.checked;
277         // Reset peaks when toggled on
278         if (peakHoldEnabled && analyser) {
279           peakValues = new Array(analyser.frequencyBinCount).fill(-100);
280         }
281       });
282     });
283   </script>
284 </body>
285 </html>
286