modusa 0.2.22__tar.gz → 0.3__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 (116) hide show
  1. {modusa-0.2.22 → modusa-0.3}/PKG-INFO +5 -16
  2. {modusa-0.2.22 → modusa-0.3}/README.md +2 -14
  3. {modusa-0.2.22 → modusa-0.3}/pyproject.toml +3 -2
  4. {modusa-0.2.22 → modusa-0.3}/src/modusa/.DS_Store +0 -0
  5. modusa-0.3/src/modusa/__init__.py +8 -0
  6. {modusa-0.2.22 → modusa-0.3}/src/modusa/decorators.py +4 -4
  7. modusa-0.3/src/modusa/devtools/generate_docs_source.py +96 -0
  8. {modusa-0.2.22 → modusa-0.3}/src/modusa/devtools/generate_template.py +13 -13
  9. {modusa-0.2.22 → modusa-0.3}/src/modusa/devtools/main.py +4 -3
  10. {modusa-0.2.22 → modusa-0.3}/src/modusa/devtools/templates/generator.py +1 -1
  11. {modusa-0.2.22 → modusa-0.3}/src/modusa/devtools/templates/io.py +1 -1
  12. modusa-0.2.22/src/modusa/devtools/templates/signal.py → modusa-0.3/src/modusa/devtools/templates/model.py +18 -11
  13. {modusa-0.2.22 → modusa-0.3}/src/modusa/devtools/templates/plugin.py +1 -1
  14. {modusa-0.2.22 → modusa-0.3}/src/modusa/devtools/templates/test.py +2 -3
  15. modusa-0.2.22/src/modusa/devtools/templates/engine.py → modusa-0.3/src/modusa/devtools/templates/tool.py +3 -8
  16. modusa-0.3/src/modusa/generators/__init__.py +13 -0
  17. modusa-0.3/src/modusa/generators/audio.py +188 -0
  18. {modusa-0.2.22 → modusa-0.3}/src/modusa/generators/audio_waveforms.py +22 -13
  19. {modusa-0.2.22 → modusa-0.3}/src/modusa/generators/base.py +1 -1
  20. modusa-0.3/src/modusa/generators/ftds.py +298 -0
  21. modusa-0.3/src/modusa/generators/s1d.py +270 -0
  22. modusa-0.3/src/modusa/generators/s2d.py +300 -0
  23. modusa-0.3/src/modusa/generators/s_ax.py +102 -0
  24. modusa-0.3/src/modusa/generators/t_ax.py +64 -0
  25. modusa-0.3/src/modusa/generators/tds.py +267 -0
  26. modusa-0.3/src/modusa/main.py +3 -0
  27. modusa-0.3/src/modusa/models/__init__.py +14 -0
  28. modusa-0.3/src/modusa/models/audio.py +90 -0
  29. modusa-0.3/src/modusa/models/base.py +70 -0
  30. modusa-0.3/src/modusa/models/data.py +457 -0
  31. modusa-0.3/src/modusa/models/ftds.py +584 -0
  32. modusa-0.3/src/modusa/models/s1d.py +578 -0
  33. modusa-0.3/src/modusa/models/s2d.py +619 -0
  34. modusa-0.3/src/modusa/models/s_ax.py +448 -0
  35. modusa-0.3/src/modusa/models/t_ax.py +335 -0
  36. modusa-0.3/src/modusa/models/tds.py +465 -0
  37. modusa-0.3/src/modusa/plugins/__init__.py +3 -0
  38. modusa-0.3/src/modusa/tmp.py +98 -0
  39. modusa-0.3/src/modusa/tools/__init__.py +7 -0
  40. modusa-0.3/src/modusa/tools/audio_converter.py +73 -0
  41. modusa-0.3/src/modusa/tools/audio_loader.py +90 -0
  42. modusa-0.3/src/modusa/tools/audio_player.py +89 -0
  43. modusa-0.3/src/modusa/tools/base.py +43 -0
  44. modusa-0.3/src/modusa/tools/math_ops.py +335 -0
  45. modusa-0.3/src/modusa/tools/plotter.py +351 -0
  46. modusa-0.3/src/modusa/tools/youtube_downloader.py +72 -0
  47. modusa-0.3/src/modusa/utils/excp.py +49 -0
  48. modusa-0.3/src/modusa/utils/np_func_cat.py +44 -0
  49. modusa-0.3/src/modusa/utils/plot.py +142 -0
  50. modusa-0.3/tests/__init__.py +0 -0
  51. modusa-0.3/tests/data/song1.mp3 +0 -0
  52. modusa-0.3/tests/data/song1.wav +0 -0
  53. modusa-0.3/tests/test_generators/test_audio.py +10 -0
  54. modusa-0.3/tests/test_generators/test_ftds.py +10 -0
  55. modusa-0.3/tests/test_generators/test_s1d.py +10 -0
  56. modusa-0.3/tests/test_generators/test_s2d.py +10 -0
  57. modusa-0.3/tests/test_generators/test_s_ax.py +10 -0
  58. modusa-0.3/tests/test_generators/test_signal.py +10 -0
  59. modusa-0.3/tests/test_generators/test_signal_generator.py +10 -0
  60. modusa-0.3/tests/test_generators/test_t_ax.py +10 -0
  61. modusa-0.3/tests/test_generators/test_tds.py +10 -0
  62. modusa-0.3/tests/test_models/test_data.py +10 -0
  63. modusa-0.3/tests/test_models/test_t_ax.py +10 -0
  64. modusa-0.3/tests/test_signals/test_axis.py +10 -0
  65. modusa-0.3/tests/test_signals/test_feature_time_domain_signal.py +10 -0
  66. modusa-0.3/tests/test_signals/test_frequency_time_domain_signal.py +10 -0
  67. modusa-0.3/tests/test_signals/test_signal1D.py +10 -0
  68. modusa-0.3/tests/test_signals/test_signal2D.py +10 -0
  69. modusa-0.3/tests/test_signals/test_time_domain_signal.py +10 -0
  70. modusa-0.3/tests/test_signals/test_u_ax.py +10 -0
  71. modusa-0.3/tests/test_signals/test_window_signal.py +10 -0
  72. modusa-0.3/tests/test_tools/test_audio_converter.py +48 -0
  73. modusa-0.3/tests/test_tools/test_fourier_tranform.py +10 -0
  74. modusa-0.3/tests/test_tools/test_math_ops.py +687 -0
  75. modusa-0.2.22/src/modusa/__init__.py +0 -1
  76. modusa-0.2.22/src/modusa/engines/.DS_Store +0 -0
  77. modusa-0.2.22/src/modusa/engines/__init__.py +0 -3
  78. modusa-0.2.22/src/modusa/engines/base.py +0 -14
  79. modusa-0.2.22/src/modusa/generators/__init__.py +0 -5
  80. modusa-0.2.22/src/modusa/io/__init__.py +0 -9
  81. modusa-0.2.22/src/modusa/io/audio_converter.py +0 -76
  82. modusa-0.2.22/src/modusa/io/audio_loader.py +0 -214
  83. modusa-0.2.22/src/modusa/io/audio_player.py +0 -72
  84. modusa-0.2.22/src/modusa/io/base.py +0 -43
  85. modusa-0.2.22/src/modusa/io/plotter.py +0 -430
  86. modusa-0.2.22/src/modusa/io/youtube_downloader.py +0 -139
  87. modusa-0.2.22/src/modusa/main.py +0 -33
  88. modusa-0.2.22/src/modusa/plugins/__init__.py +0 -1
  89. modusa-0.2.22/src/modusa/signals/__init__.py +0 -7
  90. modusa-0.2.22/src/modusa/signals/audio_signal.py +0 -483
  91. modusa-0.2.22/src/modusa/signals/base.py +0 -34
  92. modusa-0.2.22/src/modusa/signals/frequency_domain_signal.py +0 -329
  93. modusa-0.2.22/src/modusa/signals/signal_ops.py +0 -158
  94. modusa-0.2.22/src/modusa/signals/spectrogram.py +0 -465
  95. modusa-0.2.22/src/modusa/signals/time_domain_signal.py +0 -309
  96. modusa-0.2.22/src/modusa/utils/excp.py +0 -76
  97. modusa-0.2.22/tests/test_engines/audio_converter.py +0 -11
  98. modusa-0.2.22/tests/test_engines/youtube_audio_loader.py +0 -11
  99. modusa-0.2.22/tests/test_engines/youtube_downloader.py +0 -20
  100. {modusa-0.2.22 → modusa-0.3}/LICENSE.md +0 -0
  101. {modusa-0.2.22 → modusa-0.3}/src/modusa/config.py +0 -0
  102. {modusa-0.2.22 → modusa-0.3}/src/modusa/devtools/list_authors.py +0 -0
  103. {modusa-0.2.22 → modusa-0.3}/src/modusa/devtools/list_plugins.py +0 -0
  104. /modusa-0.2.22/tests/__init__.py → /modusa-0.3/src/modusa/models/__pycache__/signal1D.cpython-312.pyc.4443461152 +0 -0
  105. {modusa-0.2.22 → modusa-0.3}/src/modusa/plugins/base.py +0 -0
  106. {modusa-0.2.22 → modusa-0.3}/src/modusa/utils/.DS_Store +0 -0
  107. {modusa-0.2.22 → modusa-0.3}/src/modusa/utils/__init__.py +0 -0
  108. {modusa-0.2.22 → modusa-0.3}/src/modusa/utils/config.py +0 -0
  109. {modusa-0.2.22 → modusa-0.3}/src/modusa/utils/logger.py +0 -0
  110. {modusa-0.2.22 → modusa-0.3}/tests/test_generators/audio_waveform.py +0 -0
  111. {modusa-0.2.22 → modusa-0.3}/tests/test_io/audio_player.py +0 -0
  112. {modusa-0.2.22 → modusa-0.3}/tests/test_io/plotter.py +0 -0
  113. {modusa-0.2.22 → modusa-0.3}/tests/test_plugins/youtube_audio_loader.py +0 -0
  114. {modusa-0.2.22 → modusa-0.3}/tests/test_signals/frequency_domain_signal.py +0 -0
  115. {modusa-0.2.22 → modusa-0.3}/tests/test_signals/spectrogram.py +0 -0
  116. {modusa-0.2.22 → modusa-0.3}/tests/test_signals/time_domain_signal.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: modusa
