@jjlmoya/utils-forensic-science 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +3 -1
  3. package/src/entries.ts +5 -1
  4. package/src/index.ts +1 -0
  5. package/src/tests/locale_completeness.test.ts +2 -2
  6. package/src/tests/tool_validation.test.ts +2 -2
  7. package/src/tool/voice-spectrogram-analyzer/audio-runtime.ts +75 -0
  8. package/src/tool/voice-spectrogram-analyzer/bibliography.astro +14 -0
  9. package/src/tool/voice-spectrogram-analyzer/bibliography.ts +16 -0
  10. package/src/tool/voice-spectrogram-analyzer/component.astro +116 -0
  11. package/src/tool/voice-spectrogram-analyzer/controller.ts +219 -0
  12. package/src/tool/voice-spectrogram-analyzer/dom-views.ts +204 -0
  13. package/src/tool/voice-spectrogram-analyzer/entry.ts +31 -0
  14. package/src/tool/voice-spectrogram-analyzer/evaluator.ts +13 -0
  15. package/src/tool/voice-spectrogram-analyzer/fft.ts +64 -0
  16. package/src/tool/voice-spectrogram-analyzer/i18n/de.ts +135 -0
  17. package/src/tool/voice-spectrogram-analyzer/i18n/en.ts +122 -0
  18. package/src/tool/voice-spectrogram-analyzer/i18n/es.ts +122 -0
  19. package/src/tool/voice-spectrogram-analyzer/i18n/fr.ts +135 -0
  20. package/src/tool/voice-spectrogram-analyzer/i18n/id.ts +136 -0
  21. package/src/tool/voice-spectrogram-analyzer/i18n/it.ts +135 -0
  22. package/src/tool/voice-spectrogram-analyzer/i18n/ja.ts +135 -0
  23. package/src/tool/voice-spectrogram-analyzer/i18n/ko.ts +135 -0
  24. package/src/tool/voice-spectrogram-analyzer/i18n/nl.ts +136 -0
  25. package/src/tool/voice-spectrogram-analyzer/i18n/pl.ts +136 -0
  26. package/src/tool/voice-spectrogram-analyzer/i18n/pt.ts +135 -0
  27. package/src/tool/voice-spectrogram-analyzer/i18n/ru.ts +136 -0
  28. package/src/tool/voice-spectrogram-analyzer/i18n/sv.ts +136 -0
  29. package/src/tool/voice-spectrogram-analyzer/i18n/tr.ts +136 -0
  30. package/src/tool/voice-spectrogram-analyzer/i18n/zh.ts +135 -0
  31. package/src/tool/voice-spectrogram-analyzer/index.ts +11 -0
  32. package/src/tool/voice-spectrogram-analyzer/logic.test.ts +46 -0
  33. package/src/tool/voice-spectrogram-analyzer/logic.ts +163 -0
  34. package/src/tool/voice-spectrogram-analyzer/seo.astro +15 -0
  35. package/src/tool/voice-spectrogram-analyzer/storage.ts +33 -0
  36. package/src/tool/voice-spectrogram-analyzer/ui.ts +49 -0
  37. package/src/tool/voice-spectrogram-analyzer/voice-spectrogram-analyzer.css +547 -0
  38. package/src/tools.ts +3 -1
