modusa 0.2.21__py3-none-any.whl → 0.2.23__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.
Files changed (60) hide show
  1. modusa/decorators.py +4 -4
  2. modusa/devtools/docs/source/generators/audio_waveforms.rst +8 -0
  3. modusa/devtools/docs/source/generators/base.rst +8 -0
  4. modusa/devtools/docs/source/generators/index.rst +8 -0
  5. modusa/devtools/docs/source/io/audio_loader.rst +8 -0
  6. modusa/devtools/docs/source/io/base.rst +8 -0
  7. modusa/devtools/docs/source/io/index.rst +8 -0
  8. modusa/devtools/docs/source/plugins/base.rst +8 -0
  9. modusa/devtools/docs/source/plugins/index.rst +7 -0
  10. modusa/devtools/docs/source/signals/audio_signal.rst +8 -0
  11. modusa/devtools/docs/source/signals/base.rst +8 -0
  12. modusa/devtools/docs/source/signals/frequency_domain_signal.rst +8 -0
  13. modusa/devtools/docs/source/signals/index.rst +11 -0
  14. modusa/devtools/docs/source/signals/spectrogram.rst +8 -0
  15. modusa/devtools/docs/source/signals/time_domain_signal.rst +8 -0
  16. modusa/devtools/docs/source/tools/audio_converter.rst +8 -0
  17. modusa/devtools/docs/source/tools/audio_player.rst +8 -0
  18. modusa/devtools/docs/source/tools/base.rst +8 -0
  19. modusa/devtools/docs/source/tools/fourier_tranform.rst +8 -0
  20. modusa/devtools/docs/source/tools/index.rst +13 -0
  21. modusa/devtools/docs/source/tools/math_ops.rst +8 -0
  22. modusa/devtools/docs/source/tools/plotter.rst +8 -0
  23. modusa/devtools/docs/source/tools/youtube_downloader.rst +8 -0
  24. modusa/devtools/generate_doc_source.py +96 -0
  25. modusa/devtools/generate_template.py +8 -8
  26. modusa/devtools/main.py +3 -2
  27. modusa/devtools/templates/test.py +2 -3
  28. modusa/devtools/templates/{engine.py → tool.py} +3 -8
  29. modusa/generators/__init__.py +0 -2
  30. modusa/generators/audio_waveforms.py +22 -13
  31. modusa/generators/base.py +1 -1
  32. modusa/io/__init__.py +1 -5
  33. modusa/io/audio_loader.py +3 -33
  34. modusa/main.py +0 -30
  35. modusa/signals/__init__.py +1 -5
  36. modusa/signals/audio_signal.py +181 -124
  37. modusa/signals/base.py +1 -8
  38. modusa/signals/frequency_domain_signal.py +140 -93
  39. modusa/signals/spectrogram.py +197 -98
  40. modusa/signals/time_domain_signal.py +177 -74
  41. modusa/tools/__init__.py +2 -0
  42. modusa/{io → tools}/audio_converter.py +12 -4
  43. modusa/tools/audio_player.py +114 -0
  44. modusa/tools/base.py +43 -0
  45. modusa/tools/fourier_tranform.py +24 -0
  46. modusa/tools/math_ops.py +232 -0
  47. modusa/{io → tools}/plotter.py +155 -42
  48. modusa/{io → tools}/youtube_downloader.py +2 -2
  49. modusa/utils/excp.py +9 -42
  50. {modusa-0.2.21.dist-info → modusa-0.2.23.dist-info}/METADATA +2 -1
  51. modusa-0.2.23.dist-info/RECORD +70 -0
  52. modusa/engines/.DS_Store +0 -0
  53. modusa/engines/__init__.py +0 -3
  54. modusa/engines/base.py +0 -14
  55. modusa/io/audio_player.py +0 -72
  56. modusa/signals/signal_ops.py +0 -158
  57. modusa-0.2.21.dist-info/RECORD +0 -47
  58. {modusa-0.2.21.dist-info → modusa-0.2.23.dist-info}/WHEEL +0 -0
  59. {modusa-0.2.21.dist-info → modusa-0.2.23.dist-info}/entry_points.txt +0 -0
  60. {modusa-0.2.21.dist-info → modusa-0.2.23.dist-info}/licenses/LICENSE.md +0 -0
