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.
    • 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
                                        • MorphoiceM
                                          Morphoice @HISEnberg
                                          last edited by

                                          @HISEnberg sadly I'm far from being able to implement the hysteresis myself or port it to faust somehow, but if you ever manage to do so please let me know. the question then again is, though, does the end-user even hear the difference of is a simple saturator sufficient. I'm not even sure all the big companies like Waves tape emulation plugins even have something like exact hysteresis. Jatin points out the various solvers to calculate a hysteresis loop and they are indeed quite cpu heavy. maybe the UAD plugins did such on thing on their DSP

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

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

                                            @Morphoice

                                            If you want a c++ node for it (works in scriptnode) I can lend you mine.
                                            3% cpu usage when you oversample it, if I remember correctly. It's a clone of Chow tape's hysteresis, with some minor alterations and no dependancies.

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

                                            51

                                            Online

                                            1.7k

                                            Users

                                            11.7k

                                            Topics

                                            102.2k

                                            Posts