ZhengyangZhang commited on
Commit
3386339
·
verified ·
1 Parent(s): 4661aaa

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/torchaudio/_backend/__init__.py +61 -0
  2. lib/python3.12/site-packages/torchaudio/_backend/__pycache__/__init__.cpython-312.pyc +0 -0
  3. lib/python3.12/site-packages/torchaudio/_backend/__pycache__/backend.cpython-312.pyc +0 -0
  4. lib/python3.12/site-packages/torchaudio/_backend/__pycache__/common.cpython-312.pyc +0 -0
  5. lib/python3.12/site-packages/torchaudio/_backend/__pycache__/ffmpeg.cpython-312.pyc +0 -0
  6. lib/python3.12/site-packages/torchaudio/_backend/__pycache__/soundfile.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/torchaudio/_backend/__pycache__/soundfile_backend.cpython-312.pyc +0 -0
  8. lib/python3.12/site-packages/torchaudio/_backend/__pycache__/sox.cpython-312.pyc +0 -0
  9. lib/python3.12/site-packages/torchaudio/_backend/__pycache__/utils.cpython-312.pyc +0 -0
  10. lib/python3.12/site-packages/torchaudio/_backend/backend.py +53 -0
  11. lib/python3.12/site-packages/torchaudio/_backend/common.py +52 -0
  12. lib/python3.12/site-packages/torchaudio/_backend/ffmpeg.py +334 -0
  13. lib/python3.12/site-packages/torchaudio/_backend/soundfile.py +54 -0
  14. lib/python3.12/site-packages/torchaudio/_backend/soundfile_backend.py +457 -0
  15. lib/python3.12/site-packages/torchaudio/_backend/sox.py +91 -0
  16. lib/python3.12/site-packages/torchaudio/_backend/utils.py +317 -0
  17. lib/python3.12/site-packages/torchaudio/_extension/__init__.py +74 -0
  18. lib/python3.12/site-packages/torchaudio/_extension/__pycache__/__init__.cpython-312.pyc +0 -0
  19. lib/python3.12/site-packages/torchaudio/_extension/__pycache__/utils.cpython-312.pyc +0 -0
  20. lib/python3.12/site-packages/torchaudio/_extension/utils.py +180 -0
  21. lib/python3.12/site-packages/torchaudio/backend/__init__.py +8 -0
  22. lib/python3.12/site-packages/torchaudio/backend/_no_backend.py +25 -0
  23. lib/python3.12/site-packages/torchaudio/backend/_sox_io_backend.py +294 -0
  24. lib/python3.12/site-packages/torchaudio/backend/common.py +13 -0
  25. lib/python3.12/site-packages/torchaudio/backend/soundfile_backend.py +14 -0
  26. lib/python3.12/site-packages/torchaudio/backend/sox_io_backend.py +14 -0
  27. lib/python3.12/site-packages/torchaudio/datasets/__pycache__/cmudict.cpython-312.pyc +0 -0
  28. lib/python3.12/site-packages/torchaudio/datasets/__pycache__/gtzan.cpython-312.pyc +0 -0
  29. lib/python3.12/site-packages/torchaudio/datasets/__pycache__/ljspeech.cpython-312.pyc +0 -0
  30. lib/python3.12/site-packages/torchaudio/datasets/__pycache__/musdb_hq.cpython-312.pyc +0 -0
  31. lib/python3.12/site-packages/torchaudio/functional/__init__.py +127 -0
  32. lib/python3.12/site-packages/torchaudio/functional/__pycache__/__init__.cpython-312.pyc +0 -0
  33. lib/python3.12/site-packages/torchaudio/functional/__pycache__/_alignment.cpython-312.pyc +0 -0
  34. lib/python3.12/site-packages/torchaudio/functional/__pycache__/filtering.cpython-312.pyc +0 -0
  35. lib/python3.12/site-packages/torchaudio/functional/_alignment.py +128 -0
  36. lib/python3.12/site-packages/torchaudio/functional/filtering.py +1670 -0
  37. lib/python3.12/site-packages/torchaudio/functional/functional.py +2535 -0
  38. lib/python3.12/site-packages/torchaudio/lib/__init__.py +0 -0
  39. lib/python3.12/site-packages/torchaudio/lib/__pycache__/__init__.cpython-312.pyc +0 -0
  40. lib/python3.12/site-packages/torchaudio/models/__init__.py +85 -0
  41. lib/python3.12/site-packages/torchaudio/models/__pycache__/__init__.cpython-312.pyc +0 -0
  42. lib/python3.12/site-packages/torchaudio/models/__pycache__/_hdemucs.cpython-312.pyc +0 -0
  43. lib/python3.12/site-packages/torchaudio/models/__pycache__/conformer.cpython-312.pyc +0 -0
  44. lib/python3.12/site-packages/torchaudio/models/__pycache__/conv_tasnet.cpython-312.pyc +0 -0
  45. lib/python3.12/site-packages/torchaudio/models/__pycache__/deepspeech.cpython-312.pyc +0 -0
  46. lib/python3.12/site-packages/torchaudio/models/__pycache__/emformer.cpython-312.pyc +0 -0
  47. lib/python3.12/site-packages/torchaudio/models/__pycache__/rnnt.cpython-312.pyc +0 -0
  48. lib/python3.12/site-packages/torchaudio/models/__pycache__/rnnt_decoder.cpython-312.pyc +0 -0
  49. lib/python3.12/site-packages/torchaudio/models/__pycache__/tacotron2.cpython-312.pyc +0 -0
  50. lib/python3.12/site-packages/torchaudio/models/__pycache__/wav2letter.cpython-312.pyc +0 -0
lib/python3.12/site-packages/torchaudio/_backend/__init__.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Optional
2
+
3
+ from torchaudio._internal.module_utils import deprecated
4
+
5
+ from . import utils
6
+ from .common import AudioMetaData
7
+
8
+ __all__ = [
9
+ "AudioMetaData",
10
+ "load",
11
+ "info",
12
+ "save",
13
+ "list_audio_backends",
14
+ "get_audio_backend",
15
+ "set_audio_backend",
16
+ ]
17
+
18
+
19
+ info = utils.get_info_func()
20
+ load = utils.get_load_func()
21
+ save = utils.get_save_func()
22
+
23
+
24
+ def list_audio_backends() -> List[str]:
25
+ """List available backends
26
+
27
+ Returns:
28
+ list of str: The list of available backends.
29
+
30
+ The possible values are; ``"ffmpeg"``, ``"sox"`` and ``"soundfile"``.
31
+ """
32
+
33
+ return list(utils.get_available_backends().keys())
34
+
35
+
36
+ # Temporary until global backend is removed
37
+ @deprecated("With dispatcher enabled, this function is no-op. You can remove the function call.")
38
+ def get_audio_backend() -> Optional[str]:
39
+ """Get the name of the current global backend
40
+
41
+ Returns:
42
+ str or None:
43
+ If dispatcher mode is enabled, returns ``None`` otherwise,
44
+ the name of current backend or ``None`` (no backend is set).
45
+ """
46
+ return None
47
+
48
+
49
+ # Temporary until global backend is removed
50
+ @deprecated("With dispatcher enabled, this function is no-op. You can remove the function call.")
51
+ def set_audio_backend(backend: Optional[str]): # noqa
52
+ """Set the global backend.
53
+
54
+ This is a no-op when dispatcher mode is enabled.
55
+
56
+ Args:
57
+ backend (str or None): Name of the backend.
58
+ One of ``"sox_io"`` or ``"soundfile"`` based on availability
59
+ of the system. If ``None`` is provided the current backend is unassigned.
60
+ """
61
+ pass
lib/python3.12/site-packages/torchaudio/_backend/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (2.15 kB). View file
 
lib/python3.12/site-packages/torchaudio/_backend/__pycache__/backend.cpython-312.pyc ADDED
Binary file (2.73 kB). View file
 
lib/python3.12/site-packages/torchaudio/_backend/__pycache__/common.cpython-312.pyc ADDED
Binary file (2.31 kB). View file
 
lib/python3.12/site-packages/torchaudio/_backend/__pycache__/ffmpeg.cpython-312.pyc ADDED
Binary file (12.6 kB). View file
 
lib/python3.12/site-packages/torchaudio/_backend/__pycache__/soundfile.cpython-312.pyc ADDED
Binary file (2.89 kB). View file
 
lib/python3.12/site-packages/torchaudio/_backend/__pycache__/soundfile_backend.cpython-312.pyc ADDED
Binary file (16.8 kB). View file
 
lib/python3.12/site-packages/torchaudio/_backend/__pycache__/sox.cpython-312.pyc ADDED
Binary file (4.47 kB). View file
 
lib/python3.12/site-packages/torchaudio/_backend/__pycache__/utils.cpython-312.pyc ADDED
Binary file (15.5 kB). View file
 
