Welcome to WSL!

Make yourself at home, but before posting, please may I ask you to read the following topics.


Posting 101
Server space, screenshots, and you

Thank you!

PS. please pretty please:


Image

[Fuse] AudioWaveform

User avatar
JiiPii
Posts: 18
Joined: Tue Mar 03, 2020 8:34 am
Been thanked: 8 times

[Fuse] AudioWaveform

#1

Post by JiiPii » Wed Jun 17, 2020 10:29 am

Hi
I would like to introduce my project to you: AudioWaveform.fuse

Image

When I first started using DR / Fusion, I missed the support for audio. Then I came across the great fuse SuckLessAudioModifier from SecondMan and was thrilled. Finally audio in fusion. I've googled for similar tools, but I'm a newbie and haven't found anything that doesn't mean anything. But I was fixed and so I programmed this fuse.

The play point is set in the middle of the picture.
There are three parameters for setting the waveform's timing: 1) Proxy 2) Zoom 3) Resolution

1) Proxy: This multiplies the sample pool without increasing the number of pixels shown
2) Zoom: This also multiplies the sample pool, but all values ​​are also displayed (as far as can be displayed)
3) Resolution: Acts as a divisor for the displayable pixels -> Resolution = 2 with HD -> 1920/2

The stereo signal (Both) is generated by maximum formation of the two channels.

After loading the data from file, arrays will be filled with nummerical data. The size of the audio file is limited to 10Mb in order not to make the arrays too large.
The audio data can be moved forward or backward by up to 50 frames.
A crosshair can be shown and two different envelopes can be selected.
An extension could now be to filter the curve.

