modusa 0.3.68__tar.gz → 0.3.70__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 (94) hide show
  1. {modusa-0.3.68 → modusa-0.3.70}/PKG-INFO +1 -1
  2. {modusa-0.3.68 → modusa-0.3.70}/pyproject.toml +1 -1
  3. modusa-0.3.70/src/modusa/tools/ann_loader.py +65 -0
  4. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/tools/audio_loader.py +19 -3
  5. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/tools/plotter.py +12 -4
  6. modusa-0.3.68/src/modusa/tools/ann_loader.py +0 -38
  7. {modusa-0.3.68 → modusa-0.3.70}/LICENSE.md +0 -0
  8. {modusa-0.3.68 → modusa-0.3.70}/README.md +0 -0
  9. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/.DS_Store +0 -0
  10. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/__init__.py +0 -0
  11. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/config.py +0 -0
  12. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/decorators.py +0 -0
  13. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/devtools/generate_docs_source.py +0 -0
  14. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/devtools/generate_template.py +0 -0
  15. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/devtools/list_authors.py +0 -0
  16. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/devtools/list_plugins.py +0 -0
  17. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/devtools/main.py +0 -0
  18. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/devtools/templates/generator.py +0 -0
  19. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/devtools/templates/io.py +0 -0
  20. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/devtools/templates/model.py +0 -0
  21. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/devtools/templates/plugin.py +0 -0
  22. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/devtools/templates/test.py +0 -0
  23. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/devtools/templates/tool.py +0 -0
  24. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/fonts/NotoSansDevanagari-Regular.ttf +0 -0
  25. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/generators/__init__.py +0 -0
  26. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/generators/audio.py +0 -0
  27. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/generators/audio_waveforms.py +0 -0
  28. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/generators/base.py +0 -0
  29. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/generators/ftds.py +0 -0
  30. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/generators/s1d.py +0 -0
  31. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/generators/s2d.py +0 -0
  32. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/generators/s_ax.py +0 -0
  33. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/generators/t_ax.py +0 -0
  34. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/generators/tds.py +0 -0
  35. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/models/__init__.py +0 -0
  36. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/models/__pycache__/signal1D.cpython-312.pyc.4443461152 +0 -0
  37. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/models/audio.py +0 -0
  38. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/models/base.py +0 -0
  39. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/models/data.py +0 -0
  40. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/models/ftds.py +0 -0
  41. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/models/s1d.py +0 -0
  42. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/models/s2d.py +0 -0
  43. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/models/s_ax.py +0 -0
  44. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/models/t_ax.py +0 -0
  45. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/models/tds.py +0 -0
  46. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/plugins/__init__.py +0 -0
  47. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/plugins/base.py +0 -0
  48. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/tools/__init__.py +0 -0
  49. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/tools/_plotter_old.py +0 -0
  50. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/tools/audio_converter.py +0 -0
  51. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/tools/audio_player.py +0 -0
  52. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/tools/audio_recorder.py +0 -0
  53. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/tools/base.py +0 -0
  54. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/tools/math_ops.py +0 -0
  55. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/tools/youtube_downloader.py +0 -0
  56. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/utils/.DS_Store +0 -0
  57. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/utils/__init__.py +0 -0
  58. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/utils/config.py +0 -0
  59. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/utils/excp.py +0 -0
  60. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/utils/logger.py +0 -0
  61. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/utils/np_func_cat.py +0 -0
  62. {modusa-0.3.68 → modusa-0.3.70}/src/modusa/utils/plot.py +0 -0
  63. {modusa-0.3.68 → modusa-0.3.70}/tests/__init__.py +0 -0
  64. {modusa-0.3.68 → modusa-0.3.70}/tests/data/song1.mp3 +0 -0
  65. {modusa-0.3.68 → modusa-0.3.70}/tests/data/song1.wav +0 -0
  66. {modusa-0.3.68 → modusa-0.3.70}/tests/test_generators/audio_waveform.py +0 -0
  67. {modusa-0.3.68 → modusa-0.3.70}/tests/test_generators/test_audio.py +0 -0
  68. {modusa-0.3.68 → modusa-0.3.70}/tests/test_generators/test_ftds.py +0 -0
  69. {modusa-0.3.68 → modusa-0.3.70}/tests/test_generators/test_s1d.py +0 -0
  70. {modusa-0.3.68 → modusa-0.3.70}/tests/test_generators/test_s2d.py +0 -0
  71. {modusa-0.3.68 → modusa-0.3.70}/tests/test_generators/test_s_ax.py +0 -0
  72. {modusa-0.3.68 → modusa-0.3.70}/tests/test_generators/test_signal.py +0 -0
  73. {modusa-0.3.68 → modusa-0.3.70}/tests/test_generators/test_signal_generator.py +0 -0
  74. {modusa-0.3.68 → modusa-0.3.70}/tests/test_generators/test_t_ax.py +0 -0
  75. {modusa-0.3.68 → modusa-0.3.70}/tests/test_generators/test_tds.py +0 -0
  76. {modusa-0.3.68 → modusa-0.3.70}/tests/test_io/audio_player.py +0 -0
  77. {modusa-0.3.68 → modusa-0.3.70}/tests/test_io/plotter.py +0 -0
  78. {modusa-0.3.68 → modusa-0.3.70}/tests/test_models/test_data.py +0 -0
  79. {modusa-0.3.68 → modusa-0.3.70}/tests/test_models/test_t_ax.py +0 -0
  80. {modusa-0.3.68 → modusa-0.3.70}/tests/test_plugins/youtube_audio_loader.py +0 -0
  81. {modusa-0.3.68 → modusa-0.3.70}/tests/test_signals/frequency_domain_signal.py +0 -0
  82. {modusa-0.3.68 → modusa-0.3.70}/tests/test_signals/spectrogram.py +0 -0
  83. {modusa-0.3.68 → modusa-0.3.70}/tests/test_signals/test_axis.py +0 -0
  84. {modusa-0.3.68 → modusa-0.3.70}/tests/test_signals/test_feature_time_domain_signal.py +0 -0
  85. {modusa-0.3.68 → modusa-0.3.70}/tests/test_signals/test_frequency_time_domain_signal.py +0 -0
  86. {modusa-0.3.68 → modusa-0.3.70}/tests/test_signals/test_signal1D.py +0 -0
  87. {modusa-0.3.68 → modusa-0.3.70}/tests/test_signals/test_signal2D.py +0 -0
  88. {modusa-0.3.68 → modusa-0.3.70}/tests/test_signals/test_time_domain_signal.py +0 -0
  89. {modusa-0.3.68 → modusa-0.3.70}/tests/test_signals/test_u_ax.py +0 -0
  90. {modusa-0.3.68 → modusa-0.3.70}/tests/test_signals/test_window_signal.py +0 -0
  91. {modusa-0.3.68 → modusa-0.3.70}/tests/test_signals/time_domain_signal.py +0 -0
  92. {modusa-0.3.68 → modusa-0.3.70}/tests/test_tools/test_audio_converter.py +0 -0
  93. {modusa-0.3.68 → modusa-0.3.70}/tests/test_tools/test_fourier_tranform.py +0 -0
  94. {modusa-0.3.68 → modusa-0.3.70}/tests/test_tools/test_math_ops.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: modusa
