sonusai 1.0.11__tar.gz → 1.0.13__tar.gz

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 (139) hide show
  1. {sonusai-1.0.11 → sonusai-1.0.13}/PKG-INFO +1 -1
  2. {sonusai-1.0.11 → sonusai-1.0.13}/pyproject.toml +1 -1
  3. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/genmetrics.py +4 -6
  4. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/__init__.py +1 -0
  5. sonusai-1.0.13/sonusai/metrics/calculate_metrics.py +395 -0
  6. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/mixdb.py +22 -551
  7. {sonusai-1.0.11 → sonusai-1.0.13}/README.rst +0 -0
  8. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/__init__.py +0 -0
  9. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/aawscd_probwrite.py +0 -0
  10. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/audiofe.py +0 -0
  11. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/calc_metric_spenh.py +0 -0
  12. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/config/__init__.py +0 -0
  13. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/config/config.py +0 -0
  14. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/config/config.yml +0 -0
  15. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/config/constants.py +0 -0
  16. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/constants.py +0 -0
  17. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/data/__init__.py +0 -0
  18. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/data/genmixdb.yml +0 -0
  19. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/data/silero_vad_v5.1.jit +0 -0
  20. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/data/silero_vad_v5.1.onnx +0 -0
  21. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/data/speech_ma01_01.wav +0 -0
  22. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/data/whitenoise.wav +0 -0
  23. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/datatypes.py +0 -0
  24. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/deprecated/gentcst.py +0 -0
  25. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/deprecated/plot.py +0 -0
  26. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/deprecated/tplot.py +0 -0
  27. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/doc/__init__.py +0 -0
  28. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/doc/doc.py +0 -0
  29. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/doc.py +0 -0
  30. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/genft.py +0 -0
  31. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/genmix.py +0 -0
  32. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/genmixdb.py +0 -0
  33. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/ir_metric.py +0 -0
  34. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/lsdb.py +0 -0
  35. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/main.py +0 -0
  36. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_audio_stats.py +0 -0
  37. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_class_weights.py +0 -0
  38. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_optimal_thresholds.py +0 -0
  39. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_pcm.py +0 -0
  40. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_pesq.py +0 -0
  41. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_phase_distance.py +0 -0
  42. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_sa_sdr.py +0 -0
  43. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_sample_weights.py +0 -0
  44. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_segsnr_f.py +0 -0
  45. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_speech.py +0 -0
  46. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_wer.py +0 -0
  47. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/calc_wsdr.py +0 -0
  48. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/class_summary.py +0 -0
  49. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/confusion_matrix_summary.py +0 -0
  50. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/one_hot.py +0 -0
  51. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics/snr_summary.py +0 -0
  52. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/metrics_summary.py +0 -0
  53. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/__init__.py +0 -0
  54. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/audio.py +0 -0
  55. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/class_balancing.py +0 -0
  56. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/config.py +0 -0
  57. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/constants.py +0 -0
  58. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/data_io.py +0 -0
  59. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/db.py +0 -0
  60. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/db_datatypes.py +0 -0
  61. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/effects.py +0 -0
  62. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/feature.py +0 -0
  63. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/generation.py +0 -0
  64. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/helpers.py +0 -0
  65. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/ir_delay.py +0 -0
  66. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/ir_effects.py +0 -0
  67. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/log_duration_and_sizes.py +0 -0
  68. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/pad_audio.py +0 -0
  69. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/parse.py +0 -0
  70. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/resample.py +0 -0
  71. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/sox_effects.py +0 -0
  72. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/sox_help.py +0 -0
  73. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/spectral_mask.py +0 -0
  74. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/truth.py +0 -0
  75. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/truth_functions/__init__.py +0 -0
  76. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/truth_functions/crm.py +0 -0
  77. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/truth_functions/energy.py +0 -0
  78. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/truth_functions/file.py +0 -0
  79. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/truth_functions/metadata.py +0 -0
  80. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/truth_functions/metrics.py +0 -0
  81. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/truth_functions/phoneme.py +0 -0
  82. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/truth_functions/sed.py +0 -0
  83. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mixture/truth_functions/target.py +0 -0
  84. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/mkwav.py +0 -0
  85. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/onnx_predict.py +0 -0
  86. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/queries/__init__.py +0 -0
  87. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/queries/queries.py +0 -0
  88. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/speech/__init__.py +0 -0
  89. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/speech/l2arctic.py +0 -0
  90. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/speech/librispeech.py +0 -0
  91. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/speech/mcgill.py +0 -0
  92. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/speech/textgrid.py +0 -0
  93. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/speech/timit.py +0 -0
  94. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/speech/types.py +0 -0
  95. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/speech/vctk.py +0 -0
  96. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/speech/voxceleb.py +0 -0
  97. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/__init__.py +0 -0
  98. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/asl_p56.py +0 -0
  99. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/asr.py +0 -0
  100. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/asr_functions/__init__.py +0 -0
  101. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/asr_functions/aaware_whisper.py +0 -0
  102. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/audio_devices.py +0 -0
  103. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/braced_glob.py +0 -0
  104. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/calculate_input_shape.py +0 -0
  105. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/choice.py +0 -0
  106. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/compress.py +0 -0
  107. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/convert_string_to_number.py +0 -0
  108. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/create_timestamp.py +0 -0
  109. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/create_ts_name.py +0 -0
  110. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/dataclass_from_dict.py +0 -0
  111. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/db.py +0 -0
  112. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/docstring.py +0 -0
  113. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/energy_f.py +0 -0
  114. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/engineering_number.py +0 -0
  115. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/evaluate_random_rule.py +0 -0
  116. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/get_frames_per_batch.py +0 -0
  117. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/get_label_names.py +0 -0
  118. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/grouper.py +0 -0
  119. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/human_readable_size.py +0 -0
  120. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/keyboard_interrupt.py +0 -0
  121. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/load_object.py +0 -0
  122. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/max_text_width.py +0 -0
  123. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/model_utils.py +0 -0
  124. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/numeric_conversion.py +0 -0
  125. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/onnx_utils.py +0 -0
  126. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/parallel.py +0 -0
  127. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/path_info.py +0 -0
  128. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/print_mixture_details.py +0 -0
  129. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/rand.py +0 -0
  130. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/ranges.py +0 -0
  131. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/read_predict_data.py +0 -0
  132. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/reshape.py +0 -0
  133. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/seconds_to_hms.py +0 -0
  134. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/stacked_complex.py +0 -0
  135. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/stratified_shuffle_split.py +0 -0
  136. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/tokenized_shell_vars.py +0 -0
  137. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/write_audio.py +0 -0
  138. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/utils/yes_or_no.py +0 -0
  139. {sonusai-1.0.11 → sonusai-1.0.13}/sonusai/vars.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sonusai