modusa/decorators.py CHANGED
@@ -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,8 @@
1
+ AudioWaveformGenerator
2
+ ======================
3
+
4
+ .. autoclass:: modusa.generators.audio_waveforms.AudioWaveformGenerator
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ ModusaGenerator
2
+ ===============
3
+
4
+ .. autoclass:: modusa.generators.base.ModusaGenerator
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ Generators
2
+ ==========
3
+
4
+ .. toctree::
5
+ :maxdepth: 1
6
+
7
+ base
8
+ audio_waveforms
@@ -0,0 +1,8 @@
1
+ AudioLoader
2
+ ===========
3
+
4
+ .. autoclass:: modusa.io.audio_loader.AudioLoader
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ ModusaIO
2
+ ========
3
+
4
+ .. autoclass:: modusa.io.base.ModusaIO
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ IO
2
+ ==
3
+
4
+ .. toctree::
5
+ :maxdepth: 1
6
+
7
+ base
8
+ audio_loader
@@ -0,0 +1,8 @@
1
+ ModusaPlugin
2
+ ============
3
+
4
+ .. autoclass:: modusa.plugins.base.ModusaPlugin
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,7 @@
1
+ Plugins
2
+ =======
3
+
4
+ .. toctree::
5
+ :maxdepth: 1
6
+
7
+ base
@@ -0,0 +1,8 @@
1
+ AudioSignal
2
+ ===========
3
+
4
+ .. autoclass:: modusa.signals.audio_signal.AudioSignal
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ ModusaSignal
2
+ ============
3
+
4
+ .. autoclass:: modusa.signals.base.ModusaSignal
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ FrequencyDomainSignal
2
+ =====================
3
+
4
+ .. autoclass:: modusa.signals.frequency_domain_signal.FrequencyDomainSignal
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,11 @@
1
+ Signals
2
+ =======
3
+
4
+ .. toctree::
5
+ :maxdepth: 1
6
+
7
+ base
8
+ audio_signal
9
+ frequency_domain_signal
10
+ spectrogram
11
+ time_domain_signal
@@ -0,0 +1,8 @@
1
+ Spectrogram
2
+ ===========
3
+
4
+ .. autoclass:: modusa.signals.spectrogram.Spectrogram
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ TimeDomainSignal
2
+ ================
3
+
4
+ .. autoclass:: modusa.signals.time_domain_signal.TimeDomainSignal
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ AudioConverter
2
+ ==============
3
+
4
+ .. autoclass:: modusa.tools.audio_converter.AudioConverter
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ AudioPlayer
2
+ ===========
3
+
4
+ .. autoclass:: modusa.tools.audio_player.AudioPlayer
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ ModusaTool
2
+ ==========
3
+
4
+ .. autoclass:: modusa.tools.base.ModusaTool
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ FourierTransform
2
+ ================
3
+
4
+ .. autoclass:: modusa.tools.fourier_tranform.FourierTransform
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,13 @@
1
+ Tools
2
+ =====
3
+
4
+ .. toctree::
5
+ :maxdepth: 1
6
+
7
+ base
8
+ audio_converter
9
+ audio_player
10
+ fourier_tranform
11
+ math_ops
12
+ plotter
13
+ youtube_downloader
@@ -0,0 +1,8 @@
1
+ MathOps
2
+ =======
3
+
4
+ .. autoclass:: modusa.tools.math_ops.MathOps
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ Plotter
2
+ =======
3
+
4
+ .. autoclass:: modusa.tools.plotter.Plotter
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -0,0 +1,8 @@
1
+ YoutubeDownloader
2
+ =================
3
+
4
+ .. autoclass:: modusa.tools.youtube_downloader.YoutubeDownloader
5
+ :members:
6
+ :undoc-members:
7
+ :show-inheritance:
8
+
@@ -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.signals',
13
+ 'modusa.generators',
14
+ 'modusa.plugins',
15
+ 'modusa.io'
16
+ ]
17
+ OUTPUT_DIRS = [
18
+ Path('docs/source/tools'),
19
+ Path('docs/source/signals'),
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_doc_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}")
@@ -34,11 +34,11 @@ SIGNAL_INFO = {
34
34
  "template_dump_dp": SRC_CODE_DIR / "signals",
35
35
  "test_template_dump_dp": TESTS_DIR / "test_signals"
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
 
@@ -54,7 +54,7 @@ class TemplateGenerator():
54
54
  if for_what == "plugin": return PLUGIN_INFO
55
55
  if for_what == "io": return IO_INFO
56
56
  if for_what == "signal": return SIGNAL_INFO
57
- if for_what == "engine": return ENGINE_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']}"))
modusa/devtools/main.py CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
 
4
4
  from .generate_template import TemplateGenerator
5
+ from .generate_doc_source import generate_doc_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
26
  create_subparsers.add_parser("signal", help="Create a new signal class").set_defaults(func=lambda:TemplateGenerator.create_template("signal"))
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("doc", help="Create a new IO class").set_defaults(func=lambda:generate_doc_source())
29
30
 
30
31
  # --- LIST group ---
31
32
  list_parser = subparsers.add_parser("list", help="List information about Modusa components")
@@ -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
+
@@ -1,5 +1,3 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- from .base import ModusaGenerator
4
-
5
3
  from .audio_waveforms import AudioWaveformGenerator
@@ -3,18 +3,20 @@
3
3
 
4
4
  from modusa import excp
5
5
  from modusa.decorators import validate_args_type
6
- from modusa.generators import ModusaGenerator
7
- from modusa.signals import AudioSignal
6
+ from modusa.generators.base import ModusaGenerator
7
+ from modusa.signals.audio_signal import AudioSignal
8
8
  import numpy as np
9
9
 
10
10
  class AudioWaveformGenerator(ModusaGenerator):
11
11
  """
12
-
12
+ Generates different kinds of audio waveforms particulary helpful
13
+ in teaching signal processing concepts and testing out newly
14
+ created tools.
13
15
  """
14
16
 
15
17
  #--------Meta Information----------
16
18
  _name = "Audio Waveform Generator"
17
- _description = ""
19
+ _description = "Generates different kind of audio waveforms."
18
20
  _author_name = "Ankit Anand"
19
21
  _author_email = "ankit0.anand0@gmail.com"
20
22
  _created_at = "2025-07-07"
@@ -23,7 +25,7 @@ class AudioWaveformGenerator(ModusaGenerator):
23
25
  @staticmethod
24
26
  def generate_example() -> "AudioSignal":
25
27
  """
26
- Generates a simple sine wave audio signal for demonstration purposes.
28
+ Generates a simple sine wave audio signal as an example.
27
29
 
28
30
  Returns
29
31
  -------
@@ -65,6 +67,7 @@ class AudioWaveformGenerator(ModusaGenerator):
65
67
  y = np.random.uniform(low=-1.0, high=1.0, size=num_samples) # use uniform [-1, 1] for audio-like signal
66
68
 
67
69
  signal = AudioSignal(y=y, t=t, title="Random")
70
+
68
71
  return signal
69
72
 
70
73
  @staticmethod
@@ -77,7 +80,8 @@ class AudioWaveformGenerator(ModusaGenerator):
77
80
  sr: int = 1000,
78
81
  ) -> "AudioSignal":
79
82
  """
80
- Generates a sinusoidal AudioSignal with specified amplitude, frequency, phase, duration, and sample rate.
83
+ Generates a sinusoid audio signal with specified
84
+ amplitude, frequency, phase, duration, and sample rate.
81
85
 
82
86
  Parameters
83
87
  ----------
@@ -95,14 +99,15 @@ class AudioWaveformGenerator(ModusaGenerator):
95
99
  Returns
96
100
  -------
97
101
  AudioSignal
98
- A time-domain sinusoidal signal with the given parameters.
102
+ A sinusoidal signal with the given parameters.
99
103
  """
100
104
  A, f, phi, duration, sr = float(A), float(f), float(phi), float(duration), int(sr)
101
105
 
102
106
  t = np.arange(0, duration, 1 / sr)
103
107
  y = A * np.sin(2 * np.pi * f * t + phi)
104
108
 
105
- signal = AudioSignal(y=y, sr=sr, title="Sinusoid")
109
+ signal = AudioSignal(y=y, sr=sr, title=f"Sinusoid ({f} Hz)")
110
+
106
111
  return signal
107
112
 
108
113
  @staticmethod
@@ -115,7 +120,8 @@ class AudioWaveformGenerator(ModusaGenerator):
115
120
  sr: int = 1000,
116
121
  ) -> "AudioSignal":
117
122
  """
118
- Generates a square wave AudioSignal with specified amplitude, frequency, phase, duration, and sample rate.
123
+ Generates a square wave audio signal with specified
124
+ amplitude, frequency, phase, duration, and sample rate.
119
125
 
120
126
  Parameters
121
127
  ----------
@@ -140,7 +146,8 @@ class AudioWaveformGenerator(ModusaGenerator):
140
146
 
141
147
  y = A * np.sign(np.sin(2 * np.pi * f * t + phi))
142
148
 
143
- signal = AudioSignal(y=y, sr=sr, title="Square")
149
+ signal = AudioSignal(y=y, sr=sr, title=f"Square ({f} Hz)")
150
+
144
151
  return signal
145
152
 
146
153
 
@@ -181,7 +188,7 @@ class AudioWaveformGenerator(ModusaGenerator):
181
188
  phase_offset = phi / (2 * np.pi)
182
189
  y = A * (2 * ((f * t + phase_offset) % 1) - 1)
183
190
 
184
- signal = AudioSignal(y=y, sr=sr, title="Sawtooth")
191
+ signal = AudioSignal(y=y, sr=sr, title=f"Sawtooth ({f} Hz)")
185
192
  return signal
186
193
 
187
194
 
@@ -195,7 +202,8 @@ class AudioWaveformGenerator(ModusaGenerator):
195
202
  sr: int = 1000,
196
203
  ) -> "AudioSignal":
