├── .gitignore ├── Faug.jucer ├── Faug.vst3 ├── FaustDsp ├── Faug.dsp ├── basicDiode.dsp └── diode_issue.png ├── FaustInclude ├── UI.h ├── dsp.h └── meta.h ├── README.md ├── Source ├── Constants.cpp ├── Constants.h ├── FaustExp │ ├── FaugExp.cpp │ └── FaustIncludes.h ├── LinuxScripts │ ├── faug.sh │ ├── make.sh │ └── makeRun.sh ├── PluginProcessor.cpp ├── PluginProcessor.h ├── Scope │ ├── Spectroscope.cpp │ └── Spectroscope.h ├── Synth │ ├── FaugAudioSource.cpp │ ├── FaugAudioSource.h │ ├── FaustDspSound.cpp │ ├── FaustDspVoice.cpp │ └── FaustDspVoice.h └── UI │ ├── FaustUIBridge.cpp │ ├── FaustUIBridge.h │ ├── GuiElementsPosition.cpp │ ├── Knobs │ ├── Knob.cpp │ ├── Knob.h │ ├── KnobImage.cpp │ ├── KnobImage.h │ ├── KnobLookAndFeel.cpp │ └── KnobLookAndFeel.h │ ├── MainComponent.cpp │ ├── MainComponent.h │ ├── PluginEditor.cpp │ ├── PluginEditor.h │ ├── SplashAnimation.cpp │ ├── SplashAnimation.h │ ├── Toggles │ ├── Toggle.cpp │ ├── Toggle.h │ ├── ToggleImage.cpp │ ├── ToggleLookAndFeel.cpp │ └── ToggleLookAndFeel.h │ └── Wheel │ ├── ModWheel.cpp │ ├── ModWheel.h │ ├── ModWheelImage.cpp │ ├── ModWheelLookAndFeel.cpp │ └── ModWheelLookAndFeel.h ├── StandaloneApp └── Windows │ └── Faug.exe └── imageWork ├── background.png ├── backgroundExample.png ├── faugScreenshot.png ├── knobs ├── knobOne.png ├── knobTwo.png ├── screwKnob.png ├── wheel.png └── wheelShading.png └── toggles ├── blueToggle.png ├── brownToggle.png ├── orangeToggle.png └── whiteToggle.png /.gitignore: -------------------------------------------------------------------------------- 1 | ## My ignore statements 2 | 3 | JuceLibraryCode 4 | Builds 5 | .gitignore.bak 6 | FaustSource 7 | imageWork/inProgress 8 | .vscode 9 | 10 | ## Generated ignore statements from github's visual studio template 11 | 12 | 13 | ## Ignore Visual Studio temporary files, build results, and 14 | ## files generated by popular Visual Studio add-ons. 15 | ## 16 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 17 | 18 | # User-specific files 19 | *.rsuser 20 | *.suo 21 | *.user 22 | *.userosscache 23 | *.sln.docstates 24 | 25 | # User-specific files (MonoDevelop/Xamarin Studio) 26 | *.userprefs 27 | 28 | # Mono auto generated files 29 | mono_crash.* 30 | 31 | # Build results 32 | [Dd]ebug/ 33 | [Dd]ebugPublic/ 34 | [Rr]elease/ 35 | [Rr]eleases/ 36 | x64/ 37 | x86/ 38 | [Aa][Rr][Mm]/ 39 | [Aa][Rr][Mm]64/ 40 | bld/ 41 | [Bb]in/ 42 | [Oo]bj/ 43 | [Ll]og/ 44 | [Ll]ogs/ 45 | 46 | # Visual Studio 2015/2017 cache/options directory 47 | .vs/ 48 | # Uncomment if you have tasks that create the project's static files in wwwroot 49 | #wwwroot/ 50 | 51 | # Visual Studio 2017 auto generated files 52 | Generated\ Files/ 53 | 54 | # MSTest test Results 55 | [Tt]est[Rr]esult*/ 56 | [Bb]uild[Ll]og.* 57 | 58 | # NUnit 59 | *.VisualState.xml 60 | TestResult.xml 61 | nunit-*.xml 62 | 63 | # Build Results of an ATL Project 64 | [Dd]ebugPS/ 65 | [Rr]eleasePS/ 66 | dlldata.c 67 | 68 | # Benchmark Results 69 | BenchmarkDotNet.Artifacts/ 70 | 71 | # .NET Core 72 | project.lock.json 73 | project.fragment.lock.json 74 | artifacts/ 75 | 76 | # StyleCop 77 | StyleCopReport.xml 78 | 79 | # Files built by Visual Studio 80 | *_i.c 81 | *_p.c 82 | *_h.h 83 | *.ilk 84 | *.meta 85 | *.obj 86 | *.iobj 87 | *.pch 88 | *.pdb 89 | *.ipdb 90 | *.pgc 91 | *.pgd 92 | *.rsp 93 | *.sbr 94 | *.tlb 95 | *.tli 96 | *.tlh 97 | *.tmp 98 | *.tmp_proj 99 | *_wpftmp.csproj 100 | *.log 101 | *.vspscc 102 | *.vssscc 103 | .builds 104 | *.pidb 105 | *.svclog 106 | *.scc 107 | 108 | # Chutzpah Test files 109 | _Chutzpah* 110 | 111 | # Visual C++ cache files 112 | ipch/ 113 | *.aps 114 | *.ncb 115 | *.opendb 116 | *.opensdf 117 | *.sdf 118 | *.cachefile 119 | *.VC.db 120 | *.VC.VC.opendb 121 | 122 | # Visual Studio profiler 123 | *.psess 124 | *.vsp 125 | *.vspx 126 | *.sap 127 | 128 | # Visual Studio Trace Files 129 | *.e2e 130 | 131 | # TFS 2012 Local Workspace 132 | $tf/ 133 | 134 | # Guidance Automation Toolkit 135 | *.gpState 136 | 137 | # ReSharper is a .NET coding add-in 138 | _ReSharper*/ 139 | *.[Rr]e[Ss]harper 140 | *.DotSettings.user 141 | 142 | # TeamCity is a build add-in 143 | _TeamCity* 144 | 145 | # DotCover is a Code Coverage Tool 146 | *.dotCover 147 | 148 | # AxoCover is a Code Coverage Tool 149 | .axoCover/* 150 | !.axoCover/settings.json 151 | 152 | # Visual Studio code coverage results 153 | *.coverage 154 | *.coveragexml 155 | 156 | # NCrunch 157 | _NCrunch_* 158 | .*crunch*.local.xml 159 | nCrunchTemp_* 160 | 161 | # MightyMoose 162 | *.mm.* 163 | AutoTest.Net/ 164 | 165 | # Web workbench (sass) 166 | .sass-cache/ 167 | 168 | # Installshield output folder 169 | [Ee]xpress/ 170 | 171 | # DocProject is a documentation generator add-in 172 | DocProject/buildhelp/ 173 | DocProject/Help/*.HxT 174 | DocProject/Help/*.HxC 175 | DocProject/Help/*.hhc 176 | DocProject/Help/*.hhk 177 | DocProject/Help/*.hhp 178 | DocProject/Help/Html2 179 | DocProject/Help/html 180 | 181 | # Click-Once directory 182 | publish/ 183 | 184 | # Publish Web Output 185 | *.[Pp]ublish.xml 186 | *.azurePubxml 187 | # Note: Comment the next line if you want to checkin your web deploy settings, 188 | # but database connection strings (with potential passwords) will be unencrypted 189 | *.pubxml 190 | *.publishproj 191 | 192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 193 | # checkin your Azure Web App publish settings, but sensitive information contained 194 | # in these scripts will be unencrypted 195 | PublishScripts/ 196 | 197 | # NuGet Packages 198 | *.nupkg 199 | # NuGet Symbol Packages 200 | *.snupkg 201 | # The packages folder can be ignored because of Package Restore 202 | **/[Pp]ackages/* 203 | # except build/, which is used as an MSBuild target. 204 | !**/[Pp]ackages/build/ 205 | # Uncomment if necessary however generally it will be regenerated when needed 206 | #!**/[Pp]ackages/repositories.config 207 | # NuGet v3's project.json files produces more ignorable files 208 | *.nuget.props 209 | *.nuget.targets 210 | 211 | # Microsoft Azure Build Output 212 | csx/ 213 | *.build.csdef 214 | 215 | # Microsoft Azure Emulator 216 | ecf/ 217 | rcf/ 218 | 219 | # Windows Store app package directories and files 220 | AppPackages/ 221 | BundleArtifacts/ 222 | Package.StoreAssociation.xml 223 | _pkginfo.txt 224 | *.appx 225 | *.appxbundle 226 | *.appxupload 227 | 228 | # Visual Studio cache files 229 | # files ending in .cache can be ignored 230 | *.[Cc]ache 231 | # but keep track of directories ending in .cache 232 | !?*.[Cc]ache/ 233 | 234 | # Others 235 | ClientBin/ 236 | ~$* 237 | *~ 238 | *.dbmdl 239 | *.dbproj.schemaview 240 | *.jfm 241 | *.pfx 242 | *.publishsettings 243 | orleans.codegen.cs 244 | 245 | # Including strong name files can present a security risk 246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 247 | #*.snk 248 | 249 | # Since there are multiple workflows, uncomment next line to ignore bower_components 250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 251 | #bower_components/ 252 | 253 | # RIA/Silverlight projects 254 | Generated_Code/ 255 | 256 | # Backup & report files from converting an old project file 257 | # to a newer Visual Studio version. Backup files are not needed, 258 | # because we have git ;-) 259 | _UpgradeReport_Files/ 260 | Backup*/ 261 | UpgradeLog*.XML 262 | UpgradeLog*.htm 263 | ServiceFabricBackup/ 264 | *.rptproj.bak 265 | 266 | # SQL Server files 267 | *.mdf 268 | *.ldf 269 | *.ndf 270 | 271 | # Business Intelligence projects 272 | *.rdl.data 273 | *.bim.layout 274 | *.bim_*.settings 275 | *.rptproj.rsuser 276 | *- [Bb]ackup.rdl 277 | *- [Bb]ackup ([0-9]).rdl 278 | *- [Bb]ackup ([0-9][0-9]).rdl 279 | 280 | # Microsoft Fakes 281 | FakesAssemblies/ 282 | 283 | # GhostDoc plugin setting file 284 | *.GhostDoc.xml 285 | 286 | # Node.js Tools for Visual Studio 287 | .ntvs_analysis.dat 288 | node_modules/ 289 | 290 | # Visual Studio 6 build log 291 | *.plg 292 | 293 | # Visual Studio 6 workspace options file 294 | *.opt 295 | 296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 297 | *.vbw 298 | 299 | # Visual Studio LightSwitch build output 300 | **/*.HTMLClient/GeneratedArtifacts 301 | **/*.DesktopClient/GeneratedArtifacts 302 | **/*.DesktopClient/ModelManifest.xml 303 | **/*.Server/GeneratedArtifacts 304 | **/*.Server/ModelManifest.xml 305 | _Pvt_Extensions 306 | 307 | # Paket dependency manager 308 | .paket/paket.exe 309 | paket-files/ 310 | 311 | # FAKE - F# Make 312 | .fake/ 313 | 314 | # CodeRush personal settings 315 | .cr/personal 316 | 317 | # Python Tools for Visual Studio (PTVS) 318 | __pycache__/ 319 | *.pyc 320 | 321 | # Cake - Uncomment if you are using it 322 | # tools/** 323 | # !tools/packages.config 324 | 325 | # Tabs Studio 326 | *.tss 327 | 328 | # Telerik's JustMock configuration file 329 | *.jmconfig 330 | 331 | # BizTalk build output 332 | *.btp.cs 333 | *.btm.cs 334 | *.odx.cs 335 | *.xsd.cs 336 | 337 | # OpenCover UI analysis results 338 | OpenCover/ 339 | 340 | # Azure Stream Analytics local run output 341 | ASALocalRun/ 342 | 343 | # MSBuild Binary and Structured Log 344 | *.binlog 345 | 346 | # NVidia Nsight GPU debugger configuration file 347 | *.nvuser 348 | 349 | # MFractors (Xamarin productivity tool) working folder 350 | .mfractor/ 351 | 352 | # Local History for Visual Studio 353 | .localhistory/ 354 | 355 | # BeatPulse healthcheck temp database 356 | healthchecksdb 357 | 358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 359 | MigrationBackup/ 360 | 361 | # Ionide (cross platform F# VS Code tools) working folder 362 | .ionide/ 363 | -------------------------------------------------------------------------------- /Faug.jucer: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 32 | 34 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 58 | 60 | 62 | 63 | 65 | 66 | 67 | 68 | 69 | 71 | 73 | 74 | 75 | 76 | 77 | 78 | 80 | 82 | 83 | 85 | 87 | 89 | 90 | 92 | 93 | 95 | 96 | 97 | 98 | 99 | 101 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /Faug.vst3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/Faug.vst3 -------------------------------------------------------------------------------- /FaustDsp/Faug.dsp: -------------------------------------------------------------------------------- 1 | import("stdfaust.lib"); 2 | 3 | display(name, mini, maxi) = _ <: attach(_,vbargraph("[00]%name[style:numerical]",mini,maxi)); 4 | limit_range(mini,maxi) = _, mini : max : _, maxi : min; 5 | gain = hslider("gain[style:knob]",1.0,0.0,1.0,0.01); 6 | 7 | // Midi note 48, 130.81hz, C2 is default 1V in model D 8 | // Will use as center for keyTrackingDiff 9 | keyboardCenter = 130.81; 10 | 11 | // currently I only have 44.1khz 12 | // the things relying on this require it to be known at compile time anyway 13 | nyquist = 22050.0;//ma.SR/2; 14 | 15 | // todo variable keytracking per oscillator 16 | 17 | generateSound(fdb) = output(fdb) : filter : _*envelope 18 | with{ 19 | gate = button("[00]gate"); 20 | 21 | frequencyIn = nentry("[01]freq[unit:Hz]", 440, 20, 20000, 0.01); 22 | prevfreq = nentry("[02]prevFreq[unit:Hz]", 440, 20, 20000, 0.01); 23 | 24 | pitchbend = hslider("[03]pitchBend[style:knob]", 0, -2.5, 2.5, 0.01) : ba.semi2ratio; 25 | glide = hslider("[04]glide[style:knob]", 0.01, 0.001, 3.0, 0.001); 26 | start_time = ba.latch(frequencyIn != frequencyIn', ba.time); 27 | dt = ba.time - start_time; 28 | epsilon = 0.01; 29 | expo(tau) = exp((0-dt)/(tau*ma.SR)), epsilon : max; 30 | 31 | blend(rate, f, pf) = f*(1 - expo(rate)) + pf*expo(rate); 32 | glideOn = checkbox("[05]glideOn"); 33 | 34 | freq = blend(glide, frequencyIn, ba.if(glideOn, prevfreq, frequencyIn)) 35 | <: attach(_,vbargraph("finalFreq[style:numerical]",0,20000)); 36 | 37 | // Oscillators 38 | scale = 1.0, oscOnePower*oscOneGain*0.4 + 39 | oscTwoPower*oscTwoGain*0.4 + 40 | oscThreePower*oscThreeGain*0.4 : max; 41 | 42 | oscOnePower = checkbox("[06]oscOnePower"); 43 | oscTwoPower = checkbox("[07]oscTwoPower"); 44 | oscThreePower = checkbox("[08]oscThreePower"); 45 | 46 | oscOneGain = hslider("[09]oscOneGain[style:knob]", 1.0,0.0,1.0,0.01); 47 | oscTwoGain = hslider("[10]oscTwoGain[style:knob]", 1.0,0.0,1.0,0.01); 48 | oscThreeGain = hslider("[11]oscThreeGain[style:knob]",1.0,0.0,1.0,0.01); 49 | 50 | oscModOn = checkbox("[12]oscModOn"); 51 | 52 | // oscillators 53 | oscOne = waveOneTwo(freqOne, rangeOne, waveSelectOne)*oscOneGain*oscOnePower; 54 | oscTwoSignal = waveOneTwo(freqTwo, rangeTwo, waveSelectTwo); 55 | oscThreeSignal = waveThree (freqThree, rangeThree, waveSelectThree); 56 | 57 | oscTwo = oscTwoSignal *oscTwoGain *oscTwoPower; 58 | oscThree = oscThreeSignal*oscThreeGain*oscThreePower; 59 | 60 | oscillators = (oscOne + oscTwo + oscThree)/scale; 61 | 62 | freqOne = freq, 2^(rangeOne-4) : * : _*globalDetune*driftOne*pitchbend : modulate(oscModOn); 63 | freqTwo = freq, 2^(rangeTwo-4) : * : _*detuneTwo *driftTwo*pitchbend : modulate(oscModOn); 64 | 65 | oscThreeKeyTrack = checkbox("[13]oscThreeKeyTrack"); 66 | freqThreePre = freq, keyboardCenter : select2(oscThreeKeyTrack); 67 | freqThree = freqThreePre : _, 2^(rangeThree-4) : * : _*detuneThree *driftThree*pitchbend; 68 | 69 | // Oscillator wave selectors. 3rd option in waves one and two is a triangle saw 70 | // 3rd option in wave three is a reverse saw 71 | waveSelectOne = hslider("[14]waveOne[style:knob]" ,1,0,5,1); 72 | waveSelectTwo = hslider("[15]waveTwo[style:knob]" ,1,0,5,1); 73 | waveSelectThree = hslider("[16]waveThree[style:knob]",1,0,5,1); 74 | waveOneTwo(f,r,ws) = tri(f,r), triSaw(f,r), saw(f,r), square(f,r), 75 | rectangle(f,r,0.70), rectangle(f,r,0.85) : ba.selectn(6,ws); 76 | waveThree(f,r,ws) = tri(f,r), revSaw(f,r), saw(f,r), square(f,r), 77 | rectangle(f,r,0.70), rectangle(f,r,0.85) : ba.selectn(6,ws); 78 | 79 | driftOne = os.osc(0.05)*0.01 : 2^_ : @(ma.SR/100); 80 | driftTwo = driftOne@(ma.SR/100); 81 | driftThree = driftTwo@(ma.SR/100); 82 | 83 | rangeOne = hslider("[17]rangeOne[style:knob]",2,0,5,1); 84 | rangeTwo = hslider("[18]rangeTwo[style:knob]",2,0,5,1); 85 | rangeThree = hslider("[19]rangeThree[style:knob]",2,0,5,1); 86 | 87 | globalDetuneSemi = hslider("[20]globalDetune[style:knob]", 0, -2.5, 2.5, 0.01); 88 | globalDetune = globalDetuneSemi : ba.semi2ratio; 89 | detuneTwo = hslider("[21]detuneTwo[style:knob]", 0, -7.5, 7.5, 0.01) + globalDetuneSemi : ba.semi2ratio; 90 | detuneThree = hslider("[22]detuneThree[style:knob]", 0, -7.5, 7.5, 0.01) + globalDetuneSemi : ba.semi2ratio; 91 | 92 | tri(f,type) = os.lf_triangle(f), os.triangle(f) : select2(type); 93 | saw(f,type) = os.lf_saw(f), os.sawtooth(f) : select2(type); 94 | square(f,type) = os.lf_squarewave(f), os.square(f) : select2(type); 95 | 96 | rectangle(f,type,n) = os.lf_pulsetrain(f,n), os.pulsetrain(f,n) : select2(type); 97 | revSaw(f,type) = saw(f,type), -1: *; 98 | triSaw(f,type) = (saw(f,type) + tri(f,type))/2; 99 | 100 | 101 | // Envelope Section 102 | envelope = en.adsr(attack,decay,sustain,release,gate) <: _, si.smoo : select2(decayButton); 103 | decayButton = checkbox("[23]decayOn"); 104 | attack = hslider("[24]attack[style:knob]",1,1,10000,1)*0.001; 105 | decay = hslider("[25]decay[style:knob]",4,1,24000,1)*0.001; 106 | sustain = hslider("[26]sustain[style:knob]",0.8,0.01,1,0.01); 107 | release = 10*0.001, decay : select2(decayButton); 108 | 109 | // Filter Section 110 | // filter response is 32k, cutoff max is 20k 111 | // Have to have constant value know at compile time. 112 | // Maybe have multiple hardcoded values for different sample rates 113 | 114 | filterMax = 20000.0/nyquist; 115 | filterMin = 10.0/nyquist; 116 | cutoffIn = hslider("[27]cutoff[style:knob]",0.5,filterMin,filterMax,0.001); 117 | cutoffFreq = cutoffIn*nyquist; 118 | 119 | // key tracking stuff 120 | // offset of played frequency from keyboard center 121 | keyTrackDiff = frequencyIn-keyboardCenter; 122 | 123 | //oneThird, twoThird 124 | keyTrackOne = checkbox("[28]keyTrackOne")*keyTrackDiff; 125 | keyTrackTwo = checkbox("[29]keyTrackTwo")*2.0*keyTrackDiff; 126 | keyTrackSum = (keyTrackOne + keyTrackTwo)/3.0; 127 | cutoff_KeyTrack = cutoffFreq + keyTrackSum; 128 | 129 | // filter contour 130 | reverseContour = hslider("[30]contour_direction[style:radio{'+':0;'-':1}]",0,0,1,1); 131 | contourAmount = hslider("[31]contourAmount[style:knob]",0.0,0.0,1.0,0.001); 132 | fAttack = hslider("[32]fAttack[style:knob]",1,1,7000,1)*0.001; 133 | fDecay = hslider("[33]fDecay[style:knob]",4,1,30000,1)*0.001; 134 | fSustain = hslider("[34]fSustain[style:knob]",0.8,0.01,1.0,0.01); 135 | fRelease = 10*0.001, fDecay : select2(decayButton); 136 | 137 | // either up 4 octaves, or down 4 octaves 138 | contourPeak = 16.0, (1/16) : select2(reverseContour) : _*cutoff_KeyTrack; 139 | 140 | filterUp = cutoff_KeyTrack <: _ + contourAmount*filterContour*(contourPeak-_); 141 | filterDown = cutoff_KeyTrack <: _ - contourAmount*filterContour*(_-contourPeak); 142 | filterContour = en.adsr(fAttack, fDecay, fSustain, fRelease, gate) <: _, si.smoo : select2(decayButton); 143 | 144 | // set signal up/down a percentage of 4 octaves from the set cutoff-frequency(plus keyTrack) 145 | cutOffCombine = filterUp, filterDown : select2(reverseContour) : modulate(filterModOn) : _/nyquist : limit_range(filterMin,filterMax); 146 | 147 | filterModOn = checkbox("[35]filterModOn"); 148 | modulate(on) = _ <: _, _*(2^(modulation)) : select2(on); 149 | 150 | emphasis = hslider("[36]emphasis[style:knob]",1,0.707,25.0,0.001); 151 | filter = ve.moogLadder(cutOffCombine, emphasis); 152 | 153 | // Noise 154 | noiseSelect = checkbox("[37]noiseType"); 155 | noiseOn = checkbox("[38]noiseOn"); 156 | noiseGain = hslider("[39]noiseGain[style:knob]", 0.0, 0.0, 1.0, 0.01); 157 | noise = no.noise, no.pink_noise : select2(noiseSelect)*noiseGain*noiseOn; 158 | 159 | // Modulation 160 | modLeft = oscThreeSignal, filterContour : select2(checkbox("[40]oscThree_filterEg")); 161 | 162 | lfoRate = hslider("[41]lfoRate[style:knob]",10.0,0.5,200.0,0.01) ; 163 | lfo = os.lf_triangle(lfoRate), os.lf_squarewave(lfoRate) : select2(checkbox("[42]lfoShape")); 164 | 165 | lowBandLimit = 20; 166 | bw3 = 0.7 * ma.SR/2.0 - lowBandLimit; 167 | redNoise = no.noise : fi.spectral_tilt(3,lowBandLimit,bw3,-0.25); 168 | modNoise = no.pink_noise, redNoise : select2(noiseSelect); 169 | 170 | modRight = modNoise, lfo : select2(checkbox("[43]noise_lfo")) ; 171 | modMix = hslider("[44]modMix[style:knob]",0.0,0.0,1.0,0.01); 172 | modAmount = hslider("[45]modAmount[style:knob]",0.0,0.0,1.0,0.01); 173 | modulation = (1-modMix)*modLeft + (modMix)*modRight : _*modAmount; 174 | 175 | load = hslider("[46]load[style:knob]",1.0,1.0,3.0,0.01); 176 | output(fdb) = ((oscillators+noise)*load)+fdb; 177 | }; 178 | 179 | process = hgroup("faug", (generateSound ~ fdBackSignal) : drive : _*on*masterVolume) <: _,_ 180 | with { 181 | // Inverting power button so it defaults to on 182 | powerButton = checkbox("on"); 183 | on = 1.0,0.0 : select2(powerButton); 184 | masterVolume = hslider("masterVolume[style:knob]",1.0,0.0,1.0,0.01); 185 | 186 | fdbackOn = checkbox("feedbackOn"); 187 | fdback = hslider("feedbackGain[style:knob]",0,0,1,0.01); 188 | fdBackSignal = _*fdback*fdbackOn; 189 | drive = _ <: drySig, wetSig : + : aa.Ratanh; 190 | drySig = _, (1-fdback)*_ : select2(fdbackOn); 191 | wetSig = 0.0, _*(2^fdback) : select2(fdbackOn); 192 | }; -------------------------------------------------------------------------------- /FaustDsp/basicDiode.dsp: -------------------------------------------------------------------------------- 1 | import("stdfaust.lib"); 2 | 3 | // current/voltage constants 4 | is1 = 1e-15; 5 | is2 = 1e-15; 6 | vt1 = 26e-3; 7 | vt2 = 26e-3; 8 | 9 | // derivative of current over resistor 10 | gr = 1.0 / 2200.0; 11 | 12 | // Newton–Raphson loop 13 | nr_loop(v1, v2_guess1, v2_guess2) = (gr*v1 + id1eq - id2eq) / (gr + gd1) 14 | with { 15 | vd1 = 0.0-v2_guess1; 16 | ed1 = exp(vd1/vt1); 17 | id1 = is1*ed1-is1; 18 | gd1 = is1*ed1/vt1; 19 | id1eq = id1 - gd1*vd1; 20 | 21 | vd2 = v2_guess2-0.0; 22 | ed2 = exp(vd2/vt2); 23 | id2 = is2*ed2-is2; 24 | gd2 = is2*ed2/vt2; 25 | id2eq = id2 - gd2*vd2; 26 | }; 27 | 28 | diode_clipper = nr_loop(_, 0.0, 0.0); 29 | 30 | process = experiment <: _,_ with { 31 | sound = os.sawtooth(440)*hslider("gain[style:knob]", 1.0, 0.0, 2.0, 0.01); 32 | mix = hslider("mix[style:knob]",0.0, 0.0, 1.0, 0.01); 33 | experiment = sound <: _*(1-mix) + diode_clipper(_)*mix; 34 | }; -------------------------------------------------------------------------------- /FaustDsp/diode_issue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/FaustDsp/diode_issue.png -------------------------------------------------------------------------------- /FaustInclude/UI.h: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | FAUST Architecture File 3 | Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale 4 | --------------------------------------------------------------------- 5 | This Architecture section is free software; you can redistribute it 6 | and/or modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 3 of 8 | the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; If not, see . 17 | 18 | EXCEPTION : As a special exception, you may create a larger work 19 | that contains this FAUST architecture section and distribute 20 | that work under terms of your choice, so long as this FAUST 21 | architecture section is not modified. 22 | ************************************************************************/ 23 | 24 | #ifndef __UI_H__ 25 | #define __UI_H__ 26 | 27 | #ifndef FAUSTFLOAT 28 | #define FAUSTFLOAT float 29 | #endif 30 | 31 | /******************************************************************************* 32 | * UI : Faust DSP User Interface 33 | * User Interface as expected by the buildUserInterface() method of a DSP. 34 | * This abstract class contains only the method that the Faust compiler can 35 | * generate to describe a DSP user interface. 36 | ******************************************************************************/ 37 | 38 | struct Soundfile; 39 | 40 | class UI 41 | { 42 | 43 | public: 44 | 45 | UI() {} 46 | 47 | virtual ~UI() {} 48 | 49 | // -- widget's layouts 50 | 51 | virtual void openTabBox(const char* label) = 0; 52 | virtual void openHorizontalBox(const char* label) = 0; 53 | virtual void openVerticalBox(const char* label) = 0; 54 | virtual void closeBox() = 0; 55 | 56 | // -- active widgets 57 | 58 | virtual void addButton(const char* label, FAUSTFLOAT* zone) = 0; 59 | virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) = 0; 60 | virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) = 0; 61 | virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) = 0; 62 | virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) = 0; 63 | 64 | // -- passive widgets 65 | 66 | virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) = 0; 67 | virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) = 0; 68 | 69 | // -- soundfiles 70 | 71 | virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0; 72 | 73 | // -- metadata declarations 74 | 75 | virtual void declare(FAUSTFLOAT*, const char*, const char*) {} 76 | }; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /FaustInclude/dsp.h: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | FAUST Architecture File 3 | Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale 4 | --------------------------------------------------------------------- 5 | This Architecture section is free software; you can redistribute it 6 | and/or modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 3 of 8 | the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; If not, see . 17 | 18 | EXCEPTION : As a special exception, you may create a larger work 19 | that contains this FAUST architecture section and distribute 20 | that work under terms of your choice, so long as this FAUST 21 | architecture section is not modified. 22 | ************************************************************************/ 23 | 24 | #ifndef __dsp__ 25 | #define __dsp__ 26 | 27 | #include 28 | #include 29 | 30 | #ifndef FAUSTFLOAT 31 | #define FAUSTFLOAT float 32 | #endif 33 | 34 | class UI; 35 | struct Meta; 36 | 37 | /** 38 | * DSP memory manager. 39 | */ 40 | 41 | struct dsp_memory_manager { 42 | 43 | virtual ~dsp_memory_manager() {} 44 | 45 | virtual void* allocate(size_t size) = 0; 46 | virtual void destroy(void* ptr) = 0; 47 | 48 | }; 49 | 50 | /** 51 | * Signal processor definition. 52 | */ 53 | 54 | class dsp { 55 | 56 | public: 57 | 58 | dsp() {} 59 | virtual ~dsp() {} 60 | 61 | /* Return instance number of audio inputs */ 62 | virtual int getNumInputs() = 0; 63 | 64 | /* Return instance number of audio outputs */ 65 | virtual int getNumOutputs() = 0; 66 | 67 | /** 68 | * Trigger the ui_interface parameter with instance specific calls 69 | * to 'addBtton', 'addVerticalSlider'... in order to build the UI. 70 | * 71 | * @param ui_interface - the user interface builder 72 | */ 73 | virtual void buildUserInterface(UI* ui_interface) = 0; 74 | 75 | /* Returns the sample rate currently used by the instance */ 76 | virtual int getSampleRate() = 0; 77 | 78 | /** 79 | * Global init, calls the following methods: 80 | * - static class 'classInit': static tables initialization 81 | * - 'instanceInit': constants and instance state initialization 82 | * 83 | * @param samplingRate - the sampling rate in Hertz 84 | */ 85 | virtual void init(int samplingRate) = 0; 86 | 87 | /** 88 | * Init instance state 89 | * 90 | * @param samplingRate - the sampling rate in Hertz 91 | */ 92 | virtual void instanceInit(int samplingRate) = 0; 93 | 94 | /** 95 | * Init instance constant state 96 | * 97 | * @param samplingRate - the sampling rate in Hertz 98 | */ 99 | virtual void instanceConstants(int samplingRate) = 0; 100 | 101 | /* Init default control parameters values */ 102 | virtual void instanceResetUserInterface() = 0; 103 | 104 | /* Init instance state (delay lines...) */ 105 | virtual void instanceClear() = 0; 106 | 107 | /** 108 | * Return a clone of the instance. 109 | * 110 | * @return a copy of the instance on success, otherwise a null pointer. 111 | */ 112 | virtual dsp* clone() = 0; 113 | 114 | /** 115 | * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata. 116 | * 117 | * @param m - the Meta* meta user 118 | */ 119 | virtual void metadata(Meta* m) = 0; 120 | 121 | /** 122 | * DSP instance computation, to be called with successive in/out audio buffers. 123 | * 124 | * @param count - the number of frames to compute 125 | * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) 126 | * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) 127 | * 128 | */ 129 | virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0; 130 | 131 | /** 132 | * DSP instance computation: alternative method to be used by subclasses. 133 | * 134 | * @param date_usec - the timestamp in microsec given by audio driver. 135 | * @param count - the number of frames to compute 136 | * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) 137 | * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) 138 | * 139 | */ 140 | virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } 141 | 142 | }; 143 | 144 | /** 145 | * Generic DSP decorator. 146 | */ 147 | 148 | class decorator_dsp : public dsp { 149 | 150 | protected: 151 | 152 | dsp* fDSP; 153 | 154 | public: 155 | 156 | decorator_dsp(dsp* dsp = 0):fDSP(dsp) {} 157 | virtual ~decorator_dsp() { delete fDSP; } 158 | 159 | virtual int getNumInputs() { return fDSP->getNumInputs(); } 160 | virtual int getNumOutputs() { return fDSP->getNumOutputs(); } 161 | virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); } 162 | virtual int getSampleRate() { return fDSP->getSampleRate(); } 163 | virtual void init(int samplingRate) { fDSP->init(samplingRate); } 164 | virtual void instanceInit(int samplingRate) { fDSP->instanceInit(samplingRate); } 165 | virtual void instanceConstants(int samplingRate) { fDSP->instanceConstants(samplingRate); } 166 | virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); } 167 | virtual void instanceClear() { fDSP->instanceClear(); } 168 | virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); } 169 | virtual void metadata(Meta* m) { fDSP->metadata(m); } 170 | // Beware: subclasses usually have to overload the two 'compute' methods 171 | virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); } 172 | virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); } 173 | 174 | }; 175 | 176 | /** 177 | * DSP factory class. 178 | */ 179 | 180 | class dsp_factory { 181 | 182 | protected: 183 | 184 | // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory); 185 | virtual ~dsp_factory() {} 186 | 187 | public: 188 | 189 | virtual std::string getName() = 0; 190 | virtual std::string getSHAKey() = 0; 191 | virtual std::string getDSPCode() = 0; 192 | virtual std::string getCompileOptions() = 0; 193 | virtual std::vector getLibraryList() = 0; 194 | virtual std::vector getIncludePathnames() = 0; 195 | 196 | virtual dsp* createDSPInstance() = 0; 197 | 198 | virtual void setMemoryManager(dsp_memory_manager* manager) = 0; 199 | virtual dsp_memory_manager* getMemoryManager() = 0; 200 | 201 | }; 202 | 203 | /** 204 | * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero) 205 | * flags to avoid costly denormals. 206 | */ 207 | 208 | #ifdef __SSE__ 209 | #include 210 | #ifdef __SSE2__ 211 | #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040) 212 | #else 213 | #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000) 214 | #endif 215 | #else 216 | #define AVOIDDENORMALS 217 | #endif 218 | 219 | #endif 220 | -------------------------------------------------------------------------------- /FaustInclude/meta.h: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | FAUST Architecture File 3 | Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale 4 | --------------------------------------------------------------------- 5 | This Architecture section is free software; you can redistribute it 6 | and/or modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 3 of 8 | the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; If not, see . 17 | 18 | EXCEPTION : As a special exception, you may create a larger work 19 | that contains this FAUST architecture section and distribute 20 | that work under terms of your choice, so long as this FAUST 21 | architecture section is not modified. 22 | ************************************************************************/ 23 | 24 | #ifndef __meta__ 25 | #define __meta__ 26 | 27 | struct Meta 28 | { 29 | virtual void declare(const char* key, const char* value) = 0; 30 | virtual ~Meta() {}; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Faug 2 | A Minimoog Model D emulation with the DSP portion written in Faust. Moog + Faust = Faug 3 | 4 | ![Alt text](./imageWork/faugScreenshot.png?raw=true "Faug UI") 5 | 6 |