3
- Version: 1.0.11
3
+ Version: 1.0.13
4
4
  Summary: Framework for building deep neural network models for sound, speech, and voice AI
5
5
  Home-page: https://aaware.com
6
6
  License: GPL-3.0-only
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "sonusai"
3
- version = "1.0.11"
3
+ version = "1.0.13"
4
4
  description = "Framework for building deep neural network models for sound, speech, and voice AI"
5
5
  authors = ["Chris Eddington <chris@aaware.com>", "Jason Calderwood <jason@aaware.com>"]
6
6
  maintainers = ["Chris Eddington <chris@aaware.com>", "Jason Calderwood <jason@aaware.com>"]
@@ -147,12 +147,10 @@ def main() -> None:
147
147
  logger.info("")
148
148
  logger.info(f"Found {len(mixids):,} mixtures to process")
149
149
 
150
- if num_proc is None or len(mixids) == 1:
151
- no_par = True
152
- num_proc = None
153
- else:
154
- no_par = False
155
- num_proc = int(num_proc) # TBD add support for 'auto'
150
+ no_par = num_proc == 1 or len(mixids) == 1
151
+
152
+ if num_proc is not None:
153
+ num_proc = int(num_proc)
156
154
 
157
155
  progress = track(total=len(mixids), desc="genmetrics")
