Funbeats easy and fast scripting system for PMVs and music

This script generates funscripts for music-based videos such as PMVs based on a text file that describes patterns in musical terms. Typically it only takes 2-10 times the song duration to create a full funscript, depending on how detailed you want it. You can generate both linear and vibrator funscripts from one file.

Example generated funscript, perhaps 10-15 minutes of work


Example generated vibrator funscript from the same file

Without scripting center

Adjusting stroke interpolation in a few seconds if desired
fs4

Generated from this file

"offset_ms": 87,
"bpm": 126,
"time_signature": 4,
"first_measure": 0,
"default_intensity_interpolation": "smoothstep",
"default_center_interpolation": "smoothstep",
"default_stroke_interpolation": "linear", # "incirc mirrored @120%" in example
"default_onoff_ratio": 0.5,
"measure_onset_onoff_ratio_factor": 1.1,
"midbeat_deemphasis": 0.12,
"upbeat_deemphasis": 0.21,
"constants": {
	"min": 0.06,
	"mid": 0.51,
	"high": 0.91,
	"max": 1.0,
	"full": 4,
	"double": 8,
	"shortstroke": ["ratio 0.2"],
	"longstroke": ["ratio 0.8"],
	"fadein": [0.3, 0.7],
	"fadeout": [0.8, 0.4],
	"drop": ["full", "max"],
	"doublebeat": ["double", "max"],
	"break": [0],
	"break_end": [0],
	"melody1": ["8: X--4 X4-8 | 0.4", "mid"],
	"melody2": ["4: X-75 -X52 | 30%", "max"],
	"melody3": ["4: X-XX -XXX", "max"],
	"interlude": ["half", "mid"],
	"quarter_off": [4, "offbeat"]
	},
"segments": [
	[0, 2, 0.2, 0.7, "offbeat"],
	[4, 2, 0.1, 0.5],
	[8, 4, 0.7, 0.3],
	[12, 4, 0.2, 0.7], # comment
	[16, 2, 0.7, 0.2, "stroke outquad mirrored @50%"],
		#############
		#### part 1
	[20, "melody1", 1, 0.5],
	[23, "melody2", 0.7],
	[26, "drop"],
	[31, "break"],
	[31.875, "break_end"],
	[32, 4, 0.4, 0.8],
		#############
		#### part 2
	[34, "quarter_off", 0.7],
	[38, "quarter_off", 0.9],
	[40, "quarter_off", 0.4, 0.7],
	[42, 2, 0.1, 0.3],
	[46, 4, 0.3, 0.5],
	[50, "melody1", 0.4, 0.6],
	[54, "melody3", 0.6, 0.7],
	[58, 4, 0.6, "max", "stroke incubic @122%"],
		#############
		#### end
	[73, "break"],
	[73.875, "break_end"],
	[74, 4, 0.8],
	[79, 0, 0]
	],
"center": [
	[0, 0, 0.75],
	[20, 1],
	[23, 0.7, 1],
	[26, 0.8],
	[30, 0.8, 0.5],
	[32, 0],
	[40, 0.5, 0],
	[42, 0.3],
	[48, 0.3, 0.7],
	[50, 1],
	[58, 1, 0],
	[62, 0, 1],
	[70, 0.5, 0],	
	[78, 0]
]

Basic Guide:

  • Use the FunbeatsPrepare script to produce ogg audio files and templated funbeats files from videos. Configure the folders to work with in the script. By default it operates on the videos in its own folder.

  • Use free portable ArrowVortex software to open the ogg file and automatically get offset and BPM values and to listen through the song with a measure counter.
    Go Tempo → Adjust Sync → Find BPM → Apply BPM. Offset should be on the first beat of the first measure. Offset is the inverse of what it says in milliseconds in ArrowVortex, so -0.356 = put 356 offset in your funbeats file.
    Rarely ArrowVortex doesn’t find the correct offset, you can move it manually with the wrench icon, or with the buttons if it’s just on the wrong beat

  • Fill out your funbeats file as you listen through the song in ArrowVortex

  • Run the FunbeatsGenerate script and get your funscripts

  • New files are only generated in subfolders: funbeats_output, funbeats_funscripts

  • You could use Funscript.io to inspect the result

  • To run a Python script you can double-click it if Python is installed properly or Shift+Right click a folder in Explorer to open a PowerShell window, then type “py script.py”

Download

