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)