lib/python3.12/site-packages/torchaudio/_backend/backend.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from abc import ABC, abstractmethod
3
+ from typing import BinaryIO, Optional, Tuple, Union
4
+
5
+ from torch import Tensor
6
+ from torchaudio.io import CodecConfig
7
+
8
+ from .common import AudioMetaData
9
+
10
+
11
+ class Backend(ABC):
12
+ @staticmethod
13
+ @abstractmethod
14
+ def info(uri: Union[BinaryIO, str, os.PathLike], format: Optional[str], buffer_size: int = 4096) -> AudioMetaData:
15
+ raise NotImplementedError
16
+
17
+ @staticmethod
18
+ @abstractmethod
19
+ def load(
20
+ uri: Union[BinaryIO, str, os.PathLike],
21
+ frame_offset: int = 0,
22
+ num_frames: int = -1,
23
+ normalize: bool = True,
24
+ channels_first: bool = True,
25
+ format: Optional[str] = None,
26
+ buffer_size: int = 4096,
27
+ ) -> Tuple[Tensor, int]:
28
+ raise NotImplementedError
29
+
30
+ @staticmethod
31
+ @abstractmethod
32
+ def save(
33
+ uri: Union[BinaryIO, str, os.PathLike],
34
+ src: Tensor,
35
+ sample_rate: int,
36
+ channels_first: bool = True,
37
+ format: Optional[str] = None,
38
+ encoding: Optional[str] = None,
39
+ bits_per_sample: Optional[int] = None,
40
+ buffer_size: int = 4096,
41
+ compression: Optional[Union[CodecConfig, float, int]] = None,
42
+ ) -> None:
43
+ raise NotImplementedError
44
+
45
+ @staticmethod
46
+ @abstractmethod
47
+ def can_decode(uri: Union[BinaryIO, str, os.PathLike], format: Optional[str]) -> bool:
48
+ raise NotImplementedError
49
+
50
+ @staticmethod
51
+ @abstractmethod
52
+ def can_encode(uri: Union[BinaryIO, str, os.PathLike], format: Optional[str]) -> bool:
53
+ raise NotImplementedError
lib/python3.12/site-packages/torchaudio/_backend/common.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class AudioMetaData:
2
+ """AudioMetaData()
3
+
4
+ Return type of ``torchaudio.info`` function.
5
+
6
+ :ivar int sample_rate: Sample rate
7
+ :ivar int num_frames: The number of frames
8
+ :ivar int num_channels: The number of channels
9
+ :ivar int bits_per_sample: The number of bits per sample. This is 0 for lossy formats,
10
+ or when it cannot be accurately inferred.
11
+ :ivar str encoding: Audio encoding
12
+ The values encoding can take are one of the following:
13
+
14
+ * ``PCM_S``: Signed integer linear PCM
15
+ * ``PCM_U``: Unsigned integer linear PCM
16
+ * ``PCM_F``: Floating point linear PCM
17
+ * ``FLAC``: Flac, Free Lossless Audio Codec
18
+ * ``ULAW``: Mu-law
19
+ * ``ALAW``: A-law
20
+ * ``MP3`` : MP3, MPEG-1 Audio Layer III
21
+ * ``VORBIS``: OGG Vorbis
22
+ * ``AMR_WB``: Adaptive Multi-Rate Wideband
23
+ * ``AMR_NB``: Adaptive Multi-Rate Narrowband
24
+ * ``OPUS``: Opus
25
+ * ``HTK``: Single channel 16-bit PCM
26
+ * ``UNKNOWN`` : None of above
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ sample_rate: int,
32
+ num_frames: int,
33
+ num_channels: int,
34
+ bits_per_sample: int,
35
+ encoding: str,
36
+ ):
37
+ self.sample_rate = sample_rate
38
+ self.num_frames = num_frames
39
+ self.num_channels = num_channels
40
+ self.bits_per_sample = bits_per_sample
41
+ self.encoding = encoding
42
+
43
+ def __str__(self):
44
+ return (
45
+ f"AudioMetaData("
46
+ f"sample_rate={self.sample_rate}, "
47
+ f"num_frames={self.num_frames}, "
48
+ f"num_channels={self.num_channels}, "
49
+ f"bits_per_sample={self.bits_per_sample}, "
50
+ f"encoding={self.encoding}"
51
+ f")"
52
+ )
lib/python3.12/site-packages/torchaudio/_backend/ffmpeg.py ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import sys
4
+ from typing import BinaryIO, Optional, Tuple, Union
5
+
6
+ import torch
7
+ import torchaudio
8
+
9
+ from .backend import Backend
10
+ from .common import AudioMetaData
11
+
12
+ InputType = Union[BinaryIO, str, os.PathLike]
13
+
14
+
15
+ def info_audio(
16
+ src: InputType,
17
+ format: Optional[str],
18
+ buffer_size: int = 4096,
19
+ ) -> AudioMetaData:
20
+ s = torchaudio.io.StreamReader(src, format, None, buffer_size)
21
+ sinfo = s.get_src_stream_info(s.default_audio_stream)
22
+ if sinfo.num_frames == 0:
23
+ waveform = _load_audio(s)
24
+ num_frames = waveform.size(1)
25
+ else:
26
+ num_frames = sinfo.num_frames
27
+ return AudioMetaData(
28
+ int(sinfo.sample_rate),
29
+ num_frames,
30
+ sinfo.num_channels,
31
+ sinfo.bits_per_sample,
32
+ sinfo.codec.upper(),
33
+ )
34
+
35
+
36
+ def _get_load_filter(
37
+ frame_offset: int = 0,
38
+ num_frames: int = -1,
39
+ convert: bool = True,
40
+ ) -> Optional[str]:
41
+ if frame_offset < 0:
42
+ raise RuntimeError("Invalid argument: frame_offset must be non-negative. Found: {}".format(frame_offset))
43
+ if num_frames == 0 or num_frames < -1:
44
+ raise RuntimeError("Invalid argument: num_frames must be -1 or greater than 0. Found: {}".format(num_frames))
45
+
46
+ # All default values -> no filter
47
+ if frame_offset == 0 and num_frames == -1 and not convert:
48
+ return None
49
+ # Only convert
50
+ aformat = "aformat=sample_fmts=fltp"
51
+ if frame_offset == 0 and num_frames == -1 and convert:
52
+ return aformat
53
+ # At least one of frame_offset or num_frames has non-default value
54
+ if num_frames > 0:
55
+ atrim = "atrim=start_sample={}:end_sample={}".format(frame_offset, frame_offset + num_frames)
56
+ else:
57
+ atrim = "atrim=start_sample={}".format(frame_offset)
58
+ if not convert:
59
+ return atrim
60
+ return "{},{}".format(atrim, aformat)
61
+
62
+
63
+ def _load_audio(
64
+ s: "torchaudio.io.StreamReader",
65
+ filter: Optional[str] = None,
66
+ channels_first: bool = True,
67
+ ) -> torch.Tensor:
68
+ s.add_audio_stream(-1, -1, filter_desc=filter)
69
+ s.process_all_packets()
70
+ chunk = s.pop_chunks()[0]
71
+ if chunk is None:
72
+ raise RuntimeError("Failed to decode audio.")
73
+ waveform = chunk._elem
74
+ return waveform.T if channels_first else waveform
75
+
76
+
77
+ def load_audio(
78
+ src: InputType,
79
+ frame_offset: int = 0,
80
+ num_frames: int = -1,
81
+ convert: bool = True,
82
+ channels_first: bool = True,
83
+ format: Optional[str] = None,
84
+ buffer_size: int = 4096,
85
+ ) -> Tuple[torch.Tensor, int]:
86
+ if hasattr(src, "read") and format == "vorbis":
87
+ format = "ogg"
88
+ s = torchaudio.io.StreamReader(src, format, None, buffer_size)
89
+ sample_rate = int(s.get_src_stream_info(s.default_audio_stream).sample_rate)
90
+ filter = _get_load_filter(frame_offset, num_frames, convert)
91
+ waveform = _load_audio(s, filter, channels_first)
92
+ return waveform, sample_rate
93
+
94
+
95
+ def _get_sample_format(dtype: torch.dtype) -> str:
96
+ dtype_to_format = {
97
+ torch.uint8: "u8",
98
+ torch.int16: "s16",
99
+ torch.int32: "s32",
100
+ torch.int64: "s64",
101
+ torch.float32: "flt",
102
+ torch.float64: "dbl",
103
+ }
104
+ format = dtype_to_format.get(dtype)
105
+ if format is None:
106
+ raise ValueError(f"No format found for dtype {dtype}; dtype must be one of {list(dtype_to_format.keys())}.")
107
+ return format
108
+
109
+
110
+ def _native_endianness() -> str:
111
+ if sys.byteorder == "little":
112
+ return "le"
113
+ else:
114
+ return "be"
115
+
116
+
117
+ def _get_encoder_for_wav(encoding: str, bits_per_sample: int) -> str:
118
+ if bits_per_sample not in {None, 8, 16, 24, 32, 64}:
119
+ raise ValueError(f"Invalid bits_per_sample {bits_per_sample} for WAV encoding.")
120
+ endianness = _native_endianness()
121
+ if not encoding:
122
+ if not bits_per_sample:
123
+ # default to PCM S16
124
+ return f"pcm_s16{endianness}"
125
+ if bits_per_sample == 8:
126
+ return "pcm_u8"
127
+ return f"pcm_s{bits_per_sample}{endianness}"
128
+ if encoding == "PCM_S":
129
+ if not bits_per_sample:
130
+ bits_per_sample = 16
131
+ if bits_per_sample == 8:
132
+ raise ValueError("For WAV signed PCM, 8-bit encoding is not supported.")
133
+ return f"pcm_s{bits_per_sample}{endianness}"
134
+ if encoding == "PCM_U":
135
+ if bits_per_sample in (None, 8):
136
+ return "pcm_u8"
137
+ raise ValueError("For WAV unsigned PCM, only 8-bit encoding is supported.")
138
+ if encoding == "PCM_F":
139
+ if not bits_per_sample:
140
+ bits_per_sample = 32
141
+ if bits_per_sample in (32, 64):
142
+ return f"pcm_f{bits_per_sample}{endianness}"
143
+ raise ValueError("For WAV float PCM, only 32- and 64-bit encodings are supported.")
144
+ if encoding == "ULAW":
145
+ if bits_per_sample in (None, 8):
146
+ return "pcm_mulaw"
147
+ raise ValueError("For WAV PCM mu-law, only 8-bit encoding is supported.")
148
+ if encoding == "ALAW":
149
+ if bits_per_sample in (None, 8):
150
+ return "pcm_alaw"
151
+ raise ValueError("For WAV PCM A-law, only 8-bit encoding is supported.")
152
+ raise ValueError(f"WAV encoding {encoding} is not supported.")
153
+
154
+
155
+ def _get_flac_sample_fmt(bps):
156
+ if bps is None or bps == 16:
157
+ return "s16"
158
+ if bps == 24:
159
+ return "s32"
160
+ raise ValueError(f"FLAC only supports bits_per_sample values of 16 and 24 ({bps} specified).")
161
+
162
+
163
+ def _parse_save_args(
164
+ ext: Optional[str],
165
+ format: Optional[str],
166
+ encoding: Optional[str],
167
+ bps: Optional[int],
168
+ ):
169
+ # torchaudio's save function accepts the followings, which do not 1to1 map
170
+ # to FFmpeg.
171
+ #
172
+ # - format: audio format
173
+ # - bits_per_sample: encoder sample format
174
+ # - encoding: such as PCM_U8.
175
+ #
176
+ # In FFmpeg, format is specified with the following three (and more)
177
+ #
178
+ # - muxer: could be audio format or container format.
179
+ # the one we passed to the constructor of StreamWriter
180
+ # - encoder: the audio encoder used to encode audio
181
+ # - encoder sample format: the format used by encoder to encode audio.
182
+ #
183
+ # If encoder sample format is different from source sample format, StreamWriter
184
+ # will insert a filter automatically.
185
+ #
186
+ def _type(spec):
187
+ # either format is exactly the specified one
188
+ # or extension matches to the spec AND there is no format override.
189
+ return format == spec or (format is None and ext == spec)
190
+
191
+ if _type("wav") or _type("amb"):
192
+ # wav is special because it supports different encoding through encoders
193
+ # each encoder only supports one encoder format
194
+ #
195
+ # amb format is a special case originated from libsox.
196
+ # It is basically a WAV format, with slight modification.
197
+ # https://github.com/chirlu/sox/commit/4a4ea33edbca5972a1ed8933cc3512c7302fa67a#diff-39171191a858add9df87f5f210a34a776ac2c026842ae6db6ce97f5e68836795
198
+ # It is a format so that decoders will recognize it as ambisonic.
199
+ # https://www.ambisonia.com/Members/mleese/file-format-for-b-format/
200
+ # FFmpeg does not recognize amb because it is basically a WAV format.
201
+ muxer = "wav"
202
+ encoder = _get_encoder_for_wav(encoding, bps)
203
+ sample_fmt = None
204
+ elif _type("vorbis"):
205
+ # FFpmeg does not recognize vorbis extension, while libsox used to do.
206
+ # For the sake of bakward compatibility, (and the simplicity),
207
+ # we support the case where users want to do save("foo.vorbis")
208
+ muxer = "ogg"
209
+ encoder = "vorbis"
210
+ sample_fmt = None
211
+ else:
212
+ muxer = format
213
+ encoder = None
214
+ sample_fmt = None
215
+ if _type("flac"):
216
+ sample_fmt = _get_flac_sample_fmt(bps)
217
+ if _type("ogg"):
218
+ sample_fmt = _get_flac_sample_fmt(bps)
219
+ return muxer, encoder, sample_fmt
220
+
221
+
222
+ def save_audio(
223
+ uri: InputType,
224
+ src: torch.Tensor,
225
+ sample_rate: int,
226
+ channels_first: bool = True,
227
+ format: Optional[str] = None,
228
+ encoding: Optional[str] = None,
229
+ bits_per_sample: Optional[int] = None,
230
+ buffer_size: int = 4096,
231
+ compression: Optional[torchaudio.io.CodecConfig] = None,
232
+ ) -> None:
233
+ ext = None
234
+ if hasattr(uri, "write"):
235
+ if format is None:
236
+ raise RuntimeError("'format' is required when saving to file object.")
237
+ else:
238
+ uri = os.path.normpath(uri)
239
+ if tokens := str(uri).split(".")[1:]:
240
+ ext = tokens[-1].lower()
241
+
242
+ muxer, encoder, enc_fmt = _parse_save_args(ext, format, encoding, bits_per_sample)
243
+
244
+ if channels_first:
245
+ src = src.T
246
+
247
+ s = torchaudio.io.StreamWriter(uri, format=muxer, buffer_size=buffer_size)
248
+ s.add_audio_stream(
249
+ sample_rate,
250
+ num_channels=src.size(-1),
251
+ format=_get_sample_format(src.dtype),
252
+ encoder=encoder,
253
+ encoder_format=enc_fmt,
254
+ codec_config=compression,
255
+ )
256
+ with s.open():
257
+ s.write_audio_chunk(0, src)
258
+
259
+
260
+ def _map_encoding(encoding: str) -> str:
261
+ for dst in ["PCM_S", "PCM_U", "PCM_F"]:
262
+ if dst in encoding:
263
+ return dst
264
+ if encoding == "PCM_MULAW":
265
+ return "ULAW"
266
+ elif encoding == "PCM_ALAW":
267
+ return "ALAW"
268
+ return encoding
269
+
270
+
271
+ def _get_bits_per_sample(encoding: str, bits_per_sample: int) -> str:
272
+ if m := re.search(r"PCM_\w(\d+)\w*", encoding):
273
+ return int(m.group(1))
274
+ elif encoding in ["PCM_ALAW", "PCM_MULAW"]:
275
+ return 8
276
+ return bits_per_sample
277
+
278
+
279
+ class FFmpegBackend(Backend):
280
+ @staticmethod
281
+ def info(uri: InputType, format: Optional[str], buffer_size: int = 4096) -> AudioMetaData:
282
+ metadata = info_audio(uri, format, buffer_size)
283
+ metadata.bits_per_sample = _get_bits_per_sample(metadata.encoding, metadata.bits_per_sample)
284
+ metadata.encoding = _map_encoding(metadata.encoding)
285
+ return metadata
286
+
287
+ @staticmethod
288
+ def load(
289
+ uri: InputType,
290
+ frame_offset: int = 0,
291
+ num_frames: int = -1,
292
+ normalize: bool = True,
293
+ channels_first: bool = True,
294
+ format: Optional[str] = None,
295
+ buffer_size: int = 4096,
296
+ ) -> Tuple[torch.Tensor, int]:
297
+ return load_audio(uri, frame_offset, num_frames, normalize, channels_first, format)
298
+
299
+ @staticmethod
300
+ def save(
301
+ uri: InputType,
302
+ src: torch.Tensor,
303
+ sample_rate: int,
304
+ channels_first: bool = True,
305
+ format: Optional[str] = None,
306
+ encoding: Optional[str] = None,
307
+ bits_per_sample: Optional[int] = None,
308
+ buffer_size: int = 4096,
309
+ compression: Optional[Union[torchaudio.io.CodecConfig, float, int]] = None,
310
+ ) -> None:
311
+ if not isinstance(compression, (torchaudio.io.CodecConfig, type(None))):
312
+ raise ValueError(
313
+ "FFmpeg backend expects non-`None` value for argument `compression` to be of ",
314
+ f"type `torchaudio.io.CodecConfig`, but received value of type {type(compression)}",
315
+ )
316
+ save_audio(
317
+ uri,
318
+ src,
319
+ sample_rate,
320
+ channels_first,
321
+ format,
322
+ encoding,
323
+ bits_per_sample,
324
+ buffer_size,
325
+ compression,
326
+ )
327
+
328
+ @staticmethod
329
+ def can_decode(uri: InputType, format: Optional[str]) -> bool:
330
+ return True
331
+
332
+ @staticmethod
333
+ def can_encode(uri: InputType, format: Optional[str]) -> bool:
334
+ return True
lib/python3.12/site-packages/torchaudio/_backend/soundfile.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import BinaryIO, Optional, Tuple, Union
3
+
4
+ import torch
5
+ from torchaudio.io import CodecConfig
6
+
7
+ from . import soundfile_backend
8
+ from .backend import Backend
9
+ from .common import AudioMetaData
10
+
11
+
12
+ class SoundfileBackend(Backend):
13
+ @staticmethod
14
+ def info(uri: Union[BinaryIO, str, os.PathLike], format: Optional[str], buffer_size: int = 4096) -> AudioMetaData:
15
+ return soundfile_backend.info(uri, format)
16
+
17
+ @staticmethod
18
+ def load(
19
+ uri: Union[BinaryIO, str, os.PathLike],
20
+ frame_offset: int = 0,
21
+ num_frames: int = -1,
22
+ normalize: bool = True,
23
+ channels_first: bool = True,
24
+ format: Optional[str] = None,
25
+ buffer_size: int = 4096,
26
+ ) -> Tuple[torch.Tensor, int]:
27
+ return soundfile_backend.load(uri, frame_offset, num_frames, normalize, channels_first, format)
28
+
29
+ @staticmethod
30
+ def save(
31
+ uri: Union[BinaryIO, str, os.PathLike],
32
+ src: torch.Tensor,
33
+ sample_rate: int,
34
+ channels_first: bool = True,
35
+ format: Optional[str] = None,
36
+ encoding: Optional[str] = None,
37
+ bits_per_sample: Optional[int] = None,
38
+ buffer_size: int = 4096,
39
+ compression: Optional[Union[CodecConfig, float, int]] = None,
40
+ ) -> None:
41
+ if compression:
42
+ raise ValueError("soundfile backend does not support argument `compression`.")
43
+
44
+ soundfile_backend.save(
45
+ uri, src, sample_rate, channels_first, format=format, encoding=encoding, bits_per_sample=bits_per_sample
46
+ )
47
+
48
+ @staticmethod
49
+ def can_decode(uri, format) -> bool:
50
+ return True
51
+
52
+ @staticmethod
53
+ def can_encode(uri, format) -> bool:
54
+ return True
lib/python3.12/site-packages/torchaudio/_backend/soundfile_backend.py ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """The new soundfile backend which will become default in 0.8.0 onward"""
2
+ import warnings
3
+ from typing import Optional, Tuple
4
+
5
+ import torch
6
+ from torchaudio._internal import module_utils as _mod_utils
7
+
8
+ from .common import AudioMetaData
9
+
10
+
11
+ _IS_SOUNDFILE_AVAILABLE = False
12
+
13
+ # TODO: import soundfile only when it is used.
14
+ if _mod_utils.is_module_available("soundfile"):
15
+ try:
16
+ import soundfile
17
+
18
+ _requires_soundfile = _mod_utils.no_op
19
+ _IS_SOUNDFILE_AVAILABLE = True
20
+ except Exception:
21
+ _requires_soundfile = _mod_utils.fail_with_message(
22
+ "requires soundfile, but we failed to import it. Please check the installation of soundfile."
23
+ )
24
+ else:
25
+ _requires_soundfile = _mod_utils.fail_with_message(
26
+ "requires soundfile, but it is not installed. Please install soundfile."
27
+ )
28
+
29
+
30
+ # Mapping from soundfile subtype to number of bits per sample.
31
+ # This is mostly heuristical and the value is set to 0 when it is irrelevant
32
+ # (lossy formats) or when it can't be inferred.
33
+ # For ADPCM (and G72X) subtypes, it's hard to infer the bit depth because it's not part of the standard:
34
+ # According to https://en.wikipedia.org/wiki/Adaptive_differential_pulse-code_modulation#In_telephony,
35
+ # the default seems to be 8 bits but it can be compressed further to 4 bits.
36
+ # The dict is inspired from
37
+ # https://github.com/bastibe/python-soundfile/blob/744efb4b01abc72498a96b09115b42a4cabd85e4/soundfile.py#L66-L94
38
+ _SUBTYPE_TO_BITS_PER_SAMPLE = {
39
+ "PCM_S8": 8, # Signed 8 bit data
40
+ "PCM_16": 16, # Signed 16 bit data
41
+ "PCM_24": 24, # Signed 24 bit data
42
+ "PCM_32": 32, # Signed 32 bit data
43
+ "PCM_U8": 8, # Unsigned 8 bit data (WAV and RAW only)
44
+ "FLOAT": 32, # 32 bit float data
45
+ "DOUBLE": 64, # 64 bit float data
46
+ "ULAW": 8, # U-Law encoded. See https://en.wikipedia.org/wiki/G.711#Types
47
+ "ALAW": 8, # A-Law encoded. See https://en.wikipedia.org/wiki/G.711#Types
48
+ "IMA_ADPCM": 0, # IMA ADPCM.
49
+ "MS_ADPCM": 0, # Microsoft ADPCM.
50
+ "GSM610": 0, # GSM 6.10 encoding. (Wikipedia says 1.625 bit depth?? https://en.wikipedia.org/wiki/Full_Rate)
51
+ "VOX_ADPCM": 0, # OKI / Dialogix ADPCM
52
+ "G721_32": 0, # 32kbs G721 ADPCM encoding.
53
+ "G723_24": 0, # 24kbs G723 ADPCM encoding.
54
+ "G723_40": 0, # 40kbs G723 ADPCM encoding.
55
+ "DWVW_12": 12, # 12 bit Delta Width Variable Word encoding.
56
+ "DWVW_16": 16, # 16 bit Delta Width Variable Word encoding.
57
+ "DWVW_24": 24, # 24 bit Delta Width Variable Word encoding.
58
+ "DWVW_N": 0, # N bit Delta Width Variable Word encoding.
59
+ "DPCM_8": 8, # 8 bit differential PCM (XI only)
60
+ "DPCM_16": 16, # 16 bit differential PCM (XI only)
61
+ "VORBIS": 0, # Xiph Vorbis encoding. (lossy)
62
+ "ALAC_16": 16, # Apple Lossless Audio Codec (16 bit).
63
+ "ALAC_20": 20, # Apple Lossless Audio Codec (20 bit).
64
+ "ALAC_24": 24, # Apple Lossless Audio Codec (24 bit).
65
+ "ALAC_32": 32, # Apple Lossless Audio Codec (32 bit).
66
+ }
67
+
68
+
69
+ def _get_bit_depth(subtype):
70
+ if subtype not in _SUBTYPE_TO_BITS_PER_SAMPLE:
71
+ warnings.warn(
72
+ f"The {subtype} subtype is unknown to TorchAudio. As a result, the bits_per_sample "
73
+ "attribute will be set to 0. If you are seeing this warning, please "
74
+ "report by opening an issue on github (after checking for existing/closed ones). "
75
+ "You may otherwise ignore this warning."
76
+ )
77
+ return _SUBTYPE_TO_BITS_PER_SAMPLE.get(subtype, 0)
78
+
79
+
80
+ _SUBTYPE_TO_ENCODING = {
81
+ "PCM_S8": "PCM_S",
82
+ "PCM_16": "PCM_S",
83
+ "PCM_24": "PCM_S",
84
+ "PCM_32": "PCM_S",
85
+ "PCM_U8": "PCM_U",
86
+ "FLOAT": "PCM_F",
87
+ "DOUBLE": "PCM_F",
88
+ "ULAW": "ULAW",
89
+ "ALAW": "ALAW",
90
+ "VORBIS": "VORBIS",
91
+ }
92
+
93
+
94
+ def _get_encoding(format: str, subtype: str):
95
+ if format == "FLAC":
96
+ return "FLAC"
97
+ return _SUBTYPE_TO_ENCODING.get(subtype, "UNKNOWN")
98
+
99
+
100
+ @_requires_soundfile
101
+ def info(filepath: str, format: Optional[str] = None) -> AudioMetaData:
102
+ """Get signal information of an audio file.
103
+
104
+ Note:
105
+ ``filepath`` argument is intentionally annotated as ``str`` only, even though it accepts
106
+ ``pathlib.Path`` object as well. This is for the consistency with ``"sox_io"`` backend,
107
+ which has a restriction on type annotation due to TorchScript compiler compatiblity.
108
+
109
+ Args:
110
+ filepath (path-like object or file-like object):
111
+ Source of audio data.
112
+ format (str or None, optional):
113
+ Not used. PySoundFile does not accept format hint.
114
+
115
+ Returns:
116
+ AudioMetaData: meta data of the given audio.
117
+
118
+ """
119
+ sinfo = soundfile.info(filepath)
120
+ return AudioMetaData(
121
+ sinfo.samplerate,
122
+ sinfo.frames,
123
+ sinfo.channels,
124
+ bits_per_sample=_get_bit_depth(sinfo.subtype),
125
+ encoding=_get_encoding(sinfo.format, sinfo.subtype),
126
+ )
127
+
128
+
129
+ _SUBTYPE2DTYPE = {
130
+ "PCM_S8": "int8",
131
+ "PCM_U8": "uint8",
132
+ "PCM_16": "int16",
133
+ "PCM_32": "int32",
134
+ "FLOAT": "float32",
135
+ "DOUBLE": "float64",
136
+ }
137
+
138
+
139
+ @_requires_soundfile
140
+ def load(
141
+ filepath: str,
142
+ frame_offset: int = 0,
143
+ num_frames: int = -1,
144
+ normalize: bool = True,
145
+ channels_first: bool = True,
146
+ format: Optional[str] = None,
147
+ ) -> Tuple[torch.Tensor, int]:
148
+ """Load audio data from file.
149
+
150
+ Note:
151
+ The formats this function can handle depend on the soundfile installation.
152
+ This function is tested on the following formats;
153
+
154
+ * WAV
155
+
156
+ * 32-bit floating-point
157
+ * 32-bit signed integer
158
+ * 16-bit signed integer
159
+ * 8-bit unsigned integer
160
+
161
+ * FLAC
162
+ * OGG/VORBIS
163
+ * SPHERE
164
+
165
+ By default (``normalize=True``, ``channels_first=True``), this function returns Tensor with
166
+ ``float32`` dtype, and the shape of `[channel, time]`.
167
+
168
+ .. warning::
169
+
170
+ ``normalize`` argument does not perform volume normalization.
171
+ It only converts the sample type to `torch.float32` from the native sample
172
+ type.
173
+
174
+ When the input format is WAV with integer type, such as 32-bit signed integer, 16-bit
175
+ signed integer, 24-bit signed integer, and 8-bit unsigned integer, by providing ``normalize=False``,
176
+ this function can return integer Tensor, where the samples are expressed within the whole range
177
+ of the corresponding dtype, that is, ``int32`` tensor for 32-bit signed PCM,
178
+ ``int16`` for 16-bit signed PCM and ``uint8`` for 8-bit unsigned PCM. Since torch does not
179
+ support ``int24`` dtype, 24-bit signed PCM are converted to ``int32`` tensors.
180
+
181
+ ``normalize`` argument has no effect on 32-bit floating-point WAV and other formats, such as
182
+ ``flac`` and ``mp3``.
183
+
184
+ For these formats, this function always returns ``float32`` Tensor with values.
185
+
186
+ Note:
187
+ ``filepath`` argument is intentionally annotated as ``str`` only, even though it accepts
188
+ ``pathlib.Path`` object as well. This is for the consistency with ``"sox_io"`` backend,
189
+ which has a restriction on type annotation due to TorchScript compiler compatiblity.
190
+
191
+ Args:
192
+ filepath (path-like object or file-like object):
193
+ Source of audio data.
194
+ frame_offset (int, optional):
195
+ Number of frames to skip before start reading data.
196
+ num_frames (int, optional):
197
+ Maximum number of frames to read. ``-1`` reads all the remaining samples,
198
+ starting from ``frame_offset``.
199
+ This function may return the less number of frames if there is not enough
200
+ frames in the given file.
201
+ normalize (bool, optional):
202
+ When ``True``, this function converts the native sample type to ``float32``.
203
+ Default: ``True``.
204
+
205
+ If input file is integer WAV, giving ``False`` will change the resulting Tensor type to
206
+ integer type.
207
+ This argument has no effect for formats other than integer WAV type.
208
+
209
+ channels_first (bool, optional):
210
+ When True, the returned Tensor has dimension `[channel, time]`.
211
+ Otherwise, the returned Tensor's dimension is `[time, channel]`.
212
+ format (str or None, optional):
213
+ Not used. PySoundFile does not accept format hint.
214
+
215
+ Returns:
216
+ (torch.Tensor, int): Resulting Tensor and sample rate.
217
+ If the input file has integer wav format and normalization is off, then it has
218
+ integer type, else ``float32`` type. If ``channels_first=True``, it has
219
+ `[channel, time]` else `[time, channel]`.
220
+ """
221
+ with soundfile.SoundFile(filepath, "r") as file_:
222
+ if file_.format != "WAV" or normalize:
223
+ dtype = "float32"
224
+ elif file_.subtype not in _SUBTYPE2DTYPE:
225
+ raise ValueError(f"Unsupported subtype: {file_.subtype}")
226
+ else:
227
+ dtype = _SUBTYPE2DTYPE[file_.subtype]
228
+
229
+ frames = file_._prepare_read(frame_offset, None, num_frames)
230
+ waveform = file_.read(frames, dtype, always_2d=True)
231
+ sample_rate = file_.samplerate
232
+
233
+ waveform = torch.from_numpy(waveform)
234
+ if channels_first:
235
+ waveform = waveform.t()
236
+ return waveform, sample_rate
237
+
238
+
239
+ def _get_subtype_for_wav(dtype: torch.dtype, encoding: str, bits_per_sample: int):
240
+ if not encoding:
241
+ if not bits_per_sample:
242
+ subtype = {
243
+ torch.uint8: "PCM_U8",
244
+ torch.int16: "PCM_16",
245
+ torch.int32: "PCM_32",
246
+ torch.float32: "FLOAT",
247
+ torch.float64: "DOUBLE",
248
+ }.get(dtype)
249
+ if not subtype:
250
+ raise ValueError(f"Unsupported dtype for wav: {dtype}")
251
+ return subtype
252
+ if bits_per_sample == 8:
253
+ return "PCM_U8"
254
+ return f"PCM_{bits_per_sample}"
255
+ if encoding == "PCM_S":
256
+ if not bits_per_sample:
257
+ return "PCM_32"
258
+ if bits_per_sample == 8:
259
+ raise ValueError("wav does not support 8-bit signed PCM encoding.")
260
+ return f"PCM_{bits_per_sample}"
261
+ if encoding == "PCM_U":
262
+ if bits_per_sample in (None, 8):
263
+ return "PCM_U8"
264
+ raise ValueError("wav only supports 8-bit unsigned PCM encoding.")
265
+ if encoding == "PCM_F":
266
+ if bits_per_sample in (None, 32):
267
+ return "FLOAT"
268
+ if bits_per_sample == 64:
269
+ return "DOUBLE"
270
+ raise ValueError("wav only supports 32/64-bit float PCM encoding.")
271
+ if encoding == "ULAW":
272
+ if bits_per_sample in (None, 8):
273
+ return "ULAW"
274
+ raise ValueError("wav only supports 8-bit mu-law encoding.")
275
+ if encoding == "ALAW":
276
+ if bits_per_sample in (None, 8):
277
+ return "ALAW"
278
+ raise ValueError("wav only supports 8-bit a-law encoding.")
279
+ raise ValueError(f"wav does not support {encoding}.")
280
+
281
+
282
+ def _get_subtype_for_sphere(encoding: str, bits_per_sample: int):
283
+ if encoding in (None, "PCM_S"):
284
+ return f"PCM_{bits_per_sample}" if bits_per_sample else "PCM_32"
285
+ if encoding in ("PCM_U", "PCM_F"):
286
+ raise ValueError(f"sph does not support {encoding} encoding.")
287
+ if encoding == "ULAW":
288
+ if bits_per_sample in (None, 8):
289
+ return "ULAW"
290
+ raise ValueError("sph only supports 8-bit for mu-law encoding.")
291
+ if encoding == "ALAW":
292
+ return "ALAW"
293
+ raise ValueError(f"sph does not support {encoding}.")
294
+
295
+
296
+ def _get_subtype(dtype: torch.dtype, format: str, encoding: str, bits_per_sample: int):
297
+ if format == "wav":
298
+ return _get_subtype_for_wav(dtype, encoding, bits_per_sample)
299
+ if format == "flac":
300
+ if encoding:
301
+ raise ValueError("flac does not support encoding.")
302
+ if not bits_per_sample:
303
+ return "PCM_16"
304
+ if bits_per_sample > 24:
305
+ raise ValueError("flac does not support bits_per_sample > 24.")
306
+ return "PCM_S8" if bits_per_sample == 8 else f"PCM_{bits_per_sample}"
307
+ if format in ("ogg", "vorbis"):
308
+ if bits_per_sample:
309
+ raise ValueError("ogg/vorbis does not support bits_per_sample.")
310
+ if encoding is None or encoding == "vorbis":
311
+ return "VORBIS"
312
+ if encoding == "opus":
313
+ return "OPUS"
314
+ raise ValueError(f"Unexpected encoding: {encoding}")
315
+ if format == "mp3":
316
+ return "MPEG_LAYER_III"
317
+ if format == "sph":
318
+ return _get_subtype_for_sphere(encoding, bits_per_sample)
319
+ if format in ("nis", "nist"):
320
+ return "PCM_16"
321
+ raise ValueError(f"Unsupported format: {format}")
322
+
323
+
324
+ @_requires_soundfile
325
+ def save(
326
+ filepath: str,
327
+ src: torch.Tensor,
328
+ sample_rate: int,
329
+ channels_first: bool = True,
330
+ compression: Optional[float] = None,
331
+ format: Optional[str] = None,
332
+ encoding: Optional[str] = None,
333
+ bits_per_sample: Optional[int] = None,
334
+ ):
335
+ """Save audio data to file.
336
+
337
+ Note:
338
+ The formats this function can handle depend on the soundfile installation.
339
+ This function is tested on the following formats;
340
+
341
+ * WAV
342
+
343
+ * 32-bit floating-point
344
+ * 32-bit signed integer
345
+ * 16-bit signed integer
346
+ * 8-bit unsigned integer
347
+
348
+ * FLAC
349
+ * OGG/VORBIS
350
+ * SPHERE
351
+
352
+ Note:
353
+ ``filepath`` argument is intentionally annotated as ``str`` only, even though it accepts
354
+ ``pathlib.Path`` object as well. This is for the consistency with ``"sox_io"`` backend,
355
+ which has a restriction on type annotation due to TorchScript compiler compatiblity.
356
+
357
+ Args:
358
+ filepath (str or pathlib.Path): Path to audio file.
359
+ src (torch.Tensor): Audio data to save. must be 2D tensor.
360
+ sample_rate (int): sampling rate
361
+ channels_first (bool, optional): If ``True``, the given tensor is interpreted as `[channel, time]`,
362
+ otherwise `[time, channel]`.
363
+ compression (float of None, optional): Not used.
364
+ It is here only for interface compatibility reson with "sox_io" backend.
365
+ format (str or None, optional): Override the audio format.
366
+ When ``filepath`` argument is path-like object, audio format is
367
+ inferred from file extension. If the file extension is missing or
368
+ different, you can specify the correct format with this argument.
369
+
370
+ When ``filepath`` argument is file-like object,
371
+ this argument is required.
372
+
373
+ Valid values are ``"wav"``, ``"ogg"``, ``"vorbis"``,
374
+ ``"flac"`` and ``"sph"``.
375
+ encoding (str or None, optional): Changes the encoding for supported formats.
376
+ This argument is effective only for supported formats, sush as
377
+ ``"wav"``, ``""flac"`` and ``"sph"``. Valid values are;
378
+
379
+ - ``"PCM_S"`` (signed integer Linear PCM)
380
+ - ``"PCM_U"`` (unsigned integer Linear PCM)
381
+ - ``"PCM_F"`` (floating point PCM)
382
+ - ``"ULAW"`` (mu-law)
383
+ - ``"ALAW"`` (a-law)
384
+
385
+ bits_per_sample (int or None, optional): Changes the bit depth for the
386
+ supported formats.
387
+ When ``format`` is one of ``"wav"``, ``"flac"`` or ``"sph"``,
388
+ you can change the bit depth.
389
+ Valid values are ``8``, ``16``, ``24``, ``32`` and ``64``.
390
+
391
+ Supported formats/encodings/bit depth/compression are:
392
+
393
+ ``"wav"``
394
+ - 32-bit floating-point PCM
395
+ - 32-bit signed integer PCM
396
+ - 24-bit signed integer PCM
397
+ - 16-bit signed integer PCM
398
+ - 8-bit unsigned integer PCM
399
+ - 8-bit mu-law
400
+ - 8-bit a-law
401
+
402
+ Note:
403
+ Default encoding/bit depth is determined by the dtype of
404
+ the input Tensor.
405
+
406
+ ``"flac"``
407
+ - 8-bit
408
+ - 16-bit (default)
409
+ - 24-bit
410
+
411
+ ``"ogg"``, ``"vorbis"``
412
+ - Doesn't accept changing configuration.
413
+
414
+ ``"sph"``
415
+ - 8-bit signed integer PCM
416
+ - 16-bit signed integer PCM
417
+ - 24-bit signed integer PCM
418
+ - 32-bit signed integer PCM (default)
419
+ - 8-bit mu-law
420
+ - 8-bit a-law
421
+ - 16-bit a-law
422
+ - 24-bit a-law
423
+ - 32-bit a-law
424
+
425
+ """
426
+ if src.ndim != 2:
427
+ raise ValueError(f"Expected 2D Tensor, got {src.ndim}D.")
428
+ if compression is not None:
429
+ warnings.warn(
430
+ '`save` function of "soundfile" backend does not support "compression" parameter. '
431
+ "The argument is silently ignored."
432
+ )
433
+ if hasattr(filepath, "write"):
434
+ if format is None:
435
+ raise RuntimeError("`format` is required when saving to file object.")
436
+ ext = format.lower()
437
+ else:
438
+ ext = str(filepath).split(".")[-1].lower()
439
+
440
+ if bits_per_sample not in (None, 8, 16, 24, 32, 64):
441
+ raise ValueError("Invalid bits_per_sample.")
442
+ if bits_per_sample == 24:
443
+ warnings.warn(
444
+ "Saving audio with 24 bits per sample might warp samples near -1. "
445
+ "Using 16 bits per sample might be able to avoid this."
446
+ )
447
+ subtype = _get_subtype(src.dtype, ext, encoding, bits_per_sample)
448
+
449
+ # sph is a extension used in TED-LIUM but soundfile does not recognize it as NIST format,
450
+ # so we extend the extensions manually here
451
+ if ext in ["nis", "nist", "sph"] and format is None:
452
+ format = "NIST"
453
+
454
+ if channels_first:
455
+ src = src.t()
456
+
457
+ soundfile.write(file=filepath, data=src, samplerate=sample_rate, subtype=subtype, format=format)
lib/python3.12/site-packages/torchaudio/_backend/sox.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import BinaryIO, Optional, Tuple, Union
3
+
4
+ import torch
5
+ import torchaudio
6
+
7
+ from .backend import Backend
8
+ from .common import AudioMetaData
9
+
10
+ sox_ext = torchaudio._extension.lazy_import_sox_ext()
11
+
12
+
13
+ class SoXBackend(Backend):
14
+ @staticmethod
15
+ def info(uri: Union[BinaryIO, str, os.PathLike], format: Optional[str], buffer_size: int = 4096) -> AudioMetaData:
16
+ if hasattr(uri, "read"):
17
+ raise ValueError(
18
+ "SoX backend does not support reading from file-like objects. ",
19
+ "Please use an alternative backend that does support reading from file-like objects, e.g. FFmpeg.",
20
+ )
21
+ else:
22
+ sinfo = sox_ext.get_info(uri, format)
23
+ if sinfo:
24
+ return AudioMetaData(*sinfo)
25
+ else:
26
+ raise RuntimeError(f"Failed to fetch metadata for {uri}.")
27
+
28
+ @staticmethod
29
+ def load(
30
+ uri: Union[BinaryIO, str, os.PathLike],
31
+ frame_offset: int = 0,
32
+ num_frames: int = -1,
33
+ normalize: bool = True,
34
+ channels_first: bool = True,
35
+ format: Optional[str] = None,
36
+ buffer_size: int = 4096,
37
+ ) -> Tuple[torch.Tensor, int]:
38
+ if hasattr(uri, "read"):
39
+ raise ValueError(
40
+ "SoX backend does not support loading from file-like objects. ",
41
+ "Please use an alternative backend that does support loading from file-like objects, e.g. FFmpeg.",
42
+ )
43
+ else:
44
+ ret = sox_ext.load_audio_file(uri, frame_offset, num_frames, normalize, channels_first, format)
45
+ if not ret:
46
+ raise RuntimeError(f"Failed to load audio from {uri}.")
47
+ return ret
48
+
49
+ @staticmethod
50
+ def save(
51
+ uri: Union[BinaryIO, str, os.PathLike],
52
+ src: torch.Tensor,
53
+ sample_rate: int,
54
+ channels_first: bool = True,
55
+ format: Optional[str] = None,
56
+ encoding: Optional[str] = None,
57
+ bits_per_sample: Optional[int] = None,
58
+ buffer_size: int = 4096,
59
+ compression: Optional[Union[torchaudio.io.CodecConfig, float, int]] = None,
60
+ ) -> None:
61
+ if not isinstance(compression, (float, int, type(None))):
62
+ raise ValueError(
63
+ "SoX backend expects non-`None` value for argument `compression` to be of ",
64
+ f"type `float` or `int`, but received value of type {type(compression)}",
65
+ )
66
+ if hasattr(uri, "write"):
67
+ raise ValueError(
68
+ "SoX backend does not support writing to file-like objects. ",
69
+ "Please use an alternative backend that does support writing to file-like objects, e.g. FFmpeg.",
70
+ )
71
+ else:
72
+ sox_ext.save_audio_file(
73
+ uri,
74
+ src,
75
+ sample_rate,
76
+ channels_first,
77
+ compression,
78
+ format,
79
+ encoding,
80
+ bits_per_sample,
81
+ )
82
+
83
+ @staticmethod
84
+ def can_decode(uri: Union[BinaryIO, str, os.PathLike], format: Optional[str]) -> bool:
85
+ # i.e. not a file-like object.
86
+ return not hasattr(uri, "read")
87
+
88
+ @staticmethod
89
+ def can_encode(uri: Union[BinaryIO, str, os.PathLike], format: Optional[str]) -> bool:
90
+ # i.e. not a file-like object.
91
+ return not hasattr(uri, "write")
lib/python3.12/site-packages/torchaudio/_backend/utils.py ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from functools import lru_cache
3
+ from typing import BinaryIO, Dict, Optional, Tuple, Type, Union
4
+
5
+ import torch
6
+
7
+ from torchaudio._extension import lazy_import_sox_ext
8
+ from torchaudio.io import CodecConfig
9
+ from torio._extension import lazy_import_ffmpeg_ext
10
+
11
+ from . import soundfile_backend
12
+
13
+ from .backend import Backend
14
+ from .common import AudioMetaData
15
+ from .ffmpeg import FFmpegBackend
16
+ from .soundfile import SoundfileBackend
17
+ from .sox import SoXBackend
18
+
19
+
20
+ @lru_cache(None)
21
+ def get_available_backends() -> Dict[str, Type[Backend]]:
22
+ backend_specs: Dict[str, Type[Backend]] = {}
23
+ if lazy_import_ffmpeg_ext().is_available():
24
+ backend_specs["ffmpeg"] = FFmpegBackend
25
+ if lazy_import_sox_ext().is_available():
26
+ backend_specs["sox"] = SoXBackend
27
+ if soundfile_backend._IS_SOUNDFILE_AVAILABLE:
28
+ backend_specs["soundfile"] = SoundfileBackend
29
+ return backend_specs
30
+
31
+
32
+ def get_backend(backend_name, backends) -> Backend:
33
+ if backend := backends.get(backend_name):
34
+ return backend
35
+ else:
36
+ raise ValueError(
37
+ f"Unsupported backend '{backend_name}' specified; ",
38
+ f"please select one of {list(backends.keys())} instead.",
39
+ )
40
+
41
+
42
+ def get_info_func():
43
+ backends = get_available_backends()
44
+
45
+ def dispatcher(
46
+ uri: Union[BinaryIO, str, os.PathLike], format: Optional[str], backend_name: Optional[str]
47
+ ) -> Backend:
48
+ if backend_name is not None:
49
+ return get_backend(backend_name, backends)
50
+
51
+ for backend in backends.values():
52
+ if backend.can_decode(uri, format):
53
+ return backend
54
+ raise RuntimeError(f"Couldn't find appropriate backend to handle uri {uri} and format {format}.")
55
+
56
+ def info(
57
+ uri: Union[BinaryIO, str, os.PathLike],
58
+ format: Optional[str] = None,
59
+ buffer_size: int = 4096,
60
+ backend: Optional[str] = None,
61
+ ) -> AudioMetaData:
62
+ """Get signal information of an audio file.
63
+
64
+ Note:
65
+ When the input type is file-like object, this function cannot
66
+ get the correct length (``num_samples``) for certain formats,
67
+ such as ``vorbis``.
68
+ In this case, the value of ``num_samples`` is ``0``.
69
+
70
+ Args:
71
+ uri (path-like object or file-like object):
72
+ Source of audio data. The following types are accepted:
73
+
74
+ * ``path-like``: File path or URL.
75
+ * ``file-like``: Object with ``read(size: int) -> bytes`` method,
76
+ which returns byte string of at most ``size`` length.
77
+
78
+ format (str or None, optional):
79
+ If not ``None``, interpreted as hint that may allow backend to override the detected format.
80
+ (Default: ``None``)
81
+
82
+ buffer_size (int, optional):
83
+ Size of buffer to use when processing file-like objects, in bytes. (Default: ``4096``)
84
+
85
+ backend (str or None, optional):
86
+ I/O backend to use.
87
+ If ``None``, function selects backend given input and available backends.
88
+ Otherwise, must be one of [``"ffmpeg"``, ``"sox"``, ``"soundfile"``],
89
+ with the corresponding backend available.
90
+ (Default: ``None``)
91
+
92
+ .. seealso::
93
+ :ref:`backend`
94
+
95
+ Returns:
96
+ AudioMetaData
97
+ """
98
+ backend = dispatcher(uri, format, backend)
99
+ return backend.info(uri, format, buffer_size)
100
+
101
+ return info
102
+
103
+
104
+ def get_load_func():
105
+ backends = get_available_backends()
106
+
107
+ def dispatcher(
108
+ uri: Union[BinaryIO, str, os.PathLike], format: Optional[str], backend_name: Optional[str]
109
+ ) -> Backend:
110
+ if backend_name is not None:
111
+ return get_backend(backend_name, backends)
112
+
113
+ for backend in backends.values():
114
+ if backend.can_decode(uri, format):
115
+ return backend
116
+ raise RuntimeError(f"Couldn't find appropriate backend to handle uri {uri} and format {format}.")
117
+
118
+ def load(
119
+ uri: Union[BinaryIO, str, os.PathLike],
120
+ frame_offset: int = 0,
121
+ num_frames: int = -1,
122
+ normalize: bool = True,
123
+ channels_first: bool = True,
124
+ format: Optional[str] = None,
125
+ buffer_size: int = 4096,
126
+ backend: Optional[str] = None,
127
+ ) -> Tuple[torch.Tensor, int]:
128
+ """Load audio data from source.
129
+
130
+ By default (``normalize=True``, ``channels_first=True``), this function returns Tensor with
131
+ ``float32`` dtype, and the shape of `[channel, time]`.
132
+
133
+ Note:
134
+ The formats this function can handle depend on the availability of backends.
135
+ Please use the following functions to fetch the supported formats.
136
+
137
+ - FFmpeg: :py:func:`torchaudio.utils.ffmpeg_utils.get_audio_decoders`
138
+ - Sox: :py:func:`torchaudio.utils.sox_utils.list_read_formats`
139
+ - SoundFile: Refer to `the official document <https://pysoundfile.readthedocs.io/>`__.
140
+
141
+ .. warning::
142
+
143
+ ``normalize`` argument does not perform volume normalization.
144
+ It only converts the sample type to `torch.float32` from the native sample
145
+ type.
146
+
147
+ When the input format is WAV with integer type, such as 32-bit signed integer, 16-bit
148
+ signed integer, 24-bit signed integer, and 8-bit unsigned integer, by providing ``normalize=False``,
149
+ this function can return integer Tensor, where the samples are expressed within the whole range
150
+ of the corresponding dtype, that is, ``int32`` tensor for 32-bit signed PCM,
151
+ ``int16`` for 16-bit signed PCM and ``uint8`` for 8-bit unsigned PCM. Since torch does not
152
+ support ``int24`` dtype, 24-bit signed PCM are converted to ``int32`` tensors.
153
+
154
+ ``normalize`` argument has no effect on 32-bit floating-point WAV and other formats, such as
155
+ ``flac`` and ``mp3``.
156
+
157
+ For these formats, this function always returns ``float32`` Tensor with values.
158
+
159
+
160
+ Args:
161
+ uri (path-like object or file-like object):
162
+ Source of audio data.
163
+ frame_offset (int, optional):
164
+ Number of frames to skip before start reading data.
165
+ num_frames (int, optional):
166
+ Maximum number of frames to read. ``-1`` reads all the remaining samples,
167
+ starting from ``frame_offset``.
168
+ This function may return the less number of frames if there is not enough
169
+ frames in the given file.
170
+ normalize (bool, optional):
171
+ When ``True``, this function converts the native sample type to ``float32``.
172
+ Default: ``True``.
173
+
174
+ If input file is integer WAV, giving ``False`` will change the resulting Tensor type to
175
+ integer type.
176
+ This argument has no effect for formats other than integer WAV type.
177
+
178
+ channels_first (bool, optional):
179
+ When True, the returned Tensor has dimension `[channel, time]`.
180
+ Otherwise, the returned Tensor's dimension is `[time, channel]`.
181
+
182
+ format (str or None, optional):
183
+ If not ``None``, interpreted as hint that may allow backend to override the detected format.
184
+ (Default: ``None``)
185
+
186
+ buffer_size (int, optional):
187
+ Size of buffer to use when processing file-like objects, in bytes. (Default: ``4096``)
188
+
189
+ backend (str or None, optional):
190
+ I/O backend to use.
191
+ If ``None``, function selects backend given input and available backends.
192
+ Otherwise, must be one of [``"ffmpeg"``, ``"sox"``, ``"soundfile"``],
193
+ with the corresponding backend being available. (Default: ``None``)
194
+
195
+ .. seealso::
196
+ :ref:`backend`
197
+
198
+ Returns:
199
+ (torch.Tensor, int): Resulting Tensor and sample rate.
200
+ If the input file has integer wav format and normalization is off, then it has
201
+ integer type, else ``float32`` type. If ``channels_first=True``, it has
202
+ `[channel, time]` else `[time, channel]`.
203
+ """
204
+ backend = dispatcher(uri, format, backend)
205
+ return backend.load(uri, frame_offset, num_frames, normalize, channels_first, format, buffer_size)
206
+
207
+ return load
208
+
209
+
210
+ def get_save_func():
211
+ backends = get_available_backends()
212
+
213
+ def dispatcher(
214
+ uri: Union[BinaryIO, str, os.PathLike], format: Optional[str], backend_name: Optional[str]
215
+ ) -> Backend:
216
+ if backend_name is not None:
217
+ return get_backend(backend_name, backends)
218
+
219
+ for backend in backends.values():
220
+ if backend.can_encode(uri, format):
221
+ return backend
222
+ raise RuntimeError(f"Couldn't find appropriate backend to handle uri {uri} and format {format}.")
223
+
224
+ def save(
225
+ uri: Union[BinaryIO, str, os.PathLike],
226
+ src: torch.Tensor,
227
+ sample_rate: int,
228
+ channels_first: bool = True,
229
+ format: Optional[str] = None,
230
+ encoding: Optional[str] = None,
231
+ bits_per_sample: Optional[int] = None,
232
+ buffer_size: int = 4096,
233
+ backend: Optional[str] = None,
234
+ compression: Optional[Union[CodecConfig, float, int]] = None,
235
+ ):
236
+ """Save audio data to file.
237
+
238
+ Note:
239
+ The formats this function can handle depend on the availability of backends.
240
+ Please use the following functions to fetch the supported formats.
241
+
242
+ - FFmpeg: :py:func:`torchaudio.utils.ffmpeg_utils.get_audio_encoders`
243
+ - Sox: :py:func:`torchaudio.utils.sox_utils.list_write_formats`
244
+ - SoundFile: Refer to `the official document <https://pysoundfile.readthedocs.io/>`__.
245
+
246
+ Args:
247
+ uri (str or pathlib.Path): Path to audio file.
248
+ src (torch.Tensor): Audio data to save. must be 2D tensor.
249
+ sample_rate (int): sampling rate
250
+ channels_first (bool, optional): If ``True``, the given tensor is interpreted as `[channel, time]`,
251
+ otherwise `[time, channel]`.
252
+ format (str or None, optional): Override the audio format.
253
+ When ``uri`` argument is path-like object, audio format is
254
+ inferred from file extension. If the file extension is missing or
255
+ different, you can specify the correct format with this argument.
256
+
257
+ When ``uri`` argument is file-like object,
258
+ this argument is required.
259
+
260
+ Valid values are ``"wav"``, ``"ogg"``, and ``"flac"``.
261
+ encoding (str or None, optional): Changes the encoding for supported formats.
262
+ This argument is effective only for supported formats, i.e.
263
+ ``"wav"`` and ``""flac"```. Valid values are
264
+
265
+ - ``"PCM_S"`` (signed integer Linear PCM)
266
+ - ``"PCM_U"`` (unsigned integer Linear PCM)
267
+ - ``"PCM_F"`` (floating point PCM)
268
+ - ``"ULAW"`` (mu-law)
269
+ - ``"ALAW"`` (a-law)
270
+
271
+ bits_per_sample (int or None, optional): Changes the bit depth for the
272
+ supported formats.
273
+ When ``format`` is one of ``"wav"`` and ``"flac"``,
274
+ you can change the bit depth.
275
+ Valid values are ``8``, ``16``, ``24``, ``32`` and ``64``.
276
+
277
+ buffer_size (int, optional):
278
+ Size of buffer to use when processing file-like objects, in bytes. (Default: ``4096``)
279
+
280
+ backend (str or None, optional):
281
+ I/O backend to use.
282
+ If ``None``, function selects backend given input and available backends.
283
+ Otherwise, must be one of [``"ffmpeg"``, ``"sox"``, ``"soundfile"``],
284
+ with the corresponding backend being available.
285
+ (Default: ``None``)
286
+
287
+ .. seealso::
288
+ :ref:`backend`
289
+
290
+ compression (CodecConfig, float, int, or None, optional):
291
+ Compression configuration to apply.
292
+
293
+ If the selected backend is FFmpeg, an instance of :py:class:`CodecConfig` must be provided.
294
+
295
+ Otherwise, if the selected backend is SoX, a float or int value corresponding to option ``-C`` of the
296
+ ``sox`` command line interface must be provided. For instance:
297
+
298
+ ``"mp3"``
299
+ Either bitrate (in ``kbps``) with quality factor, such as ``128.2``, or
300
+ VBR encoding with quality factor such as ``-4.2``. Default: ``-4.5``.
301
+
302
+ ``"flac"``
303
+ Whole number from ``0`` to ``8``. ``8`` is default and highest compression.
304
+
305
+ ``"ogg"``, ``"vorbis"``
306
+ Number from ``-1`` to ``10``; ``-1`` is the highest compression
307
+ and lowest quality. Default: ``3``.
308
+
309
+ Refer to http://sox.sourceforge.net/soxformat.html for more details.
310
+
311
+ """
312
+ backend = dispatcher(uri, format, backend)
313
+ return backend.save(
314
+ uri, src, sample_rate, channels_first, format, encoding, bits_per_sample, buffer_size, compression
315
+ )
316
+
317
+ return save
lib/python3.12/site-packages/torchaudio/_extension/__init__.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os
3
+ import sys
4
+
5
+ from torchaudio._internal.module_utils import fail_with_message, is_module_available, no_op
6
+
7
+ from .utils import _check_cuda_version, _init_dll_path, _init_sox, _LazyImporter, _load_lib
8
+
9
+ _LG = logging.getLogger(__name__)
10
+
11
+
12
+ # Note:
13
+ # `_check_cuda_version` is not meant to be used by regular users.
14
+ # Builder uses it for debugging purpose, so we export it.
15
+ # https://github.com/pytorch/builder/blob/e2e4542b8eb0bdf491214451a1a4128bd606cce2/test/smoke_test/smoke_test.py#L80
16
+ __all__ = [
17
+ "_check_cuda_version",
18
+ "_IS_TORCHAUDIO_EXT_AVAILABLE",
19
+ "_IS_RIR_AVAILABLE",
20
+ "lazy_import_sox_ext",
21
+ ]
22
+
23
+
24
+ if os.name == "nt" and (3, 8) <= sys.version_info < (3, 9):
25
+ _init_dll_path()
26
+
27
+
28
+ # When the extension module is built, we initialize it.
29
+ # In case of an error, we do not catch the failure as it suggests there is something
30
+ # wrong with the installation.
31
+ _IS_TORCHAUDIO_EXT_AVAILABLE = is_module_available("torchaudio.lib._torchaudio")
32
+ # RIR features are implemented in _torchaudio extension, but they can be individually
33
+ # turned on/off at build time. Available means that _torchaudio is loaded properly, and
34
+ # RIR features are found there.
35
+ _IS_RIR_AVAILABLE = False
36
+ _IS_ALIGN_AVAILABLE = False
37
+ if _IS_TORCHAUDIO_EXT_AVAILABLE:
38
+ _load_lib("libtorchaudio")
39
+
40
+ import torchaudio.lib._torchaudio # noqa
41
+
42
+ _check_cuda_version()
43
+ _IS_RIR_AVAILABLE = torchaudio.lib._torchaudio.is_rir_available()
44
+ _IS_ALIGN_AVAILABLE = torchaudio.lib._torchaudio.is_align_available()
45
+
46
+
47
+ _SOX_EXT = None
48
+
49
+
50
+ def lazy_import_sox_ext():
51
+ """Load SoX integration based on availability in lazy manner"""
52
+
53
+ global _SOX_EXT
54
+ if _SOX_EXT is None:
55
+ _SOX_EXT = _LazyImporter("_torchaudio_sox", _init_sox)
56
+ return _SOX_EXT
57
+
58
+
59
+ fail_if_no_rir = (
60
+ no_op
61
+ if _IS_RIR_AVAILABLE
62
+ else fail_with_message(
63
+ "requires RIR extension, but TorchAudio is not compiled with it. Please build TorchAudio with RIR support."
64
+ )
65
+ )
66
+
67
+ fail_if_no_align = (
68
+ no_op
69
+ if _IS_ALIGN_AVAILABLE
70
+ else fail_with_message(
71
+ "Requires alignment extension, but TorchAudio is not compiled with it. \
72
+ Please build TorchAudio with alignment support."
73
+ )
74
+ )
lib/python3.12/site-packages/torchaudio/_extension/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (2.02 kB). View file
 
lib/python3.12/site-packages/torchaudio/_extension/__pycache__/utils.cpython-312.pyc ADDED
Binary file (8.35 kB). View file
 
lib/python3.12/site-packages/torchaudio/_extension/utils.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module to implement logics used for initializing extensions.
2
+
3
+ The implementations here should be stateless.
4
+ They should not depend on external state.
5
+ Anything that depends on external state should happen in __init__.py
6
+ """
7
+ import importlib
8
+ import logging
9
+ import os
10
+ import types
11
+ from pathlib import Path
12
+
13
+ import torch
14
+ from torchaudio._internal.module_utils import eval_env
15
+
16
+ _LG = logging.getLogger(__name__)
17
+ _LIB_DIR = Path(__file__).parent.parent / "lib"
18
+
19
+
20
+ def _get_lib_path(lib: str):
21
+ suffix = "pyd" if os.name == "nt" else "so"
22
+ path = _LIB_DIR / f"{lib}.{suffix}"
23
+ return path
24
+
25
+
26
+ def _load_lib(lib: str) -> bool:
27
+ """Load extension module
28
+
29
+ Note:
30
+ In case `torchaudio` is deployed with `pex` format, the library file
31
+ is not in a standard location.
32
+ In this case, we expect that `libtorchaudio` is available somewhere
33
+ in the search path of dynamic loading mechanism, so that importing
34
+ `_torchaudio` will have library loader find and load `libtorchaudio`.
35
+ This is the reason why the function should not raising an error when the library
36
+ file is not found.
37
+
38
+ Returns:
39
+ bool:
40
+ True if the library file is found AND the library loaded without failure.
41
+ False if the library file is not found (like in the case where torchaudio
42
+ is deployed with pex format, thus the shared library file is
43
+ in a non-standard location.).
44
+ If the library file is found but there is an issue loading the library,
45
+ (such as missing dependency) then this function raises the exception as-is.
46
+
47
+ Raises:
48
+ Exception:
49
+ If the library file is found, but there is an issue loading the library file,
50
+ (when underlying `ctype.DLL` throws an exception), this function will pass
51
+ the exception as-is, instead of catching it and returning bool.
52
+ The expected case is `OSError` thrown by `ctype.DLL` when a dynamic dependency
53
+ is not found.
54
+ This behavior was chosen because the expected failure case is not recoverable.
55
+ If a dependency is missing, then users have to install it.
56
+ """
57
+ path = _get_lib_path(lib)
58
+ if not path.exists():
59
+ return False
60
+ torch.ops.load_library(path)
61
+ return True
62
+
63
+
64
+ def _import_sox_ext():
65
+ if os.name == "nt":
66
+ raise RuntimeError("sox extension is not supported on Windows")
67
+ if not eval_env("TORCHAUDIO_USE_SOX", True):
68
+ raise RuntimeError("sox extension is disabled. (TORCHAUDIO_USE_SOX=0)")
69
+
70
+ ext = "torchaudio.lib._torchaudio_sox"
71
+
72
+ if not importlib.util.find_spec(ext):
73
+ raise RuntimeError(
74
+ # fmt: off
75
+ "TorchAudio is not built with sox extension. "
76
+ "Please build TorchAudio with libsox support. (BUILD_SOX=1)"
77
+ # fmt: on
78
+ )
79
+
80
+ _load_lib("libtorchaudio_sox")
81
+ return importlib.import_module(ext)
82
+
83
+
84
+ def _init_sox():
85
+ ext = _import_sox_ext()
86
+ ext.set_verbosity(0)
87
+
88
+ import atexit
89
+
90
+ torch.ops.torchaudio_sox.initialize_sox_effects()
91
+ atexit.register(torch.ops.torchaudio_sox.shutdown_sox_effects)
92
+
93
+ # Bundle functions registered with TORCH_LIBRARY into extension
94
+ # so that they can also be accessed in the same (lazy) manner
95
+ # from the extension.
96
+ keys = [
97
+ "get_info",
98
+ "load_audio_file",
99
+ "save_audio_file",
100
+ "apply_effects_tensor",
101
+ "apply_effects_file",
102
+ ]
103
+ for key in keys:
104
+ setattr(ext, key, getattr(torch.ops.torchaudio_sox, key))
105
+
106
+ return ext
107
+
108
+
109
+ class _LazyImporter(types.ModuleType):
110
+ """Lazily import module/extension."""
111
+
112
+ def __init__(self, name, import_func):
113
+ super().__init__(name)
114
+ self.import_func = import_func
115
+ self.module = None
116
+
117
+ # Note:
118
+ # Python caches what was retrieved with `__getattr__`, so this method will not be
119
+ # called again for the same item.
120
+ def __getattr__(self, item):
121
+ self._import_once()
122
+ return getattr(self.module, item)
123
+
124
+ def __repr__(self):
125
+ if self.module is None:
126
+ return f"<module '{self.__module__}.{self.__class__.__name__}(\"{self.name}\")'>"
127
+ return repr(self.module)
128
+
129
+ def __dir__(self):
130
+ self._import_once()
131
+ return dir(self.module)
132
+
133
+ def _import_once(self):
134
+ if self.module is None:
135
+ self.module = self.import_func()
136
+ # Note:
137
+ # By attaching the module attributes to self,
138
+ # module attributes are directly accessible.
139
+ # This allows to avoid calling __getattr__ for every attribute access.
140
+ self.__dict__.update(self.module.__dict__)
141
+
142
+ def is_available(self):
143
+ try:
144
+ self._import_once()
145
+ except Exception:
146
+ return False
147
+ return True
148
+
149
+
150
+ def _init_dll_path():
151
+ # On Windows Python-3.8+ has `os.add_dll_directory` call,
152
+ # which is called to configure dll search path.
153
+ # To find cuda related dlls we need to make sure the
154
+ # conda environment/bin path is configured Please take a look:
155
+ # https://stackoverflow.com/questions/59330863/cant-import-dll-module-in-python
156
+ # Please note: if some path can't be added using add_dll_directory we simply ignore this path
157
+ for path in os.environ.get("PATH", "").split(";"):
158
+ if os.path.exists(path):
159
+ try:
160
+ os.add_dll_directory(path)
161
+ except Exception:
162
+ pass
163
+
164
+
165
+ def _check_cuda_version():
166
+ import torchaudio.lib._torchaudio
167
+
168
+ version = torchaudio.lib._torchaudio.cuda_version()
169
+ if version is not None and torch.version.cuda is not None:
170
+ version_str = str(version)
171
+ ta_version = f"{version_str[:-3]}.{version_str[-2]}"
172
+ t_version = torch.version.cuda.split(".")
173
+ t_version = f"{t_version[0]}.{t_version[1]}"
174
+ if ta_version != t_version:
175
+ raise RuntimeError(
176
+ "Detected that PyTorch and TorchAudio were compiled with different CUDA versions. "
177
+ f"PyTorch has CUDA version {t_version} whereas TorchAudio has CUDA version {ta_version}. "
178
+ "Please install the TorchAudio version that matches your PyTorch version."
179
+ )
180
+ return version
lib/python3.12/site-packages/torchaudio/backend/__init__.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # NOTE:
2
+ # The entire `torchaudio.backend` module is deprecated.
3
+ # New things should be added to `torchaudio._backend`.
4
+ # Only things related to backward compatibility should be placed here.
5
+
6
+ from . import common, no_backend, soundfile_backend, sox_io_backend # noqa
7
+
8
+ __all__ = []
lib/python3.12/site-packages/torchaudio/backend/_no_backend.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ from typing import Callable, Optional, Tuple, Union
3
+
4
+ from torch import Tensor
5
+ from torchaudio import AudioMetaData
6
+
7
+
8
+ def load(
9
+ filepath: Union[str, Path],
10
+ out: Optional[Tensor] = None,
11
+ normalization: Union[bool, float, Callable] = True,
12
+ channels_first: bool = True,
13
+ num_frames: int = 0,
14
+ offset: int = 0,
15
+ filetype: Optional[str] = None,
16
+ ) -> Tuple[Tensor, int]:
17
+ raise RuntimeError("No audio I/O backend is available.")
18
+
19
+
20
+ def save(filepath: str, src: Tensor, sample_rate: int, precision: int = 16, channels_first: bool = True) -> None:
21
+ raise RuntimeError("No audio I/O backend is available.")
22
+
23
+
24
+ def info(filepath: str) -> AudioMetaData:
25
+ raise RuntimeError("No audio I/O backend is available.")
lib/python3.12/site-packages/torchaudio/backend/_sox_io_backend.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import Optional, Tuple
3
+
4
+ import torch
5
+ import torchaudio
6
+ from torchaudio import AudioMetaData
7
+
8
+ sox_ext = torchaudio._extension.lazy_import_sox_ext()
9
+
10
+
11
+ def info(
12
+ filepath: str,
13
+ format: Optional[str] = None,
14
+ ) -> AudioMetaData:
15
+ """Get signal information of an audio file.
16
+
17
+ Args:
18
+ filepath (str):
19
+ Source of audio data.
20
+
21
+ format (str or None, optional):
22
+ Override the format detection with the given format.
23
+ Providing the argument might help when libsox can not infer the format
24
+ from header or extension.
25
+
26
+ Returns:
27
+ AudioMetaData: Metadata of the given audio.
28
+ """
29
+ if not torch.jit.is_scripting():
30
+ if hasattr(filepath, "read"):
31
+ raise RuntimeError("sox_io backend does not support file-like object.")
32
+ filepath = os.fspath(filepath)
33
+ sinfo = sox_ext.get_info(filepath, format)
34
+ return AudioMetaData(*sinfo)
35
+
36
+
37
+ def load(
38
+ filepath: str,
39
+ frame_offset: int = 0,
40
+ num_frames: int = -1,
41
+ normalize: bool = True,
42
+ channels_first: bool = True,
43
+ format: Optional[str] = None,
44
+ ) -> Tuple[torch.Tensor, int]:
45
+ """Load audio data from file.
46
+
47
+ Note:
48
+ This function can handle all the codecs that underlying libsox can handle,
49
+ however it is tested on the following formats;
50
+
51
+ * WAV, AMB
52
+
53
+ * 32-bit floating-point
54
+ * 32-bit signed integer
55
+ * 24-bit signed integer
56
+ * 16-bit signed integer
57
+ * 8-bit unsigned integer (WAV only)
58
+
59
+ * MP3
60
+ * FLAC
61
+ * OGG/VORBIS
62
+ * OPUS
63
+ * SPHERE
64
+ * AMR-NB
65
+
66
+ To load ``MP3``, ``FLAC``, ``OGG/VORBIS``, ``OPUS`` and other codecs ``libsox`` does not
67
+ handle natively, your installation of ``torchaudio`` has to be linked to ``libsox``
68
+ and corresponding codec libraries such as ``libmad`` or ``libmp3lame`` etc.
69
+
70
+ By default (``normalize=True``, ``channels_first=True``), this function returns Tensor with
71
+ ``float32`` dtype, and the shape of `[channel, time]`.
72
+
73
+ .. warning::
74
+
75
+ ``normalize`` argument does not perform volume normalization.
76
+ It only converts the sample type to `torch.float32` from the native sample
77
+ type.
78
+
79
+ When the input format is WAV with integer type, such as 32-bit signed integer, 16-bit
80
+ signed integer, 24-bit signed integer, and 8-bit unsigned integer, by providing ``normalize=False``,
81
+ this function can return integer Tensor, where the samples are expressed within the whole range
82
+ of the corresponding dtype, that is, ``int32`` tensor for 32-bit signed PCM,
83
+ ``int16`` for 16-bit signed PCM and ``uint8`` for 8-bit unsigned PCM. Since torch does not
84
+ support ``int24`` dtype, 24-bit signed PCM are converted to ``int32`` tensors.
85
+
86
+ ``normalize`` argument has no effect on 32-bit floating-point WAV and other formats, such as
87
+ ``flac`` and ``mp3``.
88
+
89
+ For these formats, this function always returns ``float32`` Tensor with values.
90
+
91
+ Args:
92
+ filepath (path-like object): Source of audio data.
93
+ frame_offset (int):
94
+ Number of frames to skip before start reading data.
95
+ num_frames (int, optional):
96
+ Maximum number of frames to read. ``-1`` reads all the remaining samples,
97
+ starting from ``frame_offset``.
98
+ This function may return the less number of frames if there is not enough
99
+ frames in the given file.
100
+ normalize (bool, optional):
101
+ When ``True``, this function converts the native sample type to ``float32``.
102
+ Default: ``True``.
103
+
104
+ If input file is integer WAV, giving ``False`` will change the resulting Tensor type to
105
+ integer type.
106
+ This argument has no effect for formats other than integer WAV type.
107
+
108
+ channels_first (bool, optional):
109
+ When True, the returned Tensor has dimension `[channel, time]`.
110
+ Otherwise, the returned Tensor's dimension is `[time, channel]`.
111
+ format (str or None, optional):
112
+ Override the format detection with the given format.
113
+ Providing the argument might help when libsox can not infer the format
114
+ from header or extension.
115
+
116
+ Returns:
117
+ (torch.Tensor, int): Resulting Tensor and sample rate.
118
+ If the input file has integer wav format and ``normalize=False``, then it has
119
+ integer type, else ``float32`` type. If ``channels_first=True``, it has
120
+ `[channel, time]` else `[time, channel]`.
121
+ """
122
+ if not torch.jit.is_scripting():
123
+ if hasattr(filepath, "read"):
124
+ raise RuntimeError("sox_io backend does not support file-like object.")
125
+ filepath = os.fspath(filepath)
126
+ return sox_ext.load_audio_file(filepath, frame_offset, num_frames, normalize, channels_first, format)
127
+
128
+
129
+ def save(
130
+ filepath: str,
131
+ src: torch.Tensor,
132
+ sample_rate: int,
133
+ channels_first: bool = True,
134
+ compression: Optional[float] = None,
135
+ format: Optional[str] = None,
136
+ encoding: Optional[str] = None,
137
+ bits_per_sample: Optional[int] = None,
138
+ ):
139
+ """Save audio data to file.
140
+
141
+ Args:
142
+ filepath (path-like object): Path to save file.
143
+ src (torch.Tensor): Audio data to save. must be 2D tensor.
144
+ sample_rate (int): sampling rate
145
+ channels_first (bool, optional): If ``True``, the given tensor is interpreted as `[channel, time]`,
146
+ otherwise `[time, channel]`.
147
+ compression (float or None, optional): Used for formats other than WAV.
148
+ This corresponds to ``-C`` option of ``sox`` command.
149
+
150
+ ``"mp3"``
151
+ Either bitrate (in ``kbps``) with quality factor, such as ``128.2``, or
152
+ VBR encoding with quality factor such as ``-4.2``. Default: ``-4.5``.
153
+
154
+ ``"flac"``
155
+ Whole number from ``0`` to ``8``. ``8`` is default and highest compression.
156
+
157
+ ``"ogg"``, ``"vorbis"``
158
+ Number from ``-1`` to ``10``; ``-1`` is the highest compression
159
+ and lowest quality. Default: ``3``.
160
+
161
+ See the detail at http://sox.sourceforge.net/soxformat.html.
162
+ format (str or None, optional): Override the audio format.
163
+ When ``filepath`` argument is path-like object, audio format is infered from
164
+ file extension. If file extension is missing or different, you can specify the
165
+ correct format with this argument.
166
+
167
+ When ``filepath`` argument is file-like object, this argument is required.
168
+
169
+ Valid values are ``"wav"``, ``"mp3"``, ``"ogg"``, ``"vorbis"``, ``"amr-nb"``,
170
+ ``"amb"``, ``"flac"``, ``"sph"``, ``"gsm"``, and ``"htk"``.
171
+
172
+ encoding (str or None, optional): Changes the encoding for the supported formats.
173
+ This argument is effective only for supported formats, such as ``"wav"``, ``""amb"``
174
+ and ``"sph"``. Valid values are;
175
+
176
+ - ``"PCM_S"`` (signed integer Linear PCM)
177
+ - ``"PCM_U"`` (unsigned integer Linear PCM)
178
+ - ``"PCM_F"`` (floating point PCM)
179
+ - ``"ULAW"`` (mu-law)
180
+ - ``"ALAW"`` (a-law)
181
+
182
+ Default values
183
+ If not provided, the default value is picked based on ``format`` and ``bits_per_sample``.
184
+
185
+ ``"wav"``, ``"amb"``
186
+ - | If both ``encoding`` and ``bits_per_sample`` are not provided, the ``dtype`` of the
187
+ | Tensor is used to determine the default value.
188
+
189
+ - ``"PCM_U"`` if dtype is ``uint8``
190
+ - ``"PCM_S"`` if dtype is ``int16`` or ``int32``
191
+ - ``"PCM_F"`` if dtype is ``float32``
192
+
193
+ - ``"PCM_U"`` if ``bits_per_sample=8``
194
+ - ``"PCM_S"`` otherwise
195
+
196
+ ``"sph"`` format;
197
+ - the default value is ``"PCM_S"``
198
+
199
+ bits_per_sample (int or None, optional): Changes the bit depth for the supported formats.
200
+ When ``format`` is one of ``"wav"``, ``"flac"``, ``"sph"``, or ``"amb"``, you can change the
201
+ bit depth. Valid values are ``8``, ``16``, ``32`` and ``64``.
202
+
203
+ Default Value;
204
+ If not provided, the default values are picked based on ``format`` and ``"encoding"``;
205
+
206
+ ``"wav"``, ``"amb"``;
207
+ - | If both ``encoding`` and ``bits_per_sample`` are not provided, the ``dtype`` of the
208
+ | Tensor is used.
209
+
210
+ - ``8`` if dtype is ``uint8``
211
+ - ``16`` if dtype is ``int16``
212
+ - ``32`` if dtype is ``int32`` or ``float32``
213
+
214
+ - ``8`` if ``encoding`` is ``"PCM_U"``, ``"ULAW"`` or ``"ALAW"``
215
+ - ``16`` if ``encoding`` is ``"PCM_S"``
216
+ - ``32`` if ``encoding`` is ``"PCM_F"``
217
+
218
+ ``"flac"`` format;
219
+ - the default value is ``24``
220
+
221
+ ``"sph"`` format;
222
+ - ``16`` if ``encoding`` is ``"PCM_U"``, ``"PCM_S"``, ``"PCM_F"`` or not provided.
223
+ - ``8`` if ``encoding`` is ``"ULAW"`` or ``"ALAW"``
224
+
225
+ ``"amb"`` format;
226
+ - ``8`` if ``encoding`` is ``"PCM_U"``, ``"ULAW"`` or ``"ALAW"``
227
+ - ``16`` if ``encoding`` is ``"PCM_S"`` or not provided.
228
+ - ``32`` if ``encoding`` is ``"PCM_F"``
229
+
230
+ Supported formats/encodings/bit depth/compression are;
231
+
232
+ ``"wav"``, ``"amb"``
233
+ - 32-bit floating-point PCM
234
+ - 32-bit signed integer PCM
235
+ - 24-bit signed integer PCM
236
+ - 16-bit signed integer PCM
237
+ - 8-bit unsigned integer PCM
238
+ - 8-bit mu-law
239
+ - 8-bit a-law
240
+
241
+ Note: Default encoding/bit depth is determined by the dtype of the input Tensor.
242
+
243
+ ``"mp3"``
244
+ Fixed bit rate (such as 128kHz) and variable bit rate compression.
245
+ Default: VBR with high quality.
246
+
247
+ ``"flac"``
248
+ - 8-bit
249
+ - 16-bit
250
+ - 24-bit (default)
251
+
252
+ ``"ogg"``, ``"vorbis"``
253
+ - Different quality level. Default: approx. 112kbps
254
+
255
+ ``"sph"``
256
+ - 8-bit signed integer PCM
257
+ - 16-bit signed integer PCM
258
+ - 24-bit signed integer PCM
259
+ - 32-bit signed integer PCM (default)
260
+ - 8-bit mu-law
261
+ - 8-bit a-law
262
+ - 16-bit a-law
263
+ - 24-bit a-law
264
+ - 32-bit a-law
265
+
266
+ ``"amr-nb"``
267
+ Bitrate ranging from 4.75 kbit/s to 12.2 kbit/s. Default: 4.75 kbit/s
268
+
269
+ ``"gsm"``
270
+ Lossy Speech Compression, CPU intensive.
271
+
272
+ ``"htk"``
273
+ Uses a default single-channel 16-bit PCM format.
274
+
275
+ Note:
276
+ To save into formats that ``libsox`` does not handle natively, (such as ``"mp3"``,
277
+ ``"flac"``, ``"ogg"`` and ``"vorbis"``), your installation of ``torchaudio`` has
278
+ to be linked to ``libsox`` and corresponding codec libraries such as ``libmad``
279
+ or ``libmp3lame`` etc.
280
+ """
281
+ if not torch.jit.is_scripting():
282
+ if hasattr(filepath, "write"):
283
+ raise RuntimeError("sox_io backend does not handle file-like object.")
284
+ filepath = os.fspath(filepath)
285
+ sox_ext.save_audio_file(
286
+ filepath,
287
+ src,
288
+ sample_rate,
289
+ channels_first,
290
+ compression,
291
+ format,
292
+ encoding,
293
+ bits_per_sample,
294
+ )
lib/python3.12/site-packages/torchaudio/backend/common.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def __getattr__(name: str):
2
+ if name == "AudioMetaData":
3
+ import warnings
4
+
5
+ warnings.warn(
6
+ "`torchaudio.backend.common.AudioMetaData` has been moved to "
7
+ "`torchaudio.AudioMetaData`. Please update the import path.",
8
+ stacklevel=2,
9
+ )
10
+ from torchaudio import AudioMetaData
11
+
12
+ return AudioMetaData
13
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
lib/python3.12/site-packages/torchaudio/backend/soundfile_backend.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def __getattr__(name: str):
2
+ import warnings
3
+
4
+ warnings.warn(
5
+ "Torchaudio's I/O functions now support par-call bakcend dispatch. "
6
+ "Importing backend implementation directly is no longer guaranteed to work. "
7
+ "Please use `backend` keyword with load/save/info function, instead of "
8
+ "calling the udnerlying implementation directly.",
9
+ stacklevel=2,
10
+ )
11
+
12
+ from torchaudio._backend import soundfile_backend
13
+
14
+ return getattr(soundfile_backend, name)
lib/python3.12/site-packages/torchaudio/backend/sox_io_backend.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def __getattr__(name: str):
2
+ import warnings
3
+
4
+ warnings.warn(
5
+ "Torchaudio's I/O functions now support par-call bakcend dispatch. "
6
+ "Importing backend implementation directly is no longer guaranteed to work. "
7
+ "Please use `backend` keyword with load/save/info function, instead of "
8
+ "calling the udnerlying implementation directly.",
9
+ stacklevel=2,
10
+ )
11
+
12
+ from . import _sox_io_backend
13
+
14
+ return getattr(_sox_io_backend, name)
lib/python3.12/site-packages/torchaudio/datasets/__pycache__/cmudict.cpython-312.pyc ADDED
Binary file (6.83 kB). View file
 
