diff --git a/build/depends.py b/build/depends.py
index 6ff9d991fdd1..34d28545283c 100644
--- a/build/depends.py
+++ b/build/depends.py
@@ -465,6 +465,24 @@ def sources(self, build):
"dlghidden.cpp",
"dlgmissing.cpp",
+ "effects/effectmanifest.cpp",
+ "effects/effectmanifestparameter.cpp",
+
+ "effects/effectchain.cpp",
+ "effects/effect.cpp",
+ "effects/effectparameter.cpp",
+
+ "effects/effectchainslot.cpp",
+ "effects/effectslot.cpp",
+ "effects/effectparameterslot.cpp",
+
+ "effects/effectsmanager.cpp",
+ "effects/effectchainmanager.cpp",
+ "effects/effectsbackend.cpp",
+
+ "effects/native/nativebackend.cpp",
+ "effects/native/flangereffect.cpp",
+
"engine/engineworker.cpp",
"engine/engineworkerscheduler.cpp",
"engine/enginebuffer.cpp",
@@ -558,6 +576,7 @@ def sources(self, build):
"widget/wimagestore.cpp",
"widget/hexspinbox.cpp",
"widget/wtrackproperty.cpp",
+ "widget/weffectchain.cpp",
"widget/wtime.cpp",
"mathstuff.cpp",
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/background1280x1024.png b/res/skins/Deere1280x1024-SXGA-Effects/background1280x1024.png
new file mode 100644
index 000000000000..7c675ff1897a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/background1280x1024.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid1.png
new file mode 100644
index 000000000000..d6f56c23f0b3
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid1_over.png
new file mode 100644
index 000000000000..757823cf15fb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid2.png
new file mode 100644
index 000000000000..669601fa9c0d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid2_over.png
new file mode 100644
index 000000000000..6e6a8968ebb1
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatgrid2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0125.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0125.png
new file mode 100644
index 000000000000..951f8c69ebe5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0125.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0125_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0125_over.png
new file mode 100644
index 000000000000..c6968f2ea92f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0125_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0250.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0250.png
new file mode 100644
index 000000000000..f102ad1dbe7d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0250.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0250_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0250_over.png
new file mode 100644
index 000000000000..0c0be6bfff16
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0250_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0500.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0500.png
new file mode 100644
index 000000000000..f8490ee55913
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0500.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0500_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0500_over.png
new file mode 100644
index 000000000000..3f178f5ee2ec
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_0500_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_1.png
new file mode 100644
index 000000000000..be788bce542e
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_16.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_16.png
new file mode 100644
index 000000000000..951e5bb974c2
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_16.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_16_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_16_over.png
new file mode 100644
index 000000000000..699a774deca7
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_16_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_1_over.png
new file mode 100644
index 000000000000..55744a388128
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_2.png
new file mode 100644
index 000000000000..dad8a116a585
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_2_over.png
new file mode 100644
index 000000000000..070142cf3b95
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_4.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_4.png
new file mode 100644
index 000000000000..065358b86adb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_4.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_4_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_4_over.png
new file mode 100644
index 000000000000..a8cfd63a4004
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_4_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_8.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_8.png
new file mode 100644
index 000000000000..26c56d249356
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_8.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_8_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_8_over.png
new file mode 100644
index 000000000000..6884851202ad
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_8_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_double.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_double.png
new file mode 100644
index 000000000000..0588a34ff47f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_double.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_double_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_double_over.png
new file mode 100644
index 000000000000..fa171c67f942
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_double_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_halve.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_halve.png
new file mode 100644
index 000000000000..8e17b82ac7bb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_halve.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_halve_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_halve_over.png
new file mode 100644
index 000000000000..30fd18d751dc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop1_halve_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0125.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0125.png
new file mode 100644
index 000000000000..951f8c69ebe5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0125.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0125_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0125_over.png
new file mode 100644
index 000000000000..c6968f2ea92f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0125_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0250.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0250.png
new file mode 100644
index 000000000000..f102ad1dbe7d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0250.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0250_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0250_over.png
new file mode 100644
index 000000000000..0c0be6bfff16
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0250_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0500.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0500.png
new file mode 100644
index 000000000000..f8490ee55913
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0500.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0500_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0500_over.png
new file mode 100644
index 000000000000..3f178f5ee2ec
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_0500_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_1.png
new file mode 100644
index 000000000000..be788bce542e
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_16.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_16.png
new file mode 100644
index 000000000000..951e5bb974c2
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_16.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_16_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_16_over.png
new file mode 100644
index 000000000000..699a774deca7
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_16_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_1_over.png
new file mode 100644
index 000000000000..55744a388128
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_2.png
new file mode 100644
index 000000000000..dad8a116a585
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_2_over.png
new file mode 100644
index 000000000000..070142cf3b95
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_4.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_4.png
new file mode 100644
index 000000000000..065358b86adb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_4.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_4_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_4_over.png
new file mode 100644
index 000000000000..a8cfd63a4004
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_4_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_8.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_8.png
new file mode 100644
index 000000000000..26c56d249356
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_8.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_8_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_8_over.png
new file mode 100644
index 000000000000..6884851202ad
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_8_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_double.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_double.png
new file mode 100644
index 000000000000..0588a34ff47f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_double.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_double_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_double_over.png
new file mode 100644
index 000000000000..fa171c67f942
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_double_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_halve.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_halve.png
new file mode 100644
index 000000000000..8e17b82ac7bb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_halve.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_halve_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_halve_over.png
new file mode 100644
index 000000000000..30fd18d751dc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_beatloop2_halve_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping1.png
new file mode 100644
index 000000000000..4cd00bc0111c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping1_over.png
new file mode 100644
index 000000000000..fc4af96c0240
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping2.png
new file mode 100644
index 000000000000..4cd00bc0111c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping2_over.png
new file mode 100644
index 000000000000..fc4af96c0240
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_master.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_master.png
new file mode 100644
index 000000000000..0236d22d3350
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_master.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_master_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_master_over.png
new file mode 100644
index 000000000000..d0ac8d83801a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_master_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_microphone.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_microphone.png
new file mode 100644
index 000000000000..720b10227790
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_microphone.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_microphone_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_microphone_over.png
new file mode 100644
index 000000000000..c2abf84cb145
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_microphone_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_sampler.png
new file mode 100644
index 000000000000..e5a46b2794d2
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_sampler_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_sampler_over.png
new file mode 100644
index 000000000000..c86be3f0743b
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_clipping_sampler_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_cue1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_cue1.png
new file mode 100644
index 000000000000..e9330b9821fc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_cue1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_cue1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_cue1_over.png
new file mode 100644
index 000000000000..440bebd4d1ba
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_cue1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_cue2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_cue2.png
new file mode 100644
index 000000000000..c063e0ed0bec
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_cue2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_cue2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_cue2_over.png
new file mode 100644
index 000000000000..25d9a126478c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_cue2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_eject1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject1.png
new file mode 100644
index 000000000000..3a19a5b34b3a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_eject1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject1_over.png
new file mode 100644
index 000000000000..efd0f796c398
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_eject2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject2.png
new file mode 100644
index 000000000000..3a19a5b34b3a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_eject2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject2_over.png
new file mode 100644
index 000000000000..efd0f796c398
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_eject_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject_sampler.png
new file mode 100644
index 000000000000..e9478e40412b
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_eject_sampler_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject_sampler_over.png
new file mode 100644
index 000000000000..1891923bd6a6
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_eject_sampler_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_forward1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_forward1.png
new file mode 100644
index 000000000000..4aad009224dc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_forward1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_forward1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_forward1_over.png
new file mode 100644
index 000000000000..1d9a9a1ea431
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_forward1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_forward2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_forward2.png
new file mode 100644
index 000000000000..9e877d790130
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_forward2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_forward2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_forward2_over.png
new file mode 100644
index 000000000000..0633a55e9145
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_forward2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_fx1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_fx1.png
new file mode 100644
index 000000000000..a7219522dbd9
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_fx1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_fx1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_fx1_over.png
new file mode 100644
index 000000000000..dffed46f5bc7
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_fx1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_fx2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_fx2.png
new file mode 100644
index 000000000000..d89421a8adf6
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_fx2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_fx2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_fx2_over.png
new file mode 100644
index 000000000000..5846aa005450
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_fx2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_1.png
new file mode 100644
index 000000000000..6cc621709975
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_1_over.png
new file mode 100644
index 000000000000..4e17ba44c773
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_2.png
new file mode 100644
index 000000000000..86aa4c6d1184
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_2_over.png
new file mode 100644
index 000000000000..38d96fae386d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_3.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_3.png
new file mode 100644
index 000000000000..a54f54ac7cdb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_3.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_3_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_3_over.png
new file mode 100644
index 000000000000..bdd470e29e04
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_3_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_4.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_4.png
new file mode 100644
index 000000000000..44c2c175b36f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_4.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_4_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_4_over.png
new file mode 100644
index 000000000000..2432a8e36de0
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue1_4_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_1.png
new file mode 100644
index 000000000000..be788bce542e
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_1_over.png
new file mode 100644
index 000000000000..55744a388128
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_2.png
new file mode 100644
index 000000000000..dad8a116a585
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_2_over.png
new file mode 100644
index 000000000000..070142cf3b95
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_3.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_3.png
new file mode 100644
index 000000000000..11012c6d73fd
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_3.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_3_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_3_over.png
new file mode 100644
index 000000000000..7e9ede937990
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_3_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_4.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_4.png
new file mode 100644
index 000000000000..065358b86adb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_4.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_4_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_4_over.png
new file mode 100644
index 000000000000..a8cfd63a4004
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_hotcue2_4_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock1.png
new file mode 100644
index 000000000000..06afab7e2bbc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock1_over.png
new file mode 100644
index 000000000000..0ad1537c1efe
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock2.png
new file mode 100644
index 000000000000..06afab7e2bbc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock2_over.png
new file mode 100644
index 000000000000..0ad1537c1efe
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock_sampler.png
new file mode 100644
index 000000000000..06afab7e2bbc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock_sampler_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock_sampler_over.png
new file mode 100644
index 000000000000..06685d099fa4
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_keylock_sampler_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_kill.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_kill.png
new file mode 100644
index 000000000000..4ac67200a7fb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_kill.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_kill_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_kill_over.png
new file mode 100644
index 000000000000..b3708695b35f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_kill_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in1.png
new file mode 100644
index 000000000000..0015039449e5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in1_over.png
new file mode 100644
index 000000000000..1e90a2c62a50
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in2.png
new file mode 100644
index 000000000000..266007c8f76f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in2_over.png
new file mode 100644
index 000000000000..2ae510676220
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_in2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out1.png
new file mode 100644
index 000000000000..e8971ffd9021
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out1_over.png
new file mode 100644
index 000000000000..17bc8025d870
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out2.png
new file mode 100644
index 000000000000..0d49cec540c7
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out2_over.png
new file mode 100644
index 000000000000..a42d5993ecdd
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_loop_out2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_microphone_talkover.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_microphone_talkover.png
new file mode 100644
index 000000000000..03a385f6c8e2
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_microphone_talkover.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_microphone_talkover_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_microphone_talkover_over.png
new file mode 100644
index 000000000000..b76ebb10f58d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_microphone_talkover_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down1.png
new file mode 100644
index 000000000000..49dbffc029a9
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down1_over.png
new file mode 100644
index 000000000000..69b54af76b3e
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down2.png
new file mode 100644
index 000000000000..49dbffc029a9
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down2_over.png
new file mode 100644
index 000000000000..69b54af76b3e
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_down2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up1.png
new file mode 100644
index 000000000000..4f1b50a3c5bd
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up1_over.png
new file mode 100644
index 000000000000..a9077836c4da
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up2.png
new file mode 100644
index 000000000000..4f1b50a3c5bd
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up2_over.png
new file mode 100644
index 000000000000..a9077836c4da
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_nudge_up2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_microphone_left_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_microphone_left_over.png
new file mode 100644
index 000000000000..a9ca27df02c2
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_microphone_left_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_microphone_master.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_microphone_master.png
new file mode 100644
index 000000000000..529c1a93717a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_microphone_master.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_microphone_right_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_microphone_right_over.png
new file mode 100644
index 000000000000..2ff5f7542f4b
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_microphone_right_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_sampler_left_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_sampler_left_over.png
new file mode 100644
index 000000000000..a9ca27df02c2
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_sampler_left_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_sampler_master.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_sampler_master.png
new file mode 100644
index 000000000000..d5cd38384490
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_sampler_master.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_sampler_right_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_sampler_right_over.png
new file mode 100644
index 000000000000..2ff5f7542f4b
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_orientation_sampler_right_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl1.png
new file mode 100644
index 000000000000..ee995a39ffa7
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl1_over.png
new file mode 100644
index 000000000000..7c3c9545c830
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl2.png
new file mode 100644
index 000000000000..85ca6d34c6d3
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl2_over.png
new file mode 100644
index 000000000000..45e883de79d8
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl_sampler.png
new file mode 100644
index 000000000000..4750c03397ce
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl_sampler_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl_sampler_over.png
new file mode 100644
index 000000000000..6c68dfbe57cc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pfl_sampler_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down1.png
new file mode 100644
index 000000000000..e223b1b249c9
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down1_over.png
new file mode 100644
index 000000000000..04da161e9422
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down2.png
new file mode 100644
index 000000000000..e223b1b249c9
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down2_over.png
new file mode 100644
index 000000000000..04da161e9422
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_down2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up1.png
new file mode 100644
index 000000000000..9d7016b90a2c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up1_over.png
new file mode 100644
index 000000000000..f8a899661d5f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up2.png
new file mode 100644
index 000000000000..9d7016b90a2c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up2_over.png
new file mode 100644
index 000000000000..f8a899661d5f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_pitch_up2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_play1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_play1.png
new file mode 100644
index 000000000000..2473854f18cf
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_play1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_play1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_play1_over.png
new file mode 100644
index 000000000000..4a84f0b82646
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_play1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_play2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_play2.png
new file mode 100644
index 000000000000..cb94710d4581
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_play2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_play2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_play2_over.png
new file mode 100644
index 000000000000..ec26bc6e3b01
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_play2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_play_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_play_sampler.png
new file mode 100644
index 000000000000..7d661a4e85c7
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_play_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_play_sampler_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_play_sampler_over.png
new file mode 100644
index 000000000000..cfca7ec02fce
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_play_sampler_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize1.png
new file mode 100644
index 000000000000..4da4d3651c9b
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize1_over.png
new file mode 100644
index 000000000000..fbc25078d7bb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize2.png
new file mode 100644
index 000000000000..4da4d3651c9b
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize2_over.png
new file mode 100644
index 000000000000..fbc25078d7bb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_quantize2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop1.png
new file mode 100644
index 000000000000..1fdc88a8eeca
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop1_over.png
new file mode 100644
index 000000000000..33ad06d05e85
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop2.png
new file mode 100644
index 000000000000..ba636e4f746d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop2_over.png
new file mode 100644
index 000000000000..e9d46adcbeea
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_reloop2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat1.png
new file mode 100644
index 000000000000..3ba581b9157a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat1_over.png
new file mode 100644
index 000000000000..5aeb7eecbc0c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat2.png
new file mode 100644
index 000000000000..3ba581b9157a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat2_over.png
new file mode 100644
index 000000000000..5aeb7eecbc0c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat_sampler.png
new file mode 100644
index 000000000000..3ba581b9157a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat_sampler_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat_sampler_over.png
new file mode 100644
index 000000000000..84fdc89cfa13
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_repeat_sampler_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse1.png
new file mode 100644
index 000000000000..2026e7c76550
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse1_over.png
new file mode 100644
index 000000000000..4f0bc4833f03
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse2.png
new file mode 100644
index 000000000000..3c4461d4e5a4
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse2_over.png
new file mode 100644
index 000000000000..a74413b70542
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_reverse2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind1.png
new file mode 100644
index 000000000000..d82fcb2cedab
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind1_over.png
new file mode 100644
index 000000000000..2a5bfef3c2a5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind2.png
new file mode 100644
index 000000000000..99aa730e65b5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind2_over.png
new file mode 100644
index 000000000000..c5d17064ba04
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_rewind2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny1.png
new file mode 100644
index 000000000000..3e3c941ae7dd
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny1_over.png
new file mode 100644
index 000000000000..bede13b1f0cb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny2.png
new file mode 100644
index 000000000000..3e3c941ae7dd
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny2_over.png
new file mode 100644
index 000000000000..bede13b1f0cb
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_spinny2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_sync1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_sync1.png
new file mode 100644
index 000000000000..ce1099aad5d0
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_sync1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_sync1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_sync1_over.png
new file mode 100644
index 000000000000..d60b8c003487
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_sync1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_sync2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_sync2.png
new file mode 100644
index 000000000000..e567ee6abf71
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_sync2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_sync2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_sync2_over.png
new file mode 100644
index 000000000000..080abca2ee10
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_sync2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_tap1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap1.png
new file mode 100644
index 000000000000..e08ed99f938f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_tap1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap1_over.png
new file mode 100644
index 000000000000..d675d8efdf26
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_tap2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap2.png
new file mode 100644
index 000000000000..e08ed99f938f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_tap2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap2_over.png
new file mode 100644
index 000000000000..d675d8efdf26
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_tap_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap_sampler.png
new file mode 100644
index 000000000000..60d7f1b2d92f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_tap_sampler_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap_sampler_over.png
new file mode 100644
index 000000000000..3ccf8d61068c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_tap_sampler_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_hot1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_hot1.png
new file mode 100644
index 000000000000..42a8d684f9c4
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_hot1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_hot2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_hot2.png
new file mode 100644
index 000000000000..5bc03a3842a5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_hot2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_off1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_off1.png
new file mode 100644
index 000000000000..5c2d16a50949
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_off1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_off2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_off2.png
new file mode 100644
index 000000000000..ccf11e040375
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_off2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_one1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_one1.png
new file mode 100644
index 000000000000..85b2809bb7f4
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_one1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_one2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_one2.png
new file mode 100644
index 000000000000..7f4ac869fbed
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_cueing_one2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_enabled.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_enabled.png
new file mode 100644
index 000000000000..e7e4b07f3edf
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_enabled.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_enabled_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_enabled_over.png
new file mode 100644
index 000000000000..1b7021c652f4
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_enabled_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_horizontal1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_horizontal1.png
new file mode 100644
index 000000000000..43af612a954c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_horizontal1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_horizontal2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_horizontal2.png
new file mode 100644
index 000000000000..183e11ed1146
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_horizontal2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_horizontal3.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_horizontal3.png
new file mode 100644
index 000000000000..d042a146dd31
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_horizontal3.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_vertical1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_vertical1.png
new file mode 100644
index 000000000000..5f7bb42451c8
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_vertical1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_vertical2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_vertical2.png
new file mode 100644
index 000000000000..2e5a4d88d188
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_vertical2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_vertical3.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_vertical3.png
new file mode 100644
index 000000000000..8512d325e044
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_indicator_vertical3.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_abs1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_abs1.png
new file mode 100644
index 000000000000..0abbb0806177
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_abs1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_abs2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_abs2.png
new file mode 100644
index 000000000000..ba639e2a35f0
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_abs2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_const1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_const1.png
new file mode 100644
index 000000000000..c5317e61e7bc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_const1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_const2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_const2.png
new file mode 100644
index 000000000000..c9f5dd89c89d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_const2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_rel1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_rel1.png
new file mode 100644
index 000000000000..48838a55c295
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_rel1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_rel2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_rel2.png
new file mode 100644
index 000000000000..2dda4934d182
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_vinylcontrol_mode_rel2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display1.png
new file mode 100644
index 000000000000..fb5fe5218f45
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display1_over.png
new file mode 100644
index 000000000000..4d7f915724d6
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display2.png
new file mode 100644
index 000000000000..fb5fe5218f45
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display2_over.png
new file mode 100644
index 000000000000..4d7f915724d6
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master1.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master1.png
new file mode 100644
index 000000000000..fb5fe5218f45
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master1_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master1_over.png
new file mode 100644
index 000000000000..4d7f915724d6
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master1_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master2.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master2.png
new file mode 100644
index 000000000000..fb5fe5218f45
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master2_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master2_over.png
new file mode 100644
index 000000000000..4d7f915724d6
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_master2_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_microphone.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_microphone.png
new file mode 100644
index 000000000000..26fd2bb1b431
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_microphone.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_microphone_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_microphone_over.png
new file mode 100644
index 000000000000..590cc9901849
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_microphone_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_sampler.png
new file mode 100644
index 000000000000..f44252fefd57
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_sampler_over.png b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_sampler_over.png
new file mode 100644
index 000000000000..3232ff405810
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/btn_volume_display_sampler_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knob_crossfader.png b/res/skins/Deere1280x1024-SXGA-Effects/knob_crossfader.png
new file mode 100644
index 000000000000..9e1fa0582e56
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knob_crossfader.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knob_pitch1.png b/res/skins/Deere1280x1024-SXGA-Effects/knob_pitch1.png
new file mode 100644
index 000000000000..505c679896b5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knob_pitch1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knob_pitch2.png b/res/skins/Deere1280x1024-SXGA-Effects/knob_pitch2.png
new file mode 100644
index 000000000000..505c679896b5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knob_pitch2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knob_pitch_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/knob_pitch_sampler.png
new file mode 100644
index 000000000000..fe601062c693
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knob_pitch_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knob_volume1.png b/res/skins/Deere1280x1024-SXGA-Effects/knob_volume1.png
new file mode 100644
index 000000000000..505c679896b5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knob_volume1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knob_volume2.png b/res/skins/Deere1280x1024-SXGA-Effects/knob_volume2.png
new file mode 100644
index 000000000000..505c679896b5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knob_volume2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s0.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s0.png
new file mode 100644
index 000000000000..5cab9a8668ee
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s0.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s1.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s1.png
new file mode 100644
index 000000000000..1858efbe33e3
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s10.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s10.png
new file mode 100644
index 000000000000..dc28a346f387
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s10.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s11.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s11.png
new file mode 100644
index 000000000000..321ec607e0b7
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s11.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s12.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s12.png
new file mode 100644
index 000000000000..fd78f62d304d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s12.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s13.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s13.png
new file mode 100644
index 000000000000..0b2b656a651d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s13.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s14.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s14.png
new file mode 100644
index 000000000000..5521e3f6a58d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s14.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s15.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s15.png
new file mode 100644
index 000000000000..839a0d9b02bc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s15.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s16.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s16.png
new file mode 100644
index 000000000000..5ebfa13f757b
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s16.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s17.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s17.png
new file mode 100644
index 000000000000..b003f3776222
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s17.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s18.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s18.png
new file mode 100644
index 000000000000..af928dfd83ac
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s18.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s19.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s19.png
new file mode 100644
index 000000000000..aa9b408079cc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s19.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s2.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s2.png
new file mode 100644
index 000000000000..0fd1983af88a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s20.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s20.png
new file mode 100644
index 000000000000..b3ceb037352e
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s20.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s21.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s21.png
new file mode 100644
index 000000000000..0932df97b728
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s21.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s22.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s22.png
new file mode 100644
index 000000000000..bb38538fa6cd
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s22.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s23.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s23.png
new file mode 100644
index 000000000000..34411a3ae084
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s23.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s24.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s24.png
new file mode 100644
index 000000000000..40ee98c89374
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s24.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s25.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s25.png
new file mode 100644
index 000000000000..896b87324479
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s25.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s26.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s26.png
new file mode 100644
index 000000000000..4d180b2c075f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s26.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s27.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s27.png
new file mode 100644
index 000000000000..79873c702621
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s27.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s28.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s28.png
new file mode 100644
index 000000000000..d2a6adca637f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s28.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s29.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s29.png
new file mode 100644
index 000000000000..74430b65b67e
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s29.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s3.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s3.png
new file mode 100644
index 000000000000..e285c56bffee
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s3.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s30.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s30.png
new file mode 100644
index 000000000000..c103d52df26d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s30.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s31.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s31.png
new file mode 100644
index 000000000000..e846b625c9ae
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s31.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s32.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s32.png
new file mode 100644
index 000000000000..0e317c1bf540
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s32.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s33.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s33.png
new file mode 100644
index 000000000000..5b1763c84ed1
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s33.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s34.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s34.png
new file mode 100644
index 000000000000..872b5a5e9249
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s34.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s35.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s35.png
new file mode 100644
index 000000000000..d943a5c304ab
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s35.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s36.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s36.png
new file mode 100644
index 000000000000..f7f29b2f1bea
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s36.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s37.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s37.png
new file mode 100644
index 000000000000..925c3977988f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s37.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s38.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s38.png
new file mode 100644
index 000000000000..3b11403edcc0
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s38.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s39.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s39.png
new file mode 100644
index 000000000000..60b759b9aaec
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s39.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s4.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s4.png
new file mode 100644
index 000000000000..1431dacb54e6
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s4.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s40.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s40.png
new file mode 100644
index 000000000000..166a75a86dd4
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s40.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s41.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s41.png
new file mode 100644
index 000000000000..62461c3ebff1
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s41.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s42.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s42.png
new file mode 100644
index 000000000000..5830d4ecdb15
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s42.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s43.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s43.png
new file mode 100644
index 000000000000..80ae85c41069
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s43.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s44.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s44.png
new file mode 100644
index 000000000000..06a3a469c843
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s44.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s45.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s45.png
new file mode 100644
index 000000000000..5f92908958c9
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s45.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s46.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s46.png
new file mode 100644
index 000000000000..f0c76e5e58a5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s46.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s47.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s47.png
new file mode 100644
index 000000000000..4ee418b16955
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s47.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s48.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s48.png
new file mode 100644
index 000000000000..6dffde82a0ab
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s48.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s49.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s49.png
new file mode 100644
index 000000000000..3744cfb1972c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s49.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s5.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s5.png
new file mode 100644
index 000000000000..9fa16ad1f1c8
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s5.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s50.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s50.png
new file mode 100644
index 000000000000..6c885ee9c7a7
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s50.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s51.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s51.png
new file mode 100644
index 000000000000..5cab976b5acc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s51.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s52.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s52.png
new file mode 100644
index 000000000000..0ef9a18077ae
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s52.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s53.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s53.png
new file mode 100644
index 000000000000..cc53c83cafc8
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s53.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s54.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s54.png
new file mode 100644
index 000000000000..af38991384a1
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s54.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s55.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s55.png
new file mode 100644
index 000000000000..c6cad566ea61
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s55.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s56.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s56.png
new file mode 100644
index 000000000000..ca5a8fbe36d1
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s56.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s57.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s57.png
new file mode 100644
index 000000000000..ee59003d99ee
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s57.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s58.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s58.png
new file mode 100644
index 000000000000..772cef30e50e
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s58.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s59.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s59.png
new file mode 100644
index 000000000000..e4fcaaa821de
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s59.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s6.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s6.png
new file mode 100644
index 000000000000..77addca1ea13
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s6.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s60.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s60.png
new file mode 100644
index 000000000000..5a81b2a8903e
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s60.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s61.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s61.png
new file mode 100644
index 000000000000..7ae50cbca165
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s61.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s62.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s62.png
new file mode 100644
index 000000000000..df1de61d7dd6
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s62.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s63.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s63.png
new file mode 100644
index 000000000000..8e64ef6cb245
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s63.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s7.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s7.png
new file mode 100644
index 000000000000..8938db21fc59
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s7.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s8.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s8.png
new file mode 100644
index 000000000000..ffd5659c993a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s8.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s9.png b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s9.png
new file mode 100644
index 000000000000..412b9a76c88a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/knobs/knob_rotary_s9.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_cue1.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_cue1.png
new file mode 100644
index 000000000000..51385d812b03
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_cue1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_cue2.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_cue2.png
new file mode 100644
index 000000000000..51385d812b03
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_cue2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_1.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_1.png
new file mode 100644
index 000000000000..17efebc335ff
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_2.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_2.png
new file mode 100644
index 000000000000..33f3af022766
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_3.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_3.png
new file mode 100644
index 000000000000..64ee7baf16db
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_3.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_4.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_4.png
new file mode 100644
index 000000000000..2e69a88f10f3
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue1_4.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_1.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_1.png
new file mode 100644
index 000000000000..17efebc335ff
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_2.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_2.png
new file mode 100644
index 000000000000..33f3af022766
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_3.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_3.png
new file mode 100644
index 000000000000..64ee7baf16db
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_3.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_4.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_4.png
new file mode 100644
index 000000000000..2e69a88f10f3
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_hotcue2_4.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_in1.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_in1.png
new file mode 100644
index 000000000000..3afb405d9af0
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_in1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_in2.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_in2.png
new file mode 100644
index 000000000000..3afb405d9af0
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_in2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_out1.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_out1.png
new file mode 100644
index 000000000000..4afdd3ccd8f5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_out1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_out2.png b/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_out2.png
new file mode 100644
index 000000000000..4afdd3ccd8f5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/marker_loop_out2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/skin.xml b/res/skins/Deere1280x1024-SXGA-Effects/skin.xml
new file mode 100644
index 000000000000..343e0e7c5c24
--- /dev/null
+++ b/res/skins/Deere1280x1024-SXGA-Effects/skin.xml
@@ -0,0 +1,6666 @@
+
+
+
+
+
+
+
+
+ background1280x1024.png
+ #000000
+
+
+
+
+ 20,305
+ 350,43
+ horizontal
+
+
+
+ 0,0
+ 22,43
+ vertical
+
+
+ 1
+
+ 0
+ btn_nudge_up2_over.png
+ btn_nudge_up2.png
+
+
+ [EffectChain1],next_chain
+ true
+ LeftButton
+
+
+ [EffectChain1],next_chain
+ false
+ LeftButton
+
+
+
+ 1
+
+ 0
+ btn_nudge_down2_over.png
+ btn_nudge_down2.png
+
+
+ [EffectChain1],prev_chain
+ true
+ LeftButton
+
+
+ [EffectChain1],prev_chain
+ false
+ LeftButton
+
+
+
+
+
+ 20,5
+ 70,25
+ 1
+
+
+
+ 70,5
+ 2
+
+ 0
+ btn_hotcue1_1_over.png
+ btn_hotcue1_1.png
+
+
+ 1
+ btn_hotcue1_1.png
+ btn_hotcue1_1_over.png
+
+
+ [EffectChain1],channel_[Channel1]
+
+
+
+ 90,5
+ 2
+
+ 0
+ btn_hotcue2_1_over.png
+ btn_hotcue2_1.png
+
+
+ 1
+ btn_hotcue2_1.png
+ btn_hotcue2_1_over.png
+
+
+ [EffectChain1],channel_[Channel2]
+
+
+
+ 115,5
+ 2
+
+ 0
+ btn_pfl1.png
+ btn_pfl1.png
+
+
+ 1
+ btn_pfl1_over.png
+ btn_pfl1_over.png
+
+
+ [EffectChain1],channel_[Headphone]
+
+
+
+ 160,5
+ 2
+
+ 0
+ btn_play1.png
+ btn_play1.png
+
+
+ 1
+ btn_play1_over.png
+ btn_play1_over.png
+
+
+ [EffectChain1],channel_[Master]
+
+
+
+ 205,5
+ 2
+
+ 0
+ btn_fx1.png
+ btn_fx1.png
+
+
+ 1
+ btn_fx1_over.png
+ btn_fx1_over.png
+
+
+ [EffectChain1],enabled
+
+
+
+ 39
+ knobs/knob_rotary_s%1.png
+ 250,0
+
+ [EffectChain1],parameter
+
+
+
+
+
+
+
+ 875,305
+ 350,43
+ horizontal
+
+
+
+ 0,0
+ 22,43
+ vertical
+
+
+ 1
+
+ 0
+ btn_nudge_up2_over.png
+ btn_nudge_up2.png
+
+
+ [EffectChain2],next_chain
+ true
+ LeftButton
+
+
+ [EffectChain2],next_chain
+ false
+ LeftButton
+
+
+
+ 1
+
+ 0
+ btn_nudge_down2_over.png
+ btn_nudge_down2.png
+
+
+ [EffectChain2],prev_chain
+ true
+ LeftButton
+
+
+ [EffectChain2],prev_chain
+ false
+ LeftButton
+
+
+
+
+
+ 20,5
+ 70,25
+ 2
+
+
+
+ 70,5
+ 2
+
+ 0
+ btn_hotcue1_2_over.png
+ btn_hotcue1_2.png
+
+
+ 1
+ btn_hotcue1_2.png
+ btn_hotcue1_2_over.png
+
+
+ [EffectChain2],channel_[Channel1]
+
+
+
+ 90,5
+ 2
+
+ 0
+ btn_hotcue2_2_over.png
+ btn_hotcue2_2.png
+
+
+ 1
+ btn_hotcue2_2.png
+ btn_hotcue2_2_over.png
+
+
+ [EffectChain2],channel_[Channel2]
+
+
+
+ 115,5
+ 2
+
+ 0
+ btn_pfl1.png
+ btn_pfl1.png
+
+
+ 1
+ btn_pfl1_over.png
+ btn_pfl1_over.png
+
+
+ [EffectChain2],channel_[Headphone]
+
+
+
+ 160,5
+ 2
+
+ 0
+ btn_play1.png
+ btn_play1.png
+
+
+ 1
+ btn_play1_over.png
+ btn_play1_over.png
+
+
+ [EffectChain2],channel_[Master]
+
+
+
+ 205,5
+ 2
+
+ 0
+ btn_fx1.png
+ btn_fx1.png
+
+
+ 1
+ btn_fx1_over.png
+ btn_fx1_over.png
+
+
+ [EffectChain2],enabled
+
+
+
+ 39
+ knobs/knob_rotary_s%1.png
+ 250,0
+
+ [EffectChain2],parameter
+
+
+
+
+
+
+
+ 0,364
+
+ 1280,560
+ vertical
+
+
+
+
+
+ i,110
+ horizontal
+
+
+
+
+ 0,0
+ 312,110
+
+
+
+
+ Track title
Displays the artist and title of the loaded track.
Informations are extracted from the tracks tags.
+
+ [Sampler1]
+ 5,3
+ 205,21
+
+
+
+
+
+ Tempo
Displays the tempo of the loaded track in BPM (beats per minute)
+
+ [Sampler1]
+ 211,3
+ 55,21
+
+ right
+
+ [Sampler1],visual_bpm
+
+
+
+
+
+
+
+ Tempo and BPM tap
Displays the tempo of the loaded track in BPM (beats per minute)
When tapped repeatedly, adjusts the BPM to match the tapped BPM
+
+
+ 1
+
+ 0
+ btn_tap_sampler_over.png
+ btn_tap_sampler.png
+
+ 216,3
+
+ [Sampler1],bpm_tap
+ true
+
+
+
+
+
+ Waveform overview
Shows information about the track currently loaded in this channel.
Jump around in the track by clicking somewhere on the waveform.
Drop tracks from library or external file manager here.
+
+ [Sampler1]
+ 5,25
+ 210,35
+ #3087BF
+ #00FF00
+
+ [Sampler1],playposition
+ false
+
+
+
+
+
+ Gain
Adjusts the pre-fader gain of the track (to avoid clipping)
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 222,64
+
+ [Sampler1],pregain
+
+
+
+
+
+ Peak Indicator
Indicates when the signal on the channel is clipping,
(too loud for the hardware and is being distorted).
+
+ btn_clipping_sampler_over.png
+ btn_clipping_sampler.png
+ 267,3
+
+ [Sampler1],PeakIndicator
+
+
+
+
+
+ Channel volume meter
Shows the current channel volume
+
+ btn_volume_display_sampler_over.png
+ btn_volume_display_sampler.png
+ 267,24
+ false
+ 5
+ 500
+ 50
+ 2
+
+ [Sampler1],VuMeter
+
+
+
+
+
+ Pitch control
Changes the tempo of the track currently loaded when it is moved
Right-click: Reset to default value
+
+ knob_pitch_sampler.png
+ slider_pitch_sampler.png
+ 279,7
+ false
+
+ [Sampler1],rate
+ false
+
+
+
+
+
+ Play/Pause
Left-click: Toggles playing or pausing the track.
Right-click: Jumps to the beginning of the track.
+
+ 2
+
+ 0
+ btn_play_sampler.png
+ btn_play_sampler.png
+
+
+ 1
+ btn_play_sampler_over.png
+ btn_play_sampler_over.png
+
+ 10,71
+
+ [Sampler1],play
+ true
+ LeftButton
+
+
+ [Sampler1],start
+ true
+ RightButton
+
+
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_1_over.png
+ btn_hotcue1_1.png
+
+
+ 1
+ btn_hotcue1_1.png
+ btn_hotcue1_1_over.png
+
+ 80,71
+
+ [Sampler1],hotcue_1_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler1],hotcue_1_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler1],hotcue_1_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_2_over.png
+ btn_hotcue1_2.png
+
+
+ 1
+ btn_hotcue1_2.png
+ btn_hotcue1_2_over.png
+
+ 106,71
+
+ [Sampler1],hotcue_2_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler1],hotcue_2_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler1],hotcue_2_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_3_over.png
+ btn_hotcue1_3.png
+
+
+ 1
+ btn_hotcue1_3.png
+ btn_hotcue1_3_over.png
+
+ 132,71
+
+ [Sampler1],hotcue_3_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler1],hotcue_3_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler1],hotcue_3_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_4_over.png
+ btn_hotcue1_4.png
+
+
+ 1
+ btn_hotcue1_4.png
+ btn_hotcue1_4_over.png
+
+ 158,71
+
+ [Sampler1],hotcue_4_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler1],hotcue_4_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler1],hotcue_4_enabled
+ false
+
+
+
+
+
+ Headphone
Sends the selected channel's audio to the Headphones output audio device
selected in Preferences→Sound Hardware.
+
+ 2
+
+ 0
+ btn_pfl_sampler.png
+ btn_pfl_sampler.png
+
+
+ 1
+ btn_pfl_sampler_over.png
+ btn_pfl_sampler_over.png
+
+ 193,71
+
+ [Sampler1],pfl
+
+
+
+
+
+ Eject
Eject currently loaded track from player.
+
+ 1
+
+ 0
+ btn_eject_sampler_over.png
+ btn_eject_sampler.png
+
+ 242,24
+
+ [Sampler1],eject
+ true
+ LeftButton
+ false
+
+
+
+
+
+ Repeat
When active the track will repeat if you go past the end or reverse before the start.
+
+ 2
+
+ 0
+ btn_repeat_sampler.png
+ btn_repeat_sampler.png
+
+
+ 1
+ btn_repeat_sampler_over.png
+ btn_repeat_sampler_over.png
+
+ 216,24
+
+ [Sampler1],repeat
+
+
+
+
+
+ Mix orientation
Set the channel's mix orientation.
L = left side of crossfader, R = right side of crossfader, M = center (default)
+
+ 3
+
+ 0
+ btn_orientation_sampler_left_over.png
+ btn_orientation_sampler_left_over.png
+
+
+ 1
+ btn_orientation_sampler_master.png
+ btn_orientation_sampler_master.png
+
+
+ 2
+ btn_orientation_sampler_right_over.png
+ btn_orientation_sampler_right_over.png
+
+ 217,42
+
+ [Sampler1],orientation
+ true
+ LeftButton
+
+
+
+
+
+ Key-lock
Activates pitch-independent time stretch in the deck.
Toggling key-lock during playback may result in a momentary audio glitch.
+
+ 2
+
+ 0
+ btn_keylock_sampler.png
+ btn_keylock_sampler.png
+
+
+ 1
+ btn_keylock_sampler_over.png
+ btn_keylock_sampler_over.png
+
+ 241,42
+
+ [Sampler1],keylock
+
+
+
+
+
+
+ 0,0
+ 312,110
+
+
+
+
+ Track title
Displays the artist and title of the loaded track.
Informations are extracted from the tracks tags.
+
+ [Sampler2]
+ 5,3
+ 205,21
+
+
+
+
+
+ Tempo
Displays the tempo of the loaded track in BPM (beats per minute)
+
+ [Sampler2]
+ 211,3
+ 55,21
+
+ right
+
+ [Sampler2],visual_bpm
+
+
+
+
+
+
+
+ Tempo and BPM tap
Displays the tempo of the loaded track in BPM (beats per minute)
When tapped repeatedly, adjusts the BPM to match the tapped BPM
+
+
+ 1
+
+ 0
+ btn_tap_sampler_over.png
+ btn_tap_sampler.png
+
+ 216,3
+
+ [Sampler2],bpm_tap
+ true
+
+
+
+
+
+ Waveform overview
Shows information about the track currently loaded in this channel.
Jump around in the track by clicking somewhere on the waveform.
Drop tracks from library or external file manager here.
+
+ [Sampler2]
+ 5,25
+ 210,35
+ #3087BF
+ #00FF00
+
+ [Sampler2],playposition
+ false
+
+
+
+
+
+ Gain
Adjusts the pre-fader gain of the track (to avoid clipping)
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 222,64
+
+ [Sampler2],pregain
+
+
+
+
+
+ Peak Indicator
Indicates when the signal on the channel is clipping,
(too loud for the hardware and is being distorted).
+
+ btn_clipping_sampler_over.png
+ btn_clipping_sampler.png
+ 267,3
+
+ [Sampler2],PeakIndicator
+
+
+
+
+
+ Channel volume meter
Shows the current channel volume
+
+ btn_volume_display_sampler_over.png
+ btn_volume_display_sampler.png
+ 267,24
+ false
+ 5
+ 500
+ 50
+ 2
+
+ [Sampler2],VuMeter
+
+
+
+
+
+ Pitch control
Changes the tempo of the track currently loaded when it is moved
Right-click: Reset to default value
+
+ knob_pitch_sampler.png
+ slider_pitch_sampler.png
+ 279,7
+ false
+
+ [Sampler2],rate
+ false
+
+
+
+
+
+ Play/Pause
Left-click: Toggles playing or pausing the track.
Right-click: Jumps to the beginning of the track.
+
+ 2
+
+ 0
+ btn_play_sampler.png
+ btn_play_sampler.png
+
+
+ 1
+ btn_play_sampler_over.png
+ btn_play_sampler_over.png
+
+ 10,71
+
+ [Sampler2],play
+ true
+ LeftButton
+
+
+ [Sampler2],start
+ true
+ RightButton
+
+
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_1_over.png
+ btn_hotcue1_1.png
+
+
+ 1
+ btn_hotcue1_1.png
+ btn_hotcue1_1_over.png
+
+ 80,71
+
+ [Sampler2],hotcue_1_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler2],hotcue_1_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler2],hotcue_1_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_2_over.png
+ btn_hotcue1_2.png
+
+
+ 1
+ btn_hotcue1_2.png
+ btn_hotcue1_2_over.png
+
+ 106,71
+
+ [Sampler2],hotcue_2_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler2],hotcue_2_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler2],hotcue_2_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_3_over.png
+ btn_hotcue1_3.png
+
+
+ 1
+ btn_hotcue1_3.png
+ btn_hotcue1_3_over.png
+
+ 132,71
+
+ [Sampler2],hotcue_3_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler2],hotcue_3_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler2],hotcue_3_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_4_over.png
+ btn_hotcue1_4.png
+
+
+ 1
+ btn_hotcue1_4.png
+ btn_hotcue1_4_over.png
+
+ 158,71
+
+ [Sampler2],hotcue_4_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler2],hotcue_4_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler2],hotcue_4_enabled
+ false
+
+
+
+
+
+ Headphone
Sends the selected channel's audio to the Headphones output audio device
selected in Preferences→Sound Hardware.
+
+ 2
+
+ 0
+ btn_pfl_sampler.png
+ btn_pfl_sampler.png
+
+
+ 1
+ btn_pfl_sampler_over.png
+ btn_pfl_sampler_over.png
+
+ 193,71
+
+ [Sampler2],pfl
+
+
+
+
+
+ Eject
Eject currently loaded track from player.
+
+ 1
+
+ 0
+ btn_eject_sampler_over.png
+ btn_eject_sampler.png
+
+ 242,24
+
+ [Sampler2],eject
+ true
+ LeftButton
+ false
+
+
+
+
+
+ Repeat
When active the track will repeat if you go past the end or reverse before the start.
+
+ 2
+
+ 0
+ btn_repeat_sampler.png
+ btn_repeat_sampler.png
+
+
+ 1
+ btn_repeat_sampler_over.png
+ btn_repeat_sampler_over.png
+
+ 216,24
+
+ [Sampler2],repeat
+
+
+
+
+
+ Mix orientation
Set the channel's mix orientation.
L = left side of crossfader, R = right side of crossfader, M = center (default)
+
+ 3
+
+ 0
+ btn_orientation_sampler_left_over.png
+ btn_orientation_sampler_left_over.png
+
+
+ 1
+ btn_orientation_sampler_master.png
+ btn_orientation_sampler_master.png
+
+
+ 2
+ btn_orientation_sampler_right_over.png
+ btn_orientation_sampler_right_over.png
+
+ 217,42
+
+ [Sampler2],orientation
+ true
+ LeftButton
+
+
+
+
+
+ Key-lock
Activates pitch-independent time stretch in the deck.
Toggling key-lock during playback may result in a momentary audio glitch.
+
+ 2
+
+ 0
+ btn_keylock_sampler.png
+ btn_keylock_sampler.png
+
+
+ 1
+ btn_keylock_sampler_over.png
+ btn_keylock_sampler_over.png
+
+ 241,42
+
+ [Sampler2],keylock
+
+
+
+
+
+
+ 0,0
+ 312,110
+
+
+
+
+ Track title
Displays the artist and title of the loaded track.
Informations are extracted from the tracks tags.
+
+ [Sampler3]
+ 5,3
+ 205,21
+
+
+
+
+
+ Tempo
Displays the tempo of the loaded track in BPM (beats per minute)
+
+ [Sampler3]
+ 211,3
+ 55,21
+
+ right
+
+ [Sampler3],visual_bpm
+
+
+
+
+
+
+
+ Tempo and BPM tap
Displays the tempo of the loaded track in BPM (beats per minute)
When tapped repeatedly, adjusts the BPM to match the tapped BPM
+
+
+ 1
+
+ 0
+ btn_tap_sampler_over.png
+ btn_tap_sampler.png
+
+ 216,3
+
+ [Sampler3],bpm_tap
+ true
+
+
+
+
+
+ Waveform overview
Shows information about the track currently loaded in this channel.
Jump around in the track by clicking somewhere on the waveform.
Drop tracks from library or external file manager here.
+
+ [Sampler3]
+ 5,25
+ 210,35
+ #3087BF
+ #00FF00
+
+ [Sampler3],playposition
+ false
+
+
+
+
+
+ Gain
Adjusts the pre-fader gain of the track (to avoid clipping)
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 222,64
+
+ [Sampler3],pregain
+
+
+
+
+
+ Peak Indicator
Indicates when the signal on the channel is clipping,
(too loud for the hardware and is being distorted).
+
+ btn_clipping_sampler_over.png
+ btn_clipping_sampler.png
+ 267,3
+
+ [Sampler3],PeakIndicator
+
+
+
+
+
+ Channel volume meter
Shows the current channel volume
+
+ btn_volume_display_sampler_over.png
+ btn_volume_display_sampler.png
+ 267,24
+ false
+ 5
+ 500
+ 50
+ 2
+
+ [Sampler3],VuMeter
+
+
+
+
+
+ Pitch control
Changes the tempo of the track currently loaded when it is moved
Right-click: Reset to default value
+
+ knob_pitch_sampler.png
+ slider_pitch_sampler.png
+ 279,7
+ false
+
+ [Sampler3],rate
+ false
+
+
+
+
+
+ Play/Pause
Left-click: Toggles playing or pausing the track.
Right-click: Jumps to the beginning of the track.
+
+ 2
+
+ 0
+ btn_play_sampler.png
+ btn_play_sampler.png
+
+
+ 1
+ btn_play_sampler_over.png
+ btn_play_sampler_over.png
+
+ 10,71
+
+ [Sampler3],play
+ true
+ LeftButton
+
+
+ [Sampler3],start
+ true
+ RightButton
+
+
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_1_over.png
+ btn_hotcue1_1.png
+
+
+ 1
+ btn_hotcue1_1.png
+ btn_hotcue1_1_over.png
+
+ 80,71
+
+ [Sampler3],hotcue_1_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler3],hotcue_1_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler3],hotcue_1_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_2_over.png
+ btn_hotcue1_2.png
+
+
+ 1
+ btn_hotcue1_2.png
+ btn_hotcue1_2_over.png
+
+ 106,71
+
+ [Sampler3],hotcue_2_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler3],hotcue_2_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler3],hotcue_2_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_3_over.png
+ btn_hotcue1_3.png
+
+
+ 1
+ btn_hotcue1_3.png
+ btn_hotcue1_3_over.png
+
+ 132,71
+
+ [Sampler3],hotcue_3_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler3],hotcue_3_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler3],hotcue_3_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_4_over.png
+ btn_hotcue1_4.png
+
+
+ 1
+ btn_hotcue1_4.png
+ btn_hotcue1_4_over.png
+
+ 158,71
+
+ [Sampler3],hotcue_4_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler3],hotcue_4_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler3],hotcue_4_enabled
+ false
+
+
+
+
+
+ Headphone
Sends the selected channel's audio to the Headphones output audio device
selected in Preferences→Sound Hardware.
+
+ 2
+
+ 0
+ btn_pfl_sampler.png
+ btn_pfl_sampler.png
+
+
+ 1
+ btn_pfl_sampler_over.png
+ btn_pfl_sampler_over.png
+
+ 193,71
+
+ [Sampler3],pfl
+
+
+
+
+
+ Eject
Eject currently loaded track from player.
+
+ 1
+
+ 0
+ btn_eject_sampler_over.png
+ btn_eject_sampler.png
+
+ 242,24
+
+ [Sampler3],eject
+ true
+ LeftButton
+ false
+
+
+
+
+
+ Repeat
When active the track will repeat if you go past the end or reverse before the start.
+
+ 2
+
+ 0
+ btn_repeat_sampler.png
+ btn_repeat_sampler.png
+
+
+ 1
+ btn_repeat_sampler_over.png
+ btn_repeat_sampler_over.png
+
+ 216,24
+
+ [Sampler3],repeat
+
+
+
+
+
+ Mix orientation
Set the channel's mix orientation.
L = left side of crossfader, R = right side of crossfader, M = center (default)
+
+ 3
+
+ 0
+ btn_orientation_sampler_left_over.png
+ btn_orientation_sampler_left_over.png
+
+
+ 1
+ btn_orientation_sampler_master.png
+ btn_orientation_sampler_master.png
+
+
+ 2
+ btn_orientation_sampler_right_over.png
+ btn_orientation_sampler_right_over.png
+
+ 217,42
+
+ [Sampler3],orientation
+ true
+ LeftButton
+
+
+
+
+
+ Key-lock
Activates pitch-independent time stretch in the deck.
Toggling key-lock during playback may result in a momentary audio glitch.
+
+ 2
+
+ 0
+ btn_keylock_sampler.png
+ btn_keylock_sampler.png
+
+
+ 1
+ btn_keylock_sampler_over.png
+ btn_keylock_sampler_over.png
+
+ 241,42
+
+ [Sampler3],keylock
+
+
+
+
+
+
+ 0,0
+ 312,110
+
+
+
+
+ Track title
Displays the artist and title of the loaded track.
Informations are extracted from the tracks tags.
+
+ [Sampler4]
+ 5,3
+ 205,21
+
+
+
+
+
+ Tempo
Displays the tempo of the loaded track in BPM (beats per minute)
+
+ [Sampler4]
+ 211,3
+ 55,21
+
+ right
+
+ [Sampler4],visual_bpm
+
+
+
+
+
+
+
+ Tempo and BPM tap
Displays the tempo of the loaded track in BPM (beats per minute)
When tapped repeatedly, adjusts the BPM to match the tapped BPM
+
+
+ 1
+
+ 0
+ btn_tap_sampler_over.png
+ btn_tap_sampler.png
+
+ 216,3
+
+ [Sampler4],bpm_tap
+ true
+
+
+
+
+
+ Waveform overview
Shows information about the track currently loaded in this channel.
Jump around in the track by clicking somewhere on the waveform.
Drop tracks from library or external file manager here.
+
+ [Sampler4]
+ 5,25
+ 210,35
+ #3087BF
+ #00FF00
+
+ [Sampler4],playposition
+ false
+
+
+
+
+
+ Gain
Adjusts the pre-fader gain of the track (to avoid clipping)
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 222,64
+
+ [Sampler4],pregain
+
+
+
+
+
+ Peak Indicator
Indicates when the signal on the channel is clipping,
(too loud for the hardware and is being distorted).
+
+ btn_clipping_sampler_over.png
+ btn_clipping_sampler.png
+ 267,3
+
+ [Sampler4],PeakIndicator
+
+
+
+
+
+ Channel volume meter
Shows the current channel volume
+
+ btn_volume_display_sampler_over.png
+ btn_volume_display_sampler.png
+ 267,24
+ false
+ 5
+ 500
+ 50
+ 2
+
+ [Sampler4],VuMeter
+
+
+
+
+
+ Pitch control
Changes the tempo of the track currently loaded when it is moved
Right-click: Reset to default value
+
+ knob_pitch_sampler.png
+ slider_pitch_sampler.png
+ 279,7
+ false
+
+ [Sampler4],rate
+ false
+
+
+
+
+
+ Play/Pause
Left-click: Toggles playing or pausing the track.
Right-click: Jumps to the beginning of the track.
+
+ 2
+
+ 0
+ btn_play_sampler.png
+ btn_play_sampler.png
+
+
+ 1
+ btn_play_sampler_over.png
+ btn_play_sampler_over.png
+
+ 10,71
+
+ [Sampler4],play
+ true
+ LeftButton
+
+
+ [Sampler4],start
+ true
+ RightButton
+
+
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_1_over.png
+ btn_hotcue1_1.png
+
+
+ 1
+ btn_hotcue1_1.png
+ btn_hotcue1_1_over.png
+
+ 80,71
+
+ [Sampler4],hotcue_1_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler4],hotcue_1_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler4],hotcue_1_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_2_over.png
+ btn_hotcue1_2.png
+
+
+ 1
+ btn_hotcue1_2.png
+ btn_hotcue1_2_over.png
+
+ 106,71
+
+ [Sampler4],hotcue_2_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler4],hotcue_2_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler4],hotcue_2_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_3_over.png
+ btn_hotcue1_3.png
+
+
+ 1
+ btn_hotcue1_3.png
+ btn_hotcue1_3_over.png
+
+ 132,71
+
+ [Sampler4],hotcue_3_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler4],hotcue_3_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler4],hotcue_3_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_4_over.png
+ btn_hotcue1_4.png
+
+
+ 1
+ btn_hotcue1_4.png
+ btn_hotcue1_4_over.png
+
+ 158,71
+
+ [Sampler4],hotcue_4_activate
+ true
+ LeftButton
+ false
+
+
+ [Sampler4],hotcue_4_clear
+ true
+ RightButton
+ false
+
+
+ [Sampler4],hotcue_4_enabled
+ false
+
+
+
+
+
+ Headphone
Sends the selected channel's audio to the Headphones output audio device
selected in Preferences→Sound Hardware.
+
+ 2
+
+ 0
+ btn_pfl_sampler.png
+ btn_pfl_sampler.png
+
+
+ 1
+ btn_pfl_sampler_over.png
+ btn_pfl_sampler_over.png
+
+ 193,71
+
+ [Sampler4],pfl
+
+
+
+
+
+ Eject
Eject currently loaded track from player.
+
+ 1
+
+ 0
+ btn_eject_sampler_over.png
+ btn_eject_sampler.png
+
+ 242,24
+
+ [Sampler4],eject
+ true
+ LeftButton
+ false
+
+
+
+
+
+ Repeat
When active the track will repeat if you go past the end or reverse before the start.
+
+ 2
+
+ 0
+ btn_repeat_sampler.png
+ btn_repeat_sampler.png
+
+
+ 1
+ btn_repeat_sampler_over.png
+ btn_repeat_sampler_over.png
+
+ 216,24
+
+ [Sampler4],repeat
+
+
+
+
+
+ Mix orientation
Set the channel's mix orientation.
L = left side of crossfader, R = right side of crossfader, M = center (default)
+
+ 3
+
+ 0
+ btn_orientation_sampler_left_over.png
+ btn_orientation_sampler_left_over.png
+
+
+ 1
+ btn_orientation_sampler_master.png
+ btn_orientation_sampler_master.png
+
+
+ 2
+ btn_orientation_sampler_right_over.png
+ btn_orientation_sampler_right_over.png
+
+ 217,42
+
+ [Sampler4],orientation
+ true
+ LeftButton
+
+
+
+
+
+ Key-lock
Activates pitch-independent time stretch in the deck.
Toggling key-lock during playback may result in a momentary audio glitch.
+
+ 2
+
+ 0
+ btn_keylock_sampler.png
+ btn_keylock_sampler.png
+
+
+ 1
+ btn_keylock_sampler_over.png
+ btn_keylock_sampler_over.png
+
+ 241,42
+
+ [Sampler4],keylock
+
+
+
+
+
+
+ [Samplers],show_samplers
+ visible
+
+
+
+
+
+ 0,0
+ 1280,i
+ horizontal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0,0
+ 1280,20
+
+
+
+
+ 0,0
+ i,i
+
+
+
+
+
+
+
+
+
+
+ Vinyl Control Mode
Absolute mode - track position equals needle position and speed
Relative mode - track speed equals needle speed regardless of needle position
Constant mode - track speed equals last known-steady speed regardless of needle input
+
+ 3
+
+ 0
+ btn_vinylcontrol_mode_abs1.png
+ btn_vinylcontrol_mode_abs1.png
+
+
+ 1
+ btn_vinylcontrol_mode_rel1.png
+ btn_vinylcontrol_mode_rel1.png
+
+
+ 2
+ btn_vinylcontrol_mode_const1.png
+ btn_vinylcontrol_mode_const1.png
+
+ 0,0
+
+ [Channel1],vinylcontrol_mode
+
+
+
+ Vinyl Control Mode
Absolute mode - track position equals needle position and speed
Relative mode - track speed equals needle speed regardless of needle position
Constant mode - track speed equals last known-steady speed regardless of needle input
+
+ 3
+
+ 0
+ btn_vinylcontrol_mode_abs2.png
+ btn_vinylcontrol_mode_abs2.png
+
+
+ 1
+ btn_vinylcontrol_mode_rel2.png
+ btn_vinylcontrol_mode_rel2.png
+
+
+ 2
+ btn_vinylcontrol_mode_const2.png
+ btn_vinylcontrol_mode_const2.png
+
+ 864,0
+
+ [Channel2],vinylcontrol_mode
+
+
+
+
+ Vinyl Cueing Mode
Determines how cue points are treated in vinyl control Relative mode
Off - Cue points ignored
One Cue - If needle is dropped after the cue point, track will seek to that cue point
Hot Cue - Track will seek to nearest previous hot cue point
+
+ 3
+
+ 0
+ btn_vinylcontrol_cueing_off1.png
+ btn_vinylcontrol_cueing_off1.png
+
+
+ 1
+ btn_vinylcontrol_cueing_one1.png
+ btn_vinylcontrol_cueing_one1.png
+
+
+ 2
+ btn_vinylcontrol_cueing_hot1.png
+ btn_vinylcontrol_cueing_hot1.png
+
+ 239,0
+
+ [Channel1],vinylcontrol_cueing
+
+
+
+ Vinyl Cueing Mode
Determines how cue points are treated in vinyl control Relative mode
Off - Cue points ignored
One Cue - If needle is dropped after the cue point, track will seek to that cue point
Hot Cue - Track will seek to nearest previous hot cue point
+
+ 3
+
+ 0
+ btn_vinylcontrol_cueing_off2.png
+ btn_vinylcontrol_cueing_off2.png
+
+
+ 1
+ btn_vinylcontrol_cueing_one2.png
+ btn_vinylcontrol_cueing_one2.png
+
+
+ 2
+ btn_vinylcontrol_cueing_hot2.png
+ btn_vinylcontrol_cueing_hot2.png
+
+ 1102,0
+
+ [Channel2],vinylcontrol_cueing
+
+
+
+
+
+
+ [Vinylcontrol],show_vinylcontrol
+ visible
+
+
+
+
+
+ 54,296
+ 280,58
+
+
+
+
+
+ Peak Indicator
Indicates when the signal on the channel is clipping,
(too loud for the hardware and is being distorted).
+
+ btn_clipping_microphone_over.png
+ btn_clipping_microphone.png
+ 193,22
+
+ [Microphone],PeakIndicator
+
+
+
+
+
+ Microphone volume meter
Outputs the current instantaneous microphone volume
+
+ btn_volume_display_microphone_over.png
+ btn_volume_display_microphone.png
+ 121,22
+ true
+ 5
+ 500
+ 50
+ 2
+
+ [Microphone],VuMeter
+
+
+
+
+
+ Mix orientation
Set the channel's mix orientation.
L = left side of crossfader, R = right side of crossfader, M = center (default)
+
+ 3
+
+ 0
+ btn_orientation_microphone_left_over.png
+ btn_orientation_microphone_left_over.png
+
+
+ 1
+ btn_orientation_microphone_master.png
+ btn_orientation_microphone_master.png
+
+
+ 2
+ btn_orientation_microphone_right_over.png
+ btn_orientation_microphone_right_over.png
+
+ 95,18
+
+ [Microphone],orientation
+ true
+ LeftButton
+
+
+
+
+
+ Microphone volume
Adjusts the microphone volume.
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 228,14
+
+ [Microphone],volume
+
+
+
+
+
+ Talkover
Hold-to-talk and mix microphone input into the master output.
+
+ 1
+
+ 0
+ btn_microphone_talkover_over.png
+ btn_microphone_talkover.png
+
+ 25,15
+
+ [Microphone],talkover
+ true
+ LeftButton
+
+
+
+
+ [Microphone],show_microphone
+ visible
+
+
+
+
+
+ 415,0
+ 449,353
+
+
+
+
+
+ 1,-1
+ 449,353
+
+
+
+
+ Microphone
Show/hide the microphone widget.
+
+ 2
+
+ 0
+ tab_microphone.png
+ tab_microphone.png
+
+
+ 1
+ tab_microphone_over.png
+ tab_microphone_over.png
+
+ 150,104
+
+ [Microphone],show_microphone
+
+
+
+
+
+ Sampler
Show/hide the Sampler widget.
+
+ 2
+
+ 0
+ tab_sampler.png
+ tab_sampler.png
+
+
+ 1
+ tab_sampler_over.png
+ tab_sampler_over.png
+
+ 189,104
+
+ [Samplers],show_samplers
+
+
+
+
+
+
+ Vinyl Control
Show/hide the Vinyl Control widget.
Activate Vinyl Control from the Menu→Options
+
+ 2
+
+ 0
+ tab_vinylcontrol.png
+ tab_vinylcontrol.png
+
+
+ 1
+ tab_vinylcontrol_over.png
+ tab_vinylcontrol_over.png
+
+ 253,104
+
+ [Vinylcontrol],show_vinylcontrol
+
+
+
+
+
+ Pitch rate
Displays the current pitch of the track based on the original tempo.
+
+ 1
+ 7,51
+ 50,14
+
+ center
+
+ [Channel1],rate
+
+
+
+ Pitch rate
Displays the current pitch of the track based on the original tempo.
+
+ 2
+ 394,51
+ 50,14
+
+ center
+
+ [Channel2],rate
+
+
+
+
+
+ Volume control
Adjust the volume of the selected channel.
Right-click: Reset to default value
+
+ knob_volume1.png
+ slider_volume1.png
+ 150,131
+ false
+
+ [Channel1],volume
+ false
+
+
+
+ Volume control
Adjust the volume of the selected channel.
Right-click: Reset to default value
+
+ knob_volume2.png
+ slider_volume2.png
+ 252,131
+ false
+
+ [Channel2],volume
+ false
+
+
+
+
+
+ Crossfader
Fade between the channels and define what you hear through the master output.
Right-click: Reset to default value
Change the crossfader curve in Preferences→Crossfader
+
+ knob_crossfader.png
+ slider_crossfader.png
+ 137,302
+ true
+
+ [Master],crossfader
+ false
+
+
+
+
+
+ Pitch control
Changes the tempo of the track currently loaded when it is moved
Right-click: Reset to default value
+
+ knob_pitch1.png
+ slider_pitch1.png
+ 7,68
+ false
+
+ [Channel1],rate
+ false
+
+
+
+ Pitch control
Changes the tempo of the track currently loaded when it is moved
Right-click: Reset to default value
+
+ knob_pitch1.png
+ slider_pitch1.png
+ 393,68
+ false
+
+ [Channel2],rate
+ false
+
+
+
+
+
+ Syncronize tempo
Attempts to automatically match the tempo of the track in this channel
with tempo of the track in the other channel, based on the calculated BPM.
+
+ 1
+
+ 0
+ btn_sync1_over.png
+ btn_sync1.png
+
+ 7,24
+
+ [Channel1],beatsync
+ true
+ LeftButton
+
+
+
+ Syncronize tempo
Attempts to automatically match the tempo of the track in this channel
with tempo of the track in the other channel, based on the calculated BPM.
+
+ 1
+
+ 0
+ btn_sync2_over.png
+ btn_sync2.png
+
+ 392,24
+
+ [Channel2],beatsync
+ true
+ LeftButton
+
+
+
+
+
+ Raise pitch
Left-click: Sets the pitch higher
Right-click: Sets the pitch higher in small steps.
Change the the amount of the steps in Preferences→Interface menu.
+
+ 1
+
+ 0
+ btn_pitch_up1_over.png
+ btn_pitch_up1.png
+
+ 33,239
+
+ [Channel1],rate_perm_up
+ true
+ LeftButton
+
+
+ [Channel1],rate_perm_up_small
+ true
+ RightButton
+
+
+
+ Lower pitch
Left-click: Sets the pitch lower
Right-click: Sets the pitch lower in small steps.
Change the the amount of the steps in Preferences→Interface menu.
+
+ 1
+
+ 0
+ btn_pitch_down1_over.png
+ btn_pitch_down1.png
+
+ 7,239
+
+ [Channel1],rate_perm_down
+ true
+ LeftButton
+
+
+ [Channel1],rate_perm_down_small
+ true
+ RightButton
+
+
+
+
+ Raise pitch
Left-click: Sets the pitch higher
Right-click: Sets the pitch higher in small steps.
Change the the amount of the steps in Preferences→Interface menu.
+
+ 1
+
+ 0
+ btn_pitch_up2_over.png
+ btn_pitch_up2.png
+
+ 418,239
+
+ [Channel2],rate_perm_up
+ true
+ LeftButton
+
+
+ [Channel2],rate_perm_up_small
+ true
+ RightButton
+
+
+
+ Lower pitch
Left-click: Sets the pitch lower
Right-click: Sets the pitch lower in small steps.
Change the the amount of the steps in Preferences→Interface menu.
+
+ 1
+
+ 0
+ btn_pitch_down2_over.png
+ btn_pitch_down2.png
+
+ 392,239
+
+ [Channel2],rate_perm_down
+ true
+ LeftButton
+
+
+ [Channel2],rate_perm_down_small
+ true
+ RightButton
+
+
+
+
+
+ Raise pitch temporary (nudge)
Left-click: Holds the pitch higher while active
Right-click: Holds the pitch higher (small amount) while active
Change the amount of the steps in Preferences→Interface
+
+ 1
+
+ 0
+ btn_nudge_up1_over.png
+ btn_nudge_up1.png
+
+ 33,265
+
+ [Channel1],rate_temp_up
+ true
+ LeftButton
+
+
+ [Channel1],rate_temp_up_small
+ true
+ RightButton
+
+
+
+ Lower pitch temporary (nudge)
Left-click: Holds the pitch lower while active
Right-click: Holds the pitch lower (small amount) while active
Change the amount of the steps in Preferences→Interface.
+
+ 1
+
+ 0
+ btn_nudge_down1_over.png
+ btn_nudge_down1.png
+
+ 7,265
+
+ [Channel1],rate_temp_down
+ true
+ LeftButton
+
+
+ [Channel1],rate_temp_down_small
+ true
+ RightButton
+
+
+
+
+ Raise pitch temporary (nudge)
Left-click: Holds the pitch higher while active
Right-click: Holds the pitch higher (small amount) while active
Change the amount of the steps in Preferences→Interface
+
+ 1
+
+ 0
+ btn_nudge_up2_over.png
+ btn_nudge_up2.png
+
+ 418,265
+
+ [Channel2],rate_temp_up
+ true
+ LeftButton
+
+
+ [Channel2],rate_temp_up_small
+ true
+ RightButton
+
+
+
+ Lower pitch temporary (nudge)
Left-click: Holds the pitch lower while active
Right-click: Holds the pitch lower (small amount) while active
Change the amount of the steps in Preferences→Interface.
+
+ 1
+
+ 0
+ btn_nudge_down2_over.png
+ btn_nudge_down2.png
+
+ 392,265
+
+ [Channel2],rate_temp_down
+ true
+ LeftButton
+
+
+ [Channel2],rate_temp_down_small
+ true
+ RightButton
+
+
+
+
+
+ Play/Pause
Left-click: Toggles playing or pausing the track.
Right-click: Places a Cue-point at the current position on the waveform.
+
+ 2
+ true
+
+ 0
+ btn_play1.png
+ btn_play1.png
+
+
+ 1
+ btn_play1_over.png
+ btn_play1_over.png
+
+ 75,311
+
+ [Channel1],play
+ true
+ LeftButton
+
+
+ [Channel1],cue_set
+ true
+ RightButton
+ false
+
+
+
+ Play/Pause
Left-click: Toggles playing or pausing the track.
Right-click: Places a Cue-point at the current position on the waveform.
+
+ 2
+ true
+
+ 0
+ btn_play2.png
+ btn_play2.png
+
+
+ 1
+ btn_play2_over.png
+ btn_play2_over.png
+
+ 392,311
+
+ [Channel2],play
+ true
+ LeftButton
+
+
+ [Channel2],cue_set
+ true
+ RightButton
+ false
+
+
+
+
+
+ Cue
Left-click (while playing): The track will seek to the cue-point and stop (=CDJ) OR play (=simple).
Change the default cue behaviour in Preferences→Interface.
Left-click (while stopped): Places a cue-point at the current position on the waveform.
Right-click: The track will seek to the cue-point and stop.
+
+ 1
+
+ 0
+ btn_cue1_over.png
+ btn_cue1.png
+
+ 7,311
+
+ [Channel1],cue_default
+ true
+ LeftButton
+
+
+ [Channel1],cue_gotoandstop
+ true
+ RightButton
+
+
+
+ Cue
Left-click (while playing): The track will seek to the cue-point and stop (=CDJ) OR play (=simple).
Change the default cue behaviour in Preferences→Interface.
Left-click (while stopped): Places a cue-point at the current position on the waveform.
Right-click: The track will seek to the cue-point and stop.
+
+ 1
+
+ 0
+ btn_cue2_over.png
+ btn_cue2.png
+
+ 324,311
+
+ [Channel2],cue_default
+ true
+ LeftButton
+
+
+ [Channel2],cue_gotoandstop
+ true
+ RightButton
+
+
+
+
+
+ Headphone
Sends the selected channel's audio to the Headphones output audio device
selected in Preferences→Sound Hardware.
+
+ 2
+
+ 0
+ btn_pfl1.png
+ btn_pfl1.png
+
+
+ 1
+ btn_pfl1_over.png
+ btn_pfl1_over.png
+
+ 75,265
+
+ [Channel1],pfl
+
+
+
+ Headphone
Sends the selected channel's audio to the Headphones output audio device
selected in Preferences→Sound Hardware.
+
+ 2
+
+ 0
+ btn_pfl2.png
+ btn_pfl2.png
+
+
+ 1
+ btn_pfl2_over.png
+ btn_pfl2_over.png
+
+ 324,265
+
+ [Channel2],pfl
+
+
+
+
+
+ Flanger
Toggles the flange effect. Use the depth/delay/lfo knobs to adjust
+
+ 2
+
+ 0
+ btn_fx1.png
+ btn_fx1.png
+
+
+ 1
+ btn_fx1_over.png
+ btn_fx1_over.png
+
+ 75,67
+
+ [Channel1],flanger
+
+
+
+ Flanger
Toggles the flange effect. Use the depth/delay/lfo knobs to adjust
+
+ 2
+
+ 0
+ btn_fx2.png
+ btn_fx1.png
+
+
+ 1
+ btn_fx2_over.png
+ btn_fx2_over.png
+
+ 324,67
+
+ [Channel2],flanger
+
+
+
+
+
+ High EQ kill
Holds the gain of the high EQ to zero while active.
+
+ 2
+
+ 0
+ btn_kill_over.png
+ btn_kill.png
+
+
+ 1
+ btn_kill.png
+ btn_kill_over.png
+
+ 73,159
+
+ [Channel1],filterHighKill
+ true
+ LeftButton
+
+
+
+ Mid EQ kill
Holds the gain of the mid EQ to zero while active.
+
+ 2
+
+ 0
+ btn_kill_over.png
+ btn_kill.png
+
+
+ 1
+ btn_kill.png
+ btn_kill_over.png
+
+ 73,198
+
+ [Channel1],filterMidKill
+ true
+ LeftButton
+
+
+
+ Low EQ kill
Holds the gain of the low EQ to zero while active.
+
+ 2
+
+ 0
+ btn_kill_over.png
+ btn_kill.png
+
+
+ 1
+ btn_kill.png
+ btn_kill_over.png
+
+ 73,236
+
+ [Channel1],filterLowKill
+ true
+ LeftButton
+
+
+
+
+ High EQ kill
Holds the gain of the high EQ to zero while active.
+
+ 2
+
+ 0
+ btn_kill_over.png
+ btn_kill.png
+
+
+ 1
+ btn_kill.png
+ btn_kill_over.png
+
+ 358,159
+
+ [Channel2],filterHighKill
+ true
+ LeftButton
+
+
+
+ Mid EQ kill
Holds the gain of the mid EQ to zero while active.
+
+ 2
+
+ 0
+ btn_kill_over.png
+ btn_kill.png
+
+
+ 1
+ btn_kill.png
+ btn_kill_over.png
+
+ 358,198
+
+ [Channel2],filterMidKill
+ true
+ LeftButton
+
+
+
+ Low EQ kill
Holds the gain of the low EQ to zero while active.
+
+ 2
+
+ 0
+ btn_kill_over.png
+ btn_kill.png
+
+
+ 1
+ btn_kill.png
+ btn_kill_over.png
+
+ 358,236
+
+ [Channel2],filterLowKill
+ true
+ LeftButton
+
+
+
+
+
+ Master volume
Adjusts the Master output volume.
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 311,15
+
+ [Master],volume
+
+
+
+ Balance
Adjusts the left/right channel balance on the Master output.
Right-click: Reset to default value
+
+ 63
+ knobs/knob_rotary_s%1.png
+ 236,15
+
+ [Master],balance
+
+
+
+
+
+ Headphone volume
Adjusts the headphone output volume.
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 101,15
+
+ [Master],headVolume
+
+
+
+ Headphone mix
Controls what you hear on the headphone output.
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 175,15
+
+ [Master],headMix
+
+
+
+
+
+ Flanger delay
Adjusts the phase delay of the flange effect (when active).
Right-click: Reset to default value
+
+ 63
+ knobs/knob_rotary_s%1.png
+ 265,62
+
+ [EffectChain1_Effect1_Parameter1],value_normalized
+
+
+
+ Flanger depth
Adjusts the intensity of the flange effect (when active).
Right-click: Reset to default value
+
+ 63
+ knobs/knob_rotary_s%1.png
+ 206,62
+
+ [EffectChain1_Effect1_Parameter2],value_normalized
+
+
+
+ Flanger LFO period
Adjusts the wavelength of the flange effect (when active).
Right-click: Reset to default value
+
+ 63
+ knobs/knob_rotary_s%1.png
+ 146,62
+
+ [EffectChain1_Effect1_Parameter3],value_normalized
+
+
+
+
+
+ Gain
Adjusts the pre-fader gain of the track (to avoid clipping)
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 101,102
+
+ [Channel1],pregain
+
+
+
+ High EQ
Adjusts the gain of the high EQ filter.
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 101,141
+
+ [Channel1],filterHigh
+
+
+
+ Mid EQ
Adjusts the gain of the mid EQ filter.
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 101,180
+
+ [Channel1],filterMid
+
+
+
+ Low EQ
Adjusts the gain of the low EQ filter.
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 101,219
+
+ [Channel1],filterLow
+
+
+
+
+ Gain
Adjusts the pre-fader gain of the track (to avoid clipping)
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 310,102
+
+ [Channel2],pregain
+
+
+
+ High EQ
Adjusts the gain of the high EQ filter.
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 310,141
+
+ [Channel2],filterHigh
+
+
+
+ Mid EQ
Adjusts the gain of the mid EQ filter.
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 310,180
+
+ [Channel2],filterMid
+
+
+
+ Low EQ
Adjusts the gain of the low EQ filter.
Right-click: Reset to default value
+
+ 64
+ knobs/knob_rotary_s%1.png
+ 310,219
+
+ [Channel2],filterLow
+
+
+
+
+
+ Channel volume meter
Shows the current channel volume
+
+ btn_volume_display1_over.png
+ btn_volume_display1.png
+ 200,146
+ false
+ 5
+ 500
+ 50
+ 2
+
+ [Channel1],VuMeter
+
+
+
+ Channel volume meter
Shows the current channel volume
+
+ btn_volume_display2_over.png
+ btn_volume_display2.png
+ 239,146
+ false
+ 5
+ 500
+ 50
+ 2
+
+ [Channel2],VuMeter
+
+
+
+
+ Master channel volume meter
Outputs the current instantaneous master volume for the left channel.
+
+ btn_volume_display_master1_over.png
+ btn_volume_display_master1.png
+ 215,146
+ 5
+ 500
+ 50
+ 2
+
+ [Master],VuMeterL
+
+
+
+ Master channel volume meter
Outputs the current instantaneous master volume for the right channel.
+
+ btn_volume_display_master2_over.png
+ btn_volume_display_master2.png
+ 224,146
+ 5
+ 500
+ 50
+ 2
+
+ [Master],VuMeterR
+
+
+
+
+
+ Peak Indicator
Indicates when the signal on the channel is clipping,
(too loud for the hardware and is being distorted).
+
+ btn_clipping1_over.png
+ btn_clipping1.png
+ 200,132
+
+ [Channel1],PeakIndicator
+
+
+
+ Peak Indicator
Indicates when the signal on the channel is clipping,
(too loud for the hardware and is being distorted).
+
+ btn_clipping2_over.png
+ btn_clipping2.png
+ 239,132
+
+ [Channel2],PeakIndicator
+
+
+
+ Master Peak Indicator
Indicates when the signal on the Master output is clipping,
(too loud for the hardware and is being distorted).
+
+ btn_clipping_master_over.png
+ btn_clipping_master.png
+ 215,132
+
+ [Master],PeakIndicator
+
+
+
+
+
+
+
+
+
+
+ 1,16
+ 416,279
+
+
+
+
+ Vinyl Status
Provides visual feedback with regards to vinyl control status
Green for control enabled
Blinking yellow for when the needle reaches the end of the record
Red for needle skip detected
+
+ 3
+ btn_vinylcontrol_indicator_horizontal1.png
+ btn_vinylcontrol_indicator_horizontal2.png
+ btn_vinylcontrol_indicator_horizontal3.png
+ 0,0
+
+ [Channel1],vinylcontrol_status
+
+
+
+ Vinyl Status
Provides visual feedback with regards to vinyl control status
Green for control enabled
Blinking yellow for when the needle reaches the end of the record
Red for needle skip detected
+
+ 3
+ btn_vinylcontrol_indicator_horizontal1.png
+ btn_vinylcontrol_indicator_horizontal2.png
+ btn_vinylcontrol_indicator_horizontal3.png
+ 0,213
+
+ [Channel1],vinylcontrol_status
+
+
+
+ Vinyl Status
Provides visual feedback with regards to vinyl control status
Green for control enabled
Blinking yellow for when the needle reaches the end of the record
Red for needle skip detected
+
+ 3
+ btn_vinylcontrol_indicator_vertical1.png
+ btn_vinylcontrol_indicator_vertical2.png
+ btn_vinylcontrol_indicator_vertical3.png
+ 0,0
+
+ [Channel1],vinylcontrol_status
+
+
+
+ Vinyl Status
Provides visual feedback with regards to vinyl control status
Green for control enabled
Blinking yellow for when the needle reaches the end of the record
Red for needle skip detected
+
+ 3
+ btn_vinylcontrol_indicator_vertical1.png
+ btn_vinylcontrol_indicator_vertical2.png
+ btn_vinylcontrol_indicator_vertical3.png
+ 415,0
+
+ [Channel1],vinylcontrol_status
+
+
+
+
+
+ 0,50
+ 415,119
+ horizontal
+
+
+
+
+
+
+ Waveform display
Shows the loaded tracks' waveforms near the playback position.
Use the mouse to scratch, halt, spin back and push forward a track.
Drop tracks from library or external file manager here.
+
+ 1
+ 0,0
+
+
+ #1C1C1C
+ style/style_bg_waveform.png
+ #1b1b1b
+ #0099FF
+ #ffffff
+ #00FF00
+ #00FF00
+ #000000
+
+ loop_start_position
+ loop_end_position
+ loop_enabled
+ #00FF00
+ #FFFFFF
+
+
+ loop_start_position
+ marker_loop_in1.png
+ LOOP IN
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+ loop_end_position
+ marker_loop_out1.png
+ LOOP OUT
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+
+ hotcue_1_position
+ marker_hotcue1_1.png
+ HOTCUE 1
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+ hotcue_2_position
+ marker_hotcue1_2.png
+ HOTCUE 2
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+ hotcue_3_position
+ marker_hotcue1_3.png
+ HOTCUE 3
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+ hotcue_4_position
+ marker_hotcue1_4.png
+ HOTCUE 4
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+ hotcue_5_position
+ HOTCUE 5
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_6_position
+ HOTCUE 6
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_7_position
+ HOTCUE 7
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_8_position
+ HOTCUE 8
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_9_position
+ HOTCUE 9
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_10_position
+ HOTCUE 10
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_11_position
+ HOTCUE 11
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_12_position
+ HOTCUE 12
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_13_position
+ HOTCUE 13
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_14_position
+ HOTCUE 14
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_15_position
+ HOTCUE 15
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_16_position
+ HOTCUE 16
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_17_position
+ HOTCUE 17
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_18_position
+ HOTCUE 18
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_19_position
+ HOTCUE 19
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_20_position
+ HOTCUE 20
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_21_position
+ HOTCUE 21
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_22_position
+ HOTCUE 22
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_23_position
+ HOTCUE 23
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_24_position
+ HOTCUE 24
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_25_position
+ HOTCUE 25
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_26_position
+ HOTCUE 26
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_27_position
+ HOTCUE 27
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_28_position
+ HOTCUE 28
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_29_position
+ HOTCUE 29
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_30_position
+ HOTCUE 30
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_31_position
+ HOTCUE 31
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_32_position
+ HOTCUE 32
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_33_position
+ HOTCUE 33
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_34_position
+ HOTCUE 34
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_35_position
+ HOTCUE 35
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_36_position
+ HOTCUE 36
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ cue_point
+ marker_cue1.png
+ CUE
+ bottom
+ #FF001C
+ #FFFFFF
+
+
+
+
+
+
+ 119,119
+
+
+
+
+ Spinning vinyl
Rotates during playback and shows the position of a track.
Use the mouse to scratch, halt, spin back and push forward a track.
Drop tracks from library or external file manager here.
If Vinyl control is enabled, it can display the timecoded vinyls signal quality (see Preferences→Vinyl Control).
+
+ 1
+ 0,0
+ i,i
+ vinyl_spinny1_background.png
+ vinyl_spinny1_foreground.png
+ vinyl_spinny1_foreground_ghost.png
+
+
+
+ [Spinny1],show_spinny
+ visible
+
+
+
+
+
+
+
+ Waveform overview
Shows information about the track currently loaded in this channel.
Jump around in the track by clicking somewhere on the waveform.
Drop tracks from library or external file manager here.
+
+ 1
+ 0,170
+ 337,43
+ #1C1C1C
+ style/style_bg_woverview.png
+ #0099FF
+ #00FF00
+
+ [Channel1],playposition
+ false
+
+
+
+
+
+ Track title
Displays the title of the loaded track.
Informations are extracted from the tracks tags.
+
+ title
+ 1
+ 0,1
+ 315,23
+
+
+
+
+ Track Artist
Displays the artist of the loaded track.
Informations are extracted from the tracks tags.
+
+ artist
+ 1
+ 0,26
+ 290,23
+
+
+
+
+ Tempo
Displays the tempo of the loaded track in BPM (beats per minute)
+
+ 1
+ 314,1
+ 100,23
+
+ right
+
+ [Channel1],visual_bpm
+
+
+
+
+
+ Time
Displays the elapsed or remaining time of the track loaded.
Click to toggle between time elapsed/remaining time.
+
+ 1
+ 294,26
+ 120,23
+
+ right
+
+ [Channel1],playposition
+
+
+
+
+
+
+
+ Tempo and BPM tap
Displays the tempo of the loaded track in BPM (beats per minute)
When tapped repeatedly, adjusts the BPM to match the tapped BPM
+
+
+ 1
+
+ 0
+ btn_tap1_over.png
+ btn_tap1.png
+
+ 313,0
+
+ [Channel1],bpm_tap
+ true
+
+
+
+
+
+ Spinning vinyl
Show/hide the spinning vinyl widget
+
+ 2
+
+ 0
+ btn_spinny1.png
+ btn_spinny1.png
+
+
+ 1
+ btn_spinny1_over.png
+ btn_spinny1_over.png
+
+ 338,171
+
+ [Spinny1],show_spinny
+ true
+ LeftButton
+
+
+
+
+
+ Adjust beatgrid
Adjust beatgrid so closest beat is aligned with the current playposition.
+
+ 1
+
+ 0
+ btn_beatgrid1_over.png
+ btn_beatgrid1.png
+
+ 338,193
+
+ [Channel1],beats_translate_curpos
+ true
+ LeftButton
+
+
+
+
+
+ Key-lock
Activates pitch-independent time stretch in the deck.
Toggling key-lock during playback may result in a momentary audio glitch.
+
+ 2
+
+ 0
+ btn_keylock1.png
+ btn_keylock1.png
+
+
+ 1
+ btn_keylock1_over.png
+ btn_keylock1_over.png
+
+ 390,193
+
+ [Channel1],keylock
+
+
+
+
+
+ Quantize
Toggles quantization in loops and cues
+
+ 2
+
+ 0
+ btn_quantize1.png
+ btn_quantize1.png
+
+
+ 1
+ btn_quantize1_over.png
+ btn_quantize1_over.png
+
+ 364,193
+
+ [Channel1],quantize
+ true
+ LeftButton
+
+
+
+
+
+ Repeat
When active the track will repeat if you go past the end or reverse before the start.
+
+ 2
+
+ 0
+ btn_repeat1.png
+ btn_repeat1.png
+
+
+ 1
+ btn_repeat1_over.png
+ btn_repeat1_over.png
+
+ 364,171
+
+ [Channel1],repeat
+
+
+
+
+
+ Eject
Eject currently loaded track from player.
+
+ 1
+
+ 0
+ btn_eject1_over.png
+ btn_eject1.png
+
+ 390,171
+
+ [Channel1],eject
+ true
+ LeftButton
+ false
+
+
+
+
+
+ 55,222
+ 357,54
+
+
+
+
+
+ Fast Forward
Left-click: Fast forward through the track.
Right-click: Jumps to the end of the track.
+
+ 1
+
+ 0
+ btn_forward1_over.png
+ btn_forward1.png
+
+ 26,0
+
+ [Channel1],fwd
+ true
+ LeftButton
+
+
+ [Channel1],end
+ true
+ RightButton
+
+
+
+ Fast Rewind
Left-click: Fast rewind through the track.
Right-click: Jumps to the beginning of the track.
+
+ 1
+
+ 0
+ btn_rewind1_over.png
+ btn_rewind1.png
+
+ 0,0
+
+ [Channel1],back
+ true
+ LeftButton
+
+
+ [Channel1],start
+ true
+ RightButton
+
+
+
+
+
+ Reverse
Toggles reverse playback when pressed during regular playback
+
+ 1
+
+ 0
+ btn_reverse1_over.png
+ btn_reverse1.png
+
+ 0,26
+
+ [Channel1],reverse
+ true
+ LeftButton
+
+
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop1_0125.png
+ btn_beatloop1_0125.png
+
+
+ 1
+ btn_beatloop1_0125_over.png
+ btn_beatloop1_0125_over.png
+
+ 92,0
+
+ [Channel1],beatloop_0.125
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop1_0250.png
+ btn_beatloop1_0250.png
+
+
+ 1
+ btn_beatloop1_0250_over.png
+ btn_beatloop1_0250_over.png
+
+ 118,0
+
+ [Channel1],beatloop_0.25
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop1_0500.png
+ btn_beatloop1_0500.png
+
+
+ 1
+ btn_beatloop1_0500_over.png
+ btn_beatloop1_0500_over.png
+
+ 144,0
+
+ [Channel1],beatloop_0.5
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop1_1.png
+ btn_beatloop1_1.png
+
+
+ 1
+ btn_beatloop1_1_over.png
+ btn_beatloop1_1_over.png
+
+ 170,0
+
+ [Channel1],beatloop_1
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop1_2.png
+ btn_beatloop1_2.png
+
+
+ 1
+ btn_beatloop1_2_over.png
+ btn_beatloop1_2_over.png
+
+ 92,26
+
+ [Channel1],beatloop_2
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop1_4.png
+ btn_beatloop1_4.png
+
+
+ 1
+ btn_beatloop1_4_over.png
+ btn_beatloop1_4_over.png
+
+ 118,26
+
+ [Channel1],beatloop_4
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop1_8_over.png
+ btn_beatloop1_8.png
+
+
+ 1
+ btn_beatloop1_8.png
+ btn_beatloop1_8_over.png
+
+ 144,26
+
+ [Channel1],beatloop_8
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop1_16.png
+ btn_beatloop1_16.png
+
+
+ 1
+ btn_beatloop1_16_over.png
+ btn_beatloop1_16_over.png
+
+ 170,26
+
+ [Channel1],beatloop_16
+ true
+ LeftButton
+
+
+
+
+
+ Loop halve
Halves the current loop's length by moving the end marker.
Player immediately loops if past the new endpoint.
+
+ 1
+
+ 0
+ btn_beatloop1_halve_over.png
+ btn_beatloop1_halve.png
+
+ 69,0
+
+ [Channel1],loop_halve
+ true
+ LeftButton
+
+
+
+ Loop double
Doubles the current loop's length by moving the end marker.
+
+ 1
+
+ 0
+ btn_beatloop1_double_over.png
+ btn_beatloop1_double.png
+
+ 197,0
+
+ [Channel1],loop_double
+ true
+ LeftButton
+
+
+
+
+
+ Loop-In marker
Sets the player loop-in position to the current play position.
+
+ 1
+
+ 0
+ btn_loop_in1_over.png
+ btn_loop_in1.png
+
+ 236,0
+
+ [Channel1],loop_in
+ true
+ LeftButton
+
+
+
+ Loop-Out marker
Sets the player loop-out position to the current play position.
+
+ 1
+
+ 0
+ btn_loop_out1_over.png
+ btn_loop_out1.png
+
+ 262,0
+
+ [Channel1],loop_out
+ true
+ LeftButton
+
+
+
+ Reloop/Exit
Toggles the current loop on or off.
Works only if Loop-In and Loop-Out marker are set.
+
+ 2
+ true
+
+ 0
+ btn_reloop1.png
+ btn_reloop1.png
+
+
+ 1
+ btn_reloop1_over.png
+ btn_reloop1_over.png
+
+ 236,26
+
+ [Channel1],reloop_exit
+ true
+ LeftButton
+ false
+
+
+ [Channel1],loop_enabled
+ false
+
+
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_1_over.png
+ btn_hotcue1_1.png
+
+
+ 1
+ btn_hotcue1_1.png
+ btn_hotcue1_1_over.png
+
+ 304,0
+
+ [Channel1],hotcue_1_activate
+ true
+ LeftButton
+ false
+
+
+ [Channel1],hotcue_1_clear
+ true
+ RightButton
+ false
+
+
+ [Channel1],hotcue_1_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_2_over.png
+ btn_hotcue1_2.png
+
+
+ 1
+ btn_hotcue1_2.png
+ btn_hotcue1_2_over.png
+
+ 330,0
+
+ [Channel1],hotcue_2_activate
+ true
+ LeftButton
+ false
+
+
+ [Channel1],hotcue_2_clear
+ true
+ RightButton
+ false
+
+
+ [Channel1],hotcue_2_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_3_over.png
+ btn_hotcue1_3.png
+
+
+ 1
+ btn_hotcue1_3.png
+ btn_hotcue1_3_over.png
+
+ 304,26
+
+ [Channel1],hotcue_3_activate
+ true
+ LeftButton
+ false
+
+
+ [Channel1],hotcue_3_clear
+ true
+ RightButton
+ false
+
+
+ [Channel1],hotcue_3_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue1_4_over.png
+ btn_hotcue1_4.png
+
+
+ 1
+ btn_hotcue1_4.png
+ btn_hotcue1_4_over.png
+
+ 330,26
+
+ [Channel1],hotcue_4_activate
+ true
+ LeftButton
+ false
+
+
+ [Channel1],hotcue_4_clear
+ true
+ RightButton
+ false
+
+
+ [Channel1],hotcue_4_enabled
+ false
+
+
+
+
+
+
+
+
+
+
+
+ 865,16
+ 416,279
+
+
+
+
+ Vinyl Status
Provides visual feedback with regards to vinyl control status
Green for control enabled
Blinking yellow for when the needle reaches the end of the record
Red for needle skip detected
+
+ 3
+ btn_vinylcontrol_indicator_horizontal1.png
+ btn_vinylcontrol_indicator_horizontal2.png
+ btn_vinylcontrol_indicator_horizontal3.png
+ 0,0
+
+ [Channel2],vinylcontrol_status
+
+
+
+ Vinyl Status
Provides visual feedback with regards to vinyl control status
Green for control enabled
Blinking yellow for when the needle reaches the end of the record
Red for needle skip detected
+
+ 3
+ btn_vinylcontrol_indicator_horizontal1.png
+ btn_vinylcontrol_indicator_horizontal2.png
+ btn_vinylcontrol_indicator_horizontal3.png
+ 0,213
+
+ [Channel2],vinylcontrol_status
+
+
+
+ Vinyl Status
Provides visual feedback with regards to vinyl control status
Green for control enabled
Blinking yellow for when the needle reaches the end of the record
Red for needle skip detected
+
+ 3
+ btn_vinylcontrol_indicator_vertical1.png
+ btn_vinylcontrol_indicator_vertical2.png
+ btn_vinylcontrol_indicator_vertical3.png
+ 0,0
+
+ [Channel2],vinylcontrol_status
+
+
+
+ Vinyl Status
Provides visual feedback with regards to vinyl control status
Green for control enabled
Blinking yellow for when the needle reaches the end of the record
Red for needle skip detected
+
+ 3
+ btn_vinylcontrol_indicator_vertical1.png
+ btn_vinylcontrol_indicator_vertical2.png
+ btn_vinylcontrol_indicator_vertical3.png
+ 413,0
+
+ [Channel2],vinylcontrol_status
+
+
+
+
+
+ 0,50
+ 413,119
+ horizontal
+
+
+
+
+
+
+ Waveform display
Shows the loaded tracks' waveforms near the playback position.
Use the mouse to scratch, halt, spin back and push forward a track.
Drop tracks from library or external file manager here.
+
+ 2
+ 0,0
+
+
+ #1C1C1C
+ style/style_bg_waveform.png
+ #1b1b1b
+ #E17800
+ #ffffff
+ #00FF00
+ #00FF00
+ #000000
+
+ loop_start_position
+ loop_end_position
+ loop_enabled
+ #00FF00
+ #FFFFFF
+
+
+ loop_start_position
+ marker_loop_in2.png
+ LOOP IN
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+ loop_end_position
+ marker_loop_out2.png
+ LOOP OUT
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+
+ hotcue_1_position
+ marker_hotcue1_1.png
+ HOTCUE 1
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+ hotcue_2_position
+ marker_hotcue1_2.png
+ HOTCUE 2
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+ hotcue_3_position
+ marker_hotcue1_3.png
+ HOTCUE 3
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+ hotcue_4_position
+ marker_hotcue1_4.png
+ HOTCUE 4
+ bottom
+ #00FF00
+ #FFFFFF
+
+
+ hotcue_5_position
+ HOTCUE 5
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_6_position
+ HOTCUE 6
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_7_position
+ HOTCUE 7
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_8_position
+ HOTCUE 8
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_9_position
+ HOTCUE 9
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_10_position
+ HOTCUE 10
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_11_position
+ HOTCUE 11
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_12_position
+ HOTCUE 12
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_13_position
+ HOTCUE 13
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_14_position
+ HOTCUE 14
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_15_position
+ HOTCUE 15
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_16_position
+ HOTCUE 16
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_17_position
+ HOTCUE 17
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_18_position
+ HOTCUE 18
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_19_position
+ HOTCUE 19
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_20_position
+ HOTCUE 20
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_21_position
+ HOTCUE 21
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_22_position
+ HOTCUE 22
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_23_position
+ HOTCUE 23
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_24_position
+ HOTCUE 24
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_25_position
+ HOTCUE 25
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_26_position
+ HOTCUE 26
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_27_position
+ HOTCUE 27
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_28_position
+ HOTCUE 28
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_29_position
+ HOTCUE 29
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_30_position
+ HOTCUE 30
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_31_position
+ HOTCUE 31
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_32_position
+ HOTCUE 32
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_33_position
+ HOTCUE 33
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_34_position
+ HOTCUE 34
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_35_position
+ HOTCUE 35
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ hotcue_36_position
+ HOTCUE 36
+ center
+ #AE5CFF
+ #FFFFFF
+
+
+ cue_point
+ marker_cue1.png
+ CUE
+ bottom
+ #FF001C
+ #FFFFFF
+
+
+
+
+
+
+ 119,119
+
+
+
+
+ Spinning vinyl
Rotates during playback and shows the position of a track.
Use the mouse to scratch, halt, spin back and push forward a track.
Drop tracks from library or external file manager here.
If Vinyl control is enabled, it can display the timecoded vinyls signal quality (see Preferences→Vinyl Control).
+
+ 2
+ 0,0
+ i,i
+ vinyl_spinny2_background.png
+ vinyl_spinny2_foreground.png
+ vinyl_spinny2_foreground_ghost.png
+
+
+
+ [Spinny2],show_spinny
+ visible
+
+
+
+
+
+
+
+ Waveform overview
Shows information about the track currently loaded in this channel.
Jump around in the track by clicking somewhere on the waveform.
Drop tracks from library or external file manager here.
+
+ 2
+ 0,170
+ 336,43
+ #1C1C1C
+ style/style_bg_woverview.png
+ #E17800
+ #00FF00
+
+ [Channel2],playposition
+ false
+
+
+
+
+
+ Track title
Displays the title of the loaded track.
Informations are extracted from the tracks tags.
+
+ title
+ 2
+ 0,1
+ 315,23
+
+
+
+
+ Track Artist
Displays the artist of the loaded track.
Informations are extracted from the tracks tags.
+
+ artist
+ 2
+ 0,26
+ 290,23
+
+
+
+
+ Tempo
Displays the tempo of the loaded track in BPM (beats per minute)
+
+ 2
+ 314,1
+ 100,23
+
+ right
+
+ [Channel2],visual_bpm
+
+
+
+
+
+ Time
Displays the elapsed or remaining time of the track loaded.
Click to toggle between time elapsed/remaining time.
+
+ 2
+ 294,26
+ 120,23
+
+ right
+
+ [Channel2],playposition
+
+
+
+
+
+
+
+ Tempo and BPM tap
Displays the tempo of the loaded track in BPM (beats per minute)
When tapped repeatedly, adjusts the BPM to match the tapped BPM
+
+
+ 1
+
+ 0
+ btn_tap1_over.png
+ btn_tap1.png
+
+ 313,0
+
+ [Channel2],bpm_tap
+ true
+
+
+
+
+
+ Spinning vinyl
Show/hide the spinning vinyl widget
+
+ 2
+
+ 0
+ btn_spinny2.png
+ btn_spinny2.png
+
+
+ 1
+ btn_spinny1_over.png
+ btn_spinny1_over.png
+
+ 338,171
+
+ [Spinny2],show_spinny
+ true
+ LeftButton
+
+
+
+
+
+ Adjust beatgrid
Adjust beatgrid so closest beat is aligned with the current playposition.
+
+ 1
+
+ 0
+ btn_beatgrid1_over.png
+ btn_beatgrid1.png
+
+ 338,193
+
+ [Channel2],beats_translate_curpos
+ true
+ LeftButton
+
+
+
+
+
+ Key-lock
Activates pitch-independent time stretch in the deck.
Toggling key-lock during playback may result in a momentary audio glitch.
+
+ 2
+
+ 0
+ btn_keylock1.png
+ btn_keylock1.png
+
+
+ 1
+ btn_keylock1_over.png
+ btn_keylock1_over.png
+
+ 390,193
+
+ [Channel2],keylock
+
+
+
+
+
+ Quantize
Toggles quantization in loops and cues
+
+ 2
+
+ 0
+ btn_quantize1.png
+ btn_quantize1.png
+
+
+ 1
+ btn_quantize1_over.png
+ btn_quantize1_over.png
+
+ 364,193
+
+ [Channel2],quantize
+ true
+ LeftButton
+
+
+
+
+
+ Repeat
When active the track will repeat if you go past the end or reverse before the start.
+
+ 2
+
+ 0
+ btn_repeat1.png
+ btn_repeat1.png
+
+
+ 1
+ btn_repeat1_over.png
+ btn_repeat1_over.png
+
+ 364,171
+
+ [Channel2],repeat
+
+
+
+
+
+ Eject
Eject currently loaded track from player.
+
+ 1
+
+ 0
+ btn_eject1_over.png
+ btn_eject1.png
+
+ 390,171
+
+ [Channel2],eject
+ true
+ LeftButton
+ false
+
+
+
+
+
+ 6,222
+ 357,54
+
+
+
+
+
+ Fast Forward
Left-click: Fast forward through the track.
Right-click: Jumps to the end of the track.
+
+ 1
+
+ 0
+ btn_forward1_over.png
+ btn_forward1.png
+
+ 26,0
+
+ [Channel2],fwd
+ true
+ LeftButton
+
+
+ [Channel2],end
+ true
+ RightButton
+
+
+
+ Fast Rewind
Left-click: Fast rewind through the track.
Right-click: Jumps to the beginning of the track.
+
+ 1
+
+ 0
+ btn_rewind2_over.png
+ btn_rewind2.png
+
+ 0,0
+
+ [Channel2],back
+ true
+ LeftButton
+
+
+ [Channel2],start
+ true
+ RightButton
+
+
+
+
+
+ Reverse
Toggles reverse playback when pressed during regular playback
+
+ 1
+
+ 0
+ btn_reverse2_over.png
+ btn_reverse2.png
+
+ 0,26
+
+ [Channel2],reverse
+ true
+ LeftButton
+
+
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop2_0125.png
+ btn_beatloop2_0125.png
+
+
+ 1
+ btn_beatloop2_0125_over.png
+ btn_beatloop2_0125_over.png
+
+ 92,0
+
+ [Channel2],beatloop_0.125
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop2_0250.png
+ btn_beatloop2_0250.png
+
+
+ 1
+ btn_beatloop2_0250_over.png
+ btn_beatloop2_0250_over.png
+
+ 118,0
+
+ [Channel2],beatloop_0.25
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop2_0500.png
+ btn_beatloop2_0500.png
+
+
+ 1
+ btn_beatloop2_0500_over.png
+ btn_beatloop2_0500_over.png
+
+ 144,0
+
+ [Channel2],beatloop_0.5
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop2_1.png
+ btn_beatloop2_1.png
+
+
+ 1
+ btn_beatloop2_1_over.png
+ btn_beatloop2_1_over.png
+
+ 170,0
+
+ [Channel2],beatloop_1
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop2_2.png
+ btn_beatloop2_2.png
+
+
+ 1
+ btn_beatloop2_2_over.png
+ btn_beatloop2_2_over.png
+
+ 92,26
+
+ [Channel2],beatloop_2
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop2_4.png
+ btn_beatloop2_4.png
+
+
+ 1
+ btn_beatloop2_4_over.png
+ btn_beatloop2_4_over.png
+
+ 118,26
+
+ [Channel2],beatloop_4
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop2_8_over.png
+ btn_beatloop2_8.png
+
+
+ 1
+ btn_beatloop2_8.png
+ btn_beatloop2_8_over.png
+
+ 144,26
+
+ [Channel2],beatloop_8
+ true
+ LeftButton
+
+
+
+ Beatloop
Setup a loop over X beats
+
+ 2
+
+ 0
+ btn_beatloop2_16.png
+ btn_beatloop2_16.png
+
+
+ 1
+ btn_beatloop2_16_over.png
+ btn_beatloop2_16_over.png
+
+ 170,26
+
+ [Channel2],beatloop_16
+ true
+ LeftButton
+
+
+
+
+
+ Loop halve
Halves the current loop's length by moving the end marker.
Player immediately loops if past the new endpoint.
+
+ 1
+
+ 0
+ btn_beatloop2_halve_over.png
+ btn_beatloop2_halve.png
+
+ 69,0
+
+ [Channel2],loop_halve
+ true
+ LeftButton
+
+
+
+ Loop double
Doubles the current loop's length by moving the end marker.
+
+ 1
+
+ 0
+ btn_beatloop2_double_over.png
+ btn_beatloop2_double.png
+
+ 197,0
+
+ [Channel2],loop_double
+ true
+ LeftButton
+
+
+
+
+
+ Loop-In marker
Sets the player loop-in position to the current play position.
+
+ 1
+
+ 0
+ btn_loop_in2_over.png
+ btn_loop_in2.png
+
+ 236,0
+
+ [Channel2],loop_in
+ true
+ LeftButton
+
+
+
+ Loop-Out marker
Sets the player loop-out position to the current play position.
+
+ 1
+
+ 0
+ btn_loop_out2_over.png
+ btn_loop_out2.png
+
+ 262,0
+
+ [Channel2],loop_out
+ true
+ LeftButton
+
+
+
+ Reloop/Exit
Toggles the current loop on or off.
Works only if Loop-In and Loop-Out marker are set.
+
+ 2
+ true
+
+ 0
+ btn_reloop2.png
+ btn_reloop2.png
+
+
+ 1
+ btn_reloop2_over.png
+ btn_reloop2_over.png
+
+ 236,26
+
+ [Channel2],reloop_exit
+ true
+ LeftButton
+ false
+
+
+ [Channel2],loop_enabled
+ false
+
+
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue2_1_over.png
+ btn_hotcue2_1.png
+
+
+ 1
+ btn_hotcue2_1.png
+ btn_hotcue2_1_over.png
+
+ 304,0
+
+ [Channel2],hotcue_1_activate
+ true
+ LeftButton
+ false
+
+
+ [Channel2],hotcue_1_clear
+ true
+ RightButton
+ false
+
+
+ [Channel2],hotcue_1_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue2_2_over.png
+ btn_hotcue2_2.png
+
+
+ 1
+ btn_hotcue2_2.png
+ btn_hotcue2_2_over.png
+
+ 330,0
+
+ [Channel2],hotcue_2_activate
+ true
+ LeftButton
+ false
+
+
+ [Channel2],hotcue_2_clear
+ true
+ RightButton
+ false
+
+
+ [Channel2],hotcue_2_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue2_3_over.png
+ btn_hotcue2_3.png
+
+
+ 1
+ btn_hotcue2_3.png
+ btn_hotcue2_3_over.png
+
+ 304,26
+
+ [Channel2],hotcue_3_activate
+ true
+ LeftButton
+ false
+
+
+ [Channel2],hotcue_3_clear
+ true
+ RightButton
+ false
+
+
+ [Channel2],hotcue_3_enabled
+ false
+
+
+
+ Hotcue
Left-click: If Hotcue is set, seeks the player to Hotcue position.
If Hotcue is not set, sets Hotcue to the current play position.
Right-click: If Hotcue is set, clears its hotcue status (delete).
+
+ 2
+ true
+ true
+
+ 0
+ btn_hotcue2_4_over.png
+ btn_hotcue2_4.png
+
+
+ 1
+ btn_hotcue2_4.png
+ btn_hotcue2_4_over.png
+
+ 330,26
+
+ [Channel2],hotcue_4_activate
+ true
+ LeftButton
+ false
+
+
+ [Channel2],hotcue_4_clear
+ true
+ RightButton
+ false
+
+
+ [Channel2],hotcue_4_enabled
+ false
+
+
+
+
+
+
+
+
+
+
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/slider_crossfader.png b/res/skins/Deere1280x1024-SXGA-Effects/slider_crossfader.png
new file mode 100644
index 000000000000..365c2571846b
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/slider_crossfader.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/slider_pitch1.png b/res/skins/Deere1280x1024-SXGA-Effects/slider_pitch1.png
new file mode 100644
index 000000000000..7c89d3a2c20c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/slider_pitch1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/slider_pitch2.png b/res/skins/Deere1280x1024-SXGA-Effects/slider_pitch2.png
new file mode 100644
index 000000000000..7c89d3a2c20c
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/slider_pitch2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/slider_pitch_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/slider_pitch_sampler.png
new file mode 100644
index 000000000000..77e2ece3723d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/slider_pitch_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/slider_volume1.png b/res/skins/Deere1280x1024-SXGA-Effects/slider_volume1.png
new file mode 100644
index 000000000000..7486375ec6ce
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/slider_volume1.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/slider_volume2.png b/res/skins/Deere1280x1024-SXGA-Effects/slider_volume2.png
new file mode 100644
index 000000000000..7486375ec6ce
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/slider_volume2.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_microphone.png b/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_microphone.png
new file mode 100644
index 000000000000..79d9db5058e2
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_microphone.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_sampler.png
new file mode 100644
index 000000000000..0a0a8c6c2d76
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_waveform.png b/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_waveform.png
new file mode 100644
index 000000000000..749a71c2d3a1
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_waveform.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_woverview.png b/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_woverview.png
new file mode 100644
index 000000000000..da23e4737919
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/style/style_bg_woverview.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/style/style_branch_closed.png b/res/skins/Deere1280x1024-SXGA-Effects/style/style_branch_closed.png
new file mode 100644
index 000000000000..aac367c5b22a
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/style/style_branch_closed.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/style/style_branch_open.png b/res/skins/Deere1280x1024-SXGA-Effects/style/style_branch_open.png
new file mode 100644
index 000000000000..56ae3993ef5d
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/style/style_branch_open.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/style/style_checkbox_checked.png b/res/skins/Deere1280x1024-SXGA-Effects/style/style_checkbox_checked.png
new file mode 100644
index 000000000000..91ec8a6e87bd
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/style/style_checkbox_checked.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/style/style_checkbox_unchecked.png b/res/skins/Deere1280x1024-SXGA-Effects/style/style_checkbox_unchecked.png
new file mode 100644
index 000000000000..0aca84f6f212
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/style/style_checkbox_unchecked.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/style/style_handle_checked.png b/res/skins/Deere1280x1024-SXGA-Effects/style/style_handle_checked.png
new file mode 100644
index 000000000000..d6bd8ece7cdc
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/style/style_handle_checked.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/style/style_handle_unchecked.png b/res/skins/Deere1280x1024-SXGA-Effects/style/style_handle_unchecked.png
new file mode 100644
index 000000000000..ffabd6d5b5be
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/style/style_handle_unchecked.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/tab_microphone.png b/res/skins/Deere1280x1024-SXGA-Effects/tab_microphone.png
new file mode 100644
index 000000000000..026bd9def28e
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/tab_microphone.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/tab_microphone_over.png b/res/skins/Deere1280x1024-SXGA-Effects/tab_microphone_over.png
new file mode 100644
index 000000000000..213a45ff6a30
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/tab_microphone_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/tab_sampler.png b/res/skins/Deere1280x1024-SXGA-Effects/tab_sampler.png
new file mode 100644
index 000000000000..a2a319a7d78f
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/tab_sampler.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/tab_sampler_over.png b/res/skins/Deere1280x1024-SXGA-Effects/tab_sampler_over.png
new file mode 100644
index 000000000000..e08ecb1795ae
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/tab_sampler_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/tab_vinylcontrol.png b/res/skins/Deere1280x1024-SXGA-Effects/tab_vinylcontrol.png
new file mode 100644
index 000000000000..1caed8393da5
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/tab_vinylcontrol.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/tab_vinylcontrol_over.png b/res/skins/Deere1280x1024-SXGA-Effects/tab_vinylcontrol_over.png
new file mode 100644
index 000000000000..1cbb74d8427e
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/tab_vinylcontrol_over.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny1_background.png b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny1_background.png
new file mode 100644
index 000000000000..2dbdb85734d4
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny1_background.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny1_foreground.png b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny1_foreground.png
new file mode 100644
index 000000000000..8ec109ccc509
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny1_foreground.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny1_foreground_ghost.png b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny1_foreground_ghost.png
new file mode 100644
index 000000000000..87a50a0a06ef
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny1_foreground_ghost.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny2_background.png b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny2_background.png
new file mode 100644
index 000000000000..9ce6b9320f21
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny2_background.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny2_foreground.png b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny2_foreground.png
new file mode 100644
index 000000000000..8e4a8b67dec1
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny2_foreground.png differ
diff --git a/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny2_foreground_ghost.png b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny2_foreground_ghost.png
new file mode 100644
index 000000000000..87a50a0a06ef
Binary files /dev/null and b/res/skins/Deere1280x1024-SXGA-Effects/vinyl_spinny2_foreground_ghost.png differ
diff --git a/src/basetrackplayer.cpp b/src/basetrackplayer.cpp
index b224c0c5c3cd..2a70ee3ae4af 100644
--- a/src/basetrackplayer.cpp
+++ b/src/basetrackplayer.cpp
@@ -25,18 +25,18 @@ BaseTrackPlayer::BaseTrackPlayer(QObject* pParent,
EngineChannel::ChannelOrientation defaultOrientation,
QString group,
bool defaultMaster,
- bool defaultHeadphones) :
- BasePlayer(pParent, group),
- m_pConfig(pConfig),
- m_pLoadedTrack() {
-
+ bool defaultHeadphones)
+ : BasePlayer(pParent, group),
+ m_pConfig(pConfig),
+ m_pLoadedTrack() {
// Need to strdup the string because EngineChannel will save the pointer,
// but we might get deleted before the EngineChannel. TODO(XXX)
// pSafeGroupName is leaked. It's like 5 bytes so whatever.
const char* pSafeGroupName = strdup(getGroup().toAscii().constData());
- m_pChannel = new EngineDeck(pSafeGroupName,
- pConfig, defaultOrientation);
+ m_pChannel = new EngineDeck(
+ pSafeGroupName, pConfig, pMixingEngine->getEffectsManager(),
+ defaultOrientation);
EngineBuffer* pEngineBuffer = m_pChannel->getEngineBuffer();
pMixingEngine->addChannel(m_pChannel);
diff --git a/src/controlvaluedelegate.h b/src/controlvaluedelegate.h
index 3f1806df43c4..ddff58331b02 100644
--- a/src/controlvaluedelegate.h
+++ b/src/controlvaluedelegate.h
@@ -39,12 +39,15 @@ class ControlValueDelegate : public QItemDelegate
static QStringList getChannelControlValues() { return m_channelControlValues; };
static QStringList getMasterControlValues() { return m_masterControlValues; };
static QStringList getPlaylistControlValues() { return m_playlistControlValues; };
+ static QStringList getFXControlValues() { return m_fxControlValues; };
static QStringList getFlangerControlValues() { return m_flangerControlValues; };
static QStringList getMicrophoneControlValues() { return m_microphoneControlValues; };
+
private:
static QStringList m_channelControlValues;
static QStringList m_masterControlValues;
static QStringList m_playlistControlValues;
+ static QStringList m_fxControlValues;
static QStringList m_flangerControlValues;
static QStringList m_microphoneControlValues;
};
diff --git a/src/defs.h b/src/defs.h
index ce7d588afa17..17d4267abdc0 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -87,6 +87,10 @@ inline double zap_denormal(double x)
#define math_min(a,b) (((a) < (b)) ? (a) : (b))
#endif
+#ifndef math_clamp
+#define math_clamp(v, min, max) (((min) < (max)) ? (math_min((max), math_max((v), (min)))) : (math_min((min), math_max((v), (max)))))
+#endif
+
// MSVC 2005/2008 needs these
#ifndef fmax
#define fmax math_max
@@ -97,4 +101,3 @@ inline double zap_denormal(double x)
#endif
#endif
-
diff --git a/src/effects/effect.cpp b/src/effects/effect.cpp
new file mode 100644
index 000000000000..1ff30e8b95fa
--- /dev/null
+++ b/src/effects/effect.cpp
@@ -0,0 +1,64 @@
+#include
+
+#include "effects/effect.h"
+#include "effects/effectprocessor.h"
+#include "engine/effects/engineeffect.h"
+
+Effect::Effect(QObject* pParent, const EffectManifest& manifest,
+ EffectProcessor* pProcessor)
+ : QObject(pParent),
+ m_manifest(manifest),
+ m_pEngineEffect(new EngineEffect(manifest, pProcessor)) {
+ foreach (const EffectManifestParameter& parameter, m_manifest.parameters()) {
+ EffectParameter* pParameter = new EffectParameter(this, parameter);
+ m_parameters.append(pParameter);
+ if (m_parametersById.contains(parameter.id())) {
+ qDebug() << debugString() << "WARNING: Loaded EffectManifest that had parameters with duplicate IDs. Dropping one of them.";
+ }
+ m_parametersById[parameter.id()] = pParameter;
+ }
+}
+
+Effect::~Effect() {
+ qDebug() << debugString() << "destroyed";
+ m_parametersById.clear();
+ for (int i = 0; i < m_parameters.size(); ++i) {
+ EffectParameter* pParameter = m_parameters.at(i);
+ m_parameters[i] = NULL;
+ delete pParameter;
+ }
+}
+
+EngineEffect* Effect::getEngineEffect() {
+ return m_pEngineEffect;
+}
+
+void Effect::setEngineParameterById(const QString& id, const QVariant& value) {
+ if (m_pEngineEffect) {
+ m_pEngineEffect->setParameterById(id, value);
+ }
+}
+
+const EffectManifest& Effect::getManifest() const {
+ return m_manifest;
+}
+
+unsigned int Effect::numParameters() const {
+ return m_parameters.size();
+}
+
+EffectParameter* Effect::getParameterById(const QString& id) const {
+ EffectParameter* pParameter = m_parametersById.value(id, NULL);
+ if (pParameter == NULL) {
+ qDebug() << debugString() << "parameterFromId" << "WARNING: parameter for id does not exist:" << id;
+ }
+ return pParameter;
+}
+
+EffectParameter* Effect::getParameter(unsigned int parameterNumber) {
+ EffectParameter* pParameter = m_parameters.value(parameterNumber, NULL);
+ if (pParameter == NULL) {
+ qDebug() << debugString() << "WARNING: Invalid parameter index.";
+ }
+ return pParameter;
+}
diff --git a/src/effects/effect.h b/src/effects/effect.h
new file mode 100644
index 000000000000..b95c07d1f61a
--- /dev/null
+++ b/src/effects/effect.h
@@ -0,0 +1,44 @@
+#ifndef EFFECT_H
+#define EFFECT_H
+
+#include
+
+#include "defs.h"
+#include "util.h"
+#include "effects/effectmanifest.h"
+#include "effects/effectparameter.h"
+
+class EffectProcessor;
+class EngineEffect;
+
+class Effect;
+typedef QSharedPointer EffectPointer;
+
+class Effect : public QObject {
+ Q_OBJECT
+ public:
+ Effect(QObject* pParent, const EffectManifest& manifest, EffectProcessor* pProcessor);
+ virtual ~Effect();
+
+ const EffectManifest& getManifest() const;
+
+ unsigned int numParameters() const;
+ EffectParameter* getParameter(unsigned int parameterNumber);
+ EffectParameter* getParameterById(const QString& id) const;
+ EngineEffect* getEngineEffect();
+ void setEngineParameterById(const QString& id, const QVariant& value);
+
+ private:
+ QString debugString() const {
+ return QString("Effect(%1)").arg(m_manifest.name());
+ }
+
+ EffectManifest m_manifest;
+ EngineEffect* m_pEngineEffect;
+ QList m_parameters;
+ QMap m_parametersById;
+
+ DISALLOW_COPY_AND_ASSIGN(Effect);
+};
+
+#endif /* EFFECT_H */
diff --git a/src/effects/effectchain.cpp b/src/effects/effectchain.cpp
new file mode 100644
index 000000000000..d870f66f523b
--- /dev/null
+++ b/src/effects/effectchain.cpp
@@ -0,0 +1,95 @@
+#include
+
+#include "effects/effectchain.h"
+#include "sampleutil.h"
+
+EffectChain::EffectChain(QObject* pParent)
+ : QObject(),
+ m_mutex(QMutex::Recursive),
+ m_id(""),
+ m_name("") {
+}
+
+EffectChain::~EffectChain() {
+ qDebug() << debugString() << "destroyed";
+}
+
+QString EffectChain::id() const {
+ QMutexLocker locker(&m_mutex);
+ return m_id;
+}
+
+void EffectChain::setId(const QString& id) {
+ QMutexLocker locker(&m_mutex);
+ m_id = id;
+}
+
+QString EffectChain::name() const {
+ QMutexLocker locker(&m_mutex);
+ return m_name;
+}
+
+void EffectChain::setName(const QString& name) {
+ QMutexLocker locker(&m_mutex);
+ m_name = name;
+}
+
+double EffectChain::parameter() const {
+ QMutexLocker locker(&m_mutex);
+ return m_dParameter;
+}
+
+void EffectChain::setParameter(const double& dParameter) {
+ qDebug() << debugString() << "setParameter" << dParameter;
+ QMutexLocker locker(&m_mutex);
+ m_dParameter = dParameter;
+
+}
+
+void EffectChain::process(const QString& channelId,
+ const CSAMPLE* pInput,
+ CSAMPLE* pOutput,
+ const unsigned int numSamples) {
+ qDebug() << debugString() << "process" << channelId << numSamples;
+ QMutexLocker locker(&m_mutex);
+
+ // Qt implicitly shared state claims this is fine.
+ QList effects = m_effects;
+
+ ////////////////////////////////////////////////////////////////////////////
+ // AFTER THIS LINE, THE MUTEX IS UNLOCKED. DONT TOUCH ANY MEMBER STATE
+ ////////////////////////////////////////////////////////////////////////////
+ locker.unlock();
+
+ // foreach (EffectPointer pEffect, effects) {
+ // if (pEffect) {
+ // pEffect->process(channelId, pInput, pOutput, numSamples);
+ // }
+ // }
+}
+
+void EffectChain::addEffect(EffectPointer pEffect) {
+ qDebug() << debugString() << "addEffect";
+ QMutexLocker locker(&m_mutex);
+ m_effects.append(pEffect);
+ locker.unlock();
+ emit(updated());
+}
+
+unsigned int EffectChain::numEffects() const {
+ QMutexLocker locker(&m_mutex);
+ return m_effects.size();
+}
+
+QList EffectChain::getEffects() const {
+ QMutexLocker locker(&m_mutex);
+ return m_effects;
+}
+
+EffectPointer EffectChain::getEffect(unsigned int effectNumber) const {
+ QMutexLocker locker(&m_mutex);
+ if (effectNumber >= m_effects.size()) {
+ qDebug() << debugString() << "WARNING: list index out of bounds for getEffect";
+ }
+ return m_effects[effectNumber];
+}
diff --git a/src/effects/effectchain.h b/src/effects/effectchain.h
new file mode 100644
index 000000000000..5310691c5699
--- /dev/null
+++ b/src/effects/effectchain.h
@@ -0,0 +1,75 @@
+#ifndef EFFECTCHAIN_H
+#define EFFECTCHAIN_H
+
+#include
+#include
+#include
+#include
+
+#include "util.h"
+#include "effects/effect.h"
+#include "effects/effectslot.h"
+
+class EffectChain;
+typedef QSharedPointer EffectChainPointer;
+
+class EffectChain : public QObject {
+ Q_OBJECT
+ public:
+ EffectChain(QObject* pParent=NULL);
+ virtual ~EffectChain();
+
+ // The ID of an EffectChain is a unique ID given to it to help associate it
+ // with the preset from which it was loaded.
+ QString id() const;
+ void setId(const QString& id);
+
+ // Get the human-readable name of the EffectChain
+ QString name() const;
+ void setName(const QString& name);
+
+ double parameter() const;
+ void setParameter(const double& dParameter);
+
+ void addEffect(EffectPointer pEffect);
+ EffectPointer getEffect(unsigned int i) const;
+ QList getEffects() const;
+ unsigned int numEffects() const;
+
+ // Take a buffer of numSamples samples of audio from channel channelId,
+ // provided as pInput, and apply each Effect in this EffectChain to it,
+ // putting the resulting output in pOutput. If pInput is equal to pOutput,
+ // then the operation must occur in-place. Both pInput and pOutput are
+ // represented as stereo interleaved samples. There are numSamples total
+ // samples, so numSamples/2 left channel samples and numSamples/2 right
+ // channel samples. The channelId provided allows the effects to maintain
+ // state on a per-channel basis. This is important because one Effect
+ // instance may be used to process the audio of multiple channels.
+ virtual void process(const QString& channelId,
+ const CSAMPLE* pInput, CSAMPLE* pOutput,
+ const unsigned int numSamples);
+
+ signals:
+ // Signal that indicates that the EffectChain has changed (i.e. an Effect
+ // has been added or removed).
+ void updated();
+ private:
+ QString debugString() const {
+ return QString("EffectChain(%1)").arg(m_id);
+ }
+
+ mutable QMutex m_mutex;
+
+ bool m_bEnabled;
+ QString m_id;
+ QString m_name;
+ double m_dMix;
+ double m_dParameter;
+
+ QList m_effects;
+
+ DISALLOW_COPY_AND_ASSIGN(EffectChain);
+};
+
+
+#endif /* EFFECTCHAIN_H */
diff --git a/src/effects/effectchainmanager.cpp b/src/effects/effectchainmanager.cpp
new file mode 100644
index 000000000000..a77f1fa0ddce
--- /dev/null
+++ b/src/effects/effectchainmanager.cpp
@@ -0,0 +1,61 @@
+
+#include "effects/effectchainmanager.h"
+
+#include "effects/effectsmanager.h"
+
+EffectChainManager::EffectChainManager(EffectsManager* pEffectsManager)
+ : QObject(pEffectsManager),
+ m_pEffectsManager(pEffectsManager) {
+}
+
+EffectChainManager::~EffectChainManager() {
+ qDebug() << debugString() << "destroyed";
+}
+
+void EffectChainManager::addEffectChain(EffectChainPointer pEffectChain) {
+ if (pEffectChain) {
+ m_effectChains.append(pEffectChain);
+ }
+}
+
+EffectChainPointer EffectChainManager::getNextEffectChain(EffectChainPointer pEffectChain) {
+ if (m_effectChains.size() == 0)
+ return EffectChainPointer();
+
+ if (!pEffectChain) {
+ return m_effectChains[0];
+ }
+
+ int indexOf = m_effectChains.lastIndexOf(pEffectChain);
+ if (indexOf == -1) {
+ qDebug() << debugString() << "WARNING: getNextEffectChain called for an unmanaged EffectChain";
+ return m_effectChains[0];
+ }
+
+ return m_effectChains[(indexOf + 1) % m_effectChains.size()];
+}
+
+EffectChainPointer EffectChainManager::getPrevEffectChain(EffectChainPointer pEffectChain) {
+ if (m_effectChains.size() == 0)
+ return EffectChainPointer();
+
+ if (!pEffectChain) {
+ return m_effectChains[m_effectChains.size()-1];
+ }
+
+ int indexOf = m_effectChains.lastIndexOf(pEffectChain);
+ if (indexOf == -1) {
+ qDebug() << debugString() << "WARNING: getPrevEffectChain called for an unmanaged EffectChain";
+ return m_effectChains[m_effectChains.size()-1];
+ }
+
+ return m_effectChains[(indexOf - 1 + m_effectChains.size()) % m_effectChains.size()];
+}
+
+void EffectChainManager::saveEffectChains() {
+ qDebug() << debugString() << "saveEffectChains";
+}
+
+void EffectChainManager::loadEffectChains() {
+ qDebug() << debugString() << "loadEffectChains";
+}
diff --git a/src/effects/effectchainmanager.h b/src/effects/effectchainmanager.h
new file mode 100644
index 000000000000..f88256620919
--- /dev/null
+++ b/src/effects/effectchainmanager.h
@@ -0,0 +1,38 @@
+#ifndef EFFECTCHAINMANAGER_H
+#define EFFECTCHAINMANAGER_H
+
+#include
+#include
+
+#include "util.h"
+#include "effects/effectchain.h"
+
+class EffectsManager;
+
+class EffectChainManager : public QObject {
+ Q_OBJECT
+ public:
+ EffectChainManager(EffectsManager* pEffectsManager);
+ virtual ~EffectChainManager();
+
+ void addEffectChain(EffectChainPointer pEffectChain);
+
+ EffectChainPointer getNextEffectChain(EffectChainPointer pEffectChain);
+ EffectChainPointer getPrevEffectChain(EffectChainPointer pEffectChain);
+
+ void saveEffectChains();
+ void loadEffectChains();
+
+ private:
+ QString debugString() const {
+ return "EffectChainManager";
+ }
+
+ EffectsManager* m_pEffectsManager;
+ QList m_effectChains;
+
+ DISALLOW_COPY_AND_ASSIGN(EffectChainManager);
+};
+
+
+#endif /* EFFECTCHAINMANAGER_H */
diff --git a/src/effects/effectchainslot.cpp b/src/effects/effectchainslot.cpp
new file mode 100644
index 000000000000..7c03ed265d4f
--- /dev/null
+++ b/src/effects/effectchainslot.cpp
@@ -0,0 +1,263 @@
+#include
+
+#include "effects/effectchainslot.h"
+#include "sampleutil.h"
+
+#include "controlpotmeter.h"
+
+EffectChainSlot::EffectChainSlot(QObject* pParent, unsigned int iChainNumber)
+ : QObject(),
+ m_mutex(QMutex::Recursive),
+ m_iChainNumber(iChainNumber),
+ // The control group names are 1-indexed while internally everything is 0-indexed.
+ m_group(formatGroupString(iChainNumber)) {
+
+ m_pControlNumEffects = new ControlObject(ConfigKey(m_group, "num_effects"));
+ m_pControlNumEffects->set(0.0f);
+ connect(m_pControlNumEffects, SIGNAL(valueChanged(double)),
+ this, SLOT(slotControlNumEffects(double)));
+
+ m_pControlChainEnabled = new ControlObject(ConfigKey(m_group, "enabled"));
+ connect(m_pControlChainEnabled, SIGNAL(valueChanged(double)),
+ this, SLOT(slotControlChainEnabled(double)));
+ // Default chain to enabled.
+ m_pControlChainEnabled->set(1.0f);
+
+ m_pControlChainMix = new ControlPotmeter(ConfigKey(m_group, "mix"), 0.0, 1.0);
+ connect(m_pControlChainMix, SIGNAL(valueChanged(double)),
+ this, SLOT(slotControlChainMix(double)));
+
+ m_pControlChainParameter = new ControlPotmeter(ConfigKey(m_group, "parameter"), 0.0, 1.0);
+ connect(m_pControlChainParameter, SIGNAL(valueChanged(double)),
+ this, SLOT(slotControlChainParameter(double)));
+
+ m_pControlChainNextPreset = new ControlObject(ConfigKey(m_group, "next_chain"));
+ connect(m_pControlChainNextPreset, SIGNAL(valueChanged(double)),
+ this, SLOT(slotControlChainNextPreset(double)));
+
+ m_pControlChainPrevPreset = new ControlObject(ConfigKey(m_group, "prev_chain"));
+ connect(m_pControlChainPrevPreset, SIGNAL(valueChanged(double)),
+ this, SLOT(slotControlChainPrevPreset(double)));
+}
+
+EffectChainSlot::~EffectChainSlot() {
+ qDebug() << debugString() << "destroyed";
+ delete m_pControlNumEffects;
+ delete m_pControlChainEnabled;
+ delete m_pControlChainMix;
+ delete m_pControlChainParameter;
+ delete m_pControlChainPrevPreset;
+ delete m_pControlChainNextPreset;
+ m_slots.clear();
+ m_pEffectChain.clear();
+}
+
+QString EffectChainSlot::id() const {
+ QMutexLocker locker(&m_mutex);
+ if (m_pEffectChain)
+ return m_pEffectChain->id();
+ return "";
+}
+
+QString EffectChainSlot::name() const {
+ QMutexLocker locker(&m_mutex);
+ if (m_pEffectChain)
+ return m_pEffectChain->name();
+ return tr("None");
+}
+
+void EffectChainSlot::slotChainUpdated() {
+ qDebug() << debugString() << "slotChainUpdated";
+ if (m_pEffectChain) {
+ QList effects = m_pEffectChain->getEffects();
+ while (effects.size() > m_slots.size()) {
+ addEffectSlot();
+ }
+
+ for (int i = 0; i < m_slots.size(); ++i) {
+ EffectSlotPointer pSlot = m_slots[i];
+ EffectPointer pEffect;
+ if (i < effects.size()) {
+ pEffect = effects[i];
+ }
+ if (pSlot)
+ pSlot->loadEffect(pEffect);
+ }
+
+ m_pControlNumEffects->set(m_pEffectChain->numEffects());
+ m_pControlChainParameter->set(m_pEffectChain->parameter());
+
+ // TODO(rryan) is this a reasonable decision? Keep the enabled and mix
+ // values the same because a) it keeps the controls from getting out of
+ // sync with your MIDI controller, and b) they aren't something you'd
+ // care about saving as an attribute of your prototypical EffectChain.
+
+ //m_pControlChainEnabled->set()
+ //m_pControlChainMix->set()
+ }
+}
+
+void EffectChainSlot::loadEffectChain(EffectChainPointer pEffectChain) {
+ qDebug() << debugString() << "loadEffectChain" << (pEffectChain ? pEffectChain->id() : "(null)");
+ QMutexLocker locker(&m_mutex);
+
+ // Clear any loaded EffectChain
+ // -- causes a lot of signal changes just to go and load the next effect chain
+ //clear();
+
+ if (m_pEffectChain) {
+ m_pEffectChain->disconnect(this);
+ m_pEffectChain.clear();
+ }
+
+ if (pEffectChain) {
+ m_pEffectChain = pEffectChain;
+ connect(m_pEffectChain.data(), SIGNAL(updated()),
+ this, SLOT(slotChainUpdated()));
+ slotChainUpdated();
+ } else {
+ clear();
+ }
+
+ locker.unlock();
+ emit(effectChainLoaded(pEffectChain));
+ emit(updated());
+}
+
+EffectChainPointer EffectChainSlot::getEffectChain() const {
+ return m_pEffectChain;
+}
+
+void EffectChainSlot::clear() {
+ // Stop listening to signals from any loaded effect
+ if (m_pEffectChain) {
+ m_pEffectChain->disconnect(this);
+ m_pEffectChain.clear();
+
+ foreach (EffectSlotPointer pSlot, m_slots) {
+ pSlot->loadEffect(EffectPointer());
+ }
+
+ }
+ m_pControlNumEffects->set(0.0f);
+ m_pControlChainEnabled->set(0.0f);
+ m_pControlChainMix->set(0.0f);
+ m_pControlChainParameter->set(0.0f);
+}
+
+bool EffectChainSlot::isEnabled() const {
+ QMutexLocker locker(&m_mutex);
+ return privateIsEnabled();
+}
+
+bool EffectChainSlot::isEnabledForChannel(QString channelId) const {
+ QMutexLocker locker(&m_mutex);
+ if (!m_channelEnableControls.contains(channelId)) {
+ qDebug() << "WARNING: Checking whether chain is enabled for channel that hasn't been registered.";
+ return false;
+ }
+ return m_channelEnableControls[channelId]->get() > 0.0f && privateIsEnabled();
+}
+
+bool EffectChainSlot::privateIsEnabled() const {
+ return m_pControlChainEnabled->get() > 0.0f;
+}
+
+unsigned int EffectChainSlot::numSlots() const {
+ qDebug() << debugString() << "numSlots";
+ QMutexLocker locker(&m_mutex);
+ return m_slots.size();
+}
+
+void EffectChainSlot::addEffectSlot() {
+ qDebug() << debugString() << "addEffectSlot";
+ QMutexLocker locker(&m_mutex);
+
+ EffectSlot* pEffectSlot = new EffectSlot(this, m_iChainNumber, m_slots.size());
+ // Rebroadcast effectLoaded signals
+ connect(pEffectSlot, SIGNAL(effectLoaded(EffectPointer, unsigned int)),
+ this, SLOT(slotEffectLoaded(EffectPointer, unsigned int)));
+ m_slots.append(EffectSlotPointer(pEffectSlot));
+}
+
+void EffectChainSlot::registerChannel(const QString channelId) {
+ if (m_channelEnableControls.contains(channelId)) {
+ qDebug() << debugString() << "WARNING: registerChannel already has channel registered:" << channelId;
+ return;
+ }
+ ControlObject* pEnableControl = new ControlObject(
+ ConfigKey(m_group, QString("channel_%1").arg(channelId)));
+ pEnableControl->set(0.0f);
+ m_channelEnableControls[channelId] = pEnableControl;
+}
+
+void EffectChainSlot::slotEffectLoaded(EffectPointer pEffect, unsigned int slotNumber) {
+ // const int is a safe read... don't bother locking
+ emit(effectLoaded(pEffect, m_iChainNumber, slotNumber));
+}
+
+EffectSlotPointer EffectChainSlot::getEffectSlot(unsigned int slotNumber) {
+ qDebug() << debugString() << "getEffectSlot" << slotNumber;
+ QMutexLocker locker(&m_mutex);
+ if (slotNumber >= m_slots.size()) {
+ qDebug() << "WARNING: slotNumber out of range";
+ return EffectSlotPointer();
+ }
+ return m_slots[slotNumber];
+}
+
+void EffectChainSlot::slotControlNumEffects(double v) {
+ qDebug() << debugString() << "slotControlNumEffects" << v;
+ QMutexLocker locker(&m_mutex);
+ qDebug() << "WARNING: Somebody has set a read-only control. Stability may be compromised.";
+}
+
+void EffectChainSlot::slotControlChainEnabled(double v) {
+ qDebug() << debugString() << "slotControlChainEnabled" << v;
+ QMutexLocker locker(&m_mutex);
+
+ bool bEnabled = v > 0.0f;
+}
+
+void EffectChainSlot::slotControlChainMix(double v) {
+ qDebug() << debugString() << "slotControlChainMix" << v;
+ QMutexLocker locker(&m_mutex);
+
+ // Clamp to [0.0, 1.0]
+ if (v < 0.0f || v > 1.0f) {
+ qDebug() << debugString() << "value out of limits";
+ v = math_clamp(v, 0.0f, 1.0f);
+ m_pControlChainMix->set(v);
+ }
+}
+
+void EffectChainSlot::slotControlChainParameter(double v) {
+ qDebug() << debugString() << "slotControlChainParameter" << v;
+ QMutexLocker locker(&m_mutex);
+
+ // Clamp to [0.0, 1.0]
+ if (v < 0.0f || v > 1.0f) {
+ qDebug() << debugString() << "value out of limits";
+ v = math_clamp(v, 0.0f, 1.0f);
+ m_pControlChainParameter->set(v);
+ }
+ if (m_pEffectChain) {
+ m_pEffectChain->setParameter(v);
+ }
+}
+
+void EffectChainSlot::slotControlChainNextPreset(double v) {
+ qDebug() << debugString() << "slotControlChainNextPreset" << v;
+ //QMutexLocker locker(&m_mutex);
+ // const int read is not worth locking for
+ if (v > 0)
+ emit(nextChain(m_iChainNumber, m_pEffectChain));
+}
+
+void EffectChainSlot::slotControlChainPrevPreset(double v) {
+ qDebug() << debugString() << "slotControlChainPrevPreset" << v;
+ //QMutexLocker locker(&m_mutex);
+ // const int read is not worth locking for
+ if (v > 0)
+ emit(prevChain(m_iChainNumber, m_pEffectChain));
+}
diff --git a/src/effects/effectchainslot.h b/src/effects/effectchainslot.h
new file mode 100644
index 000000000000..4d86613e8b19
--- /dev/null
+++ b/src/effects/effectchainslot.h
@@ -0,0 +1,106 @@
+#ifndef EFFECTCHAINSLOT_H
+#define EFFECTCHAINSLOT_H
+
+#include
+#include
+#include
+#include
+
+#include "util.h"
+#include "effects/effect.h"
+#include "effects/effectslot.h"
+#include "effects/effectchain.h"
+
+class EffectChainSlot;
+typedef QSharedPointer EffectChainSlotPointer;
+
+class EffectChainSlot : public QObject {
+ Q_OBJECT
+ public:
+ EffectChainSlot(QObject* pParent, const unsigned int iChainNumber);
+ virtual ~EffectChainSlot();
+
+ static QString formatGroupString(const unsigned int iChainNumber) {
+ return QString("[EffectChain%1]").arg(iChainNumber+1);
+ }
+
+ // Get the ID of the loaded EffectChain
+ QString id() const;
+
+ // Get the human-readable name of the loaded EffectChain
+ QString name() const;
+
+ unsigned int numSlots() const;
+ void addEffectSlot();
+ EffectSlotPointer getEffectSlot(unsigned int slotNumber);
+
+ void loadEffectChain(EffectChainPointer pEffectChain);
+ EffectChainPointer getEffectChain() const;
+
+ bool isEnabled() const;
+ bool isEnabledForChannel(QString channelId) const;
+
+ void registerChannel(const QString channelId);
+
+ signals:
+ // Indicates that the effect pEffect has been loaded into slotNumber of
+ // EffectChainSlot chainNumber. pEffect may be an invalid pointer, which
+ // indicates that a previously loaded effect was removed from the slot.
+ void effectLoaded(EffectPointer pEffect, unsigned int chainNumber, unsigned int slotNumber);
+
+ // Indicates that the given EffectChain was loaded into this
+ // EffectChainSlot
+ void effectChainLoaded(EffectChainPointer pEffectChain);
+
+ // Signal that whoever is in charge of this EffectChainSlot should load the next
+ // EffectChain into it.
+ void nextChain(const unsigned int iChainSlotNumber, EffectChainPointer pEffectChain);
+
+ // Signal that whoever is in charge of this EffectChainSlot should load the
+ // previous EffectChain into it.
+ void prevChain(const unsigned int iChainSlotNumber, EffectChainPointer pEffectChain);
+
+ // Signal that indicates that the EffectChainSlot has been updated.
+ void updated();
+
+ private slots:
+ void slotChainUpdated();
+ void slotEffectLoaded(EffectPointer pEffect, unsigned int slotNumber);
+
+ void slotControlNumEffects(double v);
+ void slotControlChainEnabled(double v);
+ void slotControlChainMix(double v);
+ void slotControlChainParameter(double v);
+ void slotControlChainNextPreset(double v);
+ void slotControlChainPrevPreset(double v);
+
+ private:
+ QString debugString() const {
+ return QString("EffectChainSlot(%1)").arg(m_iChainNumber);
+ }
+
+ void clear();
+ bool privateIsEnabled() const;
+
+ mutable QMutex m_mutex;
+ const unsigned int m_iChainNumber;
+ const QString m_group;
+
+ EffectChainPointer m_pEffectChain;
+
+ ControlObject* m_pControlNumEffects;
+ ControlObject* m_pControlChainEnabled;
+ ControlObject* m_pControlChainMix;
+ ControlObject* m_pControlChainParameter;
+ ControlObject* m_pControlChainNextPreset;
+ ControlObject* m_pControlChainPrevPreset;
+
+ QMap m_channelEnableControls;
+
+ QList m_slots;
+
+ DISALLOW_COPY_AND_ASSIGN(EffectChainSlot);
+};
+
+
+#endif /* EFFECTCHAINSLOT_H */
diff --git a/src/effects/effectmanifest.cpp b/src/effects/effectmanifest.cpp
new file mode 100644
index 000000000000..35326f54cb93
--- /dev/null
+++ b/src/effects/effectmanifest.cpp
@@ -0,0 +1,5 @@
+#include "effects/effectmanifest.h"
+
+QDebug operator<<(QDebug dbg, const EffectManifest& manifest) {
+ return dbg.maybeSpace() << QString("EffectManifest(%1)").arg(manifest.id());
+}
diff --git a/src/effects/effectmanifest.h b/src/effects/effectmanifest.h
new file mode 100644
index 000000000000..dafc86198c0d
--- /dev/null
+++ b/src/effects/effectmanifest.h
@@ -0,0 +1,86 @@
+#ifndef EFFECTMANIFEST_H
+#define EFFECTMANIFEST_H
+
+#include
+#include
+#include
+
+#include "effects/effectmanifestparameter.h"
+
+// An EffectManifest is a full description of the metadata associated with an
+// effect (e.g. name, author, version, description, etc.) and the parameters of
+// the effect that are intended to be exposed to the rest of Mixxx for user or
+// script control.
+//
+// EffectManifest is composed purely of simple data types, and when an
+// EffectManifest is const, it should be completely immutable. EffectManifest is
+// meant to be used in most cases as a reference, and in Qt collections, so it
+// is important that the implicit copy and assign constructors work, and that
+// the no-argument constructor be non-explicit. All methods are left virtual to
+// allow a backend to replace the entire functionality with its own (for
+// example, a database-backed manifest)
+class EffectManifest {
+ public:
+ EffectManifest() { }
+ virtual ~EffectManifest() {
+ qDebug() << debugString() << "deleted";
+ }
+
+ virtual const QString& id() const {
+ return m_id;
+ }
+ virtual void setId(const QString& id) {
+ m_id = id;
+ }
+
+ virtual const QString& name() const {
+ return m_name;
+ }
+ virtual void setName(const QString& name) {
+ m_name = name;
+ }
+
+ virtual const QString& author() const {
+ return m_author;
+ }
+ virtual void setAuthor(const QString& author) {
+ m_author = author;
+ }
+
+ virtual const QString& version() const {
+ return m_version;
+ }
+ virtual void setVersion(const QString& version) {
+ m_version = version;
+ }
+
+ virtual const QString& description() const {
+ return m_description;
+ }
+ virtual void setDescription(const QString& description) {
+ m_description = description;
+ }
+
+ virtual const QList& parameters() const {
+ return m_parameters;
+ }
+
+ virtual EffectManifestParameter* addParameter() {
+ m_parameters.append(EffectManifestParameter());
+ return &m_parameters.last();
+ }
+
+ private:
+ QString debugString() const {
+ return QString("EffectManifest(%1)").arg(m_id);
+ }
+
+ QString m_id;
+ QString m_name;
+ QString m_author;
+ QString m_version;
+ QString m_description;
+ QList m_parameters;
+};
+
+#endif /* EFFECTMANIFEST_H */
diff --git a/src/effects/effectmanifestparameter.cpp b/src/effects/effectmanifestparameter.cpp
new file mode 100644
index 000000000000..0094635c1489
--- /dev/null
+++ b/src/effects/effectmanifestparameter.cpp
@@ -0,0 +1,5 @@
+#include "effects/effectmanifestparameter.h"
+
+QDebug operator<<(QDebug dbg, const EffectManifestParameter& parameter) {
+ return dbg.maybeSpace() << QString("EffectManifestParameter(%1)").arg(parameter.id());
+}
diff --git a/src/effects/effectmanifestparameter.h b/src/effects/effectmanifestparameter.h
new file mode 100644
index 000000000000..93cfb5bcd36e
--- /dev/null
+++ b/src/effects/effectmanifestparameter.h
@@ -0,0 +1,154 @@
+#ifndef EFFECTMANIFESTPARAMETER_H
+#define EFFECTMANIFESTPARAMETER_H
+
+#include
+#include
+#include
+
+class EffectManifestParameter {
+ public:
+ enum ValueHint {
+ VALUE_UNKNOWN = 0,
+ VALUE_BOOLEAN,
+ VALUE_INTEGRAL,
+ VALUE_FLOAT
+ };
+
+ enum ControlHint {
+ CONTROL_UNKNOWN = 0,
+ CONTROL_KNOB_LINEAR,
+ CONTROL_KNOB_LOGARHYTHMIC,
+ CONTROL_TOGGLE
+ };
+
+ enum SemanticHint {
+ SEMANTIC_UNKNOWN = 0,
+ SEMANTIC_SAMPLES,
+ SEMANTIC_NOTE,
+ };
+
+ enum UnitsHint {
+ UNITS_UNKNOWN = 0,
+ UNITS_TIME,
+ UNITS_HERTZ,
+ UNITS_SAMPLERATE, // fraction of the samplerate
+ };
+
+ EffectManifestParameter() { }
+ virtual ~EffectManifestParameter() {
+ qDebug() << debugString() << "destroyed";
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Parameter Information
+ ////////////////////////////////////////////////////////////////////////////////
+
+ virtual const QString& id() const {
+ return m_id;
+ }
+ virtual void setId(const QString& id) {
+ m_id = id;
+ }
+
+ virtual const QString& name() const {
+ return m_name;
+ }
+ virtual void setName(const QString& name) {
+ m_name = name;
+ }
+
+ virtual const QString& description() const {
+ return m_description;
+ }
+ virtual void setDescription(const QString& description) {
+ m_description = description;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Usage hints
+ ////////////////////////////////////////////////////////////////////////////////
+
+ virtual ControlHint controlHint() const {
+ return m_controlHint;
+ }
+ virtual void setControlHint(ControlHint controlHint) {
+ m_controlHint = controlHint;
+ }
+
+ virtual ValueHint valueHint() const {
+ return m_valueHint;
+ }
+ virtual void setValueHint(ValueHint valueHint) {
+ m_valueHint = valueHint;
+ }
+
+ virtual SemanticHint semanticHint() const {
+ return m_semanticHint;
+ }
+ virtual void setSemanticHint(SemanticHint semanticHint) {
+ m_semanticHint = semanticHint;
+ }
+
+ virtual UnitsHint unitsHint() const {
+ return m_unitsHint;
+ }
+ virtual void setUnitsHint(UnitsHint unitsHint) {
+ m_unitsHint = unitsHint;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Value Settings
+ ////////////////////////////////////////////////////////////////////////////////
+
+ virtual bool hasDefault() const {
+ return m_default.isValid();
+ }
+ virtual const QVariant& getDefault() const {
+ return m_default;
+ }
+ virtual void setDefault(const QVariant& defaultValue) {
+ m_default = defaultValue;
+ }
+
+ virtual bool hasMinimum() const {
+ return m_minimum.isValid();
+ }
+ virtual const QVariant& getMinimum() const {
+ return m_minimum;
+ }
+ virtual void setMinimum(const QVariant& minimum) {
+ m_minimum = minimum;
+ }
+
+ virtual bool hasMaximum() const {
+ return m_maximum.isValid();
+ }
+ virtual const QVariant& getMaximum() const {
+ return m_maximum;
+ }
+ virtual void setMaximum(const QVariant& maximum) {
+ m_maximum = maximum;
+ }
+
+ private:
+ QString debugString() const {
+ return QString("EffectManifestParameter(%1)").arg(m_id);
+ }
+
+ QString m_id;
+ QString m_name;
+ QString m_description;
+
+ ControlHint m_controlHint;
+ ValueHint m_valueHint;
+ SemanticHint m_semanticHint;
+ UnitsHint m_unitsHint;
+
+ QVariant m_default;
+ QVariant m_minimum;
+ QVariant m_maximum;
+};
+
+QDebug operator<<(QDebug dbg, const EffectManifestParameter& parameter);
+
+#endif /* EFFECTMANIFESTPARAMETER_H */
diff --git a/src/effects/effectparameter.cpp b/src/effects/effectparameter.cpp
new file mode 100644
index 000000000000..777bfd2e4883
--- /dev/null
+++ b/src/effects/effectparameter.cpp
@@ -0,0 +1,387 @@
+#include
+
+#include "effects/effectparameter.h"
+#include "effects/effect.h"
+
+EffectParameter::EffectParameter(Effect* pEffect, const EffectManifestParameter& parameter)
+ : QObject(),
+ m_pEffect(pEffect),
+ m_parameter(parameter) {
+ qDebug() << debugString() << "Constructing new EffectParameter from EffectManifestParameter:"
+ << m_parameter.id();
+ switch (m_parameter.valueHint()) {
+ case EffectManifestParameter::VALUE_BOOLEAN:
+ // Minimum and maximum are undefined for a boolean.
+ m_minimum = QVariant();
+ m_maximum = QVariant();
+ if (m_parameter.hasDefault() && m_parameter.getDefault().canConvert()) {
+ m_default = m_parameter.getDefault();
+ } else {
+ // Default to false if no default is given.
+ m_default = QVariant(false);
+ }
+ m_value = m_default;
+ break;
+ case EffectManifestParameter::VALUE_INTEGRAL:
+ m_minimum = m_parameter.hasMinimum() && m_parameter.getMinimum().canConvert() ?
+ m_parameter.getMinimum() : QVariant(0);
+ m_maximum = m_parameter.hasMaximum() && m_parameter.getMinimum().canConvert() ?
+ m_parameter.getMaximum() : QVariant(1);
+
+ // Sanity check the maximum and minimum
+ if (clampRanges()) {
+ qDebug() << debugString() << "WARNING: Parameter maximum is less than the minimum.";
+ }
+
+ // If the parameter specifies a default, set that. Otherwise use the minimum
+ // value.
+ if (m_parameter.hasDefault() && m_parameter.getDefault().canConvert()) {
+ m_default = m_parameter.getDefault();
+ if (m_default.toInt() < m_minimum.toInt() || m_default.toInt() > m_maximum.toInt()) {
+ qDebug() << debugString() << "WARNING: Parameter default is outside of minimum/maximum range.";
+ m_default = m_minimum;
+ }
+ } else {
+ m_default = m_minimum;
+ }
+
+ // Finally, set the value to the default.
+ m_value = m_default;
+ break;
+ case EffectManifestParameter::VALUE_UNKNOWN: // Treat unknown like float
+ case EffectManifestParameter::VALUE_FLOAT:
+ m_minimum = m_parameter.hasMinimum() && m_parameter.getMinimum().canConvert() ?
+ m_parameter.getMinimum() : QVariant(0.0f);
+ m_maximum = m_parameter.hasMaximum() && m_parameter.getMinimum().canConvert() ?
+ m_parameter.getMaximum() : QVariant(1.0f);
+ // Sanity check the maximum and minimum
+ if (m_minimum.toDouble() > m_maximum.toDouble()) {
+ qDebug() << debugString() << "WARNING: Parameter maximum is less than the minimum.";
+ m_maximum = m_minimum;
+ }
+
+ // If the parameter specifies a default, set that. Otherwise use the minimum
+ // value.
+ if (m_parameter.hasDefault() && m_parameter.getDefault().canConvert()) {
+ m_default = m_parameter.getDefault();
+ if (m_default.toDouble() < m_minimum.toDouble() || m_default.toDouble() > m_maximum.toDouble()) {
+ qDebug() << debugString() << "WARNING: Parameter default is outside of minimum/maximum range.";
+ m_default = m_minimum;
+ }
+ } else {
+ m_default = m_minimum;
+ }
+
+ // Finally, set the value to the default.
+ m_value = m_default;
+ break;
+ default:
+ qDebug() << debugString() << "ERROR: Unhandled valueHint";
+ break;
+ }
+}
+
+EffectParameter::~EffectParameter() {
+ qDebug() << debugString() << "destroyed";
+}
+
+const QString EffectParameter::name() const {
+ return m_parameter.name();
+}
+
+const QString EffectParameter::description() const {
+ return m_parameter.description();
+}
+
+// static
+bool EffectParameter::clampValue(EffectManifestParameter::ValueHint valueHint, QVariant& value,
+ const QVariant& minimum, const QVariant& maximum) {
+ switch (valueHint) {
+ case EffectManifestParameter::VALUE_BOOLEAN:
+ break;
+ case EffectManifestParameter::VALUE_INTEGRAL:
+ if (value.toInt() < minimum.toInt()) {
+ value = minimum;
+ return true;
+ } else if (value.toInt() > maximum.toInt()) {
+ value = maximum;
+ return true;
+ }
+ break;
+ case EffectManifestParameter::VALUE_FLOAT:
+ case EffectManifestParameter::VALUE_UNKNOWN:
+ if (value.toDouble() < minimum.toDouble()) {
+ value = minimum;
+ return true;
+ } else if (value.toDouble() > maximum.toDouble()) {
+ value = maximum;
+ return true;
+ }
+ break;
+ default:
+ qDebug() << "ERROR: Unhandled valueHint";
+ break;
+ }
+ return false;
+}
+
+bool EffectParameter::clampValue() {
+ return clampValue(m_parameter.valueHint(), m_value, m_minimum, m_maximum);
+}
+
+bool EffectParameter::clampDefault() {
+ return clampValue(m_parameter.valueHint(), m_default, m_minimum, m_maximum);
+}
+
+bool EffectParameter::checkType(const QVariant& value) const {
+ switch (m_parameter.valueHint()) {
+ case EffectManifestParameter::VALUE_BOOLEAN:
+ return value.canConvert();
+ case EffectManifestParameter::VALUE_INTEGRAL:
+ return value.canConvert();
+ case EffectManifestParameter::VALUE_FLOAT:
+ case EffectManifestParameter::VALUE_UNKNOWN:
+ return value.canConvert();
+ default:
+ qDebug() << debugString() << "ERROR: Unhandled valueHint";
+ break;
+ }
+ return false;
+}
+
+bool EffectParameter::clampRanges() {
+ switch (m_parameter.valueHint()) {
+ case EffectManifestParameter::VALUE_BOOLEAN:
+ break;
+ case EffectManifestParameter::VALUE_INTEGRAL:
+ if (m_minimum.toInt() > m_maximum.toInt()) {
+ m_maximum = m_minimum;
+ return true;
+ }
+ break;
+ case EffectManifestParameter::VALUE_FLOAT:
+ case EffectManifestParameter::VALUE_UNKNOWN:
+ if (m_minimum.toDouble() > m_maximum.toDouble()) {
+ m_maximum = m_minimum;
+ return true;
+ }
+ break;
+ default:
+ qDebug() << debugString() << "ERROR: Unhandled valueHint";
+ break;
+ }
+ return false;
+}
+
+QVariant EffectParameter::getValue() const {
+ return m_value;
+}
+
+void EffectParameter::setValue(QVariant value) {
+ if (!checkType(value)) {
+ qDebug() << debugString() << "WARNING: Value cannot be converted to suitable value, ignoring.";
+ return;
+ }
+
+ switch (m_parameter.valueHint()) {
+ case EffectManifestParameter::VALUE_BOOLEAN:
+ m_value = value.toBool();
+ break;
+ case EffectManifestParameter::VALUE_INTEGRAL:
+ m_value = value.toInt();
+ break;
+ case EffectManifestParameter::VALUE_UNKNOWN: // treat unknown as float
+ case EffectManifestParameter::VALUE_FLOAT:
+ // TODO(XXX) Handle inf, -inf, and nan
+ m_value = value.toDouble();
+ break;
+ default:
+ qDebug() << debugString() << "ERROR: Unhandled valueHint";
+ break;
+ }
+
+ if (clampValue()) {
+ qDebug() << debugString() << "WARNING: Value was outside of limits, clamped.";
+ }
+ if (m_pEffect) {
+ m_pEffect->setEngineParameterById(m_parameter.id(), m_value);
+ }
+}
+
+QVariant EffectParameter::getDefault() const {
+ return m_default;
+}
+
+void EffectParameter::setDefault(QVariant dflt) {
+ if (!checkType(dflt)) {
+ qDebug() << debugString() << "WARNING: Value for default cannot be converted to suitable value, ignoring.";
+ return;
+ }
+
+ switch (m_parameter.valueHint()) {
+ case EffectManifestParameter::VALUE_BOOLEAN:
+ m_default = dflt.toBool();
+ break;
+ case EffectManifestParameter::VALUE_INTEGRAL:
+ m_default = dflt.toInt();
+ break;
+ case EffectManifestParameter::VALUE_UNKNOWN:
+ case EffectManifestParameter::VALUE_FLOAT:
+ m_default = dflt.toDouble();
+ break;
+ default:
+ qDebug() << debugString() << "ERROR: Unhandled valueHint";
+ break;
+ }
+
+ if (clampDefault()) {
+ qDebug() << debugString() << "WARNING: Default parameter value was outside of range, clamped.";
+ }
+}
+
+QVariant EffectParameter::getMinimum() const {
+ return m_minimum;
+}
+
+void EffectParameter::setMinimum(QVariant minimum) {
+ if (!checkType(minimum)) {
+ qDebug() << debugString() << "WARNING: Value for minimum cannot be converted to suitable value, ignoring.";
+ return;
+ }
+
+ switch (m_parameter.valueHint()) {
+ case EffectManifestParameter::VALUE_BOOLEAN:
+ // Minimum doesn't apply to booleans
+ break;
+ case EffectManifestParameter::VALUE_INTEGRAL:
+ m_minimum = minimum.toInt();
+
+ if (m_parameter.hasMinimum() && m_minimum.toInt() < m_parameter.getMinimum().toInt()) {
+ qDebug() << debugString() << "WARNING: Minimum value is less than plugin's absolute minimum, clamping.";
+ m_minimum = m_parameter.getMinimum();
+ }
+
+ if (m_minimum.toInt() > m_maximum.toInt()) {
+ qDebug() << debugString() << "WARNING: New minimum was above maximum, clamped.";
+ m_minimum = m_maximum;
+ }
+
+ // There's a degenerate case here where the maximum could be lower
+ // than the manifest minimum. If that's the case, then the minimum
+ // value is currently below the manifest minimum. Since similar
+ // guards exist in the setMaximum call, this should not be able to
+ // happen.
+ if (m_parameter.hasMinimum()) {
+ Q_ASSERT(m_minimum.toInt() >= m_parameter.getMinimum().toInt());
+ }
+ break;
+ case EffectManifestParameter::VALUE_UNKNOWN:
+ case EffectManifestParameter::VALUE_FLOAT:
+ m_minimum = minimum.toDouble();
+
+ if (m_parameter.hasMinimum() && m_minimum.toDouble() < m_parameter.getMinimum().toDouble()) {
+ qDebug() << debugString() << "WARNING: Minimum value is less than plugin's absolute minimum, clamping.";
+ m_minimum = m_parameter.getMinimum();
+ }
+
+ if (m_minimum.toDouble() > m_maximum.toDouble()) {
+ qDebug() << debugString() << "WARNING: New minimum was above maximum, clamped.";
+ m_minimum = m_maximum;
+ }
+
+ // There's a degenerate case here where the maximum could be lower
+ // than the manifest minimum. If that's the case, then the minimum
+ // value is currently below the manifest minimum. Since similar
+ // guards exist in the setMaximum call, this should not be able to
+ // happen.
+ if (m_parameter.hasMinimum()) {
+ Q_ASSERT(m_minimum.toDouble() >= m_parameter.getMinimum().toDouble());
+ }
+ break;
+ default:
+ qDebug() << debugString() << "ERROR: Unhandled valueHint";
+ break;
+ }
+
+ if (clampValue()) {
+ qDebug() << debugString() << "WARNING: Value was outside of new minimum, clamped.";
+ }
+
+ if (clampDefault()) {
+ qDebug() << debugString() << "WARNING: Default was outside of new minimum, clamped.";
+ }
+}
+
+QVariant EffectParameter::getMaximum() const {
+ return m_maximum;
+}
+
+void EffectParameter::setMaximum(QVariant maximum) {
+ if (!checkType(maximum)) {
+ qDebug() << debugString() << "WARNING: Value for maximum cannot be converted to suitable value, ignoring.";
+ return;
+ }
+
+ switch (m_parameter.valueHint()) {
+ case EffectManifestParameter::VALUE_BOOLEAN:
+ // Maximum doesn't apply to booleans
+ break;
+ case EffectManifestParameter::VALUE_INTEGRAL:
+ m_maximum = maximum.toInt();
+
+ if (m_parameter.hasMaximum() && m_maximum.toInt() > m_parameter.getMaximum().toInt()) {
+ qDebug() << debugString() << "WARNING: Maximum value is less than plugin's absolute maximum, clamping.";
+ m_maximum = m_parameter.getMaximum();
+ }
+
+ if (m_maximum.toInt() < m_minimum.toInt()) {
+ qDebug() << debugString() << "WARNING: New maximum was below the minimum, clamped.";
+ m_maximum = m_minimum;
+ }
+
+ // There's a degenerate case here where the minimum could be larger
+ // than the manifest maximum. If that's the case, then the maximum
+ // value is currently above the manifest maximum. Since similar
+ // guards exist in the setMinimum call, this should not be able to
+ // happen.
+ if (m_parameter.hasMaximum()) {
+ Q_ASSERT(m_maximum.toInt() <= m_parameter.getMaximum().toInt());
+ }
+ break;
+ case EffectManifestParameter::VALUE_UNKNOWN:
+ case EffectManifestParameter::VALUE_FLOAT:
+ m_maximum = maximum.toDouble();
+
+ if (m_parameter.hasMaximum() && m_maximum.toDouble() > m_parameter.getMaximum().toDouble()) {
+ qDebug() << debugString() << "WARNING: Maximum value is less than plugin's absolute maximum, clamping.";
+ m_maximum = m_parameter.getMaximum();
+ }
+
+ if (m_maximum.toDouble() < m_minimum.toDouble()) {
+ qDebug() << debugString() << "WARNING: New maximum was below the minimum, clamped.";
+ m_maximum = m_minimum;
+ }
+
+ // There's a degenerate case here where the minimum could be larger
+ // than the manifest maximum. If that's the case, then the maximum
+ // value is currently above the manifest maximum. Since similar
+ // guards exist in the setMinimum call, this should not be able to
+ // happen.
+ if (m_parameter.hasMaximum()) {
+ Q_ASSERT(m_maximum.toDouble() <= m_parameter.getMaximum().toDouble());
+ }
+ break;
+ default:
+ qDebug() << debugString() << "ERROR: Unhandled valueHint";
+ break;
+ }
+
+ if (clampValue()) {
+ qDebug() << debugString() << "WARNING: Value was outside of new maximum, clamped.";
+ }
+
+ if (clampDefault()) {
+ qDebug() << debugString() << "WARNING: Default was outside of new maximum, clamped.";
+ }
+}
+
diff --git a/src/effects/effectparameter.h b/src/effects/effectparameter.h
new file mode 100644
index 000000000000..7541ebfc212a
--- /dev/null
+++ b/src/effects/effectparameter.h
@@ -0,0 +1,68 @@
+#ifndef EFFECTPARAMETER_H
+#define EFFECTPARAMETER_H
+
+#include
+#include
+
+#include "util.h"
+#include "effects/effectmanifestparameter.h"
+
+class Effect;
+
+// An EffectParameter is an instance of an EffectManifestParameter, which is in
+// charge of keeping track of the instance values for the default, minimum,
+// maximum and value for each Effect's parameter, and validating that they are
+// always within acceptable ranges.
+class EffectParameter : public QObject {
+ Q_OBJECT
+ public:
+ EffectParameter(Effect* pEffect, const EffectManifestParameter& parameter);
+ virtual ~EffectParameter();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Parameter Information
+ ///////////////////////////////////////////////////////////////////////////
+
+ const QString name() const;
+ const QString description() const;
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Value Settings
+ ///////////////////////////////////////////////////////////////////////////
+
+ QVariant getValue() const;
+ void setValue(QVariant value);
+
+ QVariant getDefault() const;
+ void setDefault(QVariant defaultValue);
+
+ QVariant getMinimum() const;
+ void setMinimum(QVariant minimum);
+
+ QVariant getMaximum() const;
+ void setMaximum(QVariant maximum);
+
+ private:
+ QString debugString() const {
+ return QString("EffectParameter(%1)").arg(m_parameter.name());
+ }
+
+ static bool clampValue(EffectManifestParameter::ValueHint valueHint, QVariant& value,
+ const QVariant& minimum, const QVariant& maximum);
+ bool clampValue();
+ bool clampDefault();
+ bool clampRanges();
+ bool checkType(const QVariant& value) const;
+
+ Effect* m_pEffect;
+ EffectManifestParameter m_parameter;
+ QVariant m_minimum;
+ QVariant m_maximum;
+ QVariant m_default;
+ QVariant m_value;
+
+ DISALLOW_COPY_AND_ASSIGN(EffectParameter);
+};
+
+
+#endif /* EFFECTPARAMETER_H */
diff --git a/src/effects/effectparameterslot.cpp b/src/effects/effectparameterslot.cpp
new file mode 100644
index 000000000000..f46cabd082bf
--- /dev/null
+++ b/src/effects/effectparameterslot.cpp
@@ -0,0 +1,237 @@
+#include
+#include
+
+#include "defs.h"
+#include "controlpotmeter.h"
+#include "effects/effectparameterslot.h"
+
+EffectParameterSlot::EffectParameterSlot(QObject* pParent, const unsigned int iChainNumber,
+ const unsigned int iSlotNumber,
+ const unsigned int iParameterNumber)
+ : QObject(),
+ m_mutex(QMutex::Recursive),
+ m_iChainNumber(iChainNumber),
+ m_iSlotNumber(iSlotNumber),
+ m_iParameterNumber(iParameterNumber),
+ m_group(formatGroupString(iChainNumber, iSlotNumber, iParameterNumber)),
+ m_pEffectParameter(NULL) {
+ m_pControlEnabled = new ControlObject(ConfigKey(m_group, QString("enabled")));
+ m_pControlLinked = new ControlObject(ConfigKey(m_group, QString("linked")));
+ m_pControlValue = new ControlObject(ConfigKey(m_group, QString("value")));
+ m_pControlValueNormalized = new ControlPotmeter(ConfigKey(m_group, QString("value_normalized")), 0.0, 1.0);
+ m_pControlValueType = new ControlObject(ConfigKey(m_group, QString("value_type")));
+ m_pControlValueDefault = new ControlObject(ConfigKey(m_group, QString("value_default")));
+ m_pControlValueMaximum = new ControlObject(ConfigKey(m_group, QString("value_max")));
+ m_pControlValueMaximumLimit = new ControlObject(ConfigKey(m_group, QString("value_max_limit")));
+ m_pControlValueMinimum = new ControlObject(ConfigKey(m_group, QString("value_min")));
+ m_pControlValueMinimumLimit = new ControlObject(ConfigKey(m_group, QString("value_min_limit")));
+
+ connect(m_pControlEnabled, SIGNAL(valueChanged(double)),
+ this, SLOT(slotEnabled(double)),
+ Qt::DirectConnection);
+ connect(m_pControlLinked, SIGNAL(valueChanged(double)),
+ this, SLOT(slotEnabled(double)),
+ Qt::DirectConnection);
+ connect(m_pControlValue, SIGNAL(valueChanged(double)),
+ this, SLOT(slotValue(double)),
+ Qt::DirectConnection);
+ connect(m_pControlValueNormalized, SIGNAL(valueChanged(double)),
+ this, SLOT(slotValueNormalized(double)),
+ Qt::DirectConnection);
+ connect(m_pControlValueType, SIGNAL(valueChanged(double)),
+ this, SLOT(slotValueType(double)),
+ Qt::DirectConnection);
+ connect(m_pControlValueDefault, SIGNAL(valueChanged(double)),
+ this, SLOT(slotValueDefault(double)),
+ Qt::DirectConnection);
+ connect(m_pControlValueMaximum, SIGNAL(valueChanged(double)),
+ this, SLOT(slotValueMaximum(double)),
+ Qt::DirectConnection);
+ connect(m_pControlValueMaximumLimit, SIGNAL(valueChanged(double)),
+ this, SLOT(slotValueMaximumLimit(double)),
+ Qt::DirectConnection);
+ connect(m_pControlValueMinimum, SIGNAL(valueChanged(double)),
+ this, SLOT(slotValueMinimum(double)),
+ Qt::DirectConnection);
+ connect(m_pControlValueMinimumLimit, SIGNAL(valueChanged(double)),
+ this, SLOT(slotValueMinimumLimit(double)),
+ Qt::DirectConnection);
+
+ clear();
+}
+
+EffectParameterSlot::~EffectParameterSlot() {
+ qDebug() << debugString() << "destroyed";
+ m_pEffectParameter = NULL;
+ m_pEffect.clear();
+ delete m_pControlEnabled;
+ delete m_pControlLinked;
+ delete m_pControlValue;
+ delete m_pControlValueNormalized;
+ delete m_pControlValueType;
+ delete m_pControlValueDefault;
+ delete m_pControlValueMaximum;
+ delete m_pControlValueMaximumLimit;
+ delete m_pControlValueMinimum;
+ delete m_pControlValueMinimumLimit;
+}
+
+void EffectParameterSlot::loadEffect(EffectPointer pEffect) {
+ qDebug() << debugString() << "loadEffect" << (pEffect ? pEffect->getManifest().name() : "(null)");
+ QMutexLocker locker(&m_mutex);
+ if (pEffect) {
+ m_pEffect = pEffect;
+ // Returns null if it doesn't have a parameter for that number
+ m_pEffectParameter = pEffect->getParameter(m_iParameterNumber);
+
+ if (m_pEffectParameter) {
+ qDebug() << debugString() << "Loading effect parameter" << m_pEffectParameter->name();
+ double dValue = m_pEffectParameter->getValue().toDouble();
+ double dMinimum = m_pEffectParameter->getMinimum().toDouble();
+ double dMinimumLimit = dMinimum; // TODO(rryan) expose limit from EffectParameter
+ double dMaximum = m_pEffectParameter->getMaximum().toDouble();
+ double dMaximumLimit = dMaximum; // TODO(rryan) expose limit from EffectParameter
+ double dDefault = m_pEffectParameter->getDefault().toDouble();
+
+ if (dValue > dMaximum || dValue < dMinimum ||
+ dMinimum < dMinimumLimit || dMaximum > dMaximumLimit ||
+ dDefault > dMaximum || dDefault < dMinimum) {
+ qDebug() << debugString() << "WARNING: EffectParameter does not satisfy basic sanity checks.";
+ }
+ double dNormalized = (dValue - dMinimum) / (dMaximum - dMinimum);
+
+ qDebug() << debugString() << QString("Val: %1 Min: %2 MinLimit: %3 Max: %4 MaxLimit: %5 Default: %6 Norm: %7").arg(dValue).arg(dMinimum).arg(dMinimumLimit).arg(dMaximum).arg(dMaximumLimit).arg(dDefault).arg(dNormalized);
+
+ m_pControlValue->set(dValue);
+ // Convert to stupid GUI widget convention
+ m_pControlValueNormalized->set(dNormalized);
+ m_pControlValueMinimum->set(dMinimum);
+ m_pControlValueMinimumLimit->set(dMinimumLimit);
+ m_pControlValueMaximum->set(dMaximum);
+ m_pControlValueMaximumLimit->set(dMaximumLimit);
+ m_pControlValueType->set(0); // TODO(rryan) expose this from EffectParameter
+ m_pControlValueDefault->set(dDefault);
+ // Default loaded parameters to enabled and unlinked
+ m_pControlEnabled->set(1.0f);
+ m_pControlLinked->set(0.0f);
+ }
+ } else {
+ clear();
+ }
+}
+
+void EffectParameterSlot::clear() {
+ qDebug() << debugString() << "clear";
+ m_pEffectParameter = NULL;
+ m_pEffect.clear();
+ m_pControlEnabled->set(0.0f);
+ m_pControlValue->set(0.0f);
+ m_pControlValueNormalized->set(0.0f);
+ m_pControlValueType->set(0.0f);
+ m_pControlValueDefault->set(0.0f);
+ m_pControlValueMaximum->set(0.0f);
+ m_pControlValueMaximumLimit->set(0.0f);
+ m_pControlValueMinimum->set(0.0f);
+ m_pControlValueMinimumLimit->set(0.0f);
+}
+
+void EffectParameterSlot::slotEnabled(double v) {
+ qDebug() << debugString() << "slotEnabled" << v;
+ QMutexLocker locker(&m_mutex);
+ qDebug() << "WARNING: Somebody has set a read-only control. Stability may be compromised.";
+ // TODO(rryan) add protection
+}
+
+void EffectParameterSlot::slotLinked(double v) {
+ qDebug() << debugString() << "slotLinked" << v;
+ QMutexLocker locker(&m_mutex);
+}
+
+void EffectParameterSlot::slotValue(double v) {
+ qDebug() << debugString() << "slotValue" << v;
+ QMutexLocker locker(&m_mutex);
+
+ double dMin = m_pControlValueMinimum->get();
+ double dMax = m_pControlValueMaximum->get();
+ if (v < dMin || v > dMax) {
+ qDebug() << debugString() << "value out of limits";
+ v = math_clamp(v, dMin, dMax);
+ m_pControlValue->set(v);
+ }
+ double dNormalized = (dMax - dMin > 0) ? (v - dMin) / (dMax - dMin) : 0.0f;
+ m_pControlValueNormalized->set(dNormalized);
+
+ if (m_pEffectParameter) {
+ m_pEffectParameter->setValue(v);
+ }
+}
+
+void EffectParameterSlot::slotValueNormalized(double v) {
+ qDebug() << debugString() << "slotValueNormalized" << v;
+ QMutexLocker locker(&m_mutex);
+
+ // Set the raw value to match the interpolated equivalent.
+ double dMin = m_pControlValueMinimum->get();
+ double dMax = m_pControlValueMaximum->get();
+ // TODO(rryan) implement curve types, just linear for now.
+ double dRaw = dMin + v * (dMax - dMin);
+ qDebug() << debugString() << "Normalized set of" << v << "produces raw value of" << dRaw;
+ m_pControlValue->set(dRaw);
+
+ if (m_pEffectParameter) {
+ m_pEffectParameter->setValue(dRaw);
+ }
+}
+
+void EffectParameterSlot::slotValueType(double v) {
+ qDebug() << debugString() << "slotValueType" << v;
+ QMutexLocker locker(&m_mutex);
+ qDebug() << debugString() << "WARNING: Somebody has set a read-only control. Stability may be compromised.";
+}
+
+void EffectParameterSlot::slotValueDefault(double v) {
+ qDebug() << debugString() << "slotValueDefault" << v;
+ QMutexLocker locker(&m_mutex);
+ qDebug() << debugString() << "WARNING: Somebody has set a read-only control. Stability may be compromised.";
+}
+
+void EffectParameterSlot::slotValueMaximum(double v) {
+ qDebug() << debugString() << "slotValueMaximum" << v;
+ QMutexLocker locker(&m_mutex);
+ double dMaxLimit = m_pControlValueMaximumLimit->get();
+ if (v > dMaxLimit) {
+ qDebug() << "WARNING: Maximum parameter value is out of limits.";
+ v = dMaxLimit;
+ m_pControlValueMaximum->set(v);
+ }
+ if (m_pEffectParameter) {
+ m_pEffectParameter->setMaximum(v);
+ }
+}
+
+void EffectParameterSlot::slotValueMaximumLimit(double v) {
+ qDebug() << debugString() << "slotValueMaximumLimit" << v;
+ QMutexLocker locker(&m_mutex);
+ qDebug() << "WARNING: Somebody has set a read-only control. Stability may be compromised.";
+}
+
+void EffectParameterSlot::slotValueMinimum(double v) {
+ qDebug() << debugString() << "slotValueMinimum" << v;
+ QMutexLocker locker(&m_mutex);
+ double dMinLimit = m_pControlValueMinimumLimit->get();
+ if (v < dMinLimit) {
+ qDebug() << "WARNING: Minimum parameter value is out of limits.";
+ v = dMinLimit;
+ m_pControlValueMinimum->set(v);
+ }
+
+ if (m_pEffectParameter) {
+ m_pEffectParameter->setMinimum(v);
+ }
+}
+
+void EffectParameterSlot::slotValueMinimumLimit(double v) {
+ qDebug() << debugString() << "slotValueMinimumLimit" << v;
+ QMutexLocker locker(&m_mutex);
+ qDebug() << debugString() << "WARNING: Somebody has set a read-only control. Stability may be compromised.";
+}
diff --git a/src/effects/effectparameterslot.h b/src/effects/effectparameterslot.h
new file mode 100644
index 000000000000..1ab0ee89f1c7
--- /dev/null
+++ b/src/effects/effectparameterslot.h
@@ -0,0 +1,78 @@
+#ifndef EFFECTPARAMETERSLOT_H
+#define EFFECTPARAMETERSLOT_H
+
+#include
+#include
+#include
+#include
+
+#include "util.h"
+#include "controlobject.h"
+#include "effects/effect.h"
+
+class EffectParameterSlot : QObject {
+ Q_OBJECT
+ public:
+ EffectParameterSlot(QObject* pParent, const unsigned int iChainNumber,
+ const unsigned int iSlotNumber, const unsigned int iParameterNumber);
+ virtual ~EffectParameterSlot();
+
+ static QString formatGroupString(const unsigned int iChainNumber, const unsigned int iSlotNumber,
+ const unsigned int iParameterNumber) {
+ return QString("[EffectChain%1_Effect%2_Parameter%3]")
+ .arg(iChainNumber+1)
+ .arg(iSlotNumber+1)
+ .arg(iParameterNumber+1);
+ }
+
+ // Load the parameter of the given effect into this EffectParameterSlot
+ void loadEffect(EffectPointer pEffect);
+
+ private slots:
+ // Solely for handling control changes
+ void slotEnabled(double v);
+ void slotLinked(double v);
+ void slotValue(double v);
+ void slotValueNormalized(double v);
+ void slotValueType(double v);
+ void slotValueDefault(double v);
+ void slotValueMaximum(double v);
+ void slotValueMaximumLimit(double v);
+ void slotValueMinimum(double v);
+ void slotValueMinimumLimit(double v);
+
+ private:
+ QString debugString() const {
+ return QString("EffectParameterSlot(%1,%2)").arg(m_group).arg(m_iParameterNumber);
+ }
+
+ // Clear the currently loaded effect
+ void clear();
+
+ mutable QMutex m_mutex;
+ const unsigned int m_iChainNumber;
+ const unsigned int m_iSlotNumber;
+ const unsigned int m_iParameterNumber;
+ const QString m_group;
+ EffectPointer m_pEffect;
+ EffectParameter* m_pEffectParameter;
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Controls exposed to the rest of Mixxx
+ ////////////////////////////////////////////////////////////////////////////////
+
+ ControlObject* m_pControlEnabled;
+ ControlObject* m_pControlLinked;
+ ControlObject* m_pControlValue;
+ ControlObject* m_pControlValueNormalized;
+ ControlObject* m_pControlValueType;
+ ControlObject* m_pControlValueDefault;
+ ControlObject* m_pControlValueMaximum;
+ ControlObject* m_pControlValueMaximumLimit;
+ ControlObject* m_pControlValueMinimum;
+ ControlObject* m_pControlValueMinimumLimit;
+
+ DISALLOW_COPY_AND_ASSIGN(EffectParameterSlot);
+};
+
+#endif /* EFFECTPARAMETERSLOT_H */
diff --git a/src/effects/effectprocessor.h b/src/effects/effectprocessor.h
new file mode 100644
index 000000000000..a9ee70560bed
--- /dev/null
+++ b/src/effects/effectprocessor.h
@@ -0,0 +1,30 @@
+#ifndef EFFECTPROCESSOR_H
+#define EFFECTPROCESSOR_H
+
+#include
+
+#include "defs.h"
+
+class EngineEffect;
+
+class EffectProcessor {
+ public:
+ virtual ~EffectProcessor() { }
+
+ virtual void initialize(EngineEffect* pEffect) = 0;
+
+ // Take a buffer of numSamples samples of audio from channel channelId,
+ // provided as pInput, process the buffer according to Effect-specific
+ // logic, and output it to the buffer pOutput. If pInput is equal to
+ // pOutput, then the operation must occur in-place. Both pInput and pOutput
+ // are represented as stereo interleaved samples. There are numSamples total
+ // samples, so numSamples/2 left channel samples and numSamples/2 right
+ // channel samples. The channelId provided allows the effect to maintain
+ // state on a per-channel basis. This is important because one Effect
+ // instance may be used to process the audio of multiple channels.
+ virtual void process(const QString& channelId,
+ const CSAMPLE* pInput, CSAMPLE* pOutput,
+ const unsigned int numSamples) = 0;
+};
+
+#endif /* EFFECTPROCESSOR_H */
diff --git a/src/effects/effectsbackend.cpp b/src/effects/effectsbackend.cpp
new file mode 100644
index 000000000000..8c7714b7db63
--- /dev/null
+++ b/src/effects/effectsbackend.cpp
@@ -0,0 +1,60 @@
+#include
+#include
+
+#include "effects/effectsbackend.h"
+
+EffectsBackend::EffectsBackend(QObject* pParent, QString name)
+ : QObject(pParent),
+ m_name(name) {
+}
+
+EffectsBackend::~EffectsBackend() {
+}
+
+const QString EffectsBackend::getName() const {
+ QMutexLocker locker(&m_mutex);
+ return m_name;
+}
+
+void EffectsBackend::registerEffect(const QString id,
+ const EffectManifest& manifest,
+ EffectInstantiator pInstantiator) {
+ QMutexLocker locker(&m_mutex);
+ if (m_registeredEffects.contains(id)) {
+ qDebug() << "WARNING: Effect" << id << "already registered";
+ return;
+ }
+
+ m_registeredEffects[id] = QPair(
+ manifest, pInstantiator);
+}
+
+const QSet EffectsBackend::getEffectIds() const {
+ QMutexLocker locker(&m_mutex);
+ return QSet::fromList(m_registeredEffects.keys());
+}
+
+EffectManifest EffectsBackend::getManifest(const QString& effectId) const {
+ QMutexLocker locker(&m_mutex);
+ if (!m_registeredEffects.contains(effectId)) {
+ qDebug() << "WARNING: Effect" << effectId << "is not registered.";
+ return EffectManifest();
+ }
+ return m_registeredEffects[effectId].first;
+}
+
+bool EffectsBackend::canInstantiateEffect(const QString& effectId) const {
+ QMutexLocker locker(&m_mutex);
+ return m_registeredEffects.contains(effectId);
+}
+
+EffectPointer EffectsBackend::instantiateEffect(const QString& effectId) {
+ QMutexLocker locker(&m_mutex);
+ if (!m_registeredEffects.contains(effectId)) {
+ qDebug() << "WARNING: Effect" << effectId << "is not registered.";
+ return EffectPointer();
+ }
+ QPair& effectInfo = m_registeredEffects[effectId];
+ return (*effectInfo.second)(this, effectInfo.first);
+}
+
diff --git a/src/effects/effectsbackend.h b/src/effects/effectsbackend.h
new file mode 100644
index 000000000000..4e34be1a890a
--- /dev/null
+++ b/src/effects/effectsbackend.h
@@ -0,0 +1,42 @@
+#ifndef EFFECTSBACKEND_H
+#define EFFECTSBACKEND_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include "effects/effect.h"
+
+class EffectsBackend;
+typedef EffectPointer (*EffectInstantiator)(EffectsBackend*, const EffectManifest&);
+
+// An EffectsBackend is an implementation of a provider of Effect's for use
+// within the rest of Mixxx. The job of the EffectsBackend is to both enumerate
+// and instantiate effects.
+class EffectsBackend : public QObject {
+ Q_OBJECT
+ public:
+ EffectsBackend(QObject* pParent, QString name);
+ virtual ~EffectsBackend();
+
+ virtual const QString getName() const;
+
+ virtual const QSet getEffectIds() const;
+ virtual EffectManifest getManifest(const QString& effectId) const;
+ virtual bool canInstantiateEffect(const QString& effectId) const;
+ virtual EffectPointer instantiateEffect(const QString& effectId);
+
+ protected:
+ void registerEffect(const QString id,
+ const EffectManifest& manifest,
+ EffectInstantiator pInstantiator);
+
+ private:
+ mutable QMutex m_mutex;
+ QString m_name;
+ QMap > m_registeredEffects;
+};
+
+#endif /* EFFECTSBACKEND_H */
diff --git a/src/effects/effectslot.cpp b/src/effects/effectslot.cpp
new file mode 100644
index 000000000000..9c57f701fcec
--- /dev/null
+++ b/src/effects/effectslot.cpp
@@ -0,0 +1,76 @@
+#include
+
+#include "effects/effectslot.h"
+
+// The maximum number of effect parameters we're going to support.
+const unsigned int kMaxParameters = 20;
+
+EffectSlot::EffectSlot(QObject* pParent, const unsigned int iChainNumber,
+ const unsigned int iSlotNumber)
+ : QObject(),
+ m_mutex(QMutex::Recursive),
+ m_iChainNumber(iChainNumber),
+ m_iSlotNumber(iSlotNumber),
+ // The control group names are 1-indexed while internally everything is 0-indexed.
+ m_group(formatGroupString(m_iChainNumber, m_iSlotNumber)) {
+ m_pControlEnabled = new ControlObject(ConfigKey(m_group, "enabled"));
+ m_pControlNumParameters = new ControlObject(ConfigKey(m_group, "num_parameters"));
+
+ for (unsigned int i = 0; i < kMaxParameters; ++i) {
+ EffectParameterSlot* pParameter = new EffectParameterSlot(
+ this, m_iChainNumber, m_iSlotNumber, m_parameters.size());
+ m_parameters.append(pParameter);
+ }
+
+ clear();
+}
+
+EffectSlot::~EffectSlot() {
+ qDebug() << debugString() << "destroyed";
+ clear();
+
+ delete m_pControlEnabled;
+ delete m_pControlNumParameters;
+
+ while (!m_parameters.isEmpty()) {
+ EffectParameterSlot* pParameter = m_parameters.takeLast();
+ delete pParameter;
+ }
+}
+
+EffectPointer EffectSlot::getEffect() const {
+ QMutexLocker locker(&m_mutex);
+ return m_pEffect;
+}
+
+void EffectSlot::loadEffect(EffectPointer pEffect) {
+ qDebug() << debugString() << "loadEffect" << (pEffect ? pEffect->getManifest().name() : "(null)");
+ QMutexLocker locker(&m_mutex);
+ if (pEffect) {
+ m_pEffect = pEffect;
+ m_pControlEnabled->set(1.0f);
+ m_pControlNumParameters->set(m_pEffect->getManifest().parameters().size());
+
+ foreach (EffectParameterSlot* pParameter, m_parameters) {
+ pParameter->loadEffect(m_pEffect);
+ }
+
+ // Always unlock before signalling to prevent deadlock
+ locker.unlock();
+ emit(effectLoaded(m_pEffect, m_iSlotNumber));
+ } else {
+ clear();
+ locker.unlock();
+ // Broadcasts a null effect pointer
+ emit(effectLoaded(m_pEffect, m_iSlotNumber));
+ }
+}
+
+void EffectSlot::clear() {
+ m_pEffect.clear();
+ m_pControlEnabled->set(0.0f);
+ m_pControlNumParameters->set(0.0f);
+ foreach (EffectParameterSlot* pParameter, m_parameters) {
+ pParameter->loadEffect(EffectPointer());
+ }
+}
diff --git a/src/effects/effectslot.h b/src/effects/effectslot.h
new file mode 100644
index 000000000000..31615c2618db
--- /dev/null
+++ b/src/effects/effectslot.h
@@ -0,0 +1,63 @@
+#ifndef EFFECTSLOT_H
+#define EFFECTSLOT_H
+
+#include
+#include
+#include
+#include
+
+#include "util.h"
+#include "controlobject.h"
+#include "effects/effect.h"
+#include "effects/effectparameterslot.h"
+
+class EffectSlot;
+typedef QSharedPointer EffectSlotPointer;
+
+class EffectSlot : public QObject {
+ Q_OBJECT
+ public:
+ EffectSlot(QObject* pParent, const unsigned int iChainNumber, const unsigned int iSlotNumber);
+ virtual ~EffectSlot();
+
+ static QString formatGroupString(const unsigned int iChainNumber, const unsigned int iSlotNumber) {
+ return QString("[EffectChain%1_Effect%2]").arg(iChainNumber+1).arg(iSlotNumber+1);
+ }
+
+ // Return the currently loaded effect, if any. If no effect is loaded,
+ // returns a null EffectPointer.
+ EffectPointer getEffect() const;
+
+ public slots:
+ // Request that this EffectSlot load the given Effect
+ void loadEffect(EffectPointer pEffect);
+
+ signals:
+ // Indicates that the effect pEffect has been loaded into this
+ // EffectSlot. The slotNumber is provided for the convenience of listeners.
+ // pEffect may be an invalid pointer, which indicates that a previously
+ // loaded effect was removed from the slot.
+ void effectLoaded(EffectPointer pEffect, unsigned int slotNumber);
+
+ private:
+ QString debugString() const {
+ return QString("EffectSlot(%1,%2)").arg(m_iChainNumber).arg(m_iSlotNumber);
+ }
+
+ // Unload the currently loaded effect
+ void clear();
+
+ mutable QMutex m_mutex;
+ const unsigned int m_iChainNumber;
+ const unsigned int m_iSlotNumber;
+ const QString m_group;
+ EffectPointer m_pEffect;
+
+ ControlObject* m_pControlEnabled;
+ ControlObject* m_pControlNumParameters;
+ QList m_parameters;
+
+ DISALLOW_COPY_AND_ASSIGN(EffectSlot);
+};
+
+#endif /* EFFECTSLOT_H */
diff --git a/src/effects/effectsmanager.cpp b/src/effects/effectsmanager.cpp
new file mode 100644
index 000000000000..3de9a6774a3c
--- /dev/null
+++ b/src/effects/effectsmanager.cpp
@@ -0,0 +1,198 @@
+
+#include "effects/effectsmanager.h"
+
+#include "effects/effectchainmanager.h"
+
+// TODO(rryan) REMOVE
+#include "effects/native/flangereffect.h"
+
+EffectsManager::EffectsManager(QObject* pParent)
+ : QObject(pParent),
+ m_mutex(QMutex::Recursive) {
+ m_pEffectChainManager = new EffectChainManager(this);
+}
+
+EffectsManager::~EffectsManager() {
+ m_effectChainSlots.clear();
+ while (!m_effectsBackends.isEmpty()) {
+ EffectsBackend* pBackend = m_effectsBackends.takeLast();
+ delete pBackend;
+ }
+ delete m_pEffectChainManager;
+}
+
+void EffectsManager::addEffectsBackend(EffectsBackend* pBackend) {
+ QMutexLocker locker(&m_mutex);
+ Q_ASSERT(pBackend);
+ m_effectsBackends.append(pBackend);
+}
+
+unsigned int EffectsManager::numEffectChainSlots() const {
+ QMutexLocker locker(&m_mutex);
+ return m_effectChainSlots.size();
+}
+
+void EffectsManager::addEffectChainSlot() {
+ QMutexLocker locker(&m_mutex);
+ EffectChainSlot* pChainSlot = new EffectChainSlot(this, m_effectChainSlots.size());
+
+ // TODO(rryan) How many should we make default? They create controls that
+ // the GUI may rely on, so the choice is important to communicate to skin
+ // designers.
+ pChainSlot->addEffectSlot();
+ pChainSlot->addEffectSlot();
+ pChainSlot->addEffectSlot();
+ pChainSlot->addEffectSlot();
+
+ connect(pChainSlot, SIGNAL(nextChain(const unsigned int, EffectChainPointer)),
+ this, SLOT(loadNextChain(const unsigned int, EffectChainPointer)));
+ connect(pChainSlot, SIGNAL(prevChain(const unsigned int, EffectChainPointer)),
+ this, SLOT(loadPrevChain(const unsigned int, EffectChainPointer)));
+
+ // Register all the existing channels with the new EffectChain
+ foreach (QString channelId, m_registeredChannels) {
+ pChainSlot->registerChannel(channelId);
+ }
+
+ m_effectChainSlots.append(EffectChainSlotPointer(pChainSlot));
+}
+
+EffectChainSlotPointer EffectsManager::getEffectChainSlot(unsigned int i) {
+ QMutexLocker locker(&m_mutex);
+ if (i >= m_effectChainSlots.size()) {
+ qDebug() << "WARNING: Invalid index for getEffectChainSlot";
+ return EffectChainSlotPointer();
+ }
+ return m_effectChainSlots[i];
+}
+
+void EffectsManager::process(const QString channelId,
+ const CSAMPLE* pInput, CSAMPLE* pOutput,
+ const unsigned int numSamples) {
+ QMutexLocker locker(&m_mutex);
+
+ QList enabledEffects;
+
+ for (int i = 0; i < m_effectChainSlots.size(); ++i) {
+ EffectChainSlotPointer pChainSlot = m_effectChainSlots[i];
+ if (pChainSlot->isEnabledForChannel(channelId))
+ enabledEffects.append(pChainSlot->getEffectChain());
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // AFTER THIS LINE, THE MUTEX IS UNLOCKED. DONT TOUCH ANY MEMBER STATE
+ ////////////////////////////////////////////////////////////////////////////
+ locker.unlock();
+
+ bool inPlace = pInput == pOutput;
+ for (int i = 0; i < enabledEffects.size(); ++i) {
+ EffectChainPointer pChain = enabledEffects[i];
+
+ if (!pChain)
+ continue;
+
+ if (inPlace) {
+ // Since we're doing this in-place, using a temporary buffer doesn't
+ // matter.
+ pChain->process(channelId, pInput, pOutput, numSamples);
+ } else {
+ qDebug() << debugString() << "WARNING: non-inplace processing not implemented!";
+ // TODO(rryan) implement. Trickier because you have to use temporary
+ // memory. Punting this for now just to get everything working.
+ }
+ }
+}
+
+void EffectsManager::registerChannel(const QString channelId) {
+ QMutexLocker locker(&m_mutex);
+ if (m_registeredChannels.contains(channelId)) {
+ qDebug() << debugString() << "WARNING: Channel already registered:" << channelId;
+ return;
+ }
+
+ m_registeredChannels.insert(channelId);
+ foreach (EffectChainSlotPointer pEffectChainSlot, m_effectChainSlots) {
+ pEffectChainSlot->registerChannel(channelId);
+ }
+}
+
+void EffectsManager::loadNextChain(const unsigned int iChainSlotNumber, EffectChainPointer pLoadedChain) {
+ QMutexLocker locker(&m_mutex);
+ EffectChainPointer pNextChain = m_pEffectChainManager->getNextEffectChain(pLoadedChain);
+ m_effectChainSlots[iChainSlotNumber]->loadEffectChain(pNextChain);
+}
+
+
+void EffectsManager::loadPrevChain(const unsigned int iChainSlotNumber, EffectChainPointer pLoadedChain) {
+ QMutexLocker locker(&m_mutex);
+ EffectChainPointer pPrevChain = m_pEffectChainManager->getPrevEffectChain(pLoadedChain);
+ m_effectChainSlots[iChainSlotNumber]->loadEffectChain(pPrevChain);
+}
+
+const QSet EffectsManager::getAvailableEffects() const {
+ QMutexLocker locker(&m_mutex);
+ QSet availableEffects;
+
+ foreach (EffectsBackend* pBackend, m_effectsBackends) {
+ QSet backendEffects = pBackend->getEffectIds();
+ foreach (QString effectId, backendEffects) {
+ if (availableEffects.contains(effectId)) {
+ qDebug() << "WARNING: Duplicate effect ID" << effectId;
+ continue;
+ }
+ availableEffects.insert(effectId);
+ }
+ }
+
+ return availableEffects;
+}
+
+EffectManifest EffectsManager::getEffectManifest(const QString& effectId) const {
+ QMutexLocker locker(&m_mutex);
+
+ foreach (EffectsBackend* pBackend, m_effectsBackends) {
+ if (pBackend->canInstantiateEffect(effectId)) {
+ return pBackend->getManifest(effectId);
+ }
+ }
+
+ return EffectManifest();
+}
+
+EffectPointer EffectsManager::instantiateEffect(const QString& effectId) {
+ QMutexLocker locker(&m_mutex);
+ foreach (EffectsBackend* pBackend, m_effectsBackends) {
+ if (pBackend->canInstantiateEffect(effectId)) {
+ return pBackend->instantiateEffect(effectId);
+ }
+ }
+ return EffectPointer();
+}
+
+void EffectsManager::setupDefaultChains() {
+ QMutexLocker locker(&m_mutex);
+ QSet effects = getAvailableEffects();
+
+ FlangerEffect flanger;
+ QString flangerId = flanger.getId();
+
+ if (effects.contains(flangerId)) {
+ EffectChainPointer pChain = EffectChainPointer(new EffectChain());
+ pChain->setId("org.mixxx.effectchain.flanger");
+ pChain->setName(tr("Flanger"));
+ pChain->setParameter(0.0f);
+
+ EffectPointer flanger = instantiateEffect(flangerId);
+ pChain->addEffect(flanger);
+ m_pEffectChainManager->addEffectChain(pChain);
+
+ pChain = EffectChainPointer(new EffectChain());
+ pChain->setId("org.mixxx.effectchain.flanger2");
+ pChain->setName(tr("Flanger2"));
+ pChain->setParameter(0.0f);
+
+ flanger = instantiateEffect(flangerId);
+ pChain->addEffect(flanger);
+ m_pEffectChainManager->addEffectChain(pChain);
+ }
+}
diff --git a/src/effects/effectsmanager.h b/src/effects/effectsmanager.h
new file mode 100644
index 000000000000..698818c5090f
--- /dev/null
+++ b/src/effects/effectsmanager.h
@@ -0,0 +1,71 @@
+#ifndef EFFECTSMANAGER_H
+#define EFFECTSMANAGER_H
+
+#include
+#include
+#include
+#include
+
+#include "util.h"
+#include "effects/effect.h"
+#include "effects/effectsbackend.h"
+#include "effects/effectchainslot.h"
+#include "effects/effectchain.h"
+
+class EffectChainManager;
+
+class EffectsManager : public QObject {
+ Q_OBJECT
+ public:
+ EffectsManager(QObject* pParent);
+ virtual ~EffectsManager();
+
+ // Add an effect backend to be managed by EffectsManager. EffectsManager
+ // takes ownership of the backend, and will delete it when EffectsManager is
+ // being deleted. Not thread safe -- use only from the GUI thread.
+ void addEffectsBackend(EffectsBackend* pEffectsBackend);
+
+ unsigned int numEffectChainSlots() const;
+ void addEffectChainSlot();
+ EffectChainSlotPointer getEffectChainSlot(unsigned int i);
+
+ // Take a buffer of numSamples samples of audio from channel channelId,
+ // provided as pInput, and apply each EffectChain enabled for this channel
+ // to it, putting the resulting output in pOutput. If pInput is equal to
+ // pOutput, then the operation must occur in-place. Both pInput and pOutput
+ // are represented as stereo interleaved samples. There are numSamples total
+ // samples, so numSamples/2 left channel samples and numSamples/2 right
+ // channel samples.
+ virtual void process(const QString channelId,
+ const CSAMPLE* pInput, CSAMPLE* pOutput,
+ const unsigned int numSamples);
+
+ void registerChannel(const QString channelID);
+
+ const QSet getAvailableEffects() const;
+ EffectManifest getEffectManifest(const QString& effectId) const;
+ EffectPointer instantiateEffect(const QString& effectId);
+
+ // Temporary, but for setting up all the default EffectChains
+ void setupDefaultChains();
+
+ private slots:
+ void loadNextChain(const unsigned int iChainSlotNumber, EffectChainPointer pLoadedChain);
+ void loadPrevChain(const unsigned int iChainSlotNumber, EffectChainPointer pLoadedChain);
+
+ private:
+ QString debugString() const {
+ return "EffectsManager";
+ }
+
+ mutable QMutex m_mutex;
+ EffectChainManager* m_pEffectChainManager;
+ QList m_effectsBackends;
+ QList m_effectChainSlots;
+ QSet m_registeredChannels;
+
+ DISALLOW_COPY_AND_ASSIGN(EffectsManager);
+};
+
+
+#endif /* EFFECTSMANAGER_H */
diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp
new file mode 100644
index 000000000000..d18ca7e44a2f
--- /dev/null
+++ b/src/effects/native/flangereffect.cpp
@@ -0,0 +1,144 @@
+#include
+
+#include "effects/native/flangereffect.h"
+
+#include "mathstuff.h"
+#include "sampleutil.h"
+
+QString FlangerEffect::getId() const {
+ return "org.mixxx.effects.flanger";
+}
+
+EffectManifest FlangerEffect::getManifest() const {
+ EffectManifest manifest;
+ manifest.setId(getId());
+ manifest.setName(QObject::tr("Flanger"));
+ manifest.setAuthor("The Mixxx Team");
+ manifest.setVersion("1.0");
+ manifest.setDescription("TODO");
+
+ EffectManifestParameter* depth = manifest.addParameter();
+ depth->setId("depth");
+ depth->setName(QObject::tr("Depth"));
+ depth->setDescription("TODO");
+ depth->setControlHint(EffectManifestParameter::CONTROL_KNOB_LINEAR);
+ depth->setValueHint(EffectManifestParameter::VALUE_FLOAT);
+ depth->setSemanticHint(EffectManifestParameter::SEMANTIC_UNKNOWN);
+ depth->setUnitsHint(EffectManifestParameter::UNITS_UNKNOWN);
+ depth->setDefault(0.0f);
+ depth->setMinimum(0.0f);
+ depth->setMaximum(1.0f);
+
+ EffectManifestParameter* delay = manifest.addParameter();
+ delay->setId("delay");
+ delay->setName(QObject::tr("Delay"));
+ delay->setDescription("TODO");
+ delay->setControlHint(EffectManifestParameter::CONTROL_KNOB_LINEAR);
+ delay->setValueHint(EffectManifestParameter::VALUE_FLOAT);
+ delay->setSemanticHint(EffectManifestParameter::SEMANTIC_UNKNOWN);
+ delay->setUnitsHint(EffectManifestParameter::UNITS_UNKNOWN);
+ delay->setDefault(50.0f);
+ delay->setMinimum(50.0f);
+ delay->setMaximum(10000.0f);
+
+ EffectManifestParameter* period = manifest.addParameter();
+ period->setId("period");
+ period->setName(QObject::tr("Period"));
+ period->setDescription("TODO");
+ period->setControlHint(EffectManifestParameter::CONTROL_KNOB_LINEAR);
+ period->setValueHint(EffectManifestParameter::VALUE_FLOAT);
+ period->setSemanticHint(EffectManifestParameter::SEMANTIC_UNKNOWN);
+ period->setUnitsHint(EffectManifestParameter::UNITS_UNKNOWN);
+ period->setDefault(50000.0f);
+ period->setMinimum(50000.0f);
+ period->setMaximum(2000000.0f);
+
+ return manifest;
+}
+
+// static
+EffectPointer FlangerEffect::create(EffectsBackend* pBackend,
+ const EffectManifest& manifest) {
+ return EffectPointer(new Effect(
+ pBackend, manifest, new FlangerEffectProcessor(manifest)));
+}
+
+FlangerEffectProcessor::FlangerEffectProcessor(const EffectManifest& manifest) {
+}
+
+FlangerEffectProcessor::~FlangerEffectProcessor() {
+ qDebug() << debugString() << "destroyed";
+
+ QMutableMapIterator it(m_flangerStates);
+
+ while (it.hasNext()) {
+ it.next();
+ FlangerState* pState = it.value();
+ it.remove();
+ delete pState;
+ }
+}
+
+void FlangerEffectProcessor::initialize(EngineEffect* pEffect) {
+ m_periodParameter = pEffect->getParameterById("period");
+ m_depthParameter = pEffect->getParameterById("depth");
+ m_delayParameter = pEffect->getParameterById("delay");
+}
+
+void FlangerEffectProcessor::process(const QString& channelId,
+ const CSAMPLE* pInput, CSAMPLE* pOutput,
+ const unsigned int numSamples) {
+ FlangerState* pState = getStateForChannel(channelId);
+
+ if (!pState) {
+ qDebug() << debugString() << "WARNING: Couldn't get flanger state for channel" << channelId;
+ return;
+ }
+
+ CSAMPLE lfoPeriod = m_periodParameter ? m_periodParameter->getValue().toDouble() : 0.0f;
+ CSAMPLE lfoDepth = m_depthParameter ? m_depthParameter->getValue().toDouble() : 0.0f;
+ // Unused in EngineFlanger
+ CSAMPLE lfoDelay = m_delayParameter ? m_delayParameter->getValue().toDouble() : 0.0f;
+
+ qDebug() << debugString() << "period" << lfoPeriod << "depth" << lfoDepth << "delay" << lfoDelay;
+
+ // TODO(rryan) check ranges
+ // period needs to be >=0
+ // delay needs to be >=0
+ // depth is ???
+
+ for (int i = 0; i < numSamples; ++i) {
+ CSAMPLE* delayBuffer = pState->delayBuffer;
+ delayBuffer[pState->delayPos] = pInput[i];
+ pState->delayPos = (pState->delayPos + 1) % kMaxDelay;
+
+ pState->time++;
+ if (pState->time > lfoPeriod) {
+ pState->time = 0;
+ }
+
+ CSAMPLE periodFraction = CSAMPLE(pState->time) / lfoPeriod;
+ CSAMPLE delay = kAverageDelayLength + kLfoAmplitude * sin(two_pi * periodFraction);
+
+ CSAMPLE prev = delayBuffer[(pState->delayPos - int(delay) + kMaxDelay - 1) % kMaxDelay];
+ CSAMPLE next = delayBuffer[(pState->delayPos - int(delay) + kMaxDelay ) % kMaxDelay];
+ CSAMPLE frac = delay - floor(delay);
+ CSAMPLE delayed_sample = prev + frac * (next - prev);
+
+ pOutput[i] = pInput[i] + lfoDepth * delayed_sample;
+ }
+}
+
+FlangerState* FlangerEffectProcessor::getStateForChannel(const QString channelId) {
+ FlangerState* pState = NULL;
+ if (!m_flangerStates.contains(channelId)) {
+ pState = new FlangerState();
+ m_flangerStates[channelId] = pState;
+ SampleUtil::applyGain(pState->delayBuffer, 0.0f, kMaxDelay);
+ pState->delayPos = 0;
+ pState->time = 0;
+ } else {
+ pState = m_flangerStates[channelId];
+ }
+ return pState;
+}
diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h
new file mode 100644
index 000000000000..434fad589aba
--- /dev/null
+++ b/src/effects/native/flangereffect.h
@@ -0,0 +1,66 @@
+#ifndef FLANGEREFFECT_H
+#define FLANGEREFFECT_H
+
+#include
+
+#include "util.h"
+#include "effects/effect.h"
+#include "engine/effects/engineeffect.h"
+#include "engine/effects/engineeffectparameter.h"
+#include "effects/native/nativebackend.h"
+#include "effects/effectprocessor.h"
+
+const unsigned int kMaxDelay = 5000;
+const unsigned int kLfoAmplitude = 240;
+const unsigned int kAverageDelayLength = 250;
+
+class FlangerEffect : public NativeEffect {
+ public:
+ FlangerEffect() { }
+ virtual ~FlangerEffect() { }
+
+ QString getId() const;
+ EffectManifest getManifest() const;
+ EffectInstantiator getInstantiator() const {
+ return FlangerEffect::create;
+ }
+ static EffectPointer create(EffectsBackend* pBackend,
+ const EffectManifest& manifest);
+};
+
+struct FlangerState {
+ CSAMPLE delayBuffer[kMaxDelay];
+ unsigned int delayPos;
+ unsigned int time;
+};
+
+class FlangerEffectProcessor : public EffectProcessor {
+ public:
+ FlangerEffectProcessor(const EffectManifest& manifest);
+ virtual ~FlangerEffectProcessor();
+
+ // See effectprocessor.h
+ void initialize(EngineEffect* pEffect);
+
+ // See effectprocessor.h
+ void process(const QString& channelId,
+ const CSAMPLE* pInput, CSAMPLE* pOutput,
+ const unsigned int numSamples);
+
+ private:
+ QString debugString() const {
+ return "FlangerEffectProcessor";
+ }
+ FlangerState* getStateForChannel(const QString channelId);
+
+ EngineEffectParameter* m_periodParameter;
+ EngineEffectParameter* m_depthParameter;
+ EngineEffectParameter* m_delayParameter;
+
+ QMap m_flangerStates;
+
+ DISALLOW_COPY_AND_ASSIGN(FlangerEffectProcessor);
+};
+
+
+#endif /* FLANGEREFFECT_H */
diff --git a/src/effects/native/nativebackend.cpp b/src/effects/native/nativebackend.cpp
new file mode 100644
index 000000000000..3ef9fef8d352
--- /dev/null
+++ b/src/effects/native/nativebackend.cpp
@@ -0,0 +1,17 @@
+#include
+
+#include "effects/native/nativebackend.h"
+#include "effects/native/flangereffect.h"
+
+NativeBackend::NativeBackend(QObject* pParent)
+ : EffectsBackend(pParent, tr("Native")) {
+ FlangerEffect flanger;
+ const EffectManifest& flanger_manifest = flanger.getManifest();
+ m_effectManifests.append(flanger_manifest);
+ registerEffect(flanger.getId(), flanger_manifest,
+ flanger.getInstantiator());
+}
+
+NativeBackend::~NativeBackend() {
+ qDebug() << debugString() << "destroyed";
+}
diff --git a/src/effects/native/nativebackend.h b/src/effects/native/nativebackend.h
new file mode 100644
index 000000000000..89c358caac4d
--- /dev/null
+++ b/src/effects/native/nativebackend.h
@@ -0,0 +1,27 @@
+#ifndef NATIVEBACKEND_H
+#define NATIVEBACKEND_H
+
+#include "effects/effectsbackend.h"
+
+class NativeEffect {
+ public:
+ virtual ~NativeEffect() { }
+ virtual QString getId() const = 0;
+ virtual EffectManifest getManifest() const = 0;
+ virtual EffectInstantiator getInstantiator() const = 0;
+};
+
+class NativeBackend : public EffectsBackend {
+ Q_OBJECT
+ public:
+ NativeBackend(QObject* pParent=NULL);
+ virtual ~NativeBackend();
+
+ private:
+ QString debugString() const {
+ return "NativeBackend";
+ }
+ QList m_effectManifests;
+};
+
+#endif /* NATIVEBACKEND_H */
diff --git a/src/engine/effects/engineeffect.h b/src/engine/effects/engineeffect.h
new file mode 100644
index 000000000000..68b62438ab50
--- /dev/null
+++ b/src/engine/effects/engineeffect.h
@@ -0,0 +1,64 @@
+#ifndef ENGINEEFFECT_H
+#define ENGINEEFFECT_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include "effects/effectmanifest.h"
+#include "effects/effectprocessor.h"
+#include "engine/effects/engineeffectparameter.h"
+
+class EngineEffect {
+ public:
+ EngineEffect(const EffectManifest& manifest, EffectProcessor* pProcessor)
+ : m_manifest(manifest),
+ m_pProcessor(pProcessor),
+ m_parameters(manifest.parameters().size()) {
+ const QList& parameters = m_manifest.parameters();
+ for (int i = 0; i < parameters.size(); ++i) {
+ const EffectManifestParameter& parameter = parameters.at(i);
+ EngineEffectParameter* pParameter =
+ new EngineEffectParameter(parameter);
+ m_parameters[i] = pParameter;
+ m_parametersById[parameter.id()] = pParameter;
+ }
+ }
+
+ virtual ~EngineEffect() {
+ qDebug() << debugString() << "destroyed";
+ delete m_pProcessor;
+ m_parametersById.clear();
+ for (int i = 0; i < m_parameters.size(); ++i) {
+ EngineEffectParameter* pParameter = m_parameters.at(i);
+ m_parameters[i] = NULL;
+ delete pParameter;
+ }
+ }
+
+ EngineEffectParameter* getParameterById(const QString& id) {
+ return m_parametersById.value(id, NULL);
+ }
+
+ void setParameterById(const QString& id, const QVariant& value) {
+ EngineEffectParameter* pParameter = getParameterById(id);
+ if (pParameter) {
+ pParameter->setValue(value);
+ }
+ }
+
+ private:
+ QString debugString() const {
+ return QString("EngineEffect(%1)").arg(m_manifest.name());
+ }
+
+ EffectManifest m_manifest;
+ EffectProcessor* m_pProcessor;
+ // Must not be modified after construction.
+ QVector m_parameters;
+ QMap m_parametersById;
+};
+
+#endif /* ENGINEEFFECT_H */
diff --git a/src/engine/effects/engineeffectparameter.h b/src/engine/effects/engineeffectparameter.h
new file mode 100644
index 000000000000..efec5da6fa53
--- /dev/null
+++ b/src/engine/effects/engineeffectparameter.h
@@ -0,0 +1,48 @@
+#ifndef ENGINEEFFECTPARAMETER_H
+#define ENGINEEFFECTPARAMETER_H
+
+#include
+#include
+
+#include "control/controlvalue.h"
+#include "util.h"
+#include "effects/effectmanifestparameter.h"
+
+class EngineEffectParameter {
+ public:
+ EngineEffectParameter(const EffectManifestParameter& parameter) : m_parameter(parameter) {
+ }
+ virtual ~EngineEffectParameter() { }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Parameter Information
+ ///////////////////////////////////////////////////////////////////////////
+
+ const QString& id() const {
+ return m_parameter.id();
+ }
+ const QString& name() const {
+ return m_parameter.name();
+ }
+ const QString& description() const {
+ return m_parameter.description();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Value Settings
+ ///////////////////////////////////////////////////////////////////////////
+
+ QVariant getValue() const {
+ return m_value.getValue();
+ }
+ void setValue(const QVariant& value) {
+ m_value.setValue(value);
+ }
+
+ private:
+ EffectManifestParameter m_parameter;
+ ControlValueAtomic m_value;
+ DISALLOW_COPY_AND_ASSIGN(EngineEffectParameter);
+};
+
+#endif /* ENGINEEFFECTPARAMETER_H */
diff --git a/src/engine/enginechannel.h b/src/engine/enginechannel.h
index 98a5ef29a5f0..81c04c9b90c0 100644
--- a/src/engine/enginechannel.h
+++ b/src/engine/enginechannel.h
@@ -27,11 +27,9 @@ class EnginePregain;
class EngineBuffer;
class EngineFilterBlock;
class EngineClipping;
-class EngineFlanger;
class EngineVuMeter;
class EngineVinylSoundEmu;
class ControlPushButton;
-class ControlObject;
class EngineChannel : public EngineObject {
Q_OBJECT
diff --git a/src/engine/enginedeck.cpp b/src/engine/enginedeck.cpp
index 5fa7cae8a29a..f75f15f4f3c1 100644
--- a/src/engine/enginedeck.cpp
+++ b/src/engine/enginedeck.cpp
@@ -16,13 +16,12 @@
***************************************************************************/
#include "controlpushbutton.h"
+#include "effects/effectsmanager.h"
#include "enginebuffer.h"
#include "enginevinylsoundemu.h"
#include "enginedeck.h"
#include "engineclipping.h"
#include "enginepregain.h"
-#include "engineflanger.h"
-#include "enginefiltereffect.h"
#include "enginefilterblock.h"
#include "enginevumeter.h"
#include "enginefilteriir.h"
@@ -30,14 +29,17 @@
#include "sampleutil.h"
EngineDeck::EngineDeck(const char* group,
- ConfigObject* pConfig,
- EngineChannel::ChannelOrientation defaultOrientation)
+ ConfigObject* pConfig,
+ EffectsManager* pEffectsManager,
+ EngineChannel::ChannelOrientation defaultOrientation)
: EngineChannel(group, defaultOrientation),
m_pConfig(pConfig),
+ m_pEffectsManager(pEffectsManager),
m_pPassing(new ControlPushButton(ConfigKey(group, "passthrough"))),
// Need a +1 here because the CircularBuffer only allows its size-1
// items to be held at once (it keeps a blank spot open persistently)
m_sampleBuffer(MAX_BUFFER_LEN+1) {
+ m_pEffectsManager->registerChannel(getGroup());
// Set up passthrough utilities and fields
m_pPassing->setButtonMode(ControlPushButton::POWERWINDOW);
@@ -53,8 +55,6 @@ EngineDeck::EngineDeck(const char* group,
// Set up additional engines
m_pPregain = new EnginePregain(group);
m_pFilter = new EngineFilterBlock(group);
- m_pFlanger = new EngineFlanger(group);
- m_pFilterEffect = new EngineFilterEffect(group);
m_pClipping = new EngineClipping(group);
m_pBuffer = new EngineBuffer(group, pConfig);
m_pVinylSoundEmu = new EngineVinylSoundEmu(pConfig, group);
@@ -68,8 +68,6 @@ EngineDeck::~EngineDeck() {
delete m_pBuffer;
delete m_pClipping;
delete m_pFilter;
- delete m_pFlanger;
- delete m_pFilterEffect;
delete m_pPregain;
delete m_pVinylSoundEmu;
delete m_pVUMeter;
@@ -109,9 +107,8 @@ void EngineDeck::process(const CSAMPLE*, const CSAMPLE * pOutput, const int iBuf
m_pPregain->process(pOut, pOut, iBufferSize);
// Filter the channel with EQs
m_pFilter->process(pOut, pOut, iBufferSize);
- // TODO(XXX) LADSPA
- m_pFlanger->process(pOut, pOut, iBufferSize);
- m_pFilterEffect->process(pOut, pOut, iBufferSize);
+ // Process effects enabled for this channel
+ m_pEffectsManager->process(getGroup(), pOut, pOut, iBufferSize);
// Apply clipping
m_pClipping->process(pOut, pOut, iBufferSize);
// Update VU meter
@@ -202,4 +199,3 @@ bool EngineDeck::isPassthroughActive() {
void EngineDeck::slotPassingToggle(double v) {
m_bPassthroughIsActive = v > 0;
}
-
diff --git a/src/engine/enginedeck.h b/src/engine/enginedeck.h
index 5061f9075d1f..27e3240a1ed2 100644
--- a/src/engine/enginedeck.h
+++ b/src/engine/enginedeck.h
@@ -31,8 +31,7 @@ class EnginePregain;
class EngineBuffer;
class EngineFilterBlock;
class EngineClipping;
-class EngineFlanger;
-class EngineFilterEffect;
+class EffectsManager;
class EngineVuMeter;
class EngineVinylSoundEmu;
class ControlPushButton;
@@ -41,6 +40,7 @@ class EngineDeck : public EngineChannel, public AudioDestination {
Q_OBJECT
public:
EngineDeck(const char *group, ConfigObject* pConfig,
+ EffectsManager* pEffectsManager,
EngineChannel::ChannelOrientation defaultOrientation = CENTER);
virtual ~EngineDeck();
@@ -74,11 +74,10 @@ class EngineDeck : public EngineChannel, public AudioDestination {
EngineBuffer* m_pBuffer;
EngineClipping* m_pClipping;
EngineFilterBlock* m_pFilter;
- EngineFlanger* m_pFlanger;
- EngineFilterEffect* m_pFilterEffect;
EnginePregain* m_pPregain;
EngineVinylSoundEmu* m_pVinylSoundEmu;
EngineVuMeter* m_pVUMeter;
+ EffectsManager* m_pEffectsManager;
// Begin vinyl passthrough fields
ControlPushButton* m_pPassing;
diff --git a/src/engine/enginemaster.cpp b/src/engine/enginemaster.cpp
index a85573c0aa83..cca5acecf221 100644
--- a/src/engine/enginemaster.cpp
+++ b/src/engine/enginemaster.cpp
@@ -28,11 +28,13 @@
#include "engine/engineworkerscheduler.h"
#include "enginebuffer.h"
#include "enginechannel.h"
+#include "engine/enginedeck.h"
#include "engineclipping.h"
#include "enginevumeter.h"
#include "enginexfader.h"
#include "engine/sidechain/enginesidechain.h"
#include "sampleutil.h"
+#include "effects/effectsmanager.h"
#include "util/timer.h"
#include "playermanager.h"
#include "engine/channelmixer.h"
@@ -43,12 +45,17 @@
EngineMaster::EngineMaster(ConfigObject * _config,
const char * group,
+ EffectsManager* pEffectsManager,
bool bEnableSidechain)
- : m_headphoneMasterGainOld(0),
+ : m_pEffectsManager(pEffectsManager),
+ m_headphoneMasterGainOld(0),
m_headphoneVolumeOld(0) {
m_pWorkerScheduler = new EngineWorkerScheduler(this);
m_pWorkerScheduler->start();
+ m_pEffectsManager->registerChannel(getMasterChannelId());
+ m_pEffectsManager->registerChannel(getHeadphoneChannelId());
+
// Master sample rate
m_pMasterSampleRate = new ControlObject(ConfigKey(group, "samplerate"), true, true);
m_pMasterSampleRate->set(44100.);
@@ -239,6 +246,9 @@ void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBuff
maxChannels, &m_channelMasterGainCache,
m_pMaster, iBufferSize);
+ // Process master channel effects
+ m_pEffectsManager->process(getMasterChannelId(), m_pMaster, m_pMaster, iBufferSize);
+
#ifdef __LADSPA__
// LADPSA master effects
m_pLadspa->process(m_pMaster, m_pMaster, iBufferSize);
@@ -276,6 +286,9 @@ void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBuff
cmaster_gain, iBufferSize);
m_headphoneMasterGainOld = cmaster_gain;
+ // Process headphone channel effects
+ m_pEffectsManager->process(getHeadphoneChannelId(), m_pHead, m_pHead, iBufferSize);
+
// Head volume and clipping
CSAMPLE headphoneVolume = m_pHeadVolume->get();
SampleUtil::applyRampingGain(m_pHead, m_headphoneVolumeOld, headphoneVolume, iBufferSize);
diff --git a/src/engine/enginemaster.h b/src/engine/enginemaster.h
index 1d5aa75ff963..9ab58258aeb5 100644
--- a/src/engine/enginemaster.h
+++ b/src/engine/enginemaster.h
@@ -27,6 +27,7 @@
class EngineWorkerScheduler;
class EngineBuffer;
class EngineChannel;
+class EngineDeck;
class EngineClipping;
class EngineFlanger;
#ifdef __LADSPA__
@@ -37,6 +38,7 @@ class ControlPotmeter;
class ControlPushButton;
class EngineVinylSoundEmu;
class EngineSideChain;
+class EffectsManager;
class SyncWorker;
class EngineMaster : public EngineObject, public AudioSource {
@@ -44,6 +46,7 @@ class EngineMaster : public EngineObject, public AudioSource {
public:
EngineMaster(ConfigObject* pConfig,
const char* pGroup,
+ EffectsManager* pEffectsManager,
bool bEnableSidechain);
virtual ~EngineMaster();
@@ -51,6 +54,18 @@ class EngineMaster : public EngineObject, public AudioSource {
// be called by SoundManager.
const CSAMPLE* buffer(AudioOutput output) const;
+ const QString getMasterChannelId() const {
+ return QString("[Master]");
+ }
+
+ const QString getHeadphoneChannelId() const {
+ return QString("[Headphone]");
+ }
+
+ EffectsManager* getEffectsManager() const {
+ return m_pEffectsManager;
+ }
+
void process(const CSAMPLE *, const CSAMPLE *pOut, const int iBufferSize);
// Add an EngineChannel to the mixing engine. This is not thread safe --
@@ -129,6 +144,7 @@ class EngineMaster : public EngineObject, public AudioSource {
double m_dVolume, m_dLeftGain, m_dCenterGain, m_dRightGain;
};
+ EffectsManager* m_pEffectsManager;
QList m_channels;
QList m_channelMasterGainCache;
QList m_channelHeadphoneGainCache;
diff --git a/src/ladspa/ladspainstancestereo.cpp b/src/ladspa/ladspainstancestereo.cpp
index 39c4dfec2dd6..acf018d84057 100644
--- a/src/ladspa/ladspainstancestereo.cpp
+++ b/src/ladspa/ladspainstancestereo.cpp
@@ -27,6 +27,7 @@ LADSPAInstanceStereo::LADSPAInstanceStereo(const LADSPA_Descriptor * descriptor,
for (unsigned long port = 0; port < descriptor->PortCount; port++)
{
qDebug() << "LADSPA: Port " << port << "u: " << descriptor->PortNames[port];
+
if (LADSPA_IS_PORT_AUDIO(descriptor->PortDescriptors [port]))
{
if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors [port]))
diff --git a/src/ladspa/ladspaloader.cpp b/src/ladspa/ladspaloader.cpp
index bf73c0939031..4d4529cee13b 100644
--- a/src/ladspa/ladspaloader.cpp
+++ b/src/ladspa/ladspaloader.cpp
@@ -17,6 +17,42 @@ LADSPALoader::LADSPALoader()
QStringList plugin_paths;
+ // set up the plugin paths...
+
+ qDebug() << "Setting up plugin paths...";
+
+ QString ladspaPath = QString(getenv("LADSPA_PATH"));
+
+ // Is this really necessary? Can't we add all the paths?
+ if (!ladspaPath.isEmpty())
+ {
+ // get the list of directories containing LADSPA plugins
+ #ifdef __WINDOWS__
+ plugin_paths.ladspaPath.split(';');
+ #else //this doesn't work, I think we need to iterate over the splitting to do it properly
+ plugin_paths = ladspaPath.split(':');
+ #endif
+ }
+ else
+ {
+ // add default path if LADSPA_PATH is not set
+ #ifdef __LINUX__
+ plugin_paths.push_back ("/usr/lib/ladspa/");
+ plugin_paths.push_back ("/usr/lib64/ladspa/");
+ #elif __APPLE__
+ QDir dir(QCoreApplication::applicationDirPath());
+ dir.cdUp();
+ dir.cd("PlugIns");
+ plugin_paths.push_back ("/Library/Audio/Plug-ins/LADSPA");
+ plugin_paths.push_back (dir.absolutePath()); //ladspa_plugins directory in Mixxx.app bundle //XXX work in QApplication::appdir()
+ #elif __WINDOWS__
+ // not tested yet but should work:
+ QString programFiles = QString(getenv("ProgramFiles"));
+ plugin_paths.push_back (programFiles+"\\LADSPA Plugins");
+ plugin_paths.push_back (programFiles+"\\Audacity\\Plug-Ins");
+ #endif
+ }
+ qDebug() << "...done.";
// load each directory
for (QStringList::iterator path = plugin_paths.begin(); path != plugin_paths.end(); path++)
@@ -66,7 +102,7 @@ LADSPALoader::~LADSPALoader()
// TODO: unload & free everything
}
-const LADSPAPlugin * LADSPALoader::getByIndex(uint index)
+LADSPAPlugin * LADSPALoader::getByIndex(uint index)
{
if (index < m_PluginCount)
{
diff --git a/src/ladspa/ladspaloader.h b/src/ladspa/ladspaloader.h
index ea48fe49940b..75f66b79d55f 100644
--- a/src/ladspa/ladspaloader.h
+++ b/src/ladspa/ladspaloader.h
@@ -19,7 +19,7 @@ class LADSPALoader
LADSPALoader();
~LADSPALoader();
- const LADSPAPlugin * getByIndex(uint index);
+ LADSPAPlugin * getByIndex(uint index);
LADSPAPlugin * getByLabel(QString label);
private:
diff --git a/src/ladspa/ladspaplugin.cpp b/src/ladspa/ladspaplugin.cpp
index 1b1b3c17a3ba..0aea7fa4f837 100644
--- a/src/ladspa/ladspaplugin.cpp
+++ b/src/ladspa/ladspaplugin.cpp
@@ -14,7 +14,7 @@
LADSPAPlugin::LADSPAPlugin(const LADSPA_Descriptor * descriptor)
{
- m_pDescriptor = descriptor;
+ m_pDescriptor = descriptor;
}
LADSPAPlugin::~LADSPAPlugin()
@@ -83,3 +83,32 @@ const QString LADSPAPlugin::getLabel()
{
return QString(m_pDescriptor->Label);
}
+
+const LADSPA_Descriptor * LADSPAPlugin::getDescriptor(){
+ return m_pDescriptor;
+}
+
+bool LADSPAPlugin::isSupported(){
+ int inputs = 0;
+ int outputs = 0;
+ for (unsigned long port = 0; port < m_pDescriptor->PortCount; port++)
+ {
+ if (LADSPA_IS_PORT_AUDIO(m_pDescriptor->PortDescriptors [port]))
+ {
+ if (LADSPA_IS_PORT_INPUT(m_pDescriptor->PortDescriptors [port]))
+ {
+ inputs++;
+ }
+ else
+ {
+ outputs++;
+ }
+ }
+ }
+
+ if ((inputs == 2 && outputs == 2) || (inputs == 1 && outputs == 1)){
+ return true;
+ } else {
+ return false;
+ }
+}
diff --git a/src/ladspa/ladspaplugin.h b/src/ladspa/ladspaplugin.h
index 09a6e0822a6c..5c44c1b95fd2 100644
--- a/src/ladspa/ladspaplugin.h
+++ b/src/ladspa/ladspaplugin.h
@@ -24,6 +24,8 @@ class LADSPAPlugin
LADSPAInstance * instantiate(int slot);
const QString getLabel();
+ const LADSPA_Descriptor * getDescriptor();
+ bool isSupported();
private:
const LADSPA_Descriptor * m_pDescriptor;
diff --git a/src/ladspa/ladspapresetslot.cpp b/src/ladspa/ladspapresetslot.cpp
index b17bf578bbbf..e1a3d9428c5a 100644
--- a/src/ladspa/ladspapresetslot.cpp
+++ b/src/ladspa/ladspapresetslot.cpp
@@ -52,7 +52,7 @@ LADSPAPresetSlot::LADSPAPresetSlot(QWidget *parent, QDomElement element, int slo
QDomElement spacingElement = element.firstChildElement("Spacing");
QString spacing = spacingElement.text();
int spacingWidth = spacing.left(spacing.indexOf(",")).toInt();
- int spacingHeight = spacing.mid(spacing.indexOf(",") + 1).toInt();
+ int spacingHeight = spacing.mid(spacing.indexOf(",") + 1).toInt();
QDomElement sizeElement = element.firstChildElement("Size");
QString size = sizeElement.text();
@@ -74,7 +74,7 @@ LADSPAPresetSlot::LADSPAPresetSlot(QWidget *parent, QDomElement element, int slo
QString slotString;
slotString.setNum(slot);
-
+
QDomNodeList buttonNodeList = element.elementsByTagName("PushButton");
for (int i = 0; i < buttonNodeList.count(); i++)
{
@@ -84,7 +84,7 @@ LADSPAPresetSlot::LADSPAPresetSlot(QWidget *parent, QDomElement element, int slo
{
QString keyString = QString("RemoveEffect") + slotString;
ConfigKey *key = new ConfigKey("[LADSPA]", keyString);
- ControlPushButton *control = new ControlPushButton(*key, false);
+ ControlPushButton *control = new ControlPushButton(*key);
m_pRemoveButton = new WPushButton(m_pScrollWidget);
buttonElement.firstChildElement("Connection").firstChildElement("ConfigKey").firstChild().setNodeValue("[LADSPA]," + keyString);
m_pRemoveButton->setup(buttonElement);
@@ -96,7 +96,7 @@ LADSPAPresetSlot::LADSPAPresetSlot(QWidget *parent, QDomElement element, int slo
QString keyString = QString("EnableEffect") + slotString;
ConfigKey *key = new ConfigKey("[LADSPA]", keyString);
qDebug() << "Key string:" << keyString;
- ControlObject *control = new ControlPushButton(*key, false);
+ ControlObject *control = new ControlPushButton(*key);
control->set(1.0f);
m_pEnableButton = new WPushButton(m_pScrollWidget);
buttonElement.firstChildElement("Connection").firstChildElement("ConfigKey").firstChild().setNodeValue("[LADSPA]," + keyString);
@@ -138,7 +138,7 @@ LADSPAPresetSlot::LADSPAPresetSlot(QWidget *parent, QDomElement element, int slo
m_qKnobElement = element.firstChildElement("Knob");
m_pPresetInstance = NULL;
-
+
ConfigKey *key = new ConfigKey("[LADSPA]", "DryWet" + slotString);
ControlPotmeter *control = new ControlPotmeter(*key, 0.0, 1.0);
m_pDryWetKnob = new WKnob(m_pScrollWidget);
@@ -274,7 +274,7 @@ void LADSPAPresetSlot::addKnob(int i)
QDomElement spacingElement = m_qKnobElement.firstChildElement("Spacing");
QString spacing = spacingElement.text();
int spacingWidth = spacing.left(spacing.indexOf(",")).toInt();
- int spacingHeight = spacing.mid(spacing.indexOf(",") + 1).toInt();
+ int spacingHeight = spacing.mid(spacing.indexOf(",") + 1).toInt();
knob->move(x + (i + 1) * (knob->width() + spacingWidth), y + (i + 1) * spacingHeight);
if (knob->x() + knob->width() > m_pScrollWidget->width())
{
@@ -288,7 +288,7 @@ void LADSPAPresetSlot::addKnob(int i)
label->show();
/*while (label->width() >= spacingWidth + knob->width() - 5)
{
- qDebug() << label->width() << ", " << length << ", " << spacingWidth << ", " << knob->width();
+ qDebug() << label->width() << ", " << length << ", " << spacingWidth << ", " << knob->width();
if (length > 10)
length = 10;
length -= 2;
diff --git a/src/ladspaview.cpp b/src/ladspaview.cpp
index f0ae47c3664d..06d0700fa830 100644
--- a/src/ladspaview.cpp
+++ b/src/ladspaview.cpp
@@ -27,7 +27,7 @@ LADSPAView::LADSPAView(QWidget * parent) : QWidget(parent)
QDomDocument skin("LADSPASkin");
QFile file(WWidget::getPath("ladspa_skin.xml"));
- if (!file.open(IO_ReadOnly))
+ if (!file.open(QIODevice::ReadOnly))
{
qDebug() << "Could not open skin definition file: " << file.fileName();
}
@@ -48,7 +48,7 @@ LADSPAView::LADSPAView(QWidget * parent) : QWidget(parent)
//bg->lower();
//this->setFixedSize(background->width(), background->height());
//parent->setMinimumSize(background->width(), background->height());
-
+
QDomElement bgColorNode = docElement.firstChildElement("BgColor");
QDomElement fgColorNode = docElement.firstChildElement("FgColor");
@@ -97,13 +97,13 @@ LADSPAView::LADSPAView(QWidget * parent) : QWidget(parent)
if (height <= 0)
{
height = this->height() + height;
- }
-
+ }
+
//m_pPresetList->resize(width, height);
m_pPresetList->setMinimumSize(65, 200);
m_pPresetList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding);
-
-
+
+
m_pPresetManager = new LADSPAPresetManager();
for (unsigned int i = 0; i < m_pPresetManager->getPresetCount(); i++)
@@ -112,13 +112,13 @@ LADSPAView::LADSPAView(QWidget * parent) : QWidget(parent)
if (preset->isValid())
m_pPresetList->addItem(preset->getName());
else
- m_pPresetList->addItem("-" + preset->getName());
+ m_pPresetList->addItem("-" + preset->getName());
}
m_pSlotTable = new QWidget(this);
-
+
QDomElement slotTableElement = docElement.firstChildElement("SlotTable");
-
+
posElement = slotTableElement.firstChildElement("Pos");
pos = posElement.text();
x = pos.left(pos.indexOf(",")).toInt();
@@ -144,7 +144,7 @@ LADSPAView::LADSPAView(QWidget * parent) : QWidget(parent)
if (height <= 0)
{
height = this->height() + height;
- }
+ }
//m_pSlotTable->resize(width, height);
m_pSlotTable->setMinimumSize(400, 200);
m_pSlotTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
diff --git a/src/main.cpp b/src/main.cpp
index ee28d7541969..58387e248571 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -36,10 +36,6 @@
#include "errordialoghandler.h"
#include "util/version.h"
-#ifdef __LADSPA__
-#include
-#endif
-
#ifdef __WINDOWS__
#ifdef DEBUGCONSOLE
#include // Debug Console
@@ -220,6 +216,7 @@ int main(int argc, char * argv[])
//Enumerate and load SoundSource plugins
SoundSourceProxy::loadPlugins();
+
#ifdef __LADSPA__
//LADSPALoader ladspaloader;
#endif
@@ -230,45 +227,6 @@ int main(int argc, char * argv[])
// if(QString("--no-visuals")==argv[i])
// bVisuals = false;
-
- // set up the plugin paths...
- /*
- qDebug() << "Setting up plugin paths...";
- plugin_paths = QStringList();
- QString ladspaPath = QString(getenv("LADSPA_PATH"));
-
- if (!ladspaPath.isEmpty())
- {
- // get the list of directories containing LADSPA plugins
-#ifdef __WINDOWS__
- //paths.ladspaPath.split(';');
-#else //this doesn't work, I think we need to iterate over the splitting to do it properly
- //paths = ladspaPath.split(':');
-#endif
- }
- else
- {
- // add default path if LADSPA_PATH is not set
-#ifdef __LINUX__
- plugin_paths.push_back ("/usr/lib/ladspa/");
- plugin_paths.push_back ("/usr/lib64/ladspa/");
-#elif __APPLE__
- QDir dir(a.applicationDirPath());
- dir.cdUp();
- dir.cd("PlugIns");
- plugin_paths.push_back ("/Library/Audio/Plug-ins/LADSPA");
- plugin_paths.push_back (dir.absolutePath()); //ladspa_plugins directory in Mixxx.app bundle //XXX work in QApplication::appdir()
-#elif __WINDOWS__
- // not tested yet but should work:
- QString programFiles = QString(getenv("ProgramFiles"));
- plugin_paths.push_back (programFiles+"\\LADSPA Plugins");
- plugin_paths.push_back (programFiles+"\\Audacity\\Plug-Ins");
-#endif
- }
- qDebug() << "...done.";
- */
-
-
#ifdef __APPLE__
QDir dir(QApplication::applicationDirPath());
// Set the search path for Qt plugins to be in the bundle's PlugIns
diff --git a/src/mixxx.cpp b/src/mixxx.cpp
index 8f4c81d13a26..8120cd962335 100644
--- a/src/mixxx.cpp
+++ b/src/mixxx.cpp
@@ -31,6 +31,15 @@
#include "dlgpreferences.h"
#include "engine/enginemaster.h"
#include "engine/enginemicrophone.h"
+#include "effects/effectsmanager.h"
+#include "effects/native/nativebackend.h"
+
+// TODO(rryan) REMOVE THESE LATER
+#include "effects/effectchain.h"
+#include "effects/effectslot.h"
+#include "effects/effect.h"
+// TODO(rryan) REMOVE THESE LATER
+
#include "engine/enginepassthrough.h"
#include "library/library.h"
#include "library/libraryscanner.h"
@@ -273,8 +282,23 @@ MixxxApp::MixxxApp(QApplication *pApp, const CmdlineArgs& args)
initializeKeyboard();
+ // Setup the effects manager with four effects chains
+ m_pEffectsManager = new EffectsManager(this);
+ m_pEffectsManager->addEffectChainSlot();
+ m_pEffectsManager->addEffectChainSlot();
+ m_pEffectsManager->addEffectChainSlot();
+ m_pEffectsManager->addEffectChainSlot();
+
// Starting the master (mixing of the channels and effects):
- m_pEngine = new EngineMaster(m_pConfig, "[Master]", true);
+ m_pEngine = new EngineMaster(m_pConfig, "[Master]", m_pEffectsManager, true);
+
+ // TODO(rryan) the only reason I'm creating the effects backends here is
+ // that I'm not totally confident some effect backend is going to want to
+ // look up a control that is produced by the engine.
+ NativeBackend* pNativeBackend = new NativeBackend(m_pEffectsManager);
+ m_pEffectsManager->addEffectsBackend(pNativeBackend);
+
+ m_pEffectsManager->setupDefaultChains();
m_pRecordingManager = new RecordingManager(m_pConfig, m_pEngine);
#ifdef __SHOUTCAST__
@@ -487,7 +511,8 @@ MixxxApp::MixxxApp(QApplication *pApp, const CmdlineArgs& args)
// Loads the skin as a child of m_pView
// assignment intentional in next line
if (!(m_pWidgetParent = m_pSkinLoader->loadDefaultSkin(
- m_pView, m_pKeyboard, m_pPlayerManager, m_pControllerManager, m_pLibrary, m_pVCManager))) {
+ m_pView, m_pKeyboard, m_pPlayerManager, m_pControllerManager,
+ m_pLibrary, m_pVCManager, m_pEffectsManager))) {
reportCriticalErrorAndQuit("default skin cannot be loaded see mixxx trace for more information.");
//TODO (XXX) add dialog to warn user and launch skin choice page
@@ -629,6 +654,9 @@ MixxxApp::~MixxxApp()
qDebug() << "delete m_pEngine " << qTime.elapsed();
delete m_pEngine;
+ qDebug() << "deleting effects manager, " << qTime.elapsed();
+ delete m_pEffectsManager;
+
// HACK: Save config again. We saved it once before doing some dangerous
// stuff. We only really want to save it here, but the first one was just
// a precaution. The earlier one can be removed when stuff is more stable
@@ -1451,13 +1479,13 @@ void MixxxApp::rebootMixxxView() {
m_pPlayerManager,
m_pControllerManager,
m_pLibrary,
- m_pVCManager))) {
+ m_pVCManager,
+ m_pEffectsManager))) {
QMessageBox::critical(this,
tr("Error in skin file"),
tr("The selected skin cannot be loaded."));
- }
- else {
+ } else {
// keep gui centered (esp for fullscreen)
m_pView->setLayout( new QHBoxLayout(m_pView));
m_pView->layout()->setContentsMargins(0,0,0,0);
diff --git a/src/mixxx.h b/src/mixxx.h
index 29bd051257b2..36ca3f646938 100644
--- a/src/mixxx.h
+++ b/src/mixxx.h
@@ -37,8 +37,8 @@ class PlayerManager;
class RecordingManager;
class ShoutcastManager;
class SkinLoader;
+class EffectsManager;
class VinylControlManager;
-
class DlgPreferences;
class SoundManager;
@@ -138,6 +138,9 @@ class MixxxApp : public QMainWindow {
QWidget* m_pView;
QWidget* m_pWidgetParent;
+ // The effects processing system
+ EffectsManager* m_pEffectsManager;
+
// The mixing engine.
EngineMaster* m_pEngine;
diff --git a/src/skin/legacyskinparser.cpp b/src/skin/legacyskinparser.cpp
index 63698210b882..a4975cdc63cf 100644
--- a/src/skin/legacyskinparser.cpp
+++ b/src/skin/legacyskinparser.cpp
@@ -26,6 +26,8 @@
#include "skin/colorschemeparser.h"
#include "skin/propertybinder.h"
+#include "effects/effectsmanager.h"
+
#include "widget/wwidget.h"
#include "widget/wabstractcontrol.h"
#include "widget/wknob.h"
@@ -41,6 +43,7 @@
#include "widget/wnumber.h"
#include "widget/wnumberpos.h"
#include "widget/wnumberrate.h"
+#include "widget/weffectchain.h"
#include "widget/woverviewlmh.h"
#include "widget/woverviewhsv.h"
#include "widget/wspinny.h"
@@ -88,14 +91,16 @@ LegacySkinParser::LegacySkinParser(ConfigObject* pConfig,
PlayerManager* pPlayerManager,
ControllerManager* pControllerManager,
Library* pLibrary,
- VinylControlManager* pVCMan)
- : m_pConfig(pConfig),
- m_pKeyboard(pKeyboard),
- m_pPlayerManager(pPlayerManager),
- m_pControllerManager(pControllerManager),
- m_pLibrary(pLibrary),
- m_pVCManager(pVCMan),
- m_pParent(NULL) {
+ VinylControlManager* pVCMan,
+ EffectsManager* pEffectsManager)
+ : m_pConfig(pConfig),
+ m_pKeyboard(pKeyboard),
+ m_pPlayerManager(pPlayerManager),
+ m_pControllerManager(pControllerManager),
+ m_pLibrary(pLibrary),
+ m_pVCManager(pVCMan),
+ m_pEffectsManager(pEffectsManager),
+ m_pParent(NULL) {
}
LegacySkinParser::~LegacySkinParser() {
@@ -348,6 +353,8 @@ QWidget* LegacySkinParser::parseNode(QDomElement node, QWidget *pGrandparent) {
return parseWidgetStack(node);
} else if (nodeName == "Style") {
return parseStyle(node);
+ } else if (nodeName == "EffectChainName") {
+ return parseEffectChainName(node);
} else if (nodeName == "Spinny") {
return parseSpinny(node);
} else if (nodeName == "Time") {
@@ -381,7 +388,6 @@ QWidget* LegacySkinParser::parseSplitter(QDomElement node) {
}
QDomNode childrenNode = XmlParse::selectNode(node, "Children");
-
QWidget* pOldParent = m_pParent;
m_pParent = pSplitter;
@@ -1178,6 +1184,26 @@ QWidget* LegacySkinParser::parseStyle(QDomElement node) {
return m_pParent;
}
+QWidget* LegacySkinParser::parseEffectChainName(QDomElement node) {
+ WEffectChain* pEffectChain = new WEffectChain(m_pParent);
+
+ // To Mixxx users, EffectChains are 1-indexed, but code-wise the chains are
+ // 0-indexed.
+ unsigned int chainNumber = XmlParse::selectNodeInt(node, "EffectChain") - 1;
+
+ EffectChainSlotPointer pChainSlot = m_pEffectsManager->getEffectChainSlot(chainNumber);
+
+ if (pChainSlot) {
+ pEffectChain->setEffectChainSlot(pChainSlot);
+ } else {
+ qDebug() << "EffectChainName node had invalid EffectChainSlot number.";
+ }
+
+ setupWidget(node, pEffectChain);
+ pEffectChain->installEventFilter(m_pKeyboard);
+ return pEffectChain;
+}
+
void LegacySkinParser::setupPosition(QDomNode node, QWidget* pWidget) {
if (!XmlParse::selectNode(node, "Pos").isNull()) {
QString pos = XmlParse::selectNodeQString(node, "Pos");
diff --git a/src/skin/legacyskinparser.h b/src/skin/legacyskinparser.h
index 09727dcd3356..30fdc9b0da05 100644
--- a/src/skin/legacyskinparser.h
+++ b/src/skin/legacyskinparser.h
@@ -17,6 +17,7 @@ class Library;
class MixxxKeyboard;
class PlayerManager;
class WAbstractControl;
+class EffectsManager;
class ControllerManager;
class LegacySkinParser : public QObject, public SkinParser {
@@ -25,7 +26,8 @@ class LegacySkinParser : public QObject, public SkinParser {
LegacySkinParser(ConfigObject* pConfig,
MixxxKeyboard* pKeyboard, PlayerManager* pPlayerManager,
ControllerManager* pControllerManager,
- Library* pLibrary, VinylControlManager* pVCMan);
+ Library* pLibrary, VinylControlManager* pVCMan,
+ EffectsManager* pEffectsManager);
virtual ~LegacySkinParser();
virtual bool canParse(QString skinPath);
@@ -67,6 +69,7 @@ class LegacySkinParser : public QObject, public SkinParser {
QWidget* parseKnob(QDomElement node);
QWidget* parseTableView(QDomElement node);
QWidget* parseStyle(QDomElement node);
+ QWidget* parseEffectChainName(QDomElement node);
QWidget* parseSpinny(QDomElement node);
QWidget* parseSearchBox(QDomElement node);
QWidget* parseSplitter(QDomElement node);
@@ -89,6 +92,7 @@ class LegacySkinParser : public QObject, public SkinParser {
ControllerManager* m_pControllerManager;
Library* m_pLibrary;
VinylControlManager* m_pVCManager;
+ EffectsManager* m_pEffectsManager;
QWidget *m_pParent;
Tooltips m_tooltips;
static QList s_channelStrs;
diff --git a/src/skin/skinloader.cpp b/src/skin/skinloader.cpp
index bb2b8ec4362f..429c2ea7ed22 100644
--- a/src/skin/skinloader.cpp
+++ b/src/skin/skinloader.cpp
@@ -10,6 +10,7 @@
#include "skin/legacyskinparser.h"
#include "controllers/controllermanager.h"
#include "library/library.h"
+#include "effects/effectsmanager.h"
#include "playermanager.h"
#include "util/debug.h"
@@ -63,9 +64,11 @@ QWidget* SkinLoader::loadDefaultSkin(QWidget* pParent,
PlayerManager* pPlayerManager,
ControllerManager* pControllerManager,
Library* pLibrary,
- VinylControlManager* pVCMan) {
+ VinylControlManager* pVCMan,
+ EffectsManager* pEffectsManager) {
QString skinPath = getConfiguredSkinPath();
-
- LegacySkinParser legacy(m_pConfig, pKeyboard, pPlayerManager, pControllerManager, pLibrary, pVCMan);
+ LegacySkinParser legacy(m_pConfig, pKeyboard, pPlayerManager,
+ pControllerManager, pLibrary, pVCMan,
+ pEffectsManager);
return legacy.parseSkin(skinPath, pParent);
}
diff --git a/src/skin/skinloader.h b/src/skin/skinloader.h
index 8fe9f4a873d6..635d15b73aeb 100644
--- a/src/skin/skinloader.h
+++ b/src/skin/skinloader.h
@@ -11,6 +11,7 @@ class ControllerManager;
class Library;
class MixxxView;
class VinylControlManager;
+class EffectsManager;
class SkinLoader {
public:
@@ -21,7 +22,8 @@ class SkinLoader {
PlayerManager* pPlayerManager,
ControllerManager* pControllerManager,
Library* pLibrary,
- VinylControlManager* pVCMan);
+ VinylControlManager* pVCMan,
+ EffectsManager* pEffectsManager);
QString getConfiguredSkinPath();
diff --git a/src/util.h b/src/util.h
index bbc436eac78e..d667ea914be5 100644
--- a/src/util.h
+++ b/src/util.h
@@ -1,6 +1,8 @@
#ifndef UTIL_H
#define UTIL_H
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
diff --git a/src/widget/weffectchain.cpp b/src/widget/weffectchain.cpp
new file mode 100644
index 000000000000..3835dadea7e2
--- /dev/null
+++ b/src/widget/weffectchain.cpp
@@ -0,0 +1,23 @@
+#include "widget/weffectchain.h"
+
+WEffectChain::WEffectChain(QWidget* pParent)
+ : QLabel(pParent) {
+}
+
+WEffectChain::~WEffectChain() {
+}
+
+void WEffectChain::setEffectChainSlot(EffectChainSlotPointer effectChainSlot) {
+ if (effectChainSlot) {
+ m_pEffectChainSlot = effectChainSlot;
+ connect(effectChainSlot.data(), SIGNAL(updated()),
+ this, SLOT(chainUpdated()));
+ chainUpdated();
+ }
+}
+
+void WEffectChain::chainUpdated() {
+ if (m_pEffectChainSlot) {
+ setText(m_pEffectChainSlot->name());
+ }
+}
diff --git a/src/widget/weffectchain.h b/src/widget/weffectchain.h
new file mode 100644
index 000000000000..273dfbd5b13b
--- /dev/null
+++ b/src/widget/weffectchain.h
@@ -0,0 +1,25 @@
+#ifndef WEFFECTCHAIN_H
+#define WEFFECTCHAIN_H
+
+#include
+#include
+
+#include "effects/effectchainslot.h"
+
+class WEffectChain : public QLabel {
+ Q_OBJECT
+ public:
+ WEffectChain(QWidget* pParent=NULL);
+ virtual ~WEffectChain();
+
+ // Set the EffectChain that should be monitored by this WEffectChain
+ void setEffectChainSlot(EffectChainSlotPointer pEffectChainSlot);
+
+ private slots:
+ void chainUpdated();
+
+ private:
+ EffectChainSlotPointer m_pEffectChainSlot;
+};
+
+#endif /* WEFFECTCHAIN_H */