@@ -0,0 +1,547 @@
1
+ :root {
2
+ --n-va-paper: #f1eee4;
3
+ --n-va-ink: #10242a;
4
+ --n-va-muted: #52676b;
5
+ --n-va-panel: #e4ded0;
6
+ --n-va-panel-soft: #faf8f2;
7
+ --n-va-canvas: #06161c;
8
+ --n-va-grid: #254048;
9
+ --n-va-a: #35d6c5;
10
+ --n-va-b: #ff739e;
11
+ --n-va-a-line: #b8fff6;
12
+ --n-va-b-line: #ffd0df;
13
+ --n-va-cursor: #ffe99a;
14
+ --n-va-border: #a9afa7;
15
+ --n-va-shadow: rgba(10, 28, 33, 0.18);
16
+ --n-va-glow-a: rgba(53, 214, 197, 0.22);
17
+ --n-va-glow-b: rgba(255, 115, 158, 0.18);
18
+ --n-va-white: #fff;
19
+ --n-va-black: #000;
20
+ }
21
+
22
+ .theme-dark {
23
+ --n-va-paper: #111c20;
24
+ --n-va-ink: #eef4ee;
25
+ --n-va-muted: #a7b8b7;
26
+ --n-va-panel: #1c2b2e;
27
+ --n-va-panel-soft: #162326;
28
+ --n-va-canvas: #020a0d;
29
+ --n-va-grid: #1d363d;
30
+ --n-va-a: #43e7d5;
31
+ --n-va-b: #ff82aa;
32
+ --n-va-a-line: #c4fff8;
33
+ --n-va-b-line: #ffd7e3;
34
+ --n-va-cursor: #ffefa4;
35
+ --n-va-border: #42575a;
36
+ --n-va-shadow: rgba(0, 0, 0, 0.42);
37
+ --n-va-glow-a: rgba(67, 231, 213, 0.2);
38
+ --n-va-glow-b: rgba(255, 130, 170, 0.17);
39
+ --n-va-white: #fff;
40
+ --n-va-black: #000;
41
+ }
42
+
43
+ .voice-spectrogram {
44
+ position: relative;
45
+ overflow: hidden;
46
+ color: var(--n-va-ink);
47
+ background:
48
+ radial-gradient(circle at 10% 2%, var(--n-va-glow-a), transparent 28%),
49
+ radial-gradient(circle at 92% 22%, var(--n-va-glow-b), transparent 26%),
50
+ var(--n-va-paper);
51
+ border: 1px solid var(--n-va-border);
52
+ border-radius: 1.8rem;
53
+ box-shadow: 0 1.8rem 4rem var(--n-va-shadow);
54
+ }
55
+
56
+ .voice-spectrogram button,
57
+ .voice-spectrogram input {
58
+ font-size: inherit;
59
+ }
60
+
61
+ .voice-spectrogram button {
62
+ color: var(--n-va-ink);
63
+ }
64
+
65
+ .voice-spectrogram-privacy {
66
+ display: flex;
67
+ align-items: center;
68
+ gap: 0.8rem;
69
+ padding: 0.85rem clamp(1rem, 3vw, 2rem);
70
+ color: var(--n-va-muted);
71
+ background: var(--n-va-panel-soft);
72
+ border-bottom: 1px solid var(--n-va-border);
73
+ }
74
+
75
+ .voice-spectrogram-privacy span {
76
+ flex: 0 0 auto;
77
+ padding: 0.28rem 0.58rem;
78
+ color: var(--n-va-canvas);
79
+ background: var(--n-va-a);
80
+ border-radius: 99rem;
81
+ font-size: 0.72rem;
82
+ font-weight: 800;
83
+ letter-spacing: 0.08em;
84
+ text-transform: uppercase;
85
+ }
86
+
87
+ .voice-spectrogram-privacy p,
88
+ .voice-spectrogram-comparison p,
89
+ .voice-spectrogram-educational {
90
+ margin: 0;
91
+ }
92
+
93
+ .voice-spectrogram-load,
94
+ .voice-spectrogram-comparison {
95
+ padding: clamp(1.1rem, 3vw, 2rem);
96
+ }
97
+
98
+ .voice-spectrogram-section-intro,
99
+ .voice-spectrogram-instrument-head {
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: space-between;
103
+ gap: 1rem;
104
+ margin-bottom: 1rem;
105
+ }
106
+
107
+ .voice-spectrogram-kicker {
108
+ margin: 0;
109
+ font-size: clamp(0.92rem, 1.8vw, 1.15rem);
110
+ font-weight: 900;
111
+ letter-spacing: 0.08em;
112
+ text-transform: uppercase;
113
+ }
114
+
115
+ .voice-spectrogram-section-intro > span {
116
+ color: var(--n-va-muted);
117
+ font-size: 0.82rem;
118
+ }
119
+
120
+ .voice-spectrogram-samples {
121
+ display: grid;
122
+ grid-template-columns: repeat(2, minmax(0, 1fr));
123
+ gap: 1rem;
124
+ }
125
+
126
+ .voice-spectrogram-sample {
127
+ position: relative;
128
+ min-width: 0;
129
+ padding: 0.85rem;
130
+ background: var(--n-va-panel-soft);
131
+ border: 1px solid var(--n-va-border);
132
+ border-radius: 1.2rem;
133
+ box-shadow: 0 0.8rem 1.8rem var(--n-va-shadow);
134
+ }
135
+
136
+ .voice-spectrogram-sample::before {
137
+ position: absolute;
138
+ inset: 0.55rem auto 0.55rem 0.55rem;
139
+ width: 0.22rem;
140
+ background: var(--n-va-a);
141
+ border-radius: 99rem;
142
+ content: "";
143
+ }
144
+
145
+ .voice-spectrogram-sample-b::before {
146
+ background: var(--n-va-b);
147
+ }
148
+
149
+ .voice-spectrogram-sample-index {
150
+ position: absolute;
151
+ top: -0.65rem;
152
+ left: 1.35rem;
153
+ padding: 0.22rem 0.55rem;
154
+ color: var(--n-va-canvas);
155
+ background: var(--n-va-a);
156
+ border-radius: 0.4rem;
157
+ font-size: 0.7rem;
158
+ font-weight: 900;
159
+ letter-spacing: 0.08em;
160
+ text-transform: uppercase;
161
+ }
162
+
163
+ .voice-spectrogram-sample-b .voice-spectrogram-sample-index {
164
+ background: var(--n-va-b);
165
+ }
166
+
167
+ .voice-spectrogram-drop {
168
+ display: grid;
169
+ grid-template-columns: minmax(0, 1fr) auto;
170
+ gap: 0.65rem 1rem;
171
+ align-items: center;
172
+ min-height: 6.8rem;
173
+ padding: 1rem 1rem 0.75rem 1.35rem;
174
+ border: 1px dashed var(--n-va-border);
175
+ border-radius: 0.8rem;
176
+ }
177
+
178
+ .voice-spectrogram-drop strong,
179
+ .voice-spectrogram-drop span {
180
+ display: block;
181
+ overflow: hidden;
182
+ text-overflow: ellipsis;
183
+ white-space: nowrap;
184
+ }
185
+
186
+ .voice-spectrogram-drop strong {
187
+ margin-bottom: 0.25rem;
188
+ }
189
+
190
+ .voice-spectrogram-drop span,
191
+ .voice-spectrogram-drop small,
192
+ .voice-spectrogram-sample-foot {
193
+ color: var(--n-va-muted);
194
+ font-size: 0.76rem;
195
+ }
196
+
197
+ .voice-spectrogram-drop small {
198
+ grid-column: 1 / -1;
199
+ }
200
+
201
+ .voice-spectrogram-file {
202
+ display: inline-flex;
203
+ padding: 0.55rem 0.75rem;
204
+ color: var(--n-va-paper);
205
+ background: var(--n-va-ink);
206
+ border-radius: 0.55rem;
207
+ cursor: pointer;
208
+ font-size: 0.78rem;
209
+ font-weight: 800;
210
+ }
211
+
212
+ .voice-spectrogram-file input {
213
+ position: absolute;
214
+ width: 1px;
215
+ height: 1px;
216
+ overflow: hidden;
217
+ clip-path: inset(50%);
218
+ white-space: nowrap;
219
+ }
220
+
221
+ .voice-spectrogram-sample-foot {
222
+ display: flex;
223
+ justify-content: space-between;
224
+ gap: 0.8rem;
225
+ padding: 0.75rem 0.15rem 0.05rem 0.55rem;
226
+ }
227
+
228
+ .voice-spectrogram-presets {
229
+ display: flex;
230
+ flex-wrap: wrap;
231
+ justify-content: flex-end;
232
+ gap: 0.35rem;
233
+ }
234
+
235
+ .voice-spectrogram-presets button,
236
+ .voice-spectrogram-segmented button {
237
+ padding: 0.28rem 0.5rem;
238
+ background: transparent;
239
+ border: 1px solid var(--n-va-border);
240
+ border-radius: 99rem;
241
+ cursor: pointer;
242
+ font-size: 0.7rem;
243
+ }
244
+
245
+ .voice-spectrogram-instrument {
246
+ margin: 0 clamp(0.65rem, 2vw, 1.25rem);
247
+ padding: clamp(0.75rem, 2vw, 1.25rem);
248
+ color: var(--n-va-white);
249
+ background: var(--n-va-canvas);
250
+ border: 1px solid var(--n-va-grid);
251
+ border-radius: 1.35rem;
252
+ box-shadow: inset 0 0 3rem var(--n-va-black), 0 1.2rem 2.8rem var(--n-va-shadow);
253
+ }
254
+
255
+ .voice-spectrogram-instrument-head > div:first-child {
256
+ display: flex;
257
+ align-items: center;
258
+ gap: 0.55rem;
259
+ font-size: 0.78rem;
260
+ letter-spacing: 0.05em;
261
+ text-transform: uppercase;
262
+ }
263
+
264
+ .voice-spectrogram-live-dot {
265
+ width: 0.6rem;
266
+ height: 0.6rem;
267
+ background: var(--n-va-a);
268
+ border-radius: 50%;
269
+ box-shadow: 0 0 0.8rem var(--n-va-a);
270
+ }
271
+
272
+ .voice-spectrogram-control-clusters {
273
+ display: flex;
274
+ flex-wrap: wrap;
275
+ justify-content: flex-end;
276
+ gap: 0.6rem;
277
+ }
278
+
279
+ .voice-spectrogram-segmented {
280
+ display: flex;
281
+ align-items: center;
282
+ gap: 0.3rem;
283
+ }
284
+
285
+ .voice-spectrogram-segmented > span {
286
+ margin-right: 0.25rem;
287
+ color: var(--n-va-muted);
288
+ font-size: 0.68rem;
289
+ text-transform: uppercase;
290
+ }
291
+
292
+ .voice-spectrogram-segmented button {
293
+ color: var(--n-va-muted);
294
+ border-color: var(--n-va-grid);
295
+ }
296
+
297
+ .voice-spectrogram-segmented button.is-active {
298
+ color: var(--n-va-canvas);
299
+ background: var(--n-va-cursor);
300
+ border-color: var(--n-va-cursor);
301
+ }
302
+
303
+ .voice-spectrogram-plate {
304
+ position: relative;
305
+ overflow: hidden;
306
+ border: 1px solid var(--n-va-grid);
307
+ border-radius: 0.9rem;
308
+ }
309
+
310
+ .voice-spectrogram-plate canvas {
311
+ display: block;
312
+ width: 100%;
313
+ height: clamp(24rem, 54vw, 34rem);
314
+ }
315
+
316
+ .voice-spectrogram-plate-glow {
317
+ position: absolute;
318
+ inset: 0;
319
+ pointer-events: none;
320
+ background: linear-gradient(90deg, var(--n-va-glow-a), transparent 38%, transparent 62%, var(--n-va-glow-b));
321
+ mix-blend-mode: screen;
322
+ }
323
+
324
+ .voice-spectrogram-transport {
325
+ display: grid;
326
+ grid-template-columns: 1fr auto 1fr;
327
+ gap: 0.7rem;
328
+ align-items: center;
329
+ max-width: 34rem;
330
+ margin: 1rem auto 0;
331
+ }
332
+
333
+ .voice-spectrogram-transport button {
334
+ min-height: 2.8rem;
335
+ color: var(--n-va-white);
336
+ background: var(--n-va-grid);
337
+ border: 1px solid var(--n-va-grid);
338
+ border-radius: 99rem;
339
+ cursor: pointer;
340
+ font-weight: 800;
341
+ }
342
+
343
+ .voice-spectrogram-transport button:not(.voice-spectrogram-stop) {
344
+ display: flex;
345
+ align-items: center;
346
+ justify-content: center;
347
+ gap: 0.55rem;
348
+ }
349
+
350
+ .voice-spectrogram-transport button span {
351
+ display: grid;
352
+ width: 1.5rem;
353
+ height: 1.5rem;
354
+ place-items: center;
355
+ color: var(--n-va-canvas);
356
+ background: var(--n-va-a);
357
+ border-radius: 50%;
358
+ }
359
+
360
+ .voice-spectrogram-transport button:last-child span {
361
+ background: var(--n-va-b);
362
+ }
363
+
364
+ .voice-spectrogram-stop {
365
+ padding: 0 1rem;
366
+ color: var(--n-va-cursor);
367
+ background: transparent;
368
+ }
369
+
370
+ .voice-spectrogram-legend {
371
+ display: flex;
372
+ flex-wrap: wrap;
373
+ justify-content: center;
374
+ gap: 0.6rem 1.3rem;
375
+ margin-top: 0.85rem;
376
+ color: var(--n-va-muted);
377
+ font-size: 0.68rem;
378
+ }
379
+
380
+ .voice-spectrogram-legend span {
381
+ display: flex;
382
+ align-items: center;
383
+ gap: 0.35rem;
384
+ }
385
+
386
+ .voice-spectrogram-swatch {
387
+ width: 0.75rem;
388
+ height: 0.75rem;
389
+ background: var(--n-va-a);
390
+ border-radius: 0.2rem;
391
+ }
392
+
393
+ .voice-spectrogram-swatch-b {
394
+ background: var(--n-va-b);
395
+ }
396
+
397
+ .voice-spectrogram-line {
398
+ width: 1.2rem;
399
+ border-top: 2px solid var(--n-va-cursor);
400
+ }
401
+
402
+ .voice-spectrogram-comparison {
403
+ display: grid;
404
+ grid-template-columns: minmax(12rem, 0.7fr) minmax(0, 1.6fr);
405
+ gap: 1.4rem;
406
+ align-items: start;
407
+ }
408
+
409
+ .voice-spectrogram-comparison-copy p:last-child {
410
+ margin-top: 0.65rem;
411
+ color: var(--n-va-muted);
412
+ line-height: 1.6;
413
+ }
414
+
415
+ .voice-spectrogram-formants {
416
+ display: grid;
417
+ grid-template-columns: repeat(3, minmax(0, 1fr));
418
+ gap: 0.65rem;
419
+ }
420
+
421
+ .voice-spectrogram-formants article {
422
+ position: relative;
423
+ overflow: hidden;
424
+ padding: 1rem;
425
+ background: var(--n-va-panel-soft);
426
+ border: 1px solid var(--n-va-border);
427
+ border-radius: 0.85rem;
428
+ }
429
+
430
+ .voice-spectrogram-formant-index {
431
+ display: block;
432
+ color: var(--n-va-muted);
433
+ font-size: 1.8rem;
434
+ font-weight: 900;
435
+ opacity: 0.4;
436
+ }
437
+
438
+ .voice-spectrogram-formants strong {
439
+ display: block;
440
+ min-height: 2.5rem;
441
+ font-size: 0.85rem;
442
+ }
443
+
444
+ .voice-spectrogram-formants dl,
445
+ .voice-spectrogram-formants dd {
446
+ margin: 0;
447
+ }
448
+
449
+ .voice-spectrogram-formants dl > div {
450
+ display: flex;
451
+ justify-content: space-between;
452
+ gap: 0.5rem;
453
+ padding: 0.38rem 0;
454
+ border-top: 1px solid var(--n-va-border);
455
+ font-size: 0.75rem;
456
+ }
457
+
458
+ .voice-spectrogram-formants dt {
459
+ color: var(--n-va-muted);
460
+ }
461
+
462
+ .voice-spectrogram-formants dd {
463
+ min-width: 4.6rem;
464
+ text-align: right;
465
+ font-variant-numeric: tabular-nums;
466
+ font-weight: 800;
467
+ }
468
+
469
+ .voice-spectrogram-educational {
470
+ padding: 0 2rem 1.5rem;
471
+ color: var(--n-va-muted);
472
+ font-size: 0.72rem;
473
+ line-height: 1.5;
474
+ text-align: center;
475
+ }
476
+
477
+ .voice-spectrogram button:focus-visible,
478
+ .voice-spectrogram input:focus-visible,
479
+ .voice-spectrogram-file:focus-within {
480
+ outline: 3px solid var(--n-va-cursor);
481
+ outline-offset: 3px;
482
+ }
483
+
484
+ @media (max-width: 760px) {
485
+ .voice-spectrogram-samples,
486
+ .voice-spectrogram-comparison {
487
+ grid-template-columns: 1fr;
488
+ }
489
+
490
+ .voice-spectrogram-instrument-head,
491
+ .voice-spectrogram-section-intro {
492
+ align-items: flex-start;
493
+ flex-direction: column;
494
+ }
495
+
496
+ .voice-spectrogram-control-clusters {
497
+ justify-content: flex-start;
498
+ }
499
+
500
+ .voice-spectrogram-plate canvas {
501
+ height: 27rem;
502
+ }
503
+ }
504
+
505
+ @media (max-width: 520px) {
506
+ .voice-spectrogram-privacy {
507
+ align-items: flex-start;
508
+ }
509
+
510
+ .voice-spectrogram-drop {
511
+ grid-template-columns: 1fr;
512
+ }
513
+
514
+ .voice-spectrogram-drop small {
515
+ grid-column: auto;
516
+ }
517
+
518
+ .voice-spectrogram-sample-foot {
519
+ align-items: flex-start;
520
+ flex-direction: column;
521
+ }
522
+
523
+ .voice-spectrogram-presets {
524
+ justify-content: flex-start;
525
+ }
526
+
527
+ .voice-spectrogram-formants {
528
+ grid-template-columns: 1fr;
529
+ }
530
+
531
+ .voice-spectrogram-transport {
532
+ grid-template-columns: 1fr 1fr;
533
+ }
534
+
535
+ .voice-spectrogram-stop {
536
+ grid-column: 1 / -1;
537
+ grid-row: 2;
538
+ }
539
+ }
540
+
541
+ @media (prefers-reduced-motion: reduce) {
542
+ .voice-spectrogram *,
543
+ .voice-spectrogram *::before,
544
+ .voice-spectrogram *::after {
545
+ scroll-behavior: auto;
546
+ }
547
+ }
package/src/tools.ts CHANGED
@@ -16,6 +16,7 @@ import { FORENSIC_FINGERPRINT_MINUTIAE_IDENTIFIER_TOOL } from './tool/forensic-f
16
16
  import { FIRE_PATTERN_ORIGIN_ANALYZER_TOOL } from './tool/fire-pattern-origin-analyzer/index';
