HISE Logo Forum
    • Categories
    • Register
    • Login

    Retro 80s Tape Wow & Flutter with faust

    Scheduled Pinned Locked Moved Presets / Scripts / Ideas
    53 Posts 8 Posters 2.0k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • MorphoiceM
      Morphoice
      last edited by Morphoice

      I made a quite good sounding wow and flutter plugin using faust
      and thought I'd share it, as probably some could use it

      import ("stdfaust.lib");
      
      wow = os.lf_trianglepos(hslider("wow",0.5,0,2,0.01)) : si.smooth(0.9999) * 1000.;
      wow_intensity = hslider("wow_intensity",0.3,0,1,0.01):si.smooth(0.9999);
      flutter = os.lf_trianglepos(hslider("flutter",8,2,20,0.01)) : si.smooth(0.9999) * 1000.;
      flutter_intensity = hslider("flutter_intensity",0.1,0,1,0.01):si.smooth(0.9999);
      hiss = hslider("hiss",0.1,0,1,0.01);
      
      drive = hslider("drive",0,0,1,0.001) ;
      
      tanh(x) = x * (27 + x * x) / (27 + 9 * x * x);
      saturator = ef.dryWetMixerConstantPower(drive,tanh * drive * 20 : co.limiter_1176_R4_mono: tanh );
      
      tapenoise = no.pink_noise*0.6 + no.sparse_noise(10)*0.2 : fi.bandpass(1,600,20600)/5 + os.triangle(60)*0.05 + os.oscsin(528)*0.003 + os.oscsin(110)*0.002;
      
      wowflutter = de.fdelay1(ma.SR, 100. + wow*(wow_intensity)  + flutter*(flutter_intensity/5)) <: saturator : fi.lowpass(2,8000) + tapenoise * hiss;
      
      
      
      process =  wowflutter, wowflutter;
      

      this sounds quite awesome on synths if you are looking for the retro 80s synthwave vibe, the saturation circuit is a bit fishy but it sounds good. if anyone has some improvements let me know.

      I'm aiming to simulate real magnetic saturation with hysteresis once I can wrap my head around it

      https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

      LindonL HISEnbergH 2 Replies Last reply Reply Quote 8
      • LindonL
        Lindon @Morphoice
        last edited by

        @Morphoice er, that just imparts a massive amount of delay here....

        HISE Development for hire.
        www.channelrobot.com

        LindonL MorphoiceM 2 Replies Last reply Reply Quote 0
        • LindonL
          Lindon @Lindon
          last edited by

          maybe try this, which uses your tape hiss, tho it would need at least some sort of bump to be more tape-like...

          import ("stdfaust.lib");
          import("music.lib");
          import("effect.lib");
          
          //Controls
          envelope = 1;
          speed = hslider("speed", 0.1, 0.1, 10, 0.05);
          depth = hslider("depth", 0, 0, 1, 0.01);
          Kontrol = 1;
          shift = hslider("shift", 0, -1, +1, 0.01)*2; //*2 needed to conform with parametric controller output
          dry_wet = 1;
          myhiss = hslider("hiss",0.1,0,1,0.01);
          //
          mixer(mix) = _*(1 - mix),_*mix:>_;
          
          //Parametric controller, combinate signals from envelope follower and oscillator
          c_folower_colibration = 6;
          parametric_controller(mix, envelope_t, freq, depth) = (amp_follower(envelope_t):_*c_folower_colibration:_*depth,osc(freq)*0.5:_,_*depth):mixer(mix):_+0.5; 
          
          //PS constants, can be changed to decrease effect delay 
          c_samples = 2048;
          c_xfade   = 1024;
          //PS implementation, copy-pasted from faust repository, see ./examples/pitch_shifter.dsp
          transpose (w, x, s, sig)  =
          	fdelay1s(d,sig)*fmin(d/x,1) + fdelay1s(d+w,sig)*(1-fmin(d/x,1))
          	   	with {
          			i = 1 - pow(2, s/12);
          			d = i : (+ : +(w) : fmod(_,w)) ~ _;
          	        };
          
          pmat = _<:_,(_<:parametric_controller(Kontrol, envelope, speed, depth)*shift,_:transpose(c_samples,c_xfade)):mixer(dry_wet);
          
          
          tapenoise = (no.pink_noise*0.6 + no.sparse_noise(10)*0.2 : fi.bandpass(1,600,20600)/5 + os.triangle(60)*0.05 + os.oscsin(528)*0.003 + os.oscsin(110)*0.002)*myhiss;
          
          
          process = pmat+ tapenoise, pmat+tapenoise;
          

          HISE Development for hire.
          www.channelrobot.com

          1 Reply Last reply Reply Quote 0
          • HISEnbergH
            HISEnberg @Morphoice
            last edited by

            @Morphoice Nice work!

            Jatin Chowdhurdy has a good paper on magnetic distortion and how hysteresis works:
            https://dafx.de/paper-archive/details/Z5Ow_E5YHVxvndTAyeEuzA

            They also have a GPL repo and free plugin for this on their Github and website:
            https://github.com/jatinchowdhury18/AnalogTapeModel
            https://chowdsp.com/products.html#tape

            @Lindon is right though it really does just introduce a lot of delay.

            I am working a an analog model of the Echoplex EP-4, I can share my method for wow and flutter here once it is done.

            MorphoiceM 1 Reply Last reply Reply Quote 0
            • MorphoiceM
              Morphoice @Lindon
              last edited by Morphoice

              @Lindon weird, it does indeed in faust IDE, but works fine in a faust scriptnode as a plugin, i mussed have messed up somewhere inbetween

              https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

              MorphoiceM 1 Reply Last reply Reply Quote 0
              • MorphoiceM
                Morphoice @Morphoice
                last edited by Morphoice

                @HISEnberg, @Lindon

                there you go, the smoothing fucked up two of the sliders, now it works as it should, no more delay

                import ("stdfaust.lib");
                
                wow = os.lf_trianglepos(hslider("wow",0.5,0,2,0.01)) : si.smooth(0.9999) * 1000.;
                wow_intensity = hslider("wow_intensity",0.3,0,1,0.01):si.smooth(0.9999);
                flutter = os.lf_trianglepos(hslider("flutter",8,2,20,0.01)) : si.smooth(0.9999) * 1000.;
                flutter_intensity = hslider("flutter_intensity",0.1,0,1,0.01):si.smooth(0.9999);
                hiss = hslider("hiss",0.1,0,1,0.01);
                
                drive = hslider("drive",0,0,1,0.001) ;
                
                tanh(x) = x * (27 + x * x) / (27 + 9 * x * x);
                saturator = ef.dryWetMixerConstantPower(drive,tanh * drive * 20 : co.limiter_1176_R4_mono: tanh );
                
                tapenoise = no.pink_noise*0.6 + no.sparse_noise(10)*0.2 : fi.bandpass(1,600,20600)/5 + os.triangle(60)*0.05 + os.oscsin(528)*0.003 + os.oscsin(110)*0.002;
                
                wowflutter = de.fdelay1(ma.SR, 100. + wow*(wow_intensity)  + flutter*(flutter_intensity/5)) <: saturator : fi.lowpass(2,8000) + tapenoise * hiss;
                
                
                
                process =  wowflutter, wowflutter;
                

                https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

                DanHD HISEnbergH 2 Replies Last reply Reply Quote 0
                • MorphoiceM
                  Morphoice @HISEnberg
                  last edited by

                  @HISEnberg there's an interesting paper about the echoplex somewhere, it should get you started, I'll drop you the link if I can find it again

                  https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

                  HISEnbergH 1 Reply Last reply Reply Quote 0
                  • DanHD
                    DanH @Morphoice
                    last edited by

                    This post is deleted!
                    1 Reply Last reply Reply Quote 0
                    • HISEnbergH
                      HISEnberg @Morphoice
                      last edited by

                      @Morphoice thanks, it might be the one I am basing my research on, by Julius Smith :)

                      1 Reply Last reply Reply Quote 0
                      • HISEnbergH
                        HISEnberg @Morphoice
                        last edited by HISEnberg

                        @Morphoice Nice that is much better, well done.

                        I thought it might be nice to introduce some randomization to the modulation so it isn't always static. I also thought some type of envelope follower on the input to attenuate the hiss would be nice, but I didn't complete it.

                        import ("stdfaust.lib");
                        import ("analyzers.lib");
                        
                        // Wow and Flutter
                        random_mod = hslider("random_mod", 0.002, 0, 0.01, 0.0001);
                        
                        wow = os.lf_trianglepos(hslider("wow", 0.5, 0, 2, 0.01)) 
                              + no.noise * random_mod : si.smooth(0.9999) * 1000.;
                        
                        wow_intensity = hslider("wow_intensity", 0.3, 0, 1, 0.01) : si.smooth(0.9999);
                        
                        flutter = (os.lf_trianglepos(hslider("flutter", 8, 2, 20, 0.01)) 
                                   + no.noise * random_mod) * (0.8 + os.oscsin(0.1) * 0.2) 
                                   : si.smooth(0.9999) * 1000.;
                        flutter_intensity = hslider("flutter_intensity", 0.1, 0, 1, 0.01) : si.smooth(0.9999);
                        
                        // Noise
                        hiss = hslider("hiss", 0.1, 0, 1, 0.01);
                        
                        tapenoise = (no.pink_noise * (0.6 + os.lf_triangle(0.1) * 0.05) 
                                     + no.sparse_noise(10) * (0.2 + os.lf_triangle(0.05) * 0.05)) 
                                     : fi.highpass(1, 50) 
                                     : fi.lowpass(1, 15000) 
                                     / 5 
                                     + os.triangle(60) * 0.05 
                                     + os.oscsin(528) * 0.003 
                                     + os.oscsin(110) * 0.002;
                        
                        // Saturator
                        drive = hslider("drive", 0, 0, 1, 0.001);
                        tanh(x) = x * (27 + x * x) / (27 + 9 * x * x);
                        saturator = ef.dryWetMixerConstantPower(drive, tanh * drive * 20 : co.limiter_1176_R4_mono : tanh);
                        
                        /*
                        // Envelope Follower for Noise Modulation
                        t_attack = 0.005;  // Attack time in seconds (5 ms)
                        t_release = 0.05;  // Release time in seconds (50 ms)
                        signal_level = an.amp_follower_ar(t_attack, t_release);
                        
                        // Dynamic Noise Control
                        modulated_noise = tapenoise * (1 - signal_level) * hiss;
                        */
                        
                        // Combine Wow, Flutter, and Noise
                        // Substitue the tapenoise * hiss for an attenuated tapenoise (modulated_noise)
                        wowflutter = de.fdelay1(ma.SR, 100. + wow*(wow_intensity)  + flutter*(flutter_intensity/5)) <: saturator : fi.lowpass(2,8000) + tapenoise * hiss;
                        
                        
                        // Output
                        process = wowflutter, wowflutter;
                        
                        
                        MorphoiceM 1 Reply Last reply Reply Quote 0
                        • MorphoiceM
                          Morphoice @HISEnberg
                          last edited by Morphoice

                          @HISEnberg brilliant thanks. I had already considered to add some randomness something like

                          rate = ma.SR/10000.0; // new random value every 100 samples (ma.SR from maths.lib)
                          process = no.lfnoiseN(3,rate)*0.5 + no.lfnoiseN(3,400)*0.1;
                          

                          instead of a fixed wow frequency. (this just being a test to create a low frequency random noise)
                          also trying to figure out some ways to "degrade" the signal. I've indeed studied Jatin's work on the tape model but it's giving me a hard time to adapt to faust

                          https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

                          HISEnbergH 1 Reply Last reply Reply Quote 0
                          • HISEnbergH
                            HISEnberg @Morphoice
                            last edited by

                            @Morphoice Same here, it is insightful but I am still making my way through it.

                            I suppose for degrading, you could consider frequency-dependent saturation? I think tape saturation ought to effect low frequencies more than high ones.
                            Other than that, a slight compression at the end.
                            I also considered something like an "Tape Age" slider which would represent newer to older tape. This could probably be implemented with another noise source and LPF, but some type of saturation would be nice too. Also it would cause greater wow and flutter, but this get's a little unwieldy so I think the values need to be readjusted.

                            Another thing I did was introduce a bit of stereo processing, so the modulations on the left and right were somewhat offset. I find it helps create a better stereo field but this isn't necessarily something tape does.

                            import ("stdfaust.lib");
                            import ("analyzers.lib");
                            
                            // Tape Age Control
                            tape_age = hslider("Tape Age", 0.0, 0.0, 1.0, 0.01);  // 0 = New, 1 = Old
                            
                            // Wow and Flutter
                            random_mod = hslider("random_mod", 0.002, 0, 0.01, 0.0001);
                            
                            wow = (os.lf_trianglepos(hslider("wow", 0.5, 0, 2, 0.01)) 
                                  + no.noise * random_mod 
                                  + os.oscsin(0.3) * 0.0005) 
                                  : si.smooth(0.9999) * 1000.;
                            
                            flutter = ((os.lf_trianglepos(hslider("flutter", 8, 2, 20, 0.01)) 
                                      + no.noise * random_mod) * (0.8 + os.oscsin(0.1) * 0.2) 
                                      + os.oscsin(15) * 0.0003) 
                                      : si.smooth(0.9999) * 1000.;
                            
                            wow_intensity = hslider("Wow Intensity", 0.3, 0, 1, 0.01) * (1 + tape_age * 0.5) : si.smooth(0.9999);  // Scaled by tape age
                            flutter_intensity = hslider("Flutter Intensity", 0.1, 0, 1, 0.01) * (1 + tape_age * 0.5) : si.smooth(0.9999);  // Scaled by tape age
                            
                            // Stereo Spread
                            stereo = hslider("Stereo Spread", 0.1, 0, 1, 0.01);
                            
                            wow_left = wow;
                            wow_right = wow + no.noise * (stereo * 0.001);  // Subtle difference
                            flutter_left = flutter;
                            flutter_right = flutter + no.noise * (stereo);  // Subtle difference
                            
                            // Noise
                            hiss = hslider("Hiss", 0.1, 0, 1, 0.01);
                            
                            tapenoise = (no.pink_noise * (0.6 + os.lf_triangle(0.1) * 0.05) 
                                         + no.sparse_noise(10) * (0.2 + os.lf_triangle(0.05) * 0.05) 
                                         + os.oscsin(50) * 0.01) 
                                         : fi.highpass(1, 50) 
                                         : fi.lowpass(1, 15000) 
                                         / 5 
                                         + os.triangle(60) * 0.05 
                                         + os.oscsin(528) * 0.003 
                                         + os.oscsin(110) * 0.002;
                            
                            // Apply Tape Age to Noise
                            aged_tapenoise = tapenoise : fi.lowpass(1, 15000 - tape_age * 8000) * (1 + tape_age * 0.5);  // Aged noise
                            
                            // Saturator
                            drive = hslider("Drive", 0, 0, 1, 0.001) + tape_age * 0.2;  // Increased drive with tape age
                            tanh(x) = x * (27 + x * x) / (27 + 9 * x * x);
                            saturator = ef.dryWetMixerConstantPower(drive, tanh * drive * 20 : co.limiter_1176_R4_mono : tanh);
                            
                            // Combine Wow, Flutter, and Noise
                            wowflutterLeft = de.fdelay1(ma.SR, 100. + wow_left * wow_intensity + flutter_left * (flutter_intensity / 5)) 
                                              <: saturator 
                                              : fi.lowpass(2, 8000) 
                                              + aged_tapenoise * hiss;
                            
                            wowflutterRight = de.fdelay1(ma.SR, 100. + wow_right * wow_intensity + flutter_right * (flutter_intensity / 5)) 
                                               <: saturator 
                                               : fi.lowpass(2, 8000) 
                                               + aged_tapenoise * hiss;
                            
                            // Output
                            process = wowflutterLeft, wowflutterRight;
                            
                            
                            MorphoiceM 1 Reply Last reply Reply Quote 0
                            • MorphoiceM
                              Morphoice @HISEnberg
                              last edited by

                              @HISEnberg I've thought about stereo processing, but it would imply having two mono tape machines... a single stereo tape would always run the same speed and flutter/vibrate the same as it's a single stereo head picking up the signal...
                              again though this would offer a bit of variance and introduce an abstract option to go more into experimental sounds

                              as for the degrading it's really the hysteresis I want to tackle, as all the normal saturation circuits we use are just a "cheat"... also having some transformer things like the Kush Omega Transformer

                              https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

                              HISEnbergH 1 Reply Last reply Reply Quote 0
                              • HISEnbergH
                                HISEnberg @Morphoice
                                last edited by

                                @Morphoice yes I thought of that too regarding the stereo processing. I suppose it would be useful if you want to retain the original, stereo signal input. Otherwise it's just for experimentation.

                                LindonL 1 Reply Last reply Reply Quote 0
                                • LindonL
                                  Lindon @HISEnberg
                                  last edited by

                                  @HISEnberg @Morphoice - as I mentioned before if your seriously thinking of emulating tape machines you will need to account for head bump - and probably make it variable as different machines from different manufacturers had different amounts in different freq...

                                  HISE Development for hire.
                                  www.channelrobot.com

                                  MorphoiceM 1 Reply Last reply Reply Quote 1
                                  • MorphoiceM
                                    Morphoice @Lindon
                                    last edited by

                                    @Lindon good point. I could add some eq to get close to an average tape response curve. Not really looking to model specific tape machines, just want to have that wobbly 80s retro sound much like baby audio's VHS

                                    https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

                                    griffinboyG HISEnbergH 2 Replies Last reply Reply Quote 1
                                    • griffinboyG
                                      griffinboy @Morphoice
                                      last edited by

                                      @Morphoice

                                      I've been doing some work to extract the real transport from multiple machines. I'm not terribly convinced by the quasi-random shapes most people use to model wow.

                                      In terms of hysteresis and the frequency response of tape, everyone else here is correct: Jatin's work (chow tape) is a good place to start.

                                      MorphoiceM 1 Reply Last reply Reply Quote 0
                                      • HISEnbergH
                                        HISEnberg @Morphoice
                                        last edited by HISEnberg

                                        @Morphoice That reflects my use case as well, I am more interested in experimenting with (and pushing) the potentials of tape styled-modulation.

                                        I agree with you that the saturation element is queue here, I will let you know if I make any headway with the hysteresis (though I remember reading its full implementation is super CPU intensive).

                                        I suppose a nifty feature to explore could be tape startup, rewind, and different tape speeds.

                                        MorphoiceM 1 Reply Last reply Reply Quote 0
                                        • B
                                          Ben Catman
                                          last edited by

                                          Good work ! 👏

                                          1 Reply Last reply Reply Quote 1
                                          • MorphoiceM
                                            Morphoice @griffinboy
                                            last edited by

                                            @griffinboy I agree. As the wow results from the revolving reel or cassette it's rather periodic than random, the random wow makes more sense to me on a tape delay which has a bunch of lose tapeloop in a cartridge

                                            https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

                                            1 Reply Last reply Reply Quote 1
                                            • First post
                                              Last post

                                            35

                                            Online

                                            1.7k

                                            Users

                                            11.7k

                                            Topics

                                            102.2k

                                            Posts