| /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