analyzeAudio 0.0.14__tar.gz → 0.0.16__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.
Files changed (23) hide show
  1. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/PKG-INFO +49 -19
  2. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/README.md +45 -14
  3. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio/analyzersUseFilename.py +19 -18
  4. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio/analyzersUseSpectrogram.py +11 -11
  5. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio/analyzersUseTensor.py +3 -2
  6. analyzeaudio-0.0.16/analyzeAudio/analyzersUseWaveform.py +26 -0
  7. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio/audioAspectsRegistry.py +8 -7
  8. analyzeaudio-0.0.16/analyzeAudio/py.typed +0 -0
  9. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio/pythonator.py +2 -1
  10. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio.egg-info/PKG-INFO +49 -19
  11. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio.egg-info/SOURCES.txt +1 -0
  12. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio.egg-info/requires.txt +1 -2
  13. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/pyproject.toml +7 -8
  14. analyzeaudio-0.0.14/analyzeAudio/analyzersUseWaveform.py +0 -26
  15. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/LICENSE +0 -0
  16. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio/__init__.py +0 -0
  17. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio.egg-info/dependency_links.txt +0 -0
  18. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio.egg-info/entry_points.txt +0 -0
  19. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/analyzeAudio.egg-info/top_level.txt +0 -0
  20. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/setup.cfg +0 -0
  21. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/tests/conftest.py +0 -0
  22. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/tests/test_audioAspectsRegistry.py +0 -0
  23. {analyzeaudio-0.0.14 → analyzeaudio-0.0.16}/tests/test_other.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: analyzeAudio
3
- Version: 0.0.14
3
+ Version: 0.0.16
4
4
  Summary: Measure one or more aspects of one or more audio files.
5
5
  Author-email: Hunter Hogan <HunterHogan@pm.me>
6
6
  License: CC-BY-NC-4.0
@@ -15,7 +15,6 @@ Classifier: Intended Audience :: End Users/Desktop
15
15
  Classifier: Intended Audience :: Science/Research
16
16
  Classifier: Intended Audience :: Information Technology
17
17
  Classifier: Intended Audience :: Other Audience
18
- Classifier: License :: Free for non-commercial use
19
18
  Classifier: Natural Language :: English
20
19
  Classifier: Operating System :: OS Independent
21
20
  Classifier: Programming Language :: Python
@@ -36,7 +35,6 @@ License-File: LICENSE
36
35
  Requires-Dist: cachetools
37
36
  Requires-Dist: librosa
38
37
  Requires-Dist: numpy
39
- Requires-Dist: optype[numpy]
40
38
  Requires-Dist: standard-aifc; python_version >= "3.13"
41
39
  Requires-Dist: standard-sunau; python_version >= "3.13"
42
40
  Requires-Dist: torch
@@ -44,10 +42,11 @@ Requires-Dist: torchmetrics[audio]
44
42
  Requires-Dist: tqdm
45
43
  Requires-Dist: Z0Z_tools
46
44
  Provides-Extra: testing
45
+ Requires-Dist: pytest; extra == "testing"
47
46
  Requires-Dist: pytest-cov; extra == "testing"
48
47
  Requires-Dist: pytest-xdist; extra == "testing"
49
- Requires-Dist: pytest; extra == "testing"
50
48
  Requires-Dist: pyupgrade; extra == "testing"
49
+ Dynamic: license-file
51
50
 
52
51
  # analyzeAudio
53
52
 
@@ -139,12 +138,45 @@ print(audioAspects['Chromagram']['analyzerParameters'])
139
138
  'Signal entropy': float
140
139
  'Spectral Bandwidth': NDArray[float64] # shape(..., 1, frames)
141
140
  'Spectral Bandwidth mean': float
142
- 'Spectral centroid': NDArray[float64] # shape(channels, frames)
143
- 'Spectral centroid mean': float
144
141
  'Spectral Centroid': NDArray[float64] # shape(..., 1, frames)
145
142
  'Spectral Centroid mean': float
146
143
  'Spectral Contrast': NDArray[float64] # shape(..., 7, frames)
147
144
  'Spectral Contrast mean': float
145
+ 'Spectral Flatness': NDArray[float64] # shape(..., 1, frames)
146
+ 'Spectral Flatness mean': float
147
+ 'SRMR': NDArray[float64] # shape(...)
148
+ 'SRMR mean': float
149
+ 'Tempo': NDArray[float64] # shape(...)
150
+ 'Tempo mean': float
151
+ 'Tempogram': NDArray[float64] # shape(..., 384, samples)
152
+ 'Tempogram mean': float
153
+ 'Zero-crossing rate': NDArray[float64] # shape(..., 1, frames)
154
+ 'Zero-crossing rate mean': float
155
+ 'Zero-crossings rate': float
156
+ ```
157
+
158
+ ### I had to revert back to these
159
+
160
+ ```python
161
+ 'Spectral centroid': float
162
+ 'Spectral crest': float
163
+ 'Spectral decrease': float
164
+ 'Spectral entropy': float
165
+ 'Spectral flatness': float
166
+ 'Spectral flux': float
167
+ 'Spectral kurtosis': float
168
+ 'Spectral rolloff': float
169
+ 'Spectral skewness': float
170
+ 'Spectral slope': float
171
+ 'Spectral spread': float
172
+ 'Spectral variance': float
173
+ ```
174
+
175
+ ### Removed (temporarily, I hope)
176
+
177
+ ```python
178
+ 'Spectral centroid': NDArray[float64] # shape(channels, frames)
179
+ 'Spectral centroid mean': float
148
180
  'Spectral crest': NDArray[float64] # shape(channels, frames)
