analyzeAudio 0.0.12__tar.gz → 0.0.13__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 (22) hide show
  1. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/PKG-INFO +81 -3
  2. analyzeaudio-0.0.13/README.md +143 -0
  3. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio/analyzersUseFilename.py +22 -18
  4. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio/pythonator.py +42 -7
  5. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio.egg-info/PKG-INFO +81 -3
  6. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/pyproject.toml +1 -1
  7. analyzeaudio-0.0.12/README.md +0 -65
  8. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/LICENSE +0 -0
  9. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio/__init__.py +0 -0
  10. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio/analyzersUseSpectrogram.py +0 -0
  11. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio/analyzersUseTensor.py +0 -0
  12. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio/analyzersUseWaveform.py +0 -0
  13. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio/audioAspectsRegistry.py +0 -0
  14. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio.egg-info/SOURCES.txt +0 -0
  15. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio.egg-info/dependency_links.txt +0 -0
  16. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio.egg-info/entry_points.txt +0 -0
  17. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio.egg-info/requires.txt +0 -0
  18. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/analyzeAudio.egg-info/top_level.txt +0 -0
  19. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/setup.cfg +0 -0
  20. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/tests/conftest.py +0 -0
  21. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/tests/test_audioAspectsRegistry.py +0 -0
  22. {analyzeaudio-0.0.12 → analyzeaudio-0.0.13}/tests/test_other.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: analyzeAudio
3
- Version: 0.0.12
3
+ Version: 0.0.13
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
@@ -85,8 +85,86 @@ print(audioAspects['Chromagram']['analyzerParameters'])
85
85
  ### Use `whatMeasurements` command line tool to list available measurements
86
86
 
