66 lines
1.7 KiB
Python
66 lines
1.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate Discord voice message waveform from an ogg file.
|
|
Usage: gen_waveform.py <ogg_file>
|
|
Output: JSON with duration_secs and base64-encoded waveform (256 points, 0-255).
|
|
"""
|
|
|
|
import subprocess
|
|
import struct
|
|
import base64
|
|
import json
|
|
import sys
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Usage: gen_waveform.py <ogg_file>")
|
|
sys.exit(1)
|
|
|
|
ogg_file = sys.argv[1]
|
|
|
|
# Get duration
|
|
result = subprocess.run(
|
|
["ffprobe", "-v", "error", "-show_entries", "format=duration",
|
|
"-of", "csv=p=0", ogg_file],
|
|
capture_output=True, text=True
|
|
)
|
|
duration_secs = float(result.stdout.strip())
|
|
|
|
# Extract raw PCM (mono, 16-bit, 48kHz)
|
|
result = subprocess.run(
|
|
["ffmpeg", "-i", ogg_file, "-f", "s16le", "-ac", "1",
|
|
"-ar", "48000", "-"],
|
|
capture_output=True
|
|
)
|
|
pcm_data = result.stdout
|
|
|
|
# Parse samples
|
|
num_samples = len(pcm_data) // 2
|
|
samples = struct.unpack(f"<{num_samples}h", pcm_data[:num_samples * 2])
|
|
|
|
# Generate 256-point waveform
|
|
points = 256
|
|
chunk_size = max(1, num_samples // points)
|
|
waveform = []
|
|
|
|
for i in range(points):
|
|
start = i * chunk_size
|
|
end = min(start + chunk_size, num_samples)
|
|
if start >= num_samples:
|
|
waveform.append(0)
|
|
continue
|
|
chunk = samples[start:end]
|
|
avg = sum(abs(s) for s in chunk) / len(chunk)
|
|
# Map to 0-255
|
|
val = min(255, int(avg / 32768 * 255))
|
|
waveform.append(val)
|
|
|
|
waveform_b64 = base64.b64encode(bytes(waveform)).decode()
|
|
|
|
print(json.dumps({
|
|
"duration_secs": round(duration_secs, 2),
|
|
"waveform": waveform_b64
|
|
}))
|
|
|
|
if __name__ == "__main__":
|
|
main()
|