149
181
  'Spectral crest mean': float
150
182
  'Spectral decrease': NDArray[float64] # shape(channels, frames)
@@ -153,8 +185,6 @@ print(audioAspects['Chromagram']['analyzerParameters'])
153
185
  'Spectral entropy mean': float
154
186
  'Spectral flatness': NDArray[float64] # shape(channels, frames)
155
187
  'Spectral flatness mean': float
156
- 'Spectral Flatness': NDArray[float64] # shape(..., 1, frames)
157
- 'Spectral Flatness mean': float
158
188
  'Spectral flux': NDArray[float64] # shape(channels, frames)
159
189
  'Spectral flux mean': float
160
190
  'Spectral kurtosis': NDArray[float64] # shape(channels, frames)
@@ -169,15 +199,6 @@ print(audioAspects['Chromagram']['analyzerParameters'])
169
199
  'Spectral spread mean': float
170
200
  'Spectral variance': NDArray[float64] # shape(channels, frames)
171
201
  'Spectral variance mean': float
172
- 'SRMR': NDArray[float64] # shape(...)
173
- 'SRMR mean': float
174
- 'Tempo': NDArray[float64] # shape(...)
175
- 'Tempo mean': float
176
- 'Tempogram': NDArray[float64] # shape(..., 384, samples)
177
- 'Tempogram mean': float
178
- 'Zero-crossing rate': NDArray[float64] # shape(..., 1, frames)
179
- 'Zero-crossing rate mean': float
180
- 'Zero-crossings rate': float
181
202
  ```
182
203
 
183
204
  ## Installation
@@ -191,4 +212,13 @@ pip install analyzeAudio
191
212
  [![Static Badge](https://img.shields.io/badge/2011_August-Homeless_since-blue?style=flat)](https://HunterThinks.com/support)
192
213
  [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UC3Gx7kz61009NbhpRtPP7tw)](https://www.youtube.com/@HunterHogan)
193
214
 
194
- [![CC-BY-NC-4.0](https://github.com/hunterhogan/analyzeAudio/blob/main/CC-BY-NC-4.0.png)](https://creativecommons.org/licenses/by-nc/4.0/)
215
+ ## How to code
216
+
217
+ Coding One Step at a Time:
218
+
219
+ 0. WRITE CODE.
220
+ 1. Don't write stupid code that's hard to revise.
221
+ 2. Write good code.
222
+ 3. When revising, write better code.
223
+
224
+ [![CC-BY-NC-4.0](https://github.com/hunterhogan/analyzeAudio/blob/main/CC-BY-NC-4.0.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
@@ -88,12 +88,45 @@ print(audioAspects['Chromagram']['analyzerParameters'])
88
88
  'Signal entropy': float
89
89
  'Spectral Bandwidth': NDArray[float64] # shape(..., 1, frames)
90
90
  'Spectral Bandwidth mean': float
91
- 'Spectral centroid': NDArray[float64] # shape(channels, frames)
92
- 'Spectral centroid mean': float
93
91
  'Spectral Centroid': NDArray[float64] # shape(..., 1, frames)
94
92
  'Spectral Centroid mean': float
95
93
  'Spectral Contrast': NDArray[float64] # shape(..., 7, frames)
96
94
  'Spectral Contrast mean': float
95
+ 'Spectral Flatness': NDArray[float64] # shape(..., 1, frames)
96
+ 'Spectral Flatness mean': float
97
+ 'SRMR': NDArray[float64] # shape(...)
98
+ 'SRMR mean': float
99
+ 'Tempo': NDArray[float64] # shape(...)
100
+ 'Tempo mean': float
101
+ 'Tempogram': NDArray[float64] # shape(..., 384, samples)
102
+ 'Tempogram mean': float
103
+ 'Zero-crossing rate': NDArray[float64] # shape(..., 1, frames)
104
+ 'Zero-crossing rate mean': float
105
+ 'Zero-crossings rate': float
106
+ ```
107
+
108
+ ### I had to revert back to these
109
+
110
+ ```python
111
+ 'Spectral centroid': float
112
+ 'Spectral crest': float
113
+ 'Spectral decrease': float
114
+ 'Spectral entropy': float
115
+ 'Spectral flatness': float
116
+ 'Spectral flux': float
117
+ 'Spectral kurtosis': float
118
+ 'Spectral rolloff': float
119
+ 'Spectral skewness': float
120
+ 'Spectral slope': float
121
+ 'Spectral spread': float
122
+ 'Spectral variance': float
123
+ ```
124
+
125
+ ### Removed (temporarily, I hope)
126
+
127
+ ```python
128
+ 'Spectral centroid': NDArray[float64] # shape(channels, frames)
129
+ 'Spectral centroid mean': float
97
130
  'Spectral crest': NDArray[float64] # shape(channels, frames)
98
131
  'Spectral crest mean': float
99
132
  'Spectral decrease': NDArray[float64] # shape(channels, frames)
@@ -102,8 +135,6 @@ print(audioAspects['Chromagram']['analyzerParameters'])
102
135
  'Spectral entropy mean': float
103
136
  'Spectral flatness': NDArray[float64] # shape(channels, frames)
104
137
  'Spectral flatness mean': float
105
- 'Spectral Flatness': NDArray[float64] # shape(..., 1, frames)
106
- 'Spectral Flatness mean': float
107
138
  'Spectral flux': NDArray[float64] # shape(channels, frames)
108
139
  'Spectral flux mean': float
109
140
  'Spectral kurtosis': NDArray[float64] # shape(channels, frames)
@@ -118,15 +149,6 @@ print(audioAspects['Chromagram']['analyzerParameters'])
118
149
  'Spectral spread mean': float
119
150
  'Spectral variance': NDArray[float64] # shape(channels, frames)
120
151
  'Spectral variance mean': float
121
- 'SRMR': NDArray[float64] # shape(...)
122
- 'SRMR mean': float
123
- 'Tempo': NDArray[float64] # shape(...)
124
- 'Tempo mean': float
125
- 'Tempogram': NDArray[float64] # shape(..., 384, samples)
126
- 'Tempogram mean': float
127
- 'Zero-crossing rate': NDArray[float64] # shape(..., 1, frames)
128
- 'Zero-crossing rate mean': float
129
- 'Zero-crossings rate': float
130
152
  ```