87
87
  ```sh
88
- (.venv) C:\apps\analyzeAudio> whatMeasurements
89
- ['Abs_Peak_count', 'Bit_depth', 'Chromagram', 'Chromagram mean', 'Crest factor', 'DC offset', 'Duration-samples', 'Dynamic range', 'Flat_factor', 'LUFS high', 'LUFS integrated', 'LUFS loudness range', 'LUFS low', 'Max_difference', 'Max_level', 'Mean_difference', 'Min_difference', 'Min_level', 'Noise_floor', 'Noise_floor_count', 'Peak dB', 'Peak_count', 'RMS from waveform', 'RMS from waveform mean', 'RMS peak', 'RMS total', 'RMS_difference', 'RMS_trough', 'SI-SDR mean', 'SRMR', 'SRMR mean', 'Signal entropy', 'Spectral Bandwidth', 'Spectral Bandwidth mean', 'Spectral Centroid', 'Spectral Centroid mean', 'Spectral Contrast', 'Spectral Contrast mean', 'Spectral Flatness', 'Spectral Flatness mean', 'Spectral centroid', 'Spectral crest', 'Spectral decrease', 'Spectral entropy', 'Spectral flatness', 'Spectral flux', 'Spectral kurtosis', 'Spectral mean', 'Spectral rolloff', 'Spectral skewness', 'Spectral slope', 'Spectral spread', 'Spectral variance', 'Tempo', 'Tempo mean', 'Tempogram', 'Tempogram mean', 'Zero-crossing rate', 'Zero-crossing rate mean', 'Zero-crossings rate']
88
+ (.venv) C:\apps\analyzeAudio>whatMeasurements
89
+ ['Abs_Peak_count', 'Bit_depth', 'Chromagram', 'Chromagram mean', 'Crest factor', 'DC offset', 'Duration-samples', 'Dynamic range', 'Flat_factor', 'LUFS high', 'LUFS integrated', 'LUFS loudness range', 'LUFS low', 'Max_difference', 'Max_level', 'Mean_difference', 'Min_difference', 'Min_level', 'Noise_floor', 'Noise_floor_count', 'Peak dB', 'Peak_count', 'Power spectral density', 'Power spectral density mean', 'RMS from waveform', 'RMS from waveform mean', 'RMS peak', 'RMS total', 'RMS_difference', 'RMS_trough', 'SI-SDR mean', 'SRMR', 'SRMR mean', 'Signal entropy', 'Spectral Bandwidth', 'Spectral Bandwidth mean', 'Spectral Centroid', 'Spectral Centroid mean', 'Spectral Contrast', 'Spectral Contrast mean', 'Spectral Flatness', 'Spectral Flatness mean', 'Spectral centroid', 'Spectral centroid mean', 'Spectral crest', 'Spectral crest mean', 'Spectral decrease', 'Spectral decrease mean', 'Spectral entropy', 'Spectral entropy mean', 'Spectral flatness', 'Spectral flatness mean', 'Spectral flux', 'Spectral flux mean', 'Spectral kurtosis', 'Spectral kurtosis mean', 'Spectral rolloff', 'Spectral rolloff mean', 'Spectral skewness', 'Spectral skewness mean', 'Spectral slope', 'Spectral slope mean', 'Spectral spread', 'Spectral spread mean', 'Spectral variance', 'Spectral variance mean', 'Tempo', 'Tempo mean', 'Tempogram', 'Tempogram mean', 'Zero-crossing rate', 'Zero-crossing rate mean', 'Zero-crossings rate']
90
+ ```
91
+
92
+ ## Some clues about the aspects
93
+
94
+ ```python
95
+ 'Abs_Peak_count': float
96
+ 'Bit_depth': float
97
+ 'Chromagram': NDArray[float64] # shape(..., 12, frames)
98
+ 'Chromagram mean': float
99
+ 'Crest factor': float
100
+ 'DC offset': float
101
+ 'Duration-samples': float
102
+ 'Dynamic range': float
103
+ 'Flat_factor': float
104
+ 'LUFS high': float
105
+ 'LUFS integrated': float
106
+ 'LUFS loudness range': float
107
+ 'LUFS low': float
108
+ 'Max_difference': float
109
+ 'Max_level': float
110
+ 'Mean_difference': float
111
+ 'Min_difference': float
112
+ 'Min_level': float
113
+ 'Noise_floor_count': float
114
+ 'Noise_floor': float
115
+ 'Peak dB': float
116
+ 'Peak_count': float
117
+ 'Power spectral density': NDArray[float64] # shape(channels, frames)
118
+ 'Power spectral density mean': float
119
+ 'RMS from waveform': NDArray[float64] # shape(..., 1, frames)
120
+ 'RMS from waveform mean': float
121
+ 'RMS peak': float
122
+ 'RMS total': float
123
+ 'RMS_difference': float
124
+ 'RMS_trough': float
125
+ 'SI-SDR mean': float
126
+ 'Signal entropy': float
127
+ 'Spectral Bandwidth': NDArray[float64] # shape(..., 1, frames)
128
+ 'Spectral Bandwidth mean': float
129
+ 'Spectral centroid': NDArray[float64] # shape(channels, frames)
130
+ 'Spectral centroid mean': float
131
+ 'Spectral Centroid': NDArray[float64] # shape(..., 1, frames)
132
+ 'Spectral Centroid mean': float
133
+ 'Spectral Contrast': NDArray[float64] # shape(..., 7, frames)
134
+ 'Spectral Contrast mean': float
135
+ 'Spectral crest': NDArray[float64] # shape(channels, frames)
136
+ 'Spectral crest mean': float
137
+ 'Spectral decrease': NDArray[float64] # shape(channels, frames)
138
+ 'Spectral decrease mean': float
139
+ 'Spectral entropy': NDArray[float64] # shape(channels, frames)
140
+ 'Spectral entropy mean': float
141
+ 'Spectral flatness': NDArray[float64] # shape(channels, frames)
142
+ 'Spectral flatness mean': float
143
+ 'Spectral Flatness': NDArray[float64] # shape(..., 1, frames)
144
+ 'Spectral Flatness mean': float
145
+ 'Spectral flux': NDArray[float64] # shape(channels, frames)
146
+ 'Spectral flux mean': float
147
+ 'Spectral kurtosis': NDArray[float64] # shape(channels, frames)
148
+ 'Spectral kurtosis mean': float
149
+ 'Spectral rolloff': NDArray[float64] # shape(channels, frames)
150
+ 'Spectral rolloff mean': float
151
+ 'Spectral skewness': NDArray[float64] # shape(channels, frames)
152
+ 'Spectral skewness mean': float
153
+ 'Spectral slope': NDArray[float64] # shape(channels, frames)
154
+ 'Spectral slope mean': float
155
+ 'Spectral spread': NDArray[float64] # shape(channels, frames)
156
+ 'Spectral spread mean': float
157
+ 'Spectral variance': NDArray[float64] # shape(channels, frames)
158
+ 'Spectral variance mean': float
159
+ 'SRMR': NDArray[float64] # shape(...)
160
+ 'SRMR mean': float
161
+ 'Tempo': NDArray[float64] # shape(...)
162
+ 'Tempo mean': float
163
+ 'Tempogram': NDArray[float64] # shape(..., 384, samples)
164
+ 'Tempogram mean': float
165
+ 'Zero-crossing rate': NDArray[float64] # shape(..., 1, frames)
166
+ 'Zero-crossing rate mean': float
167
+ 'Zero-crossings rate': float
90
168
  ```
91
169
 
92
170
  ## Installation