17
17
  import { FORENSIC_TOOLMARK_STRIATION_MATCHER_TOOL } from './tool/forensic-toolmark-striation-matcher/index';
18
18
  import { TIME_OF_DEATH_ALGOR_MORTIS_CALCULATOR_TOOL } from './tool/time-of-death-algor-mortis-calculator/index';
19
+ import { VOICE_SPECTROGRAM_ANALYZER_TOOL } from './tool/voice-spectrogram-analyzer/index';
19
20
 
20
21
  export const ALL_TOOLS: ToolDefinition[] = [
21
22
  FORENSIC_AGE_ESTIMATOR_TOOL,
@@ -33,5 +34,6 @@ export const ALL_TOOLS: ToolDefinition[] = [
33
34
  FORENSIC_FINGERPRINT_MINUTIAE_IDENTIFIER_TOOL,
34
35
  FIRE_PATTERN_ORIGIN_ANALYZER_TOOL,
35
36
  FORENSIC_TOOLMARK_STRIATION_MATCHER_TOOL,
36
- TIME_OF_DEATH_ALGOR_MORTIS_CALCULATOR_TOOL
37
+ TIME_OF_DEATH_ALGOR_MORTIS_CALCULATOR_TOOL,
38
+ VOICE_SPECTROGRAM_ANALYZER_TOOL
37
39
  ];