lib/python3.12/site-packages/torchaudio/datasets/__pycache__/gtzan.cpython-312.pyc ADDED
Binary file (19.4 kB). View file
 
lib/python3.12/site-packages/torchaudio/datasets/__pycache__/ljspeech.cpython-312.pyc ADDED
Binary file (4.77 kB). View file
 
lib/python3.12/site-packages/torchaudio/datasets/__pycache__/musdb_hq.cpython-312.pyc ADDED
Binary file (7.11 kB). View file
 
lib/python3.12/site-packages/torchaudio/functional/__init__.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ._alignment import forced_align, merge_tokens, TokenSpan
2
+ from .filtering import (
3
+ allpass_biquad,
4
+ band_biquad,
5
+ bandpass_biquad,
6
+ bandreject_biquad,
7
+ bass_biquad,
8
+ biquad,
9
+ contrast,
10
+ dcshift,
11
+ deemph_biquad,
12
+ dither,
13
+ equalizer_biquad,
14
+ filtfilt,
15
+ flanger,
16
+ gain,
17
+ highpass_biquad,
18
+ lfilter,
19
+ lowpass_biquad,
20
+ overdrive,
21
+ phaser,
22
+ riaa_biquad,
23
+ treble_biquad,
24
+ vad,
25
+ )
26
+ from .functional import (
27
+ add_noise,
28
+ amplitude_to_DB,
29
+ apply_beamforming,
30
+ apply_codec,
31
+ compute_deltas,
32
+ convolve,
33
+ create_dct,
34
+ DB_to_amplitude,
35
+ deemphasis,
36
+ detect_pitch_frequency,
37
+ edit_distance,
38
+ fftconvolve,
39
+ frechet_distance,
40
+ griffinlim,
41
+ inverse_spectrogram,
42
+ linear_fbanks,
43
+ loudness,
44
+ mask_along_axis,
45
+ mask_along_axis_iid,
46
+ melscale_fbanks,
47
+ mu_law_decoding,
48
+ mu_law_encoding,
49
+ mvdr_weights_rtf,
50
+ mvdr_weights_souden,
51
+ phase_vocoder,
52
+ pitch_shift,
53
+ preemphasis,
54
+ psd,
55
+ resample,
56
+ rnnt_loss,
57
+ rtf_evd,
58
+ rtf_power,
59
+ sliding_window_cmn,
60
+ spectral_centroid,
61
+ spectrogram,
62
+ speed,
63
+ )
64
+
65
+ __all__ = [
66
+ "amplitude_to_DB",
67
+ "compute_deltas",
68
+ "create_dct",
69
+ "melscale_fbanks",
70
+ "linear_fbanks",
71
+ "DB_to_amplitude",
72
+ "loudness",
73
+ "detect_pitch_frequency",
74
+ "griffinlim",
75
+ "mask_along_axis",
76
+ "mask_along_axis_iid",
77
+ "mu_law_encoding",
78
+ "mu_law_decoding",
79
+ "phase_vocoder",
80
+ "sliding_window_cmn",
81
+ "spectrogram",
82
+ "inverse_spectrogram",
83
+ "spectral_centroid",
84
+ "allpass_biquad",
85
+ "band_biquad",
86
+ "bandpass_biquad",
87
+ "bandreject_biquad",
88
+ "bass_biquad",
89
+ "biquad",
90
+ "contrast",
91
+ "dither",
92
+ "dcshift",
93
+ "deemph_biquad",
94
+ "equalizer_biquad",
95
+ "filtfilt",
96
+ "flanger",
97
+ "forced_align",
98
+ "merge_tokens",
99
+ "TokenSpan",
100
+ "gain",
101
+ "highpass_biquad",
102
+ "lfilter",
103
+ "lowpass_biquad",
104
+ "overdrive",
105
+ "phaser",
106
+ "riaa_biquad",
107
+ "treble_biquad",
108
+ "vad",
109
+ "apply_codec",
110
+ "resample",
111
+ "edit_distance",
112
+ "pitch_shift",
113
+ "rnnt_loss",
114
+ "psd",
115
+ "mvdr_weights_souden",
116
+ "mvdr_weights_rtf",
117
+ "rtf_evd",
118
+ "rtf_power",
119
+ "apply_beamforming",
120
+ "fftconvolve",
121
+ "convolve",
122
+ "add_noise",
123
+ "speed",
124
+ "preemphasis",
125
+ "deemphasis",
126
+ "frechet_distance",
127
+ ]
lib/python3.12/site-packages/torchaudio/functional/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (2.07 kB). View file
 
lib/python3.12/site-packages/torchaudio/functional/__pycache__/_alignment.cpython-312.pyc ADDED
Binary file (6.2 kB). View file
 
lib/python3.12/site-packages/torchaudio/functional/__pycache__/filtering.cpython-312.pyc ADDED
Binary file (70.8 kB). View file
 
