fps.py (1690B)
1 """BPM to FPS calculator for clean frame sync. 2 3 Frame sync formula: 4 - fps × 60 = fpm (frames per minute) 5 - BPM = beats per minute 6 - frames_per_beat = fpm / BPM = (fps × 60) / BPM 7 8 For clean sync: frames_per_beat must be integer. 9 Base fps = BPM / 5 (gives 12 frames/beat). 10 If fps < 24, double resolution to maintain fluidity. 11 """ 12 13 14 def bpm_to_fps(bpm: float, min_fps: int = 24) -> dict: 15 """Calculate clean fps from BPM. 16 17 Returns dict with fps, frames_per_beat, and multiplier used. 18 """ 19 # Base fps for clean sync (12 frames/beat) 20 fps_base = bpm / 5 21 22 # Double resolution if below min_fps 23 multiplier = 1 24 fps = fps_base 25 while fps < min_fps: 26 multiplier *= 2 27 fps = fps_base * multiplier 28 29 frames_per_beat = (fps * 60) / bpm 30 31 return { 32 "bpm": bpm, 33 "fps": fps, 34 "frames_per_beat": int(frames_per_beat), 35 "multiplier": multiplier, 36 "fpm": int(fps * 60), 37 } 38 39 40 def print_fps_table(bpm_range: range = range(60, 181, 10)): 41 """Print fps table for common BPMs.""" 42 print(f"{'BPM':>5} {'FPS':>5} {'FPB':>5} {'Mult':>5} {'FPM':>8}") 43 print("-" * 35) 44 for bpm in bpm_range: 45 r = bpm_to_fps(bpm) 46 print(f"{r['bpm']:>5} {r['fps']:>5.0f} {r['frames_per_beat']:>5} {r['multiplier']:>5} {r['fpm']:>8}") 47 48 49 if __name__ == "__main__": 50 import sys 51 52 if len(sys.argv) > 1: 53 bpm = float(sys.argv[1]) 54 r = bpm_to_fps(bpm) 55 print(f"BPM: {r['bpm']}") 56 print(f"FPS: {r['fps']}") 57 print(f"Frames/beat: {r['frames_per_beat']}") 58 print(f"Resolution multiplier: {r['multiplier']}x") 59 print(f"FPM: {r['fpm']}") 60 else: 61 print_fps_table()