authtransforms 0.1.0__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.
@@ -0,0 +1,65 @@
1
+ """
2
+ authtransforms — Audio augmentation pipeline for PyTorch.
3
+
4
+ Inspired by: https://jonathanbgn.com/2021/08/30/audio-augmentation.html
5
+ """
6
+
7
+ from .pipeline import Compose, Identity, Lambda, OneOf, RandomOrder, SomeOf
8
+ from .transforms import (
9
+ AddGaussianNoise,
10
+ Normalize,
11
+ RandomApply,
12
+ RandomBackgroundNoise,
13
+ RandomClip,
14
+ RandomGain,
15
+ RandomPitchShift,
16
+ RandomSpeedChange,
17
+ SpecAugment,
18
+ TimeShift,
19
+ ToMono,
20
+ RoomImpulseResponse,
21
+ TimeStretch,
22
+ )
23
+ from .utils import (
24
+ audio_info,
25
+ compare_audio,
26
+ compare_play,
27
+ play_audio,
28
+ plot_audio,
29
+ plot_spectrogram,
30
+ plot_waveform,
31
+ save_audio,
32
+ )
33
+
34
+ __all__ = [
35
+ # Pipeline combinators
36
+ "Compose",
37
+ "OneOf",
38
+ "SomeOf",
39
+ "RandomOrder",
40
+ "Identity",
41
+ "Lambda",
42
+ # Transforms
43
+ "RandomClip",
44
+ "RandomSpeedChange",
45
+ "RandomBackgroundNoise",
46
+ "RandomPitchShift",
47
+ "RandomGain",
48
+ "AddGaussianNoise",
49
+ "TimeShift",
50
+ "SpecAugment",
51
+ "RandomApply",
52
+ "Normalize",
53
+ "ToMono",
54
+ "RoomImpulseResponse",
55
+ # Utilities
56
+ "plot_waveform",
57
+ "plot_spectrogram",
58
+ "plot_audio",
59
+ "compare_audio",
60
+ "play_audio",
61
+ "compare_play",
62
+ "audio_info",
63
+ "save_audio",
64
+ "TimeStretch",
65
+ ]
@@ -0,0 +1,196 @@
1
+ """
2
+ Audio augmentation pipeline — a torchvision-style Compose for audio tensors.
3
+
4
+ Example
5
+ -------
6
+ >>> from transforms import RandomClip, RandomSpeedChange, AddGaussianNoise
7
+ >>> from pipeline import Compose, OneOf
8
+ >>>
9
+ >>> pipeline = Compose([
10
+ ... RandomClip(sample_rate=16000, clip_length=16000 * 4),
11
+ ... OneOf([
12
+ ... RandomSpeedChange(sample_rate=16000),
13
+ ... AddGaussianNoise(),
14
+ ... ]),
15
+ ... ])
16
+ >>> augmented = pipeline(audio_tensor)
17
+ """
18
+
19
+ import random
20
+ from typing import Callable, List, Optional, Sequence
21
+ import torch
22
+
23
+
24
+ class Compose:
25
+ """Apply a sequence of transforms in order — mirrors torchvision.transforms.Compose.
26
+
27
+ Args:
28
+ transforms: List of callables that accept and return a Tensor.
29
+
30
+ Example::
31
+
32
+ pipeline = Compose([
33
+ RandomClip(sample_rate, clip_length=64000),
34
+ RandomSpeedChange(sample_rate),
35
+ RandomBackgroundNoise(sample_rate, './noises'),
36
+ ])
37
+ augmented = pipeline(audio)
38
+ """
39
+
40
+ def __init__(self, transforms: List[Callable]):
41
+
42
+ # The class where all the transforms are applied to the audio
43
+
44
+ # The List[Callable] indicates that the transforms variable should have a list of callable funcations
45
+ self.transforms = transforms
46
+
47
+ def __call__(self, audio: torch.Tensor) -> torch.Tensor:
48
+ for t in self.transforms:
49
+ audio = t(audio)
50
+ return audio
51
+
52
+ def __repr__(self) -> str:
53
+ lines = [f"{self.__class__.__name__}("]
54
+ for t in self.transforms:
55
+ lines.append(f" {t},")
56
+ lines.append(")")
57
+ return "\n".join(lines)
58
+
59
+ def __len__(self) -> int:
60
+ return len(self.transforms)
61
+
62
+ def __getitem__(self, idx):
63
+ return self.transforms[idx]
64
+
65
+
66
+ class OneOf:
67
+ """Apply exactly one randomly-chosen transform from the list.
68
+
69
+ Args:
70
+ transforms: List of transforms to choose from.
71
+ weights: Optional probability weights (unnormalized). If None, uniform.
72
+
73
+ Example::
74
+
75
+ augment = OneOf([
76
+ RandomSpeedChange(sample_rate),
77
+ AddGaussianNoise(),
78
+ RandomPitchShift(sample_rate),
79
+ ])
80
+ """
81
+
82
+ def __init__(self, transforms: List[Callable], weights: Optional[List[float]] = None): #The weights of randomly chosen transforms are stored
83
+ self.transforms = transforms
84
+ self.weights = weights
85
+
86
+ def __call__(self, audio: torch.Tensor) -> torch.Tensor:
87
+ if self.weights:
88
+ t = random.choices(self.transforms, weights=self.weights, k=1)[0] #if there are weights
89
+ else:
90
+ t = random.choice(self.transforms) #if there's no weight
91
+ return t(audio)
92
+
93
+ def __repr__(self) -> str:
94
+ lines = [f"{self.__class__.__name__}("]
95
+ for t in self.transforms:
96
+ lines.append(f" {t},")
97
+ lines.append(")")
98
+ return "\n".join(lines)
99
+
100
+
101
+ class SomeOf:
102
+ """Apply a random subset of transforms (n chosen without replacement).
103
+
104
+ Args:
105
+ transforms: Pool of available transforms.
106
+ n: Number of transforms to apply each call.
107
+ shuffle: If True, apply the selected transforms in a random order.
108
+
109
+ Example::
110
+
111
+ augment = SomeOf([
112
+ RandomGain(),
113
+ AddGaussianNoise(),
114
+ TimeShift(),
115
+ RandomPitchShift(sample_rate),
116
+ ], n=2)
117
+ """
118
+
119
+ # chooses n random transforms from a list and applies them sequentially to an audio tensor.
120
+
121
+ def __init__(self, transforms: List[Callable], n: int = 2, shuffle: bool = True):
122
+ if n > len(transforms):
123
+ raise ValueError(f"n={n} exceeds number of transforms ({len(transforms)})")
124
+ self.transforms = transforms
125
+ self.n = n
126
+ self.shuffle = shuffle
127
+
128
+ # Makes the object callable like a function
129
+ def __call__(self, audio: torch.Tensor) -> torch.Tensor:
130
+ selected = random.sample(self.transforms, self.n) #selects n random transforms
131
+ if self.shuffle:
132
+ random.shuffle(selected)
133
+ for t in selected:
134
+ audio = t(audio)
135
+ return audio
136
+
137
+ def __repr__(self) -> str:
138
+ lines = [f"{self.__class__.__name__}(n={self.n},"]
139
+ for t in self.transforms:
140
+ lines.append(f" {t},")
141
+ lines.append(")")
142
+ return "\n".join(lines)
143
+
144
+
145
+ class RandomOrder:
146
+ """Apply all transforms in a random order each call.
147
+
148
+ Args:
149
+ transforms: List of transforms.
150
+ """
151
+
152
+ def __init__(self, transforms: List[Callable]):
153
+ self.transforms = transforms
154
+
155
+ def __call__(self, audio: torch.Tensor) -> torch.Tensor:
156
+ order = list(self.transforms) # The transforms is copied in the order
157
+ random.shuffle(order) # Then shuffled and applied to the audio
158
+ for t in order:
159
+ audio = t(audio)
160
+ return audio
161
+
162
+ def __repr__(self) -> str:
163
+ lines = [f"{self.__class__.__name__}("]
164
+ for t in self.transforms:
165
+ lines.append(f" {t},")
166
+ lines.append(")")
167
+ return "\n".join(lines)
168
+
169
+
170
+ class Identity:
171
+ """Pass-through transform — returns the audio unchanged. Useful as a no-op placeholder."""
172
+
173
+ def __call__(self, audio: torch.Tensor) -> torch.Tensor: #Identity is a transform that returns the input audio unchanged, mainly used as a placeholder or optional "do nothing" step in augmentation pipelines.
174
+ return audio
175
+
176
+ def __repr__(self) -> str:
177
+ return f"{self.__class__.__name__}()"
178
+
179
+
180
+ class Lambda:
181
+ """Wrap any callable as a named transform (mirrors torchvision.transforms.Lambda).
182
+
183
+ Example::
184
+
185
+ double = Lambda(lambda x: x * 2, name="Double")
186
+ """
187
+ # To add custom augmentation and convert it into a transform
188
+ def __init__(self, func: Callable, name: str = "Lambda"):
189
+ self.func = func
190
+ self.name = name
191
+
192
+ def __call__(self, audio: torch.Tensor) -> torch.Tensor:
193
+ return self.func(audio)
194
+
195
+ def __repr__(self) -> str:
196
+ return f"{self.__class__.__name__}({self.name})"