Controllers Section:

7 |
    8 |
  • Tune Knob: Global detune ranging from [-2.5, 2.5] semi-tones
  • 9 |
  • Glide Knob : Glide rate/portamento, max time ~6sec
  • 10 |
  • Modulation Mix Knob: Mixes between modulation sources determined by buttons below
  • 11 |
  • Osc-3/Filter Eg Button: Determines mod source added by left side of the Modulation Mix knob 12 |
      13 |
    • Osc-3: Osc three signal is modulation source
    • 14 |
    • Filter EG: Filter Envelope Generator is modulation source
    • 15 |
    16 |
  • 17 |
  • Noise/LFO Button: Determines mod source added by Right side of the Modulation Mix knob 18 |
      19 |
    • Noise: Noise is modulation source 20 |
        21 |
      • White Noise in mixer section means pink noise for noise modulation source
      • 22 |
      • Pink Noise in mixer section means red noise for noise modulation source
      • 23 |
      24 |
    • 25 |
    • LFO: LFO is modulation source
    • 26 |
    27 |
  • 28 |
29 | 30 |

Keyboard Section:

31 |
    32 |
  • LFO Rate Knob: Frequency of dedicated modulation Low Frequency Oscillator: [0.5Hz,200Hz] 33 |
      34 |
    • ToDo: LFO Shape toggle
    • 35 |
    36 |
  • 37 |
  • Pitch Wheel: Pitch bend for while playing. 38 |
      39 |
    • ToDo: Setting to send wheel automatically back to center when released
    • 40 |
    41 |
  • 42 |
  • Mod Wheel: Modulation Amount to modulation targets
  • 43 |
  • Glide Button: 44 |
      45 |
    • Activates glide/portamento when notes are played with no seperation
    • 46 |
    47 |
  • 48 |
  • Decay Button: 49 |
      50 |
    • Applies an Envelope's decay setting to it's release stage
    • 51 |
    • Release is ~10ms when turned off
    • 52 |
    53 |
  • 54 |
55 | 56 |

Oscillator Bank:

