Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion. Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules. |
7th April 2018, 03:57 | #1 | Link |
Retried Guesser
Join Date: Jun 2012
Posts: 1,373
|
Classic VU meters in AviSynth
Here's a classic VU meter for AviSynth! In two styles!
GetChannel(1).VUmeter("L", shownums=true, dark=false) GetChannel(2).VUmeter("R", shownums=false, dark=true) For those who might not know, VU ("Volume Unit") meters have a slower response than "peak" meters; they are useful because they show the approximate "subjective loudness" rather than the absolute peak audio level. They are better than peak meters for some purposes - such as sound mixing - but peak meters are still needed to avoid digital clipping. The VU meter presented here has auxiliary peak-reading "LED's" which are all you need, most of the time. I'm hoping people will find this useful, or at least fun to play with. An attempt was made to make the meter impulse response (the ballistics) as accurate as possible; (EDIT) after a small tweak, it is probably good enough: Performance-wise, I can watch 30fps HD video in real time, barely. (EDIT new version, much better performance) By default, "0 VU" on this meter corresponds to "-14 dbfs" on a peak meter such as Histogram. Which means -18 on Histogram("audiolevels") shows as -4 VU, which is an IEC standard, I think. There is a 'cal' argument for setting your own preferred reference level. Requires Wilbert's MinMaxAudio (which is 32-bit only) http://wilbertdijkhof.com/ http://forum.doom9.org/showthread.php?t=127530 Requires AviSynth+. Requires a couple of background images; put these in the same folder as the VUmeter script: (first image adapted from vuplayer via Wikipedia; dark image by me) Note the output is RGB32; the pixel format is taken from the ImageSource call. And here is the script itself: save as "VUmeter-01.avsi" Code:
### analog VU meter emulator ## 2018-04-06 raffriff42 ## 2018-04-08 speedup, drop shadow ## requires MinMaxAudio ## http://wilbertdijkhof.com/ ## http://forum.doom9.org/showthread.php?t=127530 ## VU meter references: ## https://en.wikipedia.org/wiki/VU_meter ## https://fr.wikipedia.org/wiki/VU-m%C3%A8tre ## http://www.aes.org/par/v/#VU_meter ## IEC 60268-17 ## https://quod.lib.umich.edu/cgi/p/pod/dod-idx/audio-signal-visualisation-and-measurement.pdf ## ?c=icmc;idno=bbp2372.2014.206;format=pdf ## ## meter ballistics emulated with a 2nd order Butterworth low-pass filter ## https://en.wikipedia.org/wiki/Butterworth_filter ## Digital filter designed by mkfilter/mkshape/gencode ## http://www-users.cs.york.ac.uk/~fisher/mkfilter global g_vu_lite = ScriptDir + "\VUmeter-blank-05.png" ## white BG global g_vu_dark = ScriptDir + "\VUmeter-blank-09.png" ## black BG ################################## ### show a VU meter ## ## @ C - source of audio, framerate and duration (and nothing else) ## (call GetChannel(n) to meter a single channel) ## @ name - a short name; required - MUST be a valid AviSynth variable name, or "" (empty) ## (suggest "L" for left channel, "R" for right, etc) ## @ cal - set dbfs for "0" VU reading; default -14 ## (aprox. IEC std: -4VU @ -18dbfs) ## @ dark - default false, if true, show white-on-dark-background style meter ## ## @ showcal - default true; if false, do NOT note calibration on VU meter ## @ showname - default true; if false, do NOT show name on VU meter ## @ showpeak - always true ## @ shownums - default false; if true, show digital readout of peak level (like Histogram) ## ## NOTE - all options except 'name' apply to all meters in the script (due to reuse of global variables) ## ## @ returns 304x190 clip, RGB32 ## function VUmeter(clip C, string name, float "cal", bool "dark", \ bool "showcal", bool "showname", bool "showpeak", bool "shownums") { showcal = Default(showcal, true) showname = Default(showname, true) showpeak = Default(showpeak, true) global vu_dark = Default(dark, false) global vu_cal = Default(cal, -14.0) global vu_nums = Default(shownums, false) I = ImageSource(vu_dark ? g_vu_dark : g_vu_lite) frnum = C.FrameRateNumerator frnum = (C.FrameRate<22.0) ? 3 * frnum \ : (C.FrameRate<33.0) ? 2 * frnum \ : frnum I = I.Trim(0, length=1) \ .Loop(C.FrameCount) \ .ChangeFPS(frnum, C.FrameRateDenominator) \ .AudioDub(C) ## size 304x190 ## pivot @ 150, 177 Assert(IsClip(I), \ "VUmeter: internal error 10") global N_pre = needle_prerender(I, vu_dark) Assert(IsClip(N_pre), \ "VUmeter: internal error 20") F_pre = \ face_prerender(I, name, vu_cal, vu_dark, showcal, showname, showpeak, 0) \ + face_prerender(I, name, vu_cal, vu_dark, showcal, showname, showpeak, 1) \ + face_prerender(I, name, vu_cal, vu_dark, showcal, showname, showpeak, 2) \ + face_prerender(I, name, vu_cal, vu_dark, showcal, showname, showpeak, 3) Assert(IsClip(F_pre), \ "VUmeter: internal error 30") Eval("global " + name + "_F_pre = F_pre") #return Eval("L_F_pre") ScriptClip(I, """ ## specs: analog levels ## - Reference Level (typically +4dBu, valid with tones only) ## - Standard Output Level (10dB above Reference, typical peak levels) ## - Clip Level (6dB above Standard Output Level, "headroom") ## - average (sqrt of rms) ## specs: ballistics ## - rising to 99% of "0VU" w/ 1 kHz sine wave for 300 ms ## - fall time same as rise time ## - overshoot not less than 1% and not more than 1.5% ## (tr ~= 0.35/BW) ## (BW ~= 0.35/tr ~= 1.167 Hz) Assert(IsClip, \ "VUmeter: internal error 40") xv0 = 0.0 xv1 = 0.0 xv2 = 0.0 yv0 = 0.0 yv1 = 0.0 yv2 = 0.0 alow = 0.0 ## lowpass result for (n=(-16), 0) { afrm = Slip(n).AudioRMS afrm = Pow(10.0, afrm/20.0) ## log to lin afrm = Pow(afrm, 0.5) ## rms to avg ## Digital filter designed by mkfilter/mkshape/gencode A.J. Fisher ## Command line: /www/usr/fisher/helpers/ ## mkfilter -Bu -Lp -o 2 -a 1.95e-02 0.0 -l ## (manually tweaked to obtain overshoot) xv0 = xv1 xv1 = xv2 xv2 = afrm / 289.849123 yv0 = yv1 yv1 = yv2 yv2 = (xv0 + xv2) \ + 2 * xv1 \ + (-0.8750 * yv0) \ + ( 1.8271 * yv1) alow = yv2 } alow = Max(0.0, alow) alow = 20.0 * Log10(alow) ## lin to log ## calibrate: set 0 VU == 'cal' dbfs vu_level = 2*alow - vu_cal + 19.5 ## backgound "faceplate" with prerendered peak LED's amax = AudioMax amax = Max(-96.0, amax) B = (amax>(-1.99)) ? """ + name + """_F_pre.Trim(3, length=1) \ : (amax>(-3.99)) ? """ + name + """_F_pre.Trim(2, length=1) \ : (amax>(-5.99)) ? """ + name + """_F_pre.Trim(1, length=1) \ : """ + name + """_F_pre.Trim(0, length=1) B.Loop(FrameCount) Assert(IsClip, \ "VUmeter: internal error 50") ## numeric readout cnul = $ff000000 ## transparent color (!vu_nums) ? Last \ : Subtitle(String(amax, "%0.1f"), size=28, \ text_color=$808080, halo_color=cnul, \ align=3, x=172, y=106) ## VU needle aavg = Float(Min(Max(-40, vu_level), 20)) N = N_pre.Trim(-Round(db_to_deflection(aavg))+55, length=1) Assert(IsClip(N), \ "VUmeter: internal error 60") R = Overlay(N, mask=N.ShowAlpha) Assert(IsClip(R), \ "VUmeter: internal error 70") return R """) Assert(IsClip, \ "VUmeter: internal error 90") (FrameRateNumerator==C.FrameRateNumerator) ? Last \ : ChangeFPS(C) return Last.Trim(0, length=C.FrameCount) } ################################## ### creates an RGB32 animation of needle deflection ## ## frame 0: 55deg left; frame 55: 0deg; frame 110: 55deg right ## function needle_prerender(clip C, bool dark) { BlankClip(C, pixel_type="RGB32", color=$0) Animate(0, 110, "needle", \ -55, dark, \ 55, dark) return Trim(0, length=111) #.ShowAlpha function needle(clip C, int angle, bool dark) { pivx = 150 ## pivot point pivy = 177 ## ' ' offx = 2 ## shadow offset offy = 6 ## ' ' cpt1 = (dark ? $c04040 : $0) ## pointer color cpt2 = transparent_color(0.5, cpt1) ## pointer border cnul = $ff000000 ## transparent ## needle N = C.subtitle_alpha("l", \ size=210, font_width=3, align=5, x=pivx, y=pivy, \ text_color=cpt1, halo_color=cpt2, \ font_angle=Float(-angle)) ## drop shadow DS = C.Subtitle("l", \ size=210, font_width=20, align=5, x=pivx+offx, y=pivy+offy, \ text_color=$0, halo_color=cnul, \ font_angle=Float(-angle)) \ .Blur(1.0).Blur(1.0).Blur(1.0) ## drop shadow alpha ds_alpha = (dark) ? $454545 : $454545 DA = C.BlankClip(pixel_type="YV12") \ .Subtitle("l", \ size=210, font_width=20, align=5, x=pivx+offx, y=pivy+offy, \ text_color=ds_alpha, halo_color=cnul, \ font_angle=Float(-angle)) \ .Blur(1.0).Blur(1.0).Blur(1.0) \ .ColorYUV(levels="TV->PC") M = Overlay( \ N.ShowAlpha("YV12"), \ DA, \ mode="add") N2 = Overlay(DS, N, mode="lighten", mask=M) return MergeARGB(M, N2, N2, N2) #\ .Subtitle(String(angle)) } } ################################## ### 1-frame clip showing the "faceplate" with selected number of peak LED's lit ## ## @ showpeak - always TRUE (to support FALSE, need to create faceplate images without bezels) ## function face_prerender(clip C, string name, float cal, bool dark, \ bool showcal, bool showname, bool showpeak, int peak) { ctxt = $707070 ## text cnul = $ff000000 ## transparent color cred = $e00000 ## LED red cyel = $e0e000 ## LED yellow coff = $101030 ## LED off pksz = 14.0 ## LED size fcir = "n" ## Marlett filled circle x1 = 220 ## LED positions x2 = x1 + (1 * 22) x3 = x1 + (2 * 22) y1 = 116.5 y2 = y1 y3 = y1 C ## channel name (!showname) ? Last \ : Subtitle(name, size=36, x=24, y=100, \ text_color=ctxt, halo_color=cnul) ## calibration scal = String(cal, "%0.0f dbfs = 0 VU") (!showcal) ? Last \ : Subtitle(scal, size=12, x=24, y=Height-12, \ text_color=ctxt, halo_color=cnul) ## peak indicators (peak<1) ? Last \ : Subtitle(fcir, font="Marlett", size=pksz, \ text_color=cyel, halo_color=cnul, \ x=x1, y=y1, align=5) (peak<2) ? Last \ : Subtitle(fcir, font="Marlett", size=pksz, \ text_color=cyel, halo_color=cnul, \ x=x2, y=y2, align=5) (peak<3) ? Last \ : Subtitle(fcir, font="Marlett", size=pksz, \ text_color=cred, halo_color=cnul, \ x=x3, y=y3, align=5) return Trim(0, length=1) } ################################## ## dB to deflection angle (HACK) function db_to_deflection(float dblevel) { return Spline(dblevel, \ -40, 55, \ -24, 50, \ -20, 47, \ -10, 34.5, \ -7, 25, \ -5, 16, \ -3, 4, \ -2, -2.5, \ -1, -10, \ 0, -19, \ 1, -28, \ 2, -38.5, \ 3, -49.5, \ 5, -54, \ 20, -55, \ cubic=false) } ################################## ### Like Subtitle, but with alpha information ## All arguments are identical to Subtitle, except: ## @ C - must be RGB32 ## @ halo_width - if < 1, halo is disabled; ## if >= 1, same as standard Subtitle halo. ## (default = 1) ## ## @ returns RGB32 subtitle with mask channel ## function subtitle_alpha(clip C, string text, float "x", float "y", \ int "first_frame", int "last_frame", \ string "font", int "size", \ int "text_color", int "halo_color", \ int "align", int "spc", int "lsp", \ float "font_width", float "font_angle", \ bool "interlaced", int "halo_width") { Assert(C.IsRGB32, \ "subtitle_alpha: source clip must be RGB32") text_color = Default(text_color, $ffff00) halo_color = Default(halo_color, $0) halo_width = Default(halo_width, 1) text_alpha = text_color.BitRShiftU(24).BitAnd($ff) halo_alpha = halo_color.BitRShiftU(24).BitAnd($ff) R = C.Subtitle(text, x, y, first_frame, last_frame, \ font, size, text_color, halo_color, align, \ spc, lsp, font_width, font_angle, interlaced) text_color = MakeRGBA(text_alpha, text_alpha, text_alpha, $0) halo_color = MakeRGBA(halo_alpha, halo_alpha, halo_alpha, $0) M = C.BlankClip(pixel_type="YV12") \ .Subtitle(text, x, y, first_frame, last_frame, \ font, size, $ffffff, (halo_width<1 ? $0 : $ffffff), align, \ spc, lsp, font_width, font_angle, interlaced) \ .ColorYUV(levels="TV->PC") return MergeARGB(M, R, R, R) } ####################################### ### given 'R', 'G', 'B', 'A' return an Avisynth color ## function MakeRGBA(int r, int g, int b, int a) { r = Min(Max(0, r), 255) g = Min(Max(0, g), 255) b = Min(Max(0, b), 255) a = Min(Max(0, a), 255) return a.BitLShift(24) \ .BitOr(r.BitLShift(16)) \ .BitOr(g.BitLShift(8)) \ .BitOr(b) } ####################################### ### "slip" (advance or delay) a clip in time. ## Adds head or tail padding; clip is normally Trimmed later. ## ## @ C - clip to be advanced or delayed ## @ offset - if positive, clip is advanced; ## if negative, clip is delayed ## function Slip(clip C, int offset) { lenTrim = (offset > 0) ? offset : 0 lenPad = (offset < 0) ? -offset : 0 C = (lenPad==0) ? C \ : C.Trim(0, -1).Loop(lenPad) + C.Trim(0, C.Framecount-lenPad) C = (lenTrim==0) ? C \ : C.Trim(lenTrim, 0) + C.Trim(C.Framecount-lenTrim, -1).Loop(lenTrim) return C } ####################################### ### given Avisynth color, set its transparency ## @ a - 0.0 = transparent, 1.0 = opaque. ## function transparent_color(float a, int color) { a = 255 - Min(Max(0, Round(a * 256.0)), 255) return BitOr(a.BitLShift(24), BitAnd(color, $ffffff)) } ##### Last edited by raffriff42; 9th April 2018 at 02:07. Reason: version 2 - speedup; post #15 bugfix; added Asserts (2) |
7th April 2018, 05:07 | #2 | Link |
47.952fps@71.928Hz
Join Date: Mar 2011
Posts: 940
|
Awesome! Will have to use this next time I watch something where they break in to a place that has sound sensitive serurity measure (Mission: Impossible, mayhaps?)
__________________
Win10 (x64) build 19041 NVIDIA GeForce GTX 1060 3GB (GP106) 3071MB/GDDR5 | (r435_95-4) NTSC | DVD: R1 | BD: A AMD Ryzen 5 2600 @3.4GHz (6c/12th, I'm on AVX2 now!)
|
7th April 2018, 23:19 | #6 | Link |
Formerly davidh*****
Join Date: Jan 2004
Posts: 2,492
|
It just needs a shadow under the indicator, then it'll be perfect...
Edit: forgive my ignorance, because I've always avoided scriptclip like the plague, but doesn't it create and new destroy new filter instances (for subtitle, for example) for every frame? Is that where some of the slowness might come from? Last edited by wonkey_monkey; 7th April 2018 at 23:22. |
7th April 2018, 23:38 | #7 | Link |
Retried Guesser
Join Date: Jun 2012
Posts: 1,373
|
>It just needs a shadow under the indicator, then it'll be perfect
It would be nice, but then it would be even slower. I dunno, I might test it. >Is [scriptclip] where some of the slowness might come from? Yes, but MinMaxAudio requires it. I think what slows this script the most is accessing 16 frames of audio per video frame, for the smoothing filter. I could possibly process at half frame rate, accessing only 8 frames, but the needle movement will not be as smooth. If this were rewritten as a plugin it could be a lot faster (anybody interested?) |
8th April 2018, 00:16 | #9 | Link |
Retried Guesser
Join Date: Jun 2012
Posts: 1,373
|
It is fixed at 16. The variation is held in check by keeping the framerate at approximately 60fps. If framerate < 22 it is multiplied by 3, else if framerate < 33 it is multiplied by 2. I have not tested it at every framerate yet; maybe a more precise conversion is needed. It's possible the filter section needs a total rethink...
Last edited by raffriff42; 8th April 2018 at 00:25. |
8th April 2018, 00:33 | #10 | Link | |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
Quote:
EDIT: Raff, how bout some 3D parallax error. (great job young man ) [EDIT: me jests, I dont give a damn about HHHHD whatever, 3D is certainly not summick I care bout]. EDIT: In recent Duplicity thread, was getting ~160FPS using scriptclip and massive amount of decisions(at times) in scriptclip, on my crap core duo. RT_Subtitle is way way faster for metrics, if you want pretty, then use builtin subtitle (and take a nap). EDIT: David (or should I say Wonky_Winky), for Scriptclip, pre-prepare eg 4 clips as subtitled, and in scriptclip, (or conditional filter or whatever), select which of those that you require, does away with construction at every frame, pretty fast, give it a try. I personally prefer to use GScript capability within Scriptclip rather than loads of Conditional/this/that/the other, is well fast if set up well.
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? Last edited by StainlessS; 8th April 2018 at 02:10. |
|
8th April 2018, 02:51 | #11 | Link |
Retried Guesser
Join Date: Jun 2012
Posts: 1,373
|
Good ideas. I was thinking you could pre-render the needle (with drop shadow) at all required angles (~100 total) and store the images in an array -- I think you do something like that for the font characters in RT_Subtitle??
|
8th April 2018, 13:22 | #13 | Link | ||
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
Quote:
Quote:
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? |
||
8th April 2018, 16:07 | #15 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
Should 'dark' here be vu_dark ? [EDIT: Using defaulted values]
Code:
global N_pre = needle_prerender(I, dark) #return N_pre F_pre = \ face_prerender(I, name, cal, dark, showcal, showname, showpeak, 0) \ + face_prerender(I, name, cal, dark, showcal, showname, showpeak, 1) \ + face_prerender(I, name, cal, dark, showcal, showname, showpeak, 2) \ + face_prerender(I, name, cal, dark, showcal, showname, showpeak, 3) EDIT: Cal looks like will have similar probs. EDIT: To below, thats very nifty raff, very nice.
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? Last edited by StainlessS; 8th April 2018 at 16:50. |
8th April 2018, 21:51 | #17 | Link |
Registered User
Join Date: Mar 2012
Location: Texas
Posts: 1,664
|
@raffriff42
Thank you for the script! I'm always interested in more visual audio tools for AviSynth . Btw, here's the original program where the VU meter picture came from: http://www.vuplayer.com/other.php A derivative of the the original analog meter by Ernest Turner: https://www.gearslutz.com/board/show...&postcount=22& |
8th April 2018, 22:45 | #19 | Link |
Formerly davidh*****
Join Date: Jan 2004
Posts: 2,492
|
Is there any functional use to be made of the needle's overshoot, or is that just aesthetics?
I'm still grappling with all the scriptclip and animate stuff. Doesn't animate() in needle_prerender also construct a new instance of subtitle() for each frame? Isn't there now a reliance on Avisynth caching to not keep calling/create instances? |
8th April 2018, 23:34 | #20 | Link |
Retried Guesser
Join Date: Jun 2012
Posts: 1,373
|
>Is there any functional use to be made of the needle's overshoot, or is that just aesthetics?
It was carefully studied back in the 1930s, and they felt a little overshoot was the way to go (so yes, aesthetics) (the one with extreme overshoot must have been interesting to work with...) ("new VI" = VU meter) (source pdf) >Doesn't animate() in needle_prerender also construct a new instance of subtitle() for each frame? Yes, but only on filter startup, not at runtime like before. Last edited by raffriff42; 9th April 2018 at 02:13. Reason: typo |
Thread Tools | Search this Thread |
Display Modes | |
|
|