amods 0.1.0__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 (52) hide show
  1. amods-0.1.0/LICENSE +21 -0
  2. amods-0.1.0/PKG-INFO +351 -0
  3. amods-0.1.0/README.md +297 -0
  4. amods-0.1.0/pyproject.toml +55 -0
  5. amods-0.1.0/setup.cfg +4 -0
  6. amods-0.1.0/src/amods/__init__.py +7 -0
  7. amods-0.1.0/src/amods/audio.py +51 -0
  8. amods-0.1.0/src/amods/cli.py +55 -0
  9. amods-0.1.0/src/amods/config.py +94 -0
  10. amods-0.1.0/src/amods/configs/__init__.py +0 -0
  11. amods-0.1.0/src/amods/configs/concealer/default.yaml +13 -0
  12. amods-0.1.0/src/amods/configs/forecaster/default.yaml +1 -0
  13. amods-0.1.0/src/amods/configs/stream/48khz_50msbuff_dock.yaml +13 -0
  14. amods-0.1.0/src/amods/configs/stream/48khz_50msbuff_nodock.yaml +13 -0
  15. amods-0.1.0/src/amods/configs/stream/default.yaml +13 -0
  16. amods-0.1.0/src/amods/configs/vad/default_concealer.yaml +2 -0
  17. amods-0.1.0/src/amods/configs/vad/default_source.yaml +2 -0
  18. amods-0.1.0/src/amods/configs/vad/none_concealer.yaml +1 -0
  19. amods-0.1.0/src/amods/configs/vad/none_source.yaml +1 -0
  20. amods-0.1.0/src/amods/configs/vad/silero_concealer.yaml +2 -0
  21. amods-0.1.0/src/amods/configs/vad/silero_source.yaml +2 -0
  22. amods-0.1.0/src/amods/configs/vad/ten_concealer.yaml +2 -0
  23. amods-0.1.0/src/amods/configs/vad/ten_source.yaml +2 -0
  24. amods-0.1.0/src/amods/configs/vad/webrtc_concealer.yaml +3 -0
  25. amods-0.1.0/src/amods/configs/vad/webrtc_source.yaml +3 -0
  26. amods-0.1.0/src/amods/gui.py +468 -0
  27. amods-0.1.0/src/amods/latency.py +133 -0
  28. amods-0.1.0/src/amods/models/__init__.py +0 -0
  29. amods-0.1.0/src/amods/models/adversary.py +11 -0
  30. amods-0.1.0/src/amods/models/concealer/__init__.py +14 -0
  31. amods-0.1.0/src/amods/models/concealer/base.py +346 -0
  32. amods-0.1.0/src/amods/models/concealer/granspeechmask.py +218 -0
  33. amods-0.1.0/src/amods/models/denoiser.py +28 -0
  34. amods-0.1.0/src/amods/models/forecaster.py +17 -0
  35. amods-0.1.0/src/amods/models/vad.py +245 -0
  36. amods-0.1.0/src/amods/stream.py +403 -0
  37. amods-0.1.0/src/amods.egg-info/PKG-INFO +351 -0
  38. amods-0.1.0/src/amods.egg-info/SOURCES.txt +50 -0
  39. amods-0.1.0/src/amods.egg-info/dependency_links.txt +1 -0
  40. amods-0.1.0/src/amods.egg-info/entry_points.txt +4 -0
  41. amods-0.1.0/src/amods.egg-info/requires.txt +17 -0
  42. amods-0.1.0/src/amods.egg-info/top_level.txt +1 -0
  43. amods-0.1.0/tests/test_audio.py +58 -0
  44. amods-0.1.0/tests/test_cli.py +69 -0
  45. amods-0.1.0/tests/test_config.py +84 -0
  46. amods-0.1.0/tests/test_models_adversary.py +28 -0
  47. amods-0.1.0/tests/test_models_concealer.py +123 -0
  48. amods-0.1.0/tests/test_models_concealer_registry.py +60 -0
  49. amods-0.1.0/tests/test_models_denoiser.py +16 -0
  50. amods-0.1.0/tests/test_models_forecaster.py +20 -0
  51. amods-0.1.0/tests/test_models_vad.py +74 -0
  52. amods-0.1.0/tests/test_stream.py +203 -0
