hunterHearsPy 1.0.0__tar.gz → 1.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/PKG-INFO +3 -2
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/pyproject.toml +9 -2
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/src/hunterHearsPy/__init__.py +9 -7
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/src/hunterHearsPy/ioAudio.py +30 -30
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/src/hunterHearsPy/theTypes.py +1 -1
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/src/hunterHearsPy/windowingFunctionsTensor.py +8 -9
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/LICENSE +0 -0
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/README.md +0 -0
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/src/hunterHearsPy/amplitude.py +0 -0
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/src/hunterHearsPy/autoRevert.py +0 -0
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/src/hunterHearsPy/clippingArrays.py +0 -0
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/src/hunterHearsPy/py.typed +0 -0
- {hunterhearspy-1.0.0 → hunterhearspy-1.0.3}/src/hunterHearsPy/windowingFunctions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hunterHearsPy
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: Audio processing.
|
|
5
5
|
Keywords:
|
|
6
6
|
Author: Hunter Hogan
|
|
@@ -16,10 +16,11 @@ Classifier: Operating System :: OS Independent
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
18
|
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Dist: huntermakespy>=0.7.0
|
|
19
20
|
Requires-Dist: numpy
|
|
20
21
|
Requires-Dist: resampy
|
|
21
22
|
Requires-Dist: scipy
|
|
22
|
-
Requires-Dist: soundfile
|
|
23
|
+
Requires-Dist: soundfile>=0.14.0
|
|
23
24
|
Requires-Dist: tqdm
|
|
24
25
|
Requires-Dist: torch ; extra == 'torch'
|
|
25
26
|
Maintainer: Hunter Hogan
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "hunterHearsPy"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.3"
|
|
4
4
|
description = "Audio processing."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -20,7 +20,14 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3.14",
|
|
21
21
|
"Typing :: Typed",
|
|
22
22
|
]
|
|
23
|
-
dependencies = [
|
|
23
|
+
dependencies = [
|
|
24
|
+
"hunterMakesPy>=0.7.0",
|
|
25
|
+
"numpy",
|
|
26
|
+
"resampy",
|
|
27
|
+
"scipy",
|
|
28
|
+
"soundfile>=0.14.0",
|
|
29
|
+
"tqdm",
|
|
30
|
+
]
|
|
24
31
|
optional-dependencies = { torch = ["torch"] }
|
|
25
32
|
|
|
26
33
|
[project.urls]
|
|
@@ -3,17 +3,19 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
from hunterHearsPy.theTypes import (
|
|
5
5
|
ArraySpectrograms as ArraySpectrograms, ArrayType as ArrayType, ArrayWaveforms as ArrayWaveforms,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
callableReturnsNDArray as callableReturnsNDArray, NormalizationReverter as NormalizationReverter,
|
|
7
|
+
ParametersShortTimeFFT as ParametersShortTimeFFT, ParametersSTFT as ParametersSTFT, ParametersUniversal as ParametersUniversal,
|
|
8
|
+
Spectrogram as Spectrogram, SpectrogramDtype as SpectrogramDtype, Waveform as Waveform, WaveformDtype as WaveformDtype,
|
|
9
|
+
WaveformMetadata as WaveformMetadata, WindowingFunction as WindowingFunction, WindowingFunctionDtype as WindowingFunctionDtype)
|
|
10
10
|
|
|
11
11
|
# isort: split
|
|
12
12
|
from hunterHearsPy.amplitude import normalizeArrayWaveforms as normalizeArrayWaveforms, normalizeWaveform as normalizeWaveform
|
|
13
|
-
from hunterHearsPy.autoRevert import moveToAxisOfOperation as moveToAxisOfOperation
|
|
14
13
|
from hunterHearsPy.clippingArrays import applyHardLimit as applyHardLimit, applyHardLimitComplexValued as applyHardLimitComplexValued
|
|
15
14
|
from hunterHearsPy.windowingFunctions import cosineWings as cosineWings, equalPower as equalPower, halfsine as halfsine, tukey as tukey
|
|
16
15
|
|
|
16
|
+
# isort: split
|
|
17
|
+
from hunterHearsPy.autoRevert import moveToAxisOfOperation as moveToAxisOfOperation
|
|
18
|
+
|
|
17
19
|
# isort: split
|
|
18
20
|
from hunterHearsPy.ioAudio import (
|
|
19
21
|
getWaveformMetadata as getWaveformMetadata, lengthWindowingFunctionDEFAULT as lengthWindowingFunctionDEFAULT,
|
|
@@ -30,5 +32,5 @@ from contextlib import suppress
|
|
|
30
32
|
|
|
31
33
|
with suppress(ModuleNotFoundError): # noqa: RUF067
|
|
32
34
|
from hunterHearsPy.windowingFunctionsTensor import (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
cosineWingsTensor as cosineWingsTensor, equalPowerTensor as equalPowerTensor, halfsineTensor as halfsineTensor,
|
|
36
|
+
tukeyTensor as tukeyTensor)
|
|
@@ -9,24 +9,24 @@ arrays shaped `(channels, samples)`. All spectrograms are complex 64-bit float
|
|
|
9
9
|
Contents
|
|
10
10
|
--------
|
|
11
11
|
Functions
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
12
|
+
getWaveformMetadata
|
|
13
|
+
Retrieve metadata for a collection of audio waveform files.
|
|
14
|
+
loadSpectrograms
|
|
15
|
+
Load spectrograms from a list of audio files.
|
|
16
|
+
loadWaveforms
|
|
17
|
+
Load a list of audio files into a single stacked NumPy array.
|
|
18
|
+
readAudioFile
|
|
19
|
+
Read an audio file and return stereo waveform data as a NumPy array.
|
|
20
|
+
resampleWaveform
|
|
21
|
+
Resample a waveform array to a target sample rate.
|
|
22
|
+
spectrogramToWAV
|
|
23
|
+
Write a complex spectrogram to a WAV file.
|
|
24
|
+
stft
|
|
25
|
+
Perform Short-Time Fourier Transform or its inverse on waveform or spectrogram data.
|
|
26
|
+
waveformSpectrogramWaveform
|
|
27
|
+
Decorate a spectrogram-processing callable to accept and return waveforms.
|
|
28
|
+
writeWAV
|
|
29
|
+
Write a waveform array to a WAV file.
|
|
30
30
|
|
|
31
31
|
"""
|
|
32
32
|
from __future__ import annotations
|
|
@@ -34,6 +34,7 @@ from __future__ import annotations
|
|
|
34
34
|
from hunterHearsPy import (
|
|
35
35
|
ArraySpectrograms, ArrayWaveforms, halfsine, ParametersShortTimeFFT, ParametersSTFT, ParametersUniversal, Spectrogram, Waveform,
|
|
36
36
|
WaveformMetadata, WindowingFunction)
|
|
37
|
+
from hunterMakesPy.filesystemToolkit import makeDirectorySafely
|
|
37
38
|
from math import ceil as ceiling, log2 as log_base2
|
|
38
39
|
from multiprocessing import set_start_method as multiprocessing_set_start_method
|
|
39
40
|
from numpy import complex64, dtype, float32, floating, ndarray
|
|
@@ -48,7 +49,6 @@ if TYPE_CHECKING:
|
|
|
48
49
|
from collections.abc import Callable, Sequence
|
|
49
50
|
from os import PathLike
|
|
50
51
|
|
|
51
|
-
# When to use multiprocessing.set_start_method https://github.com/hunterhogan/mapFolding/issues/6
|
|
52
52
|
if __name__ == '__main__':
|
|
53
53
|
multiprocessing_set_start_method('spawn')
|
|
54
54
|
|
|
@@ -138,19 +138,18 @@ def getWaveformMetadata(listPathFilenames: Sequence[str | PathLike[str]], sample
|
|
|
138
138
|
def readAudioFile(pathFilename: str | PathLike[Any] | BinaryIO, sampleRate: float | None = None) -> Waveform:
|
|
139
139
|
"""Read an audio file and return stereo waveform data as a NumPy array.
|
|
140
140
|
|
|
141
|
-
You can use this function to load any audio file that `soundfile` [1] supports. The
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
`
|
|
141
|
+
You can use this function to load any audio file that `soundfile` [1] supports. The returned
|
|
142
|
+
`Waveform` [2] is always shaped `(channels, samples)` where `channels` is `2`. When the source
|
|
143
|
+
file is mono, `readAudioFile` duplicates the single channel to produce a stereo array. When
|
|
144
|
+
`sampleRate` differs from the file's native sample rate, `readAudioFile` resamples using
|
|
145
|
+
`resampleWaveform`.
|
|
146
146
|
|
|
147
147
|
Parameters
|
|
148
148
|
----------
|
|
149
149
|
pathFilename : str | PathLike[Any] | BinaryIO
|
|
150
150
|
Path to the audio file or a binary stream compatible with `soundfile` [1].
|
|
151
|
-
sampleRate : float | None =
|
|
152
|
-
Target sample rate of the returned `Waveform` [2] in Hz. Defaults to `44100`
|
|
153
|
-
when `None`.
|
|
151
|
+
sampleRate : float | None = 44100
|
|
152
|
+
Target sample rate of the returned `Waveform` [2] in Hz. Defaults to `44100` when `None`.
|
|
154
153
|
|
|
155
154
|
Returns
|
|
156
155
|
-------
|
|
@@ -188,6 +187,7 @@ def readAudioFile(pathFilename: str | PathLike[Any] | BinaryIO, sampleRate: floa
|
|
|
188
187
|
axisTime = 0
|
|
189
188
|
axisChannels = 1
|
|
190
189
|
waveform = cast('Waveform', resampleWaveform(waveform, sampleRateDesired=sampleRate, sampleRateSource=sampleRateSource, axisTime=axisTime))
|
|
190
|
+
# TODO In my audio ecosystem, must I force a minimum of 2 channels, or can I merely force an axis for time, even if the axis is length=1?
|
|
191
191
|
if waveform.shape[axisChannels] == 1:
|
|
192
192
|
waveform = cast('Waveform', numpy.repeat(waveform, 2, axis=axisChannels))
|
|
193
193
|
return cast('Waveform', numpy.transpose(waveform, axes=(axisChannels, axisTime)))
|
|
@@ -342,7 +342,7 @@ def writeWAV(pathFilename: str | PathLike[Any] | BinaryIO, waveform: Waveform, s
|
|
|
342
342
|
"""
|
|
343
343
|
if sampleRate is None:
|
|
344
344
|
sampleRate = parametersUniversal['sampleRate']
|
|
345
|
-
|
|
345
|
+
makeDirectorySafely(pathFilename)
|
|
346
346
|
soundfile.write(file=pathFilename, data=waveform.T, samplerate=int(sampleRate), subtype='FLOAT', format='WAV')
|
|
347
347
|
|
|
348
348
|
@overload # stft 1 ndarray
|
|
@@ -352,10 +352,10 @@ def stft(arrayTarget: Waveform, *, sampleRate: float | None = None, lengthHop: i
|
|
|
352
352
|
def stft(arrayTarget: ArrayWaveforms, *, sampleRate: float | None = None, lengthHop: int | None = None, windowingFunction: WindowingFunction | None = None, lengthWindowingFunction: int | None = None, lengthFFT: int | None = None, inverse: Literal[False] = False, lengthWaveform: None = None, indexingAxis: int = -1) -> ArraySpectrograms: ...
|
|
353
353
|
|
|
354
354
|
@overload # istft 1 ndarray
|
|
355
|
-
def stft(arrayTarget: Spectrogram, *, sampleRate: float | None = None, lengthHop: int | None = None, windowingFunction: WindowingFunction | None = None, lengthWindowingFunction: int | None = None, lengthFFT: int | None = None, inverse: Literal[True]
|
|
355
|
+
def stft(arrayTarget: Spectrogram, *, sampleRate: float | None = None, lengthHop: int | None = None, windowingFunction: WindowingFunction | None = None, lengthWindowingFunction: int | None = None, lengthFFT: int | None = None, inverse: Literal[True], lengthWaveform: int, indexingAxis: None = None) -> Waveform: ...
|
|
356
356
|
|
|
357
357
|
@overload # istft many ndarray
|
|
358
|
-
def stft(arrayTarget: ArraySpectrograms, *, sampleRate: float | None = None, lengthHop: int | None = None, windowingFunction: WindowingFunction | None = None, lengthWindowingFunction: int | None = None, lengthFFT: int | None = None, inverse: Literal[True]
|
|
358
|
+
def stft(arrayTarget: ArraySpectrograms, *, sampleRate: float | None = None, lengthHop: int | None = None, windowingFunction: WindowingFunction | None = None, lengthWindowingFunction: int | None = None, lengthFFT: int | None = None, inverse: Literal[True], lengthWaveform: int, indexingAxis: int = -1) -> ArrayWaveforms: ...
|
|
359
359
|
|
|
360
360
|
def stft(arrayTarget: Waveform | ArrayWaveforms | Spectrogram | ArraySpectrograms
|
|
361
361
|
, *
|
|
@@ -16,10 +16,10 @@ from typing import Any, TYPE_CHECKING, TypeAlias, TypedDict, TypeVar
|
|
|
16
16
|
if TYPE_CHECKING:
|
|
17
17
|
from scipy.signal._short_time_fft import _FFTMode, _PadType, _ScaleTo
|
|
18
18
|
|
|
19
|
-
个 = TypeVar('个', covariant=True)
|
|
20
19
|
ArrayType = TypeVar('ArrayType', bound=ndarray[tuple[Any, ...], dtype[Any]], covariant=True)
|
|
21
20
|
WindowingFunctionDtype: TypeAlias = floating[Any]
|
|
22
21
|
WindowingFunction: TypeAlias = ndarray[tuple[int], dtype[WindowingFunctionDtype]]
|
|
22
|
+
callableReturnsNDArray = TypeVar('callableReturnsNDArray', bound=Callable[..., WindowingFunction])
|
|
23
23
|
WaveformDtype: TypeAlias = floating[Any]
|
|
24
24
|
Waveform: TypeAlias = ndarray[tuple[int, int], dtype[WaveformDtype]]
|
|
25
25
|
"""Two-axes NumPy `ndarray` representing audio waveforms.
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
"""Create PyTorch tensor windowing functions."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
from torch.types import Device
|
|
7
|
-
from typing import Any, TypeVar
|
|
4
|
+
from hunterHearsPy import callableReturnsNDArray, cosineWings, equalPower, halfsine, tukey
|
|
5
|
+
from typing import Any, TYPE_CHECKING
|
|
8
6
|
import torch
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from torch.types import Device
|
|
11
10
|
|
|
12
11
|
def _convertToTensor(*arguments: Any, callableTarget: callableReturnsNDArray, device: Device, **keywordArguments: Any) -> torch.Tensor:
|
|
13
12
|
arrayTarget = callableTarget(*arguments, **keywordArguments)
|
|
@@ -29,7 +28,7 @@ def cosineWingsTensor(lengthWindow: int, ratioTaper: float | None=None, device:
|
|
|
29
28
|
Ratio of taper length to windowing-function length. The value must be between 0 and 1,
|
|
30
29
|
inclusive.
|
|
31
30
|
device : Device = torch.device(device='cpu')
|
|
32
|
-
PyTorch device for
|
|
31
|
+
PyTorch device for `Tensor`.
|
|
33
32
|
|
|
34
33
|
Returns
|
|
35
34
|
-------
|
|
@@ -70,7 +69,7 @@ def equalPowerTensor(lengthWindow: int, ratioTaper: float | None=None, device: D
|
|
|
70
69
|
Ratio of taper length to windowing-function length. The value must be between 0 and 1,
|
|
71
70
|
inclusive.
|
|
72
71
|
device : Device = torch.device(device='cpu')
|
|
73
|
-
PyTorch device for
|
|
72
|
+
PyTorch device for `Tensor`.
|
|
74
73
|
|
|
75
74
|
Returns
|
|
76
75
|
-------
|
|
@@ -107,7 +106,7 @@ def halfsineTensor(lengthWindow: int, device: Device | None=None) -> torch.Tenso
|
|
|
107
106
|
lengthWindow : int
|
|
108
107
|
Total length of the windowing function.
|
|
109
108
|
device : Device = torch.device(device='cpu')
|
|
110
|
-
PyTorch device for
|
|
109
|
+
PyTorch device for `Tensor`.
|
|
111
110
|
|
|
112
111
|
Returns
|
|
113
112
|
-------
|
|
@@ -150,7 +149,7 @@ def tukeyTensor(lengthWindow: int, ratioTaper: float | None=None, device: Device
|
|
|
150
149
|
Additional keyword arguments. `alpha` overrides `ratioTaper` when provided, matching SciPy's
|
|
151
150
|
API.
|
|
152
151
|
device : Device = torch.device(device='cpu')
|
|
153
|
-
PyTorch device for
|
|
152
|
+
PyTorch device for `Tensor`.
|
|
154
153
|
|
|
155
154
|
Returns
|
|
156
155
|
-------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|