57 |
    58 |
  • Oscillator Modulation Button: Turns on modulation source routing to Oscillators 1 and 2
      59 |
    • Oscillator 3 doesn't get modulated b/c it is a modulation source
    • 60 |
  • 61 |
  • Osc-3 Control Button: Connects Oscillator 3's frequency to the keyboard input
      62 |
    • Uses Keyboard center of C3 - Midi Note 48 - 130.81Hz as input frequency when Oscillator 3 disengaged from keyboard
    • 63 |
  • 64 |
  • Range Column:
      65 |
    • Octave for pitch of respective oscillator
    • 66 |
    • Units are feet - tradition from organ pipes
    • 67 |
    • Midi Note 93(A6) at 16' plays 440Hz(A4)
    • 68 |
    • Lo mode uses aliasing oscillators since the pitch is in a range aliasing shouldn't occur
    • 69 |
  • 70 |
  • Frequency column:
      71 |
    • Detune for Oscillators 2 and 3
    • 72 |
    • Range of [-7.5,7.5] semi-tones
    • 73 |
    • Oscillator 1 doesn't get detuned as it serves as a reference for the other two
    • 74 |
  • 75 |
  • Waveform column:
      76 |
    • Triangle: Triangle wave
      • 77 |
      • Triangle+Saw: Oscillators 1 & 2: A triangle and saw wave added together
      • 78 |
      • Reverse Saw: Oscillator 3: A saw wave that ramps down instead of ramping up
      • 79 |
      80 |
    • Saw: Saw Wave, ramps up
    • 81 |
    • Square: Square Wave
    • 82 |
    • Rectangle: Rectangle Wave-Pulse train with 0.7 duty cycle
    • 83 |
    • Narrow Rectangle: Rectangle Wave-Pulse train with 0.85 duty cycle
    • 84 |
  • 85 |
86 | 87 |

Mixer Section:

88 |
    89 |
  • Left Column of knobs: Volume knobs for their respective oscillators
      90 |
    • Volume Knob for Oscillator 3 has no impact on it's contribution to modulation signal
    • 91 |
  • 92 |
  • Center Column of buttons: On/Off buttons for their respective noise sources contribution to the sound signal path
      93 |
    • On/Off buttons for Oscillator 3 and Noise have no impact on they're contributions to the modulation signal
    • 94 |
  • 95 |
  • Right Column:
      96 |
    • Load Screw/knob: Increases the output of Oscillators and Noise(not feedback) before being fed into the filter
        97 |
      • ToDo: Include a warm little light that increases in brightness with load
      • 98 |
      • ToDo: Improve/build upon scaling/saturation algorithms
      • 99 |
    • 100 |
    • Feedback Amount: Amount of the signal's output that gets fed back to the input
        101 |
      • ToDo: Improve and build upon feedback saturation/processing
      • 102 |
    • 103 |
    • Noise Volume: Volume knob for noise signal's contribution to sound signal
        104 |
      • Has no impact on noise's contribution to modulation signal
      • 105 |
    • 106 |
    • Noise-Type Button: Determines noise type for sound and modulation signals
        107 |
      • White Noise for sound signal is Pink Noise for modulation Signal
      • 108 |
      • Pink Noise for sound signal is Red Noise for modulation Signal
      • 109 |
    • 110 |
  • 111 |
112 | 113 |

Filter Section:

114 |

Faust Libraries-vaeffects emulation of traditional moog 4th order ladder filter

115 |
    116 |
  • Filter Modulation Button: Turns on modulation of filter cutoff by modulation sources
  • 117 |
  • Keyboard Control:
      118 |
    • Keyboard Control 1: Turns on 1/3 of key tracking for filter cutoff
    • 119 |
    • Keyboard Control 2: Turns on 2/3 of key tracking for filter cutoff
    • 120 |
    • Turning on both gives full keyboard tracking eg. Filter cutoff will move 1:1 with change in played pitch
    • 121 |
  • 122 |
  • Cutoff Frequency Knob: Determines base cutoff of filter: range [10Hz,20kHz]
  • 123 |
  • Emphasis Knob: Controls the amount of filter signal fed back into filter, resulting in a resonance peak at cutoff
      124 |
    • Self-Oscillation occurs at max value
    • 125 |
    • ToDo: Add scaling to reduce emphasis as cutoff freq goes down so bass notes still have bass frequencies
    • 126 |
  • 127 |
  • Contour Amount Knob: Adjusts the amount that the Filter's envelope generator impacts the filter cutoff
      128 |
    • Max Range of 4 octaves
    • 129 |
    • ToDo: Add filter direction toggle
    • 130 |
  • 131 |
  • Attack Time Knob: The amount of time it takes for the filter cutoff to go from the base frequency to max or min cutoff frequency.
      132 |
    • Value Range: [1ms, 7s]
    • 133 |
    • Peak change amount is determined by Contour Amount knob.
    • 134 |
  • 135 |
  • Sustain Level Knob: The percent of difference between filter cutoff and contour peak for the cutoff frequency to stay at after Decay Phase
      136 |
    • Value Range: [0%, 100%]
    • 137 |
    • 0%: After the Attack phase, the filter cutoff will decay to the base cutoff frequency set by the knob
    • 138 |
    • 50%: If contour is set to max(4 octaves), the filter cutoff will decay to 2 octaves above base cutoff frequency determined by knob
    • 139 |
    • 100%: After the Attack phase, the filter cutoff will not decay at all and will sustain at peak value until note is released
    • 140 |
  • 141 |
  • Decay Time Knob: The amount of time it takes for the filter cutoff to go from the max or min cutoff frequency to Sustain level
      142 |
    • Value Range: [4ms, 30s]
    • 143 |
    • Begins immediately after Attack phase ends
    • 144 |
  • 145 |
  • Release for filter envelope is either 10ms, or Decay Time value if Decay Button in keyboard section is on
  • 146 |
  • ToDo: Add different cutoff slopes/filter implementations
  • 147 |
148 | 149 |

Loudness Contour:

150 |
    151 |
  • Attack Time Knob: The amount of time it takes for the volume to reach it's peak.
      152 |
    • Value Range: [1ms, 10s]
    • 153 |
    • Peak determined by the Volume Knob in Power Section.
    • 154 |
  • 155 |
  • Sustain Level Knob: Volume level held after decay
      156 |
    • Value Range: [0%, 100%]
    • 157 |
    • 0%: Volume will decay to nothing
    • 158 |
    • 100%: No volume decay after attack until note released
    • 159 |
  • 160 |
  • Decay Time Knob: The amount of time it takes for the filter cutoff to go from the max or min cutoff frequency to Sustain level
      161 |
    • Value Range: [4ms, 24s]
    • 162 |
    • Begins immediately after Attack phase ends
    • 163 |
  • 164 |
  • Release for envelope is either 10ms, or Decay Time value if Decay Button in keyboard section is on
  • 165 |
166 | 167 |

Power Section:

168 |
    169 |
  • Volume Knob: Scales overall output
  • 170 |
  • Power Button: Turns output on/off
      171 |
    • ToDo: Add a warm up process
    • 172 |
  • 173 |
174 |

Other ToDos:

175 |
    176 |
  • UI Labels/knob ticks need some fine-tuning to line up
  • 177 |
  • Variable key tracking for notes
  • 178 |
  • Variable detune/drift
  • 179 |
  • Settings page to allow for these and other adjustments
  • 180 |
  • Oversampling
  • 181 |
  • Alternate UI view when no onscreen keyboard is needed
      182 |
    • Different layout, more of a rack/vertical style?
    • 183 |
    • Add Loudness Meter
    • 184 |
    • Add Spectrum graph
    • 185 |
    • Add Waveform graph
    • 186 |
    • Add to settings page to toggle them on/off
    • 187 |
  • 188 |
  • UI/visual cues to inform user which buttons are flipped on
  • 189 |
  • Contour generator builds on itself when decay button is on
  • 190 |
  • Replace various curves and contours with exponential implementations
  • 191 |