lib/python3.12/site-packages/torchaudio/functional/_alignment.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+ from typing import List, Optional, Tuple
3
+
4
+ import torch
5
+ from torch import Tensor
6
+ from torchaudio._extension import fail_if_no_align
7
+
8
+ __all__ = []
9
+
10
+
11
+ @fail_if_no_align
12
+ def forced_align(
13
+ log_probs: Tensor,
14
+ targets: Tensor,
15
+ input_lengths: Optional[Tensor] = None,
16
+ target_lengths: Optional[Tensor] = None,
17
+ blank: int = 0,
18
+ ) -> Tuple[Tensor, Tensor]:
19
+ r"""Align a CTC label sequence to an emission.
20
+
21
+ .. devices:: CPU CUDA
22
+
23
+ .. properties:: TorchScript
24
+
25
+ Args:
26
+ log_probs (Tensor): log probability of CTC emission output.
27
+ Tensor of shape `(B, T, C)`. where `B` is the batch size, `T` is the input length,
28
+ `C` is the number of characters in alphabet including blank.
29
+ targets (Tensor): Target sequence. Tensor of shape `(B, L)`,
30
+ where `L` is the target length.
31
+ input_lengths (Tensor or None, optional):
32
+ Lengths of the inputs (max value must each be <= `T`). 1-D Tensor of shape `(B,)`.
33
+ target_lengths (Tensor or None, optional):
34
+ Lengths of the targets. 1-D Tensor of shape `(B,)`.
35
+ blank_id (int, optional): The index of blank symbol in CTC emission. (Default: 0)
36
+
37
+ Returns:
38
+ Tuple(Tensor, Tensor):
39
+ Tensor: Label for each time step in the alignment path computed using forced alignment.
40
+
41
+ Tensor: Log probability scores of the labels for each time step.
42
+
43
+ Note:
44
+ The sequence length of `log_probs` must satisfy:
45
+
46
+
47
+ .. math::
48
+ L_{\text{log\_probs}} \ge L_{\text{label}} + N_{\text{repeat}}
49
+
50
+ where :math:`N_{\text{repeat}}` is the number of consecutively repeated tokens.
51
+ For example, in str `"aabbc"`, the number of repeats are `2`.
52
+
53
+ Note:
54
+ The current version only supports ``batch_size==1``.
55
+ """
56
+ if blank in targets:
57
+ raise ValueError(f"targets Tensor shouldn't contain blank index. Found {targets}.")
58
+ if torch.max(targets) >= log_probs.shape[-1]:
59
+ raise ValueError("targets values must be less than the CTC dimension")
60
+
61
+ if input_lengths is None:
62
+ batch_size, length = log_probs.size(0), log_probs.size(1)
63
+ input_lengths = torch.full((batch_size,), length, dtype=torch.int64, device=log_probs.device)
64
+ if target_lengths is None:
65
+ batch_size, length = targets.size(0), targets.size(1)
66
+ target_lengths = torch.full((batch_size,), length, dtype=torch.int64, device=targets.device)
67
+
68
+ # For TorchScript compatibility
69
+ assert input_lengths is not None
70
+ assert target_lengths is not None
71
+
72
+ paths, scores = torch.ops.torchaudio.forced_align(log_probs, targets, input_lengths, target_lengths, blank)
73
+ return paths, scores
74
+
75
+
76
+ @dataclass
77
+ class TokenSpan:
78
+ """TokenSpan()
79
+ Token with time stamps and score. Returned by :py:func:`merge_tokens`.
80
+ """
81
+
82
+ token: int
83
+ """The token"""
84
+ start: int
85
+ """The start time (inclusive) in emission time axis."""
86
+ end: int
87
+ """The end time (exclusive) in emission time axis."""
88
+ score: float
89
+ """The score of the this token."""
90
+
91
+ def __len__(self) -> int:
92
+ """Returns the time span"""
93
+ return self.end - self.start
94
+
95
+
96
+ def merge_tokens(tokens: Tensor, scores: Tensor, blank: int = 0) -> List[TokenSpan]:
97
+ """Removes repeated tokens and blank tokens from the given CTC token sequence.
98
+
99
+ Args:
100
+ tokens (Tensor): Alignment tokens (unbatched) returned from :py:func:`forced_align`.
101
+ Shape: `(time, )`.
102
+ scores (Tensor): Alignment scores (unbatched) returned from :py:func:`forced_align`.
103
+ Shape: `(time, )`. When computing the token-size score, the given score is averaged
104
+ across the corresponding time span.
105
+
106
+ Returns:
107
+ list of TokenSpan
108
+
109
+ Example:
110
+ >>> aligned_tokens, scores = forced_align(emission, targets, input_lengths, target_lengths)
111
+ >>> token_spans = merge_tokens(aligned_tokens[0], scores[0])
112
+ """
113
+ if tokens.ndim != 1 or scores.ndim != 1:
114
+ raise ValueError("`tokens` and `scores` must be 1D Tensor.")
115
+ if len(tokens) != len(scores):
116
+ raise ValueError("`tokens` and `scores` must be the same length.")
117
+
118
+ diff = torch.diff(
119
+ tokens, prepend=torch.tensor([-1], device=tokens.device), append=torch.tensor([-1], device=tokens.device)
120
+ )
121
+ changes_wo_blank = torch.nonzero((diff != 0)).squeeze().tolist()
122
+ tokens = tokens.tolist()
123
+ spans = [
124
+ TokenSpan(token=token, start=start, end=end, score=scores[start:end].mean().item())
125
+ for start, end in zip(changes_wo_blank[:-1], changes_wo_blank[1:])
126
+ if (token := tokens[start]) != blank
127
+ ]
128
+ return spans
lib/python3.12/site-packages/torchaudio/functional/filtering.py ADDED
@@ -0,0 +1,1670 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import warnings
3
+ from typing import Optional
4
+
5
+ import torch
6
+ from torch import Tensor
7
+
8
+ from torchaudio._extension import _IS_TORCHAUDIO_EXT_AVAILABLE
9
+
10
+
11
+ def _dB2Linear(x: float) -> float:
12
+ return math.exp(x * math.log(10) / 20.0)
13
+
14
+
15
+ def _generate_wave_table(
16
+ wave_type: str,
17
+ data_type: str,
18
+ table_size: int,
19
+ min: float,
20
+ max: float,
21
+ phase: float,
22
+ device: torch.device,
23
+ ) -> Tensor:
24
+ r"""A helper function for phaser. Generates a table with given parameters.
25
+
26
+ Args:
27
+ wave_type (str): SINE or TRIANGULAR
28
+ data_type (str): desired data_type ( `INT` or `FLOAT` )
29
+ table_size (int): desired table size
30
+ min (float): desired min value
31
+ max (float): desired max value
32
+ phase (float): desired phase
33
+ device (torch.device): Torch device on which table must be generated
34
+ Returns:
35
+ Tensor: A 1D tensor with wave table values
36
+ """
37
+
38
+ phase_offset = int(phase / math.pi / 2 * table_size + 0.5)
39
+
40
+ t = torch.arange(table_size, device=device, dtype=torch.int32)
41
+
42
+ point = (t + phase_offset) % table_size
43
+
44
+ d = torch.zeros_like(point, device=device, dtype=torch.float64)
45
+
46
+ if wave_type == "SINE":
47
+ d = (torch.sin(point.to(torch.float64) / table_size * 2 * math.pi) + 1) / 2
48
+ elif wave_type == "TRIANGLE":
49
+ d = point.to(torch.float64) * 2 / table_size
50
+ value = torch.div(4 * point, table_size, rounding_mode="floor")
51
+ d[value == 0] = d[value == 0] + 0.5
52
+ d[value == 1] = 1.5 - d[value == 1]
53
+ d[value == 2] = 1.5 - d[value == 2]
54
+ d[value == 3] = d[value == 3] - 1.5
55
+
56
+ d = d * (max - min) + min
57
+
58
+ if data_type == "INT":
59
+ mask = d < 0
60
+ d[mask] = d[mask] - 0.5
61
+ d[~mask] = d[~mask] + 0.5
62
+ d = d.to(torch.int32)
63
+ elif data_type == "FLOAT":
64
+ d = d.to(torch.float32)
65
+
66
+ return d
67
+
68
+
69
+ def allpass_biquad(waveform: Tensor, sample_rate: int, central_freq: float, Q: float = 0.707) -> Tensor:
70
+ r"""Design two-pole all-pass filter. Similar to SoX implementation.
71
+
72
+ .. devices:: CPU CUDA
73
+
74
+ .. properties:: Autograd TorchScript
75
+
76
+ Args:
77
+ waveform(torch.Tensor): audio waveform of dimension of `(..., time)`
78
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
79
+ central_freq (float or torch.Tensor): central frequency (in Hz)
80
+ Q (float or torch.Tensor, optional): https://en.wikipedia.org/wiki/Q_factor (Default: ``0.707``)
81
+
82
+ Returns:
83
+ Tensor: Waveform of dimension of `(..., time)`
84
+
85
+ Reference:
86
+ - http://sox.sourceforge.net/sox.html
87
+ - https://www.w3.org/2011/audio/audio-eq-cookbook.html#APF
88
+ """
89
+ dtype = waveform.dtype
90
+ device = waveform.device
91
+ central_freq = torch.as_tensor(central_freq, dtype=dtype, device=device)
92
+ Q = torch.as_tensor(Q, dtype=dtype, device=device)
93
+
94
+ w0 = 2 * math.pi * central_freq / sample_rate
95
+
96
+ alpha = torch.sin(w0) / 2 / Q
97
+
98
+ b0 = 1 - alpha
99
+ b1 = -2 * torch.cos(w0)
100
+ b2 = 1 + alpha
101
+ a0 = 1 + alpha
102
+ a1 = -2 * torch.cos(w0)
103
+ a2 = 1 - alpha
104
+ return biquad(waveform, b0, b1, b2, a0, a1, a2)
105
+
106
+
107
+ def band_biquad(
108
+ waveform: Tensor,
109
+ sample_rate: int,
110
+ central_freq: float,
111
+ Q: float = 0.707,
112
+ noise: bool = False,
113
+ ) -> Tensor:
114
+ r"""Design two-pole band filter. Similar to SoX implementation.
115
+
116
+ .. devices:: CPU CUDA
117
+
118
+ .. properties:: Autograd TorchScript
119
+
120
+ Args:
121
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
122
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
123
+ central_freq (float or torch.Tensor): central frequency (in Hz)
124
+ Q (float or torch.Tensor, optional): https://en.wikipedia.org/wiki/Q_factor (Default: ``0.707``).
125
+ noise (bool, optional) : If ``True``, uses the alternate mode for un-pitched audio (e.g. percussion).
126
+ If ``False``, uses mode oriented to pitched audio, i.e. voice, singing,
127
+ or instrumental music (Default: ``False``).
128
+
129
+ Returns:
130
+ Tensor: Waveform of dimension of `(..., time)`
131
+
132
+ Reference:
133
+ - http://sox.sourceforge.net/sox.html
134
+ - https://www.w3.org/2011/audio/audio-eq-cookbook.html#APF
135
+ """
136
+ dtype = waveform.dtype
137
+ device = waveform.device
138
+ central_freq = torch.as_tensor(central_freq, dtype=dtype, device=device)
139
+ Q = torch.as_tensor(Q, dtype=dtype, device=device)
140
+
141
+ w0 = 2 * math.pi * central_freq / sample_rate
142
+ bw_Hz = central_freq / Q
143
+
144
+ a0 = 1.0
145
+ a2 = torch.exp(-2 * math.pi * bw_Hz / sample_rate)
146
+ a1 = -4 * a2 / (1 + a2) * torch.cos(w0)
147
+
148
+ b0 = torch.sqrt(1 - a1 * a1 / (4 * a2)) * (1 - a2)
149
+
150
+ if noise:
151
+ mult = torch.sqrt(((1 + a2) * (1 + a2) - a1 * a1) * (1 - a2) / (1 + a2)) / b0
152
+ b0 = mult * b0
153
+
154
+ b1 = 0.0
155
+ b2 = 0.0
156
+
157
+ return biquad(waveform, b0, b1, b2, a0, a1, a2)
158
+
159
+
160
+ def bandpass_biquad(
161
+ waveform: Tensor,
162
+ sample_rate: int,
163
+ central_freq: float,
164
+ Q: float = 0.707,
165
+ const_skirt_gain: bool = False,
166
+ ) -> Tensor:
167
+ r"""Design two-pole band-pass filter. Similar to SoX implementation.
168
+
169
+ .. devices:: CPU CUDA
170
+
171
+ .. properties:: Autograd TorchScript
172
+
173
+ Args:
174
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
175
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
176
+ central_freq (float or torch.Tensor): central frequency (in Hz)
177
+ Q (float or torch.Tensor, optional): https://en.wikipedia.org/wiki/Q_factor (Default: ``0.707``)
178
+ const_skirt_gain (bool, optional) : If ``True``, uses a constant skirt gain (peak gain = Q).
179
+ If ``False``, uses a constant 0dB peak gain. (Default: ``False``)
180
+
181
+ Returns:
182
+ Tensor: Waveform of dimension of `(..., time)`
183
+
184
+ Reference:
185
+ - http://sox.sourceforge.net/sox.html
186
+ - https://www.w3.org/2011/audio/audio-eq-cookbook.html#APF
187
+ """
188
+ dtype = waveform.dtype
189
+ device = waveform.device
190
+ central_freq = torch.as_tensor(central_freq, dtype=dtype, device=device)
191
+ Q = torch.as_tensor(Q, dtype=dtype, device=device)
192
+
193
+ w0 = 2 * math.pi * central_freq / sample_rate
194
+ alpha = torch.sin(w0) / 2 / Q
195
+
196
+ temp = torch.sin(w0) / 2 if const_skirt_gain else alpha
197
+ b0 = temp
198
+ b1 = 0.0
199
+ b2 = -temp
200
+ a0 = 1 + alpha
201
+ a1 = -2 * torch.cos(w0)
202
+ a2 = 1 - alpha
203
+ return biquad(waveform, b0, b1, b2, a0, a1, a2)
204
+
205
+
206
+ def bandreject_biquad(waveform: Tensor, sample_rate: int, central_freq: float, Q: float = 0.707) -> Tensor:
207
+ r"""Design two-pole band-reject filter. Similar to SoX implementation.
208
+
209
+ .. devices:: CPU CUDA
210
+
211
+ .. properties:: Autograd TorchScript
212
+
213
+ Args:
214
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
215
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
216
+ central_freq (float or torch.Tensor): central frequency (in Hz)
217
+ Q (float or torch.Tensor, optional): https://en.wikipedia.org/wiki/Q_factor (Default: ``0.707``)
218
+
219
+ Returns:
220
+ Tensor: Waveform of dimension of `(..., time)`
221
+
222
+ Reference:
223
+ - http://sox.sourceforge.net/sox.html
224
+ - https://www.w3.org/2011/audio/audio-eq-cookbook.html#APF
225
+ """
226
+ dtype = waveform.dtype
227
+ device = waveform.device
228
+ central_freq = torch.as_tensor(central_freq, dtype=dtype, device=device)
229
+ Q = torch.as_tensor(Q, dtype=dtype, device=device)
230
+
231
+ w0 = 2 * math.pi * central_freq / sample_rate
232
+ alpha = torch.sin(w0) / 2 / Q
233
+
234
+ b0 = 1.0
235
+ b1 = -2 * torch.cos(w0)
236
+ b2 = 1.0
237
+ a0 = 1 + alpha
238
+ a1 = -2 * torch.cos(w0)
239
+ a2 = 1 - alpha
240
+ return biquad(waveform, b0, b1, b2, a0, a1, a2)
241
+
242
+
243
+ def bass_biquad(
244
+ waveform: Tensor,
245
+ sample_rate: int,
246
+ gain: float,
247
+ central_freq: float = 100,
248
+ Q: float = 0.707,
249
+ ) -> Tensor:
250
+ r"""Design a bass tone-control effect. Similar to SoX implementation.
251
+
252
+ .. devices:: CPU CUDA
253
+
254
+ .. properties:: Autograd TorchScript
255
+
256
+ Args:
257
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
258
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
259
+ gain (float or torch.Tensor): desired gain at the boost (or attenuation) in dB.
260
+ central_freq (float or torch.Tensor, optional): central frequency (in Hz). (Default: ``100``)
261
+ Q (float or torch.Tensor, optional): https://en.wikipedia.org/wiki/Q_factor (Default: ``0.707``).
262
+
263
+ Returns:
264
+ Tensor: Waveform of dimension of `(..., time)`
265
+
266
+ Reference:
267
+ - http://sox.sourceforge.net/sox.html
268
+ - https://www.w3.org/2011/audio/audio-eq-cookbook.html#APF
269
+ """
270
+ dtype = waveform.dtype
271
+ device = waveform.device
272
+ central_freq = torch.as_tensor(central_freq, dtype=dtype, device=device)
273
+ Q = torch.as_tensor(Q, dtype=dtype, device=device)
274
+ gain = torch.as_tensor(gain, dtype=dtype, device=device)
275
+
276
+ w0 = 2 * math.pi * central_freq / sample_rate
277
+ alpha = torch.sin(w0) / 2 / Q
278
+ A = torch.exp(gain / 40 * math.log(10))
279
+
280
+ temp1 = 2 * torch.sqrt(A) * alpha
281
+ temp2 = (A - 1) * torch.cos(w0)
282
+ temp3 = (A + 1) * torch.cos(w0)
283
+
284
+ b0 = A * ((A + 1) - temp2 + temp1)
285
+ b1 = 2 * A * ((A - 1) - temp3)
286
+ b2 = A * ((A + 1) - temp2 - temp1)
287
+ a0 = (A + 1) + temp2 + temp1
288
+ a1 = -2 * ((A - 1) + temp3)
289
+ a2 = (A + 1) + temp2 - temp1
290
+
291
+ return biquad(waveform, b0 / a0, b1 / a0, b2 / a0, a0 / a0, a1 / a0, a2 / a0)
292
+
293
+
294
+ def biquad(waveform: Tensor, b0: float, b1: float, b2: float, a0: float, a1: float, a2: float) -> Tensor:
295
+ r"""Perform a biquad filter of input tensor. Initial conditions set to 0.
296
+
297
+ .. devices:: CPU CUDA
298
+
299
+ .. properties:: Autograd TorchScript
300
+
301
+ Args:
302
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
303
+ b0 (float or torch.Tensor): numerator coefficient of current input, x[n]
304
+ b1 (float or torch.Tensor): numerator coefficient of input one time step ago x[n-1]
305
+ b2 (float or torch.Tensor): numerator coefficient of input two time steps ago x[n-2]
306
+ a0 (float or torch.Tensor): denominator coefficient of current output y[n], typically 1
307
+ a1 (float or torch.Tensor): denominator coefficient of current output y[n-1]
308
+ a2 (float or torch.Tensor): denominator coefficient of current output y[n-2]
309
+
310
+ Returns:
311
+ Tensor: Waveform with dimension of `(..., time)`
312
+
313
+ Reference:
314
+ - https://en.wikipedia.org/wiki/Digital_biquad_filter
315
+ """
316
+
317
+ device = waveform.device
318
+ dtype = waveform.dtype
319
+
320
+ b0 = torch.as_tensor(b0, dtype=dtype, device=device).view(1)
321
+ b1 = torch.as_tensor(b1, dtype=dtype, device=device).view(1)
322
+ b2 = torch.as_tensor(b2, dtype=dtype, device=device).view(1)
323
+ a0 = torch.as_tensor(a0, dtype=dtype, device=device).view(1)
324
+ a1 = torch.as_tensor(a1, dtype=dtype, device=device).view(1)
325
+ a2 = torch.as_tensor(a2, dtype=dtype, device=device).view(1)
326
+
327
+ output_waveform = lfilter(
328
+ waveform,
329
+ torch.cat([a0, a1, a2]),
330
+ torch.cat([b0, b1, b2]),
331
+ )
332
+ return output_waveform
333
+
334
+
335
+ def contrast(waveform: Tensor, enhancement_amount: float = 75.0) -> Tensor:
336
+ r"""Apply contrast effect. Similar to SoX implementation.
337
+
338
+ .. devices:: CPU CUDA
339
+
340
+ .. properties:: Autograd TorchScript
341
+
342
+ Comparable with compression, this effect modifies an audio signal to make it sound louder
343
+
344
+ Args:
345
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
346
+ enhancement_amount (float, optional): controls the amount of the enhancement
347
+ Allowed range of values for enhancement_amount : 0-100
348
+ Note that enhancement_amount = 0 still gives a significant contrast enhancement
349
+
350
+ Returns:
351
+ Tensor: Waveform of dimension of `(..., time)`
352
+
353
+ Reference:
354
+ - http://sox.sourceforge.net/sox.html
355
+ """
356
+
357
+ if not 0 <= enhancement_amount <= 100:
358
+ raise ValueError("Allowed range of values for enhancement_amount : 0-100")
359
+
360
+ contrast = enhancement_amount / 750.0
361
+
362
+ temp1 = waveform * (math.pi / 2)
363
+ temp2 = contrast * torch.sin(temp1 * 4)
364
+ output_waveform = torch.sin(temp1 + temp2)
365
+
366
+ return output_waveform
367
+
368
+
369
+ def dcshift(waveform: Tensor, shift: float, limiter_gain: Optional[float] = None) -> Tensor:
370
+ r"""Apply a DC shift to the audio. Similar to SoX implementation.
371
+
372
+ .. devices:: CPU CUDA
373
+
374
+ .. properties:: TorchScript
375
+
376
+ This can be useful to remove a DC offset
377
+ (caused perhaps by a hardware problem in the recording chain) from the audio
378
+
379
+ Args:
380
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
381
+ shift (float): indicates the amount to shift the audio
382
+ Allowed range of values for shift : -2.0 to +2.0
383
+ limiter_gain (float of None, optional): It is used only on peaks to prevent clipping
384
+ It should have a value much less than 1 (e.g. 0.05 or 0.02)
385
+
386
+ Returns:
387
+ Tensor: Waveform of dimension of `(..., time)`
388
+
389
+ Reference:
390
+ - http://sox.sourceforge.net/sox.html
391
+ """
392
+ output_waveform = waveform
393
+ limiter_threshold = 0.0
394
+
395
+ if limiter_gain is not None:
396
+ limiter_threshold = 1.0 - (abs(shift) - limiter_gain)
397
+
398
+ # Note:
399
+ # the following index-based update breaks auto-grad support
400
+ if limiter_gain is not None and shift > 0:
401
+ mask = waveform > limiter_threshold
402
+ temp = (waveform[mask] - limiter_threshold) * limiter_gain / (1 - limiter_threshold)
403
+ output_waveform[mask] = (temp + limiter_threshold + shift).clamp(max=limiter_threshold)
404
+ output_waveform[~mask] = (waveform[~mask] + shift).clamp(min=-1, max=1)
405
+ elif limiter_gain is not None and shift < 0:
406
+ mask = waveform < -limiter_threshold
407
+ temp = (waveform[mask] + limiter_threshold) * limiter_gain / (1 - limiter_threshold)
408
+ output_waveform[mask] = (temp - limiter_threshold + shift).clamp(min=-limiter_threshold)
409
+ output_waveform[~mask] = (waveform[~mask] + shift).clamp(min=-1, max=1)
410
+ else:
411
+ output_waveform = (waveform + shift).clamp(min=-1, max=1)
412
+
413
+ return output_waveform
414
+
415
+
416
+ def deemph_biquad(waveform: Tensor, sample_rate: int) -> Tensor:
417
+ r"""Apply ISO 908 CD de-emphasis (shelving) IIR filter. Similar to SoX implementation.
418
+
419
+ .. devices:: CPU CUDA
420
+
421
+ .. properties:: Autograd TorchScript
422
+
423
+ Args:
424
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
425
+ sample_rate (int): sampling rate of the waveform, Allowed sample rate ``44100`` or ``48000``
426
+
427
+ Returns:
428
+ Tensor: Waveform of dimension of `(..., time)`
429
+
430
+ Reference:
431
+ - http://sox.sourceforge.net/sox.html
432
+ - https://www.w3.org/2011/audio/audio-eq-cookbook.html#APF
433
+ """
434
+
435
+ if sample_rate == 44100:
436
+ central_freq = 5283
437
+ width_slope = 0.4845
438
+ gain = -9.477
439
+ elif sample_rate == 48000:
440
+ central_freq = 5356
441
+ width_slope = 0.479
442
+ gain = -9.62
443
+ else:
444
+ raise ValueError("Sample rate must be 44100 (audio-CD) or 48000 (DAT)")
445
+
446
+ w0 = 2 * math.pi * central_freq / sample_rate
447
+ A = math.exp(gain / 40.0 * math.log(10))
448
+ alpha = math.sin(w0) / 2 * math.sqrt((A + 1 / A) * (1 / width_slope - 1) + 2)
449
+
450
+ temp1 = 2 * math.sqrt(A) * alpha
451
+ temp2 = (A - 1) * math.cos(w0)
452
+ temp3 = (A + 1) * math.cos(w0)
453
+
454
+ b0 = A * ((A + 1) + temp2 + temp1)
455
+ b1 = -2 * A * ((A - 1) + temp3)
456
+ b2 = A * ((A + 1) + temp2 - temp1)
457
+ a0 = (A + 1) - temp2 + temp1
458
+ a1 = 2 * ((A - 1) - temp3)
459
+ a2 = (A + 1) - temp2 - temp1
460
+
461
+ return biquad(waveform, b0, b1, b2, a0, a1, a2)
462
+
463
+
464
+ def _add_noise_shaping(dithered_waveform: Tensor, waveform: Tensor) -> Tensor:
465
+ r"""Noise shaping is calculated by error:
466
+ error[n] = dithered[n] - original[n]
467
+ noise_shaped_waveform[n] = dithered[n] + error[n-1]
468
+ """
469
+ wf_shape = waveform.size()
470
+ waveform = waveform.reshape(-1, wf_shape[-1])
471
+
472
+ dithered_shape = dithered_waveform.size()
473
+ dithered_waveform = dithered_waveform.reshape(-1, dithered_shape[-1])
474
+
475
+ error = dithered_waveform - waveform
476
+
477
+ # add error[n-1] to dithered_waveform[n], so offset the error by 1 index
478
+ zeros = torch.zeros(1, dtype=error.dtype, device=error.device)
479
+ for index in range(error.size()[0]):
480
+ err = error[index]
481
+ error_offset = torch.cat((zeros, err))
482
+ error[index] = error_offset[: waveform.size()[1]]
483
+
484
+ noise_shaped = dithered_waveform + error
485
+ return noise_shaped.reshape(dithered_shape[:-1] + noise_shaped.shape[-1:])
486
+
487
+
488
+ def _apply_probability_distribution(waveform: Tensor, density_function: str = "TPDF") -> Tensor:
489
+ r"""Apply a probability distribution function on a waveform.
490
+
491
+ Triangular probability density function (TPDF) dither noise has a
492
+ triangular distribution; values in the center of the range have a higher
493
+ probability of occurring.
494
+
495
+ Rectangular probability density function (RPDF) dither noise has a
496
+ uniform distribution; any value in the specified range has the same
497
+ probability of occurring.
498
+
499
+ Gaussian probability density function (GPDF) has a normal distribution.
500
+ The relationship of probabilities of results follows a bell-shaped,
501
+ or Gaussian curve, typical of dither generated by analog sources.
502
+ Args:
503
+ waveform (Tensor): Tensor of audio of dimension (..., time)
504
+ density_function (str, optional): The density function of a
505
+ continuous random variable (Default: ``"TPDF"``)
506
+ Options: Triangular Probability Density Function - `TPDF`
507
+ Rectangular Probability Density Function - `RPDF`
508
+ Gaussian Probability Density Function - `GPDF`
509
+ Returns:
510
+ Tensor: waveform dithered with TPDF
511
+ """
512
+
513
+ # pack batch
514
+ shape = waveform.size()
515
+ waveform = waveform.reshape(-1, shape[-1])
516
+
517
+ channel_size = waveform.size()[0] - 1
518
+ time_size = waveform.size()[-1] - 1
519
+
520
+ random_channel = (
521
+ int(
522
+ torch.randint(
523
+ channel_size,
524
+ [
525
+ 1,
526
+ ],
527
+ ).item()
528
+ )
529
+ if channel_size > 0
530
+ else 0
531
+ )
532
+ random_time = (
533
+ int(
534
+ torch.randint(
535
+ time_size,
536
+ [
537
+ 1,
538
+ ],
539
+ ).item()
540
+ )
541
+ if time_size > 0
542
+ else 0
543
+ )
544
+
545
+ number_of_bits = 16
546
+ up_scaling = 2 ** (number_of_bits - 1) - 2
547
+ signal_scaled = waveform * up_scaling
548
+ down_scaling = 2 ** (number_of_bits - 1)
549
+
550
+ signal_scaled_dis = waveform
551
+ if density_function == "RPDF":
552
+ RPDF = waveform[random_channel][random_time] - 0.5
553
+
554
+ signal_scaled_dis = signal_scaled + RPDF
555
+ elif density_function == "GPDF":
556
+ # TODO Replace by distribution code once
557
+ # https://github.com/pytorch/pytorch/issues/29843 is resolved
558
+ # gaussian = torch.distributions.normal.Normal(torch.mean(waveform, -1), 1).sample()
559
+
560
+ num_rand_variables = 6
561
+
562
+ gaussian = waveform[random_channel][random_time]
563
+ for ws in num_rand_variables * [time_size]:
564
+ rand_chan = int(
565
+ torch.randint(
566
+ channel_size,
567
+ [
568
+ 1,
569
+ ],
570
+ ).item()
571
+ )
572
+ gaussian += waveform[rand_chan][
573
+ int(
574
+ torch.randint(
575
+ ws,
576
+ [
577
+ 1,
578
+ ],
579
+ ).item()
580
+ )
581
+ ]
582
+
583
+ signal_scaled_dis = signal_scaled + gaussian
584
+ else:
585
+ # dtype needed for https://github.com/pytorch/pytorch/issues/32358
586
+ TPDF = torch.bartlett_window(time_size + 1, dtype=signal_scaled.dtype, device=signal_scaled.device)
587
+ TPDF = TPDF.repeat((channel_size + 1), 1)
588
+ signal_scaled_dis = signal_scaled + TPDF
589
+
590
+ quantised_signal_scaled = torch.round(signal_scaled_dis)
591
+ quantised_signal = quantised_signal_scaled / down_scaling
592
+
593
+ # unpack batch
594
+ return quantised_signal.reshape(shape[:-1] + quantised_signal.shape[-1:])
595
+
596
+
597
+ def dither(waveform: Tensor, density_function: str = "TPDF", noise_shaping: bool = False) -> Tensor:
598
+ r"""Apply dither
599
+
600
+ .. devices:: CPU CUDA
601
+
602
+ .. properties:: TorchScript
603
+
604
+ Dither increases the perceived dynamic range of audio stored at a
605
+ particular bit-depth by eliminating nonlinear truncation distortion
606
+ (i.e. adding minimally perceived noise to mask distortion caused by quantization).
607
+
608
+ Args:
609
+ waveform (Tensor): Tensor of audio of dimension (..., time)
610
+ density_function (str, optional):
611
+ The density function of a continuous random variable. One of
612
+ ``"TPDF"`` (Triangular Probability Density Function),
613
+ ``"RPDF"`` (Rectangular Probability Density Function) or
614
+ ``"GPDF"`` (Gaussian Probability Density Function) (Default: ``"TPDF"``).
615
+ noise_shaping (bool, optional): a filtering process that shapes the spectral
616
+ energy of quantisation error (Default: ``False``)
617
+
618
+ Returns:
619
+ Tensor: waveform dithered
620
+ """
621
+ dithered = _apply_probability_distribution(waveform, density_function=density_function)
622
+
623
+ if noise_shaping:
624
+ return _add_noise_shaping(dithered, waveform)
625
+ else:
626
+ return dithered
627
+
628
+
629
+ def equalizer_biquad(
630
+ waveform: Tensor,
631
+ sample_rate: int,
632
+ center_freq: float,
633
+ gain: float,
634
+ Q: float = 0.707,
635
+ ) -> Tensor:
636
+ r"""Design biquad peaking equalizer filter and perform filtering. Similar to SoX implementation.
637
+
638
+ .. devices:: CPU CUDA
639
+
640
+ .. properties:: Autograd TorchScript
641
+
642
+ Args:
643
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
644
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
645
+ center_freq (float): filter's central frequency
646
+ gain (float or torch.Tensor): desired gain at the boost (or attenuation) in dB
647
+ Q (float or torch.Tensor, optional): https://en.wikipedia.org/wiki/Q_factor (Default: ``0.707``)
648
+
649
+ Returns:
650
+ Tensor: Waveform of dimension of `(..., time)`
651
+ """
652
+ dtype = waveform.dtype
653
+ device = waveform.device
654
+ center_freq = torch.as_tensor(center_freq, dtype=dtype, device=device)
655
+ Q = torch.as_tensor(Q, dtype=dtype, device=device)
656
+ gain = torch.as_tensor(gain, dtype=dtype, device=device)
657
+
658
+ w0 = 2 * math.pi * center_freq / sample_rate
659
+ A = torch.exp(gain / 40.0 * math.log(10))
660
+ alpha = torch.sin(w0) / 2 / Q
661
+
662
+ b0 = 1 + alpha * A
663
+ b1 = -2 * torch.cos(w0)
664
+ b2 = 1 - alpha * A
665
+ a0 = 1 + alpha / A
666
+ a1 = -2 * torch.cos(w0)
667
+ a2 = 1 - alpha / A
668
+ return biquad(waveform, b0, b1, b2, a0, a1, a2)
669
+
670
+
671
+ def filtfilt(
672
+ waveform: Tensor,
673
+ a_coeffs: Tensor,
674
+ b_coeffs: Tensor,
675
+ clamp: bool = True,
676
+ ) -> Tensor:
677
+ r"""Apply an IIR filter forward and backward to a waveform.
678
+
679
+ .. devices:: CPU CUDA
680
+
681
+ .. properties:: Autograd TorchScript
682
+
683
+ Inspired by https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.filtfilt.html
684
+
685
+ Args:
686
+ waveform (Tensor): audio waveform of dimension of `(..., time)`. Must be normalized to -1 to 1.
687
+ a_coeffs (Tensor): denominator coefficients of difference equation of dimension of either
688
+ 1D with shape `(num_order + 1)` or 2D with shape `(num_filters, num_order + 1)`.
689
+ Lower delay coefficients are first, e.g. ``[a0, a1, a2, ...]``.
690
+ Must be same size as b_coeffs (pad with 0's as necessary).
691
+ b_coeffs (Tensor): numerator coefficients of difference equation of dimension of either
692
+ 1D with shape `(num_order + 1)` or 2D with shape `(num_filters, num_order + 1)`.
693
+ Lower delay coefficients are first, e.g. ``[b0, b1, b2, ...]``.
694
+ Must be same size as a_coeffs (pad with 0's as necessary).
695
+ clamp (bool, optional): If ``True``, clamp the output signal to be in the range [-1, 1] (Default: ``True``)
696
+
697
+ Returns:
698
+ Tensor: Waveform with dimension of either `(..., num_filters, time)` if ``a_coeffs`` and ``b_coeffs``
699
+ are 2D Tensors, or `(..., time)` otherwise.
700
+ """
701
+ forward_filtered = lfilter(waveform, a_coeffs, b_coeffs, clamp=False, batching=True)
702
+ backward_filtered = lfilter(
703
+ forward_filtered.flip(-1),
704
+ a_coeffs,
705
+ b_coeffs,
706
+ clamp=clamp,
707
+ batching=True,
708
+ ).flip(-1)
709
+ return backward_filtered
710
+
711
+
712
+ def flanger(
713
+ waveform: Tensor,
714
+ sample_rate: int,
715
+ delay: float = 0.0,
716
+ depth: float = 2.0,
717
+ regen: float = 0.0,
718
+ width: float = 71.0,
719
+ speed: float = 0.5,
720
+ phase: float = 25.0,
721
+ modulation: str = "sinusoidal",
722
+ interpolation: str = "linear",
723
+ ) -> Tensor:
724
+ r"""Apply a flanger effect to the audio. Similar to SoX implementation.
725
+
726
+ .. devices:: CPU CUDA
727
+
728
+ .. properties:: Autograd TorchScript
729
+
730
+ Args:
731
+ waveform (Tensor): audio waveform of dimension of `(..., channel, time)` .
732
+ Max 4 channels allowed
733
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
734
+ delay (float, optional): desired delay in milliseconds(ms)
735
+ Allowed range of values are 0 to 30
736
+ depth (float, optional): desired delay depth in milliseconds(ms)
737
+ Allowed range of values are 0 to 10
738
+ regen (float, optional): desired regen(feedback gain) in dB
739
+ Allowed range of values are -95 to 95
740
+ width (float, optional): desired width(delay gain) in dB
741
+ Allowed range of values are 0 to 100
742
+ speed (float, optional): modulation speed in Hz
743
+ Allowed range of values are 0.1 to 10
744
+ phase (float, optional): percentage phase-shift for multi-channel
745
+ Allowed range of values are 0 to 100
746
+ modulation (str, optional): Use either "sinusoidal" or "triangular" modulation. (Default: ``sinusoidal``)
747
+ interpolation (str, optional): Use either "linear" or "quadratic" for delay-line interpolation.
748
+ (Default: ``linear``)
749
+
750
+ Returns:
751
+ Tensor: Waveform of dimension of `(..., channel, time)`
752
+
753
+ Reference:
754
+ - http://sox.sourceforge.net/sox.html
755
+
756
+ - Scott Lehman, `Effects Explained`_,
757
+
758
+ .. _Effects Explained:
759
+ https://web.archive.org/web/20051125072557/http://www.harmony-central.com/Effects/effects-explained.html
760
+ """
761
+
762
+ if modulation not in ("sinusoidal", "triangular"):
763
+ raise ValueError('Only "sinusoidal" or "triangular" modulation allowed')
764
+
765
+ if interpolation not in ("linear", "quadratic"):
766
+ raise ValueError('Only "linear" or "quadratic" interpolation allowed')
767
+
768
+ actual_shape = waveform.shape
769
+ device, dtype = waveform.device, waveform.dtype
770
+
771
+ if actual_shape[-2] > 4:
772
+ raise ValueError("Max 4 channels allowed")
773
+
774
+ # convert to 3D (batch, channels, time)
775
+ waveform = waveform.view(-1, actual_shape[-2], actual_shape[-1])
776
+
777
+ # Scaling
778
+ feedback_gain = regen / 100
779
+ delay_gain = width / 100
780
+ channel_phase = phase / 100
781
+ delay_min = delay / 1000
782
+ delay_depth = depth / 1000
783
+
784
+ n_channels = waveform.shape[-2]
785
+
786
+ if modulation == "sinusoidal":
787
+ wave_type = "SINE"
788
+ else:
789
+ wave_type = "TRIANGLE"
790
+
791
+ # Balance output:
792
+ in_gain = 1.0 / (1 + delay_gain)
793
+ delay_gain = delay_gain / (1 + delay_gain)
794
+
795
+ # Balance feedback loop:
796
+ delay_gain = delay_gain * (1 - abs(feedback_gain))
797
+
798
+ delay_buf_length = int((delay_min + delay_depth) * sample_rate + 0.5)
799
+ delay_buf_length = delay_buf_length + 2
800
+
801
+ delay_bufs = torch.zeros(waveform.shape[0], n_channels, delay_buf_length, dtype=dtype, device=device)
802
+ delay_last = torch.zeros(waveform.shape[0], n_channels, dtype=dtype, device=device)
803
+
804
+ lfo_length = int(sample_rate / speed)
805
+
806
+ table_min = math.floor(delay_min * sample_rate + 0.5)
807
+ table_max = delay_buf_length - 2.0
808
+
809
+ lfo = _generate_wave_table(
810
+ wave_type=wave_type,
811
+ data_type="FLOAT",
812
+ table_size=lfo_length,
813
+ min=float(table_min),
814
+ max=float(table_max),
815
+ phase=3 * math.pi / 2,
816
+ device=device,
817
+ )
818
+
819
+ output_waveform = torch.zeros_like(waveform, dtype=dtype, device=device)
820
+
821
+ delay_buf_pos = 0
822
+ lfo_pos = 0
823
+ channel_idxs = torch.arange(0, n_channels, device=device)
824
+
825
+ for i in range(waveform.shape[-1]):
826
+
827
+ delay_buf_pos = (delay_buf_pos + delay_buf_length - 1) % delay_buf_length
828
+
829
+ cur_channel_phase = (channel_idxs * lfo_length * channel_phase + 0.5).to(torch.int64)
830
+ delay_tensor = lfo[(lfo_pos + cur_channel_phase) % lfo_length]
831
+ frac_delay = torch.frac(delay_tensor)
832
+ delay_tensor = torch.floor(delay_tensor)
833
+
834
+ int_delay = delay_tensor.to(torch.int64)
835
+
836
+ temp = waveform[:, :, i]
837
+
838
+ delay_bufs[:, :, delay_buf_pos] = temp + delay_last * feedback_gain
839
+
840
+ delayed_0 = delay_bufs[:, channel_idxs, (delay_buf_pos + int_delay) % delay_buf_length]
841
+
842
+ int_delay = int_delay + 1
843
+
844
+ delayed_1 = delay_bufs[:, channel_idxs, (delay_buf_pos + int_delay) % delay_buf_length]
845
+
846
+ int_delay = int_delay + 1
847
+
848
+ if interpolation == "linear":
849
+ delayed = delayed_0 + (delayed_1 - delayed_0) * frac_delay
850
+ else:
851
+ delayed_2 = delay_bufs[:, channel_idxs, (delay_buf_pos + int_delay) % delay_buf_length]
852
+
853
+ int_delay = int_delay + 1
854
+
855
+ delayed_2 = delayed_2 - delayed_0
856
+ delayed_1 = delayed_1 - delayed_0
857
+ a = delayed_2 * 0.5 - delayed_1
858
+ b = delayed_1 * 2 - delayed_2 * 0.5
859
+
860
+ delayed = delayed_0 + (a * frac_delay + b) * frac_delay
861
+
862
+ delay_last = delayed
863
+ output_waveform[:, :, i] = waveform[:, :, i] * in_gain + delayed * delay_gain
864
+
865
+ lfo_pos = (lfo_pos + 1) % lfo_length
866
+
867
+ return output_waveform.clamp(min=-1, max=1).view(actual_shape)
868
+
869
+
870
+ def gain(waveform: Tensor, gain_db: float = 1.0) -> Tensor:
871
+ r"""Apply amplification or attenuation to the whole waveform.
872
+
873
+ .. devices:: CPU CUDA
874
+
875
+ .. properties:: Autograd TorchScript
876
+
877
+ Args:
878
+ waveform (Tensor): Tensor of audio of dimension (..., time).
879
+ gain_db (float, optional) Gain adjustment in decibels (dB) (Default: ``1.0``).
880
+
881
+ Returns:
882
+ Tensor: the whole waveform amplified by gain_db.
883
+ """
884
+ if gain_db == 0:
885
+ return waveform
886
+
887
+ ratio = 10 ** (gain_db / 20)
888
+
889
+ return waveform * ratio
890
+
891
+
892
+ def highpass_biquad(waveform: Tensor, sample_rate: int, cutoff_freq: float, Q: float = 0.707) -> Tensor:
893
+ r"""Design biquad highpass filter and perform filtering. Similar to SoX implementation.
894
+
895
+ .. devices:: CPU CUDA
896
+
897
+ .. properties:: Autograd TorchScript
898
+
899
+ Args:
900
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
901
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
902
+ cutoff_freq (float or torch.Tensor): filter cutoff frequency
903
+ Q (float or torch.Tensor, optional): https://en.wikipedia.org/wiki/Q_factor (Default: ``0.707``)
904
+
905
+ Returns:
906
+ Tensor: Waveform dimension of `(..., time)`
907
+ """
908
+ dtype = waveform.dtype
909
+ device = waveform.device
910
+ cutoff_freq = torch.as_tensor(cutoff_freq, dtype=dtype, device=device)
911
+ Q = torch.as_tensor(Q, dtype=dtype, device=device)
912
+
913
+ w0 = 2 * math.pi * cutoff_freq / sample_rate
914
+ alpha = torch.sin(w0) / 2.0 / Q
915
+
916
+ b0 = (1 + torch.cos(w0)) / 2
917
+ b1 = -1 - torch.cos(w0)
918
+ b2 = b0
919
+ a0 = 1 + alpha
920
+ a1 = -2 * torch.cos(w0)
921
+ a2 = 1 - alpha
922
+ return biquad(waveform, b0, b1, b2, a0, a1, a2)
923
+
924
+
925
+ def _lfilter_core_generic_loop(input_signal_windows: Tensor, a_coeffs_flipped: Tensor, padded_output_waveform: Tensor):
926
+ n_order = a_coeffs_flipped.size(1)
927
+ a_coeffs_flipped = a_coeffs_flipped.unsqueeze(2)
928
+ for i_sample, o0 in enumerate(input_signal_windows.permute(2, 0, 1)):
929
+ windowed_output_signal = padded_output_waveform[:, :, i_sample : i_sample + n_order]
930
+ o0 -= (windowed_output_signal.transpose(0, 1) @ a_coeffs_flipped)[..., 0].t()
931
+ padded_output_waveform[:, :, i_sample + n_order - 1] = o0
932
+
933
+
934
+ if _IS_TORCHAUDIO_EXT_AVAILABLE:
935
+ _lfilter_core_cpu_loop = torch.ops.torchaudio._lfilter_core_loop
936
+ else:
937
+ _lfilter_core_cpu_loop = _lfilter_core_generic_loop
938
+
939
+
940
+ def _lfilter_core(
941
+ waveform: Tensor,
942
+ a_coeffs: Tensor,
943
+ b_coeffs: Tensor,
944
+ ) -> Tensor:
945
+
946
+ if a_coeffs.size() != b_coeffs.size():
947
+ raise ValueError(
948
+ "Expected coeffs to be the same size."
949
+ f"Found a_coeffs size: {a_coeffs.size()}, b_coeffs size: {b_coeffs.size()}"
950
+ )
951
+ if waveform.ndim != 3:
952
+ raise ValueError(f"Expected waveform to be 3 dimensional. Found: {waveform.ndim}")
953
+ if not (waveform.device == a_coeffs.device == b_coeffs.device):
954
+ raise ValueError(
955
+ "Expected waveform and coeffs to be on the same device."
956
+ f"Found: waveform device:{waveform.device}, a_coeffs device: {a_coeffs.device}, "
957
+ f"b_coeffs device: {b_coeffs.device}"
958
+ )
959
+
960
+ n_batch, n_channel, n_sample = waveform.size()
961
+ n_order = a_coeffs.size(1)
962
+ if n_order <= 0:
963
+ raise ValueError(f"Expected n_order to be positive. Found: {n_order}")
964
+
965
+ # Pad the input and create output
966
+
967
+ padded_waveform = torch.nn.functional.pad(waveform, [n_order - 1, 0])
968
+ padded_output_waveform = torch.zeros_like(padded_waveform)
969
+
970
+ # Set up the coefficients matrix
971
+ # Flip coefficients' order
972
+ a_coeffs_flipped = a_coeffs.flip(1)
973
+ b_coeffs_flipped = b_coeffs.flip(1)
974
+
975
+ # calculate windowed_input_signal in parallel using convolution
976
+ input_signal_windows = torch.nn.functional.conv1d(padded_waveform, b_coeffs_flipped.unsqueeze(1), groups=n_channel)
977
+
978
+ input_signal_windows.div_(a_coeffs[:, :1])
979
+ a_coeffs_flipped.div_(a_coeffs[:, :1])
980
+
981
+ if (
982
+ input_signal_windows.device == torch.device("cpu")
983
+ and a_coeffs_flipped.device == torch.device("cpu")
984
+ and padded_output_waveform.device == torch.device("cpu")
985
+ ):
986
+ _lfilter_core_cpu_loop(input_signal_windows, a_coeffs_flipped, padded_output_waveform)
987
+ else:
988
+ _lfilter_core_generic_loop(input_signal_windows, a_coeffs_flipped, padded_output_waveform)
989
+
990
+ output = padded_output_waveform[:, :, n_order - 1 :]
991
+ return output
992
+
993
+
994
+ if _IS_TORCHAUDIO_EXT_AVAILABLE:
995
+ _lfilter = torch.ops.torchaudio._lfilter
996
+ else:
997
+ _lfilter = _lfilter_core
998
+
999
+
1000
+ def lfilter(waveform: Tensor, a_coeffs: Tensor, b_coeffs: Tensor, clamp: bool = True, batching: bool = True) -> Tensor:
1001
+ r"""Perform an IIR filter by evaluating difference equation, using differentiable implementation
1002
+ developed separately by *Yu et al.* :cite:`ismir_YuF23` and *Forgione et al.* :cite:`forgione2021dynonet`.
1003
+ The gradients of ``a_coeffs`` are computed based on a faster algorithm from :cite:`ycy2024diffapf`.
1004
+
1005
+ .. devices:: CPU CUDA
1006
+
1007
+ .. properties:: Autograd TorchScript
1008
+
1009
+ Note:
1010
+ To avoid numerical problems, small filter order is preferred.
1011
+ Using double precision could also minimize numerical precision errors.
1012
+
1013
+ Args:
1014
+ waveform (Tensor): audio waveform of dimension of `(..., time)`. Must be normalized to -1 to 1.
1015
+ a_coeffs (Tensor): denominator coefficients of difference equation of dimension of either
1016
+ 1D with shape `(num_order + 1)` or 2D with shape `(num_filters, num_order + 1)`.
1017
+ Lower delays coefficients are first, e.g. ``[a0, a1, a2, ...]``.
1018
+ Must be same size as b_coeffs (pad with 0's as necessary).
1019
+ b_coeffs (Tensor): numerator coefficients of difference equation of dimension of either
1020
+ 1D with shape `(num_order + 1)` or 2D with shape `(num_filters, num_order + 1)`.
1021
+ Lower delays coefficients are first, e.g. ``[b0, b1, b2, ...]``.
1022
+ Must be same size as a_coeffs (pad with 0's as necessary).
1023
+ clamp (bool, optional): If ``True``, clamp the output signal to be in the range [-1, 1] (Default: ``True``)
1024
+ batching (bool, optional): Effective only when coefficients are 2D. If ``True``, then waveform should be at
1025
+ least 2D, and the size of second axis from last should equals to ``num_filters``.
1026
+ The output can be expressed as ``output[..., i, :] = lfilter(waveform[..., i, :],
1027
+ a_coeffs[i], b_coeffs[i], clamp=clamp, batching=False)``. (Default: ``True``)
1028
+
1029
+ Returns:
1030
+ Tensor: Waveform with dimension of either `(..., num_filters, time)` if ``a_coeffs`` and ``b_coeffs``
1031
+ are 2D Tensors, or `(..., time)` otherwise.
1032
+ """
1033
+ if a_coeffs.size() != b_coeffs.size():
1034
+ raise ValueError(
1035
+ "Expected coeffs to be the same size."
1036
+ f"Found: a_coeffs size: {a_coeffs.size()}, b_coeffs size: {b_coeffs.size()}"
1037
+ )
1038
+ if a_coeffs.ndim > 2:
1039
+ raise ValueError(f"Expected coeffs to have greater than 1 dimension. Found: {a_coeffs.ndim}")
1040
+
1041
+ if a_coeffs.ndim > 1:
1042
+ if batching:
1043
+ if waveform.ndim <= 0:
1044
+ raise ValueError("Expected waveform to have a positive number of dimensions." f"Found: {waveform.ndim}")
1045
+ if waveform.shape[-2] != a_coeffs.shape[0]:
1046
+ raise ValueError(
1047
+ "Expected number of batches in waveform and coeffs to be the same."
1048
+ f"Found: coeffs batches: {a_coeffs.shape[0]}, waveform batches: {waveform.shape[-2]}"
1049
+ )
1050
+ else:
1051
+ waveform = torch.stack([waveform] * a_coeffs.shape[0], -2)
1052
+ else:
1053
+ a_coeffs = a_coeffs.unsqueeze(0)
1054
+ b_coeffs = b_coeffs.unsqueeze(0)
1055
+
1056
+ # pack batch
1057
+ shape = waveform.size()
1058
+ waveform = waveform.reshape(-1, a_coeffs.shape[0], shape[-1])
1059
+ output = _lfilter(waveform, a_coeffs, b_coeffs)
1060
+
1061
+ if clamp:
1062
+ output = torch.clamp(output, min=-1.0, max=1.0)
1063
+
1064
+ # unpack batch
1065
+ output = output.reshape(shape[:-1] + output.shape[-1:])
1066
+
1067
+ return output
1068
+
1069
+
1070
+ def lowpass_biquad(waveform: Tensor, sample_rate: int, cutoff_freq: float, Q: float = 0.707) -> Tensor:
1071
+ r"""Design biquad lowpass filter and perform filtering. Similar to SoX implementation.
1072
+
1073
+ .. devices:: CPU CUDA
1074
+
1075
+ .. properties:: Autograd TorchScript
1076
+
1077
+ Args:
1078
+ waveform (torch.Tensor): audio waveform of dimension of `(..., time)`
1079
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
1080
+ cutoff_freq (float or torch.Tensor): filter cutoff frequency
1081
+ Q (float or torch.Tensor, optional): https://en.wikipedia.org/wiki/Q_factor (Default: ``0.707``)
1082
+
1083
+ Returns:
1084
+ Tensor: Waveform of dimension of `(..., time)`
1085
+ """
1086
+ dtype = waveform.dtype
1087
+ device = waveform.device
1088
+ cutoff_freq = torch.as_tensor(cutoff_freq, dtype=dtype, device=device)
1089
+ Q = torch.as_tensor(Q, dtype=dtype, device=device)
1090
+
1091
+ w0 = 2 * math.pi * cutoff_freq / sample_rate
1092
+ alpha = torch.sin(w0) / 2 / Q
1093
+
1094
+ b0 = (1 - torch.cos(w0)) / 2
1095
+ b1 = 1 - torch.cos(w0)
1096
+ b2 = b0
1097
+ a0 = 1 + alpha
1098
+ a1 = -2 * torch.cos(w0)
1099
+ a2 = 1 - alpha
1100
+ return biquad(waveform, b0, b1, b2, a0, a1, a2)
1101
+
1102
+
1103
+ def _overdrive_core_loop_generic(
1104
+ waveform: Tensor, temp: Tensor, last_in: Tensor, last_out: Tensor, output_waveform: Tensor
1105
+ ):
1106
+ for i in range(waveform.shape[-1]):
1107
+ last_out = temp[:, i] - last_in + 0.995 * last_out
1108
+ last_in = temp[:, i]
1109
+ output_waveform[:, i] = waveform[:, i] * 0.5 + last_out * 0.75
1110
+
1111
+
1112
+ if _IS_TORCHAUDIO_EXT_AVAILABLE:
1113
+ _overdrive_core_loop_cpu = torch.ops.torchaudio._overdrive_core_loop
1114
+ else:
1115
+ _overdrive_core_loop_cpu = _overdrive_core_loop_generic
1116
+
1117
+
1118
+ def overdrive(waveform: Tensor, gain: float = 20, colour: float = 20) -> Tensor:
1119
+ r"""Apply a overdrive effect to the audio. Similar to SoX implementation.
1120
+
1121
+ .. devices:: CPU CUDA
1122
+
1123
+ .. properties:: Autograd TorchScript
1124
+
1125
+ This effect applies a non linear distortion to the audio signal.
1126
+
1127
+ Args:
1128
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
1129
+ gain (float, optional): desired gain at the boost (or attenuation) in dB
1130
+ Allowed range of values are 0 to 100
1131
+ colour (float, optional): controls the amount of even harmonic content in the over-driven output
1132
+ Allowed range of values are 0 to 100
1133
+
1134
+ Returns:
1135
+ Tensor: Waveform of dimension of `(..., time)`
1136
+
1137
+ Reference:
1138
+ - http://sox.sourceforge.net/sox.html
1139
+ """
1140
+ actual_shape = waveform.shape
1141
+ device, dtype = waveform.device, waveform.dtype
1142
+
1143
+ # convert to 2D (..,time)
1144
+ waveform = waveform.view(-1, actual_shape[-1])
1145
+
1146
+ gain = _dB2Linear(gain)
1147
+ colour = colour / 200
1148
+ last_in = torch.zeros(waveform.shape[:-1], dtype=dtype, device=device)
1149
+ last_out = torch.zeros(waveform.shape[:-1], dtype=dtype, device=device)
1150
+
1151
+ temp = waveform * gain + colour
1152
+
1153
+ mask1 = temp < -1
1154
+ temp[mask1] = torch.tensor(-2.0 / 3.0, dtype=dtype, device=device)
1155
+ # Wrapping the constant with Tensor is required for Torchscript
1156
+
1157
+ mask2 = temp > 1
1158
+ temp[mask2] = torch.tensor(2.0 / 3.0, dtype=dtype, device=device)
1159
+
1160
+ mask3 = ~mask1 & ~mask2
1161
+ temp[mask3] = temp[mask3] - (temp[mask3] ** 3) * (1.0 / 3)
1162
+
1163
+ output_waveform = torch.zeros_like(waveform, dtype=dtype, device=device)
1164
+
1165
+ # Uses CPU optimized loop function if available for CPU device
1166
+ if device == torch.device("cpu"):
1167
+ _overdrive_core_loop_cpu(waveform, temp, last_in, last_out, output_waveform)
1168
+ else:
1169
+ _overdrive_core_loop_generic(waveform, temp, last_in, last_out, output_waveform)
1170
+
1171
+ return output_waveform.clamp(min=-1, max=1).view(actual_shape)
1172
+
1173
+
1174
+ def phaser(
1175
+ waveform: Tensor,
1176
+ sample_rate: int,
1177
+ gain_in: float = 0.4,
1178
+ gain_out: float = 0.74,
1179
+ delay_ms: float = 3.0,
1180
+ decay: float = 0.4,
1181
+ mod_speed: float = 0.5,
1182
+ sinusoidal: bool = True,
1183
+ ) -> Tensor:
1184
+ r"""Apply a phasing effect to the audio. Similar to SoX implementation.
1185
+
1186
+ .. devices:: CPU CUDA
1187
+
1188
+ .. properties:: Autograd TorchScript
1189
+
1190
+ Args:
1191
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
1192
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
1193
+ gain_in (float, optional): desired input gain at the boost (or attenuation) in dB
1194
+ Allowed range of values are 0 to 1
1195
+ gain_out (float, optional): desired output gain at the boost (or attenuation) in dB
1196
+ Allowed range of values are 0 to 1e9
1197
+ delay_ms (float, optional): desired delay in milliseconds
1198
+ Allowed range of values are 0 to 5.0
1199
+ decay (float, optional): desired decay relative to gain-in
1200
+ Allowed range of values are 0 to 0.99
1201
+ mod_speed (float, optional): modulation speed in Hz
1202
+ Allowed range of values are 0.1 to 2
1203
+ sinusoidal (bool, optional): If ``True``, uses sinusoidal modulation (preferable for multiple instruments)
1204
+ If ``False``, uses triangular modulation (gives single instruments a sharper phasing effect)
1205
+ (Default: ``True``)
1206
+
1207
+ Returns:
1208
+ Tensor: Waveform of dimension of `(..., time)`
1209
+
1210
+ Reference:
1211
+ - http://sox.sourceforge.net/sox.html
1212
+ - Scott Lehman, `Effects Explained`_.
1213
+
1214
+ .. _Effects Explained:
1215
+ https://web.archive.org/web/20051125072557/http://www.harmony-central.com/Effects/effects-explained.html
1216
+ """
1217
+ actual_shape = waveform.shape
1218
+ device, dtype = waveform.device, waveform.dtype
1219
+
1220
+ # convert to 2D (channels,time)
1221
+ waveform = waveform.view(-1, actual_shape[-1])
1222
+
1223
+ delay_buf_len = int((delay_ms * 0.001 * sample_rate) + 0.5)
1224
+ delay_buf = torch.zeros(waveform.shape[0], delay_buf_len, dtype=dtype, device=device)
1225
+
1226
+ mod_buf_len = int(sample_rate / mod_speed + 0.5)
1227
+
1228
+ if sinusoidal:
1229
+ wave_type = "SINE"
1230
+ else:
1231
+ wave_type = "TRIANGLE"
1232
+
1233
+ mod_buf = _generate_wave_table(
1234
+ wave_type=wave_type,
1235
+ data_type="INT",
1236
+ table_size=mod_buf_len,
1237
+ min=1.0,
1238
+ max=float(delay_buf_len),
1239
+ phase=math.pi / 2,
1240
+ device=device,
1241
+ )
1242
+
1243
+ delay_pos = 0
1244
+ mod_pos = 0
1245
+
1246
+ output_waveform_pre_gain_list = []
1247
+ waveform = waveform * gain_in
1248
+ delay_buf = delay_buf * decay
1249
+ waveform_list = [waveform[:, i] for i in range(waveform.size(1))]
1250
+ delay_buf_list = [delay_buf[:, i] for i in range(delay_buf.size(1))]
1251
+ mod_buf_list = [mod_buf[i] for i in range(mod_buf.size(0))]
1252
+
1253
+ for i in range(waveform.shape[-1]):
1254
+ idx = int((delay_pos + mod_buf_list[mod_pos]) % delay_buf_len)
1255
+ mod_pos = (mod_pos + 1) % mod_buf_len
1256
+ delay_pos = (delay_pos + 1) % delay_buf_len
1257
+ temp = (waveform_list[i]) + (delay_buf_list[idx])
1258
+ delay_buf_list[delay_pos] = temp * decay
1259
+ output_waveform_pre_gain_list.append(temp)
1260
+
1261
+ output_waveform = torch.stack(output_waveform_pre_gain_list, dim=1).to(dtype=dtype, device=device)
1262
+ output_waveform.mul_(gain_out)
1263
+
1264
+ return output_waveform.clamp(min=-1, max=1).view(actual_shape)
1265
+
1266
+
1267
+ def riaa_biquad(waveform: Tensor, sample_rate: int) -> Tensor:
1268
+ r"""Apply RIAA vinyl playback equalization. Similar to SoX implementation.
1269
+
1270
+ .. devices:: CPU CUDA
1271
+
1272
+ .. properties:: Autograd TorchScript
1273
+
1274
+ Args:
1275
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
1276
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz).
1277
+ Allowed sample rates in Hz : ``44100``,``48000``,``88200``,``96000``
1278
+
1279
+ Returns:
1280
+ Tensor: Waveform of dimension of `(..., time)`
1281
+
1282
+ Reference:
1283
+ - http://sox.sourceforge.net/sox.html
1284
+ - https://www.w3.org/2011/audio/audio-eq-cookbook.html#APF
1285
+ """
1286
+
1287
+ if sample_rate == 44100:
1288
+ zeros = [-0.2014898, 0.9233820]
1289
+ poles = [0.7083149, 0.9924091]
1290
+
1291
+ elif sample_rate == 48000:
1292
+ zeros = [-0.1766069, 0.9321590]
1293
+ poles = [0.7396325, 0.9931330]
1294
+
1295
+ elif sample_rate == 88200:
1296
+ zeros = [-0.1168735, 0.9648312]
1297
+ poles = [0.8590646, 0.9964002]
1298
+
1299
+ elif sample_rate == 96000:
1300
+ zeros = [-0.1141486, 0.9676817]
1301
+ poles = [0.8699137, 0.9966946]
1302
+
1303
+ else:
1304
+ raise ValueError("Sample rate must be 44.1k, 48k, 88.2k, or 96k")
1305
+
1306
+ # polynomial coefficients with roots zeros[0] and zeros[1]
1307
+ b0 = 1.0
1308
+ b1 = -(zeros[0] + zeros[1])
1309
+ b2 = zeros[0] * zeros[1]
1310
+
1311
+ # polynomial coefficients with roots poles[0] and poles[1]
1312
+ a0 = 1.0
1313
+ a1 = -(poles[0] + poles[1])
1314
+ a2 = poles[0] * poles[1]
1315
+
1316
+ # Normalize to 0dB at 1kHz
1317
+ y = 2 * math.pi * 1000 / sample_rate
1318
+ b_re = b0 + b1 * math.cos(-y) + b2 * math.cos(-2 * y)
1319
+ a_re = a0 + a1 * math.cos(-y) + a2 * math.cos(-2 * y)
1320
+ b_im = b1 * math.sin(-y) + b2 * math.sin(-2 * y)
1321
+ a_im = a1 * math.sin(-y) + a2 * math.sin(-2 * y)
1322
+ g = 1 / math.sqrt((b_re**2 + b_im**2) / (a_re**2 + a_im**2))
1323
+
1324
+ b0 *= g
1325
+ b1 *= g
1326
+ b2 *= g
1327
+
1328
+ return biquad(waveform, b0, b1, b2, a0, a1, a2)
1329
+
1330
+
1331
+ def treble_biquad(
1332
+ waveform: Tensor,
1333
+ sample_rate: int,
1334
+ gain: float,
1335
+ central_freq: float = 3000,
1336
+ Q: float = 0.707,
1337
+ ) -> Tensor:
1338
+ r"""Design a treble tone-control effect. Similar to SoX implementation.
1339
+
1340
+ .. devices:: CPU CUDA
1341
+
1342
+ .. properties:: Autograd TorchScript
1343
+
1344
+ Args:
1345
+ waveform (Tensor): audio waveform of dimension of `(..., time)`
1346
+ sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
1347
+ gain (float or torch.Tensor): desired gain at the boost (or attenuation) in dB.
1348
+ central_freq (float or torch.Tensor, optional): central frequency (in Hz). (Default: ``3000``)
1349
+ Q (float or torch.Tensor, optional): https://en.wikipedia.org/wiki/Q_factor (Default: ``0.707``).
1350
+
1351
+ Returns:
1352
+ Tensor: Waveform of dimension of `(..., time)`
1353
+
1354
+ Reference:
1355
+ - http://sox.sourceforge.net/sox.html
1356
+ - https://www.w3.org/2011/audio/audio-eq-cookbook.html#APF
1357
+ """
1358
+ dtype = waveform.dtype
1359
+ device = waveform.device
1360
+ central_freq = torch.as_tensor(central_freq, dtype=dtype, device=device)
1361
+ Q = torch.as_tensor(Q, dtype=dtype, device=device)
1362
+ gain = torch.as_tensor(gain, dtype=dtype, device=device)
1363
+
1364
+ w0 = 2 * math.pi * central_freq / sample_rate
1365
+ alpha = torch.sin(w0) / 2 / Q
1366
+ A = torch.exp(gain / 40 * math.log(10))
1367
+
1368
+ temp1 = 2 * torch.sqrt(A) * alpha
1369
+ temp2 = (A - 1) * torch.cos(w0)
1370
+ temp3 = (A + 1) * torch.cos(w0)
1371
+
1372
+ b0 = A * ((A + 1) + temp2 + temp1)
1373
+ b1 = -2 * A * ((A - 1) + temp3)
1374
+ b2 = A * ((A + 1) + temp2 - temp1)
1375
+ a0 = (A + 1) - temp2 + temp1
1376
+ a1 = 2 * ((A - 1) - temp3)
1377
+ a2 = (A + 1) - temp2 - temp1
1378
+
1379
+ return biquad(waveform, b0, b1, b2, a0, a1, a2)
1380
+
1381
+
1382
+ def _measure(
1383
+ measure_len_ws: int,
1384
+ samples: Tensor,
1385
+ spectrum: Tensor,
1386
+ noise_spectrum: Tensor,
1387
+ spectrum_window: Tensor,
1388
+ spectrum_start: int,
1389
+ spectrum_end: int,
1390
+ cepstrum_window: Tensor,
1391
+ cepstrum_start: int,
1392
+ cepstrum_end: int,
1393
+ noise_reduction_amount: float,
1394
+ measure_smooth_time_mult: float,
1395
+ noise_up_time_mult: Tensor,
1396
+ noise_down_time_mult: Tensor,
1397
+ boot_count: int,
1398
+ ) -> float:
1399
+ device = samples.device
1400
+
1401
+ if spectrum.size(-1) != noise_spectrum.size(-1):
1402
+ raise ValueError(
1403
+ "Expected spectrum size to match noise spectrum size in final dimension."
1404
+ f"Found: spectrum size: {spectrum.size()}, noise_spectrum size: {noise_spectrum.size()}"
1405
+ )
1406
+
1407
+ dft_len_ws = spectrum.size()[-1]
1408
+
1409
+ dftBuf = torch.zeros(dft_len_ws, device=device)
1410
+
1411
+ dftBuf[:measure_len_ws] = samples * spectrum_window[:measure_len_ws]
1412
+
1413
+ # lsx_safe_rdft((int)p->dft_len_ws, 1, c->dftBuf);
1414
+ _dftBuf = torch.fft.rfft(dftBuf)
1415
+
1416
+ mult: float = boot_count / (1.0 + boot_count) if boot_count >= 0 else measure_smooth_time_mult
1417
+
1418
+ _d = _dftBuf[spectrum_start:spectrum_end].abs()
1419
+ spectrum[spectrum_start:spectrum_end].mul_(mult).add_(_d * (1 - mult))
1420
+ _d = spectrum[spectrum_start:spectrum_end] ** 2
1421
+
1422
+ _zeros = torch.zeros(spectrum_end - spectrum_start, device=device)
1423
+ _mult = (
1424
+ _zeros
1425
+ if boot_count >= 0
1426
+ else torch.where(
1427
+ _d > noise_spectrum[spectrum_start:spectrum_end],
1428
+ noise_up_time_mult, # if
1429
+ noise_down_time_mult, # else,
1430
+ )
1431
+ )
1432
+
1433
+ noise_spectrum[spectrum_start:spectrum_end].mul_(_mult).add_(_d * (1 - _mult))
1434
+ _d = torch.sqrt(
1435
+ torch.max(
1436
+ _zeros,
1437
+ _d - noise_reduction_amount * noise_spectrum[spectrum_start:spectrum_end],
1438
+ ),
1439
+ )
1440
+
1441
+ _cepstrum_Buf: Tensor = torch.zeros(dft_len_ws >> 1, device=device)
1442
+ _cepstrum_Buf[spectrum_start:spectrum_end] = _d * cepstrum_window
1443
+ _cepstrum_Buf[spectrum_end : dft_len_ws >> 1].zero_()
1444
+
1445
+ # lsx_safe_rdft((int)p->dft_len_ws >> 1, 1, c->dftBuf);
1446
+ _cepstrum_Buf = torch.fft.rfft(_cepstrum_Buf)
1447
+
1448
+ result: float = float(torch.sum(_cepstrum_Buf[cepstrum_start:cepstrum_end].abs().pow(2)))
1449
+ result = math.log(result / (cepstrum_end - cepstrum_start)) if result > 0 else -math.inf
1450
+ return max(0, 21 + result)
1451
+
1452
+
1453
+ def vad(
1454
+ waveform: Tensor,
1455
+ sample_rate: int,
1456
+ trigger_level: float = 7.0,
1457
+ trigger_time: float = 0.25,
1458
+ search_time: float = 1.0,
1459
+ allowed_gap: float = 0.25,
1460
+ pre_trigger_time: float = 0.0,
1461
+ # Fine-tuning parameters
1462
+ boot_time: float = 0.35,
1463
+ noise_up_time: float = 0.1,
1464
+ noise_down_time: float = 0.01,
1465
+ noise_reduction_amount: float = 1.35,
1466
+ measure_freq: float = 20.0,
1467
+ measure_duration: Optional[float] = None,
1468
+ measure_smooth_time: float = 0.4,
1469
+ hp_filter_freq: float = 50.0,
1470
+ lp_filter_freq: float = 6000.0,
1471
+ hp_lifter_freq: float = 150.0,
1472
+ lp_lifter_freq: float = 2000.0,
1473
+ ) -> Tensor:
1474
+ r"""Voice Activity Detector. Similar to SoX implementation.
1475
+
1476
+ .. devices:: CPU CUDA
1477
+
1478
+ .. properties:: TorchScript
1479
+
1480
+ Attempts to trim silence and quiet background sounds from the ends of recordings of speech.
1481
+ The algorithm currently uses a simple cepstral power measurement to detect voice,
1482
+ so may be fooled by other things, especially music.
1483
+
1484
+ The effect can trim only from the front of the audio,
1485
+ so in order to trim from the back, the reverse effect must also be used.
1486
+
1487
+ Args:
1488
+ waveform (Tensor): Tensor of audio of dimension `(channels, time)` or `(time)`
1489
+ Tensor of shape `(channels, time)` is treated as a multi-channel recording
1490
+ of the same event and the resulting output will be trimmed to the earliest
1491
+ voice activity in any channel.
1492
+ sample_rate (int): Sample rate of audio signal.
1493
+ trigger_level (float, optional): The measurement level used to trigger activity detection.
1494
+ This may need to be cahnged depending on the noise level, signal level,
1495
+ and other characteristics of the input audio. (Default: 7.0)
1496
+ trigger_time (float, optional): The time constant (in seconds)
1497
+ used to help ignore short bursts of sound. (Default: 0.25)
1498
+ search_time (float, optional): The amount of audio (in seconds)
1499
+ to search for quieter/shorter bursts of audio to include prior
1500
+ to the detected trigger point. (Default: 1.0)
1501
+ allowed_gap (float, optional): The allowed gap (in seconds) between
1502
+ quieter/shorter bursts of audio to include prior
1503
+ to the detected trigger point. (Default: 0.25)
1504
+ pre_trigger_time (float, optional): The amount of audio (in seconds) to preserve
1505
+ before the trigger point and any found quieter/shorter bursts. (Default: 0.0)
1506
+ boot_time (float, optional) The algorithm (internally) uses adaptive noise
1507
+ estimation/reduction in order to detect the start of the wanted audio.
1508
+ This option sets the time for the initial noise estimate. (Default: 0.35)
1509
+ noise_up_time (float, optional) Time constant used by the adaptive noise estimator
1510
+ for when the noise level is increasing. (Default: 0.1)
1511
+ noise_down_time (float, optional) Time constant used by the adaptive noise estimator
1512
+ for when the noise level is decreasing. (Default: 0.01)
1513
+ noise_reduction_amount (float, optional) Amount of noise reduction to use in
1514
+ the detection algorithm (e.g. 0, 0.5, ...). (Default: 1.35)
1515
+ measure_freq (float, optional) Frequency of the algorithm's
1516
+ processing/measurements. (Default: 20.0)
1517
+ measure_duration: (float, optional) Measurement duration.
1518
+ (Default: Twice the measurement period; i.e. with overlap.)
1519
+ measure_smooth_time (float, optional) Time constant used to smooth
1520
+ spectral measurements. (Default: 0.4)
1521
+ hp_filter_freq (float, optional) "Brick-wall" frequency of high-pass filter applied
1522
+ at the input to the detector algorithm. (Default: 50.0)
1523
+ lp_filter_freq (float, optional) "Brick-wall" frequency of low-pass filter applied
1524
+ at the input to the detector algorithm. (Default: 6000.0)
1525
+ hp_lifter_freq (float, optional) "Brick-wall" frequency of high-pass lifter used
1526
+ in the detector algorithm. (Default: 150.0)
1527
+ lp_lifter_freq (float, optional) "Brick-wall" frequency of low-pass lifter used
1528
+ in the detector algorithm. (Default: 2000.0)
1529
+
1530
+ Returns:
1531
+ Tensor: Tensor of audio of dimension `(..., time)`.
1532
+
1533
+ Reference:
1534
+ - http://sox.sourceforge.net/sox.html
1535
+ """
1536
+ device = waveform.device
1537
+
1538
+ if waveform.ndim > 2:
1539
+ warnings.warn(
1540
+ "Expected input tensor dimension of 1 for single channel"
1541
+ f" or 2 for multi-channel. Got {waveform.ndim} instead. "
1542
+ "Batch semantics is not supported. "
1543
+ "Please refer to https://github.com/pytorch/audio/issues/1348"
1544
+ " and https://github.com/pytorch/audio/issues/1468."
1545
+ )
1546
+
1547
+ measure_duration: float = 2.0 / measure_freq if measure_duration is None else measure_duration
1548
+
1549
+ measure_len_ws = int(sample_rate * measure_duration + 0.5)
1550
+ measure_len_ns = measure_len_ws
1551
+ # for (dft_len_ws = 16; dft_len_ws < measure_len_ws; dft_len_ws <<= 1);
1552
+ dft_len_ws = 16
1553
+ while dft_len_ws < measure_len_ws:
1554
+ dft_len_ws *= 2
1555
+
1556
+ measure_period_ns = int(sample_rate / measure_freq + 0.5)
1557
+ measures_len = math.ceil(search_time * measure_freq)
1558
+ search_pre_trigger_len_ns = measures_len * measure_period_ns
1559
+ gap_len = int(allowed_gap * measure_freq + 0.5)
1560
+
1561
+ fixed_pre_trigger_len_ns = int(pre_trigger_time * sample_rate + 0.5)
1562
+ samplesLen_ns = fixed_pre_trigger_len_ns + search_pre_trigger_len_ns + measure_len_ns
1563
+
1564
+ spectrum_window = torch.zeros(measure_len_ws, device=device)
1565
+ for i in range(measure_len_ws):
1566
+ # sox.h:741 define SOX_SAMPLE_MIN (sox_sample_t)SOX_INT_MIN(32)
1567
+ spectrum_window[i] = 2.0 / math.sqrt(float(measure_len_ws))
1568
+ # lsx_apply_hann(spectrum_window, (int)measure_len_ws);
1569
+ spectrum_window *= torch.hann_window(measure_len_ws, device=device, dtype=torch.float)
1570
+
1571
+ spectrum_start: int = int(hp_filter_freq / sample_rate * dft_len_ws + 0.5)
1572
+ spectrum_start: int = max(spectrum_start, 1)
1573
+ spectrum_end: int = int(lp_filter_freq / sample_rate * dft_len_ws + 0.5)
1574
+ spectrum_end: int = min(spectrum_end, dft_len_ws // 2)
1575
+
1576
+ cepstrum_window = torch.zeros(spectrum_end - spectrum_start, device=device)
1577
+ for i in range(spectrum_end - spectrum_start):
1578
+ cepstrum_window[i] = 2.0 / math.sqrt(float(spectrum_end) - spectrum_start)
1579
+ # lsx_apply_hann(cepstrum_window,(int)(spectrum_end - spectrum_start));
1580
+ cepstrum_window *= torch.hann_window(spectrum_end - spectrum_start, device=device, dtype=torch.float)
1581
+
1582
+ cepstrum_start = math.ceil(sample_rate * 0.5 / lp_lifter_freq)
1583
+ cepstrum_end = math.floor(sample_rate * 0.5 / hp_lifter_freq)
1584
+ cepstrum_end = min(cepstrum_end, dft_len_ws // 4)
1585
+
1586
+ if cepstrum_end <= cepstrum_start:
1587
+ raise ValueError(
1588
+ "Expected cepstrum_start to be smaller than cepstrum_end."
1589
+ f"Found: cepstrum_start: {cepstrum_start}, cepstrum_end: {cepstrum_end}."
1590
+ )
1591
+
1592
+ noise_up_time_mult = torch.tensor(math.exp(-1.0 / (noise_up_time * measure_freq)), device=device)
1593
+ noise_down_time_mult = torch.tensor(math.exp(-1.0 / (noise_down_time * measure_freq)), device=device)
1594
+ measure_smooth_time_mult = math.exp(-1.0 / (measure_smooth_time * measure_freq))
1595
+ trigger_meas_time_mult = math.exp(-1.0 / (trigger_time * measure_freq))
1596
+
1597
+ boot_count_max = int(boot_time * measure_freq - 0.5)
1598
+ boot_count = measures_index = flushedLen_ns = 0
1599
+
1600
+ # pack batch
1601
+ shape = waveform.size()
1602
+ waveform = waveform.view(-1, shape[-1])
1603
+
1604
+ n_channels, ilen = waveform.size()
1605
+
1606
+ mean_meas = torch.zeros(n_channels, device=device)
1607
+ spectrum = torch.zeros(n_channels, dft_len_ws, device=device)
1608
+ noise_spectrum = torch.zeros(n_channels, dft_len_ws, device=device)
1609
+ measures = torch.zeros(n_channels, measures_len, device=device)
1610
+
1611
+ has_triggered: bool = False
1612
+ num_measures_to_flush: int = 0
1613
+
1614
+ pos = 0
1615
+ for pos in range(measure_len_ns, ilen, measure_period_ns):
1616
+ for i in range(n_channels):
1617
+ meas: float = _measure(
1618
+ measure_len_ws=measure_len_ws,
1619
+ samples=waveform[i, pos - measure_len_ws : pos],
1620
+ spectrum=spectrum[i],
1621
+ noise_spectrum=noise_spectrum[i],
1622
+ spectrum_window=spectrum_window,
1623
+ spectrum_start=spectrum_start,
1624
+ spectrum_end=spectrum_end,
1625
+ cepstrum_window=cepstrum_window,
1626
+ cepstrum_start=cepstrum_start,
1627
+ cepstrum_end=cepstrum_end,
1628
+ noise_reduction_amount=noise_reduction_amount,
1629
+ measure_smooth_time_mult=measure_smooth_time_mult,
1630
+ noise_up_time_mult=noise_up_time_mult,
1631
+ noise_down_time_mult=noise_down_time_mult,
1632
+ boot_count=boot_count,
1633
+ )
1634
+ measures[i, measures_index] = meas
1635
+ mean_meas[i] = mean_meas[i] * trigger_meas_time_mult + meas * (1.0 - trigger_meas_time_mult)
1636
+
1637
+ has_triggered = has_triggered or (mean_meas[i] >= trigger_level)
1638
+ if has_triggered:
1639
+ n: int = measures_len
1640
+ k: int = measures_index
1641
+ jTrigger: int = n
1642
+ jZero: int = n
1643
+ j: int = 0
1644
+
1645
+ for j in range(n):
1646
+ if (measures[i, k] >= trigger_level) and (j <= jTrigger + gap_len):
1647
+ jZero = jTrigger = j
1648
+ elif (measures[i, k] == 0) and (jTrigger >= jZero):
1649
+ jZero = j
1650
+ k = (k + n - 1) % n
1651
+ j = min(j, jZero)
1652
+ # num_measures_to_flush = range_limit(j, num_measures_to_flush, n);
1653
+ num_measures_to_flush = min(max(num_measures_to_flush, j), n)
1654
+ # end if has_triggered
1655
+ # end for channel
1656
+ measures_index += 1
1657
+ measures_index = measures_index % measures_len
1658
+ if boot_count >= 0:
1659
+ boot_count = -1 if boot_count == boot_count_max else boot_count + 1
1660
+
1661
+ if has_triggered:
1662
+ flushedLen_ns = (measures_len - num_measures_to_flush) * measure_period_ns
1663
+ break
1664
+ # end for window
1665
+ if not has_triggered:
1666
+ return waveform[..., :0].view(shape[:-1] + torch.Size([0]))
1667
+
1668
+ res = waveform[:, max(pos - samplesLen_ns + flushedLen_ns, 0) :]
1669
+ # unpack batch
1670
+ return res.view(shape[:-1] + res.shape[-1:])
lib/python3.12/site-packages/torchaudio/functional/functional.py ADDED
@@ -0,0 +1,2535 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import math
4
+ import tempfile
5
+ import warnings
6
+ from collections.abc import Sequence
7
+ from typing import List, Optional, Tuple, Union
8
+
9
+ import torch
10
+ import torchaudio
11
+ from torch import Tensor
12
+ from torchaudio._internal.module_utils import deprecated
13
+
14
+ from .filtering import highpass_biquad, treble_biquad
15
+
16
+ __all__ = [
17
+ "spectrogram",
18
+ "inverse_spectrogram",
19
+ "griffinlim",
20
+ "amplitude_to_DB",
21
+ "DB_to_amplitude",
22
+ "compute_deltas",
23
+ "melscale_fbanks",
24
+ "linear_fbanks",
25
+ "create_dct",
26
+ "compute_deltas",
27
+ "detect_pitch_frequency",
28
+ "DB_to_amplitude",
29
+ "mu_law_encoding",
30
+ "mu_law_decoding",
31
+ "phase_vocoder",
32
+ "mask_along_axis",
33
+ "mask_along_axis_iid",
34
+ "sliding_window_cmn",
35
+ "spectral_centroid",
36
+ "apply_codec",
37
+ "resample",
38
+ "edit_distance",
39
+ "loudness",
40
+ "pitch_shift",
41
+ "rnnt_loss",
42
+ "psd",
43
+ "mvdr_weights_souden",
44
+ "mvdr_weights_rtf",
45
+ "rtf_evd",
46
+ "rtf_power",
47
+ "apply_beamforming",
48
+ "fftconvolve",
49
+ "convolve",
50
+ "add_noise",
51
+ "speed",
52
+ "preemphasis",
53
+ "deemphasis",
54
+ ]
55
+
56
+
57
+ def spectrogram(
58
+ waveform: Tensor,
59
+ pad: int,
60
+ window: Tensor,
61
+ n_fft: int,
62
+ hop_length: int,
63
+ win_length: int,
64
+ power: Optional[float],
65
+ normalized: Union[bool, str],
66
+ center: bool = True,
67
+ pad_mode: str = "reflect",
68
+ onesided: bool = True,
69
+ return_complex: Optional[bool] = None,
70
+ ) -> Tensor:
71
+ r"""Create a spectrogram or a batch of spectrograms from a raw audio signal.
72
+ The spectrogram can be either magnitude-only or complex.
73
+
74
+ .. devices:: CPU CUDA
75
+
76
+ .. properties:: Autograd TorchScript
77
+
78
+ Args:
79
+ waveform (Tensor): Tensor of audio of dimension `(..., time)`
80
+ pad (int): Two sided padding of signal
81
+ window (Tensor): Window tensor that is applied/multiplied to each frame/window
82
+ n_fft (int): Size of FFT
83
+ hop_length (int): Length of hop between STFT windows
84
+ win_length (int): Window size
85
+ power (float or None): Exponent for the magnitude spectrogram,
86
+ (must be > 0) e.g., 1 for magnitude, 2 for power, etc.
87
+ If None, then the complex spectrum is returned instead.
88
+ normalized (bool or str): Whether to normalize by magnitude after stft. If input is str, choices are
89
+ ``"window"`` and ``"frame_length"``, if specific normalization type is desirable. ``True`` maps to
90
+ ``"window"``. When normalized on ``"window"``, waveform is normalized upon the window's L2 energy. If
91
+ normalized on ``"frame_length"``, waveform is normalized by dividing by
92
+ :math:`(\text{frame\_length})^{0.5}`.
93
+ center (bool, optional): whether to pad :attr:`waveform` on both sides so
94
+ that the :math:`t`-th frame is centered at time :math:`t \times \text{hop\_length}`.
95
+ Default: ``True``
96
+ pad_mode (string, optional): controls the padding method used when
97
+ :attr:`center` is ``True``. Default: ``"reflect"``
98
+ onesided (bool, optional): controls whether to return half of results to
99
+ avoid redundancy. Default: ``True``
100
+ return_complex (bool, optional):
101
+ Deprecated and not used.
102
+
103
+ Returns:
104
+ Tensor: Dimension `(..., freq, time)`, freq is
105
+ ``n_fft // 2 + 1`` and ``n_fft`` is the number of
106
+ Fourier bins, and time is the number of window hops (n_frame).
107
+ """
108
+ if return_complex is not None:
109
+ warnings.warn(
110
+ "`return_complex` argument is now deprecated and is not effective."
111
+ "`torchaudio.functional.spectrogram(power=None)` always returns a tensor with "
112
+ "complex dtype. Please remove the argument in the function call."
113
+ )
114
+
115
+ if pad > 0:
116
+ # TODO add "with torch.no_grad():" back when JIT supports it
117
+ waveform = torch.nn.functional.pad(waveform, (pad, pad), "constant")
118
+
119
+ frame_length_norm, window_norm = _get_spec_norms(normalized)
120
+
121
+ # pack batch
122
+ shape = waveform.size()
123
+ waveform = waveform.reshape(-1, shape[-1])
124
+
125
+ # default values are consistent with librosa.core.spectrum._spectrogram
126
+ spec_f = torch.stft(
127
+ input=waveform,
128
+ n_fft=n_fft,
129
+ hop_length=hop_length,
130
+ win_length=win_length,
131
+ window=window,
132
+ center=center,
133
+ pad_mode=pad_mode,
134
+ normalized=frame_length_norm,
135
+ onesided=onesided,
136
+ return_complex=True,
137
+ )
138
+
139
+ # unpack batch
140
+ spec_f = spec_f.reshape(shape[:-1] + spec_f.shape[-2:])
141
+
142
+ if window_norm:
143
+ spec_f /= window.pow(2.0).sum().sqrt()
144
+ if power is not None:
145
+ if power == 1.0:
146
+ return spec_f.abs()
147
+ return spec_f.abs().pow(power)
148
+ return spec_f
149
+
150
+
151
+ def inverse_spectrogram(
152
+ spectrogram: Tensor,
153
+ length: Optional[int],
154
+ pad: int,
155
+ window: Tensor,
156
+ n_fft: int,
157
+ hop_length: int,
158
+ win_length: int,
159
+ normalized: Union[bool, str],
160
+ center: bool = True,
161
+ pad_mode: str = "reflect",
162
+ onesided: bool = True,
163
+ ) -> Tensor:
164
+ r"""Create an inverse spectrogram or a batch of inverse spectrograms from the provided
165
+ complex-valued spectrogram.
166
+
167
+ .. devices:: CPU CUDA
168
+
169
+ .. properties:: Autograd TorchScript
170
+
171
+ Args:
172
+ spectrogram (Tensor): Complex tensor of audio of dimension (..., freq, time).
173
+ length (int or None): The output length of the waveform.
174
+ pad (int): Two sided padding of signal. It is only effective when ``length`` is provided.
175
+ window (Tensor): Window tensor that is applied/multiplied to each frame/window
176
+ n_fft (int): Size of FFT
177
+ hop_length (int): Length of hop between STFT windows
178
+ win_length (int): Window size
179
+ normalized (bool or str): Whether the stft output was normalized by magnitude. If input is str, choices are
180
+ ``"window"`` and ``"frame_length"``, dependent on normalization mode. ``True`` maps to
181
+ ``"window"``.
182
+ center (bool, optional): whether the waveform was padded on both sides so
183
+ that the :math:`t`-th frame is centered at time :math:`t \times \text{hop\_length}`.
184
+ Default: ``True``
185
+ pad_mode (string, optional): controls the padding method used when
186
+ :attr:`center` is ``True``. This parameter is provided for compatibility with the
187
+ spectrogram function and is not used. Default: ``"reflect"``
188
+ onesided (bool, optional): controls whether spectrogram was done in onesided mode.
189
+ Default: ``True``
190
+
191
+ Returns:
192
+ Tensor: Dimension `(..., time)`. Least squares estimation of the original signal.
193
+ """
194
+
195
+ frame_length_norm, window_norm = _get_spec_norms(normalized)
196
+
197
+ if not spectrogram.is_complex():
198
+ raise ValueError("Expected `spectrogram` to be complex dtype.")
199
+
200
+ if window_norm:
201
+ spectrogram = spectrogram * window.pow(2.0).sum().sqrt()
202
+
203
+ # pack batch
204
+ shape = spectrogram.size()
205
+ spectrogram = spectrogram.reshape(-1, shape[-2], shape[-1])
206
+
207
+ # default values are consistent with librosa.core.spectrum._spectrogram
208
+ waveform = torch.istft(
209
+ input=spectrogram,
210
+ n_fft=n_fft,
211
+ hop_length=hop_length,
212
+ win_length=win_length,
213
+ window=window,
214
+ center=center,
215
+ normalized=frame_length_norm,
216
+ onesided=onesided,
217
+ length=length + 2 * pad if length is not None else None,
218
+ return_complex=False,
219
+ )
220
+
221
+ if length is not None and pad > 0:
222
+ # remove padding from front and back
223
+ waveform = waveform[:, pad:-pad]
224
+
225
+ # unpack batch
226
+ waveform = waveform.reshape(shape[:-2] + waveform.shape[-1:])
227
+
228
+ return waveform
229
+
230
+
231
+ def _get_spec_norms(normalized: Union[str, bool]):
232
+ frame_length_norm, window_norm = False, False
233
+ if torch.jit.isinstance(normalized, str):
234
+ if normalized not in ["frame_length", "window"]:
235
+ raise ValueError("Invalid normalized parameter: {}".format(normalized))
236
+ if normalized == "frame_length":
237
+ frame_length_norm = True
238
+ elif normalized == "window":
239
+ window_norm = True
240
+ elif torch.jit.isinstance(normalized, bool):
241
+ if normalized:
242
+ window_norm = True
243
+ else:
244
+ raise TypeError("Input type not supported")
245
+ return frame_length_norm, window_norm
246
+
247
+
248
+ def _get_complex_dtype(real_dtype: torch.dtype):
249
+ if real_dtype == torch.double:
250
+ return torch.cdouble
251
+ if real_dtype == torch.float:
252
+ return torch.cfloat
253
+ if real_dtype == torch.half:
254
+ return torch.complex32
255
+ raise ValueError(f"Unexpected dtype {real_dtype}")
256
+
257
+
258
+ def griffinlim(
259
+ specgram: Tensor,
260
+ window: Tensor,
261
+ n_fft: int,
262
+ hop_length: int,
263
+ win_length: int,
264
+ power: float,
265
+ n_iter: int,
266
+ momentum: float,
267
+ length: Optional[int],
268
+ rand_init: bool,
269
+ ) -> Tensor:
270
+ r"""Compute waveform from a linear scale magnitude spectrogram using the Griffin-Lim transformation.
271
+
272
+ .. devices:: CPU CUDA
273
+
274
+ .. properties:: Autograd TorchScript
275
+
276
+ Implementation ported from
277
+ *librosa* :cite:`brian_mcfee-proc-scipy-2015`, *A fast Griffin-Lim algorithm* :cite:`6701851`
278
+ and *Signal estimation from modified short-time Fourier transform* :cite:`1172092`.
279
+
280
+ Args:
281
+ specgram (Tensor): A magnitude-only STFT spectrogram of dimension `(..., freq, frames)`
282
+ where freq is ``n_fft // 2 + 1``.
283
+ window (Tensor): Window tensor that is applied/multiplied to each frame/window
284
+ n_fft (int): Size of FFT, creates ``n_fft // 2 + 1`` bins
285
+ hop_length (int): Length of hop between STFT windows. (
286
+ Default: ``win_length // 2``)
287
+ win_length (int): Window size. (Default: ``n_fft``)
288
+ power (float): Exponent for the magnitude spectrogram,
289
+ (must be > 0) e.g., 1 for magnitude, 2 for power, etc.
290
+ n_iter (int): Number of iteration for phase recovery process.
291
+ momentum (float): The momentum parameter for fast Griffin-Lim.
292
+ Setting this to 0 recovers the original Griffin-Lim method.
293
+ Values near 1 can lead to faster convergence, but above 1 may not converge.
294
+ length (int or None): Array length of the expected output.
295
+ rand_init (bool): Initializes phase randomly if True, to zero otherwise.
296
+
297
+ Returns:
298
+ Tensor: waveform of `(..., time)`, where time equals the ``length`` parameter if given.
299
+ """
300
+ if not 0 <= momentum < 1:
301
+ raise ValueError("momentum must be in range [0, 1). Found: {}".format(momentum))
302
+
303
+ momentum = momentum / (1 + momentum)
304
+
305
+ # pack batch
306
+ shape = specgram.size()
307
+ specgram = specgram.reshape([-1] + list(shape[-2:]))
308
+
309
+ specgram = specgram.pow(1 / power)
310
+
311
+ # initialize the phase
312
+ if rand_init:
313
+ angles = torch.rand(specgram.size(), dtype=_get_complex_dtype(specgram.dtype), device=specgram.device)
314
+ else:
315
+ angles = torch.full(specgram.size(), 1, dtype=_get_complex_dtype(specgram.dtype), device=specgram.device)
316
+
317
+ # And initialize the previous iterate to 0
318
+ tprev = torch.tensor(0.0, dtype=specgram.dtype, device=specgram.device)
319
+ for _ in range(n_iter):
320
+ # Invert with our current estimate of the phases
321
+ inverse = torch.istft(
322
+ specgram * angles, n_fft=n_fft, hop_length=hop_length, win_length=win_length, window=window, length=length
323
+ )
324
+
325
+ # Rebuild the spectrogram
326
+ rebuilt = torch.stft(
327
+ input=inverse,
328
+ n_fft=n_fft,
329
+ hop_length=hop_length,
330
+ win_length=win_length,
331
+ window=window,
332
+ center=True,
333
+ pad_mode="reflect",
334
+ normalized=False,
335
+ onesided=True,
336
+ return_complex=True,
337
+ )
338
+
339
+ # Update our phase estimates
340
+ angles = rebuilt
341
+ if momentum:
342
+ angles = angles - tprev.mul_(momentum)
343
+ angles = angles.div(angles.abs().add(1e-16))
344
+
345
+ # Store the previous iterate
346
+ tprev = rebuilt
347
+
348
+ # Return the final phase estimates
349
+ waveform = torch.istft(
350
+ specgram * angles, n_fft=n_fft, hop_length=hop_length, win_length=win_length, window=window, length=length
351
+ )
352
+
353
+ # unpack batch
354
+ waveform = waveform.reshape(shape[:-2] + waveform.shape[-1:])
355
+
356
+ return waveform
357
+
358
+
359
+ def amplitude_to_DB(
360
+ x: Tensor, multiplier: float, amin: float, db_multiplier: float, top_db: Optional[float] = None
361
+ ) -> Tensor:
362
+ r"""Turn a spectrogram from the power/amplitude scale to the decibel scale.
363
+
364
+ .. devices:: CPU CUDA
365
+
366
+ .. properties:: Autograd TorchScript
367
+
368
+ The output of each tensor in a batch depends on the maximum value of that tensor,
369
+ and so may return different values for an audio clip split into snippets vs. a full clip.
370
+
371
+ Args:
372
+
373
+ x (Tensor): Input spectrogram(s) before being converted to decibel scale.
374
+ The expected shapes are ``(freq, time)``, ``(channel, freq, time)`` or
375
+ ``(..., batch, channel, freq, time)``.
376
+
377
+ .. note::
378
+
379
+ When ``top_db`` is specified, cut-off values are computed for each audio
380
+ in the batch. Therefore if the input shape is 4D (or larger), different
381
+ cut-off values are used for audio data in the batch.
382
+ If the input shape is 2D or 3D, a single cutoff value is used.
383
+
384
+ multiplier (float): Use 10. for power and 20. for amplitude
385
+ amin (float): Number to clamp ``x``
386
+ db_multiplier (float): Log10(max(reference value and amin))
387
+ top_db (float or None, optional): Minimum negative cut-off in decibels. A reasonable number
388
+ is 80. (Default: ``None``)
389
+
390
+ Returns:
391
+ Tensor: Output tensor in decibel scale
392
+ """
393
+ x_db = multiplier * torch.log10(torch.clamp(x, min=amin))
394
+ x_db -= multiplier * db_multiplier
395
+
396
+ if top_db is not None:
397
+ # Expand batch
398
+ shape = x_db.size()
399
+ packed_channels = shape[-3] if x_db.dim() > 2 else 1
400
+ x_db = x_db.reshape(-1, packed_channels, shape[-2], shape[-1])
401
+
402
+ x_db = torch.max(x_db, (x_db.amax(dim=(-3, -2, -1)) - top_db).view(-1, 1, 1, 1))
403
+
404
+ # Repack batch
405
+ x_db = x_db.reshape(shape)
406
+
407
+ return x_db
408
+
409
+
410
+ def DB_to_amplitude(x: Tensor, ref: float, power: float) -> Tensor:
411
+ r"""Turn a tensor from the decibel scale to the power/amplitude scale.
412
+
413
+ .. devices:: CPU CUDA
414
+
415
+ .. properties:: TorchScript
416
+
417
+ Args:
418
+ x (Tensor): Input tensor before being converted to power/amplitude scale.
419
+ ref (float): Reference which the output will be scaled by.
420
+ power (float): If power equals 1, will compute DB to power. If 0.5, will compute DB to amplitude.
421
+
422
+ Returns:
423
+ Tensor: Output tensor in power/amplitude scale.
424
+ """
425
+ return ref * torch.pow(torch.pow(10.0, 0.1 * x), power)
426
+
427
+
428
+ def _hz_to_mel(freq: float, mel_scale: str = "htk") -> float:
429
+ r"""Convert Hz to Mels.
430
+
431
+ Args:
432
+ freqs (float): Frequencies in Hz
433
+ mel_scale (str, optional): Scale to use: ``htk`` or ``slaney``. (Default: ``htk``)
434
+
435
+ Returns:
436
+ mels (float): Frequency in Mels
437
+ """
438
+
439
+ if mel_scale not in ["slaney", "htk"]:
440
+ raise ValueError('mel_scale should be one of "htk" or "slaney".')
441
+
442
+ if mel_scale == "htk":
443
+ return 2595.0 * math.log10(1.0 + (freq / 700.0))
444
+
445
+ # Fill in the linear part
446
+ f_min = 0.0
447
+ f_sp = 200.0 / 3
448
+
449
+ mels = (freq - f_min) / f_sp
450
+
451
+ # Fill in the log-scale part
452
+ min_log_hz = 1000.0
453
+ min_log_mel = (min_log_hz - f_min) / f_sp
454
+ logstep = math.log(6.4) / 27.0
455
+
456
+ if freq >= min_log_hz:
457
+ mels = min_log_mel + math.log(freq / min_log_hz) / logstep
458
+
459
+ return mels
460
+
461
+
462
+ def _mel_to_hz(mels: Tensor, mel_scale: str = "htk") -> Tensor:
463
+ """Convert mel bin numbers to frequencies.
464
+
465
+ Args:
466
+ mels (Tensor): Mel frequencies
467
+ mel_scale (str, optional): Scale to use: ``htk`` or ``slaney``. (Default: ``htk``)
468
+
469
+ Returns:
470
+ freqs (Tensor): Mels converted in Hz
471
+ """
472
+
473
+ if mel_scale not in ["slaney", "htk"]:
474
+ raise ValueError('mel_scale should be one of "htk" or "slaney".')
475
+
476
+ if mel_scale == "htk":
477
+ return 700.0 * (10.0 ** (mels / 2595.0) - 1.0)
478
+
479
+ # Fill in the linear scale
480
+ f_min = 0.0
481
+ f_sp = 200.0 / 3
482
+ freqs = f_min + f_sp * mels
483
+
484
+ # And now the nonlinear scale
485
+ min_log_hz = 1000.0
486
+ min_log_mel = (min_log_hz - f_min) / f_sp
487
+ logstep = math.log(6.4) / 27.0
488
+
489
+ log_t = mels >= min_log_mel
490
+ freqs[log_t] = min_log_hz * torch.exp(logstep * (mels[log_t] - min_log_mel))
491
+
492
+ return freqs
493
+
494
+
495
+ def _create_triangular_filterbank(
496
+ all_freqs: Tensor,
497
+ f_pts: Tensor,
498
+ ) -> Tensor:
499
+ """Create a triangular filter bank.
500
+
501
+ Args:
502
+ all_freqs (Tensor): STFT freq points of size (`n_freqs`).
503
+ f_pts (Tensor): Filter mid points of size (`n_filter`).
504
+
505
+ Returns:
506
+ fb (Tensor): The filter bank of size (`n_freqs`, `n_filter`).
507
+ """
508
+ # Adopted from Librosa
509
+ # calculate the difference between each filter mid point and each stft freq point in hertz
510
+ f_diff = f_pts[1:] - f_pts[:-1] # (n_filter + 1)
511
+ slopes = f_pts.unsqueeze(0) - all_freqs.unsqueeze(1) # (n_freqs, n_filter + 2)
512
+ # create overlapping triangles
513
+ zero = torch.zeros(1)
514
+ down_slopes = (-1.0 * slopes[:, :-2]) / f_diff[:-1] # (n_freqs, n_filter)
515
+ up_slopes = slopes[:, 2:] / f_diff[1:] # (n_freqs, n_filter)
516
+ fb = torch.max(zero, torch.min(down_slopes, up_slopes))
517
+
518
+ return fb
519
+
520
+
521
+ def melscale_fbanks(
522
+ n_freqs: int,
523
+ f_min: float,
524
+ f_max: float,
525
+ n_mels: int,
526
+ sample_rate: int,
527
+ norm: Optional[str] = None,
528
+ mel_scale: str = "htk",
529
+ ) -> Tensor:
530
+ r"""Create a frequency bin conversion matrix.
531
+
532
+ .. devices:: CPU
533
+
534
+ .. properties:: TorchScript
535
+
536
+ Note:
537
+ For the sake of the numerical compatibility with librosa, not all the coefficients
538
+ in the resulting filter bank has magnitude of 1.
539
+
540
+ .. image:: https://download.pytorch.org/torchaudio/doc-assets/mel_fbanks.png
541
+ :alt: Visualization of generated filter bank
542
+
543
+ Args:
544
+ n_freqs (int): Number of frequencies to highlight/apply
545
+ f_min (float): Minimum frequency (Hz)
546
+ f_max (float): Maximum frequency (Hz)
547
+ n_mels (int): Number of mel filterbanks
548
+ sample_rate (int): Sample rate of the audio waveform
549
+ norm (str or None, optional): If "slaney", divide the triangular mel weights by the width of the mel band
550
+ (area normalization). (Default: ``None``)
551
+ mel_scale (str, optional): Scale to use: ``htk`` or ``slaney``. (Default: ``htk``)
552
+
553
+ Returns:
554
+ Tensor: Triangular filter banks (fb matrix) of size (``n_freqs``, ``n_mels``)
555
+ meaning number of frequencies to highlight/apply to x the number of filterbanks.
556
+ Each column is a filterbank so that assuming there is a matrix A of
557
+ size (..., ``n_freqs``), the applied result would be
558
+ ``A @ melscale_fbanks(A.size(-1), ...)``.
559
+
560
+ """
561
+
562
+ if norm is not None and norm != "slaney":
563
+ raise ValueError('norm must be one of None or "slaney"')
564
+
565
+ # freq bins
566
+ all_freqs = torch.linspace(0, sample_rate // 2, n_freqs)
567
+
568
+ # calculate mel freq bins
569
+ m_min = _hz_to_mel(f_min, mel_scale=mel_scale)
570
+ m_max = _hz_to_mel(f_max, mel_scale=mel_scale)
571
+
572
+ m_pts = torch.linspace(m_min, m_max, n_mels + 2)
573
+ f_pts = _mel_to_hz(m_pts, mel_scale=mel_scale)
574
+
575
+ # create filterbank
576
+ fb = _create_triangular_filterbank(all_freqs, f_pts)
577
+
578
+ if norm is not None and norm == "slaney":
579
+ # Slaney-style mel is scaled to be approx constant energy per channel
580
+ enorm = 2.0 / (f_pts[2 : n_mels + 2] - f_pts[:n_mels])
581
+ fb *= enorm.unsqueeze(0)
582
+
583
+ if (fb.max(dim=0).values == 0.0).any():
584
+ warnings.warn(
585
+ "At least one mel filterbank has all zero values. "
586
+ f"The value for `n_mels` ({n_mels}) may be set too high. "
587
+ f"Or, the value for `n_freqs` ({n_freqs}) may be set too low."
588
+ )
589
+
590
+ return fb
591
+
592
+
593
+ def linear_fbanks(
594
+ n_freqs: int,
595
+ f_min: float,
596
+ f_max: float,
597
+ n_filter: int,
598
+ sample_rate: int,
599
+ ) -> Tensor:
600
+ r"""Creates a linear triangular filterbank.
601
+
602
+ .. devices:: CPU
603
+
604
+ .. properties:: TorchScript
605
+
606
+ Note:
607
+ For the sake of the numerical compatibility with librosa, not all the coefficients
608
+ in the resulting filter bank has magnitude of 1.
609
+
610
+ .. image:: https://download.pytorch.org/torchaudio/doc-assets/lin_fbanks.png
611
+ :alt: Visualization of generated filter bank
612
+
613
+ Args:
614
+ n_freqs (int): Number of frequencies to highlight/apply
615
+ f_min (float): Minimum frequency (Hz)
616
+ f_max (float): Maximum frequency (Hz)
617
+ n_filter (int): Number of (linear) triangular filter
618
+ sample_rate (int): Sample rate of the audio waveform
619
+
620
+ Returns:
621
+ Tensor: Triangular filter banks (fb matrix) of size (``n_freqs``, ``n_filter``)
622
+ meaning number of frequencies to highlight/apply to x the number of filterbanks.
623
+ Each column is a filterbank so that assuming there is a matrix A of
624
+ size (..., ``n_freqs``), the applied result would be
625
+ ``A * linear_fbanks(A.size(-1), ...)``.
626
+ """
627
+ # freq bins
628
+ all_freqs = torch.linspace(0, sample_rate // 2, n_freqs)
629
+
630
+ # filter mid-points
631
+ f_pts = torch.linspace(f_min, f_max, n_filter + 2)
632
+
633
+ # create filterbank
634
+ fb = _create_triangular_filterbank(all_freqs, f_pts)
635
+
636
+ return fb
637
+
638
+
639
+ def create_dct(n_mfcc: int, n_mels: int, norm: Optional[str]) -> Tensor:
640
+ r"""Create a DCT transformation matrix with shape (``n_mels``, ``n_mfcc``),
641
+ normalized depending on norm.
642
+
643
+ .. devices:: CPU
644
+
645
+ .. properties:: TorchScript
646
+
647
+ Args:
648
+ n_mfcc (int): Number of mfc coefficients to retain
649
+ n_mels (int): Number of mel filterbanks
650
+ norm (str or None): Norm to use (either "ortho" or None)
651
+
652
+ Returns:
653
+ Tensor: The transformation matrix, to be right-multiplied to
654
+ row-wise data of size (``n_mels``, ``n_mfcc``).
655
+ """
656
+
657
+ if norm is not None and norm != "ortho":
658
+ raise ValueError('norm must be either "ortho" or None')
659
+
660
+ # http://en.wikipedia.org/wiki/Discrete_cosine_transform#DCT-II
661
+ n = torch.arange(float(n_mels))
662
+ k = torch.arange(float(n_mfcc)).unsqueeze(1)
663
+ dct = torch.cos(math.pi / float(n_mels) * (n + 0.5) * k) # size (n_mfcc, n_mels)
664
+
665
+ if norm is None:
666
+ dct *= 2.0
667
+ else:
668
+ dct[0] *= 1.0 / math.sqrt(2.0)
669
+ dct *= math.sqrt(2.0 / float(n_mels))
670
+ return dct.t()
671
+
672
+
673
+ def mu_law_encoding(x: Tensor, quantization_channels: int) -> Tensor:
674
+ r"""Encode signal based on mu-law companding.
675
+
676
+ .. devices:: CPU CUDA
677
+
678
+ .. properties:: TorchScript
679
+
680
+ For more info see the
681
+ `Wikipedia Entry <https://en.wikipedia.org/wiki/%CE%9C-law_algorithm>`_
682
+
683
+ This algorithm expects the signal has been scaled to between -1 and 1 and
684
+ returns a signal encoded with values from 0 to quantization_channels - 1.
685
+
686
+ Args:
687
+ x (Tensor): Input tensor
688
+ quantization_channels (int): Number of channels
689
+
690
+ Returns:
691
+ Tensor: Input after mu-law encoding
692
+ """
693
+ mu = quantization_channels - 1.0
694
+ if not x.is_floating_point():
695
+ warnings.warn(
696
+ "The input Tensor must be of floating type. \
697
+ This will be an error in the v0.12 release."
698
+ )
699
+ x = x.to(torch.float)
700
+ mu = torch.tensor(mu, dtype=x.dtype)
701
+ x_mu = torch.sign(x) * torch.log1p(mu * torch.abs(x)) / torch.log1p(mu)
702
+ x_mu = ((x_mu + 1) / 2 * mu + 0.5).to(torch.int64)
703
+ return x_mu
704
+
705
+
706
+ def mu_law_decoding(x_mu: Tensor, quantization_channels: int) -> Tensor:
707
+ r"""Decode mu-law encoded signal.
708
+
709
+ .. devices:: CPU CUDA
710
+
711
+ .. properties:: TorchScript
712
+
713
+ For more info see the
714
+ `Wikipedia Entry <https://en.wikipedia.org/wiki/%CE%9C-law_algorithm>`_
715
+
716
+ This expects an input with values between 0 and quantization_channels - 1
717
+ and returns a signal scaled between -1 and 1.
718
+
719
+ Args:
720
+ x_mu (Tensor): Input tensor
721
+ quantization_channels (int): Number of channels
722
+
723
+ Returns:
724
+ Tensor: Input after mu-law decoding
725
+ """
726
+ mu = quantization_channels - 1.0
727
+ if not x_mu.is_floating_point():
728
+ x_mu = x_mu.to(torch.float)
729
+ mu = torch.tensor(mu, dtype=x_mu.dtype)
730
+ x = ((x_mu) / mu) * 2 - 1.0
731
+ x = torch.sign(x) * (torch.exp(torch.abs(x) * torch.log1p(mu)) - 1.0) / mu
732
+ return x
733
+
734
+
735
+ def phase_vocoder(complex_specgrams: Tensor, rate: float, phase_advance: Tensor) -> Tensor:
736
+ r"""Given a STFT tensor, speed up in time without modifying pitch by a factor of ``rate``.
737
+
738
+ .. devices:: CPU CUDA
739
+
740
+ .. properties:: Autograd TorchScript
741
+
742
+ Args:
743
+ complex_specgrams (Tensor):
744
+ A tensor of dimension `(..., freq, num_frame)` with complex dtype.
745
+ rate (float): Speed-up factor
746
+ phase_advance (Tensor): Expected phase advance in each bin. Dimension of `(freq, 1)`
747
+
748
+ Returns:
749
+ Tensor:
750
+ Stretched spectrogram. The resulting tensor is of the same dtype as the input
751
+ spectrogram, but the number of frames is changed to ``ceil(num_frame / rate)``.
752
+
753
+ Example
754
+ >>> freq, hop_length = 1025, 512
755
+ >>> # (channel, freq, time)
756
+ >>> complex_specgrams = torch.randn(2, freq, 300, dtype=torch.cfloat)
757
+ >>> rate = 1.3 # Speed up by 30%
758
+ >>> phase_advance = torch.linspace(
759
+ >>> 0, math.pi * hop_length, freq)[..., None]
760
+ >>> x = phase_vocoder(complex_specgrams, rate, phase_advance)
761
+ >>> x.shape # with 231 == ceil(300 / 1.3)
762
+ torch.Size([2, 1025, 231])
763
+ """
764
+ if rate == 1.0:
765
+ return complex_specgrams
766
+
767
+ # pack batch
768
+ shape = complex_specgrams.size()
769
+ complex_specgrams = complex_specgrams.reshape([-1] + list(shape[-2:]))
770
+
771
+ # Figures out the corresponding real dtype, i.e. complex128 -> float64, complex64 -> float32
772
+ # Note torch.real is a view so it does not incur any memory copy.
773
+ real_dtype = torch.real(complex_specgrams).dtype
774
+ time_steps = torch.arange(0, complex_specgrams.size(-1), rate, device=complex_specgrams.device, dtype=real_dtype)
775
+
776
+ alphas = time_steps % 1.0
777
+ phase_0 = complex_specgrams[..., :1].angle()
778
+
779
+ # Time Padding
780
+ complex_specgrams = torch.nn.functional.pad(complex_specgrams, [0, 2])
781
+
782
+ # (new_bins, freq, 2)
783
+ complex_specgrams_0 = complex_specgrams.index_select(-1, time_steps.long())
784
+ complex_specgrams_1 = complex_specgrams.index_select(-1, (time_steps + 1).long())
785
+
786
+ angle_0 = complex_specgrams_0.angle()
787
+ angle_1 = complex_specgrams_1.angle()
788
+
789
+ norm_0 = complex_specgrams_0.abs()
790
+ norm_1 = complex_specgrams_1.abs()
791
+
792
+ phase = angle_1 - angle_0 - phase_advance
793
+ phase = phase - 2 * math.pi * torch.round(phase / (2 * math.pi))
794
+
795
+ # Compute Phase Accum
796
+ phase = phase + phase_advance
797
+ phase = torch.cat([phase_0, phase[..., :-1]], dim=-1)
798
+ phase_acc = torch.cumsum(phase, -1)
799
+
800
+ mag = alphas * norm_1 + (1 - alphas) * norm_0
801
+
802
+ complex_specgrams_stretch = torch.polar(mag, phase_acc)
803
+
804
+ # unpack batch
805
+ complex_specgrams_stretch = complex_specgrams_stretch.reshape(shape[:-2] + complex_specgrams_stretch.shape[1:])
806
+ return complex_specgrams_stretch
807
+
808
+
809
+ def _get_mask_param(mask_param: int, p: float, axis_length: int) -> int:
810
+ if p == 1.0:
811
+ return mask_param
812
+ else:
813
+ return min(mask_param, int(axis_length * p))
814
+
815
+
816
+ def mask_along_axis_iid(
817
+ specgrams: Tensor,
818
+ mask_param: int,
819
+ mask_value: float,
820
+ axis: int,
821
+ p: float = 1.0,
822
+ ) -> Tensor:
823
+ r"""Apply a mask along ``axis``.
824
+
825
+ .. devices:: CPU CUDA
826
+
827
+ .. properties:: Autograd TorchScript
828
+
829
+ Mask will be applied from indices ``[v_0, v_0 + v)``,
830
+ where ``v`` is sampled from ``uniform(0, max_v)`` and
831
+ ``v_0`` from ``uniform(0, specgrams.size(axis) - v)``,
832
+ with ``max_v = mask_param`` when ``p = 1.0`` and
833
+ ``max_v = min(mask_param, floor(specgrams.size(axis) * p))`` otherwise.
834
+
835
+ Args:
836
+ specgrams (Tensor): Real spectrograms `(..., freq, time)`, with at least 3 dimensions.
837
+ mask_param (int): Number of columns to be masked will be uniformly sampled from [0, mask_param]
838
+ mask_value (float): Value to assign to the masked columns
839
+ axis (int): Axis to apply masking on, which should be the one of the last two dimensions.
840
+ p (float, optional): maximum proportion of columns that can be masked. (Default: 1.0)
841
+
842
+ Returns:
843
+ Tensor: Masked spectrograms with the same dimensions as input specgrams Tensor`
844
+ """
845
+
846
+ dim = specgrams.dim()
847
+
848
+ if dim < 3:
849
+ raise ValueError(f"Spectrogram must have at least three dimensions ({dim} given).")
850
+
851
+ if axis not in [dim - 2, dim - 1]:
852
+ raise ValueError(
853
+ f"Only Frequency and Time masking are supported (axis {dim-2} and axis {dim-1} supported; {axis} given)."
854
+ )
855
+
856
+ if not 0.0 <= p <= 1.0:
857
+ raise ValueError(f"The value of p must be between 0.0 and 1.0 ({p} given).")
858
+
859
+ mask_param = _get_mask_param(mask_param, p, specgrams.shape[axis])
860
+ if mask_param < 1:
861
+ return specgrams
862
+
863
+ device = specgrams.device
864
+ dtype = specgrams.dtype
865
+
866
+ value = torch.rand(specgrams.shape[: (dim - 2)], device=device, dtype=dtype) * mask_param
867
+ min_value = torch.rand(specgrams.shape[: (dim - 2)], device=device, dtype=dtype) * (specgrams.size(axis) - value)
868
+
869
+ # Create broadcastable mask
870
+ mask_start = min_value.long()[..., None, None]
871
+ mask_end = (min_value.long() + value.long())[..., None, None]
872
+ mask = torch.arange(0, specgrams.size(axis), device=device, dtype=dtype)
873
+
874
+ # Per batch example masking
875
+ specgrams = specgrams.transpose(axis, -1)
876
+ specgrams = specgrams.masked_fill((mask >= mask_start) & (mask < mask_end), mask_value)
877
+ specgrams = specgrams.transpose(axis, -1)
878
+
879
+ return specgrams
880
+
881
+
882
+ def mask_along_axis(
883
+ specgram: Tensor,
884
+ mask_param: int,
885
+ mask_value: float,
886
+ axis: int,
887
+ p: float = 1.0,
888
+ ) -> Tensor:
889
+ r"""Apply a mask along ``axis``.
890
+
891
+ .. devices:: CPU CUDA
892
+
893
+ .. properties:: Autograd TorchScript
894
+
895
+ Mask will be applied from indices ``[v_0, v_0 + v)``,
896
+ where ``v`` is sampled from ``uniform(0, max_v)`` and
897
+ ``v_0`` from ``uniform(0, specgram.size(axis) - v)``, with
898
+ ``max_v = mask_param`` when ``p = 1.0`` and
899
+ ``max_v = min(mask_param, floor(specgram.size(axis) * p))``
900
+ otherwise.
901
+ All examples will have the same mask interval.
902
+
903
+ Args:
904
+ specgram (Tensor): Real spectrograms `(..., freq, time)`, with at least 2 dimensions.
905
+ mask_param (int): Number of columns to be masked will be uniformly sampled from [0, mask_param]
906
+ mask_value (float): Value to assign to the masked columns
907
+ axis (int): Axis to apply masking on, which should be the one of the last two dimensions.
908
+ p (float, optional): maximum proportion of columns that can be masked. (Default: 1.0)
909
+
910
+ Returns:
911
+ Tensor: Masked spectrograms with the same dimensions as input specgram Tensor
912
+ """
913
+ dim = specgram.dim()
914
+
915
+ if dim < 2:
916
+ raise ValueError(f"Spectrogram must have at least two dimensions (time and frequency) ({dim} given).")
917
+
918
+ if axis not in [dim - 2, dim - 1]:
919
+ raise ValueError(
920
+ f"Only Frequency and Time masking are supported (axis {dim-2} and axis {dim-1} supported; {axis} given)."
921
+ )
922
+
923
+ if not 0.0 <= p <= 1.0:
924
+ raise ValueError(f"The value of p must be between 0.0 and 1.0 ({p} given).")
925
+
926
+ mask_param = _get_mask_param(mask_param, p, specgram.shape[axis])
927
+ if mask_param < 1:
928
+ return specgram
929
+
930
+ # pack batch
931
+ shape = specgram.size()
932
+ specgram = specgram.reshape([-1] + list(shape[-2:]))
933
+ # After packing, specgram is a 3D tensor, and the axis corresponding to the to-be-masked dimension
934
+ # is now (axis - dim + 3), e.g. a tensor of shape (10, 2, 50, 10, 2) becomes a tensor of shape (1000, 10, 2).
935
+ value = torch.rand(1) * mask_param
936
+ min_value = torch.rand(1) * (specgram.size(axis - dim + 3) - value)
937
+
938
+ mask_start = (min_value.long()).squeeze()
939
+ mask_end = (min_value.long() + value.long()).squeeze()
940
+ mask = torch.arange(0, specgram.shape[axis - dim + 3], device=specgram.device, dtype=specgram.dtype)
941
+ mask = (mask >= mask_start) & (mask < mask_end)
942
+ # unsqueeze the mask if the axis is frequency
943
+ if axis == dim - 2:
944
+ mask = mask.unsqueeze(-1)
945
+
946
+ if mask_end - mask_start >= mask_param:
947
+ raise ValueError("Number of columns to be masked should be less than mask_param")
948
+
949
+ specgram = specgram.masked_fill(mask, mask_value)
950
+
951
+ # unpack batch
952
+ specgram = specgram.reshape(shape[:-2] + specgram.shape[-2:])
953
+
954
+ return specgram
955
+
956
+
957
+ def compute_deltas(specgram: Tensor, win_length: int = 5, mode: str = "replicate") -> Tensor:
958
+ r"""Compute delta coefficients of a tensor, usually a spectrogram:
959
+
960
+ .. devices:: CPU CUDA
961
+
962
+ .. properties:: TorchScript
963
+
964
+ .. math::
965
+ d_t = \frac{\sum_{n=1}^{\text{N}} n (c_{t+n} - c_{t-n})}{2 \sum_{n=1}^{\text{N}} n^2}
966
+
967
+ where :math:`d_t` is the deltas at time :math:`t`,
968
+ :math:`c_t` is the spectrogram coeffcients at time :math:`t`,
969
+ :math:`N` is ``(win_length-1)//2``.
970
+
971
+ Args:
972
+ specgram (Tensor): Tensor of audio of dimension `(..., freq, time)`
973
+ win_length (int, optional): The window length used for computing delta (Default: ``5``)
974
+ mode (str, optional): Mode parameter passed to padding (Default: ``"replicate"``)
975
+
976
+ Returns:
977
+ Tensor: Tensor of deltas of dimension `(..., freq, time)`
978
+
979
+ Example
980
+ >>> specgram = torch.randn(1, 40, 1000)
981
+ >>> delta = compute_deltas(specgram)
982
+ >>> delta2 = compute_deltas(delta)
983
+ """
984
+ device = specgram.device
985
+ dtype = specgram.dtype
986
+
987
+ # pack batch
988
+ shape = specgram.size()
989
+ specgram = specgram.reshape(1, -1, shape[-1])
990
+
991
+ if win_length < 3:
992
+ raise ValueError(f"Window length should be greater than or equal to 3. Found win_length {win_length}")
993
+
994
+ n = (win_length - 1) // 2
995
+
996
+ # twice sum of integer squared
997
+ denom = n * (n + 1) * (2 * n + 1) / 3
998
+
999
+ specgram = torch.nn.functional.pad(specgram, (n, n), mode=mode)
1000
+
1001
+ kernel = torch.arange(-n, n + 1, 1, device=device, dtype=dtype).repeat(specgram.shape[1], 1, 1)
1002
+
1003
+ output = torch.nn.functional.conv1d(specgram, kernel, groups=specgram.shape[1]) / denom
1004
+
1005
+ # unpack batch
1006
+ output = output.reshape(shape)
1007
+
1008
+ return output
1009
+
1010
+
1011
+ def _compute_nccf(waveform: Tensor, sample_rate: int, frame_time: float, freq_low: int) -> Tensor:
1012
+ r"""
1013
+ Compute Normalized Cross-Correlation Function (NCCF).
1014
+
1015
+ .. math::
1016
+ \phi_i(m) = \frac{\sum_{n=b_i}^{b_i + N-1} w(n) w(m+n)}{\sqrt{E(b_i) E(m+b_i)}},
1017
+
1018
+ where
1019
+ :math:`\phi_i(m)` is the NCCF at frame :math:`i` with lag :math:`m`,
1020
+ :math:`w` is the waveform,
1021
+ :math:`N` is the length of a frame,
1022
+ :math:`b_i` is the beginning of frame :math:`i`,
1023
+ :math:`E(j)` is the energy :math:`\sum_{n=j}^{j+N-1} w^2(n)`.
1024
+ """
1025
+
1026
+ EPSILON = 10 ** (-9)
1027
+
1028
+ # Number of lags to check
1029
+ lags = int(math.ceil(sample_rate / freq_low))
1030
+
1031
+ frame_size = int(math.ceil(sample_rate * frame_time))
1032
+
1033
+ waveform_length = waveform.size()[-1]
1034
+ num_of_frames = int(math.ceil(waveform_length / frame_size))
1035
+
1036
+ p = lags + num_of_frames * frame_size - waveform_length
1037
+ waveform = torch.nn.functional.pad(waveform, (0, p))
1038
+
1039
+ # Compute lags
1040
+ output_lag = []
1041
+ for lag in range(1, lags + 1):
1042
+ s1 = waveform[..., :-lag].unfold(-1, frame_size, frame_size)[..., :num_of_frames, :]
1043
+ s2 = waveform[..., lag:].unfold(-1, frame_size, frame_size)[..., :num_of_frames, :]
1044
+
1045
+ output_frames = (
1046
+ (s1 * s2).sum(-1)
1047
+ / (EPSILON + torch.linalg.vector_norm(s1, ord=2, dim=-1)).pow(2)
1048
+ / (EPSILON + torch.linalg.vector_norm(s2, ord=2, dim=-1)).pow(2)
1049
+ )
1050
+
1051
+ output_lag.append(output_frames.unsqueeze(-1))
1052
+
1053
+ nccf = torch.cat(output_lag, -1)
1054
+
1055
+ return nccf
1056
+
1057
+
1058
+ def _combine_max(a: Tuple[Tensor, Tensor], b: Tuple[Tensor, Tensor], thresh: float = 0.99) -> Tuple[Tensor, Tensor]:
1059
+ """
1060
+ Take value from first if bigger than a multiplicative factor of the second, elementwise.
1061
+ """
1062
+ mask = a[0] > thresh * b[0]
1063
+ values = mask * a[0] + ~mask * b[0]
1064
+ indices = mask * a[1] + ~mask * b[1]
1065
+ return values, indices
1066
+
1067
+
1068
+ def _find_max_per_frame(nccf: Tensor, sample_rate: int, freq_high: int) -> Tensor:
1069
+ r"""
1070
+ For each frame, take the highest value of NCCF,
1071
+ apply centered median smoothing, and convert to frequency.
1072
+
1073
+ Note: If the max among all the lags is very close
1074
+ to the first half of lags, then the latter is taken.
1075
+ """
1076
+
1077
+ lag_min = int(math.ceil(sample_rate / freq_high))
1078
+
1079
+ # Find near enough max that is smallest
1080
+
1081
+ best = torch.max(nccf[..., lag_min:], -1)
1082
+
1083
+ half_size = nccf.shape[-1] // 2
1084
+ half = torch.max(nccf[..., lag_min:half_size], -1)
1085
+
1086
+ best = _combine_max(half, best)
1087
+ indices = best[1]
1088
+
1089
+ # Add back minimal lag
1090
+ indices += lag_min
1091
+ # Add 1 empirical calibration offset
1092
+ indices += 1
1093
+
1094
+ return indices
1095
+
1096
+
1097
+ def _median_smoothing(indices: Tensor, win_length: int) -> Tensor:
1098
+ r"""
1099
+ Apply median smoothing to the 1D tensor over the given window.
1100
+ """
1101
+
1102
+ # Centered windowed
1103
+ pad_length = (win_length - 1) // 2
1104
+
1105
+ # "replicate" padding in any dimension
1106
+ indices = torch.nn.functional.pad(indices, (pad_length, 0), mode="constant", value=0.0)
1107
+
1108
+ indices[..., :pad_length] = torch.cat(pad_length * [indices[..., pad_length].unsqueeze(-1)], dim=-1)
1109
+ roll = indices.unfold(-1, win_length, 1)
1110
+
1111
+ values, _ = torch.median(roll, -1)
1112
+ return values
1113
+
1114
+
1115
+ def detect_pitch_frequency(
1116
+ waveform: Tensor,
1117
+ sample_rate: int,
1118
+ frame_time: float = 10 ** (-2),
1119
+ win_length: int = 30,
1120
+ freq_low: int = 85,
1121
+ freq_high: int = 3400,
1122
+ ) -> Tensor:
1123
+ r"""Detect pitch frequency.
1124
+
1125
+ .. devices:: CPU CUDA
1126
+
1127
+ .. properties:: TorchScript
1128
+
1129
+ It is implemented using normalized cross-correlation function and median smoothing.
1130
+
1131
+ Args:
1132
+ waveform (Tensor): Tensor of audio of dimension `(..., freq, time)`
1133
+ sample_rate (int): The sample rate of the waveform (Hz)
1134
+ frame_time (float, optional): Duration of a frame (Default: ``10 ** (-2)``).
1135
+ win_length (int, optional): The window length for median smoothing (in number of frames) (Default: ``30``).
1136
+ freq_low (int, optional): Lowest frequency that can be detected (Hz) (Default: ``85``).
1137
+ freq_high (int, optional): Highest frequency that can be detected (Hz) (Default: ``3400``).
1138
+
1139
+ Returns:
1140
+ Tensor: Tensor of freq of dimension `(..., frame)`
1141
+ """
1142
+ # pack batch
1143
+ shape = list(waveform.size())
1144
+ waveform = waveform.reshape([-1] + shape[-1:])
1145
+
1146
+ nccf = _compute_nccf(waveform, sample_rate, frame_time, freq_low)
1147
+ indices = _find_max_per_frame(nccf, sample_rate, freq_high)
1148
+ indices = _median_smoothing(indices, win_length)
1149
+
1150
+ # Convert indices to frequency
1151
+ EPSILON = 10 ** (-9)
1152
+ freq = sample_rate / (EPSILON + indices.to(torch.float))
1153
+
1154
+ # unpack batch
1155
+ freq = freq.reshape(shape[:-1] + list(freq.shape[-1:]))
1156
+
1157
+ return freq
1158
+
1159
+
1160
+ def sliding_window_cmn(
1161
+ specgram: Tensor,
1162
+ cmn_window: int = 600,
1163
+ min_cmn_window: int = 100,
1164
+ center: bool = False,
1165
+ norm_vars: bool = False,
1166
+ ) -> Tensor:
1167
+ r"""
1168
+ Apply sliding-window cepstral mean (and optionally variance) normalization per utterance.
1169
+
1170
+ .. devices:: CPU CUDA
1171
+
1172
+ .. properties:: TorchScript
1173
+
1174
+ Args:
1175
+ specgram (Tensor): Tensor of spectrogram of dimension `(..., time, freq)`
1176
+ cmn_window (int, optional): Window in frames for running average CMN computation (int, default = 600)
1177
+ min_cmn_window (int, optional): Minimum CMN window used at start of decoding (adds latency only at start).
1178
+ Only applicable if center == false, ignored if center==true (int, default = 100)
1179
+ center (bool, optional): If true, use a window centered on the current frame
1180
+ (to the extent possible, modulo end effects). If false, window is to the left. (bool, default = false)
1181
+ norm_vars (bool, optional): If true, normalize variance to one. (bool, default = false)
1182
+
1183
+ Returns:
1184
+ Tensor: Tensor matching input shape `(..., freq, time)`
1185
+ """
1186
+ input_shape = specgram.shape
1187
+ num_frames, num_feats = input_shape[-2:]
1188
+ specgram = specgram.view(-1, num_frames, num_feats)
1189
+ num_channels = specgram.shape[0]
1190
+
1191
+ dtype = specgram.dtype
1192
+ device = specgram.device
1193
+ last_window_start = last_window_end = -1
1194
+ cur_sum = torch.zeros(num_channels, num_feats, dtype=dtype, device=device)
1195
+ cur_sumsq = torch.zeros(num_channels, num_feats, dtype=dtype, device=device)
1196
+ cmn_specgram = torch.zeros(num_channels, num_frames, num_feats, dtype=dtype, device=device)
1197
+ for t in range(num_frames):
1198
+ window_start = 0
1199
+ window_end = 0
1200
+ if center:
1201
+ window_start = t - cmn_window // 2
1202
+ window_end = window_start + cmn_window
1203
+ else:
1204
+ window_start = t - cmn_window
1205
+ window_end = t + 1
1206
+ if window_start < 0:
1207
+ window_end -= window_start
1208
+ window_start = 0
1209
+ if not center:
1210
+ if window_end > t:
1211
+ window_end = max(t + 1, min_cmn_window)
1212
+ if window_end > num_frames:
1213
+ window_start -= window_end - num_frames
1214
+ window_end = num_frames
1215
+ if window_start < 0:
1216
+ window_start = 0
1217
+ if last_window_start == -1:
1218
+ input_part = specgram[:, window_start : window_end - window_start, :]
1219
+ cur_sum += torch.sum(input_part, 1)
1220
+ if norm_vars:
1221
+ cur_sumsq += torch.cumsum(input_part**2, 1)[:, -1, :]
1222
+ else:
1223
+ if window_start > last_window_start:
1224
+ frame_to_remove = specgram[:, last_window_start, :]
1225
+ cur_sum -= frame_to_remove
1226
+ if norm_vars:
1227
+ cur_sumsq -= frame_to_remove**2
1228
+ if window_end > last_window_end:
1229
+ frame_to_add = specgram[:, last_window_end, :]
1230
+ cur_sum += frame_to_add
1231
+ if norm_vars:
1232
+ cur_sumsq += frame_to_add**2
1233
+ window_frames = window_end - window_start
1234
+ last_window_start = window_start
1235
+ last_window_end = window_end
1236
+ cmn_specgram[:, t, :] = specgram[:, t, :] - cur_sum / window_frames
1237
+ if norm_vars:
1238
+ if window_frames == 1:
1239
+ cmn_specgram[:, t, :] = torch.zeros(num_channels, num_feats, dtype=dtype, device=device)
1240
+ else:
1241
+ variance = cur_sumsq
1242
+ variance = variance / window_frames
1243
+ variance -= (cur_sum**2) / (window_frames**2)
1244
+ variance = torch.pow(variance, -0.5)
1245
+ cmn_specgram[:, t, :] *= variance
1246
+
1247
+ cmn_specgram = cmn_specgram.view(input_shape[:-2] + (num_frames, num_feats))
1248
+ if len(input_shape) == 2:
1249
+ cmn_specgram = cmn_specgram.squeeze(0)
1250
+ return cmn_specgram
1251
+
1252
+
1253
+ def spectral_centroid(
1254
+ waveform: Tensor,
1255
+ sample_rate: int,
1256
+ pad: int,
1257
+ window: Tensor,
1258
+ n_fft: int,
1259
+ hop_length: int,
1260
+ win_length: int,
1261
+ ) -> Tensor:
1262
+ r"""Compute the spectral centroid for each channel along the time axis.
1263
+
1264
+ .. devices:: CPU CUDA
1265
+
1266
+ .. properties:: Autograd TorchScript
1267
+
1268
+ The spectral centroid is defined as the weighted average of the
1269
+ frequency values, weighted by their magnitude.
1270
+
1271
+ Args:
1272
+ waveform (Tensor): Tensor of audio of dimension `(..., time)`
1273
+ sample_rate (int): Sample rate of the audio waveform
1274
+ pad (int): Two sided padding of signal
1275
+ window (Tensor): Window tensor that is applied/multiplied to each frame/window
1276
+ n_fft (int): Size of FFT
1277
+ hop_length (int): Length of hop between STFT windows
1278
+ win_length (int): Window size
1279
+
1280
+ Returns:
1281
+ Tensor: Dimension `(..., time)`
1282
+ """
1283
+ specgram = spectrogram(
1284
+ waveform,
1285
+ pad=pad,
1286
+ window=window,
1287
+ n_fft=n_fft,
1288
+ hop_length=hop_length,
1289
+ win_length=win_length,
1290
+ power=1.0,
1291
+ normalized=False,
1292
+ )
1293
+ freqs = torch.linspace(0, sample_rate // 2, steps=1 + n_fft // 2, device=specgram.device).reshape((-1, 1))
1294
+ freq_dim = -2
1295
+ return (freqs * specgram).sum(dim=freq_dim) / specgram.sum(dim=freq_dim)
1296
+
1297
+
1298
+ @deprecated("Please migrate to :py:class:`torchaudio.io.AudioEffector`.", remove=False)
1299
+ def apply_codec(
1300
+ waveform: Tensor,
1301
+ sample_rate: int,
1302
+ format: str,
1303
+ channels_first: bool = True,
1304
+ compression: Optional[float] = None,
1305
+ encoding: Optional[str] = None,
1306
+ bits_per_sample: Optional[int] = None,
1307
+ ) -> Tensor:
1308
+ r"""
1309
+ Apply codecs as a form of augmentation.
1310
+
1311
+ .. devices:: CPU
1312
+
1313
+ Args:
1314
+ waveform (Tensor): Audio data. Must be 2 dimensional. See also ```channels_first```.
1315
+ sample_rate (int): Sample rate of the audio waveform.
1316
+ format (str): File format.
1317
+ channels_first (bool, optional):
1318
+ When True, both the input and output Tensor have dimension `(channel, time)`.
1319
+ Otherwise, they have dimension `(time, channel)`.
1320
+ compression (float or None, optional): Used for formats other than WAV.
1321
+ For more details see :py:func:`torchaudio.backend.sox_io_backend.save`.
1322
+ encoding (str or None, optional): Changes the encoding for the supported formats.
1323
+ For more details see :py:func:`torchaudio.backend.sox_io_backend.save`.
1324
+ bits_per_sample (int or None, optional): Changes the bit depth for the supported formats.
1325
+ For more details see :py:func:`torchaudio.backend.sox_io_backend.save`.
1326
+
1327
+ Returns:
1328
+ Tensor: Resulting Tensor.
1329
+ If ``channels_first=True``, it has `(channel, time)` else `(time, channel)`.
1330
+ """
1331
+ from torchaudio.backend import _sox_io_backend
1332
+
1333
+ with tempfile.NamedTemporaryFile() as f:
1334
+ torchaudio.backend._sox_io_backend.save(
1335
+ f.name, waveform, sample_rate, channels_first, compression, format, encoding, bits_per_sample
1336
+ )
1337
+ augmented, sr = _sox_io_backend.load(f.name, channels_first=channels_first, format=format)
1338
+ if sr != sample_rate:
1339
+ augmented = resample(augmented, sr, sample_rate)
1340
+ return augmented
1341
+
1342
+
1343
+ _CPU = torch.device("cpu")
1344
+
1345
+
1346
+ def _get_sinc_resample_kernel(
1347
+ orig_freq: int,
1348
+ new_freq: int,
1349
+ gcd: int,
1350
+ lowpass_filter_width: int = 6,
1351
+ rolloff: float = 0.99,
1352
+ resampling_method: str = "sinc_interp_hann",
1353
+ beta: Optional[float] = None,
1354
+ device: torch.device = _CPU,
1355
+ dtype: Optional[torch.dtype] = None,
1356
+ ):
1357
+ if not (int(orig_freq) == orig_freq and int(new_freq) == new_freq):
1358
+ raise Exception(
1359
+ "Frequencies must be of integer type to ensure quality resampling computation. "
1360
+ "To work around this, manually convert both frequencies to integer values "
1361
+ "that maintain their resampling rate ratio before passing them into the function. "
1362
+ "Example: To downsample a 44100 hz waveform by a factor of 8, use "
1363
+ "`orig_freq=8` and `new_freq=1` instead of `orig_freq=44100` and `new_freq=5512.5`. "
1364
+ "For more information, please refer to https://github.com/pytorch/audio/issues/1487."
1365
+ )
1366
+
1367
+ if resampling_method in ["sinc_interpolation", "kaiser_window"]:
1368
+ method_map = {
1369
+ "sinc_interpolation": "sinc_interp_hann",
1370
+ "kaiser_window": "sinc_interp_kaiser",
1371
+ }
1372
+ warnings.warn(
1373
+ f'"{resampling_method}" resampling method name is being deprecated and replaced by '
1374
+ f'"{method_map[resampling_method]}" in the next release. '
1375
+ "The default behavior remains unchanged.",
1376
+ stacklevel=3,
1377
+ )
1378
+ elif resampling_method not in ["sinc_interp_hann", "sinc_interp_kaiser"]:
1379
+ raise ValueError("Invalid resampling method: {}".format(resampling_method))
1380
+
1381
+ orig_freq = int(orig_freq) // gcd
1382
+ new_freq = int(new_freq) // gcd
1383
+
1384
+ if lowpass_filter_width <= 0:
1385
+ raise ValueError("Low pass filter width should be positive.")
1386
+ base_freq = min(orig_freq, new_freq)
1387
+ # This will perform antialiasing filtering by removing the highest frequencies.
1388
+ # At first I thought I only needed this when downsampling, but when upsampling
1389
+ # you will get edge artifacts without this, as the edge is equivalent to zero padding,
1390
+ # which will add high freq artifacts.
1391
+ base_freq *= rolloff
1392
+
1393
+ # The key idea of the algorithm is that x(t) can be exactly reconstructed from x[i] (tensor)
1394
+ # using the sinc interpolation formula:
1395
+ # x(t) = sum_i x[i] sinc(pi * orig_freq * (i / orig_freq - t))
1396
+ # We can then sample the function x(t) with a different sample rate:
1397
+ # y[j] = x(j / new_freq)
1398
+ # or,
1399
+ # y[j] = sum_i x[i] sinc(pi * orig_freq * (i / orig_freq - j / new_freq))
1400
+
1401
+ # We see here that y[j] is the convolution of x[i] with a specific filter, for which
1402
+ # we take an FIR approximation, stopping when we see at least `lowpass_filter_width` zeros crossing.
1403
+ # But y[j+1] is going to have a different set of weights and so on, until y[j + new_freq].
1404
+ # Indeed:
1405
+ # y[j + new_freq] = sum_i x[i] sinc(pi * orig_freq * ((i / orig_freq - (j + new_freq) / new_freq))
1406
+ # = sum_i x[i] sinc(pi * orig_freq * ((i - orig_freq) / orig_freq - j / new_freq))
1407
+ # = sum_i x[i + orig_freq] sinc(pi * orig_freq * (i / orig_freq - j / new_freq))
1408
+ # so y[j+new_freq] uses the same filter as y[j], but on a shifted version of x by `orig_freq`.
1409
+ # This will explain the F.conv1d after, with a stride of orig_freq.
1410
+ width = math.ceil(lowpass_filter_width * orig_freq / base_freq)
1411
+ # If orig_freq is still big after GCD reduction, most filters will be very unbalanced, i.e.,
1412
+ # they will have a lot of almost zero values to the left or to the right...
1413
+ # There is probably a way to evaluate those filters more efficiently, but this is kept for
1414
+ # future work.
1415
+ idx_dtype = dtype if dtype is not None else torch.float64
1416
+
1417
+ idx = torch.arange(-width, width + orig_freq, dtype=idx_dtype, device=device)[None, None] / orig_freq
1418
+
1419
+ t = torch.arange(0, -new_freq, -1, dtype=dtype, device=device)[:, None, None] / new_freq + idx
1420
+ t *= base_freq
1421
+ t = t.clamp_(-lowpass_filter_width, lowpass_filter_width)
1422
+
1423
+ # we do not use built in torch windows here as we need to evaluate the window
1424
+ # at specific positions, not over a regular grid.
1425
+ if resampling_method == "sinc_interp_hann":
1426
+ window = torch.cos(t * math.pi / lowpass_filter_width / 2) ** 2
1427
+ else:
1428
+ # sinc_interp_kaiser
1429
+ if beta is None:
1430
+ beta = 14.769656459379492
1431
+ beta_tensor = torch.tensor(float(beta))
1432
+ window = torch.i0(beta_tensor * torch.sqrt(1 - (t / lowpass_filter_width) ** 2)) / torch.i0(beta_tensor)
1433
+
1434
+ t *= math.pi
1435
+
1436
+ scale = base_freq / orig_freq
1437
+ kernels = torch.where(t == 0, torch.tensor(1.0).to(t), t.sin() / t)
1438
+ kernels *= window * scale
1439
+
1440
+ if dtype is None:
1441
+ kernels = kernels.to(dtype=torch.float32)
1442
+
1443
+ return kernels, width
1444
+
1445
+
1446
+ def _apply_sinc_resample_kernel(
1447
+ waveform: Tensor,
1448
+ orig_freq: int,
1449
+ new_freq: int,
1450
+ gcd: int,
1451
+ kernel: Tensor,
1452
+ width: int,
1453
+ ):
1454
+ if not waveform.is_floating_point():
1455
+ raise TypeError(f"Expected floating point type for waveform tensor, but received {waveform.dtype}.")
1456
+
1457
+ orig_freq = int(orig_freq) // gcd
1458
+ new_freq = int(new_freq) // gcd
1459
+
1460
+ # pack batch
1461
+ shape = waveform.size()
1462
+ waveform = waveform.view(-1, shape[-1])
1463
+
1464
+ num_wavs, length = waveform.shape
1465
+ waveform = torch.nn.functional.pad(waveform, (width, width + orig_freq))
1466
+ resampled = torch.nn.functional.conv1d(waveform[:, None], kernel, stride=orig_freq)
1467
+ resampled = resampled.transpose(1, 2).reshape(num_wavs, -1)
1468
+ target_length = torch.ceil(torch.as_tensor(new_freq * length / orig_freq)).long()
1469
+ resampled = resampled[..., :target_length]
1470
+
1471
+ # unpack batch
1472
+ resampled = resampled.view(shape[:-1] + resampled.shape[-1:])
1473
+ return resampled
1474
+
1475
+
1476
+ def resample(
1477
+ waveform: Tensor,
1478
+ orig_freq: int,
1479
+ new_freq: int,
1480
+ lowpass_filter_width: int = 6,
1481
+ rolloff: float = 0.99,
1482
+ resampling_method: str = "sinc_interp_hann",
1483
+ beta: Optional[float] = None,
1484
+ ) -> Tensor:
1485
+ r"""Resamples the waveform at the new frequency using bandlimited interpolation. :cite:`RESAMPLE`.
1486
+
1487
+ .. devices:: CPU CUDA
1488
+
1489
+ .. properties:: Autograd TorchScript
1490
+
1491
+ Note:
1492
+ ``transforms.Resample`` precomputes and reuses the resampling kernel, so using it will result in
1493
+ more efficient computation if resampling multiple waveforms with the same resampling parameters.
1494
+
1495
+ Args:
1496
+ waveform (Tensor): The input signal of dimension `(..., time)`
1497
+ orig_freq (int): The original frequency of the signal
1498
+ new_freq (int): The desired frequency
1499
+ lowpass_filter_width (int, optional): Controls the sharpness of the filter, more == sharper
1500
+ but less efficient. (Default: ``6``)
1501
+ rolloff (float, optional): The roll-off frequency of the filter, as a fraction of the Nyquist.
1502
+ Lower values reduce anti-aliasing, but also reduce some of the highest frequencies. (Default: ``0.99``)
1503
+ resampling_method (str, optional): The resampling method to use.
1504
+ Options: [``"sinc_interp_hann"``, ``"sinc_interp_kaiser"``] (Default: ``"sinc_interp_hann"``)
1505
+ beta (float or None, optional): The shape parameter used for kaiser window.
1506
+
1507
+ Returns:
1508
+ Tensor: The waveform at the new frequency of dimension `(..., time).`
1509
+ """
1510
+
1511
+ if orig_freq <= 0.0 or new_freq <= 0.0:
1512
+ raise ValueError("Original frequency and desired frequecy should be positive")
1513
+
1514
+ if orig_freq == new_freq:
1515
+ return waveform
1516
+
1517
+ gcd = math.gcd(int(orig_freq), int(new_freq))
1518
+
1519
+ kernel, width = _get_sinc_resample_kernel(
1520
+ orig_freq,
1521
+ new_freq,
1522
+ gcd,
1523
+ lowpass_filter_width,
1524
+ rolloff,
1525
+ resampling_method,
1526
+ beta,
1527
+ waveform.device,
1528
+ waveform.dtype,
1529
+ )
1530
+ resampled = _apply_sinc_resample_kernel(waveform, orig_freq, new_freq, gcd, kernel, width)
1531
+ return resampled
1532
+
1533
+
1534
+ @torch.jit.unused
1535
+ def edit_distance(seq1: Sequence, seq2: Sequence) -> int:
1536
+ """
1537
+ Calculate the word level edit (Levenshtein) distance between two sequences.
1538
+
1539
+ .. devices:: CPU
1540
+
1541
+ The function computes an edit distance allowing deletion, insertion and
1542
+ substitution. The result is an integer.
1543
+
1544
+ For most applications, the two input sequences should be the same type. If
1545
+ two strings are given, the output is the edit distance between the two
1546
+ strings (character edit distance). If two lists of strings are given, the
1547
+ output is the edit distance between sentences (word edit distance). Users
1548
+ may want to normalize the output by the length of the reference sequence.
1549
+
1550
+ Args:
1551
+ seq1 (Sequence): the first sequence to compare.
1552
+ seq2 (Sequence): the second sequence to compare.
1553
+ Returns:
1554
+ int: The distance between the first and second sequences.
1555
+ """
1556
+ len_sent2 = len(seq2)
1557
+ dold = list(range(len_sent2 + 1))
1558
+ dnew = [0 for _ in range(len_sent2 + 1)]
1559
+
1560
+ for i in range(1, len(seq1) + 1):
1561
+ dnew[0] = i
1562
+ for j in range(1, len_sent2 + 1):
1563
+ if seq1[i - 1] == seq2[j - 1]:
1564
+ dnew[j] = dold[j - 1]
1565
+ else:
1566
+ substitution = dold[j - 1] + 1
1567
+ insertion = dnew[j - 1] + 1
1568
+ deletion = dold[j] + 1
1569
+ dnew[j] = min(substitution, insertion, deletion)
1570
+
1571
+ dnew, dold = dold, dnew
1572
+
1573
+ return int(dold[-1])
1574
+
1575
+
1576
+ def loudness(waveform: Tensor, sample_rate: int):
1577
+ r"""Measure audio loudness according to the ITU-R BS.1770-4 recommendation.
1578
+
1579
+ .. devices:: CPU CUDA
1580
+
1581
+ .. properties:: TorchScript
1582
+
1583
+ Args:
1584
+ waveform(torch.Tensor): audio waveform of dimension `(..., channels, time)`
1585
+ sample_rate (int): sampling rate of the waveform
1586
+
1587
+ Returns:
1588
+ Tensor: loudness estimates (LKFS)
1589
+
1590
+ Reference:
1591
+ - https://www.itu.int/rec/R-REC-BS.1770-4-201510-I/en
1592
+ """
1593
+
1594
+ if waveform.size(-2) > 5:
1595
+ raise ValueError("Only up to 5 channels are supported.")
1596
+
1597
+ gate_duration = 0.4
1598
+ overlap = 0.75
1599
+ gamma_abs = -70.0
1600
+ kweight_bias = -0.691
1601
+ gate_samples = int(round(gate_duration * sample_rate))
1602
+ step = int(round(gate_samples * (1 - overlap)))
1603
+
1604
+ # Apply K-weighting
1605
+ waveform = treble_biquad(waveform, sample_rate, 4.0, 1500.0, 1 / math.sqrt(2))
1606
+ waveform = highpass_biquad(waveform, sample_rate, 38.0, 0.5)
1607
+
1608
+ # Compute the energy for each block
1609
+ energy = torch.square(waveform).unfold(-1, gate_samples, step)
1610
+ energy = torch.mean(energy, dim=-1)
1611
+
1612
+ # Compute channel-weighted summation
1613
+ g = torch.tensor([1.0, 1.0, 1.0, 1.41, 1.41], dtype=waveform.dtype, device=waveform.device)
1614
+ g = g[: energy.size(-2)]
1615
+
1616
+ energy_weighted = torch.sum(g.unsqueeze(-1) * energy, dim=-2)
1617
+ loudness = -0.691 + 10 * torch.log10(energy_weighted)
1618
+
1619
+ # Apply absolute gating of the blocks
1620
+ gated_blocks = loudness > gamma_abs
1621
+ gated_blocks = gated_blocks.unsqueeze(-2)
1622
+
1623
+ energy_filtered = torch.sum(gated_blocks * energy, dim=-1) / torch.count_nonzero(gated_blocks, dim=-1)
1624
+ energy_weighted = torch.sum(g * energy_filtered, dim=-1)
1625
+ gamma_rel = kweight_bias + 10 * torch.log10(energy_weighted) - 10
1626
+
1627
+ # Apply relative gating of the blocks
1628
+ gated_blocks = torch.logical_and(gated_blocks.squeeze(-2), loudness > gamma_rel.unsqueeze(-1))
1629
+ gated_blocks = gated_blocks.unsqueeze(-2)
1630
+
1631
+ energy_filtered = torch.sum(gated_blocks * energy, dim=-1) / torch.count_nonzero(gated_blocks, dim=-1)
1632
+ energy_weighted = torch.sum(g * energy_filtered, dim=-1)
1633
+ LKFS = kweight_bias + 10 * torch.log10(energy_weighted)
1634
+ return LKFS
1635
+
1636
+
1637
+ def pitch_shift(
1638
+ waveform: Tensor,
1639
+ sample_rate: int,
1640
+ n_steps: int,
1641
+ bins_per_octave: int = 12,
1642
+ n_fft: int = 512,
1643
+ win_length: Optional[int] = None,
1644
+ hop_length: Optional[int] = None,
1645
+ window: Optional[Tensor] = None,
1646
+ ) -> Tensor:
1647
+ """
1648
+ Shift the pitch of a waveform by ``n_steps`` steps.
1649
+
1650
+ .. devices:: CPU CUDA
1651
+
1652
+ .. properties:: TorchScript
1653
+
1654
+ Args:
1655
+ waveform (Tensor): The input waveform of shape `(..., time)`.
1656
+ sample_rate (int): Sample rate of `waveform`.
1657
+ n_steps (int): The (fractional) steps to shift `waveform`.
1658
+ bins_per_octave (int, optional): The number of steps per octave (Default: ``12``).
1659
+ n_fft (int, optional): Size of FFT, creates ``n_fft // 2 + 1`` bins (Default: ``512``).
1660
+ win_length (int or None, optional): Window size. If None, then ``n_fft`` is used. (Default: ``None``).
1661
+ hop_length (int or None, optional): Length of hop between STFT windows. If None, then
1662
+ ``win_length // 4`` is used (Default: ``None``).
1663
+ window (Tensor or None, optional): Window tensor that is applied/multiplied to each frame/window.
1664
+ If None, then ``torch.hann_window(win_length)`` is used (Default: ``None``).
1665
+
1666
+
1667
+ Returns:
1668
+ Tensor: The pitch-shifted audio waveform of shape `(..., time)`.
1669
+ """
1670
+ waveform_stretch = _stretch_waveform(
1671
+ waveform,
1672
+ n_steps,
1673
+ bins_per_octave,
1674
+ n_fft,
1675
+ win_length,
1676
+ hop_length,
1677
+ window,
1678
+ )
1679
+ rate = 2.0 ** (-float(n_steps) / bins_per_octave)
1680
+ waveform_shift = resample(waveform_stretch, int(sample_rate / rate), sample_rate)
1681
+
1682
+ return _fix_waveform_shape(waveform_shift, waveform.size())
1683
+
1684
+
1685
+ def _stretch_waveform(
1686
+ waveform: Tensor,
1687
+ n_steps: int,
1688
+ bins_per_octave: int = 12,
1689
+ n_fft: int = 512,
1690
+ win_length: Optional[int] = None,
1691
+ hop_length: Optional[int] = None,
1692
+ window: Optional[Tensor] = None,
1693
+ ) -> Tensor:
1694
+ """
1695
+ Pitch shift helper function to preprocess and stretch waveform before resampling step.
1696
+
1697
+ Args:
1698
+ See pitch_shift arg descriptions.
1699
+
1700
+ Returns:
1701
+ Tensor: The preprocessed waveform stretched prior to resampling.
1702
+ """
1703
+ if hop_length is None:
1704
+ hop_length = n_fft // 4
1705
+ if win_length is None:
1706
+ win_length = n_fft
1707
+ if window is None:
1708
+ window = torch.hann_window(window_length=win_length, device=waveform.device)
1709
+
1710
+ # pack batch
1711
+ shape = waveform.size()
1712
+ waveform = waveform.reshape(-1, shape[-1])
1713
+
1714
+ ori_len = shape[-1]
1715
+ rate = 2.0 ** (-float(n_steps) / bins_per_octave)
1716
+ spec_f = torch.stft(
1717
+ input=waveform,
1718
+ n_fft=n_fft,
1719
+ hop_length=hop_length,
1720
+ win_length=win_length,
1721
+ window=window,
1722
+ center=True,
1723
+ pad_mode="reflect",
1724
+ normalized=False,
1725
+ onesided=True,
1726
+ return_complex=True,
1727
+ )
1728
+ phase_advance = torch.linspace(0, math.pi * hop_length, spec_f.shape[-2], device=spec_f.device)[..., None]
1729
+ spec_stretch = phase_vocoder(spec_f, rate, phase_advance)
1730
+ len_stretch = int(round(ori_len / rate))
1731
+ waveform_stretch = torch.istft(
1732
+ spec_stretch, n_fft=n_fft, hop_length=hop_length, win_length=win_length, window=window, length=len_stretch
1733
+ )
1734
+ return waveform_stretch
1735
+
1736
+
1737
+ def _fix_waveform_shape(
1738
+ waveform_shift: Tensor,
1739
+ shape: List[int],
1740
+ ) -> Tensor:
1741
+ """
1742
+ PitchShift helper function to process after resampling step to fix the shape back.
1743
+
1744
+ Args:
1745
+ waveform_shift(Tensor): The waveform after stretch and resample
1746
+ shape (List[int]): The shape of initial waveform
1747
+
1748
+ Returns:
1749
+ Tensor: The pitch-shifted audio waveform of shape `(..., time)`.
1750
+ """
1751
+ ori_len = shape[-1]
1752
+ shift_len = waveform_shift.size()[-1]
1753
+ if shift_len > ori_len:
1754
+ waveform_shift = waveform_shift[..., :ori_len]
1755
+ else:
1756
+ waveform_shift = torch.nn.functional.pad(waveform_shift, [0, ori_len - shift_len])
1757
+
1758
+ # unpack batch
1759
+ waveform_shift = waveform_shift.view(shape[:-1] + waveform_shift.shape[-1:])
1760
+ return waveform_shift
1761
+
1762
+
1763
+ def rnnt_loss(
1764
+ logits: Tensor,
1765
+ targets: Tensor,
1766
+ logit_lengths: Tensor,
1767
+ target_lengths: Tensor,
1768
+ blank: int = -1,
1769
+ clamp: float = -1,
1770
+ reduction: str = "mean",
1771
+ fused_log_softmax: bool = True,
1772
+ ):
1773
+ """Compute the RNN Transducer loss from *Sequence Transduction with Recurrent Neural Networks*
1774
+ :cite:`graves2012sequence`.
1775
+
1776
+ .. devices:: CPU CUDA
1777
+
1778
+ .. properties:: Autograd TorchScript
1779
+
1780
+ The RNN Transducer loss extends the CTC loss by defining a distribution over output
1781
+ sequences of all lengths, and by jointly modelling both input-output and output-output
1782
+ dependencies.
1783
+
1784
+ Args:
1785
+ logits (Tensor): Tensor of dimension `(batch, max seq length, max target length + 1, class)`
1786
+ containing output from joiner
1787
+ targets (Tensor): Tensor of dimension `(batch, max target length)` containing targets with zero padded
1788
+ logit_lengths (Tensor): Tensor of dimension `(batch)` containing lengths of each sequence from encoder
1789
+ target_lengths (Tensor): Tensor of dimension `(batch)` containing lengths of targets for each sequence
1790
+ blank (int, optional): blank label (Default: ``-1``)
1791
+ clamp (float, optional): clamp for gradients (Default: ``-1``)
1792
+ reduction (string, optional): Specifies the reduction to apply to the output:
1793
+ ``"none"`` | ``"mean"`` | ``"sum"``. (Default: ``"mean"``)
1794
+ fused_log_softmax (bool): set to False if calling log_softmax outside of loss (Default: ``True``)
1795
+ Returns:
1796
+ Tensor: Loss with the reduction option applied. If ``reduction`` is ``"none"``, then size `(batch)`,
1797
+ otherwise scalar.
1798
+ """
1799
+ if reduction not in ["none", "mean", "sum"]:
1800
+ raise ValueError('reduction should be one of "none", "mean", or "sum"')
1801
+
1802
+ if blank < 0: # reinterpret blank index if blank < 0.
1803
+ blank = logits.shape[-1] + blank
1804
+
1805
+ costs, _ = torch.ops.torchaudio.rnnt_loss(
1806
+ logits=logits,
1807
+ targets=targets,
1808
+ logit_lengths=logit_lengths,
1809
+ target_lengths=target_lengths,
1810
+ blank=blank,
1811
+ clamp=clamp,
1812
+ fused_log_softmax=fused_log_softmax,
1813
+ )
1814
+
1815
+ if reduction == "mean":
1816
+ return costs.mean()
1817
+ elif reduction == "sum":
1818
+ return costs.sum()
1819
+
1820
+ return costs
1821
+
1822
+
1823
+ def psd(
1824
+ specgram: Tensor,
1825
+ mask: Optional[Tensor] = None,
1826
+ normalize: bool = True,
1827
+ eps: float = 1e-10,
1828
+ ) -> Tensor:
1829
+ """Compute cross-channel power spectral density (PSD) matrix.
1830
+
1831
+ .. devices:: CPU CUDA
1832
+
1833
+ .. properties:: Autograd TorchScript
1834
+
1835
+ Args:
1836
+ specgram (torch.Tensor): Multi-channel complex-valued spectrum.
1837
+ Tensor with dimensions `(..., channel, freq, time)`.
1838
+ mask (torch.Tensor or None, optional): Time-Frequency mask for normalization.
1839
+ Tensor with dimensions `(..., freq, time)`. (Default: ``None``)
1840
+ normalize (bool, optional): If ``True``, normalize the mask along the time dimension. (Default: ``True``)
1841
+ eps (float, optional): Value to add to the denominator in mask normalization. (Default: ``1e-15``)
1842
+
1843
+ Returns:
1844
+ torch.Tensor: The complex-valued PSD matrix of the input spectrum.
1845
+ Tensor with dimensions `(..., freq, channel, channel)`
1846
+ """
1847
+ specgram = specgram.transpose(-3, -2) # shape (freq, channel, time)
1848
+ # outer product:
1849
+ # (..., ch_1, time) x (..., ch_2, time) -> (..., time, ch_1, ch_2)
1850
+ psd = torch.einsum("...ct,...et->...tce", [specgram, specgram.conj()])
1851
+
1852
+ if mask is not None:
1853
+ if mask.shape[:-1] != specgram.shape[:-2] or mask.shape[-1] != specgram.shape[-1]:
1854
+ raise ValueError(
1855
+ "The dimensions of mask except the channel dimension should be the same as specgram."
1856
+ f"Found {mask.shape} for mask and {specgram.shape} for specgram."
1857
+ )
1858
+ # Normalized mask along time dimension:
1859
+ if normalize:
1860
+ mask = mask / (mask.sum(dim=-1, keepdim=True) + eps)
1861
+
1862
+ psd = psd * mask[..., None, None]
1863
+
1864
+ psd = psd.sum(dim=-3)
1865
+ return psd
1866
+
1867
+
1868
+ def _compute_mat_trace(input: torch.Tensor, dim1: int = -1, dim2: int = -2) -> torch.Tensor:
1869
+ r"""Compute the trace of a Tensor along ``dim1`` and ``dim2`` dimensions.
1870
+
1871
+ Args:
1872
+ input (torch.Tensor): Tensor with dimensions `(..., channel, channel)`.
1873
+ dim1 (int, optional): The first dimension of the diagonal matrix.
1874
+ (Default: ``-1``)
1875
+ dim2 (int, optional): The second dimension of the diagonal matrix.
1876
+ (Default: ``-2``)
1877
+
1878
+ Returns:
1879
+ Tensor: The trace of the input Tensor.
1880
+ """
1881
+ if input.ndim < 2:
1882
+ raise ValueError("The dimension of the tensor must be at least 2.")
1883
+ if input.shape[dim1] != input.shape[dim2]:
1884
+ raise ValueError("The size of ``dim1`` and ``dim2`` must be the same.")
1885
+ input = torch.diagonal(input, 0, dim1=dim1, dim2=dim2)
1886
+ return input.sum(dim=-1)
1887
+
1888
+
1889
+ def _tik_reg(mat: torch.Tensor, reg: float = 1e-7, eps: float = 1e-8) -> torch.Tensor:
1890
+ """Perform Tikhonov regularization (only modifying real part).
1891
+
1892
+ Args:
1893
+ mat (torch.Tensor): Input matrix with dimensions `(..., channel, channel)`.
1894
+ reg (float, optional): Regularization factor. (Default: 1e-8)
1895
+ eps (float, optional): Value to avoid the correlation matrix is all-zero. (Default: ``1e-8``)
1896
+
1897
+ Returns:
1898
+ Tensor: Regularized matrix with dimensions `(..., channel, channel)`.
1899
+ """
1900
+ # Add eps
1901
+ C = mat.size(-1)
1902
+ eye = torch.eye(C, dtype=mat.dtype, device=mat.device)
1903
+ epsilon = _compute_mat_trace(mat).real[..., None, None] * reg
1904
+ # in case that correlation_matrix is all-zero
1905
+ epsilon = epsilon + eps
1906
+ mat = mat + epsilon * eye[..., :, :]
1907
+ return mat
1908
+
1909
+
1910
+ def _assert_psd_matrices(psd_s: torch.Tensor, psd_n: torch.Tensor) -> None:
1911
+ """Assertion checks of the PSD matrices of target speech and noise.
1912
+
1913
+ Args:
1914
+ psd_s (torch.Tensor): The complex-valued power spectral density (PSD) matrix of target speech.
1915
+ Tensor with dimensions `(..., freq, channel, channel)`.
1916
+ psd_n (torch.Tensor): The complex-valued power spectral density (PSD) matrix of noise.
1917
+ Tensor with dimensions `(..., freq, channel, channel)`.
1918
+ """
1919
+ if psd_s.ndim < 3 or psd_n.ndim < 3:
1920
+ raise ValueError(
1921
+ "Expected at least 3D Tensor (..., freq, channel, channel) for psd_s and psd_n. "
1922
+ f"Found {psd_s.shape} for psd_s and {psd_n.shape} for psd_n."
1923
+ )
1924
+ if not (psd_s.is_complex() and psd_n.is_complex()):
1925
+ raise TypeError(
1926
+ "The type of psd_s and psd_n must be ``torch.cfloat`` or ``torch.cdouble``. "
1927
+ f"Found {psd_s.dtype} for psd_s and {psd_n.dtype} for psd_n."
1928
+ )
1929
+ if psd_s.shape != psd_n.shape:
1930
+ raise ValueError(
1931
+ f"The dimensions of psd_s and psd_n should be the same. Found {psd_s.shape} and {psd_n.shape}."
1932
+ )
1933
+ if psd_s.shape[-1] != psd_s.shape[-2]:
1934
+ raise ValueError(f"The last two dimensions of psd_s should be the same. Found {psd_s.shape}.")
1935
+
1936
+
1937
+ def mvdr_weights_souden(
1938
+ psd_s: Tensor,
1939
+ psd_n: Tensor,
1940
+ reference_channel: Union[int, Tensor],
1941
+ diagonal_loading: bool = True,
1942
+ diag_eps: float = 1e-7,
1943
+ eps: float = 1e-8,
1944
+ ) -> Tensor:
1945
+ r"""Compute the Minimum Variance Distortionless Response (*MVDR* :cite:`capon1969high`) beamforming weights
1946
+ by the method proposed by *Souden et, al.* :cite:`souden2009optimal`.
1947
+
1948
+ .. devices:: CPU CUDA
1949
+
1950
+ .. properties:: Autograd TorchScript
1951
+
1952
+ Given the power spectral density (PSD) matrix of target speech :math:`\bf{\Phi}_{\textbf{SS}}`,
1953
+ the PSD matrix of noise :math:`\bf{\Phi}_{\textbf{NN}}`, and a one-hot vector that represents the
1954
+ reference channel :math:`\bf{u}`, the method computes the MVDR beamforming weight martrix
1955
+ :math:`\textbf{w}_{\text{MVDR}}`. The formula is defined as:
1956
+
1957
+ .. math::
1958
+ \textbf{w}_{\text{MVDR}}(f) =
1959
+ \frac{{{\bf{\Phi}_{\textbf{NN}}^{-1}}(f){\bf{\Phi}_{\textbf{SS}}}}(f)}
1960
+ {\text{Trace}({{{\bf{\Phi}_{\textbf{NN}}^{-1}}(f) \bf{\Phi}_{\textbf{SS}}}(f))}}\bm{u}
1961
+
1962
+ Args:
1963
+ psd_s (torch.Tensor): The complex-valued power spectral density (PSD) matrix of target speech.
1964
+ Tensor with dimensions `(..., freq, channel, channel)`.
1965
+ psd_n (torch.Tensor): The complex-valued power spectral density (PSD) matrix of noise.
1966
+ Tensor with dimensions `(..., freq, channel, channel)`.
1967
+ reference_channel (int or torch.Tensor): Specifies the reference channel.
1968
+ If the dtype is ``int``, it represents the reference channel index.
1969
+ If the dtype is ``torch.Tensor``, its shape is `(..., channel)`, where the ``channel`` dimension
1970
+ is one-hot.
1971
+ diagonal_loading (bool, optional): If ``True``, enables applying diagonal loading to ``psd_n``.
1972
+ (Default: ``True``)
1973
+ diag_eps (float, optional): The coefficient multiplied to the identity matrix for diagonal loading.
1974
+ It is only effective when ``diagonal_loading`` is set to ``True``. (Default: ``1e-7``)
1975
+ eps (float, optional): Value to add to the denominator in the beamforming weight formula.
1976
+ (Default: ``1e-8``)
1977
+
1978
+ Returns:
1979
+ torch.Tensor: The complex-valued MVDR beamforming weight matrix with dimensions `(..., freq, channel)`.
1980
+ """
1981
+ _assert_psd_matrices(psd_s, psd_n)
1982
+
1983
+ if diagonal_loading:
1984
+ psd_n = _tik_reg(psd_n, reg=diag_eps)
1985
+ numerator = torch.linalg.solve(psd_n, psd_s) # psd_n.inv() @ psd_s
1986
+ # ws: (..., C, C) / (...,) -> (..., C, C)
1987
+ ws = numerator / (_compute_mat_trace(numerator)[..., None, None] + eps)
1988
+ if torch.jit.isinstance(reference_channel, int):
1989
+ beamform_weights = ws[..., :, reference_channel]
1990
+ elif torch.jit.isinstance(reference_channel, Tensor):
1991
+ reference_channel = reference_channel.to(psd_n.dtype)
1992
+ # h: (..., F, C_1, C_2) x (..., C_2) -> (..., F, C_1)
1993
+ beamform_weights = torch.einsum("...c,...c->...", [ws, reference_channel[..., None, None, :]])
1994
+ else:
1995
+ raise TypeError(f'Expected "int" or "Tensor" for reference_channel. Found: {type(reference_channel)}.')
1996
+
1997
+ return beamform_weights
1998
+
1999
+
2000
+ def mvdr_weights_rtf(
2001
+ rtf: Tensor,
2002
+ psd_n: Tensor,
2003
+ reference_channel: Optional[Union[int, Tensor]] = None,
2004
+ diagonal_loading: bool = True,
2005
+ diag_eps: float = 1e-7,
2006
+ eps: float = 1e-8,
2007
+ ) -> Tensor:
2008
+ r"""Compute the Minimum Variance Distortionless Response (*MVDR* :cite:`capon1969high`) beamforming weights
2009
+ based on the relative transfer function (RTF) and power spectral density (PSD) matrix of noise.
2010
+
2011
+ .. devices:: CPU CUDA
2012
+
2013
+ .. properties:: Autograd TorchScript
2014
+
2015
+ Given the relative transfer function (RTF) matrix or the steering vector of target speech :math:`\bm{v}`,
2016
+ the PSD matrix of noise :math:`\bf{\Phi}_{\textbf{NN}}`, and a one-hot vector that represents the
2017
+ reference channel :math:`\bf{u}`, the method computes the MVDR beamforming weight martrix
2018
+ :math:`\textbf{w}_{\text{MVDR}}`. The formula is defined as:
2019
+
2020
+ .. math::
2021
+ \textbf{w}_{\text{MVDR}}(f) =
2022
+ \frac{{{\bf{\Phi}_{\textbf{NN}}^{-1}}(f){\bm{v}}(f)}}
2023
+ {{\bm{v}^{\mathsf{H}}}(f){\bf{\Phi}_{\textbf{NN}}^{-1}}(f){\bm{v}}(f)}
2024
+
2025
+ where :math:`(.)^{\mathsf{H}}` denotes the Hermitian Conjugate operation.
2026
+
2027
+ Args:
2028
+ rtf (torch.Tensor): The complex-valued RTF vector of target speech.
2029
+ Tensor with dimensions `(..., freq, channel)`.
2030
+ psd_n (torch.Tensor): The complex-valued power spectral density (PSD) matrix of noise.
2031
+ Tensor with dimensions `(..., freq, channel, channel)`.
2032
+ reference_channel (int or torch.Tensor): Specifies the reference channel.
2033
+ If the dtype is ``int``, it represents the reference channel index.
2034
+ If the dtype is ``torch.Tensor``, its shape is `(..., channel)`, where the ``channel`` dimension
2035
+ is one-hot.
2036
+ diagonal_loading (bool, optional): If ``True``, enables applying diagonal loading to ``psd_n``.
2037
+ (Default: ``True``)
2038
+ diag_eps (float, optional): The coefficient multiplied to the identity matrix for diagonal loading.
2039
+ It is only effective when ``diagonal_loading`` is set to ``True``. (Default: ``1e-7``)
2040
+ eps (float, optional): Value to add to the denominator in the beamforming weight formula.
2041
+ (Default: ``1e-8``)
2042
+
2043
+ Returns:
2044
+ torch.Tensor: The complex-valued MVDR beamforming weight matrix with dimensions `(..., freq, channel)`.
2045
+ """
2046
+ if rtf.ndim < 2:
2047
+ raise ValueError(f"Expected at least 2D Tensor (..., freq, channel) for rtf. Found {rtf.shape}.")
2048
+ if psd_n.ndim < 3:
2049
+ raise ValueError(f"Expected at least 3D Tensor (..., freq, channel, channel) for psd_n. Found {psd_n.shape}.")
2050
+ if not (rtf.is_complex() and psd_n.is_complex()):
2051
+ raise TypeError(
2052
+ "The type of rtf and psd_n must be ``torch.cfloat`` or ``torch.cdouble``. "
2053
+ f"Found {rtf.dtype} for rtf and {psd_n.dtype} for psd_n."
2054
+ )
2055
+ if rtf.shape != psd_n.shape[:-1]:
2056
+ raise ValueError(
2057
+ "The dimensions of rtf and the dimensions withou the last dimension of psd_n should be the same. "
2058
+ f"Found {rtf.shape} for rtf and {psd_n.shape} for psd_n."
2059
+ )
2060
+ if psd_n.shape[-1] != psd_n.shape[-2]:
2061
+ raise ValueError(f"The last two dimensions of psd_n should be the same. Found {psd_n.shape}.")
2062
+
2063
+ if diagonal_loading:
2064
+ psd_n = _tik_reg(psd_n, reg=diag_eps)
2065
+ # numerator = psd_n.inv() @ stv
2066
+ numerator = torch.linalg.solve(psd_n, rtf.unsqueeze(-1)).squeeze(-1) # (..., freq, channel)
2067
+ # denominator = stv^H @ psd_n.inv() @ stv
2068
+ denominator = torch.einsum("...d,...d->...", [rtf.conj(), numerator])
2069
+ beamform_weights = numerator / (denominator.real.unsqueeze(-1) + eps)
2070
+ # normalize the numerator
2071
+ if reference_channel is not None:
2072
+ if torch.jit.isinstance(reference_channel, int):
2073
+ scale = rtf[..., reference_channel].conj()
2074
+ elif torch.jit.isinstance(reference_channel, Tensor):
2075
+ reference_channel = reference_channel.to(psd_n.dtype)
2076
+ scale = torch.einsum("...c,...c->...", [rtf.conj(), reference_channel[..., None, :]])
2077
+ else:
2078
+ raise TypeError(f'Expected "int" or "Tensor" for reference_channel. Found: {type(reference_channel)}.')
2079
+
2080
+ beamform_weights = beamform_weights * scale[..., None]
2081
+
2082
+ return beamform_weights
2083
+
2084
+
2085
+ def rtf_evd(psd_s: Tensor) -> Tensor:
2086
+ r"""Estimate the relative transfer function (RTF) or the steering vector by eigenvalue decomposition.
2087
+
2088
+ .. devices:: CPU CUDA
2089
+
2090
+ .. properties:: TorchScript
2091
+
2092
+ Args:
2093
+ psd_s (Tensor): The complex-valued power spectral density (PSD) matrix of target speech.
2094
+ Tensor of dimension `(..., freq, channel, channel)`
2095
+
2096
+ Returns:
2097
+ Tensor: The estimated complex-valued RTF of target speech.
2098
+ Tensor of dimension `(..., freq, channel)`
2099
+ """
2100
+ if not psd_s.is_complex():
2101
+ raise TypeError(f"The type of psd_s must be ``torch.cfloat`` or ``torch.cdouble``. Found {psd_s.dtype}.")
2102
+ if psd_s.shape[-1] != psd_s.shape[-2]:
2103
+ raise ValueError(f"The last two dimensions of psd_s should be the same. Found {psd_s.shape}.")
2104
+ _, v = torch.linalg.eigh(psd_s) # v is sorted along with eigenvalues in ascending order
2105
+ rtf = v[..., -1] # choose the eigenvector with max eigenvalue
2106
+ return rtf
2107
+
2108
+
2109
+ def rtf_power(
2110
+ psd_s: Tensor,
2111
+ psd_n: Tensor,
2112
+ reference_channel: Union[int, Tensor],
2113
+ n_iter: int = 3,
2114
+ diagonal_loading: bool = True,
2115
+ diag_eps: float = 1e-7,
2116
+ ) -> Tensor:
2117
+ r"""Estimate the relative transfer function (RTF) or the steering vector by the power method.
2118
+
2119
+ .. devices:: CPU CUDA
2120
+
2121
+ .. properties:: Autograd TorchScript
2122
+
2123
+ Args:
2124
+ psd_s (torch.Tensor): The complex-valued power spectral density (PSD) matrix of target speech.
2125
+ Tensor with dimensions `(..., freq, channel, channel)`.
2126
+ psd_n (torch.Tensor): The complex-valued power spectral density (PSD) matrix of noise.
2127
+ Tensor with dimensions `(..., freq, channel, channel)`.
2128
+ reference_channel (int or torch.Tensor): Specifies the reference channel.
2129
+ If the dtype is ``int``, it represents the reference channel index.
2130
+ If the dtype is ``torch.Tensor``, its shape is `(..., channel)`, where the ``channel`` dimension
2131
+ is one-hot.
2132
+ diagonal_loading (bool, optional): If ``True``, enables applying diagonal loading to ``psd_n``.
2133
+ (Default: ``True``)
2134
+ diag_eps (float, optional): The coefficient multiplied to the identity matrix for diagonal loading.
2135
+ It is only effective when ``diagonal_loading`` is set to ``True``. (Default: ``1e-7``)
2136
+
2137
+ Returns:
2138
+ torch.Tensor: The estimated complex-valued RTF of target speech.
2139
+ Tensor of dimension `(..., freq, channel)`.
2140
+ """
2141
+ _assert_psd_matrices(psd_s, psd_n)
2142
+ if n_iter <= 0:
2143
+ raise ValueError("The number of iteration must be greater than 0.")
2144
+
2145
+ # Apply diagonal loading to psd_n to improve robustness.
2146
+ if diagonal_loading:
2147
+ psd_n = _tik_reg(psd_n, reg=diag_eps)
2148
+ # phi is regarded as the first iteration
2149
+ phi = torch.linalg.solve(psd_n, psd_s) # psd_n.inv() @ psd_s
2150
+ if torch.jit.isinstance(reference_channel, int):
2151
+ rtf = phi[..., reference_channel]
2152
+ elif torch.jit.isinstance(reference_channel, Tensor):
2153
+ reference_channel = reference_channel.to(psd_n.dtype)
2154
+ rtf = torch.einsum("...c,...c->...", [phi, reference_channel[..., None, None, :]])
2155
+ else:
2156
+ raise TypeError(f'Expected "int" or "Tensor" for reference_channel. Found: {type(reference_channel)}.')
2157
+ rtf = rtf.unsqueeze(-1) # (..., freq, channel, 1)
2158
+ if n_iter >= 2:
2159
+ # The number of iterations in the for loop is `n_iter - 2`
2160
+ # because the `phi` above and `torch.matmul(psd_s, rtf)` are regarded as
2161
+ # two iterations.
2162
+ for _ in range(n_iter - 2):
2163
+ rtf = torch.matmul(phi, rtf)
2164
+ rtf = torch.matmul(psd_s, rtf)
2165
+ else:
2166
+ # if there is only one iteration, the rtf is the psd_s[..., referenc_channel]
2167
+ # which is psd_n @ phi @ ref_channel
2168
+ rtf = torch.matmul(psd_n, rtf)
2169
+ return rtf.squeeze(-1)
2170
+
2171
+
2172
+ def apply_beamforming(beamform_weights: Tensor, specgram: Tensor) -> Tensor:
2173
+ r"""Apply the beamforming weight to the multi-channel noisy spectrum to obtain the single-channel enhanced spectrum.
2174
+
2175
+ .. devices:: CPU CUDA
2176
+
2177
+ .. properties:: Autograd TorchScript
2178
+
2179
+ .. math::
2180
+ \hat{\textbf{S}}(f) = \textbf{w}_{\text{bf}}(f)^{\mathsf{H}} \textbf{Y}(f)
2181
+
2182
+ where :math:`\textbf{w}_{\text{bf}}(f)` is the beamforming weight for the :math:`f`-th frequency bin,
2183
+ :math:`\textbf{Y}` is the multi-channel spectrum for the :math:`f`-th frequency bin.
2184
+
2185
+ Args:
2186
+ beamform_weights (Tensor): The complex-valued beamforming weight matrix.
2187
+ Tensor of dimension `(..., freq, channel)`
2188
+ specgram (Tensor): The multi-channel complex-valued noisy spectrum.
2189
+ Tensor of dimension `(..., channel, freq, time)`
2190
+
2191
+ Returns:
2192
+ Tensor: The single-channel complex-valued enhanced spectrum.
2193
+ Tensor of dimension `(..., freq, time)`
2194
+ """
2195
+ if beamform_weights.shape[:-2] != specgram.shape[:-3]:
2196
+ raise ValueError(
2197
+ "The dimensions except the last two dimensions of beamform_weights should be the same "
2198
+ "as the dimensions except the last three dimensions of specgram. "
2199
+ f"Found {beamform_weights.shape} for beamform_weights and {specgram.shape} for specgram."
2200
+ )
2201
+
2202
+ if not (beamform_weights.is_complex() and specgram.is_complex()):
2203
+ raise TypeError(
2204
+ "The type of beamform_weights and specgram must be ``torch.cfloat`` or ``torch.cdouble``. "
2205
+ f"Found {beamform_weights.dtype} for beamform_weights and {specgram.dtype} for specgram."
2206
+ )
2207
+
2208
+ # (..., freq, channel) x (..., channel, freq, time) -> (..., freq, time)
2209
+ specgram_enhanced = torch.einsum("...fc,...cft->...ft", [beamform_weights.conj(), specgram])
2210
+ return specgram_enhanced
2211
+
2212
+
2213
+ def _check_shape_compatible(x: torch.Tensor, y: torch.Tensor) -> None:
2214
+ if x.ndim != y.ndim:
2215
+ raise ValueError(f"The operands must be the same dimension (got {x.ndim} and {y.ndim}).")
2216
+
2217
+ for i in range(x.ndim - 1):
2218
+ xi = x.size(i)
2219
+ yi = y.size(i)
2220
+ if xi == yi or xi == 1 or yi == 1:
2221
+ continue
2222
+ raise ValueError(f"Leading dimensions of x and y are not broadcastable (got {x.shape} and {y.shape}).")
2223
+
2224
+
2225
+ def _check_convolve_mode(mode: str) -> None:
2226
+ valid_convolve_modes = ["full", "valid", "same"]
2227
+ if mode not in valid_convolve_modes:
2228
+ raise ValueError(f"Unrecognized mode value '{mode}'. Please specify one of {valid_convolve_modes}.")
2229
+
2230
+
2231
+ def _apply_convolve_mode(conv_result: torch.Tensor, x_length: int, y_length: int, mode: str) -> torch.Tensor:
2232
+ valid_convolve_modes = ["full", "valid", "same"]
2233
+ if mode == "full":
2234
+ return conv_result
2235
+ elif mode == "valid":
2236
+ target_length = max(x_length, y_length) - min(x_length, y_length) + 1
2237
+ start_idx = (conv_result.size(-1) - target_length) // 2
2238
+ return conv_result[..., start_idx : start_idx + target_length]
2239
+ elif mode == "same":
2240
+ start_idx = (conv_result.size(-1) - x_length) // 2
2241
+ return conv_result[..., start_idx : start_idx + x_length]
2242
+ else:
2243
+ raise ValueError(f"Unrecognized mode value '{mode}'. Please specify one of {valid_convolve_modes}.")
2244
+
2245
+
2246
+ def fftconvolve(x: torch.Tensor, y: torch.Tensor, mode: str = "full") -> torch.Tensor:
2247
+ r"""
2248
+ Convolves inputs along their last dimension using FFT. For inputs with large last dimensions, this function
2249
+ is generally much faster than :meth:`convolve`.
2250
+ Note that, in contrast to :meth:`torch.nn.functional.conv1d`, which actually applies the valid cross-correlation
2251
+ operator, this function applies the true `convolution`_ operator.
2252
+ Also note that this function can only output float tensors (int tensor inputs will be cast to float).
2253
+
2254
+ .. devices:: CPU CUDA
2255
+
2256
+ .. properties:: Autograd TorchScript
2257
+
2258
+ Args:
2259
+ x (torch.Tensor): First convolution operand, with shape `(..., N)`.
2260
+ y (torch.Tensor): Second convolution operand, with shape `(..., M)`
2261
+ (leading dimensions must be broadcast-able with those of ``x``).
2262
+ mode (str, optional): Must be one of ("full", "valid", "same").
2263
+
2264
+ * "full": Returns the full convolution result, with shape `(..., N + M - 1)`. (Default)
2265
+ * "valid": Returns the segment of the full convolution result corresponding to where
2266
+ the two inputs overlap completely, with shape `(..., max(N, M) - min(N, M) + 1)`.
2267
+ * "same": Returns the center segment of the full convolution result, with shape `(..., N)`.
2268
+
2269
+ Returns:
2270
+ torch.Tensor: Result of convolving ``x`` and ``y``, with shape `(..., L)`, where
2271
+ the leading dimensions match those of ``x`` and `L` is dictated by ``mode``.
2272
+
2273
+ .. _convolution:
2274
+ https://en.wikipedia.org/wiki/Convolution
2275
+ """
2276
+ _check_shape_compatible(x, y)
2277
+ _check_convolve_mode(mode)
2278
+
2279
+ n = x.size(-1) + y.size(-1) - 1
2280
+ fresult = torch.fft.rfft(x, n=n) * torch.fft.rfft(y, n=n)
2281
+ result = torch.fft.irfft(fresult, n=n)
2282
+ return _apply_convolve_mode(result, x.size(-1), y.size(-1), mode)
2283
+
2284
+
2285
+ def convolve(x: torch.Tensor, y: torch.Tensor, mode: str = "full") -> torch.Tensor:
2286
+ r"""
2287
+ Convolves inputs along their last dimension using the direct method.
2288
+ Note that, in contrast to :meth:`torch.nn.functional.conv1d`, which actually applies the valid cross-correlation
2289
+ operator, this function applies the true `convolution`_ operator.
2290
+
2291
+ .. devices:: CPU CUDA
2292
+
2293
+ .. properties:: Autograd TorchScript
2294
+
2295
+ Args:
2296
+ x (torch.Tensor): First convolution operand, with shape `(..., N)`.
2297
+ y (torch.Tensor): Second convolution operand, with shape `(..., M)`
2298
+ (leading dimensions must be broadcast-able with those of ``x``).
2299
+ mode (str, optional): Must be one of ("full", "valid", "same").
2300
+
2301
+ * "full": Returns the full convolution result, with shape `(..., N + M - 1)`. (Default)
2302
+ * "valid": Returns the segment of the full convolution result corresponding to where
2303
+ the two inputs overlap completely, with shape `(..., max(N, M) - min(N, M) + 1)`.
2304
+ * "same": Returns the center segment of the full convolution result, with shape `(..., N)`.
2305
+
2306
+ Returns:
2307
+ torch.Tensor: Result of convolving ``x`` and ``y``, with shape `(..., L)`, where
2308
+ the leading dimensions match those of ``x`` and `L` is dictated by ``mode``.
2309
+
2310
+ .. _convolution:
2311
+ https://en.wikipedia.org/wiki/Convolution
2312
+ """
2313
+ _check_shape_compatible(x, y)
2314
+ _check_convolve_mode(mode)
2315
+
2316
+ x_size, y_size = x.size(-1), y.size(-1)
2317
+
2318
+ if x.size(-1) < y.size(-1):
2319
+ x, y = y, x
2320
+
2321
+ if x.shape[:-1] != y.shape[:-1]:
2322
+ new_shape = [max(i, j) for i, j in zip(x.shape[:-1], y.shape[:-1])]
2323
+ x = x.broadcast_to(new_shape + [x.shape[-1]])
2324
+ y = y.broadcast_to(new_shape + [y.shape[-1]])
2325
+
2326
+ num_signals = torch.tensor(x.shape[:-1]).prod()
2327
+ reshaped_x = x.reshape((int(num_signals), x.size(-1)))
2328
+ reshaped_y = y.reshape((int(num_signals), y.size(-1)))
2329
+ output = torch.nn.functional.conv1d(
2330
+ input=reshaped_x,
2331
+ weight=reshaped_y.flip(-1).unsqueeze(1),
2332
+ stride=1,
2333
+ groups=reshaped_x.size(0),
2334
+ padding=reshaped_y.size(-1) - 1,
2335
+ )
2336
+ output_shape = x.shape[:-1] + (-1,)
2337
+ result = output.reshape(output_shape)
2338
+ return _apply_convolve_mode(result, x_size, y_size, mode)
2339
+
2340
+
2341
+ def add_noise(
2342
+ waveform: torch.Tensor, noise: torch.Tensor, snr: torch.Tensor, lengths: Optional[torch.Tensor] = None
2343
+ ) -> torch.Tensor:
2344
+ r"""Scales and adds noise to waveform per signal-to-noise ratio.
2345
+
2346
+ Specifically, for each pair of waveform vector :math:`x \in \mathbb{R}^L` and noise vector
2347
+ :math:`n \in \mathbb{R}^L`, the function computes output :math:`y` as
2348
+
2349
+ .. math::
2350
+ y = x + a n \, \text{,}
2351
+
2352
+ where
2353
+
2354
+ .. math::
2355
+ a = \sqrt{ \frac{ ||x||_{2}^{2} }{ ||n||_{2}^{2} } \cdot 10^{-\frac{\text{SNR}}{10}} } \, \text{,}
2356
+
2357
+ with :math:`\text{SNR}` being the desired signal-to-noise ratio between :math:`x` and :math:`n`, in dB.
2358
+
2359
+ Note that this function broadcasts singleton leading dimensions in its inputs in a manner that is
2360
+ consistent with the above formulae and PyTorch's broadcasting semantics.
2361
+
2362
+ .. devices:: CPU CUDA
2363
+
2364
+ .. properties:: Autograd TorchScript
2365
+
2366
+ Args:
2367
+ waveform (torch.Tensor): Input waveform, with shape `(..., L)`.
2368
+ noise (torch.Tensor): Noise, with shape `(..., L)` (same shape as ``waveform``).
2369
+ snr (torch.Tensor): Signal-to-noise ratios in dB, with shape `(...,)`.
2370
+ lengths (torch.Tensor or None, optional): Valid lengths of signals in ``waveform`` and ``noise``, with shape
2371
+ `(...,)` (leading dimensions must match those of ``waveform``). If ``None``, all elements in ``waveform``
2372
+ and ``noise`` are treated as valid. (Default: ``None``)
2373
+
2374
+ Returns:
2375
+ torch.Tensor: Result of scaling and adding ``noise`` to ``waveform``, with shape `(..., L)`
2376
+ (same shape as ``waveform``).
2377
+ """
2378
+
2379
+ if not (waveform.ndim - 1 == noise.ndim - 1 == snr.ndim and (lengths is None or lengths.ndim == snr.ndim)):
2380
+ raise ValueError("Input leading dimensions don't match.")
2381
+
2382
+ L = waveform.size(-1)
2383
+
2384
+ if L != noise.size(-1):
2385
+ raise ValueError(f"Length dimensions of waveform and noise don't match (got {L} and {noise.size(-1)}).")
2386
+
2387
+ # compute scale
2388
+ if lengths is not None:
2389
+ mask = torch.arange(0, L, device=lengths.device).expand(waveform.shape) < lengths.unsqueeze(
2390
+ -1
2391
+ ) # (*, L) < (*, 1) = (*, L)
2392
+ masked_waveform = waveform * mask
2393
+ masked_noise = noise * mask
2394
+ else:
2395
+ masked_waveform = waveform
2396
+ masked_noise = noise
2397
+
2398
+ energy_signal = torch.linalg.vector_norm(masked_waveform, ord=2, dim=-1) ** 2 # (*,)
2399
+ energy_noise = torch.linalg.vector_norm(masked_noise, ord=2, dim=-1) ** 2 # (*,)
2400
+ original_snr_db = 10 * (torch.log10(energy_signal) - torch.log10(energy_noise))
2401
+ scale = 10 ** ((original_snr_db - snr) / 20.0) # (*,)
2402
+
2403
+ # scale noise
2404
+ scaled_noise = scale.unsqueeze(-1) * noise # (*, 1) * (*, L) = (*, L)
2405
+
2406
+ return waveform + scaled_noise # (*, L)
2407
+
2408
+
2409
+ def speed(
2410
+ waveform: torch.Tensor, orig_freq: int, factor: float, lengths: Optional[torch.Tensor] = None
2411
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
2412
+ r"""Adjusts waveform speed.
2413
+
2414
+ .. devices:: CPU CUDA
2415
+
2416
+ .. properties:: Autograd TorchScript
2417
+
2418
+ Args:
2419
+ waveform (torch.Tensor): Input signals, with shape `(..., time)`.
2420
+ orig_freq (int): Original frequency of the signals in ``waveform``.
2421
+ factor (float): Factor by which to adjust speed of input. Values greater than 1.0
2422
+ compress ``waveform`` in time, whereas values less than 1.0 stretch ``waveform`` in time.
2423
+ lengths (torch.Tensor or None, optional): Valid lengths of signals in ``waveform``, with shape `(...)`.
2424
+ If ``None``, all elements in ``waveform`` are treated as valid. (Default: ``None``)
2425
+
2426
+ Returns:
2427
+ (torch.Tensor, torch.Tensor or None):
2428
+ torch.Tensor
2429
+ Speed-adjusted waveform, with shape `(..., new_time).`
2430
+ torch.Tensor or None
2431
+ If ``lengths`` is not ``None``, valid lengths of signals in speed-adjusted waveform,
2432
+ with shape `(...)`; otherwise, ``None``.
2433
+ """
2434
+
2435
+ source_sample_rate = int(factor * orig_freq)
2436
+ target_sample_rate = int(orig_freq)
2437
+
2438
+ gcd = math.gcd(source_sample_rate, target_sample_rate)
2439
+ source_sample_rate = source_sample_rate // gcd
2440
+ target_sample_rate = target_sample_rate // gcd
2441
+
2442
+ if lengths is None:
2443
+ out_lengths = None
2444
+ else:
2445
+ out_lengths = torch.ceil(lengths * target_sample_rate / source_sample_rate).to(lengths.dtype)
2446
+
2447
+ return resample(waveform, source_sample_rate, target_sample_rate), out_lengths
2448
+
2449
+
2450
+ def preemphasis(waveform, coeff: float = 0.97) -> torch.Tensor:
2451
+ r"""Pre-emphasizes a waveform along its last dimension, i.e.
2452
+ for each signal :math:`x` in ``waveform``, computes
2453
+ output :math:`y` as
2454
+
2455
+ .. math::
2456
+ y[i] = x[i] - \text{coeff} \cdot x[i - 1]
2457
+
2458
+ .. devices:: CPU CUDA
2459
+
2460
+ .. properties:: Autograd TorchScript
2461
+
2462
+ Args:
2463
+ waveform (torch.Tensor): Waveform, with shape `(..., N)`.
2464
+ coeff (float, optional): Pre-emphasis coefficient. Typically between 0.0 and 1.0.
2465
+ (Default: 0.97)
2466
+
2467
+ Returns:
2468
+ torch.Tensor: Pre-emphasized waveform, with shape `(..., N)`.
2469
+ """
2470
+ waveform = waveform.clone()
2471
+ waveform[..., 1:] -= coeff * waveform[..., :-1]
2472
+ return waveform
2473
+
2474
+
2475
+ def deemphasis(waveform, coeff: float = 0.97) -> torch.Tensor:
2476
+ r"""De-emphasizes a waveform along its last dimension.
2477
+ Inverse of :meth:`preemphasis`. Concretely, for each signal
2478
+ :math:`x` in ``waveform``, computes output :math:`y` as
2479
+
2480
+ .. math::
2481
+ y[i] = x[i] + \text{coeff} \cdot y[i - 1]
2482
+
2483
+ .. devices:: CPU CUDA
2484
+
2485
+ .. properties:: Autograd TorchScript
2486
+
2487
+ Args:
2488
+ waveform (torch.Tensor): Waveform, with shape `(..., N)`.
2489
+ coeff (float, optional): De-emphasis coefficient. Typically between 0.0 and 1.0.
2490
+ (Default: 0.97)
2491
+
2492
+ Returns:
2493
+ torch.Tensor: De-emphasized waveform, with shape `(..., N)`.
2494
+ """
2495
+ a_coeffs = torch.tensor([1.0, -coeff], dtype=waveform.dtype, device=waveform.device)
2496
+ b_coeffs = torch.tensor([1.0, 0.0], dtype=waveform.dtype, device=waveform.device)
2497
+ return torchaudio.functional.lfilter(waveform, a_coeffs=a_coeffs, b_coeffs=b_coeffs)
2498
+
2499
+
2500
+ def frechet_distance(mu_x, sigma_x, mu_y, sigma_y):
2501
+ r"""Computes the Fréchet distance between two multivariate normal distributions :cite:`dowson1982frechet`.
2502
+
2503
+ Concretely, for multivariate Gaussians :math:`X(\mu_X, \Sigma_X)`
2504
+ and :math:`Y(\mu_Y, \Sigma_Y)`, the function computes and returns :math:`F` as
2505
+
2506
+ .. math::
2507
+ F(X, Y) = || \mu_X - \mu_Y ||_2^2
2508
+ + \text{Tr}\left( \Sigma_X + \Sigma_Y - 2 \sqrt{\Sigma_X \Sigma_Y} \right)
2509
+
2510
+ Args:
2511
+ mu_x (torch.Tensor): mean :math:`\mu_X` of multivariate Gaussian :math:`X`, with shape `(N,)`.
2512
+ sigma_x (torch.Tensor): covariance matrix :math:`\Sigma_X` of :math:`X`, with shape `(N, N)`.
2513
+ mu_y (torch.Tensor): mean :math:`\mu_Y` of multivariate Gaussian :math:`Y`, with shape `(N,)`.
2514
+ sigma_y (torch.Tensor): covariance matrix :math:`\Sigma_Y` of :math:`Y`, with shape `(N, N)`.
2515
+
2516
+ Returns:
2517
+ torch.Tensor: the Fréchet distance between :math:`X` and :math:`Y`.
2518
+ """
2519
+ if len(mu_x.size()) != 1:
2520
+ raise ValueError(f"Input mu_x must be one-dimensional; got dimension {len(mu_x.size())}.")
2521
+ if len(sigma_x.size()) != 2:
2522
+ raise ValueError(f"Input sigma_x must be two-dimensional; got dimension {len(sigma_x.size())}.")
2523
+ if sigma_x.size(0) != sigma_x.size(1) != mu_x.size(0):
2524
+ raise ValueError("Each of sigma_x's dimensions must match mu_x's size.")
2525
+ if mu_x.size() != mu_y.size():
2526
+ raise ValueError(f"Inputs mu_x and mu_y must have the same shape; got {mu_x.size()} and {mu_y.size()}.")
2527
+ if sigma_x.size() != sigma_y.size():
2528
+ raise ValueError(
2529
+ f"Inputs sigma_x and sigma_y must have the same shape; got {sigma_x.size()} and {sigma_y.size()}."
2530
+ )
2531
+
2532
+ a = (mu_x - mu_y).square().sum()
2533
+ b = sigma_x.trace() + sigma_y.trace()
2534
+ c = torch.linalg.eigvals(sigma_x @ sigma_y).sqrt().real.sum()
2535
+ return a + b - 2 * c
lib/python3.12/site-packages/torchaudio/lib/__init__.py ADDED
File without changes
lib/python3.12/site-packages/torchaudio/lib/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (193 Bytes). View file
 
lib/python3.12/site-packages/torchaudio/models/__init__.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ._hdemucs import HDemucs, hdemucs_high, hdemucs_low, hdemucs_medium
2
+ from .conformer import Conformer
3
+ from .conv_tasnet import conv_tasnet_base, ConvTasNet
4
+ from .deepspeech import DeepSpeech
5
+ from .emformer import Emformer
6
+ from .rnnt import emformer_rnnt_base, emformer_rnnt_model, RNNT
7
+ from .rnnt_decoder import Hypothesis, RNNTBeamSearch
8
+ from .squim import (
9
+ squim_objective_base,
10
+ squim_objective_model,
11
+ squim_subjective_base,
12
+ squim_subjective_model,
13
+ SquimObjective,
14
+ SquimSubjective,
15
+ )
16
+ from .tacotron2 import Tacotron2
17
+ from .wav2letter import Wav2Letter
18
+ from .wav2vec2 import (
19
+ hubert_base,
20
+ hubert_large,
21
+ hubert_pretrain_base,
22
+ hubert_pretrain_large,
23
+ hubert_pretrain_model,
24
+ hubert_pretrain_xlarge,
25
+ hubert_xlarge,
26
+ HuBERTPretrainModel,
27
+ wav2vec2_base,
28
+ wav2vec2_large,
29
+ wav2vec2_large_lv60k,
30
+ wav2vec2_model,
31
+ wav2vec2_xlsr_1b,
32
+ wav2vec2_xlsr_2b,
33
+ wav2vec2_xlsr_300m,
34
+ Wav2Vec2Model,
35
+ wavlm_base,
36
+ wavlm_large,
37
+ wavlm_model,
38
+ )
39
+ from .wavernn import WaveRNN
40
+
41
+
42
+ __all__ = [
43
+ "Wav2Letter",
44
+ "WaveRNN",
45
+ "ConvTasNet",
46
+ "conv_tasnet_base",
47
+ "DeepSpeech",
48
+ "Wav2Vec2Model",
49
+ "HuBERTPretrainModel",
50
+ "wavlm_model",
51
+ "wavlm_base",
52
+ "wavlm_large",
53
+ "wav2vec2_model",
54
+ "wav2vec2_base",
55
+ "wav2vec2_large",
56
+ "wav2vec2_large_lv60k",
57
+ "hubert_base",
58
+ "hubert_large",
59
+ "hubert_xlarge",
60
+ "hubert_pretrain_model",
61
+ "hubert_pretrain_base",
62
+ "hubert_pretrain_large",
63
+ "hubert_pretrain_xlarge",
64
+ "wav2vec2_xlsr_300m",
65
+ "wav2vec2_xlsr_1b",
66
+ "wav2vec2_xlsr_2b",
67
+ "Tacotron2",
68
+ "Conformer",
69
+ "Emformer",
70
+ "Hypothesis",
71
+ "RNNT",
72
+ "RNNTBeamSearch",
73
+ "emformer_rnnt_base",
74
+ "emformer_rnnt_model",
75
+ "HDemucs",
76
+ "hdemucs_low",
77
+ "hdemucs_medium",
78
+ "hdemucs_high",
79
+ "squim_objective_base",
80
+ "squim_objective_model",
81
+ "squim_subjective_base",
82
+ "squim_subjective_model",
83
+ "SquimObjective",
84
+ "SquimSubjective",
85
+ ]
lib/python3.12/site-packages/torchaudio/models/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (1.82 kB). View file
 
lib/python3.12/site-packages/torchaudio/models/__pycache__/_hdemucs.cpython-312.pyc ADDED
Binary file (48.5 kB). View file
 
lib/python3.12/site-packages/torchaudio/models/__pycache__/conformer.cpython-312.pyc ADDED
Binary file (13.5 kB). View file
 
lib/python3.12/site-packages/torchaudio/models/__pycache__/conv_tasnet.cpython-312.pyc ADDED
Binary file (14.5 kB). View file
 
lib/python3.12/site-packages/torchaudio/models/__pycache__/deepspeech.cpython-312.pyc ADDED
Binary file (4.57 kB). View file
 
lib/python3.12/site-packages/torchaudio/models/__pycache__/emformer.cpython-312.pyc ADDED
Binary file (46.8 kB). View file
 
lib/python3.12/site-packages/torchaudio/models/__pycache__/rnnt.cpython-312.pyc ADDED
Binary file (40.5 kB). View file
 
lib/python3.12/site-packages/torchaudio/models/__pycache__/rnnt_decoder.cpython-312.pyc ADDED
Binary file (17.6 kB). View file
 
lib/python3.12/site-packages/torchaudio/models/__pycache__/tacotron2.cpython-312.pyc ADDED
Binary file (47.4 kB). View file
 
lib/python3.12/site-packages/torchaudio/models/__pycache__/wav2letter.cpython-312.pyc ADDED
Binary file (4.13 kB). View file