diff --git a/Documents/discoDSP/OB-Xd/Banks/000 - FMR OB-Xa Patch Book.fxb b/Documents/discoDSP/OB-Xd/Banks/000 - FMR OB-Xa Patch Book.fxb new file mode 100644 index 0000000..00d2696 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/000 - FMR OB-Xa Patch Book.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/001 - Init Bank.fxb b/Documents/discoDSP/OB-Xd/Banks/001 - Init Bank.fxb new file mode 100644 index 0000000..c56791e Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/001 - Init Bank.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/002 - KVR Bass.fxb b/Documents/discoDSP/OB-Xd/Banks/002 - KVR Bass.fxb new file mode 100644 index 0000000..c894c94 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/002 - KVR Bass.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/003 - KVR Brass + Synths.fxb b/Documents/discoDSP/OB-Xd/Banks/003 - KVR Brass + Synths.fxb new file mode 100644 index 0000000..af41d40 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/003 - KVR Brass + Synths.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/004 - KVR Drums + Percussion + SFXs.fxb b/Documents/discoDSP/OB-Xd/Banks/004 - KVR Drums + Percussion + SFXs.fxb new file mode 100644 index 0000000..04f762f Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/004 - KVR Drums + Percussion + SFXs.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/005 - KVR Keys + Bells + Plucked.fxb b/Documents/discoDSP/OB-Xd/Banks/005 - KVR Keys + Bells + Plucked.fxb new file mode 100644 index 0000000..5c190e3 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/005 - KVR Keys + Bells + Plucked.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/006 - KVR Leads.fxb b/Documents/discoDSP/OB-Xd/Banks/006 - KVR Leads.fxb new file mode 100644 index 0000000..0c7cbfa Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/006 - KVR Leads.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/007 - KVR Pads + Strings + Vocalic.fxb b/Documents/discoDSP/OB-Xd/Banks/007 - KVR Pads + Strings + Vocalic.fxb new file mode 100644 index 0000000..4de9c57 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/007 - KVR Pads + Strings + Vocalic.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/008 - ABS Custom Shop v1.fxb b/Documents/discoDSP/OB-Xd/Banks/008 - ABS Custom Shop v1.fxb new file mode 100644 index 0000000..1c898b1 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/008 - ABS Custom Shop v1.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/009 - Breeze Meat-n-Potatoes.fxb b/Documents/discoDSP/OB-Xd/Banks/009 - Breeze Meat-n-Potatoes.fxb new file mode 100644 index 0000000..60e1fdf Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/009 - Breeze Meat-n-Potatoes.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/010 - Breeze Meat-n-Potatoes Extras.fxb b/Documents/discoDSP/OB-Xd/Banks/010 - Breeze Meat-n-Potatoes Extras.fxb new file mode 100644 index 0000000..b0c2655 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/010 - Breeze Meat-n-Potatoes Extras.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/011 - IW Bank 1.fxb b/Documents/discoDSP/OB-Xd/Banks/011 - IW Bank 1.fxb new file mode 100644 index 0000000..fce51df Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/011 - IW Bank 1.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/012 - Kujashi.fxb b/Documents/discoDSP/OB-Xd/Banks/012 - Kujashi.fxb new file mode 100644 index 0000000..1d430f4 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/012 - Kujashi.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/013 - KVR Community Bank - Part 1.fxb b/Documents/discoDSP/OB-Xd/Banks/013 - KVR Community Bank - Part 1.fxb new file mode 100644 index 0000000..c1c7ab0 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/013 - KVR Community Bank - Part 1.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/014 - KVR Community Bank - Part 2.fxb b/Documents/discoDSP/OB-Xd/Banks/014 - KVR Community Bank - Part 2.fxb new file mode 100644 index 0000000..d2899a9 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/014 - KVR Community Bank - Part 2.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/015 - Rin Elyran Bank.fxb b/Documents/discoDSP/OB-Xd/Banks/015 - Rin Elyran Bank.fxb new file mode 100644 index 0000000..1e8b744 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/015 - Rin Elyran Bank.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/016 - Xenos Soundworks.fxb b/Documents/discoDSP/OB-Xd/Banks/016 - Xenos Soundworks.fxb new file mode 100644 index 0000000..903effd Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/016 - Xenos Soundworks.fxb differ diff --git a/Documents/discoDSP/OB-Xd/Banks/017 - Joel Obxd.FXB b/Documents/discoDSP/OB-Xd/Banks/017 - Joel Obxd.FXB new file mode 100644 index 0000000..2661e47 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/017 - Joel Obxd.FXB differ diff --git a/Documents/discoDSP/OB-Xd/Banks/018 - TheBassProject by ThePresent.FXB b/Documents/discoDSP/OB-Xd/Banks/018 - TheBassProject by ThePresent.FXB new file mode 100644 index 0000000..2ffa9b6 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/018 - TheBassProject by ThePresent.FXB differ diff --git a/Documents/discoDSP/OB-Xd/Banks/019 - Phace.FXB b/Documents/discoDSP/OB-Xd/Banks/019 - Phace.FXB new file mode 100644 index 0000000..228c6e7 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Banks/019 - Phace.FXB differ diff --git a/Documents/discoDSP/OB-Xd/MIDI CC.txt b/Documents/discoDSP/OB-Xd/MIDI CC.txt new file mode 100644 index 0000000..ba46ce2 --- /dev/null +++ b/Documents/discoDSP/OB-Xd/MIDI CC.txt @@ -0,0 +1,82 @@ +OB-Xd 2.0 default MIDI CC controller table. +Modify by clicking LEARN button, moving a GUI element and then sending MIDI CC data. +All values can be reset using CLEAN button. + +CC# CONTROL + +21 ASPLAYEDALLOCATION +105 BANDPASS +75 BENDLFORATE +34 BENDOSC2 +118 BENDRANGE +62 BRIGHTNESS +74 CUTOFF +111 ECONOMY_MODE +115 ENV_PITCH_BOTH +108 ENVDER +107 ENVELOPE_AMT +63 ENVPITCH +38 FATK +39 FDEC +116 FENV_INVERT +18 FILTER_WARM +109 FILTERDER +103 FLT_KF +106 FOURPOLE +41 FREL +40 FSUS +73 LATK +36 LDEC +35 LEGATOMODE +118 LEVEL_DIF +22 LFO1AMT +25 LFO2AMT +49 LFOFILTER +19 LFOFREQ +47 LFOOSC1 +48 LFOOSC2 +50 LFOPW1 +51 LFOPW2 +46 LFOSHWAVE +44 LFOSINWAVE +45 LFOSQUAREWAVE +72 LREL +37 LSUS +104 MULTIMODE +102 NOISEMIX +17 OCTAVE +100 OSC1MIX +54 OSC1P +58 OSC1Pul +57 OSC1Saw +43 OSC2_DET +52 OSC2HS +101 OSC2MIX +55 OSC2P +60 OSC2Pul +59 OSC2Saw +56 OSCQuantize +81 PAN1 +82 PAN2 +83 PAN3 +84 PAN4 +85 PAN5 +86 PAN6 +87 PAN7 +88 PAN8 +110 PORTADER +23 PORTAMENTO +61 PW +113 PW_ENV +114 PW_ENV_BOTH +117 PW_OSC2_OFS +42 RESONANCE +119 SELF_OSC_PUSH +33 TUNE +24 UDET +16 UNISON +20 VAMPENV +76 VFLTENV +15 VOICE_COUNT +71 VOLUME +53 XMOD diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/button.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/button.png new file mode 100644 index 0000000..e410d33 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/button.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/button@2x.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/button@2x.png new file mode 100644 index 0000000..ca2ba4f Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/button@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/coords.xml b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/coords.xml new file mode 100644 index 0000000..243851d --- /dev/null +++ b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/coords.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/knob.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/knob.png new file mode 100644 index 0000000..7e54bcb Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/knob.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/knob@2x.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/knob@2x.png new file mode 100644 index 0000000..846531e Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/knob@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/legato.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/legato.png new file mode 100644 index 0000000..41447d8 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/legato.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/legato@2x.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/legato@2x.png new file mode 100644 index 0000000..dbeb056 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/legato@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/main.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/main.png new file mode 100644 index 0000000..8ddd93f Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/main.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/main@2x.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/main@2x.png new file mode 100644 index 0000000..dabff5d Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/main@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/menu.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/menu.png new file mode 100644 index 0000000..00d4e77 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/menu.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/menu@2x.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/menu@2x.png new file mode 100644 index 0000000..00d4e77 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/menu@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/voices.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/voices.png new file mode 100644 index 0000000..9738939 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/voices.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/voices@2x.png b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/voices@2x.png new file mode 100644 index 0000000..3cde5f3 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Ilkka Rosma Dark/voices@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/About.rtf b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/About.rtf new file mode 100755 index 0000000..73cc4b9 --- /dev/null +++ b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/About.rtf @@ -0,0 +1,10 @@ +{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf600 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +{\*\expandedcolortbl;;} +\paperw11900\paperh16840\margl1440\margr1440\vieww10800\viewh8400\viewkind0 +\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 + +\f0\fs24 \cf0 By rin_elyran {\field{\*\fldinst{HYPERLINK "https://www.kvraudio.com/forum/viewtopic.php?p=6596898#p6596898"}}{\fldrslt https://www.kvraudio.com/forum/viewtopic.php?p=6596898#p6596898}}\ +\ +2.0 Retina rework by discoDSP.} \ No newline at end of file diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/button.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/button.png new file mode 100755 index 0000000..04cf119 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/button.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/button@2x.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/button@2x.png new file mode 100755 index 0000000..858511a Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/button@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/coords.xml b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/coords.xml new file mode 100755 index 0000000..044a18b --- /dev/null +++ b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/coords.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/knob.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/knob.png new file mode 100644 index 0000000..2525342 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/knob.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/knob@2x.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/knob@2x.png new file mode 100644 index 0000000..8f0d205 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/knob@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/legato.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/legato.png new file mode 100755 index 0000000..34e9a9c Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/legato.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/legato@2x.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/legato@2x.png new file mode 100755 index 0000000..07a7f09 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/legato@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/main.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/main.png new file mode 100755 index 0000000..eb266eb Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/main.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/main@2x.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/main@2x.png new file mode 100755 index 0000000..ca00faf Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/main@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/menu.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/menu.png new file mode 100755 index 0000000..2b5e0b6 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/menu.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/menu@2x.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/menu@2x.png new file mode 100755 index 0000000..2dc9647 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/menu@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/voices.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/voices.png new file mode 100755 index 0000000..2e4d0ce Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/voices.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/voices@2x.png b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/voices@2x.png new file mode 100755 index 0000000..21e9b4d Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/Rin Elyran Classic SEM/voices@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/button.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/button.png new file mode 100644 index 0000000..fa95a0a Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/button.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/button@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/button@2x.png new file mode 100755 index 0000000..5082889 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/button@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/coords.xml b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/coords.xml new file mode 100755 index 0000000..94e3016 --- /dev/null +++ b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/coords.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/knob.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/knob.png new file mode 100644 index 0000000..483d151 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/knob.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/knob@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/knob@2x.png new file mode 100644 index 0000000..0dd5c06 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/knob@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/knob@2x_old.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/knob@2x_old.png new file mode 100755 index 0000000..f9fb07a Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/knob@2x_old.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/legato.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/legato.png new file mode 100644 index 0000000..8615d14 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/legato.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/legato@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/legato@2x.png new file mode 100755 index 0000000..ccbe90c Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/legato@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/main.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/main.png new file mode 100644 index 0000000..7892a8a Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/main.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/main@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/main@2x.png new file mode 100755 index 0000000..a102a68 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/main@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/menu.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/menu.png new file mode 100755 index 0000000..b7cfc60 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/menu.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/menu@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/menu@2x.png new file mode 100755 index 0000000..b7cfc60 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/menu@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/voices.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/voices.png new file mode 100644 index 0000000..3af3788 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/voices.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/voices@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/voices@2x.png new file mode 100755 index 0000000..11d13f3 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Blue/voices@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/button.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/button.png new file mode 100755 index 0000000..4a1e19f Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/button.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/button@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/button@2x.png new file mode 100755 index 0000000..c1ae5c7 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/button@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/coords.xml b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/coords.xml new file mode 100755 index 0000000..12edfb7 --- /dev/null +++ b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/coords.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/knob.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/knob.png new file mode 100755 index 0000000..2525342 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/knob.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/knob@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/knob@2x.png new file mode 100755 index 0000000..8f0d205 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/knob@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/legato.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/legato.png new file mode 100755 index 0000000..34e9a9c Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/legato.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/legato@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/legato@2x.png new file mode 100755 index 0000000..07a7f09 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/legato@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/main.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/main.png new file mode 100755 index 0000000..4081feb Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/main.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/main@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/main@2x.png new file mode 100755 index 0000000..def7436 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/main@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/menu.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/menu.png new file mode 100755 index 0000000..b7cfc60 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/menu.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/menu@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/menu@2x.png new file mode 100755 index 0000000..b7cfc60 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/menu@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/voices.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/voices.png new file mode 100755 index 0000000..21e9b4d Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/voices.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/voices@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/voices@2x.png new file mode 100755 index 0000000..21e9b4d Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Classic/voices@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/button.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/button.png new file mode 100644 index 0000000..fa95a0a Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/button.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/button@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/button@2x.png new file mode 100755 index 0000000..5082889 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/button@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/coords.xml b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/coords.xml new file mode 100755 index 0000000..94e3016 --- /dev/null +++ b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/coords.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/knob.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/knob.png new file mode 100755 index 0000000..d83ee6a Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/knob.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/knob@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/knob@2x.png new file mode 100755 index 0000000..2aaf8e8 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/knob@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/legato.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/legato.png new file mode 100755 index 0000000..ccbe90c Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/legato.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/legato@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/legato@2x.png new file mode 100755 index 0000000..ccbe90c Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/legato@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/main.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/main.png new file mode 100755 index 0000000..6bf348c Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/main.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/main@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/main@2x.png new file mode 100755 index 0000000..6bf348c Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/main@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/menu.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/menu.png new file mode 100755 index 0000000..2dc9647 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/menu.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/menu@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/menu@2x.png new file mode 100755 index 0000000..2dc9647 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/menu@2x.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/voices.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/voices.png new file mode 100755 index 0000000..11d13f3 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/voices.png differ diff --git a/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/voices@2x.png b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/voices@2x.png new file mode 100755 index 0000000..11d13f3 Binary files /dev/null and b/Documents/discoDSP/OB-Xd/Themes/discoDSP Grey/voices@2x.png differ diff --git a/Modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h b/Modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h new file mode 100644 index 0000000..493aa86 --- /dev/null +++ b/Modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h @@ -0,0 +1,946 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 5 End-User License + Agreement and JUCE 5 Privacy Policy (both updated and effective as of the + 27th April 2017). + + End User License Agreement: www.juce.com/juce-5-licence + Privacy Policy: www.juce.com/juce-5-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client +extern juce::AudioProcessor* JUCE_API JUCE_CALLTYPE createPluginFilterOfType (juce::AudioProcessor::WrapperType type); +#endif + +namespace juce +{ + +//============================================================================== +/** + An object that creates and plays a standalone instance of an AudioProcessor. + + The object will create your processor using the same createPluginFilter() + function that the other plugin wrappers use, and will run it through the + computer's audio/MIDI devices using AudioDeviceManager and AudioProcessorPlayer. + + @tags{Audio} +*/ +class StandalonePluginHolder : private AudioIODeviceCallback, + private Timer, + private Value::Listener +{ +public: + //============================================================================== + /** Structure used for the number of inputs and outputs. */ + struct PluginInOuts { short numIns, numOuts; }; + + //============================================================================== + /** Creates an instance of the default plugin. + + The settings object can be a PropertySet that the class should use to store its + settings - the takeOwnershipOfSettings indicates whether this object will delete + the settings automatically when no longer needed. The settings can also be nullptr. + + A default device name can be passed in. + + Preferably a complete setup options object can be used, which takes precedence over + the preferredDefaultDeviceName and allows you to select the input & output device names, + sample rate, buffer size etc. + + In all instances, the settingsToUse will take precedence over the "preferred" options if not null. + */ + StandalonePluginHolder (PropertySet* settingsToUse, + bool takeOwnershipOfSettings = true, + const String& preferredDefaultDeviceName = String(), + const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr, + const Array& channels = Array(), + #if JUCE_ANDROID || JUCE_IOS + bool shouldAutoOpenMidiDevices = true + #else + bool shouldAutoOpenMidiDevices = true + #endif + ) + + : settings (settingsToUse, takeOwnershipOfSettings), + channelConfiguration (channels), + autoOpenMidiDevices (shouldAutoOpenMidiDevices) + { + shouldMuteInput.addListener (this); + shouldMuteInput = ! isInterAppAudioConnected(); + + createPlugin(); + + auto inChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns + : processor->getMainBusNumInputChannels()); + + if (preferredSetupOptions != nullptr) + options.reset (new AudioDeviceManager::AudioDeviceSetup (*preferredSetupOptions)); + + auto audioInputRequired = (inChannels > 0); + + if (audioInputRequired && RuntimePermissions::isRequired (RuntimePermissions::recordAudio) + && ! RuntimePermissions::isGranted (RuntimePermissions::recordAudio)) + RuntimePermissions::request (RuntimePermissions::recordAudio, + [this, preferredDefaultDeviceName] (bool granted) { init (granted, preferredDefaultDeviceName); }); + else + init (audioInputRequired, preferredDefaultDeviceName); + } + + void init (bool enableAudioInput, const String& preferredDefaultDeviceName) + { + setupAudioDevices (enableAudioInput, preferredDefaultDeviceName, options.get()); + reloadPluginState(); + startPlaying(); + + if (autoOpenMidiDevices) + startTimer (500); + } + + virtual ~StandalonePluginHolder() override + { + stopTimer(); + + deletePlugin(); + shutDownAudioDevices(); + } + + //============================================================================== + virtual void createPlugin() + { + #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client + processor.reset (::createPluginFilterOfType (AudioProcessor::wrapperType_Standalone)); + #else + AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::wrapperType_Standalone); + processor.reset (createPluginFilter()); + AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::wrapperType_Undefined); + #endif + jassert (processor != nullptr); // Your createPluginFilter() function must return a valid object! + + processor->disableNonMainBuses(); + processor->setRateAndBufferSizeDetails (44100, 512); + + int inChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns + : processor->getMainBusNumInputChannels()); + + int outChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numOuts + : processor->getMainBusNumOutputChannels()); + + processorHasPotentialFeedbackLoop = (inChannels > 0 && outChannels > 0); + } + + virtual void deletePlugin() + { + stopPlaying(); + processor = nullptr; + } + + static String getFilePatterns (const String& fileSuffix) + { + if (fileSuffix.isEmpty()) + return {}; + + return (fileSuffix.startsWithChar ('.') ? "*" : "*.") + fileSuffix; + } + + //============================================================================== + Value& getMuteInputValue() { return shouldMuteInput; } + bool getProcessorHasPotentialFeedbackLoop() const { return processorHasPotentialFeedbackLoop; } + void valueChanged (Value& value) override { muteInput = (bool) value.getValue(); } + + //============================================================================== + File getLastFile() const + { + File f; + + if (settings != nullptr) + f = File (settings->getValue ("lastStateFile")); + + if (f == File()) + f = File::getSpecialLocation (File::userDocumentsDirectory); + + return f; + } + + void setLastFile (const FileChooser& fc) + { + if (settings != nullptr) + settings->setValue ("lastStateFile", fc.getResult().getFullPathName()); + } + + /** Pops up a dialog letting the user save the processor's state to a file. */ + void askUserToSaveState (const String& fileSuffix = String()) + { + #if JUCE_MODAL_LOOPS_PERMITTED + FileChooser fc (TRANS("Save current state"), getLastFile(), getFilePatterns (fileSuffix)); + + if (fc.browseForFileToSave (true)) + { + setLastFile (fc); + + MemoryBlock data; + processor->getStateInformation (data); + + if (! fc.getResult().replaceWithData (data.getData(), data.getSize())) + AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + TRANS("Error whilst saving"), + TRANS("Couldn't write to the specified file!")); + } + #else + ignoreUnused (fileSuffix); + #endif + } + + /** Pops up a dialog letting the user re-load the processor's state from a file. */ + void askUserToLoadState (const String& fileSuffix = String()) + { + #if JUCE_MODAL_LOOPS_PERMITTED + FileChooser fc (TRANS("Load a saved state"), getLastFile(), getFilePatterns (fileSuffix)); + + if (fc.browseForFileToOpen()) + { + setLastFile (fc); + + MemoryBlock data; + + if (fc.getResult().loadFileAsData (data)) + processor->setStateInformation (data.getData(), (int) data.getSize()); + else + AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + TRANS("Error whilst loading"), + TRANS("Couldn't read from the specified file!")); + } + #else + ignoreUnused (fileSuffix); + #endif + } + + //============================================================================== + void startPlaying() + { + player.setProcessor (processor.get()); + + #if JucePlugin_Enable_IAA && JUCE_IOS + if (auto device = dynamic_cast (deviceManager.getCurrentAudioDevice())) + { + processor->setPlayHead (device->getAudioPlayHead()); + device->setMidiMessageCollector (&player.getMidiMessageCollector()); + } + #endif + } + + void stopPlaying() + { + player.setProcessor (nullptr); + } + + //============================================================================== + /** Shows an audio properties dialog box modally. */ + void showAudioSettingsDialog() + { + DialogWindow::LaunchOptions o; + + int maxNumInputs = 0, maxNumOutputs = 0; + + if (channelConfiguration.size() > 0) + { + auto& defaultConfig = channelConfiguration.getReference (0); + + maxNumInputs = jmax (0, (int) defaultConfig.numIns); + maxNumOutputs = jmax (0, (int) defaultConfig.numOuts); + } + + if (auto* bus = processor->getBus (true, 0)) + maxNumInputs = jmax (0, bus->getDefaultLayout().size()); + + if (auto* bus = processor->getBus (false, 0)) + maxNumOutputs = jmax (0, bus->getDefaultLayout().size()); + + o.content.setOwned (new SettingsComponent (*this, deviceManager, maxNumInputs, maxNumOutputs)); +//#if JUCE_MAC + o.content->setSize (500, 400); +//#endif +//#if ! JUCE_MAC +// o.content->setSize (500, 450); +//#endif + o.dialogTitle = TRANS("Audio/MIDI Settings"); + o.dialogBackgroundColour = o.content->getLookAndFeel().findColour (ResizableWindow::backgroundColourId); + o.escapeKeyTriggersCloseButton = true; + o.useNativeTitleBar = true; + o.resizable = false; + + o.launchAsync(); + } + + void saveAudioDeviceState() + { + if (settings != nullptr) + { + auto xml = deviceManager.createStateXml(); + + settings->setValue ("audioSetup", xml.get()); + + #if ! (JUCE_IOS || JUCE_ANDROID) + settings->setValue ("shouldMuteInput", (bool) shouldMuteInput.getValue()); + #endif + } + } + + void reloadAudioDeviceState (bool enableAudioInput, + const String& preferredDefaultDeviceName, + const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions) + { + std::unique_ptr savedState; + + if (settings != nullptr) + { + savedState = settings->getXmlValue ("audioSetup"); + + #if ! (JUCE_IOS || JUCE_ANDROID) + shouldMuteInput.setValue (settings->getBoolValue ("shouldMuteInput", true)); + #endif + } + + auto totalInChannels = processor->getMainBusNumInputChannels(); + auto totalOutChannels = processor->getMainBusNumOutputChannels(); + + if (channelConfiguration.size() > 0) + { + auto defaultConfig = channelConfiguration.getReference (0); + totalInChannels = defaultConfig.numIns; + totalOutChannels = defaultConfig.numOuts; + } + + deviceManager.initialise (enableAudioInput ? totalInChannels : 0, + totalOutChannels, + savedState.get(), + true, + preferredDefaultDeviceName, + preferredSetupOptions); + } + + //============================================================================== + void savePluginState() + { + if (settings != nullptr && processor != nullptr) + { + MemoryBlock data; + processor->getStateInformation (data); + + settings->setValue ("filterState", data.toBase64Encoding()); + } + } + + void reloadPluginState() + { + if (settings != nullptr) + { + MemoryBlock data; + + if (data.fromBase64Encoding (settings->getValue ("filterState")) && data.getSize() > 0) + processor->setStateInformation (data.getData(), (int) data.getSize()); + } + } + + //============================================================================== + void switchToHostApplication() + { + #if JUCE_IOS + if (auto device = dynamic_cast (deviceManager.getCurrentAudioDevice())) + device->switchApplication(); + #endif + } + + bool isInterAppAudioConnected() + { + #if JUCE_IOS + if (auto device = dynamic_cast (deviceManager.getCurrentAudioDevice())) + return device->isInterAppAudioConnected(); + #endif + + return false; + } + + #if JUCE_MODULE_AVAILABLE_juce_gui_basics + Image getIAAHostIcon (int size) + { + #if JUCE_IOS && JucePlugin_Enable_IAA + if (auto device = dynamic_cast (deviceManager.getCurrentAudioDevice())) + return device->getIcon (size); + #else + ignoreUnused (size); + #endif + + return {}; + } + #endif + + static StandalonePluginHolder* getInstance(); + + //============================================================================== + OptionalScopedPointer settings; + std::unique_ptr processor; + AudioDeviceManager deviceManager; + AudioProcessorPlayer player; + Array channelConfiguration; + + // avoid feedback loop by default + bool processorHasPotentialFeedbackLoop = true; + std::atomic muteInput { true }; + Value shouldMuteInput; + AudioBuffer emptyBuffer; + bool autoOpenMidiDevices; + + std::unique_ptr options; + Array lastMidiDevices; + +private: + //============================================================================== + class SettingsComponent : public Component + { + public: + SettingsComponent (StandalonePluginHolder& pluginHolder, + AudioDeviceManager& deviceManagerToUse, + int maxAudioInputChannels, + int maxAudioOutputChannels) + : owner (pluginHolder), + deviceSelector (deviceManagerToUse, + 0, maxAudioInputChannels, + 0, maxAudioOutputChannels, + true, + false, // disables MIDI Out // (pluginHolder.processor.get() != nullptr && pluginHolder.processor->producesMidi()), + true, false), + shouldMuteLabel ("Feedback Loop:", "Feedback Loop:"), + shouldMuteButton ("Mute audio input") + { + setOpaque (true); + + shouldMuteButton.setClickingTogglesState (true); + shouldMuteButton.getToggleStateValue().referTo (owner.shouldMuteInput); + + addAndMakeVisible (deviceSelector); + + if (owner.getProcessorHasPotentialFeedbackLoop()) + { + addAndMakeVisible (shouldMuteButton); + addAndMakeVisible (shouldMuteLabel); + + shouldMuteLabel.attachToComponent (&shouldMuteButton, true); + } + } + + void paint (Graphics& g) override + { + g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); + } + + void resized() override + { + auto r = getLocalBounds(); + + if (owner.getProcessorHasPotentialFeedbackLoop()) + { + auto itemHeight = deviceSelector.getItemHeight(); + auto extra = r.removeFromTop (itemHeight); + + auto seperatorHeight = (itemHeight >> 1); + shouldMuteButton.setBounds (Rectangle (extra.proportionOfWidth (0.35f), seperatorHeight, + extra.proportionOfWidth (0.60f), deviceSelector.getItemHeight())); + + r.removeFromTop (seperatorHeight); + } + + deviceSelector.setBounds (r); + } + + private: + //============================================================================== + StandalonePluginHolder& owner; + AudioDeviceSelectorComponent deviceSelector; + Label shouldMuteLabel; + ToggleButton shouldMuteButton; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComponent) + }; + + //============================================================================== + void audioDeviceIOCallback (const float** inputChannelData, + int numInputChannels, + float** outputChannelData, + int numOutputChannels, + int numSamples) override + { + if (muteInput) + { + emptyBuffer.clear(); + inputChannelData = emptyBuffer.getArrayOfReadPointers(); + } + + player.audioDeviceIOCallback (inputChannelData, numInputChannels, + outputChannelData, numOutputChannels, numSamples); + } + + void audioDeviceAboutToStart (AudioIODevice* device) override + { + emptyBuffer.setSize (device->getActiveInputChannels().countNumberOfSetBits(), device->getCurrentBufferSizeSamples()); + emptyBuffer.clear(); + + player.audioDeviceAboutToStart (device); + player.setMidiOutput (deviceManager.getDefaultMidiOutput()); + } + + void audioDeviceStopped() override + { + player.setMidiOutput (nullptr); + player.audioDeviceStopped(); + emptyBuffer.setSize (0, 0); + } + + //============================================================================== + void setupAudioDevices (bool enableAudioInput, + const String& preferredDefaultDeviceName, + const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions) + { + deviceManager.addAudioCallback (this); + deviceManager.addMidiInputDeviceCallback ({}, &player); + + reloadAudioDeviceState (enableAudioInput, preferredDefaultDeviceName, preferredSetupOptions); + } + + void shutDownAudioDevices() + { + saveAudioDeviceState(); + + deviceManager.removeMidiInputDeviceCallback ({}, &player); + deviceManager.removeAudioCallback (this); + } + + void timerCallback() override + { + auto newMidiDevices = MidiInput::getAvailableDevices(); + + if (newMidiDevices != lastMidiDevices) + { + for (auto& oldDevice : lastMidiDevices) + if (! newMidiDevices.contains (oldDevice)) + deviceManager.setMidiInputDeviceEnabled (oldDevice.identifier, false); + + for (auto& newDevice : newMidiDevices) + if (! lastMidiDevices.contains (newDevice)) + deviceManager.setMidiInputDeviceEnabled (newDevice.identifier, true); + + lastMidiDevices = newMidiDevices; + } + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandalonePluginHolder) +}; + +//============================================================================== +/** + A class that can be used to run a simple standalone application containing your filter. + + Just create one of these objects in your JUCEApplicationBase::initialise() method, and + let it do its work. It will create your filter object using the same createPluginFilter() function + that the other plugin wrappers use. + + @tags{Audio} +*/ +class StandaloneFilterWindow : public DocumentWindow, + public Button::Listener, + public MenuBarModel +{ +public: + //============================================================================== + typedef StandalonePluginHolder::PluginInOuts PluginInOuts; + + //============================================================================== + /** Creates a window with a given title and colour. + The settings object can be a PropertySet that the class should use to + store its settings (it can also be null). If takeOwnershipOfSettings is + true, then the settings object will be owned and deleted by this object. + */ + StandaloneFilterWindow (const String& title, + Colour backgroundColour, + PropertySet* settingsToUse, + bool takeOwnershipOfSettings, + const String& preferredDefaultDeviceName = String(), + const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr, + const Array& constrainToConfiguration = {}, + #if JUCE_ANDROID || JUCE_IOS + bool autoOpenMidiDevices = true + #else + bool autoOpenMidiDevices = true + #endif + ) + : DocumentWindow ("", Colour::fromHSV (0.0f, 0.0f, 0.1f, 1.0f), DocumentWindow::minimiseButton | DocumentWindow::closeButton) + , menuBar(this) + #if ! JUCE_MAC + , optionsButton ("Settings") + #endif + { + #if JUCE_IOS || JUCE_ANDROID + setTitleBarHeight (0); + #else + setTitleBarButtonsRequired (DocumentWindow::minimiseButton | DocumentWindow::closeButton, false); + + #if JUCE_MAC + setUsingNativeTitleBar(true); + menu.addItem (1, TRANS("Audio/MIDI Settings...")); + MenuBarModel::setMacMainMenu (this, &menu); + #else + Component::addAndMakeVisible (optionsButton); + optionsButton.addListener (this); + optionsButton.setTriggeredOnMouseDown (true); + #endif + #endif + + pluginHolder.reset (new StandalonePluginHolder (settingsToUse, takeOwnershipOfSettings, + preferredDefaultDeviceName, preferredSetupOptions, + constrainToConfiguration, autoOpenMidiDevices)); + + #if JUCE_IOS || JUCE_ANDROID + setFullScreen (true); + setContentOwned (new MainContentComponent (*this), false); + #else + setContentOwned (new MainContentComponent (*this), true); + + if (auto* props = pluginHolder->settings.get()) + { + const int x = props->getIntValue ("windowX", -100); + const int y = props->getIntValue ("windowY", -100); + + if (x != -100 && y != -100) + setBoundsConstrained ({ x, y, getWidth(), getHeight() }); + else + centreWithSize (getWidth(), getHeight()); + } + else + { + centreWithSize (getWidth(), getHeight()); + } + #endif + } + + ~StandaloneFilterWindow() override + { + #if JUCE_MAC + setMacMainMenu(nullptr); + #endif + #if (! JUCE_IOS) && (! JUCE_ANDROID) + if (auto* props = pluginHolder->settings.get()) + { + props->setValue ("windowX", getX()); + props->setValue ("windowY", getY()); + } + #endif + + pluginHolder->stopPlaying(); + clearContentComponent(); + pluginHolder = nullptr; + } + + //============================================================================== + AudioProcessor* getAudioProcessor() const noexcept { return pluginHolder->processor.get(); } + AudioDeviceManager& getDeviceManager() const noexcept { return pluginHolder->deviceManager; } + + /** Deletes and re-creates the plugin, resetting it to its default state. */ + void resetToDefaultState() + { + pluginHolder->stopPlaying(); + clearContentComponent(); + pluginHolder->deletePlugin(); + + if (auto* props = pluginHolder->settings.get()) + props->removeValue ("filterState"); + + pluginHolder->createPlugin(); + setContentOwned (new MainContentComponent (*this), true); + pluginHolder->startPlaying(); + } + + //============================================================================== + void closeButtonPressed() override + { + pluginHolder->savePluginState(); + + JUCEApplicationBase::quit(); + } + + StringArray getMenuBarNames() override + { +// StringArray menuBarNames; +// menuBarNames.add("Options"); +// return menuBarNames; + const char *menuNames[] = { 0 }; + + return StringArray (menuNames); + } + + PopupMenu getMenuForIndex (int topLevelMenuIndex, const String& menuName) override + { + PopupMenu m; +// m.addItem (1, TRANS("Audio Settings...")); +// m.addSeparator(); + + return m; + } + + void menuItemSelected (int menuItemID, int topLevelMenuIndex) override + { + handleMenuResult(menuItemID); + } + + void menuBarActivated (bool isActive) override {}; + + void buttonClicked (Button*) override + { + pluginHolder->showAudioSettingsDialog(); + + // PopupMenu m; + // m.addItem (1, TRANS("Audio/MIDI Settings...")); + // m.addSeparator(); + // m.addItem (2, TRANS("Save current state...")); + // m.addItem (3, TRANS("Load a saved state...")); + // m.addSeparator(); + // m.addItem (4, TRANS("Reset to default state")); + + // m.showMenuAsync (PopupMenu::Options(),ModalCallbackFunction::forComponent (menuCallback, this)); + } + + void handleMenuResult (int result) + { + switch (result) + { + case 1: pluginHolder->showAudioSettingsDialog(); break; + case 2: pluginHolder->askUserToSaveState(); break; + case 3: pluginHolder->askUserToLoadState(); break; + case 4: resetToDefaultState(); break; + default: break; + } + } + + static void menuCallback (int result, StandaloneFilterWindow* button) + { + if (button != nullptr && result != 0) + button->handleMenuResult (result); + } + + void resized() override + { + DocumentWindow::resized(); + #if ! JUCE_MAC + optionsButton.setBounds (8, 6, 60, getTitleBarHeight() - 8); + #endif + } + + virtual StandalonePluginHolder* getPluginHolder() { return pluginHolder.get(); } + + std::unique_ptr pluginHolder; + MenuBarComponent menuBar; + PopupMenu menu; + +private: + //============================================================================== + class MainContentComponent : public Component, + private Value::Listener, + private Button::Listener, + private ComponentListener + { + public: + MainContentComponent (StandaloneFilterWindow& filterWindow) + : owner (filterWindow), notification (this), + editor (owner.getAudioProcessor()->hasEditor() ? owner.getAudioProcessor()->createEditorIfNeeded() + : new GenericAudioProcessorEditor (*owner.getAudioProcessor())) + { + Value& inputMutedValue = owner.pluginHolder->getMuteInputValue(); + + if (editor != nullptr) + { + editor->addComponentListener (this); + componentMovedOrResized (*editor, false, true); + + addAndMakeVisible (editor.get()); + } + + addChildComponent (notification); + + if (owner.pluginHolder->getProcessorHasPotentialFeedbackLoop()) + { + inputMutedValue.addListener (this); + shouldShowNotification = inputMutedValue.getValue(); + } + + inputMutedChanged (shouldShowNotification); + } + + ~MainContentComponent() override + { + if (editor != nullptr) + { + editor->removeComponentListener (this); + owner.pluginHolder->processor->editorBeingDeleted (editor.get()); + editor = nullptr; + } + } + + void resized() override + { + auto r = getLocalBounds(); + + if (shouldShowNotification) + notification.setBounds (r.removeFromTop (NotificationArea::height)); + + if (editor != nullptr) + editor->setBounds (editor->getLocalArea (this, r) + .withPosition (r.getTopLeft().transformedBy (editor->getTransform().inverted()))); + } + + private: + //============================================================================== + class NotificationArea : public Component + { + public: + enum { height = 30 }; + + NotificationArea (Button::Listener* settingsButtonListener) + : notification ("notification", "Audio input is muted to avoid feedback loop"), + #if JUCE_IOS || JUCE_ANDROID + settingsButton ("Unmute Input") + #else + settingsButton ("Settings...") + #endif + { + setOpaque (true); + + notification.setColour (Label::textColourId, Colours::black); + + settingsButton.addListener (settingsButtonListener); + + addAndMakeVisible (notification); + addAndMakeVisible (settingsButton); + } + + void paint (Graphics& g) override + { + auto r = getLocalBounds(); + + g.setColour (Colours::darkgoldenrod); + g.fillRect (r.removeFromBottom (1)); + + g.setColour (Colours::lightgoldenrodyellow); + g.fillRect (r); + } + + void resized() override + { + auto r = getLocalBounds().reduced (5); + + settingsButton.setBounds (r.removeFromRight (70)); + notification.setBounds (r); + } + private: + Label notification; + TextButton settingsButton; + }; + + //============================================================================== + void inputMutedChanged (bool newInputMutedValue) + { + shouldShowNotification = newInputMutedValue; + notification.setVisible (shouldShowNotification); + + #if JUCE_IOS || JUCE_ANDROID + resized(); + #else + if (editor != nullptr) + { + auto rect = getSizeToContainEditor(); + + setSize (rect.getWidth(), + rect.getHeight() + (shouldShowNotification ? NotificationArea::height : 0)); + } + #endif + } + + void valueChanged (Value& value) override { inputMutedChanged (value.getValue()); } + void buttonClicked (Button*) override + { + #if JUCE_IOS || JUCE_ANDROID + owner.pluginHolder->getMuteInputValue().setValue (false); + #else + owner.pluginHolder->showAudioSettingsDialog(); + #endif + } + + //============================================================================== + void componentMovedOrResized (Component&, bool, bool) override + { + if (editor != nullptr) + { + auto rect = getSizeToContainEditor(); + + setSize (rect.getWidth(), + rect.getHeight() + (shouldShowNotification ? NotificationArea::height : 0)); + } + } + + Rectangle getSizeToContainEditor() const + { + if (editor != nullptr) + return getLocalArea (editor.get(), editor->getLocalBounds()); + + return {}; + } + + //============================================================================== + StandaloneFilterWindow& owner; + NotificationArea notification; + std::unique_ptr editor; + bool shouldShowNotification = false; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent) + }; + + //============================================================================== + #if ! JUCE_MAC + TextButton optionsButton; + #endif + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandaloneFilterWindow) +}; + +inline StandalonePluginHolder* StandalonePluginHolder::getInstance() +{ + #if JucePlugin_Enable_IAA || JucePlugin_Build_Standalone + if (PluginHostType::getPluginLoadedAs() == AudioProcessor::wrapperType_Standalone) + { + auto& desktop = Desktop::getInstance(); + const int numTopLevelWindows = desktop.getNumComponents(); + + for (int i = 0; i < numTopLevelWindows; ++i) + if (auto window = dynamic_cast (desktop.getComponent (i))) + return window->getPluginHolder(); + } + #endif + + return nullptr; +} + +} // namespace juce diff --git a/OB-Xd.jucer b/OB-Xd.jucer index 4246c19..850cf80 100644 --- a/OB-Xd.jucer +++ b/OB-Xd.jucer @@ -1,28 +1,24 @@ - - + - - - - - + @@ -65,118 +61,105 @@ - + - - + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + targetName="OB-Xd" linuxArchitecture="-m64"/> + - - - - - - - - - - - - - + + + + + + + + + + + + + - + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - + - - - + + + - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/README.md b/README.md index 65c0ef6..4ba4042 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,18 @@ # discoDSP OB-Xd -Download binaries at https://www.discodsp.com/obxd/ +![](https://www.discodsp.com/img/obxd.png) # About -While not copying originals, some of the features were taken to a better point. Continuous blendable multimode filter (HP-Notch(BP)-HP in 12 dB mode and 4-1 pole in 24 dB mode). 32 and 64 bit versions included. +OB-Xd is based on the [Oberheim OB-X](https://wikipedia.org/wiki/Oberheim_OB-X). It attempts to recreate its sound and behavior, but as the original was very limited in some important ways a number of things were added or altered to the original design. OB-Xd was designed to sound as good and as rich as the original. It implements micro random detuning which is a big part of that sound. -Thanks to 2Dat for the original OB-Xd and Soshi Studio for giving the rights to continue this wonderful product. -Also thanks to all KVR members for making the amazing skins! +While not copying originals, some of the features were taken to a better point. Continuous blendable multimode filter (HP-Notch(BP)-HP in 12 dB mode and 4-1 pole in 24 dB mode). Also, like many synths of the OB-X's generation, the OB-Xd has no internal effects so its sounds and textures can be greatly enhanced by the use of additional processing like chorus, reverb, delay, etc. -OB-Xd is based on the Oberheim OB-X. It attempts to recreate its sound and behavior, but as the original was very limited in some important ways a number of things were added or altered to the original design. +Thanks to [2Dat](https://github.com/2DaT/Obxd) for the original OB-Xd and Soshi Studio for giving the rights to continue this wonderful product. Also thanks to [KVR artists for creating some amazing skins!](https://www.kvraudio.com/forum/viewtopic.php?f=1&t=471926) -The OB-Xd was designed to sound as good and as rich as the original. It implements micro random detuning which is a big part of that sound. However, it was not designed as a self-contained completely independent soft-synth. It needs to be contained within a VST framework where things like transposition, automation, layering, arpeggiation, etc., are available. +# Binaries -Also, like many synths of the OB-X's generation, the OB-Xd has no internal effects so its sounds and textures can be greatly enhanced by the use of additional processing like chorus, reverb, delay, etc. +Latest binaries can be downloaded at https://www.discodsp.com/obxd/ + +# Building + +Source code can be compiled with [JUCE 5.4.7](https://github.com/juce-framework/JUCE/archive/5.4.7.zip) and VST3 SDK. \ No newline at end of file diff --git a/Source/Engine/ParamsEnum.h b/Source/Engine/ParamsEnum.h index 9cb3eb0..fc5f771 100755 --- a/Source/Engine/ParamsEnum.h +++ b/Source/Engine/ParamsEnum.h @@ -25,7 +25,7 @@ #include "ObxdVoice.h" enum ObxdParameters { - UNDEFINED, + UNDEFINED = 0, MIDILEARN, VOLUME, VOICE_COUNT, diff --git a/Source/Engine/midiMap.h b/Source/Engine/midiMap.h index 9b04699..3a5c311 100755 --- a/Source/Engine/midiMap.h +++ b/Source/Engine/midiMap.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of Obxd synthesizer. - Copyright © 2013-2014 Filatov Vadim + Copyright © 2013-2014 Filatov Vadim Contact author via email : justdat_@_e1.ru @@ -23,13 +23,286 @@ */ #pragma once #include "SynthEngine.h" +#include "ParamsEnum.h" +#include "../JuceLibraryCode/JuceHeader.h" class MidiMap { public: int controllers[255]; + int controllers_default[255]; + + bool loaded = false; MidiMap() { - for(int i = 0 ; i < 255;i++) - controllers[i] = 0; + reset(); + set_default(); } -}; \ No newline at end of file + void reset(){ + for(int i = 0 ; i < 255;i++){ + controllers[i] = 0; + controllers_default[i] = 0; + } + } + + void set_default(){ + int midicc = 71; + controllers[midicc] = controllers_default[midicc] = VOLUME; + + midicc = 15; + controllers[midicc] = controllers_default[midicc] = VOICE_COUNT; + + midicc = 33; + controllers[midicc] = controllers_default[midicc] = TUNE; + + midicc = 17; + controllers[midicc] = controllers_default[midicc] = OCTAVE; + + midicc = 118; + controllers[midicc] = controllers_default[midicc] = BENDRANGE; + + midicc = 34; + controllers[midicc] = controllers_default[midicc] = BENDOSC2; + + midicc = 35; + controllers[midicc] = controllers_default[midicc] = LEGATOMODE; + + midicc = 75; + controllers[midicc] = controllers_default[midicc] = BENDLFORATE; + + midicc = 76; + controllers[midicc] = controllers_default[midicc] = VFLTENV; + + midicc = 20; + controllers[midicc] = controllers_default[midicc] = VAMPENV; + + midicc = 21; + controllers[midicc] = controllers_default[midicc] = ASPLAYEDALLOCATION; + + midicc = 23; + controllers[midicc] = controllers_default[midicc] = PORTAMENTO; + + midicc = 16; + controllers[midicc] = controllers_default[midicc] = UNISON; + + midicc = 24; + controllers[midicc] = controllers_default[midicc] = UDET; + + midicc = 43; + controllers[midicc] = controllers_default[midicc] = OSC2_DET; + + midicc = 19; + controllers[midicc] = controllers_default[midicc] = LFOFREQ; + + midicc = 44; + controllers[midicc] = controllers_default[midicc] = LFOSINWAVE; + + midicc = 45; + controllers[midicc] = controllers_default[midicc] = LFOSQUAREWAVE; + + midicc = 46; + controllers[midicc] = controllers_default[midicc] = LFOSHWAVE; + + midicc = 22; + controllers[midicc] = controllers_default[midicc] = LFO1AMT; + + midicc = 25; + controllers[midicc] = controllers_default[midicc] = LFO2AMT; + + midicc = 47; + controllers[midicc] = controllers_default[midicc] = LFOOSC1; + + midicc = 48; + controllers[midicc] = controllers_default[midicc] = LFOOSC2; + + midicc = 49; + controllers[midicc] = controllers_default[midicc] = LFOFILTER; + + midicc = 50; + controllers[midicc] = controllers_default[midicc] = LFOPW1; + + midicc = 51; + controllers[midicc] = controllers_default[midicc] = LFOPW2; + + midicc = 52; + controllers[midicc] = controllers_default[midicc] = OSC2HS; + + midicc = 53; + controllers[midicc] = controllers_default[midicc] = XMOD; + + midicc = 54; + controllers[midicc] = controllers_default[midicc] = OSC1P; + + midicc = 55; + controllers[midicc] = controllers_default[midicc] = OSC2P; + + midicc = 56; + controllers[midicc] = controllers_default[midicc] = OSCQuantize; + + midicc = 57; + controllers[midicc] = controllers_default[midicc] = OSC1Saw; + + midicc = 58; + controllers[midicc] = controllers_default[midicc] = OSC1Pul; + + midicc = 59; + controllers[midicc] = controllers_default[midicc] = OSC2Saw; + + midicc = 60; + controllers[midicc] = controllers_default[midicc] = OSC2Pul; + + midicc = 61; + controllers[midicc] = controllers_default[midicc] = PW; + + midicc = 62; + controllers[midicc] = controllers_default[midicc] = BRIGHTNESS; + + midicc = 63; + controllers[midicc] = controllers_default[midicc] = ENVPITCH; + + midicc = 100; + controllers[midicc] = controllers_default[midicc] = OSC1MIX; + + midicc = 101; + controllers[midicc] = controllers_default[midicc] = OSC2MIX; + + midicc = 102; + controllers[midicc] = controllers_default[midicc] = NOISEMIX; + + midicc = 103; + controllers[midicc] = controllers_default[midicc] = FLT_KF; + + midicc = 74; + controllers[midicc] = controllers_default[midicc] = CUTOFF; + + midicc = 42; + controllers[midicc] = controllers_default[midicc] = RESONANCE; + + midicc = 104; + controllers[midicc] = controllers_default[midicc] = MULTIMODE; + + midicc = 18; + controllers[midicc] = controllers_default[midicc] = FILTER_WARM; + + midicc = 105; + controllers[midicc] = controllers_default[midicc] = BANDPASS; + + midicc = 106; + controllers[midicc] = controllers_default[midicc] = FOURPOLE; + + midicc = 107; + controllers[midicc] = controllers_default[midicc] = ENVELOPE_AMT; + + midicc = 73; + controllers[midicc] = controllers_default[midicc] = LATK; + + midicc = 36; + controllers[midicc] = controllers_default[midicc] = LDEC; + + midicc = 37; + controllers[midicc] = controllers_default[midicc] = LSUS; + + midicc = 72; + controllers[midicc] = controllers_default[midicc] = LREL; + + midicc = 38; + controllers[midicc] = controllers_default[midicc] = FATK; + + midicc = 39; + controllers[midicc] = controllers_default[midicc] = FDEC; + + midicc = 40; + controllers[midicc] = controllers_default[midicc] = FSUS; + + midicc = 41; + controllers[midicc] = controllers_default[midicc] = FREL; + + midicc = 108; + controllers[midicc] = controllers_default[midicc] = ENVDER; + + midicc = 109; + controllers[midicc] = controllers_default[midicc] = FILTERDER; + + midicc = 110; + controllers[midicc] = controllers_default[midicc] = PORTADER; + + midicc = 81; + controllers[midicc] = controllers_default[midicc] = PAN1; + + midicc = 82; + controllers[midicc] = controllers_default[midicc] = PAN2; + + midicc = 83; + controllers[midicc] = controllers_default[midicc] = PAN3; + + midicc = 84; + controllers[midicc] = controllers_default[midicc] = PAN4; + + midicc = 85; + controllers[midicc] = controllers_default[midicc] = PAN5; + + midicc = 86; + controllers[midicc] = controllers_default[midicc] = PAN6; + + midicc = 87; + controllers[midicc] = controllers_default[midicc] = PAN7; + + midicc = 88; + controllers[midicc] = controllers_default[midicc] = PAN8; + + midicc = 111; + controllers[midicc] = controllers_default[midicc] = ECONOMY_MODE; + + //midicc = 112; + //controllers[midicc] = controllers_default[midicc] = LFO_SYNC; + + midicc = 113; + controllers[midicc] = controllers_default[midicc] = PW_ENV; + + midicc = 114; + controllers[midicc] = controllers_default[midicc] = PW_ENV_BOTH; + + midicc = 115; + controllers[midicc] = controllers_default[midicc] = ENV_PITCH_BOTH; + + midicc = 116; + controllers[midicc] = controllers_default[midicc] = FENV_INVERT; + + midicc = 117; + controllers[midicc] = controllers_default[midicc] = PW_OSC2_OFS; + + midicc = 118; + controllers[midicc] = controllers_default[midicc] = LEVEL_DIF; + + midicc = 119; + controllers[midicc] = controllers_default[midicc] = SELF_OSC_PUSH; + + } + + + int& operator[](int index) + { + if (index >= 255) { + exit(0); + } + return controllers[index]; + } + + void setXml( XmlElement &xml){ + for (int i = 0; i < 255; ++i) + { + xml.setAttribute("Val_" + String(i), controllers[i]); + } + } + + void getXml(XmlElement &xml){ + for (int i = 0; i < 255; ++i) + { + int tmp = xml.getIntAttribute("Val_" + String(i), controllers_default[i]); + if (tmp == 0){ + tmp = controllers_default[i]; + } + + controllers[i] = tmp; + } + } +}; diff --git a/Source/Gui/ButtonList.h b/Source/Gui/ButtonList.h index 5013018..c65ec53 100755 --- a/Source/Gui/ButtonList.h +++ b/Source/Gui/ButtonList.h @@ -23,38 +23,83 @@ */ #pragma once #include "../Source/Engine/SynthEngine.h" -class ButtonList : public ComboBox{ -private : - int count; - Image kni; - int w2,h2; + +class ButtonList : public ComboBox +{ public: - ButtonList(Image k,int fh) :ComboBox("cb") + ButtonList (Image k, int fh) : ComboBox("cb") { kni = k; count = 0; - h2 =fh; + h2 = fh; w2 = k.getWidth(); } //int addItem - void addChoise(String name) + +// Source: https://git.iem.at/audioplugins/IEMPluginSuite/-/blob/master/resources/customComponents/ReverseSlider.h +public: + class ButtonListAttachment : public juce::AudioProcessorValueTreeState::ComboBoxAttachment + { + RangedAudioParameter* parameter = nullptr; + ButtonList* buttonListToControl = nullptr; + public: + ButtonListAttachment (juce::AudioProcessorValueTreeState& stateToControl, + const juce::String& parameterID, + ButtonList& buttonListToControl) : AudioProcessorValueTreeState::ComboBoxAttachment (stateToControl, parameterID, buttonListToControl), buttonListToControl(&buttonListToControl) + { + parameter = stateToControl.getParameter (parameterID); + buttonListToControl.setParameter (parameter); + } + /* + ButtonListAttachment (juce::AudioProcessorValueTreeState& stateToControl, + const juce::String& parameterID, + ComboBox& buttonListToControl) : AudioProcessorValueTreeState::ComboBoxAttachment (stateToControl, parameterID, buttonListToControl) + { + } + */ + void updateToSlider(){ + float val = parameter->getValue(); + //buttonListToControl->setValue(val, NotificationType::dontSendNotification); + //buttonListToControl->setValue(parameter->convertFrom0to1(val0to1), NotificationType::dontSendNotification); + buttonListToControl->setValue(val, NotificationType::dontSendNotification); + } + + virtual ~ButtonListAttachment() = default; + }; + + void setParameter (const AudioProcessorParameter* p) + { + if (parameter == p) + return; + + parameter = p; + repaint(); + } + + void addChoice (String name) { - addItem(name,++count); + addItem (name, ++count); } + float getValue() { - return ((getSelectedId()-1)/ (float)(count-1)); + return ((getSelectedId() - 1) / (float) (count - 1)); } - void setValue(float val,NotificationType notify) + + void setValue (float val, NotificationType notify) { - setSelectedId((int)(val*(count -1) + 1),notify); + setSelectedId ((int) (val * (count - 1) + 1), notify); } - void paintOverChildren(Graphics& g) + + void paintOverChildren (Graphics& g) override { - int ofs = getSelectedId()-1; - g.drawImage(kni, 0, 0, getWidth(), getHeight(), - 0, h2*ofs, w2, h2); + int ofs = getSelectedId() - 1; + g.drawImage(kni, 0, 0, getWidth(), getHeight(), 0, h2 * ofs, w2, h2); } - +private: + int count; + Image kni; + int w2, h2; + const AudioProcessorParameter* parameter {nullptr}; }; diff --git a/Source/Gui/Knob.h b/Source/Gui/Knob.h index b7d465e..6d4663f 100755 --- a/Source/Gui/Knob.h +++ b/Source/Gui/Knob.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of Obxd synthesizer. - Copyright © 2013-2014 Filatov Vadim + Copyright � 2013-2014 Filatov Vadim Contact author via email : justdat_@_e1.ru @@ -23,6 +23,7 @@ */ #pragma once #include "../Source/Engine/SynthEngine.h" + class Knob : public Slider { public: @@ -33,23 +34,66 @@ public: // setSliderStyle(RotaryVerticalDrag); // setRange(0.0f, 1.0f, 0.001f); //} - Knob(Image k,int fh) : Slider("Knob") + Knob (Image k, int fh) : Slider("Knob") { - h2 =fh; + h2 = fh; w2 = k.getWidth(); numFr = k.getHeight() / h2; kni = k; }; - - void paint(Graphics& g) - { - int ofs = (int)((getValue() - getMinimum()) / (getMaximum() - getMinimum()) * (numFr - 1)); - g.drawImage(kni, 0, 0, getWidth(), getHeight(), - 0, h2*ofs, w2, h2); +// Source: https://git.iem.at/audioplugins/IEMPluginSuite/-/blob/master/resources/customComponents/ReverseSlider.h +public: + class KnobAttachment : public juce::AudioProcessorValueTreeState::SliderAttachment + { + RangedAudioParameter* parameter = nullptr; + Knob* sliderToControl = nullptr; + public: + KnobAttachment (juce::AudioProcessorValueTreeState& stateToControl, + const juce::String& parameterID, + Knob& sliderToControl) : AudioProcessorValueTreeState::SliderAttachment (stateToControl, parameterID, sliderToControl), sliderToControl(&sliderToControl) + { + parameter = stateToControl.getParameter (parameterID); + sliderToControl.setParameter (parameter); + } + + + /*KnobAttachment (juce::AudioProcessorValueTreeState& stateToControl, + const juce::String& parameterID, + Slider& sliderToControl) : AudioProcessorValueTreeState::SliderAttachment (stateToControl, parameterID, sliderToControl) + { + }*/ + + void updateToSlider(){ + float val = parameter->getValue(); + //sliderToControl->setValue(parameter->convertFrom0to1(val0to1)); + sliderToControl->setValue(val, NotificationType::dontSendNotification); + DBG(" Slider: " << sliderToControl->getName() << " " << sliderToControl->getValue() << " Parameter: "<< " " << parameter->getValue()); + } + + virtual ~KnobAttachment() = default; + }; + + void setParameter (AudioProcessorParameter* p) + { + if (parameter == p) + return; + + parameter = p; + updateText(); + repaint(); + } + + void paint (Graphics& g) override + { + int ofs = (int) ((getValue() - getMinimum()) / (getMaximum() - getMinimum()) * (numFr - 1)); + g.drawImage (kni, 0, 0, getWidth(), getHeight(), 0, h2 * ofs, w2, h2); } + + ~Knob() override {}; private: Image kni; - int fh,numFr; - int w2,h2; -}; \ No newline at end of file + int fh, numFr; + int w2, h2; + AudioProcessorParameter* parameter {nullptr}; +}; diff --git a/Source/Gui/TooglableButton.h b/Source/Gui/TooglableButton.h index a43b356..af3f055 100755 --- a/Source/Gui/TooglableButton.h +++ b/Source/Gui/TooglableButton.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of Obxd synthesizer. - Copyright © 2013-2014 Filatov Vadim + Copyright � 2013-2014 Filatov Vadim Contact author via email : justdat_@_e1.ru @@ -23,24 +23,64 @@ */ #pragma once #include "../Source/Engine/SynthEngine.h" -class TooglableButton : public ImageButton + +class TooglableButton : public ImageButton { public: - bool toogled; - TooglableButton(Image k) :ImageButton() + TooglableButton (Image k) : ImageButton() { //this->setImages kni = k; toogled = false; width = kni.getWidth(); height = kni.getHeight(); - w2=width; + w2 = width; h2 = height / 2; - this->setClickingTogglesState(true); + this->setClickingTogglesState (true); } - void clicked() + ~TooglableButton() override{ + + }; +// Source: https://git.iem.at/audioplugins/IEMPluginSuite/-/blob/master/resources/customComponents/ReverseSlider.h +public: + class ToggleAttachment : public juce::AudioProcessorValueTreeState::ButtonAttachment + { + RangedAudioParameter* parameter = nullptr; + TooglableButton* buttonToControl = nullptr; + public: + ToggleAttachment (juce::AudioProcessorValueTreeState& stateToControl, + const juce::String& parameterID, + TooglableButton& buttonToControl) : AudioProcessorValueTreeState::ButtonAttachment (stateToControl, parameterID, buttonToControl), buttonToControl(&buttonToControl) + { + parameter = stateToControl.getParameter (parameterID); + buttonToControl.setParameter (parameter); + } + /* + ToggleAttachment (juce::AudioProcessorValueTreeState& stateToControl, + const juce::String& parameterID, + Button& buttonToControl) : AudioProcessorValueTreeState::ButtonAttachment (stateToControl, parameterID, buttonToControl) + { + }*/ + void updateToSlider(){ + float val = parameter->getValue(); + //buttonToControl->setValue(parameter->convertFrom0to1(val0to1), NotificationType::dontSendNotification); + buttonToControl->setValue(val, NotificationType::dontSendNotification); + } + virtual ~ToggleAttachment() = default; + }; + + void setParameter (const AudioProcessorParameter* p) + { + if (parameter == p) + return; + + parameter = p; + repaint(); + } + + void clicked() override { - toogled = !toogled; + toogled = ! toogled; //this->setColour(1,Colours::blue); //if(toogled) // this->setColour(TextButton::ColourIds::buttonColourId,Colours::lightgreen); @@ -50,34 +90,43 @@ public: Button::clicked(); }; - void paintButton(Graphics& g, bool isMouseOverButton, bool isButtonDown) + + void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override { - int offset = 0; + int offset = 0; + if (toogled) { offset = 1; } - g.drawImage(kni, 0, 0, getWidth(), getHeight(), - 0, offset *h2, w2,h2); + + g.drawImage(kni, 0, 0, getWidth(), getHeight(), 0, offset * h2, w2, h2); } - void setValue(float state,int notify) + + void setValue (float state, int notify) { - if(state > 0.5) - toogled = true; + if (state > 0.5) + toogled = true; else toogled = false; + repaint(); } + float getValue() { - if(toogled) - return 1; + if (toogled) + return 1; else return 0; } //void paint(Graphics& g) //{ // g.drawImageTransformed(kni,AffineTransform::rotation(((getValue() - getMinimum())/(getMaximum() - getMinimum()))*float_Pi - float_Pi*2)); //} + + bool toogled; + private: Image kni; - int width,height,w2,h2; -}; \ No newline at end of file + int width, height, w2, h2; + const AudioProcessorParameter* parameter {nullptr}; +}; diff --git a/Source/Images/button.png b/Source/Images/button.png deleted file mode 100644 index 75f0db4..0000000 Binary files a/Source/Images/button.png and /dev/null differ diff --git a/Source/Images/knoblsd.png b/Source/Images/knoblsd.png deleted file mode 100644 index 930a148..0000000 Binary files a/Source/Images/knoblsd.png and /dev/null differ diff --git a/Source/Images/knobssd.png b/Source/Images/knobssd.png deleted file mode 100644 index dc66298..0000000 Binary files a/Source/Images/knobssd.png and /dev/null differ diff --git a/Source/Images/legato.png b/Source/Images/legato.png deleted file mode 100644 index 676e932..0000000 Binary files a/Source/Images/legato.png and /dev/null differ diff --git a/Source/Images/main.png b/Source/Images/main.png old mode 100755 new mode 100644 index a021854..e3c55e1 Binary files a/Source/Images/main.png and b/Source/Images/main.png differ diff --git a/Source/Images/menu.png b/Source/Images/menu.png new file mode 100755 index 0000000..d1fcc71 Binary files /dev/null and b/Source/Images/menu.png differ diff --git a/Source/Images/voices.png b/Source/Images/voices.png deleted file mode 100755 index fdaa2a3..0000000 Binary files a/Source/Images/voices.png and /dev/null differ diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 494be13..a241c73 100755 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -12,725 +12,512 @@ It contains the basic startup code for a Juce application. #include // #include "GUI/BinaryData.h" - //============================================================================== -ObxdAudioProcessorEditor::ObxdAudioProcessorEditor (ObxdAudioProcessor* ownerFilter) - : AudioProcessorEditor (ownerFilter) +ObxdAudioProcessorEditor::ObxdAudioProcessorEditor (ObxdAudioProcessor& ownerFilter) + : AudioProcessorEditor (&ownerFilter), processor (ownerFilter), + skinFolder (processor.getSkinFolder()), + progStart (2000), + bankStart (1000), + skinStart (0), + skins (processor.getSkinFiles()), + banks (processor.getBankFiles()) { - rebuildComponents(); +// skinFolder = ownerFilter.getCurrentSkinFolder(); // initialized above + loadSkin (processor); + repaint(); } +void ObxdAudioProcessorEditor::loadSkin (ObxdAudioProcessor& ownerFilter) +{ + knobAttachments.clear(); + buttonListAttachments.clear(); + toggleAttachments.clear(); + imageButtons.clear(); + popupMenus.clear(); + ownerFilter.removeChangeListener (this); + //File coords("/Users/jimmy/Downloads/coords.xml"); + skinFolder = ownerFilter.getCurrentSkinFolder(); + File coords = skinFolder.getChildFile ("coords.xml"); + bool useClassicSkin = coords.existsAsFile(); + if (!useClassicSkin) { + addMenuButton (14, 25, 20, ImageCache::getFromMemory(BinaryData::menu_png, BinaryData::menu_pngSize)); + rebuildComponents (processor); + return; + } + + XmlDocument skin (coords); + auto doc = skin.getDocumentElement(); + if (doc) { + + if (doc->getTagName() == "PROPERTIES"){ + + forEachXmlChildElementWithTagName(*doc, child, "VALUE"){ + if (child->hasAttribute("NAME") && child->hasAttribute("x") && child->hasAttribute("y")) { + String name = child->getStringAttribute("NAME"); + int x = child->getIntAttribute("x"); + int y = child->getIntAttribute("y"); + int d = child->getIntAttribute("d"); + int w = child->getIntAttribute("w"); + int h = child->getIntAttribute("h"); + + if (name == "guisize"){ setSize (x, y); } + + if (name == "resonanceKnob"){ resonanceKnob = addKnob (x, y, d, ownerFilter, RESONANCE, "Resonance", 0); } + if (name == "cutoffKnob"){ cutoffKnob = addKnob (x, y, d, ownerFilter, CUTOFF, "Cutoff", 0.4); } + if (name == "filterEnvelopeAmtKnob"){ filterEnvelopeAmtKnob = addKnob (x, y, d, ownerFilter, ENVELOPE_AMT, "Envelope", 0); } + if (name == "multimodeKnob"){ multimodeKnob = addKnob (x, y, d, ownerFilter, MULTIMODE, "Multimode", 0.5); } + + if (name == "volumeKnob"){ volumeKnob = addKnob (x, y, d, ownerFilter, VOLUME, "Volume", 0.4); } + if (name == "portamentoKnob"){ portamentoKnob = addKnob (x, y, d, ownerFilter, PORTAMENTO, "Portamento", 0); } + if (name == "osc1PitchKnob"){ osc1PitchKnob = addKnob (x, y, d, ownerFilter, OSC1P, "Osc1Pitch", 0); } + if (name == "pulseWidthKnob"){ pulseWidthKnob = addKnob (x, y, d, ownerFilter, PW, "PW", 0); } + if (name == "osc2PitchKnob"){ osc2PitchKnob = addKnob (x, y, d, ownerFilter, OSC2P, "Osc2Pitch", 0); } + + if (name == "osc1MixKnob"){ osc1MixKnob = addKnob (x, y, d, ownerFilter, OSC1MIX, "Osc1", 1); } + if (name == "osc2MixKnob"){ osc2MixKnob = addKnob (x, y, d, ownerFilter, OSC2MIX, "Osc2", 1); } + if (name == "noiseMixKnob"){ noiseMixKnob = addKnob (x, y, d, ownerFilter, NOISEMIX, "Noise", 0); } + + if (name == "xmodKnob"){ xmodKnob = addKnob (x, y, d, ownerFilter, XMOD, "Xmod", 0); } + if (name == "osc2DetuneKnob"){ osc2DetuneKnob = addKnob (x, y, d, ownerFilter, OSC2_DET, "Detune", 0); } + + if (name == "envPitchModKnob"){ envPitchModKnob = addKnob (x, y, d, ownerFilter, ENVPITCH, "PEnv", 0); } + if (name == "brightnessKnob"){ brightnessKnob = addKnob (x, y, d, ownerFilter, BRIGHTNESS, "Bri", 1); } + + if (name == "attackKnob"){ attackKnob = addKnob (x, y, d, ownerFilter, LATK, "Atk", 0); } + if (name == "decayKnob"){ decayKnob = addKnob (x, y, d, ownerFilter, LDEC, "Dec", 0); } + if (name == "sustainKnob"){ sustainKnob = addKnob (x, y, d, ownerFilter, LSUS, "Sus", 1); } + if (name == "releaseKnob"){ releaseKnob = addKnob (x, y, d, ownerFilter, LREL, "Rel", 0); } + + if (name == "fattackKnob"){ fattackKnob = addKnob (x, y, d, ownerFilter, FATK, "Atk", 0); } + if (name == "fdecayKnob"){ fdecayKnob = addKnob (x, y, d, ownerFilter, FDEC, "Dec", 0); } + if (name == "fsustainKnob"){ fsustainKnob = addKnob (x, y, d, ownerFilter, FSUS, "Sus", 1); } + if (name == "freleaseKnob"){ freleaseKnob = addKnob (x, y, d, ownerFilter, FREL, "Rel", 0); } + + if (name == "lfoFrequencyKnob"){ lfoFrequencyKnob = addKnob (x, y, d, ownerFilter, LFOFREQ, "Freq", 0); } + if (name == "lfoAmt1Knob"){ lfoAmt1Knob = addKnob (x, y, d, ownerFilter, LFO1AMT, "Pitch", 0); } + if (name == "lfoAmt2Knob"){ lfoAmt2Knob = addKnob (x, y, d, ownerFilter, LFO2AMT, "PWM", 0); } + + if (name == "lfoSinButton"){ lfoSinButton = addButton (x, y, w, h, ownerFilter, LFOSINWAVE, "Sin"); } + if (name == "lfoSquareButton"){ lfoSquareButton = addButton (x, y, w, h, ownerFilter, LFOSQUAREWAVE, "SQ"); } + if (name == "lfoSHButton"){ lfoSHButton = addButton (x, y, w, h, ownerFilter, LFOSHWAVE, "S&H"); } + + if (name == "lfoOsc1Button"){ lfoOsc1Button = addButton (x, y, w, h, ownerFilter, LFOOSC1, "Osc1"); } + if (name == "lfoOsc2Button"){ lfoOsc2Button = addButton (x, y, w, h, ownerFilter, LFOOSC2, "Osc2"); } + if (name == "lfoFilterButton"){ lfoFilterButton = addButton (x, y, w, h, ownerFilter, LFOFILTER, "Filt"); } + + if (name == "lfoPwm1Button"){ lfoPwm1Button = addButton (x, y, w, h, ownerFilter, LFOPW1, "Osc1"); } + if (name == "lfoPwm2Button"){ lfoPwm2Button = addButton (x, y, w, h, ownerFilter, LFOPW2, "Osc2"); } + + if (name == "hardSyncButton"){ hardSyncButton = addButton (x, y, w, h, ownerFilter, OSC2HS, "Sync"); } + if (name == "osc1SawButton"){ osc1SawButton = addButton (x, y, w, h, ownerFilter, OSC1Saw, "S"); } + if (name == "osc2SawButton"){ osc2SawButton = addButton (x, y, w, h, ownerFilter, OSC2Saw, "S"); } + + if (name == "osc1PulButton"){ osc1PulButton = addButton (x, y, w, h, ownerFilter, OSC1Pul, "P"); } + if (name == "osc2PulButton"){ osc2PulButton = addButton (x, y, w, h, ownerFilter, OSC2Pul, "P"); } + + if (name == "pitchQuantButton"){ pitchQuantButton = addButton (x, y, w, h, ownerFilter, OSCQuantize, "Step"); } + + if (name == "filterBPBlendButton"){ filterBPBlendButton = addButton (x, y, w, h, ownerFilter, BANDPASS, "Bp"); } + if (name == "fourPoleButton"){ fourPoleButton = addButton (x, y, w, h, ownerFilter, FOURPOLE, "24"); } + if (name == "filterHQButton"){ filterHQButton = addButton (x, y, w, h, ownerFilter, FILTER_WARM, "HQ"); } + + if (name == "filterKeyFollowButton"){ filterKeyFollowButton = addButton (x, y, w, h, ownerFilter, FLT_KF, "Key"); } + if (name == "unisonButton"){ unisonButton = addButton (x, y, w, h, ownerFilter, UNISON, "Uni"); } + + if (name == "tuneKnob"){ tuneKnob = addKnob (x, y, d, ownerFilter, TUNE, "Tune", 0.5); } + if (name == "transposeKnob"){ transposeKnob = addKnob (x, y, d, ownerFilter, OCTAVE, "Transpose", 0.5); } + + if (name == "voiceDetuneKnob"){ voiceDetuneKnob =addKnob (x, y, d, ownerFilter, UDET, "VoiceDet", 0); } + + if (name == "bendLfoRateKnob"){ bendLfoRateKnob = addKnob (x, y, d, ownerFilter, BENDLFORATE, "ModRate", 0.4); } + if (name == "veloFltEnvKnob"){ veloFltEnvKnob = addKnob (x, y, d, ownerFilter, VFLTENV, "VFE", 0); } + if (name == "veloAmpEnvKnob"){ veloAmpEnvKnob = addKnob (x, y, d, ownerFilter, VAMPENV, "VAE", 0); } + if (name == "midiLearnButton"){ midiLearnButton = addButton (x, y, w, h, ownerFilter, MIDILEARN, "LEA"); } + if (name == "midiUnlearnButton"){ midiUnlearnButton = addButton (x, y, w, h, ownerFilter, UNLEARN, "UNL"); } + + if (name == "pan1Knob"){ pan1Knob = addKnob (x, y, d, ownerFilter, PAN1, "1", 0.5); } + if (name == "pan2Knob"){ pan2Knob = addKnob (x, y, d, ownerFilter, PAN2, "2", 0.5); } + if (name == "pan3Knob"){ pan3Knob = addKnob (x, y, d, ownerFilter, PAN3, "3", 0.5); } + if (name == "pan4Knob"){ pan4Knob = addKnob (x, y, d, ownerFilter, PAN4, "4", 0.5); } + + if (name == "pan5Knob"){ pan5Knob = addKnob (x, y, d, ownerFilter, PAN5, "5", 0.5); } + if (name == "pan6Knob"){ pan6Knob = addKnob (x, y, d, ownerFilter, PAN6, "6", 0.5); } + if (name == "pan7Knob"){ pan7Knob = addKnob (x, y, d, ownerFilter, PAN7, "7", 0.5); } + if (name == "pan8Knob"){ pan8Knob = addKnob (x, y, d, ownerFilter, PAN8, "8", 0.5); } + + if (name == "bendOsc2OnlyButton"){ bendOsc2OnlyButton = addButton (x, y, w, h, ownerFilter, BENDOSC2, "Osc2"); } + if (name == "bendRangeButton"){ bendRangeButton = addButton (x, y, w, h, ownerFilter, BENDRANGE, "12"); } + if (name == "asPlayedAllocButton"){ asPlayedAllocButton = addButton (x, y, w, h, ownerFilter, ASPLAYEDALLOCATION, "APA"); } + + if (name == "filterDetuneKnob"){ filterDetuneKnob = addKnob (x, y, d, ownerFilter, FILTERDER, "Flt", 0.2); } + if (name == "portamentoDetuneKnob"){ portamentoDetuneKnob = addKnob (x, y, d, ownerFilter, PORTADER, "Port", 0.2); } + if (name == "envelopeDetuneKnob"){ envelopeDetuneKnob = addKnob (x, y, d, ownerFilter, ENVDER, "Env", 0.2); } + + if (name == "voiceSwitch"){ + //if (voiceSwitch) voiceSwitch->setVisible(false); +#if JUCE_WIN || JUCE_LINUX + voiceSwitch = addList (x, y, w, h, ownerFilter, VOICE_COUNT, "VoiceCount", ImageCache::getFromFile(skinFolder.getChildFile("voices.png"))); } +#else + voiceSwitch = addList (x, y, w, h, ownerFilter, VOICE_COUNT, "VoiceCount", ImageCache::getFromFile(skinFolder.getChildFile("voices@2x.png"))); } +#endif + + if (name == "legatoSwitch"){ + //if (legatoSwitch) legatoSwitch->setVisible(false); +#if JUCE_WIN || JUCE_LINUX + legatoSwitch = addList (x, y, w, h, ownerFilter, LEGATOMODE, "Legato", ImageCache::getFromFile(skinFolder.getChildFile("legato.png"))); } +#else + legatoSwitch = addList (x, y, w, h, ownerFilter, LEGATOMODE, "Legato", ImageCache::getFromFile(skinFolder.getChildFile("legato@2x.png"))); } +#endif + + + if (name == "menu") + { + addMenuButton (x, y, d, + ImageCache::getFromFile (skinFolder.getChildFile ("menu.png"))); + } + + //DBG(" Name: " << name << " X: " <addChoice (String (i)); + } + + auto voiceOption = ownerFilter.getPluginState().getParameter (ownerFilter.getEngineParameterId (VOICE_COUNT))->getValue(); + voiceSwitch->setValue (voiceOption, dontSendNotification); + } + + if (legatoSwitch) + { + legatoSwitch->addChoice ("Keep All"); + legatoSwitch->addChoice ("Keep Filter Envelope"); + legatoSwitch->addChoice ("Keep Amplitude Envelope"); + legatoSwitch->addChoice ("Retrig"); + auto legatoOption = ownerFilter.getPluginState().getParameter (ownerFilter.getEngineParameterId (LEGATOMODE))->getValue(); + legatoSwitch->setValue (legatoOption, dontSendNotification); + } + + createMenu(); + ownerFilter.addChangeListener (this); + repaint(); +} ObxdAudioProcessorEditor::~ObxdAudioProcessorEditor() { - getFilter()->removeChangeListener(this); - - deleteAllChildren(); + processor.removeChangeListener (this); +// deleteAllChildren(); // WATCH OUT! } -void ObxdAudioProcessorEditor::placeLabel(int x , int y , String text) +void ObxdAudioProcessorEditor::placeLabel (int x, int y, String text) { Label* lab = new Label(); - lab->setBounds(x,y,110,20); - lab->setJustificationType(Justification::centred); - lab->setText(text,dontSendNotification);lab->setInterceptsMouseClicks(false,true); - addAndMakeVisible(lab); + lab->setBounds (x, y, 110, 20); + lab->setJustificationType (Justification::centred); + lab->setText (text,dontSendNotification); + lab->setInterceptsMouseClicks (false, true); + addAndMakeVisible (lab); } -ButtonList* ObxdAudioProcessorEditor::addNormalButtonList(int x, int y,int width, ObxdAudioProcessor* filter, int parameter,String name,Image img) +ButtonList* ObxdAudioProcessorEditor::addList (int x, int y, int width, int height, ObxdAudioProcessor& filter, int parameter, String /*name*/, Image img) { - ButtonList *bl = new ButtonList(img,24); - bl->setBounds(x, y, width, 24); - //bl->setValue(filter->getParameter(parameter),dontSendNotification); - addAndMakeVisible(bl); - bl->addListener (this); + ButtonList *bl = new ButtonList (img, height*2); + buttonListAttachments.add (new ButtonList::ButtonListAttachment (filter.getPluginState(), + filter.getEngineParameterId (parameter), + *bl)); + + bl->setBounds (x, y, width, height); + addAndMakeVisible (bl); + return bl; } -Knob* ObxdAudioProcessorEditor::addNormalKnob(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval) +Knob* ObxdAudioProcessorEditor::addKnob (int x, int y, int d, ObxdAudioProcessor& filter, int parameter, String /*name*/, float defval) { - Knob* knob = new Knob(ImageCache::getFromMemory(BinaryData::knoblsd_png,BinaryData::knoblsd_pngSize),48); - //Label* knobl = new Label(); - knob->setSliderStyle(Slider::RotaryVerticalDrag); - knob->setTextBoxStyle(knob->NoTextBox,true,0,0); - knob->setRange(0,1); - addAndMakeVisible(knob); - //addAndMakeVisible(knobl); - knob->setBounds(x, y, 48,48); - knob->setValue(filter->getParameter(parameter),dontSendNotification); - //knobl->setJustificationType(Justification::centred); - //knobl->setInterceptsMouseClicks(false,true); - //knobl->setBounds(x-10,y+40,60,10); - //knobl->setText(name,dontSendNotification); - knob->setTextBoxIsEditable(false); - knob->setDoubleClickReturnValue(true,defval); - knob->addListener (this); +#if JUCE_WIN || JUCE_LINUX + Knob* knob = new Knob (ImageCache::getFromFile(skinFolder.getChildFile("knob.png")), 48); +#else + Knob* knob = new Knob (ImageCache::getFromFile(skinFolder.getChildFile("knob@2x.png")), 96); +#endif + + knobAttachments.add (new Knob::KnobAttachment (filter.getPluginState(), + filter.getEngineParameterId (parameter), + *knob)); + + knob->setSliderStyle (Slider::RotaryVerticalDrag); + knob->setTextBoxStyle (knob->NoTextBox, true, 0, 0); + knob->setRange (0, 1); + knob->setBounds (x, y, d+(d/6), d+(d/6)); + knob->setTextBoxIsEditable (false); + knob->setDoubleClickReturnValue (true, defval); + knob->setValue (filter.getPluginState().getParameter (filter.getEngineParameterId (parameter))->getValue()); + addAndMakeVisible (knob); + return knob; } -Knob* ObxdAudioProcessorEditor::addNormalKnobClassic(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval) + +void ObxdAudioProcessorEditor::clean() { - Knob* knob = new Knob(ImageCache::getFromMemory(BinaryData::knoblsd_png,BinaryData::knoblsd_pngSize),48); - //Label* knobl = new Label(); - knob->setSliderStyle(Slider::RotaryVerticalDrag); - knob->setTextBoxStyle(knob->NoTextBox,true,0,0); - knob->setRange(0,1); - addAndMakeVisible(knob); - //addAndMakeVisible(knobl); - knob->setBounds(x+2, y, 42,42); - knob->setValue(filter->getParameter(parameter),dontSendNotification); - //knobl->setJustificationType(Justification::centred); - //knobl->setInterceptsMouseClicks(false,true); - //knobl->setBounds(x-10,y+40,60,10); - //knobl->setText(name,dontSendNotification); - knob->setTextBoxIsEditable(false); - knob->setDoubleClickReturnValue(true,defval); - knob->addListener (this); - return knob; + this->removeAllChildren(); } -Knob* ObxdAudioProcessorEditor::addTinyKnob(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval) +TooglableButton* ObxdAudioProcessorEditor::addButton (int x, int y, int w, int h, ObxdAudioProcessor& filter, int parameter, String name) { - //Knob* knob = new Knob(ImageCache::getFromMemory(BinaryData::knobssd_png,BinaryData::knobssd_pngSize),42); - Knob* knob = new Knob(ImageCache::getFromMemory(BinaryData::knoblsd_png,BinaryData::knoblsd_pngSize),48); - //Label* knobl = new Label(); - knob->setSliderStyle(Slider::RotaryVerticalDrag); - knob->setTextBoxStyle(knob->NoTextBox,true,0,0); - knob->setRange(0,1); - addAndMakeVisible(knob); - //addAndMakeVisible(knobl); - knob->setBounds(x, y, 36,36); - knob->setValue(filter->getParameter(parameter),dontSendNotification); - //knobl->setJustificationType(Justification::centred); - //knobl->setInterceptsMouseClicks(false,true); - //knobl->setBounds(x-10,y+25,50,10); - //knobl->setText(name,dontSendNotification); - knob->setTextBoxIsEditable(false); - knob->setDoubleClickReturnValue(true,defval); - knob->addListener (this); - return knob; -} +#if JUCE_WIN || JUCE_LINUX + TooglableButton* button = new TooglableButton (ImageCache::getFromFile(skinFolder.getChildFile("button.png"))); +#else + TooglableButton* button = new TooglableButton (ImageCache::getFromFile(skinFolder.getChildFile("button@2x.png"))); +#endif -TooglableButton* ObxdAudioProcessorEditor::addNormalTooglableButton(int x , int y , ObxdAudioProcessor* filter,int parameter,String name) -{ - TooglableButton* button = new TooglableButton(ImageCache::getFromMemory(BinaryData::button_png,BinaryData::button_pngSize)); - // button->setButtonStyle(DrawableButton::ButtonStyle::ImageAboveTextLabel); - addAndMakeVisible(button); - button->setBounds(x,y,19,35); - button->setButtonText(name); - button->setValue(filter->getParameter(parameter),0); - button->addListener(this); - return button; -} - -TooglableButton* ObxdAudioProcessorEditor::addTinyTooglableButton(int x , int y , ObxdAudioProcessor* filter,int parameter,String name) -{ - TooglableButton* button = new TooglableButton(ImageCache::getFromMemory(BinaryData::button_png,BinaryData::button_pngSize)); - // button->setButtonStyle(DrawableButton::ButtonStyle::ImageAboveTextLabel); - addAndMakeVisible(button); - button->setBounds(x,y,20,20); - button->setButtonText(name); - button->setValue(filter->getParameter(parameter),0); - button->addListener(this); - return button; -} - -ButtonList* ObxdAudioProcessorEditor::addNormalButtonListClassic(int x, int y,int width, ObxdAudioProcessor* filter, int parameter,String name,Image img) -{ - ButtonList *bl = new ButtonList(img,32); - bl->setBounds(x, y, width, 32); - //bl->setValue(filter->getParameter(parameter),dontSendNotification); - addAndMakeVisible(bl); - bl->addListener (this); - return bl; -} - -Knob* ObxdAudioProcessorEditor::addTinyKnobClassic(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval) -{ - Knob* knob = new Knob(ImageCache::getFromMemory(BinaryData::knoblsd_png,BinaryData::knoblsd_pngSize),48); - //Label* knobl = new Label(); - knob->setSliderStyle(Slider::RotaryVerticalDrag); - knob->setTextBoxStyle(knob->NoTextBox,true,0,0); - knob->setRange(0,1); - addAndMakeVisible(knob); - //addAndMakeVisible(knobl); - knob->setBounds(x+3, y+3, 36,36); - knob->setValue(filter->getParameter(parameter),dontSendNotification); - //knobl->setJustificationType(Justification::centred); - //knobl->setInterceptsMouseClicks(false,true); - //knobl->setBounds(x-10,y+25,50,10); - //knobl->setText(name,dontSendNotification); - knob->setTextBoxIsEditable(false); - knob->setDoubleClickReturnValue(true,defval); - knob->addListener (this); - return knob; -} - -TooglableButton* ObxdAudioProcessorEditor::addNormalTooglableButtonClassic(int x , int y , ObxdAudioProcessor* filter,int parameter,String name) -{ - TooglableButton* button = new TooglableButton(ImageCache::getFromFile(skinFolder.getChildFile("button.png"))); - // button->setButtonStyle(DrawableButton::ButtonStyle::ImageAboveTextLabel); - addAndMakeVisible(button); - button->setBounds(x,y,28,35); - button->setButtonText(name); - button->setValue(filter->getParameter(parameter),0); + toggleAttachments.add (new TooglableButton::ToggleAttachment (filter.getPluginState(), + filter.getEngineParameterId (parameter), + *button)); + + button->setBounds (x, y, w, h); + button->setButtonText (name); + button->setValue (filter.getPluginState().getParameter (filter.getEngineParameterId (parameter))->getValue(), + dontSendNotification); button->addListener(this); - return button; + addAndMakeVisible (button); + + return button; } -void ObxdAudioProcessorEditor::rebuildComponents() +void ObxdAudioProcessorEditor::addMenuButton (int x, int y, int d, const Image& image) { - ObxdAudioProcessor* ownerFilter = getFilter(); + ImageButton* imageButton; + imageButtons.add (imageButton = new ImageButton()); + imageButton->setBounds (x, y, d, d); + imageButton->setImages (false, + true, + true, + image, + 1.0f, // menu transparency + Colour(), + image, + 1.0f, // menu hover transparency + Colour(), + image, + 0.3f, // menu click transparency + Colour()); + + //imageButton->addListener (this); + imageButton->onClick = [this](){ + ImageButton *imageButton = this->imageButtons[0]; + auto x = imageButton->getScreenX(); + auto y = imageButton->getScreenY(); + auto dx = imageButton->getWidth(); + auto pos = Point (x, y + dx); + resultFromMenu (pos); + }; + addAndMakeVisible (imageButton); +} - skinFolder = ownerFilter->getCurrentSkinFolder(); - bool useClassicSkin = skinFolder.getChildFile("legato.png").existsAsFile(); +void ObxdAudioProcessorEditor::rebuildComponents (ObxdAudioProcessor& ownerFilter) +{ + skinFolder = ownerFilter.getCurrentSkinFolder(); - ownerFilter->removeChangeListener(this); + ownerFilter.removeChangeListener (this); - deleteAllChildren(); - - if (! useClassicSkin) - { - // This is where our plugin's editor size is set. + // deleteAllChildren(); // WATCH OUT! setSize (1440, 450); - cutoffKnob = addNormalKnob(893,77,ownerFilter,CUTOFF,"Cutoff",0.4); - resonanceKnob = addNormalKnob(990,77,ownerFilter,RESONANCE,"Resonance",0); - filterEnvelopeAmtKnob = addNormalKnob(1088,77,ownerFilter,ENVELOPE_AMT,"Envelope",0); - multimodeKnob = addNormalKnob(990,167,ownerFilter,MULTIMODE,"Multimode",0.5); - - volumeKnob = addNormalKnob(56,77,ownerFilter,VOLUME,"Volume",0.4); - portamentoKnob = addNormalKnob(188,77,ownerFilter,PORTAMENTO,"Portamento",0); - osc1PitchKnob = addNormalKnob(593,77,ownerFilter,OSC1P,"Osc1Pitch",0); - pulseWidthKnob = addNormalKnob(691,77,ownerFilter,PW,"PW",0); - osc2PitchKnob = addNormalKnob(788,77,ownerFilter,OSC2P,"Osc2Pitch",0); - - osc1MixKnob = addNormalKnob(597,237,ownerFilter,OSC1MIX,"Osc1",1); - osc2MixKnob = addNormalKnob(788,237,ownerFilter,OSC2MIX,"Osc2",1); - noiseMixKnob = addNormalKnob(691,237,ownerFilter,NOISEMIX,"Noise",0); - - xmodKnob = addNormalKnob(656,324,ownerFilter,XMOD,"Xmod",0); - osc2DetuneKnob = addNormalKnob(800,324,ownerFilter,OSC2_DET,"Detune",0); - - envPitchModKnob = addNormalKnob(728,324,ownerFilter,ENVPITCH,"PEnv",0); - brightnessKnob = addNormalKnob(586,324,ownerFilter,BRIGHTNESS,"Bri",1); - - attackKnob = addNormalKnob(1182,165,ownerFilter,LATK,"Atk",0); - decayKnob = addNormalKnob(1246,165,ownerFilter,LDEC,"Dec",0); - sustainKnob = addNormalKnob(1309,165,ownerFilter,LSUS,"Sus",1); - releaseKnob = addNormalKnob(1373,165,ownerFilter,LREL,"Rel",0); - - fattackKnob = addNormalKnob(1182,75,ownerFilter,FATK,"Atk",0); - fdecayKnob = addNormalKnob(1246,75,ownerFilter,FDEC,"Dec",0); - fsustainKnob = addNormalKnob(1309,75,ownerFilter,FSUS,"Sus",1); - freleaseKnob = addNormalKnob(1373,75,ownerFilter,FREL,"Rel",0); - - lfoFrequencyKnob = addNormalKnob(293,77,ownerFilter,LFOFREQ,"Freq",0); - lfoAmt1Knob = addNormalKnob(390,77,ownerFilter,LFO1AMT,"Pitch",0); - lfoAmt2Knob = addNormalKnob(488,77,ownerFilter,LFO2AMT,"PWM",0); - - lfoSinButton = addNormalTooglableButton(309,162,ownerFilter,LFOSINWAVE,"Sin"); - lfoSquareButton = addNormalTooglableButton(309,252,ownerFilter,LFOSQUAREWAVE,"SQ"); - lfoSHButton = addNormalTooglableButton(309,335,ownerFilter,LFOSHWAVE,"S&H"); - - lfoOsc1Button = addNormalTooglableButton(406,162,ownerFilter,LFOOSC1,"Osc1"); - lfoOsc2Button = addNormalTooglableButton(406,252,ownerFilter,LFOOSC2,"Osc2"); - lfoFilterButton = addNormalTooglableButton(406,335,ownerFilter,LFOFILTER,"Filt"); - - lfoPwm1Button = addNormalTooglableButton(504,162,ownerFilter,LFOPW1,"Osc1"); - lfoPwm2Button = addNormalTooglableButton(504,252,ownerFilter,LFOPW2,"Osc2"); - - hardSyncButton = addNormalTooglableButton(730,162,ownerFilter,OSC2HS,"Sync"); - osc1SawButton = addNormalTooglableButton(587,162,ownerFilter,OSC1Saw,"S"); - osc2SawButton = addNormalTooglableButton(782,162,ownerFilter,OSC2Saw,"S"); - - osc1PulButton = addNormalTooglableButton(632,162,ownerFilter,OSC1Pul,"P"); - osc2PulButton = addNormalTooglableButton(827,162,ownerFilter,OSC2Pul,"P"); - - pitchQuantButton = addNormalTooglableButton(684,162,ownerFilter,OSCQuantize,"Step"); - - filterBPBlendButton = addNormalTooglableButton(1082,162,ownerFilter,BANDPASS,"Bp"); - fourPoleButton = addNormalTooglableButton(1127,162,ownerFilter,FOURPOLE,"24"); - filterHQButton = addNormalTooglableButton(932,162,ownerFilter,FILTER_WARM,"HQ"); - - filterKeyFollowButton = addNormalTooglableButton(887,162,ownerFilter,FLT_KF,"Key"); - unisonButton = addNormalTooglableButton(205,162,ownerFilter,UNISON,"Uni"); - - tuneKnob = addNormalKnob(30,252,ownerFilter,TUNE,"Tune",0.5); - transposeKnob = addNormalKnob(90,252,ownerFilter,OCTAVE,"Transpose",0.5); - - voiceDetuneKnob =addNormalKnob(188,252,ownerFilter,UDET,"VoiceDet",0); - - bendLfoRateKnob = addTinyKnob(928,300,ownerFilter,BENDLFORATE,"ModRate",0.4); - veloFltEnvKnob = addTinyKnob(1013,300,ownerFilter,VFLTENV,"VFE",0); - veloAmpEnvKnob = addTinyKnob(1111,300,ownerFilter,VAMPENV,"VAE",0); - - midiLearnButton = addNormalTooglableButton(74,162,ownerFilter,MIDILEARN,"LEA"); - midiUnlearnButton = addNormalTooglableButton(122,162,ownerFilter,UNLEARN,"UNL"); - - pan1Knob = addTinyKnob(914,368,ownerFilter,PAN1,"1",0.5); - pan2Knob = addTinyKnob(977,368,ownerFilter,PAN2,"2",0.5); - pan3Knob = addTinyKnob(1040,368,ownerFilter,PAN3,"3",0.5); - pan4Knob = addTinyKnob(1103,368,ownerFilter,PAN4,"4",0.5); - - pan5Knob = addTinyKnob(1165,368,ownerFilter,PAN5,"5",0.5); - pan6Knob = addTinyKnob(1228,368,ownerFilter,PAN6,"6",0.5); - pan7Knob = addTinyKnob(1290,368,ownerFilter,PAN7,"7",0.5); - pan8Knob = addTinyKnob(1353,368,ownerFilter,PAN8,"8",0.5); - - bendOsc2OnlyButton = addNormalTooglableButton(228,335,ownerFilter,BENDOSC2,"Osc2"); - bendRangeButton = addNormalTooglableButton(183,335,ownerFilter,BENDRANGE,"12"); - asPlayedAllocButton = addNormalTooglableButton(25,162,ownerFilter,ASPLAYEDALLOCATION,"APA"); - - filterDetuneKnob = addTinyKnob(1228,300,ownerFilter,FILTERDER,"Flt",0.2); - portamentoDetuneKnob = addTinyKnob(1291,300,ownerFilter,PORTADER,"Port",0.2); - envelopeDetuneKnob = addTinyKnob(1353,300,ownerFilter,ENVDER,"Env",0.2); - - voiceSwitch = addNormalButtonList(124,338,17,ownerFilter,VOICE_COUNT,"VoiceCount",ImageCache::getFromMemory(BinaryData::voices_png,BinaryData::voices_pngSize)); - for (int i=1; i <= 32; i++) - voiceSwitch ->addChoise(String(i)); - voiceSwitch ->setValue(ownerFilter->getParameter(VOICE_COUNT),dontSendNotification); - - legatoSwitch = addNormalButtonList(25,338,65,ownerFilter,LEGATOMODE,"Legato",ImageCache::getFromMemory(BinaryData::legato_png,BinaryData::legato_pngSize)); - legatoSwitch ->addChoise("Keep All"); - legatoSwitch ->addChoise("Keep Filter Envelope"); - legatoSwitch ->addChoise("Keep Amplitude Envelope"); - legatoSwitch ->addChoise("Retrig"); - legatoSwitch ->setValue(ownerFilter->getParameter(LEGATOMODE),dontSendNotification); - } - else - { - // This is where our plugin's editor size is set. - - setSize (1087, 442); - cutoffKnob = addNormalKnobClassic(577,40,ownerFilter,CUTOFF,"Cutoff",0.4); - resonanceKnob = addNormalKnobClassic(638,40,ownerFilter,RESONANCE,"Resonance",0); - filterEnvelopeAmtKnob = addNormalKnobClassic(699,40,ownerFilter,ENVELOPE_AMT,"Envelope",0); - multimodeKnob = addTinyKnobClassic(643,106,ownerFilter,MULTIMODE,"Multimode",0.5); - - volumeKnob = addNormalKnobClassic(53,120,ownerFilter,VOLUME,"Volume",0.4); - portamentoKnob = addNormalKnobClassic(175,241,ownerFilter,PORTAMENTO,"Portamento",0); - osc1PitchKnob = addNormalKnobClassic(271,40,ownerFilter,OSC1P,"Osc1Pitch",0); - pulseWidthKnob = addNormalKnobClassic(334,40,ownerFilter,PW,"PW",0); - osc2PitchKnob = addNormalKnobClassic(397,40,ownerFilter,OSC2P,"Osc2Pitch",0); - - osc1MixKnob = addNormalKnobClassic(490,40,ownerFilter,OSC1MIX,"Osc1",1); - osc2MixKnob = addNormalKnobClassic(490,132,ownerFilter,OSC2MIX,"Osc2",1); - noiseMixKnob = addNormalKnobClassic(490,224,ownerFilter,NOISEMIX,"Noise",0); - - xmodKnob = addNormalKnobClassic(334,168,ownerFilter,XMOD,"Xmod",0); - osc2DetuneKnob = addNormalKnobClassic(334,104,ownerFilter,OSC2_DET,"Detune",0); - - envPitchModKnob = addNormalKnobClassic(376,232,ownerFilter,ENVPITCH,"PEnv",0); - brightnessKnob = addNormalKnobClassic(291,232,ownerFilter,BRIGHTNESS,"Bri",1); - - attackKnob = addNormalKnobClassic(791,132,ownerFilter,LATK,"Atk",0); - decayKnob = addNormalKnobClassic(853,132,ownerFilter,LDEC,"Dec",0); - sustainKnob = addNormalKnobClassic(916,132,ownerFilter,LSUS,"Sus",1); - releaseKnob = addNormalKnobClassic(980,132,ownerFilter,LREL,"Rel",0); - - fattackKnob = addNormalKnobClassic(791,40,ownerFilter,FATK,"Atk",0); - fdecayKnob = addNormalKnobClassic(853,40,ownerFilter,FDEC,"Dec",0); - fsustainKnob = addNormalKnobClassic(916,40,ownerFilter,FSUS,"Sus",1); - freleaseKnob = addNormalKnobClassic(980,40,ownerFilter,FREL,"Rel",0); - - lfoFrequencyKnob = addNormalKnobClassic(576,207,ownerFilter,LFOFREQ,"Freq",0); - lfoAmt1Knob = addNormalKnobClassic(640,207,ownerFilter,LFO1AMT,"Pitch",0); - lfoAmt2Knob = addNormalKnobClassic(704,207,ownerFilter,LFO2AMT,"PWM",0); - - lfoSinButton = addNormalTooglableButtonClassic(587,269,ownerFilter,LFOSINWAVE,"Sin"); - lfoSquareButton = addNormalTooglableButtonClassic(587,323,ownerFilter,LFOSQUAREWAVE,"SQ"); - lfoSHButton = addNormalTooglableButtonClassic(587,378,ownerFilter,LFOSHWAVE,"S&H"); - - lfoOsc1Button = addNormalTooglableButtonClassic(651,269,ownerFilter,LFOOSC1,"Osc1"); - lfoOsc2Button = addNormalTooglableButtonClassic(651,323,ownerFilter,LFOOSC2,"Osc2"); - lfoFilterButton = addNormalTooglableButtonClassic(651,378,ownerFilter,LFOFILTER,"Filt"); - - lfoPwm1Button = addNormalTooglableButtonClassic(714,269,ownerFilter,LFOPW1,"Osc1"); - lfoPwm2Button = addNormalTooglableButtonClassic(714,323,ownerFilter,LFOPW2,"Osc2"); - - hardSyncButton = addNormalTooglableButtonClassic(282,178,ownerFilter,OSC2HS,"Sync"); - osc1SawButton = addNormalTooglableButtonClassic(265,114,ownerFilter,OSC1Saw,"S"); - osc2SawButton = addNormalTooglableButtonClassic(394,114,ownerFilter,OSC2Saw,"S"); - - osc1PulButton = addNormalTooglableButtonClassic(296,114,ownerFilter,OSC1Pul,"P"); - osc2PulButton = addNormalTooglableButtonClassic(425,114,ownerFilter,OSC2Pul,"P"); - - pitchQuantButton = addNormalTooglableButtonClassic(407,178,ownerFilter,OSCQuantize,"Step"); - - filterBPBlendButton = addNormalTooglableButtonClassic(697,110,ownerFilter,BANDPASS,"Bp"); - fourPoleButton = addNormalTooglableButtonClassic(728,110,ownerFilter,FOURPOLE,"24"); - filterHQButton = addNormalTooglableButtonClassic(604,110,ownerFilter,FILTER_WARM,"HQ"); - - filterKeyFollowButton = addNormalTooglableButtonClassic(573,110,ownerFilter,FLT_KF,"Key"); - unisonButton = addNormalTooglableButtonClassic(125,251,ownerFilter,UNISON,"Uni"); - tuneKnob = addNormalKnobClassic(114,120,ownerFilter,TUNE,"Tune",0.5); - voiceDetuneKnob =addNormalKnobClassic(53,241,ownerFilter,UDET,"VoiceDet",0); - - veloAmpEnvKnob = addNormalKnobClassic(486,345,ownerFilter,VAMPENV,"VAE",0); - veloFltEnvKnob = addNormalKnobClassic(428,345,ownerFilter,VFLTENV,"VFE",0); - midiLearnButton = addNormalTooglableButtonClassic(126,372,ownerFilter,MIDILEARN,"LEA"); - midiUnlearnButton = addNormalTooglableButtonClassic(185,372,ownerFilter,UNLEARN,"UNL"); - transposeKnob = addNormalKnobClassic(176,120,ownerFilter,OCTAVE,"Transpose",0.5); - - pan1Knob = addTinyKnobClassic(796,318,ownerFilter,PAN1,"1",0.5); - pan2Knob = addTinyKnobClassic(858,318,ownerFilter,PAN2,"2",0.5); - pan3Knob = addTinyKnobClassic(921,318,ownerFilter,PAN3,"3",0.5); - pan4Knob = addTinyKnobClassic(984,318,ownerFilter,PAN4,"4",0.5); - - pan5Knob = addTinyKnobClassic(796,371,ownerFilter,PAN5,"5",0.5); - pan6Knob = addTinyKnobClassic(858,371,ownerFilter,PAN6,"6",0.5); - pan7Knob = addTinyKnobClassic(921,371,ownerFilter,PAN7,"7",0.5); - pan8Knob = addTinyKnobClassic(984,371,ownerFilter,PAN8,"8",0.5); - - bendOsc2OnlyButton = addNormalTooglableButtonClassic(321,354,ownerFilter,BENDOSC2,"Osc2"); - bendRangeButton = addNormalTooglableButtonClassic(267,354,ownerFilter,BENDRANGE,"12"); - asPlayedAllocButton = addNormalTooglableButtonClassic(65,372,ownerFilter,ASPLAYEDALLOCATION,"APA"); - - filterDetuneKnob = addTinyKnobClassic(817,240,ownerFilter,FILTERDER,"Flt",0.2); - envelopeDetuneKnob = addTinyKnobClassic(963,240,ownerFilter,ENVDER,"Env",0.2); - portamentoDetuneKnob = addTinyKnobClassic(890,240,ownerFilter,PORTADER,"Port",0.2); - - bendLfoRateKnob = addNormalKnobClassic(364,345,ownerFilter,BENDLFORATE,"ModRate",0.4); - - voiceSwitch = addNormalButtonListClassic(172,321,38,ownerFilter,VOICE_COUNT,"VoiceCount",ImageCache::getFromFile(skinFolder.getChildFile("voices.png"))); - for (int i=1; i <= 32; i++) - voiceSwitch->addChoise(String(i)); - voiceSwitch ->setValue(ownerFilter->getParameter(VOICE_COUNT),dontSendNotification); - - legatoSwitch = addNormalButtonListClassic(65,321,95,ownerFilter,LEGATOMODE,"Legato",ImageCache::getFromFile(skinFolder.getChildFile("legato.png"))); - legatoSwitch ->addChoise("Keep all"); - legatoSwitch ->addChoise("Keep fenv"); - legatoSwitch ->addChoise("Keep aenv"); - legatoSwitch ->addChoise("Retrig"); - legatoSwitch ->setValue(ownerFilter->getParameter(LEGATOMODE),dontSendNotification); - } - - ownerFilter->addChangeListener(this); + ownerFilter.addChangeListener (this); repaint(); } -void ObxdAudioProcessorEditor::buttonClicked(Button * b) +void ObxdAudioProcessorEditor::createMenu () { - TooglableButton* tb = (TooglableButton*)(b); - ObxdAudioProcessor* flt = getFilter(); -#define bp(T) {flt->setParameterNotifyingHost(T,tb->getValue());} -#define handleBParam(K,T) if (tb == K) {bp(T)} else - handleBParam(hardSyncButton,OSC2HS) - handleBParam(osc1SawButton,OSC1Saw) - handleBParam(osc2SawButton,OSC2Saw) - handleBParam(osc1PulButton,OSC1Pul) - handleBParam(osc2PulButton,OSC2Pul) - handleBParam(filterKeyFollowButton,FLT_KF) - handleBParam(pitchQuantButton,OSCQuantize) - handleBParam(unisonButton,UNISON) - handleBParam(filterHQButton,FILTER_WARM) - handleBParam(filterBPBlendButton,BANDPASS) - - handleBParam(lfoSinButton,LFOSINWAVE) - handleBParam(lfoSquareButton,LFOSQUAREWAVE) - handleBParam(lfoSHButton,LFOSHWAVE) - - handleBParam(lfoOsc1Button,LFOOSC1) - handleBParam(lfoOsc2Button,LFOOSC2) - handleBParam(lfoFilterButton,LFOFILTER) - handleBParam(lfoPwm1Button,LFOPW1) - handleBParam(lfoPwm2Button,LFOPW2) - handleBParam(bendOsc2OnlyButton,BENDOSC2) - handleBParam(bendRangeButton,BENDRANGE) - handleBParam(fourPoleButton,FOURPOLE) - handleBParam(asPlayedAllocButton,ASPLAYEDALLOCATION) - handleBParam(midiLearnButton,MIDILEARN) - handleBParam(midiUnlearnButton,UNLEARN) - {}; + PopupMenu* menu = new PopupMenu(); + PopupMenu progMenu; + PopupMenu bankMenu; + PopupMenu skinMenu; + skins = processor.getSkinFiles(); + banks = processor.getBankFiles(); + + { + for (int i = 0; i < processor.getNumPrograms(); ++i) + { + progMenu.addItem (i + progStart + 1, + processor.getProgramName (i), + true, + i == processor.getCurrentProgram()); + } + + menu->addSubMenu("Programs", progMenu); + } + + { + const String currentBank = processor.getCurrentBankFile().getFileName(); + + for (int i = 0; i < banks.size(); ++i) + { + const File bank = banks.getUnchecked (i); + bankMenu.addItem (i + bankStart + 1, + bank.getFileNameWithoutExtension(), + true, + bank.getFileName() == currentBank); + } + + menu->addSubMenu ("Banks", bankMenu); + } + + { + for (int i = 0; i < skins.size(); ++i) + { + const File skin = skins.getUnchecked (i); + skinMenu.addItem (i + skinStart + 1, + skin.getFileName(), + true, + skin.getFileName() == skinFolder.getFileName()); + } + + menu->addSubMenu ("Themes", skinMenu); + // About // menu.addItem(1, String("Release ") + String(JucePlugin_VersionString).dropLastCharacters(2), false); + } + + popupMenus.add (menu); } -void ObxdAudioProcessorEditor::comboBoxChanged (ComboBox* cb) +void ObxdAudioProcessorEditor::resultFromMenu (const Point pos) { - ButtonList* bl = (ButtonList*)(cb); - ObxdAudioProcessor* flt = getFilter(); - #define cp(T) {flt->setParameterNotifyingHost(T,bl->getValue());} -#define handleCParam(K,T) if (bl == K) {cp(T)} else - handleCParam(voiceSwitch,VOICE_COUNT) - handleCParam(legatoSwitch,LEGATOMODE) - {}; + int result = popupMenus[0]->showAt (Rectangle (pos.getX(), pos.getY(), 1, 1)); + + if (result >= (skinStart + 1) && result <= (skinStart + skins.size())) + { + result -= 1; + result -= skinStart; + + const File newSkinFolder = skins.getUnchecked (result); + processor.setCurrentSkinFolder (newSkinFolder.getFileName()); + + //rebuildComponents (processor); + clean(); + loadSkin (processor); + } + else if (result >= (bankStart + 1) && result <= (bankStart + banks.size())) + { + result -= 1; + result -= bankStart; + + const File bankFile = banks.getUnchecked (result); + processor.loadFromFXBFile (bankFile); + clean(); + loadSkin (processor); + } + else if (result >= (progStart + 1) && result <= (progStart + processor.getNumPrograms())) + { + result -= 1; + result -= progStart; + processor.setCurrentProgram (result); + clean(); + loadSkin (processor); + } } -void ObxdAudioProcessorEditor::sliderValueChanged (Slider* c) +void ObxdAudioProcessorEditor::buttonClicked (Button* b) { - ObxdAudioProcessor* flt = getFilter(); - // flt->beginParameterChangeGesture(); -#define sp(T) {flt->setParameterNotifyingHost(T,c->getValue());} -#define handleSParam(K,T) if (c == K) {sp(T)} else - handleSParam(cutoffKnob,CUTOFF) - handleSParam(resonanceKnob,RESONANCE) - handleSParam(volumeKnob,VOLUME) - handleSParam(osc1PitchKnob,OSC1P) - handleSParam(osc2PitchKnob,OSC2P) - handleSParam(osc2DetuneKnob,OSC2_DET) - handleSParam(portamentoKnob,PORTAMENTO) - handleSParam(filterEnvelopeAmtKnob,ENVELOPE_AMT) - handleSParam(pulseWidthKnob,PW) - handleSParam(xmodKnob,XMOD) - handleSParam(multimodeKnob,MULTIMODE) + /* + auto imageButton = dynamic_cast (b); + + if (imageButton == imageButtons[0]) + { + auto x = imageButton->getScreenX(); + auto y = imageButton->getScreenY(); + auto dx = imageButton->getWidth(); + auto pos = Point (x, y + dx); - handleSParam(attackKnob,LATK) - handleSParam(decayKnob,LDEC) - handleSParam(sustainKnob,LSUS) - handleSParam(releaseKnob,LREL) - - handleSParam(fattackKnob,FATK) - handleSParam(fdecayKnob,FDEC) - handleSParam(fsustainKnob,FSUS) - handleSParam(freleaseKnob,FREL) - - handleSParam(osc1MixKnob,OSC1MIX) - handleSParam(osc2MixKnob,OSC2MIX) - handleSParam(noiseMixKnob,NOISEMIX) - handleSParam(voiceDetuneKnob,UDET) - - handleSParam(filterDetuneKnob,FILTERDER) - handleSParam(envelopeDetuneKnob,ENVDER) - handleSParam(portamentoDetuneKnob,PORTADER) - - handleSParam(lfoFrequencyKnob,LFOFREQ) - handleSParam(lfoAmt1Knob,LFO1AMT) - handleSParam(lfoAmt2Knob,LFO2AMT) - - handleSParam(pan1Knob,PAN1) - handleSParam(pan2Knob,PAN2) - handleSParam(pan3Knob,PAN3) - handleSParam(pan4Knob,PAN4) - handleSParam(pan5Knob,PAN5) - handleSParam(pan6Knob,PAN6) - handleSParam(pan7Knob,PAN7) - handleSParam(pan8Knob,PAN8) - - handleSParam(tuneKnob,TUNE) - handleSParam(brightnessKnob,BRIGHTNESS) - handleSParam(envPitchModKnob,ENVPITCH) - - handleSParam(bendLfoRateKnob,BENDLFORATE) - handleSParam(veloAmpEnvKnob,VAMPENV) - handleSParam(veloFltEnvKnob,VFLTENV) - handleSParam(transposeKnob,OCTAVE) - //magic crystal - {}; - - //else if(c == cutoffKnob) - //{sp(CUTOFF);} - //else if(c == resonanceKnob) - //{sp(RESONANCE);} - //else if(c == portamentoKnob) - //{sp(PORTAMENTO);} - //else if(c == volumeKnob) - //{sp(VOLUME);} - //else if(c == osc1PitchKnob) - //{sp(OSC1P);} - //else if (c == osc2PitchKnob) - //{sp(OSC2P);} + resultFromMenu (pos); + }*/ + + + auto toggleButton = dynamic_cast (b); + if (toggleButton == midiUnlearnButton){ + if (midiUnlearnButton->toogled){ + processor.getMidiMap().reset(); + processor.getMidiMap().set_default(); + processor.sendChangeMessage(); + } + } + } //============================================================================== void ObxdAudioProcessorEditor::changeListenerCallback (ChangeBroadcaster* source) { - ObxdAudioProcessor* filter = getFilter(); - - float pr[PARAM_COUNT]; - filter->getCallbackLock().enter(); - for(int i = 0 ; i < PARAM_COUNT;++i) - pr[i] = filter->getPrograms().currentProgramPtr->values[i]; - filter->getCallbackLock().exit(); -#define rn(T,P) (T->setValue(pr[P],dontSendNotification)); - rn(cutoffKnob,CUTOFF) - rn(resonanceKnob,RESONANCE) - rn(volumeKnob,VOLUME) - rn(osc1PitchKnob,OSC1P) - rn(osc2PitchKnob,OSC2P) - rn(osc2DetuneKnob,OSC2_DET) - rn(portamentoKnob,PORTAMENTO) - rn(filterEnvelopeAmtKnob,ENVELOPE_AMT) - rn(pulseWidthKnob,PW) - rn(xmodKnob,XMOD) - rn(multimodeKnob,MULTIMODE) - rn(brightnessKnob,BRIGHTNESS) - rn(envPitchModKnob,ENVPITCH) - - rn(attackKnob,LATK) - rn(decayKnob,LDEC) - rn(sustainKnob,LSUS) - rn(releaseKnob,LREL) - - rn(fattackKnob,FATK) - rn(fdecayKnob,FDEC) - rn(fsustainKnob,FSUS) - rn(freleaseKnob,FREL) - - rn(osc1MixKnob,OSC1MIX) - rn(osc2MixKnob,OSC2MIX) - rn(noiseMixKnob,NOISEMIX) - rn(voiceDetuneKnob,UDET) - - rn(lfoFrequencyKnob,LFOFREQ) - rn(lfoAmt1Knob,LFO1AMT) - rn(lfoAmt2Knob,LFO2AMT) - rn(tuneKnob,TUNE) - rn(bendLfoRateKnob,BENDLFORATE) - rn(veloAmpEnvKnob,VAMPENV) - rn(veloFltEnvKnob,VFLTENV) - //buttons - rn(hardSyncButton,OSC2HS) - rn(osc1SawButton,OSC1Saw) - rn(osc2SawButton,OSC2Saw) - rn(osc1PulButton,OSC1Pul) - rn(osc2PulButton,OSC2Pul) - - rn(filterKeyFollowButton,FLT_KF) - rn(pitchQuantButton,OSCQuantize) - rn(unisonButton,UNISON) - - rn(filterDetuneKnob,FILTERDER) - rn(envelopeDetuneKnob,ENVDER) - rn(portamentoDetuneKnob,PORTADER) - - rn(filterHQButton,FILTER_WARM) - rn(filterBPBlendButton,BANDPASS) - rn(lfoSinButton,LFOSINWAVE) - rn(lfoSquareButton,LFOSQUAREWAVE) - rn(lfoSHButton,LFOSHWAVE) - - rn(bendOsc2OnlyButton,BENDOSC2) - rn(bendRangeButton,BENDRANGE) - - rn(lfoOsc1Button,LFOOSC1) - rn(lfoOsc2Button,LFOOSC2) - rn(lfoFilterButton,LFOFILTER) - rn(lfoPwm1Button,LFOPW1) - rn(lfoPwm2Button,LFOPW2) - rn(fourPoleButton,FOURPOLE) - - rn(transposeKnob,OCTAVE) - - rn(pan1Knob,PAN1) - rn(pan2Knob,PAN2) - rn(pan3Knob,PAN3) - rn(pan4Knob,PAN4) - rn(pan5Knob,PAN5) - rn(pan6Knob,PAN6) - rn(pan7Knob,PAN7) - rn(pan8Knob,PAN8) - - rn(voiceSwitch,VOICE_COUNT) - rn(legatoSwitch,LEGATOMODE) - rn(asPlayedAllocButton,ASPLAYEDALLOCATION) - rn(midiLearnButton,MIDILEARN) - rn(midiUnlearnButton,UNLEARN) + + for (int i = 0; i < knobAttachments.size(); ++i) + { + knobAttachments[i]->updateToSlider(); + } + + for (int i = 0; i < toggleAttachments.size(); ++i) + { + toggleAttachments[i]->updateToSlider(); + } + + for (int i = 0; i < buttonListAttachments.size(); ++i) + { + buttonListAttachments[i]->updateToSlider(); + } + + // Set to unlearn to false + midiUnlearnButton->setValue(0.0, false); + + repaint(); } -void ObxdAudioProcessorEditor::mouseUp(const MouseEvent& e) +void ObxdAudioProcessorEditor::mouseUp (const MouseEvent& e) { if (e.mods.isRightButtonDown() || e.mods.isCommandDown()) { - PopupMenu menu; - PopupMenu skinMenu; - PopupMenu bankMenu; - PopupMenu progMenu; - - Array skins; - const Array& banks = getFilter()->getBankFiles(); - - int skinStart = 0; - { - DirectoryIterator it(getFilter()->getSkinFolder(), false, "*", File::findDirectories); - while (it.next()) - { - skins.addUsingDefaultSort(it.getFile()); - } - - for (int i = 0; i < skins.size(); ++i) - { - const File skin = skins.getUnchecked(i); - skinMenu.addItem(i + skinStart + 1, skin.getFileName(), true, skin.getFileName() == skinFolder.getFileName()); - } - - menu.addSubMenu("Skins", skinMenu); - } - - int bankStart = 1000; - { - const String currentBank = getFilter()->getCurrentBankFile().getFileName(); - - for (int i = 0; i < banks.size(); ++i) - { - const File bank = banks.getUnchecked(i); - bankMenu.addItem(i + bankStart + 1, bank.getFileNameWithoutExtension(), true, bank.getFileName() == currentBank); - } - - menu.addSubMenu("Banks", bankMenu); - } - - int progStart = 2000; - { - for (int i = 0; i < processor.getNumPrograms(); ++i) - { - progMenu.addItem(i + progStart + 1, processor.getProgramName(i), true, i == processor.getCurrentProgram()); - } - - menu.addSubMenu("Programs", progMenu); - } - - const Point pos = e.getMouseDownScreenPosition(); - - int result = menu.showAt(Rectangle(pos.getX(), pos.getY(), 1, 1)); - if (result >= (skinStart + 1) && result <= (skinStart + skins.size())) - { - result -= 1; - result -= skinStart; - - const File newSkinFolder = skins.getUnchecked(result); - getFilter()->setCurrentSkinFolder(newSkinFolder.getFileName()); - - rebuildComponents(); - } - else if (result >= (bankStart + 1) && result <= (bankStart + banks.size())) - { - result -= 1; - result -= bankStart; - - const File bankFile = banks.getUnchecked(result); - getFilter()->loadFromFXBFile(bankFile); - } - else if (result >= (progStart + 1) && result <= (progStart + processor.getNumPrograms())) - { - result -= 1; - result -= progStart; - processor.setCurrentProgram(result); - } + resultFromMenu (e.getMouseDownScreenPosition()); } } void ObxdAudioProcessorEditor::paint(Graphics& g) { - g.fillAll (Colours::white); - + g.fillAll (Colours::black); +#if JUCE_WIN || JUCE_LINUX const File mainFile(skinFolder.getChildFile("main.png")); - +#else + const File mainFile(skinFolder.getChildFile("main@2x.png")); +#endif + if (skinFolder.exists() && mainFile.exists()) { - const Image image = ImageCache::getFromFile(mainFile); + + const Image image = ImageCache::getFromFile(mainFile); + +#if JUCE_WIN || JUCE_LINUX + g.drawImage (image, + 0, 0, image.getWidth(), image.getHeight(), + 0, 0, image.getWidth(), image.getHeight()); +#else + g.drawImage (image, + 0, 0, image.getWidth()/2, image.getHeight()/2, + 0, 0, image.getWidth(), image.getHeight()); +#endif - g.drawImage (image, - 0, 0, image.getWidth(), image.getHeight(), - 0, 0, image.getWidth(), image.getHeight()); } else { const Image image = ImageCache::getFromMemory(BinaryData::main_png, BinaryData::main_pngSize); - + + // g.setImageResamplingQuality(Graphics::ResamplingQuality::highResamplingQuality); + g.drawImage (image, 0, 0, image.getWidth(), image.getHeight(), 0, 0, image.getWidth(), image.getHeight()); } + } diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 100f11c..7b3b9bb 100755 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -22,67 +22,130 @@ //============================================================================== /** */ -class ObxdAudioProcessorEditor : - public AudioProcessorEditor, - // public AudioProcessorListener, - public ChangeListener, - public Slider::Listener, - public Button::Listener, - public ComboBox::Listener +class ObxdAudioProcessorEditor : public AudioProcessorEditor +// , public AudioProcessorListener + , public ChangeListener +// , public Slider::Listener + , public Button::Listener +// , public ComboBox::Listener { public: - ObxdAudioProcessorEditor(ObxdAudioProcessor* ownerFilter); + ObxdAudioProcessorEditor(ObxdAudioProcessor& ownerFilter); ~ObxdAudioProcessorEditor(); - - void mouseUp(const MouseEvent& e); - void paint(Graphics& g); - + + + void mouseUp (const MouseEvent& e) override; + void paint (Graphics& g) override; + //============================================================================== - void changeListenerCallback (ChangeBroadcaster* source); + void changeListenerCallback (ChangeBroadcaster* source) override; + void buttonClicked (Button *) override; private: - Knob* addNormalKnob(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval); - Knob* addTinyKnob(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval); - void placeLabel(int x , int y,String text); - TooglableButton* addNormalTooglableButton(int x , int y , ObxdAudioProcessor* filter,int parameter,String name); - TooglableButton* addTinyTooglableButton(int x , int y , ObxdAudioProcessor* filter,int parameter,String name); - - ButtonList* addNormalButtonList(int x , int y ,int width, ObxdAudioProcessor* filter,int parameter,String name,Image img); - void sliderValueChanged (Slider*); - void buttonClicked (Button *); - void comboBoxChanged(ComboBox*); - - Knob* addNormalKnobClassic(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval); - Knob* addTinyKnobClassic(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval); - TooglableButton* addNormalTooglableButtonClassic(int x , int y , ObxdAudioProcessor* filter,int parameter,String name); - ButtonList* addNormalButtonListClassic(int x , int y ,int width, ObxdAudioProcessor* filter,int parameter,String name,Image img); - - void rebuildComponents(); + Knob* addKnob (int x, int y, int d, ObxdAudioProcessor& filter, int parameter, String name, float defval); + void placeLabel (int x, int y, String text); + TooglableButton* addButton (int x, int y, int w, int h, ObxdAudioProcessor& filter, int parameter, String name); + ButtonList* addList(int x, int y, int w, int h, ObxdAudioProcessor& filter, int parameter, String name, Image img); + void addMenuButton (int x, int y, int d, const Image&); + void createMenu (); + void resultFromMenu (const Point); + void clean(); + + void rebuildComponents (ObxdAudioProcessor&); + void loadSkin(ObxdAudioProcessor&); + //============================================================================== + ObxdAudioProcessor& processor; //============================================================================== - ObxdAudioProcessor* getFilter() noexcept { return (ObxdAudioProcessor*)getAudioProcessor();} + Knob* cutoffKnob=nullptr, + *resonanceKnob=nullptr, + *osc1PitchKnob=nullptr, + *osc2PitchKnob=nullptr, + *osc2DetuneKnob=nullptr, + *volumeKnob=nullptr, + *portamentoKnob=nullptr, + *voiceDetuneKnob=nullptr, + *filterEnvelopeAmtKnob=nullptr, + *pulseWidthKnob=nullptr, + *xmodKnob=nullptr, + *multimodeKnob=nullptr, + *attackKnob=nullptr, + *decayKnob=nullptr, + *sustainKnob=nullptr, + *releaseKnob=nullptr, + *fattackKnob=nullptr, + *fdecayKnob=nullptr, + *fsustainKnob=nullptr, + *freleaseKnob=nullptr, + *osc1MixKnob=nullptr, + *osc2MixKnob=nullptr, + *noiseMixKnob=nullptr, + *filterDetuneKnob=nullptr, + *envelopeDetuneKnob=nullptr, + *portamentoDetuneKnob=nullptr, + *tuneKnob=nullptr, + *lfoFrequencyKnob=nullptr, + *lfoAmt1Knob=nullptr, + *lfoAmt2Knob=nullptr, + *pan1Knob=nullptr, + *pan2Knob=nullptr, + *pan3Knob=nullptr, + *pan4Knob=nullptr, + *pan5Knob=nullptr, + *pan6Knob=nullptr, + *pan7Knob=nullptr, + *pan8Knob=nullptr, + *brightnessKnob=nullptr, + *envPitchModKnob=nullptr, + *bendLfoRateKnob=nullptr, + *veloAmpEnvKnob=nullptr, + *veloFltEnvKnob=nullptr, + *transposeKnob=nullptr; - //============================================================================== - Knob* cutoffKnob,*resonanceKnob,*osc1PitchKnob,*osc2PitchKnob,*osc2DetuneKnob,*volumeKnob, - *portamentoKnob,*voiceDetuneKnob,*filterEnvelopeAmtKnob,*pulseWidthKnob,*xmodKnob,*multimodeKnob,*attackKnob,*decayKnob,*sustainKnob,*releaseKnob, - *fattackKnob,*fdecayKnob,*fsustainKnob,*freleaseKnob,*osc1MixKnob,*osc2MixKnob,*noiseMixKnob, - *filterDetuneKnob,*envelopeDetuneKnob,*portamentoDetuneKnob, - *tuneKnob, - *lfoFrequencyKnob,*lfoAmt1Knob,*lfoAmt2Knob, - *pan1Knob,*pan2Knob,*pan3Knob,*pan4Knob,*pan5Knob,*pan6Knob,*pan7Knob,*pan8Knob, - *brightnessKnob,*envPitchModKnob, - *bendLfoRateKnob,*veloAmpEnvKnob,*veloFltEnvKnob,*transposeKnob; + TooglableButton* hardSyncButton=nullptr, + *osc1SawButton=nullptr, + *osc2SawButton=nullptr, + *osc1PulButton=nullptr, + *osc2PulButton=nullptr, + *filterKeyFollowButton=nullptr, + *unisonButton=nullptr, + *pitchQuantButton=nullptr, + *filterHQButton=nullptr, + *filterBPBlendButton=nullptr, + *lfoSinButton=nullptr, + *lfoSquareButton=nullptr, + *lfoSHButton=nullptr, + *lfoOsc1Button=nullptr, + *lfoOsc2Button=nullptr, + *lfoFilterButton=nullptr, + *lfoPwm1Button=nullptr, + *lfoPwm2Button=nullptr, + *bendRangeButton=nullptr, + *bendOsc2OnlyButton=nullptr, + *fourPoleButton=nullptr, + *asPlayedAllocButton=nullptr, + *midiLearnButton=nullptr, + *midiUnlearnButton=nullptr; - TooglableButton* hardSyncButton,*osc1SawButton,*osc2SawButton,*osc1PulButton,*osc2PulButton,*filterKeyFollowButton,*unisonButton,*pitchQuantButton, - *filterHQButton,*filterBPBlendButton, - *lfoSinButton,*lfoSquareButton,*lfoSHButton,*lfoOsc1Button,*lfoOsc2Button,*lfoFilterButton, - *lfoPwm1Button,*lfoPwm2Button, - *bendRangeButton,*bendOsc2OnlyButton, - *fourPoleButton,*asPlayedAllocButton,*midiLearnButton,*midiUnlearnButton; - - ButtonList *voiceSwitch,*legatoSwitch; + ButtonList *voiceSwitch = nullptr, + *legatoSwitch = nullptr; File skinFolder; + + //============================================================================== + OwnedArray knobAttachments; + OwnedArray toggleAttachments; + OwnedArray buttonListAttachments; + + OwnedArray imageButtons; + + OwnedArray popupMenus; + + int progStart; + int bankStart; + int skinStart; + Array skins; + Array banks; }; #endif // PLUGINEDITOR_H_INCLUDED diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 166cf82..5b1f1f5 100755 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -27,36 +27,68 @@ It contains the basic startup code for a Juce application. //============================================================================== #define S(T) (juce::String(T)) +//============================================================================== +AudioProcessorValueTreeState::ParameterLayout createParameterLayout() +{ + ObxdParams defaultParams; + std::vector> params; + + for (int i = 0; i < PARAM_COUNT; ++i) + { + auto id = ObxdAudioProcessor::getEngineParameterId (i); + auto name = TRANS (id); + auto range = NormalisableRange {0.0f, 1.0f}; + auto defaultValue = defaultParams.values[i]; + auto parameter = std::make_unique (id, + name, + range, + defaultValue); + + params.push_back (std::move (parameter)); + } + + return { params.begin(), params.end() }; +} + //============================================================================== ObxdAudioProcessor::ObxdAudioProcessor() : bindings() , programs() , configLock("__" JucePlugin_Name "ConfigLock__") + , apvtState (*this, &undoManager, "PARAMETERS", createParameterLayout()) { isHostAutomatedChange = true; midiControlledParamSet = false; lastMovedController = 0; lastUsedParameter = 0; - synth.setSampleRate(44100); + synth.setSampleRate (44100); PropertiesFile::Options options; options.applicationName = JucePlugin_Name; options.storageFormat = PropertiesFile::storeAsXML; options.millisecondsBeforeSaving = 2500; options.processLock = &configLock; - config = new PropertiesFile(getDocumentFolder().getChildFile("Settings.xml"), options); + config = std::unique_ptr (new PropertiesFile (getDocumentFolder().getChildFile ("Settings.xml"), options)); currentSkin = config->containsKey("skin") ? config->getValue("skin") : "discoDSP Blue"; currentBank = "Init"; scanAndUpdateBanks(); - initAllParams(); + scanAndUpdateSkins(); + initAllParams(); if (bankFiles.size() > 0) { - loadFromFXBFile(bankFiles[0]); + loadFromFXBFile (bankFiles[0]); } + + for (int i = 0; i < PARAM_COUNT; ++i) + { + apvtState.addParameterListener (getEngineParameterId (i), this); + } + + apvtState.state = ValueTree (JucePlugin_Name); } ObxdAudioProcessor::~ObxdAudioProcessor() @@ -68,438 +100,10 @@ ObxdAudioProcessor::~ObxdAudioProcessor() //============================================================================== void ObxdAudioProcessor::initAllParams() { - for (int i = 0 ; i < PARAM_COUNT; i++) - { - setParameter(i, programs.currentProgramPtr->values[i]); - } -} - -//============================================================================== -int ObxdAudioProcessor::getNumParameters() -{ - return PARAM_COUNT; -} - -float ObxdAudioProcessor::getParameter (int index) -{ - return programs.currentProgramPtr->values[index]; -} - -void ObxdAudioProcessor::setParameter (int index, float newValue) -{ - if(!midiControlledParamSet || index==MIDILEARN || index==UNLEARN) - lastUsedParameter = index; - programs.currentProgramPtr->values[index] = newValue; - switch(index) - { - case SELF_OSC_PUSH: - synth.processSelfOscPush(newValue); - break; - case PW_ENV_BOTH: - synth.processPwEnvBoth(newValue); - break; - case PW_OSC2_OFS: - synth.processPwOfs(newValue); - break; - case ENV_PITCH_BOTH: - synth.processPitchModBoth(newValue); - break; - case FENV_INVERT: - synth.processInvertFenv(newValue); - break; - case LEVEL_DIF: - synth.processLoudnessDetune(newValue); - break; - case PW_ENV: - synth.processPwEnv(newValue); - break; - case LFO_SYNC: - synth.procLfoSync(newValue); - break; - case ECONOMY_MODE: - synth.procEconomyMode(newValue); - break; - case VAMPENV: - synth.procAmpVelocityAmount(newValue); - break; - case VFLTENV: - synth.procFltVelocityAmount(newValue); - break; - case ASPLAYEDALLOCATION: - synth.procAsPlayedAlloc(newValue); - break; - case BENDLFORATE: - synth.procModWheelFrequency(newValue); - break; - case FOURPOLE: - synth.processFourPole(newValue); - break; - case LEGATOMODE: - synth.processLegatoMode(newValue); - break; - case ENVPITCH: - synth.processEnvelopeToPitch(newValue); - break; - case OSCQuantize: - synth.processPitchQuantization(newValue); - break; - case VOICE_COUNT: - synth.setVoiceCount(newValue); - break; - case BANDPASS: - synth.processBandpassSw(newValue); - break; - case FILTER_WARM: - synth.processOversampling(newValue); - break; - case BENDOSC2: - synth.procPitchWheelOsc2Only(newValue); - break; - case BENDRANGE: - synth.procPitchWheelAmount(newValue); - break; - case NOISEMIX: - synth.processNoiseMix(newValue); - break; - case OCTAVE: - synth.processOctave(newValue); - break; - case TUNE: - synth.processTune(newValue); - break; - case BRIGHTNESS: - synth.processBrightness(newValue); - break; - case MULTIMODE: - synth.processMultimode(newValue); - break; - case LFOFREQ: - synth.processLfoFrequency(newValue); - break; - case LFO1AMT: - synth.processLfoAmt1(newValue); - break; - case LFO2AMT: - synth.processLfoAmt2(newValue); - break; - case LFOSINWAVE: - synth.processLfoSine(newValue); - break; - case LFOSQUAREWAVE: - synth.processLfoSquare(newValue); - break; - case LFOSHWAVE: - synth.processLfoSH(newValue); - break; - case LFOFILTER: - synth.processLfoFilter(newValue); - break; - case LFOOSC1: - synth.processLfoOsc1(newValue); - break; - case LFOOSC2: - synth.processLfoOsc2(newValue); - break; - case LFOPW1: - synth.processLfoPw1(newValue); - break; - case LFOPW2: - synth.processLfoPw2(newValue); - break; - case PORTADER: - synth.processPortamentoDetune(newValue); - break; - case FILTERDER: - synth.processFilterDetune(newValue); - break; - case ENVDER: - synth.processEnvelopeDetune(newValue); - break; - case XMOD: - synth.processOsc2Xmod(newValue); - break; - case OSC2HS: - synth.processOsc2HardSync(newValue); - break; - case OSC2P: - synth.processOsc2Pitch(newValue); - break; - case OSC1P: - synth.processOsc1Pitch(newValue); - break; - case PORTAMENTO: - synth.processPortamento(newValue); - break; - case UNISON: - synth.processUnison(newValue); - break; - case FLT_KF: - synth.processFilterKeyFollow(newValue); - break; - case OSC1MIX: - synth.processOsc1Mix(newValue); - break; - case OSC2MIX: - synth.processOsc2Mix(newValue); - break; - case PW: - synth.processPulseWidth(newValue); - break; - case OSC1Saw: - synth.processOsc1Saw(newValue); - break; - case OSC2Saw: - synth.processOsc2Saw(newValue); - break; - case OSC1Pul: - synth.processOsc1Pulse(newValue); - break; - case OSC2Pul: - synth.processOsc2Pulse(newValue); - break; - case VOLUME: - synth.processVolume(newValue); - break; - case UDET: - synth.processDetune(newValue); - break; - case OSC2_DET: - synth.processOsc2Det(newValue); - break; - case CUTOFF: - synth.processCutoff(newValue); - break; - case RESONANCE: - synth.processResonance(newValue); - break; - case ENVELOPE_AMT: - synth.processFilterEnvelopeAmt(newValue); - break; - case LATK: - synth.processLoudnessEnvelopeAttack(newValue); - break; - case LDEC: - synth.processLoudnessEnvelopeDecay(newValue); - break; - case LSUS: - synth.processLoudnessEnvelopeSustain(newValue); - break; - case LREL: - synth.processLoudnessEnvelopeRelease(newValue); - break; - case FATK: - synth.processFilterEnvelopeAttack(newValue); - break; - case FDEC: - synth.processFilterEnvelopeDecay(newValue); - break; - case FSUS: - synth.processFilterEnvelopeSustain(newValue); - break; - case FREL: - synth.processFilterEnvelopeRelease(newValue); - break; - case PAN1: - synth.processPan(newValue,1); - break; - case PAN2: - synth.processPan(newValue,2); - break; - case PAN3: - synth.processPan(newValue,3); - break; - case PAN4: - synth.processPan(newValue,4); - break; - case PAN5: - synth.processPan(newValue,5); - break; - case PAN6: - synth.processPan(newValue,6); - break; - case PAN7: - synth.processPan(newValue,7); - break; - case PAN8: - synth.processPan(newValue,8); - break; - } - //DIRTY HACK - //This should be checked to avoid stalling on gui update - //It is needed because some hosts do wierd stuff - if(isHostAutomatedChange) - sendChangeMessage(); -} - -const String ObxdAudioProcessor::getParameterName (int index) -{ - switch(index) - { - case SELF_OSC_PUSH: - return S("SelfOscPush"); - case ENV_PITCH_BOTH: - return S("EnvPitchBoth"); - case FENV_INVERT: - return S("FenvInvert"); - case PW_OSC2_OFS: - return S("PwOfs"); - case LEVEL_DIF: - return S("LevelDif"); - case PW_ENV_BOTH: - return S("PwEnvBoth"); - case PW_ENV: - return S("PwEnv"); - case LFO_SYNC: - return S("LfoSync"); - case ECONOMY_MODE: - return S("EconomyMode"); - case UNLEARN: - return S("MidiUnlearn"); - case MIDILEARN: - return S("MidiLearn"); - case VAMPENV: - return S("VAmpFactor"); - case VFLTENV: - return S("VFltFactor"); - case ASPLAYEDALLOCATION: - return S("AsPlayedAllocation"); - case BENDLFORATE: - return S("VibratoRate"); - case FOURPOLE: - return S("FourPole"); - case LEGATOMODE: - return S("LegatoMode"); - case ENVPITCH: - return S("EnvelopeToPitch"); - case OSCQuantize: - return S("PitchQuant"); - case VOICE_COUNT: - return S("VoiceCount"); - case BANDPASS: - return S("BandpassBlend"); - case FILTER_WARM: - return S("Filter_Warm"); - case BENDRANGE: - return S("BendRange"); - case BENDOSC2: - return S("BendOsc2Only"); - case OCTAVE: - return S("Octave"); - case TUNE: - return S("Tune"); - case BRIGHTNESS: - return S("Brightness"); - case NOISEMIX: - return S("NoiseMix"); - case OSC1MIX: - return S("Osc1Mix"); - case OSC2MIX: - return S("Osc2Mix"); - case MULTIMODE: - return S("Multimode"); - case LFOSHWAVE: - return S("LfoSampleHoldWave"); - case LFOSINWAVE: - return S("LfoSineWave"); - case LFOSQUAREWAVE: - return S("LfoSquareWave"); - case LFO1AMT: - return S("LfoAmount1"); - case LFO2AMT: - return S("LfoAmount2"); - case LFOFILTER: - return S("LfoFilter"); - case LFOOSC1: - return S("LfoOsc1"); - case LFOOSC2: - return S("LfoOsc2"); - case LFOFREQ: - return S("LfoFrequency"); - case LFOPW1: - return S("LfoPw1"); - case LFOPW2: - return S("LfoPw2"); - case PORTADER: - return S("PortamentoDetune"); - case FILTERDER: - return S("FilterDetune"); - case ENVDER: - return S("EnvelopeDetune"); - case PAN1: - return S("Pan1"); - case PAN2: - return S("Pan2"); - case PAN3: - return S("Pan3"); - case PAN4: - return S("Pan4"); - case PAN5: - return S("Pan5"); - case PAN6: - return S("Pan6"); - case PAN7: - return S("Pan7"); - case PAN8: - return S("Pan8"); - case XMOD: - return S("Xmod"); - case OSC2HS: - return S("Osc2HardSync"); - case OSC1P: - return S("Osc1Pitch"); - case OSC2P: - return S("Osc2Pitch"); - case PORTAMENTO: - return S("Portamento"); - case UNISON: - return S("Unison"); - case FLT_KF: - return S("FilterKeyFollow"); - case PW: - return S("PulseWidth"); - case OSC2Saw: - return S("Osc2Saw"); - case OSC1Saw: - return S("Osc1Saw"); - case OSC1Pul: - return S("Osc1Pulse"); - case OSC2Pul: - return S("Osc2Pulse"); - case VOLUME: - return S("Volume"); - case UDET: - return S("VoiceDetune"); - case OSC2_DET: - return S("Oscillator2detune"); - case CUTOFF: - return S("Cutoff"); - case RESONANCE: - return S("Resonance"); - case ENVELOPE_AMT: - return S("FilterEnvAmount"); - case LATK: - return S("Attack"); - case LDEC: - return S("Decay"); - case LSUS: - return S("Sustain"); - case LREL: - return S("Release"); - case FATK: - return S("FilterAttack"); - case FDEC: - return S("FilterDecay"); - case FSUS: - return S("FilterSustain"); - case FREL: - return S("FilterRelease"); - } - return {}; -} - -const String ObxdAudioProcessor::getParameterText (int index) -{ - return String(programs.currentProgramPtr->values[index],2); + for (int i = 0; i < PARAM_COUNT; ++i) + { + setEngineParameterValue (i, programs.currentProgramPtr->values[i]); + } } //============================================================================== @@ -546,11 +150,6 @@ bool ObxdAudioProcessor::producesMidi() const #endif } -bool ObxdAudioProcessor::silenceInProducesSilenceOut() const -{ - return false; -} - double ObxdAudioProcessor::getTailLengthSeconds() const { return 0.0; @@ -572,8 +171,10 @@ void ObxdAudioProcessor::setCurrentProgram (int index) programs.currentProgram = index; programs.currentProgramPtr = programs.programs + programs.currentProgram; isHostAutomatedChange = false; - for(int i = 0 ; i < PARAM_COUNT;i++) - setParameter(i,programs.currentProgramPtr->values[i]); + + for (int i = 0; i < PARAM_COUNT; ++i) + setEngineParameterValue (i, programs.currentProgramPtr->values[i]); + isHostAutomatedChange = true; sendChangeMessage(); updateHostDisplay(); @@ -590,59 +191,60 @@ void ObxdAudioProcessor::changeProgramName (int index, const String& newName) } //============================================================================== -void ObxdAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) +void ObxdAudioProcessor::prepareToPlay (double sampleRate, int /*samplesPerBlock*/) { // Use this method as the place to do any pre-playback // initialisation that you need.. - nextMidi= new MidiMessage(0xF0); - midiMsg = new MidiMessage(0xF0); - synth.setSampleRate(sampleRate); + nextMidi = new MidiMessage (0xF0); + midiMsg = new MidiMessage (0xF0); + synth.setSampleRate (sampleRate); } void ObxdAudioProcessor::releaseResources() { - } -inline void ObxdAudioProcessor::processMidiPerSample(MidiBuffer::Iterator* iter,const int samplePos) +inline void ObxdAudioProcessor::processMidiPerSample (MidiBuffer::Iterator* iter, const int samplePos) { - while (getNextEvent(iter, samplePos)) + while (getNextEvent (iter, samplePos)) { - if(midiMsg->isNoteOn()) + if (midiMsg->isNoteOn()) { - synth.procNoteOn(midiMsg->getNoteNumber(),midiMsg->getFloatVelocity()); + synth.procNoteOn (midiMsg->getNoteNumber(), midiMsg->getFloatVelocity()); } if (midiMsg->isNoteOff()) { - synth.procNoteOff(midiMsg->getNoteNumber()); + synth.procNoteOff (midiMsg->getNoteNumber()); } - if(midiMsg->isPitchWheel()) + if (midiMsg->isPitchWheel()) { // [0..16383] center = 8192; - synth.procPitchWheel((midiMsg->getPitchWheelValue()-8192) / 8192.0); + synth.procPitchWheel ((midiMsg->getPitchWheelValue() - 8192) / 8192.0f); } - if(midiMsg->isController() && midiMsg->getControllerNumber()==1) - synth.procModWheel(midiMsg->getControllerValue() / 127.0); - if(midiMsg->isController()) + if (midiMsg->isController() && midiMsg->getControllerNumber() == 1) + { + synth.procModWheel (midiMsg->getControllerValue() / 127.0f); + } + if (midiMsg->isController()) { lastMovedController = midiMsg->getControllerNumber(); - if(programs.currentProgramPtr->values[MIDILEARN] > 0.5) - bindings.controllers[lastMovedController] = lastUsedParameter; - if(programs.currentProgramPtr->values[UNLEARN] >0.5) - { - midiControlledParamSet = true; - bindings.controllers[lastMovedController] = 0; - setParameter(UNLEARN,0); - lastMovedController = 0; - lastUsedParameter = 0; - midiControlledParamSet = false; - } + + if (programs.currentProgramPtr->values[MIDILEARN] > 0.5f){ + midiControlledParamSet = true; + bindings[lastMovedController] = lastUsedParameter; + setEngineParameterValue (MIDILEARN, 0, true); + lastMovedController = 0; + lastUsedParameter = 0; + midiControlledParamSet = false; + } - if(bindings.controllers[lastMovedController] > 0) + if (bindings[lastMovedController] > 0) { midiControlledParamSet = true; - setParameter(bindings.controllers[lastMovedController],midiMsg->getControllerValue() / 127.0); - setParameter(MIDILEARN,0); + setEngineParameterValue (bindings[lastMovedController], + midiMsg->getControllerValue() / 127.0f); + + setEngineParameterValue (MIDILEARN, 0); lastMovedController = 0; lastUsedParameter = 0; @@ -670,14 +272,15 @@ inline void ObxdAudioProcessor::processMidiPerSample(MidiBuffer::Iterator* iter, } } -bool ObxdAudioProcessor::getNextEvent(MidiBuffer::Iterator* iter,const int samplePos) +bool ObxdAudioProcessor::getNextEvent (MidiBuffer::Iterator* iter, const int samplePos) { if (hasMidiMessage && midiEventPos <= samplePos) { *midiMsg = *nextMidi; - hasMidiMessage = iter->getNextEvent(*nextMidi, midiEventPos); + hasMidiMessage = iter->getNextEvent (*nextMidi, midiEventPos); return true; - } + } + return false; } @@ -691,27 +294,26 @@ void ObxdAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& mi // _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); #endif - MidiBuffer::Iterator ppp(midiMessages); - hasMidiMessage = ppp.getNextEvent(*nextMidi,midiEventPos); + MidiBuffer::Iterator ppp (midiMessages); + hasMidiMessage = ppp.getNextEvent (*nextMidi, midiEventPos); int samplePos = 0; int numSamples = buffer.getNumSamples(); - float* channelData1 = buffer.getWritePointer(0); - float* channelData2 = buffer.getWritePointer(1); + float* channelData1 = buffer.getWritePointer (0); + float* channelData2 = buffer.getWritePointer (1); AudioPlayHead::CurrentPositionInfo pos; + if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (pos)) { - synth.setPlayHead(pos.bpm,pos.ppqPosition); + synth.setPlayHead(pos.bpm, pos.ppqPosition); } while (samplePos < numSamples) { - processMidiPerSample(&ppp,samplePos); - - synth.processSample(channelData1+samplePos,channelData2+samplePos); - - samplePos++; + processMidiPerSample (&ppp, samplePos); + synth.processSample (channelData1+samplePos, channelData2+samplePos); + ++samplePos; } } @@ -723,13 +325,13 @@ bool ObxdAudioProcessor::hasEditor() const AudioProcessorEditor* ObxdAudioProcessor::createEditor() { - return new ObxdAudioProcessorEditor (this); + return new ObxdAudioProcessorEditor (*this); } //============================================================================== void ObxdAudioProcessor::getStateInformation(MemoryBlock& destData) { - XmlElement xmlState = XmlElement("Datsounds"); + XmlElement xmlState = XmlElement("discoDSP"); xmlState.setAttribute(S("currentProgram"), programs.currentProgram); XmlElement* xprogs = new XmlElement("programs"); @@ -741,7 +343,7 @@ void ObxdAudioProcessor::getStateInformation(MemoryBlock& destData) for (int k = 0; k < PARAM_COUNT; ++k) { - xpr->setAttribute(String(k), programs.programs[i].values[k]); + xpr->setAttribute("Val_" + String(k), programs.programs[i].values[k]); } xprogs->addChildElement(xpr); @@ -749,21 +351,18 @@ void ObxdAudioProcessor::getStateInformation(MemoryBlock& destData) xmlState.addChildElement(xprogs); - for (int i = 0; i < 255; ++i) - { - xmlState.setAttribute(String(i), bindings.controllers[i]); - } + bindings.setXml(xmlState); copyXmlToBinary(xmlState, destData); } void ObxdAudioProcessor::getCurrentProgramStateInformation(MemoryBlock& destData) { - XmlElement xmlState = XmlElement("Datsounds"); + XmlElement xmlState = XmlElement("discoDSP"); for (int k = 0; k < PARAM_COUNT; ++k) { - xmlState.setAttribute(String(k), programs.currentProgramPtr->values[k]); + xmlState.setAttribute("Val_" + String(k), programs.currentProgramPtr->values[k]); } xmlState.setAttribute(S("voiceCount"), Motherboard::MAX_VOICES); @@ -792,7 +391,13 @@ void ObxdAudioProcessor::setStateInformation(const void* data, int sizeInBytes) for (int k = 0; k < PARAM_COUNT; ++k) { - float value = float(e->getDoubleAttribute(String(k), programs.programs[i].values[k])); + float value = 0.0; + if (e->hasAttribute("Val_" + String(k))){ + value = float(e->getDoubleAttribute("Val_" + String(k), programs.programs[i].values[k])); + } else { + value = float(e->getDoubleAttribute(String(k), programs.programs[i].values[k])); + } + if (!newFormat && k == VOICE_COUNT) value *= 0.25f; programs.programs[i].values[k] = value; } @@ -803,13 +408,12 @@ void ObxdAudioProcessor::setStateInformation(const void* data, int sizeInBytes) } } - for (int i = 0; i < 255; ++i) - { - bindings.controllers[i] = xmlState->getIntAttribute(String(i), 0); - } - + bindings.getXml(*xmlState); +#if ! DEMOVERSION setCurrentProgram(xmlState->getIntAttribute(S("currentProgram"), 0)); + sendChangeMessage(); +#endif #if JUCE_VERSION <= JUCE_543 delete xmlState; #endif @@ -830,7 +434,13 @@ void ObxdAudioProcessor::setCurrentProgramStateInformation(const void* data, in bool newFormat = e->hasAttribute("voiceCount"); for (int k = 0; k < PARAM_COUNT; ++k) { - float value = float(e->getDoubleAttribute(String(k), programs.currentProgramPtr->values[k])); + float value = 0.0 ; + if (e->hasAttribute("Val_" + String(k))){ + value = float(e->getDoubleAttribute("Val_" + String(k), programs.currentProgramPtr->values[k])); + } else { + value = float(e->getDoubleAttribute(String(k), programs.currentProgramPtr->values[k])); + } + if (!newFormat && k == VOICE_COUNT) value *= 0.25f; programs.currentProgramPtr->values[k] = value; } @@ -838,7 +448,8 @@ void ObxdAudioProcessor::setCurrentProgramStateInformation(const void* data, in programs.currentProgramPtr->name = e->getStringAttribute(S("programName"), S("Default")); setCurrentProgram(programs.currentProgram); - + + sendChangeMessage(); #if JUCE_VERSION <= JUCE_543 delete e; #endif @@ -910,7 +521,7 @@ bool ObxdAudioProcessor::loadFromFXBFile(const File& fxbFile) changeProgramName (getCurrentProgram(), prog->prgName); for (int i = 0; i < fxbSwap (prog->numParams); ++i) - setParameter (i, fxbSwapFloat (prog->params[i])); + setEngineParameterValue (i, fxbSwapFloat (prog->params[i])); } else if (compareMagic (set->fxMagic, "FBCh")) { @@ -954,7 +565,7 @@ bool ObxdAudioProcessor::restoreProgramSettings(const fxProgram* const prog) changeProgramName (getCurrentProgram(), prog->prgName); for (int i = 0; i < fxbSwap (prog->numParams); ++i) - setParameter (i, fxbSwapFloat (prog->params[i])); + setEngineParameterValue (i, fxbSwapFloat (prog->params[i])); return true; } @@ -967,18 +578,36 @@ void ObxdAudioProcessor::scanAndUpdateBanks() { bankFiles.clearQuick(); - DirectoryIterator it(getBanksFolder(), false, "*.fxb", File::findFiles); - while (it.next()) + DirectoryIterator it (getBanksFolder(), false, "*.fxb", File::findFiles); + + while (it.next()) { - bankFiles.addUsingDefaultSort(it.getFile()); + bankFiles.addUsingDefaultSort (it.getFile()); } } +void ObxdAudioProcessor::scanAndUpdateSkins() +{ + skinFiles.clearQuick(); + DirectoryIterator it (getSkinFolder(), false, "*", File::findDirectories); + + while (it.next()) + { + skinFiles.addUsingDefaultSort (it.getFile()); + } + +} + const Array& ObxdAudioProcessor::getBankFiles() const { return bankFiles; } +const Array& ObxdAudioProcessor::getSkinFiles() const +{ + return skinFiles; +} + File ObxdAudioProcessor::getCurrentBankFile() const { return getBanksFolder().getChildFile(currentBank); @@ -988,6 +617,12 @@ File ObxdAudioProcessor::getCurrentBankFile() const File ObxdAudioProcessor::getDocumentFolder() const { File folder = File::getSpecialLocation(File::userDocumentsDirectory).getChildFile("discoDSP").getChildFile("OB-Xd"); +/* + if (! folder.exists()) + { + NativeMessageBox::showMessageBox(AlertWindow::WarningIcon, "Error", "Documents > discoDSP > OB-Xd folder not found."); + } + */ if (folder.isSymbolicLink()) folder = folder.getLinkedTarget(); return folder; @@ -995,7 +630,7 @@ File ObxdAudioProcessor::getDocumentFolder() const File ObxdAudioProcessor::getSkinFolder() const { - return getDocumentFolder().getChildFile("Skins"); + return getDocumentFolder().getChildFile("Themes"); } File ObxdAudioProcessor::getBanksFolder() const @@ -1016,6 +651,386 @@ void ObxdAudioProcessor::setCurrentSkinFolder(const String& folderName) config->setNeedsToBeSaved(true); } +//============================================================================== +String ObxdAudioProcessor::getEngineParameterId (size_t index) +{ + switch (index) + { + case SELF_OSC_PUSH: return "SelfOscPush"; + case ENV_PITCH_BOTH: return "EnvPitchBoth"; + case FENV_INVERT: return "FenvInvert"; + case PW_OSC2_OFS: return "PwOfs"; + case LEVEL_DIF: return "LevelDif"; + case PW_ENV_BOTH: return "PwEnvBoth"; + case PW_ENV: return "PwEnv"; + case LFO_SYNC: return "LfoSync"; + case ECONOMY_MODE: return "EconomyMode"; + case UNLEARN: return "MidiUnlearn"; + case MIDILEARN: return "MidiLearn"; + case VAMPENV: return "VAmpFactor"; + case VFLTENV: return "VFltFactor"; + case ASPLAYEDALLOCATION: return "AsPlayedAllocation"; + case BENDLFORATE: return "VibratoRate"; + case FOURPOLE: return "FourPole"; + case LEGATOMODE: return "LegatoMode"; + case ENVPITCH: return "EnvelopeToPitch"; + case OSCQuantize: return "PitchQuant"; + case VOICE_COUNT: return "VoiceCount"; + case BANDPASS: return "BandpassBlend"; + case FILTER_WARM: return "Filter_Warm"; + case BENDRANGE: return "BendRange"; + case BENDOSC2: return "BendOsc2Only"; + case OCTAVE: return "Octave"; + case TUNE: return "Tune"; + case BRIGHTNESS: return "Brightness"; + case NOISEMIX: return "NoiseMix"; + case OSC1MIX: return "Osc1Mix"; + case OSC2MIX: return "Osc2Mix"; + case MULTIMODE: return "Multimode"; + case LFOSHWAVE: return "LfoSampleHoldWave"; + case LFOSINWAVE: return "LfoSineWave"; + case LFOSQUAREWAVE: return "LfoSquareWave"; + case LFO1AMT: return "LfoAmount1"; + case LFO2AMT: return "LfoAmount2"; + case LFOFILTER: return "LfoFilter"; + case LFOOSC1: return "LfoOsc1"; + case LFOOSC2: return "LfoOsc2"; + case LFOFREQ: return "LfoFrequency"; + case LFOPW1: return "LfoPw1"; + case LFOPW2: return "LfoPw2"; + case PORTADER: return "PortamentoDetune"; + case FILTERDER: return "FilterDetune"; + case ENVDER: return "EnvelopeDetune"; + case PAN1: return "Pan1"; + case PAN2: return "Pan2"; + case PAN3: return "Pan3"; + case PAN4: return "Pan4"; + case PAN5: return "Pan5"; + case PAN6: return "Pan6"; + case PAN7: return "Pan7"; + case PAN8: return "Pan8"; + case XMOD: return "Xmod"; + case OSC2HS: return "Osc2HardSync"; + case OSC1P: return "Osc1Pitch"; + case OSC2P: return "Osc2Pitch"; + case PORTAMENTO: return "Portamento"; + case UNISON: return "Unison"; + case FLT_KF: return "FilterKeyFollow"; + case PW: return "PulseWidth"; + case OSC2Saw: return "Osc2Saw"; + case OSC1Saw: return "Osc1Saw"; + case OSC1Pul: return "Osc1Pulse"; + case OSC2Pul: return "Osc2Pulse"; + case VOLUME: return "Volume"; + case UDET: return "VoiceDetune"; + case OSC2_DET: return "Oscillator2detune"; + case CUTOFF: return "Cutoff"; + case RESONANCE: return "Resonance"; + case ENVELOPE_AMT: return "FilterEnvAmount"; + case LATK: return "Attack"; + case LDEC: return "Decay"; + case LSUS: return "Sustain"; + case LREL: return "Release"; + case FATK: return "FilterAttack"; + case FDEC: return "FilterDecay"; + case FSUS: return "FilterSustain"; + case FREL: return "FilterRelease"; + + default: + break; + } + + return "Undefined"; +} + +int ObxdAudioProcessor::getParameterIndexFromId (String paramId) +{ + for (size_t i = 0; i < PARAM_COUNT; ++i) + { + if (paramId.compare (getEngineParameterId (i)) == 0) + { + return int (i); + } + } + + return -1; +} + +void ObxdAudioProcessor::setEngineParameterValue (int index, float newValue, bool notifyToHost) +{ + if (! midiControlledParamSet || index == MIDILEARN || index == UNLEARN) + { + lastUsedParameter = index; + } + + programs.currentProgramPtr->values[index] = newValue; + + if (notifyToHost){ + apvtState.getParameter(getEngineParameterId(index))->setValueNotifyingHost(newValue); + } else { + apvtState.getParameter(getEngineParameterId(index))->setValue(newValue); + } + + switch (index) + { + case SELF_OSC_PUSH: + synth.processSelfOscPush (newValue); + break; + case PW_ENV_BOTH: + synth.processPwEnvBoth (newValue); + break; + case PW_OSC2_OFS: + synth.processPwOfs (newValue); + break; + case ENV_PITCH_BOTH: + synth.processPitchModBoth (newValue); + break; + case FENV_INVERT: + synth.processInvertFenv (newValue); + break; + case LEVEL_DIF: + synth.processLoudnessDetune (newValue); + break; + case PW_ENV: + synth.processPwEnv (newValue); + break; + case LFO_SYNC: + synth.procLfoSync (newValue); + break; + case ECONOMY_MODE: + synth.procEconomyMode (newValue); + break; + case VAMPENV: + synth.procAmpVelocityAmount (newValue); + break; + case VFLTENV: + synth.procFltVelocityAmount (newValue); + break; + case ASPLAYEDALLOCATION: + synth.procAsPlayedAlloc (newValue); + break; + case BENDLFORATE: + synth.procModWheelFrequency (newValue); + break; + case FOURPOLE: + synth.processFourPole (newValue); + break; + case LEGATOMODE: + synth.processLegatoMode (newValue); + break; + case ENVPITCH: + synth.processEnvelopeToPitch (newValue); + break; + case OSCQuantize: + synth.processPitchQuantization (newValue); + break; + case VOICE_COUNT: + synth.setVoiceCount (newValue); + break; + case BANDPASS: + synth.processBandpassSw (newValue); + break; + case FILTER_WARM: + synth.processOversampling (newValue); + break; + case BENDOSC2: + synth.procPitchWheelOsc2Only (newValue); + break; + case BENDRANGE: + synth.procPitchWheelAmount (newValue); + break; + case NOISEMIX: + synth.processNoiseMix (newValue); + break; + case OCTAVE: + synth.processOctave (newValue); + break; + case TUNE: + synth.processTune (newValue); + break; + case BRIGHTNESS: + synth.processBrightness (newValue); + break; + case MULTIMODE: + synth.processMultimode (newValue); + break; + case LFOFREQ: + synth.processLfoFrequency (newValue); + break; + case LFO1AMT: + synth.processLfoAmt1 (newValue); + break; + case LFO2AMT: + synth.processLfoAmt2 (newValue); + break; + case LFOSINWAVE: + synth.processLfoSine (newValue); + break; + case LFOSQUAREWAVE: + synth.processLfoSquare (newValue); + break; + case LFOSHWAVE: + synth.processLfoSH (newValue); + break; + case LFOFILTER: + synth.processLfoFilter (newValue); + break; + case LFOOSC1: + synth.processLfoOsc1 (newValue); + break; + case LFOOSC2: + synth.processLfoOsc2 (newValue); + break; + case LFOPW1: + synth.processLfoPw1 (newValue); + break; + case LFOPW2: + synth.processLfoPw2 (newValue); + break; + case PORTADER: + synth.processPortamentoDetune (newValue); + break; + case FILTERDER: + synth.processFilterDetune (newValue); + break; + case ENVDER: + synth.processEnvelopeDetune (newValue); + break; + case XMOD: + synth.processOsc2Xmod (newValue); + break; + case OSC2HS: + synth.processOsc2HardSync (newValue); + break; + case OSC2P: + synth.processOsc2Pitch (newValue); + break; + case OSC1P: + synth.processOsc1Pitch (newValue); + break; + case PORTAMENTO: + synth.processPortamento (newValue); + break; + case UNISON: + synth.processUnison (newValue); + break; + case FLT_KF: + synth.processFilterKeyFollow (newValue); + break; + case OSC1MIX: + synth.processOsc1Mix (newValue); + break; + case OSC2MIX: + synth.processOsc2Mix (newValue); + break; + case PW: + synth.processPulseWidth (newValue); + break; + case OSC1Saw: + synth.processOsc1Saw (newValue); + break; + case OSC2Saw: + synth.processOsc2Saw (newValue); + break; + case OSC1Pul: + synth.processOsc1Pulse (newValue); + break; + case OSC2Pul: + synth.processOsc2Pulse (newValue); + break; + case VOLUME: + synth.processVolume (newValue); + break; + case UDET: + synth.processDetune (newValue); + break; + case OSC2_DET: + synth.processOsc2Det (newValue); + break; + case CUTOFF: + synth.processCutoff (newValue); + break; + case RESONANCE: + synth.processResonance (newValue); + break; + case ENVELOPE_AMT: + synth.processFilterEnvelopeAmt (newValue); + break; + case LATK: + synth.processLoudnessEnvelopeAttack (newValue); + break; + case LDEC: + synth.processLoudnessEnvelopeDecay (newValue); + break; + case LSUS: + synth.processLoudnessEnvelopeSustain (newValue); + break; + case LREL: + synth.processLoudnessEnvelopeRelease (newValue); + break; + case FATK: + synth.processFilterEnvelopeAttack (newValue); + break; + case FDEC: + synth.processFilterEnvelopeDecay (newValue); + break; + case FSUS: + synth.processFilterEnvelopeSustain (newValue); + break; + case FREL: + synth.processFilterEnvelopeRelease (newValue); + break; + case PAN1: + synth.processPan (newValue,1); + break; + case PAN2: + synth.processPan (newValue,2); + break; + case PAN3: + synth.processPan (newValue,3); + break; + case PAN4: + synth.processPan (newValue,4); + break; + case PAN5: + synth.processPan (newValue,5); + break; + case PAN6: + synth.processPan (newValue,6); + break; + case PAN7: + synth.processPan (newValue,7); + break; + case PAN8: + synth.processPan (newValue,8); + break; + } + + //DIRTY HACK + //This should be checked to avoid stalling on gui update + //It is needed because some hosts do wierd stuff + if (isHostAutomatedChange) + sendChangeMessage(); +} + +//============================================================================== +void ObxdAudioProcessor::parameterChanged (const String& parameter, float newValue) +{ + int index = getParameterIndexFromId (parameter); + + if ( isPositiveAndBelow (index, PARAM_COUNT) ) + { + isHostAutomatedChange = false; + setEngineParameterValue (index, newValue); + isHostAutomatedChange = true; + } +} + +AudioProcessorValueTreeState& ObxdAudioProcessor::getPluginState() +{ + return apvtState; +} + //============================================================================== // This creates new instances of the plugin.. AudioProcessor* JUCE_CALLTYPE createPluginFilter() diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 89f2ae9..104ab81 100755 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -113,10 +113,9 @@ static inline float fxbSwapFloat (const float x) noexcept //============================================================================== /** */ -class ObxdAudioProcessor : - public AudioProcessor, - // public AudioProcessorListener, - public ChangeBroadcaster +class ObxdAudioProcessor : public AudioProcessor, + public AudioProcessorValueTreeState::Listener, + public ChangeBroadcaster { public: //============================================================================== @@ -124,61 +123,55 @@ public: ~ObxdAudioProcessor(); //============================================================================== - void prepareToPlay (double sampleRate, int samplesPerBlock); - void releaseResources(); + void prepareToPlay (double sampleRate, int samplesPerBlock) override; + void releaseResources() override; - void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override; //============================================================================== - AudioProcessorEditor* createEditor(); - bool hasEditor() const; + AudioProcessorEditor* createEditor() override; + bool hasEditor() const override; //============================================================================== - void processMidiPerSample(MidiBuffer::Iterator* iter,const int samplePos); - bool getNextEvent(MidiBuffer::Iterator* iter,const int samplePos); + void processMidiPerSample (MidiBuffer::Iterator* iter, const int samplePos); + bool getNextEvent (MidiBuffer::Iterator* iter, const int samplePos); //============================================================================== - void initAllParams(); + void initAllParams(); + + const String getInputChannelName (int channelIndex) const override; // WATCH OUT! + const String getOutputChannelName (int channelIndex) const override; // WATCH OUT! + bool isInputChannelStereoPair (int index) const override; // WATCH OUT! + bool isOutputChannelStereoPair (int index) const override; // WATCH OUT! - int getNumParameters(); - - float getParameter (int index); - void setParameter (int index, float newValue); - - const String getParameterName (int index); - const String getParameterText (int index); - - const String getInputChannelName (int channelIndex) const; - const String getOutputChannelName (int channelIndex) const; - bool isInputChannelStereoPair (int index) const; - bool isOutputChannelStereoPair (int index) const; - - bool acceptsMidi() const; - bool producesMidi() const; - bool silenceInProducesSilenceOut() const; - double getTailLengthSeconds() const; - const String getName() const; + bool acceptsMidi() const override; + bool producesMidi() const override; + double getTailLengthSeconds() const override; + const String getName() const override; //============================================================================== - int getNumPrograms(); - int getCurrentProgram(); - void setCurrentProgram (int index); - const String getProgramName (int index); - void changeProgramName (int index, const String& newName); + int getNumPrograms() override; + int getCurrentProgram() override; + void setCurrentProgram (int index) override; + const String getProgramName (int index) override; + void changeProgramName (int index, const String& newName) override; //============================================================================== - void getStateInformation (MemoryBlock& destData); - void setStateInformation (const void* data, int sizeInBytes); - void setCurrentProgramStateInformation(const void* data,int sizeInBytes); - void getCurrentProgramStateInformation(MemoryBlock& destData); + void getStateInformation (MemoryBlock& destData) override; + void setStateInformation (const void* data, int sizeInBytes) override; + void setCurrentProgramStateInformation (const void* data,int sizeInBytes) override; + void getCurrentProgramStateInformation (MemoryBlock& destData) override; //============================================================================== void scanAndUpdateBanks(); + void scanAndUpdateSkins(); const Array& getBankFiles() const; + const Array& getSkinFiles() const; bool loadFromFXBFile(const File& fxbFile); bool restoreProgramSettings(const fxProgram* const prog); File getCurrentBankFile() const; + MidiMap &getMidiMap(){ return bindings; } //============================================================================== const ObxdBank& getPrograms() const { return programs; } @@ -189,6 +182,13 @@ public: File getCurrentSkinFolder() const; void setCurrentSkinFolder(const String& folderName); + + //============================================================================== + static String getEngineParameterId (size_t); + int getParameterIndexFromId (String); + void setEngineParameterValue (int, float, bool notifyToHost= false); + void parameterChanged (const String&, float) override; + AudioProcessorValueTreeState& getPluginState(); private: //============================================================================== @@ -211,9 +211,14 @@ private: String currentSkin; String currentBank; Array bankFiles; + Array skinFiles; - ScopedPointer config; + std::unique_ptr config; InterProcessLock configLock; + + //============================================================================== + AudioProcessorValueTreeState apvtState; + UndoManager undoManager; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ObxdAudioProcessor)