197
204
  """
198
- Generates a triangle wave AudioSignal with specified amplitude, frequency, phase, duration, and sample rate.
205
+ Generates a triangle wave AudioSignal with specified
206
+ amplitude, frequency, phase, duration, and sample rate.
199
207
 
200
208
  Parameters
201
209
  ----------
@@ -222,6 +230,7 @@ class AudioWaveformGenerator(ModusaGenerator):
222
230
  # Triangle wave formula: 2 * abs(2 * frac(x) - 1) - 1 scaled to amplitude
223
231
  y = A * (2 * np.abs(2 * ((f * t + phase_offset) % 1) - 1) - 1)
224
232
 
225
- signal = AudioSignal(y=y, sr=sr, title="Triangle")
233
+ signal = AudioSignal(y=y, sr=sr, title=f"Triangle ({f} Hz)")
234
+
226
235
  return signal
227
236
 
modusa/generators/base.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  from modusa import excp
4
4
  from modusa.decorators import validate_args_type, immutable_property
5
- from modusa.signals import ModusaSignal
5
+ from modusa.signals.base import ModusaSignal
6
6
  from abc import ABC, abstractmethod
7
7
  from typing import Any
8
8
 
modusa/io/__init__.py CHANGED
@@ -2,8 +2,4 @@
2
2
 
3
3
  from .base import ModusaIO
4
4
 
5
- from .audio_loader import AudioLoader
6
- from .audio_converter import AudioConverter
7
- from .youtube_downloader import YoutubeDownloader
8
- from .plotter import Plotter
9
- from .audio_player import AudioPlayer
5
+ from .audio_loader import AudioLoader
modusa/io/audio_loader.py CHANGED
@@ -59,7 +59,8 @@ class AudioLoader(ModusaIO):
59
59
  `Audio signal` instance with loaded audio content from YouTube.
60
60
  """