amods-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Modan Tailleur
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
amods-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,351 @@
1
+ Metadata-Version: 2.4
2
+ Name: amods
3
+ Version: 0.1.0
4
+ Summary: Adaptive Masking of Distracting Speech (AMODS)
5
+ Author: Modan Tailleur, Aine Drelingyte, Mathieu Lagrange, Romain Serizel
6
+ Maintainer-email: Modan Tailleur <modan.tailleur@gmail.com>
7
+ License: MIT License
8
+
9
+ Copyright (c) 2026 Modan Tailleur
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
29
+ Classifier: License :: OSI Approved :: MIT License
30
+ Classifier: Programming Language :: Python :: 3
31
+ Classifier: Operating System :: OS Independent
32
+ Classifier: Intended Audience :: Science/Research
33
+ Classifier: Topic :: Multimedia :: Sound/Audio
34
+ Classifier: Topic :: Scientific/Engineering
35
+ Requires-Python: >=3.9
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: numpy>=1.24
39
+ Requires-Dist: scipy>=1.10
40
+ Requires-Dist: librosa>=0.10
41
+ Requires-Dist: soundfile>=0.12
42
+ Requires-Dist: sounddevice>=0.4
43
+ Requires-Dist: PyYAML>=6.0
44
+ Requires-Dist: torch>=2.0
45
+ Requires-Dist: torchaudio>=2.0
46
+ Requires-Dist: denoiser>=0.1.5
47
+ Requires-Dist: ten_vad>=1.0.6
48
+ Requires-Dist: silero-vad>=5.0
49
+ Requires-Dist: webrtcvad>=2.0
50
+ Provides-Extra: gui
51
+ Provides-Extra: dev
52
+ Requires-Dist: pytest>=7.0; extra == "dev"
53
+ Dynamic: license-file
54
+
55
+ # AMODS — Adaptive Masking of Distracting Speech
56
+
57
+ A framework for real-time algorithms that make nearby speech unintelligible to
58
+ listeners.
59
+
60
+ ## Background
61
+
62
+ Intelligible background speech is one of the most disruptive sources of noise in shared and open-plan offices. It can increase stress and annoyance, disrupt social interactions, and reduce work performance. Staying focused around distracting conversations is a common concern when working in a shared space.
63
+
64
+ Two approaches are commonly used to address this problem. Sound-masking systems broadcast masking noise throughout the room, but because they do not target periods of speech activity, they need to operate at relatively high levels to be effective. Active noise cancellation (ANC), typically deployed on a per-worker basis through headphones, works well against steady and predictable sounds but is less effective against speech, whose spectral and temporal content varies rapidly.
65
+
66
+ AMODS explores a different approach: adapting the masking to speech activity, both by activating it when speech occurs and by tailoring it to the characteristics of the speech being masked. This approach is complementary to ANC on headphones. We call this general approach *speech concealing*, and the masking sound it produces a *speech concealer*.
67
+
68
+ AMODS is designed to support the development and comparison of different speech-concealing algorithms. See [Using your own concealment algorithm](#using-your-own-concealment-algorithm) to learn how to integrate a new algorithm.
69
+
70
+ The currently included concealment algorithm is **GranSpeechMask** ("granular speech
71
+ masking"). It continuously records short clips of the speaker's recent voice and,
72
+ once a sufficient number have accumulated, replays the clip whose spectral content
73
+ most closely matches the current speech. Using the speaker's own voice as the masking
74
+ signal keeps it spectrally close to the speech being covered, allowing it to operate
75
+ without reaching a distracting level.
76
+
77
+ ## Install
78
+
79
+ In a Python 3.9+ environment:
80
+
81
+ ```
82
+ pip install amods
83
+ ```
84
+
85
+ This pulls in all runtime dependencies (numpy, scipy, librosa, soundfile, sounddevice,
86
+ PyYAML, torch, torchaudio, denoiser, ten_vad, silero-vad, webrtcvad). If `ten_vad`
87
+ fails to install, you can force reinstall it with:
88
+
89
+ ```
90
+ pip install -U --force-reinstall -v git+https://github.com/TEN-framework/ten-vad.git
91
+ ```
92
+
93
+ To work on amods itself (e.g. to run the test suite), clone the repository and install
94
+ it in editable mode instead:
95
+
96
+ ```
97
+ git clone https://github.com/modantailleur/amods.git
98
+ cd amods
99
+ pip install -e ".[dev]"
100
+ pytest
101
+ ```
102
+
103
+ ## Real-Time Concealing (GUI)
104
+
105
+ The desktop UI provides the primary interface for running the demonstration. It allows
106
+ selection of the microphone, speaker, VAD type and threshold, and denoiser, and displays
107
+ the current memory size:
108
+
109
+ ```
110
+ amods-gui
111
+ ```
112
+
113
+ Settings selected in the UI are saved to configuration files
114
+ that are also read by the `amods` CLI (see [Configuration](#configuration)).
115
+
116
+ The GUI needs Tkinter, which ships with Python on most platforms; on some minimal
117
+ Linux installs it needs a separate system package, e.g. `sudo apt install python3-tk`.
118
+
119
+ ## Real-Time Concealing (CLI)
120
+
121
+ To conceal speech in real time from the command line, use headphones and run:
122
+
123
+ ```
124
+ amods
125
+ ```
126
+
127
+ By default, the CLI uses the package's bundled default configurations (see
128
+ [Configuration](#configuration)). To use a different configuration, specify it for any
129
+ of the components described in the [Configuration](#configuration) section:
130
+
131
+ ```
132
+ amods --concealerconfig yourpath/yourconfig.yaml
133
+ ```
134
+
135
+ The original stream, the concealer output, and the mix between original and concealer
136
+ will be saved to:
137
+
138
+ - `./output/stream_original.wav`
139
+ - `./output/stream_concealer.wav`
140
+ - `./output/stream_mix.wav`
141
+
142
+ ## Concealing a Pre-Recorded Audio (Real-Time Emulation)
143
+
144
+ Concealing a pre-recorded audio file provides a way to test the concealer with a
145
+ specified audio input, although this mode has no direct application purpose.
146
+
147
+ To do so, run:
148
+
149
+ ```
150
+ amods --indir mydir/to/audio/ --infile myaudio.wav
151
+ ```
152
+
153
+ The original audio, the concealer output, and the mix between original and concealer
154
+ will be saved to:
155
+
156
+ - `./output/myaudio_original.wav`
157
+ - `./output/myaudio_concealer.wav`
158
+ - `./output/myaudio_mix.wav`
159
+
160
+ ## Configuration
161
+
162
+ The configuration components are:
163
+
164
+ - `concealer` — the concealment algorithm to run and its parameters (e.g. which
165
+ `concealer_type` is registered under `amods.models.concealer`, how much audio its
166
+ memory holds, and denoising/timing settings). Select its configuration with
167
+ `--concealerconfig`.
168
+ - `stream` — real-time audio I/O settings, including sample rate, channel counts,
169
+ buffer duration, input/output devices, gains, and latency hints. Select its
170
+ configuration with `--streamconfig`.
171
+ - `vad` — voice activity detection settings, including the VAD backend and threshold. Used
172
+ in two places: as the "source" VAD, which determines when speech is present, and
173
+ internally by a concealer algorithm to filter its candidate clips. Select the source
174
+ VAD configuration with `--sourcevadconfig`; an algorithm's internal VAD is specified
175
+ in that algorithm's configuration, for example with `concealer_vad_config` for
176
+ `GranSpeechMask`.
177
+ - `forecaster` — short-term prediction of the next audio frame before it is passed to
178
+ the concealer (currently only `identity`, which passes the frame through unchanged).
179
+ Select its configuration with `--forecasterconfig`.
180
+
181
+ Each configuration option accepts either a configuration name or a path to a YAML
182
+ file. For example, `--concealerconfig default` refers to the `default` concealer
183
+ configuration, while `--concealerconfig path/to/config.yaml` refers to a specific file.
184
+
185
+ When a configuration name is provided, AMODS searches for the corresponding file in
186
+ the following order:
187
+
188
+ 1. `./configs/<component>/<name>.yaml`, relative to the current directory. This is
189
+ used when running from a repository checkout or another directory containing a
190
+ `configs/` folder.
191
+ 2. `~/.amods/configs/<component>/<name>.yaml`, where settings saved by the GUI are
192
+ stored and can be used regardless of the current directory.
193
+ 3. The package's bundled default configuration, which allows `amods` and `amods-gui`
194
+ to run immediately after `pip install` without a local `configs/` directory.
195
+
196
+ If the value already contains a `/`, it is treated as a path and used as provided.
197
+
198
+ ## Using amods as a library
199
+
200
+ ### Real-time, on your own mic/speaker
201
+
202
+ The following function blocks until Ctrl+C is pressed and then saves the
203
+ three WAV files, equivalent to running `amods` without `--infile`:
204
+
205
+ ```python
206
+ from amods.stream import run_stream_mode
207
+
208
+ run_stream_mode(
209
+ outdir="./output",
210
+ outprefix="session1",
211
+ concealerconfig="default",
212
+ streamconfig="default",
213
+ sourcevadconfig="default_source",
214
+ forecasterconfig="default",
215
+ )
216
+ ```
217
+
218
+ The bundled default stream configuration uses `device_in`/`device_out: null`, which
219
+ selects the system's default microphone and speaker. To target specific devices,
220
+ override the stream configuration with a dictionary (see below) using the indices
221
+ reported by `amods-latency --list-devices`.
222
+
223
+ For finer control, such as specifying a stop condition or processing audio as it arrives,
224
+ drive `Stream` with `sounddevice` directly. This is the approach used internally by
225
+ `run_stream_mode`:
226
+
227
+ ```python
228
+ import sounddevice as sd
229
+ from amods import Stream
230
+
231
+ stream = Stream("default_source", "default", "default", "default", is_stream=True)
232
+ stream.reset_state()
233
+ sc = stream.stream_config
234
+
235
+ with sd.InputStream(samplerate=sc["sr"], blocksize=stream.buffer_size, dtype=sc["dtype"],
236
+ channels=sc["channels_in"], device=sc["device_in"],
237
+ callback=stream.input_callback, latency=sc.get("input_latency", "low")), \
238
+ sd.OutputStream(samplerate=sc["sr"], dtype=sc["dtype"], channels=sc["channels_out"],
239
+ device=sc["device_out"], callback=stream.output_callback,
240
+ latency=sc.get("output_latency", "high")):
241
+ sd.sleep(10_000) # run for 10s; swap in your own stop condition
242
+
243
+ # stream.rec_original / rec_concealer / rec_mix now hold the recorded chunks,
244
+ # as they do after run_stream_mode; concatenate and write with soundfile as needed.
245
+ ```
246
+
247
+ ### A pre-recorded audio file
248
+
249
+ The following function reads a WAV file, processes it through the same
250
+ per-chunk pipeline as real-time mode, and writes the three output WAVs, equivalent to
251
+ `amods --infile ...`:
252
+
253
+ ```python
254
+ from amods.stream import run_file_mode
255
+
256
+ run_file_mode(
257
+ indir="./audios/",
258
+ infile="voice_sample.wav",
259
+ outdir="./output",
260
+ outprefix="", # empty -> uses the input filename as the prefix
261
+ concealerconfig="default",
262
+ streamconfig="default",
263
+ sourcevadconfig="default_source",
264
+ forecasterconfig="default",
265
+ )
266
+ ```
267
+
268
+ If the audio is already available as a NumPy array in memory (mono `float32`, at the
269
+ stream configuration's sample rate), rather than as a file on disk, pass it to
270
+ `Stream.callback` in fixed-size blocks. This is the approach used internally by
271
+ `run_file_mode`:
272
+
273
+ ```python
274
+ import numpy as np
275
+ from amods import Stream
276
+
277
+ stream = Stream("default_source", "default", "default", "default", is_stream=False)
278
+ stream.reset_state()
279
+
280
+ y = my_audio_array.astype(np.float32)
281
+ n = len(y)
282
+ n_blocks = int(np.ceil(n / stream.buffer_size))
283
+ y = np.pad(y, (0, n_blocks * stream.buffer_size - n))
284
+
285
+ for b in range(n_blocks):
286
+ block = y[b * stream.buffer_size : (b + 1) * stream.buffer_size]
287
+ indata = block.reshape(-1, 1)
288
+ outdata = np.zeros((stream.buffer_size, 2), dtype=np.float32)
289
+ stream.callback(indata, outdata, stream.buffer_size, None, None)
290
+
291
+ concealed = np.concatenate(stream.rec_concealer, axis=0)[:n] # concealer output only
292
+ mix = np.concatenate(stream.rec_mix, axis=0)[:n] # original + concealer, mixed
293
+ ```
294
+
295
+ ### Customizing configs from code
296
+
297
+ Each `*_config_name` argument accepts either a name/path (resolved as described above)
298
+ **or an already-built dictionary**, which is used directly. This permits YAML to be
299
+ omitted entirely, or allows selected fields to be overridden in a named configuration:
300
+
301
+ ```python
302
+ from amods.config import load_config
303
+
304
+ config = load_config("concealer", "default")
305
+ config["denoise"] = False # tweak just this one field
306
+ stream = Stream(source_vad_config_name="default_source",
307
+ concealer_config_name=config,
308
+ stream_config_name="default",
309
+ forecaster_config_name="default",
310
+ is_stream=False)
311
+ ```
312
+
313
+ The same applies one level down: a concealer configuration's `concealer_vad_config`
314
+ field can also be a name/path or an inline dictionary. If only one component, such as a
315
+ VAD, is required rather than the complete `Stream`, the lower-level functions
316
+ (`amods.models.vad.select_vad_model`, `amods.models.concealer.select_concealer_model`,
317
+ etc.) already take plain config dicts directly.
318
+
319
+ ### Using your own concealment algorithm
320
+
321
+ `concealer_type` in a concealer config selects which registered algorithm runs (e.g.
322
+ `granspeechmask`). To add a custom algorithm, implement `amods.models.concealer.ConcealerModel`
323
+ and decorate it with `@register_concealer("your_type_name")` — see the module docstring
324
+ in `amods/models/concealer/base.py` for the exact contract. No changes to `Stream`, the
325
+ CLI, or the examples above are required; reference the `type_name` in a concealer
326
+ configuration.
327
+
328
+ ## Testing mic/speaker devices
329
+
330
+ The `amods-latency` tool can be used to identify available audio devices and verify a
331
+ microphone/speaker pair before starting a real-time stream. First, list the available
332
+ devices and their indices:
333
+
334
+ ```
335
+ amods-latency --list-devices
336
+ ```
337
+
338
+ Then provide the input and output device indices to test a specific pair. The tool
339
+ verifies that the pair is functioning and reports the PortAudio-reported input/output
340
+ latency:
341
+
342
+ ```
343
+ amods-latency --device-in 15 --device-out 11
344
+ ```
345
+
346
+ ## Authors
347
+
348
+ - Modan Tailleur
349
+ - Aine Drelingyte
350
+ - Mathieu Lagrange
351
+ - Romain Serizel
amods-0.1.0/README.md ADDED
@@ -0,0 +1,297 @@
1
+ # AMODS — Adaptive Masking of Distracting Speech
2
+
3
+ A framework for real-time algorithms that make nearby speech unintelligible to
4
+ listeners.
5
+
6
+ ## Background
7
+
8
+ Intelligible background speech is one of the most disruptive sources of noise in shared and open-plan offices. It can increase stress and annoyance, disrupt social interactions, and reduce work performance. Staying focused around distracting conversations is a common concern when working in a shared space.
9
+
10
+ Two approaches are commonly used to address this problem. Sound-masking systems broadcast masking noise throughout the room, but because they do not target periods of speech activity, they need to operate at relatively high levels to be effective. Active noise cancellation (ANC), typically deployed on a per-worker basis through headphones, works well against steady and predictable sounds but is less effective against speech, whose spectral and temporal content varies rapidly.
11
+
12
+ AMODS explores a different approach: adapting the masking to speech activity, both by activating it when speech occurs and by tailoring it to the characteristics of the speech being masked. This approach is complementary to ANC on headphones. We call this general approach *speech concealing*, and the masking sound it produces a *speech concealer*.
13
+
14
+ AMODS is designed to support the development and comparison of different speech-concealing algorithms. See [Using your own concealment algorithm](#using-your-own-concealment-algorithm) to learn how to integrate a new algorithm.
15
+
16
+ The currently included concealment algorithm is **GranSpeechMask** ("granular speech
17
+ masking"). It continuously records short clips of the speaker's recent voice and,
18
+ once a sufficient number have accumulated, replays the clip whose spectral content
19
+ most closely matches the current speech. Using the speaker's own voice as the masking
20
+ signal keeps it spectrally close to the speech being covered, allowing it to operate
21
+ without reaching a distracting level.
22
+
23
+ ## Install
24
+
25
+ In a Python 3.9+ environment:
26
+
27
+ ```
28
+ pip install amods
29
+ ```
30
+
31
+ This pulls in all runtime dependencies (numpy, scipy, librosa, soundfile, sounddevice,
32
+ PyYAML, torch, torchaudio, denoiser, ten_vad, silero-vad, webrtcvad). If `ten_vad`
33
+ fails to install, you can force reinstall it with:
34
+
35
+ ```
36
+ pip install -U --force-reinstall -v git+https://github.com/TEN-framework/ten-vad.git
37
+ ```
38
+
39
+ To work on amods itself (e.g. to run the test suite), clone the repository and install
40
+ it in editable mode instead:
41
+
42
+ ```
43
+ git clone https://github.com/modantailleur/amods.git
44
+ cd amods
45
+ pip install -e ".[dev]"
46
+ pytest
47
+ ```
48
+
49
+ ## Real-Time Concealing (GUI)
50
+
51
+ The desktop UI provides the primary interface for running the demonstration. It allows
52
+ selection of the microphone, speaker, VAD type and threshold, and denoiser, and displays
53
+ the current memory size:
54
+
55
+ ```
56
+ amods-gui
57
+ ```
58
+
59
+ Settings selected in the UI are saved to configuration files
60
+ that are also read by the `amods` CLI (see [Configuration](#configuration)).
61
+
62
+ The GUI needs Tkinter, which ships with Python on most platforms; on some minimal
63
+ Linux installs it needs a separate system package, e.g. `sudo apt install python3-tk`.
64
+
65
+ ## Real-Time Concealing (CLI)
66
+
67
+ To conceal speech in real time from the command line, use headphones and run:
68
+
69
+ ```
70
+ amods
71
+ ```
72
+
73
+ By default, the CLI uses the package's bundled default configurations (see
74
+ [Configuration](#configuration)). To use a different configuration, specify it for any
75
+ of the components described in the [Configuration](#configuration) section:
76
+
77
+ ```
78
+ amods --concealerconfig yourpath/yourconfig.yaml
79
+ ```
80
+
81
+ The original stream, the concealer output, and the mix between original and concealer
82
+ will be saved to:
83
+
84
+ - `./output/stream_original.wav`
85
+ - `./output/stream_concealer.wav`
86
+ - `./output/stream_mix.wav`
87
+
88
+ ## Concealing a Pre-Recorded Audio (Real-Time Emulation)
89
+
90
+ Concealing a pre-recorded audio file provides a way to test the concealer with a
91
+ specified audio input, although this mode has no direct application purpose.
92
+
93
+ To do so, run:
94
+
95
+ ```
96
+ amods --indir mydir/to/audio/ --infile myaudio.wav
97
+ ```
98
+
99
+ The original audio, the concealer output, and the mix between original and concealer
100
+ will be saved to:
101
+
102
+ - `./output/myaudio_original.wav`
103
+ - `./output/myaudio_concealer.wav`
104
+ - `./output/myaudio_mix.wav`
105
+
106
+ ## Configuration
107
+
108
+ The configuration components are:
109
+
110
+ - `concealer` — the concealment algorithm to run and its parameters (e.g. which
111
+ `concealer_type` is registered under `amods.models.concealer`, how much audio its
112
+ memory holds, and denoising/timing settings). Select its configuration with
113
+ `--concealerconfig`.
114
+ - `stream` — real-time audio I/O settings, including sample rate, channel counts,
115
+ buffer duration, input/output devices, gains, and latency hints. Select its
116
+ configuration with `--streamconfig`.
117
+ - `vad` — voice activity detection settings, including the VAD backend and threshold. Used
118
+ in two places: as the "source" VAD, which determines when speech is present, and
119
+ internally by a concealer algorithm to filter its candidate clips. Select the source
120
+ VAD configuration with `--sourcevadconfig`; an algorithm's internal VAD is specified
121
+ in that algorithm's configuration, for example with `concealer_vad_config` for
122
+ `GranSpeechMask`.
123
+ - `forecaster` — short-term prediction of the next audio frame before it is passed to
124
+ the concealer (currently only `identity`, which passes the frame through unchanged).
125
+ Select its configuration with `--forecasterconfig`.
126
+
127
+ Each configuration option accepts either a configuration name or a path to a YAML
128
+ file. For example, `--concealerconfig default` refers to the `default` concealer
129
+ configuration, while `--concealerconfig path/to/config.yaml` refers to a specific file.
130
+
131
+ When a configuration name is provided, AMODS searches for the corresponding file in
132
+ the following order:
133
+
134
+ 1. `./configs/<component>/<name>.yaml`, relative to the current directory. This is
135
+ used when running from a repository checkout or another directory containing a
136
+ `configs/` folder.
137
+ 2. `~/.amods/configs/<component>/<name>.yaml`, where settings saved by the GUI are
138
+ stored and can be used regardless of the current directory.
139
+ 3. The package's bundled default configuration, which allows `amods` and `amods-gui`
140
+ to run immediately after `pip install` without a local `configs/` directory.
141
+
142
+ If the value already contains a `/`, it is treated as a path and used as provided.
143
+
144
+ ## Using amods as a library
145
+
146
+ ### Real-time, on your own mic/speaker
147
+
148
+ The following function blocks until Ctrl+C is pressed and then saves the
149
+ three WAV files, equivalent to running `amods` without `--infile`:
150
+
151
+ ```python
152
+ from amods.stream import run_stream_mode
153
+
154
+ run_stream_mode(
155
+ outdir="./output",
156
+ outprefix="session1",
157
+ concealerconfig="default",
158
+ streamconfig="default",
159
+ sourcevadconfig="default_source",
160
+ forecasterconfig="default",
161
+ )
162
+ ```
163
+
164
+ The bundled default stream configuration uses `device_in`/`device_out: null`, which
165
+ selects the system's default microphone and speaker. To target specific devices,
166
+ override the stream configuration with a dictionary (see below) using the indices
167
+ reported by `amods-latency --list-devices`.
168
+
169
+ For finer control, such as specifying a stop condition or processing audio as it arrives,
170
+ drive `Stream` with `sounddevice` directly. This is the approach used internally by
171
+ `run_stream_mode`:
172
+
173
+ ```python
174
+ import sounddevice as sd
175
+ from amods import Stream
176
+
177
+ stream = Stream("default_source", "default", "default", "default", is_stream=True)
178
+ stream.reset_state()
179
+ sc = stream.stream_config
180
+
181
+ with sd.InputStream(samplerate=sc["sr"], blocksize=stream.buffer_size, dtype=sc["dtype"],
182
+ channels=sc["channels_in"], device=sc["device_in"],
183
+ callback=stream.input_callback, latency=sc.get("input_latency", "low")), \
184
+ sd.OutputStream(samplerate=sc["sr"], dtype=sc["dtype"], channels=sc["channels_out"],
185
+ device=sc["device_out"], callback=stream.output_callback,
186
+ latency=sc.get("output_latency", "high")):
187
+ sd.sleep(10_000) # run for 10s; swap in your own stop condition
188
+
189
+ # stream.rec_original / rec_concealer / rec_mix now hold the recorded chunks,
190
+ # as they do after run_stream_mode; concatenate and write with soundfile as needed.
191
+ ```
192
+
193
+ ### A pre-recorded audio file
194
+
195
+ The following function reads a WAV file, processes it through the same
196
+ per-chunk pipeline as real-time mode, and writes the three output WAVs, equivalent to
197
+ `amods --infile ...`:
198
+
199
+ ```python
200
+ from amods.stream import run_file_mode
201
+
202
+ run_file_mode(
203
+ indir="./audios/",
204
+ infile="voice_sample.wav",
205
+ outdir="./output",
206
+ outprefix="", # empty -> uses the input filename as the prefix
207
+ concealerconfig="default",
208
+ streamconfig="default",
209
+ sourcevadconfig="default_source",
210
+ forecasterconfig="default",
211
+ )
212
+ ```
213
+
214
+ If the audio is already available as a NumPy array in memory (mono `float32`, at the
215
+ stream configuration's sample rate), rather than as a file on disk, pass it to
216
+ `Stream.callback` in fixed-size blocks. This is the approach used internally by
217
+ `run_file_mode`:
218
+
219
+ ```python
220
+ import numpy as np
221
+ from amods import Stream
222
+
223
+ stream = Stream("default_source", "default", "default", "default", is_stream=False)
224
+ stream.reset_state()
225
+
226
+ y = my_audio_array.astype(np.float32)
227
+ n = len(y)
228
+ n_blocks = int(np.ceil(n / stream.buffer_size))
229
+ y = np.pad(y, (0, n_blocks * stream.buffer_size - n))
230
+
231
+ for b in range(n_blocks):
232
+ block = y[b * stream.buffer_size : (b + 1) * stream.buffer_size]
233
+ indata = block.reshape(-1, 1)
234
+ outdata = np.zeros((stream.buffer_size, 2), dtype=np.float32)
235
+ stream.callback(indata, outdata, stream.buffer_size, None, None)
236
+
237
+ concealed = np.concatenate(stream.rec_concealer, axis=0)[:n] # concealer output only
238
+ mix = np.concatenate(stream.rec_mix, axis=0)[:n] # original + concealer, mixed
239
+ ```
240
+
241
+ ### Customizing configs from code
242
+
243
+ Each `*_config_name` argument accepts either a name/path (resolved as described above)
244
+ **or an already-built dictionary**, which is used directly. This permits YAML to be
245
+ omitted entirely, or allows selected fields to be overridden in a named configuration:
246
+
247
+ ```python
248
+ from amods.config import load_config
249
+
250
+ config = load_config("concealer", "default")
251
+ config["denoise"] = False # tweak just this one field
252
+ stream = Stream(source_vad_config_name="default_source",
253
+ concealer_config_name=config,
254
+ stream_config_name="default",
255
+ forecaster_config_name="default",
256
+ is_stream=False)
257
+ ```
258
+
259
+ The same applies one level down: a concealer configuration's `concealer_vad_config`
260
+ field can also be a name/path or an inline dictionary. If only one component, such as a
261
+ VAD, is required rather than the complete `Stream`, the lower-level functions
262
+ (`amods.models.vad.select_vad_model`, `amods.models.concealer.select_concealer_model`,
263
+ etc.) already take plain config dicts directly.
264
+
265
+ ### Using your own concealment algorithm
266
+
267
+ `concealer_type` in a concealer config selects which registered algorithm runs (e.g.
268
+ `granspeechmask`). To add a custom algorithm, implement `amods.models.concealer.ConcealerModel`
269
+ and decorate it with `@register_concealer("your_type_name")` — see the module docstring
270
+ in `amods/models/concealer/base.py` for the exact contract. No changes to `Stream`, the
271
+ CLI, or the examples above are required; reference the `type_name` in a concealer
272
+ configuration.
273
+
274
+ ## Testing mic/speaker devices
275
+
276
+ The `amods-latency` tool can be used to identify available audio devices and verify a
277
+ microphone/speaker pair before starting a real-time stream. First, list the available
278
+ devices and their indices:
279
+
280
+ ```
281
+ amods-latency --list-devices
282
+ ```
283
+
284
+ Then provide the input and output device indices to test a specific pair. The tool
285
+ verifies that the pair is functioning and reports the PortAudio-reported input/output
286
+ latency:
287
+
288
+ ```
289
+ amods-latency --device-in 15 --device-out 11
290
+ ```
291
+
292
+ ## Authors
293
+
294
+ - Modan Tailleur
295
+ - Aine Drelingyte
296
+ - Mathieu Lagrange
297
+ - Romain Serizel