1/* Speaker-only synthetic tones. No microphone, files, network, or radio output. */ 2(() => { 3 "use strict"; 4 class DemoAudio { 5 constructor(clock, isPaused) { 6 this.clock = clock; 7 this.isPaused = isPaused; 8 this.enabled = false; 9 this.volume = .25; 10 this.context = null; 11 this.voices = []; 12 this.timer = null; 13 } 14 async setEnabled(enabled) { 15 this.enabled = Boolean(enabled); 16 if (!this.enabled) { this.update(); return false; } 17 try { 18 if (!this.context) { 19 const Audio = window.AudioContext || window.webkitAudioContext; 20 if (!Audio) throw new Error("Sound is unavailable in this browser"); 21 this.context = new Audio(); 22 this.master = this.context.createGain(); 23 this.master.gain.value = 0; 24 this.master.connect(this.context.destination); 25 for (let index = 0; index < 8; index++) { 26 const oscillator = this.context.createOscillator(); 27 const gain = this.context.createGain(); 28 oscillator.type = "sine"; 29 gain.gain.value = 0; 30 oscillator.connect(gain); 31 gain.connect(this.master); 32 oscillator.start(); 33 this.voices.push({oscillator, gain}); 34 } 35 this.timer = setInterval(() => this.update(), 50); 36 } 37 await this.context.resume(); 38 this.update(); 39 return this.enabled; 40 } catch (error) { 41 this.dispose(); 42 throw error; 43 } 44 } 45 setVolume(value) { 46 const number = Number(value); 47 this.volume = Number.isFinite(number) ? Math.max(0, Math.min(1, number)) : 0; 48 this.update(); 49 } 50 update() { 51 if (!this.context) return; 52 const time = this.context.currentTime; 53 const audible = this.enabled && !this.isPaused() && !document.hidden; 54 this.master.gain.setTargetAtTime(audible ? this.volume : 0, time, .01); 55 this.voices.forEach(({oscillator, gain}, index) => { 56 const station = window.dccDemoStation?.snapshot(); 57 const tx = station?.tuning || station?.transmitting; 58 const signal = !audible ? null : tx ? (index === 0 ? { 59 hz: station.audioHz + (station.tuning ? 0 : (Math.floor(this.clock() / .16) % 8) * 6.25), amplitude: .65 60 } : null) : station?.monitoring === false ? null : window.dccDemoWaterfall.signalAt(this.clock(), index); 61 if (signal) oscillator.frequency.setTargetAtTime(signal.hz, time, .008); 62 gain.gain.setTargetAtTime(signal ? signal.amplitude * .12 : 0, time, .01); 63 }); 64 } 65 dispose() { 66 this.enabled = false; 67 if (this.timer !== null) clearInterval(this.timer); 68 this.timer = null; 69 if (this.context) { 70 this.master?.gain.setValueAtTime(0, this.context.currentTime); 71 this.voices.forEach(({oscillator}) => oscillator.stop()); 72 this.context.close().catch(() => {}); 73 } 74 this.context = null; 75 this.voices = []; 76 } 77 } 78 window.DccDemoAudio = DemoAudio; 79})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.