Log in

View Full Version : Faveworm: analog oscilloscope emulation


cretindesalpes
25th December 2024, 16:59
Another plug-in, this time an audio waveform renderer with an analog oscilloscope look.

>>> faveworm-r2.zip <<< (http://ldesoras.fr/src/vs/faveworm-r2.zip)

http://ldesoras.fr/pic/faveworm-mode0-pp-small.png (http://ldesoras.fr/pic/faveworm-mode0-pp.png)
Faveworm in trigger mode with waveform locking, post-processed output.

Note that in the previous screenshot, the grid, glowing effects, phosphor persistence and colors are not part of Faveworm. The plug-in generates grayscale pictures that can be used as building blocks for a more elaborate rendering. For example the screenshot was rendered with the following code:

import ctypes
import vapoursynth as vs
core = vs.core

def add_phosphore_persistence (video, mix, decay, nbr_frames):
if nbr_frames > 0:
v1 = video
for i in range (nbr_frames - 1):
v1 = core.std.Merge (v1, v1 [0] + v1, weight=decay)
video = core.std.Merge (video, v1 [0] + v1, weight=decay*mix)
return video

def blur_gaussian (video, radius):
npass = 4
rem = radius * 2
for p in range (npass):
r = round (rem / (npass - p))
if r > 0:
video = core.std.BoxBlur (video, hradius=r, vradius=r)
rem = rem - r
return video

def make_grad_frame (n, f):
f = f.copy ()
w = f.width
h = f.height
nd = 10 # Number of horizontal divisions
ng = 5 # Number of ticks per division
dh = w / nd
dv = dh
ch = w / 2
cv = h / 2
chi = round (ch)
cvi = round (cv)
hg = round (dh * 0.35 / ng)
s = f.get_stride (0) // 4
p = ctypes.cast (f.get_write_ptr (0), ctypes.POINTER (ctypes.c_float))
for y in range (h):
if (y - cv) % dv < 1:
for x in range (w):
p [y * s + x] = 1
elif ((y - cv) * ng) % dv < ng:
for x in range (chi - hg, chi + hg):
p [y * s + x] = 1
for x in range (w):
if (x - ch) % dh < 1:
for y in range (h):
p [y * s + x] = 1
elif ((x - ch) * ng) % dh < ng:
for y in range (cvi - hg, cvi + hg):
p [y * s + x] = 1
return f

def make_graduations (video):
grad = core.std.BlankClip (video, format=vs.GRAYS, length=1)
grad = core.std.ModifyFrame (clip=grad, clips=grad, selector=make_grad_frame)
grad = grad * video.num_frames
return grad



globscale = 2
fx_flag = True
grid_flag = True
glowlim = 3.0

audio = core.bas.Source("audio.wav", track=-1)

video = core.std.BlankClip (fpsnum=30, width=640*globscale, height=480*globscale)
video = core.fw.scope (clip=video, audio=audio, mode=0, sweep=0.020, beam_gain=1, beam_size=1.5*globscale, trig_lock=True, trig_dt=-0.010)

bckg = core.std.BlankClip (video, format=vs.RGBS, color= [0.002428, 0.009134, 0.002428])
beam = core.std.BlankClip (video, format=vs.RGBS, color= [0.5 , 1 , 0.7454 ])
gcol = core.std.BlankClip (video, format=vs.RGBS, color= [0.0015 , 0.0015 , 0.0015 ])

if fx_flag:
video = add_phosphore_persistence (video, 0.1, 0.2, 3)
else:
video = core.resize.Point (video, format=vs.RGBS)

if fx_flag:
scale1 = 2 * globscale
scale2 = 4 * globscale
scale3 = 16 * globscale
# sqrt (tanh (max (x, 0)^2))
#eg = "1 2 x 0 max x * 2 * exp 1 + / - 0.5 pow"
# Equivalent approximated function, with x already clipped:
# 1 - 1 / (1 + x + x^2 + x^3 + x^4 + x^5 + x^7 + x^8) !! no x^6 !!
# 1 - 1 / (((((((x + 1) * x*x + 1) * x + 1) * x + 1) * x + 1) * x + 1) * x + 1)
eg = "1 1 x 0 max dup dup dup dup dup dup dup 1 + * * 1 + * 1 + * 1 + * 1 + * 1 + * 1 + / -"
clip = core.std.Expr (video, eg)
glow = core.std.Expr ([video, clip], "x y - 0 max "+str(glowlim)+" min")
glow1 = blur_gaussian (glow , scale1)
glow2 = blur_gaussian (glow , scale2)
glow3 = blur_gaussian (glow , scale3)
video = core.std.Expr ([clip, glow1, glow2, glow3], "x y 0.5 * + z a + 0.25 * +")

if fx_flag:
video = core.std.MaskedMerge (bckg, beam, video, first_plane=1)

if grid_flag:
grid = make_graduations (video)
grix = core.std.Maximum (grid)
eg = "x y "+str(globscale*0.25+0.125)+" * + 0 max 1 min z 0.5 * - 0 max 1 min"
grid = core.std.Expr ([grid, grix, glow1], eg)
video = core.std.MaskedMerge (video, gcol, grid, first_plane=1)

video = core.fmtc.transfer (video, transs="linear", transd="1886")

video = core.fmtc.matrix (video, mat="709", fulls=True, fulld=True)
video = core.fmtc.resample (video, css="420")
video = core.fmtc.bitdepth (video, bits=8, fulls=True, fulld=False)

video.set_output(0)
audio.set_output(1)

The plug-in supports Vapoursynth and Avisynth+.

There is also a Git repository (https://gitlab.com/EleonoreMizo/faveworm)

cretindesalpes
12th January 2025, 11:52
New release, fixed a couple of bugs. See first post for download link.