192 | -------------------------------------------------------------------------------- /Source/Constants.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | Constants.cpp 5 | Created: 19 Sep 2022 7:40:18am 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "Constants.h" 12 | 13 | // String constants pointing to their respective parameter paths in Faust 14 | // NON-GUI 15 | 16 | // String values are Faust variable names 17 | const char* const PANIC = "Panic"; 18 | const char* const FREQ = "freq"; 19 | const char* const FINAL_FREQ = "finalFreq"; 20 | const char* const PREV_FREQ = "prevFreq"; 21 | const char* const GATE = "gate"; 22 | 23 | // CONTROLLERS 24 | const char* const GLOBAL_DETUNE = "globalDetune"; 25 | const char* const GLIDE_RATE = "glide"; 26 | 27 | // ToDos 28 | const char* const MOD_MIX = "modMix"; 29 | const char* const OSC3_FILTEG_MOD = "oscThree_filterEg"; 30 | const char* const NOISE_LFO_MOD = "noise_lfo"; 31 | 32 | //KEYBOARD 33 | //Todo 34 | const char* const LFO_RATE = "lfoRate"; 35 | const char* const GLIDE_ON = "glideOn"; 36 | const char* const DECAY_ON = "decayOn"; 37 | const char* const PITCH_BEND = "pitchBend"; 38 | const char* const MOD_AMOUNT = "modAmount"; 39 | 40 | // In-Between 41 | const char* const OSC_MOD_ON = "oscModOn"; 42 | 43 | //OscillatorBank 44 | const char* const OSC1_RANGE = "rangeOne"; 45 | const char* const OSC1_WAVE = "waveOne"; 46 | 47 | const char* const OSC2_RANGE = "rangeTwo"; 48 | const char* const OSC2_DETUNE = "detuneTwo"; 49 | const char* const OSC2_WAVE = "waveTwo"; 50 | 51 | const char* const OSC3_RANGE = "rangeThree"; 52 | const char* const OSC3_DETUNE = "detuneThree"; 53 | const char* const OSC3_WAVE = "waveThree"; 54 | //ToDo 55 | const char* const OSC3_CTRL = "oscThreeKeyTrack"; 56 | 57 | //Mixer 58 | const char* const LOAD = "load"; 59 | 60 | const char* const OSC1_ON = "oscOnePower"; 61 | const char* const OSC1_GAIN = "oscOneGain"; 62 | 63 | const char* const FEEDBACK_ON = "feedbackOn"; 64 | const char* const FEEDBACK_GAIN = "feedbackGain"; 65 | 66 | const char* const OSC2_ON = "oscTwoPower"; 67 | const char* const OSC2_GAIN = "oscTwoGain"; 68 | 69 | const char* const NOISE_ON = "noiseOn"; 70 | const char* const NOISE_GAIN = "noiseGain"; 71 | const char* const NOISE_TYPE = "noiseType"; 72 | 73 | const char* const OSC3_ON = "oscThreePower"; 74 | const char* const OSC3_GAIN = "oscThreeGain"; 75 | 76 | //In-Between 77 | const char* const F_MOD_ON = "filterModOn"; 78 | const char* const KEY_TRK1 = "keyTrackOne"; 79 | const char* const KEY_TRK2 = "keyTrackTwo"; 80 | 81 | // FILTER PARAMS 82 | const char* const F_CUTOFF = "cutoff"; 83 | const char* const F_Q = "emphasis"; 84 | const char* const F_ENV_AMOUNT = "contourAmount"; 85 | const char* const F_ENV_DIRECTION = "contourDirection"; 86 | const char* const F_ATTACK = "fAttack"; 87 | const char* const F_DECAY = "fDecay"; 88 | const char* const F_SUSTAIN = "fSustain"; 89 | 90 | // ENVELOPE PARAMS 91 | const char* const ATTACK = "attack"; 92 | const char* const DECAY = "decay"; 93 | const char* const SUSTAIN = "sustain"; 94 | 95 | // Power 96 | const char * const VOLUME = "masterVolume"; 97 | const char * const ON = "on"; 98 | 99 | // for sizing look and feels 100 | const char * const FLOAT_KNOB_EX = OSC1_GAIN; 101 | const char * const BIG_FLOAT_KNOB_EX = OSC2_DETUNE; 102 | const char * const TINY_FLOAT_KNOB_EX = VOLUME; 103 | const char * const INT_KNOB_EX = OSC1_RANGE; 104 | const char * const SCREW_FLOAT_EX = LOAD; 105 | 106 | const char * const ORANGE_TOG_EX = F_MOD_ON; 107 | const char * const BIG_ORANGE_TOG_EX = OSC_MOD_ON; 108 | const char * const BLUE_TOG_EX = OSC1_ON; 109 | const char * const WHITE_TOG_EX = GLIDE_ON; 110 | const char * const BROWN_TOG_EX = ON; 111 | 112 | const char * const MOD_WHEEL_EX = MOD_AMOUNT; 113 | 114 | const std::map skewMap = 115 | { 116 | {VOLUME,0.5}, 117 | {OSC1_GAIN,0.5}, 118 | {OSC2_GAIN,0.5}, 119 | {OSC3_GAIN,0.5}, 120 | {ATTACK, 0.5}, 121 | {DECAY, 0.5}, 122 | {F_ATTACK, 0.5}, 123 | {F_DECAY, 0.5}, 124 | {F_CUTOFF, 0.75}, 125 | {GLIDE_RATE, 0.5} 126 | }; -------------------------------------------------------------------------------- /Source/Constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // String constants pointing to their respective parameter paths in Faust 8 | // NON-GUI 9 | extern const char * const PANIC; 10 | extern const char * const GATE; 11 | extern const char * const FREQ; 12 | extern const char * const FINAL_FREQ; 13 | extern const char * const PREV_FREQ; 14 | 15 | // CONTROLLERS 16 | extern const char * const GLOBAL_DETUNE; 17 | extern const char * const GLIDE_RATE; 18 | // ToDos 19 | extern const char * const MOD_MIX; 20 | extern const char * const OSC3_FILTEG_MOD; 21 | extern const char * const NOISE_LFO_MOD; 22 | 23 | //KEYBOARD 24 | extern const char * const LFO_RATE; 25 | extern const char * const GLIDE_ON; 26 | extern const char * const DECAY_ON; 27 | //Todo 28 | //move wheel shadow from guiElement to background 29 | extern const char * const PITCH_BEND; 30 | extern const char * const MOD_AMOUNT; 31 | 32 | // In-Between 33 | extern const char * const OSC_MOD_ON; 34 | 35 | //OscillatorBank 36 | extern const char * const OSC1_RANGE; 37 | extern const char * const OSC1_WAVE; 38 | 39 | extern const char * const OSC2_RANGE; 40 | extern const char * const OSC2_DETUNE; 41 | extern const char * const OSC2_WAVE; 42 | 43 | extern const char * const OSC3_RANGE; 44 | extern const char * const OSC3_DETUNE; 45 | extern const char * const OSC3_WAVE; 46 | //ToDo 47 | extern const char * const OSC3_CTRL; 48 | 49 | //Mixer 50 | extern const char * const LOAD; 51 | 52 | extern const char * const OSC1_ON; 53 | extern const char * const OSC1_GAIN; 54 | 55 | extern const char * const FEEDBACK_ON; 56 | extern const char * const FEEDBACK_GAIN; 57 | 58 | extern const char * const OSC2_ON; 59 | extern const char * const OSC2_GAIN; 60 | 61 | extern const char * const NOISE_ON; 62 | extern const char * const NOISE_GAIN; 63 | extern const char * const NOISE_TYPE; 64 | 65 | extern const char * const OSC3_ON; 66 | extern const char * const OSC3_GAIN; 67 | 68 | //In-Between 69 | extern const char * const F_MOD_ON; 70 | extern const char * const KEY_TRK1; 71 | extern const char * const KEY_TRK2; 72 | 73 | // FILTER PARAMS 74 | extern const char * const F_CUTOFF; 75 | extern const char * const F_Q; 76 | extern const char * const F_ENV_AMOUNT; 77 | extern const char * const F_ATTACK; 78 | extern const char * const F_DECAY; 79 | extern const char * const F_SUSTAIN; 80 | 81 | // ENVELOPE PARAMS 82 | extern const char * const ATTACK; 83 | extern const char * const DECAY; 84 | extern const char * const SUSTAIN; 85 | 86 | //Power 87 | extern const char * const VOLUME; 88 | extern const char * const ON; 89 | 90 | extern const char * const FLOAT_KNOB_EX; 91 | extern const char * const BIG_FLOAT_KNOB_EX; 92 | extern const char * const TINY_FLOAT_KNOB_EX; 93 | extern const char * const INT_KNOB_EX; 94 | extern const char * const SCREW_FLOAT_EX; 95 | 96 | extern const char * const ORANGE_TOG_EX; 97 | extern const char * const BIG_ORANGE_TOG_EX; 98 | extern const char * const BLUE_TOG_EX; 99 | extern const char * const WHITE_TOG_EX; 100 | extern const char * const BROWN_TOG_EX; 101 | 102 | extern const char * const MOD_WHEEL_EX; 103 | 104 | extern const std::map> guiPositions; 105 | 106 | extern const std::map skewMap; -------------------------------------------------------------------------------- /Source/FaustExp/FaustIncludes.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | FaustIncludes.h 5 | Created: 18 Aug 2022 5:54:57pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "../FaustInclude/dsp.h" 14 | #include "../FaustInclude/meta.h" 15 | #include "../FaustInclude/UI.h" -------------------------------------------------------------------------------- /Source/LinuxScripts/faug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ../../Builds/LinuxMakefile/build/Faug -------------------------------------------------------------------------------- /Source/LinuxScripts/make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd ../../Builds/LinuxMakefile 4 | make -------------------------------------------------------------------------------- /Source/LinuxScripts/makeRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./make.sh && ./faug.sh -------------------------------------------------------------------------------- /Source/PluginProcessor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This file contains the basic framework code for a JUCE plugin processor. 4 | 5 | ============================================================================== 6 | */ 7 | 8 | #include "./Synth/FaugAudioSource.h" 9 | #include "PluginProcessor.h" 10 | #include "./UI/PluginEditor.h" 11 | #include "./UI/FaustUIBridge.h" 12 | #include "./FaustExp/FaugExp.cpp" 13 | #include "Constants.h" 14 | 15 | //============================================================================== 16 | FaugAudioProcessor::FaugAudioProcessor() 17 | #ifndef JucePlugin_PreferredChannelConfigurations 18 | : AudioProcessor(BusesProperties() 19 | #if ! JucePlugin_IsMidiEffect 20 | #if ! JucePlugin_IsSynth 21 | .withInput("Input", juce::AudioChannelSet::stereo(), true) 22 | #endif 23 | .withOutput("Output", juce::AudioChannelSet::stereo(), true) 24 | #endif 25 | ), 26 | #endif 27 | m_params(*this, nullptr), m_keyState(std::make_unique()), m_dsp(std::make_unique()) 28 | { 29 | m_audioSource = std::make_unique(*m_keyState.get(), m_params, *m_UiBridge.get(), *m_dsp.get()); 30 | m_UiBridge = std::make_unique(m_params); 31 | m_dsp.get()->buildUserInterface(m_UiBridge.get()); 32 | } 33 | 34 | FaugAudioProcessor::~FaugAudioProcessor() 35 | { 36 | } 37 | 38 | //============================================================================== 39 | const juce::String FaugAudioProcessor::getName() const 40 | { 41 | return JucePlugin_Name; 42 | } 43 | 44 | bool FaugAudioProcessor::acceptsMidi() const 45 | { 46 | #if JucePlugin_WantsMidiInput 47 | return true; 48 | #else 49 | return false; 50 | #endif 51 | } 52 | 53 | bool FaugAudioProcessor::producesMidi() const 54 | { 55 | #if JucePlugin_ProducesMidiOutput 56 | return true; 57 | #else 58 | return false; 59 | #endif 60 | } 61 | 62 | bool FaugAudioProcessor::isMidiEffect() const 63 | { 64 | #if JucePlugin_IsMidiEffect 65 | return true; 66 | #else 67 | return false; 68 | #endif 69 | } 70 | 71 | double FaugAudioProcessor::getTailLengthSeconds() const 72 | { 73 | return 0.0; 74 | } 75 | 76 | int FaugAudioProcessor::getNumPrograms() 77 | { 78 | return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, 79 | // so this should be at least 1, even if you're not really implementing programs. 80 | } 81 | 82 | int FaugAudioProcessor::getCurrentProgram() 83 | { 84 | return 0; 85 | } 86 | 87 | void FaugAudioProcessor::setCurrentProgram (int index) 88 | { 89 | } 90 | 91 | const juce::String FaugAudioProcessor::getProgramName (int index) 92 | { 93 | return {}; 94 | } 95 | 96 | void FaugAudioProcessor::changeProgramName (int index, const juce::String& newName) 97 | { 98 | } 99 | 100 | //============================================================================== 101 | void FaugAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) 102 | { 103 | juce::ValueTree root(juce::Identifier("FAUG")); 104 | 105 | m_dsp.get()->init(sampleRate); 106 | m_audioSource->prepareToPlay(samplesPerBlock, sampleRate); 107 | 108 | m_params.state = root; 109 | } 110 | 111 | void FaugAudioProcessor::releaseResources() 112 | { 113 | m_audioSource->releaseResources(); 114 | } 115 | 116 | #ifndef JucePlugin_PreferredChannelConfigurations 117 | bool FaugAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const 118 | { 119 | #if JucePlugin_IsMidiEffect 120 | juce::ignoreUnused (layouts); 121 | return true; 122 | #else 123 | // This is the place where you check if the layout is supported. 124 | // In this template code we only support mono or stereo. 125 | // Some plugin hosts, such as certain GarageBand versions, will only 126 | // load plugins that support stereo bus layouts. 127 | if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() 128 | && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) 129 | return false; 130 | 131 | // This checks if the input layout matches the output layout 132 | #if ! JucePlugin_IsSynth 133 | if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) 134 | return false; 135 | #endif 136 | 137 | return true; 138 | #endif 139 | } 140 | #endif 141 | 142 | void FaugAudioProcessor::processBlock (juce::AudioBuffer& buffer, juce::MidiBuffer& midiMessages) 143 | { 144 | m_keyState->processNextMidiBuffer(midiMessages, 0, midiMessages.getNumEvents(), false); 145 | 146 | juce::AudioSourceChannelInfo bufferToFill; 147 | bufferToFill.buffer = &buffer; 148 | bufferToFill.startSample = 0; 149 | bufferToFill.numSamples = buffer.getNumSamples(); 150 | m_audioSource->getNextAudioBlock(bufferToFill); 151 | 152 | //FaugAudioProcessorEditor* editor = static_cast(getActiveEditor()); 153 | } 154 | 155 | //============================================================================== 156 | bool FaugAudioProcessor::hasEditor() const 157 | { 158 | return true; // (change this to false if you choose to not supply an editor) 159 | } 160 | 161 | juce::AudioProcessorEditor* FaugAudioProcessor::createEditor() 162 | { 163 | return new FaugAudioProcessorEditor (*this, *m_keyState.get(), m_params); 164 | } 165 | 166 | //============================================================================== 167 | void FaugAudioProcessor::getStateInformation (juce::MemoryBlock& destData) 168 | { 169 | // You should use this method to store your parameters in the memory block. 170 | // You could do that either as raw data, or use the XML or ValueTree classes 171 | // as intermediaries to make it easy to save and load complex data. 172 | auto state = m_params.copyState(); 173 | std::unique_ptr xml(state.createXml()); 174 | copyXmlToBinary(*xml, destData); 175 | } 176 | 177 | void FaugAudioProcessor::setStateInformation (const void* data, int sizeInBytes) 178 | { 179 | // You should use this method to restore your parameters from this memory block, 180 | // whose contents will have been created by the getStateInformation() call. 181 | std::unique_ptr xmlState(getXmlFromBinary(data, sizeInBytes)); 182 | if(xmlState.get() != nullptr && 183 | xmlState->hasTagName(m_params.state.getType())) { 184 | m_params.replaceState(juce::ValueTree::fromXml(*xmlState)); 185 | } 186 | } 187 | 188 | //============================================================================== 189 | // This creates new instances of the plugin.. 190 | juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() 191 | { 192 | return new FaugAudioProcessor(); 193 | } 194 | //============================================================================== -------------------------------------------------------------------------------- /Source/PluginProcessor.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file contains the basic framework code for a JUCE plugin processor. 5 | 6 | ============================================================================== 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | class FaugAudioSource; 14 | class mydsp; 15 | class FaustUIBridge; 16 | 17 | //============================================================================== 18 | /** 19 | */ 20 | class FaugAudioProcessor : public juce::AudioProcessor 21 | #if JucePlugin_Enable_ARA 22 | , public juce::AudioProcessorARAExtension 23 | #endif, juce::MidiKeyboardStateListner 24 | { 25 | public: 26 | //============================================================================== 27 | FaugAudioProcessor(); 28 | virtual ~FaugAudioProcessor() override; 29 | 30 | //============================================================================== 31 | void prepareToPlay (double sampleRate, int samplesPerBlock) override; 32 | void releaseResources() override; 33 | 34 | #ifndef JucePlugin_PreferredChannelConfigurations 35 | bool isBusesLayoutSupported (const BusesLayout& layouts) const override; 36 | #endif 37 | 38 | void processBlock (juce::AudioBuffer&, juce::MidiBuffer&) override; 39 | 40 | //============================================================================== 41 | juce::AudioProcessorEditor* createEditor() override; 42 | bool hasEditor() const override; 43 | 44 | //============================================================================== 45 | const juce::String getName() const override; 46 | 47 | bool acceptsMidi() const override; 48 | bool producesMidi() const override; 49 | bool isMidiEffect() const override; 50 | double getTailLengthSeconds() const override; 51 | 52 | //============================================================================== 53 | int getNumPrograms() override; 54 | int getCurrentProgram() override; 55 | void setCurrentProgram (int index) override; 56 | const juce::String getProgramName (int index) override; 57 | void changeProgramName (int index, const juce::String& newName) override; 58 | 59 | //============================================================================== 60 | void getStateInformation (juce::MemoryBlock& destData) override; 61 | void setStateInformation (const void* data, int sizeInBytes) override; 62 | //============================================================================== 63 | 64 | private: 65 | //============================================================================== 66 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FaugAudioProcessor) 67 | juce::AudioProcessorValueTreeState m_params; 68 | std::unique_ptr m_audioSource; 69 | std::unique_ptr m_keyState; 70 | std::unique_ptr m_dsp; 71 | std::unique_ptr m_UiBridge; 72 | }; -------------------------------------------------------------------------------- /Source/Scope/Spectroscope.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | Spectroscope.cpp 5 | Created: 5 Aug 2022 6:54:55pm 6 | Author: tytu1 7 | 8 | // Reference: https://github.com/creativeintent/temper/blob/master/Source/SpectroscopeComponent.cpp 9 | 10 | ============================================================================== 11 | */ 12 | 13 | #include 14 | #include "Spectroscope.h" 15 | 16 | //============================================================================== 17 | Spectroscope::Spectroscope() : m_fifoIndex(0), 18 | m_fftBlockReady(false), 19 | m_forwardFFT(kFFTOrder), 20 | m_window(kFFTSize, juce::dsp::WindowingFunction::hann), 21 | m_strokeColor(juce::Colours::white), 22 | m_fillStartColor(juce:: Colours::white.withAlpha(0.2f)), 23 | m_fillStopColor(juce::Colours::white.withAlpha(0.8f)) 24 | { 25 | juce::zeromem(m_outputData, sizeof(m_outputData)); 26 | setSize(600, 150); 27 | startTimerHz(30); 28 | } 29 | 30 | Spectroscope::~Spectroscope() 31 | { 32 | stopTimer(); 33 | } 34 | 35 | void Spectroscope::paint(juce::Graphics& g) 36 | { 37 | const float width = (float)getWidth(); 38 | const float height = (float)getHeight(); 39 | 40 | // Clear the drawing target 41 | g.setColour(juce::Colours::transparentBlack); 42 | g.fillAll(); 43 | 44 | // The values in the output bins after the FFT have a range that I don't understand 45 | // and isn't explained in the docs. It seems that if I scale down by the size of the 46 | // fft buffer, I get somewhat reasonable results on the graph. But in examples I've 47 | // seen, we would just divide here by the maximum value in the bins at the time of 48 | // drawing. Seeing as that would be inconsistent between frames, I'm defaulting to the 49 | // size of the fft here unless the max value in the bins is larger. 50 | juce::Range maxBin = juce::FloatVectorOperations::findMinAndMax(m_outputData, kOutputSize); 51 | const float scale = 1.0f / juce::jmax((float)kFFTSize, maxBin.getEnd()); 52 | 53 | g.setColour(m_fillStartColor); 54 | 55 | for (int i = 0; i < kOutputSize; ++i) 56 | { 57 | float x = std::log10(1 + 39 * ((i + 1.0f) / kOutputSize)) / std::log10(40.0f) * width; 58 | 59 | const float yMag = scale * m_outputData[i]; 60 | const float yDecibel = juce::Decibels::gainToDecibels(yMag); 61 | const float y = juce::jmap(yDecibel, -90.0f, -12.0f, height, 0.0f); 62 | 63 | g.drawVerticalLine((int)x, y, height); 64 | } 65 | } 66 | 67 | void Spectroscope::resized() 68 | { 69 | } 70 | 71 | void Spectroscope::timerCallback() 72 | { 73 | if (m_fftBlockReady) 74 | { 75 | // Compute the frequency transform 76 | m_window.multiplyWithWindowingTable(m_fftData, kFFTSize); 77 | m_forwardFFT.performFrequencyOnlyForwardTransform(m_fftData); 78 | 79 | // Copy the frequency bins into the output data buffer, taking 80 | // max(output[i], fftData[i]) for each bin. Note that after computing the 81 | // FrequencyOnlyForwardTransform on an array A of size N, A[N/2, N) is full 82 | // of zeros, and A[0, N/4) is a mirror of A[N/4, N/2). Therefore we only copy 83 | // kFFTSize / 2 samples into the output data buffer here. 84 | juce::FloatVectorOperations::max(m_outputData, m_outputData, m_fftData, kOutputSize); 85 | 86 | m_fftBlockReady = false; 87 | } 88 | 89 | // Decay the output bin magnitudes 90 | for (int i = 0; i < kOutputSize; ++i) 91 | m_outputData[i] *= 0.707f; 92 | 93 | repaint(); 94 | } 95 | 96 | void Spectroscope::pushBuffer(juce::AudioSampleBuffer& buffer) 97 | { 98 | if (buffer.getNumChannels() > 0) 99 | { 100 | const int numSamples = buffer.getNumSamples(); 101 | const float* channelData = buffer.getReadPointer(0); 102 | 103 | for (int i = 0; i < numSamples; ++i) 104 | pushSample(channelData[i]); 105 | } 106 | } 107 | 108 | inline void Spectroscope::pushSample(float sample) 109 | { 110 | // When we wrap around the fifo table, we copy the data into the 111 | // FFT buffer and prepare to perform the transform. 112 | if (m_fifoIndex == kFFTSize) 113 | { 114 | if (!m_fftBlockReady) 115 | { 116 | juce::zeromem(m_fftData, sizeof(m_fftData)); 117 | memcpy(m_fftData, m_fifo, sizeof(m_fifo)); 118 | m_fftBlockReady = true; 119 | } 120 | 121 | m_fifoIndex = 0; 122 | } 123 | 124 | m_fifo[m_fifoIndex++] = sample; 125 | } 126 | 127 | void Spectroscope::setColors(juce::Colour strokeColor, juce::Colour fillStartColor, juce::Colour fillStopColor) 128 | { 129 | m_strokeColor = strokeColor; 130 | m_fillStartColor = fillStartColor; 131 | m_fillStopColor = fillStopColor; 132 | } -------------------------------------------------------------------------------- /Source/Scope/Spectroscope.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | Spectroscope.h 5 | Created: 5 Aug 2022 6:54:55pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | //============================================================================== 16 | /* 17 | */ 18 | class Spectroscope : public juce::Component, 19 | juce::Timer 20 | { 21 | public: 22 | Spectroscope(); 23 | ~Spectroscope() override; 24 | 25 | void paint (juce::Graphics&) override; 26 | void resized() override; 27 | void timerCallback() override; 28 | 29 | void pushBuffer(juce::AudioSampleBuffer& buffer); 30 | inline void pushSample(float sample); 31 | 32 | void setColors(juce::Colour strokeColor, juce::Colour fillStartColour, juce::Colour fillStopColour); 33 | 34 | enum 35 | { 36 | kFFTOrder = 11, 37 | kFFTSize = 2048, 38 | kOutputSize = 1024, 39 | }; 40 | 41 | private: 42 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Spectroscope) 43 | 44 | float m_fifo[kFFTSize]; 45 | float m_fftData[2*kFFTSize]; 46 | float m_outputData[kOutputSize]; 47 | 48 | unsigned int m_fifoIndex; 49 | bool m_fftBlockReady; 50 | 51 | juce::dsp::FFT m_forwardFFT; 52 | juce::dsp::WindowingFunction m_window; 53 | 54 | juce::Colour m_strokeColor; 55 | juce::Colour m_fillStartColor; 56 | juce::Colour m_fillStopColor; 57 | }; 58 | -------------------------------------------------------------------------------- /Source/Synth/FaugAudioSource.cpp: -------------------------------------------------------------------------------- 1 | // Holds all the info for playing and making sound with the synth 2 | // Add legato button 3 | // Need Last, Low, and High Note priorities 4 | 5 | #include "FaugAudioSource.h" 6 | #include "FaustDspSound.cpp" 7 | #include "FaustDspVoice.h" 8 | #include "../UI/FaustUIBridge.h" 9 | #include "../FaustExp/FaugExp.cpp" 10 | 11 | FaugAudioSource::FaugAudioSource(juce::MidiKeyboardState& keyState, juce::AudioProcessorValueTreeState& vts, 12 | FaustUIBridge& fBridge, mydsp& fDsp) 13 | : keyboardState(keyState), mVts(vts), numHeldNotes(0), heldNotes(), currentNote(0), prevNote(0), 14 | mBridge(fBridge), mFaust(fDsp), synth() 15 | { 16 | // This is a mono-synth, only one voice needed 17 | 18 | synth.addSound(new FaustDspSound()); 19 | synth.setNoteStealingEnabled(true); 20 | 21 | keyboardState.addListener(this); 22 | 23 | } 24 | 25 | FaugAudioSource::~FaugAudioSource() 26 | { 27 | } 28 | 29 | void FaugAudioSource::setUsingSynthSound() 30 | { 31 | synth.clearSounds(); 32 | } 33 | 34 | void FaugAudioSource::prepareToPlay(int samplesPerBlockExpected, double sampleRate) 35 | { 36 | FaustDspVoice* synthVoice = new FaustDspVoice(mVts, mFaust); 37 | synthVoice->prepareToPlay(sampleRate, samplesPerBlockExpected); 38 | synth.addVoice(synthVoice); 39 | 40 | synth.setCurrentPlaybackSampleRate(sampleRate); 41 | midiCollector.reset(sampleRate); 42 | } 43 | 44 | void FaugAudioSource::releaseResources() { 45 | synth.removeVoice(0); 46 | } 47 | 48 | void FaugAudioSource::getNextAudioBlock(const juce::AudioSourceChannelInfo & bufferToFill) 49 | { 50 | bufferToFill.clearActiveBufferRegion(); 51 | juce::MidiBuffer incomingMidi; 52 | midiCollector.removeNextBlockOfMessages(incomingMidi, bufferToFill.numSamples); 53 | 54 | keyboardState.processNextMidiBuffer(incomingMidi, bufferToFill.startSample, 55 | bufferToFill.numSamples, true); 56 | 57 | synth.renderNextBlock(*bufferToFill.buffer, incomingMidi, 58 | bufferToFill.startSample, bufferToFill.numSamples); 59 | } 60 | 61 | juce::MidiMessageCollector* FaugAudioSource::getMidiCollector() 62 | { 63 | return &midiCollector; 64 | } 65 | 66 | void FaugAudioSource::handleIncomingMidiMessage(juce::MidiInput* source, const juce::MidiMessage& message) 67 | { 68 | midiCollector.addMessageToQueue(message); 69 | keyboardState.processNextMidiEvent(message); 70 | } 71 | 72 | void FaugAudioSource::handleNoteOn(juce::MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) 73 | { 74 | //Todo: Note priority options. For now, just last note priority 75 | 76 | //If the voice is active, it's currently playing currentNote. Move it to prev_freq for glide 77 | //If the voice isn't active, set prev_freq to new note so there is no glide 78 | 79 | mVts.getParameterAsValue(PREV_FREQ).setValue(synth.getVoice(0)->isVoiceActive() ? mVts.getParameterAsValue(FREQ).getValue() : 80 | juce::MidiMessage::getMidiNoteInHertz(midiNoteNumber)); 81 | currentNote = midiNoteNumber; 82 | heldNotes[numHeldNotes++] = midiNoteNumber; 83 | synth.noteOn(midiChannel, midiNoteNumber, velocity); 84 | } 85 | 86 | void FaugAudioSource::handleNoteOff(juce::MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) 87 | { 88 | bool shift = false; 89 | for (int i = 0; i < numHeldNotes; i++) 90 | { 91 | if (heldNotes[i] == midiNoteNumber) 92 | { 93 | heldNotes[i] = 0; 94 | shift = true; 95 | continue; 96 | } 97 | 98 | if (shift) 99 | { 100 | heldNotes[i - 1] = heldNotes[i]; 101 | heldNotes[i] = 0; 102 | } 103 | } 104 | numHeldNotes--; 105 | bool triggerNewNote = (synth.getVoice(0)->getCurrentlyPlayingNote() == midiNoteNumber && numHeldNotes > 0); 106 | if (triggerNewNote) 107 | { 108 | mVts.getParameterAsValue(PREV_FREQ).setValue(mVts.getParameterAsValue(FREQ).getValue()); 109 | mVts.getParameterAsValue(GATE).setValue(false); 110 | currentNote = heldNotes[numHeldNotes-1]; 111 | if (currentNote != 0) { 112 | synth.noteOn(midiChannel, currentNote, velocity); 113 | } 114 | } 115 | synth.noteOff(midiChannel, midiNoteNumber, 0, false); 116 | } -------------------------------------------------------------------------------- /Source/Synth/FaugAudioSource.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Holds all the info for playing and making sound with the synth 4 | 5 | #include 6 | class mydsp; 7 | class FaustUIBridge; 8 | class FaustDspVoice; 9 | 10 | 11 | class FaugAudioSource : public juce::AudioSource, 12 | juce::MidiInputCallback, 13 | private juce::MidiKeyboardStateListener 14 | { 15 | public: 16 | FaugAudioSource(juce::MidiKeyboardState& keyState, juce::AudioProcessorValueTreeState& vts, 17 | FaustUIBridge& fBridge, mydsp& fDsp); 18 | virtual ~FaugAudioSource() override; 19 | 20 | void setUsingSynthSound(); 21 | 22 | void prepareToPlay(int /*samplesPerBlockExpected*/, double sampleRate) override; 23 | void releaseResources() override; 24 | void getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill) override; 25 | 26 | juce::MidiMessageCollector* getMidiCollector(); 27 | void handleIncomingMidiMessage(juce::MidiInput* source, const juce::MidiMessage& message) override; //MidiInputCallback 28 | void handleNoteOn(juce::MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override; //MidiKeyboardStateListener 29 | void handleNoteOff(juce::MidiKeyboardState*, int midiChannel, int midiNoteNumber, float /*velocity*/) override; //MidiKeyboardStateListener 30 | 31 | static const int noteMemory = 10; 32 | 33 | private: 34 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FaugAudioSource) 35 | juce::MidiMessageCollector midiCollector; 36 | juce::Synthesiser synth; 37 | juce::MidiKeyboardState& keyboardState; 38 | mydsp& mFaust; 39 | FaustUIBridge& mBridge; 40 | juce::AudioProcessorValueTreeState& mVts; 41 | 42 | int currentNote; 43 | int prevNote; 44 | int numHeldNotes; 45 | std::array heldNotes; 46 | }; -------------------------------------------------------------------------------- /Source/Synth/FaustDspSound.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct FaustDspSound : public juce::SynthesiserSound 4 | { 5 | public: 6 | FaustDspSound() {} 7 | virtual ~FaustDspSound() override {} 8 | 9 | bool appliesToNote(int) override { return true; } 10 | bool appliesToChannel(int) override { return true; } 11 | 12 | private: 13 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FaustDspSound) 14 | }; -------------------------------------------------------------------------------- /Source/Synth/FaustDspVoice.cpp: -------------------------------------------------------------------------------- 1 | #include "FaustDspSound.cpp" 2 | #include "FaustDspVoice.h" 3 | #include "../FaustExp/FaugExp.cpp" 4 | 5 | FaustDspVoice::FaustDspVoice(juce::AudioProcessorValueTreeState& valueTreeState, mydsp& faustDsp) : vts(valueTreeState), 6 | currentFreq(0.f), 7 | mFaust(faustDsp), 8 | outputs(new float* [2]) 9 | { 10 | 11 | } 12 | FaustDspVoice::~FaustDspVoice() { 13 | for (int channel = 0; channel < 2; ++channel) { 14 | delete outputs[channel]; 15 | } 16 | } 17 | 18 | void FaustDspVoice::prepareToPlay(int sampleRate, int samplesPerBlock) 19 | { 20 | for (int channel = 0; channel < 2; ++channel) { 21 | outputs[channel] = new float[samplesPerBlock]; 22 | } 23 | } 24 | 25 | void FaustDspVoice::releaseResources() 26 | { 27 | } 28 | 29 | bool FaustDspVoice::canPlaySound(juce::SynthesiserSound* sound) 30 | { 31 | return dynamic_cast (sound) != nullptr; 32 | } 33 | 34 | void FaustDspVoice::startNote(int midiNoteNumber, float velocity, 35 | juce::SynthesiserSound*, int /*currentPitchWheelPosition*/) 36 | { 37 | currentFreq = juce::MidiMessage::getMidiNoteInHertz(midiNoteNumber); 38 | keyOn(); 39 | } 40 | 41 | void FaustDspVoice::stopNote(float /*velocity*/, bool allowTailOff) 42 | { 43 | keyOff(); 44 | if (!allowTailOff) 45 | { 46 | clearCurrentNote(); 47 | } 48 | } 49 | 50 | void FaustDspVoice::renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int startSample, int numSamples) 51 | { 52 | mFaust.compute(outputBuffer.getNumSamples(), NULL, outputs); 53 | for (int i = 0; i < outputBuffer.getNumSamples(); i++) { 54 | *outputBuffer.getWritePointer(0, i) = outputs[0][i]; // left 55 | *outputBuffer.getWritePointer(1, i) = outputs[1][i]; // right 56 | } 57 | } 58 | 59 | void FaustDspVoice::pitchWheelMoved(int newPitchWheelValue){} 60 | void FaustDspVoice::controllerMoved(int controllerNumber, int newControllerValue){} 61 | 62 | void FaustDspVoice::keyOn() { 63 | setFreq(); 64 | setGate(true); 65 | } 66 | 67 | void FaustDspVoice::keyOff() { 68 | setGate(false); 69 | } 70 | 71 | void FaustDspVoice::setGate(bool on) 72 | { 73 | vts.getParameterAsValue(GATE).setValue(on); 74 | } 75 | 76 | void FaustDspVoice::setFreq() { 77 | vts.getParameterAsValue(FREQ).setValue(currentFreq); 78 | } -------------------------------------------------------------------------------- /Source/Synth/FaustDspVoice.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../Constants.h" 5 | 6 | class mydsp; 7 | 8 | class FaustDspVoice : public juce::SynthesiserVoice { 9 | public: 10 | FaustDspVoice(juce::AudioProcessorValueTreeState& valueTreeState, mydsp& faustDsp); 11 | virtual ~FaustDspVoice() override; 12 | 13 | void prepareToPlay(int sampleRate, int samplesPerBlock); 14 | void releaseResources(); 15 | 16 | bool canPlaySound(juce::SynthesiserSound* sound) override; 17 | 18 | void startNote(int midiNoteNumber, float velocity, 19 | juce::SynthesiserSound*, int /*currentPitchWheelPosition*/) override; 20 | void stopNote(float /*velocity*/, bool allowTailOff) override; 21 | void pitchWheelMoved(int newPitchWheelValue) override; 22 | void controllerMoved(int controllerNumber, int newControllerValue) override; 23 | void renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override; 24 | 25 | void keyOn(); 26 | void keyOff(); 27 | void setGate(bool on); 28 | void setFreq(); 29 | 30 | int currentFreq; 31 | 32 | private: 33 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FaustDspVoice) 34 | juce::AudioProcessorValueTreeState& vts; 35 | mydsp& mFaust; 36 | 37 | float** outputs; 38 | }; -------------------------------------------------------------------------------- /Source/UI/FaustUIBridge.cpp: -------------------------------------------------------------------------------- 1 | #include "FaustUIBridge.h" 2 | #include "../Constants.h" 3 | 4 | FaustUIBridge::FaustUIBridge(juce::AudioProcessorValueTreeState& valueTreeState) : vts(valueTreeState), 5 | listenerAssignments(), 6 | listeners(), 7 | labels(), 8 | currentFreqZone(new float(0)) 9 | { 10 | } 11 | 12 | FaustUIBridge::~FaustUIBridge() 13 | { 14 | for (int i = 0; i < listenerAssignments.size(); ++i) 15 | { 16 | ParameterListenerPair p = listenerAssignments.getUnchecked(i); 17 | juce::String paramId = p.paramId; 18 | FaustUIBridgeListener* listener = p.listener; 19 | vts.removeParameterListener(paramId, listener); 20 | } 21 | labels.clear(); 22 | listenerAssignments.clear(); 23 | } 24 | 25 | // freq and prevFreq 26 | void FaustUIBridge::addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) 27 | { 28 | addNormalComponent(label, zone, init, juce::NormalisableRange(min,max), false, false); 29 | } 30 | 31 | void FaustUIBridge::addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) 32 | { 33 | juce::NormalisableRange range; 34 | float skew; 35 | //time labels are skewed so low values are a larger percent of knob range 36 | auto skewPair = skewMap.find(label); 37 | range = juce::NormalisableRange(min, max, abs(step), (skewPair == skewMap.end() ? 1.0 : skewPair->second)); 38 | addNormalComponent(label, zone, init, range, (step==1.0), false); 39 | } 40 | 41 | void FaustUIBridge::addButton(const char* label, FAUSTFLOAT* zone) 42 | { 43 | addNormalComponent(label, zone, 0.0, juce::NormalisableRange(0.0, 1.0, 1.0), true, true); 44 | } 45 | 46 | void FaustUIBridge::addCheckButton(const char* label, FAUSTFLOAT* zone) 47 | { 48 | addNormalComponent(label, zone, 0.0, juce::NormalisableRange(0.0, 1.0, 1.0), true, true); 49 | } 50 | 51 | 52 | // Exposing final frequency calculation in faust code for glide 53 | void FaustUIBridge::addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) 54 | { 55 | currentFreqZone = zone; 56 | } 57 | 58 | float FaustUIBridge::getCurrentFreq() 59 | { 60 | return *currentFreqZone; 61 | } 62 | 63 | void FaustUIBridge::addNormalComponent(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, juce::NormalisableRange range, bool isDiscrete, bool isBoolean) 64 | { 65 | if (!vts.getParameter(label)) 66 | { 67 | vts.createAndAddParameter(label, label, juce::String(), 68 | range, init, nullptr, nullptr, 69 | false, true, isDiscrete, juce::AudioProcessorParameter::Category::genericParameter, isBoolean); 70 | } 71 | 72 | juce::String stringLabel = juce::String(label); 73 | // Attach the listener to keep the internal dsp values up to date 74 | FaustUIBridgeListener* l = new FaustUIBridgeListener(zone); 75 | listeners.add(l); 76 | listenerAssignments.add(ParameterListenerPair(stringLabel, l)); 77 | labels.add(stringLabel); 78 | vts.addParameterListener(stringLabel, l); 79 | } -------------------------------------------------------------------------------- /Source/UI/FaustUIBridge.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../FaustInclude/UI.h" 5 | 6 | //============================================================================== 7 | /** 8 | This class interfaces with the Faust UI paradigm, mapping control parameters 9 | into AudioProcessorParameters on a provided AudioProcessorValueTreeState. 10 | */ 11 | class FaustUIBridge : public UI 12 | { 13 | 14 | public: 15 | FaustUIBridge(juce::AudioProcessorValueTreeState& valueTreeState); 16 | virtual ~FaustUIBridge() override; 17 | 18 | //============================================================================== 19 | /** Widget Layout 20 | The following methods implement the widget layout functions. 21 | Because we intend for an AudioProcessorEditor to manage the 22 | visual aspect of the plugin, these methods are no-ops. 23 | */ 24 | virtual void openTabBox(const char* label) override {}; 25 | virtual void openHorizontalBox(const char* label) override {}; 26 | virtual void openVerticalBox(const char* label) override {}; 27 | virtual void closeBox() override {}; 28 | 29 | //============================================================================== 30 | /** Active Widgets 31 | The following methods implement the active widget functions 32 | by constructing an AudioProcessorParameter and installing a 33 | FaustUIBridgeListener to propagate changes back to the corresponding 34 | zone. 35 | */ 36 | virtual void addButton(const char* label, FAUSTFLOAT* zone) override; 37 | virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) override; 38 | virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override {}; 39 | virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override; 40 | virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override; 41 | 42 | //============================================================================== 43 | /** Passive Widgets 44 | The following methods implement the passive widget functions. 45 | Again, because we intend for an AudioProcessorEditor to manage the 46 | visual aspect of the plugin, these methods are no-ops. 47 | */ 48 | virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override {}; 49 | virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override; 50 | 51 | // -- soundfiles 52 | 53 | virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) override {}; 54 | 55 | void addNormalComponent(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, juce::NormalisableRange range, bool isDiscrete, bool isBoolean); 56 | 57 | //============================================================================== 58 | /** Metadata declarations 59 | An additional no-op to implement the last piece of the Faust UI interface. 60 | */ 61 | virtual void declare(FAUSTFLOAT*, const char*, const char*) override {}; 62 | 63 | float getCurrentFreq(); 64 | 65 | class FaustUIBridgeListener : public juce::AudioProcessorValueTreeState::Listener 66 | { 67 | public: 68 | FaustUIBridgeListener(float* zone) : m_zone(zone) {}; 69 | virtual ~FaustUIBridgeListener() override {}; 70 | 71 | virtual void parameterChanged(const juce::String& parameterID, float newValue) override 72 | { 73 | *m_zone = newValue; 74 | }; 75 | 76 | private: 77 | float* m_zone; 78 | }; 79 | 80 | 81 | private: 82 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FaustUIBridge) 83 | juce::AudioProcessorValueTreeState& vts; 84 | juce::Array labels; 85 | float* currentFreqZone; 86 | 87 | struct ParameterListenerPair 88 | { 89 | //============================================================================== 90 | /** Constructor. 91 | @param paramId The String Id of the AudioProcessorParameter. 92 | @param listener The attached FaustUIBridgeListener. 93 | */ 94 | ParameterListenerPair(juce::String paramId, FaustUIBridgeListener * listener) 95 | : paramId(paramId), listener(listener) {}; 96 | 97 | juce::String paramId; 98 | FaustUIBridgeListener* listener; 99 | }; 100 | // Maintain an array associating AudioProcessorParameters to the Listeners that have been 101 | // installed on those parameters. 102 | juce::Array listenerAssignments; 103 | 104 | // And an array of listeners to ensure the mapping between internal value tree values 105 | // match the float* zone members of the faust implementation. 106 | juce::OwnedArray listeners; 107 | }; -------------------------------------------------------------------------------- /Source/UI/GuiElementsPosition.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | File generated with svg2gui 5 | Contains the name, x/y position and width/height for each gui element from a single layer in the source SVG 6 | All values are relative to total gui width/height(in range 0 <= ... <= 1) 7 | VAR_NAME should be defined and imported from somewhere as a string constant. eg. #include "../Constants.h" 8 | Declare guiPositions in the same file as the string constants 9 | 10 | ============================================================================== 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "../Constants.h" 18 | 19 | // VAR_NAME should be useable everywhere (keep your naming consistent) 20 | // {VAR_NAME: [x_position, y_position, width, height]} 21 | const std::map> guiPositions = { 22 | {ATTACK,{0.7135146588541667,0.5133405672222222,0.040087898437500005,0.07126338888888889}}, 23 | {DECAY,{0.7939367421875,0.5133406135185185,0.040087898437500005,0.07126338888888889}}, 24 | {DECAY_ON,{0.11747125520833332,0.8051734375925925,0.032820416666666664,0.028113022685185184}}, 25 | {FEEDBACK_GAIN,{0.5818597109375001,0.19520999777777778,0.040087898437500005,0.07126338888888889}}, 26 | {FEEDBACK_ON,{0.5128021328125,0.2132592662962963,0.042990479166666665,0.03516484722222222}}, 27 | {F_ATTACK,{0.7135145807291666,0.3184446412962963,0.040087898437500005,0.07126338888888889}}, 28 | {F_CUTOFF,{0.7135146588541667,0.1598839237037037,0.040087898437500005,0.07126338888888889}}, 29 | {F_DECAY,{0.7939367421875,0.3184446412962963,0.040087898437500005,0.07126338888888889}}, 30 | {F_ENV_AMOUNT,{0.8746219765625,0.1598839237037037,0.040087898437500005,0.07126338888888889}}, 31 | {F_MOD_ON,{0.6513340598958333,0.12026953481481481,0.0366477734375,0.030026591203703705}}, 32 | {F_Q,{0.7939367421875,0.1598839237037037,0.040087898437500005,0.07126338888888889}}, 33 | {F_SUSTAIN,{0.8746219765625,0.3184446412962963,0.040087898437500005,0.07126338888888889}}, 34 | {GLIDE_ON,{0.11747125520833332,0.7381990857407408,0.032820416666666664,0.028113022685185184}}, 35 | {GLIDE_RATE,{0.05453968489583334,0.2625420625925926,0.040087898437500005,0.07126338888888889}}, 36 | {GLOBAL_DETUNE,{0.05453968489583334,0.10592078018518519,0.040087898437500005,0.07126249537037037}}, 37 | {KEY_TRK1,{0.6513340598958333,0.18622792833333332,0.0366477734375,0.030026591203703705}}, 38 | {KEY_TRK2,{0.6513340598958333,0.25864691907407406,0.0366477734375,0.030026591203703705}}, 39 | {LFO_RATE,{0.02235023697916667,0.7624050579629629,0.030604442708333335,0.05440486111111111}}, 40 | {LOAD,{0.5918649192708333,0.06608752555555557,0.020077498697916665,0.035691335648148144}}, 41 | {MOD_AMOUNT,{0.10707194531249999,0.8304966320370369,0.007413736979166667,0.13216086574074074}}, 42 | {MOD_MIX,{0.05453968489583334,0.4364603264814815,0.040087898437500005,0.07126338888888889}}, 43 | {NOISE_GAIN,{0.5818597109375001,0.388284007037037,0.040087898437500005,0.07126338888888889}}, 44 | {NOISE_LFO_MOD,{0.08424964583333333,0.553177557962963,0.042990541666666666,0.036815237037037034}}, 45 | {NOISE_ON,{0.5128021328125,0.40633326166666667,0.042990479166666665,0.03516484722222222}}, 46 | {NOISE_TYPE,{0.5804095546875001,0.5676118172222222,0.042988078125,0.03516681481481481}}, 47 | {ON,{0.9403034869791667,0.5376390857407407,0.04298813802083334,0.0368172962962963}}, 48 | {OSC1_GAIN,{0.4427580442708334,0.0982219237037037,0.040087898437500005,0.07126338888888889}}, 49 | {OSC1_ON,{0.5128021328125,0.11627119685185186,0.042990479166666665,0.03516484722222222}}, 50 | {OSC1_RANGE,{0.19176904893753247,0.09760207090456717,0.047211130208333334,0.08392802314814814}}, 51 | {OSC1_WAVE,{0.35307302019554304,0.09760205628050658,0.047211130208333334,0.08392802314814814}}, 52 | {OSC2_DETUNE,{0.2731150755208333,0.2814116181481482,0.04532498958333333,0.08057325925925926}}, 53 | {OSC2_GAIN,{0.4427580442708334,0.29551339592592596,0.040087898437500005,0.07126338888888889}}, 54 | {OSC2_ON,{0.5128021328125,0.31356267833333334,0.042990479166666665,0.03516484722222222}}, 55 | {OSC2_RANGE,{0.1917688556928576,0.27973280931900296,0.047211294270833336,0.0839288425925926}}, 56 | {OSC2_WAVE,{0.35307279309824857,0.27973279741540963,0.047211294270833336,0.0839288425925926}}, 57 | {OSC3_CTRL,{0.12916789583333335,0.4770811227777777,0.046371125,0.03799746851851852}}, 58 | {OSC3_DETUNE,{0.2731150755208333,0.4449709144444444,0.04532498958333333,0.08057325925925926}}, 59 | {OSC3_FILTEG_MOD,{0.019888867187500003,0.553177557962963,0.042990541666666666,0.036815237037037034}}, 60 | {OSC3_GAIN,{0.4427580442708334,0.48584112277777775,0.040087898437500005,0.07126338888888889}}, 61 | {OSC3_ON,{0.5128021328125,0.5038903357407407,0.042990479166666665,0.03516484722222222}}, 62 | {OSC3_RANGE,{0.1917688715549476,0.4432921379276213,0.047211294270833336,0.0839288425925926}}, 63 | {OSC3_WAVE,{0.3542915078125001,0.4463541551851852,0.045325,0.08057777777777778}}, 64 | {OSC_MOD_ON,{0.10875486197916666,0.21968109962962962,0.046373716145833334,0.03799534537037037}}, 65 | {PITCH_BEND,{0.06320279947916667,0.8304966320370369,0.007413736979166667,0.13216086574074074}}, 66 | {SUSTAIN,{0.8746219765625,0.5133406135185185,0.040087898437500005,0.07126338888888889}}, 67 | {VOLUME,{0.9464353098958334,0.2546287107407408,0.030604442708333335,0.05440486111111111}}, 68 | }; -------------------------------------------------------------------------------- /Source/UI/Knobs/Knob.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | Knob.cpp 5 | Created: 3 Aug 2022 8:15:21am 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "Knob.h" 12 | 13 | Knob::Knob(juce::AudioProcessorValueTreeState& vts, juce::String paramId, KnobLookAndFeel& laf) 14 | { 15 | slider = std::make_unique(); 16 | addAndMakeVisible(slider.get()); 17 | slider->setBounds(0, 0, laf.knobSize, laf.knobSize); 18 | slider->setSliderStyle(juce::Slider::RotaryHorizontalVerticalDrag); 19 | slider->setTextBoxStyle(juce::Slider::NoTextBox, true, 80, 20); 20 | slider->setLookAndFeel(&laf); 21 | attach.reset(new SliderAttachment(vts, juce::String(paramId), *slider.get())); 22 | } 23 | 24 | Knob::~Knob() 25 | { 26 | } -------------------------------------------------------------------------------- /Source/UI/Knobs/Knob.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | Knob.h 5 | Created: 3 Aug 2022 8:15:21am 6 | Author: tytu1 7 | 8 | Holds a juce::Slider, juce::SliderAttachment, and (optional) juce::Label 9 | ============================================================================== 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include "KnobLookAndFeel.h" 16 | #include "KnobImage.h" 17 | 18 | typedef juce::AudioProcessorValueTreeState::SliderAttachment SliderAttachment; 19 | 20 | class Knob : public juce::Component 21 | { 22 | public: 23 | Knob(juce::AudioProcessorValueTreeState& vts, juce::String paramId, KnobLookAndFeel& laf); 24 | virtual ~Knob() override; 25 | 26 | private: 27 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Knob) 28 | std::unique_ptr slider; 29 | std::unique_ptr attach; 30 | }; 31 | 32 | class FloatKnob : public Knob { 33 | public: 34 | FloatKnob(juce::AudioProcessorValueTreeState& vts, juce::String paramId, KnobLookAndFeel& laf) : Knob(vts, paramId, laf) {} 35 | virtual ~FloatKnob() override {} 36 | }; 37 | 38 | class BigFloatKnob : public Knob { 39 | public: 40 | BigFloatKnob(juce::AudioProcessorValueTreeState& vts, juce::String paramId, KnobLookAndFeel& laf) : Knob(vts, paramId, laf) {} 41 | virtual ~BigFloatKnob() override {} 42 | }; 43 | 44 | class TinyFloatKnob : public Knob { 45 | public: 46 | TinyFloatKnob(juce::AudioProcessorValueTreeState& vts, juce::String paramId, KnobLookAndFeel& laf) : Knob(vts, paramId, laf) {} 47 | virtual ~TinyFloatKnob() override {} 48 | }; 49 | 50 | class IntKnob : public Knob { 51 | public: 52 | IntKnob(juce::AudioProcessorValueTreeState& vts, juce::String paramId, KnobLookAndFeel& laf) : Knob(vts, paramId, laf) {} 53 | virtual ~IntKnob() override {} 54 | }; 55 | 56 | class ScrewFloatKnob : public Knob { 57 | public: 58 | ScrewFloatKnob(juce::AudioProcessorValueTreeState& vts, juce::String paramId, KnobLookAndFeel& laf) : Knob(vts, paramId, laf) {} 59 | virtual ~ScrewFloatKnob() override {} 60 | }; -------------------------------------------------------------------------------- /Source/UI/Knobs/KnobImage.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | KnobImage.cpp 5 | Created: 8 Aug 2022 3:22:40pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | class KnobImage : public juce::DrawableImage 16 | { 17 | public: 18 | KnobImage(juce::Image& knobImageIn, const int knobSize) 19 | { 20 | setSize(knobSize, knobSize); 21 | setImage(knobImageIn.rescaled(knobSize, knobSize)); 22 | pivotPoint = std::make_unique(knobSize / 2.0); 23 | } 24 | 25 | virtual ~KnobImage() override 26 | { 27 | } 28 | 29 | std::unique_ptr pivotPoint; 30 | 31 | private: 32 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(KnobImage) 33 | }; 34 | -------------------------------------------------------------------------------- /Source/UI/Knobs/KnobImage.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | KnobImage.h 5 | Created: 8 Aug 2022 3:22:40pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | class KnobImage : public juce::DrawableImage 16 | { 17 | public: 18 | KnobImage(juce::Image& knobImageIn, const int knobSize) 19 | { 20 | setSize(knobSize, knobSize); 21 | setImage(knobImageIn.rescaled(knobSize, knobSize)); 22 | pivotPoint = std::make_unique(knobSize / 2.0); 23 | } 24 | 25 | virtual ~KnobImage() override 26 | { 27 | } 28 | 29 | std::unique_ptr pivotPoint; 30 | 31 | private: 32 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(KnobImage) 33 | }; 34 | -------------------------------------------------------------------------------- /Source/UI/Knobs/KnobLookAndFeel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | FaugLookAndFeel.cpp 5 | Created: 8 Aug 2022 11:55:23am 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include 12 | #include "KnobLookAndFeel.h" 13 | #include "KnobImage.h" 14 | //============================================================================== 15 | KnobLookAndFeel::KnobLookAndFeel() 16 | { 17 | } 18 | 19 | KnobLookAndFeel::KnobLookAndFeel(juce::Image& knobImageIn, const int knobSizeIn) : 20 | knobImage(std::make_unique(knobImageIn, knobSizeIn)), knobSize(knobSizeIn) 21 | { 22 | } 23 | 24 | KnobLookAndFeel::~KnobLookAndFeel() 25 | {} 26 | 27 | juce::Font KnobLookAndFeel::getBaseFont() 28 | { 29 | return juce::Font::bold; 30 | } 31 | 32 | juce::Font KnobLookAndFeel::getLabelFont(juce::Label&) 33 | { 34 | return getBaseFont().withPointHeight(10); 35 | } 36 | 37 | juce::Font KnobLookAndFeel::getSliderReadoutFont() 38 | { 39 | return getBaseFont().withPointHeight(14); 40 | } 41 | 42 | void KnobLookAndFeel::drawLabel(juce::Graphics& g, juce::Label& l) 43 | { 44 | juce::Colour labelColour = juce::Colour::fromRGB(149, 89, 17); 45 | juce::Font labelFont = getLabelFont(l); 46 | 47 | g.setColour(labelColour); 48 | g.setFont(labelFont); 49 | 50 | juce::Rectangle textArea(l.getBorderSize().subtractedFrom(l.getLocalBounds())); 51 | 52 | g.drawFittedText(l.getText(), textArea, l.getJustificationType(), 53 | juce::jmax(1, (int)(textArea.getHeight() / labelFont.getHeight())), 54 | l.getMinimumHorizontalScale()); 55 | } 56 | 57 | void KnobLookAndFeel::drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, float sliderPos, 58 | const float rotaryStartAngle, const float rotaryEndAngle, juce::Slider& slider) 59 | { 60 | const float radius = juce::jmin(width / 2, height / 2) - 2.0f; 61 | const float centreX = x + width * 0.5f; 62 | const float centreY = y + height * 0.5f; 63 | const float rx = centreX - radius; 64 | const float ry = centreY - radius; 65 | const float rw = radius * 2.0f; 66 | const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle); 67 | const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled(); 68 | 69 | // Draw the readout 70 | // Change this to hint box? 71 | g.drawImageTransformed(knobImage->getImage(), transform.rotation(angle, *knobImage->pivotPoint.get(), *knobImage->pivotPoint.get())); 72 | 73 | 74 | /* 75 | juce::Colour readoutColour = juce::Colour::fromRGB(254, 173, 29).withAlpha(isMouseOver ? 1.0f : 0.9f); 76 | const double value = slider.getValue(); 77 | juce::String readoutValue = (value >= 1000.0 ? juce::String(value / 1000.0, 1) + "k" : juce::String(value, 2)); 78 | juce::String readout = readoutValue; 79 | 80 | g.setColour(readoutColour); 81 | g.setFont(getSliderReadoutFont()); 82 | g.drawText(readout, centreX - radius, centreY - 10.0f, rw, 24.0f, juce::Justification::centred); 83 | */ 84 | 85 | // Draw the track 86 | g.setColour(slider.findColour(juce::Slider::rotarySliderOutlineColourId)); 87 | } 88 | -------------------------------------------------------------------------------- /Source/UI/Knobs/KnobLookAndFeel.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | FaugLookAndFeel.h 5 | Created: 8 Aug 2022 11:55:23am 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | class KnobImage; 16 | 17 | //============================================================================== 18 | /* 19 | */ 20 | class KnobLookAndFeel : public juce::LookAndFeel_V4 21 | { 22 | public: 23 | KnobLookAndFeel(); 24 | KnobLookAndFeel(juce::Image& knobImageIn, const int knobSize); 25 | virtual ~KnobLookAndFeel() override; 26 | 27 | juce::Font getBaseFont(); 28 | juce::Font getLabelFont(juce::Label&) override; 29 | juce::Font getSliderReadoutFont(); 30 | 31 | void drawLabel(juce::Graphics&,juce::Label&) override; 32 | void drawRotarySlider(juce::Graphics&, int x, int y, int width, int height, 33 | float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, 34 | juce::Slider&) override; 35 | 36 | int knobSize; 37 | 38 | private: 39 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(KnobLookAndFeel) 40 | std::unique_ptr knobImage; 41 | juce::AffineTransform transform = juce::AffineTransform(); 42 | }; 43 | -------------------------------------------------------------------------------- /Source/UI/MainComponent.cpp: -------------------------------------------------------------------------------- 1 | // Main Component, holds the primary synth UI 2 | // Todo: Move MidiKeyboardComponent to it's own class to be added/removed 3 | 4 | #include "MainComponent.h" 5 | #include "../Constants.h" 6 | 7 | MainComponent::MainComponent(juce::MidiKeyboardState& keyboardState, juce::AudioProcessorValueTreeState& vts, int window_width, int window_height) 8 | : m_vts(vts), keyboardComponent(keyboardState, juce::MidiKeyboardComponent::horizontalKeyboard), window_width(window_width), window_height(window_height) 9 | 10 | { 11 | initializeAssets(); 12 | 13 | setOpaque(true); 14 | 15 | createKeyboard(); 16 | 17 | createController(); 18 | 19 | createOscBank(); 20 | 21 | createMixer(); 22 | 23 | createFilterBank(); 24 | 25 | createEnvelope(); 26 | 27 | master_volume = std::make_unique(m_vts, juce::String(VOLUME), *tinyFloatKnobLaf.get()); 28 | placeElement(*master_volume, VOLUME); 29 | 30 | master_on = std::make_unique(m_vts, juce::String(ON), *brownToggleLaf.get()); 31 | placeVerticalButton(*master_on.get(), ON); 32 | 33 | setSize(window_width, window_height); 34 | 35 | // wait .4 seconds, then grab keyboard focus to use as potential midi-input 36 | startTimer(400); 37 | } 38 | 39 | MainComponent::~MainComponent() 40 | { 41 | 42 | } 43 | 44 | void MainComponent::initializeAssets() 45 | { 46 | //knobs 47 | background = juce::Drawable::createFromImageData(BinaryData::background_png, BinaryData::background_pngSize); 48 | floatKnobImage = std::make_unique(juce::ImageFileFormat::loadFrom(BinaryData::knobOne_png, BinaryData::knobOne_pngSize)); 49 | intKnobImage = std::make_unique(juce::ImageFileFormat::loadFrom(BinaryData::knobTwo_png, BinaryData::knobTwo_pngSize)); 50 | screwKnobImage = std::make_unique(juce::ImageFileFormat::loadFrom(BinaryData::screwKnob_png, BinaryData::screwKnob_pngSize)); 51 | 52 | floatKnobLaf = std::make_unique(*floatKnobImage.get(), getElWidth(FLOAT_KNOB_EX)); 53 | tinyFloatKnobLaf = std::make_unique(*floatKnobImage.get(), getElWidth(TINY_FLOAT_KNOB_EX)); 54 | bigFloatKnobLaf = std::make_unique(*floatKnobImage.get(), getElWidth(BIG_FLOAT_KNOB_EX)); 55 | intKnobLaf = std::make_unique(*intKnobImage.get(), getElWidth(INT_KNOB_EX)); 56 | screwKnobLaf = std::make_unique(*screwKnobImage.get(), getElWidth(SCREW_FLOAT_EX)); 57 | 58 | //toggles 59 | orangeToggle = std::make_unique(juce::ImageFileFormat::loadFrom(BinaryData::orangeToggle_png, BinaryData::orangeToggle_pngSize)); 60 | blueToggle = std::make_unique(juce::ImageFileFormat::loadFrom(BinaryData::blueToggle_png, BinaryData::blueToggle_pngSize)); 61 | brownToggle = std::make_unique(juce::ImageFileFormat::loadFrom(BinaryData::brownToggle_png, BinaryData::brownToggle_pngSize)); 62 | whiteToggle = std::make_unique(juce::ImageFileFormat::loadFrom(BinaryData::whiteToggle_png, BinaryData::whiteToggle_pngSize)); 63 | 64 | orangeToggleLaf = std::make_unique(*orangeToggle.get(), getElWidth(ORANGE_TOG_EX), getElHeight(ORANGE_TOG_EX)); 65 | bigOrangeToggleLaf = std::make_unique(*orangeToggle.get(), getElWidth(BIG_ORANGE_TOG_EX), getElHeight(BIG_ORANGE_TOG_EX)); 66 | blueToggleLaf = std::make_unique(*blueToggle.get(), getElWidth(BLUE_TOG_EX), getElHeight(BLUE_TOG_EX)); 67 | brownToggleLaf = std::make_unique(*brownToggle.get(), getElWidth(BROWN_TOG_EX), getElHeight(BROWN_TOG_EX)); 68 | whiteToggleLaf = std::make_unique(*whiteToggle.get(), getElWidth(WHITE_TOG_EX), getElHeight(WHITE_TOG_EX)); 69 | 70 | //modWheel 71 | modWheelImage = std::make_unique(juce::ImageFileFormat::loadFrom(BinaryData::wheel_png, BinaryData::wheel_pngSize)); 72 | modWheelShading = std::make_unique(juce::ImageFileFormat::loadFrom(BinaryData::wheelShading_png, BinaryData::wheelShading_pngSize)); 73 | modWheelLaf = std::make_unique(*modWheelImage.get(), *modWheelShading.get(), getElWidth(MOD_WHEEL_EX), getElHeight(MOD_WHEEL_EX)); 74 | } 75 | 76 | void MainComponent::createKeyboard() 77 | { 78 | addAndMakeVisible(keyboardComponent); 79 | keyboardComponent.setKeyWidth(40); 80 | 81 | m_glideOn = std::make_unique(m_vts, juce::String(GLIDE_ON), *whiteToggleLaf.get()); 82 | placeElement(*m_glideOn.get(), GLIDE_ON); 83 | 84 | m_decayOn = std::make_unique(m_vts, juce::String(DECAY_ON), *whiteToggleLaf.get()); 85 | placeElement(*m_decayOn.get(), DECAY_ON); 86 | 87 | m_modAmount = std::make_unique(m_vts, juce::String(MOD_AMOUNT), *modWheelLaf.get()); 88 | placeElement(*m_modAmount.get(), MOD_AMOUNT); 89 | 90 | m_pitchBend = std::make_unique(m_vts, juce::String(PITCH_BEND), *modWheelLaf.get()); 91 | placeElement(*m_pitchBend.get(), PITCH_BEND); 92 | 93 | m_lfoRate = std::make_unique(m_vts, juce::String(LFO_RATE), *tinyFloatKnobLaf.get()); 94 | placeElement(*m_lfoRate.get(), LFO_RATE); 95 | } 96 | 97 | void MainComponent::createController() 98 | { 99 | m_globalDetune = std::make_unique(m_vts, juce::String(GLOBAL_DETUNE), *floatKnobLaf.get()); 100 | placeElement(*m_globalDetune, GLOBAL_DETUNE); 101 | 102 | m_glide = std::make_unique(m_vts, juce::String(GLIDE_RATE), *floatKnobLaf.get()); 103 | placeElement(*m_glide.get(), GLIDE_RATE); 104 | 105 | m_oscModOn = std::make_unique(m_vts, juce::String(OSC_MOD_ON), *bigOrangeToggleLaf.get()); 106 | placeElement(*m_oscModOn.get(), OSC_MOD_ON); 107 | 108 | m_modMix = std::make_unique(m_vts, juce::String(MOD_MIX), *floatKnobLaf.get()); 109 | placeElement(*m_modMix.get(), MOD_MIX); 110 | 111 | m_osc3_filterEG_modSrc = std::make_unique(m_vts, juce::String(OSC3_FILTEG_MOD), *brownToggleLaf.get()); 112 | placeElement(*m_osc3_filterEG_modSrc.get(), OSC3_FILTEG_MOD); 113 | 114 | m_noise_lfo_modSrc = std::make_unique(m_vts, juce::String(NOISE_LFO_MOD), *brownToggleLaf.get()); 115 | placeElement(*m_noise_lfo_modSrc.get(), NOISE_LFO_MOD); 116 | } 117 | 118 | void MainComponent::createOscBank() 119 | { 120 | // OSC ONE 121 | m_oscOneRange = std::make_unique(m_vts, juce::String(OSC1_RANGE), *intKnobLaf.get()); 122 | placeElement(*m_oscOneRange.get(), OSC1_RANGE); 123 | 124 | m_oscOneWaveForm = std::make_unique(m_vts, juce::String(OSC1_WAVE), *intKnobLaf.get()); 125 | placeElement(*m_oscOneWaveForm.get(), OSC1_WAVE); 126 | 127 | // OSC Two 128 | m_oscTwoRange = std::make_unique(m_vts, juce::String(OSC2_RANGE), *intKnobLaf.get()); 129 | placeElement(*m_oscTwoRange.get(), OSC2_RANGE); 130 | 131 | m_oscTwoDetune = std::make_unique(m_vts, juce::String(OSC2_DETUNE), *bigFloatKnobLaf.get()); 132 | placeElement(*m_oscTwoDetune.get(), OSC2_DETUNE); 133 | 134 | m_oscTwoWaveForm = std::make_unique(m_vts, juce::String(OSC2_WAVE), *intKnobLaf.get()); 135 | placeElement(*m_oscTwoWaveForm.get(), OSC2_WAVE); 136 | 137 | // OSC Three 138 | m_oscThreeRange = std::make_unique(m_vts, juce::String(OSC3_RANGE), *intKnobLaf.get()); 139 | placeElement(*m_oscThreeRange.get(), OSC3_RANGE); 140 | 141 | m_oscThreeDetune = std::make_unique(m_vts, juce::String(OSC3_DETUNE), *bigFloatKnobLaf.get()); 142 | placeElement(*m_oscThreeDetune.get(), OSC3_DETUNE); 143 | 144 | m_oscThreeWaveForm = std::make_unique(m_vts, juce::String(OSC3_WAVE), *intKnobLaf.get()); 145 | placeElement(*m_oscThreeWaveForm.get(), OSC3_WAVE); 146 | 147 | m_osc3Ctrl = std::make_unique(m_vts, juce::String(OSC3_CTRL), *bigOrangeToggleLaf.get()); 148 | placeVerticalButton(*m_osc3Ctrl.get(), OSC3_CTRL); 149 | } 150 | 151 | void MainComponent::createMixer() 152 | { 153 | m_load = std::make_unique(m_vts, juce::String(LOAD), *screwKnobLaf.get()); 154 | placeElement(*m_load.get(), LOAD); 155 | 156 | // Oscillators 157 | m_oscOneGain = std::make_unique(m_vts, juce::String(OSC1_GAIN), *floatKnobLaf.get()); 158 | placeElement(*m_oscOneGain.get(), OSC1_GAIN); 159 | 160 | m_oscOnePowerButton = std::make_unique(m_vts, juce::String(OSC1_ON), *blueToggleLaf.get()); 161 | placeElement(*m_oscOnePowerButton.get(), OSC1_ON); 162 | 163 | m_oscTwoGain = std::make_unique(m_vts, juce::String(OSC2_GAIN), *floatKnobLaf.get()); 164 | placeElement(*m_oscTwoGain.get(), OSC2_GAIN); 165 | 166 | m_oscTwoPowerButton = std::make_unique(m_vts, juce::String(OSC2_ON), *blueToggleLaf.get()); 167 | placeElement(*m_oscTwoPowerButton.get(), OSC2_ON); 168 | 169 | m_oscThreeGain = std::make_unique(m_vts, juce::String(OSC3_GAIN), *floatKnobLaf.get()); 170 | placeElement(*m_oscThreeGain.get(), OSC3_GAIN); 171 | 172 | m_oscThreePowerButton = std::make_unique(m_vts, juce::String(OSC3_ON), *blueToggleLaf.get()); 173 | placeElement(*m_oscThreePowerButton.get(), OSC3_ON); 174 | 175 | m_feedbackGain = std::make_unique(m_vts, juce::String(FEEDBACK_GAIN), *floatKnobLaf.get()); 176 | placeElement(*m_feedbackGain.get(), FEEDBACK_GAIN); 177 | 178 | m_feedbackOn = std::make_unique(m_vts, juce::String(FEEDBACK_ON), *blueToggleLaf.get()); 179 | placeElement(*m_feedbackOn.get(), FEEDBACK_ON); 180 | 181 | // Noise 182 | m_noiseGain = std::make_unique(m_vts, juce::String(NOISE_GAIN), *floatKnobLaf.get()); 183 | placeElement(*m_noiseGain.get(), NOISE_GAIN); 184 | 185 | m_noiseOn = std::make_unique(m_vts, juce::String(NOISE_ON), *blueToggleLaf.get()); 186 | placeElement(*m_noiseOn.get(), NOISE_ON); 187 | 188 | m_noiseType = std::make_unique(m_vts, juce::String(NOISE_TYPE), *blueToggleLaf.get()); 189 | placeVerticalButton(*m_noiseType.get(), NOISE_TYPE); 190 | } 191 | 192 | void MainComponent::createFilterBank() 193 | { 194 | // filter/controller border 195 | m_filterModOn = std::make_unique(m_vts, juce::String(F_MOD_ON), *orangeToggleLaf.get()); 196 | placeElement(*m_filterModOn.get(), F_MOD_ON); 197 | 198 | m_keyTrackOne = std::make_unique(m_vts, juce::String(KEY_TRK1), *orangeToggleLaf.get()); 199 | placeElement(*m_keyTrackOne.get(), KEY_TRK1); 200 | 201 | m_keyTrackTwo = std::make_unique(m_vts, juce::String(KEY_TRK2), *orangeToggleLaf.get()); 202 | placeElement(*m_keyTrackTwo.get(), KEY_TRK2); 203 | 204 | // Row One - Filter Frequency 205 | m_filterCutoff = std::make_unique(m_vts, juce::String(F_CUTOFF), *floatKnobLaf.get()); 206 | placeElement(*m_filterCutoff.get(), F_CUTOFF); 207 | 208 | m_filterQ = std::make_unique(m_vts, juce::String(F_Q), *floatKnobLaf.get()); 209 | placeElement(*m_filterQ.get(), F_Q); 210 | 211 | m_filterContourAmount = std::make_unique(m_vts, juce::String(F_ENV_AMOUNT), *floatKnobLaf.get()); 212 | placeElement(*m_filterContourAmount.get(), F_ENV_AMOUNT); 213 | 214 | // Row Two - Filter Envelope 215 | m_filterAttack = std::make_unique(m_vts, juce::String(F_ATTACK), *floatKnobLaf.get()); 216 | placeElement(*m_filterAttack.get(), F_ATTACK); 217 | 218 | m_filterDecay = std::make_unique(m_vts, juce::String(F_DECAY), *floatKnobLaf.get()); 219 | placeElement(*m_filterDecay.get(), F_DECAY); 220 | 221 | m_filterSustain = std::make_unique(m_vts, juce::String(F_SUSTAIN), *floatKnobLaf.get()); 222 | placeElement(*m_filterSustain.get(), F_SUSTAIN); 223 | } 224 | 225 | void MainComponent::createEnvelope() 226 | { 227 | m_attack = std::make_unique(m_vts, juce::String(ATTACK), *floatKnobLaf.get()); 228 | placeElement(*m_attack.get(), ATTACK); 229 | 230 | m_decay = std::make_unique(m_vts, juce::String(DECAY), *floatKnobLaf.get()); 231 | placeElement(*m_decay.get(), DECAY); 232 | 233 | m_sustain = std::make_unique(m_vts, juce::String(SUSTAIN), *floatKnobLaf.get()); 234 | placeElement(*m_sustain.get(), SUSTAIN); 235 | } 236 | 237 | void MainComponent::placeElement(juce::Component& comp, std::string label) 238 | { 239 | comp.setBounds(guiPositions.at(label)[0] * window_width, 240 | guiPositions.at(label)[1] * window_height, 241 | getElWidth(label), getElHeight(label)); 242 | addAndMakeVisible(comp); 243 | } 244 | 245 | void MainComponent::placeVerticalButton(juce::Component& comp, std::string label) 246 | { 247 | float x = guiPositions.at((label))[0] * float(window_width); 248 | float y = guiPositions.at((label))[1] * float(window_height); 249 | float width = guiPositions.at((label))[2] * float(window_width); 250 | float height = guiPositions.at((label))[3] * float(window_height); 251 | comp.setTransform(juce::AffineTransform().rotated(juce::MathConstants::halfPi, 252 | x + (width / 2), y + (height / 2))); 253 | comp.setBounds(x, y, width, height); 254 | addAndMakeVisible(comp); 255 | } 256 | 257 | int MainComponent::getElWidth(std::string label) 258 | { 259 | return guiPositions.at(label)[2] * window_width; 260 | } 261 | 262 | int MainComponent::getElHeight(std::string label) 263 | { 264 | return guiPositions.at(label)[3] * window_height; 265 | } 266 | 267 | void MainComponent::paint(juce::Graphics& g) 268 | { 269 | g.fillAll(juce::Colours::black); 270 | jassert(background != 0); 271 | if (background != 0) 272 | { 273 | background->drawWithin(g, juce::Rectangle(0, 0, window_width, window_height), 274 | juce::RectanglePlacement::stretchToFit, 1.000f); 275 | } 276 | 277 | } 278 | 279 | void MainComponent::resized() 280 | { 281 | auto area = getLocalBounds().removeFromBottom(window_height * .2925).removeFromRight(window_width * 0.825).removeFromLeft(window_width * 0.8); 282 | keyboardComponent.setBounds(area); 283 | } 284 | 285 | void MainComponent::timerCallback() 286 | { 287 | if (this->isShowing()) { 288 | keyboardComponent.grabKeyboardFocus(); 289 | } 290 | stopTimer(); 291 | } -------------------------------------------------------------------------------- /Source/UI/MainComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Main Component, holds the primary synth UI 4 | 5 | #include 6 | #include "./Knobs/Knob.h" 7 | #include "./Toggles/Toggle.h" 8 | #include "./Wheel/ModWheel.h" 9 | 10 | class MainComponent : public juce::Component, 11 | juce::Timer 12 | { 13 | public: 14 | MainComponent(juce::MidiKeyboardState& keyboardState, juce::AudioProcessorValueTreeState& vts, int windowWidth, int windowHeight); 15 | virtual ~MainComponent() override; 16 | 17 | void timerCallback() override; // timer 18 | 19 | void paint(juce::Graphics& g) override; //Component 20 | void resized() override; //Component 21 | 22 | // Load Images 23 | void initializeAssets(); 24 | 25 | // Create UI Sections 26 | void createKeyboard(); 27 | 28 | void createController(); 29 | 30 | void createOscBank(); 31 | 32 | void createMixer(); 33 | 34 | void createFilterBank(); 35 | 36 | void createEnvelope(); 37 | 38 | void placeElement(juce::Component& comp, std::string label); 39 | 40 | void placeVerticalButton(juce::Component& comp, std::string label); 41 | 42 | // getElementDimensions. single line call, but it would be annoyingly verbose to include it everywhere 43 | int getElWidth(std::string label); 44 | int getElHeight(std::string label); 45 | 46 | private: 47 | int window_width; 48 | int window_height; 49 | int toggle_width; 50 | int toggle_height; 51 | int knob_size; 52 | int small_knob_size; 53 | 54 | juce::MidiKeyboardComponent keyboardComponent; 55 | juce::AudioProcessorValueTreeState& m_vts; 56 | std::unique_ptr background; 57 | 58 | // 3 knob Image assets 59 | std::unique_ptr floatKnobImage; 60 | std::unique_ptr intKnobImage; 61 | std::unique_ptr screwKnobImage; 62 | 63 | // referenced among 5 LookAndFeels of varying size 64 | std::unique_ptr bigFloatKnobLaf; 65 | std::unique_ptr tinyFloatKnobLaf; 66 | std::unique_ptr floatKnobLaf; 67 | std::unique_ptr intKnobLaf; 68 | std::unique_ptr screwKnobLaf; 69 | 70 | // toggles 71 | std::unique_ptr orangeToggle; 72 | std::unique_ptr blueToggle; 73 | std::unique_ptr brownToggle; 74 | std::unique_ptr whiteToggle; 75 | 76 | std::unique_ptr orangeToggleLaf; 77 | std::unique_ptr bigOrangeToggleLaf; 78 | std::unique_ptr blueToggleLaf; 79 | std::unique_ptr brownToggleLaf; 80 | std::unique_ptr whiteToggleLaf; 81 | 82 | //modWheel 83 | std::unique_ptr modWheelImage; 84 | std::unique_ptr modWheelShading; 85 | std::unique_ptr modWheelLaf; 86 | 87 | 88 | // CONTROLLERS 89 | // 90 | std::unique_ptr m_globalDetune; 91 | std::unique_ptr m_glide; 92 | std::unique_ptr m_modMix; 93 | 94 | // Brown Toggles 95 | std::unique_ptr m_osc3_filterEG_modSrc; 96 | std::unique_ptr m_noise_lfo_modSrc; 97 | 98 | 99 | // KEYBOARD 100 | // 101 | std::unique_ptr m_lfoRate; 102 | 103 | // White Toggle 104 | std::unique_ptr m_glideOn; 105 | std::unique_ptr m_decayOn; 106 | 107 | // Mod Wheels 108 | std::unique_ptr m_pitchBend; 109 | std::unique_ptr m_modAmount; 110 | 111 | 112 | // In-Between CONTROLLER/OSCILLATOR 113 | std::unique_ptr m_oscModOn; 114 | 115 | 116 | // OSCILLATOR BANK 117 | std::unique_ptr m_oscOneRange; 118 | std::unique_ptr m_oscOneWaveForm; 119 | 120 | std::unique_ptr m_oscTwoRange; 121 | std::unique_ptr m_oscTwoWaveForm; 122 | std::unique_ptr m_oscTwoDetune; 123 | 124 | std::unique_ptr m_oscThreeRange; 125 | std::unique_ptr m_oscThreeWaveForm; 126 | std::unique_ptr m_oscThreeDetune; 127 | std::unique_ptrm_osc3Ctrl; 128 | 129 | // MIXER 130 | std::unique_ptr m_load; 131 | 132 | std::unique_ptr m_oscOnePowerButton; 133 | std::unique_ptr m_oscOneGain; 134 | 135 | std::unique_ptr m_oscTwoPowerButton; 136 | std::unique_ptr m_oscTwoGain; 137 | 138 | std::unique_ptr m_oscThreePowerButton; 139 | std::unique_ptr m_oscThreeGain; 140 | 141 | std::unique_ptr m_feedbackOn; 142 | std::unique_ptr m_feedbackGain; 143 | 144 | std::unique_ptr m_noiseOn; 145 | std::unique_ptr m_noiseGain; 146 | std::unique_ptr m_noiseType; 147 | 148 | // In-Between 149 | std::unique_ptr m_keyTrackOne; 150 | std::unique_ptr m_keyTrackTwo; 151 | std::unique_ptr m_filterModOn; 152 | 153 | 154 | // FILTER 155 | //knob_one 156 | std::unique_ptr m_filterCutoff; 157 | std::unique_ptr m_filterQ; 158 | std::unique_ptr m_filterContourAmount; 159 | //std::unique_ptr m_filterDirection; 160 | std::unique_ptr m_filterAttack; 161 | std::unique_ptr m_filterDecay; 162 | std::unique_ptr m_filterSustain; 163 | 164 | // ENVELOPE 165 | std::unique_ptr m_attack; 166 | std::unique_ptr m_decay; 167 | std::unique_ptr m_sustain; 168 | 169 | // POWER 170 | //tiny knob one 171 | std::unique_ptr master_volume; 172 | std::unique_ptr master_on; 173 | 174 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent) 175 | }; -------------------------------------------------------------------------------- /Source/UI/PluginEditor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file contains the basic framework code for a JUCE plugin editor. 5 | 6 | ============================================================================== 7 | */ 8 | 9 | #include "../Constants.h" 10 | #include "PluginEditor.h" 11 | #include "MainComponent.h" 12 | 13 | //============================================================================== 14 | FaugAudioProcessorEditor::FaugAudioProcessorEditor (FaugAudioProcessor& p, juce::MidiKeyboardState& state, juce::AudioProcessorValueTreeState& vts) 15 | : AudioProcessorEditor (&p), audioProcessor (p) 16 | { 17 | // Make sure that before the constructor has finished, you've set the 18 | // editor's size to whatever you need it to be. 19 | double ratio = 16.0 / 9.0; 20 | 21 | originalScreen = juce::Desktop::getInstance().getDisplays().getMainDisplay().userArea; 22 | int width = originalScreen.getWidth(); 23 | int height = originalScreen.getHeight(); 24 | 25 | m_main = std::make_unique(state, vts, height*ratio, height); 26 | addAndMakeVisible(*m_main.get()); 27 | 28 | setResizable(true, true); 29 | setResizeLimits(1280,1280/ratio, juce::jmin(3840.0, height * ratio), juce::jmin(2160,height)); 30 | getConstrainer()->setFixedAspectRatio(ratio); 31 | scaleConstant = (height * ratio) / width; 32 | currentWindow = juce::Rectangle(0, 0, height * ratio, height); 33 | setSize(height * ratio, height); 34 | 35 | } 36 | 37 | FaugAudioProcessorEditor::~FaugAudioProcessorEditor() 38 | { 39 | } 40 | 41 | //============================================================================== 42 | void FaugAudioProcessorEditor::paint (juce::Graphics& g) 43 | { 44 | // (Our component is opaque, so we must completely fill the background with a solid colour) 45 | g.fillAll (juce::Colours::black); 46 | 47 | g.setColour (juce::Colours::white); 48 | g.setFont (15.0f); 49 | } 50 | 51 | void FaugAudioProcessorEditor::resized() 52 | { 53 | juce::Rectangle resizedWindow = getBounds(); 54 | 55 | // keep my aspect ratio 56 | float scaleAmount = float(resizedWindow.getWidth()) / float(originalScreen.getWidth()); 57 | 58 | // Scale the scale amount by the original ratio of displayed screen to monitor size 59 | scaleAmount /= scaleConstant; 60 | 61 | // Dealing with rounding error by rounding num to 1.0 when close 62 | scaleAmount = (1.0 - scaleAmount) < 0.01 ? 1 : scaleAmount; 63 | juce::AffineTransform transform = juce::AffineTransform().scale(scaleAmount); 64 | m_main->setTransform(transform); 65 | 66 | currentWindow.setWidth(resizedWindow.getWidth()); 67 | currentWindow.setHeight(resizedWindow.getHeight()); 68 | } -------------------------------------------------------------------------------- /Source/UI/PluginEditor.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file contains the basic framework code for a JUCE plugin editor. 5 | 6 | ============================================================================== 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include "../PluginProcessor.h" 13 | #include "../Scope/Spectroscope.h" 14 | 15 | class MainComponent; 16 | 17 | 18 | //============================================================================== 19 | /** 20 | */ 21 | class FaugAudioProcessorEditor : public juce::AudioProcessorEditor 22 | { 23 | public: 24 | FaugAudioProcessorEditor (FaugAudioProcessor&, juce::MidiKeyboardState& state, juce::AudioProcessorValueTreeState& vts); 25 | virtual ~FaugAudioProcessorEditor() override; 26 | 27 | //============================================================================== 28 | void paint (juce::Graphics&) override; 29 | void resized() override; 30 | 31 | 32 | private: 33 | FaugAudioProcessor& audioProcessor; 34 | std::unique_ptr m_main; 35 | 36 | juce::Rectangle originalScreen; 37 | juce::Rectangle currentWindow; 38 | float scaleConstant; 39 | 40 | 41 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FaugAudioProcessorEditor) 42 | }; -------------------------------------------------------------------------------- /Source/UI/SplashAnimation.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | SplashAnimation.cpp 5 | Created: 4 Aug 2022 10:03:17pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include 12 | #include "SplashAnimation.h" 13 | 14 | //============================================================================== 15 | SplashAnimation::SplashAnimation(const juce::String& title, int width, int height, bool useDropShadow) : 16 | juce::SplashScreen(title, width, height, useDropShadow) 17 | { 18 | juce::AnimatedAppComponent::setSize(width, height); 19 | juce::AnimatedAppComponent::setFramesPerSecond(60); 20 | coinWidth = 100; 21 | coinHeight = 100; 22 | } 23 | 24 | SplashAnimation::~SplashAnimation() 25 | { 26 | } 27 | 28 | void SplashAnimation::paint (juce::Graphics& g) 29 | { 30 | g.setColour(juce::AnimatedAppComponent::getLookAndFeel().findColour(juce::Slider::thumbColourId)); 31 | 32 | auto fishLength = 15; 33 | 34 | juce::Path spinePath; 35 | 36 | for (auto i = 0; i < fishLength; ++i) 37 | { 38 | int radius = 150; 39 | 40 | juce::Point p(juce::AnimatedAppComponent::getWidth() / 2.0f + 1.0f * radius * std::sin(getFrameCounter() * 0.04f + i * 0.12f), 41 | juce::AnimatedAppComponent::getHeight() / 2.0f + 1.0f * radius * std::cos(getFrameCounter() * 0.04f + i * 0.12f)); 42 | 43 | // draw the circles along the fish 44 | g.fillEllipse(p.x - i, p.y - i, 2.0f + 2.0f * i, 2.0f + 2.0f * i); // [1] 45 | 46 | if (i == 0) 47 | spinePath.startNewSubPath(p); // if this is the first point, start a new path.. 48 | else 49 | spinePath.lineTo(p); // ...otherwise add the next point 50 | } 51 | 52 | 53 | // draw an outline around the path that we have created 54 | g.strokePath(spinePath, juce::PathStrokeType(4.0f)); 55 | } 56 | 57 | void SplashAnimation::update() 58 | { 59 | juce::SplashScreen::repaint(); 60 | } 61 | 62 | void SplashAnimation::resized() 63 | { 64 | // This method is where you should set the bounds of any child 65 | // components that your component contains.. 66 | 67 | } 68 | -------------------------------------------------------------------------------- /Source/UI/SplashAnimation.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | SplashAnimation.h 5 | Created: 4 Aug 2022 10:03:17pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | //============================================================================== 16 | /* 17 | */ 18 | class SplashAnimation : public juce::SplashScreen, 19 | private juce::AnimatedAppComponent 20 | { 21 | public: 22 | SplashAnimation(const juce::String& title, int width, int height, bool useDropShadow); 23 | virtual ~SplashAnimation() override; 24 | 25 | virtual void update() override; 26 | 27 | void paint (juce::Graphics&) override; 28 | void resized() override; 29 | 30 | private: 31 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SplashAnimation) 32 | 33 | juce::Path spinePath; 34 | int coinWidth; 35 | int coinHeight; 36 | }; 37 | -------------------------------------------------------------------------------- /Source/UI/Toggles/Toggle.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | Toggle.cpp 5 | Created: 10 Aug 2022 9:48:06pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include 12 | #include "Toggle.h" 13 | 14 | 15 | Toggle::Toggle(juce::AudioProcessorValueTreeState& vts, juce::String paramId, ToggleLookAndFeel& laf) 16 | { 17 | toggle = std::make_unique(); 18 | toggle->setBounds(0, 0, laf.toggleWidth, laf.toggleHeight); 19 | toggle->setLookAndFeel(&laf); 20 | addAndMakeVisible(toggle.get()); 21 | attach.reset(new ButtonAttachment(vts, paramId, *toggle.get())); 22 | } 23 | 24 | Toggle::~Toggle(){} -------------------------------------------------------------------------------- /Source/UI/Toggles/Toggle.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | Toggle.h 5 | Created: 10 Aug 2022 9:48:06pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include "ToggleLookAndFeel.h" 15 | 16 | typedef juce::AudioProcessorValueTreeState::ButtonAttachment ButtonAttachment; 17 | 18 | //============================================================================== 19 | /* 20 | */ 21 | class Toggle : public juce::Component 22 | { 23 | public: 24 | Toggle(juce::AudioProcessorValueTreeState& vts, juce::String paramId, ToggleLookAndFeel& laf); 25 | virtual ~Toggle() override; 26 | 27 | private: 28 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Toggle) 29 | std::unique_ptr toggle; 30 | std::unique_ptr attach; 31 | }; 32 | 33 | class BigOrangeToggle : public Toggle 34 | { 35 | public: 36 | BigOrangeToggle(juce::AudioProcessorValueTreeState& vts, juce::String paramId, ToggleLookAndFeel& laf) : Toggle(vts, paramId, laf){} 37 | virtual ~BigOrangeToggle() override {}; 38 | }; 39 | 40 | 41 | class OrangeToggle : public Toggle 42 | { 43 | public: 44 | OrangeToggle(juce::AudioProcessorValueTreeState& vts, juce::String paramId, ToggleLookAndFeel& laf) : Toggle(vts, paramId, laf){} 45 | virtual ~OrangeToggle() override {}; 46 | }; 47 | 48 | class BlueToggle : public Toggle 49 | { 50 | public: 51 | BlueToggle(juce::AudioProcessorValueTreeState& vts, juce::String paramId, ToggleLookAndFeel& laf) : Toggle(vts, paramId, laf){} 52 | virtual ~BlueToggle() override {}; 53 | }; 54 | 55 | class BrownToggle : public Toggle 56 | { 57 | public: 58 | BrownToggle(juce::AudioProcessorValueTreeState& vts, juce::String paramId, ToggleLookAndFeel& laf) : Toggle(vts, paramId, laf){} 59 | virtual ~BrownToggle() override {}; 60 | }; 61 | 62 | class WhiteToggle : public Toggle 63 | { 64 | public: 65 | WhiteToggle(juce::AudioProcessorValueTreeState& vts, juce::String paramId, ToggleLookAndFeel& laf) : Toggle(vts, paramId, laf){} 66 | virtual ~WhiteToggle() override {}; 67 | }; -------------------------------------------------------------------------------- /Source/UI/Toggles/ToggleImage.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | ToggleImage.cpp 5 | Created: 10 Aug 2022 9:49:01pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include 12 | 13 | //============================================================================== 14 | /* 15 | */ 16 | class ToggleImage : public juce::DrawableImage 17 | { 18 | public: 19 | ToggleImage(juce::Image& toggleImageIn, int toggleWidth, int toggleHeight) 20 | { 21 | setImage(toggleImageIn.rescaled(toggleWidth, toggleHeight)); 22 | pivot_x = toggleWidth / 2; 23 | pivot_y = toggleHeight / 2; 24 | } 25 | 26 | ~ToggleImage() override 27 | { 28 | } 29 | 30 | int pivot_x; 31 | int pivot_y; 32 | 33 | private: 34 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ToggleImage) 35 | std::unique_ptr toggleImage; 36 | }; 37 | -------------------------------------------------------------------------------- /Source/UI/Toggles/ToggleLookAndFeel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | ToggleLookAndFeel.cpp 5 | Created: 10 Aug 2022 9:48:34pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include 12 | #include "ToggleLookAndFeel.h" 13 | #include "ToggleImage.cpp" 14 | 15 | //============================================================================== 16 | 17 | ToggleLookAndFeel::ToggleLookAndFeel() 18 | {} 19 | 20 | ToggleLookAndFeel::~ToggleLookAndFeel() 21 | {} 22 | 23 | ToggleLookAndFeel::ToggleLookAndFeel(juce::Image& toggleImageIn, int toggleWidthIn, int toggleHeightIn) : 24 | toggleImage(std::make_unique(toggleImageIn,toggleWidth, toggleHeight)), 25 | toggleWidth(toggleWidthIn), toggleHeight(toggleHeightIn) 26 | {} 27 | 28 | juce::Font ToggleLookAndFeel::getBaseFont() 29 | { 30 | return juce::Font::bold; 31 | } 32 | 33 | juce::Font ToggleLookAndFeel::getLabelFont(juce::Label&) 34 | { 35 | return getBaseFont().withPointHeight(10); 36 | } 37 | 38 | juce::Font ToggleLookAndFeel::getSliderReadoutFont() 39 | { 40 | return getBaseFont().withPointHeight(14); 41 | } 42 | 43 | void ToggleLookAndFeel::drawTickBox(juce::Graphics& g, juce::Component& button, 44 | float x, float y, float w, float h, 45 | bool ticked, bool isEnabled, 46 | bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) 47 | { 48 | bool flipped = (ticked && !shouldDrawButtonAsDown) || (shouldDrawButtonAsDown && !ticked); 49 | g.drawImageTransformed(toggleImage->getImage(), transform.rotation(flipped * juce::MathConstants::pi, toggleImage->pivot_x, toggleImage->pivot_y)); 50 | } -------------------------------------------------------------------------------- /Source/UI/Toggles/ToggleLookAndFeel.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | ToggleLookAndFeel.h 5 | Created: 10 Aug 2022 9:48:34pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | class ToggleImage; 16 | 17 | //============================================================================== 18 | /* 19 | */ 20 | class ToggleLookAndFeel : public juce::LookAndFeel_V4 21 | { 22 | public: 23 | ToggleLookAndFeel(); 24 | ToggleLookAndFeel(juce::Image& toggleImageIn, int toggleWidth, int toggleHeight); 25 | virtual ~ToggleLookAndFeel() override; 26 | 27 | void drawTickBox(juce::Graphics&, juce::Component&, 28 | float x, float y, float w, float h, 29 | bool ticked, bool isEnabled, 30 | bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override; 31 | 32 | juce::Font getBaseFont(); 33 | juce::Font getLabelFont(juce::Label&) override; 34 | juce::Font getSliderReadoutFont(); 35 | 36 | int toggleWidth; 37 | int toggleHeight; 38 | 39 | private: 40 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ToggleLookAndFeel) 41 | std::unique_ptr toggleImage; 42 | juce::AffineTransform transform = juce::AffineTransform(); 43 | }; 44 | -------------------------------------------------------------------------------- /Source/UI/Wheel/ModWheel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | Wheel.cpp 5 | Created: 18 Nov 2022 11:11:42pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "ModWheel.h" 12 | 13 | ModWheel::ModWheel(juce::AudioProcessorValueTreeState& vts, juce::String paramId, ModWheelLookAndFeel& laf) 14 | { 15 | slider = std::make_unique(); 16 | addAndMakeVisible(slider.get()); 17 | slider->setBounds(0, 0, laf.wheelWidth, laf.wheelHeight); 18 | slider->setSliderStyle(juce::Slider::LinearVertical); 19 | slider->setTextBoxStyle(juce::Slider::NoTextBox, true, 80, 20); 20 | attach.reset(new SliderAttachment(vts, juce::String(paramId), *slider.get())); 21 | slider->setLookAndFeel(&laf); 22 | } 23 | 24 | ModWheel::~ModWheel() 25 | { 26 | } -------------------------------------------------------------------------------- /Source/UI/Wheel/ModWheel.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | Wheel.h 5 | Created: 18 Nov 2022 11:11:42pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include "ModWheelLookAndFeel.h" 15 | 16 | typedef juce::AudioProcessorValueTreeState::SliderAttachment SliderAttachment; 17 | 18 | class ModWheel : public juce::Component 19 | { 20 | public: 21 | ModWheel(juce::AudioProcessorValueTreeState& vts, juce::String paramId, ModWheelLookAndFeel& laf); 22 | virtual ~ModWheel() override; 23 | 24 | private: 25 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ModWheel) 26 | std::unique_ptr slider; 27 | std::unique_ptr attach; 28 | }; -------------------------------------------------------------------------------- /Source/UI/Wheel/ModWheelImage.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | WheelLookAndFeel.cpp 5 | Created: 18 Nov 2022 11:12:01pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | class ModWheelImage : public juce::DrawableImage 16 | { 17 | public: 18 | ModWheelImage(juce::Image& imageIn, const int wheelWidth, const int wheelHeight) 19 | { 20 | float wheelScale = float(wheelWidth) / float(imageIn.getWidth()); 21 | 22 | wheelImage = std::make_unique(imageIn.rescaled(wheelWidth, imageIn.getHeight() * wheelScale)); 23 | 24 | setSize(wheelWidth, wheelHeight); 25 | setImage(*wheelImage.get()); 26 | 27 | pivotPoint = float(wheelImage->getHeight())/2.0; 28 | } 29 | 30 | virtual ~ModWheelImage() override 31 | { 32 | } 33 | 34 | float pivotPoint; 35 | 36 | private: 37 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ModWheelImage) 38 | std::unique_ptr wheelImage; 39 | }; -------------------------------------------------------------------------------- /Source/UI/Wheel/ModWheelLookAndFeel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | WheelLookAndFeel.cpp 5 | Created: 18 Nov 2022 11:12:01pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include 12 | #include "ModWheelLookAndFeel.h" 13 | #include "ModWheelImage.cpp" 14 | //============================================================================== 15 | ModWheelLookAndFeel::ModWheelLookAndFeel(juce::Image& wheelImageIn, juce::Image& wheelShadingIn, 16 | const int wheelWidthIn, const int wheelHeightIn) : 17 | wheelImage(std::make_unique(wheelImageIn, wheelWidth, wheelHeight)), 18 | wheelShading(std::make_unique(wheelShadingIn)), 19 | wheelWidth(wheelWidthIn), wheelHeight(wheelHeightIn) 20 | { 21 | } 22 | 23 | ModWheelLookAndFeel::~ModWheelLookAndFeel() 24 | {} 25 | 26 | juce::Font ModWheelLookAndFeel::getBaseFont() 27 | { 28 | return juce::Font::bold; 29 | } 30 | 31 | juce::Font ModWheelLookAndFeel::getLabelFont(juce::Label&) 32 | { 33 | return getBaseFont().withPointHeight(10); 34 | } 35 | 36 | juce::Font ModWheelLookAndFeel::getSliderReadoutFont() 37 | { 38 | return getBaseFont().withPointHeight(14); 39 | } 40 | 41 | void ModWheelLookAndFeel::drawLabel(juce::Graphics& g, juce::Label& l) 42 | { 43 | juce::Colour labelColour = juce::Colour::fromRGB(149, 89, 17); 44 | juce::Font labelFont = getLabelFont(l); 45 | 46 | g.setColour(labelColour); 47 | g.setFont(labelFont); 48 | 49 | juce::Rectangle textArea(l.getBorderSize().subtractedFrom(l.getLocalBounds())); 50 | 51 | g.drawFittedText(l.getText(), textArea, l.getJustificationType(), 52 | juce::jmax(1, (int)(textArea.getHeight() / labelFont.getHeight())), 53 | l.getMinimumHorizontalScale()); 54 | } 55 | 56 | void ModWheelLookAndFeel::drawLinearSlider(juce::Graphics& g, int x, int y, int width, int height, 57 | float sliderPos, float minSliderPos, float maxSliderPos, 58 | const juce::Slider::SliderStyle sliderStyle, juce::Slider& slider) 59 | { 60 | const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled(); 61 | //wheelImage->setTransform(transform.translation(0, sliderPos - maxSliderPos)); 62 | g.drawImageAt(wheelImage->getImage(), 0, sliderPos-wheelImage.get()->pivotPoint, false); 63 | g.drawImageAt(*wheelShading.get(), 0, 0, false); 64 | 65 | 66 | // Draw the readout 67 | // Change this to hint box? 68 | 69 | 70 | /*juce::Colour readoutColour = juce::Colour::fromRGB(254, 173, 29).withAlpha(isMouseOver ? 1.0f : 0.9f); 71 | const double value = slider.getValue(); 72 | juce::String readoutValue = (value >= 1000.0 ? juce::String(value / 1000.0, 1) + "k" : juce::String(value, 2)); 73 | juce::String readout = readoutValue; 74 | 75 | g.setColour(readoutColour); 76 | g.setFont(getSliderReadoutFont()); 77 | g.drawText(readout, centreX - radius, centreY - 10.0f, rw, 24.0f, juce::Justification::centred);*/ 78 | 79 | // Draw the track 80 | g.setColour(slider.findColour(juce::Slider::rotarySliderOutlineColourId)); 81 | } -------------------------------------------------------------------------------- /Source/UI/Wheel/ModWheelLookAndFeel.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | WheelLookAndFeel.h 5 | Created: 18 Nov 2022 11:12:01pm 6 | Author: tytu1 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | class ModWheelImage; 16 | 17 | //============================================================================== 18 | /* 19 | */ 20 | class ModWheelLookAndFeel : public juce::LookAndFeel_V4 21 | { 22 | public: 23 | ModWheelLookAndFeel(juce::Image& wheelImage, juce::Image& wheelShading, 24 | const int wheelWidth, const int wheelHeight); 25 | virtual ~ModWheelLookAndFeel() override; 26 | 27 | juce::Font getBaseFont(); 28 | juce::Font getLabelFont(juce::Label&) override; 29 | juce::Font getSliderReadoutFont(); 30 | 31 | void drawLabel(juce::Graphics&, juce::Label&) override; 32 | void drawLinearSlider(juce::Graphics& g, int x, int y, int width, int height, 33 | float sliderPos, float minSliderPos, float maxSliderPos, 34 | const juce::Slider::SliderStyle sliderStyle, juce::Slider& slider) override; 35 | 36 | int wheelWidth; 37 | int wheelHeight; 38 | 39 | private: 40 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ModWheelLookAndFeel) 41 | std::unique_ptr wheelImage; 42 | std::unique_ptr wheelShading; 43 | juce::AffineTransform transform = juce::AffineTransform(); 44 | }; 45 | -------------------------------------------------------------------------------- /StandaloneApp/Windows/Faug.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/StandaloneApp/Windows/Faug.exe -------------------------------------------------------------------------------- /imageWork/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/background.png -------------------------------------------------------------------------------- /imageWork/backgroundExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/backgroundExample.png -------------------------------------------------------------------------------- /imageWork/faugScreenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/faugScreenshot.png -------------------------------------------------------------------------------- /imageWork/knobs/knobOne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/knobs/knobOne.png -------------------------------------------------------------------------------- /imageWork/knobs/knobTwo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/knobs/knobTwo.png -------------------------------------------------------------------------------- /imageWork/knobs/screwKnob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/knobs/screwKnob.png -------------------------------------------------------------------------------- /imageWork/knobs/wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/knobs/wheel.png -------------------------------------------------------------------------------- /imageWork/knobs/wheelShading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/knobs/wheelShading.png -------------------------------------------------------------------------------- /imageWork/toggles/blueToggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/toggles/blueToggle.png -------------------------------------------------------------------------------- /imageWork/toggles/brownToggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/toggles/brownToggle.png -------------------------------------------------------------------------------- /imageWork/toggles/orangeToggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/toggles/orangeToggle.png -------------------------------------------------------------------------------- /imageWork/toggles/whiteToggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2techno/Faug/b15d0532d84e1efe73e9acdbaecd1e90ce596186/imageWork/toggles/whiteToggle.png --------------------------------------------------------------------------------