3
- Version: 0.2.22
3
+ Version: 0.3
4
4
  Summary: A modular signal analysis python library.
5
5
  Author-Email: Ankit Anand <ankit0.anand0@gmail.com>
6
6
  License: MIT
@@ -8,23 +8,14 @@ Requires-Python: >=3.12
8
8
  Requires-Dist: numpy>=2.2.6
9
9
  Requires-Dist: matplotlib>=3.10.3
10
10
  Requires-Dist: yt-dlp>=2025.6.30
11
- Requires-Dist: librosa==0.10.1
11
+ Requires-Dist: librosa==0.11.0
12
12
  Requires-Dist: IPython>=8.0.0
13
+ Requires-Dist: pyloudnorm>=0.1.1
13
14
  Description-Content-Type: text/markdown
14
15
 
15
16
  # modusa
16
17
 
17
- [**modusa**](https://meluron-toolbox.github.io/modusa/) is a modular framework for audio signal analysis and processing, designed to help audio researchers and developers build DSP chains with minimal code.
18
-
19
- ---
20
-
21
- ## Core Components
22
-
23
- - ⚙️ **modusa Signals**
24
- - 🧩 **modusa Plugins**
25
- - 📊 **modusa Genetators**
26
- - 📥 **modusa I/O**
27
- - ♻️ **modusa Engines**
18
+ [**modusa**](https://meluron-toolbox.github.io/modusa/) is a library with utility tools especially for audio signals.
28
19
 
29
20
  ---
30
21
 
@@ -33,9 +24,7 @@ Description-Content-Type: text/markdown
33
24
  > modusa is under active development. You can install the latest version via:
34
25
 
35
26
  ```bash
36
- git clone https://github.com/meluron/modusa.git
37
- cd modusa
38
- pdm install
27
+ pip install modusa
39
28
  ```
40
29
 
41
30
  ---
@@ -1,16 +1,6 @@
1
1
  # modusa
2
2
 
3
- [**modusa**](https://meluron-toolbox.github.io/modusa/) is a modular framework for audio signal analysis and processing, designed to help audio researchers and developers build DSP chains with minimal code.
4
-
5
- ---
6
-
7
- ## Core Components
8
-
9
- - ⚙️ **modusa Signals**
10
- - 🧩 **modusa Plugins**
11
- - 📊 **modusa Genetators**
12
- - 📥 **modusa I/O**
13
- - ♻️ **modusa Engines**
3
+ [**modusa**](https://meluron-toolbox.github.io/modusa/) is a library with utility tools especially for audio signals.
14
4
 
15
5
  ---
16
6
 
@@ -19,9 +9,7 @@
19
9
  > modusa is under active development. You can install the latest version via:
20
10
 
21
11
  ```bash
22
- git clone https://github.com/meluron/modusa.git
23
- cd modusa
24
- pdm install
12
+ pip install modusa
25
13
  ```
26
14
 
27
15
  ---
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "modusa"
3
- version = "0.2.22"
3
+ version = "0.3"
4
4
  description = "A modular signal analysis python library."
5
5
  authors = [
6
6
  { name = "Ankit Anand", email = "ankit0.anand0@gmail.com" },
@@ -9,8 +9,9 @@ dependencies = [
9
9
  "numpy>=2.2.6",
10
10
  "matplotlib>=3.10.3",
11
11
  "yt-dlp>=2025.6.30",
12
- "librosa==0.10.1",
12
+ "librosa==0.11.0",
13
13
  "IPython>=8.0.0",
14
+ "pyloudnorm>=0.1.1",
14
15
  ]
15
16
  requires-python = ">=3.12"
16
17
  readme = "README.md"
@@ -0,0 +1,8 @@
1
+ from modusa.utils import excp, config
2
+
3
+ #=====Giving access to plot functions to plot multiple signals.=====
4
+ from modusa.tools import plot1d, plot2d
5
+ from modusa.tools import play, convert
6
+ from modusa.tools import download
7
+ from modusa.tools import load
8
+ #=====
@@ -110,14 +110,14 @@ def validate_arg(arg_name: str, value: Any, expected_type: Any) -> None:
110
110
  expected_names = ", ".join(
111
111
  get_origin(t).__name__ if get_origin(t) else t.__name__ for t in union_args
112
112
  )
113
- raise excp.ValidationError(
113
+ raise excp.InputTypeError(
114
114
  f"Argument '{arg_name}' must be one of ({expected_names}), got {type(value).__name__}"
115
115
  )
116
116
 
117
117
  # Handle generic types like list[float], tuple[int, str]
118
118
  elif origin is not None:
119
119
  if not isinstance(value, origin):
120
- raise excp.ValidationError(
120
+ raise excp.InputTypeError(
121
121
  f"Argument '{arg_name}' must be of type {origin.__name__}, got {type(value).__name__}"
122
122
  )
123
123
  return
@@ -125,13 +125,13 @@ def validate_arg(arg_name: str, value: Any, expected_type: Any) -> None:
125
125
  # ✅ Handle plain types
126
126
  elif isinstance(expected_type, type):
127
127
  if not isinstance(value, expected_type):
128
- raise excp.ValidationError(
128
+ raise excp.InputTypeError(
129
129
  f"Argument '{arg_name}' must be of type {expected_type.__name__}, got {type(value).__name__}"
130
130
  )
131
131
  return
132
132
  # ❌ Unsupported type structure
133
133
  else:
134
- raise excp.ValidationError(f"Unsupported annotation for '{arg_name}': {expected_type}")
134
+ raise excp.InputTypeError(f"Unsupported annotation for '{arg_name}': {expected_type}")
135
135
 
136
136
  def validate_args_type() -> Callable:
137
137
  def decorator(func: Callable) -> Callable:
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import inspect
4
+ import importlib
5
+ import pkgutil
6
+ from pathlib import Path
7
+ from collections import defaultdict
8
+
9
+ # === Configuration ===
10
+ BASE_MODULES = [
11
+ 'modusa.tools',
12
+ 'modusa.models',
13
+ 'modusa.generators',
14
+ 'modusa.plugins',
15
+ # 'modusa.io'
16
+ ]
17
+ OUTPUT_DIRS = [
18
+ Path('docs/source/tools'),
19
+ Path('docs/source/models'),
20
+ Path('docs/source/generators'),
21
+ Path('docs/source/plugins'),
22
+ # Path('docs/source/io')
23
+ ]
24
+
25
+ # Ensure output directories exist
26
+ for output_dir in OUTPUT_DIRS:
27
+ output_dir.mkdir(parents=True, exist_ok=True)
28
+
29
+ # === Utils ===
30
+ def get_classes_grouped_by_module(base_module):
31
+ """
32
+ Returns a dictionary: { module_path: [class_name, ...] }
33
+ """
34
+ found = defaultdict(list)
35
+ module = importlib.import_module(base_module)
36
+
37
+ for _, modname, _ in pkgutil.walk_packages(module.__path__, base_module + "."):
38
+ try:
39
+ submodule = importlib.import_module(modname)
40
+ for name, obj in inspect.getmembers(submodule, inspect.isclass):
41
+ if obj.__module__ == modname:
42
+ found[modname].append(name)
43
+ except Exception as e:
44
+ print(f"⚠️ Skipping {modname} due to import error: {e}")
45
+ return found
46
+
47
+ def write_module_rst_file(module_path, class_names, output_dir):
48
+ """
49
+ Writes a .rst file for a module, documenting all its classes.
50
+ Filename is based on the module, but title is the first class (Modusa* gets priority).
51
+ """
52
+ filename = output_dir / f"{module_path.split('.')[-1]}.rst"
53
+
54
+ # Prioritize 'Modusa*' classes first, then alphabetical
55
+ sorted_classes = sorted(class_names, key=lambda x: (not x.startswith("Modusa"), x.lower()))
56
+ title = sorted_classes[0] if sorted_classes else module_path.split('.')[-1]
57
+
58
+ with open(filename, 'w') as f:
59
+ f.write(f"{title}\n{'=' * len(title)}\n\n")
60
+ for class_name in sorted_classes:
61
+ f.write(f".. autoclass:: {module_path}.{class_name}\n")
62
+ f.write(" :members:\n")
63
+ f.write(" :undoc-members:\n")
64
+ f.write(" :show-inheritance:\n\n")
65
+
66
+ return filename.name
67
+
68
+ def write_index_rst_file(tools_by_module, output_dir, section_name="Tools"):
69
+ """
70
+ Writes index.rst in the given output_dir with 'base' on top, then other files alphabetically.
71
+ """
72
+ index_file = output_dir / "index.rst"
73
+ with open(index_file, "w") as f:
74
+ f.write(f"{section_name}\n{'=' * len(section_name)}\n\n")
75
+ f.write(".. toctree::\n :maxdepth: 1\n\n")
76
+
77
+ filenames = [module_path.split('.')[-1] for module_path in tools_by_module]
78
+ sorted_filenames = sorted(filenames, key=lambda x: (x != "base", x.lower()))
79
+
80
+ for name in sorted_filenames:
81
+ f.write(f" {name}\n")
82
+
83
+ # === Main Script ===
84
+ def generate_docs_source():
85
+ for base_module, output_dir in zip(BASE_MODULES, OUTPUT_DIRS):
86
+ module_class_map = get_classes_grouped_by_module(base_module)
87
+
88
+ for module_path, class_list in module_class_map.items():
89
+ write_module_rst_file(module_path, class_list, output_dir)
90
+
91
+ section_name = base_module.split('.')[-1].capitalize()
92
+ if section_name == "Io":
93
+ section_name = "IO"
94
+
95
+ write_index_rst_file(module_class_map, output_dir, section_name=section_name)
96
+ print(f"✅ Documentation generated for {base_module} in {output_dir}")
@@ -28,17 +28,17 @@ GENERATOR_INFO = {
28
28
  "template_dump_dp": SRC_CODE_DIR / "generators",
29
29
  "test_template_dump_dp": TESTS_DIR / "test_generators"
30
30
  }
31
- SIGNAL_INFO = {
32
- "template_fp": TEMPLATES_DIR / "signal.py",
31
+ MODEL_INFO = {
32
+ "template_fp": TEMPLATES_DIR / "model.py",
33
33
  "test_template_fp": TEMPLATES_DIR / "test.py",
34
- "template_dump_dp": SRC_CODE_DIR / "signals",
35
- "test_template_dump_dp": TESTS_DIR / "test_signals"
34
+ "template_dump_dp": SRC_CODE_DIR / "models",
35
+ "test_template_dump_dp": TESTS_DIR / "test_models"
36
36
  }
37
- ENGINE_INFO = {
38
- "template_fp": TEMPLATES_DIR / "engine.py",
37
+ TOOL_INFO = {
38
+ "template_fp": TEMPLATES_DIR / "tool.py",
39
39
  "test_template_fp": TEMPLATES_DIR / "test.py",
40
- "template_dump_dp": SRC_CODE_DIR / "engines",
41
- "test_template_dump_dp": TESTS_DIR / "test_engines"
40
+ "template_dump_dp": SRC_CODE_DIR / "tools",
41
+ "test_template_dump_dp": TESTS_DIR / "test_tools"
42
42
  }
43
43
 
44
44
 
@@ -53,8 +53,8 @@ class TemplateGenerator():
53
53
  def get_path_info(for_what: str):
54
54
  if for_what == "plugin": return PLUGIN_INFO
55
55
  if for_what == "io": return IO_INFO
56
- if for_what == "signal": return SIGNAL_INFO
57
- if for_what == "engine": return ENGINE_INFO
56
+ if for_what == "model": return MODEL_INFO
57
+ if for_what == "tool": return TOOL_INFO
58
58
  if for_what == "generator": return GENERATOR_INFO
59
59
 
60
60
  @staticmethod
@@ -75,7 +75,7 @@ class TemplateGenerator():
75
75
  print(f"⚠️ File already exists, choose another name.")
76
76
  sys.exit(1)
77
77
 
78
- class_name = questionary.text(f"Class name (CamelCase{for_what.capitalize()}): ").ask()
78
+ class_name = questionary.text(f"Class name (CamelCase): ").ask()
79
79
  if class_name is None:
80
80
  sys.exit(1)
81
81
 
@@ -138,7 +138,7 @@ class TemplateGenerator():
138
138
 
139
139
  # Save it to a file and put it in the correct folder
140
140
  TemplateGenerator.save_file(content=template_code, output_path=path_info['template_dump_dp'] / answers['module_name'])
141
- TemplateGenerator.save_file(content=test_code, output_path=path_info['test_template_dump_dp'] / answers['module_name'])
141
+ TemplateGenerator.save_file(content=test_code, output_path=path_info['test_template_dump_dp'] / f"test_{answers['module_name']}")
142
142
 
143
143
  print(f"✅ {for_what}:", "open " + str(path_info['template_dump_dp'] / answers['module_name']))
144
- print(f"✅ test:", "open " + str(path_info['test_template_dump_dp'] / answers['module_name']))
144
+ print(f"✅ test:", "open " + str(path_info['test_template_dump_dp'] / f"test_{answers['module_name']}"))
@@ -2,6 +2,7 @@
2
2
 
3
3
 
4
4
  from .generate_template import TemplateGenerator
5
+ from .generate_docs_source import generate_docs_source
5
6
  from .list_plugins import list_plugins
6
7
  from . import list_authors
7
8
 
@@ -20,12 +21,12 @@ def main():
20
21
  create_parser = subparsers.add_parser("create", help="Create new Modusa components")
21
22
  create_subparsers = create_parser.add_subparsers(dest="what", required=True)
22
23
 
23
- create_subparsers.add_parser("engine", help="Create a new engine class").set_defaults(func=lambda:TemplateGenerator.create_template("engine"))
24
+ create_subparsers.add_parser("tool", help="Create a new tool class").set_defaults(func=lambda:TemplateGenerator.create_template("tool"))
24
25
  create_subparsers.add_parser("plugin", help="Create a new plugin class").set_defaults(func=lambda:TemplateGenerator.create_template("plugin"))
25
- create_subparsers.add_parser("signal", help="Create a new signal class").set_defaults(func=lambda:TemplateGenerator.create_template("signal"))
26
+ create_subparsers.add_parser("model", help="Create a new model class").set_defaults(func=lambda:TemplateGenerator.create_template("model"))
26
27
  create_subparsers.add_parser("generator", help="Create a new signal generator class").set_defaults(func=lambda:TemplateGenerator.create_template("generator"))
27
28
  create_subparsers.add_parser("io", help="Create a new IO class").set_defaults(func=lambda:TemplateGenerator.create_template("io"))
28
-
29
+ create_subparsers.add_parser("docs", help="Generate the docs").set_defaults(func=lambda:generate_docs_source())
29
30
 
30
31
  # --- LIST group ---
31
32
  list_parser = subparsers.add_parser("list", help="List information about Modusa components")
@@ -3,7 +3,7 @@
3
3
 
4
4
  from modusa import excp
5
5
  from modusa.decorators import validate_args_type
6
- from modusa.generators import ModusaGenerator
6
+ from modusa.generators.base import ModusaGenerator
7
7
 
8
8
 
9
9
  class {class_name}(ModusaGenerator):
@@ -3,7 +3,7 @@
3
3
 
4
4
  from modusa import excp
5
5
  from modusa.decorators import validate_args_type
6
- from modusa.io import ModusaIO
6
+ from modusa.io.base import ModusaIO
7
7
 
8
8
 
9
9
  class {class_name}(ModusaIO):
@@ -3,17 +3,18 @@
3
3
 
4
4
  from modusa import excp
5
5
  from modusa.decorators import immutable_property, validate_args_type
6
- from modusa.signals.base import ModusaSignal
6
+ from modusa.signals.base import ?
7
7
  from typing import Self, Any
8
8
  import numpy as np
9
9
 
10
- class {class_name}(ModusaSignal):
10
+ class {class_name}():
11
11
  """
12
12
 
13
13
  """
14
14
 
15
15
  #--------Meta Information----------
16
16
  _name = ""
17
+ _nickname = "" # This is to be used in repr/str methods
17
18
  _description = ""
18
19
  _author_name = "{author_name}"
19
20
  _author_email = "{author_email}"
@@ -26,15 +27,21 @@ class {class_name}(ModusaSignal):
26
27
 
27
28
  self.title = "" # This title will be used as plot title by default
28
29
 
29
- #----------------------
30
+ #-----------------------------------
30
31
  # Properties
31
- #----------------------
32
+ #-----------------------------------
32
33
 
33
-
34
-
35
-
36
-
37
-
38
- #----------------------
34
+
35
+ #===================================
36
+
37
+
38
+
39
+
40
+
41
+
42
+ #-----------------------------------
39
43
  # Tools
40
- #----------------------
44
+ #-----------------------------------
45
+
46
+
47
+ #===================================
@@ -3,7 +3,7 @@
3
3
 
4
4
  from modusa import excp
5
5
  from modusa.decorators import immutable_property, validate_args_type, plugin_safety_check
6
- from modusa.plugins import ModusaPlugin
6
+ from modusa.plugins.base import ModusaPlugin
7
7
 
8
8
 
9
9
  class {class_name}(ModusaPlugin):
@@ -1,11 +1,10 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
+ from modusa import excp
3
4
 
4
5
  #--------Meta Information----------
5
6
  _class_name = "{class_name}"
6
7
  _author_name = "{author_name}"
7
8
  _author_email = "{author_email}"
8
9
  _created_at = "{date_created}"
9
- #----------------------------------
10
-
11
- from modusa.{for_what}s import {class_name}
10
+ #----------------------------------
@@ -3,11 +3,10 @@
3
3
 
4
4
  from modusa import excp
5
5
  from modusa.decorators import validate_args_type
6
- from modusa.engines import ModusaEngine
7
- from typing import Any
6
+ from modusa.tools.base import ModusaTool
8
7
 
9
8
 
10
- class {class_name}(ModusaEngine):
9
+ class {class_name}(ModusaTool):
11
10
  """
12
11
 
13
12
  """
@@ -22,8 +21,4 @@ class {class_name}(ModusaEngine):
22
21
 
23
22
  def __init__(self):
24
23
  super().__init__()
25
-
26
-
27
- @validate_args_type()
28
- def run(self) -> Any:
29
- pass
24
+
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from .base import ModusaGenerator
4
+
5
+ from .s_ax import SAxGen
6
+ from .t_ax import TAxGen
7
+
8
+ from .s1d import S1DGen
9
+ from .tds import TDSGen
10
+ from .audio import AudioGen
11
+
12
+ from .s2d import S2DGen
13
+ from .ftds import FTDSGen
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ from modusa import excp
5
+ from modusa.decorators import validate_args_type
6
+ from .base import ModusaGenerator
7
+ from modusa.models.t_ax import TAx
8
+ from modusa.models.audio import Audio
9
+ from modusa.models.data import Data
10
+ import numpy as np
11
+ from pathlib import Path
12
+
13
+ class AudioGen(ModusaGenerator):
14
+ """
15
+ Provides user friendly APIs to generate instances of different
16
+ `AudioSignal` instances.
17
+ """
18
+
19
+ #--------Meta Information----------
20
+ _name = ""
21
+ _description = ""
22
+ _author_name = "Ankit Anand"
23
+ _author_email = "ankit0.anand0@gmail.com"
24
+ _created_at = "2025-07-27"
25
+ #----------------------------------
26
+
27
+ @staticmethod
28
+ def from_array(
29
+ y: np.ndarray,
30
+ sr: float | int = 1.0,
31
+ t0: float | int = 0.0,
32
+ y_label: str = "Amplitude",
33
+ t_label: str = "Time (sec)",
34
+ title: str = "Audio Signal"
35
+ ) -> Audio:
36
+ """
37
+ Create `AudioSignal` instance from basic data structures.
38
+
39
+ .. code-block:: python
40
+
41
+ import modusa as ms
42
+ t = ms.tds.from_array([1, 2, 3])
43
+ print(t)
44
+ t.print_info()
45
+
46
+ Parameters
47
+ ----------
48
+ y: np.ndarray
49
+ - Audio data array.
50
+ sr: float | int
51
+ - Sampling rate.
52
+ t0: float | int
53
+ - Start timestamp.
54
+ y_label: str
55
+ - Y label for the signal.
56
+ - Default: "Y"
57
+ t_label: str
58
+ - T label for the signal.
59
+ - Default: "Time (sec)"
60
+ title: str
61
+ - Title for the signal.
62
+ - Default: "1D Signal"
63
+ Returns
64
+ -------
65
+ Audio
66
+ An instance of Audio.
67
+ """
68
+ assert isinstance(y, np.ndarray)
69
+ assert isinstance(sr, (int, float)) and isinstance(t0, (int, float))
70
+ assert isinstance(y_label, str) and isinstance(t_label, str) and isinstance(title, str)
71
+
72
+ assert y.ndim == 1
73
+
74
+ sr = float(sr)
75
+ t0 = float(t0)
76
+
77
+ t = TAx(n_points=y.shape[0], sr=sr, t0=t0, label=t_label) # Creating a signal axis instance
78
+ y = Data(values=y, label=y_label)
79
+
80
+ return Audio(y=y, t=t, title=title)
81
+
82
+ @classmethod
83
+ def from_youtube(cls, url: str, sr: int | float = None):
84
+ """
85
+ Loads audio from youtube at a given sr.
86
+ The audio is deleted from the device
87
+ after loading.
88
+
89
+ .. code-block:: python
90
+
91
+ import modusa as ms
92
+ audio = ms.audio.from_youtube(
93
+ url="https://www.youtube.com/watch?v=lIpw9-Y_N0g",
94
+ sr=None
95
+ )
96
+
97
+ PARAMETERS
98
+ ----------
99
+ url: str
100
+ Link to the YouTube video.
101
+ sr: int
102
+ Sampling rate to load the audio in.
103
+
104
+ Returns
105
+ -------
106
+ Audio:
107
+ An `Audio` instance with loaded audio content from YouTube.
108
+ """
109
+
110
+ from modusa.tools.youtube_downloader import YoutubeDownloader
111
+ from modusa import convert
112
+ import soundfile as sf
113
+ from scipy.signal import resample
114
+ import tempfile
115
+
116
+ # Download the audio in temp directory using tempfile module
117
+ with tempfile.TemporaryDirectory() as tmpdir:
118
+ audio_fp: Path = YoutubeDownloader.download(url=url, content_type="audio", output_dir=Path(tmpdir))
119
+
120
+ # Convert the audio to ".wav" form for loading
121
+ wav_audio_fp: Path = convert(inp_audio_fp=audio_fp, output_audio_fp=audio_fp.with_suffix(".wav"))
122
+
123
+ # Load the audio in memory
124
+ audio_data, audio_sr = sf.read(wav_audio_fp)
125
+
126
+ # Convert to mono if it's multi-channel
127
+ if audio_data.ndim > 1:
128
+ audio_data = audio_data.mean(axis=1)
129
+
130
+ # Resample if needed
131
+ if sr is not None:
132
+ if audio_sr != sr:
133
+ n_samples = int(len(audio_data) * sr / audio_sr)
134
+ audio_data = resample(audio_data, n_samples)
135
+ audio_sr = sr
136
+
137
+ audio = cls.from_array(y=audio_data, sr=audio_sr, title=audio_fp.stem)
138
+
139
+ return audio
140
+
141
+ @classmethod
142
+ def from_filepath(cls, fp: str | Path, sr: int | float = None):
143
+ """
144
+ Loads audio from filepath at a given sr.
145
+
146
+ .. code-block:: python
147
+
148
+ import modusa as ms
149
+ audio = ms.audio.from_filepath(
150
+ fp="path/to/audio.wav",
151
+ sr=None
152
+ )
153
+
154
+ PARAMETERS
155
+ ----------
156
+ fp: str | Path
157
+ Audio file path.
158
+ sr: int
159
+ Sampling rate to load the audio in.
160
+
161
+ Returns
162
+ -------
163
+ Audio:
164
+ An `Audio` instance with loaded audio content.
165
+ """
166
+ import soundfile as sf
167
+ from scipy.signal import resample
168
+ from pathlib import Path
169
+
170
+ fp = Path(fp)
171
+ # Load the audio in memory
172
+ audio_data, audio_sr = sf.read(fp)
173
+
174
+ # Convert to mono if it's multi-channel
175
+ if audio_data.ndim > 1:
176
+ audio_data = audio_data.mean(axis=1)
177
+
178
+ # Resample if needed
179
+ if sr is not None:
180
+ if audio_sr != sr:
181
+ n_samples = int(len(audio_data) * sr / audio_sr)
182
+ audio_data = resample(audio_data, n_samples)
183
+ audio_sr = sr
184
+
185
+ audio = cls.from_array(y=audio_data, sr=audio_sr, title=fp.stem)
186
+
187
+ return audio
188
+