@@ -0,0 +1,143 @@
1
+ # analyzeAudio
2
+
3
+ Measure one or more aspects of one or more audio files.
4
+
5
+ ## Note well: FFmpeg & FFprobe binaries must be in PATH
6
+
7
+ Some options to [download FFmpeg and FFprobe](https://www.ffmpeg.org/download.html) at ffmpeg.org.
8
+
9
+ ## Some ways to use this package
10
+
11
+ ### Use `analyzeAudioFile` to measure one or more aspects of a single audio file
12
+
13
+ ```python
14
+ from analyzeAudio import analyzeAudioFile
15
+ listAspectNames = ['LUFS integrated',
16
+ 'RMS peak',
17
+ 'SRMR mean',
18
+ 'Spectral Flatness mean']
19
+ listMeasurements = analyzeAudioFile(pathFilename, listAspectNames)
20
+ ```
21
+
22
+ ### Use `getListAvailableAudioAspects` to get a crude list of aspects this package can measure
23
+
24
+ The aspect names are accurate, but the lack of additional documentation can make things challenging. 'Zero-crossing rate', 'Zero-crossing rate mean', and 'Zero-crossings rate', for example, are different from each other. ("... lack of additional documentation ...")
25
+
26
+ ```python
27
+ import analyzeAudio
28
+ analyzeAudio.getListAvailableAudioAspects()
29
+ ```
30
+
31
+ ### Use `analyzeAudioListPathFilenames` to measure one or more aspects of individual file in a list of audio files
32
+
33
+ ### Use `audioAspects` to call an analyzer function by using the name of the aspect you wish to measure
34
+
35
+ ```python
36
+ from analyzeAudio import audioAspects
37
+ SI_SDR_channelsMean = audioAspects['SI-SDR mean']['analyzer'](pathFilenameAudioFile, pathFilenameDifferentAudioFile)
38
+ ```
39
+
40
+ Retrieve the names of the parameters for an analyzer function with the `['analyzerParameters']` key-name.
41
+
42
+ ```python
43
+ from analyzeAudio import audioAspects
44
+ print(audioAspects['Chromagram']['analyzerParameters'])
45
+ ```
46
+
47
+ ### Use `whatMeasurements` command line tool to list available measurements
48
+
49
+ ```sh
50
+ (.venv) C:\apps\analyzeAudio>whatMeasurements
51
+ ['Abs_Peak_count', 'Bit_depth', 'Chromagram', 'Chromagram mean', 'Crest factor', 'DC offset', 'Duration-samples', 'Dynamic range', 'Flat_factor', 'LUFS high', 'LUFS integrated', 'LUFS loudness range', 'LUFS low', 'Max_difference', 'Max_level', 'Mean_difference', 'Min_difference', 'Min_level', 'Noise_floor', 'Noise_floor_count', 'Peak dB', 'Peak_count', 'Power spectral density', 'Power spectral density mean', 'RMS from waveform', 'RMS from waveform mean', 'RMS peak', 'RMS total', 'RMS_difference', 'RMS_trough', 'SI-SDR mean', 'SRMR', 'SRMR mean', 'Signal entropy', 'Spectral Bandwidth', 'Spectral Bandwidth mean', 'Spectral Centroid', 'Spectral Centroid mean', 'Spectral Contrast', 'Spectral Contrast mean', 'Spectral Flatness', 'Spectral Flatness mean', 'Spectral centroid', 'Spectral centroid mean', 'Spectral crest', 'Spectral crest mean', 'Spectral decrease', 'Spectral decrease mean', 'Spectral entropy', 'Spectral entropy mean', 'Spectral flatness', 'Spectral flatness mean', 'Spectral flux', 'Spectral flux mean', 'Spectral kurtosis', 'Spectral kurtosis mean', 'Spectral rolloff', 'Spectral rolloff mean', 'Spectral skewness', 'Spectral skewness mean', 'Spectral slope', 'Spectral slope mean', 'Spectral spread', 'Spectral spread mean', 'Spectral variance', 'Spectral variance mean', 'Tempo', 'Tempo mean', 'Tempogram', 'Tempogram mean', 'Zero-crossing rate', 'Zero-crossing rate mean', 'Zero-crossings rate']
52
+ ```
53
+
54
+ ## Some clues about the aspects
55
+
56
+ ```python
57
+ 'Abs_Peak_count': float
58
+ 'Bit_depth': float
59
+ 'Chromagram': NDArray[float64] # shape(..., 12, frames)
60
+ 'Chromagram mean': float
61
+ 'Crest factor': float
62
+ 'DC offset': float
63
+ 'Duration-samples': float
64
+ 'Dynamic range': float
65
+ 'Flat_factor': float
66
+ 'LUFS high': float
67
+ 'LUFS integrated': float
68
+ 'LUFS loudness range': float
69
+ 'LUFS low': float
70
+ 'Max_difference': float
71
+ 'Max_level': float
72
+ 'Mean_difference': float
73
+ 'Min_difference': float
74
+ 'Min_level': float
75
+ 'Noise_floor_count': float
76
+ 'Noise_floor': float
77
+ 'Peak dB': float
78
+ 'Peak_count': float
79
+ 'Power spectral density': NDArray[float64] # shape(channels, frames)
80
+ 'Power spectral density mean': float
81
+ 'RMS from waveform': NDArray[float64] # shape(..., 1, frames)
82
+ 'RMS from waveform mean': float
83
+ 'RMS peak': float
84
+ 'RMS total': float
85
+ 'RMS_difference': float
86
+ 'RMS_trough': float
87
+ 'SI-SDR mean': float
88
+ 'Signal entropy': float
89
+ 'Spectral Bandwidth': NDArray[float64] # shape(..., 1, frames)
90
+ 'Spectral Bandwidth mean': float
91
+ 'Spectral centroid': NDArray[float64] # shape(channels, frames)
92
+ 'Spectral centroid mean': float
93
+ 'Spectral Centroid': NDArray[float64] # shape(..., 1, frames)
94
+ 'Spectral Centroid mean': float
95
+ 'Spectral Contrast': NDArray[float64] # shape(..., 7, frames)
96
+ 'Spectral Contrast mean': float
97
+ 'Spectral crest': NDArray[float64] # shape(channels, frames)
98
+ 'Spectral crest mean': float
99
+ 'Spectral decrease': NDArray[float64] # shape(channels, frames)
100
+ 'Spectral decrease mean': float
101
+ 'Spectral entropy': NDArray[float64] # shape(channels, frames)
102
+ 'Spectral entropy mean': float
103
+ 'Spectral flatness': NDArray[float64] # shape(channels, frames)
104
+ 'Spectral flatness mean': float
105
+ 'Spectral Flatness': NDArray[float64] # shape(..., 1, frames)
106
+ 'Spectral Flatness mean': float
107
+ 'Spectral flux': NDArray[float64] # shape(channels, frames)
108
+ 'Spectral flux mean': float
109
+ 'Spectral kurtosis': NDArray[float64] # shape(channels, frames)
110
+ 'Spectral kurtosis mean': float
111
+ 'Spectral rolloff': NDArray[float64] # shape(channels, frames)
112
+ 'Spectral rolloff mean': float
113
+ 'Spectral skewness': NDArray[float64] # shape(channels, frames)
114
+ 'Spectral skewness mean': float
115
+ 'Spectral slope': NDArray[float64] # shape(channels, frames)
116
+ 'Spectral slope mean': float
117
+ 'Spectral spread': NDArray[float64] # shape(channels, frames)
118
+ 'Spectral spread mean': float
119
+ 'Spectral variance': NDArray[float64] # shape(channels, frames)
120
+ '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
+ ```
131
+
132
+ ## Installation
133
+
134
+ ```sh
135
+ pip install analyzeAudio
136
+ ```
137
+
138
+ ## My recovery
139
+
140
+ [![Static Badge](https://img.shields.io/badge/2011_August-Homeless_since-blue?style=flat)](https://HunterThinks.com/support)
141
+ [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UC3Gx7kz61009NbhpRtPP7tw)](https://www.youtube.com/@HunterHogan)
142
+
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/)
@@ -40,7 +40,7 @@ def getSI_SDRmean(pathFilenameAlpha: str | PathLike[Any], pathFilenameBeta: str
40
40
  return SI_SDRmean
41
41
 
42
42
  @cachetools.cached(cache=cacheAudioAnalyzers)
43
- def ffprobeShotgunAndCache(pathFilename: str | PathLike[Any]) -> dict[str, float]:
43
+ def ffprobeShotgunAndCache(pathFilename: str | PathLike[Any]) -> dict[str, float | numpy.ndarray]:
44
44
  # for lavfi amovie/movie, the colons after driveLetter letters need to be escaped twice.
45
45
  pFn = pathlib.PureWindowsPath(pathFilename)
46
46
  lavfiPathFilename = pFn.drive.replace(":", "\\\\:")+pathlib.PureWindowsPath(pFn.root,pFn.relative_to(pFn.anchor)).as_posix()
@@ -63,16 +63,20 @@ def ffprobeShotgunAndCache(pathFilename: str | PathLike[Any]) -> dict[str, float
63
63
  stdoutFFprobe, _DISCARDstderr = systemProcessFFprobe.communicate()
64
64
  FFprobeStructured = pythonizeFFprobe(stdoutFFprobe.decode('utf-8'))[-1]
65
65
 
66
- dictionaryAspectsAnalyzed: dict[str, float] = {}
66
+ dictionaryAspectsAnalyzed: dict[str, float | numpy.ndarray] = {}
67
67
  if 'aspectralstats' in FFprobeStructured:
68
68
  for keyName in FFprobeStructured['aspectralstats']:
69
- dictionaryAspectsAnalyzed[keyName] = numpy.mean(FFprobeStructured['aspectralstats'][keyName]).astype(float)
69
+ # No matter how many channels, each keyName is `numpy.ndarray[tuple[int, int], numpy.dtype[numpy.float64]]`
70
+ # where `tuple[int, int]` is (channel, frame)
71
+ # 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)
70
74
  if 'r128' in FFprobeStructured:
71
75
  for keyName in FFprobeStructured['r128']:
72
76
  dictionaryAspectsAnalyzed[keyName] = FFprobeStructured['r128'][keyName][-1]
73
77
  if 'astats' in FFprobeStructured:
74
78
  for keyName, arrayFeatureValues in cast(dict[str, numpy.ndarray[Any, Any]], FFprobeStructured['astats']).items():
75
- dictionaryAspectsAnalyzed[keyName.split('.')[-1]] = numpy.mean(arrayFeatureValues[..., -1:]).astype(float)
79
+ dictionaryAspectsAnalyzed[keyName.split('.')[-1]] = numpy.mean(arrayFeatureValues[..., -1:None]).astype(float)
76
80
 
77
81
  return dictionaryAspectsAnalyzed
78
82
 
@@ -128,56 +132,56 @@ def analyzeLUFSlow(pathFilename: str | PathLike[Any]) -> float | None:
128
132
  def analyzeLUFShigh(pathFilename: str | PathLike[Any]) -> float | None:
129
133
  return ffprobeShotgunAndCache(pathFilename).get('LRA.high')
130
134
 
131
- @registrationAudioAspect('Spectral mean')
132
- def analyzeMean(pathFilename: str | PathLike[Any]) -> float | None:
135
+ @registrationAudioAspect('Power spectral density')
136
+ def analyzeMean(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
133
137
  return ffprobeShotgunAndCache(pathFilename).get('mean')
134
138
 
135
139
  @registrationAudioAspect('Spectral variance')
136
- def analyzeVariance(pathFilename: str | PathLike[Any]) -> float | None:
140
+ def analyzeVariance(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
137
141
  return ffprobeShotgunAndCache(pathFilename).get('variance')
138
142
 
139
143
  @registrationAudioAspect('Spectral centroid')
140
- def analyzeCentroid(pathFilename: str | PathLike[Any]) -> float | None:
144
+ def analyzeCentroid(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
141
145
  return ffprobeShotgunAndCache(pathFilename).get('centroid')
142
146
 
143
147
  @registrationAudioAspect('Spectral spread')
144
- def analyzeSpread(pathFilename: str | PathLike[Any]) -> float | None:
148
+ def analyzeSpread(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
145
149
  return ffprobeShotgunAndCache(pathFilename).get('spread')
146
150
 
147
151
  @registrationAudioAspect('Spectral skewness')
148
- def analyzeSkewness(pathFilename: str | PathLike[Any]) -> float | None:
152
+ def analyzeSkewness(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
149
153
  return ffprobeShotgunAndCache(pathFilename).get('skewness')
150
154
 
151
155
  @registrationAudioAspect('Spectral kurtosis')
152
- def analyzeKurtosis(pathFilename: str | PathLike[Any]) -> float | None:
156
+ def analyzeKurtosis(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
153
157
  return ffprobeShotgunAndCache(pathFilename).get('kurtosis')
154
158
 
155
159
  @registrationAudioAspect('Spectral entropy')
156
- def analyzeSpectralEntropy(pathFilename: str | PathLike[Any]) -> float | None:
160
+ def analyzeSpectralEntropy(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
157
161
  return ffprobeShotgunAndCache(pathFilename).get('entropy')
158
162
 
159
163
  @registrationAudioAspect('Spectral flatness')
160
- def analyzeFlatness(pathFilename: str | PathLike[Any]) -> float | None:
164
+ def analyzeFlatness(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
161
165
  return ffprobeShotgunAndCache(pathFilename).get('flatness')
162
166
 
163
167
  @registrationAudioAspect('Spectral crest')
164
- def analyzeCrest(pathFilename: str | PathLike[Any]) -> float | None:
168
+ def analyzeCrest(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
165
169
  return ffprobeShotgunAndCache(pathFilename).get('crest')
166
170
 
167
171
  @registrationAudioAspect('Spectral flux')
168
- def analyzeFlux(pathFilename: str | PathLike[Any]) -> float | None:
172
+ def analyzeFlux(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
169
173
  return ffprobeShotgunAndCache(pathFilename).get('flux')
170
174
 
171
175
  @registrationAudioAspect('Spectral slope')
172
- def analyzeSlope(pathFilename: str | PathLike[Any]) -> float | None:
176
+ def analyzeSlope(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
173
177
  return ffprobeShotgunAndCache(pathFilename).get('slope')
174
178
 
175
179
  @registrationAudioAspect('Spectral decrease')
176
- def analyzeDecrease(pathFilename: str | PathLike[Any]) -> float | None:
180
+ def analyzeDecrease(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
177
181
  return ffprobeShotgunAndCache(pathFilename).get('decrease')
178
182
 
179
183
  @registrationAudioAspect('Spectral rolloff')
180
- def analyzeRolloff(pathFilename: str | PathLike[Any]) -> float | None:
184
+ def analyzeRolloff(pathFilename: str | PathLike[Any]) -> numpy.ndarray:
181
185
  return ffprobeShotgunAndCache(pathFilename).get('rolloff')
182
186
 
183
187
  @registrationAudioAspect('Abs_Peak_count')
@@ -1,8 +1,20 @@
1
1
  from collections import defaultdict
2
- from typing import Any
2
+ from typing import Any, cast, NamedTuple
3
3
  import json
4
4
  import numpy
5
5
 
6
+ # NOTE hey! hey! hey!
7
+ # Is blackdetect broken?
8
+ # 1. You don't have pytest tests for anything in the entire fricken package
9
+ # 2. You tried to improve the blackdetect code, but you didn't test it with anything
10
+ # 3. Search for "uncommentToFixBlackdetect"
11
+ # NOTE You changed the code because a static type checker was mad at you. Ask yourself,
12
+ # "Are you the tool or is the type checker the tool?"
13
+
14
+ class Blackdetect(NamedTuple):
15
+ black_start: float | None = None
16
+ black_end: float | None = None
17
+
6
18
  def pythonizeFFprobe(FFprobeJSON_utf8: str):
7
19
  FFroot: dict[str, Any] = json.loads(FFprobeJSON_utf8)
8
20
  Z0Z_dictionaries: dict[str, numpy.ndarray[Any, Any] | dict[str, numpy.ndarray[Any, Any]]] = {}
@@ -25,14 +37,24 @@ def pythonizeFFprobe(FFprobeJSON_utf8: str):
25
37
  leftCrumbs = False
26
38
  if 'frames' in FFroot:
27
39
  leftCrumbs = False
28
- listTuplesBlackdetect: list[float | tuple[float]] = []
40
+ # listTuplesBlackdetect = [] # uncommentToFixBlackdetect
41
+ listTuplesBlackdetect: list[Blackdetect] = []
29
42
  for indexFrame, FFframe in enumerate(FFroot['frames']):
30
43
  if 'tags' in FFframe:
31
44
  if 'lavfi.black_start' in FFframe['tags']:
32
- listTuplesBlackdetect.append(float(FFframe['tags']['lavfi.black_start']))
45
+ # listTuplesBlackdetect.append(float(FFframe['tags']['lavfi.black_start'])) # uncommentToFixBlackdetect
46
+ listTuplesBlackdetect.append(Blackdetect(black_start=float(FFframe['tags']['lavfi.black_start'])))
33
47
  del FFframe['tags']['lavfi.black_start']
34
48
  if 'lavfi.black_end' in FFframe['tags']:
35
- listTuplesBlackdetect[-1] = (listTuplesBlackdetect[-1], float(FFframe['tags']['lavfi.black_end']))
49
+ # listTuplesBlackdetect[-1] = (listTuplesBlackdetect[-1], float(FFframe['tags']['lavfi.black_end'])) # uncommentToFixBlackdetect
50
+ tupleBlackdetectLast = listTuplesBlackdetect.pop() if listTuplesBlackdetect else Blackdetect()
51
+ match tupleBlackdetectLast.black_end:
52
+ case None:
53
+ listTuplesBlackdetect.append(Blackdetect(tupleBlackdetectLast.black_start, float(FFframe['tags']['lavfi.black_end'])))
54
+ case _:
55
+ if tupleBlackdetectLast.black_start is not None:
56
+ listTuplesBlackdetect.append(tupleBlackdetectLast)
57
+ listTuplesBlackdetect.append(Blackdetect(black_end=(float(FFframe['tags']['lavfi.black_end']))))
36
58
  del FFframe['tags']['lavfi.black_end']
37
59
 
38
60
  # This is not the way to do it
@@ -66,12 +88,15 @@ def pythonizeFFprobe(FFprobeJSON_utf8: str):
66
88
  if registrant not in Z0Z_dictionaries:
67
89
  Z0Z_dictionaries[registrant] = {}
68
90
  elif statistic not in Z0Z_dictionaries[registrant]:
69
- Z0Z_dictionaries[registrant][statistic] = numpy.zeros((channel, len(FFroot['frames'])))
91
+ # 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
+ Z0Z_dictionaries[registrant][statistic] = valueSherpa
70
94
  else:
71
95
  raise # Re-raise the exception
72
96
  except IndexError:
73
97
  if channel > Z0Z_dictionaries[registrant][statistic].shape[0]:
74
- Z0Z_dictionaries[registrant][statistic].resize((channel, len(FFroot['frames'])))
98
+ Z0Z_dictionaries[registrant][statistic] = numpy.resize(Z0Z_dictionaries[registrant][statistic], (channel, len(FFroot['frames'])))
99
+ # Z0Z_dictionaries[registrant][statistic].resize((channel, len(FFroot['frames'])))
75
100
  else:
76
101
  raise # Re-raise the exception
77
102
 
@@ -80,7 +105,17 @@ def pythonizeFFprobe(FFprobeJSON_utf8: str):
80
105
  if FFframe:
81
106
  leftCrumbs = True
82
107
  if listTuplesBlackdetect:
83
- Z0Z_dictionaries['blackdetect'] = numpy.array(listTuplesBlackdetect, dtype=[('black_start', numpy.float32), ('black_end', numpy.float32)], copy=False)
108
+ # 2025-03-06: I am _shocked_ that I was able to create a numpy structured array whenever it was that I originally wrote this code.
109
+ arrayBlackdetect = numpy.array(
110
+ [(
111
+ -1.0 if detect.black_start is None else detect.black_start,
112
+ -1.0 if detect.black_end is None else detect.black_end
113
+ ) for detect in listTuplesBlackdetect],
114
+ dtype=[('black_start', numpy.float64), ('black_end', numpy.float64)],
115
+ copy=False
116
+ )
117
+ Z0Z_dictionaries['blackdetect'] = arrayBlackdetect
118
+ # Z0Z_dictionaries['blackdetect'] = numpy.array(listTuplesBlackdetect, dtype=[('black_start', numpy.float32), ('black_end', numpy.float32)], copy=False) # uncommentToFixBlackdetect
84
119
  if not leftCrumbs:
85
120
  del FFroot['frames']
86
121
  return FFroot, Z0Z_dictionaries
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: analyzeAudio
3
- Version: 0.0.12
3
+ Version: 0.0.13
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
@@ -85,8 +85,86 @@ print(audioAspects['Chromagram']['analyzerParameters'])
85
85
  ### Use `whatMeasurements` command line tool to list available measurements
86
86
 
87
87
  ```sh
88
- (.venv) C:\apps\analyzeAudio> whatMeasurements
89
- ['Abs_Peak_count', 'Bit_depth', 'Chromagram', 'Chromagram mean', 'Crest factor', 'DC offset', 'Duration-samples', 'Dynamic range', 'Flat_factor', 'LUFS high', 'LUFS integrated', 'LUFS loudness range', 'LUFS low', 'Max_difference', 'Max_level', 'Mean_difference', 'Min_difference', 'Min_level', 'Noise_floor', 'Noise_floor_count', 'Peak dB', 'Peak_count', 'RMS from waveform', 'RMS from waveform mean', 'RMS peak', 'RMS total', 'RMS_difference', 'RMS_trough', 'SI-SDR mean', 'SRMR', 'SRMR mean', 'Signal entropy', 'Spectral Bandwidth', 'Spectral Bandwidth mean', 'Spectral Centroid', 'Spectral Centroid mean', 'Spectral Contrast', 'Spectral Contrast mean', 'Spectral Flatness', 'Spectral Flatness mean', 'Spectral centroid', 'Spectral crest', 'Spectral decrease', 'Spectral entropy', 'Spectral flatness', 'Spectral flux', 'Spectral kurtosis', 'Spectral mean', 'Spectral rolloff', 'Spectral skewness', 'Spectral slope', 'Spectral spread', 'Spectral variance', 'Tempo', 'Tempo mean', 'Tempogram', 'Tempogram mean', 'Zero-crossing rate', 'Zero-crossing rate mean', 'Zero-crossings rate']
88
+ (.venv) C:\apps\analyzeAudio>whatMeasurements
89
+ ['Abs_Peak_count', 'Bit_depth', 'Chromagram', 'Chromagram mean', 'Crest factor', 'DC offset', 'Duration-samples', 'Dynamic range', 'Flat_factor', 'LUFS high', 'LUFS integrated', 'LUFS loudness range', 'LUFS low', 'Max_difference', 'Max_level', 'Mean_difference', 'Min_difference', 'Min_level', 'Noise_floor', 'Noise_floor_count', 'Peak dB', 'Peak_count', 'Power spectral density', 'Power spectral density mean', 'RMS from waveform', 'RMS from waveform mean', 'RMS peak', 'RMS total', 'RMS_difference', 'RMS_trough', 'SI-SDR mean', 'SRMR', 'SRMR mean', 'Signal entropy', 'Spectral Bandwidth', 'Spectral Bandwidth mean', 'Spectral Centroid', 'Spectral Centroid mean', 'Spectral Contrast', 'Spectral Contrast mean', 'Spectral Flatness', 'Spectral Flatness mean', 'Spectral centroid', 'Spectral centroid mean', 'Spectral crest', 'Spectral crest mean', 'Spectral decrease', 'Spectral decrease mean', 'Spectral entropy', 'Spectral entropy mean', 'Spectral flatness', 'Spectral flatness mean', 'Spectral flux', 'Spectral flux mean', 'Spectral kurtosis', 'Spectral kurtosis mean', 'Spectral rolloff', 'Spectral rolloff mean', 'Spectral skewness', 'Spectral skewness mean', 'Spectral slope', 'Spectral slope mean', 'Spectral spread', 'Spectral spread mean', 'Spectral variance', 'Spectral variance mean', 'Tempo', 'Tempo mean', 'Tempogram', 'Tempogram mean', 'Zero-crossing rate', 'Zero-crossing rate mean', 'Zero-crossings rate']
90
+ ```
91
+
92
+ ## Some clues about the aspects
93
+
94
+ ```python
95
+ 'Abs_Peak_count': float
96
+ 'Bit_depth': float
97
+ 'Chromagram': NDArray[float64] # shape(..., 12, frames)
98
+ 'Chromagram mean': float
99
+ 'Crest factor': float
100
+ 'DC offset': float
101
+ 'Duration-samples': float
102
+ 'Dynamic range': float
103
+ 'Flat_factor': float
104
+ 'LUFS high': float
105
+ 'LUFS integrated': float
106
+ 'LUFS loudness range': float
107
+ 'LUFS low': float
108
+ 'Max_difference': float
109
+ 'Max_level': float
110
+ 'Mean_difference': float
111
+ 'Min_difference': float
112
+ 'Min_level': float
113
+ 'Noise_floor_count': float
114
+ 'Noise_floor': float
115
+ 'Peak dB': float
116
+ 'Peak_count': float
117
+ 'Power spectral density': NDArray[float64] # shape(channels, frames)
118
+ 'Power spectral density mean': float
119
+ 'RMS from waveform': NDArray[float64] # shape(..., 1, frames)
120
+ 'RMS from waveform mean': float
121
+ 'RMS peak': float
122
+ 'RMS total': float
123
+ 'RMS_difference': float
124
+ 'RMS_trough': float
125
+ 'SI-SDR mean': float
126
+ 'Signal entropy': float
127
+ 'Spectral Bandwidth': NDArray[float64] # shape(..., 1, frames)
128
+ 'Spectral Bandwidth mean': float
129
+ 'Spectral centroid': NDArray[float64] # shape(channels, frames)
130
+ 'Spectral centroid mean': float
131
+ 'Spectral Centroid': NDArray[float64] # shape(..., 1, frames)
132
+ 'Spectral Centroid mean': float
133
+ 'Spectral Contrast': NDArray[float64] # shape(..., 7, frames)
134
+ 'Spectral Contrast mean': float
135
+ 'Spectral crest': NDArray[float64] # shape(channels, frames)
136
+ 'Spectral crest mean': float
137
+ 'Spectral decrease': NDArray[float64] # shape(channels, frames)
138
+ 'Spectral decrease mean': float
139
+ 'Spectral entropy': NDArray[float64] # shape(channels, frames)
140
+ 'Spectral entropy mean': float
141
+ 'Spectral flatness': NDArray[float64] # shape(channels, frames)
142
+ 'Spectral flatness mean': float
143
+ 'Spectral Flatness': NDArray[float64] # shape(..., 1, frames)
144
+ 'Spectral Flatness mean': float
145
+ 'Spectral flux': NDArray[float64] # shape(channels, frames)
146
+ 'Spectral flux mean': float
147
+ 'Spectral kurtosis': NDArray[float64] # shape(channels, frames)
148
+ 'Spectral kurtosis mean': float
149
+ 'Spectral rolloff': NDArray[float64] # shape(channels, frames)
150
+ 'Spectral rolloff mean': float
151
+ 'Spectral skewness': NDArray[float64] # shape(channels, frames)
152
+ 'Spectral skewness mean': float
153
+ 'Spectral slope': NDArray[float64] # shape(channels, frames)
154
+ 'Spectral slope mean': float
155
+ 'Spectral spread': NDArray[float64] # shape(channels, frames)
156
+ 'Spectral spread mean': float
157
+ 'Spectral variance': NDArray[float64] # shape(channels, frames)
158
+ 'Spectral variance mean': float
159
+ 'SRMR': NDArray[float64] # shape(...)
160
+ 'SRMR mean': float
161
+ 'Tempo': NDArray[float64] # shape(...)
162
+ 'Tempo mean': float
163
+ 'Tempogram': NDArray[float64] # shape(..., 384, samples)
164
+ 'Tempogram mean': float
165
+ 'Zero-crossing rate': NDArray[float64] # shape(..., 1, frames)
166
+ 'Zero-crossing rate mean': float
167
+ 'Zero-crossings rate': float
90
168
  ```
91
169
 
92
170
  ## Installation
@@ -37,7 +37,7 @@ optional-dependencies = { testing = [
37
37
  readme = { file = "README.md", content-type = "text/markdown" }
38
38
  requires-python = ">=3.10"
39
39
  urls = { Donate = "https://www.patreon.com/integrated", Homepage = "https://github.com/hunterhogan/analyzeAudio", Repository = "https://github.com/hunterhogan/analyzeAudio.git" }
40
- version = "0.0.12"
40
+ version = "0.0.13"
41
41
 
42
42
  [project.scripts]
43
43
  whatMeasurements = "analyzeAudio.audioAspectsRegistry:getListAvailableAudioAspects"
@@ -1,65 +0,0 @@
1
- # analyzeAudio
2
-
3
- Measure one or more aspects of one or more audio files.
4
-
5
- ## Note well: FFmpeg & FFprobe binaries must be in PATH
6
-
7
- Some options to [download FFmpeg and FFprobe](https://www.ffmpeg.org/download.html) at ffmpeg.org.
8
-
9
- ## Some ways to use this package
10
-
11
- ### Use `analyzeAudioFile` to measure one or more aspects of a single audio file
12
-
13
- ```python
14
- from analyzeAudio import analyzeAudioFile
15
- listAspectNames = ['LUFS integrated',
16
- 'RMS peak',
17
- 'SRMR mean',
18
- 'Spectral Flatness mean']
19
- listMeasurements = analyzeAudioFile(pathFilename, listAspectNames)
20
- ```
21
-
22
- ### Use `getListAvailableAudioAspects` to get a crude list of aspects this package can measure
23
-
24
- The aspect names are accurate, but the lack of additional documentation can make things challenging. 'Zero-crossing rate', 'Zero-crossing rate mean', and 'Zero-crossings rate', for example, are different from each other. ("... lack of additional documentation ...")
25
-
26
- ```python
27
- import analyzeAudio
28
- analyzeAudio.getListAvailableAudioAspects()
29
- ```
30
-
31
- ### Use `analyzeAudioListPathFilenames` to measure one or more aspects of individual file in a list of audio files
32
-
33
- ### Use `audioAspects` to call an analyzer function by using the name of the aspect you wish to measure
34
-
35
- ```python
36
- from analyzeAudio import audioAspects
37
- SI_SDR_channelsMean = audioAspects['SI-SDR mean']['analyzer'](pathFilenameAudioFile, pathFilenameDifferentAudioFile)
38
- ```
39
-
40
- Retrieve the names of the parameters for an analyzer function with the `['analyzerParameters']` key-name.
41
-
42
- ```python
43
- from analyzeAudio import audioAspects
44
- print(audioAspects['Chromagram']['analyzerParameters'])
45
- ```
46
-
47
- ### Use `whatMeasurements` command line tool to list available measurements
48
-
49
- ```sh
50
- (.venv) C:\apps\analyzeAudio> whatMeasurements
51
- ['Abs_Peak_count', 'Bit_depth', 'Chromagram', 'Chromagram mean', 'Crest factor', 'DC offset', 'Duration-samples', 'Dynamic range', 'Flat_factor', 'LUFS high', 'LUFS integrated', 'LUFS loudness range', 'LUFS low', 'Max_difference', 'Max_level', 'Mean_difference', 'Min_difference', 'Min_level', 'Noise_floor', 'Noise_floor_count', 'Peak dB', 'Peak_count', 'RMS from waveform', 'RMS from waveform mean', 'RMS peak', 'RMS total', 'RMS_difference', 'RMS_trough', 'SI-SDR mean', 'SRMR', 'SRMR mean', 'Signal entropy', 'Spectral Bandwidth', 'Spectral Bandwidth mean', 'Spectral Centroid', 'Spectral Centroid mean', 'Spectral Contrast', 'Spectral Contrast mean', 'Spectral Flatness', 'Spectral Flatness mean', 'Spectral centroid', 'Spectral crest', 'Spectral decrease', 'Spectral entropy', 'Spectral flatness', 'Spectral flux', 'Spectral kurtosis', 'Spectral mean', 'Spectral rolloff', 'Spectral skewness', 'Spectral slope', 'Spectral spread', 'Spectral variance', 'Tempo', 'Tempo mean', 'Tempogram', 'Tempogram mean', 'Zero-crossing rate', 'Zero-crossing rate mean', 'Zero-crossings rate']
52
- ```
53
-
54
- ## Installation
55
-
56
- ```sh
57
- pip install analyzeAudio
58
- ```
59
-
60
- ## My recovery
61
-
62
- [![Static Badge](https://img.shields.io/badge/2011_August-Homeless_since-blue?style=flat)](https://HunterThinks.com/support)
63
- [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UC3Gx7kz61009NbhpRtPP7tw)](https://www.youtube.com/@HunterHogan)
64
-
65
- [![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/)
File without changes
File without changes