sonusai 1.0.16__cp311-abi3-macosx_10_12_x86_64.whl
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.
- sonusai/__init__.py +170 -0
- sonusai/aawscd_probwrite.py +148 -0
- sonusai/audiofe.py +481 -0
- sonusai/calc_metric_spenh.py +1136 -0
- sonusai/config/__init__.py +0 -0
- sonusai/config/asr.py +21 -0
- sonusai/config/config.py +65 -0
- sonusai/config/config.yml +49 -0
- sonusai/config/constants.py +53 -0
- sonusai/config/ir.py +124 -0
- sonusai/config/ir_delay.py +62 -0
- sonusai/config/source.py +275 -0
- sonusai/config/spectral_masks.py +15 -0
- sonusai/config/truth.py +64 -0
- sonusai/constants.py +14 -0
- sonusai/data/__init__.py +0 -0
- sonusai/data/silero_vad_v5.1.jit +0 -0
- sonusai/data/silero_vad_v5.1.onnx +0 -0
- sonusai/data/speech_ma01_01.wav +0 -0
- sonusai/data/whitenoise.wav +0 -0
- sonusai/datatypes.py +383 -0
- sonusai/deprecated/gentcst.py +632 -0
- sonusai/deprecated/plot.py +519 -0
- sonusai/deprecated/tplot.py +365 -0
- sonusai/doc.py +52 -0
- sonusai/doc_strings/__init__.py +1 -0
- sonusai/doc_strings/doc_strings.py +531 -0
- sonusai/genft.py +196 -0
- sonusai/genmetrics.py +183 -0
- sonusai/genmix.py +199 -0
- sonusai/genmixdb.py +235 -0
- sonusai/ir_metric.py +551 -0
- sonusai/lsdb.py +141 -0
- sonusai/main.py +134 -0
- sonusai/metrics/__init__.py +43 -0
- sonusai/metrics/calc_audio_stats.py +42 -0
- sonusai/metrics/calc_class_weights.py +90 -0
- sonusai/metrics/calc_optimal_thresholds.py +73 -0
- sonusai/metrics/calc_pcm.py +45 -0
- sonusai/metrics/calc_pesq.py +36 -0
- sonusai/metrics/calc_phase_distance.py +43 -0
- sonusai/metrics/calc_sa_sdr.py +64 -0
- sonusai/metrics/calc_sample_weights.py +25 -0
- sonusai/metrics/calc_segsnr_f.py +82 -0
- sonusai/metrics/calc_speech.py +382 -0
- sonusai/metrics/calc_wer.py +71 -0
- sonusai/metrics/calc_wsdr.py +57 -0
- sonusai/metrics/calculate_metrics.py +395 -0
- sonusai/metrics/class_summary.py +74 -0
- sonusai/metrics/confusion_matrix_summary.py +75 -0
- sonusai/metrics/one_hot.py +283 -0
- sonusai/metrics/snr_summary.py +128 -0
- sonusai/metrics_summary.py +314 -0
- sonusai/mixture/__init__.py +15 -0
- sonusai/mixture/audio.py +187 -0
- sonusai/mixture/class_balancing.py +103 -0
- sonusai/mixture/constants.py +3 -0
- sonusai/mixture/data_io.py +173 -0
- sonusai/mixture/db.py +169 -0
- sonusai/mixture/db_datatypes.py +92 -0
- sonusai/mixture/effects.py +344 -0
- sonusai/mixture/feature.py +78 -0
- sonusai/mixture/generation.py +1116 -0
- sonusai/mixture/helpers.py +351 -0
- sonusai/mixture/ir_effects.py +77 -0
- sonusai/mixture/log_duration_and_sizes.py +23 -0
- sonusai/mixture/mixdb.py +1857 -0
- sonusai/mixture/pad_audio.py +35 -0
- sonusai/mixture/resample.py +7 -0
- sonusai/mixture/sox_effects.py +195 -0
- sonusai/mixture/sox_help.py +650 -0
- sonusai/mixture/spectral_mask.py +51 -0
- sonusai/mixture/truth.py +61 -0
- sonusai/mixture/truth_functions/__init__.py +45 -0
- sonusai/mixture/truth_functions/crm.py +105 -0
- sonusai/mixture/truth_functions/energy.py +222 -0
- sonusai/mixture/truth_functions/file.py +48 -0
- sonusai/mixture/truth_functions/metadata.py +24 -0
- sonusai/mixture/truth_functions/metrics.py +28 -0
- sonusai/mixture/truth_functions/phoneme.py +18 -0
- sonusai/mixture/truth_functions/sed.py +98 -0
- sonusai/mixture/truth_functions/target.py +142 -0
- sonusai/mkwav.py +135 -0
- sonusai/onnx_predict.py +363 -0
- sonusai/parse/__init__.py +0 -0
- sonusai/parse/expand.py +156 -0
- sonusai/parse/parse_source_directive.py +129 -0
- sonusai/parse/rand.py +214 -0
- sonusai/py.typed +0 -0
- sonusai/queries/__init__.py +0 -0
- sonusai/queries/queries.py +239 -0
- sonusai/rs.abi3.so +0 -0
- sonusai/rs.pyi +1 -0
- sonusai/rust/__init__.py +0 -0
- sonusai/speech/__init__.py +0 -0
- sonusai/speech/l2arctic.py +121 -0
- sonusai/speech/librispeech.py +102 -0
- sonusai/speech/mcgill.py +71 -0
- sonusai/speech/textgrid.py +89 -0
- sonusai/speech/timit.py +138 -0
- sonusai/speech/types.py +12 -0
- sonusai/speech/vctk.py +53 -0
- sonusai/speech/voxceleb.py +108 -0
- sonusai/utils/__init__.py +3 -0
- sonusai/utils/asl_p56.py +130 -0
- sonusai/utils/asr.py +91 -0
- sonusai/utils/asr_functions/__init__.py +3 -0
- sonusai/utils/asr_functions/aaware_whisper.py +69 -0
- sonusai/utils/audio_devices.py +50 -0
- sonusai/utils/braced_glob.py +50 -0
- sonusai/utils/calculate_input_shape.py +26 -0
- sonusai/utils/choice.py +51 -0
- sonusai/utils/compress.py +25 -0
- sonusai/utils/convert_string_to_number.py +6 -0
- sonusai/utils/create_timestamp.py +5 -0
- sonusai/utils/create_ts_name.py +14 -0
- sonusai/utils/dataclass_from_dict.py +27 -0
- sonusai/utils/db.py +16 -0
- sonusai/utils/docstring.py +53 -0
- sonusai/utils/energy_f.py +44 -0
- sonusai/utils/engineering_number.py +166 -0
- sonusai/utils/evaluate_random_rule.py +15 -0
- sonusai/utils/get_frames_per_batch.py +2 -0
- sonusai/utils/get_label_names.py +20 -0
- sonusai/utils/grouper.py +6 -0
- sonusai/utils/human_readable_size.py +7 -0
- sonusai/utils/keyboard_interrupt.py +12 -0
- sonusai/utils/load_object.py +21 -0
- sonusai/utils/max_text_width.py +9 -0
- sonusai/utils/model_utils.py +28 -0
- sonusai/utils/numeric_conversion.py +11 -0
- sonusai/utils/onnx_utils.py +155 -0
- sonusai/utils/parallel.py +162 -0
- sonusai/utils/path_info.py +7 -0
- sonusai/utils/print_mixture_details.py +60 -0
- sonusai/utils/rand.py +13 -0
- sonusai/utils/ranges.py +43 -0
- sonusai/utils/read_predict_data.py +32 -0
- sonusai/utils/reshape.py +154 -0
- sonusai/utils/seconds_to_hms.py +7 -0
- sonusai/utils/stacked_complex.py +82 -0
- sonusai/utils/stratified_shuffle_split.py +170 -0
- sonusai/utils/tokenized_shell_vars.py +143 -0
- sonusai/utils/write_audio.py +26 -0
- sonusai/utils/yes_or_no.py +8 -0
- sonusai/vars.py +47 -0
- sonusai-1.0.16.dist-info/METADATA +56 -0
- sonusai-1.0.16.dist-info/RECORD +150 -0
- sonusai-1.0.16.dist-info/WHEEL +4 -0
- sonusai-1.0.16.dist-info/entry_points.txt +3 -0
sonusai/__init__.py
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
import atexit
|
2
|
+
import logging
|
3
|
+
from os.path import dirname
|
4
|
+
|
5
|
+
from rich.logging import RichHandler
|
6
|
+
|
7
|
+
from sonusai.rs import __version__ as rs_version
|
8
|
+
|
9
|
+
__version__ = rs_version
|
10
|
+
|
11
|
+
BASEDIR = dirname(__file__)
|
12
|
+
|
13
|
+
commands_doc = """
|
14
|
+
audiofe Audio front end
|
15
|
+
calc_metric_spenh Run speech enhancement and analysis
|
16
|
+
doc Documentation
|
17
|
+
genft Generate feature and truth data
|
18
|
+
genmetrics Generate mixture metrics data
|
19
|
+
genmix Generate mixture and truth data
|
20
|
+
genmixdb Generate a mixture database
|
21
|
+
lsdb List information about a mixture database
|
22
|
+
metrics_summary Summarize generated metrics in a mixture database
|
23
|
+
mkwav Make WAV files from a mixture database
|
24
|
+
onnx_predict Run ONNX predict on a trained model
|
25
|
+
vars List custom SonusAI variables
|
26
|
+
"""
|
27
|
+
|
28
|
+
# Global handler registry to prevent duplicates
|
29
|
+
_file_handlers = []
|
30
|
+
|
31
|
+
|
32
|
+
def _cleanup_handlers():
|
33
|
+
"""Clean up file handlers on exit"""
|
34
|
+
for handler in _file_handlers:
|
35
|
+
handler.close()
|
36
|
+
|
37
|
+
|
38
|
+
atexit.register(_cleanup_handlers)
|
39
|
+
|
40
|
+
|
41
|
+
def setup_logger(name: str = "sonusai") -> logging.Logger:
|
42
|
+
"""Setup and return configured logger"""
|
43
|
+
_logger = logging.getLogger(name)
|
44
|
+
|
45
|
+
# Avoid duplicate configuration
|
46
|
+
if _logger.handlers:
|
47
|
+
return _logger
|
48
|
+
|
49
|
+
_logger.setLevel(logging.DEBUG)
|
50
|
+
|
51
|
+
# Setup console handler
|
52
|
+
formatter = logging.Formatter("%(message)s")
|
53
|
+
|
54
|
+
console_handler = RichHandler(show_level=False, show_path=False, show_time=False)
|
55
|
+
|
56
|
+
console_handler.setLevel(logging.DEBUG)
|
57
|
+
console_handler.setFormatter(formatter)
|
58
|
+
_logger.addHandler(console_handler)
|
59
|
+
|
60
|
+
return _logger
|
61
|
+
|
62
|
+
|
63
|
+
# Initialize loggers
|
64
|
+
logger = setup_logger("sonusai")
|
65
|
+
logger_db = setup_logger("sonusai_db")
|
66
|
+
|
67
|
+
|
68
|
+
def create_file_handler(filename: str, verbose: bool = False) -> None:
|
69
|
+
"""Create a file handler with error handling"""
|
70
|
+
from pathlib import Path
|
71
|
+
|
72
|
+
try:
|
73
|
+
# Ensure directory exists
|
74
|
+
Path(filename).parent.mkdir(parents=True, exist_ok=True)
|
75
|
+
|
76
|
+
formatter = logging.Formatter("%(message)s")
|
77
|
+
formatter_db = logging.Formatter("%(asctime)s %(message)s")
|
78
|
+
|
79
|
+
fh = logging.FileHandler(filename=filename, mode="w")
|
80
|
+
fh.setLevel(logging.DEBUG)
|
81
|
+
fh.setFormatter(formatter)
|
82
|
+
logger.addHandler(fh)
|
83
|
+
_file_handlers.append(fh)
|
84
|
+
|
85
|
+
if verbose:
|
86
|
+
filename_db = Path(filename)
|
87
|
+
filename_db = filename_db.parent / (filename_db.stem + "_dbtrace" + filename_db.suffix)
|
88
|
+
fh_db = logging.FileHandler(filename=filename_db, mode="w")
|
89
|
+
fh_db.setLevel(logging.DEBUG)
|
90
|
+
fh_db.setFormatter(formatter_db)
|
91
|
+
logger_db.addHandler(fh_db)
|
92
|
+
_file_handlers.append(fh_db)
|
93
|
+
|
94
|
+
except (PermissionError, OSError) as e:
|
95
|
+
logger.warning(f"Could not create log file {filename}: {e}")
|
96
|
+
|
97
|
+
|
98
|
+
def update_console_handler(verbose: bool) -> None:
|
99
|
+
"""Update console handler verbosity"""
|
100
|
+
console_handlers = [h for h in logger.handlers if isinstance(h, logging.StreamHandler | RichHandler)]
|
101
|
+
|
102
|
+
if not console_handlers:
|
103
|
+
return
|
104
|
+
|
105
|
+
handler = console_handlers[0]
|
106
|
+
if not verbose:
|
107
|
+
handler.setLevel(logging.INFO)
|
108
|
+
else:
|
109
|
+
handler.setLevel(logging.DEBUG)
|
110
|
+
|
111
|
+
|
112
|
+
def initial_log_messages(name: str, subprocess: str | None = None) -> None:
|
113
|
+
"""Write initial log messages with error handling"""
|
114
|
+
from datetime import datetime
|
115
|
+
from getpass import getuser
|
116
|
+
from os import getcwd
|
117
|
+
from socket import gethostname
|
118
|
+
from sys import argv
|
119
|
+
|
120
|
+
try:
|
121
|
+
if subprocess is None:
|
122
|
+
logger.info(f"SonusAI {__version__}")
|
123
|
+
else:
|
124
|
+
logger.info(f"SonusAI {subprocess}")
|
125
|
+
logger.info(f"{name}")
|
126
|
+
logger.info("")
|
127
|
+
logger.debug(f"Host: {gethostname()}")
|
128
|
+
logger.debug(f"User: {getuser()}")
|
129
|
+
logger.debug(f"Directory: {getcwd()}")
|
130
|
+
logger.debug(f"Date: {datetime.now()}")
|
131
|
+
logger.debug(f"Command: {' '.join(argv)}")
|
132
|
+
logger.debug("")
|
133
|
+
except Exception as e:
|
134
|
+
logger.warning(f"Could not write initial log messages: {e}")
|
135
|
+
|
136
|
+
|
137
|
+
def commands_list(doc: str = commands_doc) -> list[str]:
|
138
|
+
"""Parse commands from a documentation string"""
|
139
|
+
commands = []
|
140
|
+
for line in doc.strip().split("\n"):
|
141
|
+
line = line.strip()
|
142
|
+
if line:
|
143
|
+
# Get the first word that's not empty
|
144
|
+
parts = line.split()
|
145
|
+
if parts:
|
146
|
+
commands.append(parts[0])
|
147
|
+
return commands
|
148
|
+
|
149
|
+
|
150
|
+
def exception_handler(e: Exception) -> None:
|
151
|
+
"""Handle exceptions with proper logging"""
|
152
|
+
import sys
|
153
|
+
|
154
|
+
logger.error(f"{type(e).__name__}: {e}")
|
155
|
+
|
156
|
+
# Find file handlers for detailed logs
|
157
|
+
file_handlers = [handler for handler in logger.handlers if isinstance(handler, logging.FileHandler)]
|
158
|
+
|
159
|
+
if file_handlers:
|
160
|
+
filenames = [handler.baseFilename for handler in file_handlers]
|
161
|
+
logger.error(f"See {', '.join(filenames)} for details")
|
162
|
+
|
163
|
+
from rich.console import Console
|
164
|
+
|
165
|
+
console = Console(color_system=None)
|
166
|
+
with console.capture() as capture:
|
167
|
+
console.print_exception(show_locals=False)
|
168
|
+
logger.debug(capture.get())
|
169
|
+
|
170
|
+
sys.exit(1)
|
@@ -0,0 +1,148 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""aawscd_probwrite
|
3
|
+
|
4
|
+
usage: aawscd_probwrite [-h] [-m MACHINE] [-f FRAMES] FILE
|
5
|
+
|
6
|
+
options:
|
7
|
+
-h, --help
|
8
|
+
-m MACHINE, --machine MACHINE IP address of aawscd platform. [default: localhost].
|
9
|
+
-f FRAMES, --frames FRAMES Number of frames to capture. [default: 10].
|
10
|
+
|
11
|
+
aawscd_probwrite connects to an Aaware platform running aawscd and writes the sound classification
|
12
|
+
probability output to an HDF5 file.
|
13
|
+
|
14
|
+
"""
|
15
|
+
|
16
|
+
from threading import Condition
|
17
|
+
|
18
|
+
import numpy as np
|
19
|
+
|
20
|
+
from sonusai.utils.parallel import track
|
21
|
+
|
22
|
+
CLIENT: str = "aawscd_probwrite"
|
23
|
+
TOPIC: str = "aawscd/sc/prob"
|
24
|
+
DONE: Condition = Condition()
|
25
|
+
FRAMES: int = 10
|
26
|
+
FRAME_COUNT: int = 0
|
27
|
+
DATA: np.ndarray | None = None
|
28
|
+
PROGRESS: track | None = None
|
29
|
+
|
30
|
+
|
31
|
+
def shutdown(_signum, _frame) -> None:
|
32
|
+
global DONE
|
33
|
+
with DONE:
|
34
|
+
DONE.notify()
|
35
|
+
|
36
|
+
|
37
|
+
def unpack_prob_entry(entry: int) -> tuple[int, int, int]:
|
38
|
+
"""Decode the packed probability data: [ 16-bit label | 8-bit zone | 8-bit probability ]."""
|
39
|
+
data = np.array(entry, dtype=np.uint32)
|
40
|
+
label = np.right_shift(data, 16)
|
41
|
+
zone = np.bitwise_and(np.right_shift(data, 8), 0xFF)
|
42
|
+
value = np.bitwise_and(entry, 0xFF)
|
43
|
+
return int(zone), int(label), int(value)
|
44
|
+
|
45
|
+
|
46
|
+
def parse_prob(payload: list) -> np.ndarray:
|
47
|
+
"""Parse MQTT probability payload from aawscd.
|
48
|
+
|
49
|
+
The 'aawscd/sc/prob' payload is a list of uint32. Each item contains packed data and is
|
50
|
+
unpacked/decoded and inserted into an array.
|
51
|
+
"""
|
52
|
+
|
53
|
+
# First pass: get the zones and labels to determine the size of the array
|
54
|
+
zones = set()
|
55
|
+
labels = set()
|
56
|
+
for entry in payload:
|
57
|
+
zone, label, _ = unpack_prob_entry(entry)
|
58
|
+
zones.add(zone)
|
59
|
+
labels.add(label)
|
60
|
+
|
61
|
+
# Create the array
|
62
|
+
prob = np.zeros((len(list(labels)), len(list(zones))), dtype=np.uint8)
|
63
|
+
|
64
|
+
# Second pass: fill in the array probability values.
|
65
|
+
for entry in payload:
|
66
|
+
zone, label, value = unpack_prob_entry(entry)
|
67
|
+
prob[label, zone] = value
|
68
|
+
|
69
|
+
# Return the array
|
70
|
+
return prob
|
71
|
+
|
72
|
+
|
73
|
+
def on_message(_client, _userdata, message):
|
74
|
+
import paho.mqtt.client as mqtt
|
75
|
+
import yaml
|
76
|
+
|
77
|
+
global TOPIC
|
78
|
+
if mqtt.topic_matches_sub(TOPIC, message.topic):
|
79
|
+
payload = yaml.safe_load(str(message.payload.decode("utf-8")))
|
80
|
+
prob = parse_prob(payload["prob"])
|
81
|
+
|
82
|
+
global DATA
|
83
|
+
global FRAMES
|
84
|
+
if DATA is None:
|
85
|
+
DATA = np.zeros((FRAMES, prob.shape[0], prob.shape[1]), dtype=prob.dtype)
|
86
|
+
|
87
|
+
global FRAME_COUNT
|
88
|
+
DATA[FRAME_COUNT] = prob
|
89
|
+
FRAME_COUNT += 1
|
90
|
+
|
91
|
+
global PROGRESS
|
92
|
+
PROGRESS.update() # pyright: ignore [reportOptionalMemberAccess]
|
93
|
+
|
94
|
+
if FRAME_COUNT == FRAMES:
|
95
|
+
global DONE
|
96
|
+
with DONE:
|
97
|
+
DONE.notify()
|
98
|
+
|
99
|
+
|
100
|
+
def main() -> None:
|
101
|
+
from docopt import docopt
|
102
|
+
|
103
|
+
args = docopt(__doc__, version="1.0.0", options_first=True)
|
104
|
+
|
105
|
+
import signal
|
106
|
+
|
107
|
+
import h5py
|
108
|
+
import paho.mqtt.client as mqtt
|
109
|
+
|
110
|
+
machine = args["--machine"]
|
111
|
+
|
112
|
+
global FRAMES
|
113
|
+
FRAMES = int(args["--frames"])
|
114
|
+
|
115
|
+
file = args["FILE"]
|
116
|
+
|
117
|
+
signal.signal(signal.SIGINT, shutdown)
|
118
|
+
signal.signal(signal.SIGTERM, shutdown)
|
119
|
+
|
120
|
+
global CLIENT
|
121
|
+
client = mqtt.Client(client_id=CLIENT)
|
122
|
+
client.on_message = on_message
|
123
|
+
client.connect(host=machine)
|
124
|
+
client.loop_start()
|
125
|
+
global TOPIC
|
126
|
+
client.subscribe(topic=TOPIC)
|
127
|
+
|
128
|
+
global PROGRESS
|
129
|
+
PROGRESS = track(total=FRAMES, desc=file)
|
130
|
+
|
131
|
+
with DONE:
|
132
|
+
DONE.wait()
|
133
|
+
|
134
|
+
PROGRESS.close()
|
135
|
+
|
136
|
+
client.unsubscribe(topic=TOPIC)
|
137
|
+
client.loop_stop()
|
138
|
+
client.disconnect()
|
139
|
+
|
140
|
+
global DATA
|
141
|
+
with h5py.File(file, "w") as f:
|
142
|
+
f.create_dataset(name="prob", data=DATA)
|
143
|
+
|
144
|
+
print(f"Wrote {file}")
|
145
|
+
|
146
|
+
|
147
|
+
if __name__ == "__main__":
|
148
|
+
main()
|