modusa 0.4.29__py3-none-any.whl → 0.4.31__py3-none-any.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.
- modusa/__init__.py +12 -8
- modusa/tools/__init__.py +11 -3
- modusa/tools/ann_saver.py +30 -0
- modusa/tools/audio_recorder.py +0 -1
- modusa/tools/audio_stft.py +72 -0
- modusa/tools/youtube_downloader.py +1 -4
- {modusa-0.4.29.dist-info → modusa-0.4.31.dist-info}/METADATA +2 -2
- modusa-0.4.31.dist-info/RECORD +22 -0
- pyproject.toml +2 -2
- modusa/config.py +0 -18
- modusa/decorators.py +0 -176
- modusa/devtools/generate_docs_source.py +0 -92
- modusa/devtools/generate_template.py +0 -144
- modusa/devtools/list_authors.py +0 -2
- modusa/devtools/list_plugins.py +0 -60
- modusa/devtools/main.py +0 -45
- modusa/devtools/templates/generator.py +0 -24
- modusa/devtools/templates/io.py +0 -24
- modusa/devtools/templates/model.py +0 -47
- modusa/devtools/templates/plugin.py +0 -41
- modusa/devtools/templates/test.py +0 -10
- modusa/devtools/templates/tool.py +0 -24
- modusa/generators/__init__.py +0 -13
- modusa/generators/audio.py +0 -188
- modusa/generators/audio_waveforms.py +0 -236
- modusa/generators/base.py +0 -29
- modusa/generators/ftds.py +0 -298
- modusa/generators/s1d.py +0 -270
- modusa/generators/s2d.py +0 -300
- modusa/generators/s_ax.py +0 -102
- modusa/generators/t_ax.py +0 -64
- modusa/generators/tds.py +0 -267
- modusa/models/__init__.py +0 -14
- modusa/models/audio.py +0 -90
- modusa/models/base.py +0 -70
- modusa/models/data.py +0 -457
- modusa/models/ftds.py +0 -584
- modusa/models/s1d.py +0 -578
- modusa/models/s2d.py +0 -619
- modusa/models/s_ax.py +0 -448
- modusa/models/t_ax.py +0 -335
- modusa/models/tds.py +0 -465
- modusa/plugins/__init__.py +0 -3
- modusa/plugins/base.py +0 -100
- modusa/tools/_plotter_old.py +0 -629
- modusa/tools/audio_saver.py +0 -30
- modusa/tools/base.py +0 -43
- modusa/tools/math_ops.py +0 -335
- modusa/utils/__init__.py +0 -1
- modusa/utils/config.py +0 -25
- modusa/utils/excp.py +0 -49
- modusa/utils/logger.py +0 -18
- modusa/utils/np_func_cat.py +0 -44
- modusa/utils/plot.py +0 -142
- modusa-0.4.29.dist-info/RECORD +0 -65
- {modusa-0.4.29.dist-info → modusa-0.4.31.dist-info}/WHEEL +0 -0
- {modusa-0.4.29.dist-info → modusa-0.4.31.dist-info}/entry_points.txt +0 -0
- {modusa-0.4.29.dist-info → modusa-0.4.31.dist-info}/licenses/LICENSE.md +0 -0
modusa/models/data.py
DELETED
@@ -1,457 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
|
3
|
-
|
4
|
-
from modusa import excp
|
5
|
-
from modusa.decorators import immutable_property, validate_args_type
|
6
|
-
from .base import ModusaSignalData
|
7
|
-
from typing import Self, Any
|
8
|
-
import numpy as np
|
9
|
-
|
10
|
-
class Data(ModusaSignalData):
|
11
|
-
"""
|
12
|
-
Space to represent any modusa signal's data.
|
13
|
-
|
14
|
-
Parameters
|
15
|
-
----------
|
16
|
-
values: array-like
|
17
|
-
Data values as an array.
|
18
|
-
label: str
|
19
|
-
Label for the data.
|
20
|
-
|
21
|
-
Note
|
22
|
-
----
|
23
|
-
- This is only for developers reference.
|
24
|
-
- As a user of the library, you will never instantiate this class.
|
25
|
-
- It is used internally and users will be provided APIs to interact with this class if necessary.
|
26
|
-
"""
|
27
|
-
|
28
|
-
#--------Meta Information----------
|
29
|
-
_name = "Data"
|
30
|
-
_nickname = "data" # This is to be used in repr/str methods
|
31
|
-
_description = "Space to represent any signal's data."
|
32
|
-
_author_name = "Ankit Anand"
|
33
|
-
_author_email = "ankit0.anand0@gmail.com"
|
34
|
-
_created_at = "2025-07-27"
|
35
|
-
#----------------------------------
|
36
|
-
|
37
|
-
def __init__(self, values, label=None):
|
38
|
-
super().__init__() # Instantiating `ModusaSignalData` class
|
39
|
-
|
40
|
-
values = np.asarray(values)
|
41
|
-
|
42
|
-
if values.ndim > 2:
|
43
|
-
raise ValueError("We do not currently support, > 2 dim arrays, please create a GitHub issue")
|
44
|
-
|
45
|
-
self._values = values
|
46
|
-
self._label = label or ""
|
47
|
-
|
48
|
-
#-----------------------------------
|
49
|
-
# Properties
|
50
|
-
#-----------------------------------
|
51
|
-
|
52
|
-
@property
|
53
|
-
def values(self) -> np.ndarray:
|
54
|
-
return self._values
|
55
|
-
|
56
|
-
@property
|
57
|
-
def label(self) -> str:
|
58
|
-
return self._label
|
59
|
-
|
60
|
-
@property
|
61
|
-
def shape(self) -> tuple:
|
62
|
-
return self.values.shape
|
63
|
-
|
64
|
-
@property
|
65
|
-
def ndim(self) -> tuple:
|
66
|
-
return self.values.ndim
|
67
|
-
|
68
|
-
@property
|
69
|
-
def size(self) -> int:
|
70
|
-
return self.values.size
|
71
|
-
|
72
|
-
#===================================
|
73
|
-
|
74
|
-
#-------------------------------
|
75
|
-
# Utility methods
|
76
|
-
#-------------------------------
|
77
|
-
|
78
|
-
def is_same_as(self, other) -> bool:
|
79
|
-
"""
|
80
|
-
Check if two data instances are same.
|
81
|
-
|
82
|
-
Parameters
|
83
|
-
----------
|
84
|
-
other: Data
|
85
|
-
Another Data object to compare with.
|
86
|
-
|
87
|
-
Returns
|
88
|
-
-------
|
89
|
-
bool
|
90
|
-
True if same ow False
|
91
|
-
|
92
|
-
Note
|
93
|
-
----
|
94
|
-
- We check the shape and all the values.
|
95
|
-
- We are not checking the labels for now.
|
96
|
-
"""
|
97
|
-
|
98
|
-
arr1 = np.asarray(self)
|
99
|
-
arr2 = np.asarray(other)
|
100
|
-
|
101
|
-
if isinstance(other, type(self)):
|
102
|
-
return False
|
103
|
-
if arr1.shape != arr2.shape:
|
104
|
-
return False
|
105
|
-
if not np.allclose(arr1, arr2):
|
106
|
-
return False
|
107
|
-
|
108
|
-
return True
|
109
|
-
|
110
|
-
def copy(self) -> Self:
|
111
|
-
"""
|
112
|
-
Return a new copy of data object.
|
113
|
-
|
114
|
-
Returns
|
115
|
-
-------
|
116
|
-
Data
|
117
|
-
A new copy of the data object.
|
118
|
-
"""
|
119
|
-
copied_values = np.asarray(self).copy()
|
120
|
-
copied_label = self.label
|
121
|
-
|
122
|
-
return self.__class__(values=copied_values, label=copied_label)
|
123
|
-
|
124
|
-
def set_meta_info(self, label):
|
125
|
-
"""
|
126
|
-
Set meta info for the data.
|
127
|
-
|
128
|
-
Parameters
|
129
|
-
----------
|
130
|
-
label: str
|
131
|
-
Label for the data (e.g. "Intensity (dB)").
|
132
|
-
Returns
|
133
|
-
-------
|
134
|
-
Self
|
135
|
-
A new Self instance with new label.
|
136
|
-
"""
|
137
|
-
|
138
|
-
if label is None:
|
139
|
-
return self
|
140
|
-
else:
|
141
|
-
return self.__class__(values=self.values.copy(), label=label)
|
142
|
-
|
143
|
-
def mask(self, condition, set_to=None) -> Self:
|
144
|
-
"""
|
145
|
-
Mask the data based on condition and
|
146
|
-
the values can be set using the `set_to` argument.
|
147
|
-
|
148
|
-
Parameters
|
149
|
-
----------
|
150
|
-
condition: Callable
|
151
|
-
- Condition function to apply on values of the data.
|
152
|
-
- E.g. lambda x: x > 10
|
153
|
-
set_to: Number
|
154
|
-
- Number to replace the masked position values.
|
155
|
-
|
156
|
-
Returns
|
157
|
-
-------
|
158
|
-
Data
|
159
|
-
Masked Data with either booleans as per the condition or with updated values.
|
160
|
-
"""
|
161
|
-
|
162
|
-
mask = condition(self)
|
163
|
-
new_value = set_to
|
164
|
-
|
165
|
-
if new_value is None: # Return the mask as the same signal but with booleans
|
166
|
-
return mask
|
167
|
-
|
168
|
-
else:
|
169
|
-
# We apply the mask and update the signal data
|
170
|
-
data_arr = self.values.copy() # We do not want to modify the data inplace
|
171
|
-
data_arr[mask] = new_value
|
172
|
-
|
173
|
-
updated_data = Data(values=data_arr, label=self.label)
|
174
|
-
|
175
|
-
return updated_data
|
176
|
-
|
177
|
-
def pad(self, left=None, right=None) -> Self:
|
178
|
-
"""
|
179
|
-
Pad the data from left or right.
|
180
|
-
|
181
|
-
Parameters
|
182
|
-
----------
|
183
|
-
left: arraylike
|
184
|
-
- What to pad to the left of the signal.
|
185
|
-
- E.g. 1 or [1, 0, 1], np.array([1, 2, 3])
|
186
|
-
right: arraylike
|
187
|
-
- What to pad to the right of the signal.
|
188
|
-
- E.g. 1 or [1, 0, 1], np.array([1, 2, 3])
|
189
|
-
|
190
|
-
Returns
|
191
|
-
-------
|
192
|
-
TDS
|
193
|
-
Padded signal.
|
194
|
-
"""
|
195
|
-
|
196
|
-
if right is None and left is None: # No padding applied
|
197
|
-
return self
|
198
|
-
|
199
|
-
values = np.asarray(self).copy()
|
200
|
-
if self.ndim == 1: # 1D
|
201
|
-
if isinstance(left, (int, float)): left = [left]
|
202
|
-
if isinstance(right, (int, float)): right = [right]
|
203
|
-
|
204
|
-
if left is not None:
|
205
|
-
values = np.concatenate((left, values))
|
206
|
-
if right is not None:
|
207
|
-
values = np.concatenate((values, right))
|
208
|
-
|
209
|
-
return self.__class__(values=values, label=self.label)
|
210
|
-
|
211
|
-
elif self.ndim == 2: # 2D
|
212
|
-
if isinstance(left, (int, float)): left = [left]
|
213
|
-
if isinstance(right, (int, float)): right = [right]
|
214
|
-
|
215
|
-
if left is not None:
|
216
|
-
left = np.asarray(left)
|
217
|
-
if left.ndim != 1:
|
218
|
-
raise ValueError(f"left must be 1 dimension")
|
219
|
-
left_cols = np.tile(left, (self.shape[0], 1))
|
220
|
-
values = np.concatenate((left_cols, values), axis=1)
|
221
|
-
|
222
|
-
if right is not None:
|
223
|
-
right = np.asarray(right)
|
224
|
-
if right.ndim != 1:
|
225
|
-
raise ValueError(f"right must be 1 dimension")
|
226
|
-
right_cols = np.tile(right, (self.shape[0], 1))
|
227
|
-
values = np.concatenate((values, right_cols), axis=1)
|
228
|
-
|
229
|
-
return self.__class__(values=values, label=self.label)
|
230
|
-
|
231
|
-
|
232
|
-
#================================
|
233
|
-
|
234
|
-
|
235
|
-
#-------------------------------
|
236
|
-
# NumPy Protocol
|
237
|
-
#-------------------------------
|
238
|
-
def __array__(self, dtype=None) -> np.ndarray:
|
239
|
-
return np.asarray(self.values, dtype=dtype)
|
240
|
-
|
241
|
-
def __index__(self):
|
242
|
-
return int(self.values)
|
243
|
-
|
244
|
-
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
|
245
|
-
"""
|
246
|
-
Provides operation support for the universal functions
|
247
|
-
on the Data object.
|
248
|
-
|
249
|
-
"""
|
250
|
-
|
251
|
-
raw_inputs = [x.values if isinstance(x, type(self)) else x for x in inputs]
|
252
|
-
|
253
|
-
# Call the actual ufunc
|
254
|
-
result = getattr(ufunc, method)(*raw_inputs, **kwargs)
|
255
|
-
|
256
|
-
if isinstance(result, (np.ndarray, np.generic)):
|
257
|
-
return self.__class__(result, label=self.label)
|
258
|
-
else:
|
259
|
-
return result
|
260
|
-
|
261
|
-
def __array_function__(self, func, types, args, kwargs):
|
262
|
-
"""
|
263
|
-
Additional numpy function support.
|
264
|
-
"""
|
265
|
-
from modusa.utils import np_func_cat as nfc
|
266
|
-
|
267
|
-
if not all(issubclass(t, type(self)) for t in types):
|
268
|
-
return NotImplemented
|
269
|
-
|
270
|
-
# Not supporting concatenate like operations as axis any random axis can't be concatenated
|
271
|
-
if func in nfc.CONCAT_FUNCS:
|
272
|
-
raise NotImplementedError(f"`{func.__name__}` is not yet tested on modusa signal, please create a GitHub issue.")
|
273
|
-
|
274
|
-
# Single signal input expected
|
275
|
-
data = args[0]
|
276
|
-
data_arr = np.asarray(data)
|
277
|
-
if "keepdims" not in kwargs: # We keep keepdim to True by default so that we remain in the same signal space.
|
278
|
-
kwargs["keepdims"] = True
|
279
|
-
result_arr = func(data_arr, **kwargs)
|
280
|
-
|
281
|
-
if func in nfc.REDUCTION_FUNCS:
|
282
|
-
return self.__class__(values=result_arr, label=data.label)
|
283
|
-
|
284
|
-
elif func in nfc.X_NEEDS_ADJUSTMENT_FUNCS:
|
285
|
-
# You must define logic for adjusting x
|
286
|
-
raise NotImplementedError(f"{func.__name__} requires x-axis adjustment logic.")
|
287
|
-
|
288
|
-
else:
|
289
|
-
raise NotImplementedError(f"`{func.__name__}` is not yet tested on modusa signal, please create a GitHub issue.")
|
290
|
-
|
291
|
-
|
292
|
-
#================================
|
293
|
-
|
294
|
-
#-------------------------------
|
295
|
-
# Indexing
|
296
|
-
#-------------------------------
|
297
|
-
|
298
|
-
def __getitem__(self, key):
|
299
|
-
"""
|
300
|
-
Return a sliced or indexed view of the data.
|
301
|
-
|
302
|
-
Parameters
|
303
|
-
----------
|
304
|
-
key : int | slice | tuple
|
305
|
-
Index to apply to the values.
|
306
|
-
|
307
|
-
Returns
|
308
|
-
-------
|
309
|
-
Data
|
310
|
-
A new Data object with sliced values and same metadata,
|
311
|
-
with singleton dimensions restored to match axis alignment.
|
312
|
-
"""
|
313
|
-
if not isinstance(key, (int, slice, tuple)):
|
314
|
-
raise TypeError(f"Invalid key type {type(key)}")
|
315
|
-
|
316
|
-
if self.ndim > 2:
|
317
|
-
raise ValueError("Support for upto 2 dim only")
|
318
|
-
|
319
|
-
sliced_arr = self.values[key]
|
320
|
-
|
321
|
-
# Case 1: 1D array
|
322
|
-
if self.ndim == 1:
|
323
|
-
if sliced_arr.ndim == 0: # We have
|
324
|
-
# We need to make it 1D
|
325
|
-
sliced_arr = np.expand_dims(sliced_arr, axis=0)
|
326
|
-
|
327
|
-
# Case 2: 2D array
|
328
|
-
if self.ndim == 2:
|
329
|
-
if sliced_arr.ndim == 0:
|
330
|
-
# We need to make it 2D
|
331
|
-
sliced_arr = np.expand_dims(sliced_arr, axis=0)
|
332
|
-
sliced_arr = np.expand_dims(sliced_arr, axis=0)
|
333
|
-
elif sliced_arr.ndim == 1:
|
334
|
-
if isinstance(key, int):
|
335
|
-
axis_of_interest = 0
|
336
|
-
# Assumption is that key is tuple of len 2 with atleast one position being integer, that is our axis of interest
|
337
|
-
elif isinstance(key, tuple) and len(key) == 2:
|
338
|
-
if isinstance(key[0], int):
|
339
|
-
axis_of_interest = 0
|
340
|
-
elif isinstance(key[1], int):
|
341
|
-
axis_of_interest = 1
|
342
|
-
# We need to make it 2D but based on the axis
|
343
|
-
sliced_arr = np.expand_dims(sliced_arr, axis=axis_of_interest)
|
344
|
-
|
345
|
-
return self.__class__(values=sliced_arr, label=self.label)
|
346
|
-
|
347
|
-
def __setitem__(self, key, value):
|
348
|
-
"""
|
349
|
-
Set values at the specified index.
|
350
|
-
|
351
|
-
Parameters
|
352
|
-
----------
|
353
|
-
key : int | slice | array-like | boolean array | S1D
|
354
|
-
Index to apply to the values.
|
355
|
-
value : int | float | array-like
|
356
|
-
Value(s) to set.
|
357
|
-
"""
|
358
|
-
if not isinstance(key, (int, slice, tuple)):
|
359
|
-
raise TypeError(f"Invalid key type {type(key)}")
|
360
|
-
|
361
|
-
self.values[key] = value # In-place assignment
|
362
|
-
|
363
|
-
#===============================
|
364
|
-
|
365
|
-
|
366
|
-
#-------------------------------
|
367
|
-
# Basic arithmetic operations
|
368
|
-
#-------------------------------
|
369
|
-
def __add__(self, other):
|
370
|
-
return np.add(self, other)
|
371
|
-
|
372
|
-
def __radd__(self, other):
|
373
|
-
return np.add(other, self)
|
374
|
-
|
375
|
-
def __sub__(self, other):
|
376
|
-
return np.subtract(self, other)
|
377
|
-
|
378
|
-
def __rsub__(self, other):
|
379
|
-
return np.subtract(other, self)
|
380
|
-
|
381
|
-
def __mul__(self, other):
|
382
|
-
return np.multiply(self, other)
|
383
|
-
|
384
|
-
def __rmul__(self, other):
|
385
|
-
return np.multiply(other, self)
|
386
|
-
|
387
|
-
def __truediv__(self, other):
|
388
|
-
return np.divide(self, other)
|
389
|
-
|
390
|
-
def __rtruediv__(self, other):
|
391
|
-
return np.divide(other, self)
|
392
|
-
|
393
|
-
def __floordiv__(self, other):
|
394
|
-
return np.floor_divide(self, other)
|
395
|
-
|
396
|
-
def __rfloordiv__(self, other):
|
397
|
-
return np.floor_divide(other, self)
|
398
|
-
|
399
|
-
def __pow__(self, other):
|
400
|
-
return np.power(self, other)
|
401
|
-
|
402
|
-
def __rpow__(self, other):
|
403
|
-
return np.power(other, self)
|
404
|
-
|
405
|
-
|
406
|
-
#-------------------------------
|
407
|
-
# Basic comparison operations
|
408
|
-
#-------------------------------
|
409
|
-
def __eq__(self, other):
|
410
|
-
return np.equal(self, other)
|
411
|
-
|
412
|
-
def __ne__(self, other):
|
413
|
-
return np.not_equal(self, other)
|
414
|
-
|
415
|
-
def __lt__(self, other):
|
416
|
-
return np.less(self, other)
|
417
|
-
|
418
|
-
def __le__(self, other):
|
419
|
-
return np.less_equal(self, other)
|
420
|
-
|
421
|
-
def __gt__(self, other):
|
422
|
-
return np.greater(self, other)
|
423
|
-
|
424
|
-
def __ge__(self, other):
|
425
|
-
return np.greater_equal(self, other)
|
426
|
-
|
427
|
-
#===============================
|
428
|
-
|
429
|
-
#-------------------------------
|
430
|
-
# Representation
|
431
|
-
#-------------------------------
|
432
|
-
|
433
|
-
def __str__(self):
|
434
|
-
|
435
|
-
arr_str = np.array2string(
|
436
|
-
np.asarray(self),
|
437
|
-
separator=", ",
|
438
|
-
threshold=30, # limit number of elements shown
|
439
|
-
edgeitems=3, # show first/last 3 rows and columns
|
440
|
-
max_line_width=120, # avoid wrapping
|
441
|
-
)
|
442
|
-
|
443
|
-
return f"{self._nickname}({arr_str})"
|
444
|
-
|
445
|
-
def __repr__(self):
|
446
|
-
|
447
|
-
arr_str = np.array2string(
|
448
|
-
np.asarray(self),
|
449
|
-
separator=", ",
|
450
|
-
threshold=30, # limit number of elements shown
|
451
|
-
edgeitems=3, # show first/last 3 rows and columns
|
452
|
-
max_line_width=120, # avoid wrapping
|
453
|
-
)
|
454
|
-
|
455
|
-
return f"{self._nickname}({arr_str})"
|
456
|
-
|
457
|
-
#===============================
|