I hope you like the fuse
  1. --[[--
  2.  
  3. AudioWaveform
  4.  
  5. Based on the fuse modifier SuckLessAudioModifier by Pieter Van Houte, this fuse represents the waveform of a PCM 16-bit audio file. The play point is set in the middle of the picture.
  6. There are three parameters for setting the waveform's timing: 1) Proxy 2) Zoom 3) Resolution
  7.  
  8. 1) Proxy: This multiplies the sample pool without increasing the number of pixels shown
  9. 2) Zoom: This also multiplies the sample pool, but all values are also displayed (as far as can be displayed)
  10. 3) Resolution: Acts as a divisor for the displayable pixels -> Resolution = 2 with HD -> 1920/2
  11.  
  12. The stereo signal (Both) is generated by maximum formation of the two channels.
  13.  
  14. After loading the data from file, arrays will be filled with nummerical data. The size of the audio file is limited to 10Mb in order not to make the arrays too large.
  15. The audio data can be moved forward or backward by up to 50 frames.
  16.  
  17. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  18. --]]--
  19.  
  20. version = "AudioWaveform V0.1 - 8 June 2020"
  21.  
  22. -- WAV-Daten
  23. sampledata = {}
  24. sampledataleft = {}
  25. sampledataright = {}
  26.  
  27. FuRegisterClass("AudioWaveform", CT_SourceTool, {
  28.     REGS_Name               = "Audio Waveform",
  29.     REGS_Category           = "Fuses", 
  30.     REGS_OpDescription      = "Audio Waveform",
  31.     REGS_OpIconString       = "AWF",
  32.     REG_Source_GlobalCtrls  = true,
  33.     REG_Source_SizeCtrls    = true,
  34.     REG_Source_AspectCtrls  = true,    
  35.     REG_TimeVariant         = true,
  36.     --REGB_Temporal             = true,         -- ensures reliability in Resolve 15
  37.     REGS_Company            = "JiPi",
  38.     REGS_URL                = "",  
  39.     REG_Version             = 000001,
  40.     })
  41.    
  42.    
  43. -------------------------------------------------------------------
  44. --       Create
  45. -------------------------------------------------------------------
  46. function Create()
  47.    
  48.     -- Audio File auswählen
  49.     InFile = self:AddInput("Wave File", "WaveFile", {
  50.         LINKID_DataType = "Text",
  51.         INPID_InputControl =  "FileControl",
  52.         FC_ClipBrowse = false,              -- true
  53.         INP_DoNotifyChanged  = true,
  54.         FCS_FilterString = "WAV-Files (*.wav)|*.wav|"
  55.         })
  56.    
  57.     Reload = self:AddInput("Reload Sample", "ReloadSample", {
  58.         INPID_InputControl =  "ButtonControl",
  59.         INP_DoNotifyChanged  = true,
  60.         INP_External = false,
  61.         BTNCS_Execute = "tool.ScriptReload[fu.TIME_UNDEFINED] = 1",
  62.         }) 
  63.    
  64.     InChannels = self:AddInput("Select Channel(s)", "SelectChannels", {
  65.         { MBTNC_AddButton = "Left", MBTNCID_AddID = "Left", },
  66.         { MBTNC_AddButton = "Right", MBTNCID_AddID = "Right", },
  67.         { MBTNC_AddButton = "Both", MBTNCID_AddID = "Both", },
  68.         INPID_DefaultID = "Both",
  69.         LINKID_DataType = "FuID",
  70.         INPID_InputControl = "MultiButtonIDControl",
  71.         MBTNC_StretchToFit = true,
  72.         MBTNC_ForceButtons = true,
  73.         IC_Visible = false
  74.         })
  75.  
  76.     InProxy = self:AddInput("Proxy (for sampling)", "Proxy", {
  77.         LINKID_DataType = "Number",
  78.         INPID_InputControl = "SliderControl",
  79.         INP_MinScale = 1,
  80.         INP_MinAllowed = 1,
  81.         INP_MaxScale = 100,
  82.         INP_Default = 25,
  83.         INP_Integer = true,
  84.         })
  85.        
  86.     InAmplitudeScale = self:AddInput("Amplitude Scale", "AmplitudeScale", {
  87.         LINKID_DataType = "Number",
  88.         INPID_InputControl = "ScrewControl",
  89.         INP_Default = 1,
  90.         INP_MaxScale = 5,
  91.         })
  92.  
  93.     InSampleStartFrame = self:AddInput("Sample Start Frame (Time Offset)", "SampleStartFrame", {
  94.         LINKID_DataType = "Number",
  95.         INPID_InputControl = "ScrewControl",
  96.         INP_Default = 0,
  97.         INP_MaxScale = 50,
  98.         INP_MinScale = -50,
  99.         INP_MaxAllowed = 50,
  100.         INP_MinAllowed = -50,
  101.         INP_Integer = true,
  102.         })
  103.        
  104.     InZoom = self:AddInput("Zoom", "Zoom", {
  105.         LINKID_DataType = "Number",
  106.         INPID_InputControl = "SliderControl",
  107.         INP_MinScale = 1,
  108.         INP_MinAllowed = 1,
  109.         INP_MaxScale = 100,
  110.         INP_Default = 2,
  111.         INP_Integer = true,
  112.         })
  113.  
  114.     InResolution = self:AddInput("Resolution", "Resolution", {
  115.         LINKID_DataType = "Number",
  116.         INPID_InputControl = "SliderControl",
  117.         INP_MinScale = 1,
  118.         INP_MinAllowed = 1,
  119.         INP_MaxScale = 10,
  120.         INP_Default = 2,
  121.         INP_Integer = true,
  122.         })
  123.  
  124.     InEnvelope = self:AddInput("Envelope 1", "Envelope", {
  125.         LINKID_DataType = "Number",
  126.         INPID_InputControl = "CheckboxControl",
  127.         INP_Default = 0,
  128.         })     
  129.                
  130.     InEnvelope2 = self:AddInput("Envelope 2", "Envelope2", {
  131.         LINKID_DataType = "Number",
  132.         INPID_InputControl = "CheckboxControl",
  133.         INP_Default = 0,
  134.         })         
  135.                
  136.     InCrosshair = self:AddInput("Crosshair", "Crosshair", {
  137.         LINKID_DataType = "Number",
  138.         INPID_InputControl = "CheckboxControl",
  139.         INP_Default = 0,
  140.         })
  141.                
  142.     InLabel = self:AddInput(version, "version", {
  143.         LINKID_DataType = "Text",
  144.         INPID_InputControl = "LabelControl",
  145.         INP_External = false,
  146.         INP_Passive = true,
  147.         })             
  148.        
  149.        
  150.     -- Layout --   
  151.     self:AddControlPage("Layout")  
  152.         self:BeginControlNest("Waveformline", "WFLine", true, {})
  153.         InThickness = self:AddInput("Thickness", "Thickness", {
  154.             LINKID_DataType    = "Number",
  155.             INPID_InputControl = "SliderControl",
  156.             INP_MinAllowed     = 0.0001,
  157.             INP_MaxScale       = 0.1,
  158.             INP_Default        = 0.002,
  159.             }) 
  160.            
  161.         -- color wheel
  162.         InRed = self:AddInput("Red", "Red", {
  163.             ICS_Name            = "Line Color",
  164.             LINKID_DataType     = "Number",
  165.             INPID_InputControl  = "ColorControl",
  166.             INP_Default         = 1.0,
  167.             INP_MaxScale        = 1.0,
  168.             CLRC_ShowWheel      = false,
  169.             IC_ControlGroup     = 1,
  170.             IC_ControlID        = 0,
  171.             })
  172.         InGreen = self:AddInput("Green", "Green", {
  173.             LINKID_DataType     = "Number",
  174.             INPID_InputControl  = "ColorControl",
  175.             INP_Default         = 1.0,
  176.             IC_ControlGroup     = 1,
  177.             IC_ControlID        = 1,
  178.             })
  179.         InBlue = self:AddInput("Blue", "Blue", {
  180.             LINKID_DataType     = "Number",
  181.             INPID_InputControl  = "ColorControl",
  182.             INP_Default         = 1.0,
  183.             IC_ControlGroup     = 1,
  184.             IC_ControlID        = 2,
  185.             })
  186.         InAlpha = self:AddInput("Alpha", "Alpha", {
  187.             LINKID_DataType     = "Number",
  188.             INPID_InputControl  = "ColorControl",
  189.             INP_Default         = 1.0,
  190.             IC_ControlGroup     = 1,
  191.             IC_ControlID        = 3,
  192.             })     
  193.         self:EndControlNest()  
  194.        
  195.         self:BeginControlNest("Crosshairline", "CHLine", true, {})
  196.         InThicknessCH = self:AddInput("Thickness", "ThicknessCH", {
  197.             LINKID_DataType    = "Number",
  198.             INPID_InputControl = "SliderControl",
  199.             INP_MinAllowed     = 0.0001,
  200.             INP_MaxScale       = 0.1,
  201.             INP_Default        = 0.002,
  202.             }) 
  203.  
  204.     InDotted = self:AddInput("Dotted", "Dotted", {
  205.         LINKID_DataType = "Number",
  206.         INPID_InputControl = "CheckboxControl",
  207.         INP_Default = 0,
  208.         })
  209.        
  210.         -- color wheel
  211.         InRedCH = self:AddInput("Red", "RedCH", {
  212.             ICS_Name            = "Line Color",
  213.             LINKID_DataType     = "Number",
  214.             INPID_InputControl  = "ColorControl",
  215.             INP_Default         = 1.0,
  216.             INP_MaxScale        = 1.0,
  217.             CLRC_ShowWheel      = false,
  218.             IC_ControlGroup     = 2,
  219.             IC_ControlID        = 0,
  220.             })
  221.        
  222.         InGreenCH = self:AddInput("GreenCH", "GreenCH", {
  223.             LINKID_DataType     = "Number",
  224.             INPID_InputControl  = "ColorControl",
  225.             INP_Default         = 1.0,
  226.             IC_ControlGroup     = 2,
  227.             IC_ControlID        = 1,
  228.             })
  229.         InBlueCH = self:AddInput("BlueCH", "BlueCH", {
  230.             LINKID_DataType     = "Number",
  231.             INPID_InputControl  = "ColorControl",
  232.             INP_Default         = 1.0,
  233.             IC_ControlGroup     = 2,
  234.             IC_ControlID        = 2,
  235.             })
  236.         InAlphaCH = self:AddInput("AlphaCH", "AlphaCH", {
  237.             LINKID_DataType     = "Number",
  238.             INPID_InputControl  = "ColorControl",
  239.             INP_Default         = 1.0,
  240.             IC_ControlGroup     = 2,
  241.             IC_ControlID        = 3,
  242.             })     
  243.         --]]--
  244.         self:EndControlNest()
  245.    
  246.        
  247.     -------------------------------------------------------------------
  248.     --FONT STYLE
  249.     -------------------------------------------------------------------
  250.     InFont = self:AddInput("Font", "Font", {
  251.         LINKID_DataType = "Text",
  252.         INPID_InputControl = "FontFileControl",
  253.         IC_ControlGroup = 2,
  254.         IC_ControlID = 0,
  255.         INP_Level = 1,
  256.         INP_DoNotifyChanged = true,
  257.         IC_Visible         = false,
  258.     })
  259.        
  260.     InFontStyle = self:AddInput("Style", "Style", {
  261.         LINKID_DataType = "Text",
  262.         INPID_InputControl = "FontFileControl",
  263.         IC_ControlGroup = 2,
  264.         IC_ControlID = 1,
  265.         INP_Level = 1,
  266.         INP_DoNotifyChanged = true,
  267.         IC_Visible         = false,
  268.     })
  269.  
  270.     InPosition = self:AddInput("Position", "Position", {
  271.         LINKID_DataType = "Point",
  272.         INPID_InputControl = "OffsetControl",
  273.         INPID_PreviewControl = "CrosshairControl",
  274.         INP_DoNotifyChanged = true,
  275.         IC_Visible         = false,
  276.         }) 
  277.        
  278.        
  279.     -- In/Out --
  280.     --[[--
  281.     InChannel0 = self:AddInput("iChannel0", "iChannel0", {
  282.         LINKID_DataType = "Image",
  283.         LINK_Main = 1,
  284.         INP_Required = false
  285.         })     
  286.     --]]--
  287.     OutImage = self:AddOutput("Output", "Output", {
  288.         LINKID_DataType = "Image",
  289.         LINK_Main = 1,
  290.         INP_Required = false           
  291.         }) 
  292. end
  293.  
  294. -------------------------------------------------------------------
  295. --       NotifyChanged
  296. -------------------------------------------------------------------
  297. function NotifyChanged(inp, param, time)
  298.        
  299.     if inp ~= nil and param ~= nil then
  300.        
  301.         if inp == InFile then  
  302.             local filedata = readAll(param.Value)
  303.             if not filedata then
  304.                 return
  305.             else
  306.                 local header = getHeader(filedata, 104)       -- Anpassen !!!!
  307.                 if header[13] == "74" then
  308.                     temp_channel = header[59]                 -- Junk-Format
  309.                 else
  310.                     temp_channel = header[23]                 -- Wave Format
  311.                 end    
  312.            
  313.                 if temp_channel == "2" then
  314.                     InChannels:SetAttrs({IC_Visible = true})
  315.                 else
  316.                     InChannels:SetAttrs({IC_Visible = false})
  317.                 end
  318.             end
  319.         end
  320.     end
  321.    
  322.     -------------------------FONT STYLE
  323.     if inp == InFont then
  324.         local f = param.Value
  325.        
  326.         if f == nil or string.len(f) == 0 then
  327.             InFont:SetSource(Text("Open Sans"), time)
  328.         end
  329.     elseif inp == InFontStyle then
  330.         local f = param.Value
  331.        
  332.         if f == nil or string.len(f) == 0 then
  333.             InFontStyle:SetSource(Text("Regular"), time)
  334.         end
  335.     end
  336.    
  337. end
  338.  
  339. -------------------------------------------------------------------
  340. --       Functions
  341. -------------------------------------------------------------------
  342. -- function for reading in a binary file
  343. function readAll(file)
  344.     local f = io.open(file, "rb")
  345.     if not f then
  346.         return
  347.     end
  348.     local content = f:read("*all")
  349.     local size = f:seek("end")      -- Abfrage auf Dateigröß -> Rückgabe nil
  350.  
  351.     f:close()
  352.     if (size > 10485760) then
  353.         return nil
  354.     else
  355.         return content
  356.     end
  357.  
  358. end
  359.  
  360. -- function for creating the header table, returns a table
  361. function getHeader(data, length)
  362.     local header = {}
  363.     for pos = 1, length do
  364.         b = string.byte(data, pos) -- create a string one byte long
  365.         table.insert(header, string.format("%i", b))
  366.     end
  367.     return header
  368. end
  369.  
  370.  
  371. --function to read a pair of bytes and put them together to form a 16 bit two's complement sample, then return the result converted to decimal numbers
  372. function sampleRead(data, first, second, complement)
  373.    
  374.     bl = string.byte(data, second) -- create a string one byte long
  375.     bh = string.byte(data, first) -- create a string one byte long
  376.     number_byte = string.format("%i", bl)
  377.     number_int  = string.format("%i", bh)*256 + bl
  378.        
  379.     if ( complement == 1 and number_int > 32767 )    then -- basically, if the bytelong binary starts with 1
  380.         sample = number_int - 65536
  381.     else
  382.         sample = number_int
  383.     end
  384.  
  385.     return sample
  386. end
  387.  
  388. -- #####################################################################
  389. -- function for reading all the required sample data for a given frame, taking into account bitrate and channels (mono,stereo) and returning the result in a table
  390. -- #####################################################################
  391. function getSampleData(filedata, channelchoice, framerate, timescale, currenttime, startframe, proxy, zoom, channels, bitrate,resolution)
  392.        
  393.     -- Initialize tables needed for WAVE data.
  394.     local sampledata = {}
  395.    
  396.     local spf = math.floor(bitrate/framerate) -- calculate the amount of samples per frame
  397.    
  398.     for pos = ((currenttime+startframe)*spf)-960*zoom*proxy/resolution, ((currenttime+startframe)*spf)+960*zoom*proxy/resolution-1, zoom*proxy do
  399.         if (pos < 1) then
  400.             table.insert(sampledata, 0)   -- Data before Startpoint
  401.         else
  402.             zoomdata = {}
  403.                
  404.             for j = 1,zoom,1 do
  405.                
  406.                 table.insert(zoomdata, filedata[pos+j])
  407.                 werte = {}
  408.                 if (pos < 8) then
  409.                     for i=1,4,1 do
  410.                         if zoomdata[i] ~= nil then
  411.                             table.insert(werte,zoomdata[i])
  412.                         else
  413.                             table.insert(werte,0)
  414.                         end
  415.                     end
  416.                 end
  417.             end    
  418.             table.insert(sampledata, getMax(zoomdata) )
  419.         end
  420.     end
  421.    
  422.     return sampledata
  423. end
  424. -- ############################################################################################### 
  425. -- function for returning the signed maximum of a table
  426. function getMax(data)
  427.     local max = 0
  428.     for i,v in ipairs(data) do
  429.         if math.abs(v) > math.abs(max) then
  430.             max = v
  431.         end
  432.     end
  433.     return max
  434. end
  435.  
  436. ----------------------------------------------------------------------------------------------------------------------------------------
  437. function drawstring(img, font, style, size, justify, quality, text,x,y)
  438.    
  439.     local font = TextStyleFont(font, style)
  440.     local tfm = TextStyleFontMetrics(font)
  441.  
  442.     local line_height = (tfm.TextAscent + tfm.TextDescent + tfm.TextExternalLeading) * 10 * size
  443.        
  444.     local mat = Matrix4()
  445.           mat:Scale(1.0/tfm.Scale, 1.0/tfm.Scale, 1.0)
  446.           mat:Scale(size, size, 1)
  447.    
  448.     local ch, prevch
  449.  
  450.     local shape = Shape()
  451.     local shh = Shape()
  452.    
  453.     local x_move = 0
  454.           mat:Move(x,y, 0)
  455.        
  456.     for line in string.gmatch(text, "%C+") do
  457.  
  458.         local line_width = 0
  459.         for i=1,#line do
  460.             ch = line:sub(i,i):byte()
  461.             line_width = line_width + tfm:CharacterWidth(ch)*10*size
  462.         end
  463.        
  464.         if justify == 0 then
  465.        
  466.         elseif justify == 1 then
  467.             mat:Move(-line_width/2, 0, 0)
  468.         elseif justify == 2 then
  469.             mat:Move(-line_width, 0, 0)
  470.         end
  471.    
  472.         for i=1,#line do
  473.             prevch = ch
  474.                
  475.             ch = line:sub(i,i):byte()
  476.             local cw = tfm:CharacterWidth(ch)*10*size
  477.  
  478.             if prevch then
  479.                 x_offset = tfm:CharacterKerning(prevch, ch)*10*size
  480.            
  481.                 x_move = x_move + x_offset
  482.                 mat:Move(x_offset, 0, 0)
  483.             end
  484.            
  485.             mat:Move(cw/2, (-size/2)/2, 0)
  486.             x_move = x_move + cw
  487.                        
  488.             shh = tfm:GetCharacterShape(ch, false)
  489.             shh = shh:TransformOfShape(mat)
  490.             shape:AddShape(shh)
  491.                                
  492.             mat:Move(cw/2, (size/2)/2, 0)
  493.        
  494.         end
  495.     end
  496.  
  497.     return shape
  498. end
  499. --]]--
  500. -- $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  501.  
  502. -------------------------------------------------------------------
  503. --       Process
  504. -------------------------------------------------------------------
  505. function Process(req)
  506.  
  507.     -- VARIABLES
  508.     --------------------------------------
  509.     -- Build the Image
  510.     --------------------------------------
  511.     local realwidth = Width;
  512.     local realheight = Height;
  513.    
  514.     -- We'll handle proxy ourselves
  515.     Width = Width / Scale
  516.     Height = Height / Scale
  517.     Scale = 1
  518.    
  519.     -- Attributes for new images
  520.     local imgattrs = {
  521.         IMG_Document = self.Comp,
  522.         { IMG_Channel = "Red", },
  523.         { IMG_Channel = "Green", },
  524.         { IMG_Channel = "Blue", },
  525.         { IMG_Channel = "Alpha", },
  526.         IMG_Width = Width,
  527.         IMG_Height = Height,
  528.         IMG_XScale = XAspect,
  529.         IMG_YScale = YAspect,
  530.         IMAT_OriginalWidth = realwidth,
  531.         IMAT_OriginalHeight = realheight,
  532.         IMG_Quality = not req:IsQuick(),
  533.         IMG_MotionBlurQuality = not req:IsNoMotionBlur(),
  534.         }  
  535.    
  536.     -- Initialize the images
  537.     --local img = Image(imgattrs)
  538.     local out = Image(imgattrs)
  539.     local p = Pixel({R=0,G=0,B=0,A=0})
  540.     --img:Fill(p) -- Clear the image so the next frame doesn't contain the previous one.
  541.     out:Fill(p)
  542.  
  543.    
  544.    
  545.     -------------------------------------
  546.     --sample 1 processing settings
  547.     --------------------------------------
  548.     local infile = InFile:GetValue(req).Value
  549.     local channelchoice = InChannels:GetValue(req).Value
  550.     local proxy = InProxy:GetValue(req).Value
  551.     --------------------------------------
  552.     local scale = InAmplitudeScale:GetValue(req).Value
  553.     local startframe = InSampleStartFrame:GetValue(req).Value
  554.    
  555.     local zoom = InZoom:GetValue(req).Value
  556.     local resolution = InResolution:GetValue(req).Value
  557.     local crosshair = InCrosshair:GetValue(req).Value
  558.    
  559.     local envelope = InEnvelope:GetValue(req).Value
  560.     local envelope2 = InEnvelope2:GetValue(req).Value
  561.    
  562.     local data_pos = 45    
  563.            
  564.     local currenttime = req.Time -- get the current frame of the comp
  565.     local framerate = self.Comp:GetPrefs("Comp.FrameFormat.Rate") -- get the frame rate of the comp set in the preferences
  566.    
  567.     local font      = InFont:GetValue(req).Value
  568.     local style     = InFontStyle:GetValue(req).Value
  569.    
  570.     if not filedata or infiledup ~= infile then -- got to load some audio files to play with :)
  571. --      print ("loading")
  572.         filedata = readAll(infile)
  573.        
  574.         if not filedata  then
  575.             print("Please load an audio WAV-File. Size less than 10 MB")
  576.            
  577.             local p = Pixel({R=0,G=0,B=0,A=1})
  578.             out:Fill(p)
  579.             -- Image with Text
  580.             local ic = ImageChannel(out, 8)
  581.             local fs = FillStyle()
  582.             local cs = ChannelStyle()
  583.                
  584.             ic:SetStyleFill(fs)
  585.        
  586.             local sh = Shape()
  587.        
  588.             local shape = {}
  589.                              
  590.             shape = drawstring(img, font, style, 0.15, 1, 32, "AudioWaveform",0.5,0.4)
  591.             shape2 = drawstring(img, font, style, 0.1, 1, 32, "Please load Audiofile",0.5,0.2)
  592.             shape:AddShape(shape2)
  593.            
  594.             ic:ShapeFill(shape)
  595.             ic:PutToImage("CM_Merge", cs)
  596.             OutImage:Set(req, out)
  597.                    
  598.             return
  599.         else       
  600.             infiledup = infile
  601.             --------------------------------------
  602.             -- Read Header
  603.             --------------------------------------
  604.             -- create a table containing the header of the file. This will then be used to get file type, sample rate etc. In wave file the header is stored in the first 44/104 bytes of the file.
  605.             local header = getHeader(filedata, 104) -- Org 44 - Junk 104
  606.            
  607.             --get some vital information out of the header, to do some form of file format checking
  608.             if header[3] == header[4] and header[17] == "16" then
  609.                            
  610.                 bitrate = sampleRead(filedata, 26, 25, 0)
  611.                 channels = tonumber(header[23])
  612.             else
  613.                 -- Junk-Format ?
  614.                 data_pos = string.find(filedata, "data")
  615.                 data_pos = data_pos + 8                   -- 4 bytes "data" und 4 byte Size
  616.                        
  617.                 if header[13] == "74" and data_pos ~= nil then
  618.                     -- Junk-Format
  619.                     bitrate = sampleRead(filedata, 62, 61, 0)
  620.                     channels = tonumber(header[59])
  621.                    
  622.                     --print ("########JUNK#########")                                      
  623.                 else
  624.                     print ("Unsupported file type.\nPlease only load of type WAV (Microsoft) signed 16-bit PCM.")
  625.                     return nil
  626.                 end
  627.             end        
  628.             -- Audiofile successfull loaded
  629.             frame_number = ((#filedata/2/channels)/bitrate) * self.Comp:GetPrefs("Comp.FrameFormat.Rate")
  630.             fn_str = string.format("Audio(%g kHz) %d frames", bitrate/1000, frame_number)
  631.             InLabel:SetAttrs({LINKS_Name = fn_str,})
  632.            
  633.             --------------------------------------
  634.             -- Fill the sampledata array with the audiodata from File
  635.             --------------------------------------
  636.             -- Clear Samplearrays
  637.             sampledata = {}     -- Mono and both channels
  638.             sampledataleft = {}
  639.             sampledataright = {}
  640.             collectgarbage()
  641.            
  642.             if (channels == 1) then -- Monofile
  643.                 for x = data_pos, math.min(#filedata,10485760), 2 do
  644.                     table.insert(sampledata, sampleRead(filedata, x+1, x, 1)/65535)
  645.                 end
  646.             else
  647.                 for x = data_pos, math.min(#filedata,10485760), 4 do
  648.                    
  649.                     left = sampleRead(filedata, x+1, x, 1)/65535
  650.                     right = sampleRead(filedata, x+3, x+2, 1)/65535
  651.                     table.insert(sampledataleft, left )
  652.                     table.insert(sampledataright, right )
  653.                    
  654.                     if math.abs(left) > math.abs(right) then    -- beide Kanäle (Both) Maximalauswahl
  655.                         table.insert(sampledata,left)
  656.                     else
  657.                         table.insert(sampledata,right)
  658.                     end
  659.                 end
  660.             end
  661.         end
  662.     end
  663.    
  664.     --------------------------------------
  665.     --sample 1 processing
  666.     --------------------------------------
  667.     waveform = {}    -- Audiodaten for one Frame
  668.     data_temp = {}   -- Parameterarray
  669.    
  670.     if channels == 1 or channelchoice == "Both" then
  671.         data_temp = sampledata
  672.     else
  673.         if channelchoice == "Right" then
  674.             data_temp = sampledataleft
  675.         else
  676.             data_temp = sampledataright
  677.         end
  678.     end
  679.  
  680.     -- Get Data
  681.     waveform = getSampleData(data_temp, channelchoice, framerate, timescale, currenttime, startframe, proxy, zoom, channels, bitrate,resolution)
  682.         if sampledata == nil then
  683.             Print ("AudioWaveform: No Data found")
  684.             return
  685.         end
  686.  
  687.     --------------------------------------
  688.     -- Build the Image
  689.     --------------------------------------
  690.     -- Parameter of the image
  691.     local thickness   = InThickness:GetValue(req).Value   --0.002
  692.    
  693.     local gain_r     = InRed:GetValue(req).Value
  694.     local gain_g     = InGreen:GetValue(req).Value
  695.     local gain_b     = InBlue:GetValue(req).Value
  696.     local gain_a     = InAlpha:GetValue(req).Value
  697.     -- Crosshair
  698.     local thicknessCH  = InThicknessCH:GetValue(req).Value
  699.     local gainCH_r     = InRedCH:GetValue(req).Value
  700.     local gainCH_g     = InGreenCH:GetValue(req).Value
  701.     local gainCH_b     = InBlueCH:GetValue(req).Value
  702.     local gainCH_a     = InAlphaCH:GetValue(req).Value
  703.    
  704.    
  705.     local ic = ImageChannel(out, 8)
  706.     local fs = FillStyle()
  707.     local cs = ChannelStyle()
  708.    
  709.     ic:SetStyleFill(fs)
  710.  
  711.     local x = 0
  712.     local y = 0.5 * (out.Height * out.YScale) / (out.Width * out.XScale)
  713.    
  714.     local sh = Shape()
  715.  
  716.     sh:MoveTo(x,y)
  717.  
  718.     if envelope == 1 then
  719.         -- Envelopecurve
  720.         for i=1,(1920/resolution)-1 do
  721.             x = (i)/(1920/resolution)
  722.             y = (0.5 + waveform[i] * scale) * (out.Height * out.YScale) / (out.Width * out.XScale)
  723.             sh:MoveTo(x,y)
  724.            
  725.             y = (0.5 + -waveform[i] * scale) * (out.Height * out.YScale) / (out.Width * out.XScale)
  726.             sh:LineTo(x,y)
  727.         end  
  728.     else
  729.         if envelope2 == 1 then
  730.             -- Envelopecurve2
  731.             for i=1,(1920/resolution)-1 do
  732.                 x = (i)/(1920/resolution)
  733.                 y = (0.75 + waveform[i] * scale/2) * (out.Height * out.YScale) / (out.Width * out.XScale)
  734.                 sh:MoveTo(x,y)
  735.                
  736.                 y = (0.25 - waveform[i] * scale/2) * (out.Height * out.YScale) / (out.Width * out.XScale)
  737.                 sh:LineTo(x,y)
  738.             end        
  739.         else
  740.             -- Standardcurve
  741.             for i=1,(1920/resolution)-1 do
  742.                 --x = 0.5 + (0.5 * i)/960
  743.                 x = (i)/(1920/resolution)
  744.                 y = (0.5 + waveform[i] * scale) * (out.Height * out.YScale) / (out.Width * out.XScale)
  745.            
  746.                 sh:LineTo(x,y)
  747.             end
  748.         end
  749.     end
  750.  
  751.     sh = sh:OutlineOfShape(thickness, "OLT_Solid", "OJT_Round", 8, "SWM_Normal", 8)
  752.    
  753.     -- Show Crosshair
  754.     if crosshair == 1 then
  755.         local x = 0
  756.         local y = 0.5 * (out.Height * out.YScale) / (out.Width * out.XScale)
  757.    
  758.         local ch = Shape()
  759.  
  760.         ch:MoveTo(0.5,0)
  761.         ch:LineTo(0.5,1*(out.Height * out.YScale) / (out.Width * out.XScale))
  762.         ch:MoveTo(0,0.5 *(out.Height * out.YScale) / (out.Width * out.XScale))
  763.         ch:LineTo(1,0.5 *(out.Height * out.YScale) / (out.Width * out.XScale))
  764.         ch = ch:OutlineOfShape(thicknessCH, "OLT_Solid", "OJT_Round", 8, "SWM_Normal", 8)
  765.  
  766.         cs.Color = Pixel({R = gainCH_r, G = gainCH_g, B = gainCH_b, A = gainCH_a})
  767.         ic:ShapeFill(ch)
  768.         ic:PutToImage("CM_Merge", cs)
  769.     end
  770.    
  771.     -- put shape to image
  772.     cs.Color = Pixel({R = gain_r, G = gain_g, B = gain_b, A = gain_a})
  773.     ic:ShapeFill(sh)
  774.     --cs.BlurType = blurfilters[filter]
  775.     --cs.SoftnessX = blur
  776.     --cs.SoftnessY = blur
  777.     if self.Status == "OK" then
  778.         ic:PutToImage("CM_Merge", cs)
  779.     end
  780.    
  781.     OutImage:Set(req,out)
  782.     collectgarbage();
  783. end

User avatar
Shem Namo
Fusionista
Posts: 522
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 14 times

Re: [Fuse] AudioWaveform

#2

Post by Shem Namo » Wed Jun 17, 2020 11:26 am

This looks AWESOME!!
Are you going to submit to Reactor?

User avatar
JiiPii
Posts: 18
Joined: Tue Mar 03, 2020 8:34 am
Been thanked: 8 times

Re: [Fuse] AudioWaveform

#3

Post by JiiPii » Wed Jun 17, 2020 11:51 am

Yes, I would like to, but I don't know exactly how to do it. I haven't found anything about it yet.

Added in 24 minutes 8 seconds:
Have now looked again carefully and found reactor submissions. Now I have to fight my way through the atom :-)

User avatar
Shem Namo
Fusionista
Posts: 522
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 14 times

Re: [Fuse] AudioWaveform

#4

Post by Shem Namo » Fri Jun 19, 2020 8:47 am

I had a play with this fuse and it’s amazing!! I really like the on-screen message(very professional). Could you please maybe make a fuse that outputs number values of different audio frequencies? I would try to write one myself, but I don’t understand anything about audio or audio theory. It’s just an idea, but it would be very cool if you could pull it off. Thank you very much for all of your hard work!!

User avatar
JiiPii
Posts: 18
Joined: Tue Mar 03, 2020 8:34 am
Been thanked: 8 times

Re: [Fuse] AudioWaveform

#5

Post by JiiPii » Fri Jun 19, 2020 2:41 pm

I already had the idea of adding an equalizer. To do this, one would have to implement an FFT. I fear this overwhelms the Lua interpreter. But I'll see if there is a solution.

User avatar
JiiPii
Posts: 18
Joined: Tue Mar 03, 2020 8:34 am
Been thanked: 8 times

Re: [Fuse] AudioWaveform

#6

Post by JiiPii » Thu Jun 25, 2020 3:51 pm

Yeah 8-) , beta version is running. It was a tough job getting the FFT to run. I come from the C world and Lua has her pitfalls. I learned a lot about "complex = {__mt = {}}" and "table.unpack (complex .__ mt)" But somehow I didn't find out which Lua version was used in DR.
The equalizer is a very first raw beta pre-release, just to see if the performance and the computer runtime are sufficient. Therefore, "only" N = 256
AudioWaveform.fuse
You do not have the required permissions to view the files attached to this post.

User avatar
Shem Namo
Fusionista
Posts: 522
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 14 times

Re: [Fuse] AudioWaveform

#7

Post by Shem Namo » Fri Jun 26, 2020 12:47 am

Thanks Man, you’re the best!!

User avatar
SteveWatson
Fusioneer
Posts: 121
Joined: Sat Oct 12, 2019 6:03 am
Answers: 3
Been thanked: 14 times

Re: [Fuse] AudioWaveform

#8

Post by SteveWatson » Fri Jun 26, 2020 2:16 am

Hi,

really nice. I looked at trying to do an FFT in lua a while ago but got completely lost trying to work out how to implement a complex number in lua - only got the vaguest idea of what one of those is !

In an ideal world it would be good to be able to select an area of the frequency waveform and then use that to drive something else, e.g use drums(low freq) for say y pos of something and synths(high freq) for something else.

Well done.

User avatar
JiiPii
Posts: 18
Joined: Tue Mar 03, 2020 8:34 am
Been thanked: 8 times

Re: [Fuse] AudioWaveform

#9

Post by JiiPii » Fri Jun 26, 2020 3:44 pm

Hi Steve,
Thank you

The selection of a frequency band could happen in AudioWaveform, but I don't know how to make the data available for other tools. Don't you need a modifier for that? I have not found a "SetValue" for a possible "output".

User avatar
Midgardsormr
Fusionator
Posts: 1817
Joined: Wed Nov 26, 2014 8:04 pm
Answers: 15
Location: Los Angeles, CA, USA
Been thanked: 109 times
Contact:

Re: [Fuse] AudioWaveform

#10

Post by Midgardsormr » Fri Jun 26, 2020 5:14 pm

A Fuse can have additional, non-image outputs (or even additional image outputs if you need them). To add such an output, though, you need to override the PreCalc process, as described here:
https://www.steakunderwater.com/VFXPedi ... nd_Process

For some examples of fuses that create additional outputs, take a look at the Lines fuse, which outputs a Point, and the Redshift Camera Extractor, which, as of the latest revision, outputs a string containing the camera's transform matrix. Both are available in Reactor.

Also, thanks for the example FFT implementation! I, also, was looking at implementing it a while ago, but didn't quite get there. Looking forward to examining your code!

User avatar
JiiPii
Posts: 18
Joined: Tue Mar 03, 2020 8:34 am
Been thanked: 8 times

Re: [Fuse] AudioWaveform

#11

Post by JiiPii » Sat Jun 27, 2020 9:14 am

Thank you very much - that is exactly the solution. Would love to try it right away, but next week it will be possible again :-)

User avatar
Kristof
Fusionista
Posts: 778
Joined: Thu Aug 07, 2014 1:30 pm
Answers: 2
Been thanked: 23 times

Re: [Fuse] AudioWaveform

#12

Post by Kristof » Sun Jun 28, 2020 2:32 am

Nice! There's an "Audio" DataType inside of Fu but I'm not aware of a node that works with that to have a look for inspiration. I'm a big fan of dedicated tools that deal with specific datatypes. This could be used to create nodes based on this:

https://pjb.com.au/comp/lua/digitalfilter.html

User avatar
JiiPii
Posts: 18
Joined: Tue Mar 03, 2020 8:34 am
Been thanked: 8 times

Re: [Fuse] AudioWaveform

#13

Post by JiiPii » Tue Jun 30, 2020 3:29 pm

Hello Kristof,

I tried to find information about the audio datatype, but was unsuccessful. Can you send me a link thank you

User avatar
JiiPii
Posts: 18
Joined: Tue Mar 03, 2020 8:34 am
Been thanked: 8 times

Re: [Fuse] AudioWaveform

#14

Post by JiiPii » Wed Jul 01, 2020 10:02 am

Version 0.4 is ready so far :banana: . An amplitude spectrum is now possible from the FFT. I made an extra control page for it. There you can switch on the spectrum first. The "elongation function" can now be switched on with a second switch. The width of the frequency band can be set with Width, the frequency position with Offset and the amplification with Amplify. The operator determines the calculation of the values ​​within the frequency band. The representation of the frequency values ​​is logarithmic!
Important!! A mathematically exact amplitude spectrum is only shown with the settings (control page) for Proxy = 1 and Zoom = 1. However, there are interesting results when you play with the parameters. (Small UI error - Spectrum active and Envelope active, Spectrum is ignored!)

Now with "Connect to" the elongation value can be linked to other parameters (an ellipse in the picture). I thought I made a mistake there. But be careful, if the area was rendered by Fusion, the display in the inspector no longer changes, but the initial values ​​are also rendered and are used correctly. So ignore the display in the inspector :D

Image
Image
AudioWaveform.fuse
You do not have the required permissions to view the files attached to this post.

User avatar
JiiPii
Posts: 18
Joined: Tue Mar 03, 2020 8:34 am
Been thanked: 8 times

Re: [Fuse] AudioWaveform

#15

Post by JiiPii » Thu Jul 09, 2020 3:55 pm

I read in the post viewtopic.php?f=45&t=3798&p=29904&hilit ... ess#p29904 about the function DoMultiProcess and wanted to use it to convert the WAV data . Unfortunately I didn't make it. The data to be transferred were all available in the chunk routine - everything was calculated correctly there, but there was no value in the tables. I tried simple variables. Everything I do with the variable remains only in the chunk routine :-? . Another major drawback is that Lua only reads strings from files (although the -rb option is available). However, when passed in DoMultiProcess, this means that the string ends after the first zero. A transfer to a table would then be an additional calculation that questions the use of the multiprocess. I gave up first. Maybe there is a solution or explanation.
I then embellished and tested my tool a little further :) :
AudioWaveform.fuse
You do not have the required permissions to view the files attached to this post.