I have used funscript dancer in the past and i really liked it, though it was very crash-y.
Now i don’t really know any programming since i am just a stupid mechanical engineer and not a software dev, and frankly i also dont really have the time to delve too deep into programming ( matlab is already enough for me xD) so i am asking gpt for stuff until it somewhat works.
it is a lot faster than the funscript dancer, but also lacking its features.
So if any skilled programmer wants to pick that up please feel free to do so
Here is the powershell script for installing the libraries, save it as .ps1 and run it with powershell
$packages = @(
"opencv-python",
"librosa",
"moviepy"
)
foreach ($package in $packages) {
pip install $package
}
Here is the code for the python script save that as .py and run it after installing the requierements.
Its straight forward, select your video/audiofile and it will do stuff for you. The shift slider is not really working from what i have seen but the amplitude is. You can also modify the base amplitude in the script itself, or make the slider bigger/ smaller. I dont know if the moan and slap functions work as imagined, but even without its mostly good enough.
import tkinter as tk
from tkinter import filedialog
import librosa
import numpy as np
import moviepy.editor as mp
import json
from scipy.signal import find_peaks
def generate_funscript(audio_waveform, sample_rate, shift, amplitude, min_time_between_positions=200, max_points=5000):
# Apply shift and amplitude modification
shifted_waveform = np.roll(audio_waveform, shift)
scaled_waveform = shifted_waveform * amplitude
# Perform downsampling to limit the number of points
downsample_factor = max(len(scaled_waveform) // max_points, 1)
downsampled_waveform = scaled_waveform[::downsample_factor]
# Generate funscript
actions = []
prev_timestamp = -min_time_between_positions # Initialize prev_timestamp to ensure the first action is written
for i, amplitude in enumerate(downsampled_waveform):
timestamp = int((i * downsample_factor / sample_rate) * 1000) # Convert time to milliseconds
intensity = int((amplitude * 200) + 50) # Scale amplitude to intensity range
# Check if the time difference between consecutive positions is less than the minimum
if timestamp - prev_timestamp < min_time_between_positions:
timestamp = prev_timestamp + min_time_between_positions
actions.append({"at": timestamp, "pos": intensity})
prev_timestamp = timestamp
funscript = {"actions": actions}
output_funscript = 'output.funscript'
with open(output_funscript, 'w') as f:
json.dump(funscript, f)
print(f'Funscript generated: {output_funscript}')
def detect_moans_slaps(audio_waveform, sample_rate):
# Example: Simple peak detection for demonstration purposes
peaks, _ = find_peaks(audio_waveform, distance=sample_rate) # Adjust distance as needed
return peaks
def open_file():
filename = filedialog.askopenfilename(filetypes=[("Video files", "*.mp4")])
if filename:
video_file_entry.delete(0, tk.END)
video_file_entry.insert(0, filename)
def generate_funscript_from_video():
video_file = video_file_entry.get()
if video_file:
# Load video file and extract audio
video = mp.VideoFileClip(video_file)
output_audio_file = 'output_audio.wav'
video.audio.write_audiofile(output_audio_file)
# Load extracted audio
audio_waveform, sample_rate = librosa.load(output_audio_file, sr=None, mono=True)
# Detect moans and slaps
moan_slap_timestamps = detect_moans_slaps(audio_waveform, sample_rate)
# Get shift and amplitude values from sliders
shift_value = shift_slider.get()
amplitude_value = amplitude_slider.get()
# Generate funscript with modified audio
generate_funscript(audio_waveform, sample_rate, shift_value, amplitude_value)
# Create GUI
root = tk.Tk()
root.title("Funscript Generator")
# Video file selection
video_file_label = tk.Label(root, text="Video File:")
video_file_label.grid(row=0, column=0)
video_file_entry = tk.Entry(root, width=50)
video_file_entry.grid(row=0, column=1)
browse_button = tk.Button(root, text="Browse", command=open_file)
browse_button.grid(row=0, column=2)
# Shift slider
shift_label = tk.Label(root, text="Shift:")
shift_label.grid(row=1, column=0)
shift_slider = tk.Scale(root, from_=0, to=1000, orient=tk.HORIZONTAL)
shift_slider.grid(row=1, column=1)
# Amplitude slider
amplitude_label = tk.Label(root, text="Amplitude:")
amplitude_label.grid(row=5, column=0)
amplitude_slider = tk.Scale(root, from_=0.1, to=5.0, resolution=0.1, orient=tk.HORIZONTAL)
amplitude_slider.grid(row=5, column=1)
# Generate button
generate_button = tk.Button(root, text="Generate Funscript", command=generate_funscript_from_video)
generate_button.grid(row=3, column=0, columnspan=3)
root.mainloop()
Here is the funscript i have done with the python script and the corresponding video
ashe vs wraith
ashe vs wraith hmv.funscript (131.2 KB)