1#[derive(Debug, Clone)]
3pub struct WebGLProfile {
4 pub vendor: String,
5 pub renderer: String,
6 pub extensions: Vec<String>,
7 pub max_texture_size: u32,
8 pub max_renderbuffer_size: u32,
9 pub max_viewport_dims: [u32; 2],
10}
11
12impl WebGLProfile {
13 pub fn firefox() -> Self {
14 WebGLProfile {
15 vendor: "Mozilla".into(),
16 renderer: "WebGL 1.0 (OpenGL ES 2.0 Chromium)".into(),
17 extensions: vec![
18 "ANGLE_instanced_arrays".into(),
19 "EXT_blend_minmax".into(),
20 "EXT_color_buffer_half_float".into(),
21 "EXT_float_blend".into(),
22 "EXT_frag_depth".into(),
23 "EXT_shader_texture_lod".into(),
24 "EXT_texture_compression_bptc".into(),
25 "EXT_texture_filter_anisotropic".into(),
26 "OES_element_index_uint".into(),
27 "OES_fbo_render_mipmap".into(),
28 "OES_standard_derivatives".into(),
29 "OES_texture_float".into(),
30 "OES_texture_float_linear".into(),
31 "OES_texture_half_float".into(),
32 "OES_texture_half_float_linear".into(),
33 "OES_vertex_array_object".into(),
34 "WEBGL_color_buffer_float".into(),
35 "WEBGL_compressed_texture_etc".into(),
36 "WEBGL_compressed_texture_s3tc".into(),
37 "WEBGL_debug_renderer_info".into(),
38 "WEBGL_debug_shaders".into(),
39 "WEBGL_depth_texture".into(),
40 "WEBGL_draw_buffers".into(),
41 "WEBGL_lose_context".into(),
42 ],
43 max_texture_size: 16384,
44 max_renderbuffer_size: 16384,
45 max_viewport_dims: [16384, 16384],
46 }
47 }
48
49 pub fn chrome() -> Self {
50 WebGLProfile {
51 vendor: "Google Inc. (NVIDIA)".into(),
52 renderer: "ANGLE (NVIDIA, NVIDIA GeForce GTX 1060, OpenGL 4.5)".into(),
53 extensions: vec![
54 "ANGLE_instanced_arrays".into(),
55 "EXT_blend_minmax".into(),
56 "EXT_color_buffer_half_float".into(),
57 "EXT_float_blend".into(),
58 "EXT_texture_filter_anisotropic".into(),
59 "OES_element_index_uint".into(),
60 "OES_standard_derivatives".into(),
61 "OES_texture_float".into(),
62 "OES_texture_float_linear".into(),
63 "OES_texture_half_float".into(),
64 "OES_texture_half_float_linear".into(),
65 "OES_vertex_array_object".into(),
66 "WEBGL_color_buffer_float".into(),
67 "WEBGL_compressed_texture_s3tc".into(),
68 "WEBGL_debug_renderer_info".into(),
69 "WEBGL_depth_texture".into(),
70 "WEBGL_draw_buffers".into(),
71 "WEBGL_lose_context".into(),
72 ],
73 max_texture_size: 16384,
74 max_renderbuffer_size: 16384,
75 max_viewport_dims: [16384, 16384],
76 }
77 }
78}
79
80#[derive(Debug, Clone)]
81pub struct AudioProfile {
82 seed: u64,
83 noise_amplitude: f64,
84 sample_rate: u32,
85}
86
87impl AudioProfile {
88 pub fn new(seed: u64) -> Self {
89 AudioProfile {
90 seed,
91 noise_amplitude: 1e-7,
92 sample_rate: 44100,
93 }
94 }
95
96 pub fn seed(&self) -> u64 {
97 self.seed
98 }
99
100 pub fn noise_amplitude(&self) -> f64 {
101 self.noise_amplitude
102 }
103
104 pub fn sample_rate(&self) -> u32 {
105 self.sample_rate
106 }
107
108 pub fn apply_noise(&self, sample: f64, index: u32) -> f64 {
109 let noise = self.deterministic_noise(index);
110 sample + noise * self.noise_amplitude
111 }
112
113 fn deterministic_noise(&self, index: u32) -> f64 {
114 let mut state = self.seed;
115 state ^= (index as u64).wrapping_mul(0x517CC1B727220A95);
116 state = state.wrapping_mul(0x2545F4914F6CDD1D);
117 state ^= state >> 33;
118 (state as f64) / (u64::MAX as f64) - 0.5
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
130 fn test_audio_profile_new() {
131 let ap = AudioProfile::new(42);
132 assert_eq!(ap.seed(), 42);
133 assert_eq!(ap.noise_amplitude(), 1e-7);
134 assert_eq!(ap.sample_rate(), 44100);
135 }
136
137 #[test]
138 fn test_audio_profile_different_seeds() {
139 let ap1 = AudioProfile::new(0);
140 let ap2 = AudioProfile::new(999);
141 assert_ne!(ap1.seed(), ap2.seed());
142 }
143
144 #[test]
145 fn test_deterministic_noise_same_seed_same_result() {
146 let ap = AudioProfile::new(12345);
147 let n1 = ap.deterministic_noise(100);
148 let n2 = ap.deterministic_noise(100);
149 assert_eq!(n1, n2);
150 }
151
152 #[test]
153 fn test_deterministic_noise_range() {
154 let ap = AudioProfile::new(42);
155 for i in 0..1000u32 {
156 let n = ap.deterministic_noise(i);
157 assert!(n >= -0.5 && n <= 0.5, "noise at index {} is {}", i, n);
158 }
159 }
160
161 #[test]
162 fn test_deterministic_noise_different_indices() {
163 let ap = AudioProfile::new(42);
164 let n0 = ap.deterministic_noise(0);
165 let n1 = ap.deterministic_noise(1);
166 assert_ne!(n0, n1);
168 }
169
170 #[test]
171 fn test_deterministic_noise_different_seeds() {
172 let ap1 = AudioProfile::new(100);
173 let ap2 = AudioProfile::new(200);
174 let n1 = ap1.deterministic_noise(50);
175 let n2 = ap2.deterministic_noise(50);
176 assert_ne!(n1, n2);
177 }
178
179 #[test]
180 fn test_apply_noise_adds_deterministic_offset() {
181 let ap = AudioProfile::new(42);
182 let sample = 1.0;
183 let index = 10u32;
184 let result = ap.apply_noise(sample, index);
185 let noise = ap.deterministic_noise(index);
186 let expected = sample + noise * ap.noise_amplitude();
188 assert!((result - expected).abs() < 1e-15);
189 }
190
191 #[test]
192 fn test_apply_noise_preserves_signal() {
193 let ap = AudioProfile::new(42);
194 let sample = 0.5;
195 let result = ap.apply_noise(sample, 0);
196 assert!((result - sample).abs() < 1e-6);
198 }
199
200 #[test]
201 fn test_apply_noise_different_indices_different_results() {
202 let ap = AudioProfile::new(42);
203 let r0 = ap.apply_noise(1.0, 0);
204 let r1 = ap.apply_noise(1.0, 1);
205 assert_ne!(r0, r1);
206 }
207
208 #[test]
209 fn test_audio_profile_clone() {
210 let ap = AudioProfile::new(42);
211 let cloned = ap.clone();
212 assert_eq!(ap.seed(), cloned.seed());
213 assert_eq!(ap.noise_amplitude(), cloned.noise_amplitude());
214 assert_eq!(ap.sample_rate(), cloned.sample_rate());
215 }
216
217 #[test]
218 fn test_audio_profile_debug_format() {
219 let ap = AudioProfile::new(42);
220 let debug_str = format!("{:?}", ap);
221 assert!(debug_str.contains("AudioProfile"));
222 }
223
224 #[test]
228 fn test_webgl_firefox_vendor() {
229 let p = WebGLProfile::firefox();
230 assert_eq!(p.vendor, "Mozilla");
231 }
232
233 #[test]
234 fn test_webgl_firefox_extensions_nonempty() {
235 let p = WebGLProfile::firefox();
236 assert!(!p.extensions.is_empty());
237 assert!(p
238 .extensions
239 .contains(&"WEBGL_debug_renderer_info".to_string()));
240 }
241
242 #[test]
243 fn test_webgl_firefox_max_texture_size() {
244 let p = WebGLProfile::firefox();
245 assert_eq!(p.max_texture_size, 16384);
246 }
247
248 #[test]
249 fn test_webgl_chrome_vendor() {
250 let p = WebGLProfile::chrome();
251 assert_eq!(p.vendor, "Google Inc. (NVIDIA)");
252 }
253
254 #[test]
255 fn test_webgl_chrome_extensions_nonempty() {
256 let p = WebGLProfile::chrome();
257 assert!(!p.extensions.is_empty());
258 assert!(p
259 .extensions
260 .contains(&"WEBGL_debug_renderer_info".to_string()));
261 }
262
263 #[test]
264 fn test_webgl_firefox_more_extensions_than_chrome() {
265 let ff = WebGLProfile::firefox();
266 let ch = WebGLProfile::chrome();
267 assert!(ff.extensions.len() > ch.extensions.len());
268 }
269
270 #[test]
271 fn test_webgl_same_max_viewport_dims() {
272 let ff = WebGLProfile::firefox();
273 let ch = WebGLProfile::chrome();
274 assert_eq!(ff.max_viewport_dims, ch.max_viewport_dims);
275 assert_eq!(ff.max_viewport_dims, [16384, 16384]);
276 }
277
278 #[test]
279 fn test_webgl_profile_clone() {
280 let p = WebGLProfile::firefox();
281 let cloned = p.clone();
282 assert_eq!(p.vendor, cloned.vendor);
283 assert_eq!(p.extensions, cloned.extensions);
284 }
285
286 #[test]
287 fn test_webgl_profile_debug_format() {
288 let p = WebGLProfile::chrome();
289 let debug = format!("{:?}", p);
290 assert!(debug.contains("WebGLProfile"));
291 assert!(debug.contains("vendor"));
292 }
293}