modusa 0.2.22__tar.gz → 0.2.23__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.
- {modusa-0.2.22 → modusa-0.2.23}/PKG-INFO +1 -1
- {modusa-0.2.22 → modusa-0.2.23}/pyproject.toml +1 -1
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/decorators.py +4 -4
- modusa-0.2.23/src/modusa/devtools/docs/source/generators/audio_waveforms.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/generators/base.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/generators/index.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/io/audio_loader.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/io/base.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/io/index.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/plugins/base.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/plugins/index.rst +7 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/signals/audio_signal.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/signals/base.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/signals/frequency_domain_signal.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/signals/index.rst +11 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/signals/spectrogram.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/signals/time_domain_signal.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/tools/audio_converter.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/tools/audio_player.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/tools/base.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/tools/fourier_tranform.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/tools/index.rst +13 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/tools/math_ops.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/tools/plotter.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/docs/source/tools/youtube_downloader.rst +8 -0
- modusa-0.2.23/src/modusa/devtools/generate_doc_source.py +96 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/devtools/generate_template.py +8 -8
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/devtools/main.py +3 -2
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/devtools/templates/test.py +2 -3
- modusa-0.2.22/src/modusa/devtools/templates/engine.py → modusa-0.2.23/src/modusa/devtools/templates/tool.py +3 -8
- modusa-0.2.23/src/modusa/generators/__init__.py +3 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/generators/audio_waveforms.py +22 -13
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/generators/base.py +1 -1
- modusa-0.2.23/src/modusa/io/__init__.py +5 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/io/audio_loader.py +3 -33
- modusa-0.2.23/src/modusa/main.py +3 -0
- modusa-0.2.23/src/modusa/signals/__init__.py +3 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/signals/audio_signal.py +181 -124
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/signals/base.py +1 -8
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/signals/frequency_domain_signal.py +140 -93
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/signals/spectrogram.py +197 -98
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/signals/time_domain_signal.py +177 -74
- modusa-0.2.23/src/modusa/tools/__init__.py +2 -0
- {modusa-0.2.22/src/modusa/io → modusa-0.2.23/src/modusa/tools}/audio_converter.py +12 -4
- modusa-0.2.23/src/modusa/tools/audio_player.py +114 -0
- modusa-0.2.23/src/modusa/tools/base.py +43 -0
- modusa-0.2.23/src/modusa/tools/fourier_tranform.py +24 -0
- modusa-0.2.23/src/modusa/tools/math_ops.py +232 -0
- {modusa-0.2.22/src/modusa/io → modusa-0.2.23/src/modusa/tools}/plotter.py +155 -42
- {modusa-0.2.22/src/modusa/io → modusa-0.2.23/src/modusa/tools}/youtube_downloader.py +2 -2
- modusa-0.2.23/src/modusa/utils/excp.py +43 -0
- modusa-0.2.23/tests/data/song1.mp3 +0 -0
- modusa-0.2.23/tests/data/song1.wav +0 -0
- modusa-0.2.23/tests/test_tools/test_audio_converter.py +48 -0
- modusa-0.2.23/tests/test_tools/test_fourier_tranform.py +10 -0
- modusa-0.2.23/tests/test_tools/test_math_ops.py +687 -0
- modusa-0.2.22/src/modusa/engines/.DS_Store +0 -0
- modusa-0.2.22/src/modusa/engines/__init__.py +0 -3
- modusa-0.2.22/src/modusa/engines/base.py +0 -14
- modusa-0.2.22/src/modusa/generators/__init__.py +0 -5
- modusa-0.2.22/src/modusa/io/__init__.py +0 -9
- modusa-0.2.22/src/modusa/io/audio_player.py +0 -72
- modusa-0.2.22/src/modusa/main.py +0 -33
- modusa-0.2.22/src/modusa/signals/__init__.py +0 -7
- modusa-0.2.22/src/modusa/signals/signal_ops.py +0 -158
- modusa-0.2.22/src/modusa/utils/excp.py +0 -76
- modusa-0.2.22/tests/test_engines/audio_converter.py +0 -11
- modusa-0.2.22/tests/test_engines/youtube_audio_loader.py +0 -11
- modusa-0.2.22/tests/test_engines/youtube_downloader.py +0 -20
- {modusa-0.2.22 → modusa-0.2.23}/LICENSE.md +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/README.md +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/.DS_Store +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/__init__.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/config.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/devtools/list_authors.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/devtools/list_plugins.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/devtools/templates/generator.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/devtools/templates/io.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/devtools/templates/plugin.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/devtools/templates/signal.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/io/base.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/plugins/__init__.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/plugins/base.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/utils/.DS_Store +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/utils/__init__.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/utils/config.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/src/modusa/utils/logger.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/tests/__init__.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/tests/test_generators/audio_waveform.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/tests/test_io/audio_player.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/tests/test_io/plotter.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/tests/test_plugins/youtube_audio_loader.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/tests/test_signals/frequency_domain_signal.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/tests/test_signals/spectrogram.py +0 -0
- {modusa-0.2.22 → modusa-0.2.23}/tests/test_signals/time_domain_signal.py +0 -0
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.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
|
-
|
|
38
|
-
"template_fp": TEMPLATES_DIR / "
|
|
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 / "
|
|
41
|
-
"test_template_dump_dp": TESTS_DIR / "
|
|
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 == "
|
|
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
|
|
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_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("
|
|
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.
|
|
7
|
-
from typing import Any
|
|
6
|
+
from modusa.tools.base import ModusaTool
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
class {class_name}(
|
|
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
|
+
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
|
@@ -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.
|
|
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
|
|
122
|
+
def from_array(y: np.ndarray, sr: int) -> AudioSignal:
|
|
153
123
|
"""
|
|
154
124
|
Loads audio with a given sampling rate.
|
|
155
125
|
|