Faust Aural Exciter
-
Has anyone tried to use Faust's built-in aural exciter? I tried it using their web api, and got the same hing—so I know it's not a HISE thing. I noticed it got some love on a semi-recent build, so I'm wondering if it's a Faust bug in the macOS version (which is what I'm running).
The exciter is based on this paper, which explains the implementation in-depth, and what/how it's supposed to work.
Since it's built into Faust's standard library, this is the only code you need to use it in a Scriptnode.
process = dm.exciter, dm.exciter;
I've encountered two problems:
-
It doesn't seem to generate harmonics! Specifically, the "Harmonics" knob doesn't seem to do anything. (The high-pass filter and mix knobs seem work, though.)
-
The levelling doesn't work. The output gets quieter as you increase the mix level—presumably because it's simply crossfading to a high-passed version of the original signal. But according to the paper, the circuit automatically compensates for level changes within itself (so there's basically gain stages at the front and back end).
To use the Faust exciter, it's just one line of code:
-
-
@clevername27 doesnt seem to do anything for me either indeed. Tested on audio and sinewaves, the latter is a dead giveaway - no harmonics.
I don't know about this demo in particular, we'd have to check its source to see what it's actually supposed to do, but the simplest way to add harmonics is the tanh function, if you want to go for the Aphex Aural Exciter approach best follow the steps from the paper and build it yourself. It's just a separate path for a filtered signal passing a soft clipper, which might not even be the best approach by today's standards, but still works suprisingly well for vocals and some synths etc. They put a compressor in later versions to even out the creation of harmonics on different levels. Should be fairly simple to reconstruct -
@Morphoice Great take, cheers. If you know how a good, specific way to build this in HISE, you're welcome to any of my code. Actually, everyone here is welcome to it anyway, so that's not much of an ask, lol.
-
@clevername27 You can do something like this. It's very basic but it should get you started.
HiseSnippet 1997.3oc0Ys7baaiFGzxvuR5Na56iZl1C1cx30x1IoY5Ay3GpwSqr0Z40oc1CIvjfVXLI.KIniUebZ2C8VOmdZ283dpW6s7GP6Lcl7OvtG6sz+BZ+.AoDoHsrjrR1tJyDS7A7A7CeuAPy.gEMLTDfLV3vN9Tjw0ws5vks2pMgwQ6tMx3OfaPBkzfpZRa1wmDFRsQFFU9PEAi4mFE+64arIwkvsn8HgPGIXVzOl4wj8n1z7iXtt0I1zCYdYF85l6ZI3aIbEQ.dpfWA4SrNkbBcOhZXSgQ2mD1FY7dXxZqY6b6a+9201Z8aU6tNj6Xac7Z28VT6UWeM5co112YM6icbPFyriMSJBZIIRZHLoaJr6zps3wb8BbDKjcrKU0nFpErxZxnsZybsalJbBQHCbydhpJZQ0qiavrYco2Sj8Gi6nZONxJzLlZPPp1H.IiLPZZMjtAtkU.yW1qGEdtFdWNnAcHftIKTziEY7T7VBX.b4xdjSo0CfFcYXwauxJ2rJ7eK8ANQbKISvqJ36Ijz84KtzBewByuvWsP096xwoz9TKSfv0kFTZ2JygfAw3h7HuioA2r5YD2HZ2ABa+7xzYFNYpkdWmYfB9tblbeeZR65BWakrR8cQM.JQrAe8W1cahjnTJIzfw4SCjLEbL1ldF3FnUQyi2lFdpT3CNBEzefkivNxkHyaNobzR5.jG4zgJEEOjI6j0QbDrwVYf1XCKDuAtISZ0tbLNUIXDjTuHvXhm4qf2wwgZI6Avow0+jw0MbkQ1MbgD2PXMUK90SZVs9mTqTGvuF2yX2Of5SBnGJZ5R5rXHwy2kd.fwaV8XWg0osXeNsnGhuFBapFwhVsIbN0MbbbjlYBEbBcfHRx3mzfHCXmCgO2KxqEDW2htUB5.ZFSo7gzsWQ0VYWzhxsia7qvujNqoZajzYszNy39sGU9XQvowpijuQFypj8uIlqa+PRT.wcmysXPjMzQzfPkYpwr3UVF9G5dtthGukvymkX.CpmXZMEtc7aK3LKEI8HR2D2ySDAqexNAxNcHg4pr3aEEBwPr2m2BFbbJQkE2dBa3qYqSr.QbmlDYakmiJJDXlRCV1pq45Ef5d1NJotNs3ELT0ZklJUKXAn6ojVpDoJUZSR.LAvXUQnlxnhhCk.LAlynvwr3PeWH28EBYc2Yso0nJkwACCiJYBShPki.sT4RDZkg.cOiBBh8kUsfHUSo8b2M7HUuVDWzQJeEkrpKPMx.TL1KxMOLW.6A+cYE8h3SO9KAcSkS9jQiofZZKXu1fwSPWu.rMHm2GsmuQKI0WEAI63ZcJ8wZXmcjZYudBJLMaScHQtxBzygPTh4tZllSqLp8hRMllmF7xtRJzoynPmAeRACuqAHNft7Iki0DNFecZkL5z4JpS+zmVTmB5u75z+wSdx2uQAc5SO46dCS8tJtp8Byb4pTD5dQRAXEqSjiFYaNyuwbnv2idsse1F5ZUa4IDx1Pti9A45lkCx0MuDbMIjaWGe.MjJK0g.gtHoGJi8zBXAm9PeAj9LmM0qhcXtJCfk61eQSqLLORgTU4qbommVbZc8J06SkaA2lddZN0cfxBrAWoXFRyylyFclAaittYAY8W+LygJti4ey6GSrApGP+rHHwYGTAyoRE0.8g1P0bs0d1F8Axm9C+v6LbfD8N+xOECxJ3+73DXrTCzUKw.cUyQKXc4N1Cvz7EV9iK0W17+94+8xDUwzmP354aLHQUCka4Pp8lXhpAAo4w6vIPk01ifMUtbUzy8C5OWUb0GwcTVtp3NFkbUcaklLcF7VYjiKganVOIg2dw3iU7dLtejbo+TejWJ9DaYmmYgiDebTWaDiJWbkN4h37sOQ8662njn6lWwJcRm5KR3mOZSNMATVWsQnNvDFlLEBN2uSJD7BBF2O1Q4pe1wouCZ7JXBm31IjtrpqxJgVQ+pjObaFbLERmMibbRDl4nLxYGyqYlNabCcYvpXz2uIZxWixPF2+4aDeCUbZ7EBDlbgUIMSN40t1cKks6FPSKNCCp+cEFe+l0QStRA9wu7muipTftr9o+yG8c+0+0+17pskxTBUtsU1pMJr2lGuIb.d6RUYWUOqg1aZXUYwARxs2RcbKrufPtArynnIe3zBrdU2Uw4otfc0XbTuMEhS8Hw2UzU9VaeKcmWC+.xYz32MI91adi31Nh.upeHENbq5tRqMfWN4mF1WNwene4j8sj.DNLfvC8EgzZ4zaTO1gfuPXNp8wwpkxwp4ivHi34mZMobiRIJpChhriyLkXlQNmYSBO2jAsyMSMTWjXul5BlZQgct89gVffgzmoXyH2P5CX1x1Yl2+SFxqlk78IA1fNzJ2kTWI+8gN8vcen4uu9LiujqOdlWTOrzzC2MZeIOwwu+eihbW.+7oXrESkneG9YTWvkNFiuJNITTJ079jMDbQ5E81yB3.pLfcxIz70DU1F5dRIw5zdTdcyCntTRXF+z207iYbJIH93GimrXzeSvR0WuMVC2pp.IUuJ5sWplYC7clpLTuyzDGui+aN8R1CEhe9+OZ52BCwzWsZr5t5+602uLdNqWFqgGwJP7PK8q.pBXLWLEXeyiOR07vg3g1UqgNq+hc8fTOOzxJ+TUfwUGWFWabYb8wkwaMtLd6wkw6LtL99WNipJCRN1sxOAJWp4N5yoZjd0RpaV42noKcsL
-
@Matt_SF that is about it! I believe the highpass of the Aphex is around 5k.
You can then try pushing your signal with a compressor to help quieter parts get evenly "excited".
bear in mind the _expr nodes need to be compiled into a hardcoded network in order to work in an exported plugin. I learned that the hard way ;) took me weeks to figure out =)
this being said, it might even be easier doing those steps in faust and ignore the demo exiter altogether
this is the full source of dm.exciter
exciter = _ <: (fi.highpass(2, fc) : compressor : pregain : harmonicCreator : postgain), _ : balance with{ // TODO: rewrite to use the standard compressor from compressors.lib compressor = ba.bypass1(cbp,compressorMono) with{ comp_group(x) = vgroup("COMPRESSOR [tooltip: Reference: http://en.wikipedia.org/wiki/Dynamic_range_compression]", x); meter_group(x) = comp_group(hgroup("[0]", x)); knob_group(x) = comp_group(hgroup("[1]", x)); cbp = meter_group(checkbox("[0] Bypass [tooltip: When this is checked, the compressor has no effect]")); gainview = co.compression_gain_mono(ratio,threshold,attack,release) : ba.linear2db : meter_group(hbargraph("[1] Compressor Gain [unit:dB] [tooltip: Current gain of the compressor in dB]",-50,+10)); displaygain = _ <: _,abs : _,gainview : attach; compressorMono = displaygain(co.compressor_mono(ratio,threshold,attack,release)); ctl_group(x) = knob_group(hgroup("[3] Compression Control", x)); ratio = ctl_group(hslider("[0] Ratio [style:knob] [tooltip: A compression Ratio of N means that for each N dB increase in input signal level above Threshold, the output level goes up 1 dB]", 5, 1, 20, 0.1)); threshold = ctl_group(hslider("[1] Threshold [unit:dB] [style:knob] [tooltip: When the signal level exceeds the Threshold (in dB), its level is compressed according to the Ratio]", -30, -100, 10, 0.1)); env_group(x) = knob_group(hgroup("[4] Compression Response", x)); attack = env_group(hslider("[1] Attack [unit:ms] [style:knob] [tooltip: Time constant in ms (1/e smoothing time) for the compression gain to approach (exponentially) a new lower target level (the compression `kicking in')]", 50, 0, 500, 0.1)) : *(0.001) : max(1/ma.SR); release = env_group(hslider("[2] Release [unit:ms] [style: knob] [tooltip: Time constant in ms (1/e smoothing time) for the compression gain to approach (exponentially) a new higher target level (the compression 'releasing')]", 500, 0, 1000, 0.1)) : *(0.001) : max(1/ma.SR); }; //Exciter GUI controls ex_group(x) = hgroup("EXCITER [tooltip: Reference: Patent US4150253 A]", x); //Highpass - selectable cutoff frequency fc = ex_group(hslider("[0] Cutoff Frequency [unit:Hz] [style:knob] [scale:log] [tooltip: Cutoff frequency for highpassed components to be excited]", 5000, 1000, 10000, 100)); //Pre-distortion gain - selectable percentage of harmonics ph = ex_group(hslider("[1] Harmonics [unit:percent] [style:knob] [tooltip: Percentage of harmonics generated]", 20, ma.EPSILON, 200, 1)) / 100; pregain = * (ph); // TODO: same thing: why doesn't this use cubicnl? //Asymmetric cubic soft clipper harmonicCreator(x) = x <: cubDist1, cubDist2, cubDist3 :> _; cubDist1(x) = (x < 0) * x; cubDist2(x) = (x >= 0) * (x <= 1) * (x - x ^ 3 / 3); cubDist3(x) = (x > 1) * 2/3; //Post-distortion gain - undoes effect of pre-gain postgain = * (1/ph); //Balance - selectable dry/wet mix ml = ex_group(hslider("[2] Mix [style:knob] [tooltip: Dry/Wet mix of original signal to excited signal]", 0.5, 0, 1, 0.01)); balance = (_ * ml), (_ * (1.0 - ml)) :> _; };
looks incomplete, maybe you can thin this down and apply your own algorithms. the cubicnl function should do a good job
-
@Morphoice also consider lowpassing the left chain signal, otherwise you'll just double everything above your cutoff frequency instead of just adding harmonics. as with all analog models, the parameters are probably not perfect, you'll need to run a variety of good test audio files which you're accustomed to through this and tweak it to what sounds and feels right to you for most use cases
-
@Morphoice I'm not comfortable with faust yet but it's on my to-do list for 2025 :)
@Morphoice said in Faust Aural Exciter:
bear in mind the _expr nodes need to be compiled into a hardcoded network in order to work in an exported plugin. I learned that the hard way ;) took me weeks to figure out =)
Yes the expr node must be compiled.
I used a simple tanh function but yes, the cubic function should work too.
Quick modification of the script node effect:
This one uses phase cancellation so the harmonics only are fed into the original signal.HiseSnippet 2527.3oc2Z07aaibEenkG4XuIEY2jcQQAJJOzC1KhUE02pEsV9KkXrwd0Zo5l.Xffwjir3ZJNrjT1VaQA5w8ufdn+OTzhdoGW2dnnnWZA5kdoE6wdLnn.8X5a3PJQJQIQoj3tnJ.wbdyLb9MuueyvF1LUpiCyFIsVq9VTjzcwM6a51Y2NDcSzA6gj9Z3CINtTaYAoc5aQbbnZHIoTOlSPZ0kQd+d4V6PLHlpzgjPnSX5pzmp2U2cH0F09HcCi5DMZK8tgFcgZGnxL2kYv5A3IENKxhndA4b5QD9vVBidBwoCR5Cwj740ZWpTkpZpEJpTsMorl5Y4qVjpkqPdZUplV47Zm0tMRJ89Z5tL6ltDWpC7R2go0uYG1UlhE3DcG8yLn7FJnlvJKHi1sitgVi.liCBIgaLjUkRvpdH9PcM8AzGxxtuWGxCmQXllzRSCRJyAjjBAokEP5cwMUs0sbG1CGOuC9.SPB1l.xlvPQLVjzM3cYv.LcyzkbAstMzXvDVuT1rORF9uM9ds6Yp5pyLkYlGwboer45ar1OYsUW6mtl7nc0tcr8wWFalgA0N1t4pC1Sahqa1q6YT6GIeIwnGcv.gseTdZ5jwSUE65PCjYdfot6GaQ8aWmYnw4U7mGWBf7YavS+vC1i3R3BEeZv3rn1t5b3HsG8RvLPHhVEuG04BWlEXHLl7CzbXZ8LHtQUm3FZ9c.7iHxPtfxzQ2seXCw4PGK6T0wRJDeWbCcW0NwiwkhAi.m5sAF8sLuGd+1soptCA3x35OaQMCyN2lgq4aFBqIewuqeS45OSIVCvOGOTY2xlZQrosXMLH8W2gz0xfdLfwGIelAS8hl5eFcbKDKAD1gOh0U6PLMoFNKhgT52PNmPGy54pad9gDWa8qA2mG0qaSvutJcWezAzjVhaCIZmk2lqWzjZp403UvO+NU3sk76TInyPleGQcuhYegm3v+YjzJbd+GfMEseAomMwX+qU0AOanSn1Nb0ToUvYy.+CssgA6pcYcsz8UfAwiGsFLi9VcXl5pbRhQDrI1tKqGr996DH5TKhtAWiuYOGvGh1Ga1DFrWHQtF2QLM3oUpSTAVb+FD2NbKGtWHPMkZmQcf55DP8PcGNWWDVbBCkKzZPrgg.sbDBL95GDdUvrgsSWNGjGbUJEueNKzGno4HYErikAD8dhfVzcbHKXpK1xJXFyfWE+xJ5KoKqTnkEi61yH5htFtK72Lb5wsZhYLi0ZoPRBtCifVvfOT27DtMH+Mw0COjb8f1JPaAyPPxuig942i1lzyvMxDPgh7.MBsvQ5wysEuE3TdIgSpCbNg2qJwHXk.05PLm6HXrJ2FBkzgV26g+TUvryV3iMxh+d3OUywJS39C6eUr1Qm+LPvxQXeCCn0hXeN0UDzm6R95f386oCZ4j96zCh3HDuQnv4tlZzqCbZsO32UCxova5ANxhndrbH0i6LT8HPp+7aFphLLZZSWpEOzPDZWPuRvs7ody4+52uFmitFtEvM5.I2DRm5W8K9lO4nUtIpN0v2118bYfkfHhNJDFWYbL9xsFGi0pkHLV6W9m+6aIT5Oly3GBv126el5m+stes3A3K2ZFXBgFCS2XmPLk62+21RXArsqKTSxPP8I+r+wW9e9Ae++G.pa92u+exCTqhOlZPINg7O769C+w+02PxXBn540BaUmFe9XNZeGvl1ll47IYM6Om4xqWp34D2AuoR1nN9Bv45gXDODbNBiy6mRgpYUTJoDgm7.bwLExkKW4rkKWnRAk7YqhDaOupTCo8xiDFgwflfPZTGx2AyQ.PZNfEe.4ypjMOLfpUqTnZ4rJ90j0rKi41AxQJPtkFmi+1iJz7IhRL2Sf52.bt6B5UNT2HgdDu7QC6jcrvN9Y5H70vLouvhAYMNhq615FbciLC5ONUsPSe5paAkpAIZkZDWz0EKzvGgG7cJuLv.iFkTJcHVc5grZeAQH0Tffu9vHB7b4pVMaghkxkKegJkqTzWfW2l9i6AYC1eDF3.8pQTRQwCjG3oRIjmJUyksZtpgP0CvUyToJ+WIkJEymMW47iiuREJqTthR47YKUToRIO7kB+InHIRDexEwl5xJ.WrRDlSZrGgPVfIPCJ1sa7oEsn1Xe.+sksp3W4xkJTIWwJ4FAOSXPyG.GX.NLOtz7RmoSmKO+bhXVnUw6aRf5zzRhDcb6V+r87xdWI4Y9Gju0fYNqj8FaoGsJ.+LNym7LNGhA+YNOXXdxIdrjqGG1E9pIrivgSioWaYOZzeuJd75H9n+dcMyn+gg3Ri.wz3cCYJrA9P9J5RL6rt2IR7g5lV8b236LB4MFntF7dVAuG8rdCLu8NLhjVrkRDaobiTrUbFKi5fNBiDpCLWBJcLPT6OgovEyia0gJen+qP1DFort4kvt2Q1E5wpCjzmLqsWCG8yMAocT19jYEwkz9WLdJXubqDkKp2HmVkpewryOdZFTd7Jk4ptb+ob6TXdRzUljeVOfVbdUbJNsc1iiQwwoid6Iq2HeFS8BYWlGIls945+efxzB3hbhu.TjCIpc6QNPr6gI.CquCMCuq3OmHdOyxs97d1BSNM1IeLPR3vhNQ7JdFZOoA51orrguuW8pqpl+uF6wX4cQJlTuys1AEooeDrCzFTH5fMjflW9lQRhRH1dRi5nWmD5KVoPoJkxCoBNXKrF5yW+2142Taw2BgJvIx1Hb8BQR6Vjf2NFTSsHhr3cSknyLLoLaOWpQPYfM5XHDBLaqeIEMqPtidfH0dcvmWdIyDeqHpMnUmfy.6M+grM1TmjKqjsyhdBlQ1fgNJuExi2NL1EcIdWcxq8kX9ffaea6dZ5rmx3S1KN4CETj4jj42qE0VYJeIA+kj9kDXk3ujfl8MU8J9JDu26JKaYSTufWfXnkhCyfBnBQ9XnVR9saG9KYno28y0zkX6Bu9HilBYp4D8MTW2v+iYPE+3u6oatobysOrwS2uo7lad5N5mKWW2oyoezwOuQqC1cPeGu+ie716K+L4VGucC4bmprYSnjV.ymJVc40KUQ9Lqtab5d18O8D10x4ybE4RTWNOEK0+yVE0k3c.z2+GsFx.1bd3MnOCucqVP+RohdEfKmrq.L5UTGZ7wbioK+15aoX4jcIty3V8+p+0xG4NmWM.iM04ZC6CUHX.VddX78v9NdBnF0r6PlIK3tMCq45BY.dNMZBYwsg7OM7ATdXsfihd.oucsmBU7RrG036s5mASrxqut+g2Ky8UH+5H2tUUyl5mVQpD8oU7FGuK9mYwaFnba7wEbarFcIp1rWnJ9lL35x2wiBruM8hRrJ9PdaYEzkilPQWvq3KTUi9pFah4VzIleQmXgEchEWzIVZQmX4EchUl8D4As7OA.tIKBcXi8EW4qzfLKjRg9uooL8ZB
@Morphoice also consider lowpassing the left chain signal, otherwise you'll just double everything above your cutoff frequency instead of just adding harmonics
Indeed, thus the phase cancellation addition :)
-
@Matt_SF brilliant, I didn't notice, so you beat me to that with an even better solution ;)))