158
156
  results = par_track(
@@ -15,6 +15,7 @@ from .calc_segsnr_f import calc_segsnr_f_bin
15
15
  from .calc_speech import calc_speech
16
16
  from .calc_wer import calc_wer
17
17
  from .calc_wsdr import calc_wsdr
18
+ from .calculate_metrics import calculate_metrics
18
19
  from .class_summary import class_summary
19
20
  from .confusion_matrix_summary import confusion_matrix_summary
20
21
  from .one_hot import one_hot
@@ -0,0 +1,395 @@
1
+ import functools
2
+ from typing import Any
3
+
4
+ import numpy as np
5
+ from pystoi import stoi
6
+
7
+ from ..constants import SAMPLE_RATE
8
+ from ..datatypes import AudioF
9
+ from ..datatypes import AudioStatsMetrics
10
+ from ..datatypes import AudioT
11
+ from ..datatypes import Segsnr
12
+ from ..datatypes import SpeechMetrics
13
+ from ..mixture.mixdb import MixtureDatabase
14
+ from ..utils.asr import calc_asr
15
+ from ..utils.db import linear_to_db
16
+ from .calc_audio_stats import calc_audio_stats
17
+ from .calc_pesq import calc_pesq
18
+ from .calc_phase_distance import calc_phase_distance
19
+ from .calc_segsnr_f import calc_segsnr_f
20
+ from .calc_segsnr_f import calc_segsnr_f_bin
21
+ from .calc_speech import calc_speech
22
+ from .calc_wer import calc_wer
23
+ from .calc_wsdr import calc_wsdr
24
+
25
+
26
+ def calculate_metrics(mixdb: MixtureDatabase, m_id: int, metrics: list[str], force: bool = False) -> dict[str, Any]:
27
+ """Get metrics data for the given mixture ID
28
+
29
+ :param mixdb: Mixture database object
30
+ :param m_id: Zero-based mixture ID
31
+ :param metrics: List of metrics to get
32
+ :param force: Force computing data from original sources regardless of whether cached data exists
33
+ :return: Dictionary of metric data
34
+ """
35
+
36
+ # Define cached functions for expensive operations
37
+ @functools.lru_cache(maxsize=1)
38
+ def mixture_sources() -> dict[str, AudioT]:
39
+ return mixdb.mixture_sources(m_id)
40
+
41
+ @functools.lru_cache(maxsize=1)
42
+ def mixture_source() -> AudioT:
43
+ return mixdb.mixture_source(m_id)
44
+
45
+ @functools.lru_cache(maxsize=1)
46
+ def mixture_source_f() -> AudioF:
47
+ return mixdb.mixture_source_f(m_id)
48
+
49
+ @functools.lru_cache(maxsize=1)
50
+ def mixture_noise() -> AudioT:
51
+ return mixdb.mixture_noise(m_id)
52
+
53
+ @functools.lru_cache(maxsize=1)
54
+ def mixture_noise_f() -> AudioF:
55
+ return mixdb.mixture_noise_f(m_id)
56
+
57
+ @functools.lru_cache(maxsize=1)
58
+ def mixture_mixture() -> AudioT:
59
+ return mixdb.mixture_mixture(m_id)
60
+
61
+ @functools.lru_cache(maxsize=1)
62
+ def mixture_mixture_f() -> AudioF:
63
+ return mixdb.mixture_mixture_f(m_id)
64
+
65
+ @functools.lru_cache(maxsize=1)
66
+ def mixture_segsnr() -> Segsnr:
67
+ return mixdb.mixture_segsnr(m_id)
68
+
69
+ @functools.lru_cache(maxsize=1)
70
+ def calculate_pesq() -> dict[str, float]:
71
+ return {category: calc_pesq(mixture_mixture(), audio) for category, audio in mixture_sources().items()}
72
+
73
+ @functools.lru_cache(maxsize=1)
74
+ def calculate_speech() -> dict[str, SpeechMetrics]:
75
+ return {
76
+ category: calc_speech(mixture_mixture(), audio, calculate_pesq()[category])
77
+ for category, audio in mixture_sources().items()
78
+ }
79
+
80
+ @functools.lru_cache(maxsize=1)
81
+ def mixture_stats() -> AudioStatsMetrics:
82
+ return calc_audio_stats(mixture_mixture(), mixdb.fg_info.ft_config.length / SAMPLE_RATE)
83
+
84
+ @functools.lru_cache(maxsize=1)
85
+ def sources_stats() -> dict[str, AudioStatsMetrics]:
86
+ return {
87
+ category: calc_audio_stats(audio, mixdb.fg_info.ft_config.length / SAMPLE_RATE)
88
+ for category, audio in mixture_sources().items()
89
+ }
90
+
91
+ @functools.lru_cache(maxsize=1)
92
+ def source_stats() -> AudioStatsMetrics:
93
+ return calc_audio_stats(mixture_source(), mixdb.fg_info.ft_config.length / SAMPLE_RATE)
94
+
95
+ @functools.lru_cache(maxsize=1)
96
+ def noise_stats() -> AudioStatsMetrics:
97
+ return calc_audio_stats(mixture_noise(), mixdb.fg_info.ft_config.length / SAMPLE_RATE)
98
+
99
+ # Cache ASR configurations
100
+ @functools.lru_cache(maxsize=32)
101
+ def get_asr_config(asr_name: str) -> dict:
102
+ value = mixdb.asr_configs.get(asr_name, None)
103
+ if value is None:
104
+ raise ValueError(f"Unrecognized ASR name: '{asr_name}'")
105
+ return value
106
+
107
+ # Cache ASR results for sources, source and mixture
108
+ @functools.lru_cache(maxsize=16)
109
+ def sources_asr(asr_name: str) -> dict[str, str]:
110
+ return {
111
+ category: calc_asr(audio, **get_asr_config(asr_name)).text for category, audio in mixture_sources().items()
112
+ }
113
+
114
+ @functools.lru_cache(maxsize=16)
115
+ def source_asr(asr_name: str) -> str:
116
+ return calc_asr(mixture_source(), **get_asr_config(asr_name)).text
117
+
118
+ @functools.lru_cache(maxsize=16)
119
+ def mixture_asr(asr_name: str) -> str:
120
+ return calc_asr(mixture_mixture(), **get_asr_config(asr_name)).text
121
+
122
+ def get_asr_name(m: str) -> str:
123
+ parts = m.split(".")
124
+ if len(parts) != 2:
125
+ raise ValueError(f"Unrecognized format: '{m}'; must be of the form: '<metric>.<name>'")
126
+ asr_name = parts[1]
127
+ return asr_name
128
+
129
+ def calc(m: str) -> Any:
130
+ if m == "mxsnr":
131
+ return {category: source.snr for category, source in mixdb.mixture(m_id).all_sources.items()}
132
+
133
+ # Get cached data first, if exists
134
+ if not force:
135
+ value = mixdb.read_mixture_data(m_id, m)[m]
136
+ if value is not None:
137
+ return value
138
+
139
+ # Otherwise, generate data as needed
140
+ if m.startswith("mxwer"):
141
+ asr_name = get_asr_name(m)
142
+
143
+ if mixdb.mixture(m_id).is_noise_only:
144
+ # noise only, ignore/reset target asr
145
+ return float("nan")
146
+
147
+ if source_asr(asr_name):
148
+ return calc_wer(mixture_asr(asr_name), source_asr(asr_name)).wer * 100
149
+
150
+ # TODO: should this be NaN like above?
151
+ return float(0)
152
+
153
+ if m.startswith("basewer"):
154
+ asr_name = get_asr_name(m)
155
+
156
+ text = mixdb.mixture_speech_metadata(m_id, "text")
157
+ return {
158
+ category: calc_wer(source, str(text[category])).wer * 100 if isinstance(text[category], str) else 0
159
+ for category, source in sources_asr(asr_name).items()
160
+ }
161
+
162
+ if m.startswith("mxasr"):
163
+ return mixture_asr(get_asr_name(m))
164
+
165
+ if m == "mxssnr_avg":
166
+ return calc_segsnr_f(mixture_segsnr()).avg
167
+
168
+ if m == "mxssnr_std":
169
+ return calc_segsnr_f(mixture_segsnr()).std
170
+
171
+ if m == "mxssnr_avg_db":
172
+ val = calc_segsnr_f(mixture_segsnr()).avg
173
+ if val is not None:
174
+ return linear_to_db(val)
175
+ return None
176
+
177
+ if m == "mxssnr_std_db":
178
+ val = calc_segsnr_f(mixture_segsnr()).std
179
+ if val is not None:
180
+ return linear_to_db(val)
181
+ return None
182
+
183
+ if m == "mxssnrdb_avg":
184
+ return calc_segsnr_f(mixture_segsnr()).db_avg
185
+
186
+ if m == "mxssnrdb_std":
187
+ return calc_segsnr_f(mixture_segsnr()).db_std
188
+
189
+ if m == "mxssnrf_avg":
190
+ return calc_segsnr_f_bin(mixture_source_f(), mixture_noise_f()).avg
191
+
192
+ if m == "mxssnrf_std":
193
+ return calc_segsnr_f_bin(mixture_source_f(), mixture_noise_f()).std
194
+
195
+ if m == "mxssnrdbf_avg":
196
+ return calc_segsnr_f_bin(mixture_source_f(), mixture_noise_f()).db_avg
197
+
198
+ if m == "mxssnrdbf_std":
199
+ return calc_segsnr_f_bin(mixture_source_f(), mixture_noise_f()).db_std
200
+
201
+ if m == "mxpesq":
202
+ if mixdb.mixture(m_id).is_noise_only:
203
+ return dict.fromkeys(calculate_pesq(), 0)
204
+ return calculate_pesq()
205
+
206
+ if m == "mxcsig":
207
+ if mixdb.mixture(m_id).is_noise_only:
208
+ return dict.fromkeys(calculate_speech(), 0)
209
+ return {category: s.csig for category, s in calculate_speech().items()}
210
+
211
+ if m == "mxcbak":
212
+ if mixdb.mixture(m_id).is_noise_only:
213
+ return dict.fromkeys(calculate_speech(), 0)
214
+ return {category: s.cbak for category, s in calculate_speech().items()}
215
+
216
+ if m == "mxcovl":
217
+ if mixdb.mixture(m_id).is_noise_only:
218
+ return dict.fromkeys(calculate_speech(), 0)
219
+ return {category: s.covl for category, s in calculate_speech().items()}
220
+
221
+ if m == "mxwsdr":
222
+ mixture = mixture_mixture()[:, np.newaxis]
223
+ target = mixture_source()[:, np.newaxis]
224
+ noise = mixture_noise()[:, np.newaxis]
225
+ return calc_wsdr(
226
+ hypothesis=np.concatenate((mixture, noise), axis=1),
227
+ reference=np.concatenate((target, noise), axis=1),
228
+ with_log=True,
229
+ )[0]
230
+
231
+ if m == "mxpd":
232
+ return calc_phase_distance(hypothesis=mixture_mixture_f(), reference=mixture_source_f())[0]
233
+
234
+ if m == "mxstoi":
235
+ return stoi(
236
+ x=mixture_source(),
237
+ y=mixture_mixture(),
238
+ fs_sig=SAMPLE_RATE,
239
+ extended=False,
240
+ )
241
+
242
+ if m == "mxdco":
243
+ return mixture_stats().dco
244
+
245
+ if m == "mxmin":
246
+ return mixture_stats().min
247
+
248
+ if m == "mxmax":
249
+ return mixture_stats().max
250
+
251
+ if m == "mxpkdb":
252
+ return mixture_stats().pkdb
253
+
254
+ if m == "mxlrms":
255
+ return mixture_stats().lrms
256
+
257
+ if m == "mxpkr":
258
+ return mixture_stats().pkr
259
+
260
+ if m == "mxtr":
261
+ return mixture_stats().tr
262
+
263
+ if m == "mxcr":
264
+ return mixture_stats().cr
265
+
266
+ if m == "mxfl":
267
+ return mixture_stats().fl
268
+
269
+ if m == "mxpkc":
270
+ return mixture_stats().pkc
271
+
272
+ if m == "sdco":
273
+ return {category: s.dco for category, s in sources_stats().items()}
274
+
275
+ if m == "smin":
276
+ return {category: s.min for category, s in sources_stats().items()}
277
+
278
+ if m == "smax":
279
+ return {category: s.max for category, s in sources_stats().items()}
280
+
281
+ if m == "spkdb":
282
+ return {category: s.pkdb for category, s in sources_stats().items()}
283
+
284
+ if m == "slrms":
285
+ return {category: s.lrms for category, s in sources_stats().items()}
286
+
287
+ if m == "spkr":
288
+ return {category: s.pkr for category, s in sources_stats().items()}
289
+
290
+ if m == "str":
291
+ return {category: s.tr for category, s in sources_stats().items()}
292
+
293
+ if m == "scr":
294
+ return {category: s.cr for category, s in sources_stats().items()}
295
+
296
+ if m == "sfl":
297
+ return {category: s.fl for category, s in sources_stats().items()}
298
+
299
+ if m == "spkc":
300
+ return {category: s.pkc for category, s in sources_stats().items()}
301
+
302
+ if m == "mxsdco":
303
+ return source_stats().dco
304
+
305
+ if m == "mxsmin":
306
+ return source_stats().min
307
+
308
+ if m == "mxsmax":
309
+ return source_stats().max
310
+
311
+ if m == "mxspkdb":
312
+ return source_stats().pkdb
313
+
314
+ if m == "mxslrms":
315
+ return source_stats().lrms
316
+
317
+ if m == "mxspkr":
318
+ return source_stats().pkr
319
+
320
+ if m == "mxstr":
321
+ return source_stats().tr
322
+
323
+ if m == "mxscr":
324
+ return source_stats().cr
325
+
326
+ if m == "mxsfl":
327
+ return source_stats().fl
328
+
329
+ if m == "mxspkc":
330
+ return source_stats().pkc
331
+
332
+ if m.startswith("sasr"):
333
+ return sources_asr(get_asr_name(m))
334
+
335
+ if m.startswith("mxsasr"):
336
+ return source_asr(get_asr_name(m))
337
+
338
+ if m == "ndco":
339
+ return noise_stats().dco
340
+
341
+ if m == "nmin":
342
+ return noise_stats().min
343
+
344
+ if m == "nmax":
345
+ return noise_stats().max
346
+
347
+ if m == "npkdb":
348
+ return noise_stats().pkdb
349
+
350
+ if m == "nlrms":
351
+ return noise_stats().lrms
352
+
353
+ if m == "npkr":
354
+ return noise_stats().pkr
355
+
356
+ if m == "ntr":
357
+ return noise_stats().tr
358
+
359
+ if m == "ncr":
360
+ return noise_stats().cr
361
+
362
+ if m == "nfl":
363
+ return noise_stats().fl
364
+
365
+ if m == "npkc":
366
+ return noise_stats().pkc
367
+
368
+ if m == "sedavg":
369
+ return 0
370
+
371
+ if m == "sedcnt":
372
+ return 0
373
+
374
+ if m == "sedtop3":
375
+ return np.zeros(3, dtype=np.float32)
376
+
377
+ if m == "sedtopn":
378
+ return 0
379
+
380
+ if m == "ssnr":
381
+ return mixture_segsnr()
382
+
383
+ raise AttributeError(f"Unrecognized metric: '{m}'")
384
+
385
+ result: dict[str, Any] = {}
386
+ for metric in metrics:
387
+ result[metric] = calc(metric)
388
+
389
+ # Check for metrics dependencies and add them even if not explicitly requested.
390
+ if metric.startswith("mxwer"):
391
+ dependencies = ("mxasr." + metric[6:], "sasr." + metric[6:])
392
+ for dependency in dependencies:
393
+ result[dependency] = calc(dependency)
394
+
395
+ return result