Files

138 lines
4.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# Generates dev test content: equirect compass video + orbiting ambiX tone.
# ambiX ACN/SN3D FOA, source at azimuth az(t) = 2*pi*t/8 (CCW from front):
# ch0 W = s(t) ch1 Y = s(t)*sin(az) (left positive)
# ch2 Z = 0 ch3 X = s(t)*cos(az) (front positive)
set -euo pipefail
# Check for required dependencies
if ! command -v ffmpeg &> /dev/null; then
echo "Error: ffmpeg not found. Install with: brew install ffmpeg" >&2
exit 1
fi
if ! command -v python3 &> /dev/null; then
echo "Error: python3 not found. Install with: brew install python3" >&2
exit 1
fi
if ! python3 -c "import PIL" 2>/dev/null; then
echo "Error: Pillow (Python PIL) not installed." >&2
echo "Install with: pip install Pillow" >&2
exit 1
fi
cd "$(dirname "$0")/.."
OUT=public/content/scenes/test-orbit
mkdir -p "$OUT"
DUR=24
ffmpeg -y -f lavfi -i "aevalsrc=\
sin(2*PI*440*t)|\
sin(2*PI*440*t)*sin(2*PI*t/8)|\
0|\
sin(2*PI*440*t)*cos(2*PI*t/8):c=quad:s=48000:d=${DUR}" \
-c:a pcm_s16le "$OUT/ambix.wav"
# Generate frames with PIL (video text rendering via frame-by-frame synthesis)
python3 - "$OUT" "$DUR" << 'PYTHON_EOF'
import sys
import subprocess
from PIL import Image, ImageDraw, ImageFont
outdir = sys.argv[1]
duration = int(sys.argv[2])
fps = 30
width, height = 1920, 960
# Try to use a system font; fallback to default
try:
font_labels = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", 72)
font_captions = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", 56)
font_time = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", 36)
except:
font_labels = ImageFont.load_default()
font_captions = ImageFont.load_default()
font_time = ImageFont.load_default()
frames_dir = f"{outdir}/frames"
import os
os.makedirs(frames_dir, exist_ok=True)
# Grid color: [email protected] alpha blended over background (0x203040 = RGB 32,48,64)
# Blend: (255*0.35 + 32*0.65, 255*0.35 + 48*0.65, 255*0.35 + 64*0.65) = (112, 124, 136)
grid_color = (112, 124, 136)
# Generate each frame
for frame_idx in range(duration * fps):
t = frame_idx / fps
# Create frame with dark background
img = Image.new('RGB', (width, height), color=(0x20, 0x30, 0x40))
draw = ImageDraw.Draw(img)
# Draw grid (pre-blended color for alpha effect)
grid_w, grid_h = 120, 120
for x in range(0, width, grid_w):
draw.line([(x, 0), (x, height)], fill=grid_color, width=1)
for y in range(0, height, grid_h):
draw.line([(0, y), (width, y)], fill=grid_color, width=1)
# Draw cardinal labels (FRONT, RIGHT, LEFT, BACK)
draw.text(((width - 120) // 2, (height - 60) // 2), "FRONT", fill=(255, 255, 255), font=font_labels)
draw.text((int(3 * width / 4 - 60), (height - 60) // 2), "RIGHT", fill=(255, 255, 255), font=font_labels)
draw.text((int(width / 4 - 40), (height - 60) // 2), "LEFT", fill=(255, 255, 255), font=font_labels)
draw.text((16, (height - 60) // 2), "BACK", fill=(255, 255, 255), font=font_labels)
# Determine caption based on time phase (8s cycle)
phase = t % 8
caption = None
if phase < 1 or phase >= 7:
caption = "SOUND FRONT"
elif 1 <= phase < 3:
caption = "SOUND LEFT"
elif 3 <= phase < 5:
caption = "SOUND BACK"
elif 5 <= phase < 7:
caption = "SOUND RIGHT"
# Draw caption
if caption:
cap_bbox = draw.textbbox((0, 0), caption, font=font_captions)
cap_w = cap_bbox[2] - cap_bbox[0]
draw.text(((width - cap_w) // 2, int(height * 0.72)), caption, fill=(255, 255, 0), font=font_captions)
# Draw timestamp
hms_str = f"t={int(t//3600):02d}:{int((t%3600)//60):02d}:{int(t%60):02d}"
draw.text((16, 16), hms_str, fill=(255, 255, 255), font=font_time)
# Save frame
frame_path = f"{frames_dir}/frame_{frame_idx:06d}.png"
img.save(frame_path)
# Encode PNG sequence to MP4 with error checking
try:
subprocess.run([
'ffmpeg', '-y',
'-framerate', str(fps),
'-i', f'{frames_dir}/frame_%06d.png',
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-an',
f'{outdir}/equirect.mp4'
], check=True, capture_output=True)
except subprocess.CalledProcessError as e:
print(f"Error: ffmpeg encoding failed: {e.stderr.decode()}", file=sys.stderr)
sys.exit(1)
# Clean up frames directory
import shutil
shutil.rmtree(frames_dir)
PYTHON_EOF
ffmpeg -y -ss 2 -i "$OUT/equirect.mp4" -frames:v 1 -update 1 \
-vf scale=480:240 "$OUT/thumb.jpg"
echo "fixtures written to $OUT"