61
61
 
62
- from modusa.io import YoutubeDownloader, AudioConverter
62
+ from modusa.tools.youtube_downloader import YoutubeDownloader
63
+ from modusa.tools.audio_converter import AudioConverter
63
64
  import librosa
64
65
 
65
66
  # Download the audio in temp directory using tempfile module
@@ -115,41 +116,10 @@ class AudioLoader(ModusaIO):
115
116
  audio_signal = AudioSignal(y=y, sr=sr, title=fp.name)
116
117
 
117
118
  return audio_signal
118
-
119
-
120
- @staticmethod
121
- def from_array(y: np.ndarray, t: np.ndarray | None = None) -> AudioSignal:
122
- """
123
- Loads audio from numpy arrays.
124
-
125
- .. code-block:: python
126
-
127
- from modusa.io import AudioSignalLoader
128
- import numpy as np
129
-
130
- # From numpy array
131
- audio_signal = AudioSignalLoader.from_array(
132
- x=np.random.random((100, )),
133
- t = None # Automatically creates time index (integer)
134
- )
135
119
 
136
- Parameters
137
- ----------
138
- y: np.ndarray
139
- Data of the audio signal.
140
- t: np.ndarray | None
141
- Corresponding time stamps of the audio signal.
142
-
143
- Returns
144
- -------
145
- AudioSignal
146
- `Audio signal` instance with loaded audio content from arrays.
147
- """
148
-
149
- return AudioSignal(y=y, t=t)
150
120
 
151
121
  @staticmethod
152
- def from_array_with_sr(y: np.ndarray, sr: int) -> AudioSignal:
122
+ def from_array(y: np.ndarray, sr: int) -> AudioSignal:
153
123
  """
154
124
  Loads audio with a given sampling rate.
155
125