Required: ArrowVortex, plus Python 3 and ffmpeg installed in your system environment path

Guide

In the funbeats file (JSON format) specify how the song should be scripted as a list of segments.

  • “segments” list for patterns & intensity: [measure, pattern, {all optional: start intensity, end intensity, intensity interpolation, stroke interpolation with “stroke” prefix, “ratio” value, “offbeat” keyword}]
  • “center” list to control linear center: [measure, start linear center, end linear center, {center interpolation}]

Center can be omitted to create only a vibration-type script.

  • Measure: Point in time in the song, can be fractional (like 27.25), a measure has 4 beats in most songs (4/4 time signature)
  • Simple pattern: 4 = quarter notes = every beat, 0 = nothing, 8 = eighth notes = 2 per beat etc. 1 = whole note = every measure, 0.5 = every 2 measures etc.
  • Custom pattern: examples: “4: X–X |30%” or “8: 0x55 0x73 xxx5 0000”
    • First number represents note duration just like in simple patterns. 4 notes at 2 duration means it’s a 2 measure pattern, 16 notes at quarters would mean 4 measures etc.
    • Numbers are beats representing intensity multiplier of the baseline intensity, X = 100%, 6 = 60%, 1 = 10%. Skip symbols: -, _ or x (small).
    • After pipe symbol |, you can optionally specify how that scales, so 30% would mean that “0” note is 70% intensity compared to “X” which is the full intensity of the segment. Default is 50%
    • Spaces are ignored and can be used for readability.

  • offbeat” keyword = for example play quarter notes offset by an 8th note (like when your head nods up rather than down)
  • Interpolation is always towards the specified target value until the next segment, not towards the value inside the next segment
  • Interpolation methods: normal easing functions with in/out/inout prefix, such as inquad, outsine, inoutexpo, also linear and smoothstep, they are listed in the script
  • Interpolation can be blended/exaggerated with something like @50% or @220%
  • Can also add “mirrored” keyword to stroke interpolation to use the opposite function on the way down, kind of like a bounce effect (for in/out functions)
  • “null” intensity or omitted = carry over value from previous segment
  • “ratio 0.3” will set stroke ratio for that segment to 30% up, 70% down (so fast/slow), or 30% on, 70% off for a vibration-type script
  • The placement of keywords or interpolation strings doesn’t matter, they are removed after being read
  • You can set range to 100 instead of 1 if you prefer to work with values from 0 to 100 instead of 0 to 1
  • The last specified segment signals the end of the song and isn’t scripted

You can specify constants for any combination of parts of a segment so you can easily adjust several segments of a song at once which you want to be scripted similarly, or non-destructively create multiple versions at different intensities. Nested constants are recursively unwrapped in place, so you could specify intensities, fades, patterns or any combination thereof.

Unlike standard JSON you can write end-of-line comments with #

ArrowVortex: It’s pretty intuitive, use space to play/pause and you can scroll with mouse wheel or up/down and snap with left/right, zoom with ctrl + wheel

There are a few defaults, for example you can emphasize the first/third beat of every measure or set the ratio of on/off duration which translates to up/down speed for linear devices.

There are a few global settings in the scripts which you can customize like what kind of script you want to generate, what speed limits prompt warnings etc.

If a song changes timing of the onset, you can move it with “timeshift” command (rarely needed). Alternatively you can treat it as a separate song with a new offset (see below)
[33.4, “timeshift”, 1, 4] ← move measure onset 1 quarter note forward (1 beat)

For videos with multiple songs, export ogg for the whole video and then get the offsets for each song in ArrowVortex and add the offset information to the list provided in the FunbeatsPrepare script and run it again. Now you will get an ogg for every song and multiple song templates in the funbeats file (If your first offset isn’t accurate, you could do math and add the new offset to the first one you had)

Aside from that you could study the file above and the generated template file to learn about the format but it should be pretty straightforward, most songs don’t really need the more involved features. And note that Python is picky with JSON, for example you can’t leave trailing commas.

Advantages:

  • Easy and fast, can make simple and complex scripts
  • End users can easily adjust funscripts if funbeats files are provided
  • Millisecond precision, frame-independent, no video seeking
  • Python script, easy to modify or customize functionality
  • Can enforce consistent style and script semantically using constants instead of copy-paste

Caveats

  • Could still have edge cases or issues as it is first release
  • Custom pattern functionality in particular is not heavily tested
3 Likes