3
- Version: 0.3.68
3
+ Version: 0.3.70
4
4
  Summary: A modular signal analysis python library.
5
5
  Author-Email: Ankit Anand <ankit0.anand0@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "modusa"
3
- version = "0.3.68"
3
+ version = "0.3.70"
4
4
  description = "A modular signal analysis python library."
5
5
  authors = [
6
6
  { name = "Ankit Anand", email = "ankit0.anand0@gmail.com" },
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env python3
2
+
3
+ #---------------------------------
4
+ # Author: Ankit Anand
5
+ # Date: 12/08/25
6
+ # Email: ankit0.anand0@gmail.com
7
+ #---------------------------------
8
+
9
+ from pathlib import Path
10
+
11
+ def load_ann(path, clip=None):
12
+ """
13
+ Load annotation from audatity label text file.
14
+
15
+ Parameters
16
+ ----------
17
+ path: str
18
+ - label text file path.
19
+ clip: tuple[number, number] | number | None
20
+ - Incase you clipped the audio signal, this parameter will help clip the annotation.
21
+ - If you clip the audio, say from (10, 20), set the clip to (10, 20).
22
+ - Default: None
23
+
24
+ Returns
25
+ -------
26
+ list[tuple, ...]
27
+ - annotation data structure
28
+ - [(start, end, tag), ...]
29
+ """
30
+
31
+ if not isinstance(path, (str, Path)):
32
+ raise ValueError(f"`path` must be one of (str, Path), got {type(path)}")
33
+
34
+ if clip is not None:
35
+ # Map clip input to the right format
36
+ if isinstance(clip, int or float):
37
+ clip = (0, clip)
38
+ elif isinstance(clip, tuple) and len(clip) > 1:
39
+ clip = (clip[0], clip[1])
40
+ else:
41
+ raise ValueError(f"Invalid clip type or length: {type(clip)}, len={len(clip)}")
42
+
43
+ ann = []
44
+
45
+ with open(str(path), "r") as f:
46
+ lines = [line.rstrip("\n") for line in f]
47
+ for line in lines:
48
+ start, end, tag = line.split("\t")
49
+ start, end = float(start), float(end)
50
+
51
+ # Incase user has clipped the audio signal, we adjust the annotation
52
+ # to match the clipped audio
53
+ if clip is not None:
54
+ offset = clip[0]
55
+ # Clamp annotation to clip boundaries
56
+ new_start = max(start, clip[0]) - offset
57
+ new_end = min(end, clip[1]) - offset
58
+
59
+ # only keep if there's still overlap
60
+ if new_start < new_end:
61
+ ann.append((new_start, new_end, tag))
62
+ else:
63
+ ann.append((start, end, tag))
64
+
65
+ return ann
@@ -10,7 +10,7 @@ from .youtube_downloader import download
10
10
  from .audio_converter import convert
11
11
 
12
12
 
13
- def load(path, sr=None):
13
+ def load(path, sr=None, clip=None):
14
14
  """
15
15
  Loads audio file from various sources.
16
16
 
@@ -19,15 +19,19 @@ def load(path, sr=None):
19
19
  import modusa as ms
20
20
  audio_fp = ms.load(
21
21
  "https://www.youtube.com/watch?v=lIpw9-Y_N0g",
22
- sr = None)
22
+ sr = None, clip=(5, 10))
23
23
 
24
24
  Parameters
25
25
  ----------
26
26
  path: str
27
27
  - Path to the audio
28
28
  - Youtube URL
29
- sr: int
29
+ sr: int | None
30
30
  - Sampling rate to load the audio in.
31
+ clip: number | tuple[number, number] | None
32
+ - Which segment of the audio you want.
33
+ - Eg., 10 => First 10 sec, (5, 10) => 5 to 10 second
34
+ - Default: None => Entire audio.
31
35
 
32
36
  Return
33
37
  ------
@@ -86,5 +90,17 @@ def load(path, sr=None):
86
90
  n_samples = int(len(audio_data) * sr / audio_sr)
87
91
  audio_data = resample(audio_data, n_samples)
88
92
  audio_sr = sr
93
+
94
+ # Clip the audio signal as per needed
95
+ if clip is not None:
96
+ # Map clip input to the right format
97
+ if isinstance(clip, int or float):
98
+ clip = (0, clip)
99
+ elif isinstance(clip, tuple) and len(clip) > 1:
100
+ clip = (clip[0], clip[1])
101
+ else:
102
+ raise ValueError(f"Invalid clip type or length: {type(clip)}, len={len(clip)}")
103
+
104
+ audio_data = audio_data[int(clip[0]*sr):int(clip[1]*sr)]
89
105
 
90
106
  return audio_data, audio_sr, title
@@ -60,9 +60,12 @@ class Fig:
60
60
  dark_mode: bool
61
61
  - Do you want dark mode?
62
62
  - Default: True
63
+ grid: bool
64
+ - Do you want the grid?
65
+ - Default: True
63
66
  """
64
67
 
65
- def __init__(self, arrangement="asm", xlim=None, width=16, dark_mode=True):
68
+ def __init__(self, arrangement="asm", xlim=None, width=16, dark_mode=True, grid=True):
66
69
 
67
70
  if dark_mode:
68
71
  plt.style.use("dark_background")
@@ -80,6 +83,7 @@ class Fig:
80
83
  self._xlim = xlim
81
84
  self._curr_row_idx = 1 # Starting from 1 because row 0 is reserved for reference subplot
82
85
  self._curr_color_idx = 0 # So that we have different color across all the subplots to avoid legend confusion
86
+ self._grid = grid
83
87
 
84
88
  # Subplot setup
85
89
  self._fig, self._axs = self._generate_subplots(arrangement, width) # This will fill in the all the above variables
@@ -134,6 +138,7 @@ class Fig:
134
138
  """
135
139
 
136
140
  xlim = self._xlim
141
+ grid: bool = self._grid
137
142
  fig_width = width
138
143
 
139
144
  n_aux_sp = arrangement.count("a")
@@ -167,14 +172,17 @@ class Fig:
167
172
  axs[i, 1].axis("off")
168
173
  elif char == "a": # Remove ticks and labels from all the aux subplots
169
174
  axs[i, 0].tick_params(left=False, bottom=False, labelleft=False, labelbottom=False)
170
- axs[i, 0].grid(True, linestyle='--', linewidth=0.7, color="lightgray" ,alpha=0.6)
175
+ if grid:
176
+ axs[i, 0].grid(True, linestyle='--', linewidth=0.7, color="lightgray" ,alpha=0.6)
171
177
  axs[i, 1].axis("off")
172
178
  elif char == "s":
173
179
  axs[i, 0].tick_params(bottom=False, labelbottom=False)
174
- axs[i, 0].grid(True, linestyle='--', linewidth=0.7, color="lightgray" ,alpha=0.6)
180
+ if grid:
181
+ axs[i, 0].grid(True, linestyle='--', linewidth=0.7, color="lightgray" ,alpha=0.6)
175
182
  axs[i, 1].axis("off")
176
183
  elif char == "m":
177
- axs[i, 0].grid(True, linestyle='--', linewidth=0.7, color="lightgray" ,alpha=0.6)
184
+ if grid:
185
+ axs[i, 0].grid(True, linestyle='--', linewidth=0.7, color="lightgray" ,alpha=0.6)
178
186
  axs[i, 0].tick_params(bottom=False, labelbottom=False)
179
187
 
180
188
  axs[i, 0].sharex(axs[0, 0])
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- #---------------------------------
4
- # Author: Ankit Anand
5
- # Date: 12/08/25
6
- # Email: ankit0.anand0@gmail.com
7
- #---------------------------------
8
-
9
- from pathlib import Path
10
-
11
- def load_ann(path):
12
- """
13
- Load annotation from audatity label text file.
14
-
15
- Parameters
16
- ----------
17
- path: str
18
- label text file path.
19
-
20
- Returns
21
- -------
22
- list[tuple, ...]
23
- - annotation data structure
24
- - [(start, end, tag), ...]
25
- """
26
-
27
- if not isinstance(path, (str, Path)):
28
- raise ValueError(f"`path` must be one of (str, Path), got {type(path)}")
29
-
30
- ann = []
31
- with open(str(path), "r") as f:
32
- lines = [line.rstrip("\n") for line in f]
33
- for line in lines:
34
- start, end, tag = line.split("\t")
35
- start, end = float(start), float(end)
36
- ann.append((start, end, tag))
37
-
38
- return ann
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes