HISE Logo Forum
    • Categories
    • Register
    • Login

    Retro 80s Tape Wow & Flutter with faust

    Scheduled Pinned Locked Moved Presets / Scripts / Ideas
    53 Posts 8 Posters 5.3k 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.
    • 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
                                          • MorphoiceM
                                            Morphoice @griffinboy
                                            last edited by

                                            @griffinboy I'd love to take a look at it, though I've never used a C++ node before and will probably fail terribly at getting it running lol

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

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

                                            19

                                            Online

                                            1.8k

                                            Users

                                            12.0k

                                            Topics

                                            104.1k

                                            Posts