131
153
 
132
154
  ## Installation
@@ -140,4 +162,13 @@ pip install analyzeAudio
140
162
  [![Static Badge](https://img.shields.io/badge/2011_August-Homeless_since-blue?style=flat)](https://HunterThinks.com/support)
141
163
  [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UC3Gx7kz61009NbhpRtPP7tw)](https://www.youtube.com/@HunterHogan)
142
164
 
143
- [![CC-BY-NC-4.0](https://github.com/hunterhogan/analyzeAudio/blob/main/CC-BY-NC-4.0.png)](https://creativecommons.org/licenses/by-nc/4.0/)
165
+ ## How to code
166
+
167
+ Coding One Step at a Time:
168
+
169
+ 0. WRITE CODE.
170
+ 1. Don't write stupid code that's hard to revise.
171
+ 2. Write good code.
172
+ 3. When revising, write better code.
173
+
174
+ [![CC-BY-NC-4.0](https://github.com/hunterhogan/analyzeAudio/blob/main/CC-BY-NC-4.0.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
@@ -1,4 +1,5 @@
1
- from .pythonator import pythonizeFFprobe
1
+ """Analyzers that use the filename of an audio file to analyze its audio data."""
2
+ from analyzeAudio.pythonator import pythonizeFFprobe
2
3
  from analyzeAudio import registrationAudioAspect, cacheAudioAnalyzers
3
4
  from os import PathLike
4
5
  from statistics import mean
@@ -40,7 +41,7 @@ def getSI_SDRmean(pathFilenameAlpha: str | PathLike[Any], pathFilenameBeta: str
40
41
  return SI_SDRmean
41
42
 
42
43
  @cachetools.cached(cache=cacheAudioAnalyzers)
43
- def ffprobeShotgunAndCache(pathFilename: str | PathLike[Any]) -> dict[str, float | numpy.ndarray]:
44
+ def ffprobeShotgunAndCache(pathFilename: str | PathLike[Any]) -> dict[str, float]:
44
45
  # for lavfi amovie/movie, the colons after driveLetter letters need to be escaped twice.
45
46
  pFn = pathlib.PureWindowsPath(pathFilename)
46
47
  lavfiPathFilename = pFn.drive.replace(":", "\\\\:")+pathlib.PureWindowsPath(pFn.root,pFn.relative_to(pFn.anchor)).as_posix()
@@ -63,14 +64,14 @@ def ffprobeShotgunAndCache(pathFilename: str | PathLike[Any]) -> dict[str, float
63
64
  stdoutFFprobe, _DISCARDstderr = systemProcessFFprobe.communicate()
64
65
  FFprobeStructured = pythonizeFFprobe(stdoutFFprobe.decode('utf-8'))[-1]
65
66
 
66
- dictionaryAspectsAnalyzed: dict[str, float | numpy.ndarray] = {}
67
+ dictionaryAspectsAnalyzed: dict[str, float] = {}
67
68
  if 'aspectralstats' in FFprobeStructured:
68
69
  for keyName in FFprobeStructured['aspectralstats']:
69
70
  # No matter how many channels, each keyName is `numpy.ndarray[tuple[int, int], numpy.dtype[numpy.float64]]`
70
71
  # where `tuple[int, int]` is (channel, frame)
71
72
  # NOTE (as of this writing) `registrar` can only understand the generic class `numpy.ndarray` and not more specific typing
72
- dictionaryAspectsAnalyzed[keyName] = FFprobeStructured['aspectralstats'][keyName]
73
- # dictionaryAspectsAnalyzed[keyName] = numpy.mean(FFprobeStructured['aspectralstats'][keyName]).astype(float)
73
+ # dictionaryAspectsAnalyzed[keyName] = FFprobeStructured['aspectralstats'][keyName]
74
+ dictionaryAspectsAnalyzed[keyName] = numpy.mean(FFprobeStructured['aspectralstats'][keyName]).astype(float)
74
75
  if 'r128' in FFprobeStructured:
75
76
  for keyName in FFprobeStructured['r128']:
76
77
  dictionaryAspectsAnalyzed[keyName] = FFprobeStructured['r128'][keyName][-1]
@@ -133,55 +134,55 @@ def analyzeLUFShigh(pathFilename: str | PathLike[Any]) -> float | None:
133
134
  return ffprobeShotgunAndCache(pathFilename).get('LRA.high')
134
135
 
135
136
  @registrationAudioAspect('Power spectral density')
136
- def analyzeMean(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
137
+ def analyzeMean(pathFilename: str | PathLike[Any]) -> float | None:
137
138
  return ffprobeShotgunAndCache(pathFilename).get('mean')
138
139
 
139
140
  @registrationAudioAspect('Spectral variance')
140
- def analyzeVariance(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
141
+ def analyzeVariance(pathFilename: str | PathLike[Any]) -> float | None:
141
142
  return ffprobeShotgunAndCache(pathFilename).get('variance')
142
143
 
143
144
  @registrationAudioAspect('Spectral centroid')
144
- def analyzeCentroid(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
145
+ def analyzeCentroid(pathFilename: str | PathLike[Any]) -> float | None:
145
146
  return ffprobeShotgunAndCache(pathFilename).get('centroid')
146
147
 
147
148
  @registrationAudioAspect('Spectral spread')
148
- def analyzeSpread(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
149
+ def analyzeSpread(pathFilename: str | PathLike[Any]) -> float | None:
149
150
  return ffprobeShotgunAndCache(pathFilename).get('spread')
150
151
 
151
152
  @registrationAudioAspect('Spectral skewness')
152
- def analyzeSkewness(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
153
+ def analyzeSkewness(pathFilename: str | PathLike[Any]) -> float | None:
153
154
  return ffprobeShotgunAndCache(pathFilename).get('skewness')
154
155
 
155
156
  @registrationAudioAspect('Spectral kurtosis')
156
- def analyzeKurtosis(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
157
+ def analyzeKurtosis(pathFilename: str | PathLike[Any]) -> float | None:
157
158
  return ffprobeShotgunAndCache(pathFilename).get('kurtosis')
158
159
 
159
160
  @registrationAudioAspect('Spectral entropy')
160
- def analyzeSpectralEntropy(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
161
+ def analyzeSpectralEntropy(pathFilename: str | PathLike[Any]) -> float | None:
161
162
  return ffprobeShotgunAndCache(pathFilename).get('entropy')
162
163
 
163
164
  @registrationAudioAspect('Spectral flatness')
164
- def analyzeFlatness(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
165
+ def analyzeFlatness(pathFilename: str | PathLike[Any]) -> float | None:
165
166
  return ffprobeShotgunAndCache(pathFilename).get('flatness')
166
167
 
167
168
  @registrationAudioAspect('Spectral crest')
168
- def analyzeCrest(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
169
+ def analyzeCrest(pathFilename: str | PathLike[Any]) -> float | None:
169
170
  return ffprobeShotgunAndCache(pathFilename).get('crest')
170
171
 
171
172
  @registrationAudioAspect('Spectral flux')
172
- def analyzeFlux(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
173
+ def analyzeFlux(pathFilename: str | PathLike[Any]) -> float | None:
173
174
  return ffprobeShotgunAndCache(pathFilename).get('flux')
174
175
 
175
176
  @registrationAudioAspect('Spectral slope')
176
- def analyzeSlope(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
177
+ def analyzeSlope(pathFilename: str | PathLike[Any]) -> float | None:
177
178
  return ffprobeShotgunAndCache(pathFilename).get('slope')
178
179
 
179
180
  @registrationAudioAspect('Spectral decrease')
180
- def analyzeDecrease(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
181
+ def analyzeDecrease(pathFilename: str | PathLike[Any]) -> float | None:
181
182
  return ffprobeShotgunAndCache(pathFilename).get('decrease')
182
183
 
183
184
  @registrationAudioAspect('Spectral rolloff')
184
- def analyzeRolloff(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
185
+ def analyzeRolloff(pathFilename: str | PathLike[Any]) -> float | None:
185
186
  return ffprobeShotgunAndCache(pathFilename).get('rolloff')
186
187
 
187
188
  @registrationAudioAspect('Abs_Peak_count')
@@ -1,30 +1,30 @@
1
+ """Analyzers that use the spectrogram to analyze audio data."""
1
2
  from analyzeAudio import registrationAudioAspect, audioAspects, cacheAudioAnalyzers
2
3
  from typing import Any
3
4
  import cachetools
4
5
  import librosa
5
6
  import numpy
6
- from optype.numpy import AnyFloatingDType, ToArray3D, ToFloat3D
7
7
  from numpy import dtype, floating
8
8
 
9
9
  @registrationAudioAspect('Chromagram')
10
- def analyzeChromagram(spectrogramPower: numpy.ndarray[Any, dtype[floating[Any]]], sampleRate: int, **keywordArguments: Any) -> numpy.ndarray:
11
- return librosa.feature.chroma_stft(S=spectrogramPower, sr=sampleRate, **keywordArguments)
10
+ def analyzeChromagram(spectrogramPower: numpy.ndarray[Any, dtype[floating[Any]]], sampleRate: int, **keywordArguments: Any) -> numpy.ndarray: # pyright: ignore [reportMissingTypeArgument, reportUnknownParameterType]
11
+ return librosa.feature.chroma_stft(S=spectrogramPower, sr=sampleRate, **keywordArguments) # type: ignore
12
12
 
13
13
  @registrationAudioAspect('Spectral Contrast')
14
- def analyzeSpectralContrast(spectrogramMagnitude: numpy.ndarray[Any, dtype[floating[Any]]], **keywordArguments: Any) -> numpy.ndarray:
15
- return librosa.feature.spectral_contrast(S=spectrogramMagnitude, **keywordArguments)
14
+ def analyzeSpectralContrast(spectrogramMagnitude: numpy.ndarray[Any, dtype[floating[Any]]], **keywordArguments: Any) -> numpy.ndarray: # pyright: ignore [reportMissingTypeArgument, reportUnknownParameterType]
15
+ return librosa.feature.spectral_contrast(S=spectrogramMagnitude, **keywordArguments) # type: ignore
16
16
 
17
17
  @registrationAudioAspect('Spectral Bandwidth')
18
- def analyzeSpectralBandwidth(spectrogramMagnitude: numpy.ndarray[Any, dtype[floating[Any]]], **keywordArguments: Any) -> numpy.ndarray:
18
+ def analyzeSpectralBandwidth(spectrogramMagnitude: numpy.ndarray[Any, dtype[floating[Any]]], **keywordArguments: Any) -> numpy.ndarray: # pyright: ignore [reportMissingTypeArgument, reportUnknownParameterType]
19
19
  centroid = audioAspects['Spectral Centroid']['analyzer'](spectrogramMagnitude)
20
- return librosa.feature.spectral_bandwidth(S=spectrogramMagnitude, centroid=centroid, **keywordArguments)
20
+ return librosa.feature.spectral_bandwidth(S=spectrogramMagnitude, centroid=centroid, **keywordArguments) # type: ignore
21
21
 
22
22
  @cachetools.cached(cache=cacheAudioAnalyzers)
23
23
  @registrationAudioAspect('Spectral Centroid')
24
- def analyzeSpectralCentroid(spectrogramMagnitude: numpy.ndarray[Any, dtype[floating[Any]]], **keywordArguments: Any) -> numpy.ndarray:
25
- return librosa.feature.spectral_centroid(S=spectrogramMagnitude, **keywordArguments)
24
+ def analyzeSpectralCentroid(spectrogramMagnitude: numpy.ndarray[Any, dtype[floating[Any]]], **keywordArguments: Any) -> numpy.ndarray: # pyright: ignore [reportMissingTypeArgument, reportUnknownParameterType]
25
+ return librosa.feature.spectral_centroid(S=spectrogramMagnitude, **keywordArguments) # type: ignore
26
26
 
27
27
  @registrationAudioAspect('Spectral Flatness')
28
- def analyzeSpectralFlatness(spectrogramMagnitude: numpy.ndarray[Any, dtype[floating[Any]]], **keywordArguments: Any) -> numpy.ndarray:
29
- spectralFlatness = librosa.feature.spectral_flatness(S=spectrogramMagnitude, **keywordArguments)
28
+ def analyzeSpectralFlatness(spectrogramMagnitude: numpy.ndarray[Any, dtype[floating[Any]]], **keywordArguments: Any) -> numpy.ndarray: # pyright: ignore [reportMissingTypeArgument, reportUnknownParameterType]
29
+ spectralFlatness: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.floating[Any]]] = librosa.feature.spectral_flatness(S=spectrogramMagnitude, **keywordArguments) # type: ignore
30
30
  return 20 * numpy.log10(spectralFlatness, where=(spectralFlatness != 0)) # dB
@@ -1,3 +1,4 @@
1
+ """Analyzers that use the tensor to analyze audio data."""
1
2
  from analyzeAudio import registrationAudioAspect
2
3
  from torchmetrics.functional.audio.srmr import speech_reverberation_modulation_energy_ratio
3
4
  from typing import Any
@@ -5,6 +6,6 @@ import numpy
5
6
  import torch
6
7
 
7
8
  @registrationAudioAspect('SRMR')
8
- def analyzeSRMR(tensorAudio: torch.Tensor, sampleRate: int, pytorchOnCPU: bool | None, **keywordArguments: Any) -> numpy.ndarray:
9
+ def analyzeSRMR(tensorAudio: torch.Tensor, sampleRate: int, pytorchOnCPU: bool | None, **keywordArguments: Any) -> numpy.ndarray: # pyright: ignore [reportMissingTypeArgument, reportUnknownParameterType]
9
10
  keywordArguments['fast'] = keywordArguments.get('fast') or pytorchOnCPU or None
10
- return torch.Tensor.numpy(speech_reverberation_modulation_energy_ratio(tensorAudio, sampleRate, **keywordArguments))
11
+ return torch.Tensor.numpy(speech_reverberation_modulation_energy_ratio(tensorAudio, sampleRate, **keywordArguments)) # type: ignore
@@ -0,0 +1,26 @@
1
+ """Analyzers that use the waveform of audio data."""
2
+ from analyzeAudio import registrationAudioAspect, audioAspects, cacheAudioAnalyzers
3
+ from typing import Any
4
+ import librosa
5
+ import numpy
6
+ import cachetools
7
+
8
+ @cachetools.cached(cache=cacheAudioAnalyzers)
9
+ @registrationAudioAspect('Tempogram')
10
+ def analyzeTempogram(waveform: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.floating[Any]]], sampleRate: int, **keywordArguments: Any) -> numpy.ndarray: # pyright: ignore [reportMissingTypeArgument, reportUnknownParameterType]
11
+ return librosa.feature.tempogram(y=waveform, sr=sampleRate, **keywordArguments) # type: ignore
12
+
13
+ # "RMS value from audio samples is faster ... However, ... spectrogram ... more accurate ... because ... windowed"
14
+ @registrationAudioAspect('RMS from waveform')
15
+ def analyzeRMS(waveform: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.floating[Any]]], **keywordArguments: Any) -> numpy.ndarray: # pyright: ignore [reportMissingTypeArgument, reportUnknownParameterType]
16
+ arrayRMS: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.floating[Any]]] = librosa.feature.rms(y=waveform, **keywordArguments) # type: ignore
17
+ return 20 * numpy.log10(arrayRMS, where=(arrayRMS != 0)) # dB
18
+
19
+ @registrationAudioAspect('Tempo')
20
+ def analyzeTempo(waveform: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.floating[Any]]], sampleRate: int, **keywordArguments: Any) -> numpy.ndarray: # pyright: ignore [reportMissingTypeArgument, reportUnknownParameterType]
21
+ tempogram = audioAspects['Tempogram']['analyzer'](waveform, sampleRate)
22
+ return librosa.feature.tempo(y=waveform, sr=sampleRate, tg=tempogram, **keywordArguments) # type: ignore
23
+
24
+ @registrationAudioAspect('Zero-crossing rate') # This is distinct from 'Zero-crossings rate'
25
+ def analyzeZeroCrossingRate(waveform: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.floating[Any]]], **keywordArguments: Any) -> numpy.ndarray: # pyright: ignore [reportMissingTypeArgument, reportUnknownParameterType]
26
+ return librosa.feature.zero_crossing_rate(y=waveform, **keywordArguments) # type: ignore
@@ -1,14 +1,12 @@
1
1
  from collections.abc import Callable, Sequence
2
2
  from concurrent.futures import ProcessPoolExecutor, as_completed
3
3
  from numpy.typing import NDArray
4
+ from os import PathLike
4
5
  from typing import Any, cast, ParamSpec, TypeAlias, TYPE_CHECKING, TypeVar
5
- from Z0Z_tools import defineConcurrencyLimit, oopsieKwargsie, stft
6
+ from Z0Z_tools import defineConcurrencyLimit, oopsieKwargsie, stft, Spectrogram
6
7
  import cachetools
7
8
  import inspect
8
- import librosa
9
- import multiprocessing
10
9
  import numpy
11
- from os import PathLike
12
10
  import pathlib
13
11
  import soundfile
14
12
  import torch
@@ -19,8 +17,11 @@ if TYPE_CHECKING:
19
17
  else:
20
18
  TypedDict = dict
21
19
 
22
- if __name__ == '__main__':
23
- multiprocessing.set_start_method('spawn')
20
+ from multiprocessing import set_start_method as multiprocessing_set_start_method
21
+ try:
22
+ multiprocessing_set_start_method('spawn')
23
+ except RuntimeError:
24
+ pass
24
25
 
25
26
  warnings.filterwarnings('ignore', category=UserWarning, module='torchmetrics', message='.*fast=True.*')
26
27
 
@@ -101,7 +102,7 @@ def analyzeAudioFile(pathFilename: str | PathLike[Any], listAspectNames: list[st
101
102
 
102
103
  # I need "lazy" loading
103
104
  tryAgain = True
104
- while tryAgain: # `tenacity`?
105
+ while tryAgain:
105
106
  try:
106
107
  tensorAudio = torch.from_numpy(waveform) # memory-sharing
107
108
  tryAgain = False
File without changes
@@ -1,3 +1,4 @@
1
+ """Convert FFprobe output to a standardized Python object."""
1
2
  from collections import defaultdict
2
3
  from typing import Any, cast, NamedTuple
3
4
  import json
@@ -89,7 +90,7 @@ def pythonizeFFprobe(FFprobeJSON_utf8: str):
89
90
  Z0Z_dictionaries[registrant] = {}
90
91
  elif statistic not in Z0Z_dictionaries[registrant]:
91
92
  # NOTE (as of this writing) `registrar` can only understand the generic class `numpy.ndarray` and not more specific typing
92
- valueSherpa = cast(numpy.ndarray, numpy.zeros((channel, len(FFroot['frames']))))
93
+ valueSherpa = cast(numpy.ndarray, numpy.zeros((channel, len(FFroot['frames'])))) # type: ignore
93
94
  Z0Z_dictionaries[registrant][statistic] = valueSherpa
94
95
  else:
95
96
  raise # Re-raise the exception
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: analyzeAudio
3
- Version: 0.0.14
3
+ Version: 0.0.16
4
4
  Summary: Measure one or more aspects of one or more audio files.
5
5
  Author-email: Hunter Hogan <HunterHogan@pm.me>
6
6
  License: CC-BY-NC-4.0
@@ -15,7 +15,6 @@ Classifier: Intended Audience :: End Users/Desktop
15
15
  Classifier: Intended Audience :: Science/Research
16
16
  Classifier: Intended Audience :: Information Technology
17
17
  Classifier: Intended Audience :: Other Audience
18
- Classifier: License :: Free for non-commercial use
19
18
  Classifier: Natural Language :: English
20
19
  Classifier: Operating System :: OS Independent
21
20
  Classifier: Programming Language :: Python
@@ -36,7 +35,6 @@ License-File: LICENSE
36
35
  Requires-Dist: cachetools
37
36
  Requires-Dist: librosa
38
37
  Requires-Dist: numpy
39
- Requires-Dist: optype[numpy]
40
38
  Requires-Dist: standard-aifc; python_version >= "3.13"
41
39
  Requires-Dist: standard-sunau; python_version >= "3.13"
42
40
  Requires-Dist: torch
@@ -44,10 +42,11 @@ Requires-Dist: torchmetrics[audio]
44
42
  Requires-Dist: tqdm
45
43
  Requires-Dist: Z0Z_tools
46
44
  Provides-Extra: testing
45
+ Requires-Dist: pytest; extra == "testing"
47
46
  Requires-Dist: pytest-cov; extra == "testing"
48
47
  Requires-Dist: pytest-xdist; extra == "testing"
49
- Requires-Dist: pytest; extra == "testing"
50
48
  Requires-Dist: pyupgrade; extra == "testing"
49
+ Dynamic: license-file
51
50
 
52
51
  # analyzeAudio
53
52
 
@@ -139,12 +138,45 @@ print(audioAspects['Chromagram']['analyzerParameters'])
139
138
  'Signal entropy': float
140
139
  'Spectral Bandwidth': NDArray[float64] # shape(..., 1, frames)
141
140
  'Spectral Bandwidth mean': float
142
- 'Spectral centroid': NDArray[float64] # shape(channels, frames)
143
- 'Spectral centroid mean': float
144
141
  'Spectral Centroid': NDArray[float64] # shape(..., 1, frames)
145
142
  'Spectral Centroid mean': float
146
143
  'Spectral Contrast': NDArray[float64] # shape(..., 7, frames)
147
144
  'Spectral Contrast mean': float
145
+ 'Spectral Flatness': NDArray[float64] # shape(..., 1, frames)
146
+ 'Spectral Flatness mean': float
147
+ 'SRMR': NDArray[float64] # shape(...)
148
+ 'SRMR mean': float
149
+ 'Tempo': NDArray[float64] # shape(...)
150
+ 'Tempo mean': float
151
+ 'Tempogram': NDArray[float64] # shape(..., 384, samples)
152
+ 'Tempogram mean': float
153
+ 'Zero-crossing rate': NDArray[float64] # shape(..., 1, frames)
154
+ 'Zero-crossing rate mean': float
155
+ 'Zero-crossings rate': float
156
+ ```
157
+
158
+ ### I had to revert back to these
159
+
160
+ ```python
161
+ 'Spectral centroid': float
162
+ 'Spectral crest': float
163
+ 'Spectral decrease': float
164
+ 'Spectral entropy': float
165
+ 'Spectral flatness': float
166
+ 'Spectral flux': float
167
+ 'Spectral kurtosis': float
168
+ 'Spectral rolloff': float
169
+ 'Spectral skewness': float
170
+ 'Spectral slope': float
171
+ 'Spectral spread': float
172
+ 'Spectral variance': float
173
+ ```
174
+
175
+ ### Removed (temporarily, I hope)
176
+
177
+ ```python
178
+ 'Spectral centroid': NDArray[float64] # shape(channels, frames)
179
+ 'Spectral centroid mean': float
148
180
  'Spectral crest': NDArray[float64] # shape(channels, frames)
149
181
  'Spectral crest mean': float
150
182
  'Spectral decrease': NDArray[float64] # shape(channels, frames)
@@ -153,8 +185,6 @@ print(audioAspects['Chromagram']['analyzerParameters'])
153
185
  'Spectral entropy mean': float
154
186
  'Spectral flatness': NDArray[float64] # shape(channels, frames)
155
187
  'Spectral flatness mean': float
156
- 'Spectral Flatness': NDArray[float64] # shape(..., 1, frames)
157
- 'Spectral Flatness mean': float
158
188
  'Spectral flux': NDArray[float64] # shape(channels, frames)
159
189
  'Spectral flux mean': float
160
190
  'Spectral kurtosis': NDArray[float64] # shape(channels, frames)
@@ -169,15 +199,6 @@ print(audioAspects['Chromagram']['analyzerParameters'])
169
199
  'Spectral spread mean': float
170
200
  'Spectral variance': NDArray[float64] # shape(channels, frames)
171
201
  'Spectral variance mean': float
172
- 'SRMR': NDArray[float64] # shape(...)
173
- 'SRMR mean': float
174
- 'Tempo': NDArray[float64] # shape(...)
175
- 'Tempo mean': float
176
- 'Tempogram': NDArray[float64] # shape(..., 384, samples)
177
- 'Tempogram mean': float
178
- 'Zero-crossing rate': NDArray[float64] # shape(..., 1, frames)
179
- 'Zero-crossing rate mean': float
180
- 'Zero-crossings rate': float
181
202
  ```
182
203
 
183
204
  ## Installation
@@ -191,4 +212,13 @@ pip install analyzeAudio
191
212
  [![Static Badge](https://img.shields.io/badge/2011_August-Homeless_since-blue?style=flat)](https://HunterThinks.com/support)
192
213
  [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UC3Gx7kz61009NbhpRtPP7tw)](https://www.youtube.com/@HunterHogan)
193
214
 
194
- [![CC-BY-NC-4.0](https://github.com/hunterhogan/analyzeAudio/blob/main/CC-BY-NC-4.0.png)](https://creativecommons.org/licenses/by-nc/4.0/)
215
+ ## How to code
216
+
217
+ Coding One Step at a Time:
218
+
219
+ 0. WRITE CODE.
220
+ 1. Don't write stupid code that's hard to revise.
221
+ 2. Write good code.
222
+ 3. When revising, write better code.
223
+
224
+ [![CC-BY-NC-4.0](https://github.com/hunterhogan/analyzeAudio/blob/main/CC-BY-NC-4.0.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
@@ -7,6 +7,7 @@ analyzeAudio/analyzersUseSpectrogram.py
7
7
  analyzeAudio/analyzersUseTensor.py
8
8
  analyzeAudio/analyzersUseWaveform.py
9
9
  analyzeAudio/audioAspectsRegistry.py
10
+ analyzeAudio/py.typed
10
11
  analyzeAudio/pythonator.py
11
12
  analyzeAudio.egg-info/PKG-INFO
12
13
  analyzeAudio.egg-info/SOURCES.txt
@@ -1,7 +1,6 @@
1
1
  cachetools
2
2
  librosa
3
3
  numpy
4
- optype[numpy]
5
4
  torch
6
5
  torchmetrics[audio]
7
6
  tqdm
@@ -12,7 +11,7 @@ standard-aifc
12
11
  standard-sunau
13
12
 
14
13
  [testing]
14
+ pytest
15
15
  pytest-cov
16
16
  pytest-xdist
17
- pytest
18
17
  pyupgrade
@@ -12,7 +12,6 @@ classifiers = [
12
12
  "Intended Audience :: Science/Research",
13
13
  "Intended Audience :: Information Technology",
14
14
  "Intended Audience :: Other Audience",
15
- "License :: Free for non-commercial use",
16
15
  "Natural Language :: English",
17
16
  "Operating System :: OS Independent",
18
17
  "Programming Language :: Python",
@@ -31,7 +30,6 @@ dependencies = [
31
30
  "cachetools",
32
31
  "librosa",
33
32
  "numpy",
34
- "optype[numpy]",
35
33
  "standard-aifc;python_version>='3.13'",
36
34
  "standard-sunau;python_version>='3.13'",
37
35
  "torch",
@@ -43,14 +41,14 @@ keywords = ["audio", "analysis", "measurement", "metrics", "torch", "spectrum",
43
41
  license = { 'text' = "CC-BY-NC-4.0" }
44
42
  name = "analyzeAudio"
45
43
  optional-dependencies = { testing = [
44
+ "pytest",
46
45
  "pytest-cov",
47
46
  "pytest-xdist",
48
- "pytest",
49
47
  "pyupgrade",] }
50
48
  readme = { file = "README.md", content-type = "text/markdown" }
51
49
  requires-python = ">=3.10"
52
50
  urls = { Donate = "https://www.patreon.com/integrated", Homepage = "https://github.com/hunterhogan/analyzeAudio", Repository = "https://github.com/hunterhogan/analyzeAudio.git" }
53
- version = "0.0.14"
51
+ version = "0.0.16"
54
52
 
55
53
  [project.scripts]
56
54
  whatMeasurements = "analyzeAudio.audioAspectsRegistry:getListAvailableAudioAspects"
@@ -69,12 +67,13 @@ run = { branch = true, concurrency = [
69
67
  xml = { output = "tests/coverage/coverage.xml" }
70
68
 
71
69
  [tool.pytest.ini_options]
72
- log_auto_indent = ["On"]
73
- addopts = ["--color=yes", "-n 4"]
70
+ log_auto_indent = true
71
+ addopts = ["--color=auto", "-n 4"]
74
72
  testpaths = ["tests"]
75
73
 
76
- [tool.setuptools.packages.find]
77
- where = ["."]
74
+ [tool.setuptools]
75
+ package-data = { "*" = ["py.typed"] }
76
+ packages = { find = {} }
78
77
 
79
78
  [tool.updateCitation]
80
79
  filenameCitationDOTcff = 'CITATION.cff'
@@ -1,26 +0,0 @@
1
- from analyzeAudio import registrationAudioAspect, audioAspects, cacheAudioAnalyzers
2
- from typing import Any
3
- import librosa
4
- import numpy
5
- from optype.numpy import ToArray2D, AnyFloatingDType
6
- import cachetools
7
-
8
- @cachetools.cached(cache=cacheAudioAnalyzers)
9
- @registrationAudioAspect('Tempogram')
10
- def analyzeTempogram(waveform: ToArray2D[AnyFloatingDType], sampleRate: int, **keywordArguments: Any) -> numpy.ndarray:
11
- return librosa.feature.tempogram(y=waveform, sr=sampleRate, **keywordArguments)
12
-
13
- # "RMS value from audio samples is faster ... However, ... spectrogram ... more accurate ... because ... windowed"
14
- @registrationAudioAspect('RMS from waveform')
15
- def analyzeRMS(waveform: ToArray2D[AnyFloatingDType], **keywordArguments: Any) -> numpy.ndarray:
16
- arrayRMS = librosa.feature.rms(y=waveform, **keywordArguments)
17
- return 20 * numpy.log10(arrayRMS, where=(arrayRMS != 0)) # dB
18
-
19
- @registrationAudioAspect('Tempo')
20
- def analyzeTempo(waveform: ToArray2D[AnyFloatingDType], sampleRate: int, **keywordArguments: Any) -> numpy.ndarray:
21
- tempogram = audioAspects['Tempogram']['analyzer'](waveform, sampleRate)
22
- return librosa.feature.tempo(y=waveform, sr=sampleRate, tg=tempogram, **keywordArguments)
23
-
24
- @registrationAudioAspect('Zero-crossing rate') # This is distinct from 'Zero-crossings rate'
25
- def analyzeZeroCrossingRate(waveform: ToArray2D[AnyFloatingDType], **keywordArguments: Any) -> numpy.ndarray:
26
- return librosa.feature.zero_crossing_rate(y=waveform, **keywordArguments)
File without changes
File without changes