spectrumalyzer 0.1.0__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.
@@ -0,0 +1,70 @@
1
+ Metadata-Version: 2.4
2
+ Name: spectrumalyzer
3
+ Version: 0.1.0
4
+ Summary: Audio beat analysis tool for motion graphics
5
+ Home-page: https://github.com/Dream-Pixels-Forge/spectrumalyzer
6
+ Author: Dream-Pixels-Forge
7
+ Author-email: dev@dream-pixels-forge.example.com
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.7
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Requires-Python: >=3.7
19
+ Description-Content-Type: text/markdown
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: requires-python
27
+ Dynamic: summary
28
+
29
+ # Spectrumalyzer
30
+
31
+ Audio beat analysis tool for motion graphics - provides BPM detection, beat timestamps, and frequency spectrum data compatible with Remotion and HyperFrames workflows.
32
+
33
+ ## Features
34
+
35
+ - BPM (Beats Per Minute) detection
36
+ - Beat timestamp extraction
37
+ - Frequency spectrum analysis
38
+ - Compatible with Remotion and HyperFrames workflows
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ pip install spectrumalyzer
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ ```python
49
+ from spectrumalyzer import analyze_audio
50
+
51
+ # Analyze an audio file
52
+ results = analyze_audio("path/to/audio/file.mp3")
53
+
54
+ # Get BPM
55
+ bpm = results["bpm"]
56
+
57
+ # Get beat timestamps
58
+ beats = results["beats"]
59
+
60
+ # Get frequency spectrum
61
+ spectrum = results["spectrum"]
62
+ ```
63
+
64
+ ## API
65
+
66
+ See the [SKILL.md](skills/spectrumalyzer/SKILL.md) file for detailed API documentation.
67
+
68
+ ## License
69
+
70
+ MIT
@@ -0,0 +1,42 @@
1
+ # Spectrumalyzer
2
+
3
+ Audio beat analysis tool for motion graphics - provides BPM detection, beat timestamps, and frequency spectrum data compatible with Remotion and HyperFrames workflows.
4
+
5
+ ## Features
6
+
7
+ - BPM (Beats Per Minute) detection
8
+ - Beat timestamp extraction
9
+ - Frequency spectrum analysis
10
+ - Compatible with Remotion and HyperFrames workflows
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pip install spectrumalyzer
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```python
21
+ from spectrumalyzer import analyze_audio
22
+
23
+ # Analyze an audio file
24
+ results = analyze_audio("path/to/audio/file.mp3")
25
+
26
+ # Get BPM
27
+ bpm = results["bpm"]
28
+
29
+ # Get beat timestamps
30
+ beats = results["beats"]
31
+
32
+ # Get frequency spectrum
33
+ spectrum = results["spectrum"]
34
+ ```
35
+
36
+ ## API
37
+
38
+ See the [SKILL.md](skills/spectrumalyzer/SKILL.md) file for detailed API documentation.
39
+
40
+ ## License
41
+
42
+ MIT
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,33 @@
1
+ from setuptools import setup
2
+
3
+ with open("README.md", "r", encoding="utf-8") as fh:
4
+ long_description = fh.read()
5
+
6
+ setup(
7
+ name="spectrumalyzer",
8
+ version="0.1.0",
9
+ author="Dream-Pixels-Forge",
10
+ author_email="dev@dream-pixels-forge.example.com",
11
+ description="Audio beat analysis tool for motion graphics",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/Dream-Pixels-Forge/spectrumalyzer",
15
+ py_modules=["spectrumalyzer"],
16
+ classifiers=[
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.7",
23
+ "Programming Language :: Python :: 3.8",
24
+ "Programming Language :: Python :: 3.9",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ ],
28
+ python_requires=">=3.7",
29
+ install_requires=[
30
+ # Add your dependencies here
31
+ # For example: "numpy>=1.19.0", "scipy>=1.5.0"
32
+ ],
33
+ )
@@ -0,0 +1,70 @@
1
+ Metadata-Version: 2.4
2
+ Name: spectrumalyzer
3
+ Version: 0.1.0
4
+ Summary: Audio beat analysis tool for motion graphics
5
+ Home-page: https://github.com/Dream-Pixels-Forge/spectrumalyzer
6
+ Author: Dream-Pixels-Forge
7
+ Author-email: dev@dream-pixels-forge.example.com
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.7
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Requires-Python: >=3.7
19
+ Description-Content-Type: text/markdown
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: requires-python
27
+ Dynamic: summary
28
+
29
+ # Spectrumalyzer
30
+
31
+ Audio beat analysis tool for motion graphics - provides BPM detection, beat timestamps, and frequency spectrum data compatible with Remotion and HyperFrames workflows.
32
+
33
+ ## Features
34
+
35
+ - BPM (Beats Per Minute) detection
36
+ - Beat timestamp extraction
37
+ - Frequency spectrum analysis
38
+ - Compatible with Remotion and HyperFrames workflows
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ pip install spectrumalyzer
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ ```python
49
+ from spectrumalyzer import analyze_audio
50
+
51
+ # Analyze an audio file
52
+ results = analyze_audio("path/to/audio/file.mp3")
53
+
54
+ # Get BPM
55
+ bpm = results["bpm"]
56
+
57
+ # Get beat timestamps
58
+ beats = results["beats"]
59
+
60
+ # Get frequency spectrum
61
+ spectrum = results["spectrum"]
62
+ ```
63
+
64
+ ## API
65
+
66
+ See the [SKILL.md](skills/spectrumalyzer/SKILL.md) file for detailed API documentation.
67
+
68
+ ## License
69
+
70
+ MIT
@@ -0,0 +1,7 @@
1
+ README.md
2
+ setup.py
3
+ spectrumalyzer.py
4
+ spectrumalyzer.egg-info/PKG-INFO
5
+ spectrumalyzer.egg-info/SOURCES.txt
6
+ spectrumalyzer.egg-info/dependency_links.txt
7
+ spectrumalyzer.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ spectrumalyzer
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env python3
2
+ import os, sys, json, math, struct, subprocess
3
+ from datetime import datetime
4
+ import argparse
5
+
6
+ def parse_args():
7
+ parser = argparse.ArgumentParser(prog='spectrumalyzer',
8
+ description='Audio beat analysis tool for motion graphics')
9
+ parser.add_argument('audio_file', help='Path to audio file')
10
+ parser.add_argument('--output', '-o', default='beats.json',
11
+ help='Output JSON file path')
12
+ parser.add_argument('--format', '-f', choices=['json', 'remotion'],
13
+ default='json', help='Output format')
14
+ parser.add_argument('--threshold', '-t', type=float, default=0.15,
15
+ help='Beat detection threshold (0.0-1.0, default: 0.15)')
16
+ parser.add_argument('--fft-size', type=int, default=1024,
17
+ help='FFT size for spectrum (256-4096)')
18
+ return parser.parse_args()
19
+
20
+ def get_audio_duration(filepath):
21
+ try:
22
+ result = subprocess.run(
23
+ ['ffprobe', '-v', 'error', '-show_entries', 'format=duration',
24
+ '-of', 'default=noprint_wrappers=1:nokey=1', filepath],
25
+ capture_output=True, text=True)
26
+ return float(result.stdout.strip())
27
+ except Exception as e:
28
+ print(f'Error getting duration: {e}', file=sys.stderr)
29
+ return 0
30
+
31
+ def extract_pcm(filepath, duration):
32
+ try:
33
+ result = subprocess.run(
34
+ ['ffmpeg', '-i', filepath, '-vn', '-acodec', 'pcm_s16le',
35
+ '-ar', '44100', '-ac', '1', '-f', 's16le',
36
+ '-ss', '0', '-t', str(duration or 999999), 'pipe:1'],
37
+ capture_output=True)
38
+ if len(result.stdout) == 0:
39
+ print('Warning: No audio data extracted', file=sys.stderr)
40
+ return []
41
+ samples = struct.unpack(f'<{len(result.stdout)//2}h', result.stdout)
42
+ return [s / 32768.0 for s in samples]
43
+ except Exception as e:
44
+ print(f'Error extracting PCM: {e}', file=sys.stderr)
45
+ return []
46
+
47
+ def fft_transform(signal, inverse=False):
48
+ n = len(signal)
49
+ if n <= 1:
50
+ return signal[:]
51
+ output = [0] * n
52
+ bits = n.bit_length() - 1
53
+ for i in range(n):
54
+ j = 0
55
+ for k in range(bits):
56
+ if i & (1 << k):
57
+ j |= (1 << (bits - 1 - k))
58
+ output[j] = signal[i]
59
+ size = 2
60
+ while size < n:
61
+ step = size * 2
62
+ angle = 2 * math.pi / step * (-1 if inverse else 1)
63
+ w_real = math.cos(angle)
64
+ w_imag = math.sin(angle)
65
+ for i in range(0, n, step):
66
+ for j in range(size):
67
+ k = i + j
68
+ m = k + size
69
+ t_real = output[m] * w_real - output[m + 1] * w_imag
70
+ t_imag = output[m] * w_imag + output[m + 1] * w_real
71
+ output[k] = output[k] - t_real
72
+ output[k + 1] = output[k + 1] - t_imag
73
+ output[m] = output[k] + t_real
74
+ output[m + 1] = output[k + 1] + t_imag
75
+ size = step
76
+ if inverse:
77
+ for i in range(n):
78
+ output[i] /= n
79
+ return output
80
+
81
+ def detect_beats(audio, sample_rate=44100, threshold=0.3, min_interval=0.5):
82
+ hop_size = 512
83
+ num_frames = len(audio) // hop_size
84
+ energies = [0] * num_frames
85
+ for i in range(num_frames):
86
+ start = i * hop_size
87
+ end = min(start + hop_size, len(audio))
88
+ energy = 0
89
+ for j in range(start, end):
90
+ energy += audio[j] * audio[j]
91
+ energies[i] = math.sqrt(energy / max(1, end - start))
92
+ onsets = [0] * (num_frames - 1)
93
+ for i in range(1, num_frames):
94
+ diff = energies[i] - energies[i - 1]
95
+ onsets[i - 1] = max(0, diff)
96
+ beat_times = []
97
+ last_beat_idx = -float('inf')
98
+ min_frames = int(min_interval * sample_rate / hop_size)
99
+ for i in range(len(onsets)):
100
+ if onsets[i] > threshold and (i - last_beat_idx) > min_frames:
101
+ beat_times.append(i * hop_size / sample_rate)
102
+ last_beat_idx = i
103
+ bpm = 0
104
+ if len(beat_times) >= 2:
105
+ intervals = []
106
+ for i in range(1, len(beat_times)):
107
+ interval = beat_times[i] - beat_times[i - 1]
108
+ if 0 < interval < 10.0:
109
+ intervals.append(interval)
110
+ if intervals:
111
+ sorted_intervals = sorted(intervals)
112
+ n = len(sorted_intervals)
113
+ mid = n // 2
114
+ median_interval = sorted_intervals[mid] if n % 2 == 1 else (sorted_intervals[mid - 1] + sorted_intervals[mid]) / 2
115
+ if median_interval > 0:
116
+ bpm = round(60 / median_interval)
117
+ bpm = max(20, min(220, bpm))
118
+ return beat_times, bpm
119
+
120
+ def generate_spectrum(audio, sample_rate=44100, fft_size=1024):
121
+ num_bins = fft_size // 2
122
+ spectrum = []
123
+ for i in range(num_bins):
124
+ start = int((i / num_bins) * len(audio))
125
+ end = int(((i + 1) / num_bins) * len(audio))
126
+ energy = 0
127
+ for j in range(start, min(end, len(audio))):
128
+ energy += audio[j] * audio[j]
129
+ energy = math.sqrt(energy / max(1, end - start)) * 100
130
+ spectrum.append(energy)
131
+ freq_data = []
132
+ for i, e in enumerate(spectrum):
133
+ freq = (i * sample_rate) / fft_size
134
+ freq_data.append({'frequency': freq, 'energy': e})
135
+ return freq_data
136
+
137
+ def main():
138
+ args = parse_args()
139
+ if not os.path.exists(args.audio_file):
140
+ print(f'Error: File not found: {args.audio_file}', file=sys.stderr)
141
+ sys.exit(1)
142
+ print('=== spectrumalyzer ===')
143
+ print(f'Input: {args.audio_file}')
144
+ duration = get_audio_duration(args.audio_file)
145
+ print(f'Duration: {duration:.2f}s')
146
+ audio = extract_pcm(args.audio_file, duration)
147
+ print(f'Samples: {len(audio)}')
148
+ if len(audio) == 0:
149
+ print('Error: No audio data could be extracted', file=sys.stderr)
150
+ sys.exit(1)
151
+ threshold = max(0.0, min(1.0, args.threshold))
152
+ min_interval = max(0.1, 2.0)
153
+ beat_times, bpm = detect_beats(audio, 44100, threshold, min_interval)
154
+ print(f'Beats: {len(beat_times)}')
155
+ print(f'BPM: {bpm}')
156
+ fft_size = max(256, min(4096, args.fft_size))
157
+ spectrum = generate_spectrum(audio, 44100, fft_size)
158
+ beat_times_rounded = [round(t, 4) for t in beat_times]
159
+ spectrum_128 = spectrum[:128]
160
+ output = {
161
+ 'bpm': bpm,
162
+ 'duration': duration,
163
+ 'sampleRate': 44100,
164
+ 'beatCount': len(beat_times),
165
+ 'beats': beat_times_rounded,
166
+ 'beatTimes': beat_times_rounded,
167
+ 'spectrum': [{'frequency': round(s['frequency'], 1), 'energy': round(s['energy'], 2)} for s in spectrum_128],
168
+ 'metadata': {'version': '1.0.0', 'created': datetime.now().isoformat(), 'analyzer': 'spectrumalyzer'}
169
+ }
170
+ out_path = os.path.abspath(args.output)
171
+ with open(out_path, 'w') as f:
172
+ json.dump(output, f, indent=2)
173
+ remotion_output = {'bpm': bpm, 'beatTimes': beat_times_rounded,
174
+ 'duration': duration, 'beatCount': len(beat_times),
175
+ 'measures': [], 'beatData': [{'time': t, 'index': i + 1} for i, t in enumerate(beat_times_rounded)]}
176
+ rem_path = out_path.replace('.json', '.remotion.json')
177
+ with open(rem_path, 'w') as f:
178
+ json.dump(remotion_output, f, indent=2)
179
+ print()
180
+ print('=== Output Summary ===')
181
+ print(f'Beats: {len(beat_times)}')
182
+ print(f'BPM: {bpm}')
183
+ print(f'Output: {out_path}')
184
+ print()
185
+ print('First 8 beats:')
186
+ for i, t in enumerate(beat_times_rounded[:8]):
187
+ print(f' {i + 1}. {t:.2f}s')
188
+ print()
189
+ print(f'Remotion format: {rem_path}')
190
+ print()
191
+ print('=== spectrumalyzer complete ===')
192
+
193
+ if __name__ == '__main__':
194
+ main()