From e4beaf0074a5bc17ef4f2973c8d036576b17e1c6 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 12 May 2014 19:17:51 +0200 Subject: [PATCH] ZynAddSubFX: imported current head Imported current head of LMMS-specific ZynAddSubFX source code. HEAD: c920930cce09b4f691f585d93a19eac85672e50e --- plugins/zynaddsubfx/zynaddsubfx/.gitignore | 8 + plugins/zynaddsubfx/zynaddsubfx/.gitmodules | 3 + plugins/zynaddsubfx/zynaddsubfx/AUTHORS.txt | 31 + .../zynaddsubfx/zynaddsubfx/CMakeLists.txt | 21 + plugins/zynaddsubfx/zynaddsubfx/COPYING | 347 ++ .../zynaddsubfx/zynaddsubfx/CTestConfig.cmake | 13 + plugins/zynaddsubfx/zynaddsubfx/ChangeLog | 1036 +++++ .../ExternalPrograms/Controller/Controller.C | 84 + .../ExternalPrograms/Controller/Controller.h | 33 + .../Controller/ControllerUI.fl | 217 + .../ExternalPrograms/Controller/main.C | 16 + .../ExternalPrograms/Spliter/Spliter.C | 82 + .../ExternalPrograms/Spliter/Spliter.h | 27 + .../ExternalPrograms/Spliter/SpliterUI.fl | 70 + .../ExternalPrograms/Spliter/main.C | 37 + .../ExternalPrograms/Spliter/readme.txt | 15 + .../zynaddsubfx/ExternalPrograms/readme.txt | 2 + plugins/zynaddsubfx/zynaddsubfx/FAQ.txt | 27 + plugins/zynaddsubfx/zynaddsubfx/HISTORY.txt | 222 + plugins/zynaddsubfx/zynaddsubfx/README.txt | 86 + .../zynaddsubfx/zynaddsubfx/ZynAddSubFX.lsm | 22 + plugins/zynaddsubfx/zynaddsubfx/bugs.txt | 0 .../zynaddsubfx/cmake/FindAlsa.cmake | 69 + .../zynaddsubfx/cmake/FindCxxTest.cmake | 200 + .../zynaddsubfx/cmake/FindJACK.cmake | 17 + .../zynaddsubfx/cmake/FindOSS.cmake | 9 + .../zynaddsubfx/cmake/Findzlib.cmake | 39 + plugins/zynaddsubfx/zynaddsubfx/doc/Doxyfile | 309 ++ .../zynaddsubfx/doc/IT/01-intro_IT.txt | 55 + .../zynaddsubfx/doc/IT/02-filter_IT.txt | 61 + .../zynaddsubfx/doc/IT/03-lfo_IT.txt | 64 + .../zynaddsubfx/doc/IT/04-envelope_IT.txt | 134 + .../zynaddsubfx/doc/IT/05-adsynth_IT.txt | 78 + .../zynaddsubfx/doc/IT/06-controller_IT.txt | 56 + .../zynaddsubfx/doc/IT/08-saving_IT.txt | 54 + .../doc/IT/APPENDIX_A-mididefaults_IT.txt | 22 + .../doc/IT/APPENDIX_B-build_IT.txt | 61 + .../doc/IT/APPENDIX_C-doc_getting_IT.txt | 63 + .../zynaddsubfx/doc/IT/zynaddsubfx_IT.txt | 28 + .../zynaddsubfx/zynaddsubfx/doc/README.txt | 9 + .../zynaddsubfx/zynaddsubfx/doc/adsynth.txt | 148 + plugins/zynaddsubfx/zynaddsubfx/doc/build.txt | 59 + .../zynaddsubfx/doc/controller.txt | 54 + .../zynaddsubfx/zynaddsubfx/doc/effects.txt | 531 +++ .../zynaddsubfx/zynaddsubfx/doc/envelope.txt | 131 + .../zynaddsubfx/zynaddsubfx/doc/filter.txt | 81 + .../zynaddsubfx/doc/gen/ad-note.tex | 37 + .../zynaddsubfx/doc/gen/chorus.tex | 38 + .../zynaddsubfx/doc/gen/distort.tex | 44 + .../zynaddsubfx/doc/gen/dynamic.tex | 46 + .../zynaddsubfx/zynaddsubfx/doc/gen/echo.tex | 43 + .../zynaddsubfx/zynaddsubfx/doc/gen/fig.sty | 14 + .../zynaddsubfx/doc/gen/reverb.tex | 50 + .../zynaddsubfx/zynaddsubfx/doc/gen/velf.tex | 16 + .../zynaddsubfx/zynaddsubfx/doc/getting.txt | 65 + .../zynaddsubfx/doc/images/ad-global.png | Bin 0 -> 81812 bytes .../zynaddsubfx/doc/images/ad-voice.png | Bin 0 -> 121442 bytes .../zynaddsubfx/doc/images/envelope1.png | Bin 0 -> 41735 bytes .../zynaddsubfx/doc/images/envelope2.png | Bin 0 -> 16088 bytes .../zynaddsubfx/doc/images/envelope3.png | Bin 0 -> 20588 bytes .../zynaddsubfx/doc/images/envelope4.png | Bin 0 -> 17789 bytes .../zynaddsubfx/doc/images/filter0.png | Bin 0 -> 245103 bytes .../zynaddsubfx/doc/images/filter1.png | Bin 0 -> 156922 bytes .../zynaddsubfx/doc/images/filter2.png | Bin 0 -> 77767 bytes .../zynaddsubfx/doc/images/lfo0.png | Bin 0 -> 25759 bytes .../zynaddsubfx/doc/images/lfo1.png | Bin 0 -> 27044 bytes .../zynaddsubfx/doc/images/lfo2.png | Bin 0 -> 60122 bytes .../doc/images/phaser-spectrogram.jpg | Bin 0 -> 28409 bytes .../zynaddsubfx/doc/images/uicontroller.png | Bin 0 -> 21647 bytes .../zynaddsubfx/doc/images/uienvelope0.jpg | Bin 0 -> 12081 bytes .../zynaddsubfx/doc/images/uienvelope1.jpg | Bin 0 -> 5700 bytes .../zynaddsubfx/doc/images/uienvelope2.jpg | Bin 0 -> 5342 bytes .../zynaddsubfx/doc/images/uienvelope3.jpg | Bin 0 -> 6674 bytes .../zynaddsubfx/doc/images/uifilter.png | Bin 0 -> 10133 bytes .../zynaddsubfx/doc/images/uilfo.jpg | Bin 0 -> 6312 bytes .../zynaddsubfx/doc/images/uimain.png | Bin 0 -> 44523 bytes .../zynaddsubfx/doc/images/uioscil.png | Bin 0 -> 38211 bytes plugins/zynaddsubfx/zynaddsubfx/doc/intro.txt | 59 + plugins/zynaddsubfx/zynaddsubfx/doc/lfo.txt | 65 + .../zynaddsubfx/doc/mididefaults.txt | 22 + plugins/zynaddsubfx/zynaddsubfx/doc/nrpn.txt | 162 + .../zynaddsubfx/zynaddsubfx/doc/saving.txt | 55 + .../zynaddsubfx/doc/zynaddsubfx.1.txt | 82 + .../zynaddsubfx/doc/zynaddsubfx.txt | 31 + .../zynaddsubfx/pixmaps/black_key.png | Bin 0 -> 892 bytes .../zynaddsubfx/pixmaps/black_key_pressed.png | Bin 0 -> 901 bytes .../zynaddsubfx/zynaddsubfx/pixmaps/knob.png | Bin 0 -> 26619 bytes .../zynaddsubfx/pixmaps/module_backdrop.png | Bin 0 -> 1779 bytes .../zynaddsubfx/pixmaps/white_key.png | Bin 0 -> 504 bytes .../zynaddsubfx/pixmaps/white_key_pressed.png | Bin 0 -> 796 bytes .../zynaddsubfx/pixmaps/window_backdrop.png | Bin 0 -> 10962 bytes .../zynaddsubfx/src/CMakeLists.txt | 384 ++ .../zynaddsubfx/src/DSP/AnalogFilter.cpp | 428 ++ .../zynaddsubfx/src/DSP/AnalogFilter.h | 85 + .../zynaddsubfx/src/DSP/CMakeLists.txt | 9 + .../zynaddsubfx/src/DSP/FFTwrapper.cpp | 85 + .../zynaddsubfx/src/DSP/FFTwrapper.h | 52 + .../zynaddsubfx/src/DSP/Filter.cpp | 75 + .../zynaddsubfx/zynaddsubfx/src/DSP/Filter.h | 64 + .../zynaddsubfx/src/DSP/FormantFilter.cpp | 230 + .../zynaddsubfx/src/DSP/FormantFilter.h | 66 + .../zynaddsubfx/src/DSP/SVFilter.cpp | 183 + .../zynaddsubfx/src/DSP/SVFilter.h | 70 + .../zynaddsubfx/src/DSP/Unison.cpp | 203 + .../zynaddsubfx/zynaddsubfx/src/DSP/Unison.h | 76 + .../zynaddsubfx/src/Effects/Alienwah.cpp | 236 + .../zynaddsubfx/src/Effects/Alienwah.h | 81 + .../zynaddsubfx/src/Effects/CMakeLists.txt | 14 + .../zynaddsubfx/src/Effects/Chorus.cpp | 269 ++ .../zynaddsubfx/src/Effects/Chorus.h | 106 + .../zynaddsubfx/src/Effects/Distorsion.cpp | 245 ++ .../zynaddsubfx/src/Effects/Distorsion.h | 61 + .../zynaddsubfx/src/Effects/DynamicFilter.cpp | 312 ++ .../zynaddsubfx/src/Effects/DynamicFilter.h | 65 + .../zynaddsubfx/src/Effects/EQ.cpp | 198 + .../zynaddsubfx/zynaddsubfx/src/Effects/EQ.h | 55 + .../zynaddsubfx/src/Effects/Echo.cpp | 232 + .../zynaddsubfx/src/Effects/Echo.h | 106 + .../zynaddsubfx/src/Effects/Effect.cpp | 67 + .../zynaddsubfx/src/Effects/Effect.h | 124 + .../zynaddsubfx/src/Effects/EffectLFO.cpp | 113 + .../zynaddsubfx/src/Effects/EffectLFO.h | 53 + .../zynaddsubfx/src/Effects/EffectMgr.cpp | 311 ++ .../zynaddsubfx/src/Effects/EffectMgr.h | 86 + .../zynaddsubfx/src/Effects/Phaser.cpp | 470 ++ .../zynaddsubfx/src/Effects/Phaser.h | 98 + .../zynaddsubfx/src/Effects/Reverb.cpp | 491 +++ .../zynaddsubfx/src/Effects/Reverb.h | 94 + .../zynaddsubfx/zynaddsubfx/src/Misc/Bank.cpp | 473 ++ .../zynaddsubfx/zynaddsubfx/src/Misc/Bank.h | 103 + .../zynaddsubfx/src/Misc/CMakeLists.txt | 28 + .../zynaddsubfx/src/Misc/Config.cpp | 315 ++ .../zynaddsubfx/zynaddsubfx/src/Misc/Config.h | 76 + .../zynaddsubfx/src/Misc/Control.h | 99 + .../zynaddsubfx/zynaddsubfx/src/Misc/Dump.cpp | 121 + .../zynaddsubfx/zynaddsubfx/src/Misc/Dump.h | 63 + .../zynaddsubfx/src/Misc/LASHClient.cpp | 103 + .../zynaddsubfx/src/Misc/LASHClient.h | 63 + .../zynaddsubfx/src/Misc/Master.cpp | 808 ++++ .../zynaddsubfx/zynaddsubfx/src/Misc/Master.h | 183 + .../zynaddsubfx/src/Misc/Microtonal.cpp | 694 +++ .../zynaddsubfx/src/Misc/Microtonal.h | 134 + .../zynaddsubfx/zynaddsubfx/src/Misc/Part.cpp | 1338 ++++++ .../zynaddsubfx/zynaddsubfx/src/Misc/Part.h | 202 + .../zynaddsubfx/src/Misc/QtXmlWrapper.cpp | 550 +++ .../zynaddsubfx/src/Misc/QtXmlWrapper.h | 125 + .../zynaddsubfx/src/Misc/Recorder.cpp | 91 + .../zynaddsubfx/src/Misc/Recorder.h | 54 + .../zynaddsubfx/src/Misc/Stereo.cpp | 38 + .../zynaddsubfx/zynaddsubfx/src/Misc/Stereo.h | 40 + .../zynaddsubfx/zynaddsubfx/src/Misc/Util.cpp | 230 + .../zynaddsubfx/zynaddsubfx/src/Misc/Util.h | 116 + .../zynaddsubfx/src/Misc/WavFile.cpp | 97 + .../zynaddsubfx/src/Misc/WavFile.h | 44 + .../zynaddsubfx/src/Misc/WaveShapeSmps.cpp | 189 + .../zynaddsubfx/src/Misc/WaveShapeSmps.h | 31 + .../zynaddsubfx/src/Misc/XMLwrapper.cpp | 623 +++ .../zynaddsubfx/src/Misc/XMLwrapper.h | 275 ++ .../zynaddsubfx/src/Nio/AlsaEngine.cpp | 366 ++ .../zynaddsubfx/src/Nio/AlsaEngine.h | 82 + .../zynaddsubfx/src/Nio/AudioOut.cpp | 58 + .../zynaddsubfx/src/Nio/AudioOut.h | 61 + .../zynaddsubfx/src/Nio/CMakeLists.txt | 47 + .../zynaddsubfx/src/Nio/Engine.cpp | 28 + .../zynaddsubfx/zynaddsubfx/src/Nio/Engine.h | 41 + .../zynaddsubfx/src/Nio/EngineMgr.cpp | 156 + .../zynaddsubfx/src/Nio/EngineMgr.h | 43 + .../zynaddsubfx/zynaddsubfx/src/Nio/InMgr.cpp | 143 + .../zynaddsubfx/zynaddsubfx/src/Nio/InMgr.h | 54 + .../zynaddsubfx/src/Nio/JackEngine.cpp | 406 ++ .../zynaddsubfx/src/Nio/JackEngine.h | 89 + .../zynaddsubfx/src/Nio/MidiIn.cpp | 77 + .../zynaddsubfx/zynaddsubfx/src/Nio/MidiIn.h | 43 + .../zynaddsubfx/zynaddsubfx/src/Nio/Nio.cpp | 138 + plugins/zynaddsubfx/zynaddsubfx/src/Nio/Nio.h | 47 + .../zynaddsubfx/src/Nio/NulEngine.cpp | 113 + .../zynaddsubfx/src/Nio/NulEngine.h | 56 + .../zynaddsubfx/src/Nio/OssEngine.cpp | 285 ++ .../zynaddsubfx/src/Nio/OssEngine.h | 76 + .../zynaddsubfx/src/Nio/OutMgr.cpp | 185 + .../zynaddsubfx/zynaddsubfx/src/Nio/OutMgr.h | 65 + .../zynaddsubfx/src/Nio/PaEngine.cpp | 118 + .../zynaddsubfx/src/Nio/PaEngine.h | 57 + .../zynaddsubfx/src/Nio/SafeQueue.cpp | 95 + .../zynaddsubfx/src/Nio/SafeQueue.h | 48 + .../zynaddsubfx/src/Nio/WavEngine.cpp | 134 + .../zynaddsubfx/src/Nio/WavEngine.h | 61 + .../src/Output/DSSIaudiooutput.cpp | 711 +++ .../zynaddsubfx/src/Output/DSSIaudiooutput.h | 123 + .../src/Params/ADnoteParameters.cpp | 792 ++++ .../zynaddsubfx/src/Params/ADnoteParameters.h | 316 ++ .../zynaddsubfx/src/Params/CMakeLists.txt | 13 + .../zynaddsubfx/src/Params/Controller.cpp | 419 ++ .../zynaddsubfx/src/Params/Controller.h | 220 + .../zynaddsubfx/src/Params/EnvelopeParams.cpp | 296 ++ .../zynaddsubfx/src/Params/EnvelopeParams.h | 89 + .../zynaddsubfx/src/Params/FilterParams.cpp | 392 ++ .../zynaddsubfx/src/Params/FilterParams.h | 102 + .../zynaddsubfx/src/Params/LFOParams.cpp | 104 + .../zynaddsubfx/src/Params/LFOParams.h | 71 + .../src/Params/PADnoteParameters.cpp | 903 ++++ .../src/Params/PADnoteParameters.h | 179 + .../zynaddsubfx/src/Params/Presets.cpp | 119 + .../zynaddsubfx/src/Params/Presets.h | 56 + .../zynaddsubfx/src/Params/PresetsArray.cpp | 146 + .../zynaddsubfx/src/Params/PresetsArray.h | 59 + .../zynaddsubfx/src/Params/PresetsStore.cpp | 184 + .../zynaddsubfx/src/Params/PresetsStore.h | 70 + .../src/Params/SUBnoteParameters.cpp | 250 ++ .../src/Params/SUBnoteParameters.h | 103 + .../zynaddsubfx/src/Synth/ADnote.cpp | 1819 ++++++++ .../zynaddsubfx/src/Synth/ADnote.h | 325 ++ .../zynaddsubfx/src/Synth/CMakeLists.txt | 11 + .../zynaddsubfx/src/Synth/Envelope.cpp | 198 + .../zynaddsubfx/src/Synth/Envelope.h | 62 + .../zynaddsubfx/zynaddsubfx/src/Synth/LFO.cpp | 183 + .../zynaddsubfx/zynaddsubfx/src/Synth/LFO.h | 58 + .../zynaddsubfx/src/Synth/OscilGen.cpp | 1503 +++++++ .../zynaddsubfx/src/Synth/OscilGen.h | 175 + .../zynaddsubfx/src/Synth/PADnote.cpp | 432 ++ .../zynaddsubfx/src/Synth/PADnote.h | 121 + .../zynaddsubfx/src/Synth/Resonance.cpp | 276 ++ .../zynaddsubfx/src/Synth/Resonance.h | 70 + .../zynaddsubfx/src/Synth/SUBnote.cpp | 588 +++ .../zynaddsubfx/src/Synth/SUBnote.h | 107 + .../zynaddsubfx/src/Synth/SynthNote.cpp | 134 + .../zynaddsubfx/src/Synth/SynthNote.h | 86 + .../zynaddsubfx/src/Tests/AdNoteTest.h | 201 + .../zynaddsubfx/src/Tests/CMakeLists.txt | 35 + .../zynaddsubfx/src/Tests/ControllerTest.h | 75 + .../zynaddsubfx/src/Tests/EchoTest.h | 126 + .../zynaddsubfx/src/Tests/MicrotonalTest.h | 136 + .../zynaddsubfx/src/Tests/OscilGenTest.h | 141 + .../zynaddsubfx/src/Tests/PadNoteTest.h | 207 + .../zynaddsubfx/src/Tests/PluginTest.h | 119 + .../zynaddsubfx/src/Tests/RandTest.h | 41 + .../zynaddsubfx/src/Tests/SubNoteTest.h | 182 + .../zynaddsubfx/src/Tests/UnisonTest.h | 183 + .../zynaddsubfx/src/Tests/XMLwrapperTest.h | 70 + .../zynaddsubfx/src/Tests/guitar-adnote.xmz | 3834 +++++++++++++++++ .../zynaddsubfx/src/UI/ADnoteUI.fl | 1162 +++++ .../zynaddsubfx/zynaddsubfx/src/UI/BankUI.fl | 368 ++ .../zynaddsubfx/src/UI/CMakeLists.txt | 45 + .../zynaddsubfx/src/UI/ConfigUI.fl | 458 ++ .../zynaddsubfx/zynaddsubfx/src/UI/EffUI.fl | 2351 ++++++++++ .../zynaddsubfx/src/UI/EnvelopeUI.fl | 868 ++++ .../zynaddsubfx/src/UI/FilterUI.fl | 638 +++ .../zynaddsubfx/zynaddsubfx/src/UI/LFOUI.fl | 181 + .../zynaddsubfx/src/UI/MasterUI.fl | 1889 ++++++++ .../zynaddsubfx/src/UI/MicrotonalUI.fl | 270 ++ plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM.C | 163 + plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM.H | 45 + .../zynaddsubfx/src/UI/NSM/Client.C | 403 ++ .../zynaddsubfx/src/UI/NSM/Client.H | 143 + .../zynaddsubfx/zynaddsubfx/src/UI/NioUI.cpp | 76 + .../zynaddsubfx/zynaddsubfx/src/UI/NioUI.h | 20 + .../zynaddsubfx/src/UI/OscilGenUI.fl | 1148 +++++ .../zynaddsubfx/src/UI/PADnoteUI.fl | 1114 +++++ .../zynaddsubfx/zynaddsubfx/src/UI/PartUI.fl | 1125 +++++ .../zynaddsubfx/src/UI/PresetsUI.fl | 278 ++ .../zynaddsubfx/src/UI/ResonanceUI.fl | 398 ++ .../zynaddsubfx/src/UI/SUBnoteUI.fl | 450 ++ .../zynaddsubfx/src/UI/VirKeyboard.fl | 487 +++ .../zynaddsubfx/src/UI/WidgetPDial.cpp | 283 ++ .../zynaddsubfx/src/UI/WidgetPDial.h | 26 + .../zynaddsubfx/zynaddsubfx/src/UI/common.H | 29 + plugins/zynaddsubfx/zynaddsubfx/src/globals.h | 249 ++ plugins/zynaddsubfx/zynaddsubfx/src/main.cpp | 551 +++ plugins/zynaddsubfx/zynaddsubfx/style.cfg | 1218 ++++++ plugins/zynaddsubfx/zynaddsubfx/style.sh | 6 + .../zynaddsubfx/zynaddsubfx-alsa.desktop | 8 + .../zynaddsubfx/zynaddsubfx-jack.desktop | 8 + .../zynaddsubfx/zynaddsubfx/zynaddsubfx.ico | Bin 0 -> 9662 bytes .../zynaddsubfx/zynaddsubfx/zynaddsubfx.svg | 313 ++ 274 files changed, 55921 insertions(+) create mode 100644 plugins/zynaddsubfx/zynaddsubfx/.gitignore create mode 100644 plugins/zynaddsubfx/zynaddsubfx/.gitmodules create mode 100644 plugins/zynaddsubfx/zynaddsubfx/AUTHORS.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/CMakeLists.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/COPYING create mode 100644 plugins/zynaddsubfx/zynaddsubfx/CTestConfig.cmake create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ChangeLog create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/Controller.C create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/Controller.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/ControllerUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/main.C create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/Spliter.C create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/Spliter.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/SpliterUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/main.C create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/readme.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/readme.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/FAQ.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/HISTORY.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/README.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/ZynAddSubFX.lsm create mode 100644 plugins/zynaddsubfx/zynaddsubfx/bugs.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/cmake/FindAlsa.cmake create mode 100644 plugins/zynaddsubfx/zynaddsubfx/cmake/FindCxxTest.cmake create mode 100644 plugins/zynaddsubfx/zynaddsubfx/cmake/FindJACK.cmake create mode 100644 plugins/zynaddsubfx/zynaddsubfx/cmake/FindOSS.cmake create mode 100644 plugins/zynaddsubfx/zynaddsubfx/cmake/Findzlib.cmake create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/Doxyfile create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/IT/01-intro_IT.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/IT/02-filter_IT.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/IT/03-lfo_IT.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/IT/04-envelope_IT.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/IT/05-adsynth_IT.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/IT/06-controller_IT.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/IT/08-saving_IT.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_A-mididefaults_IT.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_B-build_IT.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_C-doc_getting_IT.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/IT/zynaddsubfx_IT.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/README.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/adsynth.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/build.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/controller.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/effects.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/envelope.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/filter.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/gen/ad-note.tex create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/gen/chorus.tex create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/gen/distort.tex create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/gen/dynamic.tex create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/gen/echo.tex create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/gen/fig.sty create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/gen/reverb.tex create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/gen/velf.tex create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/getting.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/ad-global.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/ad-voice.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/envelope1.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/envelope2.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/envelope3.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/envelope4.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/filter0.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/filter1.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/filter2.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/lfo0.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/lfo1.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/lfo2.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/phaser-spectrogram.jpg create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/uicontroller.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope0.jpg create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope1.jpg create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope2.jpg create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope3.jpg create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/uifilter.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/uilfo.jpg create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/uimain.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/images/uioscil.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/intro.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/lfo.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/mididefaults.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/nrpn.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/saving.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/zynaddsubfx.1.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/doc/zynaddsubfx.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/pixmaps/black_key.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/pixmaps/black_key_pressed.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/pixmaps/knob.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/pixmaps/module_backdrop.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/pixmaps/white_key.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/pixmaps/white_key_pressed.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/pixmaps/window_backdrop.png create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/CMakeLists.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/AnalogFilter.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/AnalogFilter.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/CMakeLists.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/FFTwrapper.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/FFTwrapper.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/Filter.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/Filter.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/FormantFilter.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/FormantFilter.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/SVFilter.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/SVFilter.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/Unison.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/DSP/Unison.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Alienwah.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Alienwah.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/CMakeLists.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Chorus.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Chorus.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Distorsion.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Distorsion.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/DynamicFilter.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/DynamicFilter.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/EQ.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/EQ.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Echo.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Echo.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Effect.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Effect.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectLFO.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectLFO.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectMgr.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectMgr.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Phaser.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Phaser.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Reverb.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Effects/Reverb.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/CMakeLists.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Config.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Config.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Control.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Dump.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Dump.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/LASHClient.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/LASHClient.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Master.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Master.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Microtonal.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Microtonal.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Part.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Part.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/QtXmlWrapper.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/QtXmlWrapper.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Recorder.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Recorder.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Stereo.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Stereo.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Util.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/Util.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/WavFile.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/WavFile.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/WaveShapeSmps.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/WaveShapeSmps.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/XMLwrapper.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Misc/XMLwrapper.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/AlsaEngine.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/AlsaEngine.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/AudioOut.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/AudioOut.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/CMakeLists.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/Engine.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/Engine.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/EngineMgr.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/EngineMgr.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/InMgr.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/InMgr.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/JackEngine.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/JackEngine.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/MidiIn.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/MidiIn.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/Nio.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/Nio.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/NulEngine.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/NulEngine.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/OssEngine.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/OssEngine.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/OutMgr.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/OutMgr.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/PaEngine.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/PaEngine.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/SafeQueue.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/SafeQueue.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/WavEngine.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Nio/WavEngine.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Output/DSSIaudiooutput.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Output/DSSIaudiooutput.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/ADnoteParameters.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/ADnoteParameters.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/CMakeLists.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/Controller.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/Controller.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/EnvelopeParams.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/EnvelopeParams.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/FilterParams.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/FilterParams.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/LFOParams.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/LFOParams.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/PADnoteParameters.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/PADnoteParameters.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/Presets.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/Presets.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/PresetsArray.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/PresetsArray.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/PresetsStore.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/PresetsStore.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/SUBnoteParameters.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Params/SUBnoteParameters.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/ADnote.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/ADnote.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/CMakeLists.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/Envelope.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/Envelope.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/LFO.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/LFO.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/OscilGen.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/OscilGen.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/PADnote.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/PADnote.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/Resonance.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/Resonance.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/SUBnote.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/SUBnote.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/SynthNote.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Synth/SynthNote.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/AdNoteTest.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/CMakeLists.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/ControllerTest.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/EchoTest.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/MicrotonalTest.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/OscilGenTest.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/PadNoteTest.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/PluginTest.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/RandTest.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/SubNoteTest.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/UnisonTest.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/XMLwrapperTest.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/Tests/guitar-adnote.xmz create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/ADnoteUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/BankUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/CMakeLists.txt create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/ConfigUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/EffUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/EnvelopeUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/FilterUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/LFOUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/MasterUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/MicrotonalUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM.C create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM.H create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM/Client.C create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM/Client.H create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/NioUI.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/NioUI.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/OscilGenUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/PADnoteUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/PartUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/PresetsUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/ResonanceUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/SUBnoteUI.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/VirKeyboard.fl create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/WidgetPDial.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/WidgetPDial.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/UI/common.H create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/globals.h create mode 100644 plugins/zynaddsubfx/zynaddsubfx/src/main.cpp create mode 100644 plugins/zynaddsubfx/zynaddsubfx/style.cfg create mode 100755 plugins/zynaddsubfx/zynaddsubfx/style.sh create mode 100644 plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx-alsa.desktop create mode 100644 plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx-jack.desktop create mode 100644 plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx.ico create mode 100644 plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx.svg diff --git a/plugins/zynaddsubfx/zynaddsubfx/.gitignore b/plugins/zynaddsubfx/zynaddsubfx/.gitignore new file mode 100644 index 000000000..77d28419f --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/.gitignore @@ -0,0 +1,8 @@ +*.o +*~ +src/zynaddsubfx +src/Tests/runner +src/Tests/runner.cpp +CMakeCache.txt +CMakeFiles +build diff --git a/plugins/zynaddsubfx/zynaddsubfx/.gitmodules b/plugins/zynaddsubfx/zynaddsubfx/.gitmodules new file mode 100644 index 000000000..183f3f373 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/.gitmodules @@ -0,0 +1,3 @@ +[submodule "instruments"] + path = instruments + url = git://git.code.sf.net/p/zynaddsubfx/instruments diff --git a/plugins/zynaddsubfx/zynaddsubfx/AUTHORS.txt b/plugins/zynaddsubfx/zynaddsubfx/AUTHORS.txt new file mode 100644 index 000000000..e53ca18b6 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/AUTHORS.txt @@ -0,0 +1,31 @@ +Main author: + Nasca Octavian Paul + +Developers: + Mark McCurry + Harald Hvaal + +Contributors: + Gerald Folcher (legato, mono notes memory) + Lars Luthman (zombie fix,jack midi, LASH support) + Daniel Clemente (with a workaround of X11 repeated key bug) + Emmanuel Saracco (fix for JACK output) + Achim Settelmeier (QUERTZ keyboard layout for virtual keyboard) + Jérémie Andréi (AZERTY keyboard layout, Array index fix, OSS failsafe) + Alexis Ballier (const char* <-> string mismatch, NULLMidi prototype fix) + Tobias Doerffel (static-instance variables fix, missing include fix) + James Morris (Memory leaks in FLTK GUI) + Alan Calvert (Portions of New IO) + Stephen Parry (DSSI rebuild) + Ryan Billing (APhaser) + Hans Petter Selasky (OSS Midi, FreeBSD support, Bank UI bug fix) + Damien Goutte-Gattat (Bank select midi support) + Lieven Moors (Spike/Circle waveform) + Olaf Schulz (MIDI Aftertouch support) + Jonathan Liles (NSM & NTK support) + Johannes Lorenz (Effect Documentation) + Ilario Glasgo (Italian Doc Translation) + Christopher Oliver (Unison + presets fix, mousewheel support) + Filipe Coelho (Globals Cleanup) + Andre Sklenar (UI Pixmaps) + diff --git a/plugins/zynaddsubfx/zynaddsubfx/CMakeLists.txt b/plugins/zynaddsubfx/zynaddsubfx/CMakeLists.txt new file mode 100644 index 000000000..169877a3d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 2.8) +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/") +project(zynaddsubfx) +set(VERSION "2.4.3") + +enable_testing() +include(CTestConfig.cmake) +#Currently the only directory that uses cmake +add_subdirectory(src) + +install(FILES AUTHORS.txt COPYING FAQ.txt HISTORY.txt README.txt + DESTINATION share/doc/zynaddsubfx + ) +install(FILES zynaddsubfx-jack.desktop zynaddsubfx-alsa.desktop + DESTINATION share/applications) +install(FILES zynaddsubfx.svg + DESTINATION share/pixmaps) +install(DIRECTORY instruments/banks + DESTINATION share/zynaddsubfx) +install(DIRECTORY instruments/examples + DESTINATION share/zynaddsubfx) diff --git a/plugins/zynaddsubfx/zynaddsubfx/COPYING b/plugins/zynaddsubfx/zynaddsubfx/COPYING new file mode 100644 index 000000000..e47a022c6 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/COPYING @@ -0,0 +1,347 @@ + +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the ZynAddSubFX application) +is copyrighted by the authors (Nasca Octavian Paul and others) who actually wrote it. +--------------------------------------------------------------------------- + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/plugins/zynaddsubfx/zynaddsubfx/CTestConfig.cmake b/plugins/zynaddsubfx/zynaddsubfx/CTestConfig.cmake new file mode 100644 index 000000000..bf4f379ae --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/CTestConfig.cmake @@ -0,0 +1,13 @@ +## This file should be placed in the root directory of your project. +## Then modify the CMakeLists.txt file in the root directory of your +## project to incorporate the testing dashboard. +## # The following are required to uses Dart and the Cdash dashboard +## ENABLE_TESTING() +## INCLUDE(CTest) +set(CTEST_PROJECT_NAME "ZynAddSubFX") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "fundamental-code.com") +set(CTEST_DROP_LOCATION "/CDash/submit.php?project=ZynAddSubFX") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/plugins/zynaddsubfx/zynaddsubfx/ChangeLog b/plugins/zynaddsubfx/zynaddsubfx/ChangeLog new file mode 100644 index 000000000..113447e9d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ChangeLog @@ -0,0 +1,1036 @@ +6 Mar 2002 -(dupamasa - in jur de ora 4) Mi-a venit ideea exact cum sa fac cand ma plimbam pe strada Pandurilor +7/8 Mar 2002 - Started to do diagrams +10 Mar 2002 - Started to write "voice" +11 Mar 2002 - Heard first sound +12 Mar 2002 - tested with 200 voices +16 Mar 2002 - made "Note" the main class + - added vibratto + - added glissando +20 Mar 2002 - started to write the Envelope class +21 Mar 2002 - Envelope written (almost) + Volume envelope almost written +23 Mar 2002 - Scris relasenote(putin) + Envelope-ul este si in dB + "glissando" este inlocuit cu "Envelope" de frecventa + started to write the LFO class +24 Mar 2002 - Corrected a bug that could crashed the synth (forgotten to disable the amp/freq envelopeenabled when killed it) +25 Mar 2002 - Started to write the Filter class (wrote only few lines) +27 Mar 2002 - Scris filtrul(putin), si FilterEnvelope +28 Mar 2002 - Adaugat la LFO si tipul "rampup" si "rampdown" + Scris filterLFO si amplitudeLFO(termollo) + redenumiti si aranjati parametrii + Adaugat LFO delay + Scris FilterEnvelope(corect) si FilterLFO(corect) +29 Mar 2002 - Adaugat RingModulation + Adaugat FM/RM Amplitude si Frequency Envelope + Corectat un bug minor la Envelope-ASRinit(); + Adaugat FM +01 Apr 2002 - Corectat un bug care facea sa se auda paraituri la sunetele care incepeau co o faza!=0 + Scris cativa dintre parametrii globali Envelop-ulire,LFO,Filter,.. +02 Apr 2002 - Curatat putin ADnote + Adaugat VelocityScale la amplitudine, la FM si la Filtru Global +03 Apr 2002 - Aranjati toti parametrii ADnote in structuri +04 Apr 2002 - Mutati multi parametrii in ADnoteParameters + Inceput sa scriu ADnoteParameters +05 Apr 2002 - Inceput sa scriu clase speciale pentru parametrii(midi) (LFO..) +06 Apr 2002 - Continuat sa scriu clasele speciale pentru parametrii + Teoretic merge sinteza multitimbrala(Adica se poate aplea ADnote(canal,note,vel)) +07 Apr 2002 - Completat(aproape) transferul de parametri midi la cei reali +08 Apr 2002 - Added FM oscil at parameters and corrected a small FM bug +09 Apr 2002 - Inceput sa-l fac real-time +10 Apr 2002 - Merge la keyboard-ul MIDI, polifonic +27 Apr 2002 - Scris interfata la OSS, la latenta scazuta + Corectat un bug care facea ca sa se execute calcule inutile, ceea ce facea ca polifonia maxima sa scada de 10 ori +29 Apr 2002 - Inceput sa scriu interfata midi(obiect) +30 Apr 2002 - Continuat putin interfata midi (dar nu am terminat) +02 Mai 2002 - Merge in timp real cu latenta scazuta, dar se mai auda niste "pacanaituri" +03 Mai 2002 - Inceput sa scriu Reverb (acum este doar ecou) + "Pacanaiturile" au fost eliminate. +09 Mai 2002 - Reverb-ul suna a reverberatie +11 Mai 2002 - Adaugat cativa parametrii midi la Reverb +18 Mai 2002 - Adaugat filtrul AllPass la Reverb si adaugat parametrul Plohidamp +19 Mai 2002 - Adaugat InitialDelay (idelay) la Reverb +24 Iun 2002 - Clasa Filtru nu mai este dependenta de FilterParams(pot sa-l folosesc in alte scopuri) + Corectat un bug la filtru care facea ca la rezomante scazute sa amplifice f. mult basii + Adaugat High Pass Filter + Rezonanta filtrului este exponentiala + Adauga LPF+HPF la Reverb + Inceput sa scriu Generatorul de Functii (OscilGen) +25 Iun 2002 - Scris cateva forme de unda (functii) + Reverb-ul are volumul in dB si daca este zero(ca parametru) atunci se dezactiveaza +02 Iul 2002 - Adaugat inca o functie la generatorul de functii +03 Iul 2002 - Inceput sa scriu generarea de functii la OscilGen pe baza de FFT + Inlaturat DC-ul de la OscilGen +04 Iul 2002 - Adaugat ANTI-ALIASING la ADnote si insumarea armonicelor se face in domeniul frecventa + Corectat un bug care facea sa sune rau dac OSCIL_SIZE!=512 (era declarat de 2 ori) +12 Iul 2002 - Adaugat posibilitatea de a folosi ca modulator alta voce + Adaugat parametrii MIDI la OscilGen +13 Iul 2002 - Adaugat Randomness la clasa OscilGen +15 Iul 2002 - Adaugat si Panning(incl. Randomness) => instrumentul este acum stereo +16 Iul 2002 - Adaugat Randomness la LFO (faza 0 => random) + Inlaturat o eroare care facea ca amplitudinea sa nu fie interpolata +17 Iul 2002 - Volumul FM-ului este exponential + Adaugat atenuare la volumul FM-ului la note inalte +23 Iul 2002 - Adaugat EnvelopeStretch + Corectata o eroare care facea ca uneori sunetul sa se auda foarte tare la inceput + Adaugat fade-in (f. scurt) si fade out in caz ca envelop-ul are A=0 sau R=0, a.i. sa nu se auda pacanaituri +24 Iul 2002 - Corectat Relase-ul la Envelope si adaugat ForcedRelase +25 Iul 2002 - Adaugat posibilitatea de a nu folosi AntiAliasing-ul + Adaugat Frequency Modulation (nu phase modulation) + Adaugat Delay la fiecare voce + Adaugat Morphing la modulatie +26 Iul 2002 - Inceput sa scriu clasa Part +27 Iul 2002 - Se face controlul Midi folosind clasa Part si nu ADnote +28 Iul 2002 - Corectata o eroare care facea sa se instantieze clasa ADnoteParameters pt. fiecare nota => memoria era ocupata excesiv si "manca" din procesor. Cauza erorii este ca trimiteam obiectul ADnoteParameters ca parametru si nu referinta lui. Asta era cauza pacanaiturilor ce se auzeau daca apasam multe clape simultan. +29 Iul 2002 - Adaugat clasa Master (Permite acum mai multe instr. simultan => multitimbral) + Observat o eroare la Envelope +30 Iul 2002 - Adaugat EnvelopeStretch si Forcedrelase la instantierea unui obiect EnvelopeParams + Durata Sustainul-ui fortat este acceeasi indiferent de paramentrul EnvelopeStretch + Adaugat Ecou +31 Iul 2002 - Daca VelocityScaleFunction=127 atunci orice vel. va face amplitudinea maxima (ca si cand vel.=127) + Inceput sa scriu Interfata Utilizator +01 Aug 2002 - Toti parametrii sunt convertiti in REALTYPE direct de ADnote,de LFO + Inlataurate mici probleme de AntiAliasing daca detune-ul era prea sus si la unele moduri FM + Programul incepe sa fie controlabil de Interfata +02 Aug 2002 - Inlaturat o eroare stupida care facea ca sa se seteze valorile EnvelopeParams la -1 (scria din Master:: prea mult) +03 Aug 2002 - Terminata interfata pentru ADnoteParameters.GlobalPars + Adaugat inca un parametru la lfo (continous LFO) care faca ca LFO-ul sa nu inceapa la fiecare NoteOn + Corectat doua erori la ...[nvoice].AmpEnvelope si ...[nvoice].FreqEnvelope + Scrisa interfata pentru ADnoteParameters.VoicePars (fara FM+OSCIL...) +04 Aug 2002 - Scrisa interfata cu FM (fara Oscil) + Corectate doua erori cu provire la FMampenv si FMfreqenv + Inlaturat aliasing-ul la vocea FM + Modificata interfata (Voice si FM-ul sunt intr-o singura fereastra) + Inceput sa scriu schimbare voce curenta. +05 Aug 2002 - Adaugat interfata pentru cei mai importanti parametrii ai ADnote_VoicePar[nvoice] + Inceput sa scriu interfata pentru OscilGen +06 Aug 2002 - Este mult mai usoara schimbarea vocii curente. + Inceput sa scriu OscilEditor + Nu mai este necesara changebasefunc() la oscil pentru a schimba basefunction, se apeleaza automat. + OscilEditor este (aproape) complet + Toti parametrii ADnoteParameters au UI + Corectate cateva erori (cauzate de faptul ca nu am verificat daca ADnote::...Enabled!=0) +07 Aug 2002 - Corectata o eroare la envelope + Adaugat afisaj spectrum la OscilEdit + Adaugat parametrii noi: extenal oscillator (voice si FM) si oscilphase(si FM) si interfata pentru ei + Gasite mai multe erori care apar daca misc widget-urile in timp ce cant la clape (probabil este vorba de thread-uri care trebuie sa fie sincronizate sau ceva cam asa sau memory leaks) + Inceput sa scriu interfata pentru Part + Adaugat bypass la filtrul global + Adaugat conversia oscil-ului in basefunction + Corectata o mica eroare la calcularea oscil-ului referitor la faze +08 Aug 2002 - In VoiceList valorile sunt actualizate la fiecare apasare a butonului "ShowVoiceList" si formele de unda sunt afisate corect. + Corectate niste mici erori la FM + Daca se foloseste ca modulator o alta voce, interfata dezativeaza unii parametrii FM daca sunt inutili + Inceput sa scriu interfata si parametrii Master/Part + Schimbat putin Master si Part (atentie sa nu se instantieza ADnoteParameters la fiecare apasare de tasta) + Inceput sa scriu control-ul pentru Master/Parts +09 Aug 2002 - Scris parametrii Part si Master + Inceput sa scriu sincronizarea intre thread-uri +10 Aug 2002 - Adaugat o noua forma de unda la OscilGen + Adaugat sincronizarea intre thread-uri=>programul nu mai crapa daca in timp ce apas clapele, modific forma de unda + Adaugat enable/disable ADnote + Inceput sa scriu SUBnote/SUBnoteParameters + Se poate canta si la SUBnote(inceput sa scriu UI pt. el) +11 Aug 2002 - Scris controlul armonicelor + Adugati cativa parametrii la SUBnote + Adaugat AmpEnvelope la SUBnote(si UI) +12 Aug 2002 - Adaugat Detune la SUBnote si schimbat Detune-ul la ADnote + Adaugat FreqEnvelope la SUBnote +16 Aug 2002 - Corectata o eroare care facea ca VoiceOut sa fie inlaturat chiar daca era inca folosit(de alte voci) + Daca "Forced Relase" este off atunci se face relase-ul liniar + Adaugat BandWidth Envelope +17 Aug 2002 - Inceput sa pregatesc pentru EffectManager +18 Aug 2002 - Adaugat inca un parametru la Reverb: initial delay fb + Scris efectele de insertie + Inceput sa scriu efectele de sistem +19 Aug 2002 - Continuat sa scriu efectele de sistem + Inceput sa scriu interfata la Efecte (Reverb - terminat, aproape) +22 Aug 2002 - Corectata o eroare la Echo + Se poate schimba efectul de insertie + Gasita o eroare care "crapa" programul daca schimb efectul de le Reverb (rezolvata temporar, dar cu "memory leak") +23 Aug 2002 - Corectata eroarea la Reverb (a fost din cauza ca am pus ">" in loc de ">=" :-p ) + Terminat efectele de insertie(si interfata) + Adaugat Effect cleanup + Scrisa interfata pentru efectele sistem (cu exceptia sendto another sys eff) +24 Aug 2002 - Adaugate doua noi efecte: Chorus si Phaser +25 Aug 2002 - Nu se mai aude tacanit la Chorus daca schimb Delay/Depth + Corectat o mica eroare care facea ca sa nu se afiseze Pinsparts corect + Adaugat un nou efect: AlienWah + Nu se mai aude tacanit la Phaser si la AlienWah la frecvente LFO f. mari +27 Aug 2002 - Adaugata o noua forma de unda: Chirp + Adaugat Waveshaping la OscilGen + Se poate compila si fara UI + Inceput sa scriu Salvarea/Incarcarea Parametrilor +28 Aug 2002 - In ADnoteVoiceListUI se afisaza corect daca vocea este activata/dezactivata + Scrisa Salvarea/Incarcarea parametrilor (cu exceptia la OSCIL::UseAsBaseFunction) + Adaugat File Save/Open +29 Aug 2002 - Se poate salva si oscil::useasbase + Se afiseaza corect valorile dupa incarcare +01 Sep 2002 - Adaugat "codul de intrare" sa saveload 0xfe pt. a sti de unde incepe o noua "ramura" + "Codul de intrare" este folosit pentru a nu incarca "ramurile" care nu se potrivesc cu specificatiile (ex. nr. de voce sau nr. part prea mare) + Adaugat header la fisier + Imbunatatit OscilUI::useasbase +03 Sep 2002 - Modificat codurile de parmetrii: indicele par. sunt >= 0x80, parametrii <0x80 , controlerii speciali(urcare/coborare creanga) >=0xf0; Este util la versiunile viitoare, la forward/reverse compatibility. + Inceput sa scriu clasa Microtonal si interfata pt. Microtonal +04 Sep 2002 - Adaugat Pfilterbypass la salvare (am uitat sa o pun pana acum) + Aproape terminat Microtonal-ul (cu exceptia importului din fisiere .scl) +05 Sep 2002 - Facut cateva mici modificari la Microtonal si Echo + Adaugat un nou parametru la ADnote: PVolumeminus + Adaudat parametrii noi de Detune: Pcoarsedetune(coarse+octave) si Pdetunetype + Adaugat cateva tipuri de detune +06 Sep 2002 - Adaugat posibilitatea de a folosi ADnotepars:Globalpars.Pdetudetype in loc de Pdetunetype (0 = default detunetype), asa ca nu mai trebuie sa mai modific la fiecare voce detunetype: setez la 0 si modific global-ul + Facut mici modificari la MidiInput(OSS) +07 Sep 2002 - Corectata o eroare cu privire la detune si daca freq. > Nyquist + Modificat driver-ul OSSmidiin + Adaugat driver Alsa cu port virtual + Se poate salva doar instrumentele/microtonal. + Adaugata un nou fel de waveshaping(Zigzag) +08 Sep 2002 - Psysefxvol[][] sunt scalate in dB + Nu mai este periculos sa inchid fereastra principala +09 Sep 2002 - Se actualizeaza corect la incarcare la Master:Psysefxvol[][],Pvolume,Pkeyshift; si alti parametrii la Part + Adaugat nume la Part + Panic-ul (Shut-up-ul) se aplica si la efecte + Part->Penable controleaza de fapt daca Part-ul este activat/complet dezactivat. Daca se dezactiveaza un part toate notele+ efectele insertion sunt oprite. Nu mai consuma CPU daca folosesc multe part-uri. + Adaugat un nou parametru la part: Pnoteon care controleaza daca part-ul primeste mesaje NoteOn + Adaugarea extensiei se face automat. + Adaugat LFO exp_up 1 si 2 + Curatat putin de memory leaks (mai am de curatat si interfata) +10 Sep 2002 - Adaugat filtrul HPF cu un pol + Interfata se inchide corect. + Adaugat textul cu Copyright in interfata + Traduse toate comentariile in limba engleza + Adaugat licenta in fiecare fisier +11 Sep 2002 - Adaugat descriere la fiecare fisier + Corectata o eroare care facea ca SUBnote sa aiba amplitudini f. mari la freq. f. inalte + Adaugat cateva macro-uri la interpolarea amplitudinii +12 Sep 2002 - Modificat extensiile (*.mas.zyn ---> *.mas_zyn, la fel si celelalte) pentru a nu aparea fisiere *.mas.mas.zyn +13 Sep 2002 - Am decis numele programului: "ZynAddSubFX" (Zyn de la synthetizer (inlocuit S cu Z), Add de la additive, Sub de la substractive, FX de la effects) +14 Sep 2002 - Volumul din ADvoicelist se afiseaza corect +15 Sep 2002 - Adaugat inca 3 moduri de waveshaping Limiter, UpperLimiter, LowerLimiter +16 Sep 2002 - Adaugat Makefile +17 Sep 2002 - Corectata o mica eroare care facea ca sa nu se incarce fisierele cu data intotdeauna + Nu se amplifica freq. f. inalte daca freq. filtrului este mare. + Inceput sa scriu documentatia. +18 Sep 2002 - Adaugat functia de resetare a tuturor parametrilor(master si instrument) +23 Sep 2002 - Adaugat posibilitatea de a conecta efectele de insertie la iesire Master + Lfo-ul la frecventa incepe de la 0 pt. startphase=0 +24 Sep 2002 - Corectate niste mici erori la Chorus/Phaser + Adaugat si "substract" la Chorus si Phaser + Limitat tipul detune-ului la valoarea maxima +25 Sep 2002 - LANSAT PE INTERNET - PRIMA VERSIUNE (1.0.0) +-------------------------------------------------------------------------------------------------- +01 Dec 2002 - Corectat niste comentarii + - Inlaturat o eroare care facea ca ZynAddSubFX sa crape daca dezactivez un part utilizat + - Inceput sa scriu Rezonanta +02 Dec 2002 - Terminat de scris Rezonante + - Adaugat filtru trecer-banda (BPF) + - Scris Recording +03 Dec 2002 - Adaugat Gain la Resonance + - Adaugat "New Instrument" la meniu +06 Dec 2002 - LANSAT PE INTERNET - VERSIUNEA (1.0.1) +-------------------------------------------------------------------------------------------------- +08 Dec 2002 - Inceput sa scriu Bank si interfata pentru Bank +09 Dec 2002 - adaugat si "make debug" + - Continuat sa scriu Bank/UI; acum se poate folosi (dar nu salva pe HDD) +10 Dec 2002 - Terminat Bank (mai trebuie scris un "config" file pentru a alege automat ultima banka folosita) +11 Dec 2002 - Am mai lucrat ceva la Bank si am adaugat "config file" +12 Dec 2002 - Filtrul BPF suna mai tare + - Nu mai ar trebui sa fie probleme la compilarea FFTwrapper.h (fftw.h) +13 Dec 2002 - LANSAT PE INTERNET - VERSIUNEA (1.0.2) +-------------------------------------------------------------------------------------------------- + - Corectat o eroare care facea ca programul sa crape daca salvam parametrii in timp ce cantam + - LANSAT PE INTERNET - VERSIUNEA (1.0.2-1) - de acasa +-------------------------------------------------------------------------------------------------- +21 Dec 2002 - Corectate mici erori (nu mai dispare "Bypass Global Filter", inlaturat zgomotul de mica amplitudine - cauzat de reverb,nu mai apare intarzierea foarte lunga de la inceput a notelor muzicale daca conectam la aseqview) + - Adaugat filtru de rejectie banda (Notch) + - adugat randomize la Resonance + - Inceput sa scriu VU-meter-ul +22 Dec 2002 - Terminat VU-meter-ul + - Schimbat modul in care efectele de insertie se calculeaza (suna mai tare un pic) + - Adaugata o noua functie la OscilGen +23 Dec 2002 - LANSAT PE INTERNET - VERSIUNEA (1.0.3) +-------------------------------------------------------------------------------------------------- +24 Dec 2002 - Adaugata posibilitatea de a incarca fisiere ".scl" (la Microtonal) +26 Dec 2002 - Adaugata optiunea de a folosi numai OSS-ul (fara ALSA) +27 Dec 2002 - Corectate cateva erori si modificate cateva lucruri marunte la Microtonal +28 Dec 2002 - Mici modificari la Microtonal + - Panic-ul la Reverb functioneaza OK + - Inceput sa scriu Scale Degree Mapping la Microtonal +29 Dec 2002 - Continuat Scale Degree Mapping la Microtonal (dar nu am terminat) +30 Dec 2002 - Corectat lucrul cu ScaleShift-ul + - schimbat modul in care se face keyshift-ul (nu se mai schimba armonia, indiferent de sistem) +31 Dec 2002 - Terminat Mapping-ul la Microtonal(incl. incarcarea/salvarea) + Corectat eroarea care facea ca la Microtonal sa nu se incarce de fiecare data din scl_zyn unele date + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +01 Ian 2003 - Corectata o eroare la Microtonal (erau probleme la InvertKeys daca era folosit key mapping) + - Adaugata un nou tip de waveshaping (Inverse Limiter) +02 Ian 2003 - Adaugat afisaj al acordului fin (cents) + - Butoanele arata f. frumos (am adaugat un nou widget in loc de Fl_Dial) +03 Ian 2003 - Schimbate butoanele (putin) + - Nu se mai aude un tacanit la ShutUp sau AllNotesOff + - Corectat putin waveshaper-quantisize si butoanele + - Inlaturata o eroare care facea ca programul sa crape daca schimbam unii parametri ale efectelor +07 Ian 2003 - LANSAT PE INTERNET - VERSIUNEA (1.0.4) +-------------------------------------------------------------------------------------------------- +08 Ian 2003 - Am inlaturat de-a binelea eroarea (cu efectele - 3 Ian) +11 Ian 2003 - Corectate o mica eroare care facea ca volumul sa fie negativ la ADnote::voice[].PVolume <64 +13 Ian 2003 - Corectata o mica eroare la VU-Meter + - Corectata o mica eroare cu privire la panning la Reverb +15 Ian 2003 - Adaugat min/max keyresponse limits la Part + - Adaugat Filtru si FiltreEnvelope la SubNote +16 Ian 2003 - Curatat codul sursa (ADnote) prin inlaturarea unor variabile + - Durata fadein-ul este aleasa automat (a.i. sa nu rezulte click-uri la notele joase si nici fadein-ul audibil la notele inalte sau cu freqcvente inalte) + - Corectata o mica eroare care faca ca uneori instrumentul sa nu fie salvat/sters la Bank slot-ul cerut + - Imbunatatita putin interfata: La ADnote si SUBnote, butoanele care controleaza amplitudinea armonicelor sunt colorate diferit daca au amplitudinea 0 +17 Ian 2003 - Corectate erori la Chorus si la Phaser care faceau ca sa sune prea 'sec' (din cauza ca wet-ul era la 50% din volum) +18 Ian 2003 - Inceput sa scriu Preset-uri la efecte +19 Ian 2003 - Adaugat Preset-urile la efecte +20 Ian 2003 - Schimbat putin HPF-ul la Reverb +21 Ian 2003 - Adaugat tuning-ul la Reverb (si Freeverb) si Roomsize + - Schimata putin interfata si modificat putin widgetul Pdial +22 Ian 2003 - Amplificat volumul Reverb-ului cu 6 dB + - Buffer-ul foloseste liste simplu-inlantuite, asa ca nu-i mai problema la "configuratii mari" (multe part-uri) +24 Ian 2003 - LANSAT PE INTERNET - VERSIUNEA (1.0.5) +-------------------------------------------------------------------------------------------------- +26 Ian 2003 - Inceput sa scriu GetAudioOutSamples, care ar putea fi apelat in modul callback +27 Ian 2003 - Adaugat o noua fuctie la waveshaping (clip) + - Adaugat suportul pentru Jack (adica programul poate rula in modul call-back ;-) ) + - Inlaturata o eroare care facea ca npart sa fie foarte mare si ca programul sa crape +29 Ian 2003 - Schimbari foarte minore la OscilGen::waveshape (la clip) + - Daca dau "clear" la OscilEdit, butoanele care au amplitudinea zero, sunt colorate corect +30 Ian 2003 - LANSAT PE INTERNET - VERSIUNEA (1.0.6) +-------------------------------------------------------------------------------------------------- +31 Ian 2003 - Inceput sa adaug optiuni la linia de comanda + - Rata de esantionare (SAMPLE_RATE) este setata la rulare si nu la compilare +01 Feb 2003 - Inca 2 variabile sunt setate la rulare (SOUND_BUFFER_SIZE si OSCIL_SIZE) + - Volumul la Part se aplica doar dupa efecte de insertie + - Inceput sa scriu Distorsionarea (fara filtre) +02 Feb 2003 - Schimbate modurile de distorsionare (exp -> asym1 si pow -> pow ( altul ) ) + - Terminat Distorsionarea +03 Feb 2003 - Adaugata inca o functie la waveshape (asym2) + - Inceput sa scriu Controller-ii + - Adaugat controller-i PitchWheel,Expression,Panning,Filter Cutoff, Filter Q, BandWidth, Modulation Wheel + - Panning-ul si volumul sunt interpolate + - Inceput sa scriu un nou program (Controller) care timite mesaje midi (controller) catre un port ALSA + - Panning-ul la Part se aplica doar dupa efecte de insertie + - Panning-ul la efecte se aplica inainte de procesare +04 Feb 2003 - Adaugat posibilitatea de a seta intensitatea/dezactiva la controlleri(incl. UI) + - Adaugat controler-ul FMmodulationAmplitude + - Corectat o eroare la Buffer (care facea ca Buffer-ul sa nu se reseteze :-P ) +05 Feb 2003 - Corectata o eroare care facea ca programul sa consume mult din procesor (denormalisation) + - Nu mai este permisa o valoare a lui OSCIL_SIZE care sa nu fie putere a lui 2 (este ajustata automat) + - Adaugat controller-i Volume si Sustain Pedal, AllNotesOff, AllSoundOff, ResetAllControllers + - Adaugat NRPN, adica toti parametrii efectelor pot fi controlati prin controlleri +06 Feb 2003 - Pus limite la parametrii efectelor a.i. sa nu se seteze (datorita controllerilor) la valori nevalide + - Inlaturata o mica eroare la controller-ul BandWidth + - Schimbat putin EffectLFO::updateparams + - Controler-ul BandWidth afecteaza doar FineDetune-ul + - Schimbat putin identificare controlerilor si adaugat controlleri la OSS + - Schimbat putin interfata utilizator la controlleri +07 Feb 2003 - LANSAT PE INTERNET - VERSIUNEA (1.0.7) +-------------------------------------------------------------------------------------------------- +08 Feb 2003 - Adaugat modul "mono"(monofonic) la part + - Inceput sa scriu portamento-ul +09 Feb 2003 - Terminat portamento-ul +10 Feb 2003 - Inceput sa scriu Equaliser-ul + - Inlaturata o eroare care facea ca la parametrii efectelor care sunt 0 sa nu fie incarcati +11 Feb 2003 - Terminat Equaliser-ul (adica adaugat vizualizator freq response) + - Corectata o mica eroare care facea ca part-ul 0 sa fie activ chiar daca cel salvat era inactiv +12 Feb 2003 - Mici modificari la EQ (UI) + - Adaugata posibilitatea de swap (stanga <--> dreapta) + - Adaugat Q la filtrele shelf +13 Feb 2003 - Adaugat inca un parametru la Phaser (phase) + - Curatit putin codul sursa la efecte + - Adaugat system effect send to next systems effects +14 Feb 2003 - LANSAT PE INTERNET - VERSIUNEA (1.0.8) +-------------------------------------------------------------------------------------------------- + - cateva mici modificari (de la un patch primit de pe Internet) + - adaugat keylimit la Part (si first note priority) +15 Feb 2003 - Corectata o foarte mica eroare la Part +16 Feb 2003 - Se poate aplica filtrul inainte de distorsion + - Adaugat filter stages (adica filtrul se poate aplica de mai multe ori) +17 Feb 2003 - Corectata o mica eroare la Reverb si modificat putin filter-ul si UI +18 Feb 2003 - Corectata o eroare care facea ca semnalul la voice sa fie intre [-4.0..4.0] si sa faca probleme la RingModulation + - Adaugat modul Noise la ADsynth(voice) pentru a putea produce si tobe + - Adaugat parametrul fixed frequency la 440Hz +19 Feb 2003 - Corectata o mica eroare la ADnote (aparea un fadein nedorit) + - Facute inca cateva mici modificari la ADnoteUI +20 Feb 2003 - Imbunatatit foarte mult Controller-ul si adaugat la ZynAddSubFX ca program extern + - Modificat putin Waveshaper-ul (fct. L/U limit) + - Corectata o eroare la SUBnote (care facea probleme la glissando) + - Adaugat un nou parametru Punch la ADnote care face ca sa sune ca si cum ar fi o lovitura (f. util la Rhodes) +21 Feb 2003 - Adaugata inca o functie de distorsionare x(1-x) +23 Feb 2003 - Corectata o eroare (cu mutex) care facea ca sunetul sa fie extrem de tare, daca in timp ce cantam, modificam unii parametrii de sunet la ADnote +24 Feb 2003 - LANSAT PE INTERNET - VERSIUNEA (1.0.9) +-------------------------------------------------------------------------------------------------- + - Adaugata posibilitatea de a tipari notele si timpul in care au fost produse (optiunea -D) +26 Feb 2003 - Adaugat inca 2 controlleri (Resonance Center Freq. (relative) si Resonace Bandwidth(relative)) +27 Feb 2003 - Adaugata posibilitatea de a modifica parametrii (in mod direct) al oscilatorului extern +07 Mar 2003 - Portat partial(doar interfata) programul sub Windows +08 Mar 2003 - Adaugat Virtual Keyboard + - Cateva mici modificari in vederea portarii pt. windows + - Adaugat si controller la Virtual Keyboard +09 Mar 2003 - Adaugat pitch wheel la Virtual Keyboard si modificat putin controller-ul la VK +10 Mar 2003 - Adaugat Filter Frequency Tracking (adica modificarea frecventei filtrului in functie de frecventa notei) + - Marite eficienta la LFOparams - update lfotime + - Adaugat mod de normalize prin RMS + - Corectate doua erori la Distorsion (negate si mono+prefiltering) +11 Mar 2003 - In Windows, nu mai este necesar functiile getopt (scrisa o functie proprie) +12 Mar 2003 - Adaugat filtru la OscilGen +13 Mar 2003 - Adaugat mai multe filtre la OscilGen + - Facute optimzari la ADnote (adaugarea unui element la oscilsmp si fmsmp,etc.) si curatat putin codul sursa + - Corectata o eroare care amplifica fm-ul la rate de esantionare inalte + - Optimizat si curatat reverb-ul +16 Mar 2003 - Modificate optiunile de compilare in Makefile.inc si coduri sursa a.i. sa se realizeze portarea pe windows mai usor +17 Mar 2003 - Inregistrarea se face in formatul WAV si nu RAW + - Adaugat trigger la recorder (se incepe inregistrarea doar cand este apasata o nota) + - Adaugat interfata PortAudio + - Corectata eroarea care facea ca UI sa nu ruleze pt. Windows (trebuia dat show() la UI in thread-ul 3) si corectate alte erori din windows + - Si audio-ul functioneaza sub Windows + - Corectata o eroare care se manifesta foarte rar(Resonance, i era de la 0 si nu de la 1) +18 Mar 2003 - Adaugat interpolare la filtru (nu se mai aud tacanaituri, daca frecventa filtrului se schimba foarte rapid si semnalul contine putine armonice) + - Adaugat interfata Midi in Windows => consider ca programul este portat in Windows +19 Mar 2003 - Adaugat interfata de configurare + - Corectata o eroare la OscilGen care facea ca in loc ca amplitudinile sa fie reduse la -40,..,-100dB, sa fie setate la 1 si unde era intensitate mare sa file amplificate +20 Mar 2003 - Corectata o mica eroare la interfata (uneori disparea butonul ON de la ADvoice) +21 Mar 2003 - LANSAT PE INTERNET - VERSIUNEA (1.2.0) +-------------------------------------------------------------------------------------------------- + - Se interpoleaza filtrul si cand se trece peste pragul Nyquist (in sus sau in jos) +22 Mar 2003 - Corectata o eroare care facea ca nr. de esantioane scrise in headerul fisierului WAV sa nu fie initializat +26 Mar 2003 - Nu mai este permisa alegerea unui fisier wav in timpul pauzei de la record + - Gasita si corectata o eroare stupida (am pus la NRPN 0x98 in loc de 98 zecimal) +28 Mar 2003 - Inceput sa portez programul sub VST +29 Mar 2003 - Adaugat Master fine detune (-64.0 .. 63.0 cents) +01 Apr 2003 - Functioneaza portarea sub VST, dar mai este de lucru... +02 Apr 2003 - Modificat synth-ul a.i. sa se poate apela in mai multe instante in VST + - Continuata portarea in VST +03 Apr 2003 - Continuata portarea in VST (este limitat la o singura instanta) +05 Apr 2003 - Adaugata posibilitatea de a interschimba/copia parametrii efectelor + - Mici modificari la Makefile (ignora headerele inexistente la deps) +06 Apr 2003 - Adaugat posibilitatea de protectie impotriva atenuarii a notei fundamentale la rezonanta + - Pitch bend-ul merge bine in Windows +07 Apr 2003 - LANSAT PE INTERNET - VERSIUNEA (1.2.1) +-------------------------------------------------------------------------------------------------- + - Adaugat efect la part (adica efect care face parte din instrument ;-) ) +08 Apr 2003 - Adaugata interpolare la Resonance (peak-urile le interpoleaza) +09 Apr 2003 - Interfata la Envelope este o singura clasa + - Adaugat Envelope free mode (adica de orice forma) + - Adaugata posibilitatea de a copia de la o voce la alta la ADnote + - Release-ul este liniar (in loc de dB) +10 Apr 2003 - Adaugata afisarea ultimului fisier master salvat/incarcat + - Adaugata setarea notei minime/maxime la ultima nota + - Pot alege daca release-ul sa fie liniar + - Facute cateva corecturi la envelope +11 Apr 2003 - Curatat codul sursa la UI si impartit in mai multe fisiere .fl + - Corectate niste erori la Envelope si adaugat modul liniar/logaritmic la amplitudine +12 Apr 2003 - Inceput sa scriu kit-ul la part +13 Apr 2003 - Terminat de scris kit-ul la part+UI +14 Apr 2003 - Copierea vocilor este sub forma de clipboard + - ADsyn su SUBsyn check-urile de la PartUI sunt actualizate +15 Apr 2003 - LANSAT PE INTERNET - VERSIUNEA (1.4.0) +-------------------------------------------------------------------------------------------------- +16 Apr 2003 - Adaugat modul "Single" la instrument kit, care face ca sa sune doar primul instrument din kit disponibil +21 Apr 2003 - Adaugat realtime priority, care seteaza prioritatea mare la sintetizator, daca are posibilitate; merge numai pe Linux + - Gasite multe erori mici(dar potential periculoase) cu ajutorul programului Valgrind +30 Apr 2003 - Adaugat "Spectrum adjust" la OscilGen, care ajusteaza intensitatile armonicelor +03 Mai 2003 - Normalizat spectrul inaintea adjust-ului la OscilGen +04 Mai 2003 - Adaugat mod "egal temperat" la fixed frequency (440Hz), util la tobe +05 Mai 2003 - Adaugat modul "Drum mode", unde sistemul este intotdeauna temperat (12tET), toate notele sunt mapate si transpose-ul este ignorat +08 Mai 2003 - LANSAT PE INTERNET - VERSIUNEA (1.4.1) +-------------------------------------------------------------------------------------------------- +09 Iun 2003 - Am schimbat in .H in fisierele .fl (ca sa se poate compila si pe Debian) +10 Iun 2003 - Inceput sa modific interfata la filtru a.i. sa pot adauga filtrul formantic usor + - Interfata pentru filtru este o singura clasa +12 Iun 2003 - Inceput sa scriu panoul de part-uri (care afiseaza parametrii importanti ale part-urilor) + - VU-meter-ul poate afisa si intensitatea part-ului dorit (folosit la panou de part-uri) +13 Iun 2003 - Terminat panoul de part-uri + - Adaugat posibilitatea de a inchide automat fereastra bancii de instrumente, cand se incarca un instrument +19 Iun 2003 - Modificat modul cum se calculeaza frecventa filtrului (se fac doar adunari si doar la urma se ridica la putere) +22 Iun 2003 - Aproape terminat filtrul formantic (fara UI) +24 Iun 2003 - Merge mai multe instante in jack (alege porturi diferite) +26 Iun 2003 - Continuat de scris filtrul formantic +29 Iun 2003 - Adaugat vu-meter fals la Panel (in caz ca partul este dezactivat si primeste note on). De asemenea se arata daca in partul dezactivat s-a cantat ceva (apare o liniuta). +09 Iul 2003 - Inceput sa scriu interfata pentru filtrul formantic +10 Iul 2003 - Continuat filtrul formantic (interfata) +11 Iul 2003 - Eroarea vine de la Makefile pt. ca nu recompileaza si clasele care folosesc o anumita clasa, daca aceasta din urma se schimba + - Continuat filtrul formantic (interfata+adaugarea interpolarii la Q) +12 Iul 2003 - Adaugat la filtrul formantic setarile de amplitudine formanti si interpolarea acestora + - Adaugat grafic la UI-ul filtrului formantic si alti paramatrii la filtrul formantic +13 Iul 2003 - Corectata eroarea la FormantFilter care facea ca sa nu se interpoleze intre vocale + - Adaugat parametrul VowelClearness la FormantFilter care face ca sa se evite vocalele mixte +14 Iul 2003 - Inlaturat parametrul Psequence[].pos, pt. ca era confuz => fiecare vocala are zona egala + - Adaugat parametrii Psequencestretch si Psequencereversed la FormantFilter + - Adaugat parametrul Pgain la filtru (-30...30 dB) + - Terminat de scris Filtrul Formantic + - Corectata o eroare care facea ca sa nu se salveze oscilatorul la o ADnote_voce, daca vocea este dezactivata, chiar daca era folosita de o alta voce + - Prima data se cauta fisierul "default.bnk_zyn" si in dir "/usr/share/zynaddsubfx" sau "/usr/local/share/zynaddsubfx" +15 Iul 2003 - Setat Pkeylimit prestabilit la 15 la Part + - Activarea unui Part din interfata Panel schimba automat part-ul curent la acela + - Se poate alege ca un instrument din Kit sa fie procesat incepand cu un anumit efect; si se mai poate alege ca un efect din Part sa fie trimis in afara +17 Iul 2003 - LANSAT PE INTERNET - VERSIUNEA (1.4.2) +-------------------------------------------------------------------------------------------------- +21 Iul 2003 - Corectata o eroare la FilterUI care facea ca la fiecare afisare sa se initializeze FilterParames::Pgain la 64 +25 Iul 2003 - Corectata o eroare care facea ca modulatia in faza/frecventa sa sune diferit la diferite rate de esantionare/oscilsize +26 Iul 2003 - Afisat corect - valoarea OSCIL_SIZE ajustata (in caz ca a fost data optiunea "-o" incorect) + - In windows arata si numele la midi_in_device +04 Aug 2003 - Adaugat filtrele Peak,LowShelf,HighSelf la filtru si foloseste parametrul Gain de la interfata filtrelor +30 Aug 2003 - Adaugat un nou tip de filtru: State Variable Filter +31 Aug 2003 - LANSAT PE INTERNET - VERSIUNEA (1.4.3) +-------------------------------------------------------------------------------------------------- +02 Sep 2003 - Adaugata posibilitatea de a incarca de la inceput un fisier .mas_zyn "-l" + - Se poate lansa programul fara interfata utilizator ("-U") +17 Sep 2003 - Adaugat niste simple patch-uri de Frank Neumann +02 Oct 2003 - Corectata o eroare la SUBsynth care facea ca la freq inalte si Q foarte mici sa se produca filtre instabile +30 Oct 2003 - Adaugate posibilitatea (+interfata in config) de Dump (avansat) + - Adaugat ModWheel liniar si facut prestabilit (si posibilitatea de a alege in interfata modul de modwheel) +04 Nov 2003 - Modificat putin interfata la ResonanceUI +05 Nov 2003 - Marita viteza prin inlocuirea de (int) cu cod de asamblare (cu.10-50% la FM,chorus,etc.) +10 Nov 2003 - Inceput sa adaug posibilitatea de a adauga comentarii la instrumente +11 Nov 2003 - Terminat de adaugat comentariile/autor/tipuri la instrumente +12 Nov 2003 - Adaugat intefata pentru FFTW3 la fftwrapper +18 Nov 2003 - Inceput sa scriu Sequencer-ul +19 Nov 2003 - Adaugat un buton "i" pt. instrument info si facut ca instrument info sa se afiseze automat daca se schimba partul (sau se incarca instrumente,etc) +20 Nov 2003 - Continuat de scris Sequencer-ul si inceput sa ii scriu interfata + - Mici modificari la preset-urile de la Echo +26 Nov 2003 - Continuat de scris sequencerul - inceput sa scriu inregistrarea (fara timer) +27 Nov 2003 - Se poate inregistra (dar nu rula) - adaugat timerul de inregistrat + - Frecventa maxima al filtrelor este de Nyquist-500.0 pentru a evita instabilitatea filtrelor +28 Nov 2003 - Adaugata favorizarea portamento-ului in sus sau un jos; ex. se poate face ca portamento-ul sa fie doar in sus, sau portamento-ul in jos sa fie mai scurt decat cel in jos + - Inceput sa pun pe cvs la cvs.sourceforge.net +01 Dec 2003 - Am facut niste mici modificari ca urmare a unui bug-report +05 Dec 2003 - Facute cateva modificari la jack +08 Dec 2003 - Inceput sa incerc sa fac rt-safe sub jack, dar in stadiul actual suportul jack este nefunctional +11 Dec 2003 - Adaugat aleatorism la amplitudinile armonicelor +13 Dec 2003 - Adaugat LFO frequency randomness +14 Dec 2003 - Imbunatatit LFO frequency randomness +15 Dec 2003 - Corectata o mica eroare la ADnoteParameters (lipseau niste break-uri la salvarea/incarcarea parametrilor) +16 Dec 2003 - Eroarea cu break-urile se dovedeste a fi o eroare majora :( ; adica corectarea ei, necesita resalvarea tuturor instrumentelor + - Am revenit la suportul vechi de JACK, dar cel nou este disponiblil ca JACK_RT (nefunctional inca) +17 Dec 2003 - Inceput sa restucturez Part-ul (am adaugat clasele Instrument,InstrumentParams) - programul nu mai este compatibil cu versiunile anterioare + - RMS normalize este prestabilit la OscilGen +18 Dec 2003 - Continuat de restructurat Part-ul + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +01 Feb 2004 - Revenit la versiunea din 16 Dec. 2003 + - Pus iarasi RMS normalize prestabilit la OscilGen + - M-am razgandit ;) nu mai restructurez part-ul; mai bine pun acolo o functie separata pentru salvari/incarcari par instrumente + - Inceput sa adaug suportul XML +02 Feb 2004 - Corectata o eroare care facea ca numele la instrumentele din bank sa fie aratate gresit (nu era pus un \0 ) + - Continuat suportul de XML +03 Feb 2004 - Continuat de scris suportul XML - inceput sa salvezi cativa parametrii +04 Feb 2004 - Se salveaza parametrii XML la master, part, filter, lfo, envelope, resonance si adnote (partial) +05 Feb 2004 - Se salveaza toti parametrii in XML +06 Feb 2004 - Adaugat salvarea de instrument in XML + - Adaugat export la bank intr-un director XML si decis ca bank-ul sa fie un director cu mai multe fisiere xml de forma XXXX-nume.xml sau XXXX-nume.xml.gz +07 Feb 2004 - Adaugat functii de initializare si renuntat la masterdefaultbuf si instrumentdefaultbuf (adica salvarea la inceput si incarcarea bufferelor cu instrumentele prestabilite) + - Corectata o mica eroare care facea ca sa se incarce subnotepars la adnotepars (eroarea a aparut azi) +08 Feb 2004 - Modificat putin formatul XML +10 Feb 2004 - Adaugata salvarea parametrilor pt. basefunction la OscilGen + - Inceput sa scriu incarcarea parametrilor +11 Feb 2004 - Se pot incarca cativa parametrii de la master +12 Feb 2004 - Continuat incarcarea parametrilor XML si la part (neterminat) +13 Feb 2004 - Terminat de adaugat parametrii la incarcarea XML +14 Feb 2004 - Se poate incarca si instrumente + - Rezolvata o problema la coarse detune + - Corectate cateva erori la incarcarea XML-ului + - Frecventa LFO-ul de la instrumente are valoare reala intre 0..1 + - Corectata eroarea care facea ca functia de rezonanta sa fie trasata incorect + - Adaugata compresie gzip la fisiere si decompresie automata la incarcare (folosesc biblioteca zlib) +15 Feb 2004 - O mica modificare la envelope parameters in sensul ca envelope-ul prestabilit la FM nu mai este liniar +22 Feb 2004 - Adaugat normalize Full RMS la Oscil +23 Feb 2004 - Inceput sa fac ca sa pot adauga Bank bazat pe XML (adaugat temporar clasa OldBank) +24 Feb 2004 - Continuat la Bank +25 Feb 2004 - La Bank - inceput sa scriu partea ca sa arate instrumentele din banca +26 Feb 2004 - Continuat la Bank +27 Feb 2004 - Corectate erori la snprintf (nu dadeam parametru "%s" inainte de string si daca acel string continea ceva %, era periculos) si alte erori + - Micsorat timpul de marire amplitudine la ADnote (doar in cazul cand amplitudinea creste brusc ca la un LFO expdown) + - Corectata o eroare foarte veche la LFO amplitudine (amplitudinea nu scadea corespunzator) +28 Feb 2004 - Se poate incarca si salva instrumente in Bank +01 Mar 2004 - Se pot schimba bancile de instrumente + - Introduse si celelalte functii la Bank (cu exceptia salvarii/incarcarii locului bancii folosite) + - Se poate importa banci din bnk_zyn +03 Mar 2004 - Terminat (teoretic) partea de XML +05 Mar 2004 - Actualizat Copyright-ul la 2004 in fisiere +08 Mar 2004 - Corectat o mica eroare la OscilGen (se aplica gain-ul rezonantei incorect) +09 Mar 2004 - Adaugata posibilitatea de stretch la LFO in functie de frecventa notei +12 Mar 2004 - Adaugata modulatie la OscilGen (functia de baza) +13 Mar 2004 - Adaugat HarmonicShift la oscilgen +15 Mar 2004 - Inceput sa scriu partea de incarcare MIDI + - Inlaturata partea de recording din Sequencer +16 Mar 2004 - Inceput sa scriu partea de analiza midi +25 Mar 2004 - Continuat partea de analiza midi +28 Mar 2004 - Scris partea de incarcat fisier midi + - Merge partial playerul +26 Mai 2004 - Playerul merge bine cu un canal midi (rezolvata problema cu timing-ul) +03 Iun 2004 - Adaugata partea de play speed la interfata +06 Iun 2004 - Adaugata functia sigmoid la distorsionare +12 Iun 2004 - Modificat modul cum este realizat bank-urile, adica directoarele de bank-uri exista in anumite directoare si aceste directoare sunt cautate automat de bankuri; adaugat optiunea de a se folosi mai multe bank-uri +13 Iun 2004 - Adaugat filtrul "sinus" la OscilGen + - Managementul bancilor de instrumente este complet + - Se cauta bancile si in '/usr/share/zynaddsubfx/banks' si '/usr/local/share/zynaddsubfx/banks' + - Corectata o eroare la filter la OscilGen care filtra diferit componentele sin si cos + - Adaugat posibilitatea de swap la instrumentele din bank +14 Iun 2004 - Adaugat __DATE__ si __TIME__ sa stiu cand s-a compilat + - Modificat interfata la PartUI + - Imbunatatit modulatia basefunc la OscilGen (adaugat inca un parametru si inca un tip de modulatie ("power")) + - Adaugat inca o noua functie basefunc la OscilGen (sqr=atan(sin(x)*a)) +15 Iun 2004 - Adaugat posibilitatea de a face armonicele ca sa depinda de frecventa ("adaptive") si rezultatul suna foarte frumos pentru ca tendinta este de pastrare a frecventelor armonicelor si nu a numarului de ordine al lor +16 Iun 2004 - Inceput sa trec configul pe XML +17 Iun 2004 - Adaugat tipul threshUp la spectrum adjust + - Terminat de trecut config-ul pe XML (inclusiv setarile bancilor de instrumente) +18 Iun 2004 - Incercata interpolarea cubica dar am vazut ca nu merita pentru ca OSCIL_SIZE e suficient de mare si pentru o interpolare liniara + - Separat OscilGenUI din ADnoteUI + - Inceput sa scriu modulul de sinteza PADnote +19 Iun 2004 - Adaugat modul liniar de controller bandwidth si modificat modul liniar la controllerul modulation wheel + - Adaugata modulatia in frecventa la OsciGen +20 Iun 2004 - Nu se mai deschide automat fereastra de instrumente daca a fost deschisa si s-a descarcat un instrument + - Facute mici modificari la FM-ul de la Oscil +21 Iun 2004 - Inceput sa scriu conversia in sinus +22 Iun 2004 - Continuat conversia in sinus si facut teste pentru posibilitatea de "draw" cu sliderele +23 Iun 2004 - Modificat modul in care parametrii se afiseaza la OscilGen (este o functie "refresh" care face asta) + - Adaugata posibilitatea de draw la armonicele OscilGen daca se apasa tasta Shift + - Corectata o mica eroare care facea imposibila modificarea amplitudinii armonicelor cu tastatura + - Adaugat randomness de grup (adica se aplica acelasi randomness la toate vocile care folosesc acelasi oscilator) +24 Iun 2004 - Inlaturata setara de normalize la OscilGen. Intotdeauna normalize este Full RMS + - Facute cateva imbunatatiri la interfata unde sunt inlocuite comuter-urile cu setari mai usor de inteles de catre utilizator (ex. la efectele de insertie se arata "insert to Master Out" in loc de "-2") +29 Iun 2004 - Inlaturata setarea cu gain la Resonance pentru ca este inutil (datorita faptului ca normalize este Full RMS intotdeauna) +30 Iun 2004 - Inlaturata o eroare recenta la EffectUI si modificat EffectUI in sensul ca nu trebuie sters si reinstantiat pentru a se reincarca valorile curente de efecte + - Inceput sa scriu un nou efect (DynamicFilter) +01 Iul 2004 - Corectata o mica eroare la EffectUI care facea ca efectele sa nu apara activate + - Continuat de scris la DynamicFiter (mai este doar de salvat parametrii si de auto-update la filtru) +02 Iul 2004 - Continuat la DynamicFilter (adaugata auto-update, adaugat preset-uri) + - Terminat DynamicFilter + - Corectata o eroare la EQui care facea ca sa nu se actualizeze efectul curent si sa nu se obtina graficul egalizatorului +03 Iul 2004 - Corectata o mica eroare care nu activa la EffectUI daca efectul anterior era dezactivat + - Actualizat Swap/Copy la efecte ca sa proceseze si parametrii la filtre + - Adaugat Bypass la efectele de instrument + - Imbunatatit interfata utilizator (eliminate setarile "-1",etc.) + - Scris calcularea profilului la PADsynth + - Adaugat OscilGen si Resonance la PADsynth si inceput sa scriu interfata utilizator la PADsynth +04 Iul 2004 - Adaugata calcularea automata a largimii de banda echivalente si afisarea ei + - Inceput sa scriu partea de sinteza la PADsynth + - Auzit primul sunet la PADsynth +05 Iul 2004 - Nu mai face urat daca schimb parametrii in timp ce cant si apas apply + - Adaugat harmonic scale si position la PADsynth + - Se calculeaza corect si armonicele cu largime de banda mare +06 Iul 2004 - Inceput sa adaug filtre,lfo,envelopes,etc. la PADsynth +07 Iul 2004 - Corectate cateva mici erori si adaugat autoscale + - Modificata putin interfata de la filtru + - Adaugata interfata si parametrii la LFOs,Envelopes,Filter la PADsynth + - Adaugata fereastra care arata pozitiile armonicelor si continuat de lucru la acestea +08 Iul 2004 - La pozitiile armonicelor sunt aratate si valorile lor reale in dB + - Alte adaugiri minore la PADsynth + - Adaugat interpolare cubica la PADsynth +09 Iul 2004 - Modificat modul cum se calculeaza profilul armonicelor la PADsynth (nu se mai ridica la patrat) + - Corectate cateva erori la PADsynth + - Modific amplitudinea in functie de sqrt(largime de banda) => amplitudinile armonicelor sunt echivalente cu oscil +11 Iul 2004 - Acum nu se mai intrerupe sunetul la notele care canta in timp ce sunt aplicate modificarile la parametrii + - Se poate alege marimea sample-lui + - Adaugat multisampling la PADsynth + - Cand se incarca parametrii ADsynth se da volumul ceva mai incet ca sa corecteze faptul ca normalize-ul este doar RMS +12 Iul 2004 - Inlocuit codul de D/W sau Volume de la efecte cu un cod unic in EffectMgr + - Se poate face efecte la instrumente la care doar semnalul Wet e procesat de efectele urmatoare + - Modificat modul cum se calculeaza intensitatea Wet la Reverb si Echo + - Corectata eroarea la FM care facea ca daca Adaptive Harmonics!=0 sa se calculeze FM-ul gresit +13 Iul 2004 - Rezonanta la PADsynth se face in functie de armonica reala si nu de numarul de ordine al armonicei + - LFO,Envelope, Filters, etc. merg la PADnote + - Inceput sa fac partea de aratare ca parametrii au fost schimbati (butonul "Apply" se coloreaza in rosu) +14 Iul 2004 - Butonul Apply la PADsynth se coloreaza in rosu cand se modifica ceva + - Adaugat fixed freq. la PADsynth + - Sunt salvati si parametrii PADsynth => consider in mod oficial ca PADsynth este complet +15 Iul 2004 - Facuta o modificare la PADnoteUI care arata foarte frumos + - Completata partea de save/load si stabilite noile extensii ale fisierelor: master - .XMZ, instrument - .XIZ, microtonal - .XSZ + - Inlocuit memset cu un macro (ZERO) pentru ca memset nu seteaza toate valorile ci uneori doar prima valoare cu 0 (e o optimizare la gcc care face asta) + - Corectate niste erori la makefile care aveau legatura cu compilarea in windows + - Corectate 2 erori referitor la Banci de instrumente +16 Iul 2004 - Adaugat inca noi tipuri de harmonic bandwidth scale + - Adaugat inca un parametru la filter la OscilGen si inca un nou tip de filtru +17 Iul 2004 - Corectata o eroare care facea sa crape programul uneori dupa ce scria instrumentul in banca + - Modificata optiunea -l ca sa incarce un .xmz + - LANSAT PE INTERNET - VERSIUNEA (2.0.0pre1) +-------------------------------------------------------------------------------------------------- +18 Iul 2004 - Corectata o mica eroare la afisare care facea ca la PADnoteUI sa fie trasate liniile in mod gresit +19 Iul 2004 - Corectata doua mici erori (se incarca gresit parametrii filtrului de la OscilGen) + - Corectata inca o mica eroare care facea ca sa nu se coloreze butonul PAD_Synth Apply in rosu la anumiti parametrii de la oscilgen + - Se dezactiveaza butoanele Edit de la PartUI ca sa nu se poata edita module de sinteza inactive +20 Iul 2004 - Corectate cateva erori cu compilare pe windows +21 Iul 2004 - Corectata o mica eroare la Bank si alte erori +26 Iul 2004 - Acum este folosita biblioteca mxml-2 + - Corectata o eroare care facea ca sample-ul la PADnote sa nu fie ales in functie de frecventa reala de baza (cu detune) + - Mutat functiile de waveshaping in Distorsion.C/.h +27 Iul 2004 - Corectata o eroare foarte suparatoare care bloca uneori calculatorul + - Adaugat inca un nou parametru la PADsynth la base function + - Nu se mai arata butonul de apply parameters la PADsynth cand nu este necesar + - Eliminate blocarile de cateva secunde din threadul de sunet in momentul cand se incarca un nou instrument care contine parametrii PADsynth + - Adaugata schimbarea titlului ferestrei principale la load XML +29 Iul 2004 - Modificat modul cum este stocat lista de banci root dir + - Gasita o eroare care facea ca sa se stearga denormalkillbuffer inaintea lui master +30 Iul 2004 - Gasite si corectate o gramada de erori (eu stergeam elemente din ferestre si fltk le stergea din nou) + - Eliminate warning-urile pentru -Wall +31 Iul 2004 - Eliminate complet stergerile in plus de la UI din destructorele obiecte + - LANSAT PE INTERNET - VERSIUNEA (2.0.0pre2) +-------------------------------------------------------------------------------------------------- +01 Aug 2004 - Adaugat un nou tip de OvertonesPosition la PADsynth +02 Aug 2004 - Am pus din nou schimbarea schedule-ului la valoare corecta (l-am scos dintr-o greseala) +04 Aug 2004 - Am corectat niste erori la VST + - Merge VST, dar nu intotdeauna stabil (merge stabil pe vsthost.exe) + - Corectata eroare care facea ca sa nu mearga MIDI + - LANSAT PE INTERNET - VERSIUNEA (2.0.0pre2 VST) +-------------------------------------------------------------------------------------------------- +13 Aug 2004 - Inceput sa scriu modurile continous si discrete la PADnote +14 Aug 2004 - Terminat modul continous la PADnote + - Corectata o mica eroare la OscilGen care facea daca adaptive harmonics e activ si phase randomness>0 sa rezulte si aleatorism in amplitudinile armonicelor + - Inceput sa scriu Presets/Clipboard (Clipboardul, in stadiu actual va putea copia doar parametrii folositi si nu cei dezactivati) + - Merge partial partea de Copy in clipboard +15 Aug 2004 - Corectata o eroare in main.c la pitch bend + - Scos Swap/Copy la efecte si la PartUI si vechiul Copy/Paste de la ADnote voice + - Merge clipboardul la Oscil, Resonance, Filter si partial la ADsynth,SUBSynth si PADsynth +16 Aug 2004 - Corectata inca o eroare la pitch bend (aratata de Krzysztof Korpiela) + - Adaugat refresh si la Filtru si paste la ADnote, SUBnote si PADsynth sunt complete +17 Aug 2004 - Adaugat clipboard la LFO, Envelope, ADnoteVoice si Filter Vowel +18 Aug 2004 - In clipboard se salveaza toti parametrii (chiar si cei dezactivati) + - Corectata o eroare care facea ca instrumentul sa fie incarcat la fiecare salvare in banca + - Tipurile de lfo sunt compatibile intre ele la clipboard +19 Aug 2004 - Corectata o mica eroare la XMLwrapper care facea ca sa se salveze fortat toti parametrii (chiar si cei nefolositi) + - Adaugata partea de salvare/incarcare a listei directoarelor unde se afla presetarile +21 Aug 2004 - Am lucrat putin la salvare/incarcare a listei dir. cu presetari +22 Aug 2004 - Corectata o eroare de compilare + - Makefile-ul modificat, a.i. make-ul sa se opreasca in caz de eroare + - Terminat managerul de preset-uri +23 Aug 2004 - Adaugata posibilitatea de a se vedea direct din lista cu bancile de instrumente +24 Aug 2004 - Inlaturat complet suportul pentru formatele *.mas_zyn, *.ins_zyn, *.bnk_zyn si *.scl_zyn + - Ascuns Sequencer-ul de utilizator (o sa il continui mai incolo) +25 Aug 2004 - Listele de banci si de preset-uri sunt sortate + - Corectate niste erori la Oscilgen care faceau ca sa se calculeze randomness chiar daca este folosit de PADsynth si pus automat parametrul randomness daca PADsynth este folosit (in caz ca se va importa la un ADsynth) + - Gasita o eroare care face sa crape daca lucrez mult cu bancile de instrumente +27 Aug 2004 - Adaugata posibilitatea de a dezactiva aratarea starii PADsynth din instrumente + - LANSAT PE INTERNET - VERSIUNEA (2.0.0) +-------------------------------------------------------------------------------------------------- +05 Sep 2004 - Corectata o mica eroare de la SUBnote (legat de pitch wheel) +06 Sep 2004 - Eliminata variabila "disablekitloading" din Part si din UI +07 Sep 2004 - Modificat id-ul vst in 'zasf' (inainte era de 5 litere si poate cauza un crash la host) +27 Sep 2004 - Corectat un mic bug la salvare in xml la parametrul FMcoarseDetune din adnote + - La VST, daca incerc sa inchid fereastra principala, se minimizeaza + - Eliminate setarile cu indice '0' (zero) +28 Sep 2004 - Adaugata salvarea tuturor parametrilor in hostul VST (trebuie testat) + - Adaugat installer pt. windows (cu NSIS) +29 Sep 2004 - Inceput sa scriu interfata utilizator pt. incepatori +30 Sep 2004 - Terminat de scris interfata utilizator pt. incepatori si se selecteaza la pornire modul dorit + - Adaugata posibilitatea de a compila cu suport jack si oss simultan si sa se aleaga runtime ce doresc (jack/oss) +01 Oct 2004 - Corectata o mica eroare care facea ca sa nu se inchida ferestrele cu instrumente cand incarc din banca + - LANSAT PE INTERNET - VERSIUNEA (2.1.0) +-------------------------------------------------------------------------------------------------- +02 Oct 2004 - Corectata o eroare grava care facea ca sa nu pot schimba partul curent in interfata utilizator obisnuita +03 Oct 2004 - LANSAT PE INTERNET - VERSIUNEA (2.1.1) +-------------------------------------------------------------------------------------------------- +04 Oct 2004 - Corectata o eroare care face ca in modul simple UI, sa se inverseze panning-ul + - Adaugat un icon la ZynAddSubFX +10 Oct 2004 - Si controllerul de Resonance se aplica la toate item-urile din kit +12 Oct 2004 - Corectata o eroare care facea ca butoanele Addpoint si Delpoint de la Envelope sa nu fie afisate +16 Oct 2004 - Corectata o eroare care facea ca partUI-ul sa nu se actualizeze intotdeauna cand incarcam un instrument +20 Oct 2004 - Corectata o mica eroare asemanetoare cu cea din 16 Oct, dar care afecta meniul new +07 Nov 2004 - Corectata o mica eroare care facea ca sa nu se incarce corect instrumentele in linia de comanda (-l) +14 Nov 2004 - Nu mai verific in bank daca este un director sau fisier simplu, pt. ca poate sa aiba probleme +28 Nov 2004 - Curatat codul la OscilGen (acum datele sunt stocate mai bine si nu in functie de biblioteca FFTW) + - Corectata o mica eroare la OscilGen cu adaptive harmonics care facea ca energia vechilor armonice sa nu se adauge in mod corect la noile armonice (la note inalte) + - Sortarea nu mai este quicksort la bank si la presets pt. ca am vazut ca nu merge in windows intotdeauna + - Corectata o eroare la egalizator care facea ca sa se aplice si la el par. D/W +29 Nov 2004 - Marita zona de valori la adaptive harmonics power din OscilGen + - Adaugata posibilitate de a post-procesa la adaptive harmonics(adica a adauga sau a amplifica anumite armonice) +05 Dec 2004 - Corectata o eroare care facea ca functiile getChunk si setChunk sa fie supraincarcate in loc de suprascrise (dar nu am testat) + - Corectata o eroare care returna gresit la canDo in vst (netestat) +17 Dec 2004 - Inceput sa folosesc Dvorak pt. VK +18 Dec 2004 - Continuat putin la VK +20 Dec 2004 - Se poate selecta la VK dintre "qwerty" si "Dvorak" + - Corectata o mica erare care facea sa nu arate BWprofile dezactivat la PADnote + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + +04 Ian 2005 - Corectata o mica eroare care facea ca sa nu arate Force Release la Freemode Envelope +15 Ian 2005 - Corectata o eroare la controllerul bandwidth care facea ca sa ajunga la valoarea 0 si sa dea peste cap SUBsynth +22 Ian 2005 - Inceput sa scriu suportul pt. DSSI +27 Ian 2005 - Corectata eroare care facea ca in cazul in care sunt 2 banci cu acelasi nume (sau aceeasi bank root dir sa fie selectat de 2 ori) sa produca confuzie +03 Feb 2005 - Inceput sa scriu la Microtonal ca sa se faca butonul apply de culoare rosie cand se schimba ceva +06 Feb 2005 - Facuta o mica modificare care interzice punera notelor "0" in dump si alta modificare care mareste nr. de octave calculate la PADsynth + - Renuntat sa fac modificarea la Microtonal inceputa din 03 Feb, pentru ca nu am gasit cum pot schimba culoarea butonului automat cand modific un text +07 Feb 2005 - Corectata o eroare care facea ca la microtonal mapping sa nu se calculeze corect (adica sa se stocheze valoarea corecta) +12 Feb 2005 - Controllerul prestabilit la Virtual Keyboard este Filter Cutoff in loc de BandWidth + - Modificate cateva preseturi la DynamicFilter + - Adaugata posibilitatea de a mari sau micsora cu un parametru detune-ul vocilor de la ADnote +17 Feb 2005 - Corectate cateva erori la PADsynth care faceau ca sa se citeasca date din zone de memorie nealocata + - Corectata o eroare la Bank care facea ca uneori sa crape programul cand umblam mult cu bankuri +19 Feb 2005 - Corectata o eroare care facea ca uneori sa fie calculata frecventa la ADnote=nan si programul sa crape pentru ca era folosit parametrul bandwidthDetuneMultiplier inainte de a fi calculat +21 Feb 2005 - Se afiseaza corect numele fisierului proaspat salvat in fereastra principala +26 Feb 2005 - Corectata eroarea la windows si la OSS care facea ca pitch bend sa nu fie mapat corect (trebuie verificat) +27 Feb 2005 - Se afiseaza corect valoarea lui detune in centi +28 Feb 2005 - Corectata o mica eroare care facea ca sa nu se afiseze intotdeauna corect detune-ul la ADvoice + - Afisajul VU-meter la Master nu mai prezinta variatii mari in timp scurt + - Adaugata afisajul RMS la VU-meter +06 Mar 2005 - Facute cateva mici modificari referitoare in special la warning-uri + - Corectata o mica eroare care facea ca la un Paste sa nu se actulizeze unii parametrii ai filtrului in interfata +12 Mar 2005 - Imbunatatiri la interfata PADsynth, adica se poate da "apply" direct din OscilGenUI sau ResonanceUI +13 Mar 2005 - Facute cateva compilari in Makefile pt. compilare pt. Windows (standalone exe si vst) + - Se compileaza in mod cross-compile pt. windows din linux +14 Mar 2005 - Mici modificari la afisarea RMS-ului + - Actualizat textul copyright-ului la anul 2005 +22 Mar 2005 - Corectata o mica eroare care facea ca la schimbari foarte lente al parametrilor sa nu se actualizeze Format Filter +25 Mar 2005 - Corectata o eroare care facea ca uneori, la anumite setari ale lui SepctrumAdjust din OscilGen sa rezulte semnal zero + Corectata o mica eroare care facea ca daca se foloseste setarea 440Hz la Padsynth sa se aleaga sample-ul incorect +06 Apr 2005 - Modificat installerul pt. windows si pregatit pt. installer (folosit cross-compiling si nsis&wine) + - Adaugat icon in format windows (si la installer) + - Adaugat parametrul '-Y' la linia de comanda, care este folosit doar pentru installerul NSIS (parametrul este necesar pentru ca NSIS ma forteaza sa dau un parametru la program pentru ca sa adauge un icon la shortcut; zynaddsubfx ignora acest parametru) +07 Apr 2005 - Pregatit pentru release +08 Apr 2005 - Corectata o mica eroare care facea ca sa nu se incarce configul la inceput + - LANSAT PE INTERNET - VERSIUNEA (2.2.0) +-------------------------------------------------------------------------------------------------- +12 Apr 2005 - Actualizat pentru MXML 2.2 (nu o sa mearga pe vers. mai vechi de mxml) +27 Apr 2005 - Adaugata posibilitatea de a inlatura complet interfata grafica in Makefile.inc (in acest caz nu mai sunt necesare bibliotecile grafice ca fltk) + - Adaugata posibilitatea de a incarca direct un instrument cu -L (deocamdata se poate incarca doar in part-ul 0) + - LANSAT PE INTERNET - VERSIUNEA (2.2.1) +-------------------------------------------------------------------------------------------------- +28 Apr 2005 - Corectata o eroare care facea ca uneori sa fie frecventa prea mare la LFO daca era folosit random + - Nu mai afiseaza optionea -A in help daca nu este compilat si OSS si JACK +29 Mai 2005 - Corectata o eroare care facea ca sa nu se tina minte ultimul bank +27 Aug 2005 - Corectata o eroare care facea ca sa nu mearga functia Dump (se initializa inainte de citirea configurarilor) +21 Sep 2005 - Imbunatatit modul de scalare al profilei unei armonice la PADsynth +27 Sep 2005 - Gasita si rezolvata o posibila problema la PADsynth care facea ca sa nu se foloseasca mutex la stergerea de sample-uri (daca se aleg mai putine sample-uri decat initial) +09 Oct 2005 - Rezolvat un memory-leak la FFTwrapper + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +16 Apr 2006 - Corectata o eroare care facea ca sa nu se foloseasca insertion effect la master +20 Aug 2006 - Adaugat 88200 ca rata de esantionare +14 Sep 2006 - Se afiseaza spectrul la nota cu frecventa de 440 Hz la Oscil si pentru parametru Adaptive Harmonics +30 Oct 2006 - Adaugat un patch "standalone zombie fix stripped from Lars" + - Adaugat un patch "Extended mono" si "font resizing stuff" de Gerald Folcher +31 Oct 2006 - Adaugat un patch "Extended mono v.3" de Gerald Folcher + - Inlocuit fl_ask cu fl_choice in fisierele .fl + - In mod prestabilit nu se mai seteaza volumul la efectul 0 + - Efectele sunt numerotare de la 1 si in la "send to" din partui +01 Nov 2006 - Adaugat patch-urile de Jack Midi si LASH de Lars Luthman +06 Nov 2006 - Aplicat un patch "Fix for ALSA system lockup" de Lars Luthman +10 Nov 2006 - Aplicat un patch "zyn-extendedmono_v4_update-061110.diff.gz" de Gerald Folcher +14 Nov 2006 - Aplicat un patch "zyn-CVS-extendedmono_v5_update-061113.diff.gz" de Gerald Folcher + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +19 Mar 2007 - Aplicat un patch mic de la Daniel Clemente care este un workaround la bug-ul X11 cand tin tastele apasate mai mult timp +01 Apr 2007 - O mica modificare cu xclass zynaddsubfx in MasterUI.fl +09 Sep 2007 - Schimbata licenta la GPL 2 or other later + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +02 Ian 2008 - Corectate cateva mici erori la dezalocarea memoriei + - Codul de recorder wav a fost rescris + - Adaugata functia de export la sample-urile din PADsynth + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +20 Feb 2009 (Mark McCurry) + - Made several functions accept 'constant char' over 'char' to + prevent warnings + - Changed several 'delete' operations to 'delete []' based upon + the usage of 'new []' + - Gave external programs Makefiles + - Gave dials tooltips showing their value when they are being + moved + - Gave dials the ability to have normal tooltips when the mouse + hovers over them + - Created tooltips for the effects knobs + - Standardized the code, so it could compile with pedantic without + errors [it looks like some errors may have been missed] + +22 Feb 2009 (Mark McCurry) + - Fix improper deallocation in PresetsStore + - Fixed errors with drawing of the Oscillator as reported with + valgrind + +07 Mar 2009 (Mark McCurry) + - Added start of DocBook documentation + - Incorperated JACK output patch by Emmanuel Saracco + - Incorperated QUERTZ layout by Achim Settelmeier + +29 Mar 2009 (Mark McCurry) + - Started to use Doxygen within the Effects + - Started to use const within Effects + - Changing tabs->four spaces in hopes of generating a bit more + consitancy + - Began to use Initialization Lists + - Almost all changes contained in Effects until further + discussion on the style, so consistancy can be reached + +28 May 2009 (Mark McCurry) + - Added some more Doxygen comments + - Added Audio Samples classes + - Added Stereo template + - Added Control class + - Added DelayCtl class + +20 Iun 2009 (Paul Nasca) + - Bugfix: WAV export of PADsynth + +10 Iul 2009 (Paul Nasca) + - Update copyright info + +11 Jul 2009 (Mark McCurry) + - Added Proportinal Portamento + - Replaced Docbook with AsciiDoc + +18 Jul 2009 (Mark McCurry) + - Enabled volume controller by default + +20 Jul 2009 (Mark McCurry) + - Incorperated AZERTY layout by sourceforge user jimee + +02 Sep 2009 (Mark McCurry) + - Incorperated const char* <-> string mismatch by Alexis Ballier + +04 Sep 2009 (Mark McCurry) + - Incorperated NULLMidiIn function prototype fix by Alexis Ballier + +07 Sep 2009 (Mark McCurry) + - Fixed glitch in XMLwrapper, which would prevent file loading + +11 Sep 2009 (Mark McCurry) + - Moved PADsynth_used from public struct to has/set methods in + XMLwrapper + - Created wrapper functions, so that XMLwrapper can be somewhat + usable when const + - Removed multiple addparam methods and replaced it with one + variable argument function + - Replaced int2str, real2str, str2int, and str2real from XMLwrapper + with stringTo and stringFrom function templates in Util. + - Moved newFFTFREQS and deleteFFTFREQS from Util to FFTwrapper + - Removed unneeded stack from XMLwrapper + +18 Sep 2009 (Mark McCurry) + - Started to use versioning information in XMLwrapper + - Remove last of stack helper functions in XMLwrapper + - Added std::string retreval to XMLwrapper + +20 Sep 2009 (Paul Nasca) + - Started to implement the Unison effect for ADsynth + +22 Sep 2009 (Paul Nasca) + - Added vibratto and other features to Unison effect + +22 Sep 2009 (Mark McCurry) + - Changed temporary data for Oscilgen from static to instance + recommended by Tobias Doerffel + - Fixed Memory leaks in UI based upon James Morris' patch + +23 Sep 2009 (Paul Nasca) + - Added unison invert phase + - Made unison frequency spread to depend on Bandwidth controllers and parameters + - Added unison vibratto speed control and other improvements + - bugfixes: Voice Amplitude Envelope and FM + +24 Sep 2009 (Paul Nasca) + - Small enhancements and bugfixes to Unison + - Started to implement Bandwidth to the Reverb effect + +25 Sep 2009 (Mark McCurry) + - Allowed for XMLwrapper to retrieve strings stored in mxml TEXT + fields +29 Sep 2009 (Paul Nasca) + - Remove the old (FFT based) Bandwidth effect to Reverb and started rewrite it (based on multivoice chorus/unison effect) + +01 Oct 2009 (Paul Nasca) + - Corrected the ADsynth unison LFO rounding function + - Made Unison based on Bandwidth (in cents) parameter + +02 Oct 2009 (Mark McCurry) + - Added OSS failsafe by Jérémie Andréi + +04 Oct 2009 (Mark McCurry) + - fixed Ctest issues + +06 Oct 2009 (Mark McCurry) + - Added first simple profiling test + +08 Oct 2009 (Mark McCurry) + - Started to see if memset/memcpy offer performance benifits when + widely used + - Added basic SUBnote test + +09 Oct 2009 (Mark McCurry) + - Restylized codebase with uncrustify + +28 Oct 2009 (Paul Nasca) + - Disable "bw" control on Reverb when Bandwidth mode is not enabled + +30 Oct 2009 (Mark McCurry) + - Commited first stage of Nio (New IO) WIP + +18 Nov 2009 (Mark McCurry) + - Fixed segfault in VirKeyBoard + +02 Dec 2009 (Paul Nasca) + - Fixed a small typo on Virtual Keyboard + +10 Dec 2009 (Mark McCurry) + - Separated out Presets and arrayed Presets to reduce warnings from + the Wextra flag + - Minor change to Filter_ and FormantFilter to reduce unwanted warnings + +13 Dec 2009 (Mark McCurry) + - Deprecating Output system for the Nio system + - General Code Cleanup + - Adding OpenGL linking for proper compiles + +14 Jan 2010 (Mark McCurry) + - Fixed No UI Flag "-U" as it was previously partially initializing + the gui + +14 Feb 2010 (Stephen Parry) + - DSSI Support Repaired + +14 Feb 2010 (Mark McCurry) + - Made the Echo attempt to adjust the delay instead of erasing it + when length is changed + +02 May 2010 (Mark McCurry) + - Merging in cleanup from effects and adding APhaser by Ryan Billing + +27 Jun 2010 (Mark McCurry) + - Aphaser and Phaser are within same effect now + +17 Aug 2010 (Paul Nasca) + - small bugix on adsynth.cpp + +22 May 2010 (Mark McCurry) + - Mergin Nio backend + +18 Aug 2011 (Mark McCurry) + - Fixing DSSI subsystem + +29 Oct 2011 (Damien Goutte-Gattat) + - Added Bank select midi support + +05 Feb 2012 (Liven Moors) + - Added spike waveform to oscilator options + +06 Feb 2012 (Mark McCurry) + - Adding --exec-after-init option + +22 Feb 2012 (Liven Moors) + - Added circle waveform to oscilator options + +26 Mar 2012 (Mark McCurry) + - Fixed Segfault in Oscillgen + +27 Mar 2012 (Mark McCurry) + - Fixed chorus noise bug + +14 Apr 2012 (Mark McCurry) + - Removed Nio debugging code + +18 Apr 2012 (Jonathan Liles) + - Added Non-session-manager support + +31 Mar 2012 (Olaf Schulz) + - Added Midi aftertouch support diff --git a/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/Controller.C b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/Controller.C new file mode 100644 index 000000000..8f2e6d944 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/Controller.C @@ -0,0 +1,84 @@ +#include "Controller.h" +#include +#include +#include + +pthread_mutex_t mutex; +int Pexitprogram; + +Controller::Controller() { + //init + for(int i = 0; i < 6; ++i) { + pars[i].mode = 1; + pars[i].val1 = 0; + pars[i].val2 = 127; + pars[i].nrpn.cpar = 8; + pars[i].nrpn.fpar = 0; + pars[i].nrpn.cval = 0; + } + pars[0].ctl.par = 71; + pars[1].ctl.par = 74; + pars[2].ctl.par = 10; + pars[3].ctl.par = 11; + pars[4].ctl.par = 1; + pars[5].ctl.par = 75; + + //ALSA init + snd_seq_open(&midi_out, "default", SND_SEQ_OPEN_OUTPUT, 0); + + char portname[50]; sprintf(portname, "Controller"); + int alsaport = snd_seq_create_simple_port( + midi_out, + portname, + SND_SEQ_PORT_CAP_READ + | SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_SYNTH); +} + +Controller::~Controller() { + snd_seq_close(midi_out); +} + +void Controller::sendcontroller(int par, unsigned char val) { + snd_seq_event_t midievent; + snd_seq_ev_clear(&midievent); + + snd_seq_ev_set_controller(&midievent, Pchout, par, val); + + snd_seq_ev_set_subs(&midievent); + snd_seq_ev_set_direct(&midievent); + snd_seq_event_output_direct(midi_out, &midievent); + +// fprintf(stderr,"Controller: %d %d\n",par,val); +} + +void Controller::sendnrpn(int npar, unsigned char val) { +// fprintf(stderr,"NRPN: %d %d %d %d\n",pars[npar].nrpn.cpar,pars[npar].nrpn.fpar,pars[npar].nrpn.cval,val); + + sendcontroller(0x63, pars[npar].nrpn.cpar); + sendcontroller(0x62, pars[npar].nrpn.fpar); + sendcontroller(0x06, pars[npar].nrpn.cval); + sendcontroller(0x26, val); +// fprintf(stderr,"------------\n\n"); +} + +void Controller::send(int npar, float xval) { + if(pars[npar].mode == 0) + return; + int val; + if(pars[npar].val1 <= pars[npar].val2) + val = + (int) (xval + * (pars[npar].val2 - pars[npar].val1 + + 1.0) * 0.9999 + pars[npar].val1); + else + val = + (int) (xval + * (pars[npar].val2 - pars[npar].val1 + - 1.0) * 0.9999 + pars[npar].val1 + 1.0); + switch(pars[npar].mode) { + case 1: sendcontroller(pars[npar].ctl.par, val); break; + //case 2:break; + case 3: sendnrpn(npar, val); break; + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/Controller.h b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/Controller.h new file mode 100644 index 000000000..5b83744fe --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/Controller.h @@ -0,0 +1,33 @@ +#ifndef CONTROLLER_H +#define CONTROLLER_H +#include + +extern pthread_mutex_t mutex; +extern int Pexitprogram; + +class Controller +{ + public: + Controller(); + ~Controller(); + void send(int npar, float xval); + //parameters + unsigned char Pchout; + struct { + unsigned char mode; //0=off,1=ctl,2=RPN,3=NRPN + unsigned char val1, val2; + struct { + unsigned char par; + } ctl; + struct { + unsigned char cpar, fpar, cval; + } nrpn; + } pars[6]; + private: + void sendcontroller(int par, unsigned char val); + void sendnrpn(int npar, unsigned char val); + + snd_seq_t *midi_out; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/ControllerUI.fl b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/ControllerUI.fl new file mode 100644 index 000000000..b83c9c08e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/ControllerUI.fl @@ -0,0 +1,217 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cxx} +decl {\#include } {selected public +} + +decl {\#include } {public +} + +decl {\#include "Controller.h"} {public +} + +decl {Controller *controller;} {} + +class Pad {: {public Fl_Box} +} { + Function {Pad(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {} { + code {} {} + } + Function {temp_draw()} {} { + code {/*int ox=x(),oy=y(),lx=w(),ly=h(),i,ix,iy,oiy; +float freqx; + +fl_color(FL_BLACK); +fl_rectf(ox,oy,lx,ly); + + + +fl_color(FL_GRAY); + +fl_line_style(FL_SOLID); +fl_line(ox+2,oy+ly/2,ox+lx-2,oy+ly/2); +*/} {} + } + Function {sendmidi(int button,float datax,float datay)} {} { + code {controller->send(button,datax); +controller->send(button+1,datay);} {} + } + Function {handle(int event)} {return_type int + } { + code {int x_=Fl::event_x()-x(); +int y_=Fl::event_y()-y(); + +if ((event==FL_PUSH)||(event==FL_DRAG)){ + if (x_<0) x_=0;if (y_<0) y_=0; + if (x_>=w()) x_=w();if (y_>=h()-1) y_=h()-1; + + float tmpx=(float) x_/(w()); + float tmpy=1.0-(float) y_/h(); + + int b=Fl::event_buttons()>>24; + + if (b&1) sendmidi(0,tmpx,tmpy); + if (b&2) sendmidi(2,tmpx,tmpy); + if (b&4) sendmidi(4,tmpx,tmpy); + +}; + +return(1);} {} + } + decl {int oldx,oldy;} {} +} + +class ControllerUI {} { + Function {make_window()} {} { + Fl_Window controlleruiwindow { + label {Midi Controller} + callback {o->hide(); +exit(0);} + xywh {210 213 340 410} type Double hide + } { + Fl_Counter {} { + label {Output Channel} + callback {controller->Pchout=(int) o->value();} + xywh {10 13 75 22} type Simple labelsize 10 align 5 minimum 0 maximum 15 step 1 textfont 1 + code0 {o->value(controller->Pchout);} + } + Fl_Box {} { + xywh {10 80 320 320} box ENGRAVED_BOX color 176 + class Pad + } + Fl_Choice {} { + callback {nbut=(int) o->value(); +refreshvalues();} + xywh {10 50 75 20} down_box BORDER_BOX + } { + MenuItem {} { + label {But.1 X} + xywh {0 0 100 20} labelfont 1 labelsize 12 + } + MenuItem {} { + label {But.1 Y} + xywh {10 10 100 20} labelfont 1 labelsize 12 divider + } + MenuItem {} { + label {But.2 X} + xywh {10 10 100 20} labelfont 1 labelsize 12 + } + MenuItem {} { + label {But.2 Y} + xywh {20 20 100 20} labelfont 1 labelsize 12 divider + } + MenuItem {} { + label {But.3 X} + xywh {20 20 100 20} labelfont 1 labelsize 12 + } + MenuItem {} { + label {But.3 Y} + xywh {30 30 100 20} labelfont 1 labelsize 12 + } + } + Fl_Group ctlgroup { + xywh {95 35 60 40} box ENGRAVED_BOX + } { + Fl_Counter ctlcounter { + label Controller + callback {controller->pars[nbut].ctl.par=(int) o->value();} + xywh {100 50 50 15} type Simple labelsize 10 align 1 minimum 0 maximum 127 step 1 textfont 1 textsize 12 + } + } + Fl_Choice modechoice { + label Mode + callback {controller->pars[nbut].mode=(int) o->value(); +refreshvalues();} + xywh {95 13 60 20} down_box BORDER_BOX labelsize 10 align 5 + } { + MenuItem {} { + label OFF + xywh {30 30 100 20} labelfont 1 labelsize 12 + } + MenuItem {} { + label {Ctl.} + xywh {20 20 100 20} labelfont 1 labelsize 12 + } + MenuItem {} { + label RPN + xywh {30 30 100 20} labelfont 1 labelsize 12 deactivate + } + MenuItem {} { + label NRPN + xywh {40 40 100 20} labelfont 1 labelsize 12 + } + } + Fl_Group nrpngroup { + xywh {160 35 170 40} box ENGRAVED_BOX + } { + Fl_Counter cparcounter { + label {CoarseP.} + callback {controller->pars[nbut].nrpn.cpar=(int) o->value();} + xywh {165 50 50 15} type Simple labelsize 10 align 1 minimum 0 maximum 127 step 1 textfont 1 textsize 12 + } + Fl_Counter fparcounter { + label {FineP.} + callback {controller->pars[nbut].nrpn.fpar=(int) o->value();} + xywh {220 50 50 15} type Simple labelsize 10 align 1 minimum 0 maximum 127 step 1 textfont 1 textsize 12 + } + Fl_Counter cvalcounter { + label {CoarseV.} + callback {controller->pars[nbut].nrpn.cval=(int) o->value();} + xywh {275 50 50 15} type Simple labelsize 10 align 1 minimum 0 maximum 127 step 1 textfont 1 textsize 12 + } + } + Fl_Counter val1counter { + label {Val.1} + callback {controller->pars[nbut].val1=(int) o->value();} + xywh {190 15 50 15} type Simple labelsize 10 align 5 minimum 0 maximum 127 step 1 textfont 1 textsize 12 + } + Fl_Counter val2counter { + label {Val.2} + callback {controller->pars[nbut].val2=(int) o->value();} + xywh {275 15 50 15} type Simple labelsize 10 align 5 minimum 0 maximum 127 step 1 value 127 textfont 1 textsize 12 + } + Fl_Button exchangebutton { + label {<->} + callback {unsigned char tmp=controller->pars[nbut].val2; +controller->pars[nbut].val2=controller->pars[nbut].val1; +controller->pars[nbut].val1=tmp; +refreshvalues();} + xywh {245 15 25 15} box THIN_UP_BOX + } + } + } + Function {refreshvalues()} {} { + code {modechoice->value(controller->pars[nbut].mode); +val1counter->value(controller->pars[nbut].val1); +val2counter->value(controller->pars[nbut].val2); +ctlcounter->value(controller->pars[nbut].ctl.par); +cparcounter->value(controller->pars[nbut].nrpn.cpar); +fparcounter->value(controller->pars[nbut].nrpn.fpar); +cvalcounter->value(controller->pars[nbut].nrpn.cval); + +if (controller->pars[nbut].mode!=0){ + val1counter->activate(); + val2counter->activate(); + exchangebutton->activate(); +}else{ + val1counter->deactivate(); + val2counter->deactivate(); + exchangebutton->deactivate(); +}; + +if (controller->pars[nbut].mode==1) ctlgroup->activate(); + else ctlgroup->deactivate(); + +if (controller->pars[nbut].mode==3) nrpngroup->activate(); + else nrpngroup->deactivate();} {} + } + Function {ControllerUI(Controller *controller_)} {} { + code {nbut=0; +controller=controller_; +make_window(); +refreshvalues(); +controlleruiwindow->show();} {} + } + decl {int nbut;} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/main.C b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/main.C new file mode 100644 index 000000000..6018dc73d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Controller/main.C @@ -0,0 +1,16 @@ +#include "Controller.h" +#include "ControllerUI.h" + +pthread_t thr1, thr2; +Controller controller; + + + +main() +{ + ControllerUI *controllerUI = new ControllerUI(&controller); + + Fl::run(); + + delete controllerUI; +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/Spliter.C b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/Spliter.C new file mode 100644 index 000000000..bf567ffd4 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/Spliter.C @@ -0,0 +1,82 @@ +//Copyright (c) 2002-2003 Nasca Octavian Paul +//License: GNU GPL 2 + +#include "Spliter.h" +#include + +pthread_mutex_t mutex; +int Pexitprogram; + +Spliter::Spliter() { + //init + Psplitpoint = 60; + Pchin = 0; + Pchout1 = 0; + Pchout2 = 1; + Poct1 = 0; + Poct2 = 0; + //ALSA init + snd_seq_open(&midi_in, "default", SND_SEQ_OPEN_INPUT, 0); + snd_seq_open(&midi_out, "default", SND_SEQ_OPEN_OUTPUT, 0); + + char portname[50]; sprintf(portname, "Spliter IN"); + int alsaport = snd_seq_create_simple_port( + midi_in, + portname, + SND_SEQ_PORT_CAP_WRITE + | SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_SYNTH); + sprintf(portname, "Spliter OUT"); + alsaport = snd_seq_create_simple_port( + midi_out, + portname, + SND_SEQ_PORT_CAP_READ + | SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_SYNTH); +} + +Spliter::~Spliter() { + snd_seq_close(midi_in); + snd_seq_close(midi_out); +} + +// This splits the Midi events from one channel to another two channels +void Spliter::midievents() { + snd_seq_event_t *midievent; + midievent = NULL; + snd_seq_event_input(midi_in, &midievent); + + if(midievent == NULL) + return; + if((midievent->type == SND_SEQ_EVENT_NOTEON) + || (midievent->type == SND_SEQ_EVENT_NOTEOFF)) { + int cmdchan = midievent->data.note.channel; + if(cmdchan == Pchin) { + snd_seq_ev_set_subs(midievent); + snd_seq_ev_set_direct(midievent); + if(midievent->data.note.note < Psplitpoint) { + midievent->data.note.channel = Pchout1; + int tmp = midievent->data.note.note; + tmp += Poct1 * 12; if(tmp > 127) + tmp = 127; if(tmp < 0) + tmp = 0; + midievent->data.note.note = tmp; + } + else { + midievent->data.note.channel = Pchout2; + int tmp = midievent->data.note.note; + tmp += Poct2 * 12; if(tmp > 127) + tmp = 127; if(tmp < 0) + tmp = 0; + midievent->data.note.note = tmp; + } + snd_seq_event_output_direct(midi_out, midievent); + } + else { + snd_seq_ev_set_subs(midievent); + snd_seq_ev_set_direct(midievent); + snd_seq_event_output_direct(midi_out, midievent); + } + } + snd_seq_free_event(midievent); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/Spliter.h b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/Spliter.h new file mode 100644 index 000000000..9b5b83b1b --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/Spliter.h @@ -0,0 +1,27 @@ +//Copyright (c) 2002-2003 Nasca Octavian Paul +//License: GNU GPL 2 + +#ifndef SPLITER_H +#define SPLITER_H +#include +#include + +extern pthread_mutex_t mutex; +extern int Pexitprogram; + +class Spliter +{ + public: + Spliter(); + ~Spliter(); + void midievents(); + + //parameters + unsigned char Psplitpoint; + unsigned char Pchin, Pchout1, Pchout2; + signed char Poct1, Poct2; + private: + snd_seq_t *midi_in, *midi_out; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/SpliterUI.fl b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/SpliterUI.fl new file mode 100644 index 000000000..7a8b44836 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/SpliterUI.fl @@ -0,0 +1,70 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0102 +header_name {.h} +code_name {.cxx} +decl {//Copyright (c) 2002-2003 Nasca Octavian Paul} {selected +} + +decl {//License: GNU GPL 2} {} + +decl {\#include } {public +} + +decl {\#include "Spliter.h"} {public +} + +class SpliterUI {} { + Function {make_window()} {open + } { + Fl_Window spliteruiwindow { + label {Midi Spliter} + callback {o->hide(); +exit(0);} + xywh {225 187 375 72} hide + } { + Fl_Counter {} { + label {Split note} + callback {spliter->Psplitpoint=(int) o->value();} + xywh {93 27 114 24} labelsize 12 align 5 minimum 0 maximum 127 step 1 value 60 textfont 1 textsize 16 + code0 {o->value(spliter->Psplitpoint);} + code1 {o->lstep(12);} + } + Fl_Counter {} { + label {Input Channel} + callback {spliter->Pchin=(int) o->value();} + xywh {6 30 69 18} type Simple labelsize 10 align 5 minimum 0 maximum 15 step 1 textfont 1 + code0 {o->value(spliter->Pchin);} + } + Fl_Counter {} { + label {Output Channel 1} + callback {spliter->Pchout1=(int) o->value();} + xywh {285 18 69 18} type Simple labelsize 10 align 5 minimum 0 maximum 15 step 1 textfont 1 + code0 {o->value(spliter->Pchout1);} + } + Fl_Counter {} { + label {Output Channel 2} + callback {spliter->Pchout2=(int) o->value();} + xywh {285 36 69 18} type Simple labelsize 10 align 6 minimum 0 maximum 15 step 1 textfont 1 + code0 {o->value(spliter->Pchout2);} + } + Fl_Counter {} { + label {Tr.1(oct.)} + callback {spliter->Poct1=(int) o->value();} + tooltip {Transpose (octaves)} xywh {225 18 48 18} type Simple labelsize 10 align 5 minimum -8 maximum 8 step 1 textfont 1 + code0 {o->value(spliter->Poct1);} + } + Fl_Counter {} { + label {Tr.2(oct.)} + callback {spliter->Poct2=(int) o->value();} + tooltip {Transpose (octaves)} xywh {225 36 48 18} type Simple labelsize 10 align 6 minimum -8 maximum 8 step 1 textfont 1 + code0 {o->value(spliter->Poct2);} + } + } + } + Function {SpliterUI(Spliter *spliter_)} {} { + code {spliter=spliter_; +make_window(); +spliteruiwindow->show();} {} + } + decl {Spliter *spliter;} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/main.C b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/main.C new file mode 100644 index 000000000..464f8bd9b --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/main.C @@ -0,0 +1,37 @@ +//Copyright (c) 2002-2003 Nasca Octavian Paul +//License: GNU GPL 2 + +#include +#include "Spliter.h" +#include "SpliterUI.h" + +pthread_t thr1, thr2; +Spliter spliter; + +void *thread1(void *arg) { + Fl::run(); + return 0; +} +void *thread2(void *arg) { + while(Pexitprogram == 0) + spliter.midievents(); + return 0; +} + + +main() +{ + Pexitprogram = 0; + SpliterUI *spliterUI = new SpliterUI(&spliter); + + pthread_mutex_init(&mutex, NULL); + pthread_create(&thr1, NULL, thread1, NULL); + pthread_create(&thr2, NULL, thread2, NULL); + + while(Pexitprogram == 0) { + usleep(100000); + } + + pthread_mutex_destroy(&mutex); + delete spliterUI; +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/readme.txt b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/readme.txt new file mode 100644 index 000000000..10a78cc09 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/readme.txt @@ -0,0 +1,15 @@ +Spliter +------ + +This program splits the keyboard and alows you to play two instruments same time. You can use this program with ZynAddSubFX or any other synthesizer. +This requires ALSA 0.9.x. + +To compile it, run "make". +If you want to use with ZynAddSubFX send the midi events thru Spliter with aconnect like this: + + - connect the keyboard port to "Spliter IN" port + - connect the "Spliter OUT" to ZynAddSubFX + - change the midi channels that you want to play. Be sure that the both output channels are enabled and receive NoteOn in ZynAddSubFX. + +If you change some settings from Spliter while you are playing to keyboard you may ecounter "stucked keys". To clear all theese press to "Panic" button from ZynAddSubFX. + diff --git a/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/readme.txt b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/readme.txt new file mode 100644 index 000000000..965bad380 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/readme.txt @@ -0,0 +1,2 @@ +These are external programs, which can use with ZynAddSubFX or any other midi device. More information is in the documentation (html - webpages). + diff --git a/plugins/zynaddsubfx/zynaddsubfx/FAQ.txt b/plugins/zynaddsubfx/zynaddsubfx/FAQ.txt new file mode 100644 index 000000000..c774ef3d1 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/FAQ.txt @@ -0,0 +1,27 @@ + Frequently Asked Questions + -------------------------- + +Q1) What means "ZynAddSubFX" ? +A1) The name of the program comes from 4 words: + 1) Synthesizer ('S'->'Z') --> Zyn + ^^^ + 2) Additive Synthesis ------> Add + ^^^ + 3) Subtractive Synthesis ---> Sub + ^^^ + 4) Effects ----------------> FX + + So, ZynAddSubFX is a SYNthesizer with ADDitive, SUBtractive engines and effects. + + +Q2) How can I load files from older versions of ZynAddSubFX (like *.mas_zyn,etc) +A2) You need to convert them into new format. Please use 2.0.0pre1 or (recommended) 2.0.0pre2 versions of ZynAddSubFX to load old file formants and save them in the new formats + + +Q3) How can I change the number of parts, voices to ADSynth, effects, etc. ? +A3) Look in src/globals.h and change there these values. You don't have to change anything else, just recompile all. But most settings must be below 128. As the rule of the thumb if a setting is 128 or below 128, please don't make it bigger than 128. Anyway, I don't believe that you'll need more than 128 for these settings; for example you don't need 128(or more) effects same time? That's why I put the limit of 128 (using 7 bits of char). + + +Q4) How do I enable Jack support on ZynAddSubFX ? +A4) If your system has the jack libraries installed, ZynAddSubFX should automatically build with jack support. It is highly recommended that the Jack samplerate to be equal to ZynAddSubFX samplerate (SAMPLE_RATE from globals.h), otherwise the resampling will be done and this will decrease the quality a bit. + diff --git a/plugins/zynaddsubfx/zynaddsubfx/HISTORY.txt b/plugins/zynaddsubfx/zynaddsubfx/HISTORY.txt new file mode 100644 index 000000000..22f691f4e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/HISTORY.txt @@ -0,0 +1,222 @@ +2.4.3 (15 Jun 2012) + - Non-session manager support + - Midi aftertouch support + - Documentation additions + - Somewhat more sane Nio defaults + - Misc bug fixes + +2.4.2 (26 Feb 2012) + - New IO backend support + - MIDI bank select + - Spike/Circle waveform + - Faster subsynth + - --exec-after-init flag + - Version information compiled in + - Misc Bug fixes + +2.4.1 (27 Jun 2010) + - Azerty layout + - XML bug fixes + - Vibrato/Unison additions + - Reverb rewrite + - DSSI support enabled + - Adding APhaser + - other bugfixes + - code cleanup + +2.4.0 (21 Jun 2009) + - extended mono functionality + - legato mode + - export functionality on PADsynth + - inclusion of LASH client + - inclusion of DSSI audio output + - enabled tooltips for knobs (both description and value tooltips) + - added support for newer JACK api + - added quertz support for virtual keyboard + - started to encorperate cxxtest for unit testing + - many bugfixes + - code cleanup + +2.2.1 (28 Apr 2005) + - made to work with mxml-2.2 (will NOT work on older versions) + - it is possible to remove completely the graphical user interface (e.g. it can run without X). For this you need to modify the DISABLE_GUI option from the Makefile.inc + - added a commandline -L which load a instrument (.xiz) - now it only loads to part 0 (you can use this option with -l to load a master file and after this the option -L to replace the part) + +2.2.0 (8 Apr 2005) + - the VST version of ZynAddSubFX is removed from the instalation until it will be more stable (hope soon :) ) + - now, the instrument banks contains over 300 high quality instruments + - added "Apply" a button from OscilGen window for PADsynth + - added another parameter to ADsynth that controls the amount of all detunes of voices + - adaptive harmonics postprocess + - improved the VU-meter and added a RMS plot + - Dvorak support for Virtual Keyboard + - many bugs fixed and code cleanups + +2.1.1 (2 Oct 2004) + - Removed a big bug that prevented changing the part +2.1.0 (1 Oct 2004) + - Added a installer for windows (thanks to NSIS installer ( http://nsis.sourceforge.net/ ) ). Both VST and standalone vesions are contained in the same installer. + - Added a new user interface for beginners. You can switch the current user interface with that anytime do you want. + - All parts, effects, etc. are counted from '1' and not from '0' + - Added the posibility to compile the OSS and JACK support in the same binary (look in the Makefile.inc) + - VST host should be able to save all zynaddsubfx parameters into their setups (this is untested) + - Bugfixes and other + +2.0.0 (27 Aug 2004) + - VST version works (there are some issues/bugs but it works) + - Added a advanced Clipboard and Preset module - now is possible to add user preset LFOs,Envelopes, Effects, Oscillators, Resonances, Filters, etc. + - Completely removed the *.MAS_ZYN formats (masters, instruments,etc) support; use 2.0.0pre1 and 2.0.0pre2 to convert + - Corrected a error to pitch bend on VST plugin (thanks to Krzysztof Korpiela) + - Impoved the PADsynth module + - Because the PADsynth module takes a time to load, the instrument that contains such modules are shown in different colors + - Bugfixes + - Other + +2.0.0_pre2 (31 Iul 2004) + - Updated the XMLwrapper to mxml-2.0 + - Many bugfixes + - Other + +2.0.0_pre1 (17 Iul 2004) + - Added a new powerful synth engine which is called PADsynth, you can make very beautifull pads and even some strange sounds + - Now is used the XML format for all zynaddsubfx parameters(.XMZ for master parameters, .XIZ for instrument parameters and .XSZ for scale parameters).You can import older parameters. All parameters files are compressed with gzip algorithm. + - Some parameters has changed and you might ecounter different sounds that you saved in the older versions of zynaddsubfx + - The instrument banks are no longer single files, but directories that contains instrument .XIZ files (you can organize them even with a file manager). Also, you can use more than 1 banks easily. + - Added a new effect called DynamicFilter that allows you to do WahWah,AutoWah, VocalMorpher and other effects + - Speedups + - Started to write a small sequencer that allows to load and play a midi file from zynaddsubfx (unfinished) + - ZynAddSubFX is available from CVS, too. Please look at the sourceforge project page to get more information ( http://sourceforge.net/projects/zynaddsubfx ) + - The waveform generator (OscilGen) has many new parameters :) also if you press the "Shift" key, you can draw the hamonics amplitude/phases + - Many user interface improvements + - You can load a file at the start of the program with "-l" command-line parameter and you can run zynaddsubfx w/o user interface with "-U" + - It is possible to dump all MIDI notes into a text file + - The instruments can contain comments and copyright information in order to encourage sharing of them + - FFT3W library is supported + - More "randomness" options + - Other impovements + - Many, many bugfixes + - Added the full changelog (since I started to write zynaddsubfx), most is in Romanian + - Other things + +1.4.3 (31 Aug 2003) + - added state variable filters and other types to analog filters + - small user interface improvememnts + - small bugfixes + +1.4.2 (17 Iul 2003) + - added full-featured, advanced formantic filters + - added mixer panel which lets you to see/change most important part settings, and shows a vu-meters for each part + - you can choose to process the instrument's kit items only with one Part effect (eg. you can make a instrument kit that contains a reverberated piano and flanged strings) + - enabled to launch more instances in Jack + - when is launched first time, it searches for default.bnk_zyn file into /usr/share/zynaddsubfx and /usr/local/share/zynaddsubfx directories (useful for binary packages for Linux distributions) + - bugfixes + +1.4.1 (8 May 2003) + - added single mode to the instrument kit who alows only one item to be played same time + - added "Spectrum Adjust" to the ADsynth oscillator + - added "drum mode" to the instrument, where all midi keys are mapped to 12tET + - added a parameter to the "440Hz" which make the freq to varies a bit according to the key pressed (very usefull to toms and other drums) + - (for OSS audio out) if it is launched with root privileges, the synth will gain realtime scheduling priority + - bugfixes + +1.4.0 (15 Apr 2003) + - added instrument's own effect (effects that are loaded/saved with the instrument) + - FreeMode Envelopes: all Envelopes can have any shape (not only ADSR) + - Added instrument kits: It is possible to use more than one instruments into one part (used for layered synths or drum kits) + - Amplitude envelopes can be linear or logarithmic + - added interpolation on the Resonance user interface + - user interface improvements and cleanups of it's code + - initiated a mailing list to allow users to share patches for ZynAddSubFX. Please share your ZynAddSubFX patches; look at http://lists.sourceforge.net/mailman/listinfo/zynaddsubfx-user for more information about the mailing list. + +1.2.1 (6 Apr 2003) + - improved filter interpolation + - bugfix: wav header is written correctly + - bugfix: NRPN works correctly (eg:the controller was 0x98 instead of 98), now you can controll all effects parametrer realtime via MIDI + - bugfix: pitch bend works OK in windows + - added master fine detune (-64..63 cents) + - it is possible to swap effects or copy them + - started to port ZynAddSubFX to VST (not functional, yet) + - the resonace can protect the fundamental freq. against damping + +1.2.0 (21 Mar 2003) + - ZynAddSubFX is ported to Windows ;-) + - added internal Virtual Keyboard + - added Configuration window + - added frequency tracking to filter + - improved the OscilGen (harmonic filter, RMS normalisation, etc..) + - improved the recorder (uses the WAV file format and it starts only when a key is pressed) + - added filter interpolation if the frequency is changed very fast (it removes some annoying clicks) + - other improovements, bugfixes, speedups and cleanups of the code + +1.0.9 (24 Feb 2003) + - added keylimit to Part + - you can use multiple filter stages in order to make very steep filter rolloffs (eg. 48 dB/octave) + - ADsynth - added noise mode and you can make fixed frequencies; added the "Punch" parameter + - added an external program "Controller" which enables you to use the mouse for MIDI controllers + - other improvements and bugfixes + +1.0.8 (14 Feb 2003) + - added mono mode and portamento + - added the EQ effect + - the output of a system effect can be sent to others system effects + - minor bugfixes and improvements + +1.0.7 (7 Feb 2003) + - some settings (like samplerate) are set at runtime (by comand line) + - added Distorsion effect + - added controllers, and NRPNs for changing all effects parameters by midi + - bugs removed and other improvements + +1.0.6 (30 Jan 2003) + - Added JACK output ;-) + - Minor improvements and bugfixes + +1.0.5 (24 Jan 2003) + - The bug that crashed ZynAddSubFX if you change some effect parameters, it is realy removed (I forgot to update the file before upload) + - Other bugfixes and code cleanups + - Added a Global Filter to SubSynth + - Added keyresponse limits to Part + - Added presets to Effects + - The fade is smaller on high frequecy content and larger on low frequecies; so you'll don't hear starting clicks on basses and audible fadeins on higher pitched sounds + - Added tunnings to Reverb: you can choose Random of Freeverb + +1.0.4 (7 Jan 2003) + - It is possible to load Scala (.scl and .kbm) files + - Added mapping from note number to scale degree is possible to load Scala kbm files + - Corrected small bugs related to Microtonal + - If you want to use ZynAddSubFX with OSS (or you don't have ALSA) you can modify the Makefile.inc file to compile with OSS only. + - It is shown the real detune (in cents) + - Made a new widget that replaces the Dial widget + - Removed a bug that crashed ZynAddSubFX if you change some effect parameters + +1.0.3 (23 Dec 2002) + - small bugfixes: "Bypass Global Filter" from ADnoteUI dissapears sometimes ; + removed the low amplitude noise produced by the reverb; + if you "acconect" zynaddsubfx with aseqview no note was processed a long time. + - added Notch Filter + - added the option to randomize the ressonance function + - added VU-Meter + - Change the Insertion effect modes behaves (it sounds a bit louder) + - Added to the project an external program called Spliter that splits the + keyboard and alows you to play two instruments same time. You can use this + program with ZynAddSubFX or any other synthesizer. + - Added a new function to OscilGen + +1.0.2-1 (13 Dec 2002) + - bug found and removed: sometimes when Master/Instrument is saved, the synth crashed + +1.0.2 (13 Dec 2002) + - Added instrument banks + - the BandPass Filter's output amplitude was increased + - few fixes of FFTwrapper. See the documentation from "FFTwrapper.h" if you got error messages. + +1.0.1 (6 Dec 2002) + - corrected a bug that made ZynAddSubFX to crash(sometimes) if you disable a part + - wrote Resonance + - added the BandPass filter + - added the recording feature + - added "New instrument" menuitem + +1.0.0 (25 Sep 2002) + - first release, done a lot before it :-) + diff --git a/plugins/zynaddsubfx/zynaddsubfx/README.txt b/plugins/zynaddsubfx/zynaddsubfx/README.txt new file mode 100644 index 000000000..c8f419b2d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/README.txt @@ -0,0 +1,86 @@ +ZynAddSubFX +----------- +It is a realtime software synthesizer for Linux and Windows with many features. Please see the docs for details. +Copyright (c) 2002-2011 Nasca Octavian Paul and others contributors +e-mail: zynaddsubfx AT yahoo D0T com +ZynAddSubFX is free program and is distributed WITH NO WARRANTY. It is licensed under GNU General Public License version 2 (and only version 2) - see the file COPYING. + + --==## PLEASE SHARE YOUR INSTRUMENTS/MASTER SETTINGS ##==-- + --==## MADE WITH ZynAddSubFX ##==-- + Here is the mailing list where you can share your patches with others: + http://lists.sourceforge.net/mailman/listinfo/zynaddsubfx-user + + +The project page is + http://sourceforge.net/projects/zynaddsubfx + or + http://zynaddsubfx.sourceforge.net + +ZynAddSubFX is also available on many Internet sites like: + http://www-ccrma.stanford.edu/planetccrma/software/soundapps.html (Planet CCRMA) + http://www.hitsquad.com/smm/programs/ZynAddSubFX/ + http://freshmeat.net/projects/zynaddsubfx/ + http://ibiblio.org/pub/Linux/apps/sound/midi/ + or search "ZynAddSubFX" on a search engine (like www.google.com). + + +Requirements: +------------- +- a fast computer +- Linux or Windows +- FFTW 3.x.x - necessary for Fast Fourier computations +- MXML-2.5 or more recent library from www.minixml.org +- zlib library from http://www.zlib.org +- (for Linux) OpenSoundSystem (OSS) (if you don't have ALSA, only) +- (for Windows) pthreads, portaudio + +Not required, but recommended: +------------------------------ +- FLTK 1.x.x (tested with fltk 1.1.0, 1.1.1, 1.1.2,etc.) +- ALSA 0.9.x or later (with OSS emulation, if you don't use JACK) +- JACK +- a VST host for the VST version [For more information see: + http://www.kvraudio.com/forum/viewtopic.php?t=268277&sid=95be0b6c9909300d566006428bcbb97d] + +Compilation: +------------ + For the main program see doc/build.txt. + To compile the Spliter, run "make" from the "Spliter" directory. + To compile the Controller, run "make" from the "Controller" directory. + +Running on LINUX +---------------- +Under linux there are several options for both audio output and MIDI input. +Defaults are set at compile time and the desired backend can be set when starting ZynAddSubFX with the '-I' and '-O' options. +The currently supported backends are: + +- Audio Output + * ALSA (Advanced Linux Sound Architecture) + * OSS (Open Sound System) + * JACK (JACK Audio Connection Kit) + * Port Audio + +- MIDI Input + * ALSA + * OSS + * JACK + +Running on WINDOWS +------------------ +NOTE: At this time only older versions of ZynAddSubFX were compiled on windows + See older versions on sf.net (ie version 2.2.1) + If you launch zynaddsubfx.exe and nothing happens, you need pthreadGC.dll in the same directory (or windows directory). The dll files are distribuited with ZynAddSubFX windows binaries. + It might be possible that the latency will be very high. If this happens, you have to set the environment variable PA_MIN_LATENCY_MSEC to a value that represents the latency in miliseconds. + Eg: (in autoexec.bat or launched before running ZynAddSubFX) "set PA_MIN_LATENCY_MSEC=50" + Warning: if the value is too low, you might encounter severe dropouts on ZynAddSubFX. You'll have to set to a higher value and turn off automated background tasks (like virus scanners, email clients, etc.). + If you have more cards, you can select the desired card where you can play audio with the environment variable "PA_RECOMMENDED_OUTPUT_DEVICE" + Eg: "set PA_RECOMMENDED_OUTPUT_DEVICE=1" + A better way to set all of this, I will put on next versions. + + +Please send me instruments,banks,master settings,songs(midi+...xmz files) done with ZynAddSubFX. I'll appreciate this. + + +Have fun! :-) + +--The ZynAddSubFX team diff --git a/plugins/zynaddsubfx/zynaddsubfx/ZynAddSubFX.lsm b/plugins/zynaddsubfx/zynaddsubfx/ZynAddSubFX.lsm new file mode 100644 index 000000000..04322988b --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/ZynAddSubFX.lsm @@ -0,0 +1,22 @@ +Begin4 +Title: ZynAddSubFX +Version: 2.2.1 +Entered-date: 28-04-2005 +Description: A real-time software synthesizer for Linux with many + features, including polyphony, multi-timbral and microtonal + capabilities. It includes randomness of some parameters,which + makes warm sounds, like analogue synthesizers. + The program has system/insertion effects, too. + ZynAddSubFX has much more features, don't let be cheated by + the size of the source code :) +Keywords: synthesizer,MIDI,ALSA,music,X11,microtonal,real-time +Author: zynaddsubfx@yahoo.com (Nasca Paul) +Maintained-by: zynaddsubfx@yahoo.com (Nasca Paul) +Primary-site: zynaddsubfx.sourceforge.net + sourceforge.net/projects/zynaddsubfx + 920k ZynAddSubFX-2.2.0.tar.bz2 (source code) +Alternate-site: +Original-site: +Platforms: Linux with X11 +Copying-policy: GNU General Public License (version 2) +End diff --git a/plugins/zynaddsubfx/zynaddsubfx/bugs.txt b/plugins/zynaddsubfx/zynaddsubfx/bugs.txt new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/zynaddsubfx/zynaddsubfx/cmake/FindAlsa.cmake b/plugins/zynaddsubfx/zynaddsubfx/cmake/FindAlsa.cmake new file mode 100644 index 000000000..23003b39c --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/cmake/FindAlsa.cmake @@ -0,0 +1,69 @@ +# Alsa check, based on libkmid/configure.in.in. +# Only the support for Alsa >= 0.9.x was included; 0.5.x was dropped (but feel free to re-add it if you need it) +# It defines ... +# It offers the following macros: +# ALSA_CONFIGURE_FILE(config_header) - generate a config.h, typical usage: +# ALSA_CONFIGURE_FILE(${CMAKE_BINARY_DIR}/config-alsa.h) +# ALSA_VERSION_STRING(version_string) looks for alsa/version.h and reads the version string into +# the first argument passed to the macro + +# Copyright (c) 2006, David Faure, +# Copyright (c) 2007, Matthias Kretz +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(CheckIncludeFiles) +include(CheckIncludeFileCXX) +include(CheckLibraryExists) + +# Already done by toplevel +find_library(ASOUND_LIBRARY asound) +set(ASOUND_LIBRARY_DIR "") +if(ASOUND_LIBRARY) + get_filename_component(ASOUND_LIBRARY_DIR ${ASOUND_LIBRARY} PATH) +endif(ASOUND_LIBRARY) + +check_library_exists(asound snd_seq_create_simple_port "${ASOUND_LIBRARY_DIR}" HAVE_LIBASOUND2) +if(HAVE_LIBASOUND2) + message(STATUS "Found ALSA: ${ASOUND_LIBRARY}") +else(HAVE_LIBASOUND2) + message(STATUS "ALSA not found") +endif(HAVE_LIBASOUND2) +set(ALSA_FOUND ${HAVE_LIBASOUND2}) + +find_path(ALSA_INCLUDES alsa/version.h) + +macro(ALSA_VERSION_STRING _result) + # check for version in alsa/version.h + if(ALSA_INCLUDES) + file(READ "${ALSA_INCLUDES}/alsa/version.h" _ALSA_VERSION_CONTENT) + string(REGEX REPLACE ".*SND_LIB_VERSION_STR.*\"(.*)\".*" "\\1" ${_result} ${_ALSA_VERSION_CONTENT}) + else(ALSA_INCLUDES) + message(STATUS "ALSA version not known. ALSA output will probably not work correctly.") + endif(ALSA_INCLUDES) +endmacro(ALSA_VERSION_STRING _result) + + +get_filename_component(_FIND_ALSA_MODULE_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) +macro(ALSA_CONFIGURE_FILE _destFile) + check_include_files(sys/soundcard.h HAVE_SYS_SOUNDCARD_H) + check_include_files(machine/soundcard.h HAVE_MACHINE_SOUNDCARD_H) + + check_include_files(linux/awe_voice.h HAVE_LINUX_AWE_VOICE_H) + check_include_files(awe_voice.h HAVE_AWE_VOICE_H) + check_include_files(/usr/src/sys/i386/isa/sound/awe_voice.h HAVE__USR_SRC_SYS_I386_ISA_SOUND_AWE_VOICE_H) + check_include_files(/usr/src/sys/gnu/i386/isa/sound/awe_voice.h HAVE__USR_SRC_SYS_GNU_I386_ISA_SOUND_AWE_VOICE_H) + + check_include_file_cxx(sys/asoundlib.h HAVE_SYS_ASOUNDLIB_H) + check_include_file_cxx(alsa/asoundlib.h HAVE_ALSA_ASOUNDLIB_H) + + check_library_exists(asound snd_pcm_resume "${ASOUND_LIBRARY_DIR}" ASOUND_HAS_SND_PCM_RESUME) + if(ASOUND_HAS_SND_PCM_RESUME) + set(HAVE_SND_PCM_RESUME 1) + endif(ASOUND_HAS_SND_PCM_RESUME) + + configure_file(${_FIND_ALSA_MODULE_DIR}/config-alsa.h.cmake ${_destFile}) +endmacro(ALSA_CONFIGURE_FILE _destFile) + +mark_as_advanced(ALSA_INCLUDES ASOUND_LIBRARY) diff --git a/plugins/zynaddsubfx/zynaddsubfx/cmake/FindCxxTest.cmake b/plugins/zynaddsubfx/zynaddsubfx/cmake/FindCxxTest.cmake new file mode 100644 index 000000000..937142b58 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/cmake/FindCxxTest.cmake @@ -0,0 +1,200 @@ +# - Find CxxTest +# Find the CxxTest suite and declare a helper macro for creating unit tests +# and integrating them with CTest. +# For more details on CxxTest see http://cxxtest.tigris.org +# +# INPUT Variables +# +# CXXTEST_USE_PYTHON [deprecated since 1.3] +# Only used in the case both Python & Perl +# are detected on the system to control +# which CxxTest code generator is used. +# Valid only for CxxTest version 3. +# +# NOTE: In older versions of this Find Module, +# this variable controlled if the Python test +# generator was used instead of the Perl one, +# regardless of which scripting language the +# user had installed. +# +# CXXTEST_TESTGEN_ARGS (since CMake 2.8.3) +# Specify a list of options to pass to the CxxTest code +# generator. If not defined, --error-printer is +# passed. +# +# OUTPUT Variables +# +# CXXTEST_FOUND +# True if the CxxTest framework was found +# CXXTEST_INCLUDE_DIRS +# Where to find the CxxTest include directory +# CXXTEST_PERL_TESTGEN_EXECUTABLE +# The perl-based test generator +# CXXTEST_PYTHON_TESTGEN_EXECUTABLE +# The python-based test generator +# CXXTEST_TESTGEN_EXECUTABLE (since CMake 2.8.3) +# The test generator that is actually used (chosen using user preferences +# and interpreters found in the system) +# CXXTEST_TESTGEN_INTERPRETER (since CMake 2.8.3) +# The full path to the Perl or Python executable on the system +# +# MACROS for optional use by CMake users: +# +# CXXTEST_ADD_TEST( ) +# Creates a CxxTest runner and adds it to the CTest testing suite +# Parameters: +# test_name The name of the test +# gen_source_file The generated source filename to be +# generated by CxxTest +# input_files_to_testgen The list of header files containing the +# CxxTest::TestSuite's to be included in +# this runner +# +# #============== +# Example Usage: +# +# find_package(CxxTest) +# if(CXXTEST_FOUND) +# include_directories(${CXXTEST_INCLUDE_DIR}) +# enable_testing() +# +# CXXTEST_ADD_TEST(unittest_foo foo_test.cc +# ${CMAKE_CURRENT_SOURCE_DIR}/foo_test.h) +# target_link_libraries(unittest_foo foo) # as needed +# endif() +# +# This will (if CxxTest is found): +# 1. Invoke the testgen executable to autogenerate foo_test.cc in the +# binary tree from "foo_test.h" in the current source directory. +# 2. Create an executable and test called unittest_foo. +# +# #============= +# Example foo_test.h: +# +# #include +# +# class MyTestSuite : public CxxTest::TestSuite +# { +# public: +# void testAddition( void ) +# { +# TS_ASSERT( 1 + 1 > 1 ); +# TS_ASSERT_EQUALS( 1 + 1, 2 ); +# } +# }; +# + +#============================================================================= +# Copyright 2008-2010 Kitware, Inc. +# Copyright 2008-2010 Philip Lowman +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +# Version 1.4 (11/18/10) (CMake 2.8.4) +# Issue 11384: Added support to the CXX_ADD_TEST macro so header +# files (containing the tests themselves) show up in +# Visual Studio and other IDEs. +# +# Version 1.3 (8/19/10) (CMake 2.8.3) +# Included patch by Simone Rossetto to check if either Python or Perl +# are present in the system. Whichever intepreter that is detected +# is now used to run the test generator program. If both interpreters +# are detected, the CXXTEST_USE_PYTHON variable is obeyed. +# +# Also added support for CXXTEST_TESTGEN_ARGS, for manually specifying +# options to the CxxTest code generator. +# Version 1.2 (3/2/08) +# Included patch from Tyler Roscoe to have the perl & python binaries +# detected based on CXXTEST_INCLUDE_DIR +# Version 1.1 (2/9/08) +# Clarified example to illustrate need to call target_link_libraries() +# Changed commands to lowercase +# Added licensing info +# Version 1.0 (1/8/08) +# Fixed CXXTEST_INCLUDE_DIRS so it will work properly +# Eliminated superfluous CXXTEST_FOUND assignment +# Cleaned up and added more documentation + +#============================================================= +# CXXTEST_ADD_TEST (public macro) +#============================================================= +macro(CXXTEST_ADD_TEST _cxxtest_testname _cxxtest_outfname) + set(_cxxtest_real_outfname ${CMAKE_CURRENT_BINARY_DIR}/${_cxxtest_outfname}) + + add_custom_command( + OUTPUT ${_cxxtest_real_outfname} + DEPENDS ${ARGN} + COMMAND ${CXXTEST_TESTGEN_INTERPRETER} + ${CXXTEST_TESTGEN_EXECUTABLE} ${CXXTEST_TESTGEN_ARGS} -o ${_cxxtest_real_outfname} ${ARGN} + ) + + set_source_files_properties(${_cxxtest_real_outfname} PROPERTIES GENERATED true) + add_executable(${_cxxtest_testname} ${_cxxtest_real_outfname} ${ARGN}) + + if(CMAKE_RUNTIME_OUTPUT_DIRECTORY) + add_test(${_cxxtest_testname} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${_cxxtest_testname}) + elseif(EXECUTABLE_OUTPUT_PATH) + add_test(${_cxxtest_testname} ${EXECUTABLE_OUTPUT_PATH}/${_cxxtest_testname}) + else() + add_test(${_cxxtest_testname} ${CMAKE_CURRENT_BINARY_DIR}/${_cxxtest_testname}) + endif() + +endmacro(CXXTEST_ADD_TEST) + +#============================================================= +# main() +#============================================================= +if(NOT DEFINED CXXTEST_TESTGEN_ARGS) + set(CXXTEST_TESTGEN_ARGS --error-printer) +endif() + +find_package(PythonInterp QUIET) +find_package(Perl QUIET) + +find_path(CXXTEST_INCLUDE_DIR cxxtest/TestSuite.h) +find_program(CXXTEST_PYTHON_TESTGEN_EXECUTABLE + NAMES cxxtestgen cxxtestgen.py + PATHS ${CXXTEST_INCLUDE_DIR}) +find_program(CXXTEST_PERL_TESTGEN_EXECUTABLE cxxtestgen.pl + PATHS ${CXXTEST_INCLUDE_DIR}) + +if(PYTHONINTERP_FOUND OR PERL_FOUND) + include(FindPackageHandleStandardArgs) + + if(PYTHONINTERP_FOUND AND (CXXTEST_USE_PYTHON OR NOT PERL_FOUND OR NOT DEFINED CXXTEST_USE_PYTHON)) + set(CXXTEST_TESTGEN_EXECUTABLE ${CXXTEST_PYTHON_TESTGEN_EXECUTABLE}) + set(CXXTEST_TESTGEN_INTERPRETER ${PYTHON_EXECUTABLE}) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(CxxTest DEFAULT_MSG + CXXTEST_INCLUDE_DIR CXXTEST_PYTHON_TESTGEN_EXECUTABLE) + + elseif(PERL_FOUND) + set(CXXTEST_TESTGEN_EXECUTABLE ${CXXTEST_PERL_TESTGEN_EXECUTABLE}) + set(CXXTEST_TESTGEN_INTERPRETER ${PERL_EXECUTABLE}) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(CxxTest DEFAULT_MSG + CXXTEST_INCLUDE_DIR CXXTEST_PERL_TESTGEN_EXECUTABLE) + endif() + + if(CXXTEST_FOUND) + set(CXXTEST_INCLUDE_DIRS ${CXXTEST_INCLUDE_DIR}) + endif() + +else() + + set(CXXTEST_FOUND false) + if(NOT CxxTest_FIND_QUIETLY) + if(CxxTest_FIND_REQUIRED) + message(FATAL_ERROR "Neither Python nor Perl found, cannot use CxxTest, aborting!") + else() + message(STATUS "Neither Python nor Perl found, CxxTest will not be used.") + endif() + endif() + +endif() diff --git a/plugins/zynaddsubfx/zynaddsubfx/cmake/FindJACK.cmake b/plugins/zynaddsubfx/zynaddsubfx/cmake/FindJACK.cmake new file mode 100644 index 000000000..20379db8e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/cmake/FindJACK.cmake @@ -0,0 +1,17 @@ +#Find JACK Audio Connection Kit + +include(LibFindMacros) +libfind_pkg_check_modules(JACK jack) +find_path(JACK_INCLUDE_DIR + NAMES jack/jack.h + PATHS ${JACK_INCLUDE_DIRS} + ) + +find_library(JACK_LIBRARY + NAMES jack + PATHS ${JACK_LIBRARY_DIRS} + ) + +set(JACK_PROCESS_INCLUDES JACK_INCLUDE_DIR) +set(JACK_PROCESS_LIBS JACK_LIBRARY) +libfind_process(JACK) diff --git a/plugins/zynaddsubfx/zynaddsubfx/cmake/FindOSS.cmake b/plugins/zynaddsubfx/zynaddsubfx/cmake/FindOSS.cmake new file mode 100644 index 000000000..319c90ed6 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/cmake/FindOSS.cmake @@ -0,0 +1,9 @@ +# Find OSS (Open Sound System) +find_path(OSS_INCLUDE_DIR sys/soundcard.h) +set(OSS_LIBRARIES True) +mark_as_advanced(OSS_INCLUDE_DIR) + +# handle the QUIETLY and REQUIRED arguments and set OSS_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(OSS DEFAULT_MSG OSS_LIBRARIES OSS_INCLUDE_DIR) diff --git a/plugins/zynaddsubfx/zynaddsubfx/cmake/Findzlib.cmake b/plugins/zynaddsubfx/zynaddsubfx/cmake/Findzlib.cmake new file mode 100644 index 000000000..49edafd12 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/cmake/Findzlib.cmake @@ -0,0 +1,39 @@ +# - Find zlib +# Find the native ZLIB includes and library +# +# ZLIB_INCLUDE_DIRS - where to find zlib.h, etc. +# ZLIB_LIBRARIES - List of libraries when using zlib. +# ZLIB_FOUND - True if zlib found. + +#============================================================================= +# Copyright 2001-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distributed this file outside of CMake, substitute the full +# License text for the above reference.) + +IF (ZLIB_INCLUDE_DIR) + # Already in cache, be silent + SET(ZLIB_FIND_QUIETLY TRUE) +ENDIF (ZLIB_INCLUDE_DIR) + +FIND_PATH(ZLIB_INCLUDE_DIR zlib.h) + +SET(ZLIB_NAMES z zlib zdll) +FIND_LIBRARY(ZLIB_LIBRARY NAMES ${ZLIB_NAMES} ) +MARK_AS_ADVANCED( ZLIB_LIBRARY ZLIB_INCLUDE_DIR ) + +# Per-recommendation +SET(ZLIB_INCLUDE_DIRS "${ZLIB_INCLUDE_DIR}") +SET(ZLIB_LIBRARIES "${ZLIB_LIBRARY}") + +# handle the QUIETLY and REQUIRED arguments and set ZLIB_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZLIB DEFAULT_MSG ZLIB_LIBRARIES ZLIB_INCLUDE_DIRS) diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/Doxyfile b/plugins/zynaddsubfx/zynaddsubfx/doc/Doxyfile new file mode 100644 index 000000000..f48cdff2f --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/Doxyfile @@ -0,0 +1,309 @@ +# Doxyfile 1.5.7 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = ZynAddSubFX +PROJECT_NUMBER = 2.3.0 +OUTPUT_DIRECTORY = /home/mark/zynaddsubfx/doc/dox2/ +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = YES +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = /home/mark/zynaddsubfx/src/ +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 5 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +SYMBOL_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = YES +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = /home/mark/zynaddsubfx/src/ +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.vhd \ + *.vhdl \ + *.C \ + *.CC \ + *.C++ \ + *.II \ + *.I++ \ + *.H \ + *.HH \ + *.H++ \ + *.CS \ + *.PHP \ + *.PHP3 \ + *.M \ + *.MM \ + *.PY \ + *.F90 \ + *.F \ + *.VHD \ + *.VHDL +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +HTML_DYNAMIC_SECTIONS = NO +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHG_LOCATION = +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NONE +TREEVIEW_WIDTH = 250 +FORMULA_FONTSIZE = 10 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +DOT_FONTNAME = FreeSans +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = YES +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/IT/01-intro_IT.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/01-intro_IT.txt new file mode 100644 index 000000000..7db8b9575 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/01-intro_IT.txt @@ -0,0 +1,55 @@ +Come iniziare +--------------- +ZynAddSubFX è un sintetizzatore software piuttosto complesso, con un gran numero +di controlli. Perciò l'uso di ZynAddSubFX non è sempre ovvio. + +Molte applicazioni sotto Linux trasportano il MIDI con AlSA e trasmettono l'audio +con JACK. ZynAddSubFX può essere lanciato con questa configurazione eseguendo: + +------------------------------ +zynaddsubfx -I alsa -O jack -a +------------------------------ + +Questo imposta ALSA come driver input e JACK come driver audio, che dovrebbe +tentare di connettersi automaticamente alla tua scheda audio, per via del flag -a. +Se questa è la prima volta che lanci ZynAddSubFX, vedrai una schermata che ti lascia +scegliere tra l'interfaccia per principianti e quella avanzata. Attualmente +l'interfaccia 'beginner' è deprecata, quindi è raccomandato usare l'interfaccia +'advanced'. + +Ora dovresti essere in grado di vedere la finestra principale di ZynAddSubFX, dalla +quale puoi impostare patch, effetti e altre configurazioni generali, ma la cosa più +importante è che fornisce i collegamenti tra le patch. ZynAddSubFX è uno strumento +potente con una serie di patch di base, ma la vera forza sta dietro all'abilità di +creare delle patch personali. + +.Main Window +image::./images/uimain.png[] + +Per un utilizzo base, potrai usare il pulsante alla destra dell'etichetta 'enabled'. +Questo pulsante consentirà di selezionare lo strumento desiderato dai banchi +disponibili in ZynAddSubFX. Per suonare note in ZynAddSubFX puoi usare la tastiera +virtuale integrata (accessibile dal pulsante 'vK') oppure puoi connettere la tua +tastiera al sistema e usare *aconnect* per connetterla in ZynAddSubFX (supponendo +che si stia usando ALSA). + +Questa finestra principale consente l'accesso a molte features più avanzate. +Di cui alcune sono: + +* System Effects +* Insertion Effects +* Recording +* Part Settings (strumento impostazioni del livello) +* Master Settings +* Microtonal Settings + +Ad esempio, per utilizzare la funzione di registrazione deve essere selezionato un +file wave dal menù di registrazione, poi può essere avviata con il pulsante 'record' +e stoppata con il pulsante 'stop'. Questo è un modo semplice e veloce di registrare +alcuni samples da ZynAddSubFX, anche se ci sono strumenti con caratteristiche più +complete disponibili tramite gli strumenti di registrazione di JACK. + +NOTE: Dopo aver premuto 'record', il file wave non partirà la registrazione fino a +quando un nuovo tasto non sarà premuto da una sorgente MIDI esterna o dalla virtual +keyboard. Le proprietà dei 'System Effects' e degli 'Insertion Effects' sono +accessibili e disponibili così come le proprietà di ogni strumento. diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/IT/02-filter_IT.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/02-filter_IT.txt new file mode 100644 index 000000000..31e11bedd --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/02-filter_IT.txt @@ -0,0 +1,61 @@ +Filters +------- +:Author: Mark McCurry +:Date: June 24, 2012 + +ZynAddSubFX offre diversi tipi di filtri, che possono essere usati per modellare +lo spettro di un segnale. I parametri primari che influenzano le caratteristiche +del filtro sono 'cutoff', 'resonance', 'filter stages' e il tipo di filtro '(filter type)'. + +* *Cutoff (frequenza di taglio)*: Questo valore determina quale frequenza segna + il punto di cambiamento per il filtro. + In un filtro 'low-pass' (passa-bassi) questo + valore segna il punto da cui le frequenze alte + verranno attenuate. +* *Resonance*: La Resonance di un filtro determina l'enfatizzazione del filtro + sulla frequenza di taglio. In ZynAddSubFX è rappresentata dal + fattore 'Q', che è definito come la frequenza di taglio diviso + la larghezza di banda '(bandwidth)'. In altre parole un più alto + valore di Q causa un picco molto più stretto e risonante. +* *Stages*: Il numero di fasi di filtrazione in un dato filtro descrive quanto + bruscamente è in grado di applicare cambiamenti nella risposta in + frequenza. + +I filtri analogici di base '(analog)' che ZynAddSubFX offre sono mostrati di +seguito, con la frequenza centrale segnata con una linea rossa. I filtri a stato +variabile '(state variable)' dovrebbero essere molto simili. + +image:images/filter0.png[] + +Come detto in precedenza, il valore Q di un filtro influenza quanto concentrata è +l'energia del segnale nella frequenza di taglio; il risultato di differenti valori +di Q sono mostrati sotto. + +TIP: Per molti suoni analogici classici, alti valori di Q sono ussati sugli +'sweeping filters'. Un semplice low-pass filter con Q alto modulato da un forte +'envelope' solitamente è sufficiente per avere un buon suono. + +image:images/filter1.png[] + +Infine, l'effetto dell'ordine del filtro può essere visto sotto. Questo è +approssimativamente il numero di fasi del filtro '(filter stages)'. Per patches +più complesse è importante capire che la nitidezza in più nel filtro non è gratuita, +in quanto richiede molti più calcoli in corso di esecuzione; questo fenomeno +è più facilmente visibile nel 'subsynth', dove è facile avere bisogno di centinaia +di 'filter stages' per produrre una certa nota. + +image:images/filter2.png[] + +User Interface +~~~~~~~~~~~~~~ + +image:images/uifilter.png[] + +* *C.freq*: Cutoff frequency +* *Q*: Level of resonance for the filter +* *V.SnsA.*: Velocity sensing amount for filter cutoff +* *V.Sns.*: Velocity sensing function +* *freq.tr*: Frequency tracking amount. When this parameter is positive, higher + note frequencies shift the filter's cutoff frequency higher. +* *gain*: Additional gain/attenuation for filter +* *St*: Filter stages diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/IT/03-lfo_IT.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/03-lfo_IT.txt new file mode 100644 index 000000000..29a530c8d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/03-lfo_IT.txt @@ -0,0 +1,64 @@ +LFO +--- +:author: Paul Nasca + +Introduzione +~~~~~~~~~~~~ + +"LFO" significa Low Frequency Oscillator. Questi oscillatori non sono usati per +fare suoni da soli, ma per cambiare alcuni parametri (come le frequenze, le +ampiezze o i filtri). + +L'LFO ha alcuni parametri di base: + +* *Delay*: Questo parametro imposta il tempo tra l'inizio della nota e l'inizio + dell'LFO +* *Start Phase*: La posizione che avrà l'LFO alla partenza +* *Frequency*: La velocità dell'LFO (quanto velocemente il parametro è controllato + dai cambiamenti dell'LFO) +* *Depth*: L'ampiezza dell'LFO (quanto il parametro è controllato dai cambiamenti + dell'LFO) + +image:images/lfo0.png[] + +Un altro importante parametro dell'LFO è la forma '(shape)'. Ci sono molti +tipi di LFO in base alla forma. ZynAddSubFX supporta le seguenti forme: + +image:images/lfo1.png[] + +Un altro parametro è 'LFO Randomness' (casualità dell'LFO). +Esso modifica casualmente l'ampiezza o la frequenza dell'LFO ed in ZynAddSubFX puoi scegliere quanto, con questo parametro. +Nelle seguenti immagini sono mostrati alcuni esempi di casualità e come cambia la +forma d'onda triangolare dell'LFO. + +image:images/lfo2.png[] + +Altri parametri sono: + +* *Continous mode*: Se si utilizza questa modalità, l'LFO non partirà da "zero" +ad ogni nuova nota, ma sarà continuo. E' molto utile applicato sui filtri, per +fare interessanti 'sweeps'. +* *Stretch*: Controlla quanto la frequenza dell'LFO cambia in base alla frequenza +della nota. +Si può passare da 'stretch' negativo (la frequenza dell'LFO diminuisce sulle +note più alte) a zero (rimane la stessa in tutte le note) a 'stretch' positivo +(aumenta sulle note più alte). + +User Interface +~~~~~~~~~~~~~~ + +In ZynAddSubFX i parametri dell'LFO sono mostrati così: + +image:images/uilfo.jpg[] + +Questi parametri sono: + +* *Freq*: LFO Frequency +* *Depth*: LFO Depth +* *Start*: LFO Start Phase - +Se questo knob è al valore più basso, LFO Start Phase sarà random +* *Delay*: LFO Delay +* *A.R.*: LFO Amplitude Randomnes +* *F.R.*: LFO Frequency Randomness +* *C.*: LFO Continous Mode +* *Str.*: LFO Stretch - Nell'immagine sopra LFO Stretch è impostato a zero diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/IT/04-envelope_IT.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/04-envelope_IT.txt new file mode 100644 index 000000000..3586681f3 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/04-envelope_IT.txt @@ -0,0 +1,134 @@ +Envelopes +--------- + +Introduzione +~~~~~~~~~~~~ +Gli inviluppi controllano come cambiano nel tempo l'ampiezza, la frequenza o il +filtro. + +Amplitude Envelopes (inviluppi di ampiezza) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Questi inviluppi controllano l'ampiezza del suono. +In ZynAddSubFX, gli inviluppi di ampiezza possono essere lineari o logaritmici. +Nell'immagine seguente sono mostrate le differenze tra questi inviluppi. + +image:images/envelope1.png[Alt text] + +L'inviluppo di ampiezza è diviso in: +* *Attack*: Inizia all'attacco della nota (Note On). Il volume inizia da 0 al +massimo. In ZynAddSubFX l'attack è sempre lineare +* *Decay*: Il volume scende dal valore massimo ad un livello chiamato "Sustain level" +* *Sustain*: Il volume rimane costante fino a quando il tasto non viene +rilasciato (Note Off). Dopo questo, avviene l'ultima fase. +* *Release*: Il volume scende a zero. + +Frequency Envelopes +~~~~~~~~~~~~~~~~~~~ + +Questi inviluppi controllano la frequenza (o, più esattamente, il 'pitch') +degli oscillatori. +La seguente figura disegna le fasi di questi inviluppi. + +image::images/envelope2.png[Alt text] + +La linea puntinata rappresenta il pitch reale del suono senza inviluppo. + +Gli inviluppi di frequenza sono divisi in 3 fasi: + +* *Attack*: Inizia all'attacco della nota (Note On). La frequenza inizia da un +certo valore e "scivola" alla reale frequenza della nota. +* *Sustain*: La frequenza è la stessa per tutto il periodo di Sustain +* *Release*: Questa fase inizia con il Note Off e scala la frequenza della nota +ad un certo valore + +Filter Envelopes +~~~~~~~~~~~~~~~~ + +Questi inviluppi controllano la frequenza di cutoff dei filtri e sono divisi in: + +image:images/envelope3.png[Alt Text] + +* *Attack*: Inizia all'inizio della nota (Note On). La frequenza di cutoff inizia +ad un certo valore e scala ad un altro +* *Decay*: La frequenza di cutoff continua a scivolare verso il valore reale della +frequenza di cutoff del filtro (linea puntinata) +* *Sustain*: La frequenza di cutoff è la stessa per tutto il periodo di +Sustain (linea puntinata) +* *Release*: Questa fase inizia con il Note Off e scala la frequenza di cutoff della +nota ad un certo valore + +Freemode Envelopes +~~~~~~~~~~~~~~~~~~ + +Per tutti gli inviluppi c'è una modalità che consente all'utente di impostare un numero +arbitrario di fasi e punti di controllo. Questa modalità è chiamata 'Freemode'. + +image:images/envelope4.png[Alt Text] + +L'unica fase che rimane sempre definita è il Sustain, dove gli inviluppi si bloccano +fino ad un evento Note Off. + +User Interface +~~~~~~~~~~~~~~ +Tutti i tipi di inviluppi hanno alcuni controlli comuni: + +* *E*: Mostra una finestra che può far vedere la vera forma dell'inviluppo o convertirlo +in 'Freemode' per editarlo +* *Stretch*: Come l'inviluppo è allungato in base alla nota. +Sulle note più alte gli inviluppi sono più corti delle note più basse. +Nel valore più a sinistra lo 'stretch' è zero. +Il valore più a destra indica un allungamento del 200%; ciò significa che l'inviluppo +è allungato circa 4 volte/ottava. +* *frcR*: Release forzato. Se questa opzione è settata su On, il rilascio andrà al +valore finale anche se la fase di Sustain non è stata completata. +Di solito questo parametro è settato. + + +I parametri degli Amplitude Evelopes in ZynAddSubFX sono: + +image:images/uienvelope3.jpg[Alt Text] + +* *A.dt*: Durata Attack +* *D.dt*: Durata Decay +* *S.Val*: Valore di Sustain +* *R.dt*: Release time +* *L*: Se questa opzione è settata l'inviluppo è lineare, altrimenti sarà logaritmico. + + +Per i Frequency Envelopes l'interfaccia ha i seguenti parametri: + +image:images/uienvelope2.jpg[Alt Text] + +* *A.val*: Valore di Attack +* *A.dt*: Durata Attack +* *R.dt*: Release time +* *R.val*: Valore di Release + + +I Filter Evelopes hanno i parametri: + +image:images/uienvelope1.jpg[Alt Text] + +* *A.val*: Valore di Attack +* *A.dt*: Durata di Attack +* *D.val*: Valore di Decay +* *D.dt*: Decay time +* *R.dt*: Release time +* *R.val*: Valore di Release + +I Freemode Envelopes hanno una finestra separata per impostare parametri e controlli: + +image:images/uienvelope0.jpg[] + +* *Control points (punti di controllo)*: Puoi muovere i punti usando il mouse. +In basso a destra nella finestra viene mostrata la durata totale dell'inviluppo. +Se si clicka su un control point verrà visualizzata la durata della fase in cui +è quel punto. +* *Freemode*: Questo pulsante attiva o disattiva la modalità 'Freemode' +* *Add Point*: Aggiunge un punto dopo il punto correntemente selezionato. +Puoi selezionare un punto clickandoci su. +* *Delete point*: Rimuove il punto dall'inviluppo. +* *Sust.*: Imposta il punto di Sustain. E' mostrato usando la linea gialla. +* *Str.*: Envelope stretch + diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/IT/05-adsynth_IT.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/05-adsynth_IT.txt new file mode 100644 index 000000000..4a00c2f16 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/05-adsynth_IT.txt @@ -0,0 +1,78 @@ +AdSynth +------- + +AdSynth, principalmente un motore di sintesi additiva, è uno dei tre principali +motori di sintesi disponibili in ZynAddSubFX. +Il concetto base di questo motore è la somma di un insieme di voci, ciascuno dei +quali consiste in oscillatori. + +High Level (Global) +~~~~~~~~~~~~~~~~~~~ + +Il livello globale di AdSynth consiste negli elementi mostrati nella figura sotto: + +.AdSynth Global Elements +image::gen/ad-note.png[scalewidth="50%",width="700"] + +Il livello globale di AdSynth è quasi interamente composto dagli elementi +precedentemente discussi. +Comunque appaiono qui alcune nuove features, come: il rilevamento di velocità +'(velocity sensing)', 'punch', opzioni di detune e relativa bandwidth, resonance. + +.AdSynth Global Window +image::images/ad-global.png[] + +Il rilevamento di velocità è semplicemente una trasformazione esponenziale dalla +velocità della nota ad alcuni cambiamenti di parametro (!?). +Il diagramma sotto mostra come il 'velocity sensing' controlla questa traslazione +in tutto il range di velocity delle note possibili. + +.Velocity Sensing Chart +image::gen/velf.png[scalewidth="50%",width="600"] + +Il 'punch' di una nota in AdSynth è un'amplificazione costante dell'output +alla partenza della nota, con una lunghezza determinata dal 'punch time' e 'stretch' +e l'ampiezza determinata da 'punch strength' e 'velocity sensing'. +Il controllo relBw nel riquadro di frequenza è di fatto un moltiplicatore per +"scordare" '(detuning)' tutte le voci di una nota. + +NOTE: TODO Talk about resonance + + +La somma delle voci passa attraverso filtri e amplificazione per produrre il +suono finale. +Questro potrebbe far pensare che ad-note è solo un mucchio di post-elaborazione +minore e che a questo livello molto della generazione del suono è nascosta. + +Voices +~~~~~~ + +La voce da accesso ad un configurazione simile dei parametri globali più altre +cose come il modulatore, l'oscillatore e features di unison. + +.AdSynth Voice Window +image::images/ad-voice.png[] + +Modulation +^^^^^^^^^^ + +Tra le opzioni di modulazione si può selezionare: + +* Morph +* Ring Modulation +* Phase Modulation +* Frequency Modulation +* Disabled + +Unison +^^^^^^ + +Unison è utile nel creare il suono tipo chorus di più oscillatori simultanei. + +Oscillator +~~~~~~~~~~ + +NOTE: TODO show waveforms, talk about distortions somewhere, etc + +.Oscillator Window +image::images/uioscil.png[] diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/IT/06-controller_IT.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/06-controller_IT.txt new file mode 100644 index 000000000..9035666ed --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/06-controller_IT.txt @@ -0,0 +1,56 @@ +Controller +--------- + +image::./images/uicontroller.png[] + +General +~~~~~~~ + +* *ModWh*: Modulation Wheel depth (profondità della Modulation Wheel) +* *Exp MWh*: Exponential Modulation Wheel (cambia la scala di mudulazione in + esponenziale) +* *BwDpth*: Bandwidth Depth (profondità della Bandwidth) +* *Exp BW*: Exponential Bandwidth (cambia la scala di Bandwidth in esponenziale) +* *PanDpth*: Panning Depth (profondità del pan) +* *FltQ*: Filter Q (resonance) depth +* *FltCut* Filter Cutoff frequency depth (profondità della frequenza di taglio) +* *Expr*: Attiva/disattiva Expression +* *Vol*: Attiva/disattiva la ricezione di Volume controller +* *FMamp*: Attiva/disattiva la ricezione di Modulation Amplitude controller (76) +* *Sustain*: Attiva/disattiva sustain pedal +* *PWheelB.Rng (cents)*: Pitch Wheel Bend Range (cents; 100 cents = 1 semitono) + +Portamento +~~~~~~~~~~ + +* *Rcv.*: Controlla se la parte riceve il Portamento - On/Off (65) +* *time*: La durata del Portamento +* *thresh*: La soglia '(Threshold)' del Portamento. +Rappresenta il minimo o il massimo numero di semitoni (o centesimi di semitono) +richiesti per lo start del Portamento. +La differenza è calcolata tra l'ultima nota e la corrente. +* *th.type*: Il tipo di Threshold +Se selezionato significa che il Portamento si attiva quando la differenza +delle frequenze è al di sopra della soglia ("thresh"); non selezionato si attiva +quando è al di sotto della soglia. + +NOTE: La Threshold si riferisce alle frequenze e non alle note MIDI + (si potrebbe tenere in considerazione se si usano scale microtonali). + + +Proportional Portamento +^^^^^^^^^^^^^^^^^^^^^^ +//// +TODO: add graphs to explain prp.rate and prp. depth +//// + +* *Propt.*: Se il Portamento è proporzionale al rapporto '(Ratio)' delle frequenze +* *Prp. Rate*: Ratio necessaria per raddoppiare il tempo del Portamento +* *Prp. Dpth* + +Resonance +~~~~~~~~~ + +* *CFdpth*: Controlla la profondità del centro della Resonance +* *BWdpth*: Controlla la profondità della larghezza di banda (Bandwidth) della +Resonance diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/IT/08-saving_IT.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/08-saving_IT.txt new file mode 100644 index 000000000..211bf3653 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/08-saving_IT.txt @@ -0,0 +1,54 @@ +Persistence +----------- + +Come la gran parte delle applicazioni ZynAddSubFX consente di salvare il tuo +lavoro e ricaricarlo. + +Salvare tutto +~~~~~~~~~~~~~ + +Uno dei modi più semplici di salvare il tuo lavoro è quello di salvare l'intera +sessione. Questo può essere fatto dal menu File e il risultato sarà la creazione +di un .xmz file. +Una volta creato, questo file conterrà le tutte le impostazioni della sessione, +come le accordature microtonali, tutte le patch, tutti i tipi di effetti, ecc... + +Salvare delle parti +~~~~~~~~~~~~~~~~~~~ + +In alcuni casi salvare tutto non è quello che si desidera. +Di seguito c'è un esempio di salvataggio di una patch. + +Patches +^^^^^^^ +Per salvare una patch, si può salvare dal menu strumenti o dalla bank window. + +Con il menu strumenti si può solo salvare il file in una dato percorso con +l'estensione .xiz. + +Con il menu dei banchi si può assegnare una patch ad un certo slot con un banco. +Questo strumento rimarrà qui per un futuro uso fin quando non viene cancellato. +Per vedere la posizione fisica del file .xiz si può controllare la finestra in +File->Settings->Bank_Root_Dirs, per vedere i percorsi dei banchi. + +NOTE: Devi avere i permessi di scrittura per aggiungere uno strumento al banco. + +Presets +^^^^^^^ + +Avete un'impostazione favorita per un inviluppo o un oscillatore difficile da +rifare? Allora i preset fanno al caso vostro. +I preset consentono di salvare le impostazioni per ogni componente che supporta +operazioni di copia/incolla. +Questo viene fatto con i prest file (.xpz), che sono memorizzati nelle cartelle +indicate da File->Settings->Preset_Root_Dirs. + +Riepilogo +~~~~~~~ + +.Riepilogo delle estensioni +[literal] +xmz Everything +xiz Instrument +xsz Scale Settings +xpz Presets diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_A-mididefaults_IT.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_A-mididefaults_IT.txt new file mode 100644 index 000000000..2c73fb4ed --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_A-mididefaults_IT.txt @@ -0,0 +1,22 @@ +Appendice A: Default MIDI +------------------------- + +.Connessioni MIDI di Default +[literal] +001 - Modulation Wheel +007 - Volume +010 - Pan +011 - Expression +064 - Sustain +065 - Portamento Enable +071 - Filter Q +074 - Filter Cutoff +075 - Bandwidth(*) +076 - Modulation Amplitude(*) +077 - Resonance Center Frequency(*) +078 - Resonance Bandwidth(*) +120 - All Sounds Off +121 - Reset All Controllers +123 - All Notes Off + +Le voci con '(*)' non rientrano nelle specifiche General Midi. diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_B-build_IT.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_B-build_IT.txt new file mode 100644 index 000000000..34127ef63 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_B-build_IT.txt @@ -0,0 +1,61 @@ +Appendice B: Compilare ZynAddSubFX +-------------------------------- + +Introduzione a CMake +~~~~~~~~~~~~~~~~~~~~~ + +************************************************************************ +Note: Questa sezione è in gran parte copiata dal wiki di OpenSceneGraph: +http://www.openscenegraph.org/projects/osg/wiki/Build/CMake +************************************************************************ + +ZynAddSubFX usa CMake come suo sistema di compilazione unificato. CMake +è capace di leggere semplici build script dall'albero dei sorgenti e +creare da questo un sistema di compilazione specifico per la piattaforma. +Questo sistema di compilazione può avere la forma di VisualStudio project +files, Unix Makefiles o XCode project files. CMake è capace di individuare +automaticamente le dipendeze esterne e consente di attivare/disattivare +la compilazione del modulo e di configurare varie opzioni di compilazione. + +L'uso di un sistema di compilazione unificato consente di evitare rotture +di compilazione che erano presenti nel precedente metodo di compilazione, +ovvero quello di mantenere tre obiettivi di compilazione separati per +VisualStudio, Unix "make" and XCode. Si riduce anche l'onere della +manutenzione per gli sviluppatori base e collaboratori. +Nell'insieme, l'uso di CMake dovrebbe portare come risultato una migliore +coerenza e build più stabili su ogni piattaforma per gli utenti finali e +una maggiore produttività nello sviluppo di nuove versioni. +Speriamo che con una maggior coerenza di builds tra piattaforme renderà +più facile per gli sviluppatori utilizzare la 'development version' di +ZynAddSubFX e aiutare a contribuire ai testing e alle migliorie, +portando ad un codice di più alta qualità. + +Quick start guide +~~~~~~~~~~~~~~~~~ + +Per i più impazienti, qui c'è una guida veloce su come compilare +immediatamente ZynAddSubFX da sorgenti. + +************************************************************************ +Note: Ciò presuppone che tu abbia già una copia dei sorgenti. +************************************************************************ + +--------------------------------- +#enter the source directory +cd zynaddsubfx + +#make a directory for an out-of-source build +mkdir build +cd build + +#generate a cmake build project here from the cmake root, which is +#found in the directory below the current one +cmake .. + +#OPTIONAL: Adjust compile variables in the Cache file: +ccmake . + +#And finally, build as usual using make +make +--------------------------------- + diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_C-doc_getting_IT.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_C-doc_getting_IT.txt new file mode 100644 index 000000000..3c1ba8ad0 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/APPENDIX_C-doc_getting_IT.txt @@ -0,0 +1,63 @@ +Appendix C: Ottenere ZynAddSubFX +------------------------------- + +Normalmente ci sono diversi modi per ottenere una copia di ZynAddSubFX. + +SourceForge:: + http://sourceforge.net/projects/zynaddsubfx/files/ +Distribuition:: + apt/yum/others +Git:: + git clone git://git.code.sf.net/p/zynaddsubfx/code zynaddsubfxg + +Introduzione a Git +~~~~~~~~~~~~~~~~~~~ + +Per coloro che vogliono vivere "on the bleeding edge" o chi vuole essere certo che +la prossima release abbia meno bug, si può avere il codice con git. +Git è usato per gestire il codice sorgente di questo progetto ed è utile per +ottenere velocemente e facilmente una copia "up-to-date" del codice sorgente. + +Ottenere il codice sorgente +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Per avere una copia dei sorgenti di ZynAddSubFX tutto il necessario da fare è: + +--------------------------------------------- +git clone git://git.code.sf.net/p/zynaddsubfx/code zynaddsubfx + +cd zynaddsubfx + +#Download additional resources +git submodule init +git submodule update +--------------------------------------------- + +Ora dovresti essere nella directory del codice sorgente. + +Per compilare facilmente si guardi l'Appendice B del manuale. + +Dare un'occhiata ai branch +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Diciamo che lo sviluppo si è esteso ad una creazione di una feature che si vuole +vedere in anteprima. +Per il bene di questa guida, supponiamo che il nome del branch della feature che +verrà è 'foo'. + +----------------------------------------- +#checkout the foo branch from sourceforge +git checkout --track -b foo origin/foo + +#lets checkout the primary branch again +git checkout master + +#hop back to the other branch +git checkout foo +---------------------------------------- + +Ora si dovrebbe essere in grado di cambiare branches e andare nella build-directory +(come descritto in Appendice B) e ricompilare ZynAddSubFX. + +NOTE: Quando si usano i branches può venir meno un po' di stabilità. + diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/IT/zynaddsubfx_IT.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/zynaddsubfx_IT.txt new file mode 100644 index 000000000..1d9c3a035 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/IT/zynaddsubfx_IT.txt @@ -0,0 +1,28 @@ +Zynaddsubfx +=========== +:Author: Paul Nasca and Mark McCurry, translated by Ilario Glasgo + +include::01-intro_IT.txt[] + +include::02-filter_IT.txt[] + +include::03-lfo_IT.txt[] + +include::04-envelope_IT.txt[] + +include::05-adsynth_IT.txt[] + +include::06-controller_IT.txt[] + +///////////////////////////// +include 07-effects_IT.txt +include nrpn.txt +///////////////////////////// + +include::08-saving_IT.txt[] + +include::APPENDIX_A-mididefaults_IT.txt[] + +include::APPENDIX_B-build_IT.txt[] + +include::APPENDIX_C-doc_getting_IT.txt[] diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/README.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/README.txt new file mode 100644 index 000000000..5f1dcaa08 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/README.txt @@ -0,0 +1,9 @@ +Requirements for this directory: + + * standalone (latex module) + * pgfplots (latex module) + * pst-sigsys (latex module) + * auto-pst-pdf (latex module) + * pst-tools (latex module) + * gnuplot + diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/adsynth.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/adsynth.txt new file mode 100644 index 000000000..545745daf --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/adsynth.txt @@ -0,0 +1,148 @@ +AdSynth +------- + +AdSynth, a primarily additive synthesis engine, is one of the three major +synthesis engines available in ZynAddSubFX. +The basic concept of this engine is the summation of a collection of voices, +each of which consist of oscillators. + +High Level (Global) +~~~~~~~~~~~~~~~~~~~ + +AdSynth's global level consists of the elements shown in the below figure: + +.AdSynth Global Elements +image::gen/ad-note.png[scalewidth="50%",width="700"] + +The global level of adsynth is almost entirely composed of previously discussed +elements. +However a few new features appear here, this includes velocity sensing, punch, +detune options and realative bandwidth , and resonance. + +.AdSynth Global Window +image::images/ad-global.png[] + + +Velocity sensing is simply an exponental transformation from the note's velocity +to some parameter change. +The below diagram shows how the velocity senseing controls affects this +translation over the whole range of possible note velocities. + +.Velocity Sensing Chart +image::gen/velf.png[scalewidth="50%",width="600"] + +The puch of a note in AdSynth is a constant amplification to the output at the +start of the note, with its length determined by the punch time and stretch and +the amplitude being determined by the punch strength and velocity sensing. +The relBW control in the frequency pane is effectively a multiplier for detuning +all voices within an adnote. + +NOTE: TODO Talk about resonance + + +The sum of the voices are passed through filters and amplification to produce +the final sound. +This could lead one to think that ad-note is just a bunch of minor +postprocessing and at this level much of the sound generation is hidden. + +Voices +~~~~~~ + +The voice gives access to a similar setup to the global parameters and then some +more, such as the modulator, oscillator, and unison features. + +.AdSynth Voice Window +image::images/ad-voice.png[] + +Modulation +^^^^^^^^^^ + +Within the options for modulation, one can select: + +* Morph +* Ring Modulation +* Phase Modulation +* Frequency Modulation +* Disabled + +Unison +^^^^^^ + +Unison is useful in creating the chorus like sound of many simultaneous +oscillators + +Oscillator +~~~~~~~~~~ + +The oscillator is lets you choose the basic waveform, which oscillates while +the sound is playing and is then further modified. + +.Oscillator Window +image::images/uioscil.png[] + +[[adsynth::oscilllator::types_of_waveshaping, Types of Waveshaping]] +Types of Waveshaping +^^^^^^^^^^^^^^^^^^^^ + +Waveshaping can be done using the *Wsh* area in the Oscillator editor. + +The type of distortion has much influence on how the overtones are being placed. +Sometimes, you get a "fat" bass, and sometimes, high frequencies are added, +makeing the sound "crystal clear". + +Atan & Sigmoid +++++++++++++++ + +This is the default setting. It is an easy way to apply loudness to a +wave without getting undesired high overtones. Thus, it can be used both for +making instruments that sound like "real" ones, but also for electronic music. +The transformation turns, roughly said, every amplitude into a square amplitude. +Thus, sine, power, pulse and triangle turn into a usual square wave, while a saw +turns into a phased square wave. A chirp wave turns into a kind of phase +modulated square wave. + +Quants +++++++ + +http://en.wikipedia.org/wiki/Quantization_%28sound_processing%29[Quantization] +adds high overtones early. It can be seen as an unnatural effect, which is often +used for electronic music. + +The transformation is a bit similar to building +the http://en.wikipedia.org/wiki/Riemann_sum[lower sum] of a wave, +mathematically said. This means that the transformation effect turns your +"endless high" sampled wave into only a few samples. The more distortion you +will apply, the less samples will be used. Indeed, this is equivalent to say +that more input amplification is used. To see this, here is a small sample of +code, where "ws" is the (correctly scaled amount of input amplification, and "n" +the number of original samples. + +--------------------------------- +for(i = 0; i < n; ++i) + smps[i] = floor(smps[i] / ws + 0.5f) * ws; +--------------------------------- + +NOTE: If you turn on quantisation very high, you might be confused +that, especially high notes, make no sound. The reason: High frequencies are +"forgotten" if you sample with only few samples. Also, the sign of an amplitude +can be forgotten. This behaviour might make some quantisations a bit unexpected. + +Limiting & Clipping ++++++++++++++++++++ + +http://en.wikipedia.org/wiki/Limiting[Limiting] usually means that for a signal, +the amplitude is modified because it exceeds its maximum value. Overdrive, as +often used for guitars, is often achieved by limiting: It happens because an +amplifier "overdrives" the maximum amplitude it can deliver. + +ZynAddSubFX has two types of limiting. Soft limiting, here as *Lmt*, means +that the sound may not exceed a certain value. If the amplitude does so, it will +simply be reduced to the limiting value. The overtones are generated in the +lower frequencies first. + +Hard limiting, is also called clipping and abbreviated *Clip*. This means that +if the maximum is exceeded, instead of being constant at the limiting value, the +original signal still has some influence on the output signal. Still, it does +not exceed the limiting value. For ZynAddSubFX, a signal exceeding the limiting +value will continue to grow "in the negative". This leads to overtones being +generated on the full frequency band. \ No newline at end of file diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/build.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/build.txt new file mode 100644 index 000000000..35de26af1 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/build.txt @@ -0,0 +1,59 @@ +Appendix B: Building ZynAddSubFX +-------------------------------- + +Introduction to CMake +~~~~~~~~~~~~~~~~~~~~~ + +******************************************************************** +Note: This section is mostly copied from the OpenSceneGraph wiki, at: +http://www.openscenegraph.org/projects/osg/wiki/Build/CMake +******************************************************************** + +ZynAddSubFX uses CMake as its unified build system. CMake +is able to read simple build scripts from the source tree and create +from this a platform-specific build system. This build system can be in +the form of VisualStudio project files, Unix Makefiles or XCode project +files. CMake is able to automatically locate external dependencies, and +allows you to toggle on/off module compilation and configure various +build options. + +The use of a unified build system has allowed to avoid build breakages +that were common in the previous build method of maintaining three +separate build targets for VisualStudio, Unix "make" and XCode. It also +reduces the maintenance burden for core developers and contributors. +Taken together usage of CMake should result in better consistency and +more stable builds across all platforms for end users and a greater +productivity in development of new versions. Hopefully with greater +consistency of builds across platforms it will be easier for developers +to use the development version of ZynAddSubFX and help contribute +to its testing and refinement, leading to a high-quality code base. + +Quick start guide +~~~~~~~~~~~~~~~~~ + +For the impatient ones, here is a quick guide on how to immediately +build ZynAddSubFX from source. + +************************************************************** +Note: This assumes that you already have a copy of the source. +************************************************************** + +--------------------------------- +#enter the source directory +cd zynaddsubfx + +#make a directory for an out-of-source build +mkdir build +cd build + +#generate a cmake build project here from the cmake root, which is +#found in the directory below the current one +cmake .. + +#OPTIONAL: Adjust compile variables in the Cache file: +ccmake . + +#And finally, build as usual using make +make +--------------------------------- + diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/controller.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/controller.txt new file mode 100644 index 000000000..803556595 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/controller.txt @@ -0,0 +1,54 @@ +Controller +--------- + +image::./images/uicontroller.png[] + +General +~~~~~~~ + +* *ModWh*: Modullation Wheel depth +* *Exp MWh*: Exponental Modulation Wheel (changes modulation scale to + exponental) +* *BwDpth*: Bandwidth Depth +* *Exp BW*: Exponental Bandwidth (changes badwidth scale to exponental) +* *PanDpth*: Panning Depth +* *FltQ*: Filter Q (ressonance) depth +* *FltCut* Filter Cutoff frequency depth +* *Expr*: enable/disable expression +* *Vol*: enable/disable receiving volume controller +* *FMamp*: enable/disable receiving Modulation Amplitude controller (76) +* *Sustain*: enable/disable sustain pedal +* *PWheelB.Rng (cents)*: Pitch Wheel Bend Range (cents; 100 cents = 1 halftone) + +Portamento +~~~~~~~~~~ + +* *Rcv.*: If the part receives portamento On/Off (65) controller +* *time*: The duration of the portamento +* *thresh*: The threshold of the portamento. +It represents the minimum or the maximum number of halftones +(or hundried cents) required to start the portamento. +The difference is computed between the last note and current note. +* *th.type*: The threshold type. +Checked means that the portamento activates when the difference of frequencies +is above the threshold ("thresh"); not checked is for below the threshold. + +NOTE: The threshold refers to the frequencies and not to MIDI notes + (you should consider this if you use microtonal scales). + + +Proportinal Portamento +^^^^^^^^^^^^^^^^^^^^^^ +//// +TODO: add graphs to explain prp.rate and prp. depth +//// + +* *Propt.*: If the portamento is proportinal to ratio of frequencies +* *Prp. Rate*: Ratio needed to double the time of portamento +* *Prp. Dpth*: The divergence from + +Resonance +~~~~~~~~~ + +* *CFdpth*: resonance center controller depth +* *BWdpth*: resonance bandwidth controller depth diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/effects.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/effects.txt new file mode 100644 index 000000000..a0d93f2de --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/effects.txt @@ -0,0 +1,531 @@ +Effects +------- + +Effects are, generally, blackboxes that transform audio signals in a +specified way. More exactly, the only input data for an effect in ZynAddSubFX +is: + +* an array of samples, which is read *on line* +* the current system time (used for LFOs) + +The output is the transformed array of samples. + +NOTE: As described, effects have no information about anything else. For +example, key presses are not recognized. Therefore, pressing a key does not +initiate the LFO. Phase knobs will always be relative to a *global* LFO, which is +only dependent on the system time. + +ZynAddSubFX has 3 types of effects: + +* System Effects +* Insertion Effects +* Instrument Effects + +TODO: Describe these 3 types (their differences). + +[[effects::general_topics, General Topics]] +General topics +~~~~~~~~~~~~~~ + +* *Wetness* determines the mix of the results of the effect and its input. This +mix is made the effects output. If an effect is wet, it means that nothing of +the input signal is bypassing the effect. If it is dry, then the effect has no +effect. TODO: Difference between Volume and D/W? +* *Pan* lets you apply panning, which means that the sound source can move to +the right or left. Set it to 0.0 to only hear output on the right side, or to +the maximum value to only hear output on the left side. +* *LRc.* or *L/R* let you apply crossover. +* *Filter stages* are the number of times that this filter is applied in series. +So, if this number is 1, you simply have this one filter. If it is two, the +sound first passes the filter, and the results then pass the same filter again. +In ZynAddSubFX, the wetness is applied after all stages were passed. +* *LFOs* are, as the name says, oscillators with, compared to the frequency of +the sound, low frequency. They often appear in order to control the effect. +They can have some of the following controls: +** *LFO Type* determines the shape of the LFO. If not present, the LFO is a +sine wave. +** *Freq* determines the LFO's frequency. +** *Dpth* is a multiplier to the LFO. Thus, it determines the LFOs amplitude +and its influence. +** *Rnd* is the LFO amplitude's randomness +** *St.df* lets you determine how much left and right LFO are phase shifted. +64.0 means stereo, higher values increase the right LFO relatively to the left +one. +******************************************************************** +Hint: Keep in mind that Effects that can be controlled by LFO can also be +controlled arbitrary: Set the LFO depth to zero and manipulate the phase knob +(e.g. with NRPNs or maybe via OSC in the future). +******************************************************************** + +Equalizer +~~~~~~~~~ + +Introduction +^^^^^^^^^^^^ + +An http://en.wikipedia.org/wiki/Filter_%28signal_processing%29[equalizer] is a +filter effect that applies different volume to different frequencies of the +input signal. This can, for example, be used to "filter out" unwanted +frequencies. +ZynAddSubFX's implementations follow the +http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt["Cookbook formulae for +audio EQ"] by Robert Bristow-Johnson. + +Filter Types +^^^^^^^^^^^^ + +This topic is completely discussed in <>. + +Usage +^^^^^ + +We describe all parts of the GUI here. The term passband (or often just "band") +refers to the amount of frequencies which are not significantly attenuated by +the filter. + +* *Gain* (on the left) defines an offset which is added to the complete filter. +* *B.* lets you choose the passband number. Multiple passbands define one +filter. This is important if you want multiple filters to be called after each +other. Note that filters are commutative. +* *T.* lets you choose the current filter's type, as described above. +* *Freq* describes the frequencies where the filter has its poles. For some +filters, this is called the "cutoff" frequency. Note, however, that a bandpass +filter has two cutoff frequencies. +* *Gain* is only active for some filters and sets the amount of a special peak +these filters have. Note that for those filters, using the predefined gain makes +them effectless. +* *Resonance* lets you describe a peak at the given frequency for filters with +2 poles. This can be compared to real physical objects that have more gain at +their resonance frequency. +* *St.* lets you define multiple filter stages. This is equivalent to having +multiple copies of the same filter in sequence. + +Chorus +~~~~~~ + +Introduction +^^^^^^^^^^^^ + +In a chorus, many people sing together. Even if each of them sings at exactly +the same frequency, all their voices usually sound different. We say they have a +different timbre. Timbre is the way we perceive sound and makes us differ +between different music instruments. This is, physically, achieved by varying +both the amplitude envelope and the frequency spectrum. Multiple sounds with +slightly different timbres make a sound more shimmering, or powerful. This is +called the chorus effect. + +Function +^^^^^^^^ + +The chorus effect can be achieved by multiple people singing together. In +a concert, there are many instruments, resulting in the same effect. When making +electronic music, we only have an input wave and need to generate these +different timbres by ourselves. ZynAddSubFX therefore simply plays the sound, +pitch modulated by an LFO, and adds this to the original sound. This explains +the diagram below: The multiple pitches are generated by a delayed version of +the input. This version is being pitched by an LFO. More detailled, this pitch +is generated by varying the reading speed of the delayed sound; the variation +amount is controlled by an LFO. + +image:./gen/chorus.png[width=700, + title="The chorus effect. z^(-n.m) describes the delay."] + +TODO: Add LFO pointing to delay? + +Related effects to Chorus are Flangers. Flangers can be described as Chorus +with very short LFO delay and little LFO depth. You can imagine a flanger as two +copies of a sound playing at almost the same time. This leeds to interference, +which can be clearly heared. It is popular to apply flangers to guitars, giving +them more "character". + +Usage +^^^^^ + +* First, crossover is applied. +* The following 5 knobs (*Freq*, *Rnd*, *LFO Type*, *St.df*, *Depth*) control +the LFO for the pitch. If the depth is set to zero, the pitch will not be +changed at all. +* *Delay* is the time that the delayed sound is delayed "on average". Note that +the delay also depends on the current pitch. +* After the correct element of the sound buffer is found using the LFO, the +*Fb* knob lets you set how loud it shall be played. This is mostly redundant to +the *D/W* knob, but we have not applied panning and substraction yet. +* Next, the singal can be negated. If the *Substract* Checkbox is activated, +the amplitude is multiplied by -1. +* Finally, *Pan* lets you apply panning. + +Distortion +~~~~~~~~~~ + +Introduction +^^^^^^^^^^^^ + +Distortion means, in general, altering a signal. Natural instruments +usually produce sine like waves. A wave is transformed in an unnatural way when +distortion is used. The most distorted waves are usually pulse waves. It is +typical for distortion to add overtones to a sound. Distortion often increases +the power and the http://en.wikipedia.org/wiki/Loudness[loudness] of a signal, +while the db level is not increased. This is an important topic in the +http://en.wikipedia.org/wiki/Loudness_war[Loudness War]. + +NOTE: As distortion increases loudness, distorted music can cause ear damage +at lower volume levels. Thus, you might want to use it a bit careful. + +Distortion can happen in many situations when working with audio. Often, this is +not wanted. In classical music, for example, distortion does not occur +naturally. However, distortion can also be a wanted effect. It is typical for +Rock guitars, but also present in electronic music, mostly in Dubstep and +DrumNBass. + +The basic components of distortion are mainly + +* a preamplifier +* the waveshaping function +* filters + +Preamplification changes the volume before the wave is shaped, and is indeed the +amount of distortion. For example, if you clip a signal, the louder the input +gets, the more distortion you will get. This can have different meanings for +different types of distortions, as described below. + +******************************************************************** +The filters are practical. A reason for using them afterwards is that distortion +can lead to waves with undesired high frequency parts. Those can be filtered out +using the LPF. A reason for using filters before applying is to achieve +multiband distortion. ZynAddSubFX has no "real" multiband distortion by now, +however. +******************************************************************** + +Types of Distortion +^^^^^^^^^^^^^^^^^^^ + +This topic is completely discussed in +<>. Note +that you can use the +Oscillator editor in order to find out what your distortion effect does. Also +note that while the Oscillator editor's distortion is limited to some +oscillators you can produce in the Oscillator editor, the distortion effect can +be used on every wave that you can generate with ZynAddSubFX. + +Function +^^^^^^^^ + +We explain the functionality in a diagram and list the components below. + +image:./gen/distort.png[width=700, + title="The components of a distortion function."] + +* Negation is the first thing to happen. If the *Neg* Checkbox is activated, the +amplitude is multiplied by -1. +* Panning is applied. Note, however, that you have to activate the +Stereo Checkbox, labeled *St*, before. +* Pre amplification is done next. The amount can be changed using the +*Drive* nob. Indeed, this is the amount of distortion. For example, if you clip +a signal, the louder the input gets, the more distortion you will get. This can +have different meanings for different types of distortion, as described above. +* *HPF* and *LPF* are filters with 2 poles. Whether they are used before or +after the waveshape, depends on the checkbox labeled *PF*. +* The next step is the wave shape. This defines how the wave is +actually modified. The *Type* ComboBox lets you define how. We will discuss some +types below. +* After the wave shape, we scale the level again. This is called +output amplification. You can change the value using the *Level* knob. +* Crossover is the last step. This is controlled by the knob *LR Mix* and +means that afterwards, a percentage of the left side is applied to the right +side, and, synchronously, the other way round. It is a kind of interpolation +between left and right. If you set the LR Mix to 0.0, you will always have a +stereo output. + +Dynamic Filter +~~~~~~~~~~~~~ + +Introduction +^^^^^^^^^^^^ + +A dynamic filter is, as the name says, a filter which changes its parameters +dynamically, dependent on the input and current time. In ZynAddSubFX, +frequency is the only variable parameter. It can be used as an "envelope +following filter" (sometimes referenced "Auto Wah" or simply "envelope filter"). + +Function +^^^^^^^^ + +Though this filter might look a bit complicated, it is actually easy. We divide +the parameters into two classes: + +* *Filter Parameters* are the ones you get when you click on *Filter*. They +give the filter its basic settings. +* *Effect Parameters* are the other ones that control how the filter changes. + +The filter basically works like this: The input signal is passed through a +filter which dynamically changes its frequency. The frequency is an additive of: + +* the filter's base frequency +* an LFO from the effect parameters +* the "amplitude" of the input wave + +image:./gen/dynamic.png[width=700, + title="The components of a dynamical filter"] + +The amplitude of the input wave is not the current amplitude, but the so called +https://en.wikipedia.org/wiki/Root_mean_square["Root Mean Square (RMS)"] value. +This means that we build a mean on the current amplitude and the past values. +How much the new amplitude takes influence is determined by the *Amplitude +Smoothness* (see below). + +******************************************************************** +RMS value plays an important role in the term loudness. A fully distorted +signal can sound 20 db louder due to its higher RMS value. This filter takes +this into account, depending on the smoothness. +******************************************************************** + +Usage +^^^^^ + +* The 4 knobs in the middle (*Freq*, *Rnd*, *LFO Type*, *St.df*) control the +LFO. +* Two knobs let you control the way how the RMS value of the amplitudes is +measured: +** *A.M* sets the Amplitude Smoothness (this is described above). The higher +you set this value, the more slow will the filter react. +** *A.Inv.*, if being set, negates the (absolute) RMS value. This will lower +the filter frequency instead of increasing it. Note that this will not have +much effect if the effects input is not very loud. +* The following controls define the mix of the LFO and the amplitude. +** *A.S* sets the Amplitude Sensing (i.e. how much influence the amplitude +shall have). +** *LfoD* sets the LFO depth. +* The filter button lets you choose the filter type. +* After the input signal has passed through the filter, *Pan* can apply +panning. + +Echo +~~~~ + +Introduction +^^^^^^^^^^^^ + +The echo effect, also known as +http://en.wikipedia.org/wiki/Delay_%28audio_effect%29[delay effect], simulates +the natural reflection of a sound. The listener can hear the sound multiple +times, usually decreasing in volume. Echos can be useful to fill empty parts of +your songs with. + +Function +^^^^^^^^ + +In ZynAddSubFX, the echo is basically implemented as the addition of the +current sound and a delayed version of it. The delay is implemented as in the +picture below. First, we add the delayed signal to the effect input. Then, +they pass an LP1. This shall simulate the effect of dampening, which means that +low and especially high frequencies get lost earlier over distance than middle +frequencies do. Next, the sound is delayed, and then it will be output and added +to the input. + +image:./gen/echo.png[width="700", + title="The echo includes a fb line, labeled as z^-n, and a delay."] + +******************************************************************** +The exact formula in the source code for the dampening effect is as follows: + +latexmath:[$Y(t) := (1-d) \cdot X(t) + d \cdot Y(t-1)$], + +where latexmath:[t] be the time index for the input +buffer, latexmath:[d] be the dampening amount and latexmath:[X,Y] be the input, +respective the output of the dampening. This solves to + +latexmath:[$Y(z) = Z(Y(t)) = (1-d) \cdot X(z) + d \cdot Y(z) \cdot z^{-1}$] + +latexmath:[$\Leftrightarrow H(z) := \frac{Y(z)}{X(z)} = \frac{1-d}{1 - +d \cdot z^{-1}}$] + +which is used in latexmath:[$Y(z) = H(z) \cdot X(z)$]. So latexmath:[$H(z)$] is +indeed a filter, and by looking at it, we see that it is an LP1. Note that +infinite looping for d=1 is impossible. +******************************************************************** + +Description +^^^^^^^^^^^ + +* *Pan* lets you apply panning of the input. +* *Delay* sets the time for one delay. +* *LRdl.* means Left-Right-Delay. If it is set to the middle, then both sides +are delayed equally. If not, then the left echo comes earlier and the right +echo comes (the same amount) later than the average echo; or the other way +round. Set the knob to 0 to hear on the right first. +* *LRc.* applies crossover. +* Feedback describes how much of the delay is added back to the input. Set +*Fb.* to the maximum to hear an infinite echo, or to the minimum to just +hear a single repeat. +* The *Damp* value lets the LP1 reject higher frequencies earlier if +increased. + +Reverb +~~~~~~ + +Introduction +^^^^^^^^^^^^ + +A http://en.wikipedia.org/wiki/Reverberation[Reverberation] actually expresses +the effect of many echoes being played at the same time. This can happen in an +enclosed room, where the sound can be reflected in different angles. Also, in +nature, thunders approximate reverbs, because the sound is reflected in many +different ways, arriving at the listener at different times. + +In music, reverbs are popular in many ways. Reverbs with large room size can be +used to emulate sounds like in live concerts. This is useful for voices, pads, +and hand claps. A small room size can simulate the sound board of string +instruments, like guitars or pianos. + +Function +^^^^^^^^ + +As mentioned, a reverb consists of permanent echo. The reverb in ZynAddSubFX is +more complex than the echo. After the delaying, comb filters and then allpass +filters are being applied. These make the resulting sound more realistic. The +parameters for these filters depend on the roomsize. For details, consider the +information about https://ccrma.stanford.edu/~jos/pasp/Freeverb.html[Freeverb]. + +image:./gen/reverb.png[width=700, + "The reverb, being similar to the echo."] + +Description +^^^^^^^^^^^ + +* The *Type* ComboBox lets you select a reverb type: +** *Freeverb* is a preset. It was proposed by Jezar at Dreampoint. +** *Bandwidth* has the same parameters for the comb and allpass filters, but it +applies a unison before the LP/HP. The unison's bandwidth can be set using *bw*. +** Random chooses a random layout for comb and allpass each time the type or +the roomsize is being changed. +* The roomsize (*R.S.*) defines parameters only for the comb and allpass +filters. +* *Time* controls how long the whole reverb shall take, including how slow the +volume is decreased. +* The initial delay (*I.del*) is the time which the sounds need at least to +return to the user. The initial delay feedback (*I.delfb*) says how much of the +delayed sound is added to the input. +* Low pass filter (*LPF*) and high pass filter (*HPF*) can be applied before +the comb filters. +* The dampening control (*Damp*) currently only allows to damp low frequencies. +Its parameters are being used by the comb and allpass filters. +* *Pan* lets you apply panning. This is the last thing to happen. + + +Phaser +~~~~~~ + +Introduction +^^^^^^^^^^^^ + +The http://en.wikipedia.org/wiki/Phaser_%28effect%29[Phaser] is a special +dynamic filter. The result is a sweeping +sound, which is +often used on instruments with a large frequency band, like guitars or strings. +This makes it typical for genres like rock or funk, where it is often modulated +with a pedal, but also for giving strings a warm, relaxing character. + +Function +^^^^^^^^ + +The audio signal is split into two paths. One path remains unchanged. The other +one is sent to a delay line. The delay time (the so called *phase*) is made +dependent on the frequency. Therefore, an all-pass filter is applied to the +signal, which *preserves* the amplitude, but determines the delay time. In the +end, both paths are added. + +The following picture describes how this works on white noise. Light blue +signalises that the frequency is not present at the current time, and dark blue +signalises the opposite. The dark blue peaks appear if the delay time is very +short, because then, the second path almost equals the first one, which results +in duplication of the signal. If the delay line is very long, then it is --- in +the case of white noise --- totally at random whether the delayed signal +currently duplicates the unchanged path, or whether it cancels it out to zero. +This random effect results in white noise between the clear blue structures. + +image:./images/phaser-spectrogram.jpg[width="700", + title="Spectrogram of an 8-stage phaser + modulated by a sine LFO applied to white noise."] + +Phaser Types +^^^^^^^^^^^^ + +ZynAddSubFX offers different types of phasers: + +* Analog and "normal" phasers. Analog phasers are more complicated. They sound +punchier, while normal phasers sound more fluently. However, analog filters +usually need more filter stages to reach a characteristic sound. +* Sine and triangle filters. Note that an analog triangle filter with many poles +is a barber pole filter and can be used to generate +http://en.wikipedia.org/wiki/Shepard_tone[Shepard Tones], +i.e. tones that seem to increase or decrease with time, but do not really. +* The LFO function can be squared. This converts the triangle wave into a hyper +sine wave. The sine squared is simply a faster sine wave. +* TODO: Barber is deactivated, since PLFOtype is only 0 or 1? + +Description +^^^^^^^^^^^ + +For the normal phaser, first, the LFO is generated: + +* There are 4 controls (*Freq*,*Rnd*,*LFO tpye*,*St.df*) that define the +LFO. +* *Phase* and *Depth* are applied afterwards in the usual way (TODO: I don't +understand the code here for the normal phase...). For the analog phaser, +*Phase* is not implemented, yet. +** If *hyp* is being set, then the LFO function is being squared. + +Next, the input is being used. + +* *Analog* decides whether the phaser is analog or "normal". +* First, *Pan* applies panning to the original input in every loop. +* Next, barber pole phasing is being applied (Analog only). +* *Fb* applies feedback. The last sound buffer element is (after +phasing) multiplied by this value and then added to the current one. For normal +filter, the value is added before, for analog after the first phasing stage. +* Now, *Stages* phasing stages are being applied. *dist* sets the distortion +for when applying the phasing stages. This has only effect for analog phasers. +* The feedback is taken now. +* In the end, *Substract* inverts the signal, multiplying it by -1. + +Alienwah +~~~~~~~~ + +Introduction +^^^^^^^^^^^^ + +The AlienWah effect is a special, dynamic +http://en.wikipedia.org/wiki/Formant[formant] filter (TODO: is this true?). +Paul Nasca named it AlienWah because it sounded "a bit like wahwah, but more +strange". The result of the filter is a sound varying between the +vocals "Ahhhhh" (or "Uhhhhh") and "Eeeeee". + +Function +^^^^^^^^ + +The way that the filter moves between the two vocals is mainly +described by an LFO. A bit easified, Paul Nasca has stated the formula (for +latexmath:[$i^2=-1; R<1$]) as + +latexmath:[$fb=R*(\cos(\alpha)+i*\sin(\alpha))$] + +latexmath:[$y_n=y_{n-delay}*R*(\cos(\alpha)+i*\sin(\alpha))+x_n*(1-R)$]. + +The input latexmath:[$x_n$] has the real part of the samples from the wavefile +and the imaginary part is zero. The output of this effect is the real part of +latexmath:[$y_n$]. latexmath:[$\alpha$] is the phase. + +Description +^^^^^^^^^^^ + +* *Pan* +* The following 5 controls (*Freq*,*Rnd*,*LFO tpye*,*St.df*, *Dpth*) define the +LFO. +** *Fb* + +** *Delay* If this value is low, the sound is turned more into a +"wah-wah"-effect. +** *Phase* See latexmath:[$\alpha$] in the above formula. This lets you set +where the vocal is between "Ahhhhh" and "Eeeeee". +** *L/R* applies crossover in the end of every stage. This is currently not +implemented for the Analog Phaser. diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/envelope.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/envelope.txt new file mode 100644 index 000000000..157843eb3 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/envelope.txt @@ -0,0 +1,131 @@ +Envelopes +--------- + +Introduction +~~~~~~~~~~~~ +Envelopes control how the amplitude, the frequency, or the +filter changes over time. + +Amplitude Envelopes +~~~~~~~~~~~~~~~~~~~ + +These envelopes controls the amplitude of the sound. +In ZynAddSubFX, amplitude envelopes can be linear or logarithmic. +In the next image, it is shown the differences between these envelopes. + +image::images/envelope1.png[Alt text] + +The amplitude envelope is divided into: + +* *Attack*: Begins at the Note On. The volume starts from 0 to the maximum. +In ZynAddSubFX, the attack is always linear. +* *Decay*: The volume drops from the maximum value to a level called "Sustain level" +* *Sustain*: The volume remains constant until the key is depressed (Note Off). +After this, the last stage take place. +* *Release*: The volume drops to zero + + +Frequency Envelopes +~~~~~~~~~~~~~~~~~~~ + +These envelopes controls the frequency (more exactly, the pitch) of +the oscillators. +The following picture draws the stages of these envelopes. + +image::images/envelope2.png[Alt text] + +The dotted line represents the real pitch of the sound without the +envelope. + +The frequency envelopes are divided into 3 stages: + +* *Attack*: Begins at the Note On. The frequency starts from a certain value and +glides to the real frequency of the note. +* *Sustain*: The frequency is the same on over the sustain period +* *Release*: This stage begins on Note Off and glides the frequency of the note +to a certain +value + +Filter Envelopes +~~~~~~~~~~~~~~~~ +These envelopes controls the cutoff frequency of the filters and are divided +into + +image:images/envelope3.png[Alt Text] + +* *Attack*: Begins at the Note On. The cutoff frequency starts from a certain value and glides to another value +* *Decay*: The cutoff frequency continues to glide to the real cutoff frequency value of the filter (dotted line) +* *Sustain*: the cutoff frequency is the same on over the sustain period (dotted line) +* *Release*: this stage begins on Note Off and glides the filter cutoff frequency of the note to a certain value + +Freemode Envelopes +~~~~~~~~~~~~~~~~~~ + +For all envelope there is a mode that allows the user to set an arbitrary number of stages and control points. This mode is called Freemode. + +image:images/envelope4.png[Alt Text] + +Only stage that always remains defined is the Sustain, where the envelopes freezes until a Note Off event. + +User Interface +~~~~~~~~~~~~~~ +All the envelope types has some common controls: + +* *E*: Shows a window that you can view the real envelope shape or convert to free mode to edit it +* *Stretch*: How the envelope is stretched according the note. +On the higher notes the envelopes are shorter than lower notes. +In the leftmost value, the stretch is zero. +The rightmost use a stretch of 200%; this means that the envelope is stretched about 4 times/octave. +* *frcR*: Forced release. +This means that if this option is turned on, the release will go to the final value, even if the sustain stage is not reached. Usually, this must be set. + + +The parameters for Amplitude Envelopes for ZynAddSubFX are: + +image:images/uienvelope3.jpg[Alt Text] + +* *A.dt*: Attack duration +* *D.dt*: Decay duration +* *S.Val*: Sustain value +* *R.dt*: Release time +* *L*: If this option is set, the envelope is linear, otherwise, it will be +logarithmic + + +For Frequency Envelopes the interface has the following parameters: + +image:images/uienvelope2.jpg[Alt Text] + +* *A.val*: Attack value +* *A.dt*: Attack duration +* *R.dt*: Release time +* *R.val*: Release value + + +Filter Envelopes has the parameters: + +image:images/uienvelope1.jpg[Alt Text] + +* *A.val*: Attack value +* *A.dt*: Attack duration +* *D.val*: Decay value +* *D.dt*: Decay time +* *R.dt*: Release time +* *R.val*: Release value + +The Freemode envelopes has a separate window to set the parameters and controls: + +image:images/uienvelope0.jpg[] + +* *Control points*: You can move the points using the mouse. +In the right on the windows, it is shown the total duration of the envelope. +If the mouse button will be pressed on a control point, it will be shown the +duration of the stage where the point is. +* *FreeMode*: this button activates or deactivates the freemode mode. +* *Add Point*: Adds the point next to the current selected point. +You can select a point by clicking on it. +* *Delete point*: Removes the point from the envelope. +* *Sust.*: Set the sustain point. It is shown using the yellow line. +* *Str.*: Envelope stretch + + diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/filter.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/filter.txt new file mode 100644 index 000000000..b0f1c75d3 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/filter.txt @@ -0,0 +1,81 @@ +[[filters]] +Filters +------- +:Author: Mark McCurry +:Date: June 24, 2012 + +ZynAddSubFX offers several different types of filters, which can be used to +shape the spectrum of a signal. +The primary parameters that affect the characteristics of the filter are the +cutoff, resonance, filter stages, and the filter type. + +* *Cutoff*: This value determines which frequency marks the changing point for + the filter. In a low pass filter, this value marks the point where + higher frequencies are attenuated. +* *Resonance*: The resonance of a filter determines how much excess energy is + present at the cutoff frequency. In ZynAddSubFX, this is + represented by the Q-factor, which is defined to be the cutoff + frequency divided by the bandwidth. In other words higher Q + values result in a much more narrow resonant spike. +* *Stages*: The number of stages in a given filter describes how sharply it is + able to make changes in the frequency response. + +The basic 'analog' filters that ZynAddSubFX offers are shown below, with the +center frequency being marked by the red line. +The 'state variable' filters should look quite similar. + +image:images/filter0.png[] + +As previously mentioned, the Q value of a filter affects how concentrated the +signal's energy is at the cutoff frequency; The result of differing Q values are +below. + +TIP: For many classical analog sounds, high Q values were used on sweeping +filters. A simple high Q low pass filter modulated by a strong envelope is +usually sufficient to get a good sound. + +image:images/filter1.png[] + +Lastly, the affect of the order of the filter can be seen below. +This is roughly synonymous with the number of stages of the filter. +For more complex patches it is important to realize that the extra sharpness in +the filter does not come for free as it requires many more calculations being +performed; This phenomena is the most visible in subsynth, where it is easy to +need several hundred filter stages to produce a given note. + +image:images/filter2.png[] + + + +There are different types of filters. The number of poles define what will +happen at a given frequency. Mathematically, the filters are functions which +have poles that correspond to that frequency. Usually, two poles mean that the +function has more "steepness", and that you can set the exact value of the +function at the poles by defining the "resonance value". Filters with two poles +are also often referenced +as http://de.wikipedia.org/wiki/Butterworth-Filter[Butterworth Filters]. + +******************************************************************** +For the interested, functions having poles means that we are given a quotient of +polynomials. The denominator has degree 1 or 2, depending on the filter having +one or two poles. In the file _DSP/AnalogFilter.cpp_, +_AnalogFilter::computefiltercoefs()_ sets the coefficients (depending on the +filter type), and _AnalogFilter::singlefilterout()_ shows the whole polynomial +(in a formula where no quotient is needed). +******************************************************************** + +User Interface +~~~~~~~~~~~~~~ + +image:images/uifilter.png[] + +* *C.freq*: Cutoff frequency +* *Q*: Level of resonance for the filter +* *V.SnsA.*: Velocity sensing amount for filter cutoff +* *V.Sns.*: Velocity sensing function +* *freq.tr*: Frequency tracking amount. When this parameter is positive, higher + note frequencies shift the filter's cutoff frequency higher. +* *gain*: Additional gain/attenuation for filter +* *St*: Filter stages + +NOTE: TODO add a lengthy section on the formant filter setup diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/gen/ad-note.tex b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/ad-note.tex new file mode 100644 index 000000000..4dd3f3cd8 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/ad-note.tex @@ -0,0 +1,37 @@ +\documentclass{standalone} +\usepackage{tikz} +\usetikzlibrary{matrix,shapes,chains,scopes} +\begin{document} +\input{fig.sty} +\begin{tikzpicture} + \matrix (mtx) [matrix of nodes, row sep=5mm, column sep=5mm] { + & + |[lfo]| Freq LFO & + |[lfo]| Filter LFO & + |[lfo]| Amp LFO & + &\\ + |[block]| Base Fq. & + |[block]| Voices & + |[block]| Filter & + |[block, shape=isosceles triangle]| Volume & + {Output} \\ + & + |[env]| Freq Env& + |[env]| Filter Env& + |[env]| Amp Env&\\ + }; + + { [start chain=trunk] + \chainin (mtx-2-1)[join=by tip]; + \chainin (mtx-2-2)[join=by tip]; + \chainin (mtx-2-3)[join=by tip]; + \chainin (mtx-2-4)[join=by tip]; + \chainin (mtx-2-5)[join=by tip]; + } + \foreach \i in {2,3,4} + { + \draw[->] (mtx-1-\i) -- (mtx-2-\i); + \draw[->] (mtx-3-\i) -- (mtx-2-\i); + } +\end{tikzpicture} +\end{document} diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/gen/chorus.tex b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/chorus.tex new file mode 100644 index 000000000..87727e36d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/chorus.tex @@ -0,0 +1,38 @@ +\documentclass[12pt]{report} +\pagestyle{empty} +\usepackage{pst-sigsys} +\usepackage{auto-pst-pdf} +\begin{document} +\begin{pspicture}[showgrid=false](0,-1)(8,2) + %Style + \psset{framesize=1 .65} + \psset{style=Arrow} + + %In/Out + \rput(0,0){\rnode{in}{$x[n]$}} + \rput(8,0){\rnode{out}{$y[n]$}} + + %Crossover + \psfblock[framesize=2 .65](1.5,0){cross}{Crossover} + + %Feedback over fractional delay + \pscircleop(3,0){comb} + \psfblock[framesize=1.2 .65](4.5,0){delay}{$z^{-n.m}$} + \pnode(5.5,0){decoration} + \pscircleop[operation=times](4,1){fb} + \rput(4,1.5){Feedback} + \ncangle[angleA=90]{decoration}{fb} + \ncangle[angleA=180,angleB=90]{fb}{comb} + + %subtract? + \pscircleop[operation=times] (6,0){sub} + \rput(6,-0.5){Subtract} + + %Panning + \pscircleop[operation=times] (7,0){pan} + \rput(7,0.5){Panning} + + %Connections + \nclist{ncline}{in,cross,comb,delay,sub,pan,out} +\end{pspicture} +\end{document} diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/gen/distort.tex b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/distort.tex new file mode 100644 index 000000000..eb6b3a9ad --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/distort.tex @@ -0,0 +1,44 @@ +\documentclass[11pt]{report} +\pagestyle{empty} +\usepackage{pst-sigsys} +\usepackage{auto-pst-pdf} +\begin{document} +\begin{pspicture}[showgrid=false](0,-2)(10,2) + %Style + \psset{framesize=1 .65} + \psset{style=Arrow} + + %In/Out + \rput(0,0){\rnode{in}{$x[n]$}} + \rput(9.5,0){\rnode{out}{$y[n]$}} + + %Panning + \rput(1,0.5){Panning} + \pscircleop[operation=times] (1,0){pan} + \ncline{in}{pan} + + %Preamp + \rput(2,-0.5){Preamp} + \pscircleop[operation=times] (2,0){preamp} + \ncline{pan}{preamp} + + %Filtering + \psfblock(3,0){fil1}{Filter} + \ncline{preamp}{fil1} + + %Distortion + \psfblock(4.5,0){shape}{$H(n)$} + \ncline{fil1}{shape} + + %Filtering + \psfblock(6,0){fil2}{Filter} + \ncline{shape}{fil2} + + %Crossover + \psfblock[framesize=1.8 .65](8,0){cross}{Crossover} + \ncline{fil2}{cross} + \ncline{cross}{out} + + +\end{pspicture} +\end{document} diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/gen/dynamic.tex b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/dynamic.tex new file mode 100644 index 000000000..aba633099 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/dynamic.tex @@ -0,0 +1,46 @@ +\documentclass[11pt]{report} +\pagestyle{empty} +\usepackage{pst-sigsys} +\usepackage{auto-pst-pdf} +\begin{document} +\begin{pspicture}[showgrid=false](0,0)(8,2) + %Style + \psset{framesize=1 .65} + \psset{style=Arrow} + + %In/Out + \rput(0,0){\rnode{in}{$x[n]$}} + \rput(8,0){\rnode{out}{$y[n]$}} + + %Smoothing + \psfblock(1,1){smooth}{LPF} + \rput(1,1.6){smooth} + + %Sensing + \pscircleop[operation=times](2.5,1){sense} + \rput(2.5,1.5){sense} + + %Combine + \pscircleop(4,1){comb} + + %Depth + \pscircleop[operation=times](5.5,1){depth} + \rput(5.5,1.5){depth} + + %LFO + \psfblock(7,1){lfo}{LFO} + + %Filter + \psfblock(4,0){fil}{Filter} + + %Panning + \pscircleop[operation=times] (5.5,0){pan} + \rput(5.5,-0.5){Panning} + + %Connections + \pnode(1,0){decoration} + \nclist{ncline}{in,fil,pan,out} + \nclist{ncline}{decoration,smooth,sense,comb,fil} + \nclist{ncline}{lfo,depth,comb} +\end{pspicture} +\end{document} diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/gen/echo.tex b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/echo.tex new file mode 100644 index 000000000..22248b4bf --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/echo.tex @@ -0,0 +1,43 @@ +\documentclass[11pt]{report} +\pagestyle{empty} +\usepackage{pst-sigsys} +\usepackage{auto-pst-pdf} +\begin{document} +\begin{pspicture}[showgrid=false](0,0)(8,2) + %Style + \psset{framesize=1 .65} + \psset{style=Arrow} + + %In/Out + \rput(0,0){\rnode{in}{$x[n]$}} + \rput(8,0){\rnode{out}{$y[n]$}} + + %Panning + \rput(1,0.5){Panning} + \pscircleop[operation=times] (1,0){pan} + \ncline{in}{pan} + + %Crossover + \psfblock[framesize=1.8 .65](6,0){cross}{Crossover} + + %Feedback + \rput(4,1.5){Feedback} + \pscircleop[operation=times](4,1){fb} + \ncangle[angleA=90,angleB=0]{cross}{fb} + \pscircleop(2,0){combine} + \ncline{pan}{combine} + \ncangle[angleA=180,angleB=90]{fb}{combine} + + %Dampining + \psfblock(3,0){hidamp}{LPF} + \ncline{combine}{hidamp} + + %Delay + \psfblock(4.3,0){delay}{$z^{-n}$} + \ncline{hidamp}{delay} + \ncline{delay}{cross} + \ncline{cross}{out} + + +\end{pspicture} +\end{document} diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/gen/fig.sty b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/fig.sty new file mode 100644 index 000000000..076954a09 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/fig.sty @@ -0,0 +1,14 @@ +\tikzset{ +>=stealth, +thick, +block/.style={rectangle, draw=black!50, thick, + top color=white, + bottom color=red!50!black!20, + font=\itshape}, +op/.style={circle, draw=black!50, thick, + top color=white,bottom color=black!20, + font=\ttfamily}, +lfo/.style={block,bottom color=orange!80}, +env/.style={block,bottom color=yellow!80}, +tip/.style={->,shorten >=1pt} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/gen/reverb.tex b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/reverb.tex new file mode 100644 index 000000000..ead3c5f6e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/reverb.tex @@ -0,0 +1,50 @@ +\documentclass{standalone} +\usepackage{tikz} +\usetikzlibrary{matrix,shapes,chains,scopes} +\begin{document} +\input{fig.sty} + +\begin{tikzpicture}[ + point/.style={coordinate}, + every on chain/.style={rounded corners}] + %Style + + \matrix[row sep=5mm, column sep=5mm] { + & + \node (fb) {$f_b$};& + \node (del) {$Delay$}; + &&&&& \\ + & + \node[op] (pb1) {$\times$};& + \node[block] (pb2) {$z^{-n}$}; & + &&&& \\ + + \node (p1) {$x[n]$}; & + \node[op] (p2) {$+$}; & + \node[point] (p3) {};& + \node[block] (p4) {HP/LP}; & + \node[block] (p5) {Comb Filter}; & + \node[block] (p6) {Allpass}; & + \node (p7) {$y[n]$}; \\ + }; + + \draw[->] (fb) -- (pb1); + \draw[->] (del) -- (pb2); + { [start chain=trunk] + \chainin (p1)[join=by tip]; + \chainin (p2)[join=by tip] ; + \chainin (p3)[join=by {}]; + %\chainin[join by={}] (p3); + { [start branch] + \chainin (pb2)[join=by tip]; + \chainin (pb1)[join=by tip]; + \chainin (p2)[join=by tip]; + } + \chainin (p4)[join=by tip]; + \chainin (p5)[join=by tip]; + \chainin (p6)[join=by tip]; + \chainin (p7)[join=by tip]; + } + +\end{tikzpicture} +\end{document} diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/gen/velf.tex b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/velf.tex new file mode 100644 index 000000000..63884424d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/gen/velf.tex @@ -0,0 +1,16 @@ +\documentclass{standalone} +\usepackage{tikz,pgfplots} +\begin{document} +\begin{tikzpicture} + \begin{axis}[domain=0:1,samples=128,no markers, + xlabel=Note Velocity,ylabel=Param Magnitude, + legend style={ cells={anchor=east}, legend pos=outer north east}] + \addplot gnuplot{x^8^1}; + \addlegendentry{max sensing} + \addplot gnuplot{x^(8^-1)}; + \addlegendentry{min sensing} + \addplot gnuplot{x}; + \addlegendentry{avg sensing} + \end{axis} +\end{tikzpicture} +\end{document} diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/getting.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/getting.txt new file mode 100644 index 000000000..19572086a --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/getting.txt @@ -0,0 +1,65 @@ +Appendix C: Getting ZynAddSubFX +------------------------------- + +Usually there are several methods to obtain a copy of ZynAddSubFX. + +SourceForge:: + http://sourceforge.net/projects/zynaddsubfx/files/ +Distribuition:: + apt/yum/others +Git:: + git clone git://git.code.sf.net/p/zynaddsubfx/code zynaddsubfx + +Introduction to Git +~~~~~~~~~~~~~~~~~~~ + +For those who want to live on the bleeding edge or who want to assist with +making sure that the next release has fewer bugs, you will want to get aquanted +with git. +Git is used to manage the source code for this project and can be used to +quickly and easily get an up-to-date copy of the source code. + +Getting the Source Code +^^^^^^^^^^^^^^^^^^^^^^^ + +In order to get a copy of the ZynAddSubFX source code, all that needs to be done is: + +--------------------------------------------- +git clone git://git.code.sf.net/p/zynaddsubfx/code zynaddsubfx + +cd zynaddsubfx + +#Download additional resources +git submodule init +git submodule update +--------------------------------------------- + +You should now be in the directory of the source code. + +For simple steps on building, please see Appendix B of the manual. + +Checking out a branch +^^^^^^^^^^^^^^^^^^^^^ + +Lets say that development has extended into the creation of a new feature that +you want to preview. +For the sake of this guide, lets assume that the name of the branch that the +feature is on is foo. + +----------------------------------------- +#checkout the foo branch from sourceforge +git checkout --track -b foo origin/foo + +#lets checkout the primary branch again +git checkout master + +#hop back to the other branch +git checkout foo +---------------------------------------- + +Now one should be able to change branches and go into the build directory (as +described in Appendix B) and recompile ZynAddSubFX. + +NOTE: When using branches other than the master be aware that stability may + suffer + diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/ad-global.png b/plugins/zynaddsubfx/zynaddsubfx/doc/images/ad-global.png new file mode 100644 index 0000000000000000000000000000000000000000..9479a91c5d09d853aee4f1c03758b528588e5c2e GIT binary patch literal 81812 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�d%mndYo+ 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
4)bys%}-95dG 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
pb-CHR;8! z4Y?Wb=W+1Z&U4mre+tTXH3jilXW4X?ei`M~Z@1-lkU8xe`KM?GtBI1)_mQGIOV8yD@XoJp|b2)CRk<{6> zm@fELQj3oxy9m)cb<3e#FhEQr+ZU6vp#2AMgny0e*!zWaiuJPngE!NM;vSrx3x(Vs z7Q#6wpUiEKPlt&F-Op7kjRks^@L$1$0(OPY_bJ&i=+m1Jy@KwwNlxg+17^RiYi+w9 zR{$FQaLV|6A(xf)RMJMc2u9mz#Gxy*`iipSXT$DeIIflG)_1gr53ay}RVhyb>i+m; zINQi1r@`;Y{?>x$bDJuI$#;NzWi^{Sjc5ejoZf5W%Hiv2M+58lkfxvMqz>;QDW$nM zYPhK5?SONkyelE!z;|OXPO7OrclnsyOG*o}IX5I~?MAd>FON@lZ@yY)dgy#7(j8^q zzP@$mjqaMN`5`>oj7YK1csh-E@4VK4+Fuj8w5IH87eaq*SnDUP0Ip)%ghcjsDeNd9 zqB5AIJh%-|*@Knr_uv04skVHP88FvoF#q0Ju7p!KpjphHFA0YK;6YFNtKq%9n1`KiVtyEh6r?N9>z|s19>Vli=d{)2`KMw(RzpC~%RYF~r^|lHBI?KV2~#dcFG7sa0i2VpTmy_O7dT zjg6^5Yoh~=R7#dGFN)O^|NkKbpq?*5&yCh9w80(~>%(^)4BRWz^kQN;i&sP&Mo{)6 z{=j~W>?;?}Pi-TMa-*dZAJVrv&ZD<@#?!L~7Pwcz zCd-?#`{b9bjQrVV_?6sdx$)Wp{WK}9PKc%gz0ZCJDVu2Z<|}4`SYtVMY$fjTtscENnZ8D*k0F zOxTr{i}mhVhgt2NEnsrw1*&=~HzI3g6H4u#; zp7pgw%Xn>!z@Az1dqMU?O@$m_o#yYp5?$XXCEjNZ{9YcIMI)=&ohdz1`9-l>0IP~8 zzvfgEvB?ohmncW`znk}r+EcU%Qh4%oKmTVl=#=_@pUTtq82+DE<<~GC#K%41D8V!2 RCNRK5URp(}Qqtu6{{#5_HhKU6 literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/filter1.png b/plugins/zynaddsubfx/zynaddsubfx/doc/images/filter1.png new file mode 100644 index 0000000000000000000000000000000000000000..35ffb1759c4481b5f334d34350c1125d47959d48 GIT binary patch literal 156922 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 z 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%0Aq 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%0DQzH%`7Q+qP}n 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 z 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 N=b?dCxyJL5e*w@;=pFz7 literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/filter2.png b/plugins/zynaddsubfx/zynaddsubfx/doc/images/filter2.png new file mode 100644 index 0000000000000000000000000000000000000000..836a5143b6c82914358fc7932ec971906189437c GIT binary patch literal 77767 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 fYq6xIiIMoYcexHgu1|BDHo#yYdr9Kw|4J5 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∋wn^xGd!>qAY93jxE5S>? z>+=Q|mEyd0SnRJ=E85;2+B$YUx2B7yEu4yca&J?&ok?>Q*k@WHYJY`0ir&DLb4#se z$qsJ$iiGYSHCSq>YI893s;$L&kTx{>9q`qJ{){{q>m`q<*MH?aMd#2!PBby|jX{B7 z)3TqoAqdA>=76a7#-3AH+=V2)=LplP#LEj}o)I8q>_70Yr}A?0r^Q^JnW)`8XRlgD zXYqi-=Q@Sp@$4XM^#--T+|i>S5dSnN+dtw`mii)egeo+`(Z`6XA9c^_#R!mUf(t&R4;>Z`VOjRAPm(Fi;aJbN4 zNr@59jTFL@0sUM+{Y`L~dUB7yBtp2j!B!Wm#7DEjI^B)fB%soMGu0jBDoc8MUYQ<^ zqE{PdTX~cu{`dSsDy5gR)X%HFS+Z8aE~6twfE`Tc-3R?ph@P%~5$I@Iy& zONF+ZkIW9Saf>RNDwCTPQ4h>9Sn@jmTTd>p2bLVrbOEz&taUSEl{k*FiqVe>(BoHo z@gF|N%T0k%D}I4dq#a5YUScV2_l~CmJk{A`vNc27I6tTfy@M#(L?2FH;XVPM8*Y<9q4mdp~1~LoJcWancx-PS@QP;yV4y(DmiN z1jb-|Lh&^}Hb%fUUIF83_mhI;5fF z9>f08g3{u!oM}w9T7aS@=)d6DFR-rjFAV!i(%Dv&#WVhKG^nGrW|dq|cXE|UF64G= zxktH{Rw_%EW!S90nC}gI9Om180@u#vcuj8TI9x%;$9gO+5d1IuS&4n|*v@zw-Pa|+ zy+t?IssW0hNwJ{lF|85!vSY_Qn!dcj>?Sn*@USfM>u%@ej9<;S*e+jF!q@v!zC@1- z;f2T_6I*yxIXGCUiPD9OCYN8c|9Q$-Xgq*JARwX%={5&$00hWnfG-^60fI{)`E{0j zvHxqvPae-{c>Lm)I%V|?cAc9Z#K(E*w|>-rW|KVuu})a=+-cduV-2{230i8rIrThd z7?ou0WawvCk)r=K6shu0y+ga1g9m`;eEJ|+bkn+!%7fl4O7u01(6kZ)SltPj4G_ZZ zF*Xg&{BKXAAuq@$J>QA!{l0JHF0gZ#P73}y70#ImT^a;7>`q?8>RJl+iN(ehSwA&? zTQ#C?p~B6(?REG|G=~-e@X<2OvucrtlCP=SkJtm^hbu4i?+H&~U4Y$1fI%d1SQ6HB zpNUPz?``mw3Qs?}96dl|-H=BSv}TKb@PjCpR=-Lz)4BlHO}KyNMC3I_kK8{a6gc1l zX|U2T@;Y!@@!6KTI$BH`a%c`VgQTlFP2~JJWmEF|f&y6Dlri)UgabEVg+zOK;?s)R zdl;JXf6SCuQuqF|w5x@9{LN0b7iJzw9m&_N(dUx==tV}MgZ!I_D^+uslr?`p1U|4S z{k@R4CuN86s1TWl{GqvpM-eQ+Ec^>g?se|8qR$-83pBd?DvJ2G_u112w{7`YUysv7 z&KD-t&-Q)7zE~Hta5040r-@8J|Il_cyMxnfB|cKUserX;4lxJ99YUWq2sge`!#Xi~ zwvM;w#Cy6xp7iJTivXFFec@{R&+7My?Uz`_um?%twiG|uQ8KVlK}Oe1fL$YfvzDXG zZs-#k@QP|?vjU5xH5)s(Y_*u-QP$_nn`6-%DssUW(c?kGUU`HNhOZNLJIQg7b)cOi zR&4!mBPn>mQ5pJAYi&>+kvmSb3}dj$uG8`2Bv;Q$u%jb&IPUZK7kjRy7+&T6kIxlG zHx8GBX+Mv67y||PV~DnC%&+1ZKtkt^^6KDkzhgHAm9tF+{I zmhRjsFqi(pQ91$fq+dq{xoJF%4wd#L)+$tPFPdX*c^YE-C~X2?hfA8{gZ{dCJfz*ED1P4<%8toNp<*ieXi=itj&Nf2!T{Z=R`iGhd^A$d)l3 z&hI7@*gqqQ(bVm?o@yNj*j}yJ9~>=|zC-+4Z|o#=6JD&kIj`aPlGev=?~cco1_#UY zOZDF;A<#RwY2|}M8`K}_%FJS1fm^k%vH$q@Vlt)Q>tPL)>@yY=n$#)WS?Hg6d8?x<@eRg#*(XsNTHsDaUFN{oo^YhK3B6`S)k7aY8*GYGZd_GE--nSuF_ql(Sl|HPSWH`U&Ee$iY2ONdB7lX1A z*7kW7upqT^vDS9Q&wbc?h7JtUd%q=wBQ@iS_-!tx+%wE|=INAwhRwBIeWtT)$8?cLWr_;;d)=<=%3!>HyrF$dk_41X=i=t8(g(_6o}K ziEw7-=NU#J+C>UQvt|0%9gwKeZ205M!L_xJ9uwxf#F$_Wzn9k?(2iI?rlDM?j%Wf1 zOJ?D!Hi_RX>u$*$as_!H=GWIcrU4(SwpP8nNa&VbeF_X!89S;iCpH}49xd!WxbhYR z+waaoF-l(MCR}T*lixO_Lu$deim%=3ra>blwR&7`bwVIDOQ3UIP_X9)4*FWXLH|ow zT)rj+=%|vsBq@v7=t8S2Ut}8=|Jq~2#4a4!)%wPCgD}PasX#ST2)nIQ(f2+79pWZ? z+D1`%EO+gXK49t=NU9FEF;)%?nLJPJuP>Bne zlFNnJm4BA7a>!MRXy_X%$-*Qo%ZM9>^c|#p_v1lish>3e$2a2!^igpqN%Fu!cWY$~-i@zA6H7piv4j!Mm; zdzdv=d`&*x7biho3#KBEAZb_(hCttzO>ek@0h~wcT5vkvwl26W^LsNGN1g#IFSI$; z=1QJ{xh5E(99Qy$%wY+sgU(l23?wa7I)=E^WWOf(J|raNOj!SjRL?JM#h`n+5$mI& ztNpWYo1RZ7W3c*lIxg;2|B;e~?sK>4>3sOU0?_+t!E`lDrD*Ym4Jci9MJ5mLC zPan2m1Gxt?IVoL4N*Ca_@4q?~=F42Wv7^zPeWm2p;r)VI2pXb`L#HugG0rfJPd6f#-$uXY~mjjwL6|Z=p<^zdwg9%|GoyUjXL;8 zZwJG2`rIe%_qeYgD^ShK!EO$x=^%)Uekb>Z577Yav8&Vks40Km`6ej3q=L0i(|DFn zBF%rWB;9qW8>DKP*m@?az$HprGu`Wtyc7%>lSnCUysgLmx!35TVQ3gULM`|7^e9Sx zu%D12yWaVxlzni3S~U4vEBc4{#DD&K8ZR#J99faHvVGxs#k5%f%-EW96Dt>cZt-U^ z!a^#gi5wX}^yeA>E+rvqV8j5n{R1s^x`fVyXIcOzCq!-+K9=g_7>uS-|OaEyq5nEQT1Q zxIIm))bRXFL%QTip+ed*3Ls24^%&w;`3|(aYdQqEbnW|I4!mx^bA29`=@UVd~z@fNps3ROS-c6X?yF#pA1UBqtL5CvOoIjlKtUqgui=PE+=A{%<-w^wL1(FJCGz~ z0NdZ2j7p&CP?vFL*PR#pg?QpPn)sa$AF!MpE92Eaz9JJ%26CCQ1web^o}xa&$jGbr)vfPuI3(>^6%-PzdV#TR>1Swq8}1hH*sIe6(lYK?cH`p(}+$| z06fy@-|<-dk&^5jgZonEAYd#u9yU6wvsZ&Z!`Rq|Sv<}(*oOv1M-+RBnr_Q!o^%JGSQ^upFow577?W*Z{hO#f=vk9R2vT@R~ zaZ%A>FonEOI?4gufFz^@S;;{tr5qep3Vp?2g79$I=@Yx*iyc^QSZ(|q#S@=@&%@j- zH1Di}P{KNk9oJBt4qSr3Do5p;7orkE!qrnvSgzCN@DUnyLNN)nuq}niI;6K~5LS2NvqQT%-Kk$gY9Qjqd7F&o^1d1hEa1*RE%mLy1SzeLnxiQKMHdqX#*Sdh z;Vk}0rZRujwV-o`TgPdvg6=oIl$HMH65fFU!7jiSS)>7Sg_}$}Lzm8{UcT$=bvm9e za^8eRMMp0ME^Efox4(CuH7qA1~LP zL_xtvQFj*P@^XF7{<_Lec_mC z9#Vj!bV{`B#l!^57v%v0n^Wk3wHx^7%XqnZy^PF303$$H9N_-<3*H zF@ZABVQ7+Rxar3KK;$ARYsD`U+o8Qe4c2}b5F&b$9tNJOSS;Q3sp$C0;b0gvZF$-r zd}ioC(}}JZ#Mv(oALW1I*>~(G?eeIz4~4Ybz5*|~BENeWuT}UPA!glgv_c}8q}^^> zY&4b}L}D55-qX=zF_~%rZ-Q9chqGDR1GE{B8gyH4>i0+@C9Yk)Eq5Q>t{O+0N$5%r zKVNrlyIyRdd(_Q!ti^JK{){-3G(bS+emO|@--Yd+X9>N99;W`!JqJ48?54XA(4p3% zP&eOUaX-!;1K?v-wNa2b1^0Zz5CS@@p~}Oivt$O(t7UZIR_MTH*SYBS0Zo4ErCSfMPZlePz9&qZ#86Z-0ANS?s2j6aVCU?jt6^` z)Sn*MU|6`AYm*{IracJg`Ce=#y>vbA-l_56JQQNPLL8Wj`zI54q)*N#b;yk~vz`0n zmWzC|J#k0YGHp)TXDMmaKGcB&e>0LY%kl?5=)|qG@H9vv*z1cyZyZGM4Lr!oI-b4( zkH;HM(+?PIT+Z#`{0;{=nH{^r1`zC|r6=gBfaQ(S*7WI2TzsMuz_0NQk@&)ot*Obk z7^NJ8O?Ue`ds3!?)KMWRZ5yJey^Ff_w?Y8vVJ=GuSwW=lePU{OSX7r>+h2szGD{Lz z$)e8(hMbSFAiJBi-t_~K{1T?dPl;qTNijhY>HyCAugAMJkGloyZqb9O$JW0fm=9KofRW#jW$nqMvo3u z0=rpwf7)~m6m7J5Mw<`hPntH+!kGzmR^cEN<5r|F+|Z2x`p_5?Kb+Zman=@|=1x?j zE{^@RI#+hd`J}|RURF2AZ*-kOY`0lInIGYaaW+Y+(sY*|=XDZB!v<()xJ4t|IdDm{ zsrY@`wTq_7WVb=xVeq!9q~EpI6XM|V&EvfCeH7&;JTq;k!t@RSb=f53K|SkQ7SS?t z{c%VG%h7)ZePRy?mcot68+t>lc5hWK%$wAM^o-^z4#;YbP*S}p$oWRS_ z9$X~79;EVt#d{YR9-Vab4z!`ZXRi?L*|hopyr zBe}?%`!}1Z1g2h-@c>}}0*;|bOo(O%YLPfRHYe?nTr5Z8hX*+Il0AQ{idqQhG zo$l0^-0ozm0o0R$-enX|^OUB*YQ6ZP;Jm^oDVKk|kd_DVPP$X(>0BMl*iP_vnW8o+ zWkiZM5sR@eF$z@bj#hou9UVZ(qCb_qWbG)|1q`De^`5f|l6svfo7fi{vxkbRYP+K^ z)pz^jAZEk0DFEri{Z1SI^6#NdseZt}Mo=%KS*~=cpxmBJMm-E>rpNT005b6}`I`>?=D+aPC4#(( z1J`t~5j7k#kqMnwMWsVuzoipq59dXR#uh-ILii>v*#(@{Aj5+vV-_Bge}yhBogK#@ z)K89L%r#7}%@1=yxXmZkFEU(i+`wF!ugHlS7MJNGSWGqaPRK=$S@E(Y8hU=u{e8FU za_!CFkQA0~!T=LXlm&DEt*_+c?Nj9?hwkpW2|h$@B)ix ze?AKmtTH6zY_i;)obS5_^N!?!Ujwvyl`o_7$Iw2?AG^Buy4o|P&hmY|F0+R!jovGo z!^!^O>b~xMou?Z8JQy+zOOBz=e{}8XalXYxTmnd+3XNORFh0eHB9N>$G~v_@m<4l` z^)8(jvsGT*dWqbA5u2k1Kjh0S`c5fec6qZ>7dQ{2)KO2gXE78zC<@u z4RbZLPrFwwOAauI0~~u;YZPs*o|M$(=5Hq;z+;D_eV2_wCSmU-(t00>DU5suv=4rp zf9ujoV&i^MeSz~f$K;2nt8e;eR)ol4i1Ypy6$*69g8}BSh>%r704w4eGrMpkqp!B< zat;W)tVcd^c(-Nx$$pQUV4?V0BzgB;)(fF~bE2Pbd1Kz&is^f{q1Dd61m96OTB?m1 zTlLCVGRbQ^OKf3V>cB^R*T-~rZi)FC9#TM~rS(xs?-VHc&WDAHLoE_Y&At%@_eMljykY=pyrvl8IvB-kgbBU3rdV;`Z_i}U+-(?^ddp||IO3qO3n;li0 zls2sIxj@vBuvLfFOV?!wkuYkn^d>9g@thYpwH@vCBpIDwAw^N3cogTuU@OOmc~XA6 zhj$%uSMC5g4b?!tvCH0x|bu)0DDzBT}}c<_>-^ef^vKD*wczQGNV;W zZ#;Xw0y}ZyewZ~3&j6#Nw%(6R{JM4IJ0Fk`-M!cMIt;~bI-l4LRZ>MmHRkdPQ8)6B z=|V^RP&w!X1PW33_r++7ltcBDk&&_1ouQ$}n@M~?^5C#Cf)PIJUu{TBI%$irr-zwHpcU-UiT)FutzzgJEyqScK;k+XS^GxPo`&0%)E5P^J*aw%bD+qf!oFdg6P1|$e>Wm!Y6LlUW>ZV#S&TX6)6 z$BKuPz*y@z)Vs0R|>sai@?k>=( z{ZlX(JF}@qGo{#VqVD4)nUJ%2%Pt8i>E)Cy^sd0lZ3eZ)b;XWE;*uZ8J1{Ih?68_@ z-@Je29h45(28V@x%=EUhzD7@K7>8)*ef;4mQ{0C%zI255nXQ-lQzEGx60CD zCIj(0+Z7$oZ=P4(&0ga;R5NcPwW7JutO=f12kX^WB0`_EJDqBxPG)x97v`}&udhXb z+L)8#9&eM#%aVvk=P*p@|3_>$)d2HwkaqM(>}E;7fBzmE`y8LwVKnqFGo#zWe0fPl zmrYHB!4zre0&*MeB>^Ce%&P)8xGVW1t^y?>y}7AVOSe|?u!^?SW>)tKm? zoa@Hg>hlcdbW_1S#n33ThrO7sot=Bp{3q4@>YCLL%F4=~X6NElVg^$!OP@AhE2@sY zlR0=7cNe*g0m|9klVGfeuUBJuz0GSEe)UHIFX{j3@@ekX$hyp-f1 K?1d& zK5OT>o(7uogrmJ1ir&$&o4IsR2nheOYAfw)y6roc^g6w2H@jO_z8kI(uf+cF@XmRN zkJc93r7~viwq{Q7(#%j7+RNHR*s98xzwk z7X5lBNh^*yffIE8uypK)kX1uU{%}XRztt))Cl?Wqgo+v!9qYTd_g9#ggW~lo!K(`L zahjHvN+<%Zk*NL7fEr3%90hZA0MQKG4bnag_EGv2vUutx#V@{IEPC;ga2U(pSlAQU zi-u2{`}F$(*&0wopZKxpRrC74Lo#Iu^&;{c zjM3Owl=S3A{B`t%?MKD!q#zkfa#aCr?h#DHQJrhzI$7S=j(|1FpxGvQ>+PO^SpqS^ zP%HMDua!$t*n~VfXUE6;ltv;El~})S+``AP#y)YM7)Xj6)I5w&kmX&JmbQ+MFmHH> z_V};YnIsl<2Z;JQ&tt%RSF;3HZFkvg_adY+MBWs#k`8gB4+V26Py;V;hZZ^X(KWJ- z{Z{jwrnZDS+$(>;_kqC`ej*tkd}J(Rzu9_{Cw9q0Zzp%d$jtXr%N@k$Oku<$1+2)Z zpVH^>H48`^=27i_$0sO=RxCVekqeR0k8F$9p z2~3tg6MbZotwhTx*~z3^BQ#QK)!Yo3sKYdEoc{~*MnyT`!O;5|lH(H^Lr#qV|l0 zSD=O#9~Dy)YCg!dEJcCt_xzkfr;D)6<7l(p&bhX>hVZ=&{M`Ur>~(OxHAmR@hh4MX z*UlcvI7nY7K3>tTK*`>?zt>Ou%GXJt-?NJ#^>`y)s1C9{V%?Z%$xiPEt81vB=XSUd znb@g@8uET|4{!HzwZn(&WShl$#jM=`5#L7tJ%JiSp_y#lnaKEPNhTcip&poKVuHD5 z=U@iS+C+b{nk%x0*pR0gC_f7+7-ysIBD+|ES01;L1&PlHNc+=M*-E)=-FH8*0OM zXKQVs5a5Uk_jaleoKo2M*i`sPG{@sVU~N`ssDQk_oDatN-CHUVia1}Wn9j8fi$ok% zo{6u|>+t7KPaqO0Cyk`b-TgH=6Bibyx;leq`H$FY$I^p<>+7AVv*n*KaOO4^KbxDm zEy3xdeSKughIdFvbxlo^SMI|@LsvJeoB@FW5OpohAh%|_(eH{}s zJFvC0bDr=$1-VEb&6NI?XQQ5t9K5qp4;397gg6?VK?xl;gSI#oPH?(4a07j3M`wM_ z%*3Scw#=w2_yq@AQnF&XBDOp;Q{!J_D=%xlur$?Nk-@M67M%v&yjOlgLZ5pr?PJh8 z(+=rGN}@hsb5tJWOo-NKiAkrrp9& z-|wr=hCq?Pq<}LD>4@m)z1i+g7+9Du6%M-K5}m=%pFa!v-$nF6h$dkqvvcy6*+eF- z1P0RLf2UyKNjox9y?VHp)>g%RQ3KH%H7xM*i$j$F1Lz8P#)2@%#1sik>9XG`8vZ>u z2gWE#N>289IHeBi5&0P&Zf<6$(5{eL-%zh92r8;|77Nc09@n~f9p8I>dLeT-KVR{B zMUwujSki7>tzszf?|Ar64e`p=48WPl=}xl>+74TIVhk+jg8IB z%zPeCv0FYNLe-g{X_dwhj&^yZC~9b6AzDpatdGPFNy)0$eIfVo@Vvggmqvq8@ihV& zgd!k#;{i*3uk!5D-s4O7?)srt;TwFO(U^<8|=&L|* zalKL)514t8 zjgFw$AEAhQFHfA;uC7Q#JU=2it%Bh8Cyr*!Z?iOk0hw#Ju|&Xf6ta5{RjB+gxca$n~_To4}oy|nU;pr z4To$xnPMBEj;!^UfR~D%KayR&w9(`lOUjI0W@Ln~)nWMVQ}k-1C+BUMGBphi0T3wY zky0?99``;D5Z}UMO$K6CSH13m)U(8*JJtbGjEIaXa+x)gl99n6!V?PxYi9NwliDq$ zPC|J*=#2jJw1-Fh3|k;;zwC_lCpR1~JIhLM?NfYm%*8e>h;^l?vNpcAwuF_h*~N0H zK~rZP=Zi2aOIe6}|AIWkE5Gx#x5+yRA+KnVV$M1f~-Fv9lS)pwdiC60T*`n9LlYj z{$;cflBHTNa8L8)-^kvWsf<#_&^OaTb{=-fKbvREuS=b@y-m&&JBaCY+T|RLX)L4V zzKF1%mok^)l6azTmzP`j;LzK#bG#^%Tgh7iW8tiv-**uAb7YhI=fUrAVM272CaCcw`hB7LmNQ`_r92$74Xa zJnsKhPArn9ev+1f`g3%qXr`95pobYLwNRx_yFPQ`dPfHY%+BuNNR@Q-+*pJZh23&3 ztGYR+n(|=T`*pYn+0EN)X0-JT8X@7tx9yfSe1c+0XY?gq7UcZM)wJ; zxVB5{FR=CHANR}ljFo~tBD?%`39>|5(FF;rtaFZveB`~ zV7HwGc`pZpnO~x1CP`5wP&!>s&QsOKlAuEemq?%<(@W|*e zIst}6KmZZ<8UL5{Lh@x+!#_YuTmj9+Hjw2B3=a=kz#m&OYL2uk3qnBkJSvE7t-%r- zZaVn7W~Zz+2ZP<)r=ll18_q>hkK+D+)iM%QPtXpOW|4s~sDG(u@`GJD%m$9}nFsx{uD~|}2-M5XgEA|XZQY~r z|9~SOvOQ{NBUDo0tifMv^bfoL7xeLcXh;eGr|K-&1!pH`>NREump4tUx>5D>?`8)- zd>2^tlaegKYK3mt6-yPd>WJD3puH!~=dBqMQ5pbX+BMskLEhGun_^l+kZkwTibj-m zq7h(BHmq{;2i~E5e}6wF#4|CWJ3&lDPwGJ zxT$gI6;<&L$^HzL&*N-O@P$@)3h@y+NK)qDVK)WG;5+CvM+pmhu~{A}IUvfl^|3gv z-<&?Vyar*V2h$HP*%y zY0=m${`u>pi%bUQ`-c>2QAtY5kq>T4#Z`*ozw;KQwayTkKTAo3?3lV?QFdnE{DsORfH(^B#?frDJk=52HqTRL^(F2j@*HE~-bt(XFSH%P*CA zl1qI_vD0q>0Y1;ihyUAZXe+@${|p!&E-cQP1l>= z+X=KfG1qs5BPHrNF157i=<2%z7*mMVFo_FB{Lgr??CV@RXCgZK_CfDyelVZsD<3&I z`P>K|4hHz1F%JV=U|MXUe`0E$jPK~=bRB{(hgQn-!9HNnf-#v`$l8?$>?S28 z53UQrmU@dr4<@E+JT5DT5eRy7B5X!|{YmEPHt#*yRv}i!v*){O$}q1>1rH62mP`w* zw4s?JPT)q_Q+wvk-u;z$3qbI&RXO#dlsq^O`kvhX{NU%^J>D{ed43@$(@i?{y+8_F z?{GU!5RBDtV|TqMim9yn;*fuN`AeWh)vo=cD>UeKja!e~F)NCLt#5543?Y?~({-(h zi&1^ZjJ*?u#NzyF$HQS20~#6{MqEr*JH3!nR;|N9*UM=$E3L=p`iHdpAsRmV_q;q3 zfOPm79tv7uky>ZZ;-ljlh z=A(nxOiZNH>?qhR@S0)?(nOcFJl*)DQL7p5IgviszbX&59vvQ`z=DjRquT)l(haa2XqdxU9Y=CFIXj5IRjF3V)2U+oT9ee!6k8uWH8S3eot?&PwKld55Rd6q z^|&wq>-9^g8`fRRxxh2s$!!Y7{ReJ(Spajv>+xqy7S#-aMVfq@M8ogzK|zmC&t?v0 z672pjT*S<8V~p@n#sPr<;DhX2nnq!?w-1HjrDIEA6%mOTHUnV#922Al>=36y&FM6L!5^kE7@aZhEpRh%U~JFiM?}XCZwz4A z>#tw(D2?xRDXOT9Jj~Nd{0L#=;ushJE1b5aJ_J%wP{7~aQZjHraUv=@Ag)+8q_Dl@ zW*aZpogJU5m9)Ki6Beb+zG%0L1&hA+2K0}uWEB0QrsCv7MhQzzNcc80iYg^7Rp4y% z&9IOHkATpgiIpYVK}v`2ZN2j^7|sKmyW!PDXI1ea|BjOIveF>vDO}2Czm$}l^c(}7 z0rD=O%!cHXYpQof7t!pO&F68rYf{XiMdk8+ll&>;@EQi`qd(-$ADU>(KT(QrcuG{r zX7kRXsncLivfX z)|lD9ijG7d<=+*zlht&&CO(M0ihe$RBCBanQRs13cNaHchRz+~=RJUMLk2y)V-o%M zp?0!7Mq-&TO%RVC%Ygyv6-Xc)T!H1cc*uo6a|rpTS-A)?8O;W z=;IH5H0@;WyvvOR12k>f&dH-;ceEi*hgfT>^fE}{eB5ON1?vBTmmt&@4B+Z#FCuM+l%K)Ll`Uh{|lWCv-8PS~V8_b+Q|AgFSJBO=6nhdDS>IhNrGJ zJ1YhP)Z?dVWy{;i?Ym-hvb@0&X%W`E5VD#Jag`Ba5`u}squX7EmXTZDw^v%a)Bv>q z{wZ97{oT^Xf3Fp&x7*8s*+^SecrPW5{p6}E<%awlc=LZ>LLq#7`!3m@EU)*3_Q*P- z0_NJ_(_Mg(!K%=$?{yBe>u?potI}?_XG#Kdr2pZL`2R>}$o;1-ACw<$9rUS0Hp?v37hVIEith9SkLyZ2rpkmli`fG-Fp*qGEv z^nUlEnURW$X1B+a626X+I^X_i=Fi&&a?ciNyW4X~NsK2W`nN&B77Rkn!QypZpX*2R zen`k1fvajH-TU6FU#LvhRCk47(K$ zPDVziX&QyOg&D=v`s5@Av}9yec_{UNH}v&PZ7i64w|*b5bEc(b=;@f5*@Xf3V8gr$4K)q)fP)h;qKG8J#+%d<;F(rm$F z3jM~cqoc!a0W-a-SRzycKo~mY0f8I~aZSX-0))xpG3`SP>(d&jfnJ{Z6`mN4fm6FUb4hPL_v88Xs3vV>N@e7;EeH%|}LFpZQzT1X&PJ&*-(- ze+;TK0Gr$o4G@QphUaO2hW`B0Vn!W>)w+zQa3Jg9^z0M7zwkZKhiFaJ#)OQ=>r=T_ zE5MF^ca7ZsdK>51@u5%(rj~r%t^Cm4L={37<*Qe0Rf~J^Z#(h!SDE+G#>!}FS8o&{^}H5t~H>bXGdpd zW@gYRS6Elcb+uh20I1T~rjgsz()_yLXt17tD~utp{9N)ygGmPp*n@vJ zNR|mH+}duH=0mUZx;tF5wB&b06xP?@btv~saM{n3l%+YY)LVTLHq2(T{%Zx+sL0`b z9v4@9-JCe`+3Za|nQtZ#Fx4*kWngJPA<)1mSw5N19+FDPYijGLtYbr$rnS`N`xe!U z%LLIkzM5(Le82Zvu0EN=kqMM=?$?vL1YAy&6;t}xeyndvmfAFReY-FKua zM*sHvH#?Q``SL6Yd9H_-!=b#qUb%xIh1Xj5CS&!n7}S2YPo!a@juoE)&)N>ldfS60BSPojV;iEmVG~d zKxBL%D*7Vu%t-s)fVk^%O7ZB)`t(uhHmlQ?)=N4Hyku>;s#C&X)OA$b%TeM?ddllj zGE*SFJx;RHoh^$>Vq$4Iyslka^3>k!7%s;&-LN5}oNa33pae}zd#IF+@C}s8J*D3a z+qW=@^lh2dsKIp+pzW9AYOEzz*Bpuhl_Z)YHL-eIFXdV4g6nx0-aD?hGA1#AOA zbAx~|%iw?iR5E$Oi<}Ekzg=jnwnOOY>A^!WEgYW&b0IS)>Ay*c%4zTMrr+@3FQ=LCeny4%O`CX>AWK@d3Adi&(o zN7!$yp}jHK#K_<+JE%0CMEnE2xJ1iatELQ1M8M1&T`}w84sq!_vin40kSi!MAYR+qn8~wev0qCTX@vg06g9y9&62);Div| zWei(#oaov4Wm#2Y(oGFfibNb>Oqmbs=X@TXHMpdr0erAwuw8!FKWa9E3rz|KU_QZc zRDIMcJ-;{unxYaBHwJSe-C#Q1*A6Z~y0jSj_O6u8ONyvCp`n!~sy-68vlxB!D)KMh znKiqnC^nm)Gic><1&dD1Oacmm5tn&rc{KcYf{EQ?ttz{}|9_%Pf>E!DxjqE3A#v$s zUUAFUH{bzC$4jZbI4q_0jnR|E9dU6e6Ri$M%PhpJEl$h;jgr)T-qmp;t70T1*uMAj zkOUNw>nxiVrAP?a^x%ToJsmzzjN2tA0QwzN9YyKR+b%W}{sT>bg(W?lZZRKfd>brR zDT0WFjm&*reVZzsO(M3fFJs;W$ol~nYfLy~2?`va#kOeku8snEZps^<3EUus@q#0#v?aFDXOLObGO-HC=$d0E`7=Vu1>c{6xTk=9j= zCG_?6$@eH|1hR~3R3?pDQ|GW57&ckl-MiZ$Hria$IK%&OTApNGUZ9?Bge3;g6kCKU za4fP5F{Nw^wro&?Yupruh2*(e>l*6CxBJK>bra=MU?(keWim6e)VieP0cyVClRG3J z`YJ#lLI8?Ld7pt=Mc#Kv9IYk0?5t0ZFW3H;MojB%+{hUdg2Oy)9A@CIPfGaQ5}=eQ zrmkADF<82WFAPxpgt7;h^~HF6r>KAm3$Au=YRc#`VRxsEqa?R1O{WJHQ6MOifW=lV zougO=Clz=AIab#^oYVK(t!a3Ti?>Q_?$>0i=QwC)`$8UVcGVI2N_igb;)86H2 zpM5}5at_|7*LUhx7L`t|p=l#ftKD`5GKEqrxa}D>O$qq83jx0J ze7jy)_0u5O7VX1tZz**`>SyTeb-EG4S(yQdVZm`BBY3$6Oxfkb6VieJwPH!_?=#9{ z$LpXJYR8>A-6-=GrtwQFxwx~XG8K2`qKK^6tFa=hJr#TLjMu`Y6DLR1Ex|he!hJ4~ zckp954u`m-pt6j%HK^&^4dtk6n~+hM4oic|)Q^xKY}~xGd~9g;`ueXuk7n9+|53Pr zoQ*+*^%IneZCY5Ek(G@Juo-~g0vHp(j3)x6Ca@7BBO{YebP>dcrDQ3ABuc^bA4Nbx zNr@IcOhrS5fk_|B%j`(3J>*Sqp(I2T7`sPX*9pH(eX_H_EjVXu}pP#axwMz z7adYe%z{k0g_rAjf;bkFLB?!yS%nn?iyYn0ni`gKQEzla#mX+p@_h-1e38F;9Cl!1 zjY@Ux@`k}diIcU3L`m&S3qirxMBnRknBCpo$9#3+CDMO=9p7>l?)HvuGffTG8_#x? zhjgocOjPG)-yDyAM6(9poli2>j&8#;!^s{xZiXb*M@~LTmwaI{>+1-@nZ?0gJ#{d# ziIAiEkAeq~QH(MbHm4Hgm$#HyLSw;!<-}uqBDi`Tq#r+{OcXw>; z?8Crk04oDiRTUdMH^^j%0*^5JONRqL(tl)TF)ZLdS*Xy)6_=wSXGd4GxA$`1Q~;2~ zYNsP%YNe(u+D|SNT~8+6wI)Amz^P#VZw?#ZzYg054a&sEB1T6)8}Og(BsCb^_KrkP zK&b)b*X`9mX4n{l%Ey9SGlS!klfAt+bkY17jS3@9(xLg2BYU^)md`-BVX)@;#YmLC z7cV>GW_MiDtf-hyy}m)_dbi=HQ5*0Re|rD9~wQ@>_dK$oSigac@t*`?W?6O6xN|moWq$2A@WnSY zG^|)GXABM>-w*1ikft{VG2jZR>?Z)cg^#Q5^Z9qjY!`=%C{IYi)LL9ttp=`tmJ_bC zdl|qCXMXpwYA8cJAFeiI53*HD*GfHacX_SbAj^Y=YskU*7*%<$Z)zxYESt^|@Je}b z-lE$en=2die*o6}r{aVb!>8-J_=R8k@yBCwM!h~P+1xc(-jn{cZ&g{M;e~r28kx5b z_N(2&g#AmYik88o$eVY?rx0GQ_4Vg^y-{0(GL_7DT9-TKs|_B7;8@mnD^dJ=eSA-n ztKszE(m3R!WV))LS=ZFEa|pUcP-nPVKLX<$ zmn*iPyT*JVKN2siZ2snDE6`m>@i^YhKRZ$xw=^i3R!>1Mu2n(P=4P#lt80QP07%BE zj{6jXeTYNld<8&Jq1S1vQxH_LYN!_QI=so82V4qvFrXr=f?pAb?%O?~EZr{^j$tdH znOdT)`^)(8XvRlkREKq`^d4xAT=EJACdb+x^Oe_`9|k+ashfK{FW1o6_Dd@6Q#}r! zUPXene4Z-hn91V�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 C^RqSp literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/lfo1.png b/plugins/zynaddsubfx/zynaddsubfx/doc/images/lfo1.png new file mode 100644 index 0000000000000000000000000000000000000000..db20026c21aa921814da77e002d133a191a086a3 GIT binary patch literal 27044 zcmXtAXEz4zXG{qFnY z`;!sRlR5XEbN1eAueElWh7KkP@eGsKhM0t0RH@XSRf00U^>gn zOJYu76TD$2EdL<<0Q`!=S@MUoxSfrSnXNO5xTBeovzh68H%n)W_i7eq@1+AFHXYM)d2j9$uwI}Fj4SZ0|D zI;xO}Xfev-tH|;HfK8kWtA2Pyc@1Xq)YhbQ4uCV z6H^lbH#H%QdxjAlTq`9dm2X_}L*hcij2DKn5PB}ZTds6PQ!3aNmVFfym!1yINW8qh zsVd;36!ku~8px5x45uV%LE(v!QJc!LU9cGk4O_fl2JFA+J;;09hpK-k7X9q9-QM3+ zx7PbDZ>(BOi7-r}CS6x|xNM;o-dfw@!}Cjx<_qvIvc=HU*VAfb%sNJmIqz$ZRDs*!(vi7NlPmCL zyc}qiRh6>MUP(<@Sn~}&P$A$$UgnFmCP|2=3rw|+^5w;@(b2MBMn;BbX_;DR$HxRq ze@*^mtVx5JW+E7_GeT8fub?V>wa@PX1@GI z&~;CWGvzBkg4}EGUw@C^PR?qBdtCW#VZn3 z9jvs%=_VogP8LtTk(8hN)}*73EoWjoXr)#+_QKO!geQ40plpjccA`WfSErPLZ*ij+q* zj?S);{FHCEweeU|u`DX4$|`OPGN&tTDPG&y2s$6iZ5;}4kLKZQ5))(NPneHnIP5Qf zz1*K3UO~eg9UC(q>XMa9;Uo+v<5A<4F*_M%4zgunVWcsYS=&W)(!3q1(JvpuG zT13w+RJ+=iqG{`RB-O7k3ll5F0~w2%?Aw)1COdBY}7vS;cqOO z*VXb|Gt+m`r{B_1l>OWtdD!Q5+sc*#ehgNSybW*JqU9f~q9iB4a}f^XxbgV4m^J>QIemf@jC`avgw>6hEkmO7NF7Jle8}GsOlTNKA5`X zee#(r5*QKyBw&9N&S$I#WfQ5p%kMAJ3p0ESsC|m0s0CJV|l!i{wF8rkk&Di z1}|^t4?WWMedM^LF{ckE-l~PtizCSAD0C{{_CJSu4Za({E}!;b^2 z=~&nx70C{{T9Rkq7Ob$;xR^L&mRj{Xs6>6G2iBx34WtQj7^#w#@xVlT=?k(l%L`$I zI}=4j4EgzKFY(R$>z0fxwZ8u-E|wrSu1G#nEpVXWekV?AB3O64vd2zxa)0Br0n0}* zWKic!?q{|bd_z&rPz2*hCOR3;FgJ6s82OlwOZr7rlt%%zL3gu4&Lj9Pyg5`1ksPepQj{~ec>@fuNve;u%=wSqDUjFa)6hgzvT!uAt^hZ zsS~Z-?lqVudG|Hl6E9iBn;^%_b!GW5eSLksuN2AzrKe>>8%2>|d$_kf6mc%J<-vtz zfEOd9wjN-rSXj90Ye6_3Ub|`%p>HxcHV)Lfi?8B)qf&T0XnvZbUi)4gIqxE>=B0|n zxz{blTVV1qMU`u}I}KOcAA>pTrnfK2zdo(x{4LihpWtyU6A)FDmvA&#T8~fk4l8I%>UdxI+=OS(G*Iw7IQ5US6{#Y#<#nsPSDpF6~EHGhIQ>02@Tv`X9YB0UGmR2H{YB@ zwQ+Fq6$sYIzFPPl+adP>zje7i{hI%AraA+{Th==<^!t%kYTRm+U}%Uf{_rR+zW2V$ zhz$yP!V6DL#LeUn`sjoCr!o0_w;SkZm+>&{ zXQ_hDT#}0#HAiC!J_RGO-cKUJbJa8q%-OmNaWXS$v_0NyC@>(^fullbJ6Xbc! z4$IZ@MKl9av74*9gxuav6qDe9MnH?5LHqp2R6zB?;!+}dflwkI8rZcOME`D?WKw9l&-BkuBf6MR5%4l51X}2@Q?c|`If*=g* zu0C3fTXH?zxYE0m^*E1yENw{^c3^$=b&Cia@gW?XSgoppr`Y+E^h-NTRgRM9S8xQm z(`WwYA&oBRc?7AFEvHetyL)E`c2@Qm=f;;pstv9?ubqf}vmDf_iVAr1L}ZPo^6{(< z?)XMBKx2?;8P_zykO;j4>>ay@9{H5IeI3xyQ<@(6)Jgw&|J%2uocjk%&Qu&P7HG`= znr5X|w!SN#oOq7P<)^;n)4O_emcs2LnCV^df{l%>!-eZB_j@9ZOkuBGx;0FE)9Gmk z)*qE}mK>^$ZFXNSX14z-)!sJ*(>@*M*=&{JzT?3tpS1ZFFE}x^G&K^lDJ$S~&OttP zKhzy+AN)JB?=O}rM@^x!?O-KY%U2s8ans9*GTlarI1s$3qa=_`SU5dJHQ)Yx`K#~a zp4~0(+xJ6VC4oqDotg{t1<@wbGYv>(Rn^Vvo}K!nfmZWu5n1*LH&>jGP;JFc6f!qI zm&1L3?%j*X_7W-ijo#{CMHZnFH1~oo9p$Pdt9D(dQ$5Yqx-JWR`CdtVWus@x!^3x!4uFBSo*+4dF4r-Ll)c^qH zqS%Ei5D!uBd-FP6^w#gCf;_*kAr;rA&p^kh1w%7mvF?}g+zE!eTfF3_i>Q5mir*46 zM)pmKtnO}~_{#JXt@QtVC=_UFtUPZzO6K(V1f42}(6-AZVtR>k#ysjZ8iGJQkOBpg zGgB7k8d>FH7H4o=NEAL{ew)svi|O;Kk`h*6Bg}xRnmntv_`@qBq5F|y?(ISq%i7&l z=X6(Kdckl7_@BnC^FA(342L@RbB@!~9;lYK&dK9e_zfR_?CeVyk0wPoP$Z?4v&G*| zDaFmY%$y}(=!WQh`w)}rkGs1gJGx{dpAT+?ueYc2a5N?-t*^~VdKef6JV?n1vX#k@ zCEFAEgds1-dSFaT>^HHPvR8x8tBT%aJMdBeJZioE9XTvNb9zwQP#RW@(Ek;7|t@W0E3rw|~(h72yprC(PeKZ@v z8CS5nxuHj|3LQ=t>00dbNl)VK*UhuqL4glb00TA%DwZJRtNt`9899 zO)jf0)2O54HwEwS#*5vPyI{N@lSLO&exxe=KC92;q$0Y;dqa50d9w=&3OK3B>^h6& z6)8nQbGJ1mIu9(H#5`|>J(z_taU}}k`z{7vXUD`O_8gW%r|UQQBxqj!W01=pSvGo0 zMrt`xBwy)I`hp~zdW4^JUZdkTD!!fFOU&odNzm_GlcixV@JcHiwGjBl5bf?!krq-B z5Uy37jkKon7gcm*3Ce=pt0Gv5pB)%99Js^9Pwe}1r)sOx>WpEQpLYc+iPMmo{GlyU zt)p=!qFD3V`0{CcdF=ODfx>BjW%-N~Z?e%Eqp^hro%e-SmNOT9Aw%J%SxhG>I&j4I zPRH-lo7@WA3KSAH(f#Ld{kRPZGk9FE!?#AZdXZMiZ5Eu_j=wOh26W`9owMFnGu+F2Y6zv_vkxt`aGn1ugV8Y}JCfr|-pJ~~WJPOW5lAAR(+ z=x0X`i6|3cfk4cLFbMj#ndC$b7A7A~Fr7ef0H%hq>Q+jaxbS+w7`}|Pc>N~ljR&&y zwbLN6?za!+dZkbp!O;4)9T?0k#8+niuxr4{!EHK`%XCi3U(ey#lC5dud4K$DZt?-X zDXO75*ps+I$bhIh@>`u~c&#Gu;liap9NzJWN?#pCCYqra^0LBwOJQ*r5wM<{`%RJO zS*N)8_UcwhVj?jn+HgFU#V#`z9UZa+O#kz@yxdzZGqMmdn(HP1k!!oFkF$cjM&0H( z^@Nb7&Y}^ikf2ZIyAq9_jgEV_JifYhDe{<62(Qu6B)|M$K?={0hXPr#a`*XT5-MIK zjvgND4aWGP0KL%h*|Fhxqf0_8xOKYLcKS=(aQex` zDompKU?PZT&b9L5*dap{q9FRDAxFjYyWLL}oRz9Il3C?7$bjK?YCd_5;I`iE>o~R` zF24`jEVHHeRZgcS6LU8w0&1|?`SX#7AvkHcD6SWVl*?;ot7iCow@>E|~- zXaS!(N}9u48~1EGZEEi;f)Dnl#)i6sUJ(|I);ei+m4pA1wazv$!}6K$gt`pSFD9z1 ztCM+6Fbe*4jv|rWOLI^n17BGUjl`a95WR4HbMt47a&1|1e7CVN6#?Qmy3BvnoiDUJ zoU3-#p{IOk;^M&(ne9K%vW9dj(^Nz7#}*ryzKf&`9@Oj5tH4h`YCBIg#Pz0#IDW9Z zsoR{XGPmZpPe#tUjh9)u<)O&OY1*j7gR@sXvQC*Pgler<;_yX*w$6Vh(_1X_8Ja$% z4~$whrVtq!x*V`G-atXs>(Ab1+}9U_?ZuL6J?G7(Zl}}ituxEvJ2cz@Vi6O9RpUijmnuuwZq?^@$S3BFYY0*{Y z*Y|T&(-ig@Url(p)kQ3*pNq-@j^)n?}W)rWO){_Y_+rVFhZb%2`{s`?;DJ(d|GE}eYhcE1y3lw9pcXV6|5#z#1!b3&s`(M zRb(O4&+aH-Kk?QzU8I*}=|M)R-BPfTcWbbrs)5Lhf+;!O z1~)~g{<<6(aN8dCl}=ktPt=-s1@=21E|A#R*mU%Sf$vyYk!m-CH1xgwN|2>!>{4%l ziRnqiBfoZ=`Z$dK+)z0OL(l6n*F00C+H#3fPfsteo%M2-=f9r*-_wKiathb>?hdE( zghl>v+RxHv`beIJTWcZ&+%!-BS54NB4AA7+*1OpzXs|pKG10H3lvCikAQ${NfNnu% zanAg1;+Jg{rO5j7-1@Cynm?ggNwh;M2VKQ z&#ys}?6B|_g{jsD^_L(LT3Q*o&;s!h5oSy-cnS%76#iL~Nd&5kZE$e#IVFeRVtIUz z@BOwib$ecJuG08ethR>64=o+l@yban{U2IlrpGsD?}V?Z@Top`jTI3eX6QR_r&7^1 znBTp8Nip3!zynbWv9S1S%fO9{zaWE@-u`TZ=wqq$!Qm^GAO@)(hM?X$O5q^!fZ(9a za^29)iIo87ojHSzMlCfNf}aHnqgm2aK`vF_%+K`&l-ItLmMA3a{4lgN`WXj>ssx}7u0 z+st)mf)aW7l+-ZJ@XCG97Itt>DwrlB9#X8vr`jw!_6-YUM(QDJ_KaJLp{~TnyUkl5 zlU=VT)L{+V?W6XI%ZfmmzCO2uGi&}8DH$m>ad?B{4h=wD(Qo=QEVN0lrVC9JFA=QK zG#b>{YJ6=>K~YoHr?7-yEuq*m9uiAm9&DrwoD~{rg)egzq}(ALQ4)PWh5T!JT9F+v z*`2U!Z=!lxwuHrhH-mZgsP6oire&t^Lg3~Elh{{ z`j6~)k0Ek5$0lj>!Uj#5cVTW?pRUk4htoUSN28zR^lET_J;Bpoz1r4 zL>1)apI(kqJ6j_B70n?sIKBBLI#t?OMJKU(O zEu8>afZYt#;lmj@u|x(_6rc$}_9%}awV@5Fiwv+o?Pl8A%+gl})Hjd9u4&loA$U4k zH%KvCy*7;Y4n3ji+buHRCv(v`MRZ4&OTn#3DtfN93&WNDw0HS0cfYGLlDw4CUm5X{ z=C8HGpL^8SbA_nKku%zJ&Hy& zkm}F@oIeRQ5yX!NJ7aaCw-^|2Q<9q}ms}UTPC1yus(PbNxoP84H8gZzK(vHsNBfeQ zsD``S`(BVJ+NCx%I&pq_=hvdc$7y;`x65nm9~t@a$oJk7cJ(y4;`d7-oZNoz4_o*I4x$ z=+w1o)T~I7|Lcu}my_TFzZ+Q#!S}e|ezmztyg%C*)Q}1JIv+v4E_)Wexq&E@RUO7m z%QuQ+@K=&|*#RXag0V}WOlz-211gMst`sDsh-bXxA7B8h65AY&~O)&~bT?1grQejYIwBH;>$f%VYOp4Wc9Y~fSSs-5gV@YTqU zyHr)qMq7~mfm#1)t*lCD6D_@ecovF{Z(?|5dw;XXE(g8v4KsiNjM(*6_@8BfHa$6? z;Zpez-1l6(o33g)e#hnHgc}i2EY7Wl0;>E?2%uld-c%X7k4QYMm;93Z34{0KZ>%VEwJG++0i-3cAY__5t`f{O``j;F)Pd8y%?LmKe`BKGh5U7&Q-$xRs8E zdavcpo&NN>INH(KCAAz8AJECi@&e$4$Mfuu4JS0R6-*$Ss z!uxE)7~pi7n%BFxKTy&!CX_*yd7QaYFp7Y*Mptc{|G}^B*e{Nr?;N{_L z7_f2*B<>Im<7BeX;r9+Q?9o^M$5d@^c0Tm07#sq+Ial@2zxm>K8bz*9{&5>xkn*&sMmm1jP7;C%82N6 zg(a;JKG3vY>Qj1O_oFsm9bmTqTQ{BG6(aDy5S-+#H8nFkLHgg6JelB4*4E|<%XW7S z#kaZbXWKs9`m4ZqCm4{x1q@-OW0>z5oqkKG%GIzXiCDNIJQ^DxkD(&`3rzswV7_ClTK`!C9Bu&g{wjQRfZbKjd z!DrJHw|_)$X{D$_1D(OopYb5oaR@Rz|AUF4V6ZzlZd6V6c!GiSj^%aOwve^N29GD0 zPkc6RFDY*Z{>CY_=OeU*75n+Zp@i)XN-rC~#rE@k6OL{m%&=c2f)<`Im@MwIbNZ^uJ>&@_18l z=1w7@S_r!o14F&%&7b0e0ufq+e&?#nd(D(DrJn*VUhw=UM*v)_nmAAK@E}b$Y%@cC zPvB%p#M#-!;o`D;uAFVr=HB7-*pt*Xx1X8}JPIE;GoUBK0+Cq;6#@O+Pc5_T)ZHI7 zF!>}~=lz+GKjVxums3ag)uyj*gM;0#Ynh)n# z=d!K$_833|sXCWMsn7nU%tZ&>T-hUKHU=LcXMAlYc$&Whl9jy*dx_7;@W~(N)vM3m z$9@MPkuMB`m}U6J35v9ks?awl!*34CYioJSMA+;vY&+(vL{@LA%q2pe6Zy}#L8s#4 z;>25e_rDjJyCspO=U2$-}Y+TSM;kHY^*2I2Z5mCI-Uk;)- zF$Vw2DJEC2wLK^0erwL>w##E33Y77YOB>b)XWu+ju8l%X;DkS^sj8|#&++gqXR^51 zgq$(nAIM@GnWO!8AUA~4GskC97M7NXAvjw3Ih5PcIXS^Y#Q0`^4*n*M8LbcUtfRn95YSRUHDs7r^#X#Yv!-H z2U5=jgMVWh98;Zu;*N21lsaMIov6 z^;vm&dAtiH&5ttAsS1fuKmfC++vJ#1hTqDHhLgG1+J>7K^Sa;Otd0JyFkVdY2aoWI3pscD3^D>}Svl{w(r8;FojL7u(%me!qF_FG>f$pO2%-EC+DR~wv;D}GG7chWLx8A3C z4khU{dsB!qlmP-cax$kD_St9uJLCCVob)jk5Wxd_A{<FpwwJsultto#b}TKsBqTR_Aw($C7sZ;VkobJKy{nVSb~0yuJ&{4xaB3fEnU4cOauA6VD%@rK&dx-(6%-&-9#Rw2o)wbc%#k|SHTDyudUI}gI(PbIJ5i-t-~Lt)Ca^8SRlBFwMLdmO|>Y?kUf+Pu@|578?f!k*WE z3F@{=B2YRhwWqI{&5Fm(#;=W|~y!|<* z<7@S~-AEwmQBr1TX`ZAiiDqhJ{U_^AQxrOHp5(sQ7g0F`5xW}AQOko`MGQ$*q)(6?w%G>sLRXrg3%_LaD9;0jC^=uZ*HJhnhy z@+h(N=OpwQyBsabzGtQpjrdUX1+59f{)*PbBu!V(!NIM-e94!;p#ky7!K8~`eZ9n= z{^!8mL6LT-REF9B+)5J*ADb?_{ok(^$N#QnnsBrI*=0wQ3;7MttbZwpjYaTcU$vg! z&Q*grDVrAOtL;O1@%84JVzdk$w~K4{K@i{8II5M7PzMYIlz3Ex6FO;lSOfZ_u;t zC>S%{~tG0T}|pN z!@atliydEE`%8_~s(@5;&%0{3_@0LUZ#yB*`fm4Iy4hL&2CwDAwZV488n7o>`FRPm zu&MC1N!Oa4Nyz(!Tm0wCGvfta5`nKs-$_UudrW#a`Ag%+A`r-K1RPD%ZmAPOoLIvx z#-}<}Bwutg%uO<+zZ{d>++SK={zUdiy?i#ep)rNkj{zM>$UQ-!9Gz%rI6wCh0j4Df z`@o8MOUE(w9(Mp?OmslfKS-85n_EyGB|SLlUTv-CetLjvABp{PegA~E!a?15dnnzoB*P8m_Jz`nuz)(EUARZ>zqKCq8BHn?5;3MnTr(zUB8c z0V8K?t0S^rvHq=D)Cz|g^|#gS-Gi8#c2sghLo5^9vPe82T;-p2$RQkPURnRq$X18O zAC|wv@-9N}j)j4M0+5tVgM}}s=x$H2@^z~La`lmi)oSH%?faC-LrRqeR;2A=5YDE? z#%>jcl+<;tO_bf7EBe*@jm;Vs^!Frvh?XX;Gx#TzT^?{OzCHS)E`Xv8z7LlbHENmV zq?&=jW7z0b-~~A?4zFnE%+TfcooHuY_r=`b3e3y^oKfdO0s7OOGxebeTj7C{X4X<^ zQ64Y#YvpnW)m=SThAlskCb$m8LE-Nh!naisy zLB3x-Q?i#!nHp1+3^j<|p~)$0Qx!O+LaxtF2erEO8)ZyoT6wRICgty&ojGY@LWAKH z4kxE}Y-UK^i`zHZ?xeOs(| zB@#C=0lX0>*2rI2TuZ&kaU2su)sDT8w+Zs-XF6YDS`zsRf78 z&l|+etB$X^c_LZ10l`3|bGXhr&wG1YPFK$ivPSIgR*}Wc=CUF38=4s53&Ny=4$RR8 zkAlp=OQOy;J{&Fe_Ye1s&0=h!4(#r4h^vy+bHtd71}gRHF<#A{<1tTbc~esHxGoi@=BI z1V5Lcw4(yKc=hBT=Jd=|$3h)?fOOQqg{dTl$YJ#UM`STC)?vD3`yh>;u&}O=_Ui6< zrM-dJJX^N7e<-$y;_(il^{orNhlj`Xq{Ff$2%}Wc*<(!w`X(=x{vZ|*P4Wd<=$@XF zpbouWT{G}B`ubD%SEN`;UQirVh*E@YThyBFP^9$-ap|d6>@%g;LlcrMMX1QnPUi$} zcD-nZh87YJ`EZNf{UVy4)6;i=#!^wNnq-THu|?kg8`ro~JnSXBpoIMw9m8Ve)_jFH z3gGU03r$5#bW=bDU)nVT-Hbpi5GW_SDyk~UBB19hKeuEX-SAn+$$<;HBW&>kC(%dT zNt4gn*_qiDng8t@4^r93b?~UoLGl_dX6OHtfHd$C7YCZ0 z)t#x@>Bs5soF&Ou)IPxx42+CWFRZ#SES!%1!H}D!C;z+z`JipkDM#Y3w~VoTF<@+( z*H(K<9Kfus3ORR(;WWY_&l9- zK8$3FZuKO5N13o2!=+fbPa;sH6qW$asscu;a{5n2A{1zptJ*n4gFiDdY!ZK#QxF?z zM`5cV)-=ifTjBPh)z=Rrf{+OGMy+*%A*)0wY0+nAtf?H;My(aye<@xas{b4z0tlHb zbF;c_&M`XyccXu_oN`r|l5%LY>QDLZ1>MB4OCCM3!m#RO8@(`r$j%lIxG_Y0q6ru# zB=L%f55ec;_y?F56{rN;kCxc-g7b&~@w*)u?qJ6s?IERK_u z!Z__DM+!Z4deVzzqA7|6kvXm{w-T6`#c=U96*%RSyEf?=XPW3cx7Yk``}Qk0d>;*r*Q-hI|C;eZ6*2zXv<31< z<|qB|eT((&-x4}da?Ov_=!qjKl1-E?z^QZkEBfc>HBo_M%4B$>57ES96n{xZ+f1K8kj3H=#k7k z^r29}Mx~3ZSWT_!SLv+Uq(F%4!SZ}q4jtnnz-4m7%3HU>7JNJ_1mP(Sl?V9_1f<9-YqQtPAhG0yY!o_S)WZuNW1z- ztuXK6E%)#CxLyydeTfu+ObZ9#4=u}{06?IJVhi>*+i4I}LagQ`Flv7E1F-{a)qmXJ ze%{-U(m?3JU$hqX&tv{|Ej4x=`wKp>*Ft!0zUb&c_0ce` z;%ynGT)2IZ41b;q9ft0$Id?2!tez0T?_2j#2|BLN?2VuFTT_MO5aZ^E>~umDFWxeG zF4`P|3EYU^*yUMl{g~ot;yK2%R<0TvS!YIIkpBKH!{%nLum|hZ(d`F;u%UBqfMt%m zy?*tD`XW9yv-ehv9T03oyuOQyDP%4?sy%0$b)ykt36J(8{4q1xQx%$oaYG~zP%qUk zfc%EEC}LuZ@EwrnNywu%^OoJI^Oh_GIZM0^xjgI{8^ETerG0rgBdKYJf4I=XGS|W^ z$z$dO`xk&FP;Y)^b*D%n(lib)RZaB;b{6X&*_Z>obml^kR`o2vDmM(at^pUe z_i{rt>Yvw>QG0S7sfd~2b3Q-EKAIXF$&3d0Bs~5wP3R@i?h|_6(oq*EF3-#w zv@X@8InZ2HDMy17T_`v={8Q8{7vwaGi_gEXt+=yUk>?NH`T-7|)2lKFB35R)&dF=# z1fb&6L|g+Y$R`~(yEq*kozAGVzv@*277kN$b3#(G&O{%WZj5{ zHK2<#Ds0qk7{oFr7+aeCg9Y3*cwBtqE7u1hR}~HTJRK_&)Am_4d^t0v`}@;G9pYP{ zlL)u^A; zs$4Hvx_%sFx0om)-BQT-GqOzWecqjfQleWMSUtf=x6~Gr6HacfNZDK9LZ0|gDP@#; z%^{Fd;o~`4V+X0s5S3;nKD)W&`cY#^p24OK`wiB(n8*)?zpL=2M|e-KyM^M4j3jK! z^uX`^x&r6qsOFP^{`iyGu(zY5;~5Vh)AW>;Qi)$~WCkU`UjEW6@Ezamj-4Urb<5U| z%a@~?z=@IRk{)d9?QW6`d*I=$`_s@wvZvu20Jw>IK%n~DTGPwQqe3$io$G`h9O4Sl zjqzqA6~>pj!|GUQ&t~Qb#`nJd)ES274}&uaJ&XiYXZFh58GjO{9a5XGU*H z$zKt>XCQn$349r`!X!FNPE1}1yD>b~hhg#A8-vQ!O1%&`6vX+2-#Dj8{+O&pFin;MfzUq@zE1za#P;a$7?% zn%lk>5;fi6K;gh=d%SPS5Je?Ice$HjeDEeh+G>A0G@+DDs^`YoUe%lg;EiNj-SEkq zW>K8?5#+Rlgea_~B^Jmsv<~x;_!r>HOOih$qbos>E6UHmGZTHf$A{$3@o-J=H#TgHyw-S zqXgQwb!&S;H~x0cKRy7%Bby)f#hN}Y9A8)su3`3@VT=r{mI7D_HhpN7#BQY7`6)o9 zy;Wa$)pq_p3(KCF!P#lJT}uTIc9XP3pvxcj{q20E#Fa-e$r9mDhbB&LsrufEEX#93j)&<0JS( zAf(QY{&JV7#QH!+PG)&ILk~cFt4Vf=P6C@^u+qU(X0rF!^Q5)qchHc2+53i5FWYYY z0zmKocCX!3wg6ZGm#egn91o0fD>Oe=t;Y!f-r!NM_>DlnP zNc`az(6G<*)Cs~*Y&Sw`%k)Alx3YSit-*H+C!=31E-Rz7dJ^u6 zGWfXO!b#f)@9#js10pb^jP!Fr6;1`d$FFcillvFuPMo(9;a8#$tMEy9;Gvz%g;w`n zajlBhM%3pRJF7OR4*t86;#Y3N3kmXuYC z^~joOzpodR5clO^F<}^RrU(OlPv3|o$9_SRj_Bwb(pB0u15B#-L`y^r+HEmv zs>{1o|8;dZpiXiRSmzZB3>z49Qs*{pm`Xel{2Vhi_ZGKRdIu6a=P?EB^iJE8zmwF2J$&rE|4K}BD{gsFI- zz8Gp+4S-sPV>4YWucL8kVK%`U1kmnvPNs^e`Uc^}(SS2cNnXBC1R582*HgetKi}9v zawNa0>=^4Ur5IN{!W=zI7l>eq@on{-`BP;c{cWR4ELDt0y#_pfiiDhQ3>aKr>}~u$ zL?+aeS0It*MB7L0yo*4?oN)S}{^z!YK^YG8G}m@_43rFv|2dL7%Rmh3xR`Iwm%r=f zyyu}0V)_?ydb9roljlPt+5w^JWKp7FRBTzlU;u&6?dJSzM9b+4WOk_K$KhXu8&f`O zOij%D2iEH(DDlMsWxm`tWoHMlqpC~tsaiy!5^`}pPPfL{>`k34Xoe%Rp|Z$as4UU* zs%*$)b)_+~$7aajk>bCNue;ZI<$jxM2*}(Tn)#1~3Vd+|zoYhxmV>j=yNuMZ6Qpdr0Ve;dZ(W1!M{-Zh>L z{>u9dxs*FlQymxM{x)Y#vFmu{`mVvl!+#r;AS|)gpA+H_Q2W4TM#2XKKPGMr>Umy( z_BwLX_`O537KGy5w<4vsLAy0#8+C~K213Z8KznvV+uw&H(d~uKfb_j7K(keaIdi!L zS^(U~dqyE>Zj;+Y0TfG~2i;r1m5HY0n|h}D)?kIE;ztPQntuQweC-Rk5CaOwZLLi= z0G~H7V+Q^}u?XxYOP6%^@o)={{>HGTU%m3t_WbKMnEzU{Ny9uexbZ*Z%wMNMOeLAG zz?s25NQlpbVG$6L<)$=}79rrz@hTGsieL$bLBQ0{wa=aCf7Pu5?Uuu9cD+**@ktqu zJqag388Stsv3&Gr9tS~&Izq#p4}|>KK_j^aR_vEY)8F*<^}AK&v)bh_inLV76DN5A z88cdG-R!5J3zsE2;H}_H1hlv4j9ZC@N9MjCfw`$0f;?i%@J|YFl6Yjk-$nx7hsoxf z{m-ty_LA5U{euxMZ4W9%B-t+)UM`^$Si+ea9nYAG3xVeNpJy!O8X)tJp&PQ z{W9N8oUdNu?o9;iK_^L~T#A<`7&<;kbTA+$^Hq-?t+mc@1Ox=YvNos}z>QIc#~#X+ z6UCc?PQ**UaGzOeZhB z_{!senMV}RE-)^}P5x(T8*7?>Vq?7Dq;FurGe-p)$V{QmT!@u0t5)EPCZcyj!Dv$U z$$s6!_XZ^J7#X2;GUHul(4cDx1atwf)*Nzk!2`Ier`S{<^}lU5aGjsJ8jbnsTi-bG z$X*?)lL6v7Vd=r%DCFFIvgG<#FMcf1<#0SI7r;`V&W<9d%vbF8LZe!G21d5Pu7Fmj z1dojj-*~Jml^W#4j6t)IlEBYzqR6ll>E?T-IvaB=Hdym_%LQrF=RmeUP4?)I>h&% z^xriJl#qYf9#&lz!|9(D+G^Q$R(CpojcgGT5o@0`l3M7>zL)017twA0jrJ<(2V|Qr zgH5N(+|l@lw5SH8={LD1d$BHG+|2F(A{c0R4a(;RF2YHY`8-%j%QZ?yM<*D7 zfe)<~?WYVMkEdcgt*13?VmUHd5J)QFyG<-azi--L05+EOnK`KpIx|(;J9ji}c-CQ( z_PfC(Md{1`y#PJiup?;f3#O`T?Bgw&l|! zy45-DD;Rny?B!&7^bzCM<4nVfUBjm#`n&QgvK(mtnA8IU{KxGUL+1!OM_I8bU%Bqs z_5_1^((_%RT^kMSdHJpeuIBMN!lX{|_)n*maJ7xzFhS?4Q)jV?%ElA|YBInb@s`u* z6Xa+{u|0wOKU*LWKHIdo#QKm0ge$r1lo*2WLUG# z&BA>Bli|V?222cJewv48Tt!vDVH>3ve;<&rqq_p11G6*^m*-vGE81`18P>#=c?07?1ov7(+98T>7R##jtM>n2HW#6kmah7@wjyY zZUII`_IC@yu(?7kni!d^Fs;R~U&K~JK=YgF0{MHgMO6>s`^l^4HI{FssO_@v?*UJB z^myy&HHkeYCnd z0>TSO0R5KR#X(X>y5PRG53w}%MLrZcZ02D`LXw3Z$qz){b4g*~;pFdiP-0az0_ zNQFs>4e*Uc2Zlw?&ALSAzk36yxLewMEJyDnu%Gpyv}ip4qCb-f6k4R@8{fDrzF?eq z{>8;M+-%mgd4DQw`^Xg0+^9e}9 z*qCd-rbhvd11M|#2|O@hlcJ^+?yu9N;kGy8bc|3%YZ>__>uNWst4r%4z!;Bh!7vaW zMMO;757bJ-9A*YuHBB7t7p-(V@kAl}=D^oX%YnI-*-P|yOaMYJE)*1+!j$yQ>m^od zhdgj%idp))%E&Y;aFbeD9R~xqhp1dlHfr?(Bo+&6`^d%^4n@{7^6Z@LV<7?HAP1QJ z&bQ#4W_k3WEUV5|H^*bmIL)v|3skTXH_^IznEyOGY4J4hZf`N`<#$HxcD!!{%jwWHq2P6F-OefvWfvpVtL|Dx;zv&31a_ zBsc{^{m0wP4UoJe){WQD5D*mY8$ysUS?L1W)}+S}25N83{pum0W3{;qq`C@aE;cp} zthSH;&25xw&r)7UOYcVpLWKAQ$9slp6FQ$7kKUB#UC(j5(u%D&_dH@tzksvCnCl%;SAAZJy?16z!3bosx=4s`Y;GYQ8tO4*pp0FrE)m=Z$4ZMc z&>Fo5fE(Wbdo)ZocM#xCk72KMHMu7gG|OvOw-ewN)j0bZaZU&pcFzDBur+k%U#tB!ku3Qyaeh@hMfH2&|D99!Jdu!(2Jl*WWQNGtn*i4< zJB~qUvi`1kC5gFyP0v)Z9caH+x_SgrW^*oG9H#rOqg{;aes;QxBBWdkjlk=XJ8ZG7 zI3ny74K-CVfp_%>%E6HQ#ob**uruwL?P?9}iWe!)(PXR%)eI@26xU#tMrpx$WbHyp zw6_-!A5)2sEuD_FZzg`Npb}ap_fO%kywzF_)_i!OGdh#_-Rh->&rW9lvC{K`HMtYt z8vEIQza9wuhS5p{Q@B7jhBu-FVR2$?Eh>h`H5FW}IzOv4a=zq3j+T>N1*L!VnVdtU z3mn6GM`jXF13NNqMvaUNJ#P9TfNw{+J8P)%!QW6I2+x2|QON)Fn|;-7{==Y#yS>9PjHMs?kt4lv^ZQZD= zNAG}qFeD{A#BZB>|A}}va}Iz|3uN95&)4fr7+xPp;L?}`-_tK4(v^g5x8?iCK9o$| zSF!1Ye40$P-T1rO_yHrPOxxjY1$cPqz1&`rYPWqFw~YP|aN-5IcsQVpW+7b%ECuvi zy>N2*vnAB@8|*ImmN-7JGiz~f)zkIZ08sR|tBen%mNij|(2J}!5} zs66e^xvS{Kva? zq;_rtj?Se9r^o(yoYxZdP70)nH#lKuqK;mSG#?~s0!@E{6i z^7j%vPQx^!FiZ5w#pLVxqJUGqJ!Uz${|W=m(WpE6`xDKN%_4O1hCpl?%?EtC9*H5( zgGEY?5bk`CnM|GD#Qa%@1N_0?jyhqv{ydE=jq$s<nM{ITBp@9@jG(zj|(~SPxt4wnd|X*ZJCe+@>?+R zax$GL>gv4pJW7gxaFmo8yAMrsi{eHT;VFXE6aex7LcSdM6oW`DPQV`4@*aIMRV zHrX7=Hcl-OEyue9g#M+jY`KhmkrJM%w@)YhHAo%5Y zf7#Z~B0v+;4m=f=rkl8GspR+!BRMF-8o#!hK7E$(Egt_KanQDIJilsflNn@F_mL%7 z37@uNZLqkI{Bl@0r_~<4m(61vUaNwka%zB4N2cnk%&ug04R18o8mE0d;&7ZqezF7ay>5MqGB)2C`&b8lvD|9+}*(|Tuwsfky_sP9rn2b57=POh`@=u6IQ;?&D{``0lIJXr7pm{g+HFvV?_8s>yEuV@4Y(^`61@ zKEBBk8{uQEY*WW8!{CzYeVm`D(+54pBnYW~ zbS+lOw;ZdRYdg*G6eMLO@m(_ItR3x@cI8(`)e+YN#SSa3Y@Keu`QK*mx!Q{k=VpKy zrQ`K%QIzlx-xX;StUzCZvT?GTgp!1z$}1^p84=~+QpfosVmG9f_&2`DOd3fYz_$Y! zc7WRWRwC`L7a=X7&8!ep^9GGGaOfV?&=;7|OCLsklE>S#klOV+;v3zMreeyS(GP;@ zSQ0&BPl+eHtvC9ZU+l+>Hmz;kH~iM&!UkEqe~z|l6Rd3)TAA3M<_BYfu71}9vQf4< zj+N>T#vqY=G`le=Ba5V8z$(5%?~WBMIVubniZm2^-U?+H_6f1!(`}Aoe)TQ$rrQ^> zjYg4gjR)_PLF8LO?*^8Q#TZhQG3^lK!Cdu!NFRmH5HVB;x4}SKUETTa2+(r{k3gzI< zuc>d5e6%~(ZU?IejzH55EqC`DuS?Q|7X7ptlTM-Ijnjaetv2gV;D(b?Egy@aMI(x_ z1&HN%$!}oRX+ku9#R5;9s520|N#CW#4ZGX3rl~*A$nc$pn*SU0D61>MFS^a+mu*T~ zUjV7DgqcOqYuB8_y~Xfx%>g}Sq(t4U0cZ_EMUS#~Jw-lME9CCWTgkyOav{^j&&g8n-s5#SnzXLP3aMJ z`J#Z-BptqWJ`FMIdzX7wSVu?rb1aD1u+6%gml?M!QJ}dx?Gm@gRc|f znNv{p3{J6A+lrAGUT_^BE@%hWaR$W2#@~*o`kfb1Lq`$u$@H)aH%in{F5B*QZ`)~s zweIGttF(weo|9^9Pyg~_#GAgb{YdRndl4Y_lqmni6!(HKrQP2r)e9*Kgd1WfpzjFn*e}{P{{yJg?Oe(X=l_=7S_}MDvT_N6oqnP(*U&865U){OE>~4I8?!gsxi;w)$lm7Ff?fyu zG~vQ#a1Nvu~5&O3QGafeF?27$+s7c`sd76i+0MlfAJ+kPH64?ZVsX8;cFtG>AqUZaM!6 z_++lI;}~FUWWD%iw*s4bfSiCuYhkJg{g*D_5MPG$1!(dI1FkIm3d5@EI;Q4fRFT>m z=olSa>r0VKlhhBM?i)kW4rbMLKNNnf6^EFdfg?ASRhKaRgLCj!U;#GZC~bIOZbpb; z6Fq{P!!pIZF>y{B(R^5LdvJz66Y}q%W;c*ur|A4Q!6usuyHt?J3D~Q7Us5iYnwOH4 zitVZzb|$yV(mE3L88??2ntiJc&wTf%21A4fnhp5^He?`$OOuHEGzpqSP;0~Gh~aC+ z7VmBJ!RK;Am&_P4(bqzih*I0b(x|12t8ddv;n_V0{kz!-S>Hcc6AI^o)r zLgYXnl|XfDji*6eyHn@B&oZ#y5}ZAFJ{j26r5(Fm_T+ry)R0)SlXzS1*xg~fS%tNV zo&dYQCZ5dDt*E`V!3u5V@pT7+?v42rNn|4B&Yv;I^wbJlXtu_OPhKUki+-&@K264y z^`o_6F}43VO5e9R`ZTrq$;3&C&1xE$M8!q^PzZBBD7M%aPa(NpX9jU#eP0sw5elnz z9=;)n(F=vj$^au3CdQRC0=s!!;B^7-rkAvnv1O?0uy2Z%!nixJwk~zcW6_>70&6hXbJ)18Xg($h>2|)OGaBs`)hHP zQuYhSMjOaMO}dFNA~RLT8b7qK3NMuMN zxN9d_MasbgQD{oGX-=|y#!)$mlVzZg96yM@TR#g?3rKfp%cxYJ_FZo5I-*8Y8#php z9Jplb&&pu1z50^&Sfa{~XiyRRY>@_0cKo2kyy&-o6UQ+^tlCx^Zt5fS&L%d2J=vaH z`2pE08{KOVRwn+c}*3c)6-3PU_!AuEy` zH#)5Fa#f9Nt;&qSbXlT=QhmSbWMFj=E`$S9Cv-%;zO?N4=T;y)`vPiPz~MsrPB}R6 z00t})EEmO}9={s~gqqkY?8J+EgVUvCfGc-~R2XJw$e02_^(8vr7EQy^d#V!uTGg{M7HEX5bX_TtQw9A$#+nq6N%Za%p>;kgz@d3+Tsg3-6q~=^VdbTk>3>+PWG8Wl z267QouEdK@ToyZjbxlj3k3B9+%Qz*_O|mG2p`=8gkt|pbE36E?!8)8!_VNXzejyFD+x? zHC<4|2kT{Divwsj0|k*9r%>+3n7WS}QKfTc;@LnhMQRa zkuzPj40-pxjP}pOF14_f6dL{%`r5S@n#gEIq*EoOjlK~Fwg zMCQN2#?Q;`zJ3D!KX}o?_DNC+NeO?p9e4q)WW?4d^0#x{j!(Dhskl3a%9mB&R_guP zpbLi0E7ap;sQz5e9Y)csGzCmr;Q)4D`I{@QQlf{4Azun|yM$pnm*2<1=CdcG*#oNe zcv^YHh}KmSdj>Z0sG(d)VxkoiZ>?iSvXn6~y6amY*nqwe#4ybw?WLSQR*yTUr_x15}GGeEf6>S&^@#~!l{)iEF zfr7PVBi$B#ex{zB!92_#EwT8rKursz*8z&0uaO%e`r`vLdF4qnxQr{3}a`m2aId5g^RXn0yWf9a4t0acUo0A@kd6kqkTPgi@C;GsY)W zeg@pHlyc{f!8n6zZ$~(FXOP)2G)eK_@QO+2sQTn;{=@H=tIRVzpT}mlu-j9}C)m`2 zW@i@f&b4aH_;Zs_z);cuu%-{f=`)p;d(u?bBbo*Z-&nNx>d01Nl5UMT(%A2J@@9-^ z4|$A%z%z_ewKn-%LC&0|QzubqR0iPf_%xtT89#qj!OAzGr?!Yc4UvGay#OWH+eU`- z)<7UO6cejf3%FUaG$L;MJ_GD3jdE%9xlT7+V*_rBN!MkQiNvF2mGz$<&mu&>*v!&K z6qso0vv!%${p!^1nOXVKNJ0{-Y&F1Fc3a@g|LYZ=2wd_kj9m+HL-0Cg_ zp$vDhd5*v-bMy3P#oZr|3G9?;_P}=PxJLc6iP{ozbEj1Jo4KRw%2fIh6=6rxCxmX= zGy)w8ijLbF3uk=d`Gy+F+D@OYt@wBly7qpS1q;WZa|Xin&!)2ny`+s^`;&;U+i!e!IriJxO{3K@sc*l|X2VAy2d_>kp8b@-hc zErPpavv+L+=t1Uadc~wgyR-YzSm?s~Iirsvg0G{G`KChDsF(~M@Z=0(0bC8hcW zK0;0EhHsrJEA=?_I02J=dw;lSW%@`z9hRzN{=Ap1u5z04{MH(Xek(1rCafeS<=$$h)e{RJhQC+WGxwA9}bb#gZ*T1L4AhNR@m{a&@ z&y$Rba1ohypwKH*z{jTj?xa2F=8J(XVW7w-*9<$4mWd`Nk)w z`1H(-v5`CrlTZ&aw1e;uOM+Y_2-Umy@H+I^=O${nvb== z(FVMWf#;xn;5^}ByQBZO(wu3;fGhrrD$FILO6q*-4-g}FAK!eVeKYW8^<^aVpiOwG z$jlt~?{Jk!7^vOJhRbq_*K=-F zHFhBr&Dzitu)_P1#jhx`DaewgQ*fkpg$zk_LT3^tG+QUGp!BO^I8kB?8k<5i`PHc> zO6yZ=-kaPR5)P)dbqUe{Ny6Z67yLn)hDbwbw!lN6FX9;r5JG+!C1lK8=MyT6V0^83 z4YYw-m%G*(+B&>hbw?>a=UB%Jd;yf#N;Dqx)#XWo7d&`+b0q-z>v$?(pYm3WxIXi< zn9vBQX>gL&qo}M~7nD#c8n}d!XYz4+yHPn@`5gY3fYJB?htadXsN6B6XPULG;}Pz# zKdf7kSKtRw)hT6lD@fU**8ThXa%Ug`q8YMG;-v)k&W$yb${|4>$v4KqApZaIn)esN zyvt$czjx9;i};b3;~$kHzldSftrD=YVO9>$N;Cu#ACQeRv2ms0ORrJl4tn(iK=9L+>=K5nYWgUGxJBA--wtcP0eD> zuNab76rYoNux(C057*pXc}NF+qDPO?`r=eic6y2rfD!=dFz3mP0SjU8e#%^qDK`wo zgAJ+_R$-gdyeBU^Mi1l4LGO^vY< zV$c8VtJn9Qup-v&W{BR(elhobn-mYV$bXXZ8+8EMvn6R;8y0E%{`fm$HnTXGHG(>Q zm?j3NUezRbZn%G1=hcI(a%_A|Okz}6Y8E(B*TOa5;$>6xEu;Fy*-Vi1*V$z6 znljiKU6`Uev-1QVN&(#+?f7l?!v1ScgW6xcgv1~CZKPy^E-?kk4BSP`w5f!Xwq?$P zra}C^gTeT})(+@;V_nJ`FXB|VA1qTdG5P+4prBx4|9eM(|M>ad#aXTTY{=i=Bc(;4hM1PTx#c zU|R|`F^5ZqSkQn#OsA>% zAFQvu??KfNu-iPmllcFc{kUDD~gx0qjr1KA#hFqu4U;s|ZMDlKI>^`19qI3PK(I_f2`w@#I4DA8n5I8a;pwMw&csM&yV5T1t%)a;;P^wpT86W^$AweQ)^ws!QNI3I3{rSQoa0KX>UkYh^_sj7$Kec6x$Z z{upC%9JyMtX%GPf)7GU>{TJu``>JCzMd0-ewfI0M6u^U(1KD?fI-;pF>F7N(d5K{+ zIk&D2Z*)lem)0U{e4}lE#R!1PTDm%BE_;9>IeYa z^de027T@%6{0h<|P-8$Q`4Z`Nd2a}$*qfof%>r&{lW3*bPlbqME6q6$ z)Y;44|0*5gm=|vhH!E&*t7K^%e|BDs3TqrcGpjQ#r){AU?gQXWtsHTz;Z4anFT$Bz zb1Z;5>$ek6g!tH$u082Th(@4WWL?|tWK4@1S%c4vXCj?cFMzD`1$VTlZDvXY0xmA8 zXXqrd3uM`h52x zb0&+;*DCFvA!ghE{n+K}25+2>B5F*sv?9CuHVz##kHmXjUTPK(joJKaU&{^=$p&hJD!Icr!09!!P`U#&;Iw6zWR zAA9_ROy`FD z_QP$dPo==n6#v{G3u;V$c6WkPGde7lP`RQ;KZT}lrG>HJwv>e znuar8KCD{)a_~m=(RHE5_R(Ks1b))M*m4-$Eh>2=k3v#0jmBcg_;4N?@^mmAp{T>t z?s><2`Gcv&U8;-gx5uPIeBdQZ@oR6E$9~_cnTp}B7f1}*5AwSA)%sQgYHu*GlQusb zO4~o16|k*s_w^CTP8htvj6zYn6%8 zcF-fH^y6ayzi&?FvE===8Q>o&tK&*Z{f{ppS#du+IH{>AX1OH+V1tBR>c?MZV-;Jx*002kDDN|k#o z*V~KyUFCIl2I?ewlt;Ot3e796ez!MMb;f*6MF!tD#<-Wws+~U@kNXM{ljOYNY=ScF zO-XEO6;KoINc$4`-`*Q;&@l1*%zbsb9zVZZ;v>ymnCb!fIsbF|0@ad%R;2-b1;^n8 z%+jE!0g$l6x!KcZ4&kDhTS;PY?~hVm>_{*7DQ7}$o^S7SgGilLbUKIXy<78$hWe|M znVKst&%uhTgMGR4h?L7+)F+ygrR;PEgW`cu`lUNDhR3)JnQ@I-2WKlSm zx3mrTRBr(w=CJ4%tS_Jqz{V44qfbjh*^5scwb~AOW6?m5frZ)I^|~$V`3x3TkJny* zZ;TMwzyspVmv-2c*$PvQQ>p?O&FMSiK3}2HE5HN)3FX>(*?fHNsq??rw|C`O-|x|a zm50Pe^7BoMUd&(_fdElx6%dDz;i8GIaPWDvxV0d}2=4A0g1dVN?(XjH!QI{6;k(bwyz{L!f4Y0! zE;)6o>c~F(hRVx|eL}=T1c5-GB*cXkK_G}5;P(hTEbv+VsW=Vzf^(9P5rLaUMEgv` z@bKOK6nI47B%^C;!6`}!(E+aDICPTRxzTxQrrBL@~O z`ydewf8mLKF8U51 zAAZI<^IWUcOp7#aBQp3H-7f6xhx z_F>?ClzAZ;a?y;clp-KC$9P)!EtkZ%Dvb!lqs*SGP?R)YlJB zRBbvD5o$T_R9sxI>+2f?`{{=?sRMc)?o#`if?rUwUw1*5A0HrXy1?I1T?C+W(B(Cp zosvegpuexaNOBBop;3*h^)jd5>JJo?uD{cV9}W)RKchz+eQ^sMlYSfB-6t)X3(=%EGCeq8 z9x*cZmo02$ro^e^{ykR4`y#A_o11BI7w^hqO8MEk@XDf&l~T;MP!vgHiCFP5Q~FG~ z^yXR#T3n8CK$c$61eO@PCjRu4mj~wOx~WlGy%N*1d0JNgQt{lc(9FQ=^;y|+*~*pO zxAA53u}MhxEJgu#JmE;uES#<=a-``ar@#>%rfQ|3v46f$M?ntYpM~;&8KjWiv((fg z+-(PBB_McUFnNYK5)af=!__rmYz=IQ})|ck&d;0uYUDz#;H=GGAq=4>ys5) zm-_eV$i(RL#AHTBoPTpnjNk3G@$WjeT}nAfVgWa%4V7wN-}e_vIYCwI8YG)2TF z^K}sn%m6kzWD;tGrDn&N^zhe1g&XrPmBFY&HVie62WW%>qIlRr-xyGa&sN0A$ZnP> zc^qNuG{f)rj;PG^+#WGsdV0r~O|NuNiD1jImr_{+PvciRBEKV!-3#C$#D@$xws^p>;R8U(}}}Qf7;~OZxqDg5yBQ#axU!1fDY_+^-MF~=T{2Z@?u^2 zs)*3+xm};nDVBX#V=MxuyI4bF++%m!viXoEtK7t%Khu@{vekrEH1_R zV!)Hh6F$>iE55yBztl{CZl~A$H9`$t+vP$O+Zj&8jA~*q3Rq9ha!sn$r(FgVQRvlq zSTZByt?LDnU9c^2r#n<~Ckk-zw;DLUfTGF6LGsp15ix0D*SiRxv$KcJSG$cT&%JmF zVeyrD8pyoSqOZ$BGdthQ4m9!)uCz;%?ccn?HLb38h)DfUvk2d!0rAs z_=9=>QFsjvH{8=(4HGxYfXs@)=|9 zGrzktLu**kowt5+!8>Ku<#YM?#mj(wAVt!Y!Lj%>gCg8z+WemB^~t5>tr@j{jfaY? z$DYsUvxFwiy1?<-txiSb%*o(gOk&{iB}u}T2hZB4r=!{*dSYGycP;G=w(~3Q&(IUe zf-(L5+tseF{Dhr*;SlJPYXW4^;oWhW@F zYjuLBekws%3`n_^Mm5a#M!MQ=46F>ofl;GPE#Ub$M2Lrkyi9_oc6PSE%v|s!*{+55 z3>QlWO`&QhU%Tj(&#_oyj~6=BA8St(Pd;VdXmGmHVbQ3(3or$$zY87q4|_if&Oq1d z+t8kc$H+QPcl&Qof@}cV(cNgR{3k{djY2 zxjo)XfdWr0#Zzij+d&|ymhabgTdH&J(&Mq&XrDW7J`f`P?h}8kifvfk2Lcy;#c0c-i4S#@!ZwRg3`0s6xE#Q zxpQ;#U~!aor9dVUwQ`bfp7d9{|NK;yJ^${w*)W84HHGKBg#alK zEGcDE=zq1mH#)1B_49R%>-K0i*3&g-r0T0Q5YIZplh2wG4k2k7&&$$AG%hsS?JTPd z+`mOJD1k3maei{PU7)fk&LA=`3vEQ>?5ZMBkW=I+(*VolL?`bjui}^DxX@o3Gk@)H z)LQaEa$(G*-l}wu*&b0I7Qp)bGFta|LbU~n; zuDw+HJtc)JZYz$$bwwt`#&)~vv@l%-6RCH&A?n(DF4nnNH$yd1dq3oeZfJcZV^`~R zx0s`zBOyT7}89;z?9?4SfSJWIt$qp>qI)nN8@V&u_~TTf|J7S@a1OS zI>(#UcUcwZX@7;k3_KRQqNR7iaZCy$5-*Y`_OUA0+PDt!Hz^tgH9w@V5ml<;bo+I9 zhc1mnqOvA?+-idUf)E}1LpE|Q4Hoyx^;9;in)hTbd)oxMbN@ASV< zg|bs>9<`4%Izo3h51d z(*PPdkRvqhP6(x2t-=-f0LiY!+8syzUf6g;B{`V5q&mu8>uksMr4EC(Q{t}iWvp~@ z!||K_D#Qih(3}y9l(fBdHeN-TIm}YV>8lE{n$SHiL8k`-F9rStGi~^2jAZWC+>NO* z=tfj#5oLKlk%JJasXimB+AMaxrm`ZmI~8kpd!4ycBODncsfu!y%MSYKwjVC$BbDn~ zL6Od0VgtJywG@u+czdaL`m}cd+%ct~4r{id{JqXdIu7ojt+!_;UuPzMAD<`nydcfB zw!Ist)8)w6x0j?Tq$+QG4Y#%~|C4{FC|xppY09%#S+JVvfS5^>nKBy&WS5TK*Rz9@ zSy<_fwI0)u@2}pIN-&!R+FW?*PTKFY5KXaHhgop&x=hg`_>J2~dy(*M>N11jb55XJ zJl=@J8QERZ+B`a=PPRGFI_kp zF^MnymwkQEuy@DZr}?7@WnYv-_?X`$25a70FQ#9ga8lUa4O9QSK1gWpqd{1ma!7vZ z|HgwMlMTr^fHO+kBWSNkQ>xvm{gH z5BEmPbeiMbN0at!*<`R+-Q7KFz0Nd)ZqG*F78e~LZstt$^Nf-Wb}Sy5yG$>RHWJ7 zqA}g_Zpfc%>v1bwICHg=w#fovr!+tHf)TYEm(YPcg4-oCJgJfw7d$}$VmwTIz~z8M zf6PfOh{+Jl*4Mq{uF!26LS!Y(qe>D0GGB>F|22bug|Js2lu9^TvA?k7CmA5cU_#K$=%^;&&|ux*wH za?IlwKThN(zXID89LctavgrbMW21|bdXw2`lY$e?vE&699kB|?l7C#d`4M> zw2e+Q6&9d?VV-{9lN>4s)>k}dJdO#^cZHvybQzNN+PvP>Yn*W?QQn5HQ)jZMLEt!5 zIHV}*5rVEWlzo9fN%st6y{fUHp})4#gkw52&_sG0f|CMFtc7a1CNdP08N+~+iZMWz zQWve;i%XDpUMlZdZLud~emH0LI(;x%I))2r)285EP?phkMl z{np0)GTuqn#@YAbJ;VY!-Oztasj(X_rY6RVL@8Y|1| zsZ+k&=dsc~;;|V**PqO@kKX(YdD1n^rI7i;Yw@|g)rGw^teMS7Q=Z`1LSaPm(Evwj z`{n+0fA;F9bSmp0sg_aB_r-voaFk3PZS@WVvf2V^lGBkymhvu8XRy>LFLsM&d4cak zh11Dea1vvOdR~Mk5>KW0ct@gkw*LDu``8zOC{G2~5hEfudcD}^_m=-<%xQ{*OWeXZ zPFm4@KBY>FMlL&phQDUFhXry>b@2;1H6%2ALHSEsj+_iJD;z0RWHHI998x|rRX zSqGwo(Y{5bJPXAR59M!uo2+d1IO8r_U4?JIlnVvC!)L|n#Rs06i(wiUGH1*D0U2|h zmxcNdeQvK?>bK74h~IFiWzd&3ME-OYoVDy#uL(KY07!{6`MOJhz@>RA8b1)RbqC(a zJ1ec!+l{(jFw}NZa+L8cI1v0Z?vAh-pHfli4uAI(rc)UcGGb$l=t$6CK;$Rd)%9Rb@S4!7HRTh7A5;M{C^)%Et4Ij4|!9KTy7RnIB9}BgjF{ zC*r~#P0(^Ue(57z!<(%f&HvkKJ-?I6DxM1BFO>bDN&HtWg){o84gMw?=M1~?V#*)9 z;T{|t0K4?5OQu-jFG{gK5eQtPjojQ-19PKM<5Irl&aBjuMR7^2QS zPlY^7C%N}(p^v3~{~fGiV`H85jOuy^FlkS$__x_ICGaGa+-Q(qZ<*~EU5`6w*ywce zPTP*{AKKc@kWJK5k{lT!aY=yAqJ$$4=ORre8*`K{d{l?oQ2#0Pmoh1;6sd<=)F0a& zA8siwRHNL5{ef-9aZX1}q#ufcL>mKVMc7L%~k8*(} zdAMk8PU2N%g0ecekwvcMyM4LN2R)hf1_Wu;)Ok}SB@L?(Ur%{WD;zLl6Ie{7jfA1Y z4MVDT3qGGUM3Pc)r^Q0SRXL4o^78VP z#|@8R$y){*(pLEfe%~)w?~hsExZd>1Sff=+(xi&N=`xi|71ozW({sI0pz}-j*oOY<2=FhBq%Nl`84(-S{b-M#O4@@6>o*BBvdwuevo|QBpNPz)O~2n9uxo=Hs!= z5xo9>iFqmGx&gjc4IS57JJ_U;9&EKKvfyraQ}h1c(xg3M)lGTH5H*4}wmo zb|6R1LQ)d<)>q=E^KfBKZtpCg zIdoJ;#(4%UO+yePr^UBKUQf`UN!I8DhdY1yZ*D0bo1+I4Ig?Z9CNm35$aX+j-GxZ^ zWRHYYD6`+U;k)!Tu$(Q@$e1y9GTnRxrYrJwI6Cg|V?6ox3tNJVZHG1B{*t;UMp<$Q zH5h6qykV=%`xV(VtwT>nR&yP~QKTiW$?_BtY|aS-ck4(~9wy7Ope~q=pes9W2%$Qj zBg8^OXMZ&*=d z7L29!0~z09qp935>*}tkAo_)s^^g4gf`5gUI0fUWQhOG2Vad!Aiy{m;it0p2O%`{@ z(wVH$9c!)EoLoybD?^B%YLi*yI4X2SdII2(UwpJlK#df!znvCcBrIlL?uhu~v|<;Q z9#$&zbp!vzS$`>a8vyR%^8A!yV;6Nu5_a`kSX)%lj6$jIo8Zwt<+4O zZ0`@?U$jr!YWdPtX9_KAoHr8*IQcc$x-ikYQtDCW?uIWee;km(MrHNHOni`CTP${> zY#L^_FeF*8(NRFO`nl@}pl3t7F@%u{J>O=lZFI1i%??>`CC2lch126w!b3VHGwc$$YwDPF0{9%m;e5hCW2swy^|Ez?TAnlYVfw8Z(R zs3fK1|AR<%UN`Zfusj{1)hpDV)npOKil1MVFGX0($BFLpns1)04^S8~xPG$wrSo5_ zg}SvaUga{t_)Vs>Ak=G)Gp;l`uwHX#Co?!)KJex7%9`}Qa)HLa3Sj$T-5kWE#&&n; z`R+epg1Imq0{B{8;?he^p0wY*_To}!AQr0(aV0_#zJ8n#D~WwN*!ghYU9i>ysXz8A z3+u1XO7M<+RFoOZpqoIFy>;CK1``0}vSY|clF_YYHp%a?()>Y`Dv$?*j{8%K!K z_%iu(ah*!g z(^e+_LVU(;OzmK_!`(Hx;l-NqN2S2QRR~2}=f$$@b^x3`H~{`6 ziEdUe`FKX4&J`7q42=+bB=x75xSY(~4dh8OJ*56ii+)XQ4X6DhS|eE{rt=N#ASw|l zEp7PO$!#n=m`+Rhw7u2I=fgTkHce301smNUyH8|eX9&XEE_b|CjX12zdkqhS@_Nq- zsOERjtB~1{z&COVJF^QL6zycddv_o=Kvzyp8W1gtS>5uHRtDN1t>SWfy>eONjFW*W zgLUw43+d07IS3 zY75~+z~UF#S7&+KgOa_re_MPmHnWAEr(OJU@q`%Y9)UDzOmXoTTu%#GAa{bWjfVFS z_zeDMaOl<5YE;DLaX#8VMZ%j0B9B7L$p(7jp7$)*>}j(hajBn&3AoY(9Da9FI5F|@ zy|Hi4@vI-OGI|YwYa*v53n)z%Y;t{}KtMfryc(mf)1-&)6RPO4~fYg-|tyAKExB#|moBQ>pMtOzy z?~e+67g=JQMD%-OvAt&Xnl{>>b`%UrLlLfo*P5a<%lDV_(LfBDd@$d4=j3OboBIa) zUmrkq753k-LHa=5E(t!mY z67_szaH&6V1#G*$b0YOPSNiz$a-dTg6m&hg^F5(>;p+OHf`S{5l9G~;;u~=oDelpc zk+CtLAaPkq!3udh0R|LTS65L~T)|!n8MEa!F8+=VAZLQICCX~EoIP79Ql{Cy*bt+M zk4;JoQ!ORGS3u!mJTlv?+)_})kj#&JvA1t(Zgr%jL6l=;JYR1PC@eJX*wEG0WwTz- zIKrfmCt*S(($lM9VI7{B5Y3$ifc#(53D zj+)ckJfQcEh;94os0dJy#tFwYQ}9H+jnA09{fnnNYz3zR#wa(q$KR@Sg47R zIs-zhtG6`_6&2qG26svnuQ<7I^J`fFuyQCfV`ymTdyM$d@WjC2Wb`q+C5y|Y{<_x# z9~<5$medGf(2R@>arf6R`SEc8Cd^5ld3R5n`1~SCZeCnGWW+LS!TyVQlVag-!V#K? zg0CDY2_+?5v9hF#DmIX-k3}HwB8p^Ns+v%+q$Xr!SfRXM!M!}3n(Y5G4w?uE@$-`{ z6hearP9#PsbiHPzGR=7Fe#xA;w2v62C6|MgD7|gF_dt1Q4%NPt6cw765NYh?^9sQi zun`))R=c{{*)Qq_Y?15-a&hQOy9SvFj9U0zBhy;X1(aj3g_RNp{Yu+!ZH7YC_UFgP z=Pewx?{}K@_Ad6>=A#F(lKE|yiduP!)G}K9(Tc@+9_8Fk?$7eYzi>X`&kHdm&(!T9 ziRUY(HCg~-oc(*&x+8*PdD{D4q+jt)tQARr~ksif806R zlliVM0!gdVGrkt7>3l*GKWaWz#^n(B>scm^RE?@gyPW%vF8|}>lTxvy3P5uJyakS_ zDbD))CRx;bb5_}y^m6+XHUw6{<<-Mr?`V2hV(DLHnqrMcLfN2FAQz^YE6sIy=wkc} z+2Qlv)Y6KnT#x4v*z>!PqJjqN-5<;T;kIvd5T~9c=7o!}&Pp92zZNO6RAq2UMI_&1 z&fZ>SYp2qe(p66G^%cs_#hILm3f>e4=U0hxuSz~?l_EI+rCr@#_w=s7C8qNL+?uAQ zlFLf8kdg7o(=j@qFYFQaj@VO0?oz9~yqDMa-6DHI$A;haGU$*{?Wc58n7#G zd;8Vpt>E5yRBItl1r2zWue}KGFKO4pCpktrUf%Z0%j*G^y`bP^;7=^VKe|nx9yt@^ zdw_}4_1U54i%!QUTwmwp;@R2X3Jots1iPYYYpZy0F*0T(NVa)n}C+dDhWlJ7aNmJq?HFXzwhNtxSskt;y3pdt$u1QjnNyNdxF8oS7Z~;PJme0JxpN zFcHE{pu65-WMs5Sq#g_G?kf#+TxMcuYckF+;a|!${rxH;E-u&|?+|$uqB9ll#@joF zMe@yvsF*tnEiE|;rNbjbeQPt}o{ct&#hT6)y05`_&L@~YPo!9-%H;Rw1kj<-fDf{N zINv7v(s$GwTr^X~EcWmw+fFgg>3k>$`RkW_T51rsKS4Jw%XPgO0s=yrdRxlvtrH=i zhcIw4?W?Pc<}A)vJIaYvTkXjJIG&1%LNI()2J`++2D%asiM3?RcKvXT3VW)Af8R*VUFG@X(V^4Vf|vw{pwCV0U*H&H04`FcYh-j={fw zL(ft_nD2j#sXuID}e|;Jx zde+?|^8Gt!#^wFW2}rGtf&aD3%<3%N))=mdO78QK3(_WV(C98NBPb#eY@5QO%y@)YVieJA!hg7XODro%~f}S z3Pd}L%^Dj}-~nNAE18Cd23;m=FwsKUK9Cddng~NLRA_I#9x1K1d#Crl zJp8AI)`r6w%|?|nuu@)y9Mqs?Aaq~ zDWe=;FZEW#+9uYsd`X!}{>YT7WviBQHCv>{dYZ%NlP2~Z=&(>y#QhvwBOgE$~z!w@1f@`8QlJj#J*N&TwOcd7kLo8|{a{N4_GJL0eJmpMJFB1{?g*+7Q4_B7KhIRX-tl$>l?E5$AW~9h=xECW_4Y)`o336k zF$n$g{zi;6c5`blvNjxPf`P%!&7CY(9KJ|B_J%UL)@t5qzidgo_CSjyR@~n4p>Ihg z5iSJepGX5G-);9drqUPW5C{p_NWieh@NjJbc1BAA1CmAs>aS#E+Oun^JOp6j0Z}h+ z?|)RtPc>hBk0Rjpi?0p$Nh`Bp=hJ_F#s{r_A`XdX*bQ;u4$Yd;&rGL#1@DeK6*p<+CKYe_|;yB>Ty6p^*i6Dq0+m zM`E4d=MfGXC?hDgH~57AJPV4vWx2yAjukIb&!+)$J-|>Jy$@Ie-bJ0wm>1z;r)WlA zphYX!#@h%H2I5EJ zkb@AdP@s9%0{G|4J5=H-2do4JhFR-bVFA*GzXSq0vC}y+THi1boblPa7X#lo`QlVCj5ojg9~~ywYGYi?TSXI05c()Fb{$_BH0E9f78KdKC{ zQ_HTl>$u+xX@5}pA0&gz+JQpD1dH`LuC#9 zNo;bU^FqW`FRH1Apb9rGB-H4yyj@#RFR5&%9pg_9#EOzS0t71U!s&dMzw)NdzATsW zWO>cSsx5b_xRXYvH*S_gzTAc*ZaOe%3aZG(g22-`9XyRTHTuWju*+7I%Tx@h z5of=yFlvEoT^C$TDmByLa>(&us+pYQGK%#cni=8HiF@GgXy8T9N9aUtgDUQA zW?lvpaXfWWuOR{m$751r^y~c_K<-x3$ngVF#^!caGT;CBMzhz!(-{1w=#AE_KVdTK z6Nl9d!BAU$&Eq0wlr>pMqUQ|>&;6J(0H6zZV#S^_F~MT&YVYg45BRb#eKqcmQf>N1 z(+C>X7yyhRve7=87fYtz=8~yF%sBiDoe?J%7RxDLv+sT+-FD}EYxq2q$7&+E@z^XJ z)`9k*%bA-X_vK-$U5=j?dbeR$s?t9|qh6l@Fyg8rhEQhMMV=Us1HU@zrLg~bcWsj5 zRO`rneq@(6somPz}lsuyi|(JyULpC%y$i-zMJ z4TImX487ZbbfXl?Ib3G>aSS#Ovi%MdMYw1h-{IHRU-6VB`dhcaC*A~m?;64#w@k27 zw~;@*@l8(wZ1|$|Li`((6=<^2J5seD)Z7na zXLEiooz5EE?ftAyfLPyTW+gfTl&b(X$(UZl*mg!9J8p7Xxd3@lUHGD|#X?l$x_2@{ zuIQ3>_C8_!(rn0AzFQo)hqvxoE=MU9|H#*i*%^giu{w#NRni8Z-XKg&?`S<2D@Fw} zmLmDfKREyiKmrvLcm73dzd}8*G1OIMIk7mJ1;$YE8=!7jCbO}J)1~?V0C%z;NzSSe z50ycetPhd-EHZ$KE8YrTzM1!3XH?NqX!g8RdC)CO0S{U0+ahWJ8606qVM?=bKDja& zoKRiSFiTkz*W$3*wNd_@ODWw&_$`c-uk$Hrg3seKuH#zT!}w5Oo;1mZ0h%Fo(3Ocy zYD8A2fKKPzgikLmg|vYh1FQEc=e*U5jSGa3#O+xN>0aQ7i6AF7T8+h!ZfU;jf$ibH z6RqVs&;A0$&cojk5R9ePc!H90QadY^orXOTsk6l!fBS@V_IDL)N$AC>97Z$hpD}ib znDW$6Rjt?jYs000yRsx0FNTuqlRMybFLMyL7pnfaa&kTGn^L3Ctb%;=mwXZ@7G{QG zCTA|Z&ho<~R_IV68X98;Mz<)3Hr+t-HXMo)`B?n^YtN@ zSHv7kRw}*;_@7!PcPtvwq#hi~XUn0C+9 z-Qk@#`2M0%(`3uua-fcQf1QuwOHiCO#PzoBUM+=YZFPziMyL!;VvW8E6TqIL*j`pi zr#tOEmT@f5^I6F_9uD`5=$iGO>(XK@Kl%BR?pa{xvZXgPoVHq{N7a(cxcCwT8}=G3{c zdTxT=XD_kAiZ%BNXUkTIrX!*0KH3^FvZWSlo|5^Z?)y(CVaC|g7xoV-iP9T6sWU(Z zyPdAPp+Ttx?T3>G8=0FO}cp$3^RHAfv9x_O>SxaohSU(pU z3nJQRFB%ZB@$`vG;RtWu5zC%2r>2PsZ!8r*IJn7@`JiG(S4+f~%y$HC%pZV7KDvhN zmyR#!d($hIQe*W=svyerCX75JN8Q6ag-CF6?(oFJeEp(v96tm=9sdrqTG@VtK{@6t z4rh={efd`?7i$`T!*1$xe{1gZai>-=mSNr)tx4fm7;m=V&Iw>>*l?8wt07yfokVI@ zWHpDs%Z3m$GBag5ZFEvEdy#3>$}JYwiAlYdl%@Wrd*e&!3HX{YXpa2-!zcY~dvNi);*o4+kpSUWN};y*;_` zhN@iI+DN%48Qa(Y6o)6X7RWKm5^-r8dRvzqxitsPn}EOs`vm{;$vPk)q&2J9Y*QB2 zK{jbqJv$Dwnp)RNA9;fEXx1y8;}dt?A|rg_Sc@*qALeG1D1?8@B@Q*Do>voHq9ROs zzcv9cyzGB8Vg5k-@_KgwfS5@V-#F^AS?Fh-EpG{)&rBx>c*csB!qK3!h0pXl{xa_m zjiNc}@1(J}T{S$VbTg{53sAaToa>095=vi23oP^YY0r-LA^ZPE_Wus1H7=h$INcT; zRV~L|Z)CjiepGmGPJ%>eEHQLHiI*+>`90dk>3l96XSvM?2Onv|-}X^VGNv`8H0Sq| zc`*Qp6zP^?$BRX8W=8Xi8ZIVVF`(CDtNw|-@-o(`e#Rbug|H>k>o{5}HF61?=kti6 zh)E;cJh15WLWlO}sB9ZJeD`+0X@5}Qp3@yqo>pDG$r5sRU99*R9gp0J z)1O_^6uhl87kr=kB6WKXNAy>o>e2W>wssQfk3I#Yxw-j&6xcvowe9mQ1C;?RchWe7 zr>2nEd+N1e7E`HJzsvAMTe^L5wJF z`_S;*yU6ffVwY=PJuPH@;rr_w8#I?yWUnPEJoBsS5?SG!E5xOZA;xaZagD zt|NuX)8mE7XjQm_i1)8}>^KR_KgY6#hnamDppMWzG(2;{y=`$RZh>7*vf+hwjwz)Al*Pcl#!K<4Q-e7mh#`-XDefrP}9tPCifyhy$b_+=v+GEeaf?6VW#B?te%QRVN;>tn_d^DXSpCMG9tjIpi6$|lB+$?W1`hv zkPEI&3^(=tFsD3_{iA=~7AW6GWAzfj6avc#V*xhndZmtcdx)PLqew`j-z>zq@t!G+ z`YKvFJ#{|bSjJKND?Q~Qa(M-+v4z?SOKscMw@o@<@1QM$kS(J~iu%I(-NR^TWx?63ExnL>|*nBS6 zQ+UPVgd~x#$UkQrFH;14=Us6dZ#PP%GORBQaES*x-4ZDz&5zUlgr*=MCXlL(uBcqQ0^5!n2> z8ys^p47P#w;8drhwf_J<`>R*3T8YzE29cj4bG&sAuJ8O+S=2W;P`}pr>>RZRKJYj! zqbfTE22tM4IY<)Y)u~E03RGwxpI6p7Qr&qaM~+(KSZ=z?hvQL*yhkpFXK-hqXa$8B z)bGyRxx$-c6XT?Y#IN}{7b2IQMw|49Wg<_2w=-r2VR?`*z);FfyV3WvfhkQ z;E-zJjU3!o!`Bq;V%zyMF%xQKFR2vH=2N{}G5rnrVs~NuM1#|w?l@yrv0PBVRc>Ud zRk76AM(lU3ever}<#I^rNIUeI-WDZCW*Roym!VCC+%H^a@*RD8i9WR@5%P}&D89Zb;PkbFZ2 zIPxT7mRin@cbp1-%t{6J`;YeT+*O@umIZhdV(E;*rD_bdhTUHKmAXHD2#2BoB8V%$ zXivX-sEl4-`Evn-`q(Npru!{bag6o}4R@a4v_&)l_t1IfN@}%JzDl>yDa0mBXZw#0 zIsKzDqM`9&>V%#|PHOIWF9cMien#TIQ(>pub)DKeQkIS<<|ALaken~^zy{ONE9xxX zES@e_^!F?A;@?SVb@h7QUT&YRF?GEU_?{!arh~7Q0b0s4o0bv_zy=R>bS(T?$zPVn zlN*yk6;~PWadw^GR%F|Mq~#`kr3`ZGCUFA0%26=t2m!$N;ll=MlS&_U!}e{vyoi?B zZFijX=^B*@GEvto*2&fFJw2A+L1|-yNzZ6yo8b6zy+)_UU3~OM2Y+h(V3fVx20z97 zWxyK1vrAL^>|UH!Aq{FH9@lM&aHdtYW51Q`2qU=v9!Kd&&z4|(vtOe>cia|0Hv-uu zV{Ik>LzsaY9|SfQK})H(g27iS|8~3G@5L+2f|%v`X*Dv;aCwoL4p}$dck3rEL{qVL zDGj%#I$nMCjQb`HP@j~0^RI?_#$G^Dff&UB5RPuWCS2-ZlG(E- zg)~=dARj(2<+mey;l)Vc4f5GNJ6XYa-kUG~s~SLSz=J)Mgx-X)ZT#5$dA{1#9SM0w zk+CPgB$Q>^NZNB3oDV-A7(a0{mIVm{CwHw7`A+QSchjZuCXLf#A9&9+9P+rz=`Dn7 zK_#H!4CeW?r&PIN`CvgxecCLLBPSnj^6w3HJiX{_w&FZ13o8_#E!P^$;7#rC?tY3+ zuEX>@!0%|M-5TSQNQH3Y?{aW3RJg>WNU_qubaz1W)|N)b6h;0yyzI=`+LQp0L;kH9 zWsiZh+2Y}l0v1@_UYLb9n&Vvs#Gpcto*EUtQS`Bv`0DLfXh#Mot2Kj~s0o_MhflPZ zF@d;}-RxfifP3{{H`iWYsou|GLE7bR=HDE^>$ds1cb|l7qwA>xuORck6tEToh`g1w z6MuudSQcGhXm}_EC1vn&y~T>v4pL%08}ihfB}n&jC4O|zOTdy}#d6kAzmQQv#@$M_ zOhSYJi%)p;#lO+ox;hlezq#Zu+F z381^LNjW@^mAH9m_~EVohYK(Rq)$N)?@9L;ZQ=2?tazFH7+IAD`0AP#G*B%r2G^@k z%Eg-FHpcql+U&NmphR_sXFPVxi3%d{*8aY1e#so#bQZ4sv^3R{r5Z!E{_|(-ij@Rc zqr$Tb(~X%S01`D>^Po%R$^^`G+{39zyh0^8Yr&0sA%ikJVvRexmfJ7ra+K8sW3Bx_ zP}`TJpz$=NY(umixcls*oAPT}UDlDa=onJ0uGa4x+NEGK$=~w^d zpr1--O8B>Rj^km-5)z*Lgu1#B=^T;S-Y+b^^Hu}WY0`5gbEiIke{rSeHj-&5fPH=a3w-ADRaJK-#-7UDg%M1>|-Qnx(zPr!< zzJGX_>FKVju3J^N?mg#J3x9;U0UY(?olb*8UxmhT8ovpv^!s=>$?nFJj?L4)(x+Qz zcId7*mWAVa^YL+S8FTh)kOAO~q$K7Bsv9?3qXboV9S?QodR$FA`Rph2B8E{EQ_%`Z z9G$3oeV7%)w{ml0(N?9|Gg$4DXRKXvm<}Hh@Nhld7KuKe%jWDuP*h>xoTP9-y2fE; z;7FPg>dqg!sGWCQ{Daom`z?vsaVrm}+y{=r4gqby7Ne)<>^xOpsXR;2$<}IVkx8%1 zE8RCP|GK35dR5mkHEAgU1TEqs_otl8&AfD~Zhb}^ex>nI`WK_Nw0d3iuV4bW+mk^{ zLw^N;M@hEqdLh>`Juv}pd1uUHpvP1PJ?v|SpfKH zM98yBy8(0?IssD8l$KdTX;E=TbI$eRtrHFE*;F2)nw0}enrZV)wJH&U*;J*Ug<18B zYwT$+Lt4}Jh(k)JKogK?9x2Ig*VTX{02<4(^^d(U1l=4wNi6bb6kqL7$OmewbA8g} zn{ydd6%w1HK;dNqKTj%~P}lyB;)eCy+o>whRcRuQbBw)_)asJHkcOVeqS>j!jM#uJ z&E+mp>qTIXN*h8xos3mGx!?c#>w3P7vU|5KrP(LPm~_vesrX%M(<|v;4B+L{PT6k0 z>7NVohVu(%dyti(14&Eq*Bjry;&s`&ckTIq(N~RTlJmej!;kWr<1t}M!ezj|24BPr zqFxhjM}sjwwGyyO@5hI$H{5sAzB=mgLukgFT+d=P_r~<>1(vLU^qIXXMIHM38@0p;KcGOWwkVA@^SiH8 z@3yU36F(ys+y{!@BHEzb2jsH>f>KJxS!?JltL~ZPLb~8Hrw_yw)9U2C$lK5Pp%E(w zA_$dTQ8!N0C%-ZDsMMYF1H%@@(i?DSK%(SbuDe>;^tkK;iAB$E@A~EU zYvvS(*K~k1B;*b_Lgh%d=uU)w^7whTs*Q4OaJUbF0`3CyGx|_pcaFfzA9K8;;|aobtJ4jzKbVw+IoY3t?QwWQ!RYlkQk}M` zUFpEr{-dD&d|!xwDv9ax(cFo3uslJcavA#5r{yZWPoa(d#v-Ot`3IFCd(A2dnJ=Q9 zrUF;PGU~m(bmEf*z)hWjB6Z(0m+e+j2$(!l{EzQA9EY6g#H7y5+GtL zF2_*_677v7m3|9Jkd`WV^W+heMQSS7{MH*3A{qNqLVc$D-mKe*MNWyGdAgP$DN)cc zT+)FT$s z<$IXv^|^!Dr;+7K!ze3O8RrNv@6uI6|0(BKVs5$CY^xYSlYl2#T!bhwUJyGK%)o%M z9Tar@P`^>LE4-Km4cb`h7ncppusO!4tSDS^0+i)y!!SW|$TjF^6$319D2Vd0H4i=>4*c}6|+`1w|3i;T0zwxof2R624IM}>4yvQd#1a48t zVaKn#W5fN{G3q7yjsvwuXB$c(kVB})4@YZTdImMgwgS!d`<=q$M4aF0TUY%avw)JP zZJf00Ffg3;$wm(NeSdKd3Htu}a1d#ZK)@8M%Uu{A)5}Z;aa$Z!Iu&Z<%h;kaW*u|5_^po!R=m&S!+XHg5^i(?x%gs#!%^vyB5ba zWTdIkehy$o@%uFEE9eSdQZ<{upz9u!{}I56P&hNJ1@f2d6pLy(%Qg5d1qO{FZQ5GU zso)EyG%1h2P1$kR+nl*g!oXkd6s3D<(c3j@vtIJ-y6}Z`5o%jVe-RnJby(y5A~S6@ za5dUN>yEiLhjcNd9h_?xlsZ7sR3|@W#0r4(atMF{{%PdPjdvNeZd_JR@5oX9?3$CJ zD1!)gK-D%j`o*`iKMME@LgS%l@PV^HUROSTkJMC;KvLz<6fXbKnF_Os zugjCj(M0m}wLHlsQ>QSAn88Ekj|EUf3YL?lAp`2Y=AFa>(R(AUMl7IsV z;v7>X2*JVCfi@vhclqbVvr8tdUsNaN!N>w}GnJkG^( z(!$sDtZjO1$LS<`e8=fvI!RI&7;0OcNY>^`@bEf=e-~{(9(X~}NB^9?dvFyL^FWJT zYc}%T|GY0*KN3Ie}SRk>CH8hYQ^SJuh3W-=wEKHEg9gYx?B zK1D=>q(REvU2uoBepgo2M?Qv^OJuTb(}Z5z!4DrIB6wfhk)QJ*zD(lG(qG6H2DH3> z=rx^z`pL$|3j+uCG9n#a#uMD15QCxDdC`0^=Y((o;*QuPKVEB%^cY<<(*(QXbCCjH zMklC*m@^kmJ@-E}0gY<276KYIMcNIeyn{^#v0>)#7jLEt75`-C~H>Vrhs zQWH}r{+2kOgr%>n<{vk&izO421Mr!$AB``MYqweK{`5KVl0+Y)MP+f6K!62wMxP^h z0wt$+!A;geKryDiTY?UmOO$iF%GNK{j{%VM<5}MXg-}z8b!$YJM}QqKRmpjEA+iv| zcy1^?H{3zUvy*TOx`rK!f!s%#EA5vD-R;|_XBIn_uq`J>ywpI?e0kY^%M_}TccsLv zWSSQwAEC3%B~x4XVQvpBob?S#d7vNsnhHF3DlwNnrR09Jcv(Uc>_-K$Bm@*> zq0s3_>YQrvM#ps_%}*Z~$#t*CR?un!fk*yKl~nMyTZxVzJdo~}-9?m*#ir>AA-r+dFlQGZ{KnsWiY zb}WN}n_tmMQk9vS)f9Jws1UKlh}-7_$a0mz@Fapg=}EL_K8>b3a8Dg?^tm_3ePH2E znGWyz`8^DLlF1v;kWf9rL!SdB!&1{lbaN;C4+f57txveM$@#an^TxT;_0zd_$Yw`2 z;^m4gmm3ddp`r$g(qBMhpJa8EHJQLoa&iii)pOb?hlyj8qbAS!JumN;ze+f+j;p`6 zUz6%>Y_wt$Pn4YBo37;ty^I~!vvWkWdBgysbk1it)&OYtqFg5#9Msovx*U;nekWc& zQ)4i2gKK8Q+~<_C1UXTCE1(aXMWORMi!E~j!h1Cwk979L!C zMKp(;oY2i8U>-U78>w1cX9K=}h?}vZ5~6OM4y90Phcm$1B5~J^$DuVT|LEk2P@DIu z^D`A(XAUaEyG7qai! zJ|pqZq2mv7C=Qy~srRl{H5y}10%s5Rfr-Ati}b6Hmh1#y0Qkk`z0i*$dq+!3%lPikq zH@&8hB{^_Y}J=U1?#&{XtA3*>Ys^_Ye+$kJGaWf4X#fLgUs~W7;NWh{K3p#ldd-e@bF&7C0245+tWkf%>0T^+LaPZiq zxMM78MUbKGVYq~v%tL1Eu4nTjFBwbcJG~7@(+RGYlNjDyS=W||*S%mR0C)s@j!y7r z(cLz@-5FZT)6?K|b%Gt?Yf2#H9M~8H*60m`oh!LGdhhC68EBkVF!O&x2Zv5pyK*r` z+Zr3MN5}OIOk^{pGH-zo```2h4p-}OkIKrjgq*XgIst_kfY?B^dCda2hlAC!8s%_r zC^le#QWW}fczsp6REj8~l%9NU|6X+pWaEwQGA0nd^#FvfG#+OnQ1q*iNP(?!s*|(p zP~Tc6v_SJr{x#vDo415vyR4+4h)&bF%>07Wo!!d!z}t*@TdKzNh9a{`n0M*yA2DU1 zB5iW~`g(S6&kM_r4p;{$G{16DgOT)Ng%=K|EJ~eaf%{~*ttJs$XnAN@$p$~41WvWR-S*>hInz7?cqn{N^hT&dR>STwH z84u&Q01=nSxLgsYRZl3gBMU+4yO)smR32i3<47_D;BFemU6iv;oPUo~1sGr*wuIlv zrnhb#yu7r$EI6H1{DLeT7NE{5gHz4WWb)N#TWxs&D?!a2O)guiAuov|9Sd~i&TMsi z6Xdd4VE$0${(Lu{#+s9Iy1E0X-z?r9Td&p55!WJz)3^o;l<8k{`6i8g2f)2X)3`En zS61BVtlElba*Fcfq;6>wlAz%AP9?*Sj?4YN(C{3-vl0L?_W?JT{bWexh1>myn`##I zxIO-Snmo-17s)OZsj&!4_Ew^u%A|dWq?PeXx$bQJjjQh770JkQa7R*9IUz-!>Y5Q} zItGT-;%_c}pDyug^d}DQEi8aTqTS_=3rvNq;8UI%8_pMQ|BoA+M-zsw7IpToI)Jf$ zumw}62X+RW66h{fm>p*%1Er#!txgJ%qK<+T7q0stVNO=`=?ac9wN@IPnGi3XU!J0cB7OGE&im$lC zcU%mQ=RLxc*@BZ(;v&5(3#iFD6HBBwy(0t3nQT01S4nOghxgE%C1U1=%sG4$Q{rI| zHSP;YiH?II2=E?u)Y~q2h|bQ@FDIt->Q{TqThJByf}|meL-){6&P*p*QX#>JeZ?G5 z7O!_4Tey=mHQsVS277p7!u*ocy2kI*E(*M-1H&wPYIb^WV@;Ho#!rW_sBY(wzwUqY zmyRADuSFqFHtK?pOr^j_r+rx|3GBiF=PE9F2DaN#S&uYcG%gNbHbfz?jY+Ja!;9EH7u; zKXkserT=f>J=*?-F)5_fW_O1|)omK-9ywjA{-He6O}}uwKugPToj%hu@LYSKLbCtP zSS1Fk^`;~*QG@~=v(*v6khZq>HR&P_1Uh?m4Fk8r z3xyS@wL)sHQx9AcJ&n z|FXHSR|9kv)ubfM;NM)fb!Ln5FzM0ELOvfK?fpdvKHn>zJ|50Zsgy0iz{5H5aW+f~ z4SBXLwcmroro!@6%T}5KP?@V79@ML%N9-*25+wo0V79@$d|Ts*;$v;+;;Vg^S2v3= zpnNcFhVbok@?S0lRKBS85?Ys5l#rR8^%3kcwQfk8MpUW7-rvC@0|Z{qec?f>#r-oY ze1Sc`Cq`=Z_!u5l>61g@Iw+<)^x>N--UzkOmEcgGNr#}(lTxzebA!c)C*vK)uWqV> zLJF}_GeKRVRrf>UR_nh}$!TLTiJf6RS*pccR+d+G4o{-9JB3!X)7|_!k?jrq(hI}T zg2*tiaS%UW^uDe~w*~`MJ2gl-^IdN7yVTd^b!_bsY6oatl-pO-C&Wa2FH;_E_s{W> zT>&?5UI+zrb=7O-5Ltp)Jy$}Ip}QHkYf4sJe0Bzxscy!j#LNKwm7-d2U#g!g0(Xni*v=z*AoUeZ|^JtlJRmOA7~1$zYDZp@7*0&9xT5#M}P36`$ksd zbj$w@jkrj*@o$3z%f{99b^i#_W^*b)!@HJUasZUVR^1N6m5N7;ihq4b+yT~QV|Llq zZXnjO=yJq67i!A`Y&6(5Nna4FJp&1Yg2lW4thp#&XLddrogW6(M=>NFu_M94@IG)XfBr`L2pW!X7qM`V;NCr90R`P!$;u5Bd@I!6Ua10<4OdP8u) zQxrYOZIJ5?FFfvq+}kIYMm9oPq(L>V0dH5> zvx!M%2JOt`LvfpGg)VL8$*SdSnP*)%6#$UD<3fr@!e}ZbW?7rlM--z}yIMFKbVdz1 z=3-YhV>PbSW5!7ig;Y!tD9g;nw}NFLRrs;etFisVTQ+0}q*H_ZIXOaewH1z}gbJhE z^cdlJ$5;xETtq}H8PCCFT4&md>1R zf2gd&C^J5_-_&DSddyR#g1B|#_>Q@n)|_~lX0zU>iXX?aWbH$e2G~nf%h(7NW}PRQ zo3FkS{6uEQcPHT5!x?M#JbH#~^0*scX-O&!ySv;7J7;y@qv?9_Cc;c*sSUUk{KJw} zxHOatsDH7frECX$(uE9ozX4>UBpY=?rTt?vd05!xvk(5NtaWeMzg6(c*xnaO-jMx#K#&0o(MaNdan1Bwy6(ij{I#r{q z2wbp%ZE!%`ZvHr-QK@q6LHqYO6SVU9@B}cQb?KOrWs^lx1IHF@m+1_ltJm^W0t%+I z`SH4;thGwLo?YPKX=x44W*e1-n@(>`@>`C%t*8dPrE%?GALh_6beXlvjrU$wAeL7I zz=7q=xEGI`C5EggotwM+``d@R8rlnDlS#n3#3eBjMnzSJGnShb1=o^yuzEXr!Tkpz<#-O-rch)ZkjIQ zle3in<=r0pJquxf&tb#6RoAoYxs()#{&8Qogx@Woa9Gf%OxKep<{?FsxcF}7^0unu zN>iz=xpazFEiBI7sNM@n%0jtDlVKiJtEH4z#fK)NVF0mE*r znD*i+48HW5G_JbK$jSZzu@5tPmE5fCNRQ0fSkJbh#!fPON@ivJlu%Us7_eZ2LF(Ga z{`nTj%SPS&{NPuvNvT!7H#&d}Y=QG9Mh4qW%c2B(i@f3{Q^dDfSKi z>kD9i^?3;E_6iHT`F1N9}Ppa$6VBWv|aecYsx{)nKEpaxo9 znj}nbJ`~XJ1(o0G5<6c&4XYOWRF7xq%PyR~jxL_5~ZJ+H)=4g(M zP=>K(j&nLJz!3`g0`Aur;Noot_t4N%?(hX`0T4jDzWMbf^B`9{2;_1u4C4I~J~b1m z8Kev80Na8CR)X#!-QCf)_9g9n6B8aEt3y#uN=&IEOFxmY{=Sv;X|hum5u;a1$?amq1dD_*I{ri4{7X^|4+Sgje|s#IsUhlKvjsh-P{o4|LOv%_mcnfzW%Eo zxxzsQzF1M65R*t3v{nDN_?LHuw-l?pP@5){xf* z*52D*)oN$4NDJZzo<$`X+t`!keC{t8Ba7v#8(cO=11}+iL)Z1ptjnx+)MPgCzax}R zW-(io1PEMF>@ohhK)>}LfE*^y8RhCMfH25A8EYRuN75Y+CpkIY6eclWV{mR6TDg;5 zeovO`5+wVhnqqUg*U@Kc-F2S7Di(ZqKfJc7Hut{q8g&u@*&^ZV|K+Ju!kk zyt7k_%KP1386xt5R~=V;W|~=^cNXFtFGANwl6C$Ql4i7vzC2^R&uChU#l=Cb9RVJcl z(7On9%V}%;ADm633rj38HO}~dS#GGY1MEZLsf3%)v{fx%0MWR``d}P%kN1%Yg#0EB zgv|TCfO6R|5Zg=26o4}{lvh*?-JUzX^%FRO=f8VT&p8hq{$rhl{KcZFSsKQLzZ>o| zfTJ+-@Q|E)L`A`2pgeR#fD`)bO1^x)8A`Da7fZNAnIT`QloM~lmYFLP6!jI-KnHc5 z<^+oj+~G=CmqqlS_wxznA-l^A7@~eUpE3&mWfJ14sSEhsk=FcuRiq@qHMZ~tV!@@# zLGn_o;WJoPVQA2wf%UkyYXX?=TnWvV7MvzmRs$%a;0*M~iDC)9$_c9N?$ zN*8aaxwBH4y9v3Bv38`ts#zE?12 zEmn$p@9>WfBYfa-+!x{w&yI6lh{fr@Vz5uVEY{PrH5wnvIjiAF+S0VqO>LbE? ziPB0~TAy73+WX}EeEGs2DEeb5%k~@Js@w_HEvJks^zc>kKMzmzDYtW9?V3d|{0<)Z zKFKIu+0y*E*j=fgn>_^5+zjT&s=JdxL(q6{E|`DIb2gADbR%cOioZKHZ-!!ZC3&uS zS}Zrcw>SG%w*9KZlo#3ZV`Y&<(go|PNW$g!f{CLM zL3P&>#2QD*u>Hoz8z1Ge(WZ`b7%%Uo>gP~@siuUoYQ4ezRSkASADm}3+F|dm*ByJ` z0n~dG2TE*Gu^LNn!j-x97){1hzsEfNog#{Zamlgu3VgLoy#Dl)RSu72N+ZDwB{+`> zw~vRW-=NVjv59Z3r1B7gg1USZqH!1!mrB|As@e$6CsL$YB2F)!s&4=l$4G@XlTX!!+8yq zOpmw6c(1RupxW&9JK`FF7E=9?AAq$K=)?fgmEOFelWiZ**|;OJRQIvBZ7FpsP#m!* z2+u)6@@goYG_BcV$CNPG=GKYXKq`}7k};1XQiT~* zl0Bt9{O!E&PSR>0NFOWB_SC603+Rhyw?XW7+(SZW8~R18Ea04$XR~Qmp}6o3n+;b> z7YY+Zp8&#!sf=*-_+bkS)Cv#d_@CI5%9Nj|@q33=6RDY;uBEK2JpY`B2c6Em7nKFm zD6p-qB+M1!{>W&Y=CO&+SJapAefEn7Ig+;Y!nlUum{$dZ@Ez{=6~5;Px#AAjUl|Qp zOXfRuX_U{@`NMNpe7y^o&Vz-MW4~}~30ovj@{q7$y1ud&7Zz`5rrmi(XH&l$urm!Q zRWImr!+Zu)mQqkU88pMWT-P9@eBQm&?6Q=E4m4^RYK@V{U{$|@#pFiGkX0_qLP{E< zJiIL}D8#tBsvv05fNr({$9;zG&V^OIV})RO7@Z?|;^7hgO4mrB!aSs0j5H{l%sO8s ze#TI3a&<}W3ns&WaWM>l{j8%*{q6Wcq{x3(?M;FcuZ9onJ0m5rw+WP(2lwGE(B)U( zGfRPkJOFE-`{qp+xeOpH5Dp1AVaQ&GR3OamPvKo!Icw;|`smKS*vazwZ7x1w3_OBE z^uFBz;La-I=xp|#sx%^daEQfV`|R=iit?S^n*8iUW*p#UXV2(8joOx~;v}W=c*kVK z&vjw9{azS3d}R)&kmBKiSZC6G?4XgI<41eGlfmH^crJtR%8(-C6eXB93eFeglKid4 z##7X5CjleoK=ON721o(Z?U<;9g^|lC^a}GL%vkli&-s+SGG)LA-E&$m1uw0q57*U` zRAGPm^Bo`|rF$&M_s0>1l&TtgUP8Gjpm|s-=C_g9!#-d`U5v32FdHB=0!|;i7kOIh z0{u;p)esWtK49IKL`&>fN zjB5w!F?Wy27ma6u(g+W`SYyvfRFDyJSn z7XHRM6)=$|8?l0$+}I+&!`Mycier+7#b?SXrsN>=nBTvcU0r>K@m9qO&x?)|pFUC^ z93oeelT1hpr>DpA_9190Q6$Td=RIAA3tAi9gYX*9&3^y6XSP*mjQTs=mKO%r=-m+G zH$$rPW*uT94RN=9IW>mve&Ggp1vDm6EKKl7G`%odIy1UxHFP~c&e7HpF|z@klu2kI;CrG-(swx> zz|a$QDdn)jmIt7rSn-sLiVNgrJ`c}rIc?)XFh9Rb!VraoC=U#nU_0Zaia$e?a++d6 z1Yie94v+Pjj=|ZXlN#Nho3?De3(>?7MO;5=0RBTC1dDoZ7YUnS&sNw**>VH+9a|gTu)lU#2?`I5<1{vYYIbh>caMecpgPxvK zYeEg-lGzB+nzathDgwM@hi>6_ccs+^!4EK;d_KQ(1cUOwp*xJF4i)xXP`@-Rl{J_{a7B^l=76X{PWnHX0nOG!Gl89dkZXu zt2KUihv!Fca#0mTFZ}V|y?ln#F+z}%cxLc!bML-rJUDY=xPd?-8R<60N`8{Lw2*>z zWi?+$Q7j=NdSw$5=(XW;P3SmxhM7gjps}uva3t8#pJrLHF!8$W%x{$4lsm;*m(Ttv zD@-~&Z3m=3KYa@S76gC|%$ltf_N)UF7pf@F1e)^7zv63%L5<$1-{cqoN6%3;OEJx} z@l0!!A<Q$E8f5dL!Rx|m#la}w z-d3sO3nY#o2!Oqws1TrNq)WsgSn4|%6?l({9>fDdi^bPmV(P!77?Fdw*B zmBZQnjU{{E76Kp30w)fBW3Q2sO!b-kt1VyE8Oa_R5WXSTC8LlWyw*_IgS$xNuY^o= zg?Z5%80rdVqShr7mdi7iye>R$kVOPAM>cgRS^}7!&)cCttsA|Vvc&%^fp(m35LWD0 zG1KXlCr%-^n4+j)vlSE7Fo02HQVI^)Xsrj%x#?xT5%hl3Q2zCcY-)tOS(A9Z$&sc} z%di7?g4+4wpv7TD#^IEo41qU`Px8w5^^)`zdMWT38w47{c;9<>;>QY_8A$FRXmr}> z3@LgGDP{G9;}IeKaePK>))ef{oD+$OcT0s`1(8%*Cif_OiX?xPddjxeTcE<|3z zdcDb*Z4ku55$z9+To2EsFSj^h;ZA04l|&?6R;m{3K?aoY@k!Heo9KGr@E+|ebP-;$ z1x^~uhsq|Ofes=t66lrtb~Y2ja)kOuVO`T%9i3aDUKCp;RO*NX6@ze5GRs28M%Z;8 z(@BwgkbS1{i*eqoJd~E)3sSMny!gWjDYJQe@mw?r}4 z&`vKED&DvAL(*{kds>|Lm+bFKVJ3n5A1nw7uWS|v`M_jlLOwgS9@L-wK|_~^m<{fK z$;ZaTQ&OWQyXier#b)Lm!=HRO`QPb>8+5D}I`xr)XVqe86Yf6*)X1n%M>g zQ}HY{u1+0mZJxs4aX4tNpSP1GZNT3Y!~7qpT?n}ISCvFC9@lbPNY>>vuQk)MvVbl4 zH^>0oaY_y@6Kx5i)KAs~Onwu9HR?JH4c@|>tc~Sff5b{=mM+J&0-Mro%ku902vmAb zHOzl6$)#*x2pTI+`f$y49sU7-!hgbCHJD>|4sY?@e^4=7bb){?3g=8c`sdrLpaQ-? z#;?k?t3S4mf>?P{MF{J|@t0!mbJ+DUb}z$Q4-3|#zNB~Fj1VmI26Zq6s^_MyUf+Oz z22|wAs9P)2w$8%AUOl28_*OUrDsA5g;$4XKQf9d8(olkrkJwi=xP1>#Tb+>X0FBJF7nMr{*@cGy7AJ?e`5sS|kKKh53z0z^+q%CyczQBZ@jKF*4 z`*oD&X`5r?WDl4?3xae^xj9OY#}4@wB)i__2%4-U4@f%8Koxt~h!kvAfL1F$Q zgr3@sp;4BKS4+E>>KC`BJv@DvrC>F;ub3{VyVY7>loX72htAn;=NvvDNk-Jm=Ojna zvjwmJM(P~qKZamp~P5XFUl?XZA ztZib}sTstU72);!qg$V@#|LpZ|5p(}cTs}^e}bZAd0j*(R4mvrnVvC|BT2EkQ{@Z- zJtEbaTKh+L`B&zcHeQG+984lF#zJCWTovhsNbg*0&=fS_&+N-#Fh zhxrRukxSsv3>)8c=Y~(hz*HR+=vkn;#`-oS+;RH`0R(DSI*pm40EdX2`y=`}Qp*Ru zrOPx(=1t>-;YCE=HDR3~XbFR^{-UkE76PAOmVl?2rA_a9=A@@=nw!EIq?e7NHgj7` z=_;DAG4c}cD(fh$F}mOOtZlwQWbrdc)JKfSYl1&D=(p?r*Z@OQ9~mh%bwIW`wnK zRR&px%1e+l42{Wq*je5$D##nOz=WC)RMCkEIVLPhjt6T3%4FJOS}M(=;=*T-A8AA0 z8AN>sKvEXm;yI#TrRN;!@-Gi7Mw^cplsdwot_O3Y%MVzzESf$OtAcT2WxHR&TeGQ; z*$~}B+>wJg6K92H%cl!3mOnGH*t9N7*xx~tv5-N2C<2FNtP0s@90nXT$ObmxP(*}|T*$$s)UIcwH zlYgr-z+Cv+Q`T$Yyj`Bg{cv&``Z%kmHd<6=*Jvy0!MuJVlD7W0k+r4O^x{GyJ>wfl z-g+$SzOB2M)#7?*1$Y(E6r=NIpZmG#q2y`id3Kb4QBh8f3?7|)g6L>+_zbiDz7ato zgc^oS-tvYUBq$I?KSu+hd`xSdSv{43vmB=1PktjPY^zBYsqDY~Ro%tweVrZH=st%t zlwxlu9Jwv`56JZJ$ndEA?W8wSEgQZrlW;H>k#F*#@;swQUGE}6`)v14zB}(7d-6j; z=5Pf!DD6%B%r`w6BfSiB%!E)o<0A?Uq~lOnt5ktibKsI5KOrq8 z2Rgq-_kF>y@o^NIm`B7`y+KdYfWEC*-(ES+SnPA|`UCu)jjkKe{ag);)oL5l2FSl! zy-zBJWNAaW}KOU**e0oIG^*T;_`1{#x7<+ePL% zfD>9F&WKLG?CUP}$>zU#^pa_{2H(4=SVN^^?RKb4ef>S|waU!F=ebSyUSZf5KEg3W z&~rwj`>`>D2Eq5L6c8Mnzek z1o%wEg~R=rpe@6~kN)ufs%H>^vkHd;kPnk`NCuVWjCI)EhCU)FheOjd-C!X76P$3> zUcoKt@GSpjzbBT7Xtfm;p4O7T`xR}f`-Q;!}QfRr8V$r*%Vx-FG< zT!yWm0$-sg);#GKNSuN+N|JAEEn0M)@m_?_18<2N2|olXD5{7U$fc+ZOhk(ryqsfK z{cz&x>3_Nw#jtEa$Rp1m5hy<-{tN^#6=kQpYL2=Tm_VBq&3GfF-?m75-8N?wP$Z7 zK<$%?`^45m+Jue&x>wS1y*0#XGgGGr5wQIO#gfoE_ErVzB?WoCKC{o)|hcbz2{ zhU*8ynU}{6I!N+lSlj%NgG78)P7aY^VQ9t`J06NzB; zH(oe-*l$W$`0y8o`SCL3}B;0+#Q5B%QrIzcd1q0OI@K(;@B7dn8=($-2NLG_39Iugz|V z5cPv{sRi<+_s9i%h&3)-g1=t5Q|gD}E~VlOvV=d5*Y@-Q3GM5tU#M2DoQxU5l1R_( z)Fu2X`geCkL@b39h(xMNj^7VTtJR92OO0awS*++_vsbjWqC-nVNA3~^-kR|kSJamp00CP||op+mA zn%8-)=2qzNhK*|chN|(&cXN1rc?_(-i|(mXIR2t213t`$*6VajAR$EoyE$PX)5IK* z$~~1cS!O$aZNkI++G!VC&vTHdRZmfhaO=G%sab{?+7Pr zP5428v-gDz1?lsDp;QG&5P2d1tkcsf4*T3(3y}{AU?wX&y%1SwX!IVI%Ikbi*($^E z-p&W%T9er$zPbMCq`!W*xAwb&ar}XE^~%q`wwQ|ei@YW#iH70No|^x97tE5gR8rZr z%s2d!Qp=>SJxh0Xe8W$C{Bd&sc=MmI{Ztl{uPn-yZ!r%?jn!UBfg#Rc9>;;riDQz# zbPI}HvT#?+hcfK5s-^EcDh6kl%tL5rdt=Rp=A#$Yyf|JMNiz@Xq{Z}5ty0Sv1@!(a z$)67kI*>5&o4Kyn*YyTt9)dx?CL~&>wvfz!!+yYg=oW|n%I%0&pHls2aPh9|C_5@O zAsPO(JY7yO7I5Mia|HlUYY0A%DK{Y5YJTT`k7UZ7i7&DZ-{I#62w^qv0U_?G)0L*s z3*>`Glm4N=mxceq0w@6_R(YB}GxmICI#xUsy5vz`kr*j}TV+wt*Io4?ddDOY3AYh4 z(31~9G5{D_D=q@N)6+W=1j zK>M?0%99}sn?cbOVgmg#-6iy3`i2Ey-kjW=sRcNo0YBiSgoc)oP*>m+S}R32^EeR? zoTm~CT)-=_w&(YeQJEnC*m+VR58N`dYgVYaX2ec_DTx`X8o|ths|zn($3FWHI}ZRb zc$Aa?w1Ct76$S7?3mu(ZRc)BH9m``*ChWHljOgdo)UfOjO6!p?0HDKJXq*0R!Pe#0 zgpMn>e5z3nw@l@n4=}n?y<&ndJUh-#E@CBf#@y*Oo^Jn8bcCr)+u4xfR`YdSz!4Tj z8YQ@L#lfJlQzW$usg3Qa>k=C3tLp>k^v=sBnwFREiEm7(1G4V{K23CySt8ESLb{8t zTsKe(tAa{lOKr;4)=}H{h6D3G69qR~KVhqi?5C_a>wGupJt_<)ZO?BCb-Ul_evV4d zfuOuwb-Dsd7}S>RhbAZU)sH6Ll_q)?+r);RHd)rcI|}bi+`}uM$Jzt79fnb3%Rv4Q z3SG}l+}tmJxE-FMj(m{$iQ?Uf>VynG~H%M+3r94^=Q9`B?s9U*v+F53x8WA=1-=Ji_`w- z(|8wHlWthO#Nl&j*A$;#2^Qs-}#4VL4=D-5@4#F=cUmv+_R5A?b zGS53PDvFNGi+-`1$OLx1nF}@^^DwFfiO6qcJr8~T%VR4VHmM&;Y;29P2UnO--zy69 z)*b~6VKqSHYSr^h?D{VIET@1W6j6v=4s6jBO_MBNJekzr^0ld2XE34<{&0Se$p`HL zrm@n#^=I!YhwKwK*LwZCK#e`+60<%y3A6j>u^ZNBJvXZQAr#A(D^y02{DMGa|AZ~z zfJ?;n#$*m@s8aP}dg3;>67MkFbH0J6B1pj{h`?NRj-tx51c(>KG!WP1$?aR1nRyX# z?no0>Ix`U30AcSAbjH|C#o9j-v{q*8*H)kXtu;{MZR=bg+TMLT7CWVdhsSIfxbSb1vo+;2$ujVHD;&5RKlw- z2J%BImQRj|2jLf`FEl6X+SVA|=ODl9Zi8xCU|!y?k}h1>brJ+aSiT{P(Inkf81Q-n z=ex-dp0Q%!ko*`i+Xg~F5V7ziVlY+uT@pI7?CI8JdhH*O!n1%gb}{%oU+4f_HZZ4R znV3%7aDC6=i$KUiMJoWn`(A~fA{GwIibFh`yNA90D5ndd`JHVS=Yx$e&G&{FbM0*>Xh80^GMyoCvN(0(evuSY_rnkf=>z8F_tz3r`&Fg-Ie|S(R9sGnYCa0 z$rC2qw(TYxQ%y~nY};!WSC^vj9bc%o4~&&rcpx$t95Ftg>Uj?TqOie=u>(;rDwpVwW(2OCXUO=VKb zgs}bJ8KJQ`Y92DI9$C@amBAHv^>6Jh_vqqrz`MI~aGW{4Gcu9#QAR_%f$0T4uPu+* zl8z5_d*CFWhN_^UA^VRboV+@@x0um4Z5S69fyZXWA^NRX80yJ>&8IeCmswIis@o?4 zA`Vy;BU{#+m@uoKEdfA}3?|>MxBC8X%x2u0!lf|AY>upMZ_l*%$N#V=LV#@doOt)h z!sqz(?u>vSjoX){^T(eX^Q~-Ptug580!a@tBT_b&3^g5Cb-kY$iF}_qnA$gs?1F@9|>+(TOY_-#}+sECKdz_+T_h6#pBTr^+ri$sroytv1qYE{|QBcD9hWu_a60 zA8RX=eTRJ+3Yjt59j#)8qMHZ)ZK?_7h)Mzcm0PBb&3BOh+8lE}HEW?gKV=(Ap_JkZV5DC(?M?ZnGQ@$9cLQ3KS9!pM0+*ij+|aQ7a%c#rXww2wNc%+7KhX zJ32xW#=;Qb4^;ee5XW^X{94)&$057~Q87@W`FU}>Vm5iVo)rnful5^=Xp7?gkTxNo zUvfhu4$dP=K>Tebe9i1;E6ELYlqjh9zo$GEH}04F+Ccn@!?!(qU-No|#ezbvFIu8M zW+uw-D)Y8S&he;s_pWXqdZ*SRxZM#HZMQ(%n@RA*-Ib0dUtsTsHd&Z-syEzsm<}&6 zeub`U9Vvo=6EbBZpIyot{rsYMd3-F8EmSl#Y+wIxF+%|&5Ep=l&bG8^VjJYdc?sO#Wa?su*JO5y!_~jotu=>-(GS6N0R%c*8X(&%-!|)9>Y~ z>+NpY=P3cOPsbA{0&Cy=?%62)KmhHRjFkQNtNNtl=cs!=Y438Qs%c2Q!7lah-wuKv zoqP8QFxtGeNPY_{PPS`H@Z zW&B-h=aMY!^gw9PcP7u1-l^&7B~n-=zq_$fsyI*Z^4LO<>;q?+2QQ`GP7d_zPKO56G5Yzzwi1zkEzX~`^saK9bUD30l{n`wIO7RTqOan z`$Ce{r>t}o`oLo~t*bZ+4r%W&h)r9_F@9RHN3~2Pr&V4{sa~!{_4HQ$Y9c$G$y?ZO zsL9sO>6Nlq5Nd8Wg>c?>lkx|LedC|{gg3hd6`2l^f9xlik%S_BYC_-5OGy*fd-)kg74{8pCF=UY0%4HPIWsoHHqY8hlSr zfuv$%5I^fm!rkih>N%?^Nb9g5c!onCM>zz?_n|D3$8k=k94z2#;%@bXnMitqs?B3}!@Md3t&258< zQNA>a>PMv#H6-#^TaLxky7=jXhC5Um!pC^LouV0oAx^(@%rL*j;{|ep_booJ1+{wi z_D(_(W)gp0HZ})IASCMJ&`~e(i)2K-Gs_Cpc3I^$%o3C1`8e!mOINN_5$(MBYQW8c zWzib3yi{`|cbzZR5z=MJ&y}0{lbI9|3K?%#kRs<&m&161vNwr8nB2)$R8q*4G4)lH zXReKRNusnAXfr9{Q}3b?A}oz`SL`Q_DbiNpn^&{T4K>$PHo2l87H-42)WGUjWuJ>= z6+c>Xu-mKo9{z&+fuT{Jdh@SE<%@-K4T+CAI`^Y65u4(L7wm_}odWG%4~f@xhbGeC zpXub?ODXn&uWSahsaxG*{dm4a$e8ZO$Q|%*2B@8M zPG&i2C)1hkR(vCKEwVfSN;ao_D?oG&3Z*F-Y9!;}b(dpjlSC|%E24EG;AWo#Wmm{y zhz*rY8SXAwzoBDy7y5VLaQV_y=@RJ8@{I-%Gv_*^nN+O&HWW;8y4_y4q!GeYFIl<% zsKl)^d;MU_UW-qBnO78PX;1Hh>Q>30v1n)(Y9DG~qjGn6g?X&KzH5FT9Em5izGLw%%e>sP zwOZ9qO><1zVmfpr3nmQs0;xH?kGW0;@EzID6_K@3;N?wLT`!?}fl;ubAsY;n@=4wkCJj~;Cg(VZN>v8&9?9Gq76L3er zmV`l-d?yV}SD9yY-xX0g78o#7c#?_ecDI{Us01oz&jVa-xYl?KMJu8{@&2;B)Q;Hm zKN0<5uh$SN5^oZ1j2mVjjzdOU7e43-TbkW0BgI}PchV2G8{_$3wU{}$rcgkN={B7y zg;gK2rq?9G!jS~i5nn?Th%qDTjDmc|f64UqT8tyq5p@fd5S*%^pkNNh!m7+JFc#sG z?mF90v-O2kmM32yKC4hN>ZJWE&dNT75s$cvLRJ|OPxxSjN5PFwPDY49)n{-gk9yQVr7?pYZL(ebtyhfuq#_v+;KF4^NGD=}Dn-j~m>B)NU7 zW9KC;VZFr((NKlWg5x7>Rfw~1zBk}D9F#37(w8qgi_>e_7Q#-ziPqtYO3 zh}&=ys)CyHz%vn8F)m-JHg-Vv%JZ_s`>)AHG2+6zOM9+R*(_b}!~F_C%)mBhgX2`# z#IoGadNh<0l~ZgfmCNA)6{?jsXEyjH+wR#>-t);47Fv7SERVid`jqe$AMaE!VV zG!(+grxtj$oQa^}p80Lr<{nYLM*GuIio=R$wloVts=hG6Yb=#bjSPm2;YAKQW=@u( zzH0AUceIgBGozHL%>A<+h;fZ3o(HpV(9w_uJgI!_Q7MVQ2b_Y%*AtY#5|26`%nbgMX5a^ zdPxz8x1LO1Yz;_GHQnKHF-&k4O5SNc4JrBjTaKEQM&t$md-ex~?B9SXonHyipG@20 z`^=9ObM~J*&^n=|QZ3#VDSkNi?Y)L}`MNvShXXxr_c;r`WTnjn7UPtC|Hwzw8K&IH zBmb-tX*y(iqrmNBL=&F)s+FO@J{H*>bZc+ZEMq7LnATy%d3Z5pHYz}Ph zgL5hrhahTka}qjG)n&;k)>NJOGb-hzu2>vuksBc~WmiMh7G5#~<@8ia*e%v+id7v- zcS7+}k3l$}S)DQ3_wt21%eBlREf@WyUX!Hk>vcOxnV8W(^*yu@P`)3}_qQeV8G>O4 zd=I{RC43yMPD99wqCp=t%zh_*Ff*;PNVrWlDP``5cqF}KVsh`j>8fCFj&OJP`v0bV zFg5LrwvfKJQ9PHz7dew>;he$3Fop$yK4FDSs~Gbql1in&J+Re9x#_tukeS8X~Qo9Fr8qA zm&{o&H*3egz;UC3SN4YVOfS20fif9Rx1%z+B2X3R{#G1`EfSamjN8;8!k=iUNaG-6 zb997`$$I()SXn%#j)dvIp_FT!ah_aN4i8ttluSBtJ=fy_`m(%=xjkSXk>^VZ1HR|I zgPVe)15iVu%?Av5^5)LLU~i{|YGpq;t-%e#e(S54ki^N8?HgMT zr{pq@Pc|@HOX;pUor*svBD}G7ub_0*8=nOjbGM*B4gHzE50Df=xbP}ZMxL%?k|1jT z&dZxuE9)7}W4$4zS7P&XvrRs;91ku9+?UReZr*M~WfWyodybEbr_+%wLCr>{ z39^cb;mwd*zAJuIs6=!M!AxE48F;!PbkyUi0kYQ??+fy1MkurSDl`m>qAfedqL^QzV2tr8k)G#$ckzMbXLzIOlr}Kn-pj8YEAyo>S zy%G`d8uGA)=l)Ff$VnmYYlOluCXGCKKZwXASNDfjox@7oeT&l}Y)3+~e<@<02!9uJ zV{{vKr04=C&Mw}(WD65`K+Zh}(2c9nf2 ze%o) zQ<|)slnEnp=*LgzIIuXA77YB$DJ;ww!jpzV(NPsX85zqYF`k)w(+> zc@)-fI&*(@i6BKqv&=marMGE{3@?;r_{{5mz)}BdfpZ*+&KDojy<3+qVJP`AQaut6 z`Qbck(GW>A9bp6ETOfDvCoP>$c!qScGhUfBhI4H`F-SHs|7V)RIJRJDI~4rxe!S2x z)aE1>bHTXV*Q@N4K4@0wgBL9r=GM_Ibf79mL-B1M1F0cCRbWZ`GkifJrG zplQZePU_u5!cP;5?Glw^43(Ss6+LodpspX2&i}qac33uvB>w}cmEU9YcS)0AR1ro% z9!dBcR49WczPTGV=A#XoRz0`E8W);Mqrv3Er6XTGv3fv}{os<$4C{dghcmrAuak5` zl~=N6pL%80VmO3^d(=GK7bhvP98i}(UV-A#mYc!+iWw&W&_#e<52UOzC zVYSf-zFKh9KI%vbLK$AYU<@K%klp&k99(3D?mJkipOCtx>2#LXam)R@`d+y@U8i9m zPM;b$xQJN(Yb_2EMt}PnF;2Vc5i%W3S@H<(pPck|*+Rv^;3(0})MCaY)}fob2`9RAU}H_oNNVKDW?6TB`rohZ6gXj}sgwBYT1 zVwpY^@->|?mjY>mR>X5^*4;e5{#J3_Dr@7u1RpS3>Wt5$m9`GO+t?_ksH<X{X8^?uO7zV*wRH{KF+fUqPbNW-HBb|1c4DH4cUn~OUQj1r@wWP+O+hhT{%WeJcl ztof@E=SvW5?AZ2FXQ3ZmMo1NnF1`p-6+!chkmG23wszfMIu%K!IcN*|P%pgji{6V4 zEue*zE#*%%X~zEk1TPTdnS2~S^CgW7tzEEWGuVW~JQtGh*)3XTccm@hz=z7MXfI-9F z2n5j}F{g8YEOVdUiuO0FBRUXv)=lhDcZPdk7#ACa%Wx=(NP>tsc|FqhQcN#7@Alth z(&KCbF^VVFNh!Y9A1cN;fJ6Q6YyFKr?#gjWP{W}J7oA~uTg5#3s%LyXparifZ^i<9 ztE*%Z%A6Joy&mPM?6;444UxbWi)FuFarI`Y`s@@V5Us6@E=KbTee{P3tf`RKb7Gc$ zSNha9TMHk!!+!7#2W8jy*R!Lpm#VM*7ZSfwo$K5#SFN?)B^l z*?s>(jER=T<(khucnyatx+zNnfOBNmHY!$H?`ZJ8 z-R&*(L{|OZeR?v%AJnu*PgSU2ES;V(Ip9UeLhjKC5j+O&_`9KYiQ?e~Z`*{xSvBxD z0cVlV&_Djb7cXPYRTjqG%&Pq}C~IUR#;xfHU-57uqEii~DJsgIthP1(Ipq<_%;D$nyYAr@vUZgtiNOg_n+1zU(r6??DlZ+FemiS z-in$r<}S0&MVmvHgi*5~k_)>$+l?nLyI2e-C~x*BQj9S?Bp4-k4GnSTFQ^s?1rv_j z1Zd=cR)i|G9OE&6Y+omH)D<&-Rtub1jJ`6nRB^6a>3WhDq4sq91+Y=>3br$8)w8*x@OuUV?d8B)g1gqfmz);S5Q zkPpomQpV`XK*{5GLJSy8-LmTx!1PN19Ke6&%q3wrNw_Zj?1ioz13(hX^idP(;ql;R z=6#*l6Z|N^hTUo|4$c{u5_ghD)$TAU@^HYAr^6E-3-P?zeMykrYDuHoK8v0dAt~vr zOUe|QP7&`S(uE%=&X^A1$Ws%w-S_<45g=8@o+>O;?6~PETTd%Fe;WYIC%mjIC%tZf zZgyj)CLc<@1<=$ITXB7Y!~I7 zO9`DO{3qkk@*?UUxI*bUfzr`b%pWCvcC|%nnPliNNMrqt{An*Ag_LxyRV&by(N<)n zFg<0b98WzaX6j~h^x`@`+2fI!F|^slc&#zGZ7W?tWC14$g4H4zzxk;8bdsT`Yy4J2 ztk35@P3&qa9FEWHr?Wj_wN9)>U2N#ZWzs+ET4Zu3t$oh}jM%WpmI zb77@xvBS8|-)O~?hERm3dfs!K+!j;QNU(A9oSYh)2dji<)4SRL3KNw0XTDu8; zpdmnY=yVx;ETfETc0qYcmRv$_>-zox^21j5WU?*<{kwE~L`f1{!NJm#eIJ5EKiO^2 zBU}mJsw}6T&MhOiJW;x$IH|DkWVogAoJZ*faTfmMenJNNxBGH0ibjfMy=`l+MJkMC~t z?U8o$vNsUFFNhqcUXx+UR`=-NPKTRdr(#q14{8o;umd&v?eixb^yTJsKTd+U{u8Ux zU2V7kwTy8k&0!;m1f7(-N?nqlH*3wXyd&C0BJ?Q*Niu*(RKUR|diYLQnZ!}+dO5wi zYyjBbs4M|!+pSyjnF6YsYN^27sKX zea(kRi_e9Vrt4);f~>z&%+ZZyuqspT$n+}P-FxWjJVKixOk96IvU=&4a4$wP`H4`) z9>$}sFWV!Cim=I(JI;9iKO+~S%BgR;^*`QdX0MnN5u-h1SvioY%__xdM>P3vTQClX zlZ9xs|KKM}w-?jQ13?)HMuOA|o{3`K(C{^OMr!d-Ao}SLeEwtrF)E*e+b`?1Noj*| zX*a%y_0lj6hy;}BRzQ9ol*=oOBCu%c{r#gH3KcdEqdcF5Mcmpv=*2M;JdRom2Bni6He8IL3>(F-#`VXifH7cNW@=43-Xc8=UdT&HKX(_WIh`i z&G7LT{8JyAPE0F_K!(+1%jc*9tc*CV1DdW>zV{!f{l9RS- zz@rby5G&InLxR=Sm9SjhNx2FO(3Z0#8=n!lwXUX-i^lZy@if0PQiX9;{9P)a0y10S+ILsw!H|PB z-AZuY<-R-mxl=F*QBgGtH;lTv-ZR&BOIqpY{KIP6Qp;xy*#aV@DTdCUhyVzaMSSUV zu2c{uQ9kjSeRVnWCyhbNbH+v0Y(mi_Gwdy2TEs{Q==f=(v*We$smvtI$}Y^hq^Xfd zs|tyL@I9HnlFvVBrUOHja{ZYm0v)Ob`^RSc7YPY7E9)7}pTw!DxefL68ZNR$8hi}z zA>>z)vtz?L6cJu6~lSNNh!MI(qiNWFlNAnxUA%wP+_uFLuO%PiE zDE^qb9y+*L0Y_FWaFF;o+if73I%=_hUx>!v)ah~#7bg!eVgLfmrQL)Y9{k*D32PTK zzp>U9b#wnUFX~tj$#r%5QgM#&=J3kP3IUOc){Znt>wG;c6!OCV*ZwQe*+i0~wNu|l z<(Cw86%*V272=Kg%TmX@{Yp#PFkpB&4f*r#eY*EMsAw(mcC&tkI@5%%j%Qx|MlP&@ zeMg_&xo2skxJryyV4dngGix>b{fP$44?(5WGyDbc6-!MR!kAPrMVS;4z~7iX-jLHu zhPdt*Tz(kRj}gN1H{@7;PX8wM7251@QmpeGVBMt?)IW>4SdHeVOxJxunnV!{71KQ< zaufkqj2N6>4_Lu#%v-QX&C8GOFH%2W6=SE>xM2(@mF3)!z-_UM#zWo`(} zKNT(s^aYz0yfTdDJI85mV}rP*QQ`;ZKX(bWP6=yL5mC3#2WNxE^eS8nN^YAh9fgjH`<|~G2Y@7k%r*@HHo52!<$9WLbsNlZJ=WjPaLDZMG~bfAh%E55Vr6Dh zGYnl$p=V55&HYAxgN^X! z&EoBbR2j#zSQ1Ujqz)FSC`_G|6;_p(fVh7^dtA@c*qCNn5bLtF9-d;<*Q@b1{bC9t ze3e!z75|9?Z>3d}+A7o_GhnDada@}A`&v4P+)dO_gfV+ejSi(9_JRMZsYu@b z$py>Lrgr=fvS8qf_v<&>fwvQ~c@xak4woTjYEY2MCW2=kJ0}MP|2~>vwk?fjBe)EM z*g!-LJEoi4IVvdWAE#VhRGdAj4vg%uZRLauVQQCK_-qf#kRgnch9&@Gu!l_9s9Mus z%c+oJv1k;mz_{3pO~h2If>+m3lNuOg_+XI3P@~|w*6J4Jfb~BR>@@DszT>5r7!H%C z!|Hb2gxk2AWPq%}Zpab{kPBwTfFWBM33P@(RLWJ-PxrxC6EIgkfp<*nrQAKgPG0Fk zGF+K_4}&(~VNoYF+>sCc{f99ff6vpSV*2abyAtowz`rwT1KJ<|B};bUu*5U;&a0@m z|BNvv#JML7j)OfdnJ-@X*Z+H=GcH#LpQ)_h(WqwO>WP*GMijXg-Z9F|67tqA(|>h zocQ_ol^VBpCCPZwfSHi(6XNX?1{2w2`+AYt*3m40=3D?Rx`NlO}8diB@nTQ)AzW=J^C4x?-XqqGq>w6K5Z zo`L;lHJcB;ptz}=^}iE;dfIsI6mYQ=`oO{YNLRxWQjkxU({3IVcx-h5?Zyj*SO6h$IE%YqwE);Ezx*|QG4aq!3dsTsC3G5> z!u9%4vmKpJou1tGSwj1tBZSq@SuedTbQCH!~e z(FEUH-o(X4Y%vKvYsGLftA&tCT#$Gc5hqt9M4H_@+rPJKZ?I^B9#+PjI`PE zb#A=qy*zN-an;yf(s zCiMj-2j}Ma4NO8l?Y9)2-_OXt>~piP&Wc4qapXVXVbEa(Ub-b-@830IQI>%CksM{c4%yAc zri{^n#g_oT(N)a6zjV6}6tTEcp)z*re`IzeDY3fQq*4m~_OueD4nN^QSRcU+=;qmac#0LBtb2q(|pXN>X6;fv{^6; z8WL>pQD<00h=fT4H>82C)Ljp0FTE-MX1--#mmP1bhwK}!C4QKcRjGx*%{9U!56*%WbWn=p3D@aDHFn#CfeQ%GhqjVj1&;JC2g+bWU#ykq^! z;mPRt$pz9T%DS7Q>aTGipP3m3e+#v^E7;`fD7z*G=HB-ZXx4aa0hwO;Ijf_&SJ&ls zBe003bNR#aH&0MAKQs6gvHxfzORAu|l5wX4JS5=v4!vyn?N1GmB28GL3_=G#i^pwv zDRfk9uw?GAF7Yp8U2dP$CB+2xFa!XHslI3*MA7lT;%a3Ag>Fu>;YIH*TVe2J9f{l#~M7=wet0d8JWUya*wgb5^ zQ zLufJ_HrZ18xqP2jTNLAbL2)noLw$=y+-PK8&zj~#tXT9h?!dt|H5~6@@M>eZqkHvR zW&3jvN5iCtyDi(!hb#Lt@S&>^a%&GKO{bOq=36GPW?i6cR3ue}$$quc->kNRfPbg= zw277h;fX?%Bn7CkE^>LTS|!V8fx4+$Lb;?T!+=DTbl?lK(XQ$ZhSS(QC24rf(D4Kv zWMwkHr8+Ul{bPQkrM%KP1mbfB?HfI1#l(~uJ-zdFMO2hZH7T&=I6&FwpcMFRUnW2J z77(Km8Il;2sNrvI3=flkV>%cwu0P6+<6)pX1+lvvsEQ4YBR4xpiajcgw$$Zv#L`B8 zl&v;n)QmEbD49m#$^en^Q<--OJ03kOym;&g^WD8LKAohYjsHl38m@=j)WareH-Bf1kAq$!u60oPGge{@!hyW zxHW#={3P-_Emx$EnGrva$I-KYhs(=q4UiD6AkE(5ZPb5ROeh_YK>v%f+-N}qq!7{E|AA*hhNN7sXlW6gpNsdW{rSuJ6@2YBvDM?{pem`c+1%mT zT0J6Iv)3*)A=Ac*kO%l7R}~oSCNZ8BeU{pTAn%F5ziCtE+Z@Pjx1c`x%7cHJp8Enn2>~H>6ldCh z$O=3mpPSv$4qtK<-bAZF#^2e!9qA`YE7BGaa900!`LqFjzUhDKgX_u1!f9ulg1XWM zDJp{5JGkegrTI=$#sm9P2*mhCwgjBFBXGC>lHlyQ`ayc;|NB+ps(W~zT^;;?-vciA z+Mb_Vpbux%5*~lYsO66Y39GFfn6$z%4ujVryc!yGIMQCxgm*#$6a~L27;rB^(pcD#FDPm>Z>BA5?C*Pm>DuikIn8Ans`-kty#cgW>@d%21rQ^*!~ ztpd?%j7-_M(&vNiiWER3u5sXr+tG)`Yv^u$uA5sIYZutMChkGyr}J8FvnSBc5xl+` zA*;&ff&G?St=Q^*bvVGrn8TlL@(b9Vnag#737)PFr@K7!7S72aulKjK7SIP0kWtjo z5p10rd}fP3SZT3i|| z{NUJpfvUUqc}%V}ts<5k_%5R<`gGRw%Yw@$JTQUH1hbV&S0(tt^gV9wgqE0`8JP7^ zZ8qKR{72(p=KGmWS2#(6iQV}b%_PC*RQ(S;*2=*)*L&3WA%)4ql~^5)O;=6$&+ix> z5sU(*E6p0ahyPIv5po(*MJQw;{+hf%GUBhw?zv>4nLoU@#OHV-^)9VN;F&5;8xK=o z=m>@+$bzw3aA*pTC8341c<&(GluCI-&v!&~w>m>8UT)?)KME#Ret*qqM95}^k?!>n zsB?uGEG`BZTA6XT$@U-HgxMP%;ycR5C){@2sA0d+_&#?R3SL$JtbvL|gy*6+mOqbZ zdGCn+@a0myX_cwg+HSCJcKJvLY2KS{HelK22n-J0GE7};icqrUPCc0e`QF`~kI24^ zrF^xr6LuqVMH`(0^Aa22?>p}g7$2KD03GZ(hzyS;&qwigV7ytsjmplo;&D2 zaM^oSNsvq*t%^j>q1j(r;4dC+P;k+^f!hXdApi9;Ar1$3*XI%w_(;!if1*NHR1aqi z+E7!~(Zt>>HcrN@?LU66&5oUA-ByV6m@H4_KNb`CErZCPTYsj55+(mAPpkqe#qsH1 zeL;GAvruxnHPJF07IU4ZL;w$Kc%=^}eg>cc!B-O7eJtx2BN7#^RUA$N1n&@dz(>ct z2^*t|aXeo)$CSqplqe7;Bp8^_gLrDtAOkGZr#v6fZWiH!;k6<-XH*D3IHF?lG>1MDKReKVrXN_Xr71NNIE+r_W8 zH~BV(ipsK;W676D+=wAMSIF(ndpbFs`3(Im%{rEy%4cA2rm0}ff_OzbG@Qg5wZk{9O*0ryo7dD zyOaDAOzSLGx!;vfj!DLecw#+Mrf!c9f4psave-OB+hhqv#gxGK((44ulx?qnWHk#l zkIk)dVRx8ufDyI3d+pJJfNHCZ)zSHF39_GnWwM^0(fILzrQ7o{p`xws(AHXn;Tq?x z=Qw)B`8iLUEzHd^wVE8hcvo26li`S_6#+gAGyDDx%7&nW+pXpqepY-#i(U=tTa6pR zuaFqym1g}f-D{>X&5j@Jfu{82(?c0lT-Cb(Vm3`?>&^Er^bd^Q-(2?gZ2ZRr2)5>R z*x9y0?w%S8oNH^EHSI+Rc=uZ2Hih#Znj&WBjveBvM#eyyyhwsSVYcID@VGO+FZ@2T zdUSuo<4@F4wo(WM5Woz&=bx8(c^zMS%li3UQ9n9N4@ANfX%xFg98rrLbYoocH=EOi z*|5HMx*9-W~FoxzU!Sf2)=|<28fK`0R~b`GQcq^rTUN&xP=#Jpk*`=K9~xFKBt) z{y6z^H=iSD;OP&a7KMOT|J}Yefa3o*VH3>+%x-TUg;Yea#&{FmBPvIHFIwOypE-J5 z9?5!_Tkv<;kNu_Vxi}f)Gflx1n*u5}uNlo7EQUB^SpFjNi97QTU2^}D`|H!36ori6 zEkzVb7K_F1uL?gk*YvCm;-5MI5}#YUlcI#Bx%ET_)>O0iGbu`5Ua2ZA;a1DQ_H_a* zESy-8@+>f$Ut6bj#_(7QsMLNC3Dw@;VSu1N)-#LGdC4t@(nMd)XB!0;PPq&oj`iQB zYZ=0y+yvb^4R1#xG6lT+80awTKCa9a&bRu2j~=t?Q8N*4nr;rB13K(J*2*zNXrmZ@ zz`_CJ^Uc>PvG^9N_8&FiL0@ieO503RMgVIdv=rAEMp+I*r1`hV+n6V2k?>LOEjQzKR|wC7K8fQcWSJo9H%qJF|-z z?ZtWM_FfjaO%`@mlZ(S&^n{;$ms_1C7sXlQL~NN@NEdm^G_Y6C-J=jx&A#t&LoPPP z+sia(+I5)|CDo*$j*P`3p9s!oq1LJRjSvEa-1D-v}W@K^zAvH~qIse4Up9Vd0^A0HX+mPOI||PdubjlrY`-IG#F+qWOpJps)=k{RFek*~Ylc$!2-f{Wkfq=A){=MpQd`xSR)zX$5CMydun& zayYD+@*yOkgBE0>hT~#&WdC2@XWPmVPqAlRfCF<#hxHIfjeE{jb)R`H!+-cYd?P4n-=Ja zM)k3D?#ieHicx2pHe-MCbyF1kX(sY{yM6saVKX3eb+>7a+i2AX6XQw~BQ63(8b`g- zGr>{~Z-SW&NE#waP*MW!4ht)19*~()EnD+@&sekvx<7ramg2UK7)dB;NRE5s}uy6 zM4mby3-`M9N4m*!EJ*imCIePuvfQL}B3BE<^D-6`&_MOz@advPsL+_ktAC=#?dw79!Vad+3??)pyez2Cd< zK( zLGe_Ke}^?pZX;)ybP@&FG7jpk755ottR_k^x|g_eUi0juzeN5l?TBZWh$Fx^vNTPn z#Q4o^JqT%C(&Dvas=P?C=SW|w&tU29S1GUt!~c@Z|DC?v2&tMm%;Oh1ul%N=NDA)g zzpXhGl|OA*!c0b8@rnF4thFflcj1vaLNipr7c)DLB~kGPP_kc(JbkwA%}4s|4=sl6r5oWo)9&>4xA-)`BTVeTC1FUX}RAS9SGjBWQox zjl5Sj*-UcoCMvBRyWg9|R=Y8{z)sM}$V?AbTXHlC6-QF-7hx@fMct99-%B( zcK2{V$k=(hg}8WoATuLyy-?#UQrdjg13(Wz8Hca!QUILa^FZ~Z+y1gW8)aq6&J-;W zDS>d4G_Htm1ZmB8nKFcMqospdbKiTQG)4a z>Cu$(W{Vc*OmaU%z2&#nX7}P3%{bELmY+gb;gHbK|H&5=91Cu}PNWe*6{adr$w$ff zJ7!e->6P+60I~;B`VE0Vg${81>rJBU=j#X2@d6op7GmMB(jY1v>*>v|i6FjRmuzQX zO^H;bd6#`>V`JOCuBNLwSWNiwOKg;QUZS3Y%KaOZ_K~+tiZbC#R)6@%1ameMOT#=B z0>aXeFH=y*{dtOIGEp%CJeetU{i3NF4*=(9laM{_Fv+0=toAdlvPEO5HrHk}DoXG8 zJU-PH>zow;!%%jMFEp95Y-#RA@#Ru=0jayJ5H+p!p@>h{a2JO<)y$Z+Nh!HeyhFz2 z_OAMg9^Xhu@#WQSEHDa(uo2bXqK)+zh@H3J>n@E9Z@Jmu5}Kl!-=-!BIyA8FNC;ve z-_5Gv>U8oabro!#u?asuN<}8bU=!d*e6JnB6Um$W?6Z}l)RU{hz&;>GUih<*Q$!<1 z7RY=$-4$~+=!%w)G3h>?V`fv}LsE=V7V*u$kf2&?0Td1sHHz}o%Xd&_DN*KG#-SV{ z1`uhtyL|0_=q_~HU8II1b0TTnhP`WFiIe+e1hTk2{&iR(TBQ$nzkcwhj8s(eb)fov z06BEd;9-eiZ);>^s{gL>wF0$7c;m73&4%A`hMBXP43U4<9&Jws5=96?L^Jk>pDG__ zbEG2W9FvVZh0%KRf37RQVarA~dm&+Np41UFuEm0rR`3Jh_xm%PZnn2ahe`ahJl5hK zG5c2$t%EiZSa^n|{QVX|ny%W~+UY_Sp{JfVm@q7`dhqU6D4OTAJeccQMnq9e2LhUy z>#rpKjDl!rXaZM9R0e=v*;4}i9rT%<4s)HdV=e1x3LEBS4Y8ppAQFW4UqjqM5gl++ zB0;Re{(f!ZlJE^k4*xC%CX~M~qzC5s-#3y#cp~Cy?TGhup^Zom%3)#uGeiT@*(-L3 z+{Pq6=K3kN;$6pn`2X{XjXyL9xct8djRd{cz_9!z4+OrV=>MnVNguxHe?R;8i=L+9 z{8ImG{7uh1qm+#ev8UY!G6jPQD*L*n zGI82P%$pF!sK}k>gWtZiySO#`i$PhmBa-z|tBCI@14LGd7n?FM+nKOnUc#-{oe-|K zJ8L7Q+N#x)N#zY%>DMkr-RyejyCSU7QoXs*p5||^`7}yzq*K~I!>vIp(}Rd+9fs)& zI<5f8U^j3m>{g6!Wu}I{7yZlag9%ErCeRvKMB%)|v)epMrvcXn5m=x3rNFcI6Gf{v z>PD}jS!7_Xp+CJtPkJ{ITy~VVzpr;D(APs_vp0QP$lG_k?8g?pR3nkZvh#91bhNhp z(fS)!Lh|#2V{jNb?2{@?LzU)QIOje-r~J8X^^!a)Sbm?~I%gA-U#cHao>t|GvZKe~ z>)@X(3CC*XIU?YkuN_*(FqZyjMy+S*v=Vkq{j2wi6DR{AwrhSm(88^(+T(?*z}0?e zF!$B?iwPZU3zyLg#hkxLKlfRIT^qgHqnx4~l?1KS+TVr_D1vl3Sg)9V00<{@3ksz9 z>5{z*jIBHcAP|DReWG_nBvM*Npq+F*HZC3*a@C}Ou_+mRd;*g+3!e0()IdV(y`S_F zmPVifWMn?|vfsuGjK38&b8?Z8&$eK3E=M^ptx604)7|K%#eD;Bay5!y;(DckqrHiw4PHwJ1_$N6kM!`r9&xf9V$@f4C8689=O>6!piHf=-wr1Nj z9guLYDK|AYUB5lT1bh;ox~o*Y1u`uSJtngq@*);8zzltwnsO-9AptxQXwxJI83nat zwi8+6y(-QzJqF zwq!`iuhP=!(p=6a&j*GV%+7!w$>7}5Kam*;C|D#byy!(W)7Y??qEf1IC^Grqihq7o zB;$5*dCC%&229s3lLI?R($$sc3(N~OU61nYDkha`HD!N^%}NNv>#3_@NceY}=p|RB zX=Q6!j)g$60{M{k+xJ&!(G`vkeYIaDYFyNofKDz7^NfBRrYg21`r{K*nxDBPXeE*| zLo%~5i$90Mw|NIN)}rmonp^G&^+^U}5@E(jMGx~M)SC`bU7MA-<9l+CRDGTvCP^?t7{*7X=c4#cZ2d7-g(WEuDv?6g*Hpv z1YuXe%E2RmyQha%0!?OG6^@8z$48Lma>0$x0Fd`e zt)P=o-G@zu*W!BO2W#teL02S@j|3I2{hCCnG#G_hB1FD#aDVH3u^ELzW~XeXh~8<= zzJI#wtv?jwgu~{OA*FbrlB%l2J9q}Q$3QzjeU_R*z;u})Z1GP0=nPK>JT?O@Xo75P zJR5tX%Dpx79Ngx0=VX_=k+qZJ$}t! zF3F@GRkRL=UHnN=WD0s})F4lUkSIspk7;GX*x3)<<>2PbXLsjIhS;wL2{xkYf2zGt z9Y;wP#-!`Df%W~XIKTI3&2{`L4V@g2669L3Zf0}cU_iImRs2zo0m5l7>fIDYH_3y4 z`D39_<@unM=q;~T8DoAdFi4S<*bAfue14lha!6`xYPDMqkpL+bsnpEAWeT4LpAGx( z?UFKhweV0i-0d8G$hALH*m^oIwUIxZ2^g^9sdQZB z!rC&ndX})KEzweZrJdR1^hL<R*^z zNOKSUAb-~o4PqyNeSxFbhmn@wq$cm8yaGJ^N_<5v$QVi1KQ;`tv(n6*U3qX=bYcv5 ztYk<~0s@EK>BuQ1dm9n#;QFZBvi+Ut+a%Txj-I_|NEmOax%LtVS((*EjQ-5oiDfBo zou`OKj*Iz;8O@Z?oBlPG*Kg7|N4I&ra3{XlJc=|@5JmencQ(P4#gplexB6Sh$OxqQ zDl$^$XYa5*tIFV`oRFW;Ut!txLHczwWRA*@mPxM@(*4-zbRQ_YEN7uWnNZ+#)P^B* z>ba%UOse|eLMK4kGVZBWZ-k4d@H~)H+RVT8esFdwIV>{V{QhTU1RGIS<&;4&wuLj6Jo>r?ZAU=NEqYtsZ&2mfe=lsUj%Mp@4DQZz$B6` z^z?q*sVBvl+p_F-Yl)YQy|b@aMrK_ zJH>g+g@2Hn!FU5$Fe!#CW-K9;!HRUXDN|rX=i7Q22IEZzdCyw%efQ(#>A6J7ZBxf; z+E~#-5kG0WJOw9}YW~|gW?iw{FD88MW65UCv6y!kZxDH!7@wY7vpDSC4O455J63g9 zi9OfH>RS;j01CV-$N=W-HoH|%&?_G2@n!p`A!>gn!sSqiC>s~M1SsZYKKM3B&^`Sw zS=G0a*!M1C;1AFC)NuUF89kCPQEhRgP2F+8<4$7W@ zCAQaTi|N407W`xDc1H-eRE($#8d}JxP8c*AO6)^Anxik{;r@&T^n~+a{Ai%$w%D|X zC(WuiQkJ%QMJmYMjtY+&6%DEVg6DPe;JB{*E*G!N{MG(X!~K}yZ``;jRM&F-!KXEh z4Uhh0VCAmw&APa+HJ2h?K3?1lwYY}A5sY=%_JH3?DmA%H?R^8Q$KSn#JF>ZGNSD$$ zk?nq_4apvYPW7tcVU4CXowVnfy8Opb{Dp{DA+{v$Msa&rCiR3-hX|58I-x~NB> zD&h^BNg|@Oxp~Cg4dDx?;j~RyW&lM9Z2>BT110orVuQm$ubr-wjd_W7tbFPyU>$Fn zG>yH9`(Nh!)<1JvE!GJ)nOzlYwIaO7E!)0;Bmlt&+`GL)iQ~Qzw(1&jeSH$frEkuA zEA8V937_-R&z3&|D<0T?Sg%k~t;Tb>pZgScH;dl`L^9u#+4=5EtsE*u1Y=GBZJb#(FA6Tz0_64k!My;Et`nU$)nMm!V%Yk3TAc`R z*AX7tM4mB(ifUR&6fnLFj=FBg3cx)7)I+GrWM3jJ-pV)*E=Fo9oWS)wl@5X=&gI z`cRpq)}P8ReA_`GVE|6G_6-lFRz?ElvR}f%9&`mItDk@c^-8=@t`d@z(L7al3yKKrhA0iMU`y#Ns>)D-#)Q!N)m5pBVB|4NV+MTr;~cR18&m=|sX>b|sx=-s+mJ z;&1AiYwc<6-mpTgZlS1!q<_L@*Wn4e#rFt<(zv`AZj z2$eJbelNF82K7UgCz;_|SOVjZ5=Z&vB2HqSiTYa5!@MO}bqKOK6#gk>F&()r- z(hT3LtE&6Q=izl_pmWEm)rJ%TLfBsU(OQFt9k53Ft8q(sWAvHAEsi*Xh!$m;=i+}y zff|&m>gowjHfpy168(HvpPQGuR0v$t`89^9O0yM8Vk@b-^&Ir7P1J#CA*;OUCeJ+A zeD7c%Z$Vxp_C&eRgE6s{!0ec$!sXk>Kn9WXgH;1%o6QOW!w#$NXaE3nwL4mhH2P$q z^kZP+;6}+a(6BHB{!MpO#kxO}3M!CILq_qH0HV`mMbfR8#LFJ(;JC!Z&lTku8au=n zy+taA*Z4Ez;~kT;ne$<6pd9hQzdt?~uNFZDY3r=737y%RLmM3772%c{jsOIvdVl~ID@Wp>CTFFQ14V1p!0d8``pJKgDx zsR}(T+mz4ywB+HfU?}=ciD4u}UE4RaIaq_F8CQ#J;B)xJQwrqIPiD6u$OYuQ-YSP; zYIodaoedxs|HgnD#xkR9G9GFU?u214xHpi6+o{fWP}*$Q!dbr^@_+boHLaGo@Ud)P zi(eSb%v@E)0Tb|(+Jp463~o1xLKC-unx?o!m$6<-);MR7Q(xF8F#(jCsA9<%_6JyNbVb&rT(>v0?AWsEd|8A(ckVQsn*b!7M(*I8G_V-35W zkYgC_v4BdkhOqP1)sKQgDzKa~N#Ww6<&N#DT1|dIK@N#ie`;HHrL4%s*YuIJl|1-+ zeuwgmG$2_Z$*qfFG5woIljN6FccW%MUm|9&%4m}#9`ts^q|B^ie0Z~zRRs&iAk3Tv zw#6C7;AdySufojsQX0MvjKV>6Z4ox0hs#=G3 zHp~`>CfYT6-w(IuQHs#&?3gv)Pw{g39#-wlz&4>1_pLDE&9C#M(Hhpv=*olwgF)3=zp$%eOLd*hmLqAkbUP&ypY22>m`5A3}dpQ1BqAqR2@V-D%AT%mP+j z1yZ%}mghp~>w7zLs;`)i&1*p5182=^*V(f}`i6{Z=zgh8({J(mKvB|x9I8lczBJjW zH9ya8G|4nQJ+seYNv)3%O$|hd)rfA@{U&efVn&@w`C2-huTfDyhlP(Oj|Ia?=p< z^Sz~w^I#H|%4bC~vewr4S{D=bt0i8YRnv*JMBy9nJ@|Y+pxP$dEg_AjLB80!Rr=`F zg&>Y1Kx=UkisE;>xn^l;tJgiP8JyDHJ%Pk2Y zGW)u6g94=V<{R5WQ-x?$w9eD}DV5R$3yhO^d^|>bHyBC@iF2=;yz!;g-_yS%3zC4a zpOjI3i{B<$#8D}U6j}ZLv;0g?t;pcLe;V{v1KH*BX^mlnVXh<^ zfJ7ui+i?dcYq)@TLvls{ZM9gjJ@V|gK|5wWYdRG(bFEfJ|KMmQDOKNwPCbS17%3FR z`fzX9f1tY3>09UN!j|~bFXL!|<<_h6h(z+~wPd6AZFU0!S} z1;fwKKmCo)6#QJROjyrhB@k<`M=wzJ3$miogX7K1* z{Pa~{0ORjm7c`nwyms+=Ps9>_Cxem0_(9LLseFC^SrEzJN&WtQ+wKo;Lj2FLtXocV z3^ISo998RTrH>%)>6F(?rLI5&OhWvQF30Y1`rQ6GS(&_x)WBvm(t%90lcTkkn6Mv4 z#*|bIPWwVA(iqaINzyvjZ@-#^T&(x0$75}N22jrOAg}T9>7Hz39CZ}=Q2^FWi}CT@ z42HV4+#FG}2t*9|d7Otne$#Q@3oKS&P=O>iH)s?pR3{xRtpoT2?x^~O_X_RIjy^EWCLYRmDkW_Gv!!2{+otM8X>xjYiJTsoGa7HDdSCj8WaV&4U8 zSn1q_q!RyrKd)x%)d!im7hz~3;otQy<83Y8PyTtqd{3Fe=4%f#>4KDNpho*=>z75~ zZ(kP!;1i(+Ux+{6m_sq`+?^|vVMlvE`a2xlKrZ2EBIpYx2Q@yh{{yb8|3xA^9LZ!R zFHREv4c7W|RQ^_ok)^Th(CErqt7t;Aw|YiGbNze5bvbccR3Pu)y007tg4Zbd;ZIPNK#DIc{j!x(M3d;B?z`6JD4WuvBCzM8-J3gN#*ro_V3hV8$ zA*=uj&_=`-*qye*-{vD6m)+STE#+`};h_;j6eXW(U`mHoRn6}S%7u?Bw7oY=UaxOe}Md6?x3mO)kx!ZXGkp1JH1cr5(|#IZ)0#6IV~qqfgTZo zPw_0qE&hL4gkKtzb(t2g2Gl<4G8r% z+5>py0he6>fI^y@Z9E>ai@H7UUQ>7U{3Nj9HHO$oGk2gt9#8<#f+_2w+87R;JA0f` zGu(Rh5JA4yDw3~=$MWP6tdZgS>$IWRy@}Q0wn-guN|QF*?pM)KA5Msey9L2|cZ!sBP z&8I~16aBkLu!X+e!uG7Gh#;ZrxkS0})Q+AfQ`Uc7NFU+ft{UZDrF>JBJkq z2M69OxvXn91ZtM%fHUjQOC-_~wf}8AX=Hc$ce(U&f|tYN&o^9b*PbVw2DHbxPG;*N z?in4IeO@abA}E;o9BgYJq1@N;0~1A~%J)W+<;4TvA2*|E;^5Js1vu_dYAQK-O9V7^xbsvJg$5)>l|0SJ(TlB{YyR-ud z_C!@?=aQ&)zIEJ2>$0uv@(24KQCfvpJ`r9lnUYZXN(*=QZh6o2qz(|D#j7@x#8J1L zMm#Qq43<0W-}ADI{Ih(Z3<F)ldAltLr2n#%b-a!*j z9fpr>tP}B{0LjRF`Ajrpl&TX4)6Ir1^C~z=zxI&yWyU6?QfiWX}~kTx`SAw zM|FwO#0YltR}s2oEHCTf8{F|8ypGbc_MyNqFTKH`@tLU+r3onHq8dESh8Dz(%^Y4s zBIiyyS&c-CHP#L0wrbb~xVQkN#p&O!xxw&XEa907n00bHeYpvY8Rw2r!9E9a3ojh|04u z^#Dwmm)A|xNoU#`dtw~i^*J#KFc^$WN<5vnFn3J-k68Q<^ZAdq3|SI#Q;9#_dc4fD zvMQW`|JboSXoqnV3V-J{0HuRJQ(;a!i$jF=Bite4iijg) z;fXkezOI{WnA8NF#a-U{!8%MNW)2MwZYSO*)D-fTQzHm@F@YmuiGD&TRP`8(3!c=8 zxbI;UEr4`W0B~GrGt<*9FGRiA&8b>bIqx4K^NmusuZLA{D1_N*51Po4+(3pDV|4p3 Zh@D8BJX2+fXdvMASxP~&Ox!5&{{ScQ&S(Gt literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/phaser-spectrogram.jpg b/plugins/zynaddsubfx/zynaddsubfx/doc/images/phaser-spectrogram.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d6aa804584e6f737814f42cb4c64c0eea983854d GIT binary patch literal 28409 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 literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope0.jpg b/plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b5b4593a956529b7e0123c04dbfe9f77b3965570 GIT binary patch literal 12081 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 VYB4yNt8<&zrcpLDXX0E<|1Z6t*M0y1 literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope1.jpg b/plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0db6fe7b3528a1bc2ebdacbcb24d1bdcce93d1db GIT binary patch literal 5700 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 W?-jua^Xe&iE`K5De^J8SkN*N!hmnN< literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope2.jpg b/plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c9006f173f8483c827706f81c57933c94a59e28b GIT binary patch literal 5342 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 V8QM+x-CopPitf~iO1a)H{twZg^h*E$ literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope3.jpg b/plugins/zynaddsubfx/zynaddsubfx/doc/images/uienvelope3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bba11d866e047c0af01c0a33becb04ecefbec176 GIT binary patch literal 6674 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 literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/uilfo.jpg b/plugins/zynaddsubfx/zynaddsubfx/doc/images/uilfo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b8f2c140499f8152ca863243a4ace3a4a6c0eaa2 GIT binary patch literal 6312 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´Y>=P(?+urH2A^OVe~LU20;zU5=!qq^X?Dt-Zjur zj<~r(8sme44ATe9WMX3VF#qP}>T0^>a*2Xa1Xy$b{$=Lnt^N69Y;LX%n+K4Y2wjhH zp`rFuaHJd`$H&D12m+W38Xss~B6J3z_U-q(MVw^F0Iyx4Y(!VhPdZ@ABDuDe4 z&=sUK0B%t9onY>{{`?q-j_&Dc17}-PtMnYCP z>HhcL=licY|69{npsBz`C@?w#s@je&`u-gbcyzkc6P+XWYq<@?CGhS>`xn53c%$LB zf^9svY;G4gp}gE&2*wfXG8mo?k(QT{0Tmq*ndB759(m0zX$%m*9Wty3h{b3xj?71f zU#4CAKQF*cINHbCu)6cEs;c*$*G&w$5#%lcW&#$GQ)!d0eVtsIF&c88WajV4R?cC` zzls%n*+1WorNfSiuwz{@;em0ki1sJx{hSChw4uYV>+9=ri5|Nh*}It*;RVg7J#P)2 zvF9a_#zG7j)9Oxh9=jaYot>~y5+FVt(Ge_Uk+P9Qm{`ZSgoqAMnhn;0W)ncM`>(lS8#P) z>y{p8%y`Tt$cF)NsLW7LVT>C_MS~!=lamcRJ!>V>p=N4od1H7ZARBN^78Dd{8!SW{ zkZ#Mr2e}*o>`Sm^0W@#M6a^a`Ov(bN!HL)J!9?imHvr*kQJd5W0>E6d9>38T%;SCE_J!RcyC zHLg2Q9%ja@s6+U@PF+Arg_)2SFOIwKZ;+9bZy$Leh_ARIPn`V{3|>Rq=WA68H+t0C z88Vc<{tena3p+co-}%db1f~Y8EZyA!nH?vY;4foRjK|QclvBpKZ#yAq!lDkf<&*_U z;c^EXCta99y_cth15ZNNC%JU!Z5kTH4GfZ%GCQ^3OQe3wtt-0Q@v7b$>k3lwG{1(q z*(s>)M1a=r_e3m3#y?qoU&CLY-Bp*XvWY_>Wv66~Iha4E|%+=H_Nd@!)+Z+Psxu zN2W0Z6Vt7&On2|9ib^6rI_zHC^SEC!0f(deBJ0*ZX&jINK+lKBz9i?Cq2K)T2OU{# zdLAr zhsudMx~Qhstqz|l32Cm2OBW&6m9F}D(!ijiM!!#}$B$dVFbR1tE+GN>1T1Xq@o{3> z7$VI6FjQ)d+p}5}8uEvR<}nw!PgBpy^{7imo^H479QZtN%>rIwCx@wb!B6FaUs66i zG^3_MOaD8sr}f9af&EDd*qEt3pVw3O-W+CTKHK>c{q|VjlP~3EzN*SGHX*hT4E!<* z0?d5%ikF%Q!8Gkn>0ZJ%X^WPSl>d@KKm?!>lyx!pee208C)k&e?xBYR4a^I6OE0Ru zq2VS7M8YsCcOy9mqlzwrr4anaFrx|$Js6TFfvr=@?1{a5n~zWPocGQhQx#LfN;L~M zTuf7YIMR?290{h9|NG11p*`-gmy!>uXnTboM7F=}Udn`C78n%Z*=hie{P*tZhW7-&58MVJ4PCo-<%TMN1tCE} z7z|K|lt-Bk-x_5Z6H zU=z_&Hnslz&)F52Xk0h~y5(m-x+}0kdDC_kLHkT769*omWCJc7EDQ0e6Od{QV6I9g&t5U4mZD#(11D$ zRu4Ea*qOH+u`iB?>j6Lj!3EAd7)|#EhX=HmuLPfgwG9l2k$3OkOK?g*dgQq`qa+Z=bvwBX2$sIcTbNUl=4L)qbL$IC#A5Fdw=o+uivo* z4hNKU_#$ZC0GZ2wVlON%hIZ_knp%I-{nrc(4A5JpV)w%>p9a_t)+BBoo{pX#>}+Nh z7F;|$bOd~Z=P?FeuHZF-<29S`2-r=rli>mUuyV0}@#3Gd!Zpf>jC)#jRl-tB}npc z1L4UDhi;P#DK(g|k_&58FE4LC$YJ0n9$pzPnEuap4gKF1FgEa{9-xol5}}%bUU2Ce zK*awELWJ$??k4hDMmSu^=J^=v+;sN^9<{{96Kup2?ffvl} zGDd=55(r`#Tm-`Q_4(=1wkw36OEnMh;)e(I7f^Eum7y;Me@+aC9zQ%M0tQN8X_Jy* zK~w^8mlM4-^oiNzBE(@#a0{+Ux5(u3Sv8mE4(%b33$8nV&o-NhnUp^Rc>-Q`Ok`wy z{9?P43(f%c25^o?;Q4|Fo}_T!_LPB;0|uSw@Kwq~c*KH#hz>YJ3=LIOQ=2%h36HHAoSsf@HxYH&M1otI{67O128RVuNq~dnL-+;$ z43`Sa0mKXVr73?H|NKA&XhQ64_u8+JF0;#%+H_gyi0bZK>SbZ zv*5mfJp{RQ0}{{W9`q>dxCgTZz-)t;5J5tuPnL5EE-!AW|Bivd|D*1`!?FJVzu{Ag zq9i1gJwhluipt(0npJfH z-}`oa{`j;w&-eKn&*x)3iQD24sVeVla^j1`VeauY9(2r1FP}s*Gc9eP=XGuEb>bVa zCYwwd+uB^%!qb)A{B5Ndi9a~QA17o{Ta8Mwsp(FmC7*+fA|tg2H=nSut62N#GuPEm zr}pfnRa9k)Z8l{JC)&^URoEEqmKfe1t~6_L<+IaP9nyn*mw!ra?!rPE^GNX zlmL!emH@XLyhDGX2f-l>8S2unUpUAdrnlD9ZYcyrl9|3^?SDO#!X4Fm&fKl8+UQ4K z(7<|JA^Tcaq#O?kp;(70+4R@Q>&4G3@deil_4t{k+_LiX`PPXp31uZEb9e+6$J?-N zacZ4urlO+qTKKdB&zmi#weyrc4(ay!g*>9vm3Q$h7;qE`c*Q)LNxd^q1zh(Bz%sU> zhhnAzRs)<~xD%2S6ZbXao9Q$^=qoTB85;6g>QYuvQx|p9(`c|$jNVlD&Jt8^?d>f` zMFOcj)B>o5SxK@I=Lb8+Ho77B9xi+wmwSn;N$cCMduGvQ+MX5gCdxGmq_t^5Z&WE@@x(T0}fPZ zv4L;M4L!=tjN%ezxCdUe)3zMAd2UY5wt6fph)eK^U^}U+scGw-q|%e+x%dM4h*)kuaN zS+kVqxfBsOe(%i30W-fMV<~(zyh1|eC6m+H{76@|Kj!2(K_SM z#W55V6olX#+>+ww9S6|yAb^dA6*mmzTNDIdHYQ6r9#IV6Ffizk;#A(mF(7Q!q@bvn zF-Qht6}{5Sn`@o(#8V^_ykEX!poipwqHq{?z?MHA5c7Tlc5^Q|1 z`kX>>tQ^U*?~EeoMEH)vBX*U*D^Rp=9V|~$6?*Vg{-EcLd;kq z++z|CJ?^BYt=)<47(stPZSV{>H~w(bs6q@4tv9+dIN%auW5q1r1S2HQ@l$nT!-el3 zszkZD6_*{za4}&|prE4uT!WhwZZnukaNNR#B_koRg5q1vA{U8Ru_71h{XrYy&2x*1 z^?vwp6V0kzhRdhw>r)JKxnj8I@ZEh-38AQkRt8-yq%63$u@EaD8E&-(h)796ffoJ` z44Nsasr!%U(opxkKVwMcZ1KWaJ>Hc0Ce0mYG3vfJd-f(eqH=uNgnZRo#=7| zlCB3}TfwBtZ9JCmTrh(hb~o+L?Ep-OiFc9B#7BSzJ=WV3adx~eq0m14`z)0*8JUg0c zna6lR!TtSv^P3fsAl2V5Y@MfAeuA_f<>Kehr(|T9_gLKx3ere9z7GJ{Tby#1R#wN{uwcmjax1zZpAK#(I9WAVs3#P{S>j)2P%DAXb}6TjiM5<36F`r^eBm#=b` zqk&ued(BKyCcUT;D`E_Hbw5ST)$9IdbHPho_ywsT=5vWhjz(6WY+)u1$4uE*}Yy zWO}5f{{8J2u6_T$OpY4tDl~nMk1&Y=rz5_Q&-W;PH#8mG5h%$+(rV*5V)rsj>j|6X z0>&&TuBr|eqS4TDzo>Y~dA%0ARN__S>xb<<- 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 ab*O*guzg*oxke3d6J*n-JD!s+iTxLmG0L+5 literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/uioscil.png b/plugins/zynaddsubfx/zynaddsubfx/doc/images/uioscil.png new file mode 100644 index 0000000000000000000000000000000000000000..2f891c05549070880669318777420efba143ce8b GIT binary patch literal 38211 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 literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/intro.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/intro.txt new file mode 100644 index 000000000..c3d3168b8 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/intro.txt @@ -0,0 +1,59 @@ +Getting Started +--------------- + +ZynAddSubFX is a fairly complex software synthesizer with a very large number of +controls. +As such, it is not alway obvious how to use ZynAddSubFX. + +Many applications under Linux transport MIDI over ALSA and transmit audio over +JACK. +ZynAddSubFX can be run in this configuration by running: + +------------------------------ +zynaddsubfx -I alsa -O jack -a +------------------------------ + +This sets the input driver to be alsa and the output driver to be jack, which +should attempt to autoconnect to your soundcard as per the '-a' flag. +If this is your first time running ZynAddSubFX, you will see a screen that lets +you choose between the advanced and beginner interface. +Currently the beginner interface is deprecated, so the advanced one is +recommended. + +Now you should be able to see ZynAddSubFX's main window, from which you can +setup patches, effects, and general configurations, but more importatnly it +provides links into the parameters of the patches. +ZynAddSubFX is a powerful tool with a number of base patches, but its true power +lies in the ability to make your own patches. + +.Main Window +image::./images/uimain.png[] + +For basic usage, you will want to use the button to the right of the enabled +label. +This button will allow for one to select the desired instrument from the banks +that ZynAddSubFX has available. +To play notes in ZynAddSubFX, either utilize the builtin virtual keyboard +(accessible via the vK button) or connect your keyboard to the system and use +*aconnect* to connect it to ZynAddSubFX (assuming that ALSA was used). + +This main window provides access to a number of more advanced features. +Some of these features are: + +* System Effects +* Insertion Effects +* Recording +* Part Settings (instrument level settings) +* Master Settings +* Microtonal Settings + +For instance to use the recording feature, a wave file must be selected from the +recording menu and then the recording can be started with the record button and +stopped with the stop button. +This is a simple and quick way of recording some samples from ZynAddSubFX, +though there are more full featured options available via JACK recording tools. + +NOTE: After hitting record, the wave file will not start recording until a new +key has been pressed via either an external midi source or the virtual keyboard +Both system and insertion effects can be accessed, the properties are available as well as properties of each +instrument. diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/lfo.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/lfo.txt new file mode 100644 index 000000000..082321d45 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/lfo.txt @@ -0,0 +1,65 @@ +LFO +--- +:author: Paul Nasca + +Introduction +~~~~~~~~~~~~ + +"LFO" means Low Frequency Oscillator. +These oscillators are not used to make sounds by themselves, but they changes +somes parameters (like the frequencies, the amplitudes or the filters). + +The LFOs has some basic parameters: + +* *Delay*: This parameter sets how much time takes since the start of the + note to the start of the LFO +* *Start Phase*: The possition that a LFO will start at +* *Frequency*: How fast the LFO is (i.e. how fast the parameter's controlled by + the LFO changes) +* *Depth*: The amplitude of the LFO (i.e. how much the parameter's controlled + by the LFO changes) + +image:images/lfo0.png[] + +Another important LFO parameter is the shape. +There are many LFO Types according to the shape. +ZynAddSubFX supports the folowing LFO shapes: + +image:images/lfo1.png[] + +Another parameter is the LFO Randomness. +It modifies the LFO amplitude or the LFO frequency at random. +In ZynAddSubFX you can choose how much the LFO frequency or LFO amplitude +changes by this parameter. +In the folowing images are shown some examples of randomness and how changes +the shape of a triangle LFO. + +image:images/lfo2.png[] + +Other parameters are: + +* *Continous mode*: If this mode is used, the LFO will not start from "zero" on each new note, but it will be continuous. This is very usefull if you apply on filters to make interesting sweeps. +* *Stretch*: It controlls how much the LFO frequency changes according to the +note's frequency. +It can vary from negative stretch (the LFO frequency is decreased on higher +notes) to zero (the LFO frequency will be the same on all notes) to positive +stretch (the LFO frequency will be increased on higher notes). + +User Interface +~~~~~~~~~~~~~~ + +In ZynAddSubFX, LFO parameters are shown as: + +image:images/uilfo.jpg[] + +Theese parameters are: + +* *Freq*: LFO Frequency +* *Depth*: LFO Depth +* *Start*: LFO Start Phase - +If this knob is at the lowest value, the LFO Start Phase will be random. +* *Delay*: LFO Delay +* *A.R.*: LFO Amplitude Randomnes +* *F.R.*: LFO Frequency Randomness +* *C.*: LFO Continous Mode +* *Str.*: LFO Stretch - in the image above the LFO stretch is set to zero diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/mididefaults.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/mididefaults.txt new file mode 100644 index 000000000..c8f33c4f4 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/mididefaults.txt @@ -0,0 +1,22 @@ +Appendex A: MIDI Defaults +------------------------- + +.Default MIDI Connections +[literal] +001 - Modulation Wheel +007 - Volume +010 - Pan +011 - Expression +064 - Sustain +065 - Portamento Enable +071 - Filter Q +074 - Filter Cutoff +075 - Bandwidth(*) +076 - Modulation Amplitude(*) +077 - Resonance Center Frequency(*) +078 - Resonance Bandwidth(*) +120 - All Sounds Off +121 - Reset All Controllers +123 - All Notes Off + +The entries with `(*)` are not within the General Midi specification diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/nrpn.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/nrpn.txt new file mode 100644 index 000000000..94339ade6 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/nrpn.txt @@ -0,0 +1,162 @@ +NRPN (Non Registered Parameters Number) +--------------------------------------- + +NRPNs can control all system and insertion effect parameters. +For example, you may change the reverb time when playing to keyboard or +flanger's lfo frequency. +You can disable the NRPN receiving by deselecting the "NRPN" checkbox from the +main window (near "Master Keyshift" counter). +The controls can be sent on any midi channel +(the midi channels numbers are ignored). + +The parameters are: + +- NRPN coarse (99 or 0x63)sets the system/insertion effects (4 for system effects or 8 for insertion effects) +- NRPN fine (98 or 0x62)sets the number of the effect (first effect is 0) +- Data entry coarse (6) sets the parameter number of effect to change(see below) +- Data entry fine (26) sets the parameter of the effect + +You have to send NRPN coarse/fine before sending Data entry coarse/fine. +If the effect/parameter doesn't exists or is set to none, then the NRPN is +ignored. + +Example(all values in this example are hex): + + B0 63 08 // Select the insertion effects + B0 62 01 // Select the second effect (remember: the first is 00 and not 01) + B0 06 00 // Select the effect parameter 00 + B0 26 7F // Change the parameter of effect to the value 7F (127) + +WARNING: Changing of some of the effect parameters produces clicks when sounds +passes thru these effects. +I advise you to change only when the sound volume that passes thru the effect to be very low (or silence). +Some parameters produce clicks when are changed very fast. + +Here are the effects parameter number (for Data entry coarse). +The parameters that produces clicks are written in [red]#red# and have (AC) +after their entry (always clicks). +The parameter that produces clicks only when they are changed fast are written +in [orange]#orange# and have a (FC) after the entry (Fast Clicks). +Most parameters has the range from 0 to 127. +When parameters have another range, it is written as [low...high] . + +Reverb +~~~~~~ + +[verse] ++[orange]#00 - Volume or Dry/Wet (FC)# +[orange]#01 - Pan (FC)# +02 - Reverb Time +[orange]#03 - Initial Delay (FC)# +04 - Initial Delay Feedback +[grey]#05 - reserved# +[grey]#06 - reserved# +07 - Low Pass +08 - High Pass +09 - High Frequency Damping [64..127] 64=no damping +[red]#10 - Reverb Type [0..1] 0 - Random, 1 - Freeverb (AC)# +[red]#11 - Room Size (AC)#+ + +Echo +~~~~ + +[verse] ++[orange]#00 - Volume or Dry/Wet (FC)# +[orange]#01 - Pan (FC)# +[red]#02 - Delay (AC)# +[red]#03 - Delay between left and right (AC)# +[orange]#04 - Left/Right Crossing (FC)# +05 - Feedback +06 - High Frequency Damp+ + + +Chorus +~~~~~~ + +[verse] ++[orange]#00 - Volume or Dry/Wet (FC)# +[orange]#01 - Pan (FC)# +02 - LFO Frequency +03 - LFO Randomness +04 - LFO Type [0..1] +05 - LFO Stereo Difference +06 - LFO Depth +07 - Delay +08 - Feedback +[orange]#09 - Left/Right Crossing (FC)# +[grey]#10 - reserved# +[red]#11 - Mode [0..1] (0=add, 1=subtract) (AC)#+ + +Phaser +~~~~~~ + +[verse] ++[orange]#00 - Volume or Dry/Wet (FC)# +[orange]#01 - Pan (FC)# +02 - LFO Frequency +03 - LFO Randomness +04 - LFO Type [0..1] +05 - LFO Stereo Difference +06 - LFO Depth +07 - Feedback +[red]#08 - Number of stages [0..11] (AC)# +[orange]#09 - Let/Right Crossing (FC)# +[red]#10 - Mode [0..1] (0=add, 1=subtract) (AC)# +11 - Phase+ + +AlienWah +~~~~~~~~ + +[verse] ++[orange]#00 - Volume or Dry/Wet (FC)# +[orange]#01 - Pan (FC)# +02 - LFO Frequency +03 - LFO Randomness +04 - LFO Type [0..1] +05 - LFO Stereo Difference +06 - LFO Depth +07 - Feedback +08 - Delay [0..100] +[orange]#09 - Left/Right Crossing (FC)# +10 - Phase+ + +Distorsion +~~~~~~~~~~ + +[verse] ++[orange]#00 - Volume or Dry/Wet (FC)# +[orange]#01 - Pan (FC)# +02 - Left/Right Crossing +[orange]#03 - Drive (FC)# +[orange]#04 - Level (FC)# +05 - Type [0..11] +06 - Invert the signal (negate) [0..1] +07 - Low Pass +08 - High Pass +09 - Mode [0.1] (0=mono,1=stereo)+ + +EQ +~~ + +[verse] ++[orange]#00 - Gain (FC)#+ + +All other settings of the EQ are shown in a different way. +The N represent the band ("B." setting in the UI) and the first band is 0 +(and not 1), like it is shown in the UI. +Change the "N" with the band you like. +If you want to change a band that doesn't exist, the NRPN will be ignored. + +[verse] ++[red]#10+N*5 - Change the mode of the filter [0..9] (AC)# +11+N*5 - Band's filter frequency +12+N*5 - Band's filter gain +13+N*5 - Band's filter Q (bandwidth or resonance) +[grey]#14+N*5 - reserved#+ + +Example of setting the gain on the second band: + +. The bands start counting from 0, so the second band is 1 => N=1. +. The formula is 12+N*5 => 12+1*5=17, so the number of effect parameter +. (for Data entry coarse) is 17. + diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/saving.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/saving.txt new file mode 100644 index 000000000..a45b6631d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/saving.txt @@ -0,0 +1,55 @@ +Persistence +----------- + +As with most applications ZynAddSubFX allows for one to ave your work and reload +it. + +Saving it all +~~~~~~~~~~~~~ + +One of the simplest ways to save your work is to save the entire session. +This can be done through the File menu and will result in the creation of an +.xmz file. +Once created, this file will hold the settings for all settings within that +session, such as microtonal tunings, all patches, system effects, insertion +effects, etc... + +Saving Parts +~~~~~~~~~~~~ + +In many cases saving everything is not what is desired. +Saving a patch later on is one such example. + +Patches +^^^^^^^ +In order to save a patch, one can either save it from the instruments menu or +through the bank window. + +With the instrument menu, one can just save the file to any given location with +the .xiz extension. + +With the banks menu, one can assign a patch to a given slot with a bank. +This instrument will remain here for future use until it is deleted. +To see the physical location of the .xiz file, one should check the +File->Settings->Bank_Root_Dirs window to see the paths for banks. + +NOTE: You need to have write permissions to add instruments to the bank. + +Presets +^^^^^^^ +Have a favorite setting for an envelope, a difficult to reproduce oscillator? +Then presets are for you. +Presets allow for one to save the settings for any of the components which +support copy/paste operations. +This is done with preset files (.xpz), which get stored in the folders indicated +by File->Settings->Preset_Root_Dirs. + +Summary +~~~~~~~ + +.Extension Summary +[literal] +xmz Everything +xiz Instrument +xsz Scale Settings +xpz Presets diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/zynaddsubfx.1.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/zynaddsubfx.1.txt new file mode 100644 index 000000000..854476cf5 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/zynaddsubfx.1.txt @@ -0,0 +1,82 @@ +ZYNADDSUBFX(1) +============== +:doctype: manpage + +NAME +---- +zynaddsubfx - a software synthesizer + +SYNOPSIS +-------- +*zynaddsubfx* ['OPTIONS'] + +DESCRIPTION +----------- + +*zynaddsubfx* is a polyphonic multimbral synthesizer, which supports three +synthesis engines and numerous effects to generate sound. + +1) ADsynth generates sounds by adding a number of voices. + Each voice has filters, envelopes, LFOs, morphing, modulation + (Ring Modulation, Phase Modulation... the modulators can have any + shape), resonance, etc... + Each voice includes a waveform generator with up to 128 sine/non-sine + harmonics. + You can use Fourier synthesis or if you don't like it you + can wave-shaping/filtering of functions. + +2) SUBsynth is a simple engine which makes sounds through + harmonic filtering of white noise + +3) PADsynth is an engine that makes very beautiful pads and other instruments, + which can be exported for use with other programs. + +Midi and audio support exists for OSS, ALSA, JACK, and others... + +OPTIONS +------- + +*-h, --help*:: + Display command-line help and exit +*-v, --version*:: + Display version and exit +*-l, --load=FILE*:: + Loads a .xmz file +*-L, --load-instrument*=FILE:: + Loads a .xiz file +*-r --sample-rate*=SR:: + Set the sample rate SR +*-b, --buffer-size*=SR:: + Set the buffer size, which determines the granularity of how often parameter + changes can be applied +*-o, --oscil-size*=OS:: + Set the ADsynth oscillator size +*-S, --swap*:: + Swap Left and Right output channels +*-D, --dump*:: + Dumps midi note ON/OFF commands +*-U, --no-gui*:: + Run ZynAddSubFX without user interface +*-N, --named*=Name:: + Postfix IO Name when possible +*-a, --auto-connect*:: + AutoConnect when using JACK +*-O, --output*=engine:: + Set Output Engine +*-I, --input*=engine:: + Set Input Engine +*-e, --exec-after-init*=command:: + Run post-initialization script. This script will be run after midi and audio + drivers have been initialized. + +BUGS +---- +Please report any bugs to either the mailing list + zynaddsubfx-user@lists.sourceforge.net +or the bugtracker + http://sourceforge.net/tracker/?group_id=62934 + +AUTHOR +------ +ZynAddSubFX was originally written by Nasca Octavian Paul. It is currently being +maintained by Mark McCurry. diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/zynaddsubfx.txt b/plugins/zynaddsubfx/zynaddsubfx/doc/zynaddsubfx.txt new file mode 100644 index 000000000..26cd2dd21 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/doc/zynaddsubfx.txt @@ -0,0 +1,31 @@ +Zynaddsubfx +=========== +:Author: Paul Nasca and Mark McCurry + +This documentation is a work in progress + +include::intro.txt[] + +include::filter.txt[] + +include::lfo.txt[] + +include::envelope.txt[] + +include::adsynth.txt[] + +include::controller.txt[] + +include::effects.txt[] + +///////////////////////////// +include::nrpn.txt[] +///////////////////////////// + +include::saving.txt[] + +include::mididefaults.txt[] + +include::build.txt[] + +include::getting.txt[] diff --git a/plugins/zynaddsubfx/zynaddsubfx/pixmaps/black_key.png b/plugins/zynaddsubfx/zynaddsubfx/pixmaps/black_key.png new file mode 100644 index 0000000000000000000000000000000000000000..d90b6097f0abe5d3845aac8aaea24b5a518e958f GIT binary patch literal 892 zcmV-?1B3jDP)~zjtpUig41m#S z#PRVl>+${C{x|~)z-p~IKR@T>;k^51G&Blx0a(RRVOt2e8P16%eeolx4}~h}pD(C_zg&ZR&PR2rxOoO9gW-BDE)#u#FZXsvm9dEx2liO(aGm%6Tb ze0*d&oid-#F~*>^CeL#gi$ymOU^biKy>AwZF*fs7kIp%iQW#@4=t^zV(flHRDy0C3 zF}8a80!+a&WRab~x~{(%ba~Y&NQ^!kw}Ev^OQ;!RHfXjn*(gT{fx52o-Z$~?EQe6? z-Zu*(UCDPoL-5C|bO4EFLUhAGf{zdJ^y6yAHh_s#oW$x%uX zLTLI=;@c~$LRDh!(yach4k<^B5$9afW?QxDoNJ6j2+b_X>Xe*#PC;Ge&|S(Aa9503 z3KDP5IkeU|=UP?M+8vTrdG?~3YBdFJEk_z-`s~bVD!G(umsY<3(-@`rTVoW72~e`U zmsV46qN1JUkWI>a&uX>8drwtW`^A`2{oj%kV7k}r^k|G}5+mEBeD5UyX))=~ojZ97 z6t(P>oEQioop&auZHSsWf!5mAL~NycaBx7L=dBx))X`eIaqwoJkL|X2+PdV+iM(2^ zHbCBcj4=#{!{%bx07cj_#^z=v7e8xlcXy;4)nqbZHk+|rE}6|{-`{pE7W?d>fw#zqVP&(F{FdOfbLuDHLyKX`k4%K(#olc@PaYu$TzcyQTz0K*W% zk1@u-hQs0CgTWwbt^Wl60sd9MPk;g}FaW*-a^MHx7vQ(`mD&JWDfJvGi zXE-AQtbsq$H1#((H$IMI542;kSoq~~8RowM-+;9OE`iv4&*kMMMNtq%5n5}Ub3{=@ z(=;4yeq8@9fCZodmdhn^9HX^HYmHKhG)>W32Ve^1z(N2e&}gl>zP@I=-BJ_O zttrd03v7T|0S>?z!|UrS_xJbQ-QA&-!a0Yvmd$2E-&8Bte9qI;6E80>6h+YrlBOw1 zlJNZed;%10)W%?~#e0uZANvU*+R?Z6jkT8DZig|3rfG1_VT{2!_ero5pp=p*irO({ zCmi~e#x^)vIYh~OKMr)xwTt?Thrp^!D^LsZhWA5ERCi*n#W_dQG;O@bm~oHZ`}U-S z4WcM&e~(i2-qSP<#u%iOc<<3#532h~)xJR9d#ttN9sv%A19e@u9)%EJ1D0jU`};du zYnrBMLpTex)>0G&o6UyZZbyoQ^n-F~+dp?}?&_s;UMRJrPF;L6RhG zT0&|@I4%|Nvh{Oo)t$*Ns=UOJ3QBgc^E^2^lj?(w{I0c*>$)~l zN+pC)z=gH;XYc)ANs|0Mo6Wos;!ofo;J*m?1&EH9-JgIAm;n#KufXreE42hf6h&{| b`+xrjyzalt+Gxds00000NkvXXu0mjfFYumh literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/pixmaps/knob.png b/plugins/zynaddsubfx/zynaddsubfx/pixmaps/knob.png new file mode 100644 index 0000000000000000000000000000000000000000..93f2cbe0fb7138825c7be2d8bde41d4976078293 GIT binary patch literal 26619 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 X;OJI|Lhz{1XD7Zrb+p3w{CEEY=R{Uu literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/pixmaps/module_backdrop.png b/plugins/zynaddsubfx/zynaddsubfx/pixmaps/module_backdrop.png new file mode 100644 index 0000000000000000000000000000000000000000..2cba79553e95f05498d4d8ea61bfeb85f718fd33 GIT binary patch literal 1779 zcmV>vz8F_0By|0^3m?niD@$0s6Fm5RrMd8yTE2_N6z-%tDh>Akn>x>{?k z9mlcmJ;#>uSewtoysR&-1+c%>9hV_UR|>*k6C1 zr(M_8?)z@{eZL;M@4KDnxvukjCYjFMwzam}vkfPoNv6+y&t_&`nRvBXc|Lu2uapZ( z2iJAII`KksFTZo*nJ6dHdoL%(4|kmltSpkh$&P#P3r2+0GUNZ>#~AHA&tDKs8xEBA z&-1i7XCtIJX-<-GPrJ;8n-^H-{%o5MA(8LrdA2de3)`_^2F)bAlk~aDU(yeKEzf2= zwrQ=kF~$oGJ^n)6ax#Hfb|Zd4XUugLlKwK;jKOxh_PK7Ywevh*zLka3Pt(tF9Bqy< zUd}`ea}kwqcyNBF&vBRk@(uPu=o%?RM4rE{>j!38F;`9eKhLvppFf&7gWK$+cYb!1 z&X7PqA}p(o$urZA?VUFKNDhk<>_eln^`Ksv40LlgI0$kwx`FviJs@RM3sH{TfBRi(g$sM=T zlk+QBFkn>}rcV*Vgti%qaW6Y=UnA5B(P$Elp$ z4Cmn9=^NHa_kFMb)?0K~2SsW!YB~O|ESdZ1gEUY7;O{RruzM)0W)!C1oab3G76)Y1 zrjJq*o#ryi@TKleV1M zg_9<?H4rsx( zYgtUXOq;ppV&|MC5mZEx@t6~FvwQD9s+mfCADReCB zvaOxGA^}UISm~wzs@if@<*as`k)I63g%Vay0IO=wuIOi&c0*0wDqxEq^{+<)K1b=wXcf0dCemIuGCc3x9uB_YxzE1f%ncEqm_eJ zNf?7F6}6KrXR;w9O8-FGg~VSec;Z0C@1#eXma)UT<@WT0F-BQj_cN}<7=9*|RZDOX z=fog$eqC6D^j5E61;rv{klp z6`on5Ukfq+JJ9YpE2uWb{#Mlk ze8R^5pXd4UnJUp?Cg~$WpA=Zx?vgaW4OIzBTeect)oU(ej31c!Or@&SJ6Cr#vU(qe zlQX&4$t!VN9mM{1Y|{Mz9nur23V9D(dhFfUYf!blf#FF-mE9r;h{{w21 Vtu@B4t;hfX002ovPDHLkV1h6Pa^nC1 literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/pixmaps/white_key.png b/plugins/zynaddsubfx/zynaddsubfx/pixmaps/white_key.png new file mode 100644 index 0000000000000000000000000000000000000000..e9125f290546e8a37be84d1e97020b98d99611fe GIT binary patch literal 504 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 +set(FLTK_SKIP_OPENGL true) +pkg_check_modules(NTK ntk) +pkg_check_modules(NTK_IMAGES ntk_images) +find_package(FLTK) +find_package(OpenGL) #for FLTK +find_package(CxxTest) +if(CXXTEST_FOUND) + set(CXXTEST_USE_PYTHON TRUE) +endif() +# lash +pkg_search_module(LASH lash-1.0) +mark_as_advanced(LASH_LIBRARIES) +pkg_search_module(DSSI dssi>=0.9.0) +mark_as_advanced(DSSI_LIBRARIES) +pkg_search_module(LIBLO liblo>=0.26) +mark_as_advanced(LIBLO_LIBRARIES) + +CHECK_FUNCTION_EXISTS(sched_setscheduler HAVE_SCHEDULER) + +execute_process(COMMAND echo fistpl 0 + COMMAND as - + ERROR_VARIABLE AVOID_ASM) + +######### Settings ########### +# NOTE: These cache variables should normally not be changed in this +# file, but either in in CMakeCache.txt before compile, or by passing +# parameters directly into cmake using the -D flag. +SET (GuiModule fltk CACHE STRING "GUI module, either fltk, ntk or off") +SET (CompileTests ${CXXTEST_FOUND} CACHE BOOL "whether tests should be compiled in or not") +SET (AlsaEnable ${ALSA_FOUND} CACHE BOOL + "Enable support for Advanced Linux Sound Architecture") +SET (JackEnable ${JACK_FOUND} CACHE BOOL + "Enable support for JACK Audio Connection toolKit") +SET (OssEnable ${OSS_FOUND} CACHE BOOL + "Enable support for Open Sound System") +SET (PaEnable ${PORTAUDIO_FOUND} CACHE BOOL + "Enable support for Port Audio System") +SET (LashEnable ${LASH_FOUND} CACHE BOOL + "Enable LASH Audio Session Handler") +SET (DssiEnable ${DSSI_FOUND} CACHE BOOL + "Enable DSSI Plugin compilation") +SET (LibloEnable ${LIBLO_FOUND} CACHE BOOL + "Enable Liblo") + +# Now, handle the incoming settings and set define flags/variables based +# on this + +# Add version information +add_definitions(-DVERSION="${VERSION}") + +message(STATUS "Building on a '${CMAKE_SYSTEM_NAME}' System") + +if(NOT "Darwin" STREQUAL ${CMAKE_SYSTEM_NAME}) + # Add scheduler function existance info (OSX compatiability) + add_definitions(-DHAVE_SCHEDULER=${HAVE_SCHEDULER}) +endif() + + +# Give a good guess on the best Input/Output default backends +if (JackEnable) + SET (DefaultOutput jack CACHE STRING + "Default Output module: [null, alsa, oss, jack, portaudio]") + # Override with perhaps more helpful midi backends + if (AlsaEnable) + SET (DefaultInput alsa CACHE STRING + "Default Input module: [null, alsa, oss, jack]") + elseif (OssEnable) + SET (DefaultInput oss CACHE STRING + "Default Input module: [null, alsa, oss, jack]") + else () + SET (DefaultInput jack CACHE STRING + "Default Input module: [null, alsa, oss, jack]") + endif () +elseif (AlsaEnable) + SET (DefaultOutput alsa CACHE STRING + "Default Output module: [null, alsa, oss, jack, portaudio]") + SET (DefaultInput alsa CACHE STRING + "Default Input module: [null, alsa, oss, jack]") +elseif (OssEnable) + SET (DefaultOutput oss CACHE STRING + "Default Output module: [null, alsa, oss, jack, portaudio]") + SET (DefaultInput oss CACHE STRING + "Default Input module: [null, alsa, oss, jack]") +else() + SET (DefaultOutput null CACHE STRING + "Default Output module: [null, alsa, oss, jack, portaudio]") + SET (DefaultInput null CACHE STRING + "Default Input module: [null, alsa, oss, jack]") +endif() + + + +if (GuiModule STREQUAL qt AND QT_FOUND) + set (QtGui TRUE) +elseif(GuiModule STREQUAL ntk AND NTK_FOUND) + set (NtkGui TRUE) +elseif(GuiModule STREQUAL fltk AND FLTK_FOUND) + set (FltkGui TRUE) +elseif(GuiModule STREQUAL off) + add_definitions(-DDISABLE_GUI) +else () + set (GuiModule off CACHE STRING "GUI module, either fltk, qt or off") + add_definitions(-DDISABLE_GUI) + message(STATUS "GUI module defaulting to off") +endif() + + +#Build Flags +option (BuildForAMD_X86_64 "Build for AMD x86_64 system" OFF) +option (BuildForCore2_X86_64 "Build for Intel Core2 x86_64 system" OFF) +option (BuildForDebug "Include gdb debugging support" OFF) + +set(CMAKE_BUILD_TYPE "Release") + +set (BuildOptions_x86_64AMD + "-O3 -march=athlon64 -m64 -Wall -ffast-math -fno-finite-math-only -fomit-frame-pointer" + CACHE STRING "X86_64 compiler options" +) + +set (BuildOptions_X86_64Core2 + "-O3 -march=core2 -m64 -Wall -ffast-math -fno-finite-math-only -fomit-frame-pointer" + CACHE STRING "X86_64 compiler options" +) + +set (BuildOptionsBasic + "-O3 -msse -msse2 -mfpmath=sse -ffast-math -fomit-frame-pointer" + CACHE STRING "basic X86 complier options" +) + +set (BuildOptionsDebug + "-O0 -g3 -ggdb -Wall -Wpointer-arith" CACHE STRING "Debug build flags") + +########### Settings dependant code ########### +# From here on, the setting variables have been prepared so concentrate +# on the actual compiling. + +if(AlsaEnable) + list(APPEND AUDIO_LIBRARIES ${ASOUND_LIBRARY}) + list(APPEND AUDIO_LIBRARY_DIRS ${ASOUND_LIBRARY_DIRS}) + add_definitions(-DALSA=1) +endif(AlsaEnable) + +if(JackEnable) + list(APPEND AUDIO_LIBRARIES ${JACK_LIBRARIES}) + list(APPEND AUDIO_LIBRARY_DIRS ${JACK_LIBRARY_DIRS}) + add_definitions(-DJACK=1) +endif(JackEnable) + +if(OssEnable) + add_definitions(-DOSS=1) +endif(OssEnable) + +if(PaEnable) + include_directories(${PORTAUDIO_INCLUDE_DIR}) + add_definitions(-DPORTAUDIO=1) + list(APPEND AUDIO_LIBRARIES ${PORTAUDIO_LIBRARIES}) + list(APPEND AUDIO_LIBRARY_DIRS ${PORTAUDIO_LIBRARY_DIRS}) +endif() + +if (CompileTests) + ENABLE_TESTING() +endif() + +if(LashEnable) + include_directories(${LASH_INCLUDE_DIRS}) + add_definitions(-DLASH=1) + list(APPEND AUDIO_LIBRARIES ${LASH_LIBRARIES}) + list(APPEND AUDIO_LIBRARY_DIRS ${LASH_LIBRARY_DIRS}) + message(STATUS "Compiling with lash") +endif() +if(LibloEnable) + include_directories(${LIBLO_INCLUDE_DIRS}) + add_definitions(-DUSE_NSM=1) + list(APPEND AUDIO_LIBRARIES ${LIBLO_LIBRARIES}) + list(APPEND AUDIO_LIBRARY_DIRS ${LIBLO_LIBRARY_DIRS}) + message(STATUS "Compiling with liblo") +endif() + +# other include directories +include_directories(${ZLIB_INCLUDE_DIRS} ${MXML_INCLUDE_DIRS}) + +add_definitions( + -g #TODO #todo put in a better location + -Wall + -Wextra + ) +if(NOT AVOID_ASM) + message(STATUS "Compiling with x86 opcode support") + add_definitions(-DASM_F2I_YES) +endif() + +if (BuildForDebug) + set (CMAKE_BUILD_TYPE "Debug") + set (CMAKE_CXX_FLAGS_DEBUG ${BuildOptionsDebug}) + message (STATUS "Building for ${CMAKE_BUILD_TYPE}, flags: ${CMAKE_CXX_FLAGS_DEBUG}") +else (BuildForDebug) + set (CMAKE_BUILD_TYPE "Release") + if (BuildForAMD_X86_64) + set (CMAKE_CXX_FLAGS_RELEASE ${BuildOptions_x86_64AMD}) + else (BuildForAMD_X86_64) + if (BuildForCore2_X86_64) + set (CMAKE_CXX_FLAGS_RELEASE ${BuildOptions_X86_64Core2}) + else (BuildForCore2_X86_64) + set (CMAKE_CXX_FLAGS_RELEASE ${BuildOptionsBasic}) + endif (BuildForCore2_X86_64) + endif (BuildForAMD_X86_64) + message (STATUS "Building for ${CMAKE_BUILD_TYPE}, flags: ${CMAKE_CXX_FLAGS_RELEASE}") +endif (BuildForDebug) + +add_definitions(-fPIC) + +if(FLTK_FOUND) + mark_as_advanced(FORCE FLTK_BASE_LIBRARY) + mark_as_advanced(FORCE FLTK_CONFIG_SCRIPT) + mark_as_advanced(FORCE FLTK_DIR) + mark_as_advanced(FORCE FLTK_FLUID_EXECUTABLE) + mark_as_advanced(FORCE FLTK_FORMS_LIBRARY) + mark_as_advanced(FORCE FLTK_GL_LIBRARY) + mark_as_advanced(FORCE FLTK_IMAGES_LIBRARY) + mark_as_advanced(FORCE FLTK_INCLUDE_DIR) + mark_as_advanced(FORCE FLTK_MATH_LIBRARY) +endif(FLTK_FOUND) + +if(NTK_FOUND) + mark_as_advanced(FORCE NTK_BASE_LIBRARY) + mark_as_advanced(FORCE NTK_CONFIG_SCRIPT) + mark_as_advanced(FORCE NTK_DIR) + mark_as_advanced(FORCE FLTK_FLUID_EXECUTABLE) + mark_as_advanced(FORCE NTK_FORMS_LIBRARY) + mark_as_advanced(FORCE NTK_GL_LIBRARY) + mark_as_advanced(FORCE NTK_IMAGES_LIBRARY) + mark_as_advanced(FORCE NTK_INCLUDE_DIR) + mark_as_advanced(FORCE NTK_MATH_LIBRARY) +endif(NTK_FOUND) + +if(FltkGui) + #UGLY WORKAROUND + find_program (FLTK_CONFIG fltk-config) + if (FLTK_CONFIG) + execute_process (COMMAND ${FLTK_CONFIG} --use-images --ldflags OUTPUT_VARIABLE FLTK_LDFLAGS) + string(STRIP ${FLTK_LDFLAGS} FLTK_LIBRARIES) + endif() + + message(STATUS ${FLTK_LDFLAGS}) + + + set(GUI_LIBRARIES ${FLTK_LIBRARIES} ${FLTK_LIBRARIES} ${OPENGL_LIBRARIES} zynaddsubfx_gui) + + add_definitions(-DFLTK_GUI) + message(STATUS "Will build FLTK gui") + + include_directories( + ${FLTK_INCLUDE_DIR} + "${CMAKE_CURRENT_SOURCE_DIR}/UI" + "${CMAKE_CURRENT_BINARY_DIR}/UI" + ) + + add_subdirectory(UI) +endif() + +if(NtkGui) + + find_program( FLTK_FLUID_EXECUTABLE ntk-fluid) + + message(STATUS ${NTK_LDFLAGS} ${NTK_IMAGES_LDFLAGS}) + + set(GUI_LIBRARIES ${NTK_LIBRARIES} ${NTK_IMAGES_LIBRARIES} ${OPENGL_LIBRARIES} zynaddsubfx_gui) + + add_definitions(-DNTK_GUI) + + message(STATUS "Will build NTK gui") + + include_directories( + ${NTK_INCLUDE_DIRS} + "${CMAKE_CURRENT_SOURCE_DIR}/UI" + "${CMAKE_CURRENT_BINARY_DIR}/UI" + ) + + add_subdirectory(UI) +endif() + +########### General section ############## +# Following this should be only general compilation code, and no mention +# of module-specific variables + +link_directories(${AUDIO_LIBRARY_DIRS} ${ZLIB_LIBRARY_DIRS} ${FFTW_LIBRARY_DIRS} ${MXML_LIBRARY_DIRS} ${FLTK_LIBRARY_DIRS} ${NTK_LIBRARY_DIRS}) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ) + + + +set(NONGUI_LIBRARIES + zynaddsubfx_misc + zynaddsubfx_synth + zynaddsubfx_effect + zynaddsubfx_params + zynaddsubfx_dsp + zynaddsubfx_nio + ) + +add_subdirectory(Misc) +add_subdirectory(Synth) +add_subdirectory(Effects) +add_subdirectory(Params) +add_subdirectory(DSP) +add_subdirectory(Nio) + +add_library(zynaddsubfx_core STATIC + ${zynaddsubfx_dsp_SRCS} + ${zynaddsubfx_effect_SRCS} + ${zynaddsubfx_misc_SRCS} + ${zynaddsubfx_params_SRCS} + ${zynaddsubfx_synth_SRCS} + ) + +target_link_libraries(zynaddsubfx_core + ${ZLIB_LIBRARIES} + ${FFTW_LIBRARIES} + ${MXML_LIBRARIES} + ${OS_LIBRARIES} + pthread) + +if(CompileTests) + add_subdirectory(Tests) +endif(CompileTests) + +message(STATUS "using link directories: ${AUDIO_LIBRARY_DIRS} ${ZLIB_LIBRARY_DIRS} ${FFTW_LIBRARY_DIRS} ${MXML_LIBRARY_DIRS} ${FLTK_LIBRARY_DIRS}") + + +add_executable(zynaddsubfx main.cpp) + +target_link_libraries(zynaddsubfx + zynaddsubfx_core + zynaddsubfx_nio + ${GUI_LIBRARIES} + ${NIO_LIBRARIES} + ${AUDIO_LIBRARIES} + ) + +if (DssiEnable) + add_library(zynaddsubfx_dssi SHARED + Output/DSSIaudiooutput.cpp + ) + + target_link_libraries(zynaddsubfx_dssi + zynaddsubfx_core + ${OS_LIBRARIES} + ) + if (${CMAKE_SIZEOF_VOID_P} EQUAL "8") + install(TARGETS zynaddsubfx_dssi LIBRARY DESTINATION lib64/dssi/) + else () + install(TARGETS zynaddsubfx_dssi LIBRARY DESTINATION lib/dssi/) + endif () +endif() + +message(STATUS "Link libraries: ${ZLIB_LIBRARY} ${FFTW_LIBRARY} ${MXML_LIBRARIES} ${AUDIO_LIBRARIES} ${OS_LIBRARIES}") +install(TARGETS zynaddsubfx + RUNTIME DESTINATION bin + ) + +if(NtkGui) +install(DIRECTORY ../pixmaps DESTINATION share/zynaddsubfx) +add_definitions(-DPIXMAP_PATH="${CMAKE_INSTALL_PREFIX}/share/zynaddsubfx/pixmaps/") +add_definitions(-DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") +endif(NtkGui) + +include(CTest) diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/AnalogFilter.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/AnalogFilter.cpp new file mode 100644 index 000000000..58f46a213 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/AnalogFilter.cpp @@ -0,0 +1,428 @@ +/* + ZynAddSubFX - a software synthesizer + + AnalogFilter.cpp - Several analog filters (lowpass, highpass...) + Copyright (C) 2002-2005 Nasca Octavian Paul + Copyright (C) 2010-2010 Mark McCurry + Author: Nasca Octavian Paul + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include //memcpy +#include +#include + +#include "../Misc/Util.h" +#include "AnalogFilter.h" + +AnalogFilter::AnalogFilter(unsigned char Ftype, + float Ffreq, + float Fq, + unsigned char Fstages, + unsigned int srate, int bufsize) + :Filter(srate, bufsize), + type(Ftype), + stages(Fstages), + freq(Ffreq), + q(Fq), + gain(1.0), + abovenq(false), + oldabovenq(false) +{ + for(int i = 0; i < 3; ++i) + coeff.c[i] = coeff.d[i] = oldCoeff.c[i] = oldCoeff.d[i] = 0.0f; + if(stages >= MAX_FILTER_STAGES) + stages = MAX_FILTER_STAGES; + cleanup(); + firsttime = false; + setfreq_and_q(Ffreq, Fq); + firsttime = true; + coeff.d[0] = 0; //this is not used + outgain = 1.0f; +} + +AnalogFilter::~AnalogFilter() +{} + +void AnalogFilter::cleanup() +{ + for(int i = 0; i < MAX_FILTER_STAGES + 1; ++i) { + history[i].x1 = 0.0f; + history[i].x2 = 0.0f; + history[i].y1 = 0.0f; + history[i].y2 = 0.0f; + oldHistory[i] = history[i]; + } + needsinterpolation = false; +} + +void AnalogFilter::computefiltercoefs(void) +{ + float tmp; + bool zerocoefs = false; //this is used if the freq is too high + + //do not allow frequencies bigger than samplerate/2 + float freq = this->freq; + if(freq > (halfsamplerate_f - 500.0f)) { + freq = halfsamplerate_f - 500.0f; + zerocoefs = true; + } + if(freq < 0.1f) + freq = 0.1f; + //do not allow bogus Q + if(q < 0.0f) + q = 0.0f; + float tmpq, tmpgain; + if(stages == 0) { + tmpq = q; + tmpgain = gain; + } + else { + tmpq = (q > 1.0f) ? powf(q, 1.0f / (stages + 1)) : q; + tmpgain = powf(gain, 1.0f / (stages + 1)); + } + + //Alias Terms + float *c = coeff.c; + float *d = coeff.d; + + //General Constants + const float omega = 2 * PI * freq / samplerate_f; + const float sn = sinf(omega), cs = cosf(omega); + float alpha, beta; + + //most of theese are implementations of + //the "Cookbook formulae for audio EQ" by Robert Bristow-Johnson + //The original location of the Cookbook is: + //http://www.harmony-central.com/Computer/Programming/Audio-EQ-Cookbook.txt + switch(type) { + case 0: //LPF 1 pole + if(!zerocoefs) + tmp = expf(-2.0f * PI * freq / samplerate_f); + else + tmp = 0.0f; + c[0] = 1.0f - tmp; + c[1] = 0.0f; + c[2] = 0.0f; + d[1] = tmp; + d[2] = 0.0f; + order = 1; + break; + case 1: //HPF 1 pole + if(!zerocoefs) + tmp = expf(-2.0f * PI * freq / samplerate_f); + else + tmp = 0.0f; + c[0] = (1.0f + tmp) / 2.0f; + c[1] = -(1.0f + tmp) / 2.0f; + c[2] = 0.0f; + d[1] = tmp; + d[2] = 0.0f; + order = 1; + break; + case 2: //LPF 2 poles + if(!zerocoefs) { + alpha = sn / (2.0f * tmpq); + tmp = 1 + alpha; + c[1] = (1.0f - cs) / tmp; + c[0] = c[2] = c[1] / 2.0f; + d[1] = -2.0f * cs / tmp * -1.0f; + d[2] = (1.0f - alpha) / tmp * -1.0f; + } + else { + c[0] = 1.0f; + c[1] = c[2] = d[1] = d[2] = 0.0f; + } + order = 2; + break; + case 3: //HPF 2 poles + if(!zerocoefs) { + alpha = sn / (2.0f * tmpq); + tmp = 1 + alpha; + c[0] = (1.0f + cs) / 2.0f / tmp; + c[1] = -(1.0f + cs) / tmp; + c[2] = (1.0f + cs) / 2.0f / tmp; + d[1] = -2.0f * cs / tmp * -1.0f; + d[2] = (1.0f - alpha) / tmp * -1.0f; + } + else + c[0] = c[1] = c[2] = d[1] = d[2] = 0.0f; + order = 2; + break; + case 4: //BPF 2 poles + if(!zerocoefs) { + alpha = sn / (2.0f * tmpq); + tmp = 1.0f + alpha; + c[0] = alpha / tmp *sqrtf(tmpq + 1.0f); + c[1] = 0.0f; + c[2] = -alpha / tmp *sqrtf(tmpq + 1.0f); + d[1] = -2.0f * cs / tmp * -1.0f; + d[2] = (1.0f - alpha) / tmp * -1.0f; + } + else + c[0] = c[1] = c[2] = d[1] = d[2] = 0.0f; + order = 2; + break; + case 5: //NOTCH 2 poles + if(!zerocoefs) { + alpha = sn / (2.0f * sqrtf(tmpq)); + tmp = 1.0f + alpha; + c[0] = 1.0f / tmp; + c[1] = -2.0f * cs / tmp; + c[2] = 1.0f / tmp; + d[1] = -2.0f * cs / tmp * -1.0f; + d[2] = (1.0f - alpha) / tmp * -1.0f; + } + else { + c[0] = 1.0f; + c[1] = c[2] = d[1] = d[2] = 0.0f; + } + order = 2; + break; + case 6: //PEAK (2 poles) + if(!zerocoefs) { + tmpq *= 3.0f; + alpha = sn / (2.0f * tmpq); + tmp = 1.0f + alpha / tmpgain; + c[0] = (1.0f + alpha * tmpgain) / tmp; + c[1] = (-2.0f * cs) / tmp; + c[2] = (1.0f - alpha * tmpgain) / tmp; + d[1] = -2.0f * cs / tmp * -1.0f; + d[2] = (1.0f - alpha / tmpgain) / tmp * -1.0f; + } + else { + c[0] = 1.0f; + c[1] = c[2] = d[1] = d[2] = 0.0f; + } + order = 2; + break; + case 7: //Low Shelf - 2 poles + if(!zerocoefs) { + tmpq = sqrtf(tmpq); + alpha = sn / (2.0f * tmpq); + beta = sqrtf(tmpgain) / tmpq; + tmp = (tmpgain + 1.0f) + (tmpgain - 1.0f) * cs + beta * sn; + + c[0] = tmpgain + * ((tmpgain + + 1.0f) - (tmpgain - 1.0f) * cs + beta * sn) / tmp; + c[1] = 2.0f * tmpgain + * ((tmpgain - 1.0f) - (tmpgain + 1.0f) * cs) / tmp; + c[2] = tmpgain + * ((tmpgain + + 1.0f) - (tmpgain - 1.0f) * cs - beta * sn) / tmp; + d[1] = -2.0f * ((tmpgain - 1.0f) + (tmpgain + 1.0f) * cs) + / tmp * -1.0f; + d[2] = ((tmpgain + 1.0f) + (tmpgain - 1.0f) * cs - beta * sn) + / tmp * -1.0f; + } + else { + c[0] = tmpgain; + c[1] = c[2] = d[1] = d[2] = 0.0f; + } + order = 2; + break; + case 8: //High Shelf - 2 poles + if(!zerocoefs) { + tmpq = sqrtf(tmpq); + alpha = sn / (2.0f * tmpq); + beta = sqrtf(tmpgain) / tmpq; + tmp = (tmpgain + 1.0f) - (tmpgain - 1.0f) * cs + beta * sn; + + c[0] = tmpgain + * ((tmpgain + + 1.0f) + (tmpgain - 1.0f) * cs + beta * sn) / tmp; + c[1] = -2.0f * tmpgain + * ((tmpgain - 1.0f) + (tmpgain + 1.0f) * cs) / tmp; + c[2] = tmpgain + * ((tmpgain + + 1.0f) + (tmpgain - 1.0f) * cs - beta * sn) / tmp; + d[1] = 2.0f * ((tmpgain - 1.0f) - (tmpgain + 1.0f) * cs) + / tmp * -1.0f; + d[2] = ((tmpgain + 1.0f) - (tmpgain - 1.0f) * cs - beta * sn) + / tmp * -1.0f; + } + else { + c[0] = 1.0f; + c[1] = c[2] = d[1] = d[2] = 0.0f; + } + order = 2; + break; + default: //wrong type + type = 0; + computefiltercoefs(); + break; + } +} + + +void AnalogFilter::setfreq(float frequency) +{ + if(frequency < 0.1f) + frequency = 0.1f; + float rap = freq / frequency; + if(rap < 1.0f) + rap = 1.0f / rap; + + oldabovenq = abovenq; + abovenq = frequency > (halfsamplerate_f - 500.0f); + + bool nyquistthresh = (abovenq ^ oldabovenq); + + + //if the frequency is changed fast, it needs interpolation + if((rap > 3.0f) || nyquistthresh) { //(now, filter and coeficients backup) + oldCoeff = coeff; + for(int i = 0; i < MAX_FILTER_STAGES + 1; ++i) + oldHistory[i] = history[i]; + if(!firsttime) + needsinterpolation = true; + } + freq = frequency; + computefiltercoefs(); + firsttime = false; +} + +void AnalogFilter::setfreq_and_q(float frequency, float q_) +{ + q = q_; + setfreq(frequency); +} + +void AnalogFilter::setq(float q_) +{ + q = q_; + computefiltercoefs(); +} + +void AnalogFilter::settype(int type_) +{ + type = type_; + computefiltercoefs(); +} + +void AnalogFilter::setgain(float dBgain) +{ + gain = dB2rap(dBgain); + computefiltercoefs(); +} + +void AnalogFilter::setstages(int stages_) +{ + if(stages_ >= MAX_FILTER_STAGES) + stages_ = MAX_FILTER_STAGES - 1; + stages = stages_; + cleanup(); + computefiltercoefs(); +} + +inline void AnalogBiquadFilterA(const float coeff[5], float &src, float work[4]) +{ + work[3] = src*coeff[0] + + work[0]*coeff[1] + + work[1]*coeff[2] + + work[2]*coeff[3] + + work[3]*coeff[4]; + work[1] = src; + src = work[3]; +} + +inline void AnalogBiquadFilterB(const float coeff[5], float &src, float work[4]) +{ + work[2] = src*coeff[0] + + work[1]*coeff[1] + + work[0]*coeff[2] + + work[3]*coeff[3] + + work[2]*coeff[4]; + work[0] = src; + src = work[2]; +} + +void AnalogFilter::singlefilterout(float *smp, fstage &hist, + const Coeff &coeff) +{ + assert((buffersize % 8) == 0); + if(order == 1) { //First order filter + for(int i = 0; i < buffersize; ++i) { + float y0 = smp[i] * coeff.c[0] + hist.x1 * coeff.c[1] + + hist.y1 * coeff.d[1]; + hist.y1 = y0; + hist.x1 = smp[i]; + smp[i] = y0; + } + } else if(order == 2) {//Second order filter + const float coeff_[5] = {coeff.c[0], coeff.c[1], coeff.c[2], coeff.d[1], coeff.d[2]}; + float work[4] = {hist.x1, hist.x2, hist.y1, hist.y2}; + for(int i = 0; i < buffersize; i+=8) { + AnalogBiquadFilterA(coeff_, smp[i + 0], work); + AnalogBiquadFilterB(coeff_, smp[i + 1], work); + AnalogBiquadFilterA(coeff_, smp[i + 2], work); + AnalogBiquadFilterB(coeff_, smp[i + 3], work); + AnalogBiquadFilterA(coeff_, smp[i + 4], work); + AnalogBiquadFilterB(coeff_, smp[i + 5], work); + AnalogBiquadFilterA(coeff_, smp[i + 6], work); + AnalogBiquadFilterB(coeff_, smp[i + 7], work); + } + hist.x1 = work[0]; + hist.x2 = work[1]; + hist.y1 = work[2]; + hist.y2 = work[3]; + } +} + +void AnalogFilter::filterout(float *smp) +{ + for(int i = 0; i < stages + 1; ++i) + singlefilterout(smp, history[i], coeff); + + if(needsinterpolation) { + //Merge Filter at old coeff with new coeff + float ismp[buffersize]; + memcpy(ismp, smp, bufferbytes); + + for(int i = 0; i < stages + 1; ++i) + singlefilterout(ismp, oldHistory[i], oldCoeff); + + for(int i = 0; i < buffersize; ++i) { + float x = (float)i / buffersize_f; + smp[i] = ismp[i] * (1.0f - x) + smp[i] * x; + } + needsinterpolation = false; + } + + for(int i = 0; i < buffersize; ++i) + smp[i] *= outgain; +} + +float AnalogFilter::H(float freq) +{ + float fr = freq / samplerate_f * PI * 2.0f; + float x = coeff.c[0], y = 0.0f; + for(int n = 1; n < 3; ++n) { + x += cosf(n * fr) * coeff.c[n]; + y -= sinf(n * fr) * coeff.c[n]; + } + float h = x * x + y * y; + x = 1.0f; + y = 0.0f; + for(int n = 1; n < 3; ++n) { + x -= cosf(n * fr) * coeff.d[n]; + y += sinf(n * fr) * coeff.d[n]; + } + h = h / (x * x + y * y); + return powf(h, (stages + 1.0f) / 2.0f); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/AnalogFilter.h b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/AnalogFilter.h new file mode 100644 index 000000000..8a012d47d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/AnalogFilter.h @@ -0,0 +1,85 @@ +/* + ZynAddSubFX - a software synthesizer + + Analog Filter.h - Several analog filters (lowpass, highpass...) + Copyright (C) 2002-2005 Nasca Octavian Paul + Copyright (C) 2010-2010 Mark McCurry + Author: Nasca Octavian Paul + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef ANALOG_FILTER_H +#define ANALOG_FILTER_H + +#include "../globals.h" +#include "Filter.h" + +/**Implementation of Several analog filters (lowpass, highpass...) + * Implemented with IIR filters + * Coefficients generated with "Cookbook formulae for audio EQ"*/ +class AnalogFilter:public Filter +{ + public: + AnalogFilter(unsigned char Ftype, float Ffreq, float Fq, + unsigned char Fstages, unsigned int srate, int bufsize); + ~AnalogFilter(); + void filterout(float *smp); + void setfreq(float frequency); + void setfreq_and_q(float frequency, float q_); + void setq(float q_); + + void settype(int type_); + void setgain(float dBgain); + void setstages(int stages_); + void cleanup(); + + float H(float freq); //Obtains the response for a given frequency + + private: + struct fstage { + float x1, x2; //Input History + float y1, y2; //Output History + } history[MAX_FILTER_STAGES + 1], oldHistory[MAX_FILTER_STAGES + 1]; + + struct Coeff { + float c[3], //Feed Forward + d[3]; //Feed Back + } coeff, oldCoeff; + //old coeffs are used for interpolation when paremeters change quickly + + //Apply IIR filter to Samples, with coefficients, and past history + void singlefilterout(float *smp, fstage &hist, const Coeff &coeff); + //Update coeff and order + void computefiltercoefs(void); + + int type; //The type of the filter (LPF1,HPF1,LPF2,HPF2...) + int stages; //how many times the filter is applied (0->1,1->2,etc.) + float freq; //Frequency given in Hz + float q; //Q factor (resonance or Q factor) + float gain; //the gain of the filter (if are shelf/peak) filters + + int order; //the order of the filter (number of poles) + + bool needsinterpolation, //Interpolation between coeff changes + firsttime; //First Iteration of filter + bool abovenq, //if the frequency is above the nyquist + oldabovenq; //if the last time was above nyquist + //(used to see if it needs interpolation) +}; + + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/CMakeLists.txt b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/CMakeLists.txt new file mode 100644 index 000000000..84ac3853f --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/CMakeLists.txt @@ -0,0 +1,9 @@ +set(zynaddsubfx_dsp_SRCS + DSP/AnalogFilter.cpp + DSP/FFTwrapper.cpp + DSP/Filter.cpp + DSP/FormantFilter.cpp + DSP/SVFilter.cpp + DSP/Unison.cpp + PARENT_SCOPE +) diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FFTwrapper.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FFTwrapper.cpp new file mode 100644 index 000000000..4d995b035 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FFTwrapper.cpp @@ -0,0 +1,85 @@ +/* + ZynAddSubFX - a software synthesizer + + FFTwrapper.c - A wrapper for Fast Fourier Transforms + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include +#include "FFTwrapper.h" + +FFTwrapper::FFTwrapper(int fftsize_) +{ + fftsize = fftsize_; + time = new fftw_real[fftsize]; + fft = new fftwf_complex[fftsize + 1]; + planfftw = fftwf_plan_dft_r2c_1d(fftsize, + time, + fft, + FFTW_ESTIMATE); + planfftw_inv = fftwf_plan_dft_c2r_1d(fftsize, + fft, + time, + FFTW_ESTIMATE); +} + +FFTwrapper::~FFTwrapper() +{ + fftwf_destroy_plan(planfftw); + fftwf_destroy_plan(planfftw_inv); + + delete [] time; + delete [] fft; +} + +void FFTwrapper::smps2freqs(const float *smps, fft_t *freqs) +{ + //Load data + for(int i = 0; i < fftsize; ++i) + time[i] = smps[i]; + + //DFT + fftwf_execute(planfftw); + + //Grab data + memcpy((void *)freqs, (const void *)fft, fftsize * sizeof(fftw_real)); +} + +void FFTwrapper::freqs2smps(const fft_t *freqs, float *smps) +{ + //Load data + memcpy((void *)fft, (const void *)freqs, fftsize * sizeof(fftw_real)); + + //clear unused freq channel + fft[fftsize / 2][0] = 0.0f; + fft[fftsize / 2][1] = 0.0f; + + //IDFT + fftwf_execute(planfftw_inv); + + //Grab data + for(int i = 0; i < fftsize; ++i) + smps[i] = static_cast(time[i]); +} + +void FFT_cleanup() +{ + fftwf_cleanup(); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FFTwrapper.h b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FFTwrapper.h new file mode 100644 index 000000000..6eb603336 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FFTwrapper.h @@ -0,0 +1,52 @@ +/* + ZynAddSubFX - a software synthesizer + + FFTwrapper.h - A wrapper for Fast Fourier Transforms + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef FFT_WRAPPER_H +#define FFT_WRAPPER_H +#include +#include +typedef float fftw_real; +typedef std::complex fft_t; + +/**A wrapper for the FFTW library (Fast Fourier Transforms)*/ +class FFTwrapper +{ + public: + /**Constructor + * @param fftsize The size of samples to be fed to fftw*/ + FFTwrapper(int fftsize_); + /**Destructor*/ + ~FFTwrapper(); + /**Convert Samples to Frequencies using Fourier Transform + * @param smps Pointer to Samples to be converted; has length fftsize_ + * @param freqs Structure FFTFREQS which stores the frequencies*/ + void smps2freqs(const float *smps, fft_t *freqs); + void freqs2smps(const fft_t *freqs, float *smps); + private: + int fftsize; + fftw_real *time; + fftwf_complex *fft; + fftwf_plan planfftw, planfftw_inv; +}; + +void FFT_cleanup(); +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Filter.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Filter.cpp new file mode 100644 index 000000000..9487fb475 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Filter.cpp @@ -0,0 +1,75 @@ +/* + ZynAddSubFX - a software synthesizer + + Filter.cpp - Filters, uses analog,formant,etc. filters + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include + +#include "Filter.h" +#include "AnalogFilter.h" +#include "FormantFilter.h" +#include "SVFilter.h" +#include "../Params/FilterParams.h" + +Filter::Filter(unsigned int srate, int bufsize) + : outgain(1.0f), + samplerate(srate), + buffersize(bufsize) +{ + alias(); +} + +Filter *Filter::generate(FilterParams *pars, unsigned int srate, int bufsize) +{ + if (srate == 0) + srate = synth->samplerate; + if (bufsize == 0) + bufsize = synth->buffersize; + + unsigned char Ftype = pars->Ptype; + unsigned char Fstages = pars->Pstages; + + Filter *filter; + switch(pars->Pcategory) { + case 1: + filter = new FormantFilter(pars, srate, bufsize); + break; + case 2: + filter = new SVFilter(Ftype, 1000.0f, pars->getq(), Fstages, srate, bufsize); + filter->outgain = dB2rap(pars->getgain()); + if(filter->outgain > 1.0f) + filter->outgain = sqrt(filter->outgain); + break; + default: + filter = new AnalogFilter(Ftype, 1000.0f, pars->getq(), Fstages, srate, bufsize); + if((Ftype >= 6) && (Ftype <= 8)) + filter->setgain(pars->getgain()); + else + filter->outgain = dB2rap(pars->getgain()); + break; + } + return filter; +} + +float Filter::getrealfreq(float freqpitch) +{ + return powf(2.0f, freqpitch + 9.96578428f); //log2(1000)=9.95748f +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Filter.h b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Filter.h new file mode 100644 index 000000000..52e3675c1 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Filter.h @@ -0,0 +1,64 @@ +/* + ZynAddSubFX - a software synthesizer + + Filter.h - Filters, uses analog,formant,etc. filters + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef FILTER_H +#define FILTER_H + +#include "../globals.h" + +class Filter +{ + public: + static float getrealfreq(float freqpitch); + static Filter *generate(class FilterParams * pars, unsigned int srate = 0, int bufsize = 0); + + Filter(unsigned int srate, int bufsize); + virtual ~Filter() {} + virtual void filterout(float *smp) = 0; + virtual void setfreq(float frequency) = 0; + virtual void setfreq_and_q(float frequency, float q_) = 0; + virtual void setq(float q_) = 0; + virtual void setgain(float dBgain) = 0; + + protected: + float outgain; + + // current setup + unsigned int samplerate; + int buffersize; + + // alias for above terms + float samplerate_f; + float halfsamplerate_f; + float buffersize_f; + int bufferbytes; + + inline void alias() + { + samplerate_f = samplerate; + halfsamplerate_f = samplerate_f / 2.0f; + buffersize_f = buffersize; + bufferbytes = buffersize * sizeof(float); + } +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FormantFilter.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FormantFilter.cpp new file mode 100644 index 000000000..36db5f6d1 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FormantFilter.cpp @@ -0,0 +1,230 @@ +/* + ZynAddSubFX - a software synthesizer + + FormantFilter.cpp - formant filters + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include "../Misc/Util.h" +#include "FormantFilter.h" +#include "AnalogFilter.h" +#include "../Params/FilterParams.h" + +FormantFilter::FormantFilter(FilterParams *pars, unsigned int srate, int bufsize) + : Filter(srate, bufsize) +{ + numformants = pars->Pnumformants; + for(int i = 0; i < numformants; ++i) + formant[i] = new AnalogFilter(4 /*BPF*/, 1000.0f, 10.0f, pars->Pstages, srate, bufsize); + cleanup(); + + for(int j = 0; j < FF_MAX_VOWELS; ++j) + for(int i = 0; i < numformants; ++i) { + formantpar[j][i].freq = pars->getformantfreq( + pars->Pvowels[j].formants[i].freq); + formantpar[j][i].amp = pars->getformantamp( + pars->Pvowels[j].formants[i].amp); + formantpar[j][i].q = pars->getformantq( + pars->Pvowels[j].formants[i].q); + } + + for(int i = 0; i < FF_MAX_FORMANTS; ++i) + oldformantamp[i] = 1.0f; + for(int i = 0; i < numformants; ++i) { + currentformants[i].freq = 1000.0f; + currentformants[i].amp = 1.0f; + currentformants[i].q = 2.0f; + } + + formantslowness = powf(1.0f - (pars->Pformantslowness / 128.0f), 3.0f); + + sequencesize = pars->Psequencesize; + if(sequencesize == 0) + sequencesize = 1; + for(int k = 0; k < sequencesize; ++k) + sequence[k].nvowel = pars->Psequence[k].nvowel; + + vowelclearness = powf(10.0f, (pars->Pvowelclearness - 32.0f) / 48.0f); + + sequencestretch = powf(0.1f, (pars->Psequencestretch - 32.0f) / 48.0f); + if(pars->Psequencereversed) + sequencestretch *= -1.0f; + + outgain = dB2rap(pars->getgain()); + + oldinput = -1.0f; + Qfactor = 1.0f; + oldQfactor = Qfactor; + firsttime = 1; +} + +FormantFilter::~FormantFilter() +{ + for(int i = 0; i < numformants; ++i) + delete (formant[i]); +} + +void FormantFilter::cleanup() +{ + for(int i = 0; i < numformants; ++i) + formant[i]->cleanup(); +} + +void FormantFilter::setpos(float input) +{ + int p1, p2; + + if(firsttime != 0) + slowinput = input; + else + slowinput = slowinput + * (1.0f - formantslowness) + input * formantslowness; + + if((fabsf(oldinput - input) < 0.001f) && (fabsf(slowinput - input) < 0.001f) + && (fabsf(Qfactor - oldQfactor) < 0.001f)) { + // oldinput=input; daca setez asta, o sa faca probleme la schimbari foarte lente + firsttime = 0; + return; + } + else + oldinput = input; + + float pos = fmodf(input * sequencestretch, 1.0f); + if(pos < 0.0f) + pos += 1.0f; + + F2I(pos * sequencesize, p2); + p1 = p2 - 1; + if(p1 < 0) + p1 += sequencesize; + + pos = fmodf(pos * sequencesize, 1.0f); + if(pos < 0.0f) + pos = 0.0f; + else + if(pos > 1.0f) + pos = 1.0f; + pos = + (atanf((pos * 2.0f + - 1.0f) + * vowelclearness) / atanf(vowelclearness) + 1.0f) * 0.5f; + + p1 = sequence[p1].nvowel; + p2 = sequence[p2].nvowel; + + if(firsttime != 0) { + for(int i = 0; i < numformants; ++i) { + currentformants[i].freq = + formantpar[p1][i].freq + * (1.0f - pos) + formantpar[p2][i].freq * pos; + currentformants[i].amp = + formantpar[p1][i].amp + * (1.0f - pos) + formantpar[p2][i].amp * pos; + currentformants[i].q = + formantpar[p1][i].q * (1.0f - pos) + formantpar[p2][i].q * pos; + formant[i]->setfreq_and_q(currentformants[i].freq, + currentformants[i].q * Qfactor); + oldformantamp[i] = currentformants[i].amp; + } + firsttime = 0; + } + else + for(int i = 0; i < numformants; ++i) { + currentformants[i].freq = + currentformants[i].freq * (1.0f - formantslowness) + + (formantpar[p1][i].freq + * (1.0f - pos) + formantpar[p2][i].freq * pos) + * formantslowness; + + currentformants[i].amp = + currentformants[i].amp * (1.0f - formantslowness) + + (formantpar[p1][i].amp * (1.0f - pos) + + formantpar[p2][i].amp * pos) * formantslowness; + + currentformants[i].q = currentformants[i].q + * (1.0f - formantslowness) + + (formantpar[p1][i].q * (1.0f - pos) + + formantpar[p2][i].q + * pos) * formantslowness; + + + formant[i]->setfreq_and_q(currentformants[i].freq, + currentformants[i].q * Qfactor); + } + + oldQfactor = Qfactor; +} + +void FormantFilter::setfreq(float frequency) +{ + setpos(frequency); +} + +void FormantFilter::setq(float q_) +{ + Qfactor = q_; + for(int i = 0; i < numformants; ++i) + formant[i]->setq(Qfactor * currentformants[i].q); +} + +void FormantFilter::setgain(float /*dBgain*/) +{} + +inline float log_2(float x) +{ + return logf(x) / logf(2.0f); +} + +void FormantFilter::setfreq_and_q(float frequency, float q_) +{ + //Convert form real freq[Hz] + const float freq = log_2(frequency) - 9.96578428f; //log2(1000)=9.95748f. + + Qfactor = q_; + setpos(freq); +} + + +void FormantFilter::filterout(float *smp) +{ + float inbuffer[buffersize]; + + memcpy(inbuffer, smp, bufferbytes); + memset(smp, 0, bufferbytes); + + for(int j = 0; j < numformants; ++j) { + float tmpbuf[buffersize]; + for(int i = 0; i < buffersize; ++i) + tmpbuf[i] = inbuffer[i] * outgain; + formant[j]->filterout(tmpbuf); + + if(ABOVE_AMPLITUDE_THRESHOLD(oldformantamp[j], currentformants[j].amp)) + for(int i = 0; i < buffersize; ++i) + smp[i] += tmpbuf[i] + * INTERPOLATE_AMPLITUDE(oldformantamp[j], + currentformants[j].amp, + i, + buffersize); + else + for(int i = 0; i < buffersize; ++i) + smp[i] += tmpbuf[i] * currentformants[j].amp; + oldformantamp[j] = currentformants[j].amp; + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FormantFilter.h b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FormantFilter.h new file mode 100644 index 000000000..715f00f03 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/FormantFilter.h @@ -0,0 +1,66 @@ +/* + ZynAddSubFX - a software synthesizer + + FormantFilter.h - formant filter + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef FORMANT_FILTER_H +#define FORMANT_FILTER_H + +#include "../globals.h" +#include "Filter.h" + + +class FormantFilter:public Filter +{ + public: + FormantFilter(class FilterParams *pars, unsigned int srate, int bufsize); + ~FormantFilter(); + void filterout(float *smp); + void setfreq(float frequency); + void setfreq_and_q(float frequency, float q_); + void setq(float q_); + void setgain(float dBgain); + + void cleanup(void); + + private: + void setpos(float input); + + + class AnalogFilter * formant[FF_MAX_FORMANTS]; + + struct { + float freq, amp, q; //frequency,amplitude,Q + } formantpar[FF_MAX_VOWELS][FF_MAX_FORMANTS], + currentformants[FF_MAX_FORMANTS]; + + struct { + unsigned char nvowel; + } sequence [FF_MAX_SEQUENCE]; + + float oldformantamp[FF_MAX_FORMANTS]; + + int sequencesize, numformants, firsttime; + float oldinput, slowinput; + float Qfactor, formantslowness, oldQfactor; + float vowelclearness, sequencestretch; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/SVFilter.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/SVFilter.cpp new file mode 100644 index 000000000..5e27c39c3 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/SVFilter.cpp @@ -0,0 +1,183 @@ +/* + ZynAddSubFX - a software synthesizer + + SVFilter.cpp - Several state-variable filters + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include "../Misc/Util.h" +#include "SVFilter.h" + +SVFilter::SVFilter(unsigned char Ftype, float Ffreq, float Fq, + unsigned char Fstages, unsigned int srate, int bufsize) + :Filter(srate, bufsize), + type(Ftype), + stages(Fstages), + freq(Ffreq), + q(Fq), + gain(1.0f), + needsinterpolation(false), + firsttime(true) +{ + if(stages >= MAX_FILTER_STAGES) + stages = MAX_FILTER_STAGES; + outgain = 1.0f; + cleanup(); + setfreq_and_q(Ffreq, Fq); +} + +SVFilter::~SVFilter() +{} + +void SVFilter::cleanup() +{ + for(int i = 0; i < MAX_FILTER_STAGES + 1; ++i) + st[i].low = st[i].high = st[i].band = st[i].notch = 0.0f; + oldabovenq = false; + abovenq = false; +} + +void SVFilter::computefiltercoefs(void) +{ + par.f = freq / samplerate_f * 4.0f; + if(par.f > 0.99999f) + par.f = 0.99999f; + par.q = 1.0f - atanf(sqrtf(q)) * 2.0f / PI; + par.q = powf(par.q, 1.0f / (stages + 1)); + par.q_sqrt = sqrtf(par.q); +} + + +void SVFilter::setfreq(float frequency) +{ + if(frequency < 0.1f) + frequency = 0.1f; + float rap = freq / frequency; + if(rap < 1.0f) + rap = 1.0f / rap; + + oldabovenq = abovenq; + abovenq = frequency > (samplerate_f / 2 - 500.0f); + + bool nyquistthresh = (abovenq ^ oldabovenq); + + //if the frequency is changed fast, it needs interpolation + if((rap > 3.0f) || nyquistthresh) { //(now, filter and coeficients backup) + if(!firsttime) + needsinterpolation = true; + ipar = par; + } + freq = frequency; + computefiltercoefs(); + firsttime = false; +} + +void SVFilter::setfreq_and_q(float frequency, float q_) +{ + q = q_; + setfreq(frequency); +} + +void SVFilter::setq(float q_) +{ + q = q_; + computefiltercoefs(); +} + +void SVFilter::settype(int type_) +{ + type = type_; + computefiltercoefs(); +} + +void SVFilter::setgain(float dBgain) +{ + gain = dB2rap(dBgain); + computefiltercoefs(); +} + +void SVFilter::setstages(int stages_) +{ + if(stages_ >= MAX_FILTER_STAGES) + stages_ = MAX_FILTER_STAGES - 1; + stages = stages_; + cleanup(); + computefiltercoefs(); +} + +void SVFilter::singlefilterout(float *smp, fstage &x, parameters &par) +{ + float *out = NULL; + switch(type) { + case 0: + out = &x.low; + break; + case 1: + out = &x.high; + break; + case 2: + out = &x.band; + break; + case 3: + out = &x.notch; + break; + default: +#ifndef WIN32 + errx(1, "Impossible SVFilter type encountered [%d]", type); +#endif + break; + } + + for(int i = 0; i < buffersize; ++i) { + x.low = x.low + par.f * x.band; + x.high = par.q_sqrt * smp[i] - x.low - par.q * x.band; + x.band = par.f * x.high + x.band; + x.notch = x.high + x.low; + smp[i] = *out; + } +} + +void SVFilter::filterout(float *smp) +{ + for(int i = 0; i < stages + 1; ++i) + singlefilterout(smp, st[i], par); + + if(needsinterpolation) { + float ismp[buffersize]; + memcpy(ismp, smp, bufferbytes); + + for(int i = 0; i < stages + 1; ++i) + singlefilterout(ismp, st[i], ipar); + + for(int i = 0; i < buffersize; ++i) { + float x = i / buffersize_f; + smp[i] = ismp[i] * (1.0f - x) + smp[i] * x; + } + needsinterpolation = false; + } + + for(int i = 0; i < buffersize; ++i) + smp[i] *= outgain; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/SVFilter.h b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/SVFilter.h new file mode 100644 index 000000000..e9d002c7b --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/SVFilter.h @@ -0,0 +1,70 @@ +/* + ZynAddSubFX - a software synthesizer + + SV Filter.h - Several state-variable filters + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef SV_FILTER_H +#define SV_FILTER_H + +#include "../globals.h" +#include "Filter.h" + +class SVFilter:public Filter +{ + public: + SVFilter(unsigned char Ftype, + float Ffreq, + float Fq, + unsigned char Fstages, + unsigned int srate, int bufsize); + ~SVFilter(); + void filterout(float *smp); + void setfreq(float frequency); + void setfreq_and_q(float frequency, float q_); + void setq(float q_); + + void settype(int type_); + void setgain(float dBgain); + void setstages(int stages_); + void cleanup(); + + private: + struct fstage { + float low, high, band, notch; + } st[MAX_FILTER_STAGES + 1]; + + struct parameters { + float f, q, q_sqrt; + } par, ipar; + + void singlefilterout(float *smp, fstage &x, parameters &par); + void computefiltercoefs(void); + int type; // The type of the filter (LPF1,HPF1,LPF2,HPF2...) + int stages; // how many times the filter is applied (0->1,1->2,etc.) + float freq; // Frequency given in Hz + float q; // Q factor (resonance or Q factor) + float gain; // the gain of the filter (if are shelf/peak) filters + + bool abovenq, //if the frequency is above the nyquist + oldabovenq; + bool needsinterpolation, firsttime; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Unison.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Unison.cpp new file mode 100644 index 000000000..540a7ef11 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Unison.cpp @@ -0,0 +1,203 @@ +/* + ZynAddSubFX - a software synthesizer + + Unison.cpp - Unison effect (multivoice chorus) + Copyright (C) 2002-2009 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#ifndef WIN32 +#include +#endif + +#include "Unison.h" + +Unison::Unison(int update_period_samples_, float max_delay_sec_, float srate_f) + :unison_size(0), + base_freq(1.0f), + uv(NULL), + update_period_samples(update_period_samples_), + update_period_sample_k(0), + max_delay((int)(srate_f * max_delay_sec_) + 1), + delay_k(0), + first_time(false), + delay_buffer(NULL), + unison_amplitude_samples(0.0f), + unison_bandwidth_cents(10.0f), + samplerate_f(srate_f) +{ + if(max_delay < 10) + max_delay = 10; + delay_buffer = new float[max_delay]; + memset(delay_buffer, 0, max_delay * sizeof(float)); + setSize(1); +} + +Unison::~Unison() { + delete [] delay_buffer; + delete [] uv; +} + +void Unison::setSize(int new_size) +{ + if(new_size < 1) + new_size = 1; + unison_size = new_size; + if(uv) + delete [] uv; + uv = new UnisonVoice[unison_size]; + first_time = true; + updateParameters(); +} + +void Unison::setBaseFrequency(float freq) +{ + base_freq = freq; + updateParameters(); +} + +void Unison::setBandwidth(float bandwidth) +{ + if(bandwidth < 0) + bandwidth = 0.0f; + if(bandwidth > 1200.0f) + bandwidth = 1200.0f; + + /* If the bandwidth is too small, the audio may cancel itself out + * (due to the sign change of the outputs) + * TODO figure out the acceptable lower bound and codify it + */ + unison_bandwidth_cents = bandwidth; + updateParameters(); +} + +void Unison::updateParameters(void) +{ + if(!uv) + return; + float increments_per_second = samplerate_f + / (float) update_period_samples; +// printf("#%g, %g\n",increments_per_second,base_freq); + for(int i = 0; i < unison_size; ++i) { + float base = powf(UNISON_FREQ_SPAN, SYNTH_T::numRandom() * 2.0f - 1.0f); + uv[i].relative_amplitude = base; + float period = base / base_freq; + float m = 4.0f / (period * increments_per_second); + if(SYNTH_T::numRandom() < 0.5f) + m = -m; + uv[i].step = m; +// printf("%g %g\n",uv[i].relative_amplitude,period); + } + + float max_speed = powf(2.0f, unison_bandwidth_cents / 1200.0f); + unison_amplitude_samples = 0.125f * (max_speed - 1.0f) + * samplerate_f / base_freq; + + //If functions exceed this limit, they should have requested a bigguer delay + //and thus are buggy + if(unison_amplitude_samples >= max_delay - 1) { +#ifndef WIN32 + warnx("BUG: Unison amplitude samples too big"); + warnx("Unision max_delay should be larger"); +#endif + unison_amplitude_samples = max_delay - 2; + } + + updateUnisonData(); +} + +void Unison::process(int bufsize, float *inbuf, float *outbuf) +{ + if(!uv) + return; + if(!outbuf) + outbuf = inbuf; + + float volume = 1.0f / sqrtf(unison_size); + float xpos_step = 1.0f / (float) update_period_samples; + float xpos = (float) update_period_sample_k * xpos_step; + for(int i = 0; i < bufsize; ++i) { + if(update_period_sample_k++ >= update_period_samples) { + updateUnisonData(); + update_period_sample_k = 0; + xpos = 0.0f; + } + xpos += xpos_step; + float in = inbuf[i], out = 0.0f; + float sign = 1.0f; + for(int k = 0; k < unison_size; ++k) { + float vpos = uv[k].realpos1 * (1.0f - xpos) + uv[k].realpos2 * xpos; //optimize + float pos = (float)(delay_k + max_delay) - vpos - 1.0f; + int posi; + F2I(pos, posi); //optimize! + int posi_next = posi + 1; + if(posi >= max_delay) + posi -= max_delay; + if(posi_next >= max_delay) + posi_next -= max_delay; + float posf = pos - floorf(pos); + out += ((1.0f - posf) * delay_buffer[posi] + posf + * delay_buffer[posi_next]) * sign; + sign = -sign; + } + outbuf[i] = out * volume; +// printf("%d %g\n",i,outbuf[i]); + delay_buffer[delay_k] = in; + delay_k = (++delay_k < max_delay) ? delay_k : 0; + } +} + +void Unison::updateUnisonData() +{ + if(!uv) + return; + + for(int k = 0; k < unison_size; ++k) { + float pos = uv[k].position; + float step = uv[k].step; + pos += step; + if(pos <= -1.0f) { + pos = -1.0f; + step = -step; + } + else + if(pos >= 1.0f) { + pos = 1.0f; + step = -step; + } + float vibratto_val = (pos - 0.333333333f * pos * pos * pos) * 1.5f; //make the vibratto lfo smoother + + //Relative amplitude is utilized, so the delay may be larger than the + //whole buffer, if the buffer is too small, this indicates a buggy call + //to Unison() + float newval = 1.0f + 0.5f + * (vibratto_val + 1.0f) * unison_amplitude_samples + * uv[k].relative_amplitude; + + if(first_time) + uv[k].realpos1 = uv[k].realpos2 = newval; + else { + uv[k].realpos1 = uv[k].realpos2; + uv[k].realpos2 = newval; + } + + uv[k].position = pos; + uv[k].step = step; + } + first_time = false; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Unison.h b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Unison.h new file mode 100644 index 000000000..bca5ee859 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/DSP/Unison.h @@ -0,0 +1,76 @@ +/* + ZynAddSubFX - a software synthesizer + + Unison.h - Unison effect (multivoice chorus) + Copyright (C) 2002-2009 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef UNISON_H +#define UNISON_H + +#include "../Misc/Util.h" + +//how much the unison frequencies varies (always >= 1.0) +#define UNISON_FREQ_SPAN 2.0f + +class Unison +{ + public: + Unison(int update_period_samples_, float max_delay_sec_, float srate_f); + ~Unison(); + + void setSize(int new_size); + void setBaseFrequency(float freq); + void setBandwidth(float bandwidth_cents); + + void process(int bufsize, float *inbuf, float *outbuf = NULL); + + private: + void updateParameters(void); + void updateUnisonData(void); + + int unison_size; + float base_freq; + struct UnisonVoice { + float step; //base LFO + float position; + float realpos1; //the position regarding samples + float realpos2; + float relative_amplitude; + float lin_fpos; + float lin_ffreq; + UnisonVoice() { + position = RND * 1.8f - 0.9f; + realpos1 = 0.0f; + realpos2 = 0.0f; + step = 0.0f; + relative_amplitude = 1.0f; + } + } *uv; + + int update_period_samples; + int update_period_sample_k; + int max_delay, delay_k; + bool first_time; + float *delay_buffer; + float unison_amplitude_samples; + float unison_bandwidth_cents; + + // current setup + float samplerate_f; +}; +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Alienwah.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Alienwah.cpp new file mode 100644 index 000000000..fae1860b6 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Alienwah.cpp @@ -0,0 +1,236 @@ +/* + ZynAddSubFX - a software synthesizer + + Alienwah.cpp - "AlienWah" effect + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include "Alienwah.h" + +Alienwah::Alienwah(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize) + :Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize), + lfo(srate, bufsize), + oldl(NULL), + oldr(NULL) +{ + setpreset(Ppreset); + cleanup(); + oldclfol = complex(fb, 0.0f); + oldclfor = complex(fb, 0.0f); +} + +Alienwah::~Alienwah() +{ + if(oldl != NULL) + delete [] oldl; + if(oldr != NULL) + delete [] oldr; +} + + +//Apply the effect +void Alienwah::out(const Stereo &smp) +{ + float lfol, lfor; //Left/Right LFOs + complex clfol, clfor; + /**\todo Rework, as optimization can be used when the new complex type is + * utilized. + * Before all calculations needed to be done with individual float, + * but now they can be done together*/ + lfo.effectlfoout(&lfol, &lfor); + lfol *= depth * PI * 2.0f; + lfor *= depth * PI * 2.0f; + clfol = complex(cosf(lfol + phase) * fb, sinf(lfol + phase) * fb); //rework + clfor = complex(cosf(lfor + phase) * fb, sinf(lfor + phase) * fb); //rework + + for(int i = 0; i < buffersize; ++i) { + float x = ((float) i) / buffersize_f; + float x1 = 1.0f - x; + //left + complex tmp = clfol * x + oldclfol * x1; + + complex out = tmp * oldl[oldk]; + out += (1 - fabs(fb)) * smp.l[i] * pangainL; + + oldl[oldk] = out; + float l = out.real() * 10.0f * (fb + 0.1f); + + //right + tmp = clfor * x + oldclfor * x1; + + out = tmp * oldr[oldk]; + out += (1 - fabs(fb)) * smp.r[i] * pangainR; + + oldr[oldk] = out; + float r = out.real() * 10.0f * (fb + 0.1f); + + + if(++oldk >= Pdelay) + oldk = 0; + //LRcross + efxoutl[i] = l * (1.0f - lrcross) + r * lrcross; + efxoutr[i] = r * (1.0f - lrcross) + l * lrcross; + } + + oldclfol = clfol; + oldclfor = clfor; +} + +//Cleanup the effect +void Alienwah::cleanup(void) +{ + for(int i = 0; i < Pdelay; ++i) { + oldl[i] = complex(0.0f, 0.0f); + oldr[i] = complex(0.0f, 0.0f); + } + oldk = 0; +} + + +//Parameter control +void Alienwah::setdepth(unsigned char _Pdepth) +{ + Pdepth = _Pdepth; + depth = Pdepth / 127.0f; +} + +void Alienwah::setfb(unsigned char _Pfb) +{ + Pfb = _Pfb; + fb = fabs((Pfb - 64.0f) / 64.1f); + fb = sqrtf(fb); + if(fb < 0.4f) + fb = 0.4f; + if(Pfb < 64) + fb = -fb; +} + +void Alienwah::setvolume(unsigned char _Pvolume) +{ + Pvolume = _Pvolume; + outvolume = Pvolume / 127.0f; + if(insertion == 0) + volume = 1.0f; + else + volume = outvolume; +} + +void Alienwah::setphase(unsigned char _Pphase) +{ + Pphase = _Pphase; + phase = (Pphase - 64.0f) / 64.0f * PI; +} + +void Alienwah::setdelay(unsigned char _Pdelay) +{ + if(oldl != NULL) + delete [] oldl; + if(oldr != NULL) + delete [] oldr; + Pdelay = (_Pdelay >= MAX_ALIENWAH_DELAY) ? MAX_ALIENWAH_DELAY : _Pdelay; + oldl = new complex[Pdelay]; + oldr = new complex[Pdelay]; + cleanup(); +} + +void Alienwah::setpreset(unsigned char npreset) +{ + const int PRESET_SIZE = 11; + const int NUM_PRESETS = 4; + unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { + //AlienWah1 + {127, 64, 70, 0, 0, 62, 60, 105, 25, 0, 64}, + //AlienWah2 + {127, 64, 73, 106, 0, 101, 60, 105, 17, 0, 64}, + //AlienWah3 + {127, 64, 63, 0, 1, 100, 112, 105, 31, 0, 42}, + //AlienWah4 + {93, 64, 25, 0, 1, 66, 101, 11, 47, 0, 86} + }; + + if(npreset >= NUM_PRESETS) + npreset = NUM_PRESETS - 1; + for(int n = 0; n < PRESET_SIZE; ++n) + changepar(n, presets[npreset][n]); + if(insertion == 0) + changepar(0, presets[npreset][0] / 2); //lower the volume if this is system effect + Ppreset = npreset; +} + + +void Alienwah::changepar(int npar, unsigned char value) +{ + switch(npar) { + case 0: + setvolume(value); + break; + case 1: + setpanning(value); + break; + case 2: + lfo.Pfreq = value; + lfo.updateparams(); + break; + case 3: + lfo.Prandomness = value; + lfo.updateparams(); + break; + case 4: + lfo.PLFOtype = value; + lfo.updateparams(); + break; + case 5: + lfo.Pstereo = value; + lfo.updateparams(); + break; + case 6: + setdepth(value); + break; + case 7: + setfb(value); + break; + case 8: + setdelay(value); + break; + case 9: + setlrcross(value); + break; + case 10: + setphase(value); + break; + } +} + +unsigned char Alienwah::getpar(int npar) const +{ + switch(npar) { + case 0: return Pvolume; + case 1: return Ppanning; + case 2: return lfo.Pfreq; + case 3: return lfo.Prandomness; + case 4: return lfo.PLFOtype; + case 5: return lfo.Pstereo; + case 6: return Pdepth; + case 7: return Pfb; + case 8: return Pdelay; + case 9: return Plrcross; + case 10: return Pphase; + default: return 0; + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Alienwah.h b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Alienwah.h new file mode 100644 index 000000000..52019ac94 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Alienwah.h @@ -0,0 +1,81 @@ +/* + ZynAddSubFX - a software synthesizer + + Alienwah.h - "AlienWah" effect + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef ALIENWAH_H +#define ALIENWAH_H + +#include +#include "Effect.h" +#include "EffectLFO.h" + +using namespace std; + +#define MAX_ALIENWAH_DELAY 100 + +/**"AlienWah" Effect*/ +class Alienwah:public Effect +{ + public: + /** + * Constructor + * @param insertion_ true for insertion Effect + * @param efxoutl_ Pointer to Alienwah's left channel output buffer + * @param efxoutr_ Pointer to Alienwah's left channel output buffer + * @return Initialized Alienwah + */ + Alienwah(bool insertion_, + float *const efxoutl_, + float *const efxoutr_, + unsigned int srate, int bufsize); + ~Alienwah(); + void out(const Stereo &smp); + + void setpreset(unsigned char npreset); + void changepar(int npar, unsigned char value); + unsigned char getpar(int npar) const; + void cleanup(void); + + private: + //Alienwah Parameters + EffectLFO lfo; //lfo-ul Alienwah + unsigned char Pvolume; + unsigned char Pdepth; //the depth of the Alienwah + unsigned char Pfb; //feedback + unsigned char Pdelay; + unsigned char Pphase; + + + //Control Parameters + void setvolume(unsigned char _Pvolume); + void setdepth(unsigned char _Pdepth); + void setfb(unsigned char _Pfb); + void setdelay(unsigned char _Pdelay); + void setphase(unsigned char _Pphase); + + //Internal Values + float fb, depth, phase; + complex *oldl, *oldr; + complex oldclfol, oldclfor; + int oldk; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/CMakeLists.txt b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/CMakeLists.txt new file mode 100644 index 000000000..803cbca1b --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/CMakeLists.txt @@ -0,0 +1,14 @@ +set(zynaddsubfx_effect_SRCS + Effects/Alienwah.cpp + Effects/Chorus.cpp + Effects/Distorsion.cpp + Effects/DynamicFilter.cpp + Effects/Echo.cpp + Effects/Effect.cpp + Effects/EffectLFO.cpp + Effects/EffectMgr.cpp + Effects/EQ.cpp + Effects/Phaser.cpp + Effects/Reverb.cpp + PARENT_SCOPE +) diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Chorus.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Chorus.cpp new file mode 100644 index 000000000..7e95a2153 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Chorus.cpp @@ -0,0 +1,269 @@ +/* + ZynAddSubFX - a software synthesizer + + Chorus.cpp - Chorus and Flange effects + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include "Chorus.h" +#include + +using namespace std; + +Chorus::Chorus(bool insertion_, float *const efxoutl_, float *efxoutr_, unsigned int srate, int bufsize) + :Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize), + lfo(srate, bufsize), + maxdelay((int)(MAX_CHORUS_DELAY / 1000.0f * samplerate_f)), + delaySample(new float[maxdelay], new float[maxdelay]) +{ + dlk = 0; + drk = 0; + setpreset(Ppreset); + changepar(1, 64); + lfo.effectlfoout(&lfol, &lfor); + dl2 = getdelay(lfol); + dr2 = getdelay(lfor); + cleanup(); +} + +Chorus::~Chorus() +{ + delete [] delaySample.l; + delete [] delaySample.r; +} + +//get the delay value in samples; xlfo is the current lfo value +float Chorus::getdelay(float xlfo) +{ + float result = + (Pflangemode) ? 0 : (delay + xlfo * depth) * samplerate_f; + + //check if delay is too big (caused by bad setdelay() and setdepth() + if((result + 0.5f) >= maxdelay) { + cerr + << + "WARNING: Chorus.cpp::getdelay(..) too big delay (see setdelay and setdepth funcs.)" + << endl; + result = maxdelay - 1.0f; + } + return result; +} + +//Apply the effect +void Chorus::out(const Stereo &input) +{ + const float one = 1.0f; + dl1 = dl2; + dr1 = dr2; + lfo.effectlfoout(&lfol, &lfor); + + dl2 = getdelay(lfol); + dr2 = getdelay(lfor); + + for(int i = 0; i < buffersize; ++i) { + float inL = input.l[i]; + float inR = input.r[i]; + //LRcross + Stereo tmpc(inL, inR); + inL = tmpc.l * (1.0f - lrcross) + tmpc.r * lrcross; + inR = tmpc.r * (1.0f - lrcross) + tmpc.l * lrcross; + + //Left channel + + //compute the delay in samples using linear interpolation between the lfo delays + float mdel = + (dl1 * (buffersize - i) + dl2 * i) / buffersize_f; + if(++dlk >= maxdelay) + dlk = 0; + float tmp = dlk - mdel + maxdelay * 2.0f; //where should I get the sample from + + dlhi = (int) tmp; + dlhi %= maxdelay; + + float dlhi2 = (dlhi - 1 + maxdelay) % maxdelay; + float dllo = 1.0f - fmod(tmp, one); + efxoutl[i] = cinterpolate(delaySample.l, maxdelay, dlhi2) * dllo + + cinterpolate(delaySample.l, maxdelay, + dlhi) * (1.0f - dllo); + delaySample.l[dlk] = inL + efxoutl[i] * fb; + + //Right channel + + //compute the delay in samples using linear interpolation between the lfo delays + mdel = (dr1 * (buffersize - i) + dr2 * i) / buffersize_f; + if(++drk >= maxdelay) + drk = 0; + tmp = drk * 1.0f - mdel + maxdelay * 2.0f; //where should I get the sample from + + dlhi = (int) tmp; + dlhi %= maxdelay; + + dlhi2 = (dlhi - 1 + maxdelay) % maxdelay; + dllo = 1.0f - fmodf(tmp, one); + efxoutr[i] = cinterpolate(delaySample.r, maxdelay, dlhi2) * dllo + + cinterpolate(delaySample.r, maxdelay, + dlhi) * (1.0f - dllo); + delaySample.r[dlk] = inR + efxoutr[i] * fb; + } + + if(Poutsub) + for(int i = 0; i < buffersize; ++i) { + efxoutl[i] *= -1.0f; + efxoutr[i] *= -1.0f; + } + + for(int i = 0; i < buffersize; ++i) { + efxoutl[i] *= pangainL; + efxoutr[i] *= pangainR; + } +} + +//Cleanup the effect +void Chorus::cleanup(void) +{ + memset(delaySample.l, 0, maxdelay * sizeof(float)); + memset(delaySample.r, 0, maxdelay * sizeof(float)); +} + +//Parameter control +void Chorus::setdepth(unsigned char _Pdepth) +{ + Pdepth = _Pdepth; + depth = (powf(8.0f, (Pdepth / 127.0f) * 2.0f) - 1.0f) / 1000.0f; //seconds +} + +void Chorus::setdelay(unsigned char _Pdelay) +{ + Pdelay = _Pdelay; + delay = (powf(10.0f, (Pdelay / 127.0f) * 2.0f) - 1.0f) / 1000.0f; //seconds +} + +void Chorus::setfb(unsigned char _Pfb) +{ + Pfb = _Pfb; + fb = (Pfb - 64.0f) / 64.1f; +} + +void Chorus::setvolume(unsigned char _Pvolume) +{ + Pvolume = _Pvolume; + outvolume = Pvolume / 127.0f; + volume = (!insertion) ? 1.0f : outvolume; +} + + +void Chorus::setpreset(unsigned char npreset) +{ + const int PRESET_SIZE = 12; + const int NUM_PRESETS = 10; + unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { + //Chorus1 + {64, 64, 50, 0, 0, 90, 40, 85, 64, 119, 0, 0}, + //Chorus2 + {64, 64, 45, 0, 0, 98, 56, 90, 64, 19, 0, 0}, + //Chorus3 + {64, 64, 29, 0, 1, 42, 97, 95, 90, 127, 0, 0}, + //Celeste1 + {64, 64, 26, 0, 0, 42, 115, 18, 90, 127, 0, 0}, + //Celeste2 + {64, 64, 29, 117, 0, 50, 115, 9, 31, 127, 0, 1}, + //Flange1 + {64, 64, 57, 0, 0, 60, 23, 3, 62, 0, 0, 0}, + //Flange2 + {64, 64, 33, 34, 1, 40, 35, 3, 109, 0, 0, 0}, + //Flange3 + {64, 64, 53, 34, 1, 94, 35, 3, 54, 0, 0, 1}, + //Flange4 + {64, 64, 40, 0, 1, 62, 12, 19, 97, 0, 0, 0}, + //Flange5 + {64, 64, 55, 105, 0, 24, 39, 19, 17, 0, 0, 1} + }; + + if(npreset >= NUM_PRESETS) + npreset = NUM_PRESETS - 1; + for(int n = 0; n < PRESET_SIZE; ++n) + changepar(n, presets[npreset][n]); + Ppreset = npreset; +} + + +void Chorus::changepar(int npar, unsigned char value) +{ + switch(npar) { + case 0: + setvolume(value); + break; + case 1: + setpanning(value); + break; + case 2: + lfo.Pfreq = value; + lfo.updateparams(); + break; + case 3: + lfo.Prandomness = value; + lfo.updateparams(); + break; + case 4: + lfo.PLFOtype = value; + lfo.updateparams(); + break; + case 5: + lfo.Pstereo = value; + lfo.updateparams(); + break; + case 6: + setdepth(value); + break; + case 7: + setdelay(value); + break; + case 8: + setfb(value); + break; + case 9: + setlrcross(value); + break; + case 10: + Pflangemode = (value > 1) ? 1 : value; + break; + case 11: + Poutsub = (value > 1) ? 1 : value; + break; + } +} + +unsigned char Chorus::getpar(int npar) const +{ + switch(npar) { + case 0: return Pvolume; + case 1: return Ppanning; + case 2: return lfo.Pfreq; + case 3: return lfo.Prandomness; + case 4: return lfo.PLFOtype; + case 5: return lfo.Pstereo; + case 6: return Pdepth; + case 7: return Pdelay; + case 8: return Pfb; + case 9: return Plrcross; + case 10: return Pflangemode; + case 11: return Poutsub; + default: return 0; + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Chorus.h b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Chorus.h new file mode 100644 index 000000000..7772d4668 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Chorus.h @@ -0,0 +1,106 @@ +/* + ZynAddSubFX - a software synthesizer + + Chorus.h - Chorus and Flange effects + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef CHORUS_H +#define CHORUS_H +#include "Effect.h" +#include "EffectLFO.h" +#include "../Misc/Stereo.h" + +#define MAX_CHORUS_DELAY 250.0f //ms + +/**Chorus and Flange effects*/ +class Chorus:public Effect +{ + public: + Chorus(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize); + /**Destructor*/ + ~Chorus(); + void out(const Stereo &input); + void setpreset(unsigned char npreset); + /** + * Sets the value of the chosen variable + * + * The possible parameters are: + * -# Volume + * -# Panning + * -# LFO Frequency + * -# LFO Randomness + * -# LFO Type + * -# LFO stereo + * -# Depth + * -# Delay + * -# Feedback + * -# Flange Mode + * -# Subtractive + * @param npar number of chosen parameter + * @param value the new value + */ + void changepar(int npar, unsigned char value); + /** + * Gets the value of the chosen variable + * + * The possible parameters are: + * -# Volume + * -# Panning + * -# LFO Frequency + * -# LFO Randomness + * -# LFO Type + * -# LFO stereo + * -# Depth + * -# Delay + * -# Feedback + * -# Flange Mode + * -# Subtractive + * @param npar number of chosen parameter + * @return the value of the parameter + */ + unsigned char getpar(int npar) const; + void cleanup(void); + + private: + //Chorus Parameters + unsigned char Pvolume; + unsigned char Pdepth; //the depth of the Chorus(ms) + unsigned char Pdelay; //the delay (ms) + unsigned char Pfb; //feedback + unsigned char Pflangemode; //how the LFO is scaled, to result chorus or flange + unsigned char Poutsub; //if I wish to substract the output instead of the adding it + EffectLFO lfo; //lfo-ul chorus + + + //Parameter Controls + void setvolume(unsigned char _Pvolume); + void setdepth(unsigned char _Pdepth); + void setdelay(unsigned char _Pdelay); + void setfb(unsigned char _Pfb); + + //Internal Values + float depth, delay, fb; + float dl1, dl2, dr1, dr2, lfol, lfor; + int maxdelay; + Stereo delaySample; + int dlk, drk, dlhi; + float getdelay(float xlfo); +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Distorsion.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Distorsion.cpp new file mode 100644 index 000000000..5d5c8f6f9 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Distorsion.cpp @@ -0,0 +1,245 @@ +/* + ZynAddSubFX - a software synthesizer + + Distorsion.cpp - Distorsion effect + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "Distorsion.h" +#include "../DSP/AnalogFilter.h" +#include "../Misc/WaveShapeSmps.h" +#include + +Distorsion::Distorsion(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize) + :Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize), + Pvolume(50), + Pdrive(90), + Plevel(64), + Ptype(0), + Pnegate(0), + Plpf(127), + Phpf(0), + Pstereo(0), + Pprefiltering(0) +{ + lpfl = new AnalogFilter(2, 22000, 1, 0, srate, bufsize); + lpfr = new AnalogFilter(2, 22000, 1, 0, srate, bufsize); + hpfl = new AnalogFilter(3, 20, 1, 0, srate, bufsize); + hpfr = new AnalogFilter(3, 20, 1, 0, srate, bufsize); + setpreset(Ppreset); + cleanup(); +} + +Distorsion::~Distorsion() +{ + delete lpfl; + delete lpfr; + delete hpfl; + delete hpfr; +} + +//Cleanup the effect +void Distorsion::cleanup(void) +{ + lpfl->cleanup(); + hpfl->cleanup(); + lpfr->cleanup(); + hpfr->cleanup(); +} + + +//Apply the filters +void Distorsion::applyfilters(float *efxoutl, float *efxoutr) +{ + lpfl->filterout(efxoutl); + hpfl->filterout(efxoutl); + if(Pstereo != 0) { //stereo + lpfr->filterout(efxoutr); + hpfr->filterout(efxoutr); + } +} + + +//Effect output +void Distorsion::out(const Stereo &smp) +{ + float inputvol = powf(5.0f, (Pdrive - 32.0f) / 127.0f); + if(Pnegate) + inputvol *= -1.0f; + + if(Pstereo) //Stereo + for(int i = 0; i < buffersize; ++i) { + efxoutl[i] = smp.l[i] * inputvol * pangainL; + efxoutr[i] = smp.r[i] * inputvol * pangainR; + } + else //Mono + for(int i = 0; i < buffersize; ++i) + efxoutl[i] = (smp.l[i] * pangainL + smp.r[i] * pangainR) * inputvol; + + if(Pprefiltering) + applyfilters(efxoutl, efxoutr); + + waveShapeSmps(buffersize, efxoutl, Ptype + 1, Pdrive); + if(Pstereo) + waveShapeSmps(buffersize, efxoutr, Ptype + 1, Pdrive); + + if(!Pprefiltering) + applyfilters(efxoutl, efxoutr); + + if(!Pstereo) + memcpy(efxoutr, efxoutl, bufferbytes); + + float level = dB2rap(60.0f * Plevel / 127.0f - 40.0f); + for(int i = 0; i < buffersize; ++i) { + float lout = efxoutl[i]; + float rout = efxoutr[i]; + float l = lout * (1.0f - lrcross) + rout * lrcross; + float r = rout * (1.0f - lrcross) + lout * lrcross; + lout = l; + rout = r; + + efxoutl[i] = lout * 2.0f * level; + efxoutr[i] = rout * 2.0f * level; + } +} + + +//Parameter control +void Distorsion::setvolume(unsigned char _Pvolume) +{ + Pvolume = _Pvolume; + + if(insertion == 0) { + outvolume = powf(0.01f, (1.0f - Pvolume / 127.0f)) * 4.0f; + volume = 1.0f; + } + else + volume = outvolume = Pvolume / 127.0f; + if(Pvolume == 0) + cleanup(); +} + +void Distorsion::setlpf(unsigned char _Plpf) +{ + Plpf = _Plpf; + float fr = expf(powf(Plpf / 127.0f, 0.5f) * logf(25000.0f)) + 40.0f; + lpfl->setfreq(fr); + lpfr->setfreq(fr); +} + +void Distorsion::sethpf(unsigned char _Phpf) +{ + Phpf = _Phpf; + float fr = expf(powf(Phpf / 127.0f, 0.5f) * logf(25000.0f)) + 20.0f; + hpfl->setfreq(fr); + hpfr->setfreq(fr); +} + + +void Distorsion::setpreset(unsigned char npreset) +{ + const int PRESET_SIZE = 11; + const int NUM_PRESETS = 6; + unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { + //Overdrive 1 + {127, 64, 35, 56, 70, 0, 0, 96, 0, 0, 0}, + //Overdrive 2 + {127, 64, 35, 29, 75, 1, 0, 127, 0, 0, 0}, + //A. Exciter 1 + {64, 64, 35, 75, 80, 5, 0, 127, 105, 1, 0}, + //A. Exciter 2 + {64, 64, 35, 85, 62, 1, 0, 127, 118, 1, 0}, + //Guitar Amp + {127, 64, 35, 63, 75, 2, 0, 55, 0, 0, 0}, + //Quantisize + {127, 64, 35, 88, 75, 4, 0, 127, 0, 1, 0} + }; + + if(npreset >= NUM_PRESETS) + npreset = NUM_PRESETS - 1; + for(int n = 0; n < PRESET_SIZE; ++n) + changepar(n, presets[npreset][n]); + if(!insertion) //lower the volume if this is system effect + changepar(0, (int) (presets[npreset][0] / 1.5f)); + Ppreset = npreset; + cleanup(); +} + + +void Distorsion::changepar(int npar, unsigned char value) +{ + switch(npar) { + case 0: + setvolume(value); + break; + case 1: + setpanning(value); + break; + case 2: + setlrcross(value); + break; + case 3: + Pdrive = value; + break; + case 4: + Plevel = value; + break; + case 5: + if(value > 13) + Ptype = 13; //this must be increased if more distorsion types are added + else + Ptype = value; + break; + case 6: + if(value > 1) + Pnegate = 1; + else + Pnegate = value; + break; + case 7: + setlpf(value); + break; + case 8: + sethpf(value); + break; + case 9: + Pstereo = (value > 1) ? 1 : value; + break; + case 10: + Pprefiltering = value; + break; + } +} + +unsigned char Distorsion::getpar(int npar) const +{ + switch(npar) { + case 0: return Pvolume; + case 1: return Ppanning; + case 2: return Plrcross; + case 3: return Pdrive; + case 4: return Plevel; + case 5: return Ptype; + case 6: return Pnegate; + case 7: return Plpf; + case 8: return Phpf; + case 9: return Pstereo; + case 10: return Pprefiltering; + default: return 0; //in case of bogus parameter number + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Distorsion.h b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Distorsion.h new file mode 100644 index 000000000..9d577ddd8 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Distorsion.h @@ -0,0 +1,61 @@ +/* + ZynAddSubFX - a software synthesizer + + Distorsion.h - Distorsion Effect + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef DISTORSION_H +#define DISTORSION_H + +#include "Effect.h" + +/**Distortion Effect*/ +class Distorsion:public Effect +{ + public: + Distorsion(bool insertion, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize); + ~Distorsion(); + void out(const Stereo &smp); + void setpreset(unsigned char npreset); + void changepar(int npar, unsigned char value); + unsigned char getpar(int npar) const; + void cleanup(void); + void applyfilters(float *efxoutl, float *efxoutr); + + private: + //Parameters + unsigned char Pvolume; //Volume or E/R + unsigned char Pdrive; //the input amplification + unsigned char Plevel; //the output amplification + unsigned char Ptype; //Distorsion type + unsigned char Pnegate; //if the input is negated + unsigned char Plpf; //lowpass filter + unsigned char Phpf; //highpass filter + unsigned char Pstereo; //0=mono, 1=stereo + unsigned char Pprefiltering; //if you want to do the filtering before the distorsion + + void setvolume(unsigned char _Pvolume); + void setlpf(unsigned char _Plpf); + void sethpf(unsigned char _Phpf); + + //Real Parameters + class AnalogFilter * lpfl, *lpfr, *hpfl, *hpfr; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/DynamicFilter.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/DynamicFilter.cpp new file mode 100644 index 000000000..650a882be --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/DynamicFilter.cpp @@ -0,0 +1,312 @@ +/* + ZynAddSubFX - a software synthesizer + + DynamicFilter.cpp - "WahWah" effect and others + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include "DynamicFilter.h" +#include "../DSP/Filter.h" + +DynamicFilter::DynamicFilter(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize) + :Effect(insertion_, efxoutl_, efxoutr_, new FilterParams(0, 64, 64), 0, srate, bufsize), + lfo(srate, bufsize), + Pvolume(110), + Pdepth(0), + Pampsns(90), + Pampsnsinv(0), + Pampsmooth(60), + filterl(NULL), + filterr(NULL) +{ + setpreset(Ppreset); + cleanup(); +} + +DynamicFilter::~DynamicFilter() +{ + delete filterpars; + delete filterl; + delete filterr; +} + + +// Apply the effect +void DynamicFilter::out(const Stereo &smp) +{ + if(filterpars->changed) { + filterpars->changed = false; + cleanup(); + } + + float lfol, lfor; + lfo.effectlfoout(&lfol, &lfor); + lfol *= depth * 5.0f; + lfor *= depth * 5.0f; + const float freq = filterpars->getfreq(); + const float q = filterpars->getq(); + + for(int i = 0; i < buffersize; ++i) { + efxoutl[i] = smp.l[i]; + efxoutr[i] = smp.r[i]; + + const float x = (fabsf(smp.l[i]) + fabsf(smp.r[i])) * 0.5f; + ms1 = ms1 * (1.0f - ampsmooth) + x * ampsmooth + 1e-10; + } + + const float ampsmooth2 = powf(ampsmooth, 0.2f) * 0.3f; + ms2 = ms2 * (1.0f - ampsmooth2) + ms1 * ampsmooth2; + ms3 = ms3 * (1.0f - ampsmooth2) + ms2 * ampsmooth2; + ms4 = ms4 * (1.0f - ampsmooth2) + ms3 * ampsmooth2; + const float rms = (sqrtf(ms4)) * ampsns; + + const float frl = Filter::getrealfreq(freq + lfol + rms); + const float frr = Filter::getrealfreq(freq + lfor + rms); + + filterl->setfreq_and_q(frl, q); + filterr->setfreq_and_q(frr, q); + + filterl->filterout(efxoutl); + filterr->filterout(efxoutr); + + //panning + for(int i = 0; i < buffersize; ++i) { + efxoutl[i] *= pangainL; + efxoutr[i] *= pangainR; + } +} + +// Cleanup the effect +void DynamicFilter::cleanup(void) +{ + reinitfilter(); + ms1 = ms2 = ms3 = ms4 = 0.0f; +} + + +//Parameter control +void DynamicFilter::setdepth(unsigned char _Pdepth) +{ + Pdepth = _Pdepth; + depth = powf(Pdepth / 127.0f, 2.0f); +} + + +void DynamicFilter::setvolume(unsigned char _Pvolume) +{ + Pvolume = _Pvolume; + outvolume = Pvolume / 127.0f; + if(!insertion) + volume = 1.0f; + else + volume = outvolume; +} + +void DynamicFilter::setampsns(unsigned char _Pampsns) +{ + Pampsns = _Pampsns; + ampsns = powf(Pampsns / 127.0f, 2.5f) * 10.0f; + if(Pampsnsinv) + ampsns = -ampsns; + ampsmooth = expf(-Pampsmooth / 127.0f * 10.0f) * 0.99f; +} + +void DynamicFilter::reinitfilter(void) +{ + delete filterl; + delete filterr; + filterl = Filter::generate(filterpars, samplerate, buffersize); + filterr = Filter::generate(filterpars, samplerate, buffersize); +} + +void DynamicFilter::setpreset(unsigned char npreset) +{ + const int PRESET_SIZE = 10; + const int NUM_PRESETS = 5; + unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { + //WahWah + {110, 64, 80, 0, 0, 64, 0, 90, 0, 60}, + //AutoWah + {110, 64, 70, 0, 0, 80, 70, 0, 0, 60}, + //Sweep + {100, 64, 30, 0, 0, 50, 80, 0, 0, 60}, + //VocalMorph1 + {110, 64, 80, 0, 0, 64, 0, 64, 0, 60}, + //VocalMorph1 + {127, 64, 50, 0, 0, 96, 64, 0, 0, 60} + }; + + if(npreset >= NUM_PRESETS) + npreset = NUM_PRESETS - 1; + for(int n = 0; n < PRESET_SIZE; ++n) + changepar(n, presets[npreset][n]); + + filterpars->defaults(); + + switch(npreset) { + case 0: + filterpars->Pcategory = 0; + filterpars->Ptype = 2; + filterpars->Pfreq = 45; + filterpars->Pq = 64; + filterpars->Pstages = 1; + filterpars->Pgain = 64; + break; + case 1: + filterpars->Pcategory = 2; + filterpars->Ptype = 0; + filterpars->Pfreq = 72; + filterpars->Pq = 64; + filterpars->Pstages = 0; + filterpars->Pgain = 64; + break; + case 2: + filterpars->Pcategory = 0; + filterpars->Ptype = 4; + filterpars->Pfreq = 64; + filterpars->Pq = 64; + filterpars->Pstages = 2; + filterpars->Pgain = 64; + break; + case 3: + filterpars->Pcategory = 1; + filterpars->Ptype = 0; + filterpars->Pfreq = 50; + filterpars->Pq = 70; + filterpars->Pstages = 1; + filterpars->Pgain = 64; + + filterpars->Psequencesize = 2; + // "I" + filterpars->Pvowels[0].formants[0].freq = 34; + filterpars->Pvowels[0].formants[0].amp = 127; + filterpars->Pvowels[0].formants[0].q = 64; + filterpars->Pvowels[0].formants[1].freq = 99; + filterpars->Pvowels[0].formants[1].amp = 122; + filterpars->Pvowels[0].formants[1].q = 64; + filterpars->Pvowels[0].formants[2].freq = 108; + filterpars->Pvowels[0].formants[2].amp = 112; + filterpars->Pvowels[0].formants[2].q = 64; + // "A" + filterpars->Pvowels[1].formants[0].freq = 61; + filterpars->Pvowels[1].formants[0].amp = 127; + filterpars->Pvowels[1].formants[0].q = 64; + filterpars->Pvowels[1].formants[1].freq = 71; + filterpars->Pvowels[1].formants[1].amp = 121; + filterpars->Pvowels[1].formants[1].q = 64; + filterpars->Pvowels[1].formants[2].freq = 99; + filterpars->Pvowels[1].formants[2].amp = 117; + filterpars->Pvowels[1].formants[2].q = 64; + break; + case 4: + filterpars->Pcategory = 1; + filterpars->Ptype = 0; + filterpars->Pfreq = 64; + filterpars->Pq = 70; + filterpars->Pstages = 1; + filterpars->Pgain = 64; + + filterpars->Psequencesize = 2; + filterpars->Pnumformants = 2; + filterpars->Pvowelclearness = 0; + + filterpars->Pvowels[0].formants[0].freq = 70; + filterpars->Pvowels[0].formants[0].amp = 127; + filterpars->Pvowels[0].formants[0].q = 64; + filterpars->Pvowels[0].formants[1].freq = 80; + filterpars->Pvowels[0].formants[1].amp = 122; + filterpars->Pvowels[0].formants[1].q = 64; + + filterpars->Pvowels[1].formants[0].freq = 20; + filterpars->Pvowels[1].formants[0].amp = 127; + filterpars->Pvowels[1].formants[0].q = 64; + filterpars->Pvowels[1].formants[1].freq = 100; + filterpars->Pvowels[1].formants[1].amp = 121; + filterpars->Pvowels[1].formants[1].q = 64; + break; + } + +// for (int i=0;i<5;i++){ +// printf("freq=%d amp=%d q=%d\n",filterpars->Pvowels[0].formants[i].freq,filterpars->Pvowels[0].formants[i].amp,filterpars->Pvowels[0].formants[i].q); +// }; + if(insertion == 0) //lower the volume if this is system effect + changepar(0, presets[npreset][0] * 0.5f); + Ppreset = npreset; + reinitfilter(); +} + + +void DynamicFilter::changepar(int npar, unsigned char value) +{ + switch(npar) { + case 0: + setvolume(value); + break; + case 1: + setpanning(value); + break; + case 2: + lfo.Pfreq = value; + lfo.updateparams(); + break; + case 3: + lfo.Prandomness = value; + lfo.updateparams(); + break; + case 4: + lfo.PLFOtype = value; + lfo.updateparams(); + break; + case 5: + lfo.Pstereo = value; + lfo.updateparams(); + break; + case 6: + setdepth(value); + break; + case 7: + setampsns(value); + break; + case 8: + Pampsnsinv = value; + setampsns(Pampsns); + break; + case 9: + Pampsmooth = value; + setampsns(Pampsns); + break; + } +} + +unsigned char DynamicFilter::getpar(int npar) const +{ + switch(npar) { + case 0: return Pvolume; + case 1: return Ppanning; + case 2: return lfo.Pfreq; + case 3: return lfo.Prandomness; + case 4: return lfo.PLFOtype; + case 5: return lfo.Pstereo; + case 6: return Pdepth; + case 7: return Pampsns; + case 8: return Pampsnsinv; + case 9: return Pampsmooth; + default: return 0; + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/DynamicFilter.h b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/DynamicFilter.h new file mode 100644 index 000000000..91d1f5e49 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/DynamicFilter.h @@ -0,0 +1,65 @@ +/* + ZynAddSubFX - a software synthesizer + + DynamicFilter.h - "WahWah" effect and others + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef DYNAMICFILTER_H +#define DYNAMICFILTER_H + +#include "Effect.h" +#include "EffectLFO.h" + +/**DynamicFilter Effect*/ +class DynamicFilter:public Effect +{ + public: + DynamicFilter(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize); + ~DynamicFilter(); + void out(const Stereo &smp); + + void setpreset(unsigned char npreset); + void changepar(int npar, unsigned char value); + unsigned char getpar(int npar) const; + void cleanup(void); + + private: + //Parametrii DynamicFilter + EffectLFO lfo; //lfo-ul DynamicFilter + unsigned char Pvolume; //Volume + unsigned char Pdepth; //the depth of the lfo + unsigned char Pampsns; //how the filter varies according to the input amplitude + unsigned char Pampsnsinv; //if the filter freq is lowered if the input amplitude rises + unsigned char Pampsmooth; //how smooth the input amplitude changes the filter + + //Parameter Control + void setvolume(unsigned char _Pvolume); + void setdepth(unsigned char _Pdepth); + void setampsns(unsigned char _Pampsns); + + void reinitfilter(void); + + //Internal Values + float depth, ampsns, ampsmooth; + + class Filter * filterl, *filterr; + float ms1, ms2, ms3, ms4; //mean squares +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EQ.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EQ.cpp new file mode 100644 index 000000000..e03fcef71 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EQ.cpp @@ -0,0 +1,198 @@ +/* + ZynAddSubFX - a software synthesizer + + EQ.cpp - EQ effect + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include "EQ.h" +#include "../DSP/AnalogFilter.h" + +EQ::EQ(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize) + :Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize) +{ + for(int i = 0; i < MAX_EQ_BANDS; ++i) { + filter[i].Ptype = 0; + filter[i].Pfreq = 64; + filter[i].Pgain = 64; + filter[i].Pq = 64; + filter[i].Pstages = 0; + filter[i].l = new AnalogFilter(6, 1000.0f, 1.0f, 0, srate, bufsize); + filter[i].r = new AnalogFilter(6, 1000.0f, 1.0f, 0, srate, bufsize); + } + //default values + Pvolume = 50; + + setpreset(Ppreset); + cleanup(); +} + + +// Cleanup the effect +void EQ::cleanup(void) +{ + for(int i = 0; i < MAX_EQ_BANDS; ++i) { + filter[i].l->cleanup(); + filter[i].r->cleanup(); + } +} + +//Effect output +void EQ::out(const Stereo &smp) +{ + for(int i = 0; i < buffersize; ++i) { + efxoutl[i] = smp.l[i] * volume; + efxoutr[i] = smp.r[i] * volume; + } + + for(int i = 0; i < MAX_EQ_BANDS; ++i) { + if(filter[i].Ptype == 0) + continue; + filter[i].l->filterout(efxoutl); + filter[i].r->filterout(efxoutr); + } +} + + +//Parameter control +void EQ::setvolume(unsigned char _Pvolume) +{ + Pvolume = _Pvolume; + outvolume = powf(0.005f, (1.0f - Pvolume / 127.0f)) * 10.0f; + volume = (!insertion) ? 1.0f : outvolume; +} + + +void EQ::setpreset(unsigned char npreset) +{ + const int PRESET_SIZE = 1; + const int NUM_PRESETS = 2; + unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { + {67}, //EQ 1 + {67} //EQ 2 + }; + + if(npreset >= NUM_PRESETS) + npreset = NUM_PRESETS - 1; + for(int n = 0; n < PRESET_SIZE; ++n) + changepar(n, presets[npreset][n]); + Ppreset = npreset; +} + + +void EQ::changepar(int npar, unsigned char value) +{ + switch(npar) { + case 0: + setvolume(value); + break; + } + if(npar < 10) + return; + + int nb = (npar - 10) / 5; //number of the band (filter) + if(nb >= MAX_EQ_BANDS) + return; + int bp = npar % 5; //band paramenter + + float tmp; + switch(bp) { + case 0: + filter[nb].Ptype = value; + if(value > 9) + filter[nb].Ptype = 0; //has to be changed if more filters will be added + if(filter[nb].Ptype != 0) { + filter[nb].l->settype(value - 1); + filter[nb].r->settype(value - 1); + } + break; + case 1: + filter[nb].Pfreq = value; + tmp = 600.0f * powf(30.0f, (value - 64.0f) / 64.0f); + filter[nb].l->setfreq(tmp); + filter[nb].r->setfreq(tmp); + break; + case 2: + filter[nb].Pgain = value; + tmp = 30.0f * (value - 64.0f) / 64.0f; + filter[nb].l->setgain(tmp); + filter[nb].r->setgain(tmp); + break; + case 3: + filter[nb].Pq = value; + tmp = powf(30.0f, (value - 64.0f) / 64.0f); + filter[nb].l->setq(tmp); + filter[nb].r->setq(tmp); + break; + case 4: + filter[nb].Pstages = value; + if(value >= MAX_FILTER_STAGES) + filter[nb].Pstages = MAX_FILTER_STAGES - 1; + filter[nb].l->setstages(value); + filter[nb].r->setstages(value); + break; + } +} + +unsigned char EQ::getpar(int npar) const +{ + switch(npar) { + case 0: + return Pvolume; + break; + } + + if(npar < 10) + return 0; + + int nb = (npar - 10) / 5; //number of the band (filter) + if(nb >= MAX_EQ_BANDS) + return 0; + int bp = npar % 5; //band paramenter + switch(bp) { + case 0: + return filter[nb].Ptype; + break; + case 1: + return filter[nb].Pfreq; + break; + case 2: + return filter[nb].Pgain; + break; + case 3: + return filter[nb].Pq; + break; + case 4: + return filter[nb].Pstages; + break; + default: return 0; //in case of bogus parameter number + } +} + + +float EQ::getfreqresponse(float freq) +{ + float resp = 1.0f; + for(int i = 0; i < MAX_EQ_BANDS; ++i) { + if(filter[i].Ptype == 0) + continue; + resp *= filter[i].l->H(freq); + } + return rap2dB(resp * outvolume); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EQ.h b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EQ.h new file mode 100644 index 000000000..b2e9e89a9 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EQ.h @@ -0,0 +1,55 @@ +/* + ZynAddSubFX - a software synthesizer + + EQ.h - EQ Effect + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef EQ_H +#define EQ_H + +#include "Effect.h" + +/**EQ Effect*/ +class EQ:public Effect +{ + public: + EQ(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize); + ~EQ() {} + void out(const Stereo &smp); + void setpreset(unsigned char npreset); + void changepar(int npar, unsigned char value); + unsigned char getpar(int npar) const; + void cleanup(void); + float getfreqresponse(float freq); + + private: + //Parameters + unsigned char Pvolume; + + void setvolume(unsigned char _Pvolume); + + struct { + //parameters + unsigned char Ptype, Pfreq, Pgain, Pq, Pstages; + //internal values + class AnalogFilter * l, *r; + } filter[MAX_EQ_BANDS]; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Echo.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Echo.cpp new file mode 100644 index 000000000..712f291da --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Echo.cpp @@ -0,0 +1,232 @@ +/* + ZynAddSubFX - a software synthesizer + + Echo.cpp - Echo effect + Copyright (C) 2002-2005 Nasca Octavian Paul + Copyright (C) 2009-2010 Mark McCurry + Author: Nasca Octavian Paul + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include "Echo.h" + +#define MAX_DELAY 2 + +Echo::Echo(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize) + :Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize), + samplerate(srate), + Pvolume(50), + Pdelay(60), + Plrdelay(100), + Pfb(40), + Phidamp(60), + delayTime(1), + lrdelay(0), + avgDelay(0), + delay(new float[(int)(MAX_DELAY * srate)], + new float[(int)(MAX_DELAY * srate)]), + old(0.0f), + pos(0), + delta(1), + ndelta(1) +{ + initdelays(); + setpreset(Ppreset); +} + +Echo::~Echo() +{ + delete[] delay.l; + delete[] delay.r; +} + +//Cleanup the effect +void Echo::cleanup(void) +{ + memset(delay.l, 0, MAX_DELAY * samplerate * sizeof(float)); + memset(delay.r, 0, MAX_DELAY * samplerate * sizeof(float)); + old = Stereo(0.0f); +} + +inline int max(int a, int b) +{ + return a > b ? a : b; +} + +//Initialize the delays +void Echo::initdelays(void) +{ + cleanup(); + //number of seconds to delay left chan + float dl = avgDelay - lrdelay; + + //number of seconds to delay right chan + float dr = avgDelay + lrdelay; + + ndelta.l = max(1, (int) (dl * samplerate)); + ndelta.r = max(1, (int) (dr * samplerate)); +} + +//Effect output +void Echo::out(const Stereo &input) +{ + for(int i = 0; i < buffersize; ++i) { + float ldl = delay.l[pos.l]; + float rdl = delay.r[pos.r]; + ldl = ldl * (1.0f - lrcross) + rdl * lrcross; + rdl = rdl * (1.0f - lrcross) + ldl * lrcross; + + efxoutl[i] = ldl * 2.0f; + efxoutr[i] = rdl * 2.0f; + + ldl = input.l[i] * pangainL - ldl * fb; + rdl = input.r[i] * pangainR - rdl * fb; + + //LowPass Filter + old.l = delay.l[(pos.l + delta.l) % (MAX_DELAY * samplerate)] = + ldl * hidamp + old.l * (1.0f - hidamp); + old.r = delay.r[(pos.r + delta.r) % (MAX_DELAY * samplerate)] = + rdl * hidamp + old.r * (1.0f - hidamp); + + //increment + ++pos.l; // += delta.l; + ++pos.r; // += delta.r; + + //ensure that pos is still in bounds + pos.l %= MAX_DELAY * samplerate; + pos.r %= MAX_DELAY * samplerate; + + //adjust delay if needed + delta.l = (15 * delta.l + ndelta.l) / 16; + delta.r = (15 * delta.r + ndelta.r) / 16; + } +} + + +//Parameter control +void Echo::setvolume(unsigned char _Pvolume) +{ + Pvolume = _Pvolume; + + if(insertion == 0) { + outvolume = powf(0.01f, (1.0f - Pvolume / 127.0f)) * 4.0f; + volume = 1.0f; + } + else + volume = outvolume = Pvolume / 127.0f; + if(Pvolume == 0) + cleanup(); +} + +void Echo::setdelay(unsigned char _Pdelay) +{ + Pdelay = _Pdelay; + avgDelay = (Pdelay / 127.0f * 1.5f); //0 .. 1.5 sec + initdelays(); +} + +void Echo::setlrdelay(unsigned char _Plrdelay) +{ + float tmp; + Plrdelay = _Plrdelay; + tmp = + (powf(2.0f, fabsf(Plrdelay - 64.0f) / 64.0f * 9.0f) - 1.0f) / 1000.0f; + if(Plrdelay < 64.0f) + tmp = -tmp; + lrdelay = tmp; + initdelays(); +} + +void Echo::setfb(unsigned char _Pfb) +{ + Pfb = _Pfb; + fb = Pfb / 128.0f; +} + +void Echo::sethidamp(unsigned char _Phidamp) +{ + Phidamp = _Phidamp; + hidamp = 1.0f - Phidamp / 127.0f; +} + +void Echo::setpreset(unsigned char npreset) +{ + const int PRESET_SIZE = 7; + const int NUM_PRESETS = 9; + unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { + {67, 64, 35, 64, 30, 59, 0 }, //Echo 1 + {67, 64, 21, 64, 30, 59, 0 }, //Echo 2 + {67, 75, 60, 64, 30, 59, 10}, //Echo 3 + {67, 60, 44, 64, 30, 0, 0 }, //Simple Echo + {67, 60, 102, 50, 30, 82, 48}, //Canyon + {67, 64, 44, 17, 0, 82, 24}, //Panning Echo 1 + {81, 60, 46, 118, 100, 68, 18}, //Panning Echo 2 + {81, 60, 26, 100, 127, 67, 36}, //Panning Echo 3 + {62, 64, 28, 64, 100, 90, 55} //Feedback Echo + }; + + if(npreset >= NUM_PRESETS) + npreset = NUM_PRESETS - 1; + for(int n = 0; n < PRESET_SIZE; ++n) + changepar(n, presets[npreset][n]); + if(insertion) + setvolume(presets[npreset][0] / 2); //lower the volume if this is insertion effect + Ppreset = npreset; +} + + +void Echo::changepar(int npar, unsigned char value) +{ + switch(npar) { + case 0: + setvolume(value); + break; + case 1: + setpanning(value); + break; + case 2: + setdelay(value); + break; + case 3: + setlrdelay(value); + break; + case 4: + setlrcross(value); + break; + case 5: + setfb(value); + break; + case 6: + sethidamp(value); + break; + } +} + +unsigned char Echo::getpar(int npar) const +{ + switch(npar) { + case 0: return Pvolume; + case 1: return Ppanning; + case 2: return Pdelay; + case 3: return Plrdelay; + case 4: return Plrcross; + case 5: return Pfb; + case 6: return Phidamp; + default: return 0; // in case of bogus parameter number + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Echo.h b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Echo.h new file mode 100644 index 000000000..4eb606b82 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Echo.h @@ -0,0 +1,106 @@ +/* + ZynAddSubFX - a software synthesizer + + Echo.h - Echo Effect + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef ECHO_H +#define ECHO_H + +#include "Effect.h" +#include "../Misc/Stereo.h" + +/**Echo Effect*/ +class Echo:public Effect +{ + public: + Echo(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize); + ~Echo(); + + void out(const Stereo &input); + void setpreset(unsigned char npreset); + /** + * Sets the value of the chosen variable + * + * The possible parameters are: + * -# Volume + * -# Panning + * -# Delay + * -# L/R Delay + * -# L/R Crossover + * -# Feedback + * -# Dampening + * @param npar number of chosen parameter + * @param value the new value + */ + void changepar(int npar, unsigned char value); + + /** + * Gets the specified parameter + * + * The possible parameters are + * -# Volume + * -# Panning + * -# Delay + * -# L/R Delay + * -# L/R Crossover + * -# Feedback + * -# Dampening + * @param npar number of chosen parameter + * @return value of parameter + */ + unsigned char getpar(int npar) const; + int getnumparams(void); + void cleanup(void); + private: + int samplerate; + + //Parameters + unsigned char Pvolume; /**<#1 Volume or Dry/Wetness*/ + unsigned char Pdelay; /**<#3 Delay of the Echo*/ + unsigned char Plrdelay; /**<#4 L/R delay difference*/ + unsigned char Pfb; /**<#6Feedback*/ + unsigned char Phidamp; /**<#7Dampening of the Echo*/ + + void setvolume(unsigned char _Pvolume); + void setdelay(unsigned char _Pdelay); + void setlrdelay(unsigned char _Plrdelay); + void setfb(unsigned char _Pfb); + void sethidamp(unsigned char _Phidamp); + + //Real Parameters + float fb, hidamp; + //Left/Right delay lengths + Stereo delayTime; + float lrdelay; + float avgDelay; + + void initdelays(void); + //2 channel ring buffer + Stereo delay; + Stereo old; + + //position of reading/writing from delaysample + Stereo pos; + //step size for delay buffer + Stereo delta; + Stereo ndelta; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Effect.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Effect.cpp new file mode 100644 index 000000000..bc1c0b006 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Effect.cpp @@ -0,0 +1,67 @@ +/* + ZynAddSubFX - a software synthesizer + + Effect.cpp - this class is inherited by the all effects(Reverb, Echo, ..) + Copyright (C) 2002-2005 Nasca Octavian Paul + Copyright 2011, Alan Calvert + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "Effect.h" +#include "../Params/FilterParams.h" +#include + +Effect::Effect(bool insertion_, float *efxoutl_, float *efxoutr_, + FilterParams *filterpars_, unsigned char Ppreset_, + unsigned int srate, int bufsize) + :Ppreset(Ppreset_), + efxoutl(efxoutl_), + efxoutr(efxoutr_), + filterpars(filterpars_), + insertion(insertion_), + samplerate(srate), + buffersize(bufsize) +{ + alias(); +} + +void Effect::out(float *const smpsl, float *const smpsr) +{ + out(Stereo(smpsl, smpsr)); +} + +void Effect::crossover(float &a, float &b, float crossover) +{ + float tmpa = a; + float tmpb = b; + a = tmpa * (1.0f - crossover) + tmpb * crossover; + b = tmpb * (1.0f - crossover) + tmpa * crossover; +} + +void Effect::setpanning(char Ppanning_) +{ + Ppanning = Ppanning_; + float t = (Ppanning > 0) ? (float)(Ppanning - 1) / 126.0f : 0.0f; + pangainL = cosf(t * PI / 2.0f); + pangainR = cosf((1.0f - t) * PI / 2.0f); +} + +void Effect::setlrcross(char Plrcross_) +{ + Plrcross = Plrcross_; + lrcross = (float)Plrcross / 127.0f; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Effect.h b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Effect.h new file mode 100644 index 000000000..d53bb9b71 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Effect.h @@ -0,0 +1,124 @@ +/* + ZynAddSubFX - a software synthesizer + + Effect.h - this class is inherited by the all effects(Reverb, Echo, ..) + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef EFFECT_H +#define EFFECT_H + +#include "../Misc/Util.h" +#include "../globals.h" +#include "../Params/FilterParams.h" +#include "../Misc/Stereo.h" + +class FilterParams; + +/**this class is inherited by the all effects(Reverb, Echo, ..)*/ +class Effect +{ + public: + /** + * Effect Constructor + * @param insertion_ 1 when it is an insertion Effect + * @param efxoutl_ Effect output buffer Left channel + * @param efxoutr_ Effect output buffer Right channel + * @param filterpars_ pointer to FilterParams array + * @param Ppreset_ chosen preset + * @return Initialized Effect object*/ + Effect(bool insertion_, float *efxoutl_, float *efxoutr_, + FilterParams *filterpars_, unsigned char Ppreset_, + unsigned int srate, int bufsize); + virtual ~Effect() {} + /** + * Choose a preset + * @param npreset number of chosen preset*/ + virtual void setpreset(unsigned char npreset) = 0; + /**Change parameter npar to value + * @param npar chosen parameter + * @param value chosen new value*/ + virtual void changepar(int npar, unsigned char value) = 0; + /**Get the value of parameter npar + * @param npar chosen parameter + * @return the value of the parameter in an unsigned char or 0 if it + * does not exist*/ + virtual unsigned char getpar(int npar) const = 0; + /**Output result of effect based on the given buffers + * + * This method should result in the effect generating its results + * and placing them into the efxoutl and efxoutr buffers. + * Every Effect should overide this method. + * + * @param smpsl Input buffer for the Left channel + * @param smpsr Input buffer for the Right channel + */ + void out(float *const smpsl, float *const smpsr); + virtual void out(const Stereo &smp) = 0; + /**Reset the state of the effect*/ + virtual void cleanup(void) {} + virtual float getfreqresponse(float freq) { return freq; } + + unsigned char Ppreset; /** + +EffectLFO::EffectLFO(float srate_f, float bufsize_f) + :Pfreq(40), + Prandomness(0), + PLFOtype(0), + Pstereo(64), + xl(0.0f), + xr(0.0f), + ampl1(RND), + ampl2(RND), + ampr1(RND), + ampr2(RND), + lfornd(0.0f), + samplerate_f(srate_f), + buffersize_f(bufsize_f) +{ + updateparams(); +} + +EffectLFO::~EffectLFO() {} + +//Update the changed parameters +void EffectLFO::updateparams(void) +{ + float lfofreq = (powf(2.0f, Pfreq / 127.0f * 10.0f) - 1.0f) * 0.03f; + incx = fabsf(lfofreq) * buffersize_f / samplerate_f; + if(incx > 0.49999999f) + incx = 0.499999999f; //Limit the Frequency + + lfornd = Prandomness / 127.0f; + lfornd = (lfornd > 1.0f) ? 1.0f : lfornd; + + if(PLFOtype > 1) + PLFOtype = 1; //this has to be updated if more lfo's are added + lfotype = PLFOtype; + xr = fmodf(xl + (Pstereo - 64.0f) / 127.0f + 1.0f, 1.0f); +} + + +//Compute the shape of the LFO +float EffectLFO::getlfoshape(float x) +{ + float out; + switch(lfotype) { + case 1: //EffectLFO_TRIANGLE + if((x > 0.0f) && (x < 0.25f)) + out = 4.0f * x; + else + if((x > 0.25f) && (x < 0.75f)) + out = 2.0f - 4.0f * x; + else + out = 4.0f * x - 4.0f; + break; + //when adding more, ensure ::updateparams() gets updated + default: + out = cosf(x * 2.0f * PI); //EffectLFO_SINE + } + return out; +} + +//LFO output +void EffectLFO::effectlfoout(float *outl, float *outr) +{ + float out; + + out = getlfoshape(xl); + if((lfotype == 0) || (lfotype == 1)) + out *= (ampl1 + xl * (ampl2 - ampl1)); + xl += incx; + if(xl > 1.0f) { + xl -= 1.0f; + ampl1 = ampl2; + ampl2 = (1.0f - lfornd) + lfornd * RND; + } + *outl = (out + 1.0f) * 0.5f; + + out = getlfoshape(xr); + if((lfotype == 0) || (lfotype == 1)) + out *= (ampr1 + xr * (ampr2 - ampr1)); + xr += incx; + if(xr > 1.0f) { + xr -= 1.0f; + ampr1 = ampr2; + ampr2 = (1.0f - lfornd) + lfornd * RND; + } + *outr = (out + 1.0f) * 0.5f; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectLFO.h b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectLFO.h new file mode 100644 index 000000000..82cbf87f7 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectLFO.h @@ -0,0 +1,53 @@ +/* + ZynAddSubFX - a software synthesizer + + EffectLFO.h - Stereo LFO used by some effects + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef EFFECT_LFO_H +#define EFFECT_LFO_H + +/**LFO for some of the Effect objects + * \todo see if this should inherit LFO*/ +class EffectLFO +{ + public: + EffectLFO(float srate_f, float bufsize_f); + ~EffectLFO(); + void effectlfoout(float *outl, float *outr); + void updateparams(void); + unsigned char Pfreq; + unsigned char Prandomness; + unsigned char PLFOtype; + unsigned char Pstereo; // 64 is centered + private: + float getlfoshape(float x); + + float xl, xr; + float incx; + float ampl1, ampl2, ampr1, ampr2; //necessary for "randomness" + float lfornd; + char lfotype; + + // current setup + float samplerate_f; + float buffersize_f; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectMgr.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectMgr.cpp new file mode 100644 index 000000000..08d4f1d88 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectMgr.cpp @@ -0,0 +1,311 @@ +/* + ZynAddSubFX - a software synthesizer + + EffectMgr.cpp - Effect manager, an interface betwen the program and effects + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "EffectMgr.h" +#include "Effect.h" +#include "Reverb.h" +#include "Echo.h" +#include "Chorus.h" +#include "Distorsion.h" +#include "EQ.h" +#include "DynamicFilter.h" +#include "../Misc/XMLwrapper.h" +#include "../Params/FilterParams.h" + +#include +using namespace std; + +EffectMgr::EffectMgr(const bool insertion_, pthread_mutex_t *mutex_) + :insertion(insertion_), + efxoutl(new float[synth->buffersize]), + efxoutr(new float[synth->buffersize]), + filterpars(NULL), + nefx(0), + efx(NULL), + mutex(mutex_), + dryonly(false) +{ + setpresettype("Peffect"); + memset(efxoutl, 0, synth->bufferbytes); + memset(efxoutr, 0, synth->bufferbytes); + defaults(); +} + + +EffectMgr::~EffectMgr() +{ + delete efx; + delete [] efxoutl; + delete [] efxoutr; +} + +void EffectMgr::defaults(void) +{ + changeeffect(0); + setdryonly(false); +} + +//Change the effect +void EffectMgr::changeeffect(int _nefx) +{ + cleanup(); + if(nefx == _nefx) + return; + nefx = _nefx; + memset(efxoutl, 0, synth->bufferbytes); + memset(efxoutr, 0, synth->bufferbytes); + delete efx; + switch(nefx) { + case 1: + efx = new Reverb(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize); + break; + case 2: + efx = new Echo(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize); + break; + case 3: + efx = new Chorus(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize); + break; + case 4: + efx = new Phaser(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize); + break; + case 5: + efx = new Alienwah(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize); + break; + case 6: + efx = new Distorsion(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize); + break; + case 7: + efx = new EQ(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize); + break; + case 8: + efx = new DynamicFilter(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize); + break; + //put more effect here + default: + efx = NULL; + break; //no effect (thru) + } + + if(efx) + filterpars = efx->filterpars; +} + +//Obtain the effect number +int EffectMgr::geteffect(void) +{ + return nefx; +} + +// Cleanup the current effect +void EffectMgr::cleanup(void) +{ + if(efx) + efx->cleanup(); +} + + +// Get the preset of the current effect +unsigned char EffectMgr::getpreset(void) +{ + if(efx) + return efx->Ppreset; + else + return 0; +} + +// Change the preset of the current effect +void EffectMgr::changepreset_nolock(unsigned char npreset) +{ + if(efx) + efx->setpreset(npreset); +} + +//Change the preset of the current effect(with thread locking) +void EffectMgr::changepreset(unsigned char npreset) +{ + pthread_mutex_lock(mutex); + changepreset_nolock(npreset); + pthread_mutex_unlock(mutex); +} + + +//Change a parameter of the current effect +void EffectMgr::seteffectpar_nolock(int npar, unsigned char value) +{ + if(!efx) + return; + efx->changepar(npar, value); +} + +// Change a parameter of the current effect (with thread locking) +void EffectMgr::seteffectpar(int npar, unsigned char value) +{ + pthread_mutex_lock(mutex); + seteffectpar_nolock(npar, value); + pthread_mutex_unlock(mutex); +} + +//Get a parameter of the current effect +unsigned char EffectMgr::geteffectpar(int npar) +{ + if(!efx) + return 0; + return efx->getpar(npar); +} + +// Apply the effect +void EffectMgr::out(float *smpsl, float *smpsr) +{ + if(!efx) { + if(!insertion) + for(int i = 0; i < synth->buffersize; ++i) { + smpsl[i] = 0.0f; + smpsr[i] = 0.0f; + efxoutl[i] = 0.0f; + efxoutr[i] = 0.0f; + } + return; + } + for(int i = 0; i < synth->buffersize; ++i) { + smpsl[i] += denormalkillbuf[i]; + smpsr[i] += denormalkillbuf[i]; + efxoutl[i] = 0.0f; + efxoutr[i] = 0.0f; + } + efx->out(smpsl, smpsr); + + float volume = efx->volume; + + if(nefx == 7) { //this is need only for the EQ effect + memcpy(smpsl, efxoutl, synth->bufferbytes); + memcpy(smpsr, efxoutr, synth->bufferbytes); + return; + } + + //Insertion effect + if(insertion != 0) { + float v1, v2; + if(volume < 0.5f) { + v1 = 1.0f; + v2 = volume * 2.0f; + } + else { + v1 = (1.0f - volume) * 2.0f; + v2 = 1.0f; + } + if((nefx == 1) || (nefx == 2)) + v2 *= v2; //for Reverb and Echo, the wet function is not liniar + + if(dryonly) //this is used for instrument effect only + for(int i = 0; i < synth->buffersize; ++i) { + smpsl[i] *= v1; + smpsr[i] *= v1; + efxoutl[i] *= v2; + efxoutr[i] *= v2; + } + else // normal instrument/insertion effect + for(int i = 0; i < synth->buffersize; ++i) { + smpsl[i] = smpsl[i] * v1 + efxoutl[i] * v2; + smpsr[i] = smpsr[i] * v1 + efxoutr[i] * v2; + } + } + else // System effect + for(int i = 0; i < synth->buffersize; ++i) { + efxoutl[i] *= 2.0f * volume; + efxoutr[i] *= 2.0f * volume; + smpsl[i] = efxoutl[i]; + smpsr[i] = efxoutr[i]; + } +} + + +// Get the effect volume for the system effect +float EffectMgr::sysefxgetvolume(void) +{ + return (!efx) ? 1.0f : efx->outvolume; +} + + +// Get the EQ response +float EffectMgr::getEQfreqresponse(float freq) +{ + return (nefx == 7) ? efx->getfreqresponse(freq) : 0.0f; +} + + +void EffectMgr::setdryonly(bool value) +{ + dryonly = value; +} + +void EffectMgr::add2XML(XMLwrapper *xml) +{ + xml->addpar("type", geteffect()); + + if(!efx || !geteffect()) + return; + xml->addpar("preset", efx->Ppreset); + + xml->beginbranch("EFFECT_PARAMETERS"); + for(int n = 0; n < 128; ++n) { + int par = geteffectpar(n); + if(par == 0) + continue; + xml->beginbranch("par_no", n); + xml->addpar("par", par); + xml->endbranch(); + } + if(filterpars) { + xml->beginbranch("FILTER"); + filterpars->add2XML(xml); + xml->endbranch(); + } + xml->endbranch(); +} + +void EffectMgr::getfromXML(XMLwrapper *xml) +{ + changeeffect(xml->getpar127("type", geteffect())); + + if(!efx || !geteffect()) + return; + + efx->Ppreset = xml->getpar127("preset", efx->Ppreset); + + if(xml->enterbranch("EFFECT_PARAMETERS")) { + for(int n = 0; n < 128; ++n) { + seteffectpar_nolock(n, 0); //erase effect parameter + if(xml->enterbranch("par_no", n) == 0) + continue; + int par = geteffectpar(n); + seteffectpar_nolock(n, xml->getpar127("par", par)); + xml->exitbranch(); + } + if(filterpars) + if(xml->enterbranch("FILTER")) { + filterpars->getfromXML(xml); + xml->exitbranch(); + } + xml->exitbranch(); + } + cleanup(); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectMgr.h b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectMgr.h new file mode 100644 index 000000000..256a8d81d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/EffectMgr.h @@ -0,0 +1,86 @@ +/* + ZynAddSubFX - a software synthesizer + + EffectMgr.h - Effect manager, an interface betwen the program and effects + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef EFFECTMGR_H +#define EFFECTMGR_H + +#include + +#include "Alienwah.h" +#include "Phaser.h" +#include "../Params/Presets.h" + +class Effect; +class FilterParams; +class XMLwrapper; + +#include "Distorsion.h" +#include "EQ.h" +#include "DynamicFilter.h" +#include "../Misc/XMLwrapper.h" +#include "../Params/FilterParams.h" +#include "../Params/Presets.h" + +/**Effect manager, an interface betwen the program and effects*/ +class EffectMgr:public Presets +{ + public: + EffectMgr(const bool insertion_, pthread_mutex_t *mutex_); + ~EffectMgr(); + + void add2XML(XMLwrapper *xml); + void defaults(void); + void getfromXML(XMLwrapper *xml); + + void out(float *smpsl, float *smpsr); + + void setdryonly(bool value); + + /**get the output(to speakers) volume of the systemeffect*/ + float sysefxgetvolume(void); + + void cleanup(void); + + void changeeffect(int nefx_); + int geteffect(void); + void changepreset(unsigned char npreset); + void changepreset_nolock(unsigned char npreset); + unsigned char getpreset(void); + void seteffectpar(int npar, unsigned char value); + void seteffectpar_nolock(int npar, unsigned char value); + unsigned char geteffectpar(int npar); + + const bool insertion; + float *efxoutl, *efxoutr; + + // used by UI + float getEQfreqresponse(float freq); + + FilterParams *filterpars; + + private: + int nefx; + Effect *efx; + pthread_mutex_t *mutex; + bool dryonly; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Phaser.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Phaser.cpp new file mode 100644 index 000000000..66a9283e3 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Phaser.cpp @@ -0,0 +1,470 @@ +/* + + Phaser.cpp - Phasing and Approximate digital model of an analog JFET phaser. + Analog modeling implemented by Ryan Billing aka Transmogrifox. + ZynAddSubFX - a software synthesizer + + Phaser.cpp - Phaser effect + Copyright (C) 2002-2005 Nasca Octavian Paul + Copyright (C) 2009-2010 Ryan Billing + Copyright (C) 2010-2010 Mark McCurry + Author: Nasca Octavian Paul + Ryan Billing + Mark McCurry + + DSP analog modeling theory & practice largely influenced by various CCRMA publications, particularly works by Julius O. Smith. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include "Phaser.h" + +using namespace std; + +#define PHASER_LFO_SHAPE 2 +#define ONE_ 0.99999f // To prevent LFO ever reaching 1.0f for filter stability purposes +#define ZERO_ 0.00001f // Same idea as above. + +Phaser::Phaser(const int &insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize) + :Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize), lfo(srate, bufsize), old(NULL), xn1(NULL), + yn1(NULL), diff(0.0f), oldgain(0.0f), fb(0.0f) +{ + analog_setup(); + setpreset(Ppreset); + cleanup(); +} + +void Phaser::analog_setup() +{ + //model mismatch between JFET devices + offset[0] = -0.2509303f; + offset[1] = 0.9408924f; + offset[2] = 0.998f; + offset[3] = -0.3486182f; + offset[4] = -0.2762545f; + offset[5] = -0.5215785f; + offset[6] = 0.2509303f; + offset[7] = -0.9408924f; + offset[8] = -0.998f; + offset[9] = 0.3486182f; + offset[10] = 0.2762545f; + offset[11] = 0.5215785f; + + barber = 0; //Deactivate barber pole phasing by default + + mis = 1.0f; + Rmin = 625.0f; // 2N5457 typical on resistance at Vgs = 0 + Rmax = 22000.0f; // Resistor parallel to FET + Rmx = Rmin / Rmax; + Rconst = 1.0f + Rmx; // Handle parallel resistor relationship + C = 0.00000005f; // 50 nF + CFs = 2.0f * samplerate_f * C; + invperiod = 1.0f / buffersize_f; +} + +Phaser::~Phaser() +{ + if(old.l) + delete[] old.l; + if(xn1.l) + delete[] xn1.l; + if(yn1.l) + delete[] yn1.l; + if(old.r) + delete[] old.r; + if(xn1.r) + delete[] xn1.r; + if(yn1.r) + delete[] yn1.r; +} + +/* + * Effect output + */ +void Phaser::out(const Stereo &input) +{ + if(Panalog) + AnalogPhase(input); + else + normalPhase(input); +} + +void Phaser::AnalogPhase(const Stereo &input) +{ + Stereo gain(0.0f), lfoVal(0.0f), mod(0.0f), g(0.0f), b(0.0f), hpf( + 0.0f); + + lfo.effectlfoout(&lfoVal.l, &lfoVal.r); + mod.l = lfoVal.l * width + (depth - 0.5f); + mod.r = lfoVal.r * width + (depth - 0.5f); + + mod.l = limit(mod.l, ZERO_, ONE_); + mod.r = limit(mod.r, ZERO_, ONE_); + + if(Phyper) { + //Triangle wave squared is approximately sin on bottom, tri on top + //Result is exponential sweep more akin to filter in synth with + //exponential generator circuitry. + mod.l *= mod.l; + mod.r *= mod.r; + } + + //g.l,g.r is Vp - Vgs. Typical FET drain-source resistance follows constant/[1-sqrt(Vp - Vgs)] + mod.l = sqrtf(1.0f - mod.l); + mod.r = sqrtf(1.0f - mod.r); + + diff.r = (mod.r - oldgain.r) * invperiod; + diff.l = (mod.l - oldgain.l) * invperiod; + + g = oldgain; + oldgain = mod; + + for(int i = 0; i < buffersize; ++i) { + g.l += diff.l; // Linear interpolation between LFO samples + g.r += diff.r; + + Stereo xn(input.l[i] * pangainL, input.r[i] * pangainR); + + if(barber) { + g.l = fmodf((g.l + 0.25f), ONE_); + g.r = fmodf((g.r + 0.25f), ONE_); + } + + xn.l = applyPhase(xn.l, g.l, fb.l, hpf.l, yn1.l, xn1.l); + xn.r = applyPhase(xn.r, g.r, fb.r, hpf.r, yn1.r, xn1.r); + + + fb.l = xn.l * feedback; + fb.r = xn.r * feedback; + efxoutl[i] = xn.l; + efxoutr[i] = xn.r; + } + + if(Poutsub) { + invSignal(efxoutl, buffersize); + invSignal(efxoutr, buffersize); + } +} + +float Phaser::applyPhase(float x, float g, float fb, + float &hpf, float *yn1, float *xn1) +{ + for(int j = 0; j < Pstages; ++j) { //Phasing routine + mis = 1.0f + offsetpct * offset[j]; + + //This is symmetrical. + //FET is not, so this deviates slightly, however sym dist. is + //better sounding than a real FET. + float d = (1.0f + 2.0f * (0.25f + g) * hpf * hpf * distortion) * mis; + Rconst = 1.0f + mis * Rmx; + + // This is 1/R. R is being modulated to control filter fc. + float b = (Rconst - g) / (d * Rmin); + float gain = (CFs - b) / (CFs + b); + yn1[j] = gain * (x + yn1[j]) - xn1[j]; + + //high pass filter: + //Distortion depends on the high-pass part of the AP stage. + hpf = yn1[j] + (1.0f - gain) * xn1[j]; + + xn1[j] = x; + x = yn1[j]; + if(j == 1) + x += fb; //Insert feedback after first phase stage + } + return x; +} +void Phaser::normalPhase(const Stereo &input) +{ + Stereo gain(0.0f), lfoVal(0.0f); + + lfo.effectlfoout(&lfoVal.l, &lfoVal.r); + gain.l = + (expf(lfoVal.l + * PHASER_LFO_SHAPE) - 1) / (expf(PHASER_LFO_SHAPE) - 1.0f); + gain.r = + (expf(lfoVal.r + * PHASER_LFO_SHAPE) - 1) / (expf(PHASER_LFO_SHAPE) - 1.0f); + + gain.l = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * gain.l * depth; + gain.r = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * gain.r * depth; + + gain.l = limit(gain.l, ZERO_, ONE_); + gain.r = limit(gain.r, ZERO_, ONE_); + + for(int i = 0; i < buffersize; ++i) { + float x = (float) i / buffersize_f; + float x1 = 1.0f - x; + //TODO think about making panning an external feature + Stereo xn(input.l[i] * pangainL + fb.l, + input.r[i] * pangainR + fb.r); + + Stereo g(gain.l * x + oldgain.l * x1, + gain.r * x + oldgain.r * x1); + + xn.l = applyPhase(xn.l, g.l, old.l); + xn.r = applyPhase(xn.r, g.r, old.r); + + //Left/Right crossing + crossover(xn.l, xn.r, lrcross); + + fb.l = xn.l * feedback; + fb.r = xn.r * feedback; + efxoutl[i] = xn.l; + efxoutr[i] = xn.r; + } + + oldgain = gain; + + if(Poutsub) { + invSignal(efxoutl, buffersize); + invSignal(efxoutr, buffersize); + } +} + +float Phaser::applyPhase(float x, float g, float *old) +{ + for(int j = 0; j < Pstages * 2; ++j) { //Phasing routine + float tmp = old[j]; + old[j] = g * tmp + x; + x = tmp - g * old[j]; + } + return x; +} + +/* + * Cleanup the effect + */ +void Phaser::cleanup() +{ + fb = oldgain = Stereo(0.0f); + for(int i = 0; i < Pstages * 2; ++i) { + old.l[i] = 0.0f; + old.r[i] = 0.0f; + } + for(int i = 0; i < Pstages; ++i) { + xn1.l[i] = 0.0f; + yn1.l[i] = 0.0f; + xn1.r[i] = 0.0f; + yn1.r[i] = 0.0f; + } +} + +/* + * Parameter control + */ +void Phaser::setwidth(unsigned char Pwidth) +{ + this->Pwidth = Pwidth; + width = ((float)Pwidth / 127.0f); +} + +void Phaser::setfb(unsigned char Pfb) +{ + this->Pfb = Pfb; + feedback = (float) (Pfb - 64) / 64.2f; +} + +void Phaser::setvolume(unsigned char Pvolume) +{ + this->Pvolume = Pvolume; + outvolume = Pvolume / 127.0f; + if(insertion == 0) + volume = 1.0f; + else + volume = outvolume; +} + +void Phaser::setdistortion(unsigned char Pdistortion) +{ + this->Pdistortion = Pdistortion; + distortion = (float)Pdistortion / 127.0f; +} + +void Phaser::setoffset(unsigned char Poffset) +{ + this->Poffset = Poffset; + offsetpct = (float)Poffset / 127.0f; +} + +void Phaser::setstages(unsigned char Pstages) +{ + if(old.l) + delete[] old.l; + if(xn1.l) + delete[] xn1.l; + if(yn1.l) + delete[] yn1.l; + if(old.r) + delete[] old.r; + if(xn1.r) + delete[] xn1.r; + if(yn1.r) + delete[] yn1.r; + + + this->Pstages = min(MAX_PHASER_STAGES, (int)Pstages); + + old = Stereo(new float[Pstages * 2], + new float[Pstages * 2]); + + xn1 = Stereo(new float[Pstages], + new float[Pstages]); + + yn1 = Stereo(new float[Pstages], + new float[Pstages]); + + cleanup(); +} + +void Phaser::setphase(unsigned char Pphase) +{ + this->Pphase = Pphase; + phase = (Pphase / 127.0f); +} + +void Phaser::setdepth(unsigned char Pdepth) +{ + this->Pdepth = Pdepth; + depth = (float)(Pdepth) / 127.0f; +} + + +void Phaser::setpreset(unsigned char npreset) +{ + const int PRESET_SIZE = 15; + const int NUM_PRESETS = 12; + unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { + //Phaser + //0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + {64, 64, 36, 0, 0, 64, 110, 64, 1, 0, 0, 20, + 0, 0, + 0 }, + {64, 64, 35, 0, 0, 88, 40, 64, 3, 0, 0, 20, 0, 0, + 0 }, + {64, 64, 31, 0, 0, 66, 68, 107, 2, 0, 0, 20, 0, 0, + 0 }, + {39, 64, 22, 0, 0, 66, 67, 10, 5, 0, 1, 20, 0, 0, + 0 }, + {64, 64, 20, 0, 1, 110, 67, 78, 10, 0, 0, 20, 0, 0, + 0 }, + {64, 64, 53, 100, 0, 58, 37, 78, 3, 0, 0, 20, 0, 0, + 0 }, + //APhaser + //0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + {64, 64, 14, 0, 1, 64, 64, 40, 4, 10, 0, 110,1, 20, + 1 }, + {64, 64, 14, 5, 1, 64, 70, 40, 6, 10, 0, 110,1, 20, + 1 }, + {64, 64, 9, 0, 0, 64, 60, 40, 8, 10, 0, 40, 0, 20, + 1 }, + {64, 64, 14, 10, 0, 64, 45, 80, 7, 10, 1, 110,1, 20, + 1 }, + {25, 64, 127, 10, 0, 64, 25, 16, 8, 100, 0, 25, 0, 20, + 1 }, + {64, 64, 1, 10, 1, 64, 70, 40, 12, 10, 0, 110,1, 20, + 1 } + }; + if(npreset >= NUM_PRESETS) + npreset = NUM_PRESETS - 1; + for(int n = 0; n < PRESET_SIZE; ++n) + changepar(n, presets[npreset][n]); + Ppreset = npreset; +} + + +void Phaser::changepar(int npar, unsigned char value) +{ + switch(npar) { + case 0: + setvolume(value); + break; + case 1: + setpanning(value); + break; + case 2: + lfo.Pfreq = value; + lfo.updateparams(); + break; + case 3: + lfo.Prandomness = value; + lfo.updateparams(); + break; + case 4: + lfo.PLFOtype = value; + lfo.updateparams(); + barber = (2 == value); + break; + case 5: + lfo.Pstereo = value; + lfo.updateparams(); + break; + case 6: + setdepth(value); + break; + case 7: + setfb(value); + break; + case 8: + setstages(value); + break; + case 9: + setlrcross(value); + setoffset(value); + break; + case 10: + Poutsub = min((int)value, 1); + break; + case 11: + setphase(value); + setwidth(value); + break; + case 12: + Phyper = min((int)value, 1); + break; + case 13: + setdistortion(value); + break; + case 14: + Panalog = value; + break; + } +} + +unsigned char Phaser::getpar(int npar) const +{ + switch(npar) { + case 0: return Pvolume; + case 1: return Ppanning; + case 2: return lfo.Pfreq; + case 3: return lfo.Prandomness; + case 4: return lfo.PLFOtype; + case 5: return lfo.Pstereo; + case 6: return Pdepth; + case 7: return Pfb; + case 8: return Pstages; + case 9: return Plrcross; + return Poffset; //same + case 10: return Poutsub; + case 11: return Pphase; + return Pwidth; //same + case 12: return Phyper; + case 13: return Pdistortion; + case 14: return Panalog; + default: return 0; + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Phaser.h b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Phaser.h new file mode 100644 index 000000000..47961870d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Phaser.h @@ -0,0 +1,98 @@ +/* + ZynAddSubFX - a software synthesizer + + Phaser.h - Phaser effect + Copyright (C) 2002-2005 Nasca Octavian Paul + Copyright (C) 2009-2010 Ryan Billing + Copyright (C) 2010-2010 Mark McCurry + Author: Nasca Octavian Paul + Ryan Billing + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef PHASER_H +#define PHASER_H +#include "../globals.h" +#include "Effect.h" +#include "EffectLFO.h" + +#define MAX_PHASER_STAGES 12 + +class Phaser:public Effect +{ + public: + Phaser(const int &insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize); + ~Phaser(); + void out(const Stereo &input); + void setpreset(unsigned char npreset); + void changepar(int npar, unsigned char value); + unsigned char getpar(int npar) const; + void cleanup(); + + private: + //Phaser parameters + EffectLFO lfo; //Phaser modulator + unsigned char Pvolume; //Used to set wet/dry mix + unsigned char Pdistortion; //Model distortion added by FET element + unsigned char Pdepth; //Depth of phaser sweep + unsigned char Pwidth; //Phaser width (LFO amplitude) + unsigned char Pfb; //feedback + unsigned char Poffset; //Model mismatch between variable resistors + unsigned char Pstages; //Number of first-order All-Pass stages + unsigned char Poutsub; //if I wish to subtract the output instead of adding + unsigned char Pphase; + unsigned char Phyper; //lfo^2 -- converts tri into hyper-sine + unsigned char Panalog; + + //Control parameters + void setvolume(unsigned char Pvolume); + void setdepth(unsigned char Pdepth); + void setfb(unsigned char Pfb); + void setdistortion(unsigned char Pdistortion); + void setwidth(unsigned char Pwidth); + void setoffset(unsigned char Poffset); + void setstages(unsigned char Pstages); + void setphase(unsigned char Pphase); + + //Internal Variables + bool barber; //Barber pole phasing flag + float distortion, width, offsetpct; + float feedback, depth, phase; + Stereo old, xn1, yn1; + Stereo diff, oldgain, fb; + float invperiod; + float offset[12]; + + float mis; + float Rmin; // 3N5457 typical on resistance at Vgs = 0 + float Rmax; // Resistor parallel to FET + float Rmx; // Rmin/Rmax to avoid division in loop + float Rconst; // Handle parallel resistor relationship + float C; // Capacitor + float CFs; // A constant derived from capacitor and resistor relationships + + void analog_setup(); + void AnalogPhase(const Stereo &input); + //analog case + float applyPhase(float x, float g, float fb, + float &hpf, float *yn1, float *xn1); + + void normalPhase(const Stereo &input); + float applyPhase(float x, float g, float *old); +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Reverb.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Reverb.cpp new file mode 100644 index 000000000..74961b2f5 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Reverb.cpp @@ -0,0 +1,491 @@ +/* + ZynAddSubFX - a software synthesizer + + Reverb.cpp - Reverberation effect + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "Reverb.h" +#include "../Misc/Util.h" +#include "../DSP/AnalogFilter.h" +#include "../DSP/Unison.h" +#include + +Reverb::Reverb(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize) + :Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize), + // defaults + Pvolume(48), + Ptime(64), + Pidelay(40), + Pidelayfb(0), + Plpf(127), + Phpf(0), + Plohidamp(80), + Ptype(1), + Proomsize(64), + Pbandwidth(30), + roomsize(1.0f), + rs(1.0f), + bandwidth(NULL), + idelay(NULL), + lpf(NULL), + hpf(NULL) // no filter +{ + for(int i = 0; i < REV_COMBS * 2; ++i) { + comblen[i] = 800 + (int)(RND * 1400.0f); + combk[i] = 0; + lpcomb[i] = 0; + combfb[i] = -0.97f; + comb[i] = NULL; + } + + for(int i = 0; i < REV_APS * 2; ++i) { + aplen[i] = 500 + (int)(RND * 500.0f); + apk[i] = 0; + ap[i] = NULL; + } + setpreset(Ppreset); + cleanup(); //do not call this before the comb initialisation +} + + +Reverb::~Reverb() +{ + delete [] idelay; + delete hpf; + delete lpf; + + for(int i = 0; i < REV_APS * 2; ++i) + delete [] ap[i]; + for(int i = 0; i < REV_COMBS * 2; ++i) + delete [] comb[i]; + + if(bandwidth) + delete bandwidth; +} + +//Cleanup the effect +void Reverb::cleanup(void) +{ + int i, j; + for(i = 0; i < REV_COMBS * 2; ++i) { + lpcomb[i] = 0.0f; + for(j = 0; j < comblen[i]; ++j) + comb[i][j] = 0.0f; + } + + for(i = 0; i < REV_APS * 2; ++i) + for(j = 0; j < aplen[i]; ++j) + ap[i][j] = 0.0f; + + if(idelay) + for(i = 0; i < idelaylen; ++i) + idelay[i] = 0.0f; + if(hpf) + hpf->cleanup(); + if(lpf) + lpf->cleanup(); +} + +//Process one channel; 0=left, 1=right +void Reverb::processmono(int ch, float *output, float *inputbuf) +{ + //todo: implement the high part from lohidamp + + for(int j = REV_COMBS * ch; j < REV_COMBS * (ch + 1); ++j) { + int &ck = combk[j]; + const int comblength = comblen[j]; + float &lpcombj = lpcomb[j]; + + for(int i = 0; i < buffersize; ++i) { + float fbout = comb[j][ck] * combfb[j]; + fbout = fbout * (1.0f - lohifb) + lpcombj * lohifb; + lpcombj = fbout; + + comb[j][ck] = inputbuf[i] + fbout; + output[i] += fbout; + + if((++ck) >= comblength) + ck = 0; + } + } + + for(int j = REV_APS * ch; j < REV_APS * (1 + ch); ++j) { + int &ak = apk[j]; + const int aplength = aplen[j]; + for(int i = 0; i < buffersize; ++i) { + float tmp = ap[j][ak]; + ap[j][ak] = 0.7f * tmp + output[i]; + output[i] = tmp - 0.7f * ap[j][ak]; + if((++ak) >= aplength) + ak = 0; + } + } +} + +//Effect output +void Reverb::out(const Stereo &smp) +{ + if(!Pvolume && insertion) + return; + + float inputbuf[buffersize]; + for(int i = 0; i < buffersize; ++i) + inputbuf[i] = (smp.l[i] + smp.r[i]) / 2.0f; + + if(idelay) + for(int i = 0; i < buffersize; ++i) { + //Initial delay r + float tmp = inputbuf[i] + idelay[idelayk] * idelayfb; + inputbuf[i] = idelay[idelayk]; + idelay[idelayk] = tmp; + idelayk++; + if(idelayk >= idelaylen) + idelayk = 0; + } + + if(bandwidth) + bandwidth->process(buffersize, inputbuf); + + if(lpf) + lpf->filterout(inputbuf); + if(hpf) + hpf->filterout(inputbuf); + + processmono(0, efxoutl, inputbuf); //left + processmono(1, efxoutr, inputbuf); //right + + float lvol = rs / REV_COMBS * pangainL; + float rvol = rs / REV_COMBS * pangainR; + if(insertion != 0) { + lvol *= 2.0f; + rvol *= 2.0f; + } + for(int i = 0; i < buffersize; ++i) { + efxoutl[i] *= lvol; + efxoutr[i] *= rvol; + } +} + + +//Parameter control +void Reverb::setvolume(unsigned char _Pvolume) +{ + Pvolume = _Pvolume; + if(!insertion) { + outvolume = powf(0.01f, (1.0f - Pvolume / 127.0f)) * 4.0f; + volume = 1.0f; + } + else { + volume = outvolume = Pvolume / 127.0f; + if(Pvolume == 0) + cleanup(); + } +} + +void Reverb::settime(unsigned char _Ptime) +{ + Ptime = _Ptime; + float t = powf(60.0f, Ptime / 127.0f) - 0.97f; + + for(int i = 0; i < REV_COMBS * 2; ++i) + combfb[i] = + -expf((float)comblen[i] / samplerate_f * logf(0.001f) / t); + //the feedback is negative because it removes the DC +} + +void Reverb::setlohidamp(unsigned char _Plohidamp) +{ + Plohidamp = (_Plohidamp < 64) ? 64 : _Plohidamp; + //remove this when the high part from lohidamp is added + if(Plohidamp == 64) { + lohidamptype = 0; + lohifb = 0.0f; + } + else { + if(Plohidamp < 64) + lohidamptype = 1; + if(Plohidamp > 64) + lohidamptype = 2; + float x = fabsf((float)(Plohidamp - 64) / 64.1f); + lohifb = x * x; + } +} + +void Reverb::setidelay(unsigned char _Pidelay) +{ + Pidelay = _Pidelay; + float delay = powf(50.0f * Pidelay / 127.0f, 2.0f) - 1.0f; + + if(idelay) + delete [] idelay; + idelay = NULL; + + idelaylen = (int) (samplerate_f * delay / 1000); + if(idelaylen > 1) { + idelayk = 0; + idelay = new float[idelaylen]; + memset(idelay, 0, idelaylen * sizeof(float)); + } +} + +void Reverb::setidelayfb(unsigned char _Pidelayfb) +{ + Pidelayfb = _Pidelayfb; + idelayfb = Pidelayfb / 128.0f; +} + +void Reverb::sethpf(unsigned char _Phpf) +{ + Phpf = _Phpf; + if(Phpf == 0) { //No HighPass + if(hpf) + delete hpf; + hpf = NULL; + } + else { + float fr = expf(powf(Phpf / 127.0f, 0.5f) * logf(10000.0f)) + 20.0f; + if(hpf == NULL) + hpf = new AnalogFilter(3, fr, 1, 0, samplerate, buffersize); + else + hpf->setfreq(fr); + } +} + +void Reverb::setlpf(unsigned char _Plpf) +{ + Plpf = _Plpf; + if(Plpf == 127) { //No LowPass + if(lpf) + delete lpf; + lpf = NULL; + } + else { + float fr = expf(powf(Plpf / 127.0f, 0.5f) * logf(25000.0f)) + 40.0f; + if(!lpf) + lpf = new AnalogFilter(2, fr, 1, 0, samplerate, buffersize); + else + lpf->setfreq(fr); + } +} + +void Reverb::settype(unsigned char _Ptype) +{ + Ptype = _Ptype; + const int NUM_TYPES = 3; + const int combtunings[NUM_TYPES][REV_COMBS] = { + //this is unused (for random) + {0, 0, 0, 0, 0, 0, 0, 0 }, + //Freeverb by Jezar at Dreampoint + {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }, + //duplicate of Freeverb by Jezar at Dreampoint + {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 } + }; + + const int aptunings[NUM_TYPES][REV_APS] = { + //this is unused (for random) + {0, 0, 0, 0 }, + //Freeverb by Jezar at Dreampoint + {225, 341, 441, 556 }, + //duplicate of Freeverb by Jezar at Dreampoint + {225, 341, 441, 556 } + }; + + if(Ptype >= NUM_TYPES) + Ptype = NUM_TYPES - 1; + + // adjust the combs according to the samplerate + float samplerate_adjust = samplerate_f / 44100.0f; + float tmp; + for(int i = 0; i < REV_COMBS * 2; ++i) { + if(Ptype == 0) + tmp = 800.0f + (int)(RND * 1400.0f); + else + tmp = combtunings[Ptype][i % REV_COMBS]; + tmp *= roomsize; + if(i > REV_COMBS) + tmp += 23.0f; + tmp *= samplerate_adjust; //adjust the combs according to the samplerate + if(tmp < 10.0f) + tmp = 10.0f; + comblen[i] = (int) tmp; + combk[i] = 0; + lpcomb[i] = 0; + if(comb[i]) + delete [] comb[i]; + comb[i] = new float[comblen[i]]; + } + + for(int i = 0; i < REV_APS * 2; ++i) { + if(Ptype == 0) + tmp = 500 + (int)(RND * 500.0f); + else + tmp = aptunings[Ptype][i % REV_APS]; + tmp *= roomsize; + if(i > REV_APS) + tmp += 23.0f; + tmp *= samplerate_adjust; //adjust the combs according to the samplerate + if(tmp < 10) + tmp = 10; + aplen[i] = (int) tmp; + apk[i] = 0; + if(ap[i]) + delete [] ap[i]; + ap[i] = new float[aplen[i]]; + } + delete bandwidth; + bandwidth = NULL; + if(Ptype == 2) { //bandwidth + //TODO the size of the unison buffer may be too small, though this has + //not been verified yet. + //As this cannot be resized in a RT context, a good upper bound should + //be found + bandwidth = new Unison(buffersize / 4 + 1, 2.0f, samplerate_f); + bandwidth->setSize(50); + bandwidth->setBaseFrequency(1.0f); + } + settime(Ptime); + cleanup(); +} + +void Reverb::setroomsize(unsigned char _Proomsize) +{ + Proomsize = _Proomsize; + if(!Proomsize) + this->Proomsize = 64; //this is because the older versions consider roomsize=0 + roomsize = (this->Proomsize - 64.0f) / 64.0f; + if(roomsize > 0.0f) + roomsize *= 2.0f; + roomsize = powf(10.0f, roomsize); + rs = sqrtf(roomsize); + settype(Ptype); +} + +void Reverb::setbandwidth(unsigned char _Pbandwidth) +{ + Pbandwidth = _Pbandwidth; + float v = Pbandwidth / 127.0f; + if(bandwidth) + bandwidth->setBandwidth(powf(v, 2.0f) * 200.0f); +} + +void Reverb::setpreset(unsigned char npreset) +{ + const int PRESET_SIZE = 13; + const int NUM_PRESETS = 13; + unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { + //Cathedral1 + {80, 64, 63, 24, 0, 0, 0, 85, 5, 83, 1, 64, 20}, + //Cathedral2 + {80, 64, 69, 35, 0, 0, 0, 127, 0, 71, 0, 64, 20}, + //Cathedral3 + {80, 64, 69, 24, 0, 0, 0, 127, 75, 78, 1, 85, 20}, + //Hall1 + {90, 64, 51, 10, 0, 0, 0, 127, 21, 78, 1, 64, 20}, + //Hall2 + {90, 64, 53, 20, 0, 0, 0, 127, 75, 71, 1, 64, 20}, + //Room1 + {100, 64, 33, 0, 0, 0, 0, 127, 0, 106, 0, 30, 20}, + //Room2 + {100, 64, 21, 26, 0, 0, 0, 62, 0, 77, 1, 45, 20}, + //Basement + {110, 64, 14, 0, 0, 0, 0, 127, 5, 71, 0, 25, 20}, + //Tunnel + {85, 80, 84, 20, 42, 0, 0, 51, 0, 78, 1, 105, 20}, + //Echoed1 + {95, 64, 26, 60, 71, 0, 0, 114, 0, 64, 1, 64, 20}, + //Echoed2 + {90, 64, 40, 88, 71, 0, 0, 114, 0, 88, 1, 64, 20}, + //VeryLong1 + {90, 64, 93, 15, 0, 0, 0, 114, 0, 77, 0, 95, 20}, + //VeryLong2 + {90, 64, 111, 30, 0, 0, 0, 114, 90, 74, 1, 80, 20} + }; + + if(npreset >= NUM_PRESETS) + npreset = NUM_PRESETS - 1; + for(int n = 0; n < PRESET_SIZE; ++n) + changepar(n, presets[npreset][n]); + if(insertion) + changepar(0, presets[npreset][0] / 2); //lower the volume if reverb is insertion effect + Ppreset = npreset; +} + + +void Reverb::changepar(int npar, unsigned char value) +{ + switch(npar) { + case 0: + setvolume(value); + break; + case 1: + setpanning(value); + break; + case 2: + settime(value); + break; + case 3: + setidelay(value); + break; + case 4: + setidelayfb(value); + break; +// case 5: +// setrdelay(value); +// break; +// case 6: +// seterbalance(value); +// break; + case 7: + setlpf(value); + break; + case 8: + sethpf(value); + break; + case 9: + setlohidamp(value); + break; + case 10: + settype(value); + break; + case 11: + setroomsize(value); + break; + case 12: + setbandwidth(value); + break; + } +} + +unsigned char Reverb::getpar(int npar) const +{ + switch(npar) { + case 0: return Pvolume; + case 1: return Ppanning; + case 2: return Ptime; + case 3: return Pidelay; + case 4: return Pidelayfb; + case 7: return Plpf; + case 8: return Phpf; + case 9: return Plohidamp; + case 10: return Ptype; + case 11: return Proomsize; + case 12: return Pbandwidth; + default: return 0; + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Reverb.h b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Reverb.h new file mode 100644 index 000000000..5363759b6 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Effects/Reverb.h @@ -0,0 +1,94 @@ +/* + ZynAddSubFX - a software synthesizer + + Reverb.h - Reverberation effect + Copyright (C) 2002-2009 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef REVERB_H +#define REVERB_H + +#include "Effect.h" + +#define REV_COMBS 8 +#define REV_APS 4 + +/**Creates Reverberation Effects*/ +class Reverb:public Effect +{ + public: + Reverb(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize); + ~Reverb(); + void out(const Stereo &smp); + void cleanup(void); + + void setpreset(unsigned char npreset); + void changepar(int npar, unsigned char value); + unsigned char getpar(int npar) const; + + private: + //Parametrii + unsigned char Pvolume; + unsigned char Ptime; //duration + unsigned char Pidelay; //initial delay + unsigned char Pidelayfb; //initial feedback + unsigned char Plpf; + unsigned char Phpf; + unsigned char Plohidamp; //Low/HighFrequency Damping + unsigned char Ptype; //reverb type + unsigned char Proomsize; //room size + unsigned char Pbandwidth; //bandwidth + + //parameter control + void setvolume(unsigned char _Pvolume); + void settime(unsigned char _Ptime); + void setlohidamp(unsigned char _Plohidamp); + void setidelay(unsigned char _Pidelay); + void setidelayfb(unsigned char _Pidelayfb); + void sethpf(unsigned char _Phpf); + void setlpf(unsigned char _Plpf); + void settype(unsigned char _Ptype); + void setroomsize(unsigned char _Proomsize); + void setbandwidth(unsigned char _Pbandwidth); + void processmono(int ch, float *output, float *inputbuf); + + + //Parameters + int lohidamptype; //0=disable, 1=highdamp (lowpass), 2=lowdamp (highpass) + int idelaylen; + int idelayk; + float lohifb; + float idelayfb; + float roomsize; + float rs; //rs is used to "normalise" the volume according to the roomsize + int comblen[REV_COMBS * 2]; + int aplen[REV_APS * 2]; + class Unison * bandwidth; + + //Internal Variables + float *comb[REV_COMBS * 2]; + int combk[REV_COMBS * 2]; + float combfb[REV_COMBS * 2]; //feedback-ul fiecarui filtru "comb" + float lpcomb[REV_COMBS * 2]; //pentru Filtrul LowPass + float *ap[REV_APS * 2]; + int apk[REV_APS * 2]; + float *idelay; + class AnalogFilter * lpf, *hpf; //filters +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.cpp new file mode 100644 index 000000000..a1f9f5d6b --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.cpp @@ -0,0 +1,473 @@ +/* + ZynAddSubFX - a software synthesizer + + Bank.cpp - Instrument Bank + Copyright (C) 2002-2005 Nasca Octavian Paul + Copyright (C) 2010-2010 Mark McCurry + Author: Nasca Octavian Paul + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "Bank.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Config.h" +#include "Util.h" +#include "Part.h" + +#define INSTRUMENT_EXTENSION ".xiz" + +//if this file exists into a directory, this make the directory to be considered as a bank, even if it not contains a instrument file +#define FORCE_BANK_DIR_FILE ".bankdir" + +using namespace std; + +Bank::Bank() + :defaultinsname(" ") +{ + clearbank(); + bankfiletitle = dirname; + loadbank(config.cfg.currentBankDir); +} + +Bank::~Bank() +{ + clearbank(); +} + +/* + * Get the name of an instrument from the bank + */ +string Bank::getname(unsigned int ninstrument) +{ + if(emptyslot(ninstrument)) + return defaultinsname; + return ins[ninstrument].name; +} + +/* + * Get the numbered name of an instrument from the bank + */ +string Bank::getnamenumbered(unsigned int ninstrument) +{ + if(emptyslot(ninstrument)) + return defaultinsname; + + return stringFrom(ninstrument + 1) + ". " + getname(ninstrument); +} + +/* + * Changes the name of an instrument (and the filename) + */ +void Bank::setname(unsigned int ninstrument, const string &newname, int newslot) +{ + if(emptyslot(ninstrument)) + return; + + string newfilename; + char tmpfilename[100 + 1]; + tmpfilename[100] = 0; + + if(newslot >= 0) + snprintf(tmpfilename, 100, "%4d-%s", newslot + 1, newname.c_str()); + else + snprintf(tmpfilename, 100, "%4d-%s", ninstrument + 1, newname.c_str()); + + //add the zeroes at the start of filename + for(int i = 0; i < 4; ++i) + if(tmpfilename[i] == ' ') + tmpfilename[i] = '0'; + + newfilename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz"; + + rename(ins[ninstrument].filename.c_str(), newfilename.c_str()); + + ins[ninstrument].filename = newfilename; + ins[ninstrument].name = newname; +} + +/* + * Check if there is no instrument on a slot from the bank + */ +bool Bank::emptyslot(unsigned int ninstrument) +{ + if(ninstrument >= BANK_SIZE) + return true; + if(ins[ninstrument].filename.empty()) + return true; + + if(ins[ninstrument].used) + return false; + else + return true; +} + +/* + * Removes the instrument from the bank + */ +void Bank::clearslot(unsigned int ninstrument) +{ + if(emptyslot(ninstrument)) + return; + + remove(ins[ninstrument].filename.c_str()); + deletefrombank(ninstrument); +} + +/* + * Save the instrument to a slot + */ +void Bank::savetoslot(unsigned int ninstrument, Part *part) +{ + clearslot(ninstrument); + + const int maxfilename = 200; + char tmpfilename[maxfilename + 20]; + ZERO(tmpfilename, maxfilename + 20); + + snprintf(tmpfilename, + maxfilename, + "%4d-%s", + ninstrument + 1, + (char *)part->Pname); + + //add the zeroes at the start of filename + for(int i = 0; i < 4; ++i) + if(tmpfilename[i] == ' ') + tmpfilename[i] = '0'; + + string filename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz"; + + remove(filename.c_str()); + part->saveXML(filename.c_str()); + addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz", (char *) part->Pname); +} + +/* + * Loads the instrument from the bank + */ +void Bank::loadfromslot(unsigned int ninstrument, Part *part) +{ + if(emptyslot(ninstrument)) + return; + + part->AllNotesOff(); + part->defaultsinstrument(); + + part->loadXMLinstrument(ins[ninstrument].filename.c_str()); +} + +/* + * Makes current a bank directory + */ +int Bank::loadbank(string bankdirname) +{ + DIR *dir = opendir(bankdirname.c_str()); + clearbank(); + + if(dir == NULL) + return -1; + + dirname = bankdirname; + + bankfiletitle = dirname; + + struct dirent *fn; + + while((fn = readdir(dir))) { + const char *filename = fn->d_name; + + //check for extension + if(strstr(filename, INSTRUMENT_EXTENSION) == NULL) + continue; + + //verify if the name is like this NNNN-name (where N is a digit) + int no = 0; + unsigned int startname = 0; + + for(unsigned int i = 0; i < 4; ++i) { + if(strlen(filename) <= i) + break; + + if((filename[i] >= '0') && (filename[i] <= '9')) { + no = no * 10 + (filename[i] - '0'); + startname++; + } + } + + if((startname + 1) < strlen(filename)) + startname++; //to take out the "-" + + string name = filename; + + //remove the file extension + for(int i = name.size() - 1; i >= 2; i--) + if(name[i] == '.') { + name = name.substr(0, i); + break; + } + + if(no != 0) //the instrument position in the bank is found + addtobank(no - 1, filename, name.substr(startname)); + else + addtobank(-1, filename, name); + } + + closedir(dir); + + if(!dirname.empty()) + config.cfg.currentBankDir = dirname; + + return 0; +} + +/* + * Makes a new bank, put it on a file and makes it current bank + */ +int Bank::newbank(string newbankdirname) +{ + string bankdir; + bankdir = config.cfg.bankRootDirList[0]; + + if(((bankdir[bankdir.size() - 1]) != '/') + && ((bankdir[bankdir.size() - 1]) != '\\')) + bankdir += "/"; + + bankdir += newbankdirname; +#ifdef WIN32 + if(mkdir(bankdir.c_str()) < 0) +#else + if(mkdir(bankdir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) +#endif + return -1; + + const string tmpfilename = bankdir + '/' + FORCE_BANK_DIR_FILE; + + FILE *tmpfile = fopen(tmpfilename.c_str(), "w+"); + fclose(tmpfile); + + return loadbank(bankdir); +} + +/* + * Check if the bank is locked (i.e. the file opened was readonly) + */ +int Bank::locked() +{ + return dirname.empty(); +} + +/* + * Swaps a slot with another + */ +void Bank::swapslot(unsigned int n1, unsigned int n2) +{ + if((n1 == n2) || (locked())) + return; + if(emptyslot(n1) && (emptyslot(n2))) + return; + if(emptyslot(n1)) //change n1 to n2 in order to make + swap(n1, n2); + + if(emptyslot(n2)) { //this is just a movement from slot1 to slot2 + setname(n1, getname(n1), n2); + ins[n2] = ins[n1]; + ins[n1] = ins_t(); + } + else { //if both slots are used + if(ins[n1].name == ins[n2].name) //change the name of the second instrument if the name are equal + ins[n2].name += "2"; + + setname(n1, getname(n1), n2); + setname(n2, getname(n2), n1); + swap(ins[n2], ins[n1]); + } +} + + +bool Bank::bankstruct::operator<(const bankstruct &b) const +{ + return name < b.name; +} + +/* + * Re-scan for directories containing instrument banks + */ + +void Bank::rescanforbanks() +{ + //remove old banks + banks.clear(); + + for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) + if(!config.cfg.bankRootDirList[i].empty()) + scanrootdir(config.cfg.bankRootDirList[i]); + + //sort the banks + sort(banks.begin(), banks.end()); + + //remove duplicate bank names + int dupl = 0; + for(int j = 0; j < (int) banks.size() - 1; ++j) + for(int i = j + 1; i < (int) banks.size(); ++i) { + if(banks[i].name == banks[j].name) { + //add a [1] to the first bankname and [n] to others + banks[i].name = banks[i].name + '[' + + stringFrom(dupl + 2) + ']'; + if(dupl == 0) + banks[j].name += "[1]"; + + dupl++; + } + else + dupl = 0; + } +} + + +// private stuff + +void Bank::scanrootdir(string rootdir) +{ + DIR *dir = opendir(rootdir.c_str()); + if(dir == NULL) + return; + + bankstruct bank; + + const char *separator = "/"; + if(rootdir.size()) { + char tmp = rootdir[rootdir.size() - 1]; + if((tmp == '/') || (tmp == '\\')) + separator = ""; + } + + struct dirent *fn; + while((fn = readdir(dir))) { + const char *dirname = fn->d_name; + if(dirname[0] == '.') + continue; + + bank.dir = rootdir + separator + dirname + '/'; + bank.name = dirname; + //find out if the directory contains at least 1 instrument + bool isbank = false; + + DIR *d = opendir(bank.dir.c_str()); + if(d == NULL) + continue; + + struct dirent *fname; + + while((fname = readdir(d))) { + if((strstr(fname->d_name, INSTRUMENT_EXTENSION) != NULL) + || (strstr(fname->d_name, FORCE_BANK_DIR_FILE) != NULL)) { + isbank = true; + break; //could put a #instrument counter here instead + } + } + + if(isbank) + banks.push_back(bank); + + closedir(d); + } + + closedir(dir); +} + +void Bank::clearbank() +{ + for(int i = 0; i < BANK_SIZE; ++i) + ins[i] = ins_t(); + + bankfiletitle.clear(); + dirname.clear(); +} + +int Bank::addtobank(int pos, string filename, string name) +{ + if((pos >= 0) && (pos < BANK_SIZE)) { + if(ins[pos].used) + pos = -1; //force it to find a new free position + } + else + if(pos >= BANK_SIZE) + pos = -1; + + + if(pos < 0) //find a free position + for(int i = BANK_SIZE - 1; i >= 0; i--) + if(!ins[i].used) { + pos = i; + break; + } + + if(pos < 0) + return -1; //the bank is full + + deletefrombank(pos); + + ins[pos].used = true; + ins[pos].name = name; + ins[pos].filename = dirname + '/' + filename; + + //see if PADsynth is used + if(config.cfg.CheckPADsynth) { + XMLwrapper xml; + xml.loadXMLfile(ins[pos].filename); + + ins[pos].info.PADsynth_used = xml.hasPadSynth(); + } + else + ins[pos].info.PADsynth_used = false; + + return 0; +} + +bool Bank::isPADsynth_used(unsigned int ninstrument) +{ + if(config.cfg.CheckPADsynth == 0) + return 0; + else + return ins[ninstrument].info.PADsynth_used; +} + + +void Bank::deletefrombank(int pos) +{ + if((pos < 0) || (pos >= BANK_SIZE)) + return; + ins[pos] = ins_t(); +} + +Bank::ins_t::ins_t() + :used(false), name(""), filename("") +{ + info.PADsynth_used = false; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.h new file mode 100644 index 000000000..5a7ed9da2 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.h @@ -0,0 +1,103 @@ +/* + ZynAddSubFX - a software synthesizer + + Bank.h - Instrument Bank + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef BANK_H +#define BANK_H + +#include +#include + +//entries in a bank +#define BANK_SIZE 160 + +/**The instrument Bank*/ +class Bank +{ + public: + /**Constructor*/ + Bank(); + ~Bank(); + std::string getname(unsigned int ninstrument); + std::string getnamenumbered(unsigned int ninstrument); + void setname(unsigned int ninstrument, + const std::string &newname, + int newslot); //if newslot==-1 then this is ignored, else it will be put on that slot + bool isPADsynth_used(unsigned int ninstrument); + + /**returns true when slot is empty*/ + bool emptyslot(unsigned int ninstrument); + + /**Empties out the selected slot*/ + void clearslot(unsigned int ninstrument); + /**Saves the given Part to slot*/ + void savetoslot(unsigned int ninstrument, class Part * part); + /**Loads the given slot into a Part*/ + void loadfromslot(unsigned int ninstrument, class Part * part); + + /**Swaps Slots*/ + void swapslot(unsigned int n1, unsigned int n2); + + int loadbank(std::string bankdirname); + int newbank(std::string newbankdirname); + + std::string bankfiletitle; //this is shown on the UI of the bank (the title of the window) + int locked(); + + void rescanforbanks(); + + struct bankstruct { + bool operator<(const bankstruct &b) const; + std::string dir; + std::string name; + }; + + std::vector banks; + + private: + + //it adds a filename to the bank + //if pos is -1 it try to find a position + //returns -1 if the bank is full, or 0 if the instrument was added + int addtobank(int pos, std::string filename, std::string name); + + void deletefrombank(int pos); + + void clearbank(); + + std::string defaultinsname; + + struct ins_t { + ins_t(); + bool used; + std::string name; + std::string filename; + struct { + bool PADsynth_used; + } info; + } ins[BANK_SIZE]; + + std::string dirname; + + void scanrootdir(std::string rootdir); //scans a root dir for banks +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/CMakeLists.txt b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/CMakeLists.txt new file mode 100644 index 000000000..eab8ca052 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/CMakeLists.txt @@ -0,0 +1,28 @@ +include_directories(${MXML_INCLUDE_DIR}) + +set(zynaddsubfx_misc_SRCS + Misc/Bank.cpp + Misc/Config.cpp + Misc/Dump.cpp + Misc/Master.cpp + Misc/Microtonal.cpp + Misc/Part.cpp + Misc/Util.cpp + Misc/XMLwrapper.cpp + Misc/Recorder.cpp + Misc/WavFile.cpp + Misc/WaveShapeSmps.cpp +) + + + +if(LashEnable) + set(zynaddsubfx_misc_SRCS + ${zynaddsubfx_misc_SRCS} + Misc/LASHClient.cpp + PARENT_SCOPE) +else() + set(zynaddsubfx_misc_SRCS + ${zynaddsubfx_misc_SRCS} + PARENT_SCOPE) +endif() diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Config.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Config.cpp new file mode 100644 index 000000000..cbad8993a --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Config.cpp @@ -0,0 +1,315 @@ +/* + ZynAddSubFX - a software synthesizer + + Config.cpp - Configuration file functions + Copyright (C) 2003-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include +#include +#include +#include + + +#include "Config.h" +#include "XMLwrapper.h" + +using namespace std; + +Config::Config() : + workingDir( NULL ) +{} +void Config::init() +{ + maxstringsize = MAX_STRING_SIZE; //for ui + //defaults + cfg.SampleRate = 44100; + cfg.SoundBufferSize = 256; + cfg.OscilSize = 1024; + cfg.SwapStereo = 0; + + cfg.LinuxOSSWaveOutDev = new char[MAX_STRING_SIZE]; + snprintf(cfg.LinuxOSSWaveOutDev, MAX_STRING_SIZE, "/dev/dsp"); + cfg.LinuxOSSSeqInDev = new char[MAX_STRING_SIZE]; + snprintf(cfg.LinuxOSSSeqInDev, MAX_STRING_SIZE, "/dev/sequencer"); + + cfg.DumpFile = "zynaddsubfx_dump.txt"; + + cfg.WindowsWaveOutId = 0; + cfg.WindowsMidiInId = 0; + + cfg.BankUIAutoClose = 0; + cfg.DumpNotesToFile = 0; + cfg.DumpAppend = 1; + + cfg.GzipCompression = 3; + + cfg.Interpolation = 0; + cfg.CheckPADsynth = 1; + cfg.IgnoreProgramChange = 0; + + cfg.UserInterfaceMode = 0; + cfg.VirKeybLayout = 1; + winwavemax = 1; + winmidimax = 1; + //try to find out how many input midi devices are there + winmididevices = new winmidionedevice[winmidimax]; + for(int i = 0; i < winmidimax; ++i) { + winmididevices[i].name = new char[MAX_STRING_SIZE]; + for(int j = 0; j < MAX_STRING_SIZE; ++j) + winmididevices[i].name[j] = '\0'; + } + + +//get the midi input devices name + cfg.currentBankDir = "./testbnk"; + + char filename[MAX_STRING_SIZE]; + getConfigFileName(filename, MAX_STRING_SIZE); + readConfig(filename); + + if(cfg.bankRootDirList[0].empty()) { + //banks + cfg.bankRootDirList[0] = "~/banks"; + cfg.bankRootDirList[1] = "./"; + cfg.bankRootDirList[2] = "/usr/share/zynaddsubfx/banks"; + cfg.bankRootDirList[3] = "/usr/local/share/zynaddsubfx/banks"; + cfg.bankRootDirList[4] = "../banks"; + cfg.bankRootDirList[5] = "banks"; + } + + if(cfg.presetsDirList[0].empty()) { + //presets + cfg.presetsDirList[0] = "./"; + cfg.presetsDirList[1] = "../presets"; + cfg.presetsDirList[2] = "presets"; + cfg.presetsDirList[3] = "/usr/share/zynaddsubfx/presets"; + cfg.presetsDirList[4] = "/usr/local/share/zynaddsubfx/presets"; + } + cfg.LinuxALSAaudioDev = "default"; + cfg.nameTag = ""; +} + +Config::~Config() +{ + delete [] cfg.LinuxOSSWaveOutDev; + delete [] cfg.LinuxOSSSeqInDev; + + for(int i = 0; i < winmidimax; ++i) + delete [] winmididevices[i].name; + delete [] winmididevices; +} + + +void Config::save() +{ + char filename[MAX_STRING_SIZE]; + getConfigFileName(filename, MAX_STRING_SIZE); + saveConfig(filename); +} + +void Config::clearbankrootdirlist() +{ + for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) + cfg.bankRootDirList[i].clear(); +} + +void Config::clearpresetsdirlist() +{ + for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) + cfg.presetsDirList[i].clear(); +} + +void Config::readConfig(const char *filename) +{ + XMLwrapper xmlcfg; + if(xmlcfg.loadXMLfile(filename) < 0) + return; + if(xmlcfg.enterbranch("CONFIGURATION")) { + cfg.SampleRate = xmlcfg.getpar("sample_rate", + cfg.SampleRate, + 4000, + 1024000); + cfg.SoundBufferSize = xmlcfg.getpar("sound_buffer_size", + cfg.SoundBufferSize, + 16, + 8192); + cfg.OscilSize = xmlcfg.getpar("oscil_size", + cfg.OscilSize, + MAX_AD_HARMONICS * 2, + 131072); + cfg.SwapStereo = xmlcfg.getpar("swap_stereo", + cfg.SwapStereo, + 0, + 1); + cfg.BankUIAutoClose = xmlcfg.getpar("bank_window_auto_close", + cfg.BankUIAutoClose, + 0, + 1); + + cfg.DumpNotesToFile = xmlcfg.getpar("dump_notes_to_file", + cfg.DumpNotesToFile, + 0, + 1); + cfg.DumpAppend = xmlcfg.getpar("dump_append", + cfg.DumpAppend, + 0, + 1); + cfg.DumpFile = xmlcfg.getparstr("dump_file", ""); + + cfg.GzipCompression = xmlcfg.getpar("gzip_compression", + cfg.GzipCompression, + 0, + 9); + + cfg.currentBankDir = xmlcfg.getparstr("bank_current", ""); + cfg.Interpolation = xmlcfg.getpar("interpolation", + cfg.Interpolation, + 0, + 1); + + cfg.CheckPADsynth = xmlcfg.getpar("check_pad_synth", + cfg.CheckPADsynth, + 0, + 1); + + cfg.IgnoreProgramChange = xmlcfg.getpar("ignore_program_change", + cfg.IgnoreProgramChange, + 0, + 1); + + + cfg.UserInterfaceMode = xmlcfg.getpar("user_interface_mode", + cfg.UserInterfaceMode, + 0, + 2); + cfg.VirKeybLayout = xmlcfg.getpar("virtual_keyboard_layout", + cfg.VirKeybLayout, + 0, + 10); + + //get bankroot dirs + for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) + if(xmlcfg.enterbranch("BANKROOT", i)) { + cfg.bankRootDirList[i] = xmlcfg.getparstr("bank_root", ""); + xmlcfg.exitbranch(); + } + + //get preset root dirs + for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) + if(xmlcfg.enterbranch("PRESETSROOT", i)) { + cfg.presetsDirList[i] = xmlcfg.getparstr("presets_root", ""); + xmlcfg.exitbranch(); + } + + //linux stuff + xmlcfg.getparstr("linux_oss_wave_out_dev", + cfg.LinuxOSSWaveOutDev, + MAX_STRING_SIZE); + xmlcfg.getparstr("linux_oss_seq_in_dev", + cfg.LinuxOSSSeqInDev, + MAX_STRING_SIZE); + + //windows stuff + cfg.WindowsWaveOutId = xmlcfg.getpar("windows_wave_out_id", + cfg.WindowsWaveOutId, + 0, + winwavemax); + cfg.WindowsMidiInId = xmlcfg.getpar("windows_midi_in_id", + cfg.WindowsMidiInId, + 0, + winmidimax); + + xmlcfg.exitbranch(); + } + + cfg.OscilSize = (int) powf(2, ceil(logf(cfg.OscilSize - 1.0f) / logf(2.0f))); +} + +void Config::saveConfig(const char *filename) +{ + XMLwrapper *xmlcfg = new XMLwrapper(); + + xmlcfg->beginbranch("CONFIGURATION"); + + xmlcfg->addpar("sample_rate", cfg.SampleRate); + xmlcfg->addpar("sound_buffer_size", cfg.SoundBufferSize); + xmlcfg->addpar("oscil_size", cfg.OscilSize); + xmlcfg->addpar("swap_stereo", cfg.SwapStereo); + xmlcfg->addpar("bank_window_auto_close", cfg.BankUIAutoClose); + + xmlcfg->addpar("dump_notes_to_file", cfg.DumpNotesToFile); + xmlcfg->addpar("dump_append", cfg.DumpAppend); + xmlcfg->addparstr("dump_file", cfg.DumpFile); + + xmlcfg->addpar("gzip_compression", cfg.GzipCompression); + + xmlcfg->addpar("check_pad_synth", cfg.CheckPADsynth); + xmlcfg->addpar("ignore_program_change", cfg.IgnoreProgramChange); + + xmlcfg->addparstr("bank_current", cfg.currentBankDir); + + xmlcfg->addpar("user_interface_mode", cfg.UserInterfaceMode); + xmlcfg->addpar("virtual_keyboard_layout", cfg.VirKeybLayout); + + + for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) + if(!cfg.bankRootDirList[i].empty()) { + xmlcfg->beginbranch("BANKROOT", i); + xmlcfg->addparstr("bank_root", cfg.bankRootDirList[i]); + xmlcfg->endbranch(); + } + + for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) + if(!cfg.presetsDirList[i].empty()) { + xmlcfg->beginbranch("PRESETSROOT", i); + xmlcfg->addparstr("presets_root", cfg.presetsDirList[i]); + xmlcfg->endbranch(); + } + + xmlcfg->addpar("interpolation", cfg.Interpolation); + + //linux stuff + xmlcfg->addparstr("linux_oss_wave_out_dev", cfg.LinuxOSSWaveOutDev); + xmlcfg->addparstr("linux_oss_seq_in_dev", cfg.LinuxOSSSeqInDev); + + //windows stuff + xmlcfg->addpar("windows_wave_out_id", cfg.WindowsWaveOutId); + xmlcfg->addpar("windows_midi_in_id", cfg.WindowsMidiInId); + + xmlcfg->endbranch(); + + int tmp = cfg.GzipCompression; + cfg.GzipCompression = 0; + xmlcfg->saveXMLfile(filename); + cfg.GzipCompression = tmp; + + delete (xmlcfg); +} + +void Config::getConfigFileName(char *name, int namesize) +{ + name[0] = 0; + if( workingDir != NULL ) + { + snprintf(name, namesize, "%s%s", workingDir, ".zynaddsubfxXML.cfg"); + } + else + { + snprintf(name, namesize, "%s%s", getenv("HOME"), "/.zynaddsubfxXML.cfg"); + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Config.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Config.h new file mode 100644 index 000000000..1c875d87a --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Config.h @@ -0,0 +1,76 @@ +/* + ZynAddSubFX - a software synthesizer + + Config.h - Configuration file functions + Copyright (C) 2003-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef CONFIG_H +#define CONFIG_H +#include "../globals.h" +#include +#define MAX_STRING_SIZE 4000 +#define MAX_BANK_ROOT_DIRS 100 + +/**Configuration file functions*/ +class Config +{ + public: + /** Constructor*/ + Config(); + /** Destructor*/ + ~Config(); + struct { + char *LinuxOSSWaveOutDev, *LinuxOSSSeqInDev; + int SampleRate, SoundBufferSize, OscilSize, SwapStereo; + int WindowsWaveOutId, WindowsMidiInId; + int BankUIAutoClose; + int DumpNotesToFile, DumpAppend; + int GzipCompression; + int Interpolation; + std::string DumpFile; + std::string bankRootDirList[MAX_BANK_ROOT_DIRS], currentBankDir; + std::string presetsDirList[MAX_BANK_ROOT_DIRS]; + int CheckPADsynth; + int IgnoreProgramChange; + int UserInterfaceMode; + int VirKeybLayout; + std::string LinuxALSAaudioDev; + std::string nameTag; + } cfg; + int winwavemax, winmidimax; //number of wave/midi devices on Windows + int maxstringsize; + + char * workingDir; + + struct winmidionedevice { + char *name; + }; + winmidionedevice *winmididevices; + + void clearbankrootdirlist(); + void clearpresetsdirlist(); + void init(); + void save(); + + private: + void readConfig(const char *filename); + void saveConfig(const char *filename); + void getConfigFileName(char *name, int namesize); +}; +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Control.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Control.h new file mode 100644 index 000000000..a605a66b4 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Control.h @@ -0,0 +1,99 @@ +/* + ZynAddSubFX - a software synthesizer + + Control.h - Defines a variable that can be controled from a frontend + + Copyright (C) 2009 Harald Hvaal + Author: Harald Hvaal + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _CONTROL_H_ +#define _CONTROL_H_ + +#include + +class Control +{ + public: + /** + * The parent is the logical owner of this control. Parent should only + * be null for the root node. + * The id is a string uniquely identifying this control within the + * context of the parent control. No spaces or dots are allowed in this + * id. + * Children id's are denoted by ., so that one + * can refer to any control in the hierarchy by separating them with + * dots. Example: Main.AddSynth.FrequencyLFO.Amplitude + */ + Control(Control *parent, string id); + + /** + * Will recursively get the XML representation for all the subcontrols. + * Used for saving to file and copy-pasting settings + */ + string getXMLRepresentation(); + + /** + * Set the value of this (and possibly subcomponents as well) based on + * a xml description. + */ + void restoreFromXML(string xml); + + /** + * Register a controluser. This will cause this user to be notified + * whenever the contents of the control changes. + */ + void registerControlUser(ControlUser *user); + + /** + * This should return a string representation of the controls internal + * value + */ + virtual string getStringRepresentation() = 0; +}; + +class FloatControl:public Control +{ + public: + /** + * Set the value of this control. If the ControlUser variable is set, + * then this user will not be updated with the new value. This is to + * avoid setting a value being set back to the source that set it + * (which would be redundant, or possibly causing infinite setValue + * loops). + * NOTE: this function is thread-safe (using a mutex internally) + */ + void setValue(float value, ControlUser *user = NULL); + + /** + * Reimplemented from Control + */ + virtual string getStringRepresentation(); + + float value(); +}; + +class ControlUser +{ + public: + /** + * Pure virtual method, to notify the controluser that the value has + * been changed internally, and needs to be read again. + */ + virtual void controlUpdated(Control *control) = 0; +}; + +#endif /* _CONTROL_H_ */ diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Dump.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Dump.cpp new file mode 100644 index 000000000..e98074b40 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Dump.cpp @@ -0,0 +1,121 @@ +/* + ZynAddSubFX - a software synthesizer + + Dump.cpp - It dumps the notes to a text file + + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include "Util.h" +#include "Dump.h" + +Dump dump; + +Dump::Dump() +{ + file = NULL; + tick = 0; + k = 0; + keyspressed = 0; +} + +Dump::~Dump() +{ + if(file != NULL) { + int duration = tick * synth->buffersize_f / synth->samplerate_f; + fprintf( + file, + "\n# statistics: duration = %d seconds; keyspressed = %d\n\n\n\n", + duration, + keyspressed); + fclose(file); + } +} + +void Dump::startnow() +{ + if(file != NULL) + return; //the file is already open + + if(config.cfg.DumpNotesToFile != 0) { + if(config.cfg.DumpAppend != 0) + file = fopen(config.cfg.DumpFile.c_str(), "a"); + else + file = fopen(config.cfg.DumpFile.c_str(), "w"); + if(file == NULL) + return; + if(config.cfg.DumpAppend != 0) + fprintf(file, "%s", "#************************************\n"); + + time_t tm = time(NULL); + + fprintf(file, "#date/time = %s\n", ctime(&tm)); + fprintf(file, "#1 tick = %g milliseconds\n", + synth->buffersize_f * 1000.0f / synth->samplerate_f); + fprintf(file, "SAMPLERATE = %d\n", synth->samplerate); + fprintf(file, "TICKSIZE = %d #samples\n", synth->buffersize); + fprintf(file, "\n\nSTART\n"); + } +} + +void Dump::inctick() +{ + tick++; +} + + +void Dump::dumpnote(char chan, char note, char vel) +{ + if(file == NULL) + return; + if(note == 0) + return; + if(vel == 0) + fprintf(file, "n %d -> %d %d \n", tick, chan, note); //note off + else + fprintf(file, "N %d -> %d %d %d \n", tick, chan, note, vel); //note on + + if(vel != 0) + keyspressed++; +#ifndef JACKAUDIOOUT + if(k++ > 25) { + fflush(file); + k = 0; + } +#endif +} + +void Dump::dumpcontroller(char chan, unsigned int type, int par) +{ + if(file == NULL) + return; + switch(type) { + case C_pitchwheel: + fprintf(file, "P %d -> %d %d\n", tick, chan, par); + break; + default: + fprintf(file, "C %d -> %d %d %d\n", tick, chan, type, par); + break; + } +#ifndef JACKAUDIOOUT + if(k++ > 25) { + fflush(file); + k = 0; + } +#endif +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Dump.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Dump.h new file mode 100644 index 000000000..dc543cf09 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Dump.h @@ -0,0 +1,63 @@ +/* + ZynAddSubFX - a software synthesizer + + Dump.h - It dumps the notes to a text file + + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef DUMP_H +#define DUMP_H + +#include + +/**Object used to dump the notes into a text file + * \todo see if this object should have knowledge about the file + * that it will write to + * \todo upgrade from stdio to iostream*/ +class Dump +{ + public: + /**Constructor*/ + Dump(); + /**Destructor + * Closes the dumpfile*/ + ~Dump(); + /**Open dumpfile and prepare it for dumps + * \todo see if this fits better in the constructor*/ + void startnow(); + /**Tick the timestamp*/ + void inctick(); + /**Dump Note to dumpfile + * @param chan The channel of the note + * @param note The note + * @param vel The velocity of the note*/ + void dumpnote(char chan, char note, char vel); + /** Dump the Controller + * @param chan The channel of the Controller + * @param type The type + * @param par The value of the controller + * \todo figure out what type is exactly meaning*/ + void dumpcontroller(char chan, unsigned int type, int par); + + private: + FILE *file; + int tick; + int k; //This appears to be a constant used to flush the file + //periodically when JACK is used + int keyspressed; +}; +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/LASHClient.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/LASHClient.cpp new file mode 100644 index 000000000..57979a329 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/LASHClient.cpp @@ -0,0 +1,103 @@ +/* + ZynAddSubFX - a software synthesizer + + LASHClient.cpp - LASH support + Copyright (C) 2006-2009 Lars Luthman + Author: Lars Luthman + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include +#include +#include + +#include "LASHClient.h" + + +LASHClient::LASHClient(int *argc, char ***argv) +{ + client = lash_init(lash_extract_args(argc, argv), "ZynAddSubFX", + LASH_Config_File, LASH_PROTOCOL(2, 0)); +} + + +void LASHClient::setalsaid(int id) +{ + if(lash_enabled(client)) + if(id != -1) + lash_alsa_client_id(client, id); +} + + +void LASHClient::setjackname(const char *name) +{ + if(lash_enabled(client)) + if(name != NULL) { + lash_jack_client_name(client, name); + + lash_event_t *event = lash_event_new_with_type(LASH_Client_Name); + lash_event_set_string(event, name); + lash_send_event(client, event); + } +} + + +LASHClient::Event LASHClient::checkevents(std::string &filename) +{ + if(!lash_enabled(client)) + return NoEvent; + + Event received = NoEvent; + lash_event_t *event; + while((event = lash_get_event(client))) { + // save + if(lash_event_get_type(event) == LASH_Save_File) { + std::cerr << "LASH event: LASH_Save_File" << std::endl; + filename = std::string(lash_event_get_string(event)) + + "/master.xmz"; + received = Save; + break; + } + // restore + else + if(lash_event_get_type(event) == LASH_Restore_File) { + std::cerr << "LASH event: LASH_Restore_File" << std::endl; + filename = std::string(lash_event_get_string(event)) + + "/master.xmz"; + received = Restore; + break; + } + // quit + else + if(lash_event_get_type(event) == LASH_Quit) { + std::cerr << "LASH event: LASH_Quit" << std::endl; + received = Quit; + break; + } + + lash_event_destroy(event); + } + return received; +} + + +void LASHClient::confirmevent(Event event) +{ + if(event == Save) + lash_send_event(client, lash_event_new_with_type(LASH_Save_File)); + else + if(event == Restore) + lash_send_event(client, lash_event_new_with_type(LASH_Restore_File)); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/LASHClient.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/LASHClient.h new file mode 100644 index 000000000..1f3a49467 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/LASHClient.h @@ -0,0 +1,63 @@ +/* + ZynAddSubFX - a software synthesizer + + LASHClient.h - LASH support + Copyright (C) 2006-2009 Lars Luthman + Author: Lars Luthman + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef LASHClient_h +#define LASHClient_h + +#include +#include +#include + + +/** This class wraps up some functions for initialising and polling + * the LASH daemon.*/ +class LASHClient +{ + public: + /**Enum to represent the LASH events that are currently handled*/ + enum Event { + Save, + Restore, + Quit, + NoEvent + }; + + /** Constructor + * @param argc number of arguments + * @param argv the text arguments*/ + LASHClient(int *argc, char ***argv); + + /**set the ALSA id + * @param id new ALSA id*/ + void setalsaid(int id); + /**Set the JACK name + * @param name the new name*/ + void setjackname(const char *name); + Event checkevents(std::string &filename); + void confirmevent(Event event); + + private: + + lash_client_t *client; +}; + + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Master.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Master.cpp new file mode 100644 index 000000000..c4b8947b8 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Master.cpp @@ -0,0 +1,808 @@ +/* + ZynAddSubFX - a software synthesizer + + Master.cpp - It sends Midi Messages to Parts, receives samples from parts, + process them with system/insertion effects and mix them + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "Master.h" + +#include "Part.h" + +#include "../Params/LFOParams.h" +#include "../Effects/EffectMgr.h" +#include "../DSP/FFTwrapper.h" + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +vuData::vuData(void) + :outpeakl(0.0f), outpeakr(0.0f), maxoutpeakl(0.0f), maxoutpeakr(0.0f), + rmspeakl(0.0f), rmspeakr(0.0f), clipped(0) +{} + +static Master* masterInstance = NULL; + +Master::Master() +{ + swaplr = 0; + off = 0; + smps = 0; + bufl = new float[synth->buffersize]; + bufr = new float[synth->buffersize]; + + pthread_mutex_init(&mutex, NULL); + pthread_mutex_init(&vumutex, NULL); + fft = new FFTwrapper(synth->oscilsize); + + shutup = 0; + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { + vuoutpeakpart[npart] = 1e-9; + fakepeakpart[npart] = 0; + } + + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) + part[npart] = new Part(µtonal, fft, &mutex); + + //Insertion Effects init + for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) + insefx[nefx] = new EffectMgr(1, &mutex); + + //System Effects init + for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) + sysefx[nefx] = new EffectMgr(0, &mutex); + + + defaults(); +} + +void Master::defaults() +{ + volume = 1.0f; + setPvolume(80); + setPkeyshift(64); + + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { + part[npart]->defaults(); + part[npart]->Prcvchn = npart % NUM_MIDI_CHANNELS; + } + + partonoff(0, 1); //enable the first part + + for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) { + insefx[nefx]->defaults(); + Pinsparts[nefx] = -1; + } + + //System Effects init + for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) { + sysefx[nefx]->defaults(); + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) + setPsysefxvol(npart, nefx, 0); + + for(int nefxto = 0; nefxto < NUM_SYS_EFX; ++nefxto) + setPsysefxsend(nefx, nefxto, 0); + } + + microtonal.defaults(); + ShutUp(); +} + +bool Master::mutexLock(lockset request) +{ + switch(request) { + case MUTEX_TRYLOCK: + return !pthread_mutex_trylock(&mutex); + case MUTEX_LOCK: + return !pthread_mutex_lock(&mutex); + case MUTEX_UNLOCK: + return !pthread_mutex_unlock(&mutex); + } + return false; +} + +Master &Master::getInstance() +{ + if (!masterInstance) + masterInstance = new Master; + + return *masterInstance; +} + +void Master::deleteInstance() +{ + if (masterInstance) + { + delete masterInstance; + masterInstance = NULL; + } +} + +/* + * Note On Messages (velocity=0 for NoteOff) + */ +void Master::noteOn(char chan, char note, char velocity) +{ + if(velocity) { + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) + if(chan == part[npart]->Prcvchn) { + fakepeakpart[npart] = velocity * 2; + if(part[npart]->Penabled) + part[npart]->NoteOn(note, velocity, keyshift); + } + } + else + this->noteOff(chan, note); + HDDRecorder.triggernow(); +} + +/* + * Note Off Messages + */ +void Master::noteOff(char chan, char note) +{ + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) + if((chan == part[npart]->Prcvchn) && part[npart]->Penabled) + part[npart]->NoteOff(note); +} + +/* + * Pressure Messages (velocity=0 for NoteOff) + */ +void Master::polyphonicAftertouch(char chan, char note, char velocity) +{ + if(velocity) { + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) + if(chan == part[npart]->Prcvchn) + if(part[npart]->Penabled) + part[npart]->PolyphonicAftertouch(note, velocity, keyshift); + + } + else + this->noteOff(chan, note); +} + +/* + * Controllers + */ +void Master::setController(char chan, int type, int par) +{ + if((type == C_dataentryhi) || (type == C_dataentrylo) + || (type == C_nrpnhi) || (type == C_nrpnlo)) { //Process RPN and NRPN by the Master (ignore the chan) + ctl.setparameternumber(type, par); + + int parhi = -1, parlo = -1, valhi = -1, vallo = -1; + if(ctl.getnrpn(&parhi, &parlo, &valhi, &vallo) == 0) //this is NRPN + //fprintf(stderr,"rcv. NRPN: %d %d %d %d\n",parhi,parlo,valhi,vallo); + switch(parhi) { + case 0x04: //System Effects + if(parlo < NUM_SYS_EFX) + sysefx[parlo]->seteffectpar_nolock(valhi, vallo); + ; + break; + case 0x08: //Insertion Effects + if(parlo < NUM_INS_EFX) + insefx[parlo]->seteffectpar_nolock(valhi, vallo); + ; + break; + } + ; + } + else + if(type == C_bankselectmsb) { // Change current bank + if(((unsigned int)par < bank.banks.size()) + && (bank.banks[par].dir != bank.bankfiletitle)) + bank.loadbank(bank.banks[par].dir); + } + else { //other controllers + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) //Send the controller to all part assigned to the channel + if((chan == part[npart]->Prcvchn) && (part[npart]->Penabled != 0)) + part[npart]->SetController(type, par); + ; + + if(type == C_allsoundsoff) { //cleanup insertion/system FX + for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) + sysefx[nefx]->cleanup(); + for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) + insefx[nefx]->cleanup(); + } + } +} + +void Master::setProgram(char chan, unsigned int pgm) +{ + if(config.cfg.IgnoreProgramChange) + return; + + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) + if(chan == part[npart]->Prcvchn) { + bank.loadfromslot(pgm, part[npart]); + + //Hack to get pad note parameters to update + //this is not real time safe and makes assumptions about the calling + //convention of this function... + pthread_mutex_unlock(&mutex); + part[npart]->applyparameters(); + pthread_mutex_lock(&mutex); + } +} + +void Master::vuUpdate(const float *outl, const float *outr) +{ + //Peak computation (for vumeters) + vu.outpeakl = 1e-12; + vu.outpeakr = 1e-12; + for(int i = 0; i < synth->buffersize; ++i) { + if(fabs(outl[i]) > vu.outpeakl) + vu.outpeakl = fabs(outl[i]); + if(fabs(outr[i]) > vu.outpeakr) + vu.outpeakr = fabs(outr[i]); + } + if((vu.outpeakl > 1.0f) || (vu.outpeakr > 1.0f)) + vu.clipped = 1; + if(vu.maxoutpeakl < vu.outpeakl) + vu.maxoutpeakl = vu.outpeakl; + if(vu.maxoutpeakr < vu.outpeakr) + vu.maxoutpeakr = vu.outpeakr; + + //RMS Peak computation (for vumeters) + vu.rmspeakl = 1e-12; + vu.rmspeakr = 1e-12; + for(int i = 0; i < synth->buffersize; ++i) { + vu.rmspeakl += outl[i] * outl[i]; + vu.rmspeakr += outr[i] * outr[i]; + } + vu.rmspeakl = sqrt(vu.rmspeakl / synth->buffersize_f); + vu.rmspeakr = sqrt(vu.rmspeakr / synth->buffersize_f); + + //Part Peak computation (for Part vumeters or fake part vumeters) + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { + vuoutpeakpart[npart] = 1.0e-12f; + if(part[npart]->Penabled != 0) { + float *outl = part[npart]->partoutl, + *outr = part[npart]->partoutr; + for(int i = 0; i < synth->buffersize; ++i) { + float tmp = fabs(outl[i] + outr[i]); + if(tmp > vuoutpeakpart[npart]) + vuoutpeakpart[npart] = tmp; + } + vuoutpeakpart[npart] *= volume; + } + else + if(fakepeakpart[npart] > 1) + fakepeakpart[npart]--; + } +} + +/* + * Enable/Disable a part + */ +void Master::partonoff(int npart, int what) +{ + if(npart >= NUM_MIDI_PARTS) + return; + if(what == 0) { //disable part + fakepeakpart[npart] = 0; + part[npart]->Penabled = 0; + part[npart]->cleanup(); + for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) { + if(Pinsparts[nefx] == npart) + insefx[nefx]->cleanup(); + ; + } + } + else { //enabled + part[npart]->Penabled = 1; + fakepeakpart[npart] = 0; + } +} + +/* + * Master audio out (the final sound) + */ +void Master::AudioOut(float *outl, float *outr) +{ + //Swaps the Left channel with Right Channel + if(swaplr) + swap(outl, outr); + + //clean up the output samples (should not be needed?) + memset(outl, 0, synth->bufferbytes); + memset(outr, 0, synth->bufferbytes); + + //Compute part samples and store them part[npart]->partoutl,partoutr + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { + if(part[npart]->Penabled != 0 && !pthread_mutex_trylock(&part[npart]->load_mutex)) { + part[npart]->ComputePartSmps(); + pthread_mutex_unlock(&part[npart]->load_mutex); + } + } + + //Insertion effects + for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) + if(Pinsparts[nefx] >= 0) { + int efxpart = Pinsparts[nefx]; + if(part[efxpart]->Penabled) + insefx[nefx]->out(part[efxpart]->partoutl, + part[efxpart]->partoutr); + } + + + //Apply the part volumes and pannings (after insertion effects) + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { + if(part[npart]->Penabled == 0) + continue; + + Stereo newvol(part[npart]->volume), + oldvol(part[npart]->oldvolumel, + part[npart]->oldvolumer); + + float pan = part[npart]->panning; + if(pan < 0.5f) + newvol.l *= pan * 2.0f; + else + newvol.r *= (1.0f - pan) * 2.0f; + + //the volume or the panning has changed and needs interpolation + if(ABOVE_AMPLITUDE_THRESHOLD(oldvol.l, newvol.l) + || ABOVE_AMPLITUDE_THRESHOLD(oldvol.r, newvol.r)) { + for(int i = 0; i < synth->buffersize; ++i) { + Stereo vol(INTERPOLATE_AMPLITUDE(oldvol.l, newvol.l, + i, synth->buffersize), + INTERPOLATE_AMPLITUDE(oldvol.r, newvol.r, + i, synth->buffersize)); + part[npart]->partoutl[i] *= vol.l; + part[npart]->partoutr[i] *= vol.r; + } + part[npart]->oldvolumel = newvol.l; + part[npart]->oldvolumer = newvol.r; + } + else + for(int i = 0; i < synth->buffersize; ++i) { //the volume did not changed + part[npart]->partoutl[i] *= newvol.l; + part[npart]->partoutr[i] *= newvol.r; + } + } + + + //System effects + for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) { + if(sysefx[nefx]->geteffect() == 0) + continue; //the effect is disabled + + float tmpmixl[synth->buffersize]; + float tmpmixr[synth->buffersize]; + //Clean up the samples used by the system effects + memset(tmpmixl, 0, synth->bufferbytes); + memset(tmpmixr, 0, synth->bufferbytes); + + //Mix the channels according to the part settings about System Effect + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { + //skip if the part has no output to effect + if(Psysefxvol[nefx][npart] == 0) + continue; + + //skip if the part is disabled + if(part[npart]->Penabled == 0) + continue; + + //the output volume of each part to system effect + const float vol = sysefxvol[nefx][npart]; + for(int i = 0; i < synth->buffersize; ++i) { + tmpmixl[i] += part[npart]->partoutl[i] * vol; + tmpmixr[i] += part[npart]->partoutr[i] * vol; + } + } + + // system effect send to next ones + for(int nefxfrom = 0; nefxfrom < nefx; ++nefxfrom) + if(Psysefxsend[nefxfrom][nefx] != 0) { + const float vol = sysefxsend[nefxfrom][nefx]; + for(int i = 0; i < synth->buffersize; ++i) { + tmpmixl[i] += sysefx[nefxfrom]->efxoutl[i] * vol; + tmpmixr[i] += sysefx[nefxfrom]->efxoutr[i] * vol; + } + } + + sysefx[nefx]->out(tmpmixl, tmpmixr); + + //Add the System Effect to sound output + const float outvol = sysefx[nefx]->sysefxgetvolume(); + for(int i = 0; i < synth->buffersize; ++i) { + outl[i] += tmpmixl[i] * outvol; + outr[i] += tmpmixr[i] * outvol; + } + } + + //Mix all parts + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) + if(part[npart]->Penabled) //only mix active parts + for(int i = 0; i < synth->buffersize; ++i) { //the volume did not changed + outl[i] += part[npart]->partoutl[i]; + outr[i] += part[npart]->partoutr[i]; + } + + //Insertion effects for Master Out + for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) + if(Pinsparts[nefx] == -2) + insefx[nefx]->out(outl, outr); + + + //Master Volume + for(int i = 0; i < synth->buffersize; ++i) { + outl[i] *= volume; + outr[i] *= volume; + } + + if(!pthread_mutex_trylock(&vumutex)) { + vuUpdate(outl, outr); + pthread_mutex_unlock(&vumutex); + } + + //Shutup if it is asked (with fade-out) + if(shutup) { + for(int i = 0; i < synth->buffersize; ++i) { + float tmp = (synth->buffersize_f - i) / synth->buffersize_f; + outl[i] *= tmp; + outr[i] *= tmp; + } + ShutUp(); + } + + //update the LFO's time + LFOParams::time++; + + dump.inctick(); +} + +//TODO review the respective code from yoshimi for this +//If memory serves correctly, libsamplerate was used +void Master::GetAudioOutSamples(size_t nsamples, + unsigned samplerate, + float *outl, + float *outr) +{ + off_t out_off = 0; + + //Fail when resampling rather than doing a poor job + if(synth->samplerate != samplerate) { + printf("darn it: %d vs %d\n", synth->samplerate, samplerate); + return; + } + + while(nsamples) { + //use all available samples + if(nsamples >= smps) { + memcpy(outl + out_off, bufl + off, sizeof(float) * smps); + memcpy(outr + out_off, bufr + off, sizeof(float) * smps); + nsamples -= smps; + + //generate samples + AudioOut(bufl, bufr); + off = 0; + out_off += smps; + smps = synth->buffersize; + } + else { //use some samples + memcpy(outl + out_off, bufl + off, sizeof(float) * nsamples); + memcpy(outr + out_off, bufr + off, sizeof(float) * nsamples); + smps -= nsamples; + off += nsamples; + nsamples = 0; + } + } +} + +Master::~Master() +{ + delete []bufl; + delete []bufr; + + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) + delete part[npart]; + for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) + delete insefx[nefx]; + for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) + delete sysefx[nefx]; + + delete fft; + + pthread_mutex_destroy(&mutex); + pthread_mutex_destroy(&vumutex); +} + + +/* + * Parameter control + */ +void Master::setPvolume(char Pvolume_) +{ + Pvolume = Pvolume_; + volume = dB2rap((Pvolume - 96.0f) / 96.0f * 40.0f); +} + +void Master::setPkeyshift(char Pkeyshift_) +{ + Pkeyshift = Pkeyshift_; + keyshift = (int)Pkeyshift - 64; +} + + +void Master::setPsysefxvol(int Ppart, int Pefx, char Pvol) +{ + Psysefxvol[Pefx][Ppart] = Pvol; + sysefxvol[Pefx][Ppart] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f); +} + +void Master::setPsysefxsend(int Pefxfrom, int Pefxto, char Pvol) +{ + Psysefxsend[Pefxfrom][Pefxto] = Pvol; + sysefxsend[Pefxfrom][Pefxto] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f); +} + + +/* + * Panic! (Clean up all parts and effects) + */ +void Master::ShutUp() +{ + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { + part[npart]->cleanup(); + fakepeakpart[npart] = 0; + } + for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) + insefx[nefx]->cleanup(); + for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) + sysefx[nefx]->cleanup(); + vuresetpeaks(); + shutup = 0; +} + + +/* + * Reset peaks and clear the "cliped" flag (for VU-meter) + */ +void Master::vuresetpeaks() +{ + pthread_mutex_lock(&vumutex); + vu.outpeakl = 1e-9; + vu.outpeakr = 1e-9; + vu.maxoutpeakl = 1e-9; + vu.maxoutpeakr = 1e-9; + vu.clipped = 0; + pthread_mutex_unlock(&vumutex); +} + +vuData Master::getVuData() +{ + vuData tmp; + pthread_mutex_lock(&vumutex); + tmp = vu; + pthread_mutex_unlock(&vumutex); + return tmp; +} + +void Master::applyparameters(bool lockmutex) +{ + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) + part[npart]->applyparameters(lockmutex); +} + +void Master::add2XML(XMLwrapper *xml) +{ + xml->addpar("volume", Pvolume); + xml->addpar("key_shift", Pkeyshift); + xml->addparbool("nrpn_receive", ctl.NRPN.receive); + + xml->beginbranch("MICROTONAL"); + microtonal.add2XML(xml); + xml->endbranch(); + + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { + xml->beginbranch("PART", npart); + part[npart]->add2XML(xml); + xml->endbranch(); + } + + xml->beginbranch("SYSTEM_EFFECTS"); + for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) { + xml->beginbranch("SYSTEM_EFFECT", nefx); + xml->beginbranch("EFFECT"); + sysefx[nefx]->add2XML(xml); + xml->endbranch(); + + for(int pefx = 0; pefx < NUM_MIDI_PARTS; ++pefx) { + xml->beginbranch("VOLUME", pefx); + xml->addpar("vol", Psysefxvol[nefx][pefx]); + xml->endbranch(); + } + + for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) { + xml->beginbranch("SENDTO", tonefx); + xml->addpar("send_vol", Psysefxsend[nefx][tonefx]); + xml->endbranch(); + } + + + xml->endbranch(); + } + xml->endbranch(); + + xml->beginbranch("INSERTION_EFFECTS"); + for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) { + xml->beginbranch("INSERTION_EFFECT", nefx); + xml->addpar("part", Pinsparts[nefx]); + + xml->beginbranch("EFFECT"); + insefx[nefx]->add2XML(xml); + xml->endbranch(); + xml->endbranch(); + } + + xml->endbranch(); +} + + +int Master::getalldata(char **data) +{ + XMLwrapper *xml = new XMLwrapper(); + + xml->beginbranch("MASTER"); + + pthread_mutex_lock(&mutex); + add2XML(xml); + pthread_mutex_unlock(&mutex); + + xml->endbranch(); + + *data = xml->getXMLdata(); + delete (xml); + return strlen(*data) + 1; +} + +void Master::putalldata(char *data, int /*size*/) +{ + XMLwrapper *xml = new XMLwrapper(); + if(!xml->putXMLdata(data)) { + delete (xml); + return; + } + + if(xml->enterbranch("MASTER") == 0) + return; + + pthread_mutex_lock(&mutex); + getfromXML(xml); + pthread_mutex_unlock(&mutex); + + xml->exitbranch(); + + delete (xml); +} + +int Master::saveXML(const char *filename) +{ + XMLwrapper *xml = new XMLwrapper(); + + xml->beginbranch("MASTER"); + add2XML(xml); + xml->endbranch(); + + int result = xml->saveXMLfile(filename); + delete (xml); + return result; +} + + + +int Master::loadXML(const char *filename) +{ + XMLwrapper *xml = new XMLwrapper(); + if(xml->loadXMLfile(filename) < 0) { + delete (xml); + return -1; + } + + if(xml->enterbranch("MASTER") == 0) + return -10; + getfromXML(xml); + xml->exitbranch(); + + delete (xml); + return 0; +} + +void Master::getfromXML(XMLwrapper *xml) +{ + setPvolume(xml->getpar127("volume", Pvolume)); + setPkeyshift(xml->getpar127("key_shift", Pkeyshift)); + ctl.NRPN.receive = xml->getparbool("nrpn_receive", ctl.NRPN.receive); + + + part[0]->Penabled = 0; + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { + if(xml->enterbranch("PART", npart) == 0) + continue; + part[npart]->getfromXML(xml); + xml->exitbranch(); + } + + if(xml->enterbranch("MICROTONAL")) { + microtonal.getfromXML(xml); + xml->exitbranch(); + } + + sysefx[0]->changeeffect(0); + if(xml->enterbranch("SYSTEM_EFFECTS")) { + for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) { + if(xml->enterbranch("SYSTEM_EFFECT", nefx) == 0) + continue; + if(xml->enterbranch("EFFECT")) { + sysefx[nefx]->getfromXML(xml); + xml->exitbranch(); + } + + for(int partefx = 0; partefx < NUM_MIDI_PARTS; ++partefx) { + if(xml->enterbranch("VOLUME", partefx) == 0) + continue; + setPsysefxvol(partefx, nefx, + xml->getpar127("vol", Psysefxvol[partefx][nefx])); + xml->exitbranch(); + } + + for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) { + if(xml->enterbranch("SENDTO", tonefx) == 0) + continue; + setPsysefxsend(nefx, tonefx, + xml->getpar127("send_vol", + Psysefxsend[nefx][tonefx])); + xml->exitbranch(); + } + xml->exitbranch(); + } + xml->exitbranch(); + } + + + if(xml->enterbranch("INSERTION_EFFECTS")) { + for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) { + if(xml->enterbranch("INSERTION_EFFECT", nefx) == 0) + continue; + Pinsparts[nefx] = xml->getpar("part", + Pinsparts[nefx], + -2, + NUM_MIDI_PARTS); + if(xml->enterbranch("EFFECT")) { + insefx[nefx]->getfromXML(xml); + xml->exitbranch(); + } + xml->exitbranch(); + } + + xml->exitbranch(); + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Master.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Master.h new file mode 100644 index 000000000..2238d7593 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Master.h @@ -0,0 +1,183 @@ +/* + ZynAddSubFX - a software synthesizer + + Master.h - It sends Midi Messages to Parts, receives samples from parts, + process them with system/insertion effects and mix them + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef MASTER_H +#define MASTER_H +#include + +#include "../globals.h" +#include "Microtonal.h" + +#include "Bank.h" +#include "Recorder.h" +#include "Dump.h" +#include "XMLwrapper.h" + +#include "../Params/Controller.h" + +typedef enum { + MUTEX_TRYLOCK, MUTEX_LOCK, MUTEX_UNLOCK +} lockset; + +extern Dump dump; + +struct vuData { + vuData(void); + float outpeakl, outpeakr, maxoutpeakl, maxoutpeakr, + rmspeakl, rmspeakr; + int clipped; +}; + + +/** It sends Midi Messages to Parts, receives samples from parts, + * process them with system/insertion effects and mix them */ +class Master +{ + public: + /** Constructor TODO make private*/ + Master(); + /** Destructor*/ + ~Master(); + + static Master &getInstance(); + static void deleteInstance(); + + /**Saves all settings to a XML file + * @return 0 for ok or <0 if there is an error*/ + int saveXML(const char *filename); + + /**This adds the parameters to the XML data*/ + void add2XML(XMLwrapper *xml); + + void defaults(); + + + /**loads all settings from a XML file + * @return 0 for ok or -1 if there is an error*/ + int loadXML(const char *filename); + void applyparameters(bool lockmutex = true); + + void getfromXML(XMLwrapper *xml); + + /**get all data to a newly allocated array (used for VST) + * @return the datasize*/ + int getalldata(char **data); + /**put all data from the *data array to zynaddsubfx parameters (used for VST)*/ + void putalldata(char *data, int size); + + //Mutex control + /**Control the Master's mutex state. + * @param lockset either trylock, lock, or unlock. + * @return true when successful false otherwise.*/ + bool mutexLock(lockset request); + + //Midi IN + void noteOn(char chan, char note, char velocity); + void noteOff(char chan, char note); + void polyphonicAftertouch(char chan, char note, char velocity); + void setController(char chan, int type, int par); + void setProgram(char chan, unsigned int pgm); + //void NRPN... + + + void ShutUp(); + int shutup; + + void vuUpdate(const float *outl, const float *outr); + + /**Audio Output*/ + void AudioOut(float *outl, float *outr); + /**Audio Output (for callback mode). This allows the program to be controled by an external program*/ + void GetAudioOutSamples(size_t nsamples, + unsigned samplerate, + float *outl, + float *outr); + + + void partonoff(int npart, int what); + + /**parts \todo see if this can be made to be dynamic*/ + class Part * part[NUM_MIDI_PARTS]; + + //parameters + + unsigned char Pvolume; + unsigned char Pkeyshift; + unsigned char Psysefxvol[NUM_SYS_EFX][NUM_MIDI_PARTS]; + unsigned char Psysefxsend[NUM_SYS_EFX][NUM_SYS_EFX]; + + //parameters control + void setPvolume(char Pvolume_); + void setPkeyshift(char Pkeyshift_); + void setPsysefxvol(int Ppart, int Pefx, char Pvol); + void setPsysefxsend(int Pefxfrom, int Pefxto, char Pvol); + + //effects + class EffectMgr * sysefx[NUM_SYS_EFX]; //system + class EffectMgr * insefx[NUM_INS_EFX]; //insertion +// void swapcopyeffects(int what,int type,int neff1,int neff2); + + //HDD recorder + Recorder HDDRecorder; + + //part that's apply the insertion effect; -1 to disable + short int Pinsparts[NUM_INS_EFX]; + + + //peaks for VU-meter + void vuresetpeaks(); + //get VU-meter data + vuData getVuData(); + + //peaks for part VU-meters + /**\todo synchronize this with a mutex*/ + float vuoutpeakpart[NUM_MIDI_PARTS]; + unsigned char fakepeakpart[NUM_MIDI_PARTS]; //this is used to compute the "peak" when the part is disabled + + Controller ctl; + bool swaplr; //if L and R are swapped + + //other objects + Microtonal microtonal; + Bank bank; + + class FFTwrapper * fft; + pthread_mutex_t mutex; + pthread_mutex_t vumutex; + + + private: + vuData vu; + float volume; + float sysefxvol[NUM_SYS_EFX][NUM_MIDI_PARTS]; + float sysefxsend[NUM_SYS_EFX][NUM_SYS_EFX]; + int keyshift; + + //information relevent to generating plugin audio samples + float *bufl; + float *bufr; + off_t off; + size_t smps; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Microtonal.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Microtonal.cpp new file mode 100644 index 000000000..fc0e015e1 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Microtonal.cpp @@ -0,0 +1,694 @@ +/* + ZynAddSubFX - a software synthesizer + + Microtonal.cpp - Tuning settings and microtonal capabilities + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include "Microtonal.h" + +#define MAX_LINE_SIZE 80 + +Microtonal::Microtonal() +{ + Pname = new unsigned char[MICROTONAL_MAX_NAME_LEN]; + Pcomment = new unsigned char[MICROTONAL_MAX_NAME_LEN]; + defaults(); +} + +void Microtonal::defaults() +{ + Pinvertupdown = 0; + Pinvertupdowncenter = 60; + octavesize = 12; + Penabled = 0; + PAnote = 69; + PAfreq = 440.0f; + Pscaleshift = 64; + + Pfirstkey = 0; + Plastkey = 127; + Pmiddlenote = 60; + Pmapsize = 12; + Pmappingenabled = 0; + + for(int i = 0; i < 128; ++i) + Pmapping[i] = i; + + for(int i = 0; i < MAX_OCTAVE_SIZE; ++i) { + octave[i].tuning = tmpoctave[i].tuning = powf( + 2, + (i % octavesize + + 1) / 12.0f); + octave[i].type = tmpoctave[i].type = 1; + octave[i].x1 = tmpoctave[i].x1 = (i % octavesize + 1) * 100; + octave[i].x2 = tmpoctave[i].x2 = 0; + } + octave[11].type = 2; + octave[11].x1 = 2; + octave[11].x2 = 1; + for(int i = 0; i < MICROTONAL_MAX_NAME_LEN; ++i) { + Pname[i] = '\0'; + Pcomment[i] = '\0'; + } + snprintf((char *) Pname, MICROTONAL_MAX_NAME_LEN, "12tET"); + snprintf((char *) Pcomment, + MICROTONAL_MAX_NAME_LEN, + "Equal Temperament 12 notes per octave"); + Pglobalfinedetune = 64; +} + +Microtonal::~Microtonal() +{ + delete [] Pname; + delete [] Pcomment; +} + +/* + * Get the size of the octave + */ +unsigned char Microtonal::getoctavesize() const +{ + if(Penabled != 0) + return octavesize; + else + return 12; +} + +/* + * Get the frequency according the note number + */ +float Microtonal::getnotefreq(int note, int keyshift) const +{ + // in this function will appears many times things like this: + // var=(a+b*100)%b + // I had written this way because if I use var=a%b gives unwanted results when a<0 + // This is the same with divisions. + + if((Pinvertupdown != 0) && ((Pmappingenabled == 0) || (Penabled == 0))) + note = (int) Pinvertupdowncenter * 2 - note; + + //compute global fine detune + float globalfinedetunerap = powf(2.0f, + (Pglobalfinedetune - 64.0f) / 1200.0f); //-64.0f .. 63.0f cents + + if(Penabled == 0) + return powf(2.0f, + (note - PAnote + + keyshift) / 12.0f) * PAfreq * globalfinedetunerap; //12tET + + int scaleshift = + ((int)Pscaleshift - 64 + (int) octavesize * 100) % octavesize; + + //compute the keyshift + float rap_keyshift = 1.0f; + if(keyshift != 0) { + int kskey = (keyshift + (int)octavesize * 100) % octavesize; + int ksoct = (keyshift + (int)octavesize * 100) / octavesize - 100; + rap_keyshift = (kskey == 0) ? (1.0f) : (octave[kskey - 1].tuning); + rap_keyshift *= powf(octave[octavesize - 1].tuning, ksoct); + } + + //if the mapping is enabled + if(Pmappingenabled != 0) { + if((note < Pfirstkey) || (note > Plastkey)) + return -1.0f; + //Compute how many mapped keys are from middle note to reference note + //and find out the proportion between the freq. of middle note and "A" note + int tmp = PAnote - Pmiddlenote, minus = 0; + if(tmp < 0) { + tmp = -tmp; + minus = 1; + } + int deltanote = 0; + for(int i = 0; i < tmp; ++i) + if(Pmapping[i % Pmapsize] >= 0) + deltanote++; + float rap_anote_middlenote = + (deltanote == + 0) ? (1.0f) : (octave[(deltanote - 1) % octavesize].tuning); + if(deltanote != 0) + rap_anote_middlenote *= + powf(octave[octavesize - 1].tuning, + (deltanote - 1) / octavesize); + if(minus != 0) + rap_anote_middlenote = 1.0f / rap_anote_middlenote; + + //Convert from note (midi) to degree (note from the tunning) + int degoct = + (note - (int)Pmiddlenote + (int) Pmapsize + * 200) / (int)Pmapsize - 200; + int degkey = (note - Pmiddlenote + (int)Pmapsize * 100) % Pmapsize; + degkey = Pmapping[degkey]; + if(degkey < 0) + return -1.0f; //this key is not mapped + + //invert the keyboard upside-down if it is asked for + //TODO: do the right way by using Pinvertupdowncenter + if(Pinvertupdown != 0) { + degkey = octavesize - degkey - 1; + degoct = -degoct; + } + //compute the frequency of the note + degkey = degkey + scaleshift; + degoct += degkey / octavesize; + degkey %= octavesize; + + float freq = (degkey == 0) ? (1.0f) : octave[degkey - 1].tuning; + freq *= powf(octave[octavesize - 1].tuning, degoct); + freq *= PAfreq / rap_anote_middlenote; + freq *= globalfinedetunerap; + if(scaleshift != 0) + freq /= octave[scaleshift - 1].tuning; + return freq * rap_keyshift; + } + else { //if the mapping is disabled + int nt = note - PAnote + scaleshift; + int ntkey = (nt + (int)octavesize * 100) % octavesize; + int ntoct = (nt - ntkey) / octavesize; + + float oct = octave[octavesize - 1].tuning; + float freq = + octave[(ntkey + octavesize - 1) % octavesize].tuning * powf(oct, + ntoct) + * PAfreq; + if(ntkey == 0) + freq /= oct; + if(scaleshift != 0) + freq /= octave[scaleshift - 1].tuning; +// fprintf(stderr,"note=%d freq=%.3f cents=%d\n",note,freq,(int)floor(logf(freq/PAfreq)/logf(2.0f)*1200.0f+0.5f)); + freq *= globalfinedetunerap; + return freq * rap_keyshift; + } +} + +bool Microtonal::operator==(const Microtonal µ) const +{ + return !(*this != micro); +} + +bool Microtonal::operator!=(const Microtonal µ) const +{ + //A simple macro to test equality MiCRotonal EQuals (not the perfect + //approach, but good enough) +#define MCREQ(x) if(x != micro.x) \ + return true + + //for floats +#define FMCREQ(x) if(!((x < micro.x + 0.0001f) && (x > micro.x - 0.0001f))) \ + return true + + MCREQ(Pinvertupdown); + MCREQ(Pinvertupdowncenter); + MCREQ(octavesize); + MCREQ(Penabled); + MCREQ(PAnote); + FMCREQ(PAfreq); + MCREQ(Pscaleshift); + + MCREQ(Pfirstkey); + MCREQ(Plastkey); + MCREQ(Pmiddlenote); + MCREQ(Pmapsize); + MCREQ(Pmappingenabled); + + for(int i = 0; i < 128; ++i) + MCREQ(Pmapping[i]); + + for(int i = 0; i < octavesize; ++i) { + FMCREQ(octave[i].tuning); + MCREQ(octave[i].type); + MCREQ(octave[i].x1); + MCREQ(octave[i].x2); + } + if(strcmp((const char *)this->Pname, (const char *)micro.Pname)) + return true; + if(strcmp((const char *)this->Pcomment, (const char *)micro.Pcomment)) + return true; + MCREQ(Pglobalfinedetune); + return false; + + //undefine macros, as they are no longer needed +#undef MCREQ +#undef FMCREQ +} + + +/* + * Convert a line to tunings; returns -1 if it ok + */ +int Microtonal::linetotunings(unsigned int nline, const char *line) +{ + int x1 = -1, x2 = -1, type = -1; + float x = -1.0f, tmp, tuning = 1.0f; + if(strstr(line, "/") == NULL) { + if(strstr(line, ".") == NULL) { // M case (M=M/1) + sscanf(line, "%d", &x1); + x2 = 1; + type = 2; //division + } + else { // float number case + sscanf(line, "%f", &x); + if(x < 0.000001f) + return 1; + type = 1; //float type(cents) + } + } + else { // M/N case + sscanf(line, "%d/%d", &x1, &x2); + if((x1 < 0) || (x2 < 0)) + return 1; + if(x2 == 0) + x2 = 1; + type = 2; //division + } + + if(x1 <= 0) + x1 = 1; //not allow zero frequency sounds (consider 0 as 1) + + //convert to float if the number are too big + if((type == 2) + && ((x1 > (128 * 128 * 128 - 1)) || (x2 > (128 * 128 * 128 - 1)))) { + type = 1; + x = ((float) x1) / x2; + } + switch(type) { + case 1: + x1 = (int) floor(x); + tmp = fmod(x, 1.0f); + x2 = (int) (floor(tmp * 1e6)); + tuning = powf(2.0f, x / 1200.0f); + break; + case 2: + x = ((float)x1) / x2; + tuning = x; + break; + } + + tmpoctave[nline].tuning = tuning; + tmpoctave[nline].type = type; + tmpoctave[nline].x1 = x1; + tmpoctave[nline].x2 = x2; + + return -1; //ok +} + +/* + * Convert the text to tunnings + */ +int Microtonal::texttotunings(const char *text) +{ + unsigned int i, k = 0, nl = 0; + char *lin; + lin = new char[MAX_LINE_SIZE + 1]; + while(k < strlen(text)) { + for(i = 0; i < MAX_LINE_SIZE; ++i) { + lin[i] = text[k++]; + if(lin[i] < 0x20) + break; + } + lin[i] = '\0'; + if(strlen(lin) == 0) + continue; + int err = linetotunings(nl, lin); + if(err != -1) { + delete [] lin; + return nl; //Parse error + } + nl++; + } + delete [] lin; + if(nl > MAX_OCTAVE_SIZE) + nl = MAX_OCTAVE_SIZE; + if(nl == 0) + return -2; //the input is empty + octavesize = nl; + for(i = 0; i < octavesize; ++i) { + octave[i].tuning = tmpoctave[i].tuning; + octave[i].type = tmpoctave[i].type; + octave[i].x1 = tmpoctave[i].x1; + octave[i].x2 = tmpoctave[i].x2; + } + return -1; //ok +} + +/* + * Convert the text to mapping + */ +void Microtonal::texttomapping(const char *text) +{ + unsigned int i, k = 0; + char *lin; + lin = new char[MAX_LINE_SIZE + 1]; + for(i = 0; i < 128; ++i) + Pmapping[i] = -1; + int tx = 0; + while(k < strlen(text)) { + for(i = 0; i < MAX_LINE_SIZE; ++i) { + lin[i] = text[k++]; + if(lin[i] < 0x20) + break; + } + lin[i] = '\0'; + if(strlen(lin) == 0) + continue; + + int tmp = 0; + if(sscanf(lin, "%d", &tmp) == 0) + tmp = -1; + if(tmp < -1) + tmp = -1; + Pmapping[tx] = tmp; + + if((tx++) > 127) + break; + } + delete [] lin; + + if(tx == 0) + tx = 1; + Pmapsize = tx; +} + +/* + * Convert tunning to text line + */ +void Microtonal::tuningtoline(int n, char *line, int maxn) +{ + if((n > octavesize) || (n > MAX_OCTAVE_SIZE)) { + line[0] = '\0'; + return; + } + if(octave[n].type == 1) + snprintf(line, maxn, "%d.%06d", octave[n].x1, octave[n].x2); + if(octave[n].type == 2) + snprintf(line, maxn, "%d/%d", octave[n].x1, octave[n].x2); +} + + +int Microtonal::loadline(FILE *file, char *line) +{ + do { + if(fgets(line, 500, file) == 0) + return 1; + } while(line[0] == '!'); + return 0; +} +/* + * Loads the tunnings from a scl file + */ +int Microtonal::loadscl(const char *filename) +{ + FILE *file = fopen(filename, "r"); + char tmp[500]; + fseek(file, 0, SEEK_SET); + //loads the short description + if(loadline(file, &tmp[0]) != 0) + return 2; + for(int i = 0; i < 500; ++i) + if(tmp[i] < 32) + tmp[i] = 0; + snprintf((char *) Pname, MICROTONAL_MAX_NAME_LEN, "%s", tmp); + snprintf((char *) Pcomment, MICROTONAL_MAX_NAME_LEN, "%s", tmp); + //loads the number of the notes + if(loadline(file, &tmp[0]) != 0) + return 2; + int nnotes = MAX_OCTAVE_SIZE; + sscanf(&tmp[0], "%d", &nnotes); + if(nnotes > MAX_OCTAVE_SIZE) + return 2; + //load the tunnings + for(int nline = 0; nline < nnotes; ++nline) { + if(loadline(file, &tmp[0]) != 0) + return 2; + linetotunings(nline, &tmp[0]); + } + fclose(file); + + octavesize = nnotes; + for(int i = 0; i < octavesize; ++i) { + octave[i].tuning = tmpoctave[i].tuning; + octave[i].type = tmpoctave[i].type; + octave[i].x1 = tmpoctave[i].x1; + octave[i].x2 = tmpoctave[i].x2; + } + + return 0; +} + +/* + * Loads the mapping from a kbm file + */ +int Microtonal::loadkbm(const char *filename) +{ + FILE *file = fopen(filename, "r"); + int x; + char tmp[500]; + + fseek(file, 0, SEEK_SET); + //loads the mapsize + if(loadline(file, &tmp[0]) != 0) + return 2; + if(sscanf(&tmp[0], "%d", &x) == 0) + return 2; + if(x < 1) + x = 0; + if(x > 127) + x = 127; //just in case... + Pmapsize = x; + //loads first MIDI note to retune + if(loadline(file, &tmp[0]) != 0) + return 2; + if(sscanf(&tmp[0], "%d", &x) == 0) + return 2; + if(x < 1) + x = 0; + if(x > 127) + x = 127; //just in case... + Pfirstkey = x; + //loads last MIDI note to retune + if(loadline(file, &tmp[0]) != 0) + return 2; + if(sscanf(&tmp[0], "%d", &x) == 0) + return 2; + if(x < 1) + x = 0; + if(x > 127) + x = 127; //just in case... + Plastkey = x; + //loads last the middle note where scale fro scale degree=0 + if(loadline(file, &tmp[0]) != 0) + return 2; + if(sscanf(&tmp[0], "%d", &x) == 0) + return 2; + if(x < 1) + x = 0; + if(x > 127) + x = 127; //just in case... + Pmiddlenote = x; + //loads the reference note + if(loadline(file, &tmp[0]) != 0) + return 2; + if(sscanf(&tmp[0], "%d", &x) == 0) + return 2; + if(x < 1) + x = 0; + if(x > 127) + x = 127; //just in case... + PAnote = x; + //loads the reference freq. + if(loadline(file, &tmp[0]) != 0) + return 2; + float tmpPAfreq = 440.0f; + if(sscanf(&tmp[0], "%f", &tmpPAfreq) == 0) + return 2; + PAfreq = tmpPAfreq; + + //the scale degree(which is the octave) is not loaded, it is obtained by the tunnings with getoctavesize() method + if(loadline(file, &tmp[0]) != 0) + return 2; + + //load the mappings + if(Pmapsize != 0) { + for(int nline = 0; nline < Pmapsize; ++nline) { + if(loadline(file, &tmp[0]) != 0) + return 2; + if(sscanf(&tmp[0], "%d", &x) == 0) + x = -1; + Pmapping[nline] = x; + } + Pmappingenabled = 1; + } + else { + Pmappingenabled = 0; + Pmapping[0] = 0; + Pmapsize = 1; + } + fclose(file); + + return 0; +} + + + +void Microtonal::add2XML(XMLwrapper *xml) const +{ + xml->addparstr("name", (char *) Pname); + xml->addparstr("comment", (char *) Pcomment); + + xml->addparbool("invert_up_down", Pinvertupdown); + xml->addpar("invert_up_down_center", Pinvertupdowncenter); + + xml->addparbool("enabled", Penabled); + xml->addpar("global_fine_detune", Pglobalfinedetune); + + xml->addpar("a_note", PAnote); + xml->addparreal("a_freq", PAfreq); + + if((Penabled == 0) && (xml->minimal)) + return; + + xml->beginbranch("SCALE"); + xml->addpar("scale_shift", Pscaleshift); + xml->addpar("first_key", Pfirstkey); + xml->addpar("last_key", Plastkey); + xml->addpar("middle_note", Pmiddlenote); + + xml->beginbranch("OCTAVE"); + xml->addpar("octave_size", octavesize); + for(int i = 0; i < octavesize; ++i) { + xml->beginbranch("DEGREE", i); + if(octave[i].type == 1) + xml->addparreal("cents", octave[i].tuning); + ; + if(octave[i].type == 2) { + xml->addpar("numerator", octave[i].x1); + xml->addpar("denominator", octave[i].x2); + } + xml->endbranch(); + } + xml->endbranch(); + + xml->beginbranch("KEYBOARD_MAPPING"); + xml->addpar("map_size", Pmapsize); + xml->addpar("mapping_enabled", Pmappingenabled); + for(int i = 0; i < Pmapsize; ++i) { + xml->beginbranch("KEYMAP", i); + xml->addpar("degree", Pmapping[i]); + xml->endbranch(); + } + + xml->endbranch(); + xml->endbranch(); +} + +void Microtonal::getfromXML(XMLwrapper *xml) +{ + xml->getparstr("name", (char *) Pname, MICROTONAL_MAX_NAME_LEN); + xml->getparstr("comment", (char *) Pcomment, MICROTONAL_MAX_NAME_LEN); + + Pinvertupdown = xml->getparbool("invert_up_down", Pinvertupdown); + Pinvertupdowncenter = xml->getpar127("invert_up_down_center", + Pinvertupdowncenter); + + Penabled = xml->getparbool("enabled", Penabled); + Pglobalfinedetune = xml->getpar127("global_fine_detune", Pglobalfinedetune); + + PAnote = xml->getpar127("a_note", PAnote); + PAfreq = xml->getparreal("a_freq", PAfreq, 1.0f, 10000.0f); + + if(xml->enterbranch("SCALE")) { + Pscaleshift = xml->getpar127("scale_shift", Pscaleshift); + Pfirstkey = xml->getpar127("first_key", Pfirstkey); + Plastkey = xml->getpar127("last_key", Plastkey); + Pmiddlenote = xml->getpar127("middle_note", Pmiddlenote); + + if(xml->enterbranch("OCTAVE")) { + octavesize = xml->getpar127("octave_size", octavesize); + for(int i = 0; i < octavesize; ++i) { + if(xml->enterbranch("DEGREE", i) == 0) + continue; + octave[i].x2 = 0; + octave[i].tuning = xml->getparreal("cents", octave[i].tuning); + octave[i].x1 = xml->getpar127("numerator", octave[i].x1); + octave[i].x2 = xml->getpar127("denominator", octave[i].x2); + + if(octave[i].x2 != 0) + octave[i].type = 2; + else { + octave[i].type = 1; + //populate fields for display + float x = logf(octave[i].tuning) / LOG_2 * 1200.0f; + octave[i].x1 = (int) floor(x); + octave[i].x2 = (int) (floor(fmodf(x, 1.0f) * 1e6)); + } + + + xml->exitbranch(); + } + xml->exitbranch(); + } + + if(xml->enterbranch("KEYBOARD_MAPPING")) { + Pmapsize = xml->getpar127("map_size", Pmapsize); + Pmappingenabled = xml->getpar127("mapping_enabled", Pmappingenabled); + for(int i = 0; i < Pmapsize; ++i) { + if(xml->enterbranch("KEYMAP", i) == 0) + continue; + Pmapping[i] = xml->getpar127("degree", Pmapping[i]); + xml->exitbranch(); + } + xml->exitbranch(); + } + xml->exitbranch(); + } +} + + + +int Microtonal::saveXML(const char *filename) const +{ + XMLwrapper *xml = new XMLwrapper(); + + xml->beginbranch("MICROTONAL"); + add2XML(xml); + xml->endbranch(); + + int result = xml->saveXMLfile(filename); + delete (xml); + return result; +} + +int Microtonal::loadXML(const char *filename) +{ + XMLwrapper *xml = new XMLwrapper(); + if(xml->loadXMLfile(filename) < 0) { + delete (xml); + return -1; + } + + if(xml->enterbranch("MICROTONAL") == 0) + return -10; + getfromXML(xml); + xml->exitbranch(); + + delete (xml); + return 0; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Microtonal.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Microtonal.h new file mode 100644 index 000000000..638a1590f --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Microtonal.h @@ -0,0 +1,134 @@ +/* + ZynAddSubFX - a software synthesizer + + Microtonal.h - Tuning settings and microtonal capabilities + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef MICROTONAL_H +#define MICROTONAL_H + +#include "../globals.h" +#include "XMLwrapper.h" + +#define MAX_OCTAVE_SIZE 128 +#define MICROTONAL_MAX_NAME_LEN 120 + +#include + + +/**Tuning settings and microtonal capabilities*/ +class Microtonal +{ + public: + /**Constructor*/ + Microtonal(); + /**Destructor*/ + ~Microtonal(); + void defaults(); + /**Calculates the frequency for a given note + */ + float getnotefreq(int note, int keyshift) const; + + + //Parameters + /**if the keys are inversed (the pitch is lower to keys from the right direction)*/ + unsigned char Pinvertupdown; + + /**the central key of the inversion*/ + unsigned char Pinvertupdowncenter; + + /**0 for 12 key temperate scale, 1 for microtonal*/ + unsigned char Penabled; + + /**the note of "A" key*/ + unsigned char PAnote; + + /**the frequency of the "A" note*/ + float PAfreq; + + /**if the scale is "tuned" to a note, you can tune to other note*/ + unsigned char Pscaleshift; + + //first and last key (to retune) + unsigned char Pfirstkey; + unsigned char Plastkey; + + /**The middle note where scale degree 0 is mapped to*/ + unsigned char Pmiddlenote; + + /**Map size*/ + unsigned char Pmapsize; + + /**Mapping ON/OFF*/ + unsigned char Pmappingenabled; + /**Mapping (keys)*/ + short int Pmapping[128]; + + /**Fine detune to be applied to all notes*/ + unsigned char Pglobalfinedetune; + + // Functions + /** Return the current octave size*/ + unsigned char getoctavesize() const; + /**Convert tunning to string*/ + void tuningtoline(int n, char *line, int maxn); + /**load the tunnings from a .scl file*/ + int loadscl(const char *filename); + /**load the mapping from .kbm file*/ + int loadkbm(const char *filename); + /**Load text into the internal tunings + * + *\todo better description*/ + int texttotunings(const char *text); + /**Load text into the internal mappings + * + *\todo better description*/ + void texttomapping(const char *text); + + /**Name of Microtonal tuning*/ + unsigned char *Pname; + /**Comment about the tuning*/ + unsigned char *Pcomment; + + void add2XML(XMLwrapper *xml) const; + void getfromXML(XMLwrapper *xml); + int saveXML(const char *filename) const; + int loadXML(const char *filename); + + //simple operators primarily for debug + bool operator==(const Microtonal µ) const; + bool operator!=(const Microtonal µ) const; + + private: + int linetotunings(unsigned int nline, const char *line); + int loadline(FILE *file, char *line); //loads a line from the text file, while ignoring the lines beggining with "!" + unsigned char octavesize; + struct { + unsigned char type; //1 for cents or 2 for division + + // the real tuning (eg. +1.05946f for one halftone) + // or 2.0f for one octave + float tuning; + + //the real tunning is x1/x2 + unsigned int x1, x2; + } octave[MAX_OCTAVE_SIZE], tmpoctave[MAX_OCTAVE_SIZE]; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Part.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Part.cpp new file mode 100644 index 000000000..50d4cedec --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Part.cpp @@ -0,0 +1,1338 @@ +/* + ZynAddSubFX - a software synthesizer + + Part.cpp - Part implementation + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "Part.h" +#include "Microtonal.h" +#include "Util.h" +#include "XMLwrapper.h" +#include "../Effects/EffectMgr.h" +#include "../Params/ADnoteParameters.h" +#include "../Params/SUBnoteParameters.h" +#include "../Params/PADnoteParameters.h" +#include "../Synth/ADnote.h" +#include "../Synth/SUBnote.h" +#include "../Synth/PADnote.h" +#include "../DSP/FFTwrapper.h" +#include +#include +#include + +Part::Part(Microtonal *microtonal_, FFTwrapper *fft_, pthread_mutex_t *mutex_) +{ + microtonal = microtonal_; + fft = fft_; + mutex = mutex_; + pthread_mutex_init(&load_mutex, NULL); + partoutl = new float [synth->buffersize]; + partoutr = new float [synth->buffersize]; + + for(int n = 0; n < NUM_KIT_ITEMS; ++n) { + kit[n].Pname = new unsigned char [PART_MAX_NAME_LEN]; + kit[n].adpars = NULL; + kit[n].subpars = NULL; + kit[n].padpars = NULL; + } + + kit[0].adpars = new ADnoteParameters(fft); + kit[0].subpars = new SUBnoteParameters(); + kit[0].padpars = new PADnoteParameters(fft, mutex); + + //Part's Insertion Effects init + for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) { + partefx[nefx] = new EffectMgr(1, mutex); + Pefxbypass[nefx] = false; + } + + for(int n = 0; n < NUM_PART_EFX + 1; ++n) { + partfxinputl[n] = new float [synth->buffersize]; + partfxinputr[n] = new float [synth->buffersize]; + } + + killallnotes = 0; + oldfreq = -1.0f; + + for(int i = 0; i < POLIPHONY; ++i) { + partnote[i].status = KEY_OFF; + partnote[i].note = -1; + partnote[i].itemsplaying = 0; + for(int j = 0; j < NUM_KIT_ITEMS; ++j) { + partnote[i].kititem[j].adnote = NULL; + partnote[i].kititem[j].subnote = NULL; + partnote[i].kititem[j].padnote = NULL; + } + partnote[i].time = 0; + } + cleanup(); + + Pname = new unsigned char [PART_MAX_NAME_LEN]; + + oldvolumel = oldvolumer = 0.5f; + lastnote = -1; + lastpos = 0; // lastpos will store previously used NoteOn(...)'s pos. + lastlegatomodevalid = false; // To store previous legatomodevalid value. + + defaults(); +} + +void Part::defaults() +{ + Penabled = 0; + Pminkey = 0; + Pmaxkey = 127; + Pnoteon = 1; + Ppolymode = 1; + Plegatomode = 0; + setPvolume(96); + Pkeyshift = 64; + Prcvchn = 0; + setPpanning(64); + Pvelsns = 64; + Pveloffs = 64; + Pkeylimit = 15; + defaultsinstrument(); + ctl.defaults(); +} + +void Part::defaultsinstrument() +{ + ZERO(Pname, PART_MAX_NAME_LEN); + + info.Ptype = 0; + ZERO(info.Pauthor, MAX_INFO_TEXT_SIZE + 1); + ZERO(info.Pcomments, MAX_INFO_TEXT_SIZE + 1); + + Pkitmode = 0; + Pdrummode = 0; + + for(int n = 0; n < NUM_KIT_ITEMS; ++n) { + kit[n].Penabled = 0; + kit[n].Pmuted = 0; + kit[n].Pminkey = 0; + kit[n].Pmaxkey = 127; + kit[n].Padenabled = 0; + kit[n].Psubenabled = 0; + kit[n].Ppadenabled = 0; + ZERO(kit[n].Pname, PART_MAX_NAME_LEN); + kit[n].Psendtoparteffect = 0; + if(n != 0) + setkititemstatus(n, 0); + } + kit[0].Penabled = 1; + kit[0].Padenabled = 1; + kit[0].adpars->defaults(); + kit[0].subpars->defaults(); + kit[0].padpars->defaults(); + + for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) { + partefx[nefx]->defaults(); + Pefxroute[nefx] = 0; //route to next effect + } +} + + + +/* + * Cleanup the part + */ +void Part::cleanup(bool final_) +{ + for(int k = 0; k < POLIPHONY; ++k) + KillNotePos(k); + for(int i = 0; i < synth->buffersize; ++i) { + partoutl[i] = final_ ? 0.0f : denormalkillbuf[i]; + partoutr[i] = final_ ? 0.0f : denormalkillbuf[i]; + } + ctl.resetall(); + for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) + partefx[nefx]->cleanup(); + for(int n = 0; n < NUM_PART_EFX + 1; ++n) + for(int i = 0; i < synth->buffersize; ++i) { + partfxinputl[n][i] = final_ ? 0.0f : denormalkillbuf[i]; + partfxinputr[n][i] = final_ ? 0.0f : denormalkillbuf[i]; + } +} + +Part::~Part() +{ + cleanup(true); + for(int n = 0; n < NUM_KIT_ITEMS; ++n) { + if(kit[n].adpars != NULL) + delete (kit[n].adpars); + if(kit[n].subpars != NULL) + delete (kit[n].subpars); + if(kit[n].padpars != NULL) + delete (kit[n].padpars); + kit[n].adpars = NULL; + kit[n].subpars = NULL; + kit[n].padpars = NULL; + delete [] kit[n].Pname; + } + + delete [] Pname; + delete [] partoutl; + delete [] partoutr; + for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) + delete (partefx[nefx]); + for(int n = 0; n < NUM_PART_EFX + 1; ++n) { + delete [] partfxinputl[n]; + delete [] partfxinputr[n]; + } +} + +/* + * Note On Messages + */ +void Part::NoteOn(unsigned char note, + unsigned char velocity, + int masterkeyshift) +{ + int i, pos; + + // Legato and MonoMem used vars: + int posb = POLIPHONY - 1; // Just a dummy initial value. + bool legatomodevalid = false; //true when legato mode is determined applicable. + bool doinglegato = false; // true when we determined we do a legato note. + bool ismonofirstnote = false; /*(In Mono/Legato) true when we determined + no other notes are held down or sustained.*/ + int lastnotecopy = lastnote; //Useful after lastnote has been changed. + + if(Pnoteon == 0) + return; + if((note < Pminkey) || (note > Pmaxkey)) + return; + + // MonoMem stuff: + if(Ppolymode == 0) { // If Poly is off + monomemnotes.push_back(note); // Add note to the list. + monomem[note].velocity = velocity; // Store this note's velocity. + monomem[note].mkeyshift = masterkeyshift; /* Store masterkeyshift too, + I'm not sure why though... */ + if((partnote[lastpos].status != KEY_PLAYING) + && (partnote[lastpos].status != KEY_RELASED_AND_SUSTAINED)) + ismonofirstnote = true; // No other keys are held or sustained. + } + else + // Poly mode is On so just make sure the list is empty. + if(not monomemnotes.empty()) + monomemnotes.clear(); + + lastnote = note; + + pos = -1; + for(i = 0; i < POLIPHONY; ++i) + if(partnote[i].status == KEY_OFF) { + pos = i; + break; + } + + if((Plegatomode != 0) && (Pdrummode == 0)) { + if(Ppolymode != 0) { + fprintf( + stderr, + "ZynAddSubFX WARNING: Poly and Legato modes are both On, that should not happen ! ... Disabling Legato mode ! - (Part.cpp::NoteOn(..))\n"); + Plegatomode = 0; + } + else { + // Legato mode is on and applicable. + legatomodevalid = true; + if((not ismonofirstnote) && (lastlegatomodevalid)) { + // At least one other key is held or sustained, and the + // previous note was played while in valid legato mode. + doinglegato = true; // So we'll do a legato note. + pos = lastpos; // A legato note uses same pos as previous.. + posb = lastposb; // .. same goes for posb. + } + else { + // Legato mode is valid, but this is only a first note. + for(i = 0; i < POLIPHONY; ++i) + if((partnote[i].status == KEY_PLAYING) + || (partnote[i].status == KEY_RELASED_AND_SUSTAINED)) + RelaseNotePos(i); + + // Set posb + posb = (pos + 1) % POLIPHONY; //We really want it (if the following fails) + for(i = 0; i < POLIPHONY; ++i) + if((partnote[i].status == KEY_OFF) && (pos != i)) { + posb = i; + break; + } + } + lastposb = posb; // Keep a trace of used posb + } + } + else // Legato mode is either off or non-applicable. + if(Ppolymode == 0) { //if the mode is 'mono' turn off all other notes + for(i = 0; i < POLIPHONY; ++i) + if(partnote[i].status == KEY_PLAYING) + RelaseNotePos(i); + RelaseSustainedKeys(); + } + lastlegatomodevalid = legatomodevalid; + + if(pos == -1) + //test + fprintf(stderr, + "%s", + "NOTES TOO MANY (> POLIPHONY) - (Part.cpp::NoteOn(..))\n"); + else { + //start the note + partnote[pos].status = KEY_PLAYING; + partnote[pos].note = note; + if(legatomodevalid) { + partnote[posb].status = KEY_PLAYING; + partnote[posb].note = note; + } + + //this computes the velocity sensing of the part + float vel = VelF(velocity / 127.0f, Pvelsns); + + //compute the velocity offset + vel += (Pveloffs - 64.0f) / 64.0f; + if(vel < 0.0f) + vel = 0.0f; + else + if(vel > 1.0f) + vel = 1.0f; + + //compute the keyshift + int partkeyshift = (int)Pkeyshift - 64; + int keyshift = masterkeyshift + partkeyshift; + + //initialise note frequency + float notebasefreq; + if(Pdrummode == 0) { + notebasefreq = microtonal->getnotefreq(note, keyshift); + if(notebasefreq < 0.0f) + return; //the key is no mapped + } + else + notebasefreq = 440.0f * powf(2.0f, (note - 69.0f) / 12.0f); + ; + + //Portamento + if(oldfreq < 1.0f) + oldfreq = notebasefreq; //this is only the first note is played + + // For Mono/Legato: Force Portamento Off on first + // notes. That means it is required that the previous note is + // still held down or sustained for the Portamento to activate + // (that's like Legato). + int portamento = 0; + if((Ppolymode != 0) || (not ismonofirstnote)) + // I added a third argument to the + // ctl.initportamento(...) function to be able + // to tell it if we're doing a legato note. + portamento = ctl.initportamento(oldfreq, notebasefreq, doinglegato); + + if(portamento != 0) + ctl.portamento.noteusing = pos; + oldfreq = notebasefreq; + + lastpos = pos; // Keep a trace of used pos. + + if(doinglegato) { + // Do Legato note + if(Pkitmode == 0) { // "normal mode" legato note + if((kit[0].Padenabled != 0) + && (partnote[pos].kititem[0].adnote != NULL) + && (partnote[posb].kititem[0].adnote != NULL)) { + partnote[pos].kititem[0].adnote->legatonote(notebasefreq, + vel, + portamento, + note, + true); //'true' is to tell it it's being called from here. + partnote[posb].kititem[0].adnote->legatonote(notebasefreq, + vel, + portamento, + note, + true); + } + + if((kit[0].Psubenabled != 0) + && (partnote[pos].kititem[0].subnote != NULL) + && (partnote[posb].kititem[0].subnote != NULL)) { + partnote[pos].kititem[0].subnote->legatonote( + notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[0].subnote->legatonote( + notebasefreq, vel, portamento, note, true); + } + + if((kit[0].Ppadenabled != 0) + && (partnote[pos].kititem[0].padnote != NULL) + && (partnote[posb].kititem[0].padnote != NULL)) { + partnote[pos].kititem[0].padnote->legatonote( + notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[0].padnote->legatonote( + notebasefreq, vel, portamento, note, true); + } + } + else { // "kit mode" legato note + int ci = 0; + for(int item = 0; item < NUM_KIT_ITEMS; ++item) { + if(kit[item].Pmuted != 0) + continue; + if((note < kit[item].Pminkey) || (note > kit[item].Pmaxkey)) + continue; + + if((lastnotecopy < kit[item].Pminkey) + || (lastnotecopy > kit[item].Pmaxkey)) + continue; // We will not perform legato across 2 key regions. + + partnote[pos].kititem[ci].sendtoparteffect = + (kit[item].Psendtoparteffect < + NUM_PART_EFX ? kit[item].Psendtoparteffect : + NUM_PART_EFX); //if this parameter is 127 for "unprocessed" + partnote[posb].kititem[ci].sendtoparteffect = + (kit[item].Psendtoparteffect < + NUM_PART_EFX ? kit[item].Psendtoparteffect : + NUM_PART_EFX); + + if((kit[item].Padenabled != 0) && (kit[item].adpars != NULL) + && (partnote[pos].kititem[ci].adnote != NULL) + && (partnote[posb].kititem[ci].adnote != NULL)) { + partnote[pos].kititem[ci].adnote->legatonote( + notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[ci].adnote->legatonote( + notebasefreq, vel, portamento, note, true); + } + if((kit[item].Psubenabled != 0) + && (kit[item].subpars != NULL) + && (partnote[pos].kititem[ci].subnote != NULL) + && (partnote[posb].kititem[ci].subnote != NULL)) { + partnote[pos].kititem[ci].subnote->legatonote( + notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[ci].subnote->legatonote( + notebasefreq, vel, portamento, note, true); + } + if((kit[item].Ppadenabled != 0) + && (kit[item].padpars != NULL) + && (partnote[pos].kititem[ci].padnote != NULL) + && (partnote[posb].kititem[ci].padnote != NULL)) { + partnote[pos].kititem[ci].padnote->legatonote( + notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[ci].padnote->legatonote( + notebasefreq, vel, portamento, note, true); + } + + if((kit[item].adpars != NULL) + || (kit[item].subpars != NULL) + || (kit[item].padpars != NULL)) { + ci++; + if(((kit[item].Padenabled != 0) + || (kit[item].Psubenabled != 0) + || (kit[item].Ppadenabled != 0)) && (Pkitmode == 2)) + break; + } + } + if(ci == 0) { + // No legato were performed at all, so pretend nothing happened: + monomemnotes.pop_back(); // Remove last note from the list. + lastnote = lastnotecopy; // Set lastnote back to previous value. + } + } + return; // Ok, Legato note done, return. + } + + partnote[pos].itemsplaying = 0; + if(legatomodevalid) + partnote[posb].itemsplaying = 0; + + if(Pkitmode == 0) { //init the notes for the "normal mode" + partnote[pos].kititem[0].sendtoparteffect = 0; + if(kit[0].Padenabled != 0) + partnote[pos].kititem[0].adnote = new ADnote(kit[0].adpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + false); + if(kit[0].Psubenabled != 0) + partnote[pos].kititem[0].subnote = new SUBnote(kit[0].subpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + false); + if(kit[0].Ppadenabled != 0) + partnote[pos].kititem[0].padnote = new PADnote(kit[0].padpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + false); + if((kit[0].Padenabled != 0) || (kit[0].Psubenabled != 0) + || (kit[0].Ppadenabled != 0)) + partnote[pos].itemsplaying++; + + // Spawn another note (but silent) if legatomodevalid==true + if(legatomodevalid) { + partnote[posb].kititem[0].sendtoparteffect = 0; + if(kit[0].Padenabled != 0) + partnote[posb].kititem[0].adnote = new ADnote(kit[0].adpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + true); //true for silent. + if(kit[0].Psubenabled != 0) + partnote[posb].kititem[0].subnote = new SUBnote( + kit[0].subpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + true); + if(kit[0].Ppadenabled != 0) + partnote[posb].kititem[0].padnote = new PADnote( + kit[0].padpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + true); + if((kit[0].Padenabled != 0) || (kit[0].Psubenabled != 0) + || (kit[0].Ppadenabled != 0)) + partnote[posb].itemsplaying++; + } + } + else //init the notes for the "kit mode" + for(int item = 0; item < NUM_KIT_ITEMS; ++item) { + if(kit[item].Pmuted != 0) + continue; + if((note < kit[item].Pminkey) || (note > kit[item].Pmaxkey)) + continue; + + int ci = partnote[pos].itemsplaying; //ci=current item + + //if this parameter is 127 for "unprocessed" + partnote[pos].kititem[ci].sendtoparteffect = + (kit[item].Psendtoparteffect < NUM_PART_EFX ? + kit[item].Psendtoparteffect : NUM_PART_EFX); + + if((kit[item].adpars != NULL) && ((kit[item].Padenabled) != 0)) + partnote[pos].kititem[ci].adnote = new ADnote( + kit[item].adpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + false); + + if((kit[item].subpars != NULL) && ((kit[item].Psubenabled) != 0)) + partnote[pos].kititem[ci].subnote = new SUBnote( + kit[item].subpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + false); + + if((kit[item].padpars != NULL) && ((kit[item].Ppadenabled) != 0)) + partnote[pos].kititem[ci].padnote = new PADnote( + kit[item].padpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + false); + + // Spawn another note (but silent) if legatomodevalid==true + if(legatomodevalid) { + partnote[posb].kititem[ci].sendtoparteffect = + (kit[item].Psendtoparteffect < + NUM_PART_EFX ? kit[item].Psendtoparteffect : + NUM_PART_EFX); //if this parameter is 127 for "unprocessed" + + if((kit[item].adpars != NULL) + && ((kit[item].Padenabled) != 0)) + partnote[posb].kititem[ci].adnote = new ADnote( + kit[item].adpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + true); //true for silent. + if((kit[item].subpars != NULL) + && ((kit[item].Psubenabled) != 0)) + partnote[posb].kititem[ci].subnote = + new SUBnote(kit[item].subpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + true); + if((kit[item].padpars != NULL) + && ((kit[item].Ppadenabled) != 0)) + partnote[posb].kititem[ci].padnote = + new PADnote(kit[item].padpars, + &ctl, + notebasefreq, + vel, + portamento, + note, + true); + + if((kit[item].adpars != NULL) || (kit[item].subpars != NULL)) + partnote[posb].itemsplaying++; + } + + if((kit[item].adpars != NULL) || (kit[item].subpars != NULL)) { + partnote[pos].itemsplaying++; + if(((kit[item].Padenabled != 0) + || (kit[item].Psubenabled != 0) + || (kit[item].Ppadenabled != 0)) + && (Pkitmode == 2)) + break; + } + } + } + + //this only relase the keys if there is maximum number of keys allowed + setkeylimit(Pkeylimit); +} + +/* + * Note Off Messages + */ +void Part::NoteOff(unsigned char note) //relase the key +{ + int i; + + // This note is released, so we remove it from the list. + if(not monomemnotes.empty()) + monomemnotes.remove(note); + + for(i = POLIPHONY - 1; i >= 0; i--) //first note in, is first out if there are same note multiple times + if((partnote[i].status == KEY_PLAYING) && (partnote[i].note == note)) { + if(ctl.sustain.sustain == 0) { //the sustain pedal is not pushed + if((Ppolymode == 0) && (not monomemnotes.empty())) + MonoMemRenote(); // To play most recent still held note. + else + RelaseNotePos(i); + /// break; + } + else //the sustain pedal is pushed + partnote[i].status = KEY_RELASED_AND_SUSTAINED; + } +} + +void Part::PolyphonicAftertouch(unsigned char note, + unsigned char velocity, + int masterkeyshift) +{ + (void) masterkeyshift; + if(!Pnoteon || (note < Pminkey) || (note > Pmaxkey)) + return; + if(Pdrummode) + return; + + // MonoMem stuff: + if(!Ppolymode) // if Poly is off + + monomem[note].velocity = velocity; // Store this note's velocity. + + + for(int i = 0; i < POLIPHONY; ++i) + if((partnote[i].note == note) && (partnote[i].status == KEY_PLAYING)) { + /* update velocity */ + // compute the velocity offset + float vel = + VelF(velocity / 127.0f, Pvelsns) + (Pveloffs - 64.0f) / 64.0f; + vel = (vel < 0.0f) ? 0.0f : vel; + vel = (vel > 1.0f) ? 1.0f : vel; + + if(!Pkitmode) { // "normal mode" + if(kit[0].Padenabled && partnote[i].kititem[0].adnote) + partnote[i].kititem[0].adnote->setVelocity(vel); + if(kit[0].Psubenabled && partnote[i].kititem[0].subnote) + partnote[i].kititem[0].subnote->setVelocity(vel); + if(kit[0].Ppadenabled && partnote[i].kititem[0].padnote) + partnote[i].kititem[0].padnote->setVelocity(vel); + } + else // "kit mode" + for(int item = 0; item < NUM_KIT_ITEMS; ++item) { + if(kit[item].Pmuted) + continue; + if((note < kit[item].Pminkey) + || (note > kit[item].Pmaxkey)) + continue; + + if(kit[item].Padenabled && partnote[i].kititem[item].adnote) + partnote[i].kititem[item].adnote->setVelocity(vel); + if(kit[item].Psubenabled && partnote[i].kititem[item].subnote) + partnote[i].kititem[item].subnote->setVelocity(vel); + if(kit[item].Ppadenabled && partnote[i].kititem[item].padnote) + partnote[i].kititem[item].padnote->setVelocity(vel); + } + } + +} + +/* + * Controllers + */ +void Part::SetController(unsigned int type, int par) +{ + switch(type) { + case C_pitchwheel: + ctl.setpitchwheel(par); + break; + case C_expression: + ctl.setexpression(par); + setPvolume(Pvolume); //update the volume + break; + case C_portamento: + ctl.setportamento(par); + break; + case C_panning: + ctl.setpanning(par); + setPpanning(Ppanning); //update the panning + break; + case C_filtercutoff: + ctl.setfiltercutoff(par); + break; + case C_filterq: + ctl.setfilterq(par); + break; + case C_bandwidth: + ctl.setbandwidth(par); + break; + case C_modwheel: + ctl.setmodwheel(par); + break; + case C_fmamp: + ctl.setfmamp(par); + break; + case C_volume: + ctl.setvolume(par); + if(ctl.volume.receive != 0) + volume = ctl.volume.volume; + else + setPvolume(Pvolume); + break; + case C_sustain: + ctl.setsustain(par); + if(ctl.sustain.sustain == 0) + RelaseSustainedKeys(); + break; + case C_allsoundsoff: + AllNotesOff(); //Panic + break; + case C_resetallcontrollers: + ctl.resetall(); + RelaseSustainedKeys(); + if(ctl.volume.receive != 0) + volume = ctl.volume.volume; + else + setPvolume(Pvolume); + setPvolume(Pvolume); //update the volume + setPpanning(Ppanning); //update the panning + + for(int item = 0; item < NUM_KIT_ITEMS; ++item) { + if(kit[item].adpars == NULL) + continue; + kit[item].adpars->GlobalPar.Reson-> + sendcontroller(C_resonance_center, 1.0f); + + kit[item].adpars->GlobalPar.Reson-> + sendcontroller(C_resonance_bandwidth, 1.0f); + } + //more update to add here if I add controllers + break; + case C_allnotesoff: + RelaseAllKeys(); + break; + case C_resonance_center: + ctl.setresonancecenter(par); + for(int item = 0; item < NUM_KIT_ITEMS; ++item) { + if(kit[item].adpars == NULL) + continue; + kit[item].adpars->GlobalPar.Reson-> + sendcontroller(C_resonance_center, + ctl.resonancecenter.relcenter); + } + break; + case C_resonance_bandwidth: + ctl.setresonancebw(par); + kit[0].adpars->GlobalPar.Reson-> + sendcontroller(C_resonance_bandwidth, ctl.resonancebandwidth.relbw); + break; + } +} +/* + * Relase the sustained keys + */ + +void Part::RelaseSustainedKeys() +{ + // Let's call MonoMemRenote() on some conditions: + if((Ppolymode == 0) && (not monomemnotes.empty())) + if(monomemnotes.back() != lastnote) // Sustain controller manipulation would cause repeated same note respawn without this check. + MonoMemRenote(); // To play most recent still held note. + + for(int i = 0; i < POLIPHONY; ++i) + if(partnote[i].status == KEY_RELASED_AND_SUSTAINED) + RelaseNotePos(i); +} + +/* + * Relase all keys + */ + +void Part::RelaseAllKeys() +{ + for(int i = 0; i < POLIPHONY; ++i) + if((partnote[i].status != KEY_RELASED) + && (partnote[i].status != KEY_OFF)) //thanks to Frank Neumann + RelaseNotePos(i); +} + +// Call NoteOn(...) with the most recent still held key as new note +// (Made for Mono/Legato). +void Part::MonoMemRenote() +{ + unsigned char mmrtempnote = monomemnotes.back(); // Last list element. + monomemnotes.pop_back(); // We remove it, will be added again in NoteOn(...). + if(Pnoteon == 0) + RelaseNotePos(lastpos); + else + NoteOn(mmrtempnote, monomem[mmrtempnote].velocity, + monomem[mmrtempnote].mkeyshift); +} + +/* + * Release note at position + */ +void Part::RelaseNotePos(int pos) +{ + for(int j = 0; j < NUM_KIT_ITEMS; ++j) { + if(partnote[pos].kititem[j].adnote != NULL) + if(partnote[pos].kititem[j].adnote) + partnote[pos].kititem[j].adnote->relasekey(); + + if(partnote[pos].kititem[j].subnote != NULL) + if(partnote[pos].kititem[j].subnote != NULL) + partnote[pos].kititem[j].subnote->relasekey(); + + if(partnote[pos].kititem[j].padnote != NULL) + if(partnote[pos].kititem[j].padnote) + partnote[pos].kititem[j].padnote->relasekey(); + } + partnote[pos].status = KEY_RELASED; +} + + +/* + * Kill note at position + */ +void Part::KillNotePos(int pos) +{ + partnote[pos].status = KEY_OFF; + partnote[pos].note = -1; + partnote[pos].time = 0; + partnote[pos].itemsplaying = 0; + + for(int j = 0; j < NUM_KIT_ITEMS; ++j) { + if(partnote[pos].kititem[j].adnote != NULL) { + delete (partnote[pos].kititem[j].adnote); + partnote[pos].kititem[j].adnote = NULL; + } + if(partnote[pos].kititem[j].subnote != NULL) { + delete (partnote[pos].kititem[j].subnote); + partnote[pos].kititem[j].subnote = NULL; + } + if(partnote[pos].kititem[j].padnote != NULL) { + delete (partnote[pos].kititem[j].padnote); + partnote[pos].kititem[j].padnote = NULL; + } + } + if(pos == ctl.portamento.noteusing) { + ctl.portamento.noteusing = -1; + ctl.portamento.used = 0; + } +} + + +/* + * Set Part's key limit + */ +void Part::setkeylimit(unsigned char Pkeylimit) +{ + this->Pkeylimit = Pkeylimit; + int keylimit = Pkeylimit; + if(keylimit == 0) + keylimit = POLIPHONY - 5; + + //release old keys if the number of notes>keylimit + if(Ppolymode != 0) { + int notecount = 0; + for(int i = 0; i < POLIPHONY; ++i) + if((partnote[i].status == KEY_PLAYING) + || (partnote[i].status == KEY_RELASED_AND_SUSTAINED)) + notecount++; + + int oldestnotepos = -1; + if(notecount > keylimit) //find out the oldest note + for(int i = 0; i < POLIPHONY; ++i) { + int maxtime = 0; + if(((partnote[i].status == KEY_PLAYING) + || (partnote[i].status == KEY_RELASED_AND_SUSTAINED)) + && (partnote[i].time > maxtime)) { + maxtime = partnote[i].time; + oldestnotepos = i; + } + } + if(oldestnotepos != -1) + RelaseNotePos(oldestnotepos); + } +} + + +/* + * Prepare all notes to be turned off + */ +void Part::AllNotesOff() +{ + killallnotes = 1; +} + +void Part::RunNote(unsigned int k) +{ + unsigned noteplay = 0; + for(int item = 0; item < partnote[k].itemsplaying; ++item) { + int sendcurrenttofx = partnote[k].kititem[item].sendtoparteffect; + + for(unsigned type = 0; type < 3; ++type) { + //Select a note + SynthNote **note = NULL; + if(type == 0) + note = &partnote[k].kititem[item].adnote; + else if(type == 1) + note = &partnote[k].kititem[item].subnote; + else if(type == 2) + note = &partnote[k].kititem[item].padnote; + + //Process if it exists + if(!(*note)) + continue; + noteplay++; + + float tmpoutr[synth->buffersize]; + float tmpoutl[synth->buffersize]; + (*note)->noteout(&tmpoutl[0], &tmpoutr[0]); + + if((*note)->finished()) { + delete (*note); + (*note) = NULL; + } + for(int i = 0; i < synth->buffersize; ++i) { //add the note to part(mix) + partfxinputl[sendcurrenttofx][i] += tmpoutl[i]; + partfxinputr[sendcurrenttofx][i] += tmpoutr[i]; + } + } + } + + //Kill note if there is no synth on that note + if(noteplay == 0) + KillNotePos(k); +} + +/* + * Compute Part samples and store them in the partoutl[] and partoutr[] + */ +void Part::ComputePartSmps() +{ + for(unsigned nefx = 0; nefx < NUM_PART_EFX + 1; ++nefx) + for(int i = 0; i < synth->buffersize; ++i) { + partfxinputl[nefx][i] = 0.0f; + partfxinputr[nefx][i] = 0.0f; + } + + for(unsigned k = 0; k < POLIPHONY; ++k) { + if(partnote[k].status == KEY_OFF) + continue; + partnote[k].time++; + //get the sampledata of the note and kill it if it's finished + RunNote(k); + } + + + //Apply part's effects and mix them + for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) { + if(!Pefxbypass[nefx]) { + partefx[nefx]->out(partfxinputl[nefx], partfxinputr[nefx]); + if(Pefxroute[nefx] == 2) + for(int i = 0; i < synth->buffersize; ++i) { + partfxinputl[nefx + 1][i] += partefx[nefx]->efxoutl[i]; + partfxinputr[nefx + 1][i] += partefx[nefx]->efxoutr[i]; + } + } + int routeto = ((Pefxroute[nefx] == 0) ? nefx + 1 : NUM_PART_EFX); + for(int i = 0; i < synth->buffersize; ++i) { + partfxinputl[routeto][i] += partfxinputl[nefx][i]; + partfxinputr[routeto][i] += partfxinputr[nefx][i]; + } + } + for(int i = 0; i < synth->buffersize; ++i) { + partoutl[i] = partfxinputl[NUM_PART_EFX][i]; + partoutr[i] = partfxinputr[NUM_PART_EFX][i]; + } + + //Kill All Notes if killallnotes!=0 + if(killallnotes != 0) { + for(int i = 0; i < synth->buffersize; ++i) { + float tmp = (synth->buffersize_f - i) / synth->buffersize_f; + partoutl[i] *= tmp; + partoutr[i] *= tmp; + } + for(int k = 0; k < POLIPHONY; ++k) + KillNotePos(k); + killallnotes = 0; + for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) + partefx[nefx]->cleanup(); + } + ctl.updateportamento(); +} + +/* + * Parameter control + */ +void Part::setPvolume(char Pvolume_) +{ + Pvolume = Pvolume_; + volume = + dB2rap((Pvolume - 96.0f) / 96.0f * 40.0f) * ctl.expression.relvolume; +} + +void Part::setPpanning(char Ppanning_) +{ + Ppanning = Ppanning_; + panning = Ppanning / 127.0f + ctl.panning.pan; + if(panning < 0.0f) + panning = 0.0f; + else + if(panning > 1.0f) + panning = 1.0f; +} + +/* + * Enable or disable a kit item + */ +void Part::setkititemstatus(int kititem, int Penabled_) +{ + if((kititem == 0) || (kititem >= NUM_KIT_ITEMS)) + return; //nonexistent kit item and the first kit item is always enabled + kit[kititem].Penabled = Penabled_; + + bool resetallnotes = false; + if(Penabled_ == 0) { + if(kit[kititem].adpars != NULL) + delete (kit[kititem].adpars); + if(kit[kititem].subpars != NULL) + delete (kit[kititem].subpars); + if(kit[kititem].padpars != NULL) { + delete (kit[kititem].padpars); + resetallnotes = true; + } + kit[kititem].adpars = NULL; + kit[kititem].subpars = NULL; + kit[kititem].padpars = NULL; + kit[kititem].Pname[0] = '\0'; + } + else { + if(kit[kititem].adpars == NULL) + kit[kititem].adpars = new ADnoteParameters(fft); + if(kit[kititem].subpars == NULL) + kit[kititem].subpars = new SUBnoteParameters(); + if(kit[kititem].padpars == NULL) + kit[kititem].padpars = new PADnoteParameters(fft, mutex); + } + + if(resetallnotes) + for(int k = 0; k < POLIPHONY; ++k) + KillNotePos(k); +} + +void Part::add2XMLinstrument(XMLwrapper *xml) +{ + xml->beginbranch("INFO"); + xml->addparstr("name", (char *)Pname); + xml->addparstr("author", (char *)info.Pauthor); + xml->addparstr("comments", (char *)info.Pcomments); + xml->addpar("type", info.Ptype); + xml->endbranch(); + + + xml->beginbranch("INSTRUMENT_KIT"); + xml->addpar("kit_mode", Pkitmode); + xml->addparbool("drum_mode", Pdrummode); + + for(int i = 0; i < NUM_KIT_ITEMS; ++i) { + xml->beginbranch("INSTRUMENT_KIT_ITEM", i); + xml->addparbool("enabled", kit[i].Penabled); + if(kit[i].Penabled != 0) { + xml->addparstr("name", (char *)kit[i].Pname); + + xml->addparbool("muted", kit[i].Pmuted); + xml->addpar("min_key", kit[i].Pminkey); + xml->addpar("max_key", kit[i].Pmaxkey); + + xml->addpar("send_to_instrument_effect", kit[i].Psendtoparteffect); + + xml->addparbool("add_enabled", kit[i].Padenabled); + if((kit[i].Padenabled != 0) && (kit[i].adpars != NULL)) { + xml->beginbranch("ADD_SYNTH_PARAMETERS"); + kit[i].adpars->add2XML(xml); + xml->endbranch(); + } + + xml->addparbool("sub_enabled", kit[i].Psubenabled); + if((kit[i].Psubenabled != 0) && (kit[i].subpars != NULL)) { + xml->beginbranch("SUB_SYNTH_PARAMETERS"); + kit[i].subpars->add2XML(xml); + xml->endbranch(); + } + + xml->addparbool("pad_enabled", kit[i].Ppadenabled); + if((kit[i].Ppadenabled != 0) && (kit[i].padpars != NULL)) { + xml->beginbranch("PAD_SYNTH_PARAMETERS"); + kit[i].padpars->add2XML(xml); + xml->endbranch(); + } + } + xml->endbranch(); + } + xml->endbranch(); + + xml->beginbranch("INSTRUMENT_EFFECTS"); + for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) { + xml->beginbranch("INSTRUMENT_EFFECT", nefx); + xml->beginbranch("EFFECT"); + partefx[nefx]->add2XML(xml); + xml->endbranch(); + + xml->addpar("route", Pefxroute[nefx]); + partefx[nefx]->setdryonly(Pefxroute[nefx] == 2); + xml->addparbool("bypass", Pefxbypass[nefx]); + xml->endbranch(); + } + xml->endbranch(); +} + +void Part::add2XML(XMLwrapper *xml) +{ + //parameters + xml->addparbool("enabled", Penabled); + if((Penabled == 0) && (xml->minimal)) + return; + + xml->addpar("volume", Pvolume); + xml->addpar("panning", Ppanning); + + xml->addpar("min_key", Pminkey); + xml->addpar("max_key", Pmaxkey); + xml->addpar("key_shift", Pkeyshift); + xml->addpar("rcv_chn", Prcvchn); + + xml->addpar("velocity_sensing", Pvelsns); + xml->addpar("velocity_offset", Pveloffs); + + xml->addparbool("note_on", Pnoteon); + xml->addparbool("poly_mode", Ppolymode); + xml->addpar("legato_mode", Plegatomode); + xml->addpar("key_limit", Pkeylimit); + + xml->beginbranch("INSTRUMENT"); + add2XMLinstrument(xml); + xml->endbranch(); + + xml->beginbranch("CONTROLLER"); + ctl.add2XML(xml); + xml->endbranch(); +} + +int Part::saveXML(const char *filename) +{ + XMLwrapper *xml; + xml = new XMLwrapper(); + + xml->beginbranch("INSTRUMENT"); + add2XMLinstrument(xml); + xml->endbranch(); + + int result = xml->saveXMLfile(filename); + delete (xml); + return result; +} + +int Part::loadXMLinstrument(const char *filename) /*{*/ +{ + XMLwrapper *xml = new XMLwrapper(); + if(xml->loadXMLfile(filename) < 0) { + delete (xml); + return -1; + } + + if(xml->enterbranch("INSTRUMENT") == 0) + return -10; + getfromXMLinstrument(xml); + xml->exitbranch(); + + delete (xml); + return 0; +} /*}*/ + +void Part::applyparameters(bool lockmutex) /*{*/ +{ + for(int n = 0; n < NUM_KIT_ITEMS; ++n) + if((kit[n].padpars != NULL) && (kit[n].Ppadenabled != 0)) + kit[n].padpars->applyparameters(lockmutex); +} /*}*/ + +void Part::getfromXMLinstrument(XMLwrapper *xml) +{ + if(xml->enterbranch("INFO")) { + xml->getparstr("name", (char *)Pname, PART_MAX_NAME_LEN); + xml->getparstr("author", (char *)info.Pauthor, MAX_INFO_TEXT_SIZE); + xml->getparstr("comments", (char *)info.Pcomments, MAX_INFO_TEXT_SIZE); + info.Ptype = xml->getpar("type", info.Ptype, 0, 16); + + xml->exitbranch(); + } + + if(xml->enterbranch("INSTRUMENT_KIT")) { + Pkitmode = xml->getpar127("kit_mode", Pkitmode); + Pdrummode = xml->getparbool("drum_mode", Pdrummode); + + setkititemstatus(0, 0); + for(int i = 0; i < NUM_KIT_ITEMS; ++i) { + if(xml->enterbranch("INSTRUMENT_KIT_ITEM", i) == 0) + continue; + setkititemstatus(i, xml->getparbool("enabled", kit[i].Penabled)); + if(kit[i].Penabled == 0) { + xml->exitbranch(); + continue; + } + + xml->getparstr("name", (char *)kit[i].Pname, PART_MAX_NAME_LEN); + + kit[i].Pmuted = xml->getparbool("muted", kit[i].Pmuted); + kit[i].Pminkey = xml->getpar127("min_key", kit[i].Pminkey); + kit[i].Pmaxkey = xml->getpar127("max_key", kit[i].Pmaxkey); + + kit[i].Psendtoparteffect = xml->getpar127( + "send_to_instrument_effect", + kit[i].Psendtoparteffect); + + kit[i].Padenabled = xml->getparbool("add_enabled", + kit[i].Padenabled); + if(xml->enterbranch("ADD_SYNTH_PARAMETERS")) { + kit[i].adpars->getfromXML(xml); + xml->exitbranch(); + } + + kit[i].Psubenabled = xml->getparbool("sub_enabled", + kit[i].Psubenabled); + if(xml->enterbranch("SUB_SYNTH_PARAMETERS")) { + kit[i].subpars->getfromXML(xml); + xml->exitbranch(); + } + + kit[i].Ppadenabled = xml->getparbool("pad_enabled", + kit[i].Ppadenabled); + if(xml->enterbranch("PAD_SYNTH_PARAMETERS")) { + kit[i].padpars->getfromXML(xml); + xml->exitbranch(); + } + + xml->exitbranch(); + } + + xml->exitbranch(); + } + + + if(xml->enterbranch("INSTRUMENT_EFFECTS")) { + for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) { + if(xml->enterbranch("INSTRUMENT_EFFECT", nefx) == 0) + continue; + if(xml->enterbranch("EFFECT")) { + partefx[nefx]->getfromXML(xml); + xml->exitbranch(); + } + + Pefxroute[nefx] = xml->getpar("route", + Pefxroute[nefx], + 0, + NUM_PART_EFX); + partefx[nefx]->setdryonly(Pefxroute[nefx] == 2); + Pefxbypass[nefx] = xml->getparbool("bypass", Pefxbypass[nefx]); + xml->exitbranch(); + } + xml->exitbranch(); + } +} + +void Part::getfromXML(XMLwrapper *xml) +{ + Penabled = xml->getparbool("enabled", Penabled); + + setPvolume(xml->getpar127("volume", Pvolume)); + setPpanning(xml->getpar127("panning", Ppanning)); + + Pminkey = xml->getpar127("min_key", Pminkey); + Pmaxkey = xml->getpar127("max_key", Pmaxkey); + Pkeyshift = xml->getpar127("key_shift", Pkeyshift); + Prcvchn = xml->getpar127("rcv_chn", Prcvchn); + + Pvelsns = xml->getpar127("velocity_sensing", Pvelsns); + Pveloffs = xml->getpar127("velocity_offset", Pveloffs); + + Pnoteon = xml->getparbool("note_on", Pnoteon); + Ppolymode = xml->getparbool("poly_mode", Ppolymode); + Plegatomode = xml->getparbool("legato_mode", Plegatomode); //older versions + if(!Plegatomode) + Plegatomode = xml->getpar127("legato_mode", Plegatomode); + Pkeylimit = xml->getpar127("key_limit", Pkeylimit); + + + if(xml->enterbranch("INSTRUMENT")) { + getfromXMLinstrument(xml); + xml->exitbranch(); + } + + if(xml->enterbranch("CONTROLLER")) { + ctl.getfromXML(xml); + xml->exitbranch(); + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Part.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Part.h new file mode 100644 index 000000000..ed247d30b --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Part.h @@ -0,0 +1,202 @@ +/* + ZynAddSubFX - a software synthesizer + + Part.h - Part implementation + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef PART_H +#define PART_H + +#define MAX_INFO_TEXT_SIZE 1000 + +#include "../globals.h" +#include "../Params/Controller.h" +#include "../Misc/Microtonal.h" + +#include +#include // For the monomemnotes list. + +class EffectMgr; +class ADnoteParameters; +class SUBnoteParameters; +class PADnoteParameters; +class SynthNote; +class XMLWrapper; +class FFTwrapper; + +/** Part implementation*/ +class Part +{ + public: + /**Constructor + * @param microtonal_ Pointer to the microtonal object + * @param fft_ Pointer to the FFTwrapper + * @param mutex_ Pointer to the master pthread_mutex_t*/ + Part(Microtonal *microtonal_, FFTwrapper *fft_, pthread_mutex_t *mutex_); + /**Destructor*/ + ~Part(); + + // Midi commands implemented + void NoteOn(unsigned char note, + unsigned char velocity, + int masterkeyshift); + void NoteOff(unsigned char note); + void PolyphonicAftertouch(unsigned char note, + unsigned char velocity, + int masterkeyshift); + void AllNotesOff(); //panic + void SetController(unsigned int type, int par); + void RelaseSustainedKeys(); //this is called when the sustain pedal is relased + void RelaseAllKeys(); //this is called on AllNotesOff controller + + /* The synthesizer part output */ + void ComputePartSmps(); //Part output + + //instrumentonly: 0 - save all, 1 - save only instrumnet, 2 - save only instrument without the name(used in bank) + + + //saves the instrument settings to a XML file + //returns 0 for ok or <0 if there is an error + int saveXML(const char *filename); + int loadXMLinstrument(const char *filename); + + void add2XML(XMLwrapper *xml); + void add2XMLinstrument(XMLwrapper *xml); + + void defaults(); + void defaultsinstrument(); + + void applyparameters(bool lockmutex = true); + + void getfromXML(XMLwrapper *xml); + void getfromXMLinstrument(XMLwrapper *xml); + + void cleanup(bool final = false); + + //the part's kit + struct { + unsigned char Penabled, Pmuted, Pminkey, Pmaxkey; + unsigned char *Pname; + unsigned char Padenabled, Psubenabled, Ppadenabled; + unsigned char Psendtoparteffect; + ADnoteParameters *adpars; + SUBnoteParameters *subpars; + PADnoteParameters *padpars; + } kit[NUM_KIT_ITEMS]; + + + //Part parameters + void setkeylimit(unsigned char Pkeylimit); + void setkititemstatus(int kititem, int Penabled_); + + unsigned char Penabled; /** monomemnotes; // A list to remember held notes. + struct { + unsigned char velocity; + int mkeyshift; // I'm not sure masterkeyshift should be remembered. + } monomem[256]; + /* 256 is to cover all possible note values. + monomem[] is used in conjunction with the list to + store the velocity and masterkeyshift values of a given note (the list only store note values). + For example 'monomem[note].velocity' would be the velocity value of the note 'note'.*/ + + PartNotes partnote[POLIPHONY]; + + float oldfreq; //this is used for portamento + Microtonal *microtonal; + FFTwrapper *fft; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/QtXmlWrapper.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/QtXmlWrapper.cpp new file mode 100644 index 000000000..d8deefe95 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/QtXmlWrapper.cpp @@ -0,0 +1,550 @@ +/* + * QtXmlWrapper.cpp - a QtXml based XML backend for ZynAddSubxFX + * + * Copyright (c) 2009 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +/* File derived from QtXmlWrapper.C: */ +/* + ZynAddSubFX - a software synthesizer + + QtXmlWrapper.C - XML wrapper + Copyright (C) 2003-2005 Nasca Octavian Paul + Copyright (C) 2009-2009 Mark McCurry + Author: Nasca Octavian Paul + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include "QtXmlWrapper.h" +#include +#include +#include +#include +#include "lmmsconfig.h" +#include "../globals.h" +#include "Util.h" + + +struct XmlData +{ + XmlData() : + m_doc( "ZynAddSubFX-data" ) + { + } + QDomDocument m_doc; + QDomElement m_node; + QDomElement m_info; + + QDomElement addparams( const char *name, unsigned int params, ... ); +} ; + + + +QtXmlWrapper::QtXmlWrapper() : + d( new XmlData ) +{ + version.Major = 2; + version.Minor = 4; + version.Revision = 1; + + minimal = true; + + d->m_node = d->m_doc.createElement( "ZynAddSubFX-data" ); + d->m_node.setAttribute( "version-major", QString::number( version.Major ) ); + d->m_node.setAttribute( "version-minor", QString::number( version.Minor ) ); + d->m_node.setAttribute( "version-revision", QString::number( version.Revision ) ); + d->m_node.setAttribute( "ZynAddSubFX-author", "Nasca Octavian Paul" ); + d->m_doc.appendChild( d->m_node ); + + //make the empty branch that will contain the information parameters + d->m_info = d->addparams("INFORMATION", 0); + + //save zynaddsubfx specifications + beginbranch("BASE_PARAMETERS"); + addpar("max_midi_parts", NUM_MIDI_PARTS); + addpar("max_kit_items_per_instrument", NUM_KIT_ITEMS); + + addpar("max_system_effects", NUM_SYS_EFX); + addpar("max_insertion_effects", NUM_INS_EFX); + addpar("max_instrument_effects", NUM_PART_EFX); + + addpar("max_addsynth_voices", NUM_VOICES); + endbranch(); +} + + + + +QtXmlWrapper::~QtXmlWrapper() +{ + delete d; +} + + + + +void QtXmlWrapper::setPadSynth(bool enabled) +{ + /**@bug this might create multiple nodes when only one is needed*/ + QDomElement oldNode = d->m_node; + d->m_node = d->m_info; + //Info storing + addparbool("PADsynth_used", enabled); + d->m_node = oldNode; +} + +QDomElement findElement( QDomElement root, const QString & tagname, const QString & attrname, + const QString & attrval ) +{ + QDomNodeList list = root.elementsByTagName( tagname ); + for( int i = 0; i < list.size(); ++i ) + { + QDomNode n = list.at( i ); + if( n.isElement() ) + { + QDomElement e = n.toElement(); + if( e.hasAttribute( attrname ) && e.attribute( attrname ) == attrval ) + { + return e; + } + } + } + + return QDomElement(); +} + + + + +bool QtXmlWrapper::hasPadSynth() const +{ + /**Right now this has a copied implementation of setparbool, so this should + * be reworked as XMLwrapper evolves*/ + QDomElement tmp = d->m_doc.elementsByTagName( "INFORMATION" ).at( 0 ).toElement(); + QDomElement parameter = findElement( tmp, "par_bool", "name", "PADsynth_used" ); + if( !parameter.isNull() ) + { + const QString val = parameter.attribute( "value" ).toLower(); + return val[0] == 'y'; + } + return false; +} + + +/* SAVE XML members */ + +int QtXmlWrapper::saveXMLfile(const std::string &filename) const +{ + char *xmldata = getXMLdata(); + if(xmldata == NULL) + return -2; + + int compression = config.cfg.GzipCompression; + int result = dosavefile(filename.c_str(), compression, xmldata); + + delete[] xmldata; + return result; +} + + +char *QtXmlWrapper::getXMLdata() const +{ + QString xml = d->m_doc.toString( 1 ); + return qstrdup( xml.toUtf8().constData() ); +} + + +int QtXmlWrapper::dosavefile(const char *filename, + int compression, + const char *xmldata) const +{ + if(compression == 0) { + FILE *file; + file = fopen(filename, "w"); + if(file == NULL) + return -1; + fputs(xmldata, file); + fclose(file); + } + else { + if(compression > 9) + compression = 9; + if(compression < 1) + compression = 1; + char options[10]; + snprintf(options, 10, "wb%d", compression); + + gzFile gzfile; + gzfile = gzopen(filename, options); + if(gzfile == NULL) + return -1; + gzputs(gzfile, xmldata); + gzclose(gzfile); + } + + return 0; +} + + + +void QtXmlWrapper::addpar(const std::string &name, int val) +{ + d->addparams("par", 2, "name", name.c_str(), "value", stringFrom( + val).c_str()); +} + +void QtXmlWrapper::addparreal(const std::string &name, float val) +{ + d->addparams("par_real", 2, "name", name.c_str(), "value", + stringFrom(val).c_str()); +} + +void QtXmlWrapper::addparbool(const std::string &name, int val) +{ + if(val != 0) + d->addparams("par_bool", 2, "name", name.c_str(), "value", "yes"); + else + d->addparams("par_bool", 2, "name", name.c_str(), "value", "no"); +} + +void QtXmlWrapper::addparstr(const std::string &name, const std::string &val) +{ + QDomElement e = d->m_doc.createElement( "string" ); + e.setAttribute( "name", name.c_str() ); + e.appendChild( d->m_doc.createTextNode( val.c_str() ) ); + d->m_node.appendChild( e ); +} + + +void QtXmlWrapper::beginbranch(const std::string &name) +{ + d->m_node = d->addparams(name.c_str(), 0); +} + +void QtXmlWrapper::beginbranch(const std::string &name, int id) +{ + d->m_node = d->addparams(name.c_str(), 1, "id", stringFrom(id).c_str()); +} + +void QtXmlWrapper::endbranch() +{ + d->m_node = d->m_node.parentNode().toElement(); +} + + + +/* LOAD XML members */ + +int QtXmlWrapper::loadXMLfile(const std::string &filename) +{ + const char *xmldata = doloadfile(filename.c_str()); + if(xmldata == NULL) + { + qDebug() << "QtXmlWrapper::loadXMLfile(): empty data"; + return -1; //the file could not be loaded or uncompressed + } + + QByteArray b( xmldata ); + while( !b.isEmpty() && b[0] != '<' ) + { + // remove first blank line + b.remove( 0, 1 ); + } + + if( !d->m_doc.setContent( b ) ) + { + qDebug() << "QtXmlWrapper::loadXMLfile(): could not set document content"; + delete[] xmldata; + return -2; + } + delete[] xmldata; + + d->m_node = d->m_doc.elementsByTagName( "ZynAddSubFX-data" ).at( 0 ).toElement(); + if( d->m_node.isNull() || !d->m_node.isElement() ) + { + qDebug() << "QtXmlWrapper::loadXMLfile(): missing root node"; + return -3; //the XML doesnt embbed zynaddsubfx data + } + QDomElement root = d->m_node.toElement(); + //fetch version information + version.Major = root.attribute( "version-major").toInt(); + version.Minor = root.attribute( "version-minor").toInt(); + version.Revision = root.attribute( "version-revision").toInt(); + + return 0; +} + + +char *QtXmlWrapper::doloadfile(const std::string &filename) const +{ + char *xmldata = NULL; + gzFile gzfile = gzopen(filename.c_str(), "rb"); + + if(gzfile != NULL) { //The possibly compressed file opened + std::stringstream strBuf; //reading stream + const int bufSize = 500; //fetch size + char fetchBuf[bufSize + 1]; //fetch buffer + int read = 0; //chars read in last fetch + + fetchBuf[bufSize] = 0; //force null termination + + while(bufSize == (read = gzread(gzfile, fetchBuf, bufSize))) + strBuf << fetchBuf; + + fetchBuf[read] = 0; //Truncate last partial read + strBuf << fetchBuf; + + gzclose(gzfile); + + //Place data in output format + std::string tmp = strBuf.str(); + xmldata = new char[tmp.size() + 1]; + strncpy(xmldata, tmp.c_str(), tmp.size() + 1); + } + + return xmldata; +} + +bool QtXmlWrapper::putXMLdata(const char *xmldata) +{ + d->m_doc.setContent( QString::fromUtf8( xmldata ) ); + + d->m_node = d->m_doc.elementsByTagName( "ZynAddSubFX-data" ).at( 0 ).toElement(); + if( d->m_node.isNull() ) + { + return false; + } + + return true; +} + + + +int QtXmlWrapper::enterbranch(const std::string &name) +{ + QDomElement tmp = d->m_node.firstChildElement( name.c_str() ); + if( tmp.isNull() ) + { + return 0; + } + + d->m_node = tmp; + + return 1; +} + +int QtXmlWrapper::enterbranch(const std::string &name, int id) +{ + QDomElement tmp = findElement( d->m_node, name.c_str(), + "id", QString::number( id ) ); + if( tmp.isNull() ) + { + return 0; + } + + d->m_node = tmp; + + return 1; +} + + +void QtXmlWrapper::exitbranch() +{ + d->m_node = d->m_node.parentNode().toElement(); +} + + +int QtXmlWrapper::getbranchid(int min, int max) const +{ + if( !d->m_node.isElement() ) + { + return min; + } + QDomElement tmp = d->m_node.toElement(); + if( !tmp.hasAttribute( "id" ) ) + { + return min; + } + int id = tmp.attribute( "id" ).toInt(); + if((min == 0) && (max == 0)) + return id; + + if(id < min) + id = min; + else + if(id > max) + id = max; + + return id; +} + +int QtXmlWrapper::getpar(const std::string &name, int defaultpar, int min, + int max) const +{ + QDomElement tmp = findElement( d->m_node, "par", "name", name.c_str() ); + if( tmp.isNull() || !tmp.hasAttribute( "value" ) ) + { + return defaultpar; + } + + int val = tmp.attribute( "value" ).toInt(); + if(val < min) + val = min; + else + if(val > max) + val = max; + + return val; +} + +int QtXmlWrapper::getpar127(const std::string &name, int defaultpar) const +{ + return getpar(name, defaultpar, 0, 127); +} + +int QtXmlWrapper::getparbool(const std::string &name, int defaultpar) const +{ + QDomElement tmp = findElement( d->m_node, "par_bool", "name", name.c_str() ); + if( tmp.isNull() || !tmp.hasAttribute( "value" ) ) + { + return defaultpar; + } + + const QString val = tmp.attribute( "value" ).toLower(); + if( val[0] == 'y' ) + { + return 1; + } + return 0; +} + +void QtXmlWrapper::getparstr(const std::string &name, char *par, int maxstrlen) const +{ + ZERO(par, maxstrlen); + QDomNode tmp = findElement( d->m_node, "string", "name", name.c_str() ); + if( tmp.isNull() || !tmp.hasChildNodes() ) + { + return; + } + + tmp = tmp.firstChild(); + if( tmp.nodeType() == QDomNode::ElementNode ) + { + snprintf(par, maxstrlen, "%s", tmp.toElement().tagName().toUtf8().constData() ); + return; + } + if( tmp.nodeType() == QDomNode::TextNode ) + { + snprintf(par, maxstrlen, "%s", tmp.toText().data().toUtf8().constData() ); + return; + } +} + +std::string QtXmlWrapper::getparstr(const std::string &name, + const std::string &defaultpar) const +{ + QDomNode tmp = findElement( d->m_node, "string", "name", name.c_str() ); + if( tmp.isNull() || !tmp.hasChildNodes() ) + { + return defaultpar; + } + + tmp = tmp.firstChild(); + if( tmp.nodeType() == QDomNode::ElementNode && !tmp.toElement().tagName().isEmpty() ) + { + return tmp.toElement().tagName().toUtf8().constData(); + } + if( tmp.nodeType() == QDomNode::TextNode && !tmp.toText().data().isEmpty() ) + { + return tmp.toText().data().toUtf8().constData(); + } + + return defaultpar; +} + +float QtXmlWrapper::getparreal(const char *name, float defaultpar) const +{ + QDomElement tmp = findElement( d->m_node, "par_real", "name", name ); + if( tmp.isNull() || !tmp.hasAttribute( "value" ) ) + { + return defaultpar; + } + + return tmp.attribute( "value" ).toFloat(); +} + +float QtXmlWrapper::getparreal(const char *name, + float defaultpar, + float min, + float max) const +{ + float result = getparreal(name, defaultpar); + + if(result < min) + result = min; + else + if(result > max) + result = max; + return result; +} + + +/** Private members **/ + +QDomElement XmlData::addparams(const char *name, unsigned int params, + ...) +{ + /**@todo make this function send out a good error message if something goes + * wrong**/ + QDomElement element = m_doc.createElement( name ); + m_node.appendChild( element ); + + if(params) { + va_list variableList; + va_start(variableList, params); + + const char *ParamName; + const char *ParamValue; + while(params--) { + ParamName = va_arg(variableList, const char *); + ParamValue = va_arg(variableList, const char *); + element.setAttribute( ParamName, ParamValue); + } + } + return element; +} + diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/QtXmlWrapper.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/QtXmlWrapper.h new file mode 100644 index 000000000..c525480bc --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/QtXmlWrapper.h @@ -0,0 +1,125 @@ +/* + * QtXmlWrapper.h - a QtXml based XML backend for ZynAddSubxFX + * + * Copyright (c) 2009 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +/* File derived from XMLwrapper.h: */ +/* + ZynAddSubFX - a software synthesizer + + XMLwrapper.h - XML wrapper + Copyright (C) 2003-2005 Nasca Octavian Paul + Copyright (C) 2009-2009 Mark McCurry + Author: Nasca Octavian Paul + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef QT_XML_WRAPPER_H +#define QT_XML_WRAPPER_H + +#include "../globals.h" + +#include +#include +#include +#include + +#define QtXmlWrapper XMLwrapper + +struct XmlData; + +class QtXmlWrapper +{ +public: + QtXmlWrapper(); + ~QtXmlWrapper(); + + int saveXMLfile( const std::string & filename ) const; + int loadXMLfile( const std::string & filename ); + + char *getXMLdata() const; + bool putXMLdata( const char *xmldata ); + + void addpar( const std::string & name, int val ); + void addparreal( const std::string & name, float val); + void addparbool( const std::string & name, int val ); + void addparstr( const std::string & name, const std::string & val ); + + void beginbranch( const std::string & name ); + void beginbranch( const std::string & name, int id ); + void endbranch(); + + + int enterbranch( const std::string & name ); + int enterbranch( const std::string & name, int id ); + void exitbranch(); + int getbranchid( int min, int max ) const; + + int getpar( const std::string & name, int defaultpar, int min, int max ) const; + int getpar127( const std::string & name, int defaultpar ) const; + int getparbool( const std::string & name, int defaultpar ) const; + + void getparstr( const std::string & name, char * par, int maxstrlen ) const; + std::string getparstr( const std::string & name, const std::string & defaultpar ) const; + float getparreal( const char * name, float defaultpar ) const; + + float getparreal(const char *name, float defaultpar, float min, float max) const; + + bool minimal; /** +#include "Recorder.h" +#include "WavFile.h" +#include "../Nio/Nio.h" + +Recorder::Recorder() + :status(0), notetrigger(0) +{} + +Recorder::~Recorder() +{ + if(recording() == 1) + stop(); +} + +int Recorder::preparefile(std::string filename_, int overwrite) +{ + if(!overwrite) { + struct stat fileinfo; + int statr; + statr = stat(filename_.c_str(), &fileinfo); + if(statr == 0) //file exists + return 1; + } + + Nio::waveNew(new WavFile(filename_, synth->samplerate, 2)); + + status = 1; //ready + + return 0; +} + +void Recorder::start() +{ + notetrigger = 0; + status = 2; //recording +} + +void Recorder::stop() +{ + Nio::waveStop(); + Nio::waveStart(); + status = 0; +} + +void Recorder::pause() +{ + status = 0; + Nio::waveStop(); +} + +int Recorder::recording() +{ + if((status == 2) && (notetrigger != 0)) + return 1; + else + return 0; +} + +void Recorder::triggernow() +{ + if(status == 2) { + if(notetrigger != 1) + Nio::waveStart(); + notetrigger = 1; + } +} + +//TODO move recorder inside nio system diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Recorder.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Recorder.h new file mode 100644 index 000000000..0e3f02f10 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Recorder.h @@ -0,0 +1,54 @@ +/* + ZynAddSubFX - a software synthesizer + + Recorder.h - Records sound to a file + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef RECORDER_H +#define RECORDER_H +#include +#include "../globals.h" + +/**Records sound to a file*/ +class Recorder +{ + public: + + Recorder(); + ~Recorder(); + /**Prepare the given file. + * @returns 1 if the file exists */ + int preparefile(std::string filename_, int overwrite); + void start(); + void stop(); + void pause(); + int recording(); + void triggernow(); + + /** Status: + * 0 - not ready(no file selected), + * 1 - ready + * 2 - recording */ + int status; + + private: + int notetrigger; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Stereo.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Stereo.cpp new file mode 100644 index 000000000..ac2f31727 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Stereo.cpp @@ -0,0 +1,38 @@ +/* + ZynAddSubFX - a software synthesizer + + Stereo.cpp - Object for storing a pair of objects + Copyright (C) 2009-2009 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +template +Stereo::Stereo(const T &left, const T &right) + :l(left), r(right) +{} + +template +Stereo::Stereo(const T &val) + :l(val), r(val) +{} + +template +Stereo &Stereo::operator=(const Stereo &nstr) +{ + l = nstr.l; + r = nstr.r; + return *this; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Stereo.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Stereo.h new file mode 100644 index 000000000..516d31870 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Stereo.h @@ -0,0 +1,40 @@ +/* + ZynAddSubFX - a software synthesizer + + Stereo.h - Object for storing a pair of objects + Copyright (C) 2009-2009 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef STEREO_H +#define STEREO_H + +template +struct Stereo { + public: + Stereo(const T &left, const T &right); + + /**Initializes Stereo with left and right set to val + * @param val the value for both channels*/ + Stereo(const T &val); + ~Stereo() {} + + Stereo &operator=(const Stereo &smp); + + //data + T l, r; +}; +#include "Stereo.cpp" +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Util.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Util.cpp new file mode 100644 index 000000000..9025c622a --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Util.cpp @@ -0,0 +1,230 @@ +/* + ZynAddSubFX - a software synthesizer + + Util.cpp - Miscellaneous functions + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "Util.h" +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SCHEDULER +#include +#endif + + +prng_t prng_state = 0x1234; + +Config config; +float *denormalkillbuf; + + +/* + * Transform the velocity according the scaling parameter (velocity sensing) + */ +float VelF(float velocity, unsigned char scaling) +{ + float x; + x = powf(VELOCITY_MAX_SCALE, (64.0f - scaling) / 64.0f); + if((scaling == 127) || (velocity > 0.99f)) + return 1.0f; + else + return powf(velocity, x); +} + +/* + * Get the detune in cents + */ +float getdetune(unsigned char type, + unsigned short int coarsedetune, + unsigned short int finedetune) +{ + float det = 0.0f, octdet = 0.0f, cdet = 0.0f, findet = 0.0f; + //Get Octave + int octave = coarsedetune / 1024; + if(octave >= 8) + octave -= 16; + octdet = octave * 1200.0f; + + //Coarse and fine detune + int cdetune = coarsedetune % 1024; + if(cdetune > 512) + cdetune -= 1024; + + int fdetune = finedetune - 8192; + + switch(type) { +// case 1: is used for the default (see below) + case 2: + cdet = fabs(cdetune * 10.0f); + findet = fabs(fdetune / 8192.0f) * 10.0f; + break; + case 3: + cdet = fabs(cdetune * 100); + findet = powf(10, fabs(fdetune / 8192.0f) * 3.0f) / 10.0f - 0.1f; + break; + case 4: + cdet = fabs(cdetune * 701.95500087f); //perfect fifth + findet = + (powf(2, fabs(fdetune / 8192.0f) * 12.0f) - 1.0f) / 4095 * 1200; + break; + //case ...: need to update N_DETUNE_TYPES, if you'll add more + default: + cdet = fabs(cdetune * 50.0f); + findet = fabs(fdetune / 8192.0f) * 35.0f; //almost like "Paul's Sound Designer 2" + break; + } + if(finedetune < 8192) + findet = -findet; + if(cdetune < 0) + cdet = -cdet; + + det = octdet + cdet + findet; + return det; +} + + +bool fileexists(const char *filename) +{ + struct stat tmp; + int result = stat(filename, &tmp); + if(result >= 0) + return true; + + return false; +} + +void set_realtime() +{ +#ifdef HAVE_SCHEDULER + sched_param sc; + sc.sched_priority = 60; + //if you want get "sched_setscheduler undeclared" from compilation, + //you can safely remove the folowing line: + sched_setscheduler(0, SCHED_FIFO, &sc); + //if (err==0) printf("Real-time"); +#endif +} + +void os_sleep(long length) +{ + usleep(length); +} + +std::string legalizeFilename(std::string filename) +{ + for(int i = 0; i < (int) filename.size(); ++i) { + char c = filename[i]; + if(!(isdigit(c) || isalpha(c) || (c == '-') || (c == ' '))) + filename[i] = '_'; + } + return filename; +} + +void invSignal(float *sig, size_t len) +{ + for(size_t i = 0; i < len; ++i) + sig[i] *= -1.0f; +} + +//Some memory pools for short term buffer use +//(avoid the use of new in RT thread(s)) + +struct pool_entry { + bool free; + float *dat; +}; +typedef std::vector pool_t; +typedef pool_t::iterator pool_itr_t; + +pool_t pool; + +float *getTmpBuffer() +{ + for(pool_itr_t itr = pool.begin(); itr != pool.end(); ++itr) + if(itr->free) { //Use Pool + itr->free = false; + return itr->dat; + } + pool_entry p; //Extend Pool + p.free = false; + p.dat = new float[synth->buffersize]; + pool.push_back(p); + + return p.dat; +} + +void returnTmpBuffer(float *buf) +{ + for(pool_itr_t itr = pool.begin(); itr != pool.end(); ++itr) + if(itr->dat == buf) { //Return to Pool + itr->free = true; + return; + } + fprintf(stderr, + "ERROR: invalid buffer returned %s %d\n", + __FILE__, + __LINE__); +} + +void clearTmpBuffers(void) +{ + for(pool_itr_t itr = pool.begin(); itr != pool.end(); ++itr) { +#ifndef WIN32 + if(!itr->free) //Warn about used buffers + warn("Temporary buffer (%p) about to be freed may be in use", + itr->dat); +#endif + delete [] itr->dat; + } + pool.clear(); +} + +float SYNTH_T::numRandom() +{ + return RND; +} + +float interpolate(const float *data, size_t len, float pos) +{ + assert(len > (size_t)pos + 1); + const int l_pos = (int)pos, + r_pos = l_pos + 1; + const float leftness = pos - l_pos; + return data[l_pos] * leftness + data[r_pos] * (1.0f - leftness); +} + +float cinterpolate(const float *data, size_t len, float pos) +{ + const int l_pos = ((int)pos) % len, + r_pos = (l_pos + 1) % len; + const float leftness = pos - l_pos; + return data[l_pos] * leftness + data[r_pos] * (1.0f - leftness); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Util.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Util.h new file mode 100644 index 000000000..45d6f84fc --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Util.h @@ -0,0 +1,116 @@ +/* + ZynAddSubFX - a software synthesizer + + Util.h - Miscellaneous functions + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef UTIL_H +#define UTIL_H + +#include +#include +#include +#include "Config.h" +#include "../globals.h" + +//Velocity Sensing function +extern float VelF(float velocity, unsigned char scaling); + +bool fileexists(const char *filename); + +#define N_DETUNE_TYPES 4 //the number of detune types +extern float getdetune(unsigned char type, + unsigned short int coarsedetune, + unsigned short int finedetune); + +/**Try to set current thread to realtime priority program priority + * \todo see if the right pid is being sent + * \todo see if this is having desired effect, if not then look at + * pthread_attr_t*/ +void set_realtime(); + +/**Os independent sleep in microsecond*/ +void os_sleep(long length); + +std::string legalizeFilename(std::string filename); + +extern float *denormalkillbuf; /** +std::string stringFrom(T x) +{ + std::stringstream ss; + ss << x; + return ss.str(); +} + +template +T stringTo(const char *x) +{ + std::string str = x != NULL ? x : "0"; //should work for the basic float/int + std::stringstream ss(str); + T ans; + ss >> ans; + return ans; +} + +template +T limit(T val, T min, T max) +{ + return val < min ? min : (val > max ? max : val); +} + +//Random number generator + +typedef uint32_t prng_t; +extern prng_t prng_state; + +// Portable Pseudo-Random Number Generator +inline prng_t prng_r(prng_t &p) +{ + return p = p * 1103515245 + 12345; +} + +inline prng_t prng(void) +{ + return prng_r(prng_state) & 0x7fffffff; +} + +inline void sprng(prng_t p) +{ + prng_state = p; +} + +/* + * The random generator (0.0f..1.0f) + */ +# define INT32_MAX (2147483647) +#define RND (prng() / (INT32_MAX * 1.0f)) + +//Linear Interpolation +float interpolate(const float *data, size_t len, float pos); + +//Linear circular interpolation +float cinterpolate(const float *data, size_t len, float pos); + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WavFile.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WavFile.cpp new file mode 100644 index 000000000..78db9d52e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WavFile.cpp @@ -0,0 +1,97 @@ +/* + Copyright (C) 2006 Nasca Octavian Paul + Author: Nasca Octavian Paul + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include "WavFile.h" +using namespace std; + +WavFile::WavFile(string filename, int samplerate, int channels) + :sampleswritten(0), samplerate(samplerate), channels(channels), + file(fopen(filename.c_str(), "w")) + +{ + if(file) { + cout << "INFO: Making space for wave file header" << endl; + //making space for the header written at destruction + char tmp[44]; + memset(tmp, 0, 44 * sizeof(char)); + fwrite(tmp, 1, 44, file); + } +} + +WavFile::~WavFile() +{ + if(file) { + cout << "INFO: Writing wave file header" << endl; + + unsigned int chunksize; + rewind(file); + + fwrite("RIFF", 4, 1, file); + chunksize = sampleswritten * 4 + 36; + fwrite(&chunksize, 4, 1, file); + + fwrite("WAVEfmt ", 8, 1, file); + chunksize = 16; + fwrite(&chunksize, 4, 1, file); + unsigned short int formattag = 1; //uncompresed wave + fwrite(&formattag, 2, 1, file); + unsigned short int nchannels = channels; //stereo + fwrite(&nchannels, 2, 1, file); + unsigned int samplerate_ = samplerate; //samplerate + fwrite(&samplerate_, 4, 1, file); + unsigned int bytespersec = samplerate * 2 * channels; //bytes/sec + fwrite(&bytespersec, 4, 1, file); + unsigned short int blockalign = 2 * channels; //2 channels * 16 bits/8 + fwrite(&blockalign, 2, 1, file); + unsigned short int bitspersample = 16; + fwrite(&bitspersample, 2, 1, file); + + fwrite("data", 4, 1, file); + chunksize = sampleswritten * blockalign; + fwrite(&chunksize, 4, 1, file); + + fclose(file); + file = NULL; + } +} + +bool WavFile::good() const +{ + return file; +} + +void WavFile::writeStereoSamples(int nsmps, short int *smps) +{ + if(file) { + fwrite(smps, nsmps, 4, file); + sampleswritten += nsmps; + } +} + +void WavFile::writeMonoSamples(int nsmps, short int *smps) +{ + if(file) { + fwrite(smps, nsmps, 2, file); + sampleswritten += nsmps; + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WavFile.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WavFile.h new file mode 100644 index 000000000..4b29efa02 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WavFile.h @@ -0,0 +1,44 @@ +/* + ZynAddSubFX - a software synthesizer + + WavFile.h - Records sound to a file + Copyright (C) 2008 Nasca Octavian Paul + Author: Nasca Octavian Paul + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef WAVFILE_H +#define WAVFILE_H +#include + +class WavFile +{ + public: + WavFile(std::string filename, int samplerate, int channels); + ~WavFile(); + + bool good() const; + + void writeMonoSamples(int nsmps, short int *smps); + void writeStereoSamples(int nsmps, short int *smps); + + private: + int sampleswritten; + int samplerate; + int channels; + FILE *file; +}; +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WaveShapeSmps.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WaveShapeSmps.cpp new file mode 100644 index 000000000..cf2812960 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WaveShapeSmps.cpp @@ -0,0 +1,189 @@ +/* + ZynAddSubFX - a software synthesizer + + WaveShapeSmps.cpp - Sample Distortion + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "WaveShapeSmps.h" +#include + +void waveShapeSmps(int n, + float *smps, + unsigned char type, + unsigned char drive) +{ + int i; + float ws = drive / 127.0f; + float tmpv; + + switch(type) { + case 1: + ws = powf(10, ws * ws * 3.0f) - 1.0f + 0.001f; //Arctangent + for(i = 0; i < n; ++i) + smps[i] = atanf(smps[i] * ws) / atanf(ws); + break; + case 2: + ws = ws * ws * 32.0f + 0.0001f; //Asymmetric + if(ws < 1.0f) + tmpv = sinf(ws) + 0.1f; + else + tmpv = 1.1f; + for(i = 0; i < n; ++i) + smps[i] = sinf(smps[i] * (0.1f + ws - ws * smps[i])) / tmpv; + ; + break; + case 3: + ws = ws * ws * ws * 20.0f + 0.0001f; //Pow + for(i = 0; i < n; ++i) { + smps[i] *= ws; + if(fabs(smps[i]) < 1.0f) { + smps[i] = (smps[i] - powf(smps[i], 3.0f)) * 3.0f; + if(ws < 1.0f) + smps[i] /= ws; + } + else + smps[i] = 0.0f; + } + break; + case 4: + ws = ws * ws * ws * 32.0f + 0.0001f; //Sine + if(ws < 1.57f) + tmpv = sinf(ws); + else + tmpv = 1.0f; + for(i = 0; i < n; ++i) + smps[i] = sinf(smps[i] * ws) / tmpv; + break; + case 5: + ws = ws * ws + 0.000001f; //Quantisize + for(i = 0; i < n; ++i) + smps[i] = floor(smps[i] / ws + 0.5f) * ws; + break; + case 6: + ws = ws * ws * ws * 32 + 0.0001f; //Zigzag + if(ws < 1.0f) + tmpv = sinf(ws); + else + tmpv = 1.0f; + for(i = 0; i < n; ++i) + smps[i] = asinf(sinf(smps[i] * ws)) / tmpv; + break; + case 7: + ws = powf(2.0f, -ws * ws * 8.0f); //Limiter + for(i = 0; i < n; ++i) { + float tmp = smps[i]; + if(fabs(tmp) > ws) { + if(tmp >= 0.0f) + smps[i] = 1.0f; + else + smps[i] = -1.0f; + } + else + smps[i] /= ws; + } + break; + case 8: + ws = powf(2.0f, -ws * ws * 8.0f); //Upper Limiter + for(i = 0; i < n; ++i) { + float tmp = smps[i]; + if(tmp > ws) + smps[i] = ws; + smps[i] *= 2.0f; + } + break; + case 9: + ws = powf(2.0f, -ws * ws * 8.0f); //Lower Limiter + for(i = 0; i < n; ++i) { + float tmp = smps[i]; + if(tmp < -ws) + smps[i] = -ws; + smps[i] *= 2.0f; + } + break; + case 10: + ws = (powf(2.0f, ws * 6.0f) - 1.0f) / powf(2.0f, 6.0f); //Inverse Limiter + for(i = 0; i < n; ++i) { + float tmp = smps[i]; + if(fabs(tmp) > ws) { + if(tmp >= 0.0f) + smps[i] = tmp - ws; + else + smps[i] = tmp + ws; + } + else + smps[i] = 0; + } + break; + case 11: + ws = powf(5, ws * ws * 1.0f) - 1.0f; //Clip + for(i = 0; i < n; ++i) + smps[i] = smps[i] + * (ws + 0.5f) * 0.9999f - floor( + 0.5f + smps[i] * (ws + 0.5f) * 0.9999f); + break; + case 12: + ws = ws * ws * ws * 30 + 0.001f; //Asym2 + if(ws < 0.3f) + tmpv = ws; + else + tmpv = 1.0f; + for(i = 0; i < n; ++i) { + float tmp = smps[i] * ws; + if((tmp > -2.0f) && (tmp < 1.0f)) + smps[i] = tmp * (1.0f - tmp) * (tmp + 2.0f) / tmpv; + else + smps[i] = 0.0f; + } + break; + case 13: + ws = ws * ws * ws * 32.0f + 0.0001f; //Pow2 + if(ws < 1.0f) + tmpv = ws * (1 + ws) / 2.0f; + else + tmpv = 1.0f; + for(i = 0; i < n; ++i) { + float tmp = smps[i] * ws; + if((tmp > -1.0f) && (tmp < 1.618034f)) + smps[i] = tmp * (1.0f - tmp) / tmpv; + else + if(tmp > 0.0f) + smps[i] = -1.0f; + else + smps[i] = -2.0f; + } + break; + case 14: + ws = powf(ws, 5.0f) * 80.0f + 0.0001f; //sigmoid + if(ws > 10.0f) + tmpv = 0.5f; + else + tmpv = 0.5f - 1.0f / (expf(ws) + 1.0f); + for(i = 0; i < n; ++i) { + float tmp = smps[i] * ws; + if(tmp < -10.0f) + tmp = -10.0f; + else + if(tmp > 10.0f) + tmp = 10.0f; + tmp = 0.5f - 1.0f / (expf(tmp) + 1.0f); + smps[i] = tmp / tmpv; + } + break; + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WaveShapeSmps.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WaveShapeSmps.h new file mode 100644 index 000000000..980eb28bc --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/WaveShapeSmps.h @@ -0,0 +1,31 @@ +/* + ZynAddSubFX - a software synthesizer + + WaveShapeSmps.h - Sample distortions + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef WAVESHAPESMPS_H +#define WAVESHAPESMPS_H + +//Waveshaping(called by Distorsion effect and waveshape from OscilGen) +void waveShapeSmps(int n, + float *smps, + unsigned char type, + unsigned char drive); + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/XMLwrapper.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/XMLwrapper.cpp new file mode 100644 index 000000000..0fd40f35c --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/XMLwrapper.cpp @@ -0,0 +1,623 @@ +/* + ZynAddSubFX - a software synthesizer + + XMLwrapper.cpp - XML wrapper + Copyright (C) 2003-2005 Nasca Octavian Paul + Copyright (C) 2009-2009 Mark McCurry + Author: Nasca Octavian Paul + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "XMLwrapper.h" +#include +#include +#include +#include +#include +#include +#include + +#include "../globals.h" +#include "Util.h" + +using namespace std; + +int xml_k = 0; +bool verbose = false; + +const char *XMLwrapper_whitespace_callback(mxml_node_t *node, int where) +{ + const char *name = node->value.element.name; + + if((where == MXML_WS_BEFORE_OPEN) && (!strcmp(name, "?xml"))) + return NULL; + if((where == MXML_WS_BEFORE_CLOSE) && (!strcmp(name, "string"))) + return NULL; + + if((where == MXML_WS_BEFORE_OPEN) || (where == MXML_WS_BEFORE_CLOSE)) + /* const char *tmp=node->value.element.name; + if (tmp!=NULL) { + if ((strstr(tmp,"par")!=tmp)&&(strstr(tmp,"string")!=tmp)) { + printf("%s ",tmp); + if (where==MXML_WS_BEFORE_OPEN) xml_k++; + if (where==MXML_WS_BEFORE_CLOSE) xml_k--; + if (xml_k>=STACKSIZE) xml_k=STACKSIZE-1; + if (xml_k<0) xml_k=0; + printf("%d\n",xml_k); + printf("\n"); + }; + + }; + int i=0; + for (i=1;i(mxmlFindElement( + const_cast(node), + const_cast(top), + name, attr, value, descend)); +} + +//temporary const overload of mxmlElementGetAttr +const char *mxmlElementGetAttr(const mxml_node_t *node, const char *name) +{ + return mxmlElementGetAttr(const_cast(node), name); +} + +XMLwrapper::XMLwrapper() +{ + version.Major = 2; + version.Minor = 4; + version.Revision = 3; + + minimal = true; + + node = tree = mxmlNewElement(MXML_NO_PARENT, + "?xml version=\"1.0f\" encoding=\"UTF-8\"?"); + /* for mxml 2.1f (and older) + tree=mxmlNewElement(MXML_NO_PARENT,"?xml"); + mxmlElementSetAttr(tree,"version","1.0f"); + mxmlElementSetAttr(tree,"encoding","UTF-8"); + */ + + mxml_node_t *doctype = mxmlNewElement(tree, "!DOCTYPE"); + mxmlElementSetAttr(doctype, "ZynAddSubFX-data", NULL); + + node = root = addparams("ZynAddSubFX-data", 4, + "version-major", stringFrom( + version.Major).c_str(), + "version-minor", stringFrom( + version.Minor).c_str(), + "version-revision", + stringFrom(version.Revision).c_str(), + "ZynAddSubFX-author", "Nasca Octavian Paul"); + + //make the empty branch that will contain the information parameters + info = addparams("INFORMATION", 0); + + //save zynaddsubfx specifications + beginbranch("BASE_PARAMETERS"); + addpar("max_midi_parts", NUM_MIDI_PARTS); + addpar("max_kit_items_per_instrument", NUM_KIT_ITEMS); + + addpar("max_system_effects", NUM_SYS_EFX); + addpar("max_insertion_effects", NUM_INS_EFX); + addpar("max_instrument_effects", NUM_PART_EFX); + + addpar("max_addsynth_voices", NUM_VOICES); + endbranch(); +} + +XMLwrapper::~XMLwrapper() +{ + if(tree) + mxmlDelete(tree); +} + +void XMLwrapper::setPadSynth(bool enabled) +{ + /**@bug this might create multiple nodes when only one is needed*/ + mxml_node_t *oldnode = node; + node = info; + //Info storing + addparbool("PADsynth_used", enabled); + node = oldnode; +} + +bool XMLwrapper::hasPadSynth() const +{ + /**Right now this has a copied implementation of setparbool, so this should + * be reworked as XMLwrapper evolves*/ + mxml_node_t *tmp = mxmlFindElement(tree, + tree, + "INFORMATION", + NULL, + NULL, + MXML_DESCEND); + + mxml_node_t *parameter = mxmlFindElement(tmp, + tmp, + "par_bool", + "name", + "PADsynth_used", + MXML_DESCEND_FIRST); + if(parameter == NULL) //no information availiable + return false; + + const char *strval = mxmlElementGetAttr(parameter, "value"); + if(strval == NULL) //no information available + return false; + + if((strval[0] == 'Y') || (strval[0] == 'y')) + return true; + else + return false; +} + + +/* SAVE XML members */ + +int XMLwrapper::saveXMLfile(const string &filename) const +{ + char *xmldata = getXMLdata(); + if(xmldata == NULL) + return -2; + + int compression = config.cfg.GzipCompression; + int result = dosavefile(filename.c_str(), compression, xmldata); + + free(xmldata); + return result; +} + +char *XMLwrapper::getXMLdata() const +{ + xml_k = 0; + + char *xmldata = mxmlSaveAllocString(tree, XMLwrapper_whitespace_callback); + + return xmldata; +} + + +int XMLwrapper::dosavefile(const char *filename, + int compression, + const char *xmldata) const +{ + if(compression == 0) { + FILE *file; + file = fopen(filename, "w"); + if(file == NULL) + return -1; + fputs(xmldata, file); + fclose(file); + } + else { + if(compression > 9) + compression = 9; + if(compression < 1) + compression = 1; + char options[10]; + snprintf(options, 10, "wb%d", compression); + + gzFile gzfile; + gzfile = gzopen(filename, options); + if(gzfile == NULL) + return -1; + gzputs(gzfile, xmldata); + gzclose(gzfile); + } + + return 0; +} + + + +void XMLwrapper::addpar(const string &name, int val) +{ + addparams("par", 2, "name", name.c_str(), "value", stringFrom( + val).c_str()); +} + +void XMLwrapper::addparreal(const string &name, float val) +{ + addparams("par_real", 2, "name", name.c_str(), "value", + stringFrom(val).c_str()); +} + +void XMLwrapper::addparbool(const string &name, int val) +{ + if(val != 0) + addparams("par_bool", 2, "name", name.c_str(), "value", "yes"); + else + addparams("par_bool", 2, "name", name.c_str(), "value", "no"); +} + +void XMLwrapper::addparstr(const string &name, const string &val) +{ + mxml_node_t *element = mxmlNewElement(node, "string"); + mxmlElementSetAttr(element, "name", name.c_str()); + mxmlNewText(element, 0, val.c_str()); +} + + +void XMLwrapper::beginbranch(const string &name) +{ + if(verbose) + cout << "beginbranch()" << name << endl; + node = addparams(name.c_str(), 0); +} + +void XMLwrapper::beginbranch(const string &name, int id) +{ + if(verbose) + cout << "beginbranch(" << id << ")" << name << endl; + node = addparams(name.c_str(), 1, "id", stringFrom(id).c_str()); +} + +void XMLwrapper::endbranch() +{ + if(verbose) + cout << "endbranch()" << node << "-" << node->value.element.name + << " To " + << node->parent << "-" << node->parent->value.element.name << endl; + node = node->parent; +} + + +//workaround for memory leak +const char *trimLeadingWhite(const char *c) +{ + while(isspace(*c)) + ++c; + return c; +} + +/* LOAD XML members */ + +int XMLwrapper::loadXMLfile(const string &filename) +{ + if(tree != NULL) + mxmlDelete(tree); + tree = NULL; + + const char *xmldata = doloadfile(filename.c_str()); + if(xmldata == NULL) + return -1; //the file could not be loaded or uncompressed + + root = tree = mxmlLoadString(NULL, trimLeadingWhite( + xmldata), MXML_OPAQUE_CALLBACK); + + delete[] xmldata; + + if(tree == NULL) + return -2; //this is not XML + + node = root = mxmlFindElement(tree, + tree, + "ZynAddSubFX-data", + NULL, + NULL, + MXML_DESCEND); + if(root == NULL) + return -3; //the XML doesnt embbed zynaddsubfx data + + //fetch version information + version.Major = stringTo(mxmlElementGetAttr(root, "version-major")); + version.Minor = stringTo(mxmlElementGetAttr(root, "version-minor")); + version.Revision = + stringTo(mxmlElementGetAttr(root, "version-revision")); + + if(verbose) + cout << "loadXMLfile() version: " << version.Major << '.' + << version.Minor << '.' << version.Revision << endl; + + return 0; +} + + +char *XMLwrapper::doloadfile(const string &filename) const +{ + char *xmldata = NULL; + gzFile gzfile = gzopen(filename.c_str(), "rb"); + + if(gzfile != NULL) { //The possibly compressed file opened + stringstream strBuf; //reading stream + const int bufSize = 500; //fetch size + char fetchBuf[bufSize + 1]; //fetch buffer + int read = 0; //chars read in last fetch + + fetchBuf[bufSize] = 0; //force null termination + + while(bufSize == (read = gzread(gzfile, fetchBuf, bufSize))) + strBuf << fetchBuf; + + fetchBuf[read] = 0; //Truncate last partial read + strBuf << fetchBuf; + + gzclose(gzfile); + + //Place data in output format + string tmp = strBuf.str(); + xmldata = new char[tmp.size() + 1]; + strncpy(xmldata, tmp.c_str(), tmp.size() + 1); + } + + return xmldata; +} + +bool XMLwrapper::putXMLdata(const char *xmldata) +{ + if(tree != NULL) + mxmlDelete(tree); + + tree = NULL; + if(xmldata == NULL) + return false; + + root = tree = mxmlLoadString(NULL, trimLeadingWhite( + xmldata), MXML_OPAQUE_CALLBACK); + if(tree == NULL) + return false; + + node = root = mxmlFindElement(tree, + tree, + "ZynAddSubFX-data", + NULL, + NULL, + MXML_DESCEND); + if(root == NULL) + return false; + + return true; +} + + + +int XMLwrapper::enterbranch(const string &name) +{ + if(verbose) + cout << "enterbranch() " << name << endl; + mxml_node_t *tmp = mxmlFindElement(node, node, + name.c_str(), NULL, NULL, + MXML_DESCEND_FIRST); + if(tmp == NULL) + return 0; + + node = tmp; + return 1; +} + +int XMLwrapper::enterbranch(const string &name, int id) +{ + if(verbose) + cout << "enterbranch(" << id << ") " << name << endl; + mxml_node_t *tmp = mxmlFindElement(node, node, + name.c_str(), "id", stringFrom( + id).c_str(), MXML_DESCEND_FIRST); + if(tmp == NULL) + return 0; + + node = tmp; + return 1; +} + + +void XMLwrapper::exitbranch() +{ + if(verbose) + cout << "exitbranch()" << node << "-" << node->value.element.name + << " To " + << node->parent << "-" << node->parent->value.element.name << endl; + node = node->parent; +} + + +int XMLwrapper::getbranchid(int min, int max) const +{ + int id = stringTo(mxmlElementGetAttr(node, "id")); + if((min == 0) && (max == 0)) + return id; + + if(id < min) + id = min; + else + if(id > max) + id = max; + + return id; +} + +int XMLwrapper::getpar(const string &name, int defaultpar, int min, + int max) const +{ + const mxml_node_t *tmp = mxmlFindElement(node, + node, + "par", + "name", + name.c_str(), + MXML_DESCEND_FIRST); + + if(tmp == NULL) + return defaultpar; + + const char *strval = mxmlElementGetAttr(tmp, "value"); + if(strval == NULL) + return defaultpar; + + int val = stringTo(strval); + if(val < min) + val = min; + else + if(val > max) + val = max; + + return val; +} + +int XMLwrapper::getpar127(const string &name, int defaultpar) const +{ + return getpar(name, defaultpar, 0, 127); +} + +int XMLwrapper::getparbool(const string &name, int defaultpar) const +{ + const mxml_node_t *tmp = mxmlFindElement(node, + node, + "par_bool", + "name", + name.c_str(), + MXML_DESCEND_FIRST); + + if(tmp == NULL) + return defaultpar; + + const char *strval = mxmlElementGetAttr(tmp, "value"); + if(strval == NULL) + return defaultpar; + + if((strval[0] == 'Y') || (strval[0] == 'y')) + return 1; + else + return 0; +} + +void XMLwrapper::getparstr(const string &name, char *par, int maxstrlen) const +{ + ZERO(par, maxstrlen); + const mxml_node_t *tmp = mxmlFindElement(node, + node, + "string", + "name", + name.c_str(), + MXML_DESCEND_FIRST); + + if(tmp == NULL) + return; + if(tmp->child == NULL) + return; + if(tmp->child->type == MXML_OPAQUE) { + snprintf(par, maxstrlen, "%s", tmp->child->value.element.name); + return; + } + if((tmp->child->type == MXML_TEXT) + && (tmp->child->value.text.string != NULL)) { + snprintf(par, maxstrlen, "%s", tmp->child->value.text.string); + return; + } +} + +string XMLwrapper::getparstr(const string &name, + const std::string &defaultpar) const +{ + const mxml_node_t *tmp = mxmlFindElement(node, + node, + "string", + "name", + name.c_str(), + MXML_DESCEND_FIRST); + + if((tmp == NULL) || (tmp->child == NULL)) + return defaultpar; + + if((tmp->child->type == MXML_OPAQUE) + && (tmp->child->value.element.name != NULL)) + return tmp->child->value.element.name; + + if((tmp->child->type == MXML_TEXT) + && (tmp->child->value.text.string != NULL)) + return tmp->child->value.text.string; + + return defaultpar; +} + +float XMLwrapper::getparreal(const char *name, float defaultpar) const +{ + const mxml_node_t *tmp = mxmlFindElement(node, + node, + "par_real", + "name", + name, + MXML_DESCEND_FIRST); + if(tmp == NULL) + return defaultpar; + + const char *strval = mxmlElementGetAttr(tmp, "value"); + if(strval == NULL) + return defaultpar; + + return stringTo(strval); +} + +float XMLwrapper::getparreal(const char *name, + float defaultpar, + float min, + float max) const +{ + float result = getparreal(name, defaultpar); + + if(result < min) + result = min; + else + if(result > max) + result = max; + return result; +} + + +/** Private members **/ + +mxml_node_t *XMLwrapper::addparams(const char *name, unsigned int params, + ...) const +{ + /**@todo make this function send out a good error message if something goes + * wrong**/ + mxml_node_t *element = mxmlNewElement(node, name); + + if(params) { + va_list variableList; + va_start(variableList, params); + + const char *ParamName; + const char *ParamValue; + while(params--) { + ParamName = va_arg(variableList, const char *); + ParamValue = va_arg(variableList, const char *); + if(verbose) + cout << "addparams()[" << params << "]=" << name << " " + << ParamName << "=\"" << ParamValue << "\"" << endl; + mxmlElementSetAttr(element, ParamName, ParamValue); + } + } + return element; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/XMLwrapper.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/XMLwrapper.h new file mode 100644 index 000000000..d046888b0 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/XMLwrapper.h @@ -0,0 +1,275 @@ +/* + ZynAddSubFX - a software synthesizer + + XMLwrapper.h - XML wrapper + Copyright (C) 2003-2005 Nasca Octavian Paul + Copyright (C) 2009-2009 Mark McCurry + Author: Nasca Octavian Paul + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#if 1 +#include "QtXmlWrapper.h" +#else +#include +#include +#ifndef float +#define float float +#endif + +#ifndef XML_WRAPPER_H +#define XML_WRAPPER_H + +/**Mxml wrapper*/ +class XMLwrapper +{ + public: + /** + * Constructor. + * Will Construct the object and fill in top level branch + * */ + XMLwrapper(); + + /**Destructor*/ + ~XMLwrapper(); + + /** + * Saves the XML to a file. + * @param filename the name of the destination file. + * @returns 0 if ok or -1 if the file cannot be saved. + */ + int saveXMLfile(const std::string &filename) const; + + /** + * Return XML tree as a string. + * Note: The string must be freed with free() to deallocate + * @returns a newly allocated NULL terminated string of the XML data. + */ + char *getXMLdata() const; + + /** + * Add simple parameter. + * @param name The name of the mXML node. + * @param val The string value of the mXml node + */ + void addpar(const std::string &name, int val); + + /** + * Adds a realtype parameter. + * @param name The name of the mXML node. + * @param val The float value of the node. + */ + void addparreal(const std::string &name, float val); + + /** + * Add boolean parameter. + * \todo Fix this reverse boolean logic. + * @param name The name of the mXML node. + * @param val The boolean value of the node (0->"yes";else->"no"). + */ + void addparbool(const std::string &name, int val); + + /** + * Add string parameter. + * @param name The name of the mXML node. + * @param val The string value of the node. + */ + void addparstr(const std::string &name, const std::string &val); + + /** + * Create a new branch. + * @param name Name of new branch + * @see void endbranch() + */ + void beginbranch(const std::string &name); + /** + * Create a new branch. + * @param name Name of new branch + * @param id "id" value of branch + * @see void endbranch() + */ + void beginbranch(const std::string &name, int id); + + /**Closes new branches. + * This must be called to exit each branch created by beginbranch( ). + * @see void beginbranch(const std::string &name) + * @see void beginbranch(const std::string &name, int id) + */ + void endbranch(); + + /** + * Loads file into XMLwrapper. + * @param filename file to be loaded + * @returns 0 if ok or -1 if the file cannot be loaded + */ + int loadXMLfile(const std::string &filename); + + /** + * Loads string into XMLwrapper. + * @param xmldata NULL terminated string of XML data. + * @returns true if successful. + */ + bool putXMLdata(const char *xmldata); + + /** + * Enters the branch. + * @param name Name of branch. + * @returns 1 if is ok, or 0 otherwise. + */ + int enterbranch(const std::string &name); + + /** + * Enter into the branch \c name with id \c id. + * @param name Name of branch. + * @param id Value of branch's "id". + * @returns 1 if is ok, or 0 otherwise. + */ + int enterbranch(const std::string &name, int id); + + /**Exits from a branch*/ + void exitbranch(); + + /**Get the the branch_id and limits it between the min and max. + * if min==max==0, it will not limit it + * if there isn't any id, will return min + * this must be called only imediately after enterbranch() + */ + int getbranchid(int min, int max) const; + + /** + * Returns the integer value stored in node name. + * It returns the integer value between the limits min and max. + * If min==max==0, then the value will not be limited. + * If there is no location named name, then defaultpar will be returned. + * @param name The parameter name. + * @param defaultpar The default value if the real value is not found. + * @param min The minimum return value. + * @param max The maximum return value. + */ + int getpar(const std::string &name, int defaultpar, int min, + int max) const; + + /** + * Returns the integer value stored in the node with range [0,127]. + * @param name The parameter name. + * @param defaultpar The default value if the real value is not found. + */ + int getpar127(const std::string &name, int defaultpar) const; + + /** + * Returns the boolean value stored in the node. + * @param name The parameter name. + * @param defaultpar The default value if the real value is not found. + */ + int getparbool(const std::string &name, int defaultpar) const; + + /** + * Get the string value stored in the node. + * @param name The parameter name. + * @param par Pointer to destination string + * @param maxstrlen Max string length for destination + */ + void getparstr(const std::string &name, char *par, int maxstrlen) const; + + /** + * Get the string value stored in the node. + * @param name The parameter name. + * @param defaultpar The default value if the real value is not found. + */ + std::string getparstr(const std::string &name, + const std::string &defaultpar) const; + + /** + * Returns the real value stored in the node. + * @param name The parameter name. + * @param defaultpar The default value if the real value is not found. + */ + float getparreal(const char *name, float defaultpar) const; + + /** + * Returns the real value stored in the node. + * @param name The parameter name. + * @param defaultpar The default value if the real value is not found. + * @param min The minimum value + * @param max The maximum value + */ + float getparreal(const char *name, + float defaultpar, + float min, + float max) const; + + bool minimal; /** + * + * @param name The name of the xml node + * @param params The number of the attributes + * @param ... const char * pairs that are in the format attribute_name, + * attribute_value + */ + mxml_node_t *addparams(const char *name, unsigned int params, + ...) const; + + /**@todo keep these numbers up to date*/ + struct { + int Major; /**. +*/ + +#include +#include + +using namespace std; + +#include "../Misc/Util.h" +#include "../Misc/Config.h" +#include "InMgr.h" +#include "AlsaEngine.h" + +AlsaEngine::AlsaEngine() + :AudioOut() +{ + audio.buffer = new short[synth->buffersize * 2]; + name = "ALSA"; + audio.handle = NULL; + + midi.handle = NULL; + midi.alsaId = -1; + midi.pThread = 0; +} + +AlsaEngine::~AlsaEngine() +{ + Stop(); + delete[] audio.buffer; +} + +void *AlsaEngine::_AudioThread(void *arg) +{ + return (static_cast(arg))->AudioThread(); +} + +void *AlsaEngine::AudioThread() +{ + set_realtime(); + return processAudio(); +} + +bool AlsaEngine::Start() +{ + return openAudio() && openMidi(); +} + +void AlsaEngine::Stop() +{ + if(getMidiEn()) + setMidiEn(false); + if(getAudioEn()) + setAudioEn(false); + snd_config_update_free_global(); +} + +void AlsaEngine::setMidiEn(bool nval) +{ + if(nval) + openMidi(); + else + stopMidi(); +} + +bool AlsaEngine::getMidiEn() const +{ + return midi.handle; +} + +void AlsaEngine::setAudioEn(bool nval) +{ + if(nval) + openAudio(); + else + stopAudio(); +} + +bool AlsaEngine::getAudioEn() const +{ + return audio.handle; +} + +void *AlsaEngine::_MidiThread(void *arg) +{ + return static_cast(arg)->MidiThread(); +} + + +void *AlsaEngine::MidiThread(void) +{ + snd_seq_event_t *event; + MidiEvent ev; + set_realtime(); + while(snd_seq_event_input(midi.handle, &event) > 0) { + //ensure ev is empty + ev.channel = 0; + ev.num = 0; + ev.value = 0; + ev.type = 0; + + if(!event) + continue; + switch(event->type) { + case SND_SEQ_EVENT_NOTEON: + if(event->data.note.note) { + ev.type = M_NOTE; + ev.channel = event->data.note.channel; + ev.num = event->data.note.note; + ev.value = event->data.note.velocity; + InMgr::getInstance().putEvent(ev); + } + break; + + case SND_SEQ_EVENT_NOTEOFF: + ev.type = M_NOTE; + ev.channel = event->data.note.channel; + ev.num = event->data.note.note; + ev.value = 0; + InMgr::getInstance().putEvent(ev); + break; + + case SND_SEQ_EVENT_KEYPRESS: + ev.type = M_PRESSURE; + ev.channel = event->data.note.channel; + ev.num = event->data.note.note; + ev.value = event->data.note.velocity; + InMgr::getInstance().putEvent(ev); + break; + + case SND_SEQ_EVENT_PITCHBEND: + ev.type = M_CONTROLLER; + ev.channel = event->data.control.channel; + ev.num = C_pitchwheel; + ev.value = event->data.control.value; + InMgr::getInstance().putEvent(ev); + break; + + case SND_SEQ_EVENT_CONTROLLER: + ev.type = M_CONTROLLER; + ev.channel = event->data.control.channel; + ev.num = event->data.control.param; + ev.value = event->data.control.value; + InMgr::getInstance().putEvent(ev); + break; + + case SND_SEQ_EVENT_PGMCHANGE: + ev.type = M_PGMCHANGE; + ev.channel = event->data.control.channel; + ev.num = event->data.control.value; + InMgr::getInstance().putEvent(ev); + break; + + case SND_SEQ_EVENT_RESET: // reset to power-on state + ev.type = M_CONTROLLER; + ev.channel = event->data.control.channel; + ev.num = C_resetallcontrollers; + ev.value = 0; + InMgr::getInstance().putEvent(ev); + break; + + case SND_SEQ_EVENT_PORT_SUBSCRIBED: // ports connected + if(true) + cout << "Info, alsa midi port connected" << endl; + break; + + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // ports disconnected + if(true) + cout << "Info, alsa midi port disconnected" << endl; + break; + + case SND_SEQ_EVENT_SYSEX: // system exclusive + case SND_SEQ_EVENT_SENSING: // midi device still there + break; + + default: + if(true) + cout << "Info, other non-handled midi event, type: " + << (int)event->type << endl; + break; + } + snd_seq_free_event(event); + } + return NULL; +} + +bool AlsaEngine::openMidi() +{ + if(getMidiEn()) + return true; + + int alsaport; + midi.handle = NULL; + + if(snd_seq_open(&midi.handle, "default", SND_SEQ_OPEN_INPUT, 0) != 0) + return false; + + snd_seq_set_client_name(midi.handle, "ZynAddSubFX"); + + alsaport = snd_seq_create_simple_port( + midi.handle, + "ZynAddSubFX", + SND_SEQ_PORT_CAP_WRITE + | SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_SYNTH); + if(alsaport < 0) + return false; + + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&midi.pThread, &attr, _MidiThread, this); + return true; +} + +void AlsaEngine::stopMidi() +{ + if(!getMidiEn()) + return; + + snd_seq_t *handle = midi.handle; + if((NULL != midi.handle) && midi.pThread) + pthread_cancel(midi.pThread); + midi.handle = NULL; + if(handle) + snd_seq_close(handle); +} + +short *AlsaEngine::interleave(const Stereo &smps) +{ + /**\todo TODO fix repeated allocation*/ + short *shortInterleaved = audio.buffer; + memset(shortInterleaved, 0, bufferSize * 2 * sizeof(short)); + int idx = 0; //possible off by one error here + double scaled; + for(int frame = 0; frame < bufferSize; ++frame) { // with a nod to libsamplerate ... + scaled = smps.l[frame] * (8.0f * 0x10000000); + shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16); + scaled = smps.r[frame] * (8.0f * 0x10000000); + shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16); + } + return shortInterleaved; +} + +bool AlsaEngine::openAudio() +{ + if(getAudioEn()) + return true; + + int rc = 0; + /* Open PCM device for playback. */ + audio.handle = NULL; + rc = snd_pcm_open(&audio.handle, "hw:0", + SND_PCM_STREAM_PLAYBACK, 0); + if(rc < 0) { + fprintf(stderr, + "unable to open pcm device: %s\n", + snd_strerror(rc)); + return false; + } + + /* Allocate a hardware parameters object. */ + snd_pcm_hw_params_alloca(&audio.params); + + /* Fill it in with default values. */ + snd_pcm_hw_params_any(audio.handle, audio.params); + + /* Set the desired hardware parameters. */ + + /* Interleaved mode */ + snd_pcm_hw_params_set_access(audio.handle, audio.params, + SND_PCM_ACCESS_RW_INTERLEAVED); + + /* Signed 16-bit little-endian format */ + snd_pcm_hw_params_set_format(audio.handle, audio.params, + SND_PCM_FORMAT_S16_LE); + + /* Two channels (stereo) */ + snd_pcm_hw_params_set_channels(audio.handle, audio.params, 2); + + audio.sampleRate = synth->samplerate; + snd_pcm_hw_params_set_rate_near(audio.handle, audio.params, + &audio.sampleRate, NULL); + + audio.frames = 512; + snd_pcm_hw_params_set_period_size_near(audio.handle, + audio.params, &audio.frames, NULL); + + audio.periods = 4; + snd_pcm_hw_params_set_periods_near(audio.handle, + audio.params, &audio.periods, NULL); + + /* Write the parameters to the driver */ + rc = snd_pcm_hw_params(audio.handle, audio.params); + if(rc < 0) { + fprintf(stderr, + "unable to set hw parameters: %s\n", + snd_strerror(rc)); + return false; + } + + /* Set buffer size (in frames). The resulting latency is given by */ + /* latency = periodsize * periods / (rate * bytes_per_frame) */ + snd_pcm_hw_params_set_buffer_size(audio.handle, + audio.params, + synth->buffersize); + + //snd_pcm_hw_params_get_period_size(audio.params, &audio.frames, NULL); + //snd_pcm_hw_params_get_period_time(audio.params, &val, NULL); + + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_create(&audio.pThread, &attr, _AudioThread, this); + return true; +} + +void AlsaEngine::stopAudio() +{ + if(!getAudioEn()) + return; + + snd_pcm_t *handle = audio.handle; + audio.handle = NULL; + pthread_join(audio.pThread, NULL); + snd_pcm_drain(handle); + if(snd_pcm_close(handle)) + cout << "Error: in snd_pcm_close " << __LINE__ << ' ' << __FILE__ + << endl; +} + +void *AlsaEngine::processAudio() +{ + while(audio.handle) { + audio.buffer = interleave(getNext()); + snd_pcm_t *handle = audio.handle; + int rc = snd_pcm_writei(handle, audio.buffer, synth->buffersize); + if(rc == -EPIPE) { + /* EPIPE means underrun */ + cerr << "underrun occurred" << endl; + snd_pcm_prepare(handle); + } + else + if(rc < 0) + cerr << "error from writei: " << snd_strerror(rc) << endl; + } + return NULL; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/AlsaEngine.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/AlsaEngine.h new file mode 100644 index 000000000..0b9cc3c15 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/AlsaEngine.h @@ -0,0 +1,82 @@ +/* + AlsaEngine.h + + Copyright 2009, Alan Calvert + 2010, Mark McCurry + + This file is part of ZynAddSubFX, which is free software: you can + redistribute it and/or modify it under the terms of the GNU General + Public License as published by the Free Software Foundation, either + version 3 of the License, or (at your option) any later version. + + ZynAddSubFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ZynAddSubFX. If not, see . +*/ + +#ifndef ALSA_ENGINE_H +#define ALSA_ENGINE_H + +#include +#include +#include +#include + +#include "AudioOut.h" +#include "MidiIn.h" +#include "OutMgr.h" +#include "../Misc/Stereo.h" + +class AlsaEngine:public AudioOut, MidiIn +{ + public: + AlsaEngine(); + ~AlsaEngine(); + + bool Start(); + void Stop(); + + void setAudioEn(bool nval); + bool getAudioEn() const; + void setMidiEn(bool nval); + bool getMidiEn() const; + + protected: + void *AudioThread(); + static void *_AudioThread(void *arg); + void *MidiThread(); + static void *_MidiThread(void *arg); + + private: + bool openMidi(); + void stopMidi(); + bool openAudio(); + void stopAudio(); + + short *interleave(const Stereo &smps); + + struct { + std::string device; + snd_seq_t *handle; + int alsaId; + pthread_t pThread; + } midi; + + struct { + snd_pcm_t *handle; + snd_pcm_hw_params_t *params; + unsigned int sampleRate; + snd_pcm_uframes_t frames; + unsigned int periods; + short *buffer; + pthread_t pThread; + } audio; + + void *processAudio(); +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/AudioOut.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/AudioOut.cpp new file mode 100644 index 000000000..8c4f2d449 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/AudioOut.cpp @@ -0,0 +1,58 @@ +/* + ZynAddSubFX - a software synthesizer + + AudioOut.h - Audio Output superclass + Copyright (C) 2009-2010 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include "SafeQueue.h" + +using namespace std; + +#include "OutMgr.h" +#include "../Misc/Master.h" +#include "AudioOut.h" + +AudioOut::AudioOut() + :samplerate(synth->samplerate), bufferSize(synth->buffersize) +{} + +AudioOut::~AudioOut() +{} + +void AudioOut::setSamplerate(int _samplerate) +{ + samplerate = _samplerate; +} + +int AudioOut::getSampleRate() +{ + return samplerate; +} + +void AudioOut::setBufferSize(int _bufferSize) +{ + bufferSize = _bufferSize; +} + +const Stereo AudioOut::getNext() +{ + return OutMgr::getInstance().tick(bufferSize); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/AudioOut.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/AudioOut.h new file mode 100644 index 000000000..f8e1a97ed --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/AudioOut.h @@ -0,0 +1,61 @@ +/* + ZynAddSubFX - a software synthesizer + + AudioOut.h - Audio Output superclass + Copyright (C) 2009-2010 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef AUDIO_OUT_H +#define AUDIO_OUT_H + +#include "../Misc/Stereo.h" +#include "../globals.h" +#include "Engine.h" + +class AudioOut:public virtual Engine +{ + public: + AudioOut(); + virtual ~AudioOut(); + + /**Sets the Sample Rate of this Output + * (used for getNext()).*/ + void setSamplerate(int _samplerate); + + /**Sets the Samples required per Out of this driver + * not a realtime opperation */ + int getSampleRate(); + void setBufferSize(int _bufferSize); + + /**Sets the Frame Size for output*/ + void bufferingSize(int nBuffering); + int bufferingSize(); + + virtual void setAudioEn(bool nval) = 0; + virtual bool getAudioEn() const = 0; + + protected: + /**Get the next sample for output. + * (has nsamples sampled at a rate of samplerate)*/ + const Stereo getNext(); + + int samplerate; + int bufferSize; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/CMakeLists.txt b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/CMakeLists.txt new file mode 100644 index 000000000..9386610b7 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/CMakeLists.txt @@ -0,0 +1,47 @@ +#Defaults: +# - Wave Output (enabled with the record function) +# - Null Output +# - Null Output Running by default +# - Managed with OutMgr +set(zynaddsubfx_nio_SRCS + WavEngine.cpp + NulEngine.cpp + AudioOut.cpp + MidiIn.cpp + OutMgr.cpp + InMgr.cpp + Engine.cpp + EngineMgr.cpp + Nio.cpp + ) + +set(zynaddsubfx_nio_lib ) + +add_definitions(-DOUT_DEFAULT="${DefaultOutput}") +add_definitions(-DIN_DEFAULT="${DefaultInput}") + +if(JackEnable) + include_directories(${JACK_INCLUDE_DIR}) + list(APPEND zynaddsubfx_nio_SRCS JackEngine.cpp) + list(APPEND zynaddsubfx_nio_lib ${JACK_LIBRARIES}) +endif(JackEnable) + +if(PaEnable) + include_directories(${PORTAUDIO_INCLUDE_DIR}) + list(APPEND zynaddsubfx_nio_SRCS PaEngine.cpp) + list(APPEND zynaddsubfx_nio_lib ${PORTAUDIO_LIBRARIES}) +endif(PaEnable) + +if(AlsaEnable) + list(APPEND zynaddsubfx_nio_SRCS AlsaEngine.cpp) + list(APPEND zynaddsubfx_nio_lib ${ASOUND_LIBRARY}) +endif(AlsaEnable) + +if(OssEnable) + list(APPEND zynaddsubfx_nio_SRCS OssEngine.cpp) +endif(OssEnable) + + +add_library(zynaddsubfx_nio STATIC + ${zynaddsubfx_nio_SRCS} + ) diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Engine.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Engine.cpp new file mode 100644 index 000000000..5e846b708 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Engine.cpp @@ -0,0 +1,28 @@ +/* + ZynAddSubFX - a software synthesizer + + Engine.cpp - Audio Driver base class + Copyright (C) 2009-2010 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include "Engine.h" + +Engine::Engine() +{} + +Engine::~Engine() +{} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Engine.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Engine.h new file mode 100644 index 000000000..9de4422c7 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Engine.h @@ -0,0 +1,41 @@ +/* + ZynAddSubFX - a software synthesizer + + Engine.h - Audio Driver base class + Copyright (C) 2009-2010 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef ENGINE_H +#define ENGINE_H +#include +/**Marker for input/output driver*/ +class Engine +{ + public: + Engine(); + virtual ~Engine(); + + /**Start the Driver with all capabilities + * @return true on success*/ + virtual bool Start() = 0; + /**Completely stop the Driver*/ + virtual void Stop() = 0; + + std::string name; +}; +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/EngineMgr.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/EngineMgr.cpp new file mode 100644 index 000000000..ee8f7b924 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/EngineMgr.cpp @@ -0,0 +1,156 @@ +#include "EngineMgr.h" +#include +#include +#include "Nio.h" +#include "InMgr.h" +#include "OutMgr.h" +#include "AudioOut.h" +#include "MidiIn.h" +#include "NulEngine.h" +#if OSS +#include "OssEngine.h" +#endif +#if ALSA +#include "AlsaEngine.h" +#endif +#if JACK +#include "JackEngine.h" +#endif +#if PORTAUDIO +#include "PaEngine.h" +#endif + +using namespace std; + +EngineMgr &EngineMgr::getInstance() +{ + static EngineMgr instance; + return instance; +} + +EngineMgr::EngineMgr() +{ + Engine *defaultEng = new NulEngine(); + + //conditional compiling mess (but contained) + engines.push_back(defaultEng); +#if OSS + engines.push_back(new OssEngine()); +#endif +#if ALSA + engines.push_back(new AlsaEngine()); +#endif +#if JACK + engines.push_back(new JackEngine()); +#endif +#if PORTAUDIO + engines.push_back(new PaEngine()); +#endif + + defaultOut = dynamic_cast(defaultEng); + + defaultIn = dynamic_cast(defaultEng); + + //Accept command line/compile time options + if(!Nio::defaultSink.empty()) + setOutDefault(Nio::defaultSink); + + if(!Nio::defaultSource.empty()) + setInDefault(Nio::defaultSource); +} + +EngineMgr::~EngineMgr() +{ + for(list::iterator itr = engines.begin(); + itr != engines.end(); ++itr) + delete *itr; +} + +Engine *EngineMgr::getEng(string name) +{ + transform(name.begin(), name.end(), name.begin(), ::toupper); + for(list::iterator itr = engines.begin(); + itr != engines.end(); ++itr) + if((*itr)->name == name) + return *itr; + return NULL; +} + +bool EngineMgr::start() +{ + bool expected = true; + if(!(defaultOut && defaultIn)) { + cerr << "ERROR: It looks like someone broke the Nio Output\n" + << " Attempting to recover by defaulting to the\n" + << " Null Engine." << endl; + defaultOut = dynamic_cast(getEng("NULL")); + defaultIn = dynamic_cast(getEng("NULL")); + } + + OutMgr::getInstance(). currentOut = defaultOut; + InMgr::getInstance(). current = defaultIn; + + //open up the default output(s) + cout << "Starting Audio: " << defaultOut->name << endl; + defaultOut->setAudioEn(true); + if(defaultOut->getAudioEn()) + cout << "Audio Started" << endl; + else { + expected = false; + cerr << "ERROR: The default audio output failed to open!" << endl; + OutMgr::getInstance(). currentOut = + dynamic_cast(getEng("NULL")); + OutMgr::getInstance(). currentOut->setAudioEn(true); + } + + cout << "Starting MIDI: " << defaultIn->name << endl; + defaultIn->setMidiEn(true); + if(defaultIn->getMidiEn()) + cout << "MIDI Started" << endl; + else { //recover + expected = false; + cerr << "ERROR: The default MIDI input failed to open!" << endl; + InMgr::getInstance(). current = dynamic_cast(getEng("NULL")); + InMgr::getInstance(). current->setMidiEn(true); + } + + //Show if expected drivers were booted + return expected; +} + +void EngineMgr::stop() +{ + for(list::iterator itr = engines.begin(); + itr != engines.end(); ++itr) + (*itr)->Stop(); +} + +bool EngineMgr::setInDefault(string name) +{ + MidiIn *chosen; + if((chosen = dynamic_cast(getEng(name)))) { //got the input + defaultIn = chosen; + return true; + } + + //Warn user + cerr << "Error: " << name << " is not a recognized MIDI input source" + << endl; + cerr << " Defaulting to the NULL input source" << endl; + + return false; +} + +bool EngineMgr::setOutDefault(string name) +{ + AudioOut *chosen; + if((chosen = dynamic_cast(getEng(name)))) { //got the output + defaultOut = chosen; + return true; + } + + //Warn user + cerr << "Error: " << name << " is not a recognized audio backend" << endl; + cerr << " Defaulting to the NULL audio backend" << endl; + return false; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/EngineMgr.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/EngineMgr.h new file mode 100644 index 000000000..4d56d2993 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/EngineMgr.h @@ -0,0 +1,43 @@ +#ifndef ENGINE_MGR_H +#define ENGINE_MGR_H + +#include +#include +#include "Engine.h" + + +class MidiIn; +class AudioOut; +class OutMgr; +/**Container/Owner of the long lived Engines*/ +class EngineMgr +{ + public: + static EngineMgr &getInstance(); + ~EngineMgr(); + + /**Gets requested engine + * @param name case unsensitive name of engine + * @return pointer to Engine or NULL + */ + Engine *getEng(std::string name); + + /**Start up defaults*/ + bool start(); + + /**Stop all engines*/ + void stop(); + + std::list engines; + + //return false on failure + bool setInDefault(std::string name); + bool setOutDefault(std::string name); + + //default I/O + AudioOut *defaultOut; + MidiIn *defaultIn; + private: + EngineMgr(); +}; +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/InMgr.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/InMgr.cpp new file mode 100644 index 000000000..b9db3fbd6 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/InMgr.cpp @@ -0,0 +1,143 @@ +#include "InMgr.h" +#include "MidiIn.h" +#include "EngineMgr.h" +#include "../Misc/Master.h" +#include + +using namespace std; + +ostream &operator<<(ostream &out, const MidiEvent &ev) +{ + switch(ev.type) { + case M_NOTE: + out << "MidiNote: note(" << ev.num << ")\n" + << " channel(" << ev.channel << ")\n" + << " velocity(" << ev.value << ")"; + break; + + case M_CONTROLLER: + out << "MidiCtl: controller(" << ev.num << ")\n" + << " channel(" << ev.channel << ")\n" + << " value(" << ev.value << ")"; + break; + + case M_PGMCHANGE: + out << "PgmChange: program(" << ev.num << ")\n" + << " channel(" << ev.channel << ")"; + break; + } + + return out; +} + +MidiEvent::MidiEvent() + :channel(0), type(0), num(0), value(0), time(0) +{} + +InMgr &InMgr::getInstance() +{ + static InMgr instance; + return instance; +} + +InMgr::InMgr() + :queue(100), master(Master::getInstance()) +{ + current = NULL; + sem_init(&work, PTHREAD_PROCESS_PRIVATE, 0); +} + +InMgr::~InMgr() +{ + //lets stop the consumer thread + sem_destroy(&work); +} + +void InMgr::putEvent(MidiEvent ev) +{ + if(queue.push(ev)) //check for error + cerr << "ERROR: Midi Ringbuffer is FULL" << endl; + else + sem_post(&work); +} + +void InMgr::flush(unsigned frameStart, unsigned frameStop) +{ + MidiEvent ev; + while(!sem_trywait(&work)) { + queue.peak(ev); + if(ev.time < (int)frameStart || ev.time > (int)frameStop) { + //Back out of transaction + sem_post(&work); + //printf("%d vs [%d..%d]\n",ev.time, frameStart, frameStop); + break; + } + queue.pop(ev); + //cout << ev << endl; + + switch(ev.type) { + case M_NOTE: + dump.dumpnote(ev.channel, ev.num, ev.value); + + if(ev.value) + master.noteOn(ev.channel, ev.num, ev.value); + else + master.noteOff(ev.channel, ev.num); + break; + + case M_CONTROLLER: + dump.dumpcontroller(ev.channel, ev.num, ev.value); + master.setController(ev.channel, ev.num, ev.value); + break; + + case M_PGMCHANGE: + master.setProgram(ev.channel, ev.num); + break; + case M_PRESSURE: + master.polyphonicAftertouch(ev.channel, ev.num, ev.value); + break; + } + } +} + +bool InMgr::empty(void) const +{ + int semvalue = 0; + sem_getvalue(&work, &semvalue); + return semvalue <= 0; +} + +bool InMgr::setSource(string name) +{ + MidiIn *src = getIn(name); + + if(!src) + return false; + + if(current) + current->setMidiEn(false); + current = src; + current->setMidiEn(true); + + bool success = current->getMidiEn(); + + //Keep system in a valid state (aka with a running driver) + if(!success) + (current = getIn("NULL"))->setMidiEn(true); + + return success; +} + +string InMgr::getSource() const +{ + if(current) + return current->name; + else + return "ERROR"; +} + +MidiIn *InMgr::getIn(string name) +{ + EngineMgr &eng = EngineMgr::getInstance(); + return dynamic_cast(eng.getEng(name)); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/InMgr.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/InMgr.h new file mode 100644 index 000000000..e9ddc580e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/InMgr.h @@ -0,0 +1,54 @@ +#ifndef INMGR_H +#define INMGR_H + +#include +#include "SafeQueue.h" + +enum midi_type { + M_NOTE = 1, + M_CONTROLLER = 2, + M_PGMCHANGE = 3, + M_PRESSURE = 4 +}; //type=1 for note, type=2 for controller, type=3 for program change +//type=4 for polyphonic aftertouch + +struct MidiEvent { + MidiEvent(); + int channel; //the midi channel for the event + int type; //type=1 for note, type=2 for controller + int num; //note, controller or program number + int value; //velocity or controller value + int time; //time offset of event (used only in jack->jack case at the moment) +}; + +//super simple class to manage the inputs +class InMgr +{ + public: + static InMgr &getInstance(); + ~InMgr(); + + void putEvent(MidiEvent ev); + + /**Flush the Midi Queue*/ + void flush(unsigned frameStart, unsigned frameStop); + + bool empty() const; + + bool setSource(std::string name); + + std::string getSource() const; + + friend class EngineMgr; + private: + InMgr(); + class MidiIn *getIn(std::string name); + SafeQueue queue; + mutable sem_t work; + class MidiIn * current; + + /**the link to the rest of zyn*/ + class Master & master; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/JackEngine.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/JackEngine.cpp new file mode 100644 index 000000000..1201024dd --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/JackEngine.cpp @@ -0,0 +1,406 @@ +/* + JackEngine.cpp + + Copyright 2009, Alan Calvert + + This file is part of yoshimi, which is free software: you can + redistribute it and/or modify it under the terms of the GNU General + Public License as published by the Free Software Foundation, either + version 3 of the License, or (at your option) any later version. + + yoshimi is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with yoshimi. If not, see . +*/ + +#include + +#include +#include +#include +#include +#include + +#include "Nio.h" +#include "InMgr.h" + +#include "JackEngine.h" + +using namespace std; + +extern char *instance_name; + +JackEngine::JackEngine() + :AudioOut(), jackClient(NULL) +{ + name = "JACK"; + audio.jackSamplerate = 0; + audio.jackNframes = 0; + for(int i = 0; i < 2; ++i) { + audio.ports[i] = NULL; + audio.portBuffs[i] = NULL; + } + midi.inport = NULL; + midi.jack_sync = false; +} + +bool JackEngine::connectServer(string server) +{ + bool autostart_jack = true; + if(jackClient) + return true; + + string clientname = "zynaddsubfx"; + string postfix = Nio::getPostfix(); + if(!postfix.empty()) + clientname += "_" + postfix; + jack_status_t jackstatus; + bool use_server_name = server.size() && server.compare("default") != 0; + jack_options_t jopts = (jack_options_t) + (((!instance_name + && use_server_name) ? JackServerName : + JackNullOption) + | ((autostart_jack) ? JackNullOption : + JackNoStartServer)); + + if(instance_name) + jackClient = jack_client_open(instance_name, jopts, &jackstatus); + else { + if(use_server_name) + jackClient = jack_client_open( + clientname.c_str(), jopts, &jackstatus, + server.c_str()); + else + jackClient = jack_client_open( + clientname.c_str(), jopts, &jackstatus); + } + + + if(NULL != jackClient) + return true; + else + cerr << "Error, failed to open jack client on server: " << server + << " status " << jackstatus << endl; + return false; +} + +bool JackEngine::connectJack() +{ + connectServer(""); + if(NULL != jackClient) { + setBufferSize(jack_get_buffer_size(jackClient)); + int chk; + jack_set_error_function(_errorCallback); + jack_set_info_function(_infoCallback); + if(jack_set_buffer_size_callback(jackClient, _bufferSizeCallback, this)) + cerr << "Error setting the bufferSize callback" << endl; + if((chk = jack_set_xrun_callback(jackClient, _xrunCallback, this))) + cerr << "Error setting jack xrun callback" << endl; + if(jack_set_process_callback(jackClient, _processCallback, this)) { + cerr << "Error, JackEngine failed to set process callback" << endl; + return false; + } + if(jack_activate(jackClient)) { + cerr << "Error, failed to activate jack client" << endl; + return false; + } + + return true; + } + else + cerr << "Error, NULL jackClient through Start()" << endl; + return false; +} + +void JackEngine::disconnectJack() +{ + if(jackClient) { + cout << "Deactivating and closing JACK client" << endl; + + jack_deactivate(jackClient); + jack_client_close(jackClient); + jackClient = NULL; + } +} + +bool JackEngine::Start() +{ + return openMidi() && openAudio(); +} + +void JackEngine::Stop() +{ + stopMidi(); + stopAudio(); +} + +void JackEngine::setMidiEn(bool nval) +{ + if(nval) + openMidi(); + else + stopMidi(); +} + +bool JackEngine::getMidiEn() const +{ + return midi.inport; +} + +void JackEngine::setAudioEn(bool nval) +{ + if(nval) + openAudio(); + else + stopAudio(); +} + +bool JackEngine::getAudioEn() const +{ + return audio.ports[0]; +} + +bool JackEngine::openAudio() +{ + if(getAudioEn()) + return true; + + if(!getMidiEn()) + if(!connectJack()) + return false; + + + const char *portnames[] = { "out_1", "out_2" }; + for(int port = 0; port < 2; ++port) + audio.ports[port] = jack_port_register( + jackClient, + portnames[port], + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput + | JackPortIsTerminal, + 0); + if((NULL != audio.ports[0]) && (NULL != audio.ports[1])) { + audio.jackSamplerate = jack_get_sample_rate(jackClient); + audio.jackNframes = jack_get_buffer_size(jackClient); + samplerate = audio.jackSamplerate; + bufferSize = audio.jackNframes; + + + //Attempt to autoConnect when specified + if(Nio::autoConnect) { + const char **outPorts = jack_get_ports( + jackClient, + NULL, + NULL, + JackPortIsPhysical + | JackPortIsInput); + if(outPorts != NULL) { + //Verify that stereo is available + assert(outPorts[0]); + assert(outPorts[1]); + + //Connect to physical outputs + jack_connect(jackClient, jack_port_name( + audio.ports[0]), outPorts[0]); + jack_connect(jackClient, jack_port_name( + audio.ports[1]), outPorts[1]); + } + else + cerr << "Warning, No outputs to autoconnect to" << endl; + } + midi.jack_sync = true; + return true; + } + else + cerr << "Error, failed to register jack audio ports" << endl; + midi.jack_sync = false; + return false; +} + +void JackEngine::stopAudio() +{ + for(int i = 0; i < 2; ++i) { + jack_port_t *port = audio.ports[i]; + audio.ports[i] = NULL; + if(NULL != port) + jack_port_unregister(jackClient, port); + } + midi.jack_sync = false; + if(!getMidiEn()) + disconnectJack(); +} + +bool JackEngine::openMidi() +{ + if(getMidiEn()) + return true; + if(!getAudioEn()) + if(!connectJack()) + return false; + + midi.inport = jack_port_register(jackClient, "midi_input", + JACK_DEFAULT_MIDI_TYPE, + JackPortIsInput | JackPortIsTerminal, 0); + return midi.inport; +} + +void JackEngine::stopMidi() +{ + jack_port_t *port = midi.inport; + midi.inport = NULL; + if(port) + jack_port_unregister(jackClient, port); + + if(!getAudioEn()) + disconnectJack(); +} + +int JackEngine::clientId() +{ + if(NULL != jackClient) + return (long)jack_client_thread_id(jackClient); + else + return -1; +} + +string JackEngine::clientName() +{ + if(NULL != jackClient) + return string(jack_get_client_name(jackClient)); + else + cerr << "Error, clientName() with null jackClient" << endl; + return string("Oh, yoshimi :-("); +} + +int JackEngine::_processCallback(jack_nframes_t nframes, void *arg) +{ + return static_cast(arg)->processCallback(nframes); +} + +int JackEngine::processCallback(jack_nframes_t nframes) +{ + bool okaudio = true; + + handleMidi(nframes); + if((NULL != audio.ports[0]) && (NULL != audio.ports[1])) + okaudio = processAudio(nframes); + return okaudio ? 0 : -1; +} + +bool JackEngine::processAudio(jack_nframes_t nframes) +{ + for(int port = 0; port < 2; ++port) { + audio.portBuffs[port] = + (jsample_t *)jack_port_get_buffer(audio.ports[port], nframes); + if(NULL == audio.portBuffs[port]) { + cerr << "Error, failed to get jack audio port buffer: " + << port << endl; + return false; + } + } + + Stereo smp = getNext(); + + //Assumes size of smp.l == nframes + memcpy(audio.portBuffs[0], smp.l, bufferSize * sizeof(float)); + memcpy(audio.portBuffs[1], smp.r, bufferSize * sizeof(float)); + return true; +} + +int JackEngine::_xrunCallback(void *) +{ + cerr << "Jack reports xrun" << endl; + return 0; +} + +void JackEngine::_errorCallback(const char *msg) +{ + cerr << "Jack reports error: " << msg << endl; +} + +void JackEngine::_infoCallback(const char *msg) +{ + cerr << "Jack info message: " << msg << endl; +} + +int JackEngine::_bufferSizeCallback(jack_nframes_t nframes, void *arg) +{ + return static_cast(arg)->bufferSizeCallback(nframes); +} + +int JackEngine::bufferSizeCallback(jack_nframes_t nframes) +{ + cerr << "Jack buffer resized" << endl; + setBufferSize(nframes); + return 0; +} + +void JackEngine::handleMidi(unsigned long frames) +{ + if(!midi.inport) + return; + void *midi_buf = jack_port_get_buffer(midi.inport, frames); + jack_midi_event_t jack_midi_event; + jack_nframes_t event_index = 0; + unsigned char *midi_data; + unsigned char type; + + while(jack_midi_event_get(&jack_midi_event, midi_buf, + event_index++) == 0) { + MidiEvent ev; + midi_data = jack_midi_event.buffer; + type = midi_data[0] & 0xF0; + ev.channel = midi_data[0] & 0x0F; + ev.time = midi.jack_sync ? jack_midi_event.time : 0; + + switch(type) { + case 0x80: /* note-off */ + ev.type = M_NOTE; + ev.num = midi_data[1]; + ev.value = 0; + InMgr::getInstance().putEvent(ev); + break; + + case 0x90: /* note-on */ + ev.type = M_NOTE; + ev.num = midi_data[1]; + ev.value = midi_data[2]; + InMgr::getInstance().putEvent(ev); + break; + + case 0xA0: /* pressure, aftertouch */ + ev.type = M_PRESSURE; + ev.num = midi_data[1]; + ev.value = midi_data[2]; + InMgr::getInstance().putEvent(ev); + break; + + case 0xB0: /* controller */ + ev.type = M_CONTROLLER; + ev.num = midi_data[1]; + ev.value = midi_data[2]; + InMgr::getInstance().putEvent(ev); + break; + + case 0xC0: /* program change */ + ev.type = M_PGMCHANGE; + ev.num = midi_data[1]; + ev.value = 0; + InMgr::getInstance().putEvent(ev); + break; + + case 0xE0: /* pitch bend */ + ev.type = M_CONTROLLER; + ev.num = C_pitchwheel; + ev.value = ((midi_data[2] << 7) | midi_data[1]) - 8192; + InMgr::getInstance().putEvent(ev); + break; + + /* XXX TODO: handle MSB/LSB controllers and RPNs and NRPNs */ + } + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/JackEngine.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/JackEngine.h new file mode 100644 index 000000000..705ff3ecf --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/JackEngine.h @@ -0,0 +1,89 @@ +/* + JackEngine.h + + Copyright 2009, Alan Calvert + + This file is part of yoshimi, which is free software: you can + redistribute it and/or modify it under the terms of the GNU General + Public License as published by the Free Software Foundation, either + version 3 of the License, or (at your option) any later version. + + yoshimi is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with yoshimi. If not, see . +*/ + +#ifndef JACK_ENGINE_H +#define JACK_ENGINE_H + +#include +#include +#include +#include +#include + +#include "MidiIn.h" +#include "AudioOut.h" + +typedef jack_default_audio_sample_t jsample_t; + +class JackEngine:public AudioOut, MidiIn +{ + public: + JackEngine(); + ~JackEngine() { } + + bool Start(); + void Stop(); + + void setMidiEn(bool nval); + bool getMidiEn() const; + + void setAudioEn(bool nval); + bool getAudioEn() const; + + int getBuffersize() { return audio.jackNframes; } + + std::string clientName(); + int clientId(); + + protected: + + int processCallback(jack_nframes_t nframes); + static int _processCallback(jack_nframes_t nframes, void *arg); + int bufferSizeCallback(jack_nframes_t nframes); + static int _bufferSizeCallback(jack_nframes_t nframes, void *arg); + static void _errorCallback(const char *msg); + static void _infoCallback(const char *msg); + static int _xrunCallback(void *arg); + + private: + bool connectServer(std::string server); + bool connectJack(); + void disconnectJack(); + bool openAudio(); + void stopAudio(); + bool processAudio(jack_nframes_t nframes); + bool openMidi(); + void stopMidi(); + + jack_client_t *jackClient; + struct audio { + unsigned int jackSamplerate; + unsigned int jackNframes; + jack_port_t *ports[2]; + jsample_t *portBuffs[2]; + } audio; + struct midi { + jack_port_t *inport; + bool jack_sync; + } midi; + + void handleMidi(unsigned long frames); +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/MidiIn.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/MidiIn.cpp new file mode 100644 index 000000000..3635bde2e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/MidiIn.cpp @@ -0,0 +1,77 @@ +/* + ZynAddSubFX - a software synthesizer + + MidiIn.C - This class is inherited by all the Midi input classes + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "MidiIn.h" +#include "../globals.h" +#include "InMgr.h" + +void MidiIn::midiProcess(unsigned char head, + unsigned char num, + unsigned char value) +{ + MidiEvent ev; + unsigned char chan = head & 0x0f; + switch(head & 0xf0) { + case 0x80: //Note Off + ev.type = M_NOTE; + ev.channel = chan; + ev.num = num; + ev.value = 0; + InMgr::getInstance().putEvent(ev); + break; + case 0x90: //Note On + ev.type = M_NOTE; + ev.channel = chan; + ev.num = num; + ev.value = value; + InMgr::getInstance().putEvent(ev); + break; + case 0xA0: /* pressure, aftertouch */ + ev.type = M_PRESSURE; + ev.channel = chan; + ev.num = num; + ev.value = value; + InMgr::getInstance().putEvent(ev); + break; + case 0xb0: //Controller + ev.type = M_CONTROLLER; + ev.channel = chan; + ev.num = num; + ev.value = value; + InMgr::getInstance().putEvent(ev); + break; + case 0xc0: //Program Change + ev.type = M_PGMCHANGE; + ev.channel = chan; + ev.num = num; + ev.value = 0; + InMgr::getInstance().putEvent(ev); + break; + case 0xe0: //Pitch Wheel + ev.type = M_CONTROLLER; + ev.channel = chan; + ev.num = C_pitchwheel; + ev.value = (num + value * (int) 128) - 8192; + InMgr::getInstance().putEvent(ev); + break; + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/MidiIn.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/MidiIn.h new file mode 100644 index 000000000..780a67ff5 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/MidiIn.h @@ -0,0 +1,43 @@ +/* + ZynAddSubFX - a software synthesizer + + MidiIn.h - This class is inherited by all the Midi input classes + Copyright (C) 2002-2005 Nasca Octavian Paul + Copyright (C) 2009-2010 Mark McCurry + Author: Nasca Octavian Paula + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef MIDI_IN_H +#define MIDI_IN_H + +#include "Engine.h" + +/**This class is inherited by all the Midi input classes*/ +class MidiIn:public virtual Engine +{ + public: + /**Enables or disables driver based upon value*/ + virtual void setMidiEn(bool nval) = 0; + /**Returns if driver is initialized*/ + virtual bool getMidiEn() const = 0; + static void midiProcess(unsigned char head, + unsigned char num, + unsigned char value); +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Nio.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Nio.cpp new file mode 100644 index 000000000..0026437a1 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Nio.cpp @@ -0,0 +1,138 @@ +#include "Nio.h" +#include "OutMgr.h" +#include "InMgr.h" +#include "EngineMgr.h" +#include "MidiIn.h" +#include "AudioOut.h" +#include "WavEngine.h" +#include +#include +using std::string; +using std::set; +using std::cerr; +using std::endl; + +InMgr *in = NULL; +OutMgr *out = NULL; +EngineMgr *eng = NULL; +string postfix; + +bool Nio::autoConnect = false; +string Nio::defaultSource = IN_DEFAULT; +string Nio::defaultSink = OUT_DEFAULT; + +void Nio::init(void) +{ + in = &InMgr::getInstance(); //Enable input wrapper + out = &OutMgr::getInstance(); //Initialize the Output Systems + eng = &EngineMgr::getInstance(); //Initialize The Engines +} + +bool Nio::start() +{ + init(); + return eng->start(); +} + +void Nio::stop() +{ + eng->stop(); +} + +void Nio::setDefaultSource(string name) +{ + std::transform(name.begin(), name.end(), name.begin(), ::toupper); + defaultSource = name; +} + +void Nio::setDefaultSink(string name) +{ + std::transform(name.begin(), name.end(), name.begin(), ::toupper); + defaultSink = name; +} + +bool Nio::setSource(string name) +{ + return in->setSource(name); +} + +bool Nio::setSink(string name) +{ + return out->setSink(name); +} + +void Nio::setPostfix(std::string post) +{ + postfix = post; +} + +std::string Nio::getPostfix(void) +{ + return postfix; +} + +set Nio::getSources(void) +{ + set sources; + for(std::list::iterator itr = eng->engines.begin(); + itr != eng->engines.end(); ++itr) + if(dynamic_cast(*itr)) + sources.insert((*itr)->name); + return sources; +} + +set Nio::getSinks(void) +{ + set sinks; + for(std::list::iterator itr = eng->engines.begin(); + itr != eng->engines.end(); ++itr) + if(dynamic_cast(*itr)) + sinks.insert((*itr)->name); + return sinks; +} + +string Nio::getSource() +{ + return in->getSource(); +} + +string Nio::getSink() +{ + return out->getSink(); +} + +#if JACK +#include +void Nio::preferedSampleRate(unsigned &rate) +{ + jack_client_t *client = jack_client_open("temp-client", + JackNoStartServer, 0); + if(client) { + rate = jack_get_sample_rate(client); + jack_client_close(client); + } +} +#else +void Nio::preferedSampleRate(unsigned &) +{} +#endif + +void Nio::waveNew(class WavFile *wave) +{ + out->wave->newFile(wave); +} + +void Nio::waveStart(void) +{ + out->wave->Start(); +} + +void Nio::waveStop(void) +{ + out->wave->Stop(); +} + +void Nio::waveEnd(void) +{ + out->wave->destroyFile(); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Nio.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Nio.h new file mode 100644 index 000000000..ba050b8ed --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/Nio.h @@ -0,0 +1,47 @@ +#ifndef NIO_H +#define NIO_H +#include +#include + +class WavFile; + +/**Interface to Nio Subsystem + * + * Should be only externally included header */ +namespace Nio +{ + void init(void); + bool start(void); + void stop(void); + + void setDefaultSource(std::string name); + void setDefaultSink(std::string name); + + bool setSource(std::string name); + bool setSink(std::string name); + + void setPostfix(std::string post); + std::string getPostfix(void); + + std::set getSources(void); + std::set getSinks(void); + + std::string getSource(void); + std::string getSink(void); + + //Get the prefered sample rate from jack (if running) + void preferedSampleRate(unsigned &rate); + + + //Wave writing + void waveNew(class WavFile *wave); + void waveStart(void); + void waveStop(void); + void waveEnd(void); + + extern bool autoConnect; + extern std::string defaultSource; + extern std::string defaultSink; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/NulEngine.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/NulEngine.cpp new file mode 100644 index 000000000..633e2ae24 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/NulEngine.cpp @@ -0,0 +1,113 @@ +/* + ZynAddSubFX - a software synthesizer + + OSSaudiooutput.C - Audio output for Open Sound System + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "NulEngine.h" +#include "../globals.h" + +#include +#include + +using namespace std; + +NulEngine::NulEngine() + :AudioOut(), pThread(NULL) +{ + name = "NULL"; + playing_until.tv_sec = 0; + playing_until.tv_usec = 0; +} + +void *NulEngine::_AudioThread(void *arg) +{ + return (static_cast(arg))->AudioThread(); +} + +void *NulEngine::AudioThread() +{ + while(pThread) { + getNext(); + + struct timeval now; + int remaining = 0; + gettimeofday(&now, NULL); + if((playing_until.tv_usec == 0) && (playing_until.tv_sec == 0)) { + playing_until.tv_usec = now.tv_usec; + playing_until.tv_sec = now.tv_sec; + } + else { + remaining = (playing_until.tv_usec - now.tv_usec) + + (playing_until.tv_sec - now.tv_sec) * 1000000; + if(remaining > 10000) //Don't sleep() less than 10ms. + //This will add latency... + usleep(remaining - 10000); + if(remaining < 0) + cerr << "WARNING - too late" << endl; + } + playing_until.tv_usec += synth->buffersize * 1000000 + / synth->samplerate; + if(remaining < 0) + playing_until.tv_usec -= remaining; + playing_until.tv_sec += playing_until.tv_usec / 1000000; + playing_until.tv_usec %= 1000000; + } + return NULL; +} + +NulEngine::~NulEngine() +{} + +bool NulEngine::Start() +{ + setAudioEn(true); + return getAudioEn(); +} + +void NulEngine::Stop() +{ + setAudioEn(false); +} + +void NulEngine::setAudioEn(bool nval) +{ + if(nval) { + if(!getAudioEn()) { + pthread_t *thread = new pthread_t; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pThread = thread; + pthread_create(pThread, &attr, _AudioThread, this); + } + } + else + if(getAudioEn()) { + pthread_t *thread = pThread; + pThread = NULL; + pthread_join(*thread, NULL); + delete thread; + } +} + +bool NulEngine::getAudioEn() const +{ + return pThread; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/NulEngine.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/NulEngine.h new file mode 100644 index 000000000..18341809e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/NulEngine.h @@ -0,0 +1,56 @@ +/* + ZynAddSubFX - a software synthesizer + + NulEngine.h - Dummy In/Out driver + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef NUL_ENGINE_H +#define NUL_ENGINE_H + +#include +#include +#include "../globals.h" +#include "AudioOut.h" +#include "MidiIn.h" + +class NulEngine:public AudioOut, MidiIn +{ + public: + NulEngine(); + ~NulEngine(); + + bool Start(); + void Stop(); + + void setAudioEn(bool nval); + bool getAudioEn() const; + + void setMidiEn(bool) {} + bool getMidiEn() const {return true; } + + protected: + void *AudioThread(); + static void *_AudioThread(void *arg); + + private: + struct timeval playing_until; + pthread_t *pThread; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OssEngine.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OssEngine.cpp new file mode 100644 index 000000000..1c6cdf11c --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OssEngine.cpp @@ -0,0 +1,285 @@ +/* + ZynAddSubFX - a software synthesizer + + OSSaudiooutput.C - Audio output for Open Sound System + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "OssEngine.h" +#include "../Misc/Util.h" +#include "../globals.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "InMgr.h" + +using namespace std; + +OssEngine::OssEngine() + :AudioOut(), engThread(NULL) +{ + name = "OSS"; + + midi.handle = -1; + audio.handle = -1; + + audio.smps = new short[synth->buffersize * 2]; + memset(audio.smps, 0, synth->bufferbytes); +} + +OssEngine::~OssEngine() +{ + Stop(); + delete [] audio.smps; +} + +bool OssEngine::openAudio() +{ + if(audio.handle != -1) + return true; //already open + + int snd_bitsize = 16; + int snd_fragment = 0x00080009; //fragment size (?); + int snd_stereo = 1; //stereo; + int snd_format = AFMT_S16_LE; + int snd_samplerate = synth->samplerate; + + const char *device = config.cfg.LinuxOSSWaveOutDev; + if(getenv("DSP_DEVICE")) + device = getenv("DSP_DEVICE"); + + audio.handle = open(device, O_WRONLY, 0); + if(audio.handle == -1) { + cerr << "ERROR - I can't open the " + << device << '.' << endl; + return false; + } + ioctl(audio.handle, SNDCTL_DSP_RESET, NULL); + ioctl(audio.handle, SNDCTL_DSP_SETFMT, &snd_format); + ioctl(audio.handle, SNDCTL_DSP_STEREO, &snd_stereo); + ioctl(audio.handle, SNDCTL_DSP_SPEED, &snd_samplerate); + ioctl(audio.handle, SNDCTL_DSP_SAMPLESIZE, &snd_bitsize); + ioctl(audio.handle, SNDCTL_DSP_SETFRAGMENT, &snd_fragment); + + if(!getMidiEn()) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + engThread = new pthread_t; + pthread_create(engThread, &attr, _thread, this); + } + + return true; +} + +void OssEngine::stopAudio() +{ + int handle = audio.handle; + if(handle == -1) //already closed + return; + audio.handle = -1; + + if(!getMidiEn() && engThread) + pthread_join(*engThread, NULL); + delete engThread; + engThread = NULL; + + close(handle); +} + +bool OssEngine::Start() +{ + bool good = true; + + if(!openAudio()) { + cerr << "Failed to open OSS audio" << endl; + good = false; + } + + if(!openMidi()) { + cerr << "Failed to open OSS midi" << endl; + good = false; + } + + return good; +} + +void OssEngine::Stop() +{ + stopAudio(); + stopMidi(); +} + +void OssEngine::setMidiEn(bool nval) +{ + if(nval) + openMidi(); + else + stopMidi(); +} + +bool OssEngine::getMidiEn() const +{ + return midi.handle != -1; +} + +void OssEngine::setAudioEn(bool nval) +{ + if(nval) + openAudio(); + else + stopAudio(); +} + +bool OssEngine::getAudioEn() const +{ + return audio.handle != -1; +} + +bool OssEngine::openMidi() +{ + int handle = midi.handle; + if(handle != -1) + return true; //already open + + handle = open(config.cfg.LinuxOSSSeqInDev, O_RDONLY, 0); + + if(-1 == handle) + return false; + midi.handle = handle; + + if(!getAudioEn()) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + engThread = new pthread_t; + pthread_create(engThread, &attr, _thread, this); + } + + return true; +} + +void OssEngine::stopMidi() +{ + int handle = midi.handle; + if(handle == -1) //already closed + return; + + midi.handle = -1; + + if(!getAudioEn() && engThread) { + pthread_join(*engThread, NULL); + delete engThread; + engThread = NULL; + } + + close(handle); +} + +void *OssEngine::_thread(void *arg) +{ + return (static_cast(arg))->thread(); +} + +void *OssEngine::thread() +{ + unsigned char tmp[4] = {0, 0, 0, 0}; + set_realtime(); + while(getAudioEn() || getMidiEn()) { + if(getAudioEn()) { + const Stereo smps = getNext(); + + float l, r; + for(int i = 0; i < synth->buffersize; ++i) { + l = smps.l[i]; + r = smps.r[i]; + + if(l < -1.0f) + l = -1.0f; + else + if(l > 1.0f) + l = 1.0f; + if(r < -1.0f) + r = -1.0f; + else + if(r > 1.0f) + r = 1.0f; + + audio.smps[i * 2] = (short int) (l * 32767.0f); + audio.smps[i * 2 + 1] = (short int) (r * 32767.0f); + } + int handle = audio.handle; + if(handle != -1) + write(handle, audio.smps, synth->buffersize * 4); // *2 because is 16 bit, again * 2 because is stereo + else + break; + } + + //Collect up to 30 midi events + for(int k = 0; k < 30 && getMidiEn(); ++k) { + static char escaped; + + memset(tmp, 0, 4); + + if(escaped) { + tmp[0] = escaped; + escaped = 0; + } + else { + getMidi(tmp); + if(!(tmp[0] & 0x80)) + continue; + } + getMidi(tmp + 1); + if(tmp[1] & 0x80) { + escaped = tmp[1]; + tmp[1] = 0; + } + else { + getMidi(tmp + 2); + if(tmp[2] & 0x80) { + escaped = tmp[2]; + tmp[2] = 0; + } + else { + getMidi(tmp + 3); + if(tmp[3] & 0x80) { + escaped = tmp[3]; + tmp[3] = 0; + } + } + } + midiProcess(tmp[0], tmp[1], tmp[2]); + } + } + pthread_exit(NULL); + return NULL; +} + +void OssEngine::getMidi(unsigned char *midiPtr) +{ + read(midi.handle, midiPtr, 1); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OssEngine.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OssEngine.h new file mode 100644 index 000000000..cdccc8120 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OssEngine.h @@ -0,0 +1,76 @@ +/* + ZynAddSubFX - a software synthesizer + + OSSaudiooutput.h - Audio output for Open Sound System + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef OSS_ENGINE_H +#define OSS_ENGINE_H + +#include +#include "../globals.h" +#include "AudioOut.h" +#include "MidiIn.h" + +class OssEngine:public AudioOut, MidiIn +{ + public: + OssEngine(); + ~OssEngine(); + + bool Start(); + void Stop(); + + void setAudioEn(bool nval); + bool getAudioEn() const; + + void setMidiEn(bool nval); + bool getMidiEn() const; + + + protected: + void *thread(); + static void *_thread(void *arg); + + private: + pthread_t *engThread; + + //Audio + bool openAudio(); + void stopAudio(); + + struct audio { + int handle; + short int *smps; //Samples to be sent to soundcard + bool en; + } audio; + + //Midi + bool openMidi(); + void stopMidi(); + void getMidi(unsigned char *midiPtr); + + struct midi { + int handle; + bool en; + bool run; + } midi; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OutMgr.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OutMgr.cpp new file mode 100644 index 000000000..7fbe2417e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OutMgr.cpp @@ -0,0 +1,185 @@ +#include "OutMgr.h" +#include +#include +#include +#include "AudioOut.h" +#include "Engine.h" +#include "EngineMgr.h" +#include "InMgr.h" +#include "WavEngine.h" +#include "../Misc/Master.h" +#include "../Misc/Util.h" //for set_realtime() + +using namespace std; + +OutMgr &OutMgr::getInstance() +{ + static OutMgr instance; + return instance; +} + +OutMgr::OutMgr() + :wave(new WavEngine()), + priBuf(new float[4096], + new float[4096]), priBuffCurrent(priBuf), + master(Master::getInstance()) +{ + currentOut = NULL; + stales = 0; + master = Master::getInstance(); + + //init samples + outr = new float[synth->buffersize]; + outl = new float[synth->buffersize]; + memset(outl, 0, synth->bufferbytes); + memset(outr, 0, synth->bufferbytes); +} + +OutMgr::~OutMgr() +{ + delete wave; + delete [] priBuf.l; + delete [] priBuf.r; + delete [] outr; + delete [] outl; +} + +/* Sequence of a tick + * 1) Lets remove old/stale samples + * 2) Apply appliciable midi events + * 3) Lets see if we need to generate samples + * 4) Lets generate some + * 5) Goto 2 if more are needed + * 6) Lets return those samples to the primary and secondary outputs + * 7) Lets wait for another tick + */ +const Stereo OutMgr::tick(unsigned int frameSize) +{ + InMgr &midi = InMgr::getInstance(); + //SysEv->execute(); + removeStaleSmps(); + int i=0; + while(frameSize > storedSmps()) { + if(!midi.empty()) { + pthread_mutex_lock(&(master.mutex)); + midi.flush(i*synth->buffersize, (i+1)*synth->buffersize); + pthread_mutex_unlock(&(master.mutex)); + } + pthread_mutex_lock(&(master.mutex)); + master.AudioOut(outl, outr); + pthread_mutex_unlock(&(master.mutex)); + addSmps(outl, outr); + i++; + } + stales = frameSize; + return priBuf; +} + +AudioOut *OutMgr::getOut(string name) +{ + return dynamic_cast(EngineMgr::getInstance().getEng(name)); +} + +string OutMgr::getDriver() const +{ + return currentOut->name; +} + +bool OutMgr::setSink(string name) +{ + AudioOut *sink = getOut(name); + + if(!sink) + return false; + + if(currentOut) + currentOut->setAudioEn(false); + + currentOut = sink; + currentOut->setAudioEn(true); + + bool success = currentOut->getAudioEn(); + + //Keep system in a valid state (aka with a running driver) + if(!success) + (currentOut = getOut("NULL"))->setAudioEn(true); + + return success; +} + +string OutMgr::getSink() const +{ + if(currentOut) + return currentOut->name; + else { + cerr << "BUG: No current output in OutMgr " << __LINE__ << endl; + return "ERROR"; + } + return "ERROR"; +} + +//perform a cheap linear interpolation for resampling +//This will result in some distortion at frame boundries +//returns number of samples produced +static size_t resample(float *dest, + const float *src, + float s_in, + float s_out, + size_t elms) +{ + size_t out_elms = elms * s_out / s_in; + float r_pos = 0.0f; + for(int i = 0; i < (int)out_elms; ++i, r_pos += s_in / s_out) + dest[i] = interpolate(src, elms, r_pos); + + return out_elms; +} + +void OutMgr::addSmps(float *l, float *r) +{ + //allow wave file to syphon off stream + wave->push(Stereo(l, r), synth->buffersize); + + const int s_out = currentOut->getSampleRate(), + s_sys = synth->samplerate; + + if(s_out != s_sys) { //we need to resample + const size_t steps = resample(priBuffCurrent.l, + l, + s_sys, + s_out, + synth->buffersize); + resample(priBuffCurrent.r, r, s_sys, s_out, synth->buffersize); + + priBuffCurrent.l += steps; + priBuffCurrent.r += steps; + } + else { //just copy the samples + memcpy(priBuffCurrent.l, l, synth->bufferbytes); + memcpy(priBuffCurrent.r, r, synth->bufferbytes); + priBuffCurrent.l += synth->buffersize; + priBuffCurrent.r += synth->buffersize; + } +} + +void OutMgr::removeStaleSmps() +{ + if(!stales) + return; + + const int leftover = storedSmps() - stales; + + assert(leftover > -1); + + //leftover samples [seen at very low latencies] + if(leftover) { + memmove(priBuf.l, priBuffCurrent.l - leftover, leftover * sizeof(float)); + memmove(priBuf.r, priBuffCurrent.r - leftover, leftover * sizeof(float)); + priBuffCurrent.l = priBuf.l + leftover; + priBuffCurrent.r = priBuf.r + leftover; + } + else + priBuffCurrent = priBuf; + + stales = 0; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OutMgr.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OutMgr.h new file mode 100644 index 000000000..bc3a922d5 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/OutMgr.h @@ -0,0 +1,65 @@ +#ifndef OUTMGR_H +#define OUTMGR_H + +#include "../Misc/Stereo.h" +#include +#include +#include +#include + + +class AudioOut; +class OutMgr +{ + public: + static OutMgr &getInstance(); + ~OutMgr(); + + /**Execute a tick*/ + const Stereo tick(unsigned int frameSize); + + /**Request a new set of samples + * @param n number of requested samples (defaults to 1) + * @return -1 for locking issues 0 for valid request*/ + void requestSamples(unsigned int n = 1); + + /**Gets requested driver + * @param name case unsensitive name of driver + * @return pointer to Audio Out or NULL + */ + AudioOut *getOut(std::string name); + + /**Gets the name of the first running driver + * Deprecated + * @return if no running output, "" is returned + */ + std::string getDriver() const; + + bool setSink(std::string name); + + std::string getSink() const; + + class WavEngine * wave; /** priBuf; //buffer for primary drivers + Stereo priBuffCurrent; //current array accessor + + float *outl; + float *outr; + class Master & master; + + int stales; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/PaEngine.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/PaEngine.cpp new file mode 100644 index 000000000..d41543b04 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/PaEngine.cpp @@ -0,0 +1,118 @@ +/* + ZynAddSubFX - a software synthesizer + + PaEngine.cpp - Audio output for PortAudio + Copyright (C) 2002 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "PaEngine.h" +#include + +using namespace std; + +PaEngine::PaEngine() + :stream(NULL) +{ + name = "PA"; +} + + +PaEngine::~PaEngine() +{ + Stop(); +} + +bool PaEngine::Start() +{ + if(getAudioEn()) + return true; + Pa_Initialize(); + + PaStreamParameters outputParameters; + outputParameters.device = Pa_GetDefaultOutputDevice(); + if(outputParameters.device == paNoDevice) { + cerr << "Error: No default output device." << endl; + Pa_Terminate(); + return false; + } + outputParameters.channelCount = 2; /* stereo output */ + outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ + outputParameters.suggestedLatency = + Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + + + Pa_OpenStream(&stream, + NULL, + &outputParameters, + synth->samplerate, + synth->buffersize, + 0, + PAprocess, + (void *) this); + Pa_StartStream(stream); + return true; +} + +void PaEngine::setAudioEn(bool nval) +{ + if(nval) + Start(); + else + Stop(); +} + +bool PaEngine::getAudioEn() const +{ + return stream; +} + +int PaEngine::PAprocess(const void *inputBuffer, + void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *outTime, + PaStreamCallbackFlags flags, + void *userData) +{ + (void) inputBuffer; + (void) outTime; + (void) flags; + return static_cast(userData)->process((float *) outputBuffer, + framesPerBuffer); +} + +int PaEngine::process(float *out, unsigned long framesPerBuffer) +{ + const Stereo smp = getNext(); + for(unsigned i = 0; i < framesPerBuffer; ++i) { + *out++ = smp.l[i]; + *out++ = smp.r[i]; + } + + return 0; +} + +void PaEngine::Stop() +{ + if(!getAudioEn()) + return; + Pa_StopStream(stream); + Pa_CloseStream(stream); + stream = NULL; + Pa_Terminate(); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/PaEngine.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/PaEngine.h new file mode 100644 index 000000000..12fd766a8 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/PaEngine.h @@ -0,0 +1,57 @@ +/* + ZynAddSubFX - a software synthesizer + + PAaudiooutput.h - Audio output for PortAudio + Copyright (C) 2002 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef PA_ENGINE_H +#define PA_ENGINE_H + +#include + +#include "../globals.h" +#include "AudioOut.h" + +class PaEngine:public AudioOut +{ + public: + PaEngine(); + ~PaEngine(); + + bool Start(); + void Stop(); + + void setAudioEn(bool nval); + bool getAudioEn() const; + + protected: + static int PAprocess(const void *inputBuffer, + void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *outTime, + PaStreamCallbackFlags flags, + void *userData); + int process(float *out, unsigned long framesPerBuffer); + private: + PaStream *stream; +}; + + +void PAfinish(); + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/SafeQueue.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/SafeQueue.cpp new file mode 100644 index 000000000..25b358900 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/SafeQueue.cpp @@ -0,0 +1,95 @@ + +template +SafeQueue::SafeQueue(size_t maxlen) + :writePtr(0), readPtr(0), bufSize(maxlen) +{ + sem_init(&w_space, PTHREAD_PROCESS_PRIVATE, maxlen - 1); + sem_init(&r_space, PTHREAD_PROCESS_PRIVATE, 0); + buffer = new T[maxlen]; +} + +template +SafeQueue::~SafeQueue() +{ + sem_destroy(&w_space); + sem_destroy(&r_space); + delete [] buffer; +} + +template +unsigned int SafeQueue::size() const +{ + return rSpace(); +} + +template +unsigned int SafeQueue::rSpace() const +{ + int space = 0; + sem_getvalue(&r_space, &space); + return space; +} + +template +unsigned int SafeQueue::wSpace() const +{ + int space = 0; + sem_getvalue(&w_space, &space); + return space; +} + +template +int SafeQueue::push(const T &in) +{ + if(!wSpace()) + return -1; + + //ok, there is space to write + size_t w = (writePtr + 1) % bufSize; + buffer[w] = in; + writePtr = w; + + //adjust ranges + sem_wait(&w_space); //guaranteed not to wait + sem_post(&r_space); + return 0; +} + +template +int SafeQueue::peak(T &out) const +{ + if(!rSpace()) + return -1; + + //ok, there is space to read + size_t r = (readPtr + 1) % bufSize; + out = buffer[r]; + + return 0; +} + +template +int SafeQueue::pop(T &out) +{ + if(!rSpace()) + return -1; + + //ok, there is space to read + size_t r = (readPtr + 1) % bufSize; + out = buffer[r]; + readPtr = r; + + //adjust ranges + sem_wait(&r_space); //guaranteed not to wait + sem_post(&w_space); + return 0; +} + +template +void SafeQueue::clear() +{ + //thread unsafe + while(!sem_trywait(&r_space)) + sem_post(&w_space); + readPtr = writePtr; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/SafeQueue.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/SafeQueue.h new file mode 100644 index 000000000..d1ad1099a --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/SafeQueue.h @@ -0,0 +1,48 @@ + +#ifndef SAFEQUEUE_H +#define SAFEQUEUE_H +#include +#include +#include + +/** + * C++ thread safe lockless queue + * Based off of jack's ringbuffer*/ +template +class SafeQueue +{ + public: + SafeQueue(size_t maxlen); + ~SafeQueue(); + + /**Return read size*/ + unsigned int size() const; + + /**Returns 0 for normal + * Returns -1 on error*/ + int push(const T &in); + int peak(T &out) const; + int pop(T &out); + + //clears reading space + void clear(); + + private: + unsigned int wSpace() const; + unsigned int rSpace() const; + + //write space + mutable sem_t w_space; + //read space + mutable sem_t r_space; + + //next writing spot + size_t writePtr; + //next reading spot + size_t readPtr; + const size_t bufSize; + T *buffer; +}; + +#include "SafeQueue.cpp" +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/WavEngine.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/WavEngine.cpp new file mode 100644 index 000000000..9d0d2df09 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/WavEngine.cpp @@ -0,0 +1,134 @@ +/* + Copyright (C) 2006 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "WavEngine.h" +#include +#include +#include +#include "../Misc/WavFile.h" +#include "../Misc/Util.h" + +using namespace std; + +WavEngine::WavEngine() + :AudioOut(), file(NULL), buffer(synth->samplerate * 4), pThread(NULL) +{ + sem_init(&work, PTHREAD_PROCESS_PRIVATE, 0); +} + +WavEngine::~WavEngine() +{ + Stop(); + sem_destroy(&work); + destroyFile(); +} + +bool WavEngine::openAudio() +{ + return file && file->good(); +} + +bool WavEngine::Start() +{ + if(pThread) + return true; + pThread = new pthread_t; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_create(pThread, &attr, _AudioThread, this); + + return true; +} + +void WavEngine::Stop() +{ + if(!pThread) + return; + + pthread_t *tmp = pThread; + pThread = NULL; + + sem_post(&work); + pthread_join(*tmp, NULL); + delete pThread; +} + +void WavEngine::push(Stereo smps, size_t len) +{ + if(!pThread) + return; + + + //copy the input [overflow when needed] + for(size_t i = 0; i < len; ++i) { + buffer.push(*smps.l++); + buffer.push(*smps.r++); + } + sem_post(&work); +} + +void WavEngine::newFile(WavFile *_file) +{ + //ensure system is clean + destroyFile(); + file = _file; + + //check state + if(!file->good()) + cerr + << "ERROR: WavEngine handed bad file output WavEngine::newFile()" + << endl; +} + +void WavEngine::destroyFile() +{ + if(file) + delete file; + file = NULL; +} + +void *WavEngine::_AudioThread(void *arg) +{ + return (static_cast(arg))->AudioThread(); +} + +void *WavEngine::AudioThread() +{ + short *recordbuf_16bit = new short[2 * synth->buffersize]; + + while(!sem_wait(&work) && pThread) { + for(int i = 0; i < synth->buffersize; ++i) { + float left = 0.0f, right = 0.0f; + buffer.pop(left); + buffer.pop(right); + recordbuf_16bit[2 * i] = limit((int)(left * 32767.0f), + -32768, + 32767); + recordbuf_16bit[2 * i + 1] = limit((int)(right * 32767.0f), + -32768, + 32767); + } + file->writeStereoSamples(synth->buffersize, recordbuf_16bit); + } + + delete[] recordbuf_16bit; + + return NULL; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Nio/WavEngine.h b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/WavEngine.h new file mode 100644 index 000000000..8f55238e2 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Nio/WavEngine.h @@ -0,0 +1,61 @@ +/* + ZynAddSubFX - a software synthesizer + + WavEngine.h - Records sound to a file + Copyright (C) 2008 Nasca Octavian Paul + Author: Nasca Octavian Paul + Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef WAVENGINE_H +#define WAVENGINE_H +#include "AudioOut.h" +#include +#include +#include +#include "SafeQueue.h" + +class WavFile; +class WavEngine:public AudioOut +{ + public: + WavEngine(); + ~WavEngine(); + + bool openAudio(); + bool Start(); + void Stop(); + + void setAudioEn(bool /*nval*/) {} + bool getAudioEn() const {return true; } + + void push(Stereo smps, size_t len); + + void newFile(WavFile *_file); + void destroyFile(); + + protected: + void *AudioThread(); + static void *_AudioThread(void *arg); + + private: + WavFile *file; + sem_t work; + SafeQueue buffer; + + pthread_t *pThread; +}; +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Output/DSSIaudiooutput.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Output/DSSIaudiooutput.cpp new file mode 100644 index 000000000..b343a486c --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Output/DSSIaudiooutput.cpp @@ -0,0 +1,711 @@ +/* + ZynAddSubFX - a software synthesizer + + DSSIaudiooutput.cpp - Audio functions for DSSI + Copyright (C) 2002 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +/* + * Inital working DSSI output code contributed by Stephen G. Parry + */ + +//this file contains code used from trivial_synth.c from +//the DSSI (published by Steve Harris under public domain) as a template. + +#include "DSSIaudiooutput.h" +#include "../Misc/Config.h" +#include "../Misc/Bank.h" +#include "../Misc/Util.h" +#include +#include + +using std::string; +using std::vector; + +//Dummy variables and functions for linking purposes +const char *instance_name = 0; +class WavFile; +namespace Nio { + bool start(void){return 1;}; + void stop(void){}; + void waveNew(WavFile *){} + void waveStart(void){} + void waveStop(void){} + void waveEnd(void){} +} + +// +// Static stubs for LADSPA member functions +// +// LADSPA is essentially a C handle based API; This plug-in implementation is +// a C++ OO one so we need stub functions to map from C API calls to C++ object +// method calls. +void DSSIaudiooutput::stub_connectPort(LADSPA_Handle instance, + unsigned long port, + LADSPA_Data *data) +{ + getInstance(instance)->connectPort(port, data); +} + +void DSSIaudiooutput::stub_activate(LADSPA_Handle instance) +{ + getInstance(instance)->activate(); +} + +void DSSIaudiooutput::stub_run(LADSPA_Handle instance, + unsigned long sample_count) +{ + getInstance(instance)->run(sample_count); +} + +void DSSIaudiooutput::stub_deactivate(LADSPA_Handle instance) +{ + getInstance(instance)->deactivate(); +} + + +void DSSIaudiooutput::stub_cleanup(LADSPA_Handle instance) +{ + DSSIaudiooutput *plugin_instance = getInstance(instance); + plugin_instance->cleanup(); + delete plugin_instance; +} + + +const LADSPA_Descriptor *ladspa_descriptor(unsigned long index) +{ + return DSSIaudiooutput::getLadspaDescriptor(index); +} + +// +// Static stubs for DSSI member functions +// +// DSSI is essentially a C handle based API; This plug-in implementation is +// a C++ OO one so we need stub functions to map from C API calls to C++ object +// method calls. +const DSSI_Program_Descriptor *DSSIaudiooutput::stub_getProgram( + LADSPA_Handle instance, + unsigned long index) +{ + return getInstance(instance)->getProgram(index); +} + +void DSSIaudiooutput::stub_selectProgram(LADSPA_Handle instance, + unsigned long bank, + unsigned long program) +{ + getInstance(instance)->selectProgram(bank, program); +} + +int DSSIaudiooutput::stub_getMidiControllerForPort(LADSPA_Handle instance, + unsigned long port) +{ + return getInstance(instance)->getMidiControllerForPort(port); +} + +void DSSIaudiooutput::stub_runSynth(LADSPA_Handle instance, + unsigned long sample_count, + snd_seq_event_t *events, + unsigned long event_count) +{ + getInstance(instance)->runSynth(sample_count, events, event_count); +} + +const DSSI_Descriptor *dssi_descriptor(unsigned long index) +{ + return DSSIaudiooutput::getDssiDescriptor(index); +} + +// +// LADSPA member functions +// + +/** + * Instantiates a plug-in. + * + * This LADSPA member function instantiates a plug-in. + * Note that instance initialisation should generally occur in + * activate() rather than here. + * + * Zyn Implementation + * ------------------ + * This implementation creates a C++ class object and hides its pointer + * in the handle by type casting. + * + * @param descriptor [in] the descriptor for this plug-in + * @param s_rate [in] the sample rate + * @return the plug-in instance handle if successful else NULL + */ +LADSPA_Handle DSSIaudiooutput::instantiate(const LADSPA_Descriptor *descriptor, + unsigned long s_rate) +{ + if(descriptor->UniqueID == dssiDescriptor->LADSPA_Plugin->UniqueID) + return (LADSPA_Handle)(new DSSIaudiooutput(s_rate)); + else + return NULL; +} + +/** + * Connects a port on an instantiated plug-in. + * + * This LADSPA member function connects a port on an instantiated plug-in to a + * memory location at which a block of data for the port will be read/written. + * The data location is expected to be an array of LADSPA_Data for audio ports + * or a single LADSPA_Data value for control ports. Memory issues will be + * managed by the host. The plug-in must read/write the data at these locations + * every time run() or run_adding() is called and the data present at the time + * of this connection call should not be considered meaningful. + * + * Zyn Implementation + * ------------------ + * The buffer pointers are stored as member variables + * + * @param port [in] the port to be connected + * @param data [in] the data buffer to write to / read from + */ +void DSSIaudiooutput::connectPort(unsigned long port, LADSPA_Data *data) +{ + switch(port) { + case 0: + outl = data; + break; + case 1: + outr = data; + break; + } +} + +/** + * Initialises a plug-in instance and activates it for use. + * + * This LADSPA member function initialises a plug-in instance and activates it + * for use. This is separated from instantiate() to aid real-time support and + * so that hosts can reinitialise a plug-in instance by calling deactivate() and + * then activate(). In this case the plug-in instance must reset all state + * information dependent on the history of the plug-in instance except for any + * data locations provided by connect_port() and any gain set by + * set_run_adding_gain(). + * + * Zyn Implementation + * ------------------ + * Currently this does nothing; Care must be taken as to code placed here as + * too much code here seems to cause time-out problems in jack-dssi-host. +*/ +void DSSIaudiooutput::activate() +{} + +/** + * Runs an instance of a plug-in for a block. + * + * This LADSPA member function runs an instance of a plug-in for a block. + * Note that if an activate() function exists then it must be called before + * run() or run_adding(). If deactivate() is called for a plug-in instance then + * the plug-in instance may not be reused until activate() has been called again. + * + * Zyn Implementation + * ------------------ + * This is a LADSPA function that does not process any MIDI events; it is hence + * implemented by simply calling runSynth() with an empty event list. + * + * @param sample_count [in] the block size (in samples) for which the plug-in instance may run + */ +void DSSIaudiooutput::run(unsigned long sample_count) +{ + runSynth(sample_count, NULL, (unsigned long)0); +} + +/** + * Counterpart to activate(). + * + * This LADSPA member function is the counterpart to activate() (see above). + * Deactivation is not similar to pausing as the plug-in instance will be + * reinitialised when activate() is called to reuse it. + * + * Zyn Implementation + * ------------------ + * Currently this function does nothing. + */ +void DSSIaudiooutput::deactivate() +{} + +/** + * Deletes a plug-in instance that is no longer required. + * + * LADSPA member function; once an instance of a plug-in has been finished with + * it can be deleted using this function. The instance handle ceases to be + * valid after this call. + * + * If activate() was called for a plug-in instance then a corresponding call to + * deactivate() must be made before cleanup() is called. + * + * Zyn Implementation + * ------------------ + * Currently cleanup is deferred to the destructor that is invoked after cleanup() + */ +void DSSIaudiooutput::cleanup() +{} + +/** + * Initial entry point for the LADSPA plug-in library. + * + * This LADSPA function is the initial entry point for the plug-in library. + * The LADSPA host looks for this entry point in each shared library object it + * finds and then calls the function to enumerate the plug-ins within the + * library. + * + * Zyn Implementation + * ------------------ + * As the Zyn plug-in is a DSSI plug-in, the LADSPA descriptor is embedded inside + * the DSSI descriptor, which is created by DSSIaudiooutput::initDssiDescriptor() + * statically when the library is loaded. This function then merely returns a pointer + * to that embedded descriptor. + * + * @param index [in] the index number of the plug-in within the library. + * @return if index is in range, a pointer to the plug-in descriptor is returned, else NULL + */ +const LADSPA_Descriptor *DSSIaudiooutput::getLadspaDescriptor( + unsigned long index) +{ + if((index > 0) || (dssiDescriptor == NULL)) + return NULL; + else + return dssiDescriptor->LADSPA_Plugin; +} + +// +// DSSI member functions +// + +/** + * Provides a description of a program available on this synth. + * + * This DSSI member function pointer provides a description of a program (named + * preset sound) available on this synth. + * + * Zyn Implementation + * ------------------ + * The instruments in all Zyn's bank directories, as shown by the `instrument + * -> show instrument bank` command, are enumerated to the host by this + * function, allowing access to all those instruments. + * The first time an instrument is requested, the bank it is in and any + * unmapped ones preceding that are mapped; all the instruments names and + * filenames from those banks are stored in the programMap member variable for + * later use. This is done on demand in this way, rather than up front in one + * go because loading all the instrument names in one go can lead to timeouts + * and zombies. + * + * @param index [in] index into the plug-in's list of + * programs, not a program number as represented by the Program + * field of the DSSI_Program_Descriptor. (This distinction is + * needed to support synths that use non-contiguous program or + * bank numbers.) + * @return a DSSI_Program_Descriptor pointer that is + * guaranteed to be valid only until the next call to get_program, + * deactivate, or configure, on the same plug-in instance, or NULL if index is out of range. + */ +const DSSI_Program_Descriptor *DSSIaudiooutput::getProgram(unsigned long index) +{ + static DSSI_Program_Descriptor retVal; + + /* Make sure we have the list of banks loaded */ + initBanks(); + + /* Make sure that the bank containing the instrument has been mapped */ + while(index >= programMap.size() && mapNextBank()) + /* DO NOTHING MORE */; + + if(index >= programMap.size()) + /* No more instruments */ + return NULL; + else { + /* OK, return the instrument */ + retVal.Name = programMap[index].name.c_str(); + retVal.Program = programMap[index].program; + retVal.Bank = programMap[index].bank; + return &retVal; + } +} + +/** + * Selects a new program for this synth. + * + * This DSSI member function selects a new program for this synth. The program + * change will take effect immediately at the start of the next run_synth() + * call. An invalid bank / instrument combination is ignored. + * + * Zyn Implementation + * ------------------ + * the banks and instruments are as shown in the `instrument -> show instrument + * bank` command in Zyn. The bank no is a 1-based index into the list of banks + * Zyn loads and shows in the drop down and the program number is the + * instrument within that bank. + * + * @param bank [in] the bank number to select + * @param program [in] the program number within the bank to select + */ +void DSSIaudiooutput::selectProgram(unsigned long bank, unsigned long program) +{ + initBanks(); +// cerr << "selectProgram(" << (bank & 0x7F) << ':' << ((bank >> 7) & 0x7F) << "," << program << ")" << '\n'; + if((bank < master->bank.banks.size()) && (program < BANK_SIZE)) { + const std::string bankdir = master->bank.banks[bank].dir; + if(!bankdir.empty()) { + pthread_mutex_lock(&master->mutex); + + /* We have to turn off the CheckPADsynth functionality, else + * the program change takes way too long and we get timeouts + * and hence zombies (!) */ + int save = config.cfg.CheckPADsynth; + config.cfg.CheckPADsynth = 0; + + /* Load the bank... */ + master->bank.loadbank(bankdir); + + /* restore the CheckPADsynth flag */ + config.cfg.CheckPADsynth = save; + + /* Now load the instrument... */ + master->bank.loadfromslot((unsigned int)program, master->part[0]); + + pthread_mutex_unlock(&master->mutex); + } + } +} + +/** + * Returns the MIDI controller number or NRPN for a input control port + * + * This DSSI member function returns the MIDI controller number or NRPN that + * should be mapped to the given input control port. If the given port should + * not have any MIDI controller mapped to it, the function will return DSSI_NONE. + * The behaviour of this function is undefined if the given port + * number does not correspond to an input control port. + * + * Zyn Implementation + * ------------------ + * Currently Zyn does not define any controller ports, but may do in the future. + * + * @param port [in] the input controller port + * @return the CC and NRPN values shifted and ORed together. + */ +int DSSIaudiooutput::getMidiControllerForPort(unsigned long /*port*/) +{ + return DSSI_NONE; +} + +/** + * Runs the synth for a block. + * + * This DSSI member function runs the synth for a block. This is identical in + * function to the LADSPA run() function, except that it also supplies events + * to the synth. + * + * Zyn Implementation + * ------------------ + * Zyn implements synthesis in Master::GetAudioOutSamples; runSynth calls this + * function in chunks delimited by the sample_count and the frame indexes in + * the events block, calling the appropriate NoteOn, NoteOff and SetController + * members of Master to process the events supplied between each chunk. + * + * @param sample_count [in] the block size (in samples) for which the synth + * instance may run. + * @param events [in] The Events pointer points to a block of ALSA + * sequencer events, used to communicate MIDI and related events to the synth. + * Each event must be timestamped relative to the start of the block, + * (mis)using the ALSA "tick time" field as a frame count. The host is + * responsible for ensuring that events with differing timestamps are already + * ordered by time. Must not include NOTE (only NOTE_ON / NOTE_OFF), LSB or MSB + * events. + * @param event_count [in] the number of entries in the `events` block + */ +void DSSIaudiooutput::runSynth(unsigned long sample_count, + snd_seq_event_t *events, + unsigned long event_count) +{ + unsigned long from_frame = 0; + unsigned long event_index = 0; + unsigned long next_event_frame = 0; + unsigned long to_frame = 0; + pthread_mutex_lock(&master->mutex); + + do { + /* Find the time of the next event, if any */ + if((events == NULL) || (event_index >= event_count)) + next_event_frame = ULONG_MAX; + else + next_event_frame = events[event_index].time.tick; + + /* find the end of the sub-sample to be processed this time round... */ + /* if the next event falls within the desired sample interval... */ + if((next_event_frame < sample_count) && (next_event_frame >= to_frame)) + /* set the end to be at that event */ + to_frame = next_event_frame; + else + /* ...else go for the whole remaining sample */ + to_frame = sample_count; + if(from_frame < to_frame) { + // call master to fill from `from_frame` to `to_frame`: + master->GetAudioOutSamples(to_frame - from_frame, + (int)sampleRate, + &(outl[from_frame]), + &(outr[from_frame])); + // next sub-sample please... + from_frame = to_frame; + } + + // Now process any event(s) at the current timing point + while(events != NULL && event_index < event_count + && events[event_index].time.tick == to_frame) { + if(events[event_index].type == SND_SEQ_EVENT_NOTEON) + master->noteOn(events[event_index].data.note.channel, + events[event_index].data.note.note, + events[event_index].data.note.velocity); + else + if(events[event_index].type == SND_SEQ_EVENT_NOTEOFF) + master->noteOff(events[event_index].data.note.channel, + events[event_index].data.note.note); + else + if(events[event_index].type == SND_SEQ_EVENT_CONTROLLER) + master->setController(events[event_index].data.control.channel, + events[event_index].data.control.param, + events[event_index].data.control.value); + else {} + event_index++; + } + + // Keep going until we have the desired total length of sample... + } while(to_frame < sample_count); + + pthread_mutex_unlock(&master->mutex); +} + +/** + * Initial entry point for the DSSI plug-in library. + * + * This DSSI function is the initial entry point for the plug-in library. + * The DSSI host looks for this entry point in each shared library object it + * finds and then calls the function to enumerate the plug-ins within the + * library. + * + * Zyn Implementation + * ------------------ + * The descriptor is created statically by DSSIaudiooutput::initDssiDescriptor() + * when the plug-in library is loaded. This function merely returns a pointer to + * that descriptor. + * + * @param index [in] the index number of the plug-in within the library. + * @return if index is in range, a pointer to the plug-in descriptor is returned, else NULL + */ +const DSSI_Descriptor *DSSIaudiooutput::getDssiDescriptor(unsigned long index) +{ + if((index > 0) || (dssiDescriptor == NULL)) + return NULL; + else + return dssiDescriptor; +} + +// +// Internal member functions +// + +// Initialise the DSSI descriptor, statically: +DSSI_Descriptor *DSSIaudiooutput::dssiDescriptor = + DSSIaudiooutput::initDssiDescriptor(); + +/** + * Initializes the DSSI (and LADSPA) descriptor, returning it is an object. + */ +DSSI_Descriptor *DSSIaudiooutput::initDssiDescriptor() +{ + DSSI_Descriptor *newDssiDescriptor = new DSSI_Descriptor; + + LADSPA_PortDescriptor *newPortDescriptors; + const char **newPortNames; + LADSPA_PortRangeHint *newPortRangeHints; + + if(newDssiDescriptor) { + LADSPA_Descriptor *newLadspaDescriptor = new LADSPA_Descriptor; + if(newLadspaDescriptor) { + newLadspaDescriptor->UniqueID = 100; + newLadspaDescriptor->Label = "ZASF"; + newLadspaDescriptor->Properties = 0; + newLadspaDescriptor->Name = "ZynAddSubFX"; + newLadspaDescriptor->Maker = + "Nasca Octavian Paul "; + newLadspaDescriptor->Copyright = "GNU General Public License v.2"; + newLadspaDescriptor->PortCount = 2; + + newPortNames = new const char *[newLadspaDescriptor->PortCount]; + newPortNames[0] = "Output L"; + newPortNames[1] = "Output R"; + newLadspaDescriptor->PortNames = newPortNames; + + newPortDescriptors = + new LADSPA_PortDescriptor[newLadspaDescriptor->PortCount]; + newPortDescriptors[0] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + newPortDescriptors[1] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + newLadspaDescriptor->PortDescriptors = newPortDescriptors; + + newPortRangeHints = + new LADSPA_PortRangeHint[newLadspaDescriptor->PortCount]; + newPortRangeHints[0].HintDescriptor = 0; + newPortRangeHints[1].HintDescriptor = 0; + newLadspaDescriptor->PortRangeHints = newPortRangeHints; + + newLadspaDescriptor->activate = stub_activate; + newLadspaDescriptor->cleanup = stub_cleanup; + newLadspaDescriptor->connect_port = stub_connectPort; + newLadspaDescriptor->deactivate = stub_deactivate; + newLadspaDescriptor->instantiate = instantiate; + newLadspaDescriptor->run = stub_run; + newLadspaDescriptor->run_adding = NULL; + newLadspaDescriptor->set_run_adding_gain = NULL; + } + newDssiDescriptor->LADSPA_Plugin = newLadspaDescriptor; + newDssiDescriptor->DSSI_API_Version = 1; + newDssiDescriptor->configure = NULL; + newDssiDescriptor->get_program = stub_getProgram; + newDssiDescriptor->get_midi_controller_for_port = + stub_getMidiControllerForPort; + newDssiDescriptor->select_program = stub_selectProgram; + newDssiDescriptor->run_synth = stub_runSynth; + newDssiDescriptor->run_synth_adding = NULL; + newDssiDescriptor->run_multiple_synths = NULL; + newDssiDescriptor->run_multiple_synths_adding = NULL; + } + + dssiDescriptor = newDssiDescriptor; + + return dssiDescriptor; +} + +/** + * Converts a LADSPA / DSSI handle into a DSSIaudiooutput instance. + * + * @param instance [in] + * @return the instance + */ +DSSIaudiooutput *DSSIaudiooutput::getInstance(LADSPA_Handle instance) +{ + return (DSSIaudiooutput *)(instance); +} + +SYNTH_T *synth; + +/** + * The private sole constructor for the DSSIaudiooutput class. + * + * Only ever called via instantiate(). + * @param sampleRate [in] the sample rate to be used by the synth. + * @return + */ +DSSIaudiooutput::DSSIaudiooutput(unsigned long sampleRate) +{ + synth = new SYNTH_T; + synth->samplerate = sampleRate; + + this->sampleRate = sampleRate; + this->banksInited = false; + + config.init(); + + sprng(time(NULL)); + denormalkillbuf = new float [synth->buffersize]; + for(int i = 0; i < synth->buffersize; i++) + denormalkillbuf[i] = (RND - 0.5f) * 1e-16; + + synth->alias(); + this->master = new Master(); +} + +/** + * The destructor for the DSSIaudiooutput class + * @return + */ +DSSIaudiooutput::~DSSIaudiooutput() +{} + +/** + * Ensures the list of bank (directories) has been initialised. + */ +void DSSIaudiooutput::initBanks(void) +{ + if(!banksInited) { + pthread_mutex_lock(&master->mutex); + master->bank.rescanforbanks(); + banksInited = true; + pthread_mutex_unlock(&master->mutex); + } +} + +/** + * constructor for the internally used ProgramDescriptor class + * + * @param _bank [in] bank number + * @param _program [in] program number + * @param _name [in] instrument / sample name + * @return + */ +DSSIaudiooutput::ProgramDescriptor::ProgramDescriptor(unsigned long _bank, + unsigned long _program, + char *_name) + :bank(_bank), program(_program), name(_name) +{} + +/** + * The map of programs available; held as a single shared statically allocated object. + */ +vector DSSIaudiooutput::programMap = + vector(); + +/** + * Index controlling the map of banks + */ +long DSSIaudiooutput::bankNoToMap = 1; + +/** + * Queries and maps the next available bank of instruments. + * + * If the program index requested to getProgram() lies beyond the banks mapped to date, + * this member function is called to map the next one. + * @return true if a new bank has been found and mapped, else false. + */ +bool DSSIaudiooutput::mapNextBank() +{ + pthread_mutex_lock(&master->mutex); + Bank &bank = master->bank; + bool retval; + if((bankNoToMap >= (int)bank.banks.size()) + || bank.banks[bankNoToMap].dir.empty()) + retval = false; + else { + bank.loadbank(bank.banks[bankNoToMap].dir); + for(unsigned long instrument = 0; instrument < BANK_SIZE; + ++instrument) { + string insName = bank.getname(instrument); + if(!insName.empty() && (insName[0] != '\0') && (insName[0] != ' ')) + programMap.push_back(ProgramDescriptor(bankNoToMap, instrument, + const_cast( + insName.c_str()))); + } + bankNoToMap++; + retval = true; + } + pthread_mutex_unlock(&master->mutex); + return retval; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Output/DSSIaudiooutput.h b/plugins/zynaddsubfx/zynaddsubfx/src/Output/DSSIaudiooutput.h new file mode 100644 index 000000000..f7eaaf359 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Output/DSSIaudiooutput.h @@ -0,0 +1,123 @@ +/* + ZynAddSubFX - a software synthesizer + + VSTaudiooutput.h - Audio output for VST + Copyright (C) 2002 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef VST_AUDIO_OUTPUT_H +#define VST_AUDIO_OUTPUT_H + +#include + +#include "../globals.h" +#include "../Misc/Master.h" + +#include +#include +#include + +class DSSIaudiooutput +{ + public: + // + // Static stubs for LADSPA member functions + // + static void stub_connectPort(LADSPA_Handle instance, + unsigned long port, + LADSPA_Data *data); + static void stub_activate(LADSPA_Handle instance); + static void stub_run(LADSPA_Handle instance, unsigned long sample_count); + static void stub_deactivate(LADSPA_Handle Instance); + static void stub_cleanup(LADSPA_Handle instance); + + // + // Static stubs for DSSI member functions + // + static const DSSI_Program_Descriptor *stub_getProgram( + LADSPA_Handle instance, + unsigned long Index); + static void stub_selectProgram(LADSPA_Handle instance, + unsigned long bank, + unsigned long program); + static int stub_getMidiControllerForPort(LADSPA_Handle instance, + unsigned long port); + static void stub_runSynth(LADSPA_Handle instance, + unsigned long sample_count, + snd_seq_event_t *events, + unsigned long event_count); + + /* + * LADSPA member functions + */ + static LADSPA_Handle instantiate(const LADSPA_Descriptor *descriptor, + unsigned long s_rate); + void connectPort(unsigned long port, LADSPA_Data *data); + void activate(); + void run(unsigned long sample_count); + void deactivate(); + void cleanup(); + static const LADSPA_Descriptor *getLadspaDescriptor(unsigned long index); + + /* + * DSSI member functions + */ + const DSSI_Program_Descriptor *getProgram(unsigned long Index); + void selectProgram(unsigned long bank, unsigned long program); + int getMidiControllerForPort(unsigned long port); + void runSynth(unsigned long sample_count, + snd_seq_event_t *events, + unsigned long event_count); + static const DSSI_Descriptor *getDssiDescriptor(unsigned long index); + + struct ProgramDescriptor { + unsigned long bank; + unsigned long program; + std::string name; + ProgramDescriptor(unsigned long _bank, + unsigned long _program, + char *_name); + }; + + private: + + DSSIaudiooutput(unsigned long sampleRate); + ~DSSIaudiooutput(); + static DSSI_Descriptor *initDssiDescriptor(); + static DSSIaudiooutput *getInstance(LADSPA_Handle instance); + void initBanks(); + bool mapNextBank(); + + LADSPA_Data *outl; + LADSPA_Data *outr; + long sampleRate; + Master *master; + static DSSI_Descriptor *dssiDescriptor; + static std::string bankDirNames[]; + static + std::vector programMap; + + /** + * Flag controlling the list of bank directories + */ + bool banksInited; + + static + long bankNoToMap; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/ADnoteParameters.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Params/ADnoteParameters.cpp new file mode 100644 index 000000000..9ebdcc681 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/ADnoteParameters.cpp @@ -0,0 +1,792 @@ +/* + ZynAddSubFX - a software synthesizer + + ADnoteParameters.cpp - Parameters for ADnote (ADsynth) + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include + +#include "ADnoteParameters.h" +#include "EnvelopeParams.h" +#include "LFOParams.h" +#include "../Misc/XMLwrapper.h" +#include "../DSP/FFTwrapper.h" +#include "../Synth/OscilGen.h" +#include "../Synth/Resonance.h" +#include "FilterParams.h" + +int ADnote_unison_sizes[] = +{1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 25, 30, 40, 50, 0}; + +ADnoteParameters::ADnoteParameters(FFTwrapper *fft_) + :PresetsArray() +{ + setpresettype("Padsynth"); + fft = fft_; + + + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) + EnableVoice(nvoice); + + defaults(); +} + +ADnoteGlobalParam::ADnoteGlobalParam() +{ + FreqEnvelope = new EnvelopeParams(0, 0); + FreqEnvelope->ASRinit(64, 50, 64, 60); + FreqLfo = new LFOParams(70, 0, 64, 0, 0, 0, 0, 0); + + AmpEnvelope = new EnvelopeParams(64, 1); + AmpEnvelope->ADSRinit_dB(0, 40, 127, 25); + AmpLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 1); + + GlobalFilter = new FilterParams(2, 94, 40); + FilterEnvelope = new EnvelopeParams(0, 1); + FilterEnvelope->ADSRinit_filter(64, 40, 64, 70, 60, 64); + FilterLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 2); + Reson = new Resonance(); +} + +void ADnoteParameters::defaults() +{ + //Default Parameters + GlobalPar.defaults(); + + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) + defaults(nvoice); + + VoicePar[0].Enabled = 1; +} + +void ADnoteGlobalParam::defaults() +{ + /* Frequency Global Parameters */ + PStereo = 1; //stereo + PDetune = 8192; //zero + PCoarseDetune = 0; + PDetuneType = 1; + FreqEnvelope->defaults(); + FreqLfo->defaults(); + PBandwidth = 64; + + /* Amplitude Global Parameters */ + PVolume = 90; + PPanning = 64; //center + PAmpVelocityScaleFunction = 64; + AmpEnvelope->defaults(); + AmpLfo->defaults(); + PPunchStrength = 0; + PPunchTime = 60; + PPunchStretch = 64; + PPunchVelocitySensing = 72; + Hrandgrouping = 0; + + /* Filter Global Parameters*/ + PFilterVelocityScale = 64; + PFilterVelocityScaleFunction = 64; + GlobalFilter->defaults(); + FilterEnvelope->defaults(); + FilterLfo->defaults(); + Reson->defaults(); +} + +/* + * Defaults a voice + */ +void ADnoteParameters::defaults(int n) +{ + VoicePar[n].defaults(); +} + +void ADnoteVoiceParam::defaults() +{ + Enabled = 0; + + Unison_size = 1; + Unison_frequency_spread = 60; + Unison_stereo_spread = 64; + Unison_vibratto = 64; + Unison_vibratto_speed = 64; + Unison_invert_phase = 0; + + Type = 0; + Pfixedfreq = 0; + PfixedfreqET = 0; + Presonance = 1; + Pfilterbypass = 0; + Pextoscil = -1; + PextFMoscil = -1; + Poscilphase = 64; + PFMoscilphase = 64; + PDelay = 0; + PVolume = 100; + PVolumeminus = 0; + PPanning = 64; //center + PDetune = 8192; //8192=0 + PCoarseDetune = 0; + PDetuneType = 0; + PFreqLfoEnabled = 0; + PFreqEnvelopeEnabled = 0; + PAmpEnvelopeEnabled = 0; + PAmpLfoEnabled = 0; + PAmpVelocityScaleFunction = 127; + PFilterEnabled = 0; + PFilterEnvelopeEnabled = 0; + PFilterLfoEnabled = 0; + PFMEnabled = 0; + + //I use the internal oscillator (-1) + PFMVoice = -1; + + PFMVolume = 90; + PFMVolumeDamp = 64; + PFMDetune = 8192; + PFMCoarseDetune = 0; + PFMDetuneType = 0; + PFMFreqEnvelopeEnabled = 0; + PFMAmpEnvelopeEnabled = 0; + PFMVelocityScaleFunction = 64; + + OscilSmp->defaults(); + FMSmp->defaults(); + + AmpEnvelope->defaults(); + AmpLfo->defaults(); + + FreqEnvelope->defaults(); + FreqLfo->defaults(); + + VoiceFilter->defaults(); + FilterEnvelope->defaults(); + FilterLfo->defaults(); + + FMFreqEnvelope->defaults(); + FMAmpEnvelope->defaults(); +} + + + +/* + * Init the voice parameters + */ +void ADnoteParameters::EnableVoice(int nvoice) +{ + VoicePar[nvoice].enable(fft, GlobalPar.Reson); +} + +void ADnoteVoiceParam::enable(FFTwrapper *fft, Resonance *Reson) +{ + OscilSmp = new OscilGen(fft, Reson); + FMSmp = new OscilGen(fft, NULL); + + AmpEnvelope = new EnvelopeParams(64, 1); + AmpEnvelope->ADSRinit_dB(0, 100, 127, 100); + AmpLfo = new LFOParams(90, 32, 64, 0, 0, 30, 0, 1); + + FreqEnvelope = new EnvelopeParams(0, 0); + FreqEnvelope->ASRinit(30, 40, 64, 60); + FreqLfo = new LFOParams(50, 40, 0, 0, 0, 0, 0, 0); + + VoiceFilter = new FilterParams(2, 50, 60); + FilterEnvelope = new EnvelopeParams(0, 0); + FilterEnvelope->ADSRinit_filter(90, 70, 40, 70, 10, 40); + FilterLfo = new LFOParams(50, 20, 64, 0, 0, 0, 0, 2); + + FMFreqEnvelope = new EnvelopeParams(0, 0); + FMFreqEnvelope->ASRinit(20, 90, 40, 80); + FMAmpEnvelope = new EnvelopeParams(64, 1); + FMAmpEnvelope->ADSRinit(80, 90, 127, 100); +} + +/* + * Get the Multiplier of the fine detunes of the voices + */ +float ADnoteParameters::getBandwidthDetuneMultiplier() +{ + float bw = (GlobalPar.PBandwidth - 64.0f) / 64.0f; + bw = powf(2.0f, bw * powf(fabs(bw), 0.2f) * 5.0f); + + return bw; +} + +/* + * Get the unison spread in cents for a voice + */ + +float ADnoteParameters::getUnisonFrequencySpreadCents(int nvoice) { + float unison_spread = VoicePar[nvoice].Unison_frequency_spread / 127.0f; + unison_spread = powf(unison_spread * 2.0f, 2.0f) * 50.0f; //cents + return unison_spread; +} + +/* + * Kill the voice + */ +void ADnoteParameters::KillVoice(int nvoice) +{ + VoicePar[nvoice].kill(); +} + +void ADnoteVoiceParam::kill() +{ + delete OscilSmp; + delete FMSmp; + + delete AmpEnvelope; + delete AmpLfo; + + delete FreqEnvelope; + delete FreqLfo; + + delete VoiceFilter; + delete FilterEnvelope; + delete FilterLfo; + + delete FMFreqEnvelope; + delete FMAmpEnvelope; +} + + +ADnoteGlobalParam::~ADnoteGlobalParam() +{ + delete FreqEnvelope; + delete FreqLfo; + delete AmpEnvelope; + delete AmpLfo; + delete GlobalFilter; + delete FilterEnvelope; + delete FilterLfo; + delete Reson; +} + +ADnoteParameters::~ADnoteParameters() +{ + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) + KillVoice(nvoice); +} + +int ADnoteParameters::get_unison_size_index(int nvoice) { + int index = 0; + if(nvoice >= NUM_VOICES) + return 0; + int unison = VoicePar[nvoice].Unison_size; + + while(1) { + if(ADnote_unison_sizes[index] >= unison) + return index; + + if(ADnote_unison_sizes[index] == 0) + return index - 1; + + index++; + } + return 0; +} + +void ADnoteParameters::set_unison_size_index(int nvoice, int index) { + int unison = 1; + for(int i = 0; i <= index; ++i) { + unison = ADnote_unison_sizes[i]; + if(unison == 0) { + unison = ADnote_unison_sizes[i - 1]; + break; + } + } + + VoicePar[nvoice].Unison_size = unison; +} + + + +void ADnoteParameters::add2XMLsection(XMLwrapper *xml, int n) +{ + int nvoice = n; + if(nvoice >= NUM_VOICES) + return; + + int oscilused = 0, fmoscilused = 0; //if the oscil or fmoscil are used by another voice + + for(int i = 0; i < NUM_VOICES; ++i) { + if(VoicePar[i].Pextoscil == nvoice) + oscilused = 1; + if(VoicePar[i].PextFMoscil == nvoice) + fmoscilused = 1; + } + + xml->addparbool("enabled", VoicePar[nvoice].Enabled); + if(((VoicePar[nvoice].Enabled == 0) && (oscilused == 0) + && (fmoscilused == 0)) && (xml->minimal)) + return; + + VoicePar[nvoice].add2XML(xml, fmoscilused); +} + +void ADnoteVoiceParam::add2XML(XMLwrapper *xml, bool fmoscilused) +{ + xml->addpar("type", Type); + + xml->addpar("unison_size", Unison_size); + xml->addpar("unison_frequency_spread", + Unison_frequency_spread); + xml->addpar("unison_stereo_spread", Unison_stereo_spread); + xml->addpar("unison_vibratto", Unison_vibratto); + xml->addpar("unison_vibratto_speed", Unison_vibratto_speed); + xml->addpar("unison_invert_phase", Unison_invert_phase); + + xml->addpar("delay", PDelay); + xml->addparbool("resonance", Presonance); + + xml->addpar("ext_oscil", Pextoscil); + xml->addpar("ext_fm_oscil", PextFMoscil); + + xml->addpar("oscil_phase", Poscilphase); + xml->addpar("oscil_fm_phase", PFMoscilphase); + + xml->addparbool("filter_enabled", PFilterEnabled); + xml->addparbool("filter_bypass", Pfilterbypass); + + xml->addpar("fm_enabled", PFMEnabled); + + xml->beginbranch("OSCIL"); + OscilSmp->add2XML(xml); + xml->endbranch(); + + + xml->beginbranch("AMPLITUDE_PARAMETERS"); + xml->addpar("panning", PPanning); + xml->addpar("volume", PVolume); + xml->addparbool("volume_minus", PVolumeminus); + xml->addpar("velocity_sensing", PAmpVelocityScaleFunction); + + xml->addparbool("amp_envelope_enabled", + PAmpEnvelopeEnabled); + if((PAmpEnvelopeEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("AMPLITUDE_ENVELOPE"); + AmpEnvelope->add2XML(xml); + xml->endbranch(); + } + xml->addparbool("amp_lfo_enabled", PAmpLfoEnabled); + if((PAmpLfoEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("AMPLITUDE_LFO"); + AmpLfo->add2XML(xml); + xml->endbranch(); + } + xml->endbranch(); + + xml->beginbranch("FREQUENCY_PARAMETERS"); + xml->addparbool("fixed_freq", Pfixedfreq); + xml->addpar("fixed_freq_et", PfixedfreqET); + xml->addpar("detune", PDetune); + xml->addpar("coarse_detune", PCoarseDetune); + xml->addpar("detune_type", PDetuneType); + + xml->addparbool("freq_envelope_enabled", + PFreqEnvelopeEnabled); + if((PFreqEnvelopeEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("FREQUENCY_ENVELOPE"); + FreqEnvelope->add2XML(xml); + xml->endbranch(); + } + xml->addparbool("freq_lfo_enabled", PFreqLfoEnabled); + if((PFreqLfoEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("FREQUENCY_LFO"); + FreqLfo->add2XML(xml); + xml->endbranch(); + } + xml->endbranch(); + + + if((PFilterEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("FILTER_PARAMETERS"); + xml->beginbranch("FILTER"); + VoiceFilter->add2XML(xml); + xml->endbranch(); + + xml->addparbool("filter_envelope_enabled", + PFilterEnvelopeEnabled); + if((PFilterEnvelopeEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("FILTER_ENVELOPE"); + FilterEnvelope->add2XML(xml); + xml->endbranch(); + } + + xml->addparbool("filter_lfo_enabled", + PFilterLfoEnabled); + if((PFilterLfoEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("FILTER_LFO"); + FilterLfo->add2XML(xml); + xml->endbranch(); + } + xml->endbranch(); + } + + if((PFMEnabled != 0) || (fmoscilused != 0) + || (!xml->minimal)) { + xml->beginbranch("FM_PARAMETERS"); + xml->addpar("input_voice", PFMVoice); + + xml->addpar("volume", PFMVolume); + xml->addpar("volume_damp", PFMVolumeDamp); + xml->addpar("velocity_sensing", + PFMVelocityScaleFunction); + + xml->addparbool("amp_envelope_enabled", + PFMAmpEnvelopeEnabled); + if((PFMAmpEnvelopeEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("AMPLITUDE_ENVELOPE"); + FMAmpEnvelope->add2XML(xml); + xml->endbranch(); + } + xml->beginbranch("MODULATOR"); + xml->addpar("detune", PFMDetune); + xml->addpar("coarse_detune", PFMCoarseDetune); + xml->addpar("detune_type", PFMDetuneType); + + xml->addparbool("freq_envelope_enabled", + PFMFreqEnvelopeEnabled); + if((PFMFreqEnvelopeEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("FREQUENCY_ENVELOPE"); + FMFreqEnvelope->add2XML(xml); + xml->endbranch(); + } + + xml->beginbranch("OSCIL"); + FMSmp->add2XML(xml); + xml->endbranch(); + + xml->endbranch(); + xml->endbranch(); + } +} + +void ADnoteGlobalParam::add2XML(XMLwrapper *xml) +{ + xml->addparbool("stereo", PStereo); + + xml->beginbranch("AMPLITUDE_PARAMETERS"); + xml->addpar("volume", PVolume); + xml->addpar("panning", PPanning); + xml->addpar("velocity_sensing", PAmpVelocityScaleFunction); + xml->addpar("punch_strength", PPunchStrength); + xml->addpar("punch_time", PPunchTime); + xml->addpar("punch_stretch", PPunchStretch); + xml->addpar("punch_velocity_sensing", PPunchVelocitySensing); + xml->addpar("harmonic_randomness_grouping", Hrandgrouping); + + xml->beginbranch("AMPLITUDE_ENVELOPE"); + AmpEnvelope->add2XML(xml); + xml->endbranch(); + + xml->beginbranch("AMPLITUDE_LFO"); + AmpLfo->add2XML(xml); + xml->endbranch(); + xml->endbranch(); + + xml->beginbranch("FREQUENCY_PARAMETERS"); + xml->addpar("detune", PDetune); + + xml->addpar("coarse_detune", PCoarseDetune); + xml->addpar("detune_type", PDetuneType); + + xml->addpar("bandwidth", PBandwidth); + + xml->beginbranch("FREQUENCY_ENVELOPE"); + FreqEnvelope->add2XML(xml); + xml->endbranch(); + + xml->beginbranch("FREQUENCY_LFO"); + FreqLfo->add2XML(xml); + xml->endbranch(); + xml->endbranch(); + + + xml->beginbranch("FILTER_PARAMETERS"); + xml->addpar("velocity_sensing_amplitude", PFilterVelocityScale); + xml->addpar("velocity_sensing", PFilterVelocityScaleFunction); + + xml->beginbranch("FILTER"); + GlobalFilter->add2XML(xml); + xml->endbranch(); + + xml->beginbranch("FILTER_ENVELOPE"); + FilterEnvelope->add2XML(xml); + xml->endbranch(); + + xml->beginbranch("FILTER_LFO"); + FilterLfo->add2XML(xml); + xml->endbranch(); + xml->endbranch(); + + xml->beginbranch("RESONANCE"); + Reson->add2XML(xml); + xml->endbranch(); +} + +void ADnoteParameters::add2XML(XMLwrapper *xml) +{ + GlobalPar.add2XML(xml); + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + xml->beginbranch("VOICE", nvoice); + add2XMLsection(xml, nvoice); + xml->endbranch(); + } +} + + +void ADnoteGlobalParam::getfromXML(XMLwrapper *xml) +{ + PStereo = xml->getparbool("stereo", PStereo); + + if(xml->enterbranch("AMPLITUDE_PARAMETERS")) { + PVolume = xml->getpar127("volume", PVolume); + PPanning = xml->getpar127("panning", PPanning); + PAmpVelocityScaleFunction = xml->getpar127("velocity_sensing", + PAmpVelocityScaleFunction); + + PPunchStrength = xml->getpar127("punch_strength", PPunchStrength); + PPunchTime = xml->getpar127("punch_time", PPunchTime); + PPunchStretch = xml->getpar127("punch_stretch", PPunchStretch); + PPunchVelocitySensing = xml->getpar127("punch_velocity_sensing", + PPunchVelocitySensing); + Hrandgrouping = xml->getpar127("harmonic_randomness_grouping", + Hrandgrouping); + + if(xml->enterbranch("AMPLITUDE_ENVELOPE")) { + AmpEnvelope->getfromXML(xml); + xml->exitbranch(); + } + + if(xml->enterbranch("AMPLITUDE_LFO")) { + AmpLfo->getfromXML(xml); + xml->exitbranch(); + } + + xml->exitbranch(); + } + + if(xml->enterbranch("FREQUENCY_PARAMETERS")) { + PDetune = xml->getpar("detune", PDetune, 0, 16383); + PCoarseDetune = xml->getpar("coarse_detune", PCoarseDetune, 0, 16383); + PDetuneType = xml->getpar127("detune_type", PDetuneType); + PBandwidth = xml->getpar127("bandwidth", PBandwidth); + + xml->enterbranch("FREQUENCY_ENVELOPE"); + FreqEnvelope->getfromXML(xml); + xml->exitbranch(); + + xml->enterbranch("FREQUENCY_LFO"); + FreqLfo->getfromXML(xml); + xml->exitbranch(); + + xml->exitbranch(); + } + + + if(xml->enterbranch("FILTER_PARAMETERS")) { + PFilterVelocityScale = xml->getpar127("velocity_sensing_amplitude", + PFilterVelocityScale); + PFilterVelocityScaleFunction = xml->getpar127( + "velocity_sensing", + PFilterVelocityScaleFunction); + + xml->enterbranch("FILTER"); + GlobalFilter->getfromXML(xml); + xml->exitbranch(); + + xml->enterbranch("FILTER_ENVELOPE"); + FilterEnvelope->getfromXML(xml); + xml->exitbranch(); + + xml->enterbranch("FILTER_LFO"); + FilterLfo->getfromXML(xml); + xml->exitbranch(); + xml->exitbranch(); + } + + if(xml->enterbranch("RESONANCE")) { + Reson->getfromXML(xml); + xml->exitbranch(); + } +} + +void ADnoteParameters::getfromXML(XMLwrapper *xml) +{ + GlobalPar.getfromXML(xml); + + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + VoicePar[nvoice].Enabled = 0; + if(xml->enterbranch("VOICE", nvoice) == 0) + continue; + getfromXMLsection(xml, nvoice); + xml->exitbranch(); + } +} + +void ADnoteParameters::getfromXMLsection(XMLwrapper *xml, int n) +{ + int nvoice = n; + if(nvoice >= NUM_VOICES) + return; + + VoicePar[nvoice].getfromXML(xml, nvoice); +} + + +void ADnoteVoiceParam::getfromXML(XMLwrapper *xml, unsigned nvoice) +{ + Enabled = xml->getparbool("enabled", 0); + Unison_size = xml->getpar127("unison_size", Unison_size); + Unison_frequency_spread = xml->getpar127("unison_frequency_spread", + Unison_frequency_spread); + Unison_stereo_spread = xml->getpar127("unison_stereo_spread", + Unison_stereo_spread); + Unison_vibratto = xml->getpar127("unison_vibratto", Unison_vibratto); + Unison_vibratto_speed = xml->getpar127("unison_vibratto_speed", + Unison_vibratto_speed); + Unison_invert_phase = xml->getpar127("unison_invert_phase", + Unison_invert_phase); + + Type = xml->getpar127("type", Type); + PDelay = xml->getpar127("delay", PDelay); + Presonance = xml->getparbool("resonance", Presonance); + + Pextoscil = xml->getpar("ext_oscil", -1, -1, nvoice - 1); + PextFMoscil = xml->getpar("ext_fm_oscil", -1, -1, nvoice - 1); + + Poscilphase = xml->getpar127("oscil_phase", Poscilphase); + PFMoscilphase = xml->getpar127("oscil_fm_phase", PFMoscilphase); + PFilterEnabled = xml->getparbool("filter_enabled", PFilterEnabled); + Pfilterbypass = xml->getparbool("filter_bypass", Pfilterbypass); + PFMEnabled = xml->getpar127("fm_enabled", PFMEnabled); + + if(xml->enterbranch("OSCIL")) { + OscilSmp->getfromXML(xml); + xml->exitbranch(); + } + + + if(xml->enterbranch("AMPLITUDE_PARAMETERS")) { + PPanning = xml->getpar127("panning", PPanning); + PVolume = xml->getpar127("volume", PVolume); + PVolumeminus = xml->getparbool("volume_minus", PVolumeminus); + PAmpVelocityScaleFunction = xml->getpar127("velocity_sensing", + PAmpVelocityScaleFunction); + + PAmpEnvelopeEnabled = xml->getparbool("amp_envelope_enabled", + PAmpEnvelopeEnabled); + if(xml->enterbranch("AMPLITUDE_ENVELOPE")) { + AmpEnvelope->getfromXML(xml); + xml->exitbranch(); + } + + PAmpLfoEnabled = xml->getparbool("amp_lfo_enabled", PAmpLfoEnabled); + if(xml->enterbranch("AMPLITUDE_LFO")) { + AmpLfo->getfromXML(xml); + xml->exitbranch(); + } + xml->exitbranch(); + } + + if(xml->enterbranch("FREQUENCY_PARAMETERS")) { + Pfixedfreq = xml->getparbool("fixed_freq", Pfixedfreq); + PfixedfreqET = xml->getpar127("fixed_freq_et", PfixedfreqET); + PDetune = xml->getpar("detune", PDetune, 0, 16383); + PCoarseDetune = xml->getpar("coarse_detune", PCoarseDetune, 0, 16383); + PDetuneType = xml->getpar127("detune_type", PDetuneType); + PFreqEnvelopeEnabled = xml->getparbool("freq_envelope_enabled", + PFreqEnvelopeEnabled); + + if(xml->enterbranch("FREQUENCY_ENVELOPE")) { + FreqEnvelope->getfromXML(xml); + xml->exitbranch(); + } + + PFreqLfoEnabled = xml->getparbool("freq_lfo_enabled", PFreqLfoEnabled); + + if(xml->enterbranch("FREQUENCY_LFO")) { + FreqLfo->getfromXML(xml); + xml->exitbranch(); + } + xml->exitbranch(); + } + + if(xml->enterbranch("FILTER_PARAMETERS")) { + if(xml->enterbranch("FILTER")) { + VoiceFilter->getfromXML(xml); + xml->exitbranch(); + } + + PFilterEnvelopeEnabled = xml->getparbool("filter_envelope_enabled", + PFilterEnvelopeEnabled); + if(xml->enterbranch("FILTER_ENVELOPE")) { + FilterEnvelope->getfromXML(xml); + xml->exitbranch(); + } + + PFilterLfoEnabled = xml->getparbool("filter_lfo_enabled", + PFilterLfoEnabled); + if(xml->enterbranch("FILTER_LFO")) { + FilterLfo->getfromXML(xml); + xml->exitbranch(); + } + xml->exitbranch(); + } + + if(xml->enterbranch("FM_PARAMETERS")) { + PFMVoice = xml->getpar("input_voice", PFMVoice, -1, nvoice - 1); + PFMVolume = xml->getpar127("volume", PFMVolume); + PFMVolumeDamp = xml->getpar127("volume_damp", PFMVolumeDamp); + PFMVelocityScaleFunction = xml->getpar127("velocity_sensing", + PFMVelocityScaleFunction); + + PFMAmpEnvelopeEnabled = xml->getparbool("amp_envelope_enabled", + PFMAmpEnvelopeEnabled); + if(xml->enterbranch("AMPLITUDE_ENVELOPE")) { + FMAmpEnvelope->getfromXML(xml); + xml->exitbranch(); + } + + if(xml->enterbranch("MODULATOR")) { + PFMDetune = xml->getpar("detune", PFMDetune, 0, 16383); + PFMCoarseDetune = xml->getpar("coarse_detune", + PFMCoarseDetune, + 0, + 16383); + PFMDetuneType = xml->getpar127("detune_type", PFMDetuneType); + + PFMFreqEnvelopeEnabled = xml->getparbool("freq_envelope_enabled", + PFMFreqEnvelopeEnabled); + if(xml->enterbranch("FREQUENCY_ENVELOPE")) { + FMFreqEnvelope->getfromXML(xml); + xml->exitbranch(); + } + + if(xml->enterbranch("OSCIL")) { + FMSmp->getfromXML(xml); + xml->exitbranch(); + } + + xml->exitbranch(); + } + xml->exitbranch(); + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/ADnoteParameters.h b/plugins/zynaddsubfx/zynaddsubfx/src/Params/ADnoteParameters.h new file mode 100644 index 000000000..291d5b420 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/ADnoteParameters.h @@ -0,0 +1,316 @@ +/* + ZynAddSubFX - a software synthesizer + + ADnoteParameters.h - Parameters for ADnote (ADsynth) + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef AD_NOTE_PARAMETERS_H +#define AD_NOTE_PARAMETERS_H + + +#include "../globals.h" +#include "../Misc/Util.h" +#include "PresetsArray.h" + +class EnvelopeParams; +class LFOParams; +class FilterParams; +class Resonance; +class OscilGen; +class FFTwrapper; + +enum FMTYPE { + NONE, MORPH, RING_MOD, PHASE_MOD, FREQ_MOD, PITCH_MOD +}; +extern int ADnote_unison_sizes[]; + +/*****************************************************************/ +/* GLOBAL PARAMETERS */ +/*****************************************************************/ + +struct ADnoteGlobalParam { + ADnoteGlobalParam(); + ~ADnoteGlobalParam(); + void defaults(); + void add2XML(XMLwrapper *xml); + void getfromXML(XMLwrapper *xml); + /* The instrument type - MONO/STEREO + If the mode is MONO, the panning of voices are not used + Stereo=1, Mono=0. */ + + unsigned char PStereo; + + + /****************************************** + * FREQUENCY GLOBAL PARAMETERS * + ******************************************/ + unsigned short int PDetune; //fine detune + unsigned short int PCoarseDetune; //coarse detune+octave + unsigned char PDetuneType; //detune type + + unsigned char PBandwidth; //how much the relative fine detunes of the voices are changed + + EnvelopeParams *FreqEnvelope; //Frequency Envelope + + LFOParams *FreqLfo; //Frequency LFO + + /******************************************** + * AMPLITUDE GLOBAL PARAMETERS * + ********************************************/ + + /* Panning - 0 - random + 1 - left + 64 - center + 127 - right */ + unsigned char PPanning; + + unsigned char PVolume; + + unsigned char PAmpVelocityScaleFunction; + + EnvelopeParams *AmpEnvelope; + + LFOParams *AmpLfo; + + unsigned char PPunchStrength, PPunchTime, PPunchStretch, + PPunchVelocitySensing; + + /****************************************** + * FILTER GLOBAL PARAMETERS * + ******************************************/ + FilterParams *GlobalFilter; + + // filter velocity sensing + unsigned char PFilterVelocityScale; + + // filter velocity sensing + unsigned char PFilterVelocityScaleFunction; + + EnvelopeParams *FilterEnvelope; + + LFOParams *FilterLfo; + + // RESONANCE + Resonance *Reson; + + //how the randomness is applied to the harmonics on more voices using the same oscillator + unsigned char Hrandgrouping; +}; + + + +/***********************************************************/ +/* VOICE PARAMETERS */ +/***********************************************************/ +struct ADnoteVoiceParam { + void getfromXML(XMLwrapper *xml, unsigned nvoice); + void add2XML(XMLwrapper *xml, bool fmoscilused); + void defaults(); + void enable(FFTwrapper *fft, Resonance *Reson); + void kill(); + /** If the voice is enabled */ + unsigned char Enabled; + + /** How many subvoices are used in this voice */ + unsigned char Unison_size; + + /** How subvoices are spread */ + unsigned char Unison_frequency_spread; + + /** Stereo spread of the subvoices*/ + unsigned char Unison_stereo_spread; + + /** Vibratto of the subvoices (which makes the unison more "natural")*/ + unsigned char Unison_vibratto; + + /** Medium speed of the vibratto of the subvoices*/ + unsigned char Unison_vibratto_speed; + + /** Unison invert phase */ + unsigned char Unison_invert_phase; //0=none,1=random,2=50%,3=33%,4=25% + + /** Type of the voice (0=Sound,1=Noise)*/ + unsigned char Type; + + /** Voice Delay */ + unsigned char PDelay; + + /** If the resonance is enabled for this voice */ + unsigned char Presonance; + + // What external oscil should I use, -1 for internal OscilSmp&FMSmp + short int Pextoscil, PextFMoscil; + // it is not allowed that the externoscil,externFMoscil => current voice + + // oscillator phases + unsigned char Poscilphase, PFMoscilphase; + + // filter bypass + unsigned char Pfilterbypass; + + /** Voice oscillator */ + OscilGen *OscilSmp; + + /********************************** + * FREQUENCY PARAMETERS * + **********************************/ + + /** If the base frequency is fixed to 440 Hz*/ + unsigned char Pfixedfreq; + + /* Equal temperate (this is used only if the Pfixedfreq is enabled) + If this parameter is 0, the frequency is fixed (to 440 Hz); + if this parameter is 64, 1 MIDI halftone -> 1 frequency halftone */ + unsigned char PfixedfreqET; + + /** Fine detune */ + unsigned short int PDetune; + + /** Coarse detune + octave */ + unsigned short int PCoarseDetune; + + /** Detune type */ + unsigned char PDetuneType; + + /* Frequency Envelope */ + unsigned char PFreqEnvelopeEnabled; + EnvelopeParams *FreqEnvelope; + + /* Frequency LFO */ + unsigned char PFreqLfoEnabled; + LFOParams *FreqLfo; + + + /*************************** + * AMPLITUDE PARAMETERS * + ***************************/ + + /* Panning 0 - random + 1 - left + 64 - center + 127 - right + The Panning is ignored if the instrument is mono */ + unsigned char PPanning; + + /* Voice Volume */ + unsigned char PVolume; + + /* If the Volume negative */ + unsigned char PVolumeminus; + + /* Velocity sensing */ + unsigned char PAmpVelocityScaleFunction; + + /* Amplitude Envelope */ + unsigned char PAmpEnvelopeEnabled; + EnvelopeParams *AmpEnvelope; + + /* Amplitude LFO */ + unsigned char PAmpLfoEnabled; + LFOParams *AmpLfo; + + + + /************************* + * FILTER PARAMETERS * + *************************/ + + /* Voice Filter */ + unsigned char PFilterEnabled; + FilterParams *VoiceFilter; + + /* Filter Envelope */ + unsigned char PFilterEnvelopeEnabled; + EnvelopeParams *FilterEnvelope; + + /* LFO Envelope */ + unsigned char PFilterLfoEnabled; + LFOParams *FilterLfo; + + /**************************** + * MODULLATOR PARAMETERS * + ****************************/ + + /* Modullator Parameters (0=off,1=Morph,2=RM,3=PM,4=FM.. */ + unsigned char PFMEnabled; + + /* Voice that I use as modullator instead of FMSmp. + It is -1 if I use FMSmp(default). + It maynot be equal or bigger than current voice */ + short int PFMVoice; + + /* Modullator oscillator */ + OscilGen *FMSmp; + + /* Modullator Volume */ + unsigned char PFMVolume; + + /* Modullator damping at higher frequencies */ + unsigned char PFMVolumeDamp; + + /* Modullator Velocity Sensing */ + unsigned char PFMVelocityScaleFunction; + + /* Fine Detune of the Modullator*/ + unsigned short int PFMDetune; + + /* Coarse Detune of the Modullator */ + unsigned short int PFMCoarseDetune; + + /* The detune type */ + unsigned char PFMDetuneType; + + /* Frequency Envelope of the Modullator */ + unsigned char PFMFreqEnvelopeEnabled; + EnvelopeParams *FMFreqEnvelope; + + /* Frequency Envelope of the Modullator */ + unsigned char PFMAmpEnvelopeEnabled; + EnvelopeParams *FMAmpEnvelope; +}; + +class ADnoteParameters:public PresetsArray +{ + public: + ADnoteParameters(FFTwrapper *fft_); + ~ADnoteParameters(); + + ADnoteGlobalParam GlobalPar; + ADnoteVoiceParam VoicePar[NUM_VOICES]; + + void defaults(); + void add2XML(XMLwrapper *xml); + void getfromXML(XMLwrapper *xml); + + float getBandwidthDetuneMultiplier(); + float getUnisonFrequencySpreadCents(int nvoice); + int get_unison_size_index(int nvoice); + void set_unison_size_index(int nvoice, int index); + private: + void defaults(int n); //n is the nvoice + + void EnableVoice(int nvoice); + void KillVoice(int nvoice); + FFTwrapper *fft; + + void add2XMLsection(XMLwrapper *xml, int n); + void getfromXMLsection(XMLwrapper *xml, int n); +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/CMakeLists.txt b/plugins/zynaddsubfx/zynaddsubfx/src/Params/CMakeLists.txt new file mode 100644 index 000000000..d5758affa --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/CMakeLists.txt @@ -0,0 +1,13 @@ +set(zynaddsubfx_params_SRCS + Params/ADnoteParameters.cpp + Params/Controller.cpp + Params/EnvelopeParams.cpp + Params/FilterParams.cpp + Params/LFOParams.cpp + Params/PADnoteParameters.cpp + Params/Presets.cpp + Params/PresetsArray.cpp + Params/PresetsStore.cpp + Params/SUBnoteParameters.cpp + PARENT_SCOPE +) diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/Controller.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Params/Controller.cpp new file mode 100644 index 000000000..5d1265450 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/Controller.cpp @@ -0,0 +1,419 @@ +/* + ZynAddSubFX - a software synthesizer + + Controller.cpp - (Midi) Controllers implementation + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "Controller.h" +#include +#include + +Controller::Controller() +{ + defaults(); + resetall(); +} + +Controller::~Controller() +{} + +void Controller::defaults() +{ + setpitchwheelbendrange(100); //1 halftones + expression.receive = 1; + panning.depth = 64; + filtercutoff.depth = 64; + filterq.depth = 64; + bandwidth.depth = 64; + bandwidth.exponential = 0; + modwheel.depth = 80; + modwheel.exponential = 0; + fmamp.receive = 1; + volume.receive = 1; + sustain.receive = 1; + NRPN.receive = 1; + + portamento.portamento = 0; + portamento.used = 0; + portamento.proportional = 0; + portamento.propRate = 80; + portamento.propDepth = 90; + portamento.receive = 1; + portamento.time = 64; + portamento.updowntimestretch = 64; + portamento.pitchthresh = 3; + portamento.pitchthreshtype = 1; + portamento.noteusing = -1; + resonancecenter.depth = 64; + resonancebandwidth.depth = 64; + + initportamento(440.0f, 440.0f, false); // Now has a third argument + setportamento(0); +} + +void Controller::resetall() +{ + setpitchwheel(0); //center + setexpression(127); + setpanning(64); + setfiltercutoff(64); + setfilterq(64); + setbandwidth(64); + setmodwheel(64); + setfmamp(127); + setvolume(127); + setsustain(0); + setresonancecenter(64); + setresonancebw(64); + + //reset the NRPN + NRPN.parhi = -1; + NRPN.parlo = -1; + NRPN.valhi = -1; + NRPN.vallo = -1; +} + +void Controller::setpitchwheel(int value) +{ + pitchwheel.data = value; + float cents = value / 8192.0f; + cents *= pitchwheel.bendrange; + pitchwheel.relfreq = powf(2, cents / 1200.0f); + //fprintf(stderr,"%ld %ld -> %.3f\n",pitchwheel.bendrange,pitchwheel.data,pitchwheel.relfreq);fflush(stderr); +} + +void Controller::setpitchwheelbendrange(unsigned short int value) +{ + pitchwheel.bendrange = value; +} + +void Controller::setexpression(int value) +{ + expression.data = value; + if(expression.receive != 0) + expression.relvolume = value / 127.0f; + else + expression.relvolume = 1.0f; +} + +void Controller::setpanning(int value) +{ + panning.data = value; + panning.pan = (value / 128.0f - 0.5f) * (panning.depth / 64.0f); +} + +void Controller::setfiltercutoff(int value) +{ + filtercutoff.data = value; + filtercutoff.relfreq = + (value - 64.0f) * filtercutoff.depth / 4096.0f * 3.321928f; //3.3219f..=ln2(10) +} + +void Controller::setfilterq(int value) +{ + filterq.data = value; + filterq.relq = powf(30.0f, (value - 64.0f) / 64.0f * (filterq.depth / 64.0f)); +} + +void Controller::setbandwidth(int value) +{ + bandwidth.data = value; + if(bandwidth.exponential == 0) { + float tmp = powf(25.0f, powf(bandwidth.depth / 127.0f, 1.5f)) - 1.0f; + if((value < 64) && (bandwidth.depth >= 64)) + tmp = 1.0f; + bandwidth.relbw = (value / 64.0f - 1.0f) * tmp + 1.0f; + if(bandwidth.relbw < 0.01f) + bandwidth.relbw = 0.01f; + } + else + bandwidth.relbw = + powf(25.0f, (value - 64.0f) / 64.0f * (bandwidth.depth / 64.0f)); + ; +} + +void Controller::setmodwheel(int value) +{ + modwheel.data = value; + if(modwheel.exponential == 0) { + float tmp = + powf(25.0f, powf(modwheel.depth / 127.0f, 1.5f) * 2.0f) / 25.0f; + if((value < 64) && (modwheel.depth >= 64)) + tmp = 1.0f; + modwheel.relmod = (value / 64.0f - 1.0f) * tmp + 1.0f; + if(modwheel.relmod < 0.0f) + modwheel.relmod = 0.0f; + } + else + modwheel.relmod = + powf(25.0f, (value - 64.0f) / 64.0f * (modwheel.depth / 80.0f)); +} + +void Controller::setfmamp(int value) +{ + fmamp.data = value; + fmamp.relamp = value / 127.0f; + if(fmamp.receive != 0) + fmamp.relamp = value / 127.0f; + else + fmamp.relamp = 1.0f; +} + +void Controller::setvolume(int value) +{ + volume.data = value; + if(volume.receive != 0) + volume.volume = powf(0.1f, (127 - value) / 127.0f * 2.0f); + else + volume.volume = 1.0f; +} + +void Controller::setsustain(int value) +{ + sustain.data = value; + if(sustain.receive != 0) + sustain.sustain = ((value < 64) ? 0 : 1); + else + sustain.sustain = 0; +} + +void Controller::setportamento(int value) +{ + portamento.data = value; + if(portamento.receive != 0) + portamento.portamento = ((value < 64) ? 0 : 1); +} + +int Controller::initportamento(float oldfreq, + float newfreq, + bool legatoflag) +{ + portamento.x = 0.0f; + + if(legatoflag) { // Legato in progress + if(portamento.portamento == 0) + return 0; + } + else // No legato, do the original if...return + if((portamento.used != 0) || (portamento.portamento == 0)) + return 0; + ; + + float portamentotime = powf(100.0f, portamento.time / 127.0f) / 50.0f; //portamento time in seconds + + if(portamento.proportional) { + //If there is a min(float,float) and a max(float,float) then they + //could be used here + //Linear functors could also make this nicer + if(oldfreq > newfreq) //2 is the center of propRate + portamentotime *= + powf(oldfreq / newfreq + / (portamento.propRate / 127.0f * 3 + .05), + (portamento.propDepth / 127.0f * 1.6f + .2)); + else //1 is the center of propDepth + portamentotime *= + powf(newfreq / oldfreq + / (portamento.propRate / 127.0f * 3 + .05), + (portamento.propDepth / 127.0f * 1.6f + .2)); + } + + if((portamento.updowntimestretch >= 64) && (newfreq < oldfreq)) { + if(portamento.updowntimestretch == 127) + return 0; + portamentotime *= powf(0.1f, + (portamento.updowntimestretch - 64) / 63.0f); + } + if((portamento.updowntimestretch < 64) && (newfreq > oldfreq)) { + if(portamento.updowntimestretch == 0) + return 0; + portamentotime *= powf(0.1f, + (64.0f - portamento.updowntimestretch) / 64.0f); + } + + //printf("%f->%f : Time %f\n",oldfreq,newfreq,portamentotime); + + portamento.dx = synth->buffersize_f / (portamentotime * synth->samplerate_f); + portamento.origfreqrap = oldfreq / newfreq; + + float tmprap = ((portamento.origfreqrap > 1.0f) ? + (portamento.origfreqrap) : + (1.0f / portamento.origfreqrap)); + + float thresholdrap = powf(2.0f, portamento.pitchthresh / 12.0f); + if((portamento.pitchthreshtype == 0) && (tmprap - 0.00001f > thresholdrap)) + return 0; + if((portamento.pitchthreshtype == 1) && (tmprap + 0.00001f < thresholdrap)) + return 0; + + portamento.used = 1; + portamento.freqrap = portamento.origfreqrap; + return 1; +} + +void Controller::updateportamento() +{ + if(portamento.used == 0) + return; + + portamento.x += portamento.dx; + if(portamento.x > 1.0f) { + portamento.x = 1.0f; + portamento.used = 0; + } + portamento.freqrap = + (1.0f - portamento.x) * portamento.origfreqrap + portamento.x; +} + + +void Controller::setresonancecenter(int value) +{ + resonancecenter.data = value; + resonancecenter.relcenter = + powf(3.0f, (value - 64.0f) / 64.0f * (resonancecenter.depth / 64.0f)); +} +void Controller::setresonancebw(int value) +{ + resonancebandwidth.data = value; + resonancebandwidth.relbw = + powf(1.5f, (value - 64.0f) / 64.0f * (resonancebandwidth.depth / 127.0f)); +} + + +//Returns 0 if there is NRPN or 1 if there is not +int Controller::getnrpn(int *parhi, int *parlo, int *valhi, int *vallo) +{ + if(NRPN.receive == 0) + return 1; + if((NRPN.parhi < 0) || (NRPN.parlo < 0) || (NRPN.valhi < 0) + || (NRPN.vallo < 0)) + return 1; + + *parhi = NRPN.parhi; + *parlo = NRPN.parlo; + *valhi = NRPN.valhi; + *vallo = NRPN.vallo; + return 0; +} + + +void Controller::setparameternumber(unsigned int type, int value) +{ + switch(type) { + case C_nrpnhi: + NRPN.parhi = value; + NRPN.valhi = -1; + NRPN.vallo = -1; //clear the values + break; + case C_nrpnlo: + NRPN.parlo = value; + NRPN.valhi = -1; + NRPN.vallo = -1; //clear the values + break; + case C_dataentryhi: + if((NRPN.parhi >= 0) && (NRPN.parlo >= 0)) + NRPN.valhi = value; + break; + case C_dataentrylo: + if((NRPN.parhi >= 0) && (NRPN.parlo >= 0)) + NRPN.vallo = value; + break; + } +} + + + +void Controller::add2XML(XMLwrapper *xml) +{ + xml->addpar("pitchwheel_bendrange", pitchwheel.bendrange); + + xml->addparbool("expression_receive", expression.receive); + xml->addpar("panning_depth", panning.depth); + xml->addpar("filter_cutoff_depth", filtercutoff.depth); + xml->addpar("filter_q_depth", filterq.depth); + xml->addpar("bandwidth_depth", bandwidth.depth); + xml->addpar("mod_wheel_depth", modwheel.depth); + xml->addparbool("mod_wheel_exponential", modwheel.exponential); + xml->addparbool("fm_amp_receive", fmamp.receive); + xml->addparbool("volume_receive", volume.receive); + xml->addparbool("sustain_receive", sustain.receive); + + xml->addparbool("portamento_receive", portamento.receive); + xml->addpar("portamento_time", portamento.time); + xml->addpar("portamento_pitchthresh", portamento.pitchthresh); + xml->addpar("portamento_pitchthreshtype", portamento.pitchthreshtype); + xml->addpar("portamento_portamento", portamento.portamento); + xml->addpar("portamento_updowntimestretch", portamento.updowntimestretch); + xml->addpar("portamento_proportional", portamento.proportional); + xml->addpar("portamento_proprate", portamento.propRate); + xml->addpar("portamento_propdepth", portamento.propDepth); + + xml->addpar("resonance_center_depth", resonancecenter.depth); + xml->addpar("resonance_bandwidth_depth", resonancebandwidth.depth); +} + +void Controller::getfromXML(XMLwrapper *xml) +{ + pitchwheel.bendrange = xml->getpar("pitchwheel_bendrange", + pitchwheel.bendrange, + -6400, + 6400); + + expression.receive = xml->getparbool("expression_receive", + expression.receive); + panning.depth = xml->getpar127("panning_depth", panning.depth); + filtercutoff.depth = xml->getpar127("filter_cutoff_depth", + filtercutoff.depth); + filterq.depth = xml->getpar127("filter_q_depth", filterq.depth); + bandwidth.depth = xml->getpar127("bandwidth_depth", bandwidth.depth); + modwheel.depth = xml->getpar127("mod_wheel_depth", modwheel.depth); + modwheel.exponential = xml->getparbool("mod_wheel_exponential", + modwheel.exponential); + fmamp.receive = xml->getparbool("fm_amp_receive", + fmamp.receive); + volume.receive = xml->getparbool("volume_receive", + volume.receive); + sustain.receive = xml->getparbool("sustain_receive", + sustain.receive); + + portamento.receive = xml->getparbool("portamento_receive", + portamento.receive); + portamento.time = xml->getpar127("portamento_time", + portamento.time); + portamento.pitchthresh = xml->getpar127("portamento_pitchthresh", + portamento.pitchthresh); + portamento.pitchthreshtype = xml->getpar127("portamento_pitchthreshtype", + portamento.pitchthreshtype); + portamento.portamento = xml->getpar127("portamento_portamento", + portamento.portamento); + portamento.updowntimestretch = xml->getpar127( + "portamento_updowntimestretch", + portamento.updowntimestretch); + portamento.proportional = xml->getpar127("portamento_proportional", + portamento.proportional); + portamento.propRate = xml->getpar127("portamento_proprate", + portamento.propRate); + portamento.propDepth = xml->getpar127("portamento_propdepth", + portamento.propDepth); + + + resonancecenter.depth = xml->getpar127("resonance_center_depth", + resonancecenter.depth); + resonancebandwidth.depth = xml->getpar127("resonance_bandwidth_depth", + resonancebandwidth.depth); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/Controller.h b/plugins/zynaddsubfx/zynaddsubfx/src/Params/Controller.h new file mode 100644 index 000000000..22483eaaa --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/Controller.h @@ -0,0 +1,220 @@ +/* + ZynAddSubFX - a software synthesizer + + Controller.h - (Midi) Controllers implementation + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + + +#ifndef CONTROLLER_H +#define CONTROLLER_H + +#include "../globals.h" +#include "../Misc/XMLwrapper.h" + +/**(Midi) Controllers implementation*/ +class Controller +{ + public: + Controller(); + ~Controller(); + void resetall(); + + void add2XML(XMLwrapper *xml); + void defaults(); + void getfromXML(XMLwrapper *xml); + + //Controllers functions + void setpitchwheel(int value); + void setpitchwheelbendrange(unsigned short int value); + void setexpression(int value); + void setpanning(int value); + void setfiltercutoff(int value); + void setfilterq(int value); + void setbandwidth(int value); + void setmodwheel(int value); + void setfmamp(int value); + void setvolume(int value); + void setsustain(int value); + /**Enable or disable portamento + * @param value 0-127 MIDI value (greater than 64 enables)*/ + void setportamento(int value); + void setresonancecenter(int value); + void setresonancebw(int value); + + + void setparameternumber(unsigned int type, int value); //used for RPN and NRPN's + int getnrpn(int *parhi, int *parlo, int *valhi, int *vallo); + + /** + * Initialize a portamento + * + * @param oldfreq Starting frequency of the portamento (Hz) + * @param newfreq Ending frequency of the portamento (Hz) + * @param legatoflag true when legato is in progress, false otherwise + * @returns 1 if properly initialized, 0 otherwise*/ + int initportamento(float oldfreq, float newfreq, bool legatoflag); + /**Update portamento's freqrap to next value based upon dx*/ + void updateportamento(); + + // Controllers values + struct { //Pitch Wheel + int data; + short int bendrange; //bendrange is in cents + float relfreq; //the relative frequency (default is 1.0f) + } pitchwheel; + + struct { //Expression + int data; + float relvolume; + unsigned char receive; + } expression; + + struct { //Panning + int data; + float pan; + unsigned char depth; + } panning; + + + struct { //Filter cutoff + int data; + float relfreq; + unsigned char depth; + } filtercutoff; + + struct { //Filter Q + int data; + float relq; + unsigned char depth; + } filterq; + + struct { //Bandwidth + int data; + float relbw; + unsigned char depth; + unsigned char exponential; + } bandwidth; + + struct { //Modulation Wheel + int data; + float relmod; + unsigned char depth; + unsigned char exponential; + } modwheel; + + struct { //FM amplitude + int data; + float relamp; + unsigned char receive; + } fmamp; + + struct { //Volume + int data; + float volume; + unsigned char receive; + } volume; + + struct { //Sustain + int data, sustain; + unsigned char receive; + } sustain; + + struct { /** + +#include +#include +#include "EnvelopeParams.h" + +EnvelopeParams::EnvelopeParams(unsigned char Penvstretch_, + unsigned char Pforcedrelease_):Presets() +{ + int i; + + PA_dt = 10; + PD_dt = 10; + PR_dt = 10; + PA_val = 64; + PD_val = 64; + PS_val = 64; + PR_val = 64; + + for(i = 0; i < MAX_ENVELOPE_POINTS; ++i) { + Penvdt[i] = 32; + Penvval[i] = 64; + } + Penvdt[0] = 0; //no used + Penvsustain = 1; + Penvpoints = 1; + Envmode = 1; + Penvstretch = Penvstretch_; + Pforcedrelease = Pforcedrelease_; + Pfreemode = 1; + Plinearenvelope = 0; + + store2defaults(); +} + +EnvelopeParams::~EnvelopeParams() +{} + +float EnvelopeParams::getdt(char i) +{ + float result = (powf(2.0f, Penvdt[(int)i] / 127.0f * 12.0f) - 1.0f) * 10.0f; //miliseconds + return result; +} + + +/* + * ADSR/ASR... initialisations + */ +void EnvelopeParams::ADSRinit(char A_dt, char D_dt, char S_val, char R_dt) +{ + setpresettype("Penvamplitude"); + Envmode = 1; + PA_dt = A_dt; + PD_dt = D_dt; + PS_val = S_val; + PR_dt = R_dt; + Pfreemode = 0; + converttofree(); + + store2defaults(); +} + +void EnvelopeParams::ADSRinit_dB(char A_dt, char D_dt, char S_val, char R_dt) +{ + setpresettype("Penvamplitude"); + Envmode = 2; + PA_dt = A_dt; + PD_dt = D_dt; + PS_val = S_val; + PR_dt = R_dt; + Pfreemode = 0; + converttofree(); + + store2defaults(); +} + +void EnvelopeParams::ASRinit(char A_val, char A_dt, char R_val, char R_dt) +{ + setpresettype("Penvfrequency"); + Envmode = 3; + PA_val = A_val; + PA_dt = A_dt; + PR_val = R_val; + PR_dt = R_dt; + Pfreemode = 0; + converttofree(); + + store2defaults(); +} + +void EnvelopeParams::ADSRinit_filter(char A_val, + char A_dt, + char D_val, + char D_dt, + char R_dt, + char R_val) +{ + setpresettype("Penvfilter"); + Envmode = 4; + PA_val = A_val; + PA_dt = A_dt; + PD_val = D_val; + PD_dt = D_dt; + PR_dt = R_dt; + PR_val = R_val; + Pfreemode = 0; + converttofree(); + store2defaults(); +} + +void EnvelopeParams::ASRinit_bw(char A_val, char A_dt, char R_val, char R_dt) +{ + setpresettype("Penvbandwidth"); + Envmode = 5; + PA_val = A_val; + PA_dt = A_dt; + PR_val = R_val; + PR_dt = R_dt; + Pfreemode = 0; + converttofree(); + store2defaults(); +} + +/* + * Convert the Envelope to freemode + */ +void EnvelopeParams::converttofree() +{ + switch(Envmode) { + case 1: + Penvpoints = 4; + Penvsustain = 2; + Penvval[0] = 0; + Penvdt[1] = PA_dt; + Penvval[1] = 127; + Penvdt[2] = PD_dt; + Penvval[2] = PS_val; + Penvdt[3] = PR_dt; + Penvval[3] = 0; + break; + case 2: + Penvpoints = 4; + Penvsustain = 2; + Penvval[0] = 0; + Penvdt[1] = PA_dt; + Penvval[1] = 127; + Penvdt[2] = PD_dt; + Penvval[2] = PS_val; + Penvdt[3] = PR_dt; + Penvval[3] = 0; + break; + case 3: + Penvpoints = 3; + Penvsustain = 1; + Penvval[0] = PA_val; + Penvdt[1] = PA_dt; + Penvval[1] = 64; + Penvdt[2] = PR_dt; + Penvval[2] = PR_val; + break; + case 4: + Penvpoints = 4; + Penvsustain = 2; + Penvval[0] = PA_val; + Penvdt[1] = PA_dt; + Penvval[1] = PD_val; + Penvdt[2] = PD_dt; + Penvval[2] = 64; + Penvdt[3] = PR_dt; + Penvval[3] = PR_val; + break; + case 5: + Penvpoints = 3; + Penvsustain = 1; + Penvval[0] = PA_val; + Penvdt[1] = PA_dt; + Penvval[1] = 64; + Penvdt[2] = PR_dt; + Penvval[2] = PR_val; + break; + } +} + + + + +void EnvelopeParams::add2XML(XMLwrapper *xml) +{ + xml->addparbool("free_mode", Pfreemode); + xml->addpar("env_points", Penvpoints); + xml->addpar("env_sustain", Penvsustain); + xml->addpar("env_stretch", Penvstretch); + xml->addparbool("forced_release", Pforcedrelease); + xml->addparbool("linear_envelope", Plinearenvelope); + xml->addpar("A_dt", PA_dt); + xml->addpar("D_dt", PD_dt); + xml->addpar("R_dt", PR_dt); + xml->addpar("A_val", PA_val); + xml->addpar("D_val", PD_val); + xml->addpar("S_val", PS_val); + xml->addpar("R_val", PR_val); + + if((Pfreemode != 0) || (!xml->minimal)) + for(int i = 0; i < Penvpoints; ++i) { + xml->beginbranch("POINT", i); + if(i != 0) + xml->addpar("dt", Penvdt[i]); + xml->addpar("val", Penvval[i]); + xml->endbranch(); + } +} + + + +void EnvelopeParams::getfromXML(XMLwrapper *xml) +{ + Pfreemode = xml->getparbool("free_mode", Pfreemode); + Penvpoints = xml->getpar127("env_points", Penvpoints); + Penvsustain = xml->getpar127("env_sustain", Penvsustain); + Penvstretch = xml->getpar127("env_stretch", Penvstretch); + Pforcedrelease = xml->getparbool("forced_release", Pforcedrelease); + Plinearenvelope = xml->getparbool("linear_envelope", Plinearenvelope); + + PA_dt = xml->getpar127("A_dt", PA_dt); + PD_dt = xml->getpar127("D_dt", PD_dt); + PR_dt = xml->getpar127("R_dt", PR_dt); + PA_val = xml->getpar127("A_val", PA_val); + PD_val = xml->getpar127("D_val", PD_val); + PS_val = xml->getpar127("S_val", PS_val); + PR_val = xml->getpar127("R_val", PR_val); + + for(int i = 0; i < Penvpoints; ++i) { + if(xml->enterbranch("POINT", i) == 0) + continue; + if(i != 0) + Penvdt[i] = xml->getpar127("dt", Penvdt[i]); + Penvval[i] = xml->getpar127("val", Penvval[i]); + xml->exitbranch(); + } + + if(!Pfreemode) + converttofree(); +} + + +void EnvelopeParams::defaults() +{ + Penvstretch = Denvstretch; + Pforcedrelease = Dforcedrelease; + Plinearenvelope = Dlinearenvelope; + PA_dt = DA_dt; + PD_dt = DD_dt; + PR_dt = DR_dt; + PA_val = DA_val; + PD_val = DD_val; + PS_val = DS_val; + PR_val = DR_val; + Pfreemode = 0; + converttofree(); +} + +void EnvelopeParams::store2defaults() +{ + Denvstretch = Penvstretch; + Dforcedrelease = Pforcedrelease; + Dlinearenvelope = Plinearenvelope; + DA_dt = PA_dt; + DD_dt = PD_dt; + DR_dt = PR_dt; + DA_val = PA_val; + DD_val = PD_val; + DS_val = PS_val; + DR_val = PR_val; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/EnvelopeParams.h b/plugins/zynaddsubfx/zynaddsubfx/src/Params/EnvelopeParams.h new file mode 100644 index 000000000..0b3e99c96 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/EnvelopeParams.h @@ -0,0 +1,89 @@ +/* + ZynAddSubFX - a software synthesizer + + EnvelopeParams.h - Parameters for Envelope + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef ENVELOPE_PARAMS_H +#define ENVELOPE_PARAMS_H + +#include "../globals.h" +#include "../Misc/XMLwrapper.h" +#include "Presets.h" + +#define MAX_ENVELOPE_POINTS 40 +#define MIN_ENVELOPE_DB -400 + +class EnvelopeParams:public Presets +{ + public: + EnvelopeParams(unsigned char Penvstretch_, + unsigned char Pforcedrelease_); + ~EnvelopeParams(); + void ADSRinit(char A_dt, char D_dt, char S_val, char R_dt); + void ADSRinit_dB(char A_dt, char D_dt, char S_val, char R_dt); + void ASRinit(char A_val, char A_dt, char R_val, char R_dt); + void ADSRinit_filter(char A_val, + char A_dt, + char D_val, + char D_dt, + char R_dt, + char R_val); + void ASRinit_bw(char A_val, char A_dt, char R_val, char R_dt); + void converttofree(); + + void add2XML(XMLwrapper *xml); + void defaults(); + void getfromXML(XMLwrapper *xml); + + float getdt(char i); + + /* MIDI Parameters */ + unsigned char Pfreemode; //1 daca este in modul free sau 0 daca este in mod ADSR,ASR,... + unsigned char Penvpoints; + unsigned char Penvsustain; //127 pentru dezactivat + unsigned char Penvdt[MAX_ENVELOPE_POINTS]; + unsigned char Penvval[MAX_ENVELOPE_POINTS]; + unsigned char Penvstretch; //64=normal stretch (piano-like), 0=no stretch + unsigned char Pforcedrelease; //0 - OFF, 1 - ON + unsigned char Plinearenvelope; //if the amplitude envelope is linear + + unsigned char PA_dt, PD_dt, PR_dt, + PA_val, PD_val, PS_val, PR_val; + + + + int Envmode; // 1 for ADSR parameters (linear amplitude) + // 2 for ADSR_dB parameters (dB amplitude) + // 3 for ASR parameters (frequency LFO) + // 4 for ADSR_filter parameters (filter parameters) + // 5 for ASR_bw parameters (bandwidth parameters) + + private: + void store2defaults(); + + /* Default parameters */ + unsigned char Denvstretch; + unsigned char Dforcedrelease; + unsigned char Dlinearenvelope; + unsigned char DA_dt, DD_dt, DR_dt, + DA_val, DD_val, DS_val, DR_val; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/FilterParams.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Params/FilterParams.cpp new file mode 100644 index 000000000..81309d6c7 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/FilterParams.cpp @@ -0,0 +1,392 @@ +/* + ZynAddSubFX - a software synthesizer + + FilterParams.cpp - Parameters for filter + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "FilterParams.h" +#include "../Misc/Util.h" +#include +#include +#include + +FilterParams::FilterParams(unsigned char Ptype_, + unsigned char Pfreq_, + unsigned char Pq_) + :PresetsArray() +{ + setpresettype("Pfilter"); + Dtype = Ptype_; + Dfreq = Pfreq_; + Dq = Pq_; + + changed = false; + defaults(); +} + +FilterParams::~FilterParams() +{} + + +void FilterParams::defaults() +{ + Ptype = Dtype; + Pfreq = Dfreq; + Pq = Dq; + + Pstages = 0; + Pfreqtrack = 64; + Pgain = 64; + Pcategory = 0; + + Pnumformants = 3; + Pformantslowness = 64; + for(int j = 0; j < FF_MAX_VOWELS; ++j) + defaults(j); + ; + + Psequencesize = 3; + for(int i = 0; i < FF_MAX_SEQUENCE; ++i) + Psequence[i].nvowel = i % FF_MAX_VOWELS; + + Psequencestretch = 40; + Psequencereversed = 0; + Pcenterfreq = 64; //1 kHz + Poctavesfreq = 64; + Pvowelclearness = 64; +} + +void FilterParams::defaults(int n) +{ + int j = n; + for(int i = 0; i < FF_MAX_FORMANTS; ++i) { + Pvowels[j].formants[i].freq = (int)(RND * 127.0f); //some random freqs + Pvowels[j].formants[i].q = 64; + Pvowels[j].formants[i].amp = 127; + } +} + + +/* + * Get the parameters from other FilterParams + */ + +void FilterParams::getfromFilterParams(FilterParams *pars) +{ + defaults(); + + if(pars == NULL) + return; + + Ptype = pars->Ptype; + Pfreq = pars->Pfreq; + Pq = pars->Pq; + + Pstages = pars->Pstages; + Pfreqtrack = pars->Pfreqtrack; + Pgain = pars->Pgain; + Pcategory = pars->Pcategory; + + Pnumformants = pars->Pnumformants; + Pformantslowness = pars->Pformantslowness; + for(int j = 0; j < FF_MAX_VOWELS; ++j) + for(int i = 0; i < FF_MAX_FORMANTS; ++i) { + Pvowels[j].formants[i].freq = pars->Pvowels[j].formants[i].freq; + Pvowels[j].formants[i].q = pars->Pvowels[j].formants[i].q; + Pvowels[j].formants[i].amp = pars->Pvowels[j].formants[i].amp; + } + + Psequencesize = pars->Psequencesize; + for(int i = 0; i < FF_MAX_SEQUENCE; ++i) + Psequence[i].nvowel = pars->Psequence[i].nvowel; + + Psequencestretch = pars->Psequencestretch; + Psequencereversed = pars->Psequencereversed; + Pcenterfreq = pars->Pcenterfreq; + Poctavesfreq = pars->Poctavesfreq; + Pvowelclearness = pars->Pvowelclearness; +} + + +/* + * Parameter control + */ +float FilterParams::getfreq() +{ + return (Pfreq / 64.0f - 1.0f) * 5.0f; +} + +float FilterParams::getq() +{ + return expf(powf((float) Pq / 127.0f, 2) * logf(1000.0f)) - 0.9f; +} +float FilterParams::getfreqtracking(float notefreq) +{ + return logf(notefreq / 440.0f) * (Pfreqtrack - 64.0f) / (64.0f * LOG_2); +} + +float FilterParams::getgain() +{ + return (Pgain / 64.0f - 1.0f) * 30.0f; //-30..30dB +} + +/* + * Get the center frequency of the formant's graph + */ +float FilterParams::getcenterfreq() +{ + return 10000.0f * powf(10, -(1.0f - Pcenterfreq / 127.0f) * 2.0f); +} + +/* + * Get the number of octave that the formant functions applies to + */ +float FilterParams::getoctavesfreq() +{ + return 0.25f + 10.0f * Poctavesfreq / 127.0f; +} + +/* + * Get the frequency from x, where x is [0..1] + */ +float FilterParams::getfreqx(float x) +{ + if(x > 1.0f) + x = 1.0f; + float octf = powf(2.0f, getoctavesfreq()); + return getcenterfreq() / sqrt(octf) * powf(octf, x); +} + +/* + * Get the x coordinate from frequency (used by the UI) + */ +float FilterParams::getfreqpos(float freq) +{ + return (logf(freq) - logf(getfreqx(0.0f))) / logf(2.0f) / getoctavesfreq(); +} + + +/* + * Get the freq. response of the formant filter + */ +void FilterParams::formantfilterH(int nvowel, int nfreqs, float *freqs) +{ + float c[3], d[3]; + float filter_freq, filter_q, filter_amp; + float omega, sn, cs, alpha; + + for(int i = 0; i < nfreqs; ++i) + freqs[i] = 0.0f; + + //for each formant... + for(int nformant = 0; nformant < Pnumformants; ++nformant) { + //compute formant parameters(frequency,amplitude,etc.) + filter_freq = getformantfreq(Pvowels[nvowel].formants[nformant].freq); + filter_q = getformantq(Pvowels[nvowel].formants[nformant].q) * getq(); + if(Pstages > 0) + filter_q = + (filter_q > + 1.0f ? powf(filter_q, 1.0f / (Pstages + 1)) : filter_q); + + filter_amp = getformantamp(Pvowels[nvowel].formants[nformant].amp); + + + if(filter_freq <= (synth->samplerate / 2 - 100.0f)) { + omega = 2 * PI * filter_freq / synth->samplerate_f; + sn = sinf(omega); + cs = cosf(omega); + alpha = sn / (2 * filter_q); + float tmp = 1 + alpha; + c[0] = alpha / tmp *sqrt(filter_q + 1); + c[1] = 0; + c[2] = -alpha / tmp *sqrt(filter_q + 1); + d[1] = -2 * cs / tmp * (-1); + d[2] = (1 - alpha) / tmp * (-1); + } + else + continue; + + + for(int i = 0; i < nfreqs; ++i) { + float freq = getfreqx(i / (float) nfreqs); + if(freq > synth->samplerate / 2) { + for(int tmp = i; tmp < nfreqs; ++tmp) + freqs[tmp] = 0.0f; + break; + } + float fr = freq / synth->samplerate * PI * 2.0f; + float x = c[0], y = 0.0f; + for(int n = 1; n < 3; ++n) { + x += cosf(n * fr) * c[n]; + y -= sinf(n * fr) * c[n]; + } + float h = x * x + y * y; + x = 1.0f; + y = 0.0f; + for(int n = 1; n < 3; ++n) { + x -= cosf(n * fr) * d[n]; + y += sinf(n * fr) * d[n]; + } + h = h / (x * x + y * y); + + freqs[i] += powf(h, (Pstages + 1.0f) / 2.0f) * filter_amp; + } + } + for(int i = 0; i < nfreqs; ++i) { + if(freqs[i] > 0.000000001f) + freqs[i] = rap2dB(freqs[i]) + getgain(); + else + freqs[i] = -90.0f; + } +} + +/* + * Transforms a parameter to the real value + */ +float FilterParams::getformantfreq(unsigned char freq) +{ + float result = getfreqx(freq / 127.0f); + return result; +} + +float FilterParams::getformantamp(unsigned char amp) +{ + float result = powf(0.1f, (1.0f - amp / 127.0f) * 4.0f); + return result; +} + +float FilterParams::getformantq(unsigned char q) +{ + //temp + float result = powf(25.0f, (q - 32.0f) / 64.0f); + return result; +} + + + +void FilterParams::add2XMLsection(XMLwrapper *xml, int n) +{ + int nvowel = n; + for(int nformant = 0; nformant < FF_MAX_FORMANTS; ++nformant) { + xml->beginbranch("FORMANT", nformant); + xml->addpar("freq", Pvowels[nvowel].formants[nformant].freq); + xml->addpar("amp", Pvowels[nvowel].formants[nformant].amp); + xml->addpar("q", Pvowels[nvowel].formants[nformant].q); + xml->endbranch(); + } +} + +void FilterParams::add2XML(XMLwrapper *xml) +{ + //filter parameters + xml->addpar("category", Pcategory); + xml->addpar("type", Ptype); + xml->addpar("freq", Pfreq); + xml->addpar("q", Pq); + xml->addpar("stages", Pstages); + xml->addpar("freq_track", Pfreqtrack); + xml->addpar("gain", Pgain); + + //formant filter parameters + if((Pcategory == 1) || (!xml->minimal)) { + xml->beginbranch("FORMANT_FILTER"); + xml->addpar("num_formants", Pnumformants); + xml->addpar("formant_slowness", Pformantslowness); + xml->addpar("vowel_clearness", Pvowelclearness); + xml->addpar("center_freq", Pcenterfreq); + xml->addpar("octaves_freq", Poctavesfreq); + for(int nvowel = 0; nvowel < FF_MAX_VOWELS; ++nvowel) { + xml->beginbranch("VOWEL", nvowel); + add2XMLsection(xml, nvowel); + xml->endbranch(); + } + xml->addpar("sequence_size", Psequencesize); + xml->addpar("sequence_stretch", Psequencestretch); + xml->addparbool("sequence_reversed", Psequencereversed); + for(int nseq = 0; nseq < FF_MAX_SEQUENCE; ++nseq) { + xml->beginbranch("SEQUENCE_POS", nseq); + xml->addpar("vowel_id", Psequence[nseq].nvowel); + xml->endbranch(); + } + xml->endbranch(); + } +} + + +void FilterParams::getfromXMLsection(XMLwrapper *xml, int n) +{ + int nvowel = n; + for(int nformant = 0; nformant < FF_MAX_FORMANTS; ++nformant) { + if(xml->enterbranch("FORMANT", nformant) == 0) + continue; + Pvowels[nvowel].formants[nformant].freq = xml->getpar127( + "freq", + Pvowels[nvowel + ].formants[nformant].freq); + Pvowels[nvowel].formants[nformant].amp = xml->getpar127( + "amp", + Pvowels[nvowel + ].formants[nformant].amp); + Pvowels[nvowel].formants[nformant].q = + xml->getpar127("q", Pvowels[nvowel].formants[nformant].q); + xml->exitbranch(); + } +} + +void FilterParams::getfromXML(XMLwrapper *xml) +{ + //filter parameters + Pcategory = xml->getpar127("category", Pcategory); + Ptype = xml->getpar127("type", Ptype); + Pfreq = xml->getpar127("freq", Pfreq); + Pq = xml->getpar127("q", Pq); + Pstages = xml->getpar127("stages", Pstages); + Pfreqtrack = xml->getpar127("freq_track", Pfreqtrack); + Pgain = xml->getpar127("gain", Pgain); + + //formant filter parameters + if(xml->enterbranch("FORMANT_FILTER")) { + Pnumformants = xml->getpar127("num_formants", Pnumformants); + Pformantslowness = xml->getpar127("formant_slowness", Pformantslowness); + Pvowelclearness = xml->getpar127("vowel_clearness", Pvowelclearness); + Pcenterfreq = xml->getpar127("center_freq", Pcenterfreq); + Poctavesfreq = xml->getpar127("octaves_freq", Poctavesfreq); + + for(int nvowel = 0; nvowel < FF_MAX_VOWELS; ++nvowel) { + if(xml->enterbranch("VOWEL", nvowel) == 0) + continue; + getfromXMLsection(xml, nvowel); + xml->exitbranch(); + } + Psequencesize = xml->getpar127("sequence_size", Psequencesize); + Psequencestretch = xml->getpar127("sequence_stretch", Psequencestretch); + Psequencereversed = xml->getparbool("sequence_reversed", + Psequencereversed); + for(int nseq = 0; nseq < FF_MAX_SEQUENCE; ++nseq) { + if(xml->enterbranch("SEQUENCE_POS", nseq) == 0) + continue; + Psequence[nseq].nvowel = xml->getpar("vowel_id", + Psequence[nseq].nvowel, + 0, + FF_MAX_VOWELS - 1); + xml->exitbranch(); + } + xml->exitbranch(); + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/FilterParams.h b/plugins/zynaddsubfx/zynaddsubfx/src/Params/FilterParams.h new file mode 100644 index 000000000..22a1a94f8 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/FilterParams.h @@ -0,0 +1,102 @@ +/* + ZynAddSubFX - a software synthesizer + + FilterParams.h - Parameters for filter + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef FILTER_PARAMS_H +#define FILTER_PARAMS_H + +#include "../globals.h" +#include "../Misc/XMLwrapper.h" +#include "PresetsArray.h" + +class FilterParams:public PresetsArray +{ + public: + FilterParams(unsigned char Ptype_, + unsigned char Pfreq, + unsigned char Pq_); + ~FilterParams(); + + void add2XML(XMLwrapper *xml); + void add2XMLsection(XMLwrapper *xml, int n); + void defaults(); + void getfromXML(XMLwrapper *xml); + void getfromXMLsection(XMLwrapper *xml, int n); + + + void getfromFilterParams(FilterParams *pars); + + float getfreq(); + float getq(); + float getfreqtracking(float notefreq); + float getgain(); + + unsigned char Pcategory; //Filter category (Analog/Formant/StVar) + unsigned char Ptype; // Filter type (for analog lpf,hpf,bpf..) + unsigned char Pfreq; // Frequency (64-central frequency) + unsigned char Pq; // Q parameters (resonance or bandwidth) + unsigned char Pstages; //filter stages+1 + unsigned char Pfreqtrack; //how the filter frequency is changing according the note frequency + unsigned char Pgain; //filter's output gain + + //Formant filter parameters + unsigned char Pnumformants; //how many formants are used + unsigned char Pformantslowness; //how slow varies the formants + unsigned char Pvowelclearness; //how vowels are kept clean (how much try to avoid "mixed" vowels) + unsigned char Pcenterfreq, Poctavesfreq; //the center frequency of the res. func., and the number of octaves + + struct { + struct { + unsigned char freq, amp, q; //frequency,amplitude,Q + } formants[FF_MAX_FORMANTS]; + } Pvowels[FF_MAX_VOWELS]; + + + unsigned char Psequencesize; //how many vowels are in the sequence + unsigned char Psequencestretch; //how the sequence is stretched (how the input from filter envelopes/LFOs/etc. is "stretched") + unsigned char Psequencereversed; //if the input from filter envelopes/LFOs/etc. is reversed(negated) + struct { + unsigned char nvowel; //the vowel from the position + } Psequence[FF_MAX_SEQUENCE]; + + float getcenterfreq(); + float getoctavesfreq(); + float getfreqpos(float freq); + float getfreqx(float x); + + void formantfilterH(int nvowel, int nfreqs, float *freqs); //used by UI + + float getformantfreq(unsigned char freq); + float getformantamp(unsigned char amp); + float getformantq(unsigned char q); + + bool changed; + + private: + void defaults(int n); + + //stored default parameters + unsigned char Dtype; + unsigned char Dfreq; + unsigned char Dq; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/LFOParams.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Params/LFOParams.cpp new file mode 100644 index 000000000..72f2269da --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/LFOParams.cpp @@ -0,0 +1,104 @@ +/* + ZynAddSubFX - a software synthesizer + + LFOParams.cpp - Parameters for LFO + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include "../globals.h" +#include "LFOParams.h" + +int LFOParams::time; + +LFOParams::LFOParams(char Pfreq_, + char Pintensity_, + char Pstartphase_, + char PLFOtype_, + char Prandomness_, + char Pdelay_, + char Pcontinous_, + char fel_):Presets() +{ + switch(fel_) { + case 0: + setpresettype("Plfofrequency"); + break; + case 1: + setpresettype("Plfoamplitude"); + break; + case 2: + setpresettype("Plfofilter"); + break; + } + Dfreq = Pfreq_; + Dintensity = Pintensity_; + Dstartphase = Pstartphase_; + DLFOtype = PLFOtype_; + Drandomness = Prandomness_; + Ddelay = Pdelay_; + Dcontinous = Pcontinous_; + fel = fel_; + time = 0; + + defaults(); +} + +LFOParams::~LFOParams() +{} + +void LFOParams::defaults() +{ + Pfreq = Dfreq / 127.0f; + Pintensity = Dintensity; + Pstartphase = Dstartphase; + PLFOtype = DLFOtype; + Prandomness = Drandomness; + Pdelay = Ddelay; + Pcontinous = Dcontinous; + Pfreqrand = 0; + Pstretch = 64; +} + + +void LFOParams::add2XML(XMLwrapper *xml) +{ + xml->addparreal("freq", Pfreq); + xml->addpar("intensity", Pintensity); + xml->addpar("start_phase", Pstartphase); + xml->addpar("lfo_type", PLFOtype); + xml->addpar("randomness_amplitude", Prandomness); + xml->addpar("randomness_frequency", Pfreqrand); + xml->addpar("delay", Pdelay); + xml->addpar("stretch", Pstretch); + xml->addparbool("continous", Pcontinous); +} + +void LFOParams::getfromXML(XMLwrapper *xml) +{ + Pfreq = xml->getparreal("freq", Pfreq, 0.0f, 1.0f); + Pintensity = xml->getpar127("intensity", Pintensity); + Pstartphase = xml->getpar127("start_phase", Pstartphase); + PLFOtype = xml->getpar127("lfo_type", PLFOtype); + Prandomness = xml->getpar127("randomness_amplitude", Prandomness); + Pfreqrand = xml->getpar127("randomness_frequency", Pfreqrand); + Pdelay = xml->getpar127("delay", Pdelay); + Pstretch = xml->getpar127("stretch", Pstretch); + Pcontinous = xml->getparbool("continous", Pcontinous); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/LFOParams.h b/plugins/zynaddsubfx/zynaddsubfx/src/Params/LFOParams.h new file mode 100644 index 000000000..6ca307193 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/LFOParams.h @@ -0,0 +1,71 @@ +/* + ZynAddSubFX - a software synthesizer + + LFOParams.h - Parameters for LFO + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef LFO_PARAMS_H +#define LFO_PARAMS_H + +#include "../Misc/XMLwrapper.h" +#include "Presets.h" + +class LFOParams:public Presets +{ + public: + LFOParams(char Pfreq_, + char Pintensity_, + char Pstartphase_, + char PLFOtype_, + char Prandomness_, + char Pdelay_, + char Pcontinous, + char fel_); + ~LFOParams(); + + void add2XML(XMLwrapper *xml); + void defaults(); + /**Loads the LFO from the xml*/ + void getfromXML(XMLwrapper *xml); + + /* MIDI Parameters*/ + float Pfreq; /** +#include "PADnoteParameters.h" +#include "../Misc/WavFile.h" + +PADnoteParameters::PADnoteParameters(FFTwrapper *fft_, + pthread_mutex_t *mutex_):Presets() +{ + setpresettype("Ppadsynth"); + + fft = fft_; + mutex = mutex_; + + resonance = new Resonance(); + oscilgen = new OscilGen(fft_, resonance); + oscilgen->ADvsPAD = true; + + FreqEnvelope = new EnvelopeParams(0, 0); + FreqEnvelope->ASRinit(64, 50, 64, 60); + FreqLfo = new LFOParams(70, 0, 64, 0, 0, 0, 0, 0); + + AmpEnvelope = new EnvelopeParams(64, 1); + AmpEnvelope->ADSRinit_dB(0, 40, 127, 25); + AmpLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 1); + + GlobalFilter = new FilterParams(2, 94, 40); + FilterEnvelope = new EnvelopeParams(0, 1); + FilterEnvelope->ADSRinit_filter(64, 40, 64, 70, 60, 64); + FilterLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 2); + + for(int i = 0; i < PAD_MAX_SAMPLES; ++i) + sample[i].smp = NULL; + newsample.smp = NULL; + + defaults(); +} + +PADnoteParameters::~PADnoteParameters() +{ + deletesamples(); + delete (oscilgen); + delete (resonance); + + delete (FreqEnvelope); + delete (FreqLfo); + delete (AmpEnvelope); + delete (AmpLfo); + delete (GlobalFilter); + delete (FilterEnvelope); + delete (FilterLfo); +} + +void PADnoteParameters::defaults() +{ + Pmode = 0; + Php.base.type = 0; + Php.base.par1 = 80; + Php.freqmult = 0; + Php.modulator.par1 = 0; + Php.modulator.freq = 30; + Php.width = 127; + Php.amp.type = 0; + Php.amp.mode = 0; + Php.amp.par1 = 80; + Php.amp.par2 = 64; + Php.autoscale = true; + Php.onehalf = 0; + + setPbandwidth(500); + Pbwscale = 0; + + resonance->defaults(); + oscilgen->defaults(); + + Phrpos.type = 0; + Phrpos.par1 = 64; + Phrpos.par2 = 64; + Phrpos.par3 = 0; + + Pquality.samplesize = 3; + Pquality.basenote = 4; + Pquality.oct = 3; + Pquality.smpoct = 2; + + PStereo = 1; //stereo + /* Frequency Global Parameters */ + Pfixedfreq = 0; + PfixedfreqET = 0; + PDetune = 8192; //zero + PCoarseDetune = 0; + PDetuneType = 1; + FreqEnvelope->defaults(); + FreqLfo->defaults(); + + /* Amplitude Global Parameters */ + PVolume = 90; + PPanning = 64; //center + PAmpVelocityScaleFunction = 64; + AmpEnvelope->defaults(); + AmpLfo->defaults(); + PPunchStrength = 0; + PPunchTime = 60; + PPunchStretch = 64; + PPunchVelocitySensing = 72; + + /* Filter Global Parameters*/ + PFilterVelocityScale = 64; + PFilterVelocityScaleFunction = 64; + GlobalFilter->defaults(); + FilterEnvelope->defaults(); + FilterLfo->defaults(); + + deletesamples(); +} + +void PADnoteParameters::deletesample(int n) +{ + if((n < 0) || (n >= PAD_MAX_SAMPLES)) + return; + if(sample[n].smp != NULL) { + delete[] sample[n].smp; + sample[n].smp = NULL; + } + sample[n].size = 0; + sample[n].basefreq = 440.0f; +} + +void PADnoteParameters::deletesamples() +{ + for(int i = 0; i < PAD_MAX_SAMPLES; ++i) + deletesample(i); +} + +/* + * Get the harmonic profile (i.e. the frequency distributio of a single harmonic) + */ +float PADnoteParameters::getprofile(float *smp, int size) +{ + for(int i = 0; i < size; ++i) + smp[i] = 0.0f; + const int supersample = 16; + float basepar = powf(2.0f, (1.0f - Php.base.par1 / 127.0f) * 12.0f); + float freqmult = floor(powf(2.0f, + Php.freqmult / 127.0f + * 5.0f) + 0.000001f); + + float modfreq = floor(powf(2.0f, + Php.modulator.freq / 127.0f + * 5.0f) + 0.000001f); + float modpar1 = powf(Php.modulator.par1 / 127.0f, 4.0f) * 5.0f / sqrt( + modfreq); + float amppar1 = + powf(2.0f, powf(Php.amp.par1 / 127.0f, 2.0f) * 10.0f) - 0.999f; + float amppar2 = (1.0f - Php.amp.par2 / 127.0f) * 0.998f + 0.001f; + float width = powf(150.0f / (Php.width + 22.0f), 2.0f); + + for(int i = 0; i < size * supersample; ++i) { + bool makezero = false; + float x = i * 1.0f / (size * (float) supersample); + + float origx = x; + + //do the sizing (width) + x = (x - 0.5f) * width + 0.5f; + if(x < 0.0f) { + x = 0.0f; + makezero = true; + } + else + if(x > 1.0f) { + x = 1.0f; + makezero = true; + } + + //compute the full profile or one half + switch(Php.onehalf) { + case 1: + x = x * 0.5f + 0.5f; + break; + case 2: + x = x * 0.5f; + break; + } + + float x_before_freq_mult = x; + + //do the frequency multiplier + x *= freqmult; + + //do the modulation of the profile + x += sinf(x_before_freq_mult * 3.1415926f * modfreq) * modpar1; + x = fmod(x + 1000.0f, 1.0f) * 2.0f - 1.0f; + + + //this is the base function of the profile + float f; + switch(Php.base.type) { + case 1: + f = expf(-(x * x) * basepar); + if(f < 0.4f) + f = 0.0f; + else + f = 1.0f; + break; + case 2: + f = expf(-(fabs(x)) * sqrt(basepar)); + break; + default: + f = expf(-(x * x) * basepar); + break; + } + if(makezero) + f = 0.0f; + + float amp = 1.0f; + origx = origx * 2.0f - 1.0f; + + //compute the amplitude multiplier + switch(Php.amp.type) { + case 1: + amp = expf(-(origx * origx) * 10.0f * amppar1); + break; + case 2: + amp = 0.5f + * (1.0f + + cosf(3.1415926f * origx * sqrt(amppar1 * 4.0f + 1.0f))); + break; + case 3: + amp = 1.0f + / (powf(origx * (amppar1 * 2.0f + 0.8f), 14.0f) + 1.0f); + break; + } + + //apply the amplitude multiplier + float finalsmp = f; + if(Php.amp.type != 0) + switch(Php.amp.mode) { + case 0: + finalsmp = amp * (1.0f - amppar2) + finalsmp * amppar2; + break; + case 1: + finalsmp *= amp * (1.0f - amppar2) + amppar2; + break; + case 2: + finalsmp = finalsmp + / (amp + powf(amppar2, 4.0f) * 20.0f + 0.0001f); + break; + case 3: + finalsmp = amp + / (finalsmp + + powf(amppar2, 4.0f) * 20.0f + 0.0001f); + break; + } + ; + + smp[i / supersample] += finalsmp / supersample; + } + + //normalize the profile (make the max. to be equal to 1.0f) + float max = 0.0f; + for(int i = 0; i < size; ++i) { + if(smp[i] < 0.0f) + smp[i] = 0.0f; + if(smp[i] > max) + max = smp[i]; + } + if(max < 0.00001f) + max = 1.0f; + for(int i = 0; i < size; ++i) + smp[i] /= max; + + if(!Php.autoscale) + return 0.5f; + + //compute the estimated perceived bandwidth + float sum = 0.0f; + int i; + for(i = 0; i < size / 2 - 2; ++i) { + sum += smp[i] * smp[i] + smp[size - i - 1] * smp[size - i - 1]; + if(sum >= 4.0f) + break; + } + + float result = 1.0f - 2.0f * i / (float) size; + return result; +} + +/* + * Compute the real bandwidth in cents and returns it + * Also, sets the bandwidth parameter + */ +float PADnoteParameters::setPbandwidth(int Pbandwidth) +{ + this->Pbandwidth = Pbandwidth; + float result = powf(Pbandwidth / 1000.0f, 1.1f); + result = powf(10.0f, result * 4.0f) * 0.25f; + return result; +} + +/* + * Get the harmonic(overtone) position + */ +float PADnoteParameters::getNhr(int n) +{ + float result = 1.0f; + float par1 = powf(10.0f, -(1.0f - Phrpos.par1 / 255.0f) * 3.0f); + float par2 = Phrpos.par2 / 255.0f; + + float n0 = n - 1.0f; + float tmp = 0.0f; + int thresh = 0; + switch(Phrpos.type) { + case 1: + thresh = (int)(par2 * par2 * 100.0f) + 1; + if(n < thresh) + result = n; + else + result = 1.0f + n0 + (n0 - thresh + 1.0f) * par1 * 8.0f; + break; + case 2: + thresh = (int)(par2 * par2 * 100.0f) + 1; + if(n < thresh) + result = n; + else + result = 1.0f + n0 - (n0 - thresh + 1.0f) * par1 * 0.90f; + break; + case 3: + tmp = par1 * 100.0f + 1.0f; + result = powf(n0 / tmp, 1.0f - par2 * 0.8f) * tmp + 1.0f; + break; + case 4: + result = n0 + * (1.0f + - par1) + + powf(n0 * 0.1f, par2 * 3.0f + + 1.0f) * par1 * 10.0f + 1.0f; + break; + case 5: + result = n0 + + sinf(n0 * par2 * par2 * PI + * 0.999f) * sqrt(par1) * 2.0f + 1.0f; + break; + case 6: + tmp = powf(par2 * 2.0f, 2.0f) + 0.1f; + result = n0 * powf(1.0f + par1 * powf(n0 * 0.8f, tmp), tmp) + 1.0f; + break; + default: + result = n; + break; + } + + float par3 = Phrpos.par3 / 255.0f; + + float iresult = floor(result + 0.5f); + float dresult = result - iresult; + + result = iresult + (1.0f - par3) * dresult; + + return result; +} + +/* + * Generates the long spectrum for Bandwidth mode (only amplitudes are generated; phases will be random) + */ +void PADnoteParameters::generatespectrum_bandwidthMode(float *spectrum, + int size, + float basefreq, + float *profile, + int profilesize, + float bwadjust) +{ + for(int i = 0; i < size; ++i) + spectrum[i] = 0.0f; + + float harmonics[synth->oscilsize / 2]; + for(int i = 0; i < synth->oscilsize / 2; ++i) + harmonics[i] = 0.0f; + //get the harmonic structure from the oscillator (I am using the frequency amplitudes, only) + oscilgen->get(harmonics, basefreq, false); + + //normalize + float max = 0.0f; + for(int i = 0; i < synth->oscilsize / 2; ++i) + if(harmonics[i] > max) + max = harmonics[i]; + if(max < 0.000001f) + max = 1; + for(int i = 0; i < synth->oscilsize / 2; ++i) + harmonics[i] /= max; + + for(int nh = 1; nh < synth->oscilsize / 2; ++nh) { //for each harmonic + float realfreq = getNhr(nh) * basefreq; + if(realfreq > synth->samplerate_f * 0.49999f) + break; + if(realfreq < 20.0f) + break; + if(harmonics[nh - 1] < 1e-4) + continue; + + //compute the bandwidth of each harmonic + float bandwidthcents = setPbandwidth(Pbandwidth); + float bw = + (powf(2.0f, bandwidthcents / 1200.0f) - 1.0f) * basefreq / bwadjust; + float power = 1.0f; + switch(Pbwscale) { + case 0: + power = 1.0f; + break; + case 1: + power = 0.0f; + break; + case 2: + power = 0.25f; + break; + case 3: + power = 0.5f; + break; + case 4: + power = 0.75f; + break; + case 5: + power = 1.5f; + break; + case 6: + power = 2.0f; + break; + case 7: + power = -0.5f; + break; + } + bw = bw * powf(realfreq / basefreq, power); + int ibw = (int)((bw / (synth->samplerate_f * 0.5f) * size)) + 1; + + float amp = harmonics[nh - 1]; + if(resonance->Penabled) + amp *= resonance->getfreqresponse(realfreq); + + if(ibw > profilesize) { //if the bandwidth is larger than the profilesize + float rap = sqrt((float)profilesize / (float)ibw); + int cfreq = + (int) (realfreq + / (synth->samplerate_f * 0.5f) * size) - ibw / 2; + for(int i = 0; i < ibw; ++i) { + int src = (int)(i * rap * rap); + int spfreq = i + cfreq; + if(spfreq < 0) + continue; + if(spfreq >= size) + break; + spectrum[spfreq] += amp * profile[src] * rap; + } + } + else { //if the bandwidth is smaller than the profilesize + float rap = sqrt((float)ibw / (float)profilesize); + float ibasefreq = realfreq / (synth->samplerate_f * 0.5f) * size; + for(int i = 0; i < profilesize; ++i) { + float idfreq = i / (float)profilesize - 0.5f; + idfreq *= ibw; + int spfreq = (int) (idfreq + ibasefreq); + float fspfreq = fmodf((float)idfreq + ibasefreq, 1.0f); + if(spfreq <= 0) + continue; + if(spfreq >= size - 1) + break; + spectrum[spfreq] += amp * profile[i] * rap + * (1.0f - fspfreq); + spectrum[spfreq + 1] += amp * profile[i] * rap * fspfreq; + } + } + } +} + +/* + * Generates the long spectrum for non-Bandwidth modes (only amplitudes are generated; phases will be random) + */ +void PADnoteParameters::generatespectrum_otherModes(float *spectrum, + int size, + float basefreq) +{ + for(int i = 0; i < size; ++i) + spectrum[i] = 0.0f; + + float harmonics[synth->oscilsize / 2]; + for(int i = 0; i < synth->oscilsize / 2; ++i) + harmonics[i] = 0.0f; + //get the harmonic structure from the oscillator (I am using the frequency amplitudes, only) + oscilgen->get(harmonics, basefreq, false); + + //normalize + float max = 0.0f; + for(int i = 0; i < synth->oscilsize / 2; ++i) + if(harmonics[i] > max) + max = harmonics[i]; + if(max < 0.000001f) + max = 1; + for(int i = 0; i < synth->oscilsize / 2; ++i) + harmonics[i] /= max; + + for(int nh = 1; nh < synth->oscilsize / 2; ++nh) { //for each harmonic + float realfreq = getNhr(nh) * basefreq; + + ///sa fac aici interpolarea si sa am grija daca frecv descresc + + if(realfreq > synth->samplerate_f * 0.49999f) + break; + if(realfreq < 20.0f) + break; +// if (harmonics[nh-1]<1e-4) continue; + + + float amp = harmonics[nh - 1]; + if(resonance->Penabled) + amp *= resonance->getfreqresponse(realfreq); + int cfreq = (int) (realfreq / (synth->samplerate_f * 0.5f) * size); + + spectrum[cfreq] = amp + 1e-9; + } + + if(Pmode != 1) { + int old = 0; + for(int k = 1; k < size; ++k) + if((spectrum[k] > 1e-10) || (k == (size - 1))) { + int delta = k - old; + float val1 = spectrum[old]; + float val2 = spectrum[k]; + float idelta = 1.0f / delta; + for(int i = 0; i < delta; ++i) { + float x = idelta * i; + spectrum[old + i] = val1 * (1.0f - x) + val2 * x; + } + old = k; + } + } +} + +/* + * Applies the parameters (i.e. computes all the samples, based on parameters); + */ +void PADnoteParameters::applyparameters(bool lockmutex) +{ + const int samplesize = (((int) 1) << (Pquality.samplesize + 14)); + int spectrumsize = samplesize / 2; + float *spectrum = new float[spectrumsize]; + int profilesize = 512; + float profile[profilesize]; + + + float bwadjust = getprofile(profile, profilesize); +// for (int i=0;ifreqs2smps(fftfreqs, newsample.smp); //that's all; here is the only ifft for the whole sample; no windows are used ;-) + + + //normalize(rms) + float rms = 0.0f; + for(int i = 0; i < samplesize; ++i) + rms += newsample.smp[i] * newsample.smp[i]; + rms = sqrt(rms); + if(rms < 0.000001f) + rms = 1.0f; + rms *= sqrt(262144.0f / samplesize); + for(int i = 0; i < samplesize; ++i) + newsample.smp[i] *= 1.0f / rms * 50.0f; + + //prepare extra samples used by the linear or cubic interpolation + for(int i = 0; i < extra_samples; ++i) + newsample.smp[i + samplesize] = newsample.smp[i]; + + //replace the current sample with the new computed sample + if(lockmutex) { + pthread_mutex_lock(mutex); + deletesample(nsample); + sample[nsample].smp = newsample.smp; + sample[nsample].size = samplesize; + sample[nsample].basefreq = basefreq * basefreqadjust; + pthread_mutex_unlock(mutex); + } + else { + deletesample(nsample); + sample[nsample].smp = newsample.smp; + sample[nsample].size = samplesize; + sample[nsample].basefreq = basefreq * basefreqadjust; + } + newsample.smp = NULL; + } + delete (fft); + delete[] fftfreqs; + delete[] spectrum; + + //delete the additional samples that might exists and are not useful + if(lockmutex) { + pthread_mutex_lock(mutex); + for(int i = samplemax; i < PAD_MAX_SAMPLES; ++i) + deletesample(i); + pthread_mutex_unlock(mutex); + } + else + for(int i = samplemax; i < PAD_MAX_SAMPLES; ++i) + deletesample(i); + ; +} + +void PADnoteParameters::export2wav(std::string basefilename) +{ + applyparameters(true); + basefilename += "_PADsynth_"; + for(int k = 0; k < PAD_MAX_SAMPLES; ++k) { + if(sample[k].smp == NULL) + continue; + char tmpstr[20]; + snprintf(tmpstr, 20, "_%02d", k + 1); + std::string filename = basefilename + std::string(tmpstr) + ".wav"; + WavFile wav(filename, synth->samplerate, 1); + if(wav.good()) { + int nsmps = sample[k].size; + short int *smps = new short int[nsmps]; + for(int i = 0; i < nsmps; ++i) + smps[i] = (short int)(sample[k].smp[i] * 32767.0f); + wav.writeMonoSamples(nsmps, smps); + } + } +} + +void PADnoteParameters::add2XML(XMLwrapper *xml) +{ + xml->setPadSynth(true); + + xml->addparbool("stereo", PStereo); + xml->addpar("mode", Pmode); + xml->addpar("bandwidth", Pbandwidth); + xml->addpar("bandwidth_scale", Pbwscale); + + xml->beginbranch("HARMONIC_PROFILE"); + xml->addpar("base_type", Php.base.type); + xml->addpar("base_par1", Php.base.par1); + xml->addpar("frequency_multiplier", Php.freqmult); + xml->addpar("modulator_par1", Php.modulator.par1); + xml->addpar("modulator_frequency", Php.modulator.freq); + xml->addpar("width", Php.width); + xml->addpar("amplitude_multiplier_type", Php.amp.type); + xml->addpar("amplitude_multiplier_mode", Php.amp.mode); + xml->addpar("amplitude_multiplier_par1", Php.amp.par1); + xml->addpar("amplitude_multiplier_par2", Php.amp.par2); + xml->addparbool("autoscale", Php.autoscale); + xml->addpar("one_half", Php.onehalf); + xml->endbranch(); + + xml->beginbranch("OSCIL"); + oscilgen->add2XML(xml); + xml->endbranch(); + + xml->beginbranch("RESONANCE"); + resonance->add2XML(xml); + xml->endbranch(); + + xml->beginbranch("HARMONIC_POSITION"); + xml->addpar("type", Phrpos.type); + xml->addpar("parameter1", Phrpos.par1); + xml->addpar("parameter2", Phrpos.par2); + xml->addpar("parameter3", Phrpos.par3); + xml->endbranch(); + + xml->beginbranch("SAMPLE_QUALITY"); + xml->addpar("samplesize", Pquality.samplesize); + xml->addpar("basenote", Pquality.basenote); + xml->addpar("octaves", Pquality.oct); + xml->addpar("samples_per_octave", Pquality.smpoct); + xml->endbranch(); + + xml->beginbranch("AMPLITUDE_PARAMETERS"); + xml->addpar("volume", PVolume); + xml->addpar("panning", PPanning); + xml->addpar("velocity_sensing", PAmpVelocityScaleFunction); + xml->addpar("punch_strength", PPunchStrength); + xml->addpar("punch_time", PPunchTime); + xml->addpar("punch_stretch", PPunchStretch); + xml->addpar("punch_velocity_sensing", PPunchVelocitySensing); + + xml->beginbranch("AMPLITUDE_ENVELOPE"); + AmpEnvelope->add2XML(xml); + xml->endbranch(); + + xml->beginbranch("AMPLITUDE_LFO"); + AmpLfo->add2XML(xml); + xml->endbranch(); + + xml->endbranch(); + + xml->beginbranch("FREQUENCY_PARAMETERS"); + xml->addpar("fixed_freq", Pfixedfreq); + xml->addpar("fixed_freq_et", PfixedfreqET); + xml->addpar("detune", PDetune); + xml->addpar("coarse_detune", PCoarseDetune); + xml->addpar("detune_type", PDetuneType); + + xml->beginbranch("FREQUENCY_ENVELOPE"); + FreqEnvelope->add2XML(xml); + xml->endbranch(); + + xml->beginbranch("FREQUENCY_LFO"); + FreqLfo->add2XML(xml); + xml->endbranch(); + xml->endbranch(); + + xml->beginbranch("FILTER_PARAMETERS"); + xml->addpar("velocity_sensing_amplitude", PFilterVelocityScale); + xml->addpar("velocity_sensing", PFilterVelocityScaleFunction); + + xml->beginbranch("FILTER"); + GlobalFilter->add2XML(xml); + xml->endbranch(); + + xml->beginbranch("FILTER_ENVELOPE"); + FilterEnvelope->add2XML(xml); + xml->endbranch(); + + xml->beginbranch("FILTER_LFO"); + FilterLfo->add2XML(xml); + xml->endbranch(); + xml->endbranch(); +} + +void PADnoteParameters::getfromXML(XMLwrapper *xml) +{ + PStereo = xml->getparbool("stereo", PStereo); + Pmode = xml->getpar127("mode", 0); + Pbandwidth = xml->getpar("bandwidth", Pbandwidth, 0, 1000); + Pbwscale = xml->getpar127("bandwidth_scale", Pbwscale); + + if(xml->enterbranch("HARMONIC_PROFILE")) { + Php.base.type = xml->getpar127("base_type", Php.base.type); + Php.base.par1 = xml->getpar127("base_par1", Php.base.par1); + Php.freqmult = xml->getpar127("frequency_multiplier", + Php.freqmult); + Php.modulator.par1 = xml->getpar127("modulator_par1", + Php.modulator.par1); + Php.modulator.freq = xml->getpar127("modulator_frequency", + Php.modulator.freq); + Php.width = xml->getpar127("width", Php.width); + Php.amp.type = xml->getpar127("amplitude_multiplier_type", + Php.amp.type); + Php.amp.mode = xml->getpar127("amplitude_multiplier_mode", + Php.amp.mode); + Php.amp.par1 = xml->getpar127("amplitude_multiplier_par1", + Php.amp.par1); + Php.amp.par2 = xml->getpar127("amplitude_multiplier_par2", + Php.amp.par2); + Php.autoscale = xml->getparbool("autoscale", Php.autoscale); + Php.onehalf = xml->getpar127("one_half", Php.onehalf); + xml->exitbranch(); + } + + if(xml->enterbranch("OSCIL")) { + oscilgen->getfromXML(xml); + xml->exitbranch(); + } + + if(xml->enterbranch("RESONANCE")) { + resonance->getfromXML(xml); + xml->exitbranch(); + } + + if(xml->enterbranch("HARMONIC_POSITION")) { + Phrpos.type = xml->getpar127("type", Phrpos.type); + Phrpos.par1 = xml->getpar("parameter1", Phrpos.par1, 0, 255); + Phrpos.par2 = xml->getpar("parameter2", Phrpos.par2, 0, 255); + Phrpos.par3 = xml->getpar("parameter3", Phrpos.par3, 0, 255); + xml->exitbranch(); + } + + if(xml->enterbranch("SAMPLE_QUALITY")) { + Pquality.samplesize = xml->getpar127("samplesize", Pquality.samplesize); + Pquality.basenote = xml->getpar127("basenote", Pquality.basenote); + Pquality.oct = xml->getpar127("octaves", Pquality.oct); + Pquality.smpoct = xml->getpar127("samples_per_octave", + Pquality.smpoct); + xml->exitbranch(); + } + + if(xml->enterbranch("AMPLITUDE_PARAMETERS")) { + PVolume = xml->getpar127("volume", PVolume); + PPanning = xml->getpar127("panning", PPanning); + PAmpVelocityScaleFunction = xml->getpar127("velocity_sensing", + PAmpVelocityScaleFunction); + PPunchStrength = xml->getpar127("punch_strength", PPunchStrength); + PPunchTime = xml->getpar127("punch_time", PPunchTime); + PPunchStretch = xml->getpar127("punch_stretch", PPunchStretch); + PPunchVelocitySensing = xml->getpar127("punch_velocity_sensing", + PPunchVelocitySensing); + + xml->enterbranch("AMPLITUDE_ENVELOPE"); + AmpEnvelope->getfromXML(xml); + xml->exitbranch(); + + xml->enterbranch("AMPLITUDE_LFO"); + AmpLfo->getfromXML(xml); + xml->exitbranch(); + + xml->exitbranch(); + } + + if(xml->enterbranch("FREQUENCY_PARAMETERS")) { + Pfixedfreq = xml->getpar127("fixed_freq", Pfixedfreq); + PfixedfreqET = xml->getpar127("fixed_freq_et", PfixedfreqET); + PDetune = xml->getpar("detune", PDetune, 0, 16383); + PCoarseDetune = xml->getpar("coarse_detune", PCoarseDetune, 0, 16383); + PDetuneType = xml->getpar127("detune_type", PDetuneType); + + xml->enterbranch("FREQUENCY_ENVELOPE"); + FreqEnvelope->getfromXML(xml); + xml->exitbranch(); + + xml->enterbranch("FREQUENCY_LFO"); + FreqLfo->getfromXML(xml); + xml->exitbranch(); + xml->exitbranch(); + } + + if(xml->enterbranch("FILTER_PARAMETERS")) { + PFilterVelocityScale = xml->getpar127("velocity_sensing_amplitude", + PFilterVelocityScale); + PFilterVelocityScaleFunction = xml->getpar127( + "velocity_sensing", + PFilterVelocityScaleFunction); + + xml->enterbranch("FILTER"); + GlobalFilter->getfromXML(xml); + xml->exitbranch(); + + xml->enterbranch("FILTER_ENVELOPE"); + FilterEnvelope->getfromXML(xml); + xml->exitbranch(); + + xml->enterbranch("FILTER_LFO"); + FilterLfo->getfromXML(xml); + xml->exitbranch(); + xml->exitbranch(); + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/PADnoteParameters.h b/plugins/zynaddsubfx/zynaddsubfx/src/Params/PADnoteParameters.h new file mode 100644 index 000000000..b3283216a --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/PADnoteParameters.h @@ -0,0 +1,179 @@ +/* + ZynAddSubFX - a software synthesizer + + PADnoteParameters.h - Parameters for PADnote (PADsynth) + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef PAD_NOTE_PARAMETERS_H +#define PAD_NOTE_PARAMETERS_H + +#include "../Misc/XMLwrapper.h" +#include "../DSP/FFTwrapper.h" +#include "../globals.h" +#include "../Synth/OscilGen.h" +#include "../Synth/Resonance.h" +#include "../Misc/Util.h" + +#include "EnvelopeParams.h" +#include "LFOParams.h" +#include "FilterParams.h" +#include "Presets.h" +#include +#include + +class PADnoteParameters:public Presets +{ + public: + PADnoteParameters(FFTwrapper *fft_, pthread_mutex_t *mutex_); + ~PADnoteParameters(); + + void defaults(); + void add2XML(XMLwrapper *xml); + void getfromXML(XMLwrapper *xml); + + //returns a value between 0.0f-1.0f that represents the estimation perceived bandwidth + float getprofile(float *smp, int size); + + //parameters + + //the mode: 0 - bandwidth, 1 - discrete (bandwidth=0), 2 - continous + //the harmonic profile is used only on mode 0 + unsigned char Pmode; + + //Harmonic profile (the frequency distribution of a single harmonic) + struct { + struct { //base function + unsigned char type; + unsigned char par1; + } base; + unsigned char freqmult; //frequency multiplier of the distribution + struct { //the modulator of the distribution + unsigned char par1; + unsigned char freq; + } modulator; + + unsigned char width; //the width of the resulting function after the modulation + struct { //the amplitude multiplier of the harmonic profile + unsigned char mode; + unsigned char type; + unsigned char par1; + unsigned char par2; + } amp; + bool autoscale; //if the scale of the harmonic profile is computed automaticaly + unsigned char onehalf; //what part of the base function is used to make the distribution + } Php; + + + unsigned int Pbandwidth; //the values are from 0 to 1000 + unsigned char Pbwscale; //how the bandwidth is increased according to the harmonic's frequency + + struct { //where are positioned the harmonics (on integer multimplier or different places) + unsigned char type; + unsigned char par1, par2, par3; //0..255 + } Phrpos; + + struct { //quality of the samples (how many samples, the length of them,etc.) + unsigned char samplesize; + unsigned char basenote, oct, smpoct; + } Pquality; + + //frequency parameters + //If the base frequency is fixed to 440 Hz + unsigned char Pfixedfreq; + + /* Equal temperate (this is used only if the Pfixedfreq is enabled) + If this parameter is 0, the frequency is fixed (to 440 Hz); + if this parameter is 64, 1 MIDI halftone -> 1 frequency halftone */ + unsigned char PfixedfreqET; + unsigned short int PDetune; //fine detune + unsigned short int PCoarseDetune; //coarse detune+octave + unsigned char PDetuneType; //detune type + + EnvelopeParams *FreqEnvelope; //Frequency Envelope + LFOParams *FreqLfo; //Frequency LFO + + //Amplitude parameters + unsigned char PStereo; + /* Panning - 0 - random + 1 - left + 64 - center + 127 - right */ + unsigned char PPanning; + + unsigned char PVolume; + + unsigned char PAmpVelocityScaleFunction; + + EnvelopeParams *AmpEnvelope; + + LFOParams *AmpLfo; + + unsigned char PPunchStrength, PPunchTime, PPunchStretch, + PPunchVelocitySensing; + + //Filter Parameters + FilterParams *GlobalFilter; + + // filter velocity sensing + unsigned char PFilterVelocityScale; + + // filter velocity sensing + unsigned char PFilterVelocityScaleFunction; + + EnvelopeParams *FilterEnvelope; + LFOParams *FilterLfo; + + + + + float setPbandwidth(int Pbandwidth); //returns the BandWidth in cents + float getNhr(int n); //gets the n-th overtone position relatively to N harmonic + + void applyparameters(bool lockmutex); + void export2wav(std::string basefilename); + + OscilGen *oscilgen; + Resonance *resonance; + + struct { + int size; + float basefreq; + float *smp; + } sample[PAD_MAX_SAMPLES], newsample; + + private: + void generatespectrum_bandwidthMode(float *spectrum, + int size, + float basefreq, + float *profile, + int profilesize, + float bwadjust); + void generatespectrum_otherModes(float *spectrum, + int size, + float basefreq); + void deletesamples(); + void deletesample(int n); + + FFTwrapper *fft; + pthread_mutex_t *mutex; +}; + + + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/Presets.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Params/Presets.cpp new file mode 100644 index 000000000..d96fc536e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/Presets.cpp @@ -0,0 +1,119 @@ +/* + ZynAddSubFX - a software synthesizer + + Presets.cpp - Presets and Clipboard management + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "Presets.h" +#include + + +Presets::Presets() +{ + type[0] = 0; +} + +Presets::~Presets() +{} + +void Presets::setpresettype(const char *type) +{ + strcpy(this->type, type); +} + +void Presets::copy(const char *name) +{ + XMLwrapper *xml = new XMLwrapper(); + + //used only for the clipboard + if(name == NULL) + xml->minimal = false; + + char type[MAX_PRESETTYPE_SIZE]; + strcpy(type, this->type); + //strcat(type, "n"); + if(name == NULL) + if(strstr(type, "Plfo") != NULL) + strcpy(type, "Plfo"); + + xml->beginbranch(type); + add2XML(xml); + xml->endbranch(); + + if(name == NULL) + presetsstore.copyclipboard(xml, type); + else + presetsstore.copypreset(xml, type, name); + + delete (xml); +} + +void Presets::paste(int npreset) +{ + char type[MAX_PRESETTYPE_SIZE]; + strcpy(type, this->type); + //strcat(type, "n"); + + if(npreset == 0) + if(strstr(type, "Plfo") != NULL) + strcpy(type, "Plfo"); + + XMLwrapper *xml = new XMLwrapper(); + if(npreset == 0) { + if(!checkclipboardtype()) { + delete (xml); + return; + } + if(!presetsstore.pasteclipboard(xml)) { + delete (xml); + return; + } + } + else + if(!presetsstore.pastepreset(xml, npreset)) { + delete (xml); + return; + } + + if(xml->enterbranch(type) == 0) + return; + + defaults(); + getfromXML(xml); + + xml->exitbranch(); + + delete (xml); +} + +bool Presets::checkclipboardtype() +{ + return presetsstore.checkclipboardtype(type); +} + +void Presets::rescanforpresets() +{ + presetsstore.rescanforpresets(type); +} + + +void Presets::deletepreset(int npreset) +{ + presetsstore.deletepreset(npreset); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/Presets.h b/plugins/zynaddsubfx/zynaddsubfx/src/Params/Presets.h new file mode 100644 index 000000000..a64d25e74 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/Presets.h @@ -0,0 +1,56 @@ +/* + ZynAddSubFX - a software synthesizer + + Presets.h - Presets and Clipboard management + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef PRESETS_H +#define PRESETS_H + +#include "../Misc/XMLwrapper.h" + +#include "PresetsStore.h" + +/**Presets and Clipboard management*/ +class Presets +{ + friend class PresetsArray; + public: + Presets(); + virtual ~Presets(); + + virtual void copy(const char *name); /** + + +PresetsArray::PresetsArray() +{ + type[0] = 0; + nelement = -1; +} + +PresetsArray::~PresetsArray() +{} + +void PresetsArray::setpresettype(const char *type) +{ + strcpy(this->type, type); +} + +void PresetsArray::copy(const char *name) +{ + XMLwrapper *xml = new XMLwrapper(); + + //used only for the clipboard + if(name == NULL) + xml->minimal = false; + + char type[MAX_PRESETTYPE_SIZE]; + strcpy(type, this->type); + if(nelement != -1) + strcat(type, "n"); + if(name == NULL) + if(strstr(type, "Plfo") != NULL) + strcpy(type, "Plfo"); + ; + + xml->beginbranch(type); + if(nelement == -1) + add2XML(xml); + else + add2XMLsection(xml, nelement); + xml->endbranch(); + + if(name == NULL) + presetsstore.copyclipboard(xml, type); + else + presetsstore.copypreset(xml, type, name); + + delete (xml); + nelement = -1; +} + +void PresetsArray::paste(int npreset) +{ + char type[MAX_PRESETTYPE_SIZE]; + strcpy(type, this->type); + if(nelement != -1) + strcat(type, "n"); + if(npreset == 0) + if(strstr(type, "Plfo") != NULL) + strcpy(type, "Plfo"); + ; + + XMLwrapper *xml = new XMLwrapper(); + if(npreset == 0) { + if(!checkclipboardtype()) { + nelement = -1; + delete (xml); + return; + } + if(!presetsstore.pasteclipboard(xml)) { + delete (xml); + nelement = -1; + return; + } + } + else + if(!presetsstore.pastepreset(xml, npreset)) { + delete (xml); + nelement = -1; + return; + } + + if(xml->enterbranch(type) == 0) { + nelement = -1; + return; + } + if(nelement == -1) { + defaults(); + getfromXML(xml); + } + else { + defaults(nelement); + getfromXMLsection(xml, nelement); + } + xml->exitbranch(); + + delete (xml); + nelement = -1; +} + +bool PresetsArray::checkclipboardtype() +{ + char type[MAX_PRESETTYPE_SIZE]; + strcpy(type, this->type); + if(nelement != -1) + strcat(type, "n"); + + return presetsstore.checkclipboardtype(type); +} + +void PresetsArray::rescanforpresets() +{ + char type[MAX_PRESETTYPE_SIZE]; + strcpy(type, this->type); + if(nelement != -1) + strcat(type, "n"); + + presetsstore.rescanforpresets(type); +} + +void PresetsArray::setelement(int n) +{ + nelement = n; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/PresetsArray.h b/plugins/zynaddsubfx/zynaddsubfx/src/Params/PresetsArray.h new file mode 100644 index 000000000..724dc0b58 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/PresetsArray.h @@ -0,0 +1,59 @@ +/* + ZynAddSubFX - a software synthesizer + + PresetsArray.h - PresetsArray and Clipboard management + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef PRESETSARRAY_H +#define PRESETSARRAY_H + +#include "../Misc/XMLwrapper.h" + +#include "Presets.h" + +/**PresetsArray and Clipboard management*/ +class PresetsArray:public Presets +{ + public: + PresetsArray(); + virtual ~PresetsArray(); + + void copy(const char *name); /** +#include +#include +#include +#include +#include +#include + +#include "PresetsStore.h" +#include "../Misc/Util.h" + +using namespace std; + +PresetsStore presetsstore; + +PresetsStore::PresetsStore() +{ + clipboard.data = NULL; + clipboard.type[0] = 0; +} + +PresetsStore::~PresetsStore() +{ + if(clipboard.data != NULL) + free(clipboard.data); + clearpresets(); +} + +//Clipboard management + +void PresetsStore::copyclipboard(XMLwrapper *xml, char *type) +{ + strcpy(clipboard.type, type); + if(clipboard.data != NULL) + free(clipboard.data); + clipboard.data = xml->getXMLdata(); +} + +bool PresetsStore::pasteclipboard(XMLwrapper *xml) +{ + if(clipboard.data != NULL) + xml->putXMLdata(clipboard.data); + else + return false; + return true; +} + +bool PresetsStore::checkclipboardtype(const char *type) +{ + //makes LFO's compatible + if((strstr(type, + "Plfo") != NULL) && (strstr(clipboard.type, "Plfo") != NULL)) + return true; + return strcmp(type, clipboard.type) == 0; +} + +//Presets management +void PresetsStore::clearpresets() +{ + presets.clear(); +} + +//a helper function that compares 2 presets[] +bool PresetsStore::presetstruct::operator<(const presetstruct &b) const +{ + return name < b.name; +} + + +void PresetsStore::rescanforpresets(const string &type) +{ + //std::cout << "Scanning For Presets" << std::endl; + //std::cout << "Of Type: " << type << std::endl; + + clearpresets(); + string ftype = "." + type.substr(1) + ".xpz"; + + for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) { + if(config.cfg.presetsDirList[i].empty()) + continue; + + //open directory + string dirname = config.cfg.presetsDirList[i]; + DIR *dir = opendir(dirname.c_str()); + if(dir == NULL) + continue; + struct dirent *fn; + + //check all files in directory + while((fn = readdir(dir))) { + string filename = fn->d_name; + if(filename.find(ftype) == string::npos) + continue; + + //ensure proper path is formed + char tmpc = dirname[dirname.size() - 1]; + const char *tmps; + if((tmpc == '/') || (tmpc == '\\')) + tmps = ""; + else + tmps = "/"; + + string location = "" + dirname + tmps + filename; + + //trim file type off of name + string name = filename.substr(0, filename.find(ftype)); + + //put on list + presets.push_back(presetstruct(location, name)); + } + + closedir(dir); + } + + //sort the presets + sort(presets.begin(), presets.end()); +} + + +void PresetsStore::copypreset(XMLwrapper *xml, char *type, string name) +{ + if(config.cfg.presetsDirList[0].empty()) + return; + + //make the filenames legal + name = legalizeFilename(name); + + //make path legal + const string dirname = config.cfg.presetsDirList[0]; + char tmpc = dirname[dirname.size() - 1]; + const char *tmps; + if((tmpc == '/') || (tmpc == '\\')) + tmps = ""; + else + tmps = "/"; + + string filename("" + dirname + tmps + name + "." + &type[1] + ".xpz"); + + xml->saveXMLfile(filename); +} + +bool PresetsStore::pastepreset(XMLwrapper *xml, unsigned int npreset) +{ + npreset--; + if(npreset >= presets.size()) + return false; + string filename = presets[npreset].file; + if(filename.empty()) + return false; + bool result = (xml->loadXMLfile(filename) >= 0); + return result; +} + +void PresetsStore::deletepreset(unsigned int npreset) +{ + npreset--; + if(npreset >= presets.size()) + return; + string filename = presets[npreset].file; + if(filename.empty()) + return; + remove(filename.c_str()); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/PresetsStore.h b/plugins/zynaddsubfx/zynaddsubfx/src/Params/PresetsStore.h new file mode 100644 index 000000000..a2f48fd21 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/PresetsStore.h @@ -0,0 +1,70 @@ +/* + ZynAddSubFX - a software synthesizer + + PresetsStore.cpp - Presets and Clipboard store + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef PRESETSTORE_H +#define PRESETSTORE_H + +#include +#include +#include "../Misc/XMLwrapper.h" +#include "../Misc/Config.h" + +#define MAX_PRESETTYPE_SIZE 30 + +class PresetsStore +{ + public: + PresetsStore(); + ~PresetsStore(); + + //Clipboard stuff + void copyclipboard(XMLwrapper *xml, char *type); + bool pasteclipboard(XMLwrapper *xml); + bool checkclipboardtype(const char *type); + + //presets stuff + void copypreset(XMLwrapper *xml, char *type, std::string name); + bool pastepreset(XMLwrapper *xml, unsigned int npreset); + void deletepreset(unsigned int npreset); + + struct presetstruct { + presetstruct(std::string _file, std::string _name) + :file(_file), name(_name) {} + bool operator<(const presetstruct &b) const; + std::string file; + std::string name; + }; + std::vector presets; + + void rescanforpresets(const std::string &type); + + private: + struct { + char *data; + char type[MAX_PRESETTYPE_SIZE]; + } clipboard; + + void clearpresets(); +}; + +extern PresetsStore presetsstore; +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/SUBnoteParameters.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Params/SUBnoteParameters.cpp new file mode 100644 index 000000000..5813b268e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/SUBnoteParameters.cpp @@ -0,0 +1,250 @@ +/* + ZynAddSubFX - a software synthesizer + + SUBnoteParameters.cpp - Parameters for SUBnote (SUBsynth) + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "../globals.h" +#include "SUBnoteParameters.h" +#include + +SUBnoteParameters::SUBnoteParameters():Presets() +{ + setpresettype("Psubsynth"); + AmpEnvelope = new EnvelopeParams(64, 1); + AmpEnvelope->ADSRinit_dB(0, 40, 127, 25); + FreqEnvelope = new EnvelopeParams(64, 0); + FreqEnvelope->ASRinit(30, 50, 64, 60); + BandWidthEnvelope = new EnvelopeParams(64, 0); + BandWidthEnvelope->ASRinit_bw(100, 70, 64, 60); + + GlobalFilter = new FilterParams(2, 80, 40); + GlobalFilterEnvelope = new EnvelopeParams(0, 1); + GlobalFilterEnvelope->ADSRinit_filter(64, 40, 64, 70, 60, 64); + + defaults(); +} + + +void SUBnoteParameters::defaults() +{ + PVolume = 96; + PPanning = 64; + PAmpVelocityScaleFunction = 90; + + Pfixedfreq = 0; + PfixedfreqET = 0; + Pnumstages = 2; + Pbandwidth = 40; + Phmagtype = 0; + Pbwscale = 64; + Pstereo = 1; + Pstart = 1; + + PDetune = 8192; + PCoarseDetune = 0; + PDetuneType = 1; + PFreqEnvelopeEnabled = 0; + PBandWidthEnvelopeEnabled = 0; + + for(int n = 0; n < MAX_SUB_HARMONICS; ++n) { + Phmag[n] = 0; + Phrelbw[n] = 64; + } + Phmag[0] = 127; + + PGlobalFilterEnabled = 0; + PGlobalFilterVelocityScale = 64; + PGlobalFilterVelocityScaleFunction = 64; + + AmpEnvelope->defaults(); + FreqEnvelope->defaults(); + BandWidthEnvelope->defaults(); + GlobalFilter->defaults(); + GlobalFilterEnvelope->defaults(); +} + + + +SUBnoteParameters::~SUBnoteParameters() +{ + delete (AmpEnvelope); + delete (FreqEnvelope); + delete (BandWidthEnvelope); + delete (GlobalFilter); + delete (GlobalFilterEnvelope); +} + + + + +void SUBnoteParameters::add2XML(XMLwrapper *xml) +{ + xml->addpar("num_stages", Pnumstages); + xml->addpar("harmonic_mag_type", Phmagtype); + xml->addpar("start", Pstart); + + xml->beginbranch("HARMONICS"); + for(int i = 0; i < MAX_SUB_HARMONICS; ++i) { + if((Phmag[i] == 0) && (xml->minimal)) + continue; + xml->beginbranch("HARMONIC", i); + xml->addpar("mag", Phmag[i]); + xml->addpar("relbw", Phrelbw[i]); + xml->endbranch(); + } + xml->endbranch(); + + xml->beginbranch("AMPLITUDE_PARAMETERS"); + xml->addparbool("stereo", Pstereo); + xml->addpar("volume", PVolume); + xml->addpar("panning", PPanning); + xml->addpar("velocity_sensing", PAmpVelocityScaleFunction); + xml->beginbranch("AMPLITUDE_ENVELOPE"); + AmpEnvelope->add2XML(xml); + xml->endbranch(); + xml->endbranch(); + + xml->beginbranch("FREQUENCY_PARAMETERS"); + xml->addparbool("fixed_freq", Pfixedfreq); + xml->addpar("fixed_freq_et", PfixedfreqET); + + xml->addpar("detune", PDetune); + xml->addpar("coarse_detune", PCoarseDetune); + xml->addpar("detune_type", PDetuneType); + + xml->addpar("bandwidth", Pbandwidth); + xml->addpar("bandwidth_scale", Pbwscale); + + xml->addparbool("freq_envelope_enabled", PFreqEnvelopeEnabled); + if((PFreqEnvelopeEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("FREQUENCY_ENVELOPE"); + FreqEnvelope->add2XML(xml); + xml->endbranch(); + } + + xml->addparbool("band_width_envelope_enabled", PBandWidthEnvelopeEnabled); + if((PBandWidthEnvelopeEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("BANDWIDTH_ENVELOPE"); + BandWidthEnvelope->add2XML(xml); + xml->endbranch(); + } + xml->endbranch(); + + xml->beginbranch("FILTER_PARAMETERS"); + xml->addparbool("enabled", PGlobalFilterEnabled); + if((PGlobalFilterEnabled != 0) || (!xml->minimal)) { + xml->beginbranch("FILTER"); + GlobalFilter->add2XML(xml); + xml->endbranch(); + + xml->addpar("filter_velocity_sensing", + PGlobalFilterVelocityScaleFunction); + xml->addpar("filter_velocity_sensing_amplitude", + PGlobalFilterVelocityScale); + + xml->beginbranch("FILTER_ENVELOPE"); + GlobalFilterEnvelope->add2XML(xml); + xml->endbranch(); + } + xml->endbranch(); +} + +void SUBnoteParameters::getfromXML(XMLwrapper *xml) +{ + Pnumstages = xml->getpar127("num_stages", Pnumstages); + Phmagtype = xml->getpar127("harmonic_mag_type", Phmagtype); + Pstart = xml->getpar127("start", Pstart); + + if(xml->enterbranch("HARMONICS")) { + Phmag[0] = 0; + for(int i = 0; i < MAX_SUB_HARMONICS; ++i) { + if(xml->enterbranch("HARMONIC", i) == 0) + continue; + Phmag[i] = xml->getpar127("mag", Phmag[i]); + Phrelbw[i] = xml->getpar127("relbw", Phrelbw[i]); + xml->exitbranch(); + } + xml->exitbranch(); + } + + if(xml->enterbranch("AMPLITUDE_PARAMETERS")) { + Pstereo = xml->getparbool("stereo", Pstereo); + PVolume = xml->getpar127("volume", PVolume); + PPanning = xml->getpar127("panning", PPanning); + PAmpVelocityScaleFunction = xml->getpar127("velocity_sensing", + PAmpVelocityScaleFunction); + if(xml->enterbranch("AMPLITUDE_ENVELOPE")) { + AmpEnvelope->getfromXML(xml); + xml->exitbranch(); + } + xml->exitbranch(); + } + + if(xml->enterbranch("FREQUENCY_PARAMETERS")) { + Pfixedfreq = xml->getparbool("fixed_freq", Pfixedfreq); + PfixedfreqET = xml->getpar127("fixed_freq_et", PfixedfreqET); + + PDetune = xml->getpar("detune", PDetune, 0, 16383); + PCoarseDetune = xml->getpar("coarse_detune", PCoarseDetune, 0, 16383); + PDetuneType = xml->getpar127("detune_type", PDetuneType); + + Pbandwidth = xml->getpar127("bandwidth", Pbandwidth); + Pbwscale = xml->getpar127("bandwidth_scale", Pbwscale); + + PFreqEnvelopeEnabled = xml->getparbool("freq_envelope_enabled", + PFreqEnvelopeEnabled); + if(xml->enterbranch("FREQUENCY_ENVELOPE")) { + FreqEnvelope->getfromXML(xml); + xml->exitbranch(); + } + + PBandWidthEnvelopeEnabled = xml->getparbool( + "band_width_envelope_enabled", + PBandWidthEnvelopeEnabled); + if(xml->enterbranch("BANDWIDTH_ENVELOPE")) { + BandWidthEnvelope->getfromXML(xml); + xml->exitbranch(); + } + + xml->exitbranch(); + } + + if(xml->enterbranch("FILTER_PARAMETERS")) { + PGlobalFilterEnabled = xml->getparbool("enabled", PGlobalFilterEnabled); + if(xml->enterbranch("FILTER")) { + GlobalFilter->getfromXML(xml); + xml->exitbranch(); + } + + PGlobalFilterVelocityScaleFunction = xml->getpar127( + "filter_velocity_sensing", + PGlobalFilterVelocityScaleFunction); + PGlobalFilterVelocityScale = xml->getpar127( + "filter_velocity_sensing_amplitude", + PGlobalFilterVelocityScale); + + if(xml->enterbranch("FILTER_ENVELOPE")) { + GlobalFilterEnvelope->getfromXML(xml); + xml->exitbranch(); + } + + xml->exitbranch(); + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Params/SUBnoteParameters.h b/plugins/zynaddsubfx/zynaddsubfx/src/Params/SUBnoteParameters.h new file mode 100644 index 000000000..111841e5c --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Params/SUBnoteParameters.h @@ -0,0 +1,103 @@ +/* + ZynAddSubFX - a software synthesizer + + SUBnoteParameters.h - Parameters for SUBnote (SUBsynth) + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef SUB_NOTE_PARAMETERS_H +#define SUB_NOTE_PARAMETERS_H + +#include "../globals.h" +#include "../Misc/XMLwrapper.h" +#include "EnvelopeParams.h" +#include "FilterParams.h" +#include "Presets.h" + +class SUBnoteParameters:public Presets +{ + public: + SUBnoteParameters(); + ~SUBnoteParameters(); + + void add2XML(XMLwrapper *xml); + void defaults(); + void getfromXML(XMLwrapper *xml); + + //Parameters + //AMPLITUDE PARAMETRERS + unsigned char Pstereo; //0 for mono,1 for stereo + unsigned char PVolume; + unsigned char PPanning; + unsigned char PAmpVelocityScaleFunction; + EnvelopeParams *AmpEnvelope; + + //Frequency Parameters + unsigned short int PDetune; + unsigned short int PCoarseDetune; + unsigned char PDetuneType; + unsigned char PFreqEnvelopeEnabled; + EnvelopeParams *FreqEnvelope; + unsigned char PBandWidthEnvelopeEnabled; + EnvelopeParams *BandWidthEnvelope; + + //Filter Parameters (Global) + unsigned char PGlobalFilterEnabled; + FilterParams *GlobalFilter; + unsigned char PGlobalFilterVelocityScale; + unsigned char PGlobalFilterVelocityScaleFunction; + EnvelopeParams *GlobalFilterEnvelope; + + + //Other Parameters + + //If the base frequency is fixed to 440 Hz + unsigned char Pfixedfreq; + + /* Equal temperate (this is used only if the Pfixedfreq is enabled) + If this parameter is 0, the frequency is fixed (to 440 Hz); + if this parameter is 64, 1 MIDI halftone -> 1 frequency halftone */ + unsigned char PfixedfreqET; + + + //how many times the filters are applied + unsigned char Pnumstages; + + //bandwidth + unsigned char Pbandwidth; + + //How the magnitudes are computed (0=linear,1=-60dB,2=-60dB) + unsigned char Phmagtype; + + //Magnitudes + unsigned char Phmag[MAX_SUB_HARMONICS]; + + //Relative BandWidth ("64"=1.0f) + unsigned char Phrelbw[MAX_SUB_HARMONICS]; + + //how much the bandwidth is increased according to lower/higher frequency; 64-default + unsigned char Pbwscale; + + //how the harmonics start("0"=0,"1"=random,"2"=1) + unsigned char Pstart; + + + private: +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/ADnote.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/ADnote.cpp new file mode 100644 index 000000000..f4d169ced --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/ADnote.cpp @@ -0,0 +1,1819 @@ +/* + ZynAddSubFX - a software synthesizer + + ADnote.cpp - The "additive" synthesizer + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include + +#include "../globals.h" +#include "../Misc/Util.h" +#include "../DSP/Filter.h" +#include "OscilGen.h" +#include "ADnote.h" + + +ADnote::ADnote(ADnoteParameters *pars, + Controller *ctl_, + float freq, + float velocity, + int portamento_, + int midinote_, + bool besilent) + :SynthNote(freq, velocity, portamento_, midinote_, besilent) +{ + tmpwavel = new float [synth->buffersize]; + tmpwaver = new float [synth->buffersize]; + bypassl = new float [synth->buffersize]; + bypassr = new float [synth->buffersize]; + + partparams = pars; + ctl = ctl_; + portamento = portamento_; + midinote = midinote_; + NoteEnabled = ON; + basefreq = freq; + if(velocity > 1.0f) + velocity = 1.0f; + this->velocity = velocity; + time = 0.0f; + stereo = pars->GlobalPar.PStereo; + + NoteGlobalPar.Detune = getdetune(pars->GlobalPar.PDetuneType, + pars->GlobalPar.PCoarseDetune, + pars->GlobalPar.PDetune); + bandwidthDetuneMultiplier = pars->getBandwidthDetuneMultiplier(); + + if(pars->GlobalPar.PPanning == 0) + NoteGlobalPar.Panning = RND; + else + NoteGlobalPar.Panning = pars->GlobalPar.PPanning / 128.0f; + + + NoteGlobalPar.FilterCenterPitch = pars->GlobalPar.GlobalFilter->getfreq() //center freq + + pars->GlobalPar.PFilterVelocityScale + / 127.0f * 6.0f //velocity sensing + * (VelF(velocity, + pars->GlobalPar. + PFilterVelocityScaleFunction) - 1); + + if(pars->GlobalPar.PPunchStrength != 0) { + NoteGlobalPar.Punch.Enabled = 1; + NoteGlobalPar.Punch.t = 1.0f; //start from 1.0f and to 0.0f + NoteGlobalPar.Punch.initialvalue = + ((powf(10, 1.5f * pars->GlobalPar.PPunchStrength / 127.0f) - 1.0f) + * VelF(velocity, + pars->GlobalPar.PPunchVelocitySensing)); + float time = + powf(10, 3.0f * pars->GlobalPar.PPunchTime / 127.0f) / 10000.0f; //0.1f .. 100 ms + float stretch = powf(440.0f / freq, + pars->GlobalPar.PPunchStretch / 64.0f); + NoteGlobalPar.Punch.dt = 1.0f / (time * synth->samplerate_f * stretch); + } + else + NoteGlobalPar.Punch.Enabled = 0; + + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + pars->VoicePar[nvoice].OscilSmp->newrandseed(prng()); + NoteVoicePar[nvoice].OscilSmp = NULL; + NoteVoicePar[nvoice].FMSmp = NULL; + NoteVoicePar[nvoice].VoiceOut = NULL; + + NoteVoicePar[nvoice].FMVoice = -1; + unison_size[nvoice] = 1; + + if(pars->VoicePar[nvoice].Enabled == 0) { + NoteVoicePar[nvoice].Enabled = OFF; + continue; //the voice is disabled + } + + unison_stereo_spread[nvoice] = + pars->VoicePar[nvoice].Unison_stereo_spread / 127.0f; + int unison = pars->VoicePar[nvoice].Unison_size; + if(unison < 1) + unison = 1; + + //compute unison + unison_size[nvoice] = unison; + + unison_base_freq_rap[nvoice] = new float[unison]; + unison_freq_rap[nvoice] = new float[unison]; + unison_invert_phase[nvoice] = new bool[unison]; + float unison_spread = pars->getUnisonFrequencySpreadCents( + nvoice); + float unison_real_spread = powf(2.0f, (unison_spread * 0.5f) / 1200.0f); + float unison_vibratto_a = pars->VoicePar[nvoice].Unison_vibratto + / 127.0f; //0.0f .. 1.0f + + + switch(unison) { + case 1: + unison_base_freq_rap[nvoice][0] = 1.0f; //if the unison is not used, always make the only subvoice to have the default note + break; + case 2: { //unison for 2 subvoices + unison_base_freq_rap[nvoice][0] = 1.0f / unison_real_spread; + unison_base_freq_rap[nvoice][1] = unison_real_spread; + }; + break; + default: { //unison for more than 2 subvoices + float unison_values[unison]; + float min = -1e-6, max = 1e-6; + for(int k = 0; k < unison; ++k) { + float step = (k / (float) (unison - 1)) * 2.0f - 1.0f; //this makes the unison spread more uniform + float val = step + (RND * 2.0f - 1.0f) / (unison - 1); + unison_values[k] = val; + if (min > val) { + min = val; + } + if (max < val) { + max = val; + } + } + float diff = max - min; + for(int k = 0; k < unison; ++k) { + unison_values[k] = + (unison_values[k] - (max + min) * 0.5f) / diff; //the lowest value will be -1 and the highest will be 1 + unison_base_freq_rap[nvoice][k] = + powf(2.0f, (unison_spread * unison_values[k]) / 1200); + } + }; + } + + //unison vibrattos + if(unison > 1) + for(int k = 0; k < unison; ++k) //reduce the frequency difference for larger vibrattos + unison_base_freq_rap[nvoice][k] = 1.0f + + (unison_base_freq_rap[ + nvoice][k] - 1.0f) + * (1.0f - unison_vibratto_a); + unison_vibratto[nvoice].step = new float[unison]; + unison_vibratto[nvoice].position = new float[unison]; + unison_vibratto[nvoice].amplitude = + (unison_real_spread - 1.0f) * unison_vibratto_a; + + float increments_per_second = synth->samplerate_f / synth->buffersize_f; + float vibratto_base_period = 0.25f + * powf( + 2.0f, + (1.0f + - pars->VoicePar[nvoice]. + Unison_vibratto_speed + / 127.0f) * 4.0f); + for(int k = 0; k < unison; ++k) { + unison_vibratto[nvoice].position[k] = RND * 1.8f - 0.9f; + //make period to vary randomly from 50% to 200% vibratto base period + float vibratto_period = vibratto_base_period + * powf(2.0f, RND * 2.0f - 1.0f); + + float m = 4.0f / (vibratto_period * increments_per_second); + if(RND < 0.5f) + m = -m; + unison_vibratto[nvoice].step[k] = m; + } + + if(unison == 1) { //no vibratto for a single voice + unison_vibratto[nvoice].step[0] = 0.0f; + unison_vibratto[nvoice].position[0] = 0.0f; + unison_vibratto[nvoice].amplitude = 0.0f; + } + + //phase invert for unison + unison_invert_phase[nvoice][0] = false; + if(unison != 1) { + int inv = pars->VoicePar[nvoice].Unison_invert_phase; + switch(inv) { + case 0: for(int k = 0; k < unison; ++k) + unison_invert_phase[nvoice][k] = false; + break; + case 1: for(int k = 0; k < unison; ++k) + unison_invert_phase[nvoice][k] = (RND > 0.5f); + break; + default: for(int k = 0; k < unison; ++k) + unison_invert_phase[nvoice][k] = + (k % inv == 0) ? true : false; + break; + } + } + + + oscfreqhi[nvoice] = new int[unison]; + oscfreqlo[nvoice] = new float[unison]; + oscfreqhiFM[nvoice] = new unsigned int[unison]; + oscfreqloFM[nvoice] = new float[unison]; + oscposhi[nvoice] = new int[unison]; + oscposlo[nvoice] = new float[unison]; + oscposhiFM[nvoice] = new unsigned int[unison]; + oscposloFM[nvoice] = new float[unison]; + + NoteVoicePar[nvoice].Enabled = ON; + NoteVoicePar[nvoice].fixedfreq = pars->VoicePar[nvoice].Pfixedfreq; + NoteVoicePar[nvoice].fixedfreqET = pars->VoicePar[nvoice].PfixedfreqET; + + //use the Globalpars.detunetype if the detunetype is 0 + if(pars->VoicePar[nvoice].PDetuneType != 0) { + NoteVoicePar[nvoice].Detune = getdetune( + pars->VoicePar[nvoice].PDetuneType, + pars->VoicePar[nvoice]. + PCoarseDetune, + 8192); //coarse detune + NoteVoicePar[nvoice].FineDetune = getdetune( + pars->VoicePar[nvoice].PDetuneType, + 0, + pars->VoicePar[nvoice].PDetune); //fine detune + } + else { + NoteVoicePar[nvoice].Detune = getdetune( + pars->GlobalPar.PDetuneType, + pars->VoicePar[nvoice]. + PCoarseDetune, + 8192); //coarse detune + NoteVoicePar[nvoice].FineDetune = getdetune( + pars->GlobalPar.PDetuneType, + 0, + pars->VoicePar[nvoice].PDetune); //fine detune + } + if(pars->VoicePar[nvoice].PFMDetuneType != 0) + NoteVoicePar[nvoice].FMDetune = getdetune( + pars->VoicePar[nvoice].PFMDetuneType, + pars->VoicePar[nvoice]. + PFMCoarseDetune, + pars->VoicePar[nvoice].PFMDetune); + else + NoteVoicePar[nvoice].FMDetune = getdetune( + pars->GlobalPar.PDetuneType, + pars->VoicePar[nvoice]. + PFMCoarseDetune, + pars->VoicePar[nvoice].PFMDetune); + + + + for(int k = 0; k < unison; ++k) { + oscposhi[nvoice][k] = 0; + oscposlo[nvoice][k] = 0.0f; + oscposhiFM[nvoice][k] = 0; + oscposloFM[nvoice][k] = 0.0f; + } + + //the extra points contains the first point + NoteVoicePar[nvoice].OscilSmp = + new float[synth->oscilsize + OSCIL_SMP_EXTRA_SAMPLES]; + + //Get the voice's oscil or external's voice oscil + int vc = nvoice; + if(pars->VoicePar[nvoice].Pextoscil != -1) + vc = pars->VoicePar[nvoice].Pextoscil; + if(!pars->GlobalPar.Hrandgrouping) + pars->VoicePar[vc].OscilSmp->newrandseed(prng()); + int oscposhi_start = + pars->VoicePar[vc].OscilSmp->get(NoteVoicePar[nvoice].OscilSmp, + getvoicebasefreq(nvoice), + pars->VoicePar[nvoice].Presonance); + + //I store the first elments to the last position for speedups + for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; ++i) + NoteVoicePar[nvoice].OscilSmp[synth->oscilsize + + i] = + NoteVoicePar[nvoice].OscilSmp[i]; + + oscposhi_start += + (int)((pars->VoicePar[nvoice].Poscilphase + - 64.0f) / 128.0f * synth->oscilsize + synth->oscilsize * 4); + oscposhi_start %= synth->oscilsize; + + for(int k = 0; k < unison; ++k) { + oscposhi[nvoice][k] = oscposhi_start; + oscposhi_start = (int)(RND * (synth->oscilsize - 1)); //put random starting point for other subvoices + } + + NoteVoicePar[nvoice].FreqLfo = NULL; + NoteVoicePar[nvoice].FreqEnvelope = NULL; + + NoteVoicePar[nvoice].AmpLfo = NULL; + NoteVoicePar[nvoice].AmpEnvelope = NULL; + + NoteVoicePar[nvoice].VoiceFilterL = NULL; + NoteVoicePar[nvoice].VoiceFilterR = NULL; + NoteVoicePar[nvoice].FilterEnvelope = NULL; + NoteVoicePar[nvoice].FilterLfo = NULL; + + NoteVoicePar[nvoice].FilterCenterPitch = + pars->VoicePar[nvoice].VoiceFilter->getfreq(); + NoteVoicePar[nvoice].filterbypass = + pars->VoicePar[nvoice].Pfilterbypass; + + switch(pars->VoicePar[nvoice].PFMEnabled) { + case 1: + NoteVoicePar[nvoice].FMEnabled = MORPH; + break; + case 2: + NoteVoicePar[nvoice].FMEnabled = RING_MOD; + break; + case 3: + NoteVoicePar[nvoice].FMEnabled = PHASE_MOD; + break; + case 4: + NoteVoicePar[nvoice].FMEnabled = FREQ_MOD; + break; + case 5: + NoteVoicePar[nvoice].FMEnabled = PITCH_MOD; + break; + default: + NoteVoicePar[nvoice].FMEnabled = NONE; + } + + NoteVoicePar[nvoice].FMVoice = pars->VoicePar[nvoice].PFMVoice; + NoteVoicePar[nvoice].FMFreqEnvelope = NULL; + NoteVoicePar[nvoice].FMAmpEnvelope = NULL; + + //Compute the Voice's modulator volume (incl. damping) + float fmvoldamp = powf(440.0f / getvoicebasefreq( + nvoice), + pars->VoicePar[nvoice].PFMVolumeDamp / 64.0f + - 1.0f); + switch(NoteVoicePar[nvoice].FMEnabled) { + case PHASE_MOD: + fmvoldamp = + powf(440.0f / getvoicebasefreq( + nvoice), pars->VoicePar[nvoice].PFMVolumeDamp + / 64.0f); + NoteVoicePar[nvoice].FMVolume = + (expf(pars->VoicePar[nvoice].PFMVolume / 127.0f + * FM_AMP_MULTIPLIER) - 1.0f) * fmvoldamp * 4.0f; + break; + case FREQ_MOD: + NoteVoicePar[nvoice].FMVolume = + (expf(pars->VoicePar[nvoice].PFMVolume / 127.0f + * FM_AMP_MULTIPLIER) - 1.0f) * fmvoldamp * 4.0f; + break; + // case PITCH_MOD:NoteVoicePar[nvoice].FMVolume=(pars->VoicePar[nvoice].PFMVolume/127.0f*8.0f)*fmvoldamp;//??????????? + // break; + default: + if(fmvoldamp > 1.0f) + fmvoldamp = 1.0f; + NoteVoicePar[nvoice].FMVolume = + pars->VoicePar[nvoice].PFMVolume + / 127.0f * fmvoldamp; + } + + //Voice's modulator velocity sensing + NoteVoicePar[nvoice].FMVolume *= + VelF(velocity, + partparams->VoicePar[nvoice].PFMVelocityScaleFunction); + + FMoldsmp[nvoice] = new float [unison]; + for(int k = 0; k < unison; ++k) + FMoldsmp[nvoice][k] = 0.0f; //this is for FM (integration) + + firsttick[nvoice] = 1; + NoteVoicePar[nvoice].DelayTicks = + (int)((expf(pars->VoicePar[nvoice].PDelay / 127.0f + * logf(50.0f)) + - 1.0f) / synth->buffersize_f / 10.0f * synth->samplerate_f); + } + + max_unison = 1; + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) + if(unison_size[nvoice] > max_unison) + max_unison = unison_size[nvoice]; + + + tmpwave_unison = new float *[max_unison]; + for(int k = 0; k < max_unison; ++k) { + tmpwave_unison[k] = new float[synth->buffersize]; + memset(tmpwave_unison[k], 0, synth->bufferbytes); + } + + initparameters(); +} + +// ADlegatonote: This function is (mostly) a copy of ADnote(...) and +// initparameters() stuck together with some lines removed so that it +// only alter the already playing note (to perform legato). It is +// possible I left stuff that is not required for this. +void ADnote::legatonote(float freq, float velocity, int portamento_, + int midinote_, bool externcall) +{ + ADnoteParameters *pars = partparams; + + // Manage legato stuff + if(legato.update(freq, velocity, portamento_, midinote_, externcall)) + return; + + portamento = portamento_; + midinote = midinote_; + basefreq = freq; + + if(velocity > 1.0f) + velocity = 1.0f; + this->velocity = velocity; + + NoteGlobalPar.Detune = getdetune(pars->GlobalPar.PDetuneType, + pars->GlobalPar.PCoarseDetune, + pars->GlobalPar.PDetune); + bandwidthDetuneMultiplier = pars->getBandwidthDetuneMultiplier(); + + if(pars->GlobalPar.PPanning == 0) + NoteGlobalPar.Panning = RND; + else + NoteGlobalPar.Panning = pars->GlobalPar.PPanning / 128.0f; + + //center freq + NoteGlobalPar.FilterCenterPitch = pars->GlobalPar.GlobalFilter->getfreq() + + pars->GlobalPar.PFilterVelocityScale + / 127.0f * 6.0f //velocity sensing + * (VelF(velocity, + pars->GlobalPar. + PFilterVelocityScaleFunction) - 1); + + + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + if(NoteVoicePar[nvoice].Enabled == OFF) + continue; //(gf) Stay the same as first note in legato. + + NoteVoicePar[nvoice].fixedfreq = pars->VoicePar[nvoice].Pfixedfreq; + NoteVoicePar[nvoice].fixedfreqET = pars->VoicePar[nvoice].PfixedfreqET; + + //use the Globalpars.detunetype if the detunetype is 0 + if(pars->VoicePar[nvoice].PDetuneType != 0) { + NoteVoicePar[nvoice].Detune = getdetune( + pars->VoicePar[nvoice].PDetuneType, + pars->VoicePar[nvoice].PCoarseDetune, + 8192); //coarse detune + NoteVoicePar[nvoice].FineDetune = getdetune( + pars->VoicePar[nvoice].PDetuneType, + 0, + pars->VoicePar[nvoice].PDetune); //fine detune + } + else { + NoteVoicePar[nvoice].Detune = getdetune( + pars->GlobalPar.PDetuneType, + pars->VoicePar[nvoice].PCoarseDetune, + 8192); //coarse detune + NoteVoicePar[nvoice].FineDetune = getdetune( + pars->GlobalPar.PDetuneType, + 0, + pars->VoicePar[nvoice].PDetune); //fine detune + } + if(pars->VoicePar[nvoice].PFMDetuneType != 0) + NoteVoicePar[nvoice].FMDetune = getdetune( + pars->VoicePar[nvoice].PFMDetuneType, + pars->VoicePar[nvoice].PFMCoarseDetune, + pars->VoicePar[nvoice].PFMDetune); + else + NoteVoicePar[nvoice].FMDetune = getdetune( + pars->GlobalPar.PDetuneType, + pars->VoicePar[nvoice].PFMCoarseDetune, + pars->VoicePar[nvoice].PFMDetune); + + + //Get the voice's oscil or external's voice oscil + int vc = nvoice; + if(pars->VoicePar[nvoice].Pextoscil != -1) + vc = pars->VoicePar[nvoice].Pextoscil; + if(!pars->GlobalPar.Hrandgrouping) + pars->VoicePar[vc].OscilSmp->newrandseed(prng()); + + pars->VoicePar[vc].OscilSmp->get(NoteVoicePar[nvoice].OscilSmp, + getvoicebasefreq(nvoice), + pars->VoicePar[nvoice].Presonance); //(gf)Modif of the above line. + + //I store the first elments to the last position for speedups + for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; ++i) + NoteVoicePar[nvoice].OscilSmp[synth->oscilsize + + i] = + NoteVoicePar[nvoice].OscilSmp[i]; + + + NoteVoicePar[nvoice].FilterCenterPitch = + pars->VoicePar[nvoice].VoiceFilter->getfreq(); + NoteVoicePar[nvoice].filterbypass = + pars->VoicePar[nvoice].Pfilterbypass; + + + NoteVoicePar[nvoice].FMVoice = pars->VoicePar[nvoice].PFMVoice; + + //Compute the Voice's modulator volume (incl. damping) + float fmvoldamp = powf(440.0f / getvoicebasefreq(nvoice), + pars->VoicePar[nvoice].PFMVolumeDamp / 64.0f + - 1.0f); + + switch(NoteVoicePar[nvoice].FMEnabled) { + case PHASE_MOD: + fmvoldamp = + powf(440.0f / getvoicebasefreq( + nvoice), pars->VoicePar[nvoice].PFMVolumeDamp + / 64.0f); + NoteVoicePar[nvoice].FMVolume = + (expf(pars->VoicePar[nvoice].PFMVolume / 127.0f + * FM_AMP_MULTIPLIER) - 1.0f) * fmvoldamp * 4.0f; + break; + case FREQ_MOD: + NoteVoicePar[nvoice].FMVolume = + (expf(pars->VoicePar[nvoice].PFMVolume / 127.0f + * FM_AMP_MULTIPLIER) - 1.0f) * fmvoldamp * 4.0f; + break; + // case PITCH_MOD:NoteVoicePar[nvoice].FMVolume=(pars->VoicePar[nvoice].PFMVolume/127.0f*8.0f)*fmvoldamp;//??????????? + // break; + default: + if(fmvoldamp > 1.0f) + fmvoldamp = 1.0f; + NoteVoicePar[nvoice].FMVolume = + pars->VoicePar[nvoice].PFMVolume + / 127.0f * fmvoldamp; + } + + //Voice's modulator velocity sensing + NoteVoicePar[nvoice].FMVolume *= + VelF(velocity, + partparams->VoicePar[nvoice].PFMVelocityScaleFunction); + + NoteVoicePar[nvoice].DelayTicks = + (int)((expf(pars->VoicePar[nvoice].PDelay / 127.0f + * logf(50.0f)) + - 1.0f) / synth->buffersize_f / 10.0f * synth->samplerate_f); + } + + /// initparameters(); + + /////////////// + // Altered content of initparameters(): + + int tmp[NUM_VOICES]; + + NoteGlobalPar.Volume = 4.0f + * powf(0.1f, 3.0f + * (1.0f - partparams->GlobalPar.PVolume + / 96.0f)) //-60 dB .. 0 dB + * VelF( + velocity, + partparams->GlobalPar. + PAmpVelocityScaleFunction); //velocity sensing + + globalnewamplitude = NoteGlobalPar.Volume + * NoteGlobalPar.AmpEnvelope->envout_dB() + * NoteGlobalPar.AmpLfo->amplfoout(); + + NoteGlobalPar.FilterQ = partparams->GlobalPar.GlobalFilter->getq(); + NoteGlobalPar.FilterFreqTracking = + partparams->GlobalPar.GlobalFilter->getfreqtracking(basefreq); + + // Forbids the Modulation Voice to be greater or equal than voice + for(int i = 0; i < NUM_VOICES; ++i) + if(NoteVoicePar[i].FMVoice >= i) + NoteVoicePar[i].FMVoice = -1; + + // Voice Parameter init + for(unsigned nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + if(NoteVoicePar[nvoice].Enabled == 0) + continue; + + NoteVoicePar[nvoice].noisetype = partparams->VoicePar[nvoice].Type; + /* Voice Amplitude Parameters Init */ + NoteVoicePar[nvoice].Volume = + powf(0.1f, 3.0f + * (1.0f - partparams->VoicePar[nvoice].PVolume / 127.0f)) // -60 dB .. 0 dB + * VelF(velocity, + partparams->VoicePar[nvoice].PAmpVelocityScaleFunction); //velocity + + if(partparams->VoicePar[nvoice].PVolumeminus != 0) + NoteVoicePar[nvoice].Volume = -NoteVoicePar[nvoice].Volume; + + if(partparams->VoicePar[nvoice].PPanning == 0) + NoteVoicePar[nvoice].Panning = RND; // random panning + else + NoteVoicePar[nvoice].Panning = + partparams->VoicePar[nvoice].PPanning / 128.0f; + + newamplitude[nvoice] = 1.0f; + if((partparams->VoicePar[nvoice].PAmpEnvelopeEnabled != 0) + && (NoteVoicePar[nvoice].AmpEnvelope != NULL)) + newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpEnvelope->envout_dB(); + + + if((partparams->VoicePar[nvoice].PAmpLfoEnabled != 0) + && (NoteVoicePar[nvoice].AmpLfo != NULL)) + newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpLfo->amplfoout(); + + + + NoteVoicePar[nvoice].FilterFreqTracking = + partparams->VoicePar[nvoice].VoiceFilter->getfreqtracking(basefreq); + + /* Voice Modulation Parameters Init */ + if((NoteVoicePar[nvoice].FMEnabled != NONE) + && (NoteVoicePar[nvoice].FMVoice < 0)) { + partparams->VoicePar[nvoice].FMSmp->newrandseed(prng()); + + //Perform Anti-aliasing only on MORPH or RING MODULATION + + int vc = nvoice; + if(partparams->VoicePar[nvoice].PextFMoscil != -1) + vc = partparams->VoicePar[nvoice].PextFMoscil; + + if(!partparams->GlobalPar.Hrandgrouping) + partparams->VoicePar[vc].FMSmp->newrandseed(prng()); + + for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; ++i) + NoteVoicePar[nvoice].FMSmp[synth->oscilsize + i] = + NoteVoicePar[nvoice].FMSmp[i]; + } + + FMnewamplitude[nvoice] = NoteVoicePar[nvoice].FMVolume + * ctl->fmamp.relamp; + + if((partparams->VoicePar[nvoice].PFMAmpEnvelopeEnabled != 0) + && (NoteVoicePar[nvoice].FMAmpEnvelope != NULL)) + FMnewamplitude[nvoice] *= + NoteVoicePar[nvoice].FMAmpEnvelope->envout_dB(); + } + + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + for(unsigned i = nvoice + 1; i < NUM_VOICES; ++i) + tmp[i] = 0; + for(unsigned i = nvoice + 1; i < NUM_VOICES; ++i) + if((NoteVoicePar[i].FMVoice == nvoice) && (tmp[i] == 0)) + tmp[i] = 1; + } +} + + +/* + * Kill a voice of ADnote + */ +void ADnote::KillVoice(int nvoice) +{ + delete [] oscfreqhi[nvoice]; + delete [] oscfreqlo[nvoice]; + delete [] oscfreqhiFM[nvoice]; + delete [] oscfreqloFM[nvoice]; + delete [] oscposhi[nvoice]; + delete [] oscposlo[nvoice]; + delete [] oscposhiFM[nvoice]; + delete [] oscposloFM[nvoice]; + + delete [] unison_base_freq_rap[nvoice]; + delete [] unison_freq_rap[nvoice]; + delete [] unison_invert_phase[nvoice]; + delete [] FMoldsmp[nvoice]; + delete [] unison_vibratto[nvoice].step; + delete [] unison_vibratto[nvoice].position; + + NoteVoicePar[nvoice].kill(); +} + +/* + * Kill the note + */ +void ADnote::KillNote() +{ + for(unsigned nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + if(NoteVoicePar[nvoice].Enabled == ON) + KillVoice(nvoice); + + if(NoteVoicePar[nvoice].VoiceOut) + delete NoteVoicePar[nvoice].VoiceOut; + NoteVoicePar[nvoice].VoiceOut = NULL; + } + + NoteGlobalPar.kill(); + + NoteEnabled = OFF; +} + +ADnote::~ADnote() +{ + if(NoteEnabled == ON) + KillNote(); + delete [] tmpwavel; + delete [] tmpwaver; + delete [] bypassl; + delete [] bypassr; + for(int k = 0; k < max_unison; ++k) + delete[] tmpwave_unison[k]; + delete[] tmpwave_unison; +} + + +/* + * Init the parameters + */ +void ADnote::initparameters() +{ + int tmp[NUM_VOICES]; + + // Global Parameters + NoteGlobalPar.initparameters(partparams->GlobalPar, basefreq, velocity, + stereo); + + NoteGlobalPar.AmpEnvelope->envout_dB(); //discard the first envelope output + globalnewamplitude = NoteGlobalPar.Volume + * NoteGlobalPar.AmpEnvelope->envout_dB() + * NoteGlobalPar.AmpLfo->amplfoout(); + + // Forbids the Modulation Voice to be greater or equal than voice + for(int i = 0; i < NUM_VOICES; ++i) + if(NoteVoicePar[i].FMVoice >= i) + NoteVoicePar[i].FMVoice = -1; + + // Voice Parameter init + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + Voice &vce = NoteVoicePar[nvoice]; + ADnoteVoiceParam ¶m = partparams->VoicePar[nvoice]; + + if(vce.Enabled == 0) + continue; + + vce.noisetype = param.Type; + /* Voice Amplitude Parameters Init */ + vce.Volume = powf(0.1f, 3.0f * (1.0f - param.PVolume / 127.0f)) // -60dB..0dB + * VelF(velocity, param.PAmpVelocityScaleFunction); + + if(param.PVolumeminus) + vce.Volume = -vce.Volume; + + if(param.PPanning == 0) + vce.Panning = RND; // random panning + else + vce.Panning = param.PPanning / 128.0f; + + newamplitude[nvoice] = 1.0f; + if(param.PAmpEnvelopeEnabled) { + vce.AmpEnvelope = new Envelope(param.AmpEnvelope, basefreq); + vce.AmpEnvelope->envout_dB(); //discard the first envelope sample + newamplitude[nvoice] *= vce.AmpEnvelope->envout_dB(); + } + + if(param.PAmpLfoEnabled) { + vce.AmpLfo = new LFO(param.AmpLfo, basefreq); + newamplitude[nvoice] *= vce.AmpLfo->amplfoout(); + } + + /* Voice Frequency Parameters Init */ + if(param.PFreqEnvelopeEnabled != 0) + vce.FreqEnvelope = new Envelope(param.FreqEnvelope, basefreq); + + if(param.PFreqLfoEnabled != 0) + vce.FreqLfo = new LFO(param.FreqLfo, basefreq); + + /* Voice Filter Parameters Init */ + if(param.PFilterEnabled != 0) { + vce.VoiceFilterL = Filter::generate(param.VoiceFilter); + vce.VoiceFilterR = Filter::generate(param.VoiceFilter); + } + + if(param.PFilterEnvelopeEnabled != 0) + vce.FilterEnvelope = new Envelope(param.FilterEnvelope, basefreq); + + if(param.PFilterLfoEnabled != 0) + vce.FilterLfo = new LFO(param.FilterLfo, basefreq); + + vce.FilterFreqTracking = + param.VoiceFilter->getfreqtracking(basefreq); + + /* Voice Modulation Parameters Init */ + if((vce.FMEnabled != NONE) && (vce.FMVoice < 0)) { + param.FMSmp->newrandseed(prng()); + vce.FMSmp = new float[synth->oscilsize + OSCIL_SMP_EXTRA_SAMPLES]; + + //Perform Anti-aliasing only on MORPH or RING MODULATION + + int vc = nvoice; + if(param.PextFMoscil != -1) + vc = param.PextFMoscil; + + float tmp = 1.0f; + if((partparams->VoicePar[vc].FMSmp->Padaptiveharmonics != 0) + || (vce.FMEnabled == MORPH) + || (vce.FMEnabled == RING_MOD)) + tmp = getFMvoicebasefreq(nvoice); + + if(!partparams->GlobalPar.Hrandgrouping) + partparams->VoicePar[vc].FMSmp->newrandseed(prng()); + + for(int k = 0; k < unison_size[nvoice]; ++k) + oscposhiFM[nvoice][k] = (oscposhi[nvoice][k] + + partparams->VoicePar[vc].FMSmp->get( + vce.FMSmp, tmp)) + % synth->oscilsize; + + for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; ++i) + vce.FMSmp[synth->oscilsize + i] = vce.FMSmp[i]; + int oscposhiFM_add = + (int)((param.PFMoscilphase + - 64.0f) / 128.0f * synth->oscilsize + + synth->oscilsize * 4); + for(int k = 0; k < unison_size[nvoice]; ++k) { + oscposhiFM[nvoice][k] += oscposhiFM_add; + oscposhiFM[nvoice][k] %= synth->oscilsize; + } + } + + if(param.PFMFreqEnvelopeEnabled != 0) + vce.FMFreqEnvelope = new Envelope(param.FMFreqEnvelope, basefreq); + + FMnewamplitude[nvoice] = vce.FMVolume * ctl->fmamp.relamp; + + if(param.PFMAmpEnvelopeEnabled != 0) { + vce.FMAmpEnvelope = new Envelope(param.FMAmpEnvelope, + basefreq); + FMnewamplitude[nvoice] *= vce.FMAmpEnvelope->envout_dB(); + } + } + + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + for(int i = nvoice + 1; i < NUM_VOICES; ++i) + tmp[i] = 0; + for(int i = nvoice + 1; i < NUM_VOICES; ++i) + if((NoteVoicePar[i].FMVoice == nvoice) && (tmp[i] == 0)) { + NoteVoicePar[nvoice].VoiceOut = new float[synth->buffersize]; + tmp[i] = 1; + } + + if(NoteVoicePar[nvoice].VoiceOut) + memset(NoteVoicePar[nvoice].VoiceOut, 0, synth->bufferbytes); + } +} + + +/* + * Computes the relative frequency of each unison voice and it's vibratto + * This must be called before setfreq* functions + */ +void ADnote::compute_unison_freq_rap(int nvoice) { + if(unison_size[nvoice] == 1) { //no unison + unison_freq_rap[nvoice][0] = 1.0f; + return; + } + float relbw = ctl->bandwidth.relbw * bandwidthDetuneMultiplier; + for(int k = 0; k < unison_size[nvoice]; ++k) { + float pos = unison_vibratto[nvoice].position[k]; + float step = unison_vibratto[nvoice].step[k]; + pos += step; + if(pos <= -1.0f) { + pos = -1.0f; + step = -step; + } + if(pos >= 1.0f) { + pos = 1.0f; + step = -step; + } + float vibratto_val = (pos - 0.333333333f * pos * pos * pos) * 1.5f; //make the vibratto lfo smoother + unison_freq_rap[nvoice][k] = 1.0f + + ((unison_base_freq_rap[nvoice][k] + - 1.0f) + vibratto_val + * unison_vibratto[nvoice].amplitude) + * relbw; + + unison_vibratto[nvoice].position[k] = pos; + step = unison_vibratto[nvoice].step[k] = step; + } +} + + +/* + * Computes the frequency of an oscillator + */ +void ADnote::setfreq(int nvoice, float in_freq) +{ + for(int k = 0; k < unison_size[nvoice]; ++k) { + float freq = fabs(in_freq) * unison_freq_rap[nvoice][k]; + float speed = freq * synth->oscilsize_f / synth->samplerate_f; + if(speed > synth->oscilsize_f) + speed = synth->oscilsize_f; + + F2I(speed, oscfreqhi[nvoice][k]); + oscfreqlo[nvoice][k] = speed - floor(speed); + } +} + +/* + * Computes the frequency of an modullator oscillator + */ +void ADnote::setfreqFM(int nvoice, float in_freq) +{ + for(int k = 0; k < unison_size[nvoice]; ++k) { + float freq = fabs(in_freq) * unison_freq_rap[nvoice][k]; + float speed = freq * synth->oscilsize_f / synth->samplerate_f; + if(speed > synth->samplerate_f) + speed = synth->samplerate_f; + + F2I(speed, oscfreqhiFM[nvoice][k]); + oscfreqloFM[nvoice][k] = speed - floor(speed); + } +} + +/* + * Get Voice base frequency + */ +float ADnote::getvoicebasefreq(int nvoice) const +{ + float detune = NoteVoicePar[nvoice].Detune / 100.0f + + NoteVoicePar[nvoice].FineDetune / 100.0f + * ctl->bandwidth.relbw * bandwidthDetuneMultiplier + + NoteGlobalPar.Detune / 100.0f; + + if(NoteVoicePar[nvoice].fixedfreq == 0) + return this->basefreq * powf(2, detune / 12.0f); + else { //the fixed freq is enabled + float fixedfreq = 440.0f; + int fixedfreqET = NoteVoicePar[nvoice].fixedfreqET; + if(fixedfreqET != 0) { //if the frequency varies according the keyboard note + float tmp = + (midinote + - 69.0f) / 12.0f + * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f); + if(fixedfreqET <= 64) + fixedfreq *= powf(2.0f, tmp); + else + fixedfreq *= powf(3.0f, tmp); + } + return fixedfreq * powf(2.0f, detune / 12.0f); + } +} + +/* + * Get Voice's Modullator base frequency + */ +float ADnote::getFMvoicebasefreq(int nvoice) const +{ + float detune = NoteVoicePar[nvoice].FMDetune / 100.0f; + return getvoicebasefreq(nvoice) * powf(2, detune / 12.0f); +} + +/* + * Computes all the parameters for each tick + */ +void ADnote::computecurrentparameters() +{ + int nvoice; + float voicefreq, voicepitch, filterpitch, filterfreq, FMfreq, + FMrelativepitch, globalpitch, globalfilterpitch; + globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout() + + NoteGlobalPar.FreqLfo->lfoout() + * ctl->modwheel.relmod); + globaloldamplitude = globalnewamplitude; + globalnewamplitude = NoteGlobalPar.Volume + * NoteGlobalPar.AmpEnvelope->envout_dB() + * NoteGlobalPar.AmpLfo->amplfoout(); + + globalfilterpitch = NoteGlobalPar.FilterEnvelope->envout() + + NoteGlobalPar.FilterLfo->lfoout() + + NoteGlobalPar.FilterCenterPitch; + + float tmpfilterfreq = globalfilterpitch + ctl->filtercutoff.relfreq + + NoteGlobalPar.FilterFreqTracking; + + tmpfilterfreq = Filter::getrealfreq(tmpfilterfreq); + + float globalfilterq = NoteGlobalPar.FilterQ * ctl->filterq.relq; + NoteGlobalPar.GlobalFilterL->setfreq_and_q(tmpfilterfreq, globalfilterq); + if(stereo != 0) + NoteGlobalPar.GlobalFilterR->setfreq_and_q(tmpfilterfreq, globalfilterq); + + //compute the portamento, if it is used by this note + float portamentofreqrap = 1.0f; + if(portamento != 0) { //this voice use portamento + portamentofreqrap = ctl->portamento.freqrap; + if(ctl->portamento.used == 0) //the portamento has finished + portamento = 0; //this note is no longer "portamented" + } + + //compute parameters for all voices + for(nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + if(NoteVoicePar[nvoice].Enabled != ON) + continue; + NoteVoicePar[nvoice].DelayTicks -= 1; + if(NoteVoicePar[nvoice].DelayTicks > 0) + continue; + + compute_unison_freq_rap(nvoice); + + /*******************/ + /* Voice Amplitude */ + /*******************/ + oldamplitude[nvoice] = newamplitude[nvoice]; + newamplitude[nvoice] = 1.0f; + + if(NoteVoicePar[nvoice].AmpEnvelope != NULL) + newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpEnvelope->envout_dB(); + + if(NoteVoicePar[nvoice].AmpLfo != NULL) + newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpLfo->amplfoout(); + + /****************/ + /* Voice Filter */ + /****************/ + if(NoteVoicePar[nvoice].VoiceFilterL != NULL) { + filterpitch = NoteVoicePar[nvoice].FilterCenterPitch; + + if(NoteVoicePar[nvoice].FilterEnvelope != NULL) + filterpitch += NoteVoicePar[nvoice].FilterEnvelope->envout(); + + if(NoteVoicePar[nvoice].FilterLfo != NULL) + filterpitch += NoteVoicePar[nvoice].FilterLfo->lfoout(); + + filterfreq = filterpitch + NoteVoicePar[nvoice].FilterFreqTracking; + filterfreq = Filter::getrealfreq(filterfreq); + + NoteVoicePar[nvoice].VoiceFilterL->setfreq(filterfreq); + if(stereo && NoteVoicePar[nvoice].VoiceFilterR) + NoteVoicePar[nvoice].VoiceFilterR->setfreq(filterfreq); + } + + if(NoteVoicePar[nvoice].noisetype == 0) { //compute only if the voice isn't noise + /*******************/ + /* Voice Frequency */ + /*******************/ + voicepitch = 0.0f; + if(NoteVoicePar[nvoice].FreqLfo != NULL) + voicepitch += NoteVoicePar[nvoice].FreqLfo->lfoout() / 100.0f + * ctl->bandwidth.relbw; + + if(NoteVoicePar[nvoice].FreqEnvelope != NULL) + voicepitch += NoteVoicePar[nvoice].FreqEnvelope->envout() + / 100.0f; + voicefreq = getvoicebasefreq(nvoice) + * powf(2, (voicepitch + globalpitch) / 12.0f); //Hz frequency + voicefreq *= ctl->pitchwheel.relfreq; //change the frequency by the controller + setfreq(nvoice, voicefreq * portamentofreqrap); + + /***************/ + /* Modulator */ + /***************/ + if(NoteVoicePar[nvoice].FMEnabled != NONE) { + FMrelativepitch = NoteVoicePar[nvoice].FMDetune / 100.0f; + if(NoteVoicePar[nvoice].FMFreqEnvelope != NULL) + FMrelativepitch += + NoteVoicePar[nvoice].FMFreqEnvelope->envout() / 100; + FMfreq = + powf(2.0f, FMrelativepitch + / 12.0f) * voicefreq * portamentofreqrap; + setfreqFM(nvoice, FMfreq); + + FMoldamplitude[nvoice] = FMnewamplitude[nvoice]; + FMnewamplitude[nvoice] = NoteVoicePar[nvoice].FMVolume + * ctl->fmamp.relamp; + if(NoteVoicePar[nvoice].FMAmpEnvelope != NULL) + FMnewamplitude[nvoice] *= + NoteVoicePar[nvoice].FMAmpEnvelope->envout_dB(); + } + } + } + time += synth->buffersize_f / synth->samplerate_f; +} + + +/* + * Fadein in a way that removes clicks but keep sound "punchy" + */ +inline void ADnote::fadein(float *smps) const +{ + int zerocrossings = 0; + for(int i = 1; i < synth->buffersize; ++i) + if((smps[i - 1] < 0.0f) && (smps[i] > 0.0f)) + zerocrossings++; //this is only the possitive crossings + + float tmp = (synth->buffersize_f - 1.0f) / (zerocrossings + 1) / 3.0f; + if(tmp < 8.0f) + tmp = 8.0f; + + int n; + F2I(tmp, n); //how many samples is the fade-in + if(n > synth->buffersize) + n = synth->buffersize; + for(int i = 0; i < n; ++i) { //fade-in + float tmp = 0.5f - cosf((float)i / (float) n * PI) * 0.5f; + smps[i] *= tmp; + } +} + +/* + * Computes the Oscillator (Without Modulation) - LinearInterpolation + */ + +/* As the code here is a bit odd due to optimization, here is what happens + * First the current possition and frequency are retrieved from the running + * state. These are broken up into high and low portions to indicate how many + * samples are skipped in one step and how many fractional samples are skipped. + * Outside of this method the fractional samples are just handled with floating + * point code, but that's a bit slower than it needs to be. In this code the low + * portions are known to exist between 0.0 and 1.0 and it is known that they are + * stored in single precision floating point IEEE numbers. This implies that + * a maximum of 24 bits are significant. The below code does your standard + * linear interpolation that you'll see throughout this codebase, but by + * sticking to integers for tracking the overflow of the low portion, around 15% + * of the execution time was shaved off in the ADnote test. + */ +inline void ADnote::ComputeVoiceOscillator_LinearInterpolation(int nvoice) +{ + for(int k = 0; k < unison_size[nvoice]; ++k) { + int poshi = oscposhi[nvoice][k]; + int poslo = oscposlo[nvoice][k] * (1<<24); + int freqhi = oscfreqhi[nvoice][k]; + int freqlo = oscfreqlo[nvoice][k] * (1<<24); + float *smps = NoteVoicePar[nvoice].OscilSmp; + float *tw = tmpwave_unison[k]; + assert(oscfreqlo[nvoice][k] < 1.0f); + for(int i = 0; i < synth->buffersize; ++i) { + tw[i] = (smps[poshi] * ((1<<24) - poslo) + smps[poshi + 1] * poslo)/(1.0f*(1<<24)); + poslo += freqlo; + poshi += freqhi + (poslo>>24); + poslo &= 0xffffff; + poshi &= synth->oscilsize - 1; + } + oscposhi[nvoice][k] = poshi; + oscposlo[nvoice][k] = poslo/(1.0f*(1<<24)); + } +} + + + +/* + * Computes the Oscillator (Without Modulation) - CubicInterpolation + * + The differences from the Linear are to little to deserve to be used. This is because I am using a large synth->oscilsize (>512) +inline void ADnote::ComputeVoiceOscillator_CubicInterpolation(int nvoice){ + int i,poshi; + float poslo; + + poshi=oscposhi[nvoice]; + poslo=oscposlo[nvoice]; + float *smps=NoteVoicePar[nvoice].OscilSmp; + float xm1,x0,x1,x2,a,b,c; + for (i=0;ibuffersize;i++){ + xm1=smps[poshi]; + x0=smps[poshi+1]; + x1=smps[poshi+2]; + x2=smps[poshi+3]; + a=(3.0f * (x0-x1) - xm1 + x2) / 2.0f; + b = 2.0f*x1 + xm1 - (5.0f*x0 + x2) / 2.0f; + c = (x1 - xm1) / 2.0f; + tmpwave[i]=(((a * poslo) + b) * poslo + c) * poslo + x0; + printf("a\n"); + //tmpwave[i]=smps[poshi]*(1.0f-poslo)+smps[poshi+1]*poslo; + poslo+=oscfreqlo[nvoice]; + if (poslo>=1.0f) { + poslo-=1.0f; + poshi++; + }; + poshi+=oscfreqhi[nvoice]; + poshi&=synth->oscilsize-1; + }; + oscposhi[nvoice]=poshi; + oscposlo[nvoice]=poslo; +}; +*/ +/* + * Computes the Oscillator (Morphing) + */ +inline void ADnote::ComputeVoiceOscillatorMorph(int nvoice) +{ + int i; + float amp; + ComputeVoiceOscillator_LinearInterpolation(nvoice); + if(FMnewamplitude[nvoice] > 1.0f) + FMnewamplitude[nvoice] = 1.0f; + if(FMoldamplitude[nvoice] > 1.0f) + FMoldamplitude[nvoice] = 1.0f; + + if(NoteVoicePar[nvoice].FMVoice >= 0) { + //if I use VoiceOut[] as modullator + int FMVoice = NoteVoicePar[nvoice].FMVoice; + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + for(i = 0; i < synth->buffersize; ++i) { + amp = INTERPOLATE_AMPLITUDE(FMoldamplitude[nvoice], + FMnewamplitude[nvoice], + i, + synth->buffersize); + tw[i] = tw[i] + * (1.0f + - amp) + amp * NoteVoicePar[FMVoice].VoiceOut[i]; + } + } + } + else + for(int k = 0; k < unison_size[nvoice]; ++k) { + int poshiFM = oscposhiFM[nvoice][k]; + float posloFM = oscposloFM[nvoice][k]; + int freqhiFM = oscfreqhiFM[nvoice][k]; + float freqloFM = oscfreqloFM[nvoice][k]; + float *tw = tmpwave_unison[k]; + + for(i = 0; i < synth->buffersize; ++i) { + amp = INTERPOLATE_AMPLITUDE(FMoldamplitude[nvoice], + FMnewamplitude[nvoice], + i, + synth->buffersize); + tw[i] = tw[i] * (1.0f - amp) + amp + * (NoteVoicePar[nvoice].FMSmp[poshiFM] * (1 - posloFM) + + NoteVoicePar[nvoice].FMSmp[poshiFM + 1] * posloFM); + posloFM += freqloFM; + if(posloFM >= 1.0f) { + posloFM -= 1.0f; + poshiFM++; + } + poshiFM += freqhiFM; + poshiFM &= synth->oscilsize - 1; + } + oscposhiFM[nvoice][k] = poshiFM; + oscposloFM[nvoice][k] = posloFM; + } +} + +/* + * Computes the Oscillator (Ring Modulation) + */ +inline void ADnote::ComputeVoiceOscillatorRingModulation(int nvoice) +{ + int i; + float amp; + ComputeVoiceOscillator_LinearInterpolation(nvoice); + if(FMnewamplitude[nvoice] > 1.0f) + FMnewamplitude[nvoice] = 1.0f; + if(FMoldamplitude[nvoice] > 1.0f) + FMoldamplitude[nvoice] = 1.0f; + if(NoteVoicePar[nvoice].FMVoice >= 0) + // if I use VoiceOut[] as modullator + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + for(i = 0; i < synth->buffersize; ++i) { + amp = INTERPOLATE_AMPLITUDE(FMoldamplitude[nvoice], + FMnewamplitude[nvoice], + i, + synth->buffersize); + int FMVoice = NoteVoicePar[nvoice].FMVoice; + tw[i] *= (1.0f - amp) + amp * NoteVoicePar[FMVoice].VoiceOut[i]; + } + } + else + for(int k = 0; k < unison_size[nvoice]; ++k) { + int poshiFM = oscposhiFM[nvoice][k]; + float posloFM = oscposloFM[nvoice][k]; + int freqhiFM = oscfreqhiFM[nvoice][k]; + float freqloFM = oscfreqloFM[nvoice][k]; + float *tw = tmpwave_unison[k]; + + for(i = 0; i < synth->buffersize; ++i) { + amp = INTERPOLATE_AMPLITUDE(FMoldamplitude[nvoice], + FMnewamplitude[nvoice], + i, + synth->buffersize); + tw[i] *= (NoteVoicePar[nvoice].FMSmp[poshiFM] * (1.0f - posloFM) + + NoteVoicePar[nvoice].FMSmp[poshiFM + + 1] * posloFM) * amp + + (1.0f - amp); + posloFM += freqloFM; + if(posloFM >= 1.0f) { + posloFM -= 1.0f; + poshiFM++; + } + poshiFM += freqhiFM; + poshiFM &= synth->oscilsize - 1; + } + oscposhiFM[nvoice][k] = poshiFM; + oscposloFM[nvoice][k] = posloFM; + } +} + + + +/* + * Computes the Oscillator (Phase Modulation or Frequency Modulation) + */ +inline void ADnote::ComputeVoiceOscillatorFrequencyModulation(int nvoice, + int FMmode) +{ + int carposhi = 0; + int i, FMmodfreqhi = 0; + float FMmodfreqlo = 0, carposlo = 0; + + if(NoteVoicePar[nvoice].FMVoice >= 0) + //if I use VoiceOut[] as modulator + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + memcpy(tw, NoteVoicePar[NoteVoicePar[nvoice].FMVoice].VoiceOut, + synth->bufferbytes); + } + else + //Compute the modulator and store it in tmpwave_unison[][] + for(int k = 0; k < unison_size[nvoice]; ++k) { + int poshiFM = oscposhiFM[nvoice][k]; + float posloFM = oscposloFM[nvoice][k]; + int freqhiFM = oscfreqhiFM[nvoice][k]; + float freqloFM = oscfreqloFM[nvoice][k]; + float *tw = tmpwave_unison[k]; + + for(i = 0; i < synth->buffersize; ++i) { + tw[i] = + (NoteVoicePar[nvoice].FMSmp[poshiFM] * (1.0f - posloFM) + + NoteVoicePar[nvoice].FMSmp[poshiFM + 1] * posloFM); + posloFM += freqloFM; + if(posloFM >= 1.0f) { + posloFM = fmod(posloFM, 1.0f); + poshiFM++; + } + poshiFM += freqhiFM; + poshiFM &= synth->oscilsize - 1; + } + oscposhiFM[nvoice][k] = poshiFM; + oscposloFM[nvoice][k] = posloFM; + } + // Amplitude interpolation + if(ABOVE_AMPLITUDE_THRESHOLD(FMoldamplitude[nvoice], + FMnewamplitude[nvoice])) + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + for(i = 0; i < synth->buffersize; ++i) + tw[i] *= INTERPOLATE_AMPLITUDE(FMoldamplitude[nvoice], + FMnewamplitude[nvoice], + i, + synth->buffersize); + } + else + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + for(i = 0; i < synth->buffersize; ++i) + tw[i] *= FMnewamplitude[nvoice]; + } + + + //normalize: makes all sample-rates, oscil_sizes to produce same sound + if(FMmode != 0) { //Frequency modulation + float normalize = synth->oscilsize_f / 262144.0f * 44100.0f + / synth->samplerate_f; + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + float fmold = FMoldsmp[nvoice][k]; + for(i = 0; i < synth->buffersize; ++i) { + fmold = fmod(fmold + tw[i] * normalize, synth->oscilsize); + tw[i] = fmold; + } + FMoldsmp[nvoice][k] = fmold; + } + } + else { //Phase modulation + float normalize = synth->oscilsize_f / 262144.0f; + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + for(i = 0; i < synth->buffersize; ++i) + tw[i] *= normalize; + } + } + + //do the modulation + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + int poshi = oscposhi[nvoice][k]; + float poslo = oscposlo[nvoice][k]; + int freqhi = oscfreqhi[nvoice][k]; + float freqlo = oscfreqlo[nvoice][k]; + + for(i = 0; i < synth->buffersize; ++i) { + F2I(tw[i], FMmodfreqhi); + FMmodfreqlo = fmod(tw[i] + 0.0000000001f, 1.0f); + if(FMmodfreqhi < 0) + FMmodfreqlo++; + + //carrier + carposhi = poshi + FMmodfreqhi; + carposlo = poslo + FMmodfreqlo; + + if(carposlo >= 1.0f) { + carposhi++; + carposlo = fmod(carposlo, 1.0f); + } + carposhi &= (synth->oscilsize - 1); + + tw[i] = NoteVoicePar[nvoice].OscilSmp[carposhi] + * (1.0f - carposlo) + + NoteVoicePar[nvoice].OscilSmp[carposhi + + 1] * carposlo; + + poslo += freqlo; + if(poslo >= 1.0f) { + poslo = fmod(poslo, 1.0f); + poshi++; + } + + poshi += freqhi; + poshi &= synth->oscilsize - 1; + } + oscposhi[nvoice][k] = poshi; + oscposlo[nvoice][k] = poslo; + } +} + + +/*Calculeaza Oscilatorul cu PITCH MODULATION*/ +inline void ADnote::ComputeVoiceOscillatorPitchModulation(int /*nvoice*/) +{ +//TODO +} + +/* + * Computes the Noise + */ +inline void ADnote::ComputeVoiceNoise(int nvoice) +{ + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + for(int i = 0; i < synth->buffersize; ++i) + tw[i] = RND * 2.0f - 1.0f; + } +} + + + +/* + * Compute the ADnote samples + * Returns 0 if the note is finished + */ +int ADnote::noteout(float *outl, float *outr) +{ + memcpy(outl, denormalkillbuf, synth->bufferbytes); + memcpy(outr, denormalkillbuf, synth->bufferbytes); + + if(NoteEnabled == OFF) + return 0; + + memset(bypassl, 0, synth->bufferbytes); + memset(bypassr, 0, synth->bufferbytes); + computecurrentparameters(); + + for(unsigned nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + if((NoteVoicePar[nvoice].Enabled != ON) + || (NoteVoicePar[nvoice].DelayTicks > 0)) + continue; + if(NoteVoicePar[nvoice].noisetype == 0) //voice mode=sound + switch(NoteVoicePar[nvoice].FMEnabled) { + case MORPH: + ComputeVoiceOscillatorMorph(nvoice); + break; + case RING_MOD: + ComputeVoiceOscillatorRingModulation(nvoice); + break; + case PHASE_MOD: + ComputeVoiceOscillatorFrequencyModulation(nvoice, 0); + break; + case FREQ_MOD: + ComputeVoiceOscillatorFrequencyModulation(nvoice, 1); + break; + //case PITCH_MOD:ComputeVoiceOscillatorPitchModulation(nvoice);break; + default: + ComputeVoiceOscillator_LinearInterpolation(nvoice); + //if (config.cfg.Interpolation) ComputeVoiceOscillator_CubicInterpolation(nvoice); + } + else + ComputeVoiceNoise(nvoice); + // Voice Processing + + + //mix subvoices into voice + memset(tmpwavel, 0, synth->bufferbytes); + if(stereo) + memset(tmpwaver, 0, synth->bufferbytes); + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + if(stereo) { + float stereo_pos = 0; + if(unison_size[nvoice] > 1) + stereo_pos = k + / (float)(unison_size[nvoice] + - 1) * 2.0f - 1.0f; + float stereo_spread = unison_stereo_spread[nvoice] * 2.0f; //between 0 and 2.0f + if(stereo_spread > 1.0f) { + float stereo_pos_1 = (stereo_pos >= 0.0f) ? 1.0f : -1.0f; + stereo_pos = + (2.0f + - stereo_spread) * stereo_pos + + (stereo_spread - 1.0f) * stereo_pos_1; + } + else + stereo_pos *= stereo_spread; + + if(unison_size[nvoice] == 1) + stereo_pos = 0.0f; + float panning = (stereo_pos + 1.0f) * 0.5f; + + + float lvol = (1.0f - panning) * 2.0f; + if(lvol > 1.0f) + lvol = 1.0f; + + float rvol = panning * 2.0f; + if(rvol > 1.0f) + rvol = 1.0f; + + if(unison_invert_phase[nvoice][k]) { + lvol = -lvol; + rvol = -rvol; + } + + for(int i = 0; i < synth->buffersize; ++i) + tmpwavel[i] += tw[i] * lvol; + for(int i = 0; i < synth->buffersize; ++i) + tmpwaver[i] += tw[i] * rvol; + } + else + for(int i = 0; i < synth->buffersize; ++i) + tmpwavel[i] += tw[i]; + } + + + float unison_amplitude = 1.0f / sqrt(unison_size[nvoice]); //reduce the amplitude for large unison sizes + // Amplitude + float oldam = oldamplitude[nvoice] * unison_amplitude; + float newam = newamplitude[nvoice] * unison_amplitude; + + if(ABOVE_AMPLITUDE_THRESHOLD(oldam, newam)) { + int rest = synth->buffersize; + //test if the amplitude if raising and the difference is high + if((newam > oldam) && ((newam - oldam) > 0.25f)) { + rest = 10; + if(rest > synth->buffersize) + rest = synth->buffersize; + for(int i = 0; i < synth->buffersize - rest; ++i) + tmpwavel[i] *= oldam; + if(stereo) + for(int i = 0; i < synth->buffersize - rest; ++i) + tmpwaver[i] *= oldam; + } + // Amplitude interpolation + for(int i = 0; i < rest; ++i) { + float amp = INTERPOLATE_AMPLITUDE(oldam, newam, i, rest); + tmpwavel[i + (synth->buffersize - rest)] *= amp; + if(stereo) + tmpwaver[i + (synth->buffersize - rest)] *= amp; + } + } + else { + for(int i = 0; i < synth->buffersize; ++i) + tmpwavel[i] *= newam; + if(stereo) + for(int i = 0; i < synth->buffersize; ++i) + tmpwaver[i] *= newam; + } + + // Fade in + if(firsttick[nvoice] != 0) { + fadein(&tmpwavel[0]); + if(stereo) + fadein(&tmpwaver[0]); + firsttick[nvoice] = 0; + } + + + // Filter + if(NoteVoicePar[nvoice].VoiceFilterL != NULL) + NoteVoicePar[nvoice].VoiceFilterL->filterout(&tmpwavel[0]); + if((stereo) && (NoteVoicePar[nvoice].VoiceFilterR != NULL)) + NoteVoicePar[nvoice].VoiceFilterR->filterout(&tmpwaver[0]); + + //check if the amplitude envelope is finished, if yes, the voice will be fadeout + if(NoteVoicePar[nvoice].AmpEnvelope != NULL) + if(NoteVoicePar[nvoice].AmpEnvelope->finished() != 0) { + for(int i = 0; i < synth->buffersize; ++i) + tmpwavel[i] *= 1.0f - (float)i / synth->buffersize_f; + if(stereo) + for(int i = 0; i < synth->buffersize; ++i) + tmpwaver[i] *= 1.0f - (float)i / synth->buffersize_f; + } + //the voice is killed later + + + // Put the ADnote samples in VoiceOut (without appling Global volume, because I wish to use this voice as a modullator) + if(NoteVoicePar[nvoice].VoiceOut != NULL) { + if(stereo) + for(int i = 0; i < synth->buffersize; ++i) + NoteVoicePar[nvoice].VoiceOut[i] = tmpwavel[i] + + tmpwaver[i]; + else //mono + for(int i = 0; i < synth->buffersize; ++i) + NoteVoicePar[nvoice].VoiceOut[i] = tmpwavel[i]; + } + + + // Add the voice that do not bypass the filter to out + if(NoteVoicePar[nvoice].filterbypass == 0) { //no bypass + if(stereo) + for(int i = 0; i < synth->buffersize; ++i) { //stereo + outl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume + * NoteVoicePar[nvoice].Panning * 2.0f; + outr[i] += tmpwaver[i] * NoteVoicePar[nvoice].Volume + * (1.0f - NoteVoicePar[nvoice].Panning) * 2.0f; + } + else + for(int i = 0; i < synth->buffersize; ++i) //mono + outl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume; + } + else { //bypass the filter + if(stereo) + for(int i = 0; i < synth->buffersize; ++i) { //stereo + bypassl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume + * NoteVoicePar[nvoice].Panning * 2.0f; + bypassr[i] += tmpwaver[i] * NoteVoicePar[nvoice].Volume + * (1.0f + - NoteVoicePar[nvoice].Panning) * 2.0f; + } + else + for(int i = 0; i < synth->buffersize; ++i) //mono + bypassl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume; + } + // chech if there is necesary to proces the voice longer (if the Amplitude envelope isn't finished) + if(NoteVoicePar[nvoice].AmpEnvelope != NULL) + if(NoteVoicePar[nvoice].AmpEnvelope->finished() != 0) + KillVoice(nvoice); + } + + + //Processing Global parameters + NoteGlobalPar.GlobalFilterL->filterout(&outl[0]); + + if(stereo == 0) { //set the right channel=left channel + memcpy(outr, outl, synth->bufferbytes); + memcpy(bypassr, bypassl, synth->bufferbytes); + } + else + NoteGlobalPar.GlobalFilterR->filterout(&outr[0]); + + for(int i = 0; i < synth->buffersize; ++i) { + outl[i] += bypassl[i]; + outr[i] += bypassr[i]; + } + + if(ABOVE_AMPLITUDE_THRESHOLD(globaloldamplitude, globalnewamplitude)) + // Amplitude Interpolation + for(int i = 0; i < synth->buffersize; ++i) { + float tmpvol = INTERPOLATE_AMPLITUDE(globaloldamplitude, + globalnewamplitude, + i, + synth->buffersize); + outl[i] *= tmpvol * NoteGlobalPar.Panning; + outr[i] *= tmpvol * (1.0f - NoteGlobalPar.Panning); + } + else + for(int i = 0; i < synth->buffersize; ++i) { + outl[i] *= globalnewamplitude * NoteGlobalPar.Panning; + outr[i] *= globalnewamplitude * (1.0f - NoteGlobalPar.Panning); + } + + //Apply the punch + if(NoteGlobalPar.Punch.Enabled != 0) + for(int i = 0; i < synth->buffersize; ++i) { + float punchamp = NoteGlobalPar.Punch.initialvalue + * NoteGlobalPar.Punch.t + 1.0f; + outl[i] *= punchamp; + outr[i] *= punchamp; + NoteGlobalPar.Punch.t -= NoteGlobalPar.Punch.dt; + if(NoteGlobalPar.Punch.t < 0.0f) { + NoteGlobalPar.Punch.Enabled = 0; + break; + } + } + + + // Apply legato-specific sound signal modifications + legato.apply(*this, outl, outr); + + + // Check if the global amplitude is finished. + // If it does, disable the note + if(NoteGlobalPar.AmpEnvelope->finished()) { + for(int i = 0; i < synth->buffersize; ++i) { //fade-out + float tmp = 1.0f - (float)i / synth->buffersize_f; + outl[i] *= tmp; + outr[i] *= tmp; + } + KillNote(); + } + return 1; +} + + +/* + * Relase the key (NoteOff) + */ +void ADnote::relasekey() +{ + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) + NoteVoicePar[nvoice].releasekey(); + NoteGlobalPar.FreqEnvelope->relasekey(); + NoteGlobalPar.FilterEnvelope->relasekey(); + NoteGlobalPar.AmpEnvelope->relasekey(); +} + +/* + * Check if the note is finished + */ +int ADnote::finished() const +{ + if(NoteEnabled == ON) + return 0; + else + return 1; +} + +void ADnote::Voice::releasekey() +{ + if(!Enabled) + return; + if(AmpEnvelope) + AmpEnvelope->relasekey(); + if(FreqEnvelope) + FreqEnvelope->relasekey(); + if(FilterEnvelope) + FilterEnvelope->relasekey(); + if(FMFreqEnvelope) + FMFreqEnvelope->relasekey(); + if(FMAmpEnvelope) + FMAmpEnvelope->relasekey(); +} + +template +static inline void nullify(T &t) {delete t; t = NULL; } +template +static inline void arrayNullify(T &t) {delete [] t; t = NULL; } + +void ADnote::Voice::kill() +{ + arrayNullify(OscilSmp); + nullify(FreqEnvelope); + nullify(FreqLfo); + nullify(AmpEnvelope); + nullify(AmpLfo); + nullify(VoiceFilterL); + nullify(VoiceFilterR); + nullify(FilterEnvelope); + nullify(FilterLfo); + nullify(FMFreqEnvelope); + nullify(FMAmpEnvelope); + + if((FMEnabled != NONE) && (FMVoice < 0)) { + delete[] FMSmp; + FMSmp = NULL; + } + + if(VoiceOut) + memset(VoiceOut, 0, synth->bufferbytes); + //do not delete, yet: perhaps is used by another voice + + Enabled = OFF; +} + +void ADnote::Global::kill() +{ + nullify(FreqEnvelope); + nullify(FreqLfo); + nullify(AmpEnvelope); + nullify(AmpLfo); + nullify(GlobalFilterL); + nullify(GlobalFilterR); + nullify(FilterEnvelope); + nullify(FilterLfo); +} + +void ADnote::Global::initparameters(const ADnoteGlobalParam ¶m, + float basefreq, float velocity, + bool stereo) +{ + FreqEnvelope = new Envelope(param.FreqEnvelope, basefreq); + FreqLfo = new LFO(param.FreqLfo, basefreq); + + AmpEnvelope = new Envelope(param.AmpEnvelope, basefreq); + AmpLfo = new LFO(param.AmpLfo, basefreq); + + Volume = 4.0f * powf(0.1f, 3.0f * (1.0f - param.PVolume / 96.0f)) //-60 dB .. 0 dB + * VelF(velocity, param.PAmpVelocityScaleFunction); //sensing + + GlobalFilterL = Filter::generate(param.GlobalFilter); + if(stereo) + GlobalFilterR = Filter::generate(param.GlobalFilter); + else + GlobalFilterR = NULL; + + FilterEnvelope = new Envelope(param.FilterEnvelope, basefreq); + FilterLfo = new LFO(param.FilterLfo, basefreq); + FilterQ = param.GlobalFilter->getq(); + FilterFreqTracking = param.GlobalFilter->getfreqtracking(basefreq); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/ADnote.h b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/ADnote.h new file mode 100644 index 000000000..53420ee0e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/ADnote.h @@ -0,0 +1,325 @@ +/* + ZynAddSubFX - a software synthesizer + + ADnote.h - The "additive" synthesizer + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef AD_NOTE_H +#define AD_NOTE_H + +#include "SynthNote.h" +#include "Envelope.h" +#include "LFO.h" +#include "../Params/ADnoteParameters.h" +#include "../Params/Controller.h" + +//Globals + +/**FM amplitude tune*/ +#define FM_AMP_MULTIPLIER 14.71280603f + +#define OSCIL_SMP_EXTRA_SAMPLES 5 + +/**The "additive" synthesizer*/ +class ADnote:public SynthNote +{ + public: + /**Constructor. + * @param pars Note Parameters + * @param ctl_ Pointer to system Controller + * @param freq Base frequency for note + * @param velocity Velocity of note + * @param portamento_ 1 if the note has portamento + * @param midinote_ The midi number of the note + * @param besilent Start silent note if true*/ + ADnote(ADnoteParameters *pars, Controller *ctl_, float freq, + float velocity, int portamento_, int midinote_, + bool besilent); + /**Destructor*/ + ~ADnote(); + + /**Alters the playing note for legato effect*/ + void legatonote(float freq, float velocity, int portamento_, + int midinote_, bool externcall); + + int noteout(float *outl, float *outr); + void relasekey(); + int finished() const; + private: + + /**Changes the frequency of an oscillator. + * @param nvoice voice to run computations on + * @param in_freq new frequency*/ + void setfreq(int nvoice, float in_freq); + /**Set the frequency of the modulator oscillator*/ + void setfreqFM(int nvoice, float in_freq); + /**Computes relative frequency for unison and unison's vibratto. + * Note: Must be called before setfreq* functions.*/ + void compute_unison_freq_rap(int nvoice); + /**Compute parameters for next tick*/ + void computecurrentparameters(); + /**Initializes All Parameters*/ + void initparameters(); + /**Deallocate/Cleanup given voice*/ + void KillVoice(int nvoice); + /**Deallocate Note resources and voice resources*/ + void KillNote(); + /**Get the Voice's base frequency*/ + inline float getvoicebasefreq(int nvoice) const; + /**Get modulator's base frequency*/ + inline float getFMvoicebasefreq(int nvoice) const; + /**Compute the Oscillator's samples. + * Affects tmpwave_unison and updates oscposhi/oscposlo*/ + inline void ComputeVoiceOscillator_LinearInterpolation(int nvoice); + /**Compute the Oscillator's samples. + * Affects tmpwave_unison and updates oscposhi/oscposlo + * @todo remove this declaration if it is commented out*/ + inline void ComputeVoiceOscillator_CubicInterpolation(int nvoice); + /**Computes the Oscillator samples with morphing. + * updates tmpwave_unison*/ + inline void ComputeVoiceOscillatorMorph(int nvoice); + /**Computes the Ring Modulated Oscillator.*/ + inline void ComputeVoiceOscillatorRingModulation(int nvoice); + /**Computes the Frequency Modulated Oscillator. + * @param FMmode modulation type 0=Phase 1=Frequency*/ + inline void ComputeVoiceOscillatorFrequencyModulation(int nvoice, + int FMmode); + // inline void ComputeVoiceOscillatorFrequencyModulation(int nvoice); + /**TODO*/ + inline void ComputeVoiceOscillatorPitchModulation(int nvoice); + + /**Generate Noise Samples for Voice*/ + inline void ComputeVoiceNoise(int nvoice); + + /**Fadein in a way that removes clicks but keep sound "punchy"*/ + inline void fadein(float *smps) const; + + + //GLOBALS + ADnoteParameters *partparams; + unsigned char stereo; //if the note is stereo (allows note Panning) + int midinote; + float velocity, basefreq; + + ONOFFTYPE NoteEnabled; + Controller *ctl; + + /*****************************************************************/ + /* GLOBAL PARAMETERS */ + /*****************************************************************/ + + struct Global { + void kill(); + void initparameters(const ADnoteGlobalParam ¶m, + float basefreq, float velocity, + bool stereo); + /****************************************** + * FREQUENCY GLOBAL PARAMETERS * + ******************************************/ + float Detune; //cents + + Envelope *FreqEnvelope; + LFO *FreqLfo; + + /******************************************** + * AMPLITUDE GLOBAL PARAMETERS * + ********************************************/ + float Volume; // [ 0 .. 1 ] + + float Panning; // [ 0 .. 1 ] + + Envelope *AmpEnvelope; + LFO *AmpLfo; + + struct { + int Enabled; + float initialvalue, dt, t; + } Punch; + + /****************************************** + * FILTER GLOBAL PARAMETERS * + ******************************************/ + class Filter * GlobalFilterL, *GlobalFilterR; + + float FilterCenterPitch; //octaves + float FilterQ; + float FilterFreqTracking; + + Envelope *FilterEnvelope; + + LFO *FilterLfo; + } NoteGlobalPar; + + + + /***********************************************************/ + /* VOICE PARAMETERS */ + /***********************************************************/ + struct Voice { + void releasekey(); + void kill(); + /* If the voice is enabled */ + ONOFFTYPE Enabled; + + /* Voice Type (sound/noise)*/ + int noisetype; + + /* Filter Bypass */ + int filterbypass; + + /* Delay (ticks) */ + int DelayTicks; + + /* Waveform of the Voice */ + float *OscilSmp; + + /************************************ + * FREQUENCY PARAMETERS * + ************************************/ + int fixedfreq; //if the frequency is fixed to 440 Hz + int fixedfreqET; //if the "fixed" frequency varies according to the note (ET) + + // cents = basefreq*VoiceDetune + float Detune, FineDetune; + + Envelope *FreqEnvelope; + LFO *FreqLfo; + + + /*************************** + * AMPLITUDE PARAMETERS * + ***************************/ + + /* Panning 0.0f=left, 0.5f - center, 1.0f = right */ + float Panning; + float Volume; // [-1.0f .. 1.0f] + + Envelope *AmpEnvelope; + LFO *AmpLfo; + + /************************* + * FILTER PARAMETERS * + *************************/ + + class Filter * VoiceFilterL; + class Filter * VoiceFilterR; + + float FilterCenterPitch; /* Filter center Pitch*/ + float FilterFreqTracking; + + Envelope *FilterEnvelope; + LFO *FilterLfo; + + + /**************************** + * MODULLATOR PARAMETERS * + ****************************/ + + FMTYPE FMEnabled; + + int FMVoice; + + // Voice Output used by other voices if use this as modullator + float *VoiceOut; + + /* Wave of the Voice */ + float *FMSmp; + + float FMVolume; + float FMDetune; //in cents + + Envelope *FMFreqEnvelope; + Envelope *FMAmpEnvelope; + } NoteVoicePar[NUM_VOICES]; + + + /********************************************************/ + /* INTERNAL VALUES OF THE NOTE AND OF THE VOICES */ + /********************************************************/ + + //time from the start of the note + float time; + + //the size of unison for a single voice + int unison_size[NUM_VOICES]; + + //the stereo spread of the unison subvoices (0.0f=mono,1.0f=max) + float unison_stereo_spread[NUM_VOICES]; + + //fractional part (skip) + float *oscposlo[NUM_VOICES], *oscfreqlo[NUM_VOICES]; + + //integer part (skip) + int *oscposhi[NUM_VOICES], *oscfreqhi[NUM_VOICES]; + + //fractional part (skip) of the Modullator + float *oscposloFM[NUM_VOICES], *oscfreqloFM[NUM_VOICES]; + + //the unison base_value + float *unison_base_freq_rap[NUM_VOICES]; + + //how the unison subvoice's frequency is changed (1.0f for no change) + float *unison_freq_rap[NUM_VOICES]; + + //which subvoice has phase inverted + bool *unison_invert_phase[NUM_VOICES]; + + //unison vibratto + struct { + float amplitude; //amplitude which be added to unison_freq_rap + float *step; //value which increments the position + float *position; //between -1.0f and 1.0f + } unison_vibratto[NUM_VOICES]; + + + //integer part (skip) of the Modullator + unsigned int *oscposhiFM[NUM_VOICES], *oscfreqhiFM[NUM_VOICES]; + + //used to compute and interpolate the amplitudes of voices and modullators + float oldamplitude[NUM_VOICES], + newamplitude[NUM_VOICES], + FMoldamplitude[NUM_VOICES], + FMnewamplitude[NUM_VOICES]; + + //used by Frequency Modulation (for integration) + float *FMoldsmp[NUM_VOICES]; + + //temporary buffer + float *tmpwavel; + float *tmpwaver; + int max_unison; + float **tmpwave_unison; + + //Filter bypass samples + float *bypassl, *bypassr; + + //interpolate the amplitudes + float globaloldamplitude, globalnewamplitude; + + //1 - if it is the fitst tick (used to fade in the sound) + char firsttick[NUM_VOICES]; + + //1 if the note has portamento + int portamento; + + //how the fine detunes are made bigger or smaller + float bandwidthDetuneMultiplier; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/CMakeLists.txt b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/CMakeLists.txt new file mode 100644 index 000000000..ce45fa54c --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/CMakeLists.txt @@ -0,0 +1,11 @@ +set(zynaddsubfx_synth_SRCS + Synth/SynthNote.cpp + Synth/ADnote.cpp + Synth/Envelope.cpp + Synth/LFO.cpp + Synth/OscilGen.cpp + Synth/PADnote.cpp + Synth/Resonance.cpp + Synth/SUBnote.cpp + PARENT_SCOPE +) diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Envelope.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Envelope.cpp new file mode 100644 index 000000000..d03a2d622 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Envelope.cpp @@ -0,0 +1,198 @@ +/* + ZynAddSubFX - a software synthesizer + + Envelope.cpp - Envelope implementation + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include "Envelope.h" +#include "../Params/EnvelopeParams.h" + +Envelope::Envelope(EnvelopeParams *envpars, float basefreq) +{ + int i; + envpoints = envpars->Penvpoints; + if(envpoints > MAX_ENVELOPE_POINTS) + envpoints = MAX_ENVELOPE_POINTS; + envsustain = (envpars->Penvsustain == 0) ? -1 : envpars->Penvsustain; + forcedrelase = envpars->Pforcedrelease; + envstretch = powf(440.0f / basefreq, envpars->Penvstretch / 64.0f); + linearenvelope = envpars->Plinearenvelope; + + if(envpars->Pfreemode == 0) + envpars->converttofree(); + + float bufferdt = synth->buffersize_f / synth->samplerate_f; + + int mode = envpars->Envmode; + + //for amplitude envelopes + if((mode == 1) && (linearenvelope == 0)) + mode = 2; //change to log envelope + if((mode == 2) && (linearenvelope != 0)) + mode = 1; //change to linear + + for(i = 0; i < MAX_ENVELOPE_POINTS; ++i) { + float tmp = envpars->getdt(i) / 1000.0f * envstretch; + if(tmp > bufferdt) + envdt[i] = bufferdt / tmp; + else + envdt[i] = 2.0f; //any value larger than 1 + + switch(mode) { + case 2: + envval[i] = (1.0f - envpars->Penvval[i] / 127.0f) * -40; + break; + case 3: + envval[i] = + (powf(2, 6.0f + * fabs(envpars->Penvval[i] + - 64.0f) / 64.0f) - 1.0f) * 100.0f; + if(envpars->Penvval[i] < 64) + envval[i] = -envval[i]; + break; + case 4: + envval[i] = (envpars->Penvval[i] - 64.0f) / 64.0f * 6.0f; //6 octaves (filtru) + break; + case 5: + envval[i] = (envpars->Penvval[i] - 64.0f) / 64.0f * 10; + break; + default: + envval[i] = envpars->Penvval[i] / 127.0f; + } + } + + envdt[0] = 1.0f; + + currentpoint = 1; //the envelope starts from 1 + keyreleased = false; + t = 0.0f; + envfinish = false; + inct = envdt[1]; + envoutval = 0.0f; +} + +Envelope::~Envelope() +{} + + +/* + * Relase the key (note envelope) + */ +void Envelope::relasekey() +{ + if(keyreleased) + return; + keyreleased = true; + if(forcedrelase != 0) + t = 0.0f; +} + +/* + * Envelope Output + */ +float Envelope::envout() +{ + float out; + + if(envfinish) { //if the envelope is finished + envoutval = envval[envpoints - 1]; + return envoutval; + } + if((currentpoint == envsustain + 1) && !keyreleased) { //if it is sustaining now + envoutval = envval[envsustain]; + return envoutval; + } + + if(keyreleased && (forcedrelase != 0)) { //do the forced release + int tmp = (envsustain < 0) ? (envpoints - 1) : (envsustain + 1); //if there is no sustain point, use the last point for release + + if(envdt[tmp] < 0.00000001f) + out = envval[tmp]; + else + out = envoutval + (envval[tmp] - envoutval) * t; + t += envdt[tmp] * envstretch; + + if(t >= 1.0f) { + currentpoint = envsustain + 2; + forcedrelase = 0; + t = 0.0f; + inct = envdt[currentpoint]; + if((currentpoint >= envpoints) || (envsustain < 0)) + envfinish = true; + } + return out; + } + if(inct >= 1.0f) + out = envval[currentpoint]; + else + out = envval[currentpoint - 1] + + (envval[currentpoint] - envval[currentpoint - 1]) * t; + + t += inct; + if(t >= 1.0f) { + if(currentpoint >= envpoints - 1) + envfinish = true; + else + currentpoint++; + t = 0.0f; + inct = envdt[currentpoint]; + } + + envoutval = out; + return out; +} + +/* + * Envelope Output (dB) + */ +float Envelope::envout_dB() +{ + float out; + if(linearenvelope != 0) + return envout(); + + if((currentpoint == 1) && (!keyreleased || (forcedrelase == 0))) { //first point is always lineary interpolated + float v1 = dB2rap(envval[0]); + float v2 = dB2rap(envval[1]); + out = v1 + (v2 - v1) * t; + + t += inct; + if(t >= 1.0f) { + t = 0.0f; + inct = envdt[2]; + currentpoint++; + out = v2; + } + + if(out > 0.001f) + envoutval = rap2dB(out); + else + envoutval = MIN_ENVELOPE_DB; + } + else + out = dB2rap(envout()); + + return out; +} + +bool Envelope::finished() const +{ + return envfinish; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Envelope.h b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Envelope.h new file mode 100644 index 000000000..ee75e0a33 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Envelope.h @@ -0,0 +1,62 @@ +/* + ZynAddSubFX - a software synthesizer + + Envelope.h - Envelope implementation + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef ENVELOPE_H +#define ENVELOPE_H + +#include "../globals.h" +#include "../Params/EnvelopeParams.h" + +/**Implementation of a general Envelope*/ +class Envelope +{ + public: + + /**Constructor*/ + Envelope(class EnvelopeParams *envpars, float basefreq); + /**Destructor*/ + ~Envelope(); + void relasekey(); + float envout(); + float envout_dB(); + /**Determines the status of the Envelope + * @return returns 1 if the envelope is finished*/ + bool finished() const; + private: + int envpoints; + int envsustain; //"-1" means disabled + float envdt[MAX_ENVELOPE_POINTS]; //millisecons + float envval[MAX_ENVELOPE_POINTS]; // [0.0f .. 1.0f] + float envstretch; + int linearenvelope; + + int currentpoint; //current envelope point (starts from 1) + int forcedrelase; + bool keyreleased; //if the key was released + bool envfinish; + float t; // the time from the last point + float inct; // the time increment + float envoutval; //used to do the forced release +}; + + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/LFO.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/LFO.cpp new file mode 100644 index 000000000..af9d10d77 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/LFO.cpp @@ -0,0 +1,183 @@ +/* + ZynAddSubFX - a software synthesizer + + LFO.cpp - LFO implementation + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "LFO.h" +#include "../Misc/Util.h" + +#include +#include +#include + +LFO::LFO(LFOParams *lfopars, float basefreq) +{ + if(lfopars->Pstretch == 0) + lfopars->Pstretch = 1; + float lfostretch = powf(basefreq / 440.0f, + (lfopars->Pstretch - 64.0f) / 63.0f); //max 2x/octave + + float lfofreq = + (powf(2, lfopars->Pfreq * 10.0f) - 1.0f) / 12.0f * lfostretch; + incx = fabs(lfofreq) * synth->buffersize_f / synth->samplerate_f; + + if(lfopars->Pcontinous == 0) { + if(lfopars->Pstartphase == 0) + x = RND; + else + x = fmod((lfopars->Pstartphase - 64.0f) / 127.0f + 1.0f, 1.0f); + } + else { + float tmp = fmod(lfopars->time * incx, 1.0f); + x = fmod((lfopars->Pstartphase - 64.0f) / 127.0f + 1.0f + tmp, 1.0f); + } + + //Limit the Frequency(or else...) + if(incx > 0.49999999f) + incx = 0.499999999f; + + + lfornd = lfopars->Prandomness / 127.0f; + if(lfornd < 0.0f) + lfornd = 0.0f; + else + if(lfornd > 1.0f) + lfornd = 1.0f; + +// lfofreqrnd=powf(lfopars->Pfreqrand/127.0f,2.0f)*2.0f*4.0f; + lfofreqrnd = powf(lfopars->Pfreqrand / 127.0f, 2.0f) * 4.0f; + + switch(lfopars->fel) { + case 1: + lfointensity = lfopars->Pintensity / 127.0f; + break; + case 2: + lfointensity = lfopars->Pintensity / 127.0f * 4.0f; + break; //in octave + default: + lfointensity = powf(2, lfopars->Pintensity / 127.0f * 11.0f) - 1.0f; //in centi + x -= 0.25f; //chance the starting phase + break; + } + + amp1 = (1 - lfornd) + lfornd * RND; + amp2 = (1 - lfornd) + lfornd * RND; + lfotype = lfopars->PLFOtype; + lfodelay = lfopars->Pdelay / 127.0f * 4.0f; //0..4 sec + incrnd = nextincrnd = 1.0f; + freqrndenabled = (lfopars->Pfreqrand != 0); + computenextincrnd(); + computenextincrnd(); //twice because I want incrnd & nextincrnd to be random +} + +LFO::~LFO() +{} + +/* + * LFO out + */ +float LFO::lfoout() +{ + float out; + switch(lfotype) { + case 1: //LFO_TRIANGLE + if((x >= 0.0f) && (x < 0.25f)) + out = 4.0f * x; + else + if((x > 0.25f) && (x < 0.75f)) + out = 2 - 4 * x; + else + out = 4.0f * x - 4.0f; + break; + case 2: //LFO_SQUARE + if(x < 0.5f) + out = -1; + else + out = 1; + break; + case 3: //LFO_RAMPUP + out = (x - 0.5f) * 2.0f; + break; + case 4: //LFO_RAMPDOWN + out = (0.5f - x) * 2.0f; + break; + case 5: //LFO_EXP_DOWN 1 + out = powf(0.05f, x) * 2.0f - 1.0f; + break; + case 6: //LFO_EXP_DOWN 2 + out = powf(0.001f, x) * 2.0f - 1.0f; + break; + default: + out = cosf(x * 2.0f * PI); //LFO_SINE + } + + + if((lfotype == 0) || (lfotype == 1)) + out *= lfointensity * (amp1 + x * (amp2 - amp1)); + else + out *= lfointensity * amp2; + if(lfodelay < 0.00001f) { + if(freqrndenabled == 0) + x += incx; + else { + float tmp = (incrnd * (1.0f - x) + nextincrnd * x); + if(tmp > 1.0f) + tmp = 1.0f; + else + if(tmp < 0.0f) + tmp = 0.0f; + x += incx * tmp; + } + if(x >= 1) { + x = fmod(x, 1.0f); + amp1 = amp2; + amp2 = (1 - lfornd) + lfornd * RND; + + computenextincrnd(); + } + } + else + lfodelay -= synth->buffersize_f / synth->samplerate_f; + return out; +} + +/* + * LFO out (for amplitude) + */ +float LFO::amplfoout() +{ + float out; + out = 1.0f - lfointensity + lfoout(); + if(out < -1.0f) + out = -1.0f; + else + if(out > 1.0f) + out = 1.0f; + return out; +} + + +void LFO::computenextincrnd() +{ + if(freqrndenabled == 0) + return; + incrnd = nextincrnd; + nextincrnd = powf(0.5f, lfofreqrnd) + RND * (powf(2.0f, lfofreqrnd) - 1.0f); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/LFO.h b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/LFO.h new file mode 100644 index 000000000..2b933a34d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/LFO.h @@ -0,0 +1,58 @@ +/* + ZynAddSubFX - a software synthesizer + + LFO.h - LFO implementation + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef LFO_H +#define LFO_H + +#include "../globals.h" +#include "../Params/LFOParams.h" + +/**Class for creating Low Frequency Ocillators*/ +class LFO +{ + public: + /**Constructor + * + * @param lfopars pointer to a LFOParams object + * @param basefreq base frequency of LFO + */ + LFO(LFOParams *lfopars, float basefreq); + /**Deconstructor*/ + ~LFO(); + float lfoout(); + float amplfoout(); + private: + float x; + float incx, incrnd, nextincrnd; + float amp1, amp2; // used for randomness + float lfointensity; + float lfornd, lfofreqrnd; + float lfodelay; + /**\todo see if an enum would be better here*/ + char lfotype; + int freqrndenabled; + + + void computenextincrnd(); +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/OscilGen.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/OscilGen.cpp new file mode 100644 index 000000000..2d9d2f4a9 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/OscilGen.cpp @@ -0,0 +1,1503 @@ +/* + ZynAddSubFX - a software synthesizer + + OscilGen.cpp - Waveform generator for ADnote + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "OscilGen.h" +#include "../Misc/WaveShapeSmps.h" + +#include +#include +#include +#include + + +//operations on FFTfreqs +inline void clearAll(fft_t *freqs) +{ + memset(freqs, 0, synth->oscilsize / 2 * sizeof(fft_t)); +} + +inline void clearDC(fft_t *freqs) +{ + freqs[0] = fft_t(0.0f, 0.0f); +} + +//return magnitude squared +inline float normal(const fft_t *freqs, off_t x) +{ + return norm(freqs[x]); +} + +//return magnitude +inline float abs(const fft_t *freqs, off_t x) +{ + return abs(freqs[x]); +} + +//return angle aka phase from a sine (not cosine wave) +inline float arg(const fft_t *freqs, off_t x) +{ + const fft_t tmp(freqs[x].imag(), freqs[x].real()); + return arg(tmp); +} + +/** + * Take frequency spectrum and ensure values are normalized based upon + * magnitude to 0<=x<=1 + */ +void normalize(fft_t *freqs) +{ + float normMax = 0.0f; + for(int i = 0; i < synth->oscilsize / 2; ++i) { + //magnitude squared + const float norm = normal(freqs, i); + if(normMax < norm) + normMax = norm; + } + + const float max = sqrt(normMax); + if(max < 1e-8) //data is all ~zero, do not amplify noise + return; + + for(int i = 0; i < synth->oscilsize / 2; ++i) + freqs[i] /= max; +} + +//Full RMS normalize +void rmsNormalize(fft_t *freqs) +{ + float sum = 0.0f; + for(int i = 1; i < synth->oscilsize / 2; ++i) + sum += normal(freqs, i); + + if(sum < 0.000001f) + return; //data is all ~zero, do not amplify noise + + const float gain = 1.0f / sqrt(sum); + + for(int i = 1; i < synth->oscilsize / 2; ++i) + freqs[i] *= gain; +} + +#define DIFF(par) (old ## par != P ## par) + +OscilGen::OscilGen(FFTwrapper *fft_, Resonance *res_):Presets() +{ + assert(fft_); + + setpresettype("Poscilgen"); + fft = fft_; + res = res_; + + + tmpsmps = new float[synth->oscilsize]; + outoscilFFTfreqs = new fft_t[synth->oscilsize / 2]; + oscilFFTfreqs = new fft_t[synth->oscilsize / 2]; + basefuncFFTfreqs = new fft_t[synth->oscilsize / 2]; + + randseed = 1; + ADvsPAD = false; + + defaults(); +} + +OscilGen::~OscilGen() +{ + delete[] tmpsmps; + delete[] outoscilFFTfreqs; + delete[] basefuncFFTfreqs; + delete[] oscilFFTfreqs; +} + + +void OscilGen::defaults() +{ + oldbasefunc = 0; + oldbasepar = 64; + oldhmagtype = 0; + oldwaveshapingfunction = 0; + oldwaveshaping = 64; + oldbasefuncmodulation = 0; + oldharmonicshift = 0; + oldbasefuncmodulationpar1 = 0; + oldbasefuncmodulationpar2 = 0; + oldbasefuncmodulationpar3 = 0; + oldmodulation = 0; + oldmodulationpar1 = 0; + oldmodulationpar2 = 0; + oldmodulationpar3 = 0; + + for(int i = 0; i < MAX_AD_HARMONICS; ++i) { + hmag[i] = 0.0f; + hphase[i] = 0.0f; + Phmag[i] = 64; + Phphase[i] = 64; + } + Phmag[0] = 127; + Phmagtype = 0; + if(ADvsPAD) + Prand = 127; //max phase randomness (usefull if the oscil will be imported to a ADsynth from a PADsynth + else + Prand = 64; //no randomness + + Pcurrentbasefunc = 0; + Pbasefuncpar = 64; + + Pbasefuncmodulation = 0; + Pbasefuncmodulationpar1 = 64; + Pbasefuncmodulationpar2 = 64; + Pbasefuncmodulationpar3 = 32; + + Pmodulation = 0; + Pmodulationpar1 = 64; + Pmodulationpar2 = 64; + Pmodulationpar3 = 32; + + Pwaveshapingfunction = 0; + Pwaveshaping = 64; + Pfiltertype = 0; + Pfilterpar1 = 64; + Pfilterpar2 = 64; + Pfilterbeforews = 0; + Psatype = 0; + Psapar = 64; + + Pamprandpower = 64; + Pamprandtype = 0; + + Pharmonicshift = 0; + Pharmonicshiftfirst = 0; + + Padaptiveharmonics = 0; + Padaptiveharmonicspower = 100; + Padaptiveharmonicsbasefreq = 128; + Padaptiveharmonicspar = 50; + + clearAll(oscilFFTfreqs); + clearAll(basefuncFFTfreqs); + oscilprepared = 0; + oldfilterpars = 0; + oldsapars = 0; + prepare(); +} + +void OscilGen::convert2sine() +{ + float mag[MAX_AD_HARMONICS], phase[MAX_AD_HARMONICS]; + float oscil[synth->oscilsize]; + fft_t *freqs = new fft_t[synth->oscilsize / 2]; + + get(oscil, -1.0f); + FFTwrapper *fft = new FFTwrapper(synth->oscilsize); + fft->smps2freqs(oscil, freqs); + delete (fft); + + normalize(freqs); + + mag[0] = 0; + phase[0] = 0; + for(int i = 0; i < MAX_AD_HARMONICS; ++i) { + mag[i] = abs(freqs, i + 1); + phase[i] = arg(freqs, i + 1); + } + + defaults(); + + for(int i = 0; i < MAX_AD_HARMONICS - 1; ++i) { + float newmag = mag[i]; + float newphase = phase[i]; + + Phmag[i] = (int) ((newmag) * 64.0f) + 64; + + Phphase[i] = 64 - (int) (64.0f * newphase / PI); + if(Phphase[i] > 127) + Phphase[i] = 127; + + if(Phmag[i] == 64) + Phphase[i] = 64; + } + delete[] freqs; + prepare(); +} + +/* + * Get the base function + */ +void OscilGen::getbasefunction(float *smps) +{ + int i; + float par = (Pbasefuncpar + 0.5f) / 128.0f; + if(Pbasefuncpar == 64) + par = 0.5f; + + float basefuncmodulationpar1 = Pbasefuncmodulationpar1 / 127.0f, + basefuncmodulationpar2 = Pbasefuncmodulationpar2 / 127.0f, + basefuncmodulationpar3 = Pbasefuncmodulationpar3 / 127.0f; + + switch(Pbasefuncmodulation) { + case 1: + basefuncmodulationpar1 = + (powf(2, basefuncmodulationpar1 * 5.0f) - 1.0f) / 10.0f; + basefuncmodulationpar3 = + floor((powf(2, basefuncmodulationpar3 * 5.0f) - 1.0f)); + if(basefuncmodulationpar3 < 0.9999f) + basefuncmodulationpar3 = -1.0f; + break; + case 2: + basefuncmodulationpar1 = + (powf(2, basefuncmodulationpar1 * 5.0f) - 1.0f) / 10.0f; + basefuncmodulationpar3 = 1.0f + + floor((powf(2, basefuncmodulationpar3 + * 5.0f) - 1.0f)); + break; + case 3: + basefuncmodulationpar1 = + (powf(2, basefuncmodulationpar1 * 7.0f) - 1.0f) / 10.0f; + basefuncmodulationpar3 = 0.01f + + (powf(2, basefuncmodulationpar3 + * 16.0f) - 1.0f) / 10.0f; + break; + } + + base_func func = getBaseFunction(Pcurrentbasefunc); + + for(i = 0; i < synth->oscilsize; ++i) { + float t = i * 1.0f / synth->oscilsize; + + switch(Pbasefuncmodulation) { + case 1: + t = t * basefuncmodulationpar3 + sinf( + (t + + basefuncmodulationpar2) * 2.0f + * PI) * basefuncmodulationpar1; //rev + break; + case 2: + t = t + sinf( + (t * basefuncmodulationpar3 + + basefuncmodulationpar2) * 2.0f + * PI) * basefuncmodulationpar1; //sine + break; + case 3: + t = t + powf((1.0f - cosf( + (t + + basefuncmodulationpar2) * 2.0f + * PI)) * 0.5f, + basefuncmodulationpar3) * basefuncmodulationpar1; //power + break; + } + + t = t - floor(t); + + if(func) + smps[i] = func(t, par); + else + smps[i] = -sinf(2.0f * PI * i / synth->oscilsize); + } +} + + +/* + * Filter the oscillator + */ +void OscilGen::oscilfilter() +{ + if(Pfiltertype == 0) + return; + + const float par = 1.0f - Pfilterpar1 / 128.0f; + const float par2 = Pfilterpar2 / 127.0f; + filter_func filter = getFilter(Pfiltertype); + + for(int i = 1; i < synth->oscilsize / 2; ++i) + oscilFFTfreqs[i] *= filter(i, par, par2); + + normalize(oscilFFTfreqs); +} + + +/* + * Change the base function + */ +void OscilGen::changebasefunction() +{ + if(Pcurrentbasefunc != 0) { + getbasefunction(tmpsmps); + fft->smps2freqs(tmpsmps, basefuncFFTfreqs); + clearDC(basefuncFFTfreqs); + } + else //in this case basefuncFFTfreqs are not used + clearAll(basefuncFFTfreqs); + oscilprepared = 0; + oldbasefunc = Pcurrentbasefunc; + oldbasepar = Pbasefuncpar; + oldbasefuncmodulation = Pbasefuncmodulation; + oldbasefuncmodulationpar1 = Pbasefuncmodulationpar1; + oldbasefuncmodulationpar2 = Pbasefuncmodulationpar2; + oldbasefuncmodulationpar3 = Pbasefuncmodulationpar3; +} + +inline void normalize(float *smps, size_t N) +{ + //Find max + float max = 0.0f; + for(size_t i = 0; i < N; ++i) + if(max < fabs(smps[i])) + max = fabs(smps[i]); + if(max < 0.00001f) + max = 1.0f; + + //Normalize to +-1 + for(size_t i = 0; i < N; ++i) + smps[i] /= max; +} + +/* + * Waveshape + */ +void OscilGen::waveshape() +{ + oldwaveshapingfunction = Pwaveshapingfunction; + oldwaveshaping = Pwaveshaping; + if(Pwaveshapingfunction == 0) + return; + + clearDC(oscilFFTfreqs); + //reduce the amplitude of the freqs near the nyquist + for(int i = 1; i < synth->oscilsize / 8; ++i) { + float gain = i / (synth->oscilsize / 8.0f); + oscilFFTfreqs[synth->oscilsize / 2 - i] *= gain; + } + fft->freqs2smps(oscilFFTfreqs, tmpsmps); + + //Normalize + normalize(tmpsmps, synth->oscilsize); + + //Do the waveshaping + waveShapeSmps(synth->oscilsize, tmpsmps, Pwaveshapingfunction, Pwaveshaping); + + fft->smps2freqs(tmpsmps, oscilFFTfreqs); //perform FFT +} + + +/* + * Do the Frequency Modulation of the Oscil + */ +void OscilGen::modulation() +{ + int i; + + oldmodulation = Pmodulation; + oldmodulationpar1 = Pmodulationpar1; + oldmodulationpar2 = Pmodulationpar2; + oldmodulationpar3 = Pmodulationpar3; + if(Pmodulation == 0) + return; + + + float modulationpar1 = Pmodulationpar1 / 127.0f, + modulationpar2 = 0.5f - Pmodulationpar2 / 127.0f, + modulationpar3 = Pmodulationpar3 / 127.0f; + + switch(Pmodulation) { + case 1: + modulationpar1 = (powf(2, modulationpar1 * 7.0f) - 1.0f) / 100.0f; + modulationpar3 = floor((powf(2, modulationpar3 * 5.0f) - 1.0f)); + if(modulationpar3 < 0.9999f) + modulationpar3 = -1.0f; + break; + case 2: + modulationpar1 = (powf(2, modulationpar1 * 7.0f) - 1.0f) / 100.0f; + modulationpar3 = 1.0f + + floor((powf(2, modulationpar3 * 5.0f) - 1.0f)); + break; + case 3: + modulationpar1 = (powf(2, modulationpar1 * 9.0f) - 1.0f) / 100.0f; + modulationpar3 = 0.01f + + (powf(2, modulationpar3 * 16.0f) - 1.0f) / 10.0f; + break; + } + + clearDC(oscilFFTfreqs); //remove the DC + //reduce the amplitude of the freqs near the nyquist + for(i = 1; i < synth->oscilsize / 8; ++i) { + float tmp = i / (synth->oscilsize / 8.0f); + oscilFFTfreqs[synth->oscilsize / 2 - i] *= tmp; + } + fft->freqs2smps(oscilFFTfreqs, tmpsmps); + int extra_points = 2; + float *in = new float[synth->oscilsize + extra_points]; + + //Normalize + normalize(tmpsmps, synth->oscilsize); + + for(i = 0; i < synth->oscilsize; ++i) + in[i] = tmpsmps[i]; + for(i = 0; i < extra_points; ++i) + in[i + synth->oscilsize] = tmpsmps[i]; + + //Do the modulation + for(i = 0; i < synth->oscilsize; ++i) { + float t = i * 1.0f / synth->oscilsize; + + switch(Pmodulation) { + case 1: + t = t * modulationpar3 + + sinf((t + modulationpar2) * 2.0f * PI) * modulationpar1; //rev + break; + case 2: + t = t + + sinf((t * modulationpar3 + + modulationpar2) * 2.0f * PI) * modulationpar1; //sine + break; + case 3: + t = t + powf((1.0f - cosf( + (t + modulationpar2) * 2.0f * PI)) * 0.5f, + modulationpar3) * modulationpar1; //power + break; + } + + t = (t - floor(t)) * synth->oscilsize; + + int poshi = (int) t; + float poslo = t - floor(t); + + tmpsmps[i] = in[poshi] * (1.0f - poslo) + in[poshi + 1] * poslo; + } + + delete [] in; + fft->smps2freqs(tmpsmps, oscilFFTfreqs); //perform FFT +} + + +/* + * Adjust the spectrum + */ +void OscilGen::spectrumadjust() +{ + if(Psatype == 0) + return; + float par = Psapar / 127.0f; + switch(Psatype) { + case 1: + par = 1.0f - par * 2.0f; + if(par >= 0.0f) + par = powf(5.0f, par); + else + par = powf(8.0f, par); + break; + case 2: + par = powf(10.0f, (1.0f - par) * 3.0f) * 0.25f; + break; + case 3: + par = powf(10.0f, (1.0f - par) * 3.0f) * 0.25f; + break; + } + + + normalize(oscilFFTfreqs); + + for(int i = 0; i < synth->oscilsize / 2; ++i) { + float mag = abs(oscilFFTfreqs, i); + float phase = arg(oscilFFTfreqs, i); + + switch(Psatype) { + case 1: + mag = powf(mag, par); + break; + case 2: + if(mag < par) + mag = 0.0f; + break; + case 3: + mag /= par; + if(mag > 1.0f) + mag = 1.0f; + break; + } + oscilFFTfreqs[i] = std::polar(mag, phase); + } +} + +void OscilGen::shiftharmonics() +{ + if(Pharmonicshift == 0) + return; + + int harmonicshift = -Pharmonicshift; + fft_t h; + + if(harmonicshift > 0) + for(int i = synth->oscilsize / 2 - 2; i >= 0; i--) { + int oldh = i - harmonicshift; + if(oldh < 0) + h = 0.0f; + else + h = oscilFFTfreqs[oldh + 1]; + oscilFFTfreqs[i + 1] = h; + } + else + for(int i = 0; i < synth->oscilsize / 2 - 1; ++i) { + int oldh = i + abs(harmonicshift); + if(oldh >= (synth->oscilsize / 2 - 1)) + h = 0.0f; + else { + h = oscilFFTfreqs[oldh + 1]; + if(abs(h) < 0.000001f) + h = 0.0f; + } + + oscilFFTfreqs[i + 1] = h; + } + + clearDC(oscilFFTfreqs); +} + +/* + * Prepare the Oscillator + */ +void OscilGen::prepare() +{ + if((oldbasepar != Pbasefuncpar) || (oldbasefunc != Pcurrentbasefunc) + || DIFF(basefuncmodulation) || DIFF(basefuncmodulationpar1) + || DIFF(basefuncmodulationpar2) || DIFF(basefuncmodulationpar3)) + changebasefunction(); + + for(int i = 0; i < MAX_AD_HARMONICS; ++i) + hphase[i] = (Phphase[i] - 64.0f) / 64.0f * PI / (i + 1); + + for(int i = 0; i < MAX_AD_HARMONICS; ++i) { + const float hmagnew = 1.0f - fabs(Phmag[i] / 64.0f - 1.0f); + switch(Phmagtype) { + case 1: + hmag[i] = expf(hmagnew * logf(0.01f)); + break; + case 2: + hmag[i] = expf(hmagnew * logf(0.001f)); + break; + case 3: + hmag[i] = expf(hmagnew * logf(0.0001f)); + break; + case 4: + hmag[i] = expf(hmagnew * logf(0.00001f)); + break; + default: + hmag[i] = 1.0f - hmagnew; + break; + } + + if(Phmag[i] < 64) + hmag[i] = -hmag[i]; + } + + //remove the harmonics where Phmag[i]==64 + for(int i = 0; i < MAX_AD_HARMONICS; ++i) + if(Phmag[i] == 64) + hmag[i] = 0.0f; + + + clearAll(oscilFFTfreqs); + if(Pcurrentbasefunc == 0) //the sine case + for(int i = 0; i < MAX_AD_HARMONICS - 1; ++i) { + oscilFFTfreqs[i + 1] = + std::complex(-hmag[i] * sinf(hphase[i] * (i + 1)) / 2.0f, + hmag[i] * cosf(hphase[i] * (i + 1)) / 2.0f); + } + else + for(int j = 0; j < MAX_AD_HARMONICS; ++j) { + if(Phmag[j] == 64) + continue; + for(int i = 1; i < synth->oscilsize / 2; ++i) { + int k = i * (j + 1); + if(k >= synth->oscilsize / 2) + break; + oscilFFTfreqs[k] += basefuncFFTfreqs[i] * std::polar( + hmag[j], + hphase[j] * k); + } + } + + if(Pharmonicshiftfirst != 0) + shiftharmonics(); + + if(Pfilterbeforews == 0) { + waveshape(); + oscilfilter(); + } + else { + oscilfilter(); + waveshape(); + } + + modulation(); + spectrumadjust(); + if(Pharmonicshiftfirst == 0) + shiftharmonics(); + + clearDC(oscilFFTfreqs); + + oldhmagtype = Phmagtype; + oldharmonicshift = Pharmonicshift + Pharmonicshiftfirst * 256; + + oscilprepared = 1; +} + +void OscilGen::adaptiveharmonic(fft_t *f, float freq) +{ + if(Padaptiveharmonics == 0 /*||(freq<1.0f)*/) + return; + if(freq < 1.0f) + freq = 440.0f; + + fft_t *inf = new fft_t[synth->oscilsize / 2]; + for(int i = 0; i < synth->oscilsize / 2; ++i) + inf[i] = f[i]; + clearAll(f); + clearDC(inf); + + float hc = 0.0f, hs = 0.0f; + float basefreq = 30.0f * powf(10.0f, Padaptiveharmonicsbasefreq / 128.0f); + float power = (Padaptiveharmonicspower + 1.0f) / 101.0f; + + float rap = freq / basefreq; + + rap = powf(rap, power); + + bool down = false; + if(rap > 1.0f) { + rap = 1.0f / rap; + down = true; + } + + for(int i = 0; i < synth->oscilsize / 2 - 2; ++i) { + float h = i * rap; + int high = (int)(i * rap); + float low = fmod(h, 1.0f); + + if(high >= (synth->oscilsize / 2 - 2)) + break; + else { + if(down) { + f[high] = + std::complex(f[high].real() + inf[i].real() * (1.0f - low), + f[high].imag() + inf[i].imag() * (1.0f - low)); + + f[high + 1] = std::complex(f[high + 1].real() + inf[i].real() * low, + f[high + 1].imag() + inf[i].imag() * low); + } + else { + hc = inf[high].real() + * (1.0f - low) + inf[high + 1].real() * low; + hs = inf[high].imag() + * (1.0f - low) + inf[high + 1].imag() * low; + } + if(fabs(hc) < 0.000001f) + hc = 0.0f; + if(fabs(hs) < 0.000001f) + hs = 0.0f; + } + + if(!down) { + if(i == 0) { //corect the aplitude of the first harmonic + hc *= rap; + hs *= rap; + } + f[i] = fft_t(hc, hs); + } + } + + f[1] += f[0]; + clearDC(f); + delete[] inf; +} + +void OscilGen::adaptiveharmonicpostprocess(fft_t *f, int size) +{ + if(Padaptiveharmonics <= 1) + return; + fft_t *inf = new fft_t[size]; + float par = Padaptiveharmonicspar * 0.01f; + par = 1.0f - powf((1.0f - par), 1.5f); + + for(int i = 0; i < size; ++i) { + inf[i] = f[i] * par; + f[i] *= (1.0f - par); + } + + + if(Padaptiveharmonics == 2) { //2n+1 + for(int i = 0; i < size; ++i) + if((i % 2) == 0) + f[i] += inf[i]; //i=0 pt prima armonica,etc. + } + else { //celelalte moduri + int nh = (Padaptiveharmonics - 3) / 2 + 2; + int sub_vs_add = (Padaptiveharmonics - 3) % 2; + if(sub_vs_add == 0) { + for(int i = 0; i < size; ++i) + if(((i + 1) % nh) == 0) + f[i] += inf[i]; + } + else + for(int i = 0; i < size / nh - 1; ++i) + f[(i + 1) * nh - 1] += inf[i]; + } + + delete [] inf; +} + +void OscilGen::newrandseed(unsigned int randseed) +{ + this->randseed = randseed; +} + +bool OscilGen::needPrepare(void) +{ + bool outdated = false; + + //Check function parameters + if((oldbasepar != Pbasefuncpar) || (oldbasefunc != Pcurrentbasefunc) + || DIFF(hmagtype) || DIFF(waveshaping) || DIFF(waveshapingfunction)) + outdated = true; + + //Check filter parameters + if(oldfilterpars != Pfiltertype * 256 + Pfilterpar1 + Pfilterpar2 * 65536 + + Pfilterbeforews * 16777216) { + outdated = true; + oldfilterpars = Pfiltertype * 256 + Pfilterpar1 + Pfilterpar2 * 65536 + + Pfilterbeforews * 16777216; + } + + //Check spectrum adjustments + if(oldsapars != Psatype * 256 + Psapar) { + outdated = true; + oldsapars = Psatype * 256 + Psapar; + } + + //Check function modulation + if(DIFF(basefuncmodulation) || DIFF(basefuncmodulationpar1) + || DIFF(basefuncmodulationpar2) || DIFF(basefuncmodulationpar3)) + outdated = true; + + //Check overall modulation + if(DIFF(modulation) || DIFF(modulationpar1) + || DIFF(modulationpar2) || DIFF(modulationpar3)) + outdated = true; + + //Check harmonic shifts + if(oldharmonicshift != Pharmonicshift + Pharmonicshiftfirst * 256) + outdated = true; + + return outdated == true || oscilprepared == false; +} + +/* + * Get the oscillator function + */ +short int OscilGen::get(float *smps, float freqHz, int resonance) +{ + if(needPrepare()) + prepare(); + + int outpos = + (int)((RND * 2.0f + - 1.0f) * synth->oscilsize_f * (Prand - 64.0f) / 64.0f); + outpos = (outpos + 2 * synth->oscilsize) % synth->oscilsize; + + + clearAll(outoscilFFTfreqs); + + int nyquist = (int)(0.5f * synth->samplerate_f / fabs(freqHz)) + 2; + if(ADvsPAD) + nyquist = (int)(synth->oscilsize / 2); + if(nyquist > synth->oscilsize / 2) + nyquist = synth->oscilsize / 2; + + //Process harmonics + { + int realnyquist = nyquist; + + if(Padaptiveharmonics != 0) + nyquist = synth->oscilsize / 2; + for(int i = 1; i < nyquist - 1; ++i) + outoscilFFTfreqs[i] = oscilFFTfreqs[i]; + + adaptiveharmonic(outoscilFFTfreqs, freqHz); + adaptiveharmonicpostprocess(&outoscilFFTfreqs[1], + synth->oscilsize / 2 - 1); + + nyquist = realnyquist; + } + + if(Padaptiveharmonics) //do the antialiasing in the case of adaptive harmonics + for(int i = nyquist; i < synth->oscilsize / 2; ++i) + outoscilFFTfreqs[i] = fft_t(0.0f, 0.0f); + + // Randomness (each harmonic), the block type is computed + // in ADnote by setting start position according to this setting + if((Prand > 64) && (freqHz >= 0.0f) && (!ADvsPAD)) { + const float rnd = PI * powf((Prand - 64.0f) / 64.0f, 2.0f); + for(int i = 1; i < nyquist - 1; ++i) //to Nyquist only for AntiAliasing + outoscilFFTfreqs[i] *= + std::polar(1.0f, (float)(rnd * i * RND)); + } + + //Harmonic Amplitude Randomness + if((freqHz > 0.1f) && (!ADvsPAD)) { + unsigned int realrnd = prng(); + sprng(randseed); + float power = Pamprandpower / 127.0f; + float normalize = 1.0f / (1.2f - power); + switch(Pamprandtype) { + case 1: + power = power * 2.0f - 0.5f; + power = powf(15.0f, power); + for(int i = 1; i < nyquist - 1; ++i) + outoscilFFTfreqs[i] *= powf(RND, power) * normalize; + break; + case 2: + power = power * 2.0f - 0.5f; + power = powf(15.0f, power) * 2.0f; + float rndfreq = 2 * PI * RND; + for(int i = 1; i < nyquist - 1; ++i) + outoscilFFTfreqs[i] *= powf(fabs(sinf(i * rndfreq)), power) + * normalize; + break; + } + sprng(realrnd + 1); + } + + if((freqHz > 0.1f) && (resonance != 0)) + res->applyres(nyquist - 1, outoscilFFTfreqs, freqHz); + + rmsNormalize(outoscilFFTfreqs); + + if((ADvsPAD) && (freqHz > 0.1f)) //in this case the smps will contain the freqs + for(int i = 1; i < synth->oscilsize / 2; ++i) + smps[i - 1] = abs(outoscilFFTfreqs, i); + else { + fft->freqs2smps(outoscilFFTfreqs, smps); + for(int i = 0; i < synth->oscilsize; ++i) + smps[i] *= 0.25f; //correct the amplitude + } + + if(Prand < 64) + return outpos; + else + return 0; +} + + +/* + * Get the spectrum of the oscillator for the UI + */ +void OscilGen::getspectrum(int n, float *spc, int what) +{ + if(n > synth->oscilsize / 2) + n = synth->oscilsize / 2; + + for(int i = 1; i < n; ++i) { + if(what == 0) + spc[i - 1] = abs(oscilFFTfreqs, i); + else { + if(Pcurrentbasefunc == 0) + spc[i - 1] = ((i == 1) ? (1.0f) : (0.0f)); + else + spc[i - 1] = abs(basefuncFFTfreqs, i); + } + } + + if(what == 0) { + for(int i = 0; i < n; ++i) + outoscilFFTfreqs[i] = fft_t(spc[i], spc[i]); + memset(outoscilFFTfreqs + n, 0, + (synth->oscilsize / 2 - n) * sizeof(fft_t)); + adaptiveharmonic(outoscilFFTfreqs, 0.0f); + adaptiveharmonicpostprocess(outoscilFFTfreqs, n - 1); + for(int i = 0; i < n; ++i) + spc[i] = outoscilFFTfreqs[i].imag(); + } +} + + +/* + * Convert the oscillator as base function + */ +void OscilGen::useasbase() +{ + for(int i = 0; i < synth->oscilsize / 2; ++i) + basefuncFFTfreqs[i] = oscilFFTfreqs[i]; + + oldbasefunc = Pcurrentbasefunc = 127; + prepare(); +} + + +/* + * Get the base function for UI + */ +void OscilGen::getcurrentbasefunction(float *smps) +{ + if(Pcurrentbasefunc != 0) + fft->freqs2smps(basefuncFFTfreqs, smps); + else + getbasefunction(smps); //the sine case +} + +void OscilGen::add2XML(XMLwrapper *xml) +{ + xml->addpar("harmonic_mag_type", Phmagtype); + + xml->addpar("base_function", Pcurrentbasefunc); + xml->addpar("base_function_par", Pbasefuncpar); + xml->addpar("base_function_modulation", Pbasefuncmodulation); + xml->addpar("base_function_modulation_par1", Pbasefuncmodulationpar1); + xml->addpar("base_function_modulation_par2", Pbasefuncmodulationpar2); + xml->addpar("base_function_modulation_par3", Pbasefuncmodulationpar3); + + xml->addpar("modulation", Pmodulation); + xml->addpar("modulation_par1", Pmodulationpar1); + xml->addpar("modulation_par2", Pmodulationpar2); + xml->addpar("modulation_par3", Pmodulationpar3); + + xml->addpar("wave_shaping", Pwaveshaping); + xml->addpar("wave_shaping_function", Pwaveshapingfunction); + + xml->addpar("filter_type", Pfiltertype); + xml->addpar("filter_par1", Pfilterpar1); + xml->addpar("filter_par2", Pfilterpar2); + xml->addpar("filter_before_wave_shaping", Pfilterbeforews); + + xml->addpar("spectrum_adjust_type", Psatype); + xml->addpar("spectrum_adjust_par", Psapar); + + xml->addpar("rand", Prand); + xml->addpar("amp_rand_type", Pamprandtype); + xml->addpar("amp_rand_power", Pamprandpower); + + xml->addpar("harmonic_shift", Pharmonicshift); + xml->addparbool("harmonic_shift_first", Pharmonicshiftfirst); + + xml->addpar("adaptive_harmonics", Padaptiveharmonics); + xml->addpar("adaptive_harmonics_base_frequency", Padaptiveharmonicsbasefreq); + xml->addpar("adaptive_harmonics_power", Padaptiveharmonicspower); + + xml->beginbranch("HARMONICS"); + for(int n = 0; n < MAX_AD_HARMONICS; ++n) { + if((Phmag[n] == 64) && (Phphase[n] == 64)) + continue; + xml->beginbranch("HARMONIC", n + 1); + xml->addpar("mag", Phmag[n]); + xml->addpar("phase", Phphase[n]); + xml->endbranch(); + } + xml->endbranch(); + + if(Pcurrentbasefunc == 127) { + normalize(basefuncFFTfreqs); + + xml->beginbranch("BASE_FUNCTION"); + for(int i = 1; i < synth->oscilsize / 2; ++i) { + float xc = basefuncFFTfreqs[i].real(); + float xs = basefuncFFTfreqs[i].imag(); + if((fabs(xs) > 0.00001f) && (fabs(xs) > 0.00001f)) { + xml->beginbranch("BF_HARMONIC", i); + xml->addparreal("cos", xc); + xml->addparreal("sin", xs); + xml->endbranch(); + } + } + xml->endbranch(); + } +} + +void OscilGen::getfromXML(XMLwrapper *xml) +{ + Phmagtype = xml->getpar127("harmonic_mag_type", Phmagtype); + + Pcurrentbasefunc = xml->getpar127("base_function", Pcurrentbasefunc); + Pbasefuncpar = xml->getpar127("base_function_par", Pbasefuncpar); + + Pbasefuncmodulation = xml->getpar127("base_function_modulation", + Pbasefuncmodulation); + Pbasefuncmodulationpar1 = xml->getpar127("base_function_modulation_par1", + Pbasefuncmodulationpar1); + Pbasefuncmodulationpar2 = xml->getpar127("base_function_modulation_par2", + Pbasefuncmodulationpar2); + Pbasefuncmodulationpar3 = xml->getpar127("base_function_modulation_par3", + Pbasefuncmodulationpar3); + + Pmodulation = xml->getpar127("modulation", Pmodulation); + Pmodulationpar1 = xml->getpar127("modulation_par1", + Pmodulationpar1); + Pmodulationpar2 = xml->getpar127("modulation_par2", + Pmodulationpar2); + Pmodulationpar3 = xml->getpar127("modulation_par3", + Pmodulationpar3); + + Pwaveshaping = xml->getpar127("wave_shaping", Pwaveshaping); + Pwaveshapingfunction = xml->getpar127("wave_shaping_function", + Pwaveshapingfunction); + + Pfiltertype = xml->getpar127("filter_type", Pfiltertype); + Pfilterpar1 = xml->getpar127("filter_par1", Pfilterpar1); + Pfilterpar2 = xml->getpar127("filter_par2", Pfilterpar2); + Pfilterbeforews = xml->getpar127("filter_before_wave_shaping", + Pfilterbeforews); + + Psatype = xml->getpar127("spectrum_adjust_type", Psatype); + Psapar = xml->getpar127("spectrum_adjust_par", Psapar); + + Prand = xml->getpar127("rand", Prand); + Pamprandtype = xml->getpar127("amp_rand_type", Pamprandtype); + Pamprandpower = xml->getpar127("amp_rand_power", Pamprandpower); + + Pharmonicshift = xml->getpar("harmonic_shift", + Pharmonicshift, + -64, + 64); + Pharmonicshiftfirst = xml->getparbool("harmonic_shift_first", + Pharmonicshiftfirst); + + Padaptiveharmonics = xml->getpar("adaptive_harmonics", + Padaptiveharmonics, + 0, + 127); + Padaptiveharmonicsbasefreq = xml->getpar( + "adaptive_harmonics_base_frequency", + Padaptiveharmonicsbasefreq, + 0, + 255); + Padaptiveharmonicspower = xml->getpar("adaptive_harmonics_power", + Padaptiveharmonicspower, + 0, + 200); + + + if(xml->enterbranch("HARMONICS")) { + Phmag[0] = 64; + Phphase[0] = 64; + for(int n = 0; n < MAX_AD_HARMONICS; ++n) { + if(xml->enterbranch("HARMONIC", n + 1) == 0) + continue; + Phmag[n] = xml->getpar127("mag", 64); + Phphase[n] = xml->getpar127("phase", 64); + xml->exitbranch(); + } + xml->exitbranch(); + } + + if(Pcurrentbasefunc != 0) + changebasefunction(); + + + if(xml->enterbranch("BASE_FUNCTION")) { + for(int i = 1; i < synth->oscilsize / 2; ++i) + if(xml->enterbranch("BF_HARMONIC", i)) { + basefuncFFTfreqs[i] = + std::complex(xml->getparreal("cos", 0.0f), + xml->getparreal("sin", 0.0f)); + xml->exitbranch(); + } + xml->exitbranch(); + + clearDC(basefuncFFTfreqs); + normalize(basefuncFFTfreqs); + } +} + + +//Define basic functions +#define FUNC(b) float basefunc_ ## b(float x, float a) + +FUNC(pulse) +{ + return (fmod(x, 1.0f) < a) ? -1.0f : 1.0f; +} + +FUNC(saw) +{ + if(a < 0.00001f) + a = 0.00001f; + else + if(a > 0.99999f) + a = 0.99999f; + x = fmod(x, 1); + if(x < a) + return x / a * 2.0f - 1.0f; + else + return (1.0f - x) / (1.0f - a) * 2.0f - 1.0f; +} + +FUNC(triangle) +{ + x = fmod(x + 0.25f, 1); + a = 1 - a; + if(a < 0.00001f) + a = 0.00001f; + if(x < 0.5f) + x = x * 4 - 1.0f; + else + x = (1.0f - x) * 4 - 1.0f; + x /= -a; + if(x < -1.0f) + x = -1.0f; + if(x > 1.0f) + x = 1.0f; + return x; +} + +FUNC(power) +{ + x = fmod(x, 1); + if(a < 0.00001f) + a = 0.00001f; + else + if(a > 0.99999f) + a = 0.99999f; + return powf(x, expf((a - 0.5f) * 10.0f)) * 2.0f - 1.0f; +} + +FUNC(gauss) +{ + x = fmod(x, 1) * 2.0f - 1.0f; + if(a < 0.00001f) + a = 0.00001f; + return expf(-x * x * (expf(a * 8) + 5.0f)) * 2.0f - 1.0f; +} + +FUNC(diode) +{ + if(a < 0.00001f) + a = 0.00001f; + else + if(a > 0.99999f) + a = 0.99999f; + a = a * 2.0f - 1.0f; + x = cosf((x + 0.5f) * 2.0f * PI) - a; + if(x < 0.0f) + x = 0.0f; + return x / (1.0f - a) * 2 - 1.0f; +} + +FUNC(abssine) +{ + x = fmod(x, 1); + if(a < 0.00001f) + a = 0.00001f; + else + if(a > 0.99999f) + a = 0.99999f; + return sinf(powf(x, expf((a - 0.5f) * 5.0f)) * PI) * 2.0f - 1.0f; +} + +FUNC(pulsesine) +{ + if(a < 0.00001f) + a = 0.00001f; + x = (fmod(x, 1) - 0.5f) * expf((a - 0.5f) * logf(128)); + if(x < -0.5f) + x = -0.5f; + else + if(x > 0.5f) + x = 0.5f; + x = sinf(x * PI * 2.0f); + return x; +} + +FUNC(stretchsine) +{ + x = fmod(x + 0.5f, 1) * 2.0f - 1.0f; + a = (a - 0.5f) * 4; + if(a > 0.0f) + a *= 2; + a = powf(3.0f, a); + float b = powf(fabs(x), a); + if(x < 0) + b = -b; + return -sinf(b * PI); +} + +FUNC(chirp) +{ + x = fmod(x, 1.0f) * 2.0f * PI; + a = (a - 0.5f) * 4; + if(a < 0.0f) + a *= 2.0f; + a = powf(3.0f, a); + return sinf(x / 2.0f) * sinf(a * x * x); +} + +FUNC(absstretchsine) +{ + x = fmod(x + 0.5f, 1) * 2.0f - 1.0f; + a = (a - 0.5f) * 9; + a = powf(3.0f, a); + float b = powf(fabs(x), a); + if(x < 0) + b = -b; + return -powf(sinf(b * PI), 2); +} + +FUNC(chebyshev) +{ + a = a * a * a * 30.0f + 1.0f; + return cosf(acosf(x * 2.0f - 1.0f) * a); +} + +FUNC(sqr) +{ + a = a * a * a * a * 160.0f + 0.001f; + return -atanf(sinf(x * 2.0f * PI) * a); +} + +FUNC(spike) +{ + float b = a * 0.66666; // the width of the range: if a == 0.5, b == 0.33333 + + if(x < 0.5) { + if(x < (0.5 - (b / 2.0))) + return 0.0; + else { + x = (x + (b / 2) - 0.5) * (2.0 / b); // shift to zero, and expand to range from 0 to 1 + return x * (2.0 / b); // this is the slope: 1 / (b / 2) + } + } + else { + if(x > (0.5 + (b / 2.0))) + return 0.0; + else { + x = (x - 0.5) * (2.0 / b); + return (1 - x) * (2.0 / b); + } + } +} + +FUNC(circle) +{ + // a is parameter: 0 -> 0.5 -> 1 // O.5 = circle + float b, y; + + b = 2 - (a * 2); // b goes from 2 to 0 + x = x * 4; + + if(x < 2) { + x = x - 1; // x goes from -1 to 1 + if((x < -b) || (x > b)) + y = 0; + else + y = sqrt(1 - (pow(x, 2) / pow(b, 2))); // normally * a^2, but a stays 1 + } + else { + x = x - 3; // x goes from -1 to 1 as well + if((x < -b) || (x > b)) + y = 0; + else + y = -sqrt(1 - (pow(x, 2) / pow(b, 2))); + } + return y; +} + +typedef float (*base_func)(float, float); + +base_func getBaseFunction(unsigned char func) +{ + if(!func) + return NULL; + + if(func == 127) //should be the custom wave + return NULL; + + func--; + assert(func < 15); + base_func functions[] = { + basefunc_triangle, + basefunc_pulse, + basefunc_saw, + basefunc_power, + basefunc_gauss, + basefunc_diode, + basefunc_abssine, + basefunc_pulsesine, + basefunc_stretchsine, + basefunc_chirp, + basefunc_absstretchsine, + basefunc_chebyshev, + basefunc_sqr, + basefunc_spike, + basefunc_circle, + }; + return functions[func]; +} + +//And filters + +#define FILTER(x) float osc_ ## x(unsigned int i, float par, float par2) +FILTER(lp) +{ + float gain = powf(1.0f - par * par * par * 0.99f, i); + float tmp = par2 * par2 * par2 * par2 * 0.5f + 0.0001f; + if(gain < tmp) + gain = powf(gain, 10.0f) / powf(tmp, 9.0f); + return gain; +} + +FILTER(hp1) +{ + float gain = 1.0f - powf(1.0f - par * par, i + 1); + return powf(gain, par2 * 2.0f + 0.1f); +} + +FILTER(hp1b) +{ + if(par < 0.2f) + par = par * 0.25f + 0.15f; + float gain = 1.0f - powf(1.0f - par * par * 0.999f + 0.001f, + i * 0.05f * i + 1.0f); + float tmp = powf(5.0f, par2 * 2.0f); + return powf(gain, tmp); +} + +FILTER(bp1) +{ + float gain = i + 1 - powf(2, (1.0f - par) * 7.5f); + gain = 1.0f / (1.0f + gain * gain / (i + 1.0f)); + float tmp = powf(5.0f, par2 * 2.0f); + gain = powf(gain, tmp); + if(gain < 1e-5) + gain = 1e-5; + return gain; +} + +FILTER(bs1) +{ + float gain = i + 1 - powf(2, (1.0f - par) * 7.5f); + gain = powf(atanf(gain / (i / 10.0f + 1)) / 1.57f, 6); + return powf(gain, par2 * par2 * 3.9f + 0.1f); +} + +FILTER(lp2) +{ + return (i + 1 > + powf(2, (1.0f - par) * 10) ? 0.0f : 1.0f) * par2 + (1.0f - par2); +} + +FILTER(hp2) +{ + if(par == 1) + return 1.0f; + return (i + 1 > + powf(2, (1.0f - par) * 7) ? 1.0f : 0.0f) * par2 + (1.0f - par2); +} + +FILTER(bp2) +{ + return (fabs(powf(2, + (1.0f + - par) + * 7) + - i) > i / 2 + 1 ? 0.0f : 1.0f) * par2 + (1.0f - par2); +} + +FILTER(bs2) +{ + return (fabs(powf(2, + (1.0f + - par) + * 7) + - i) < i / 2 + 1 ? 0.0f : 1.0f) * par2 + (1.0f - par2); +} + +bool floatEq(float a, float b) +{ + const float fudge = .01; + return a + fudge > b && a - fudge < b; +} + +FILTER(cos) +{ + float tmp = powf(5.0f, par2 * 2.0f - 1.0f); + tmp = powf(i / 32.0f, tmp) * 32.0f; + if(floatEq(par2 * 127.0f, 64.0f)) + tmp = i; + float gain = cosf(par * par * PI / 2.0f * tmp); + gain *= gain; + return gain; +} + +FILTER(sin) +{ + float tmp = powf(5.0f, par2 * 2.0f - 1.0f); + tmp = powf(i / 32.0f, tmp) * 32.0f; + if(floatEq(par2 * 127.0f, 64.0f)) + tmp = i; + float gain = sinf(par * par * PI / 2.0f * tmp); + gain *= gain; + return gain; +} + +FILTER(low_shelf) +{ + float p2 = 1.0f - par + 0.2f; + float x = i / (64.0f * p2 * p2); + if(x < 0.0f) + x = 0.0f; + else + if(x > 1.0f) + x = 1.0f; + float tmp = powf(1.0f - par2, 2.0f); + return cosf(x * PI) * (1.0f - tmp) + 1.01f + tmp; +} + +FILTER(s) +{ + unsigned int tmp = (int) (powf(2.0f, (1.0f - par) * 7.2f)); + float gain = 1.0f; + if(i == tmp) + gain = powf(2.0f, par2 * par2 * 8.0f); + return gain; +} +#undef FILTER + +typedef float (*filter_func)(unsigned int, float, float); +filter_func getFilter(unsigned char func) +{ + if(!func) + return NULL; + + func--; + assert(func < 13); + filter_func functions[] = { + osc_lp, + osc_hp1, + osc_hp1b, + osc_bp1, + osc_bs1, + osc_lp2, + osc_hp2, + osc_bp2, + osc_bs2, + osc_cos, + osc_sin, + osc_low_shelf, + osc_s + }; + return functions[func]; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/OscilGen.h b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/OscilGen.h new file mode 100644 index 000000000..483a3859a --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/OscilGen.h @@ -0,0 +1,175 @@ +/* + ZynAddSubFX - a software synthesizer + + OscilGen.h - Waveform generator for ADnote + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef OSCIL_GEN_H +#define OSCIL_GEN_H + +#include "../globals.h" +#include "../Misc/XMLwrapper.h" +#include "../DSP/FFTwrapper.h" +#include "../Params/Presets.h" +#include "Resonance.h" + +class OscilGen:public Presets +{ + public: + OscilGen(FFTwrapper *fft_, Resonance *res_); + ~OscilGen(); + + /**computes the full spectrum of oscil from harmonics,phases and basefunc*/ + void prepare(); + + /**do the antialiasing(cut off higher freqs.),apply randomness and do a IFFT*/ + //returns where should I start getting samples, used in block type randomness + short get(float *smps, float freqHz, int resonance = 0); + //if freqHz is smaller than 0, return the "un-randomized" sample for UI + + void getbasefunction(float *smps); + + //called by UI + void getspectrum(int n, float *spc, int what); //what=0 pt. oscil,1 pt. basefunc + void getcurrentbasefunction(float *smps); + /**convert oscil to base function*/ + void useasbase(); + + void add2XML(XMLwrapper *xml); + void defaults(); + void getfromXML(XMLwrapper *xml); + + void convert2sine(); + + //Parameters + + /** + * The hmag and hphase starts counting from 0, so the first harmonic(1) has the index 0, + * 2-nd harmonic has index 1, ..the 128 harminic has index 127 + */ + unsigned char Phmag[MAX_AD_HARMONICS], Phphase[MAX_AD_HARMONICS]; //the MIDI parameters for mag. and phases + + + /**The Type of magnitude: + * 0 - Linear + * 1 - dB scale (-40) + * 2 - dB scale (-60) + * 3 - dB scale (-80) + * 4 - dB scale (-100)*/ + unsigned char Phmagtype; + + unsigned char Pcurrentbasefunc; //The base function used - 0=sin, 1=... + unsigned char Pbasefuncpar; //the parameter of the base function + + unsigned char Pbasefuncmodulation; //what modulation is applied to the basefunc + unsigned char Pbasefuncmodulationpar1, Pbasefuncmodulationpar2, + Pbasefuncmodulationpar3; //the parameter of the base function modulation + + /*the Randomness: + 64=no randomness + 63..0 - block type randomness - 0 is maximum + 65..127 - each harmonic randomness - 127 is maximum*/ + unsigned char Prand; + unsigned char Pwaveshaping, Pwaveshapingfunction; + unsigned char Pfiltertype, Pfilterpar1, Pfilterpar2; + unsigned char Pfilterbeforews; + unsigned char Psatype, Psapar; //spectrum adjust + + unsigned char Pamprandpower, Pamprandtype; //amplitude randomness + int Pharmonicshift; //how the harmonics are shifted + int Pharmonicshiftfirst; //if the harmonic shift is done before waveshaping and filter + + unsigned char Padaptiveharmonics; //the adaptive harmonics status (off=0,on=1,etc..) + unsigned char Padaptiveharmonicsbasefreq; //the base frequency of the adaptive harmonic (30..3000Hz) + unsigned char Padaptiveharmonicspower; //the strength of the effect (0=off,100=full) + unsigned char Padaptiveharmonicspar; //the parameters in 2,3,4.. modes of adaptive harmonics + + unsigned char Pmodulation; //what modulation is applied to the oscil + unsigned char Pmodulationpar1, Pmodulationpar2, Pmodulationpar3; //the parameter of the parameters + + + //makes a new random seed for Amplitude Randomness + //this should be called every note on event + void newrandseed(unsigned int randseed); + + bool ADvsPAD; //if it is used by ADsynth or by PADsynth + + private: + //This array stores some termporary data and it has OSCIL_SIZE elements + float *tmpsmps; + fft_t *outoscilFFTfreqs; + + float hmag[MAX_AD_HARMONICS], hphase[MAX_AD_HARMONICS]; //the magnituides and the phases of the sine/nonsine harmonics +// private: + FFTwrapper *fft; + //computes the basefunction and make the FFT; newbasefunc<0 = same basefunc + void changebasefunction(); + //Waveshaping + void waveshape(); + + //Filter the oscillator accotding to Pfiltertype and Pfilterpar + void oscilfilter(); + + //Adjust the spectrum + void spectrumadjust(); + + //Shift the harmonics + void shiftharmonics(); + + //Do the oscil modulation stuff + void modulation(); + + //Check system for needed updates + bool needPrepare(void); + + //Do the adaptive harmonic stuff + void adaptiveharmonic(fft_t *f, float freq); + + //Do the adaptive harmonic postprocessing (2n+1,2xS,2xA,etc..) + //this function is called even for the user interface + //this can be called for the sine and components, and for the spectrum + //(that's why the sine and cosine components should be processed with a separate call) + void adaptiveharmonicpostprocess(fft_t *f, int size); + + //Internal Data + unsigned char oldbasefunc, oldbasepar, oldhmagtype, + oldwaveshapingfunction, oldwaveshaping; + int oldfilterpars, oldsapars, oldbasefuncmodulation, + oldbasefuncmodulationpar1, oldbasefuncmodulationpar2, + oldbasefuncmodulationpar3, oldharmonicshift; + int oldmodulation, oldmodulationpar1, oldmodulationpar2, + oldmodulationpar3; + + + fft_t *basefuncFFTfreqs; //Base Function Frequencies + fft_t *oscilFFTfreqs; //Oscillator Frequencies - this is different than the hamonics set-up by the user, it may contains time-domain data if the antialiasing is turned off + int oscilprepared; //1 if the oscil is prepared, 0 if it is not prepared and is need to call ::prepare() before ::get() + + Resonance *res; + + unsigned int randseed; +}; + +typedef float (*filter_func)(unsigned int, float, float); +filter_func getFilter(unsigned char func); +typedef float (*base_func)(float, float); +base_func getBaseFunction(unsigned char func); + + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/PADnote.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/PADnote.cpp new file mode 100644 index 000000000..1778b3388 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/PADnote.cpp @@ -0,0 +1,432 @@ +/* + ZynAddSubFX - a software synthesizer + + pADnote.cpp - The "pad" synthesizer + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include "PADnote.h" +#include "../Misc/Config.h" +#include "../DSP/Filter.h" + +PADnote::PADnote(PADnoteParameters *parameters, + Controller *ctl_, + float freq, + float velocity, + int portamento_, + int midinote, + bool besilent) + :SynthNote(freq, velocity, portamento_, midinote, besilent) +{ + pars = parameters; + + ctl = ctl_; + firsttime = true; + setup(freq, velocity, portamento_, midinote); +} + + +void PADnote::setup(float freq, + float velocity, + int portamento_, + int midinote, + bool legato) +{ + portamento = portamento_; + this->velocity = velocity; + finished_ = false; + + + if(pars->Pfixedfreq == 0) + basefreq = freq; + else { + basefreq = 440.0f; + int fixedfreqET = pars->PfixedfreqET; + if(fixedfreqET != 0) { //if the frequency varies according the keyboard note + float tmp = + (midinote + - 69.0f) / 12.0f + * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f); + if(fixedfreqET <= 64) + basefreq *= powf(2.0f, tmp); + else + basefreq *= powf(3.0f, tmp); + } + } + + firsttime = true; + released = false; + realfreq = basefreq; + if(!legato) + NoteGlobalPar.Detune = getdetune(pars->PDetuneType, pars->PCoarseDetune, + pars->PDetune); + + + //find out the closest note + float logfreq = logf(basefreq * powf(2.0f, NoteGlobalPar.Detune / 1200.0f)); + float mindist = fabs(logfreq - logf(pars->sample[0].basefreq + 0.0001f)); + nsample = 0; + for(int i = 1; i < PAD_MAX_SAMPLES; ++i) { + if(pars->sample[i].smp == NULL) + break; + float dist = fabs(logfreq - logf(pars->sample[i].basefreq + 0.0001f)); + + if(dist < mindist) { + nsample = i; + mindist = dist; + } + } + + int size = pars->sample[nsample].size; + if(size == 0) + size = 1; + + + if(!legato) { //not sure + poshi_l = (int)(RND * (size - 1)); + if(pars->PStereo != 0) + poshi_r = (poshi_l + size / 2) % size; + else + poshi_r = poshi_l; + poslo = 0.0f; + } + + + if(pars->PPanning == 0) + NoteGlobalPar.Panning = RND; + else + NoteGlobalPar.Panning = pars->PPanning / 128.0f; + + NoteGlobalPar.FilterCenterPitch = pars->GlobalFilter->getfreq() //center freq + + pars->PFilterVelocityScale / 127.0f + * 6.0f //velocity sensing + * (VelF(velocity, + pars-> + PFilterVelocityScaleFunction) - 1); + + if(!legato) { + if(pars->PPunchStrength != 0) { + NoteGlobalPar.Punch.Enabled = 1; + NoteGlobalPar.Punch.t = 1.0f; //start from 1.0f and to 0.0f + NoteGlobalPar.Punch.initialvalue = + ((powf(10, 1.5f * pars->PPunchStrength / 127.0f) - 1.0f) + * VelF(velocity, + pars->PPunchVelocitySensing)); + float time = + powf(10, 3.0f * pars->PPunchTime / 127.0f) / 10000.0f; //0.1f .. 100 ms + float stretch = powf(440.0f / freq, pars->PPunchStretch / 64.0f); + NoteGlobalPar.Punch.dt = 1.0f + / (time * synth->samplerate_f * stretch); + } + else + NoteGlobalPar.Punch.Enabled = 0; + + NoteGlobalPar.FreqEnvelope = new Envelope(pars->FreqEnvelope, basefreq); + NoteGlobalPar.FreqLfo = new LFO(pars->FreqLfo, basefreq); + + NoteGlobalPar.AmpEnvelope = new Envelope(pars->AmpEnvelope, basefreq); + NoteGlobalPar.AmpLfo = new LFO(pars->AmpLfo, basefreq); + } + + NoteGlobalPar.Volume = 4.0f + * powf(0.1f, 3.0f * (1.0f - pars->PVolume / 96.0f)) //-60 dB .. 0 dB + * VelF(velocity, pars->PAmpVelocityScaleFunction); //velocity sensing + + NoteGlobalPar.AmpEnvelope->envout_dB(); //discard the first envelope output + globaloldamplitude = globalnewamplitude = NoteGlobalPar.Volume + * NoteGlobalPar.AmpEnvelope-> + envout_dB() + * NoteGlobalPar.AmpLfo->amplfoout(); + + if(!legato) { + NoteGlobalPar.GlobalFilterL = Filter::generate(pars->GlobalFilter); + NoteGlobalPar.GlobalFilterR = Filter::generate(pars->GlobalFilter); + + NoteGlobalPar.FilterEnvelope = new Envelope(pars->FilterEnvelope, + basefreq); + NoteGlobalPar.FilterLfo = new LFO(pars->FilterLfo, basefreq); + } + NoteGlobalPar.FilterQ = pars->GlobalFilter->getq(); + NoteGlobalPar.FilterFreqTracking = pars->GlobalFilter->getfreqtracking( + basefreq); + + if(pars->sample[nsample].smp == NULL) { + finished_ = true; + return; + } +} + +void PADnote::legatonote(float freq, + float velocity, + int portamento_, + int midinote, + bool externcall) +{ + // Manage legato stuff + if(legato.update(freq, velocity, portamento_, midinote, externcall)) + return; + + setup(freq, velocity, portamento_, midinote, true); +} + + +PADnote::~PADnote() +{ + delete (NoteGlobalPar.FreqEnvelope); + delete (NoteGlobalPar.FreqLfo); + delete (NoteGlobalPar.AmpEnvelope); + delete (NoteGlobalPar.AmpLfo); + delete (NoteGlobalPar.GlobalFilterL); + delete (NoteGlobalPar.GlobalFilterR); + delete (NoteGlobalPar.FilterEnvelope); + delete (NoteGlobalPar.FilterLfo); +} + + +inline void PADnote::fadein(float *smps) +{ + int zerocrossings = 0; + for(int i = 1; i < synth->buffersize; ++i) + if((smps[i - 1] < 0.0f) && (smps[i] > 0.0f)) + zerocrossings++; //this is only the possitive crossings + + float tmp = (synth->buffersize_f - 1.0f) / (zerocrossings + 1) / 3.0f; + if(tmp < 8.0f) + tmp = 8.0f; + + int n; + F2I(tmp, n); //how many samples is the fade-in + if(n > synth->buffersize) + n = synth->buffersize; + for(int i = 0; i < n; ++i) { //fade-in + float tmp = 0.5f - cosf((float)i / (float) n * PI) * 0.5f; + smps[i] *= tmp; + } +} + + +void PADnote::computecurrentparameters() +{ + float globalpitch, globalfilterpitch; + globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout() + + NoteGlobalPar.FreqLfo->lfoout() + * ctl->modwheel.relmod + NoteGlobalPar.Detune); + globaloldamplitude = globalnewamplitude; + globalnewamplitude = NoteGlobalPar.Volume + * NoteGlobalPar.AmpEnvelope->envout_dB() + * NoteGlobalPar.AmpLfo->amplfoout(); + + globalfilterpitch = NoteGlobalPar.FilterEnvelope->envout() + + NoteGlobalPar.FilterLfo->lfoout() + + NoteGlobalPar.FilterCenterPitch; + + float tmpfilterfreq = globalfilterpitch + ctl->filtercutoff.relfreq + + NoteGlobalPar.FilterFreqTracking; + + tmpfilterfreq = Filter::getrealfreq(tmpfilterfreq); + + float globalfilterq = NoteGlobalPar.FilterQ * ctl->filterq.relq; + NoteGlobalPar.GlobalFilterL->setfreq_and_q(tmpfilterfreq, globalfilterq); + NoteGlobalPar.GlobalFilterR->setfreq_and_q(tmpfilterfreq, globalfilterq); + + //compute the portamento, if it is used by this note + float portamentofreqrap = 1.0f; + if(portamento != 0) { //this voice use portamento + portamentofreqrap = ctl->portamento.freqrap; + if(ctl->portamento.used == 0) //the portamento has finished + portamento = 0; //this note is no longer "portamented" + ; + } + + realfreq = basefreq * portamentofreqrap + * powf(2.0f, globalpitch / 12.0f) * ctl->pitchwheel.relfreq; +} + + +int PADnote::Compute_Linear(float *outl, + float *outr, + int freqhi, + float freqlo) +{ + float *smps = pars->sample[nsample].smp; + if(smps == NULL) { + finished_ = true; + return 1; + } + int size = pars->sample[nsample].size; + for(int i = 0; i < synth->buffersize; ++i) { + poshi_l += freqhi; + poshi_r += freqhi; + poslo += freqlo; + if(poslo >= 1.0f) { + poshi_l += 1; + poshi_r += 1; + poslo -= 1.0f; + } + if(poshi_l >= size) + poshi_l %= size; + if(poshi_r >= size) + poshi_r %= size; + + outl[i] = smps[poshi_l] * (1.0f - poslo) + smps[poshi_l + 1] * poslo; + outr[i] = smps[poshi_r] * (1.0f - poslo) + smps[poshi_r + 1] * poslo; + } + return 1; +} +int PADnote::Compute_Cubic(float *outl, + float *outr, + int freqhi, + float freqlo) +{ + float *smps = pars->sample[nsample].smp; + if(smps == NULL) { + finished_ = true; + return 1; + } + int size = pars->sample[nsample].size; + float xm1, x0, x1, x2, a, b, c; + for(int i = 0; i < synth->buffersize; ++i) { + poshi_l += freqhi; + poshi_r += freqhi; + poslo += freqlo; + if(poslo >= 1.0f) { + poshi_l += 1; + poshi_r += 1; + poslo -= 1.0f; + } + if(poshi_l >= size) + poshi_l %= size; + if(poshi_r >= size) + poshi_r %= size; + + + //left + xm1 = smps[poshi_l]; + x0 = smps[poshi_l + 1]; + x1 = smps[poshi_l + 2]; + x2 = smps[poshi_l + 3]; + a = (3.0f * (x0 - x1) - xm1 + x2) * 0.5f; + b = 2.0f * x1 + xm1 - (5.0f * x0 + x2) * 0.5f; + c = (x1 - xm1) * 0.5f; + outl[i] = (((a * poslo) + b) * poslo + c) * poslo + x0; + //right + xm1 = smps[poshi_r]; + x0 = smps[poshi_r + 1]; + x1 = smps[poshi_r + 2]; + x2 = smps[poshi_r + 3]; + a = (3.0f * (x0 - x1) - xm1 + x2) * 0.5f; + b = 2.0f * x1 + xm1 - (5.0f * x0 + x2) * 0.5f; + c = (x1 - xm1) * 0.5f; + outr[i] = (((a * poslo) + b) * poslo + c) * poslo + x0; + } + return 1; +} + + +int PADnote::noteout(float *outl, float *outr) +{ + computecurrentparameters(); + float *smps = pars->sample[nsample].smp; + if(smps == NULL) { + for(int i = 0; i < synth->buffersize; ++i) { + outl[i] = 0.0f; + outr[i] = 0.0f; + } + return 1; + } + float smpfreq = pars->sample[nsample].basefreq; + + + float freqrap = realfreq / smpfreq; + int freqhi = (int) (floor(freqrap)); + float freqlo = freqrap - floor(freqrap); + + + if(config.cfg.Interpolation) + Compute_Cubic(outl, outr, freqhi, freqlo); + else + Compute_Linear(outl, outr, freqhi, freqlo); + + + if(firsttime) { + fadein(outl); + fadein(outr); + firsttime = false; + } + + NoteGlobalPar.GlobalFilterL->filterout(outl); + NoteGlobalPar.GlobalFilterR->filterout(outr); + + //Apply the punch + if(NoteGlobalPar.Punch.Enabled != 0) + for(int i = 0; i < synth->buffersize; ++i) { + float punchamp = NoteGlobalPar.Punch.initialvalue + * NoteGlobalPar.Punch.t + 1.0f; + outl[i] *= punchamp; + outr[i] *= punchamp; + NoteGlobalPar.Punch.t -= NoteGlobalPar.Punch.dt; + if(NoteGlobalPar.Punch.t < 0.0f) { + NoteGlobalPar.Punch.Enabled = 0; + break; + } + } + + if(ABOVE_AMPLITUDE_THRESHOLD(globaloldamplitude, globalnewamplitude)) + // Amplitude Interpolation + for(int i = 0; i < synth->buffersize; ++i) { + float tmpvol = INTERPOLATE_AMPLITUDE(globaloldamplitude, + globalnewamplitude, + i, + synth->buffersize); + outl[i] *= tmpvol * NoteGlobalPar.Panning; + outr[i] *= tmpvol * (1.0f - NoteGlobalPar.Panning); + } + else + for(int i = 0; i < synth->buffersize; ++i) { + outl[i] *= globalnewamplitude * NoteGlobalPar.Panning; + outr[i] *= globalnewamplitude * (1.0f - NoteGlobalPar.Panning); + } + + + // Apply legato-specific sound signal modifications + legato.apply(*this, outl, outr); + + // Check if the global amplitude is finished. + // If it does, disable the note + if(NoteGlobalPar.AmpEnvelope->finished() != 0) { + for(int i = 0; i < synth->buffersize; ++i) { //fade-out + float tmp = 1.0f - (float)i / synth->buffersize_f; + outl[i] *= tmp; + outr[i] *= tmp; + } + finished_ = 1; + } + + return 1; +} + +int PADnote::finished() const +{ + return finished_; +} + +void PADnote::relasekey() +{ + NoteGlobalPar.FreqEnvelope->relasekey(); + NoteGlobalPar.FilterEnvelope->relasekey(); + NoteGlobalPar.AmpEnvelope->relasekey(); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/PADnote.h b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/PADnote.h new file mode 100644 index 000000000..f40199672 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/PADnote.h @@ -0,0 +1,121 @@ +/* + ZynAddSubFX - a software synthesizer + + PADnote.h - The "pad" synthesizer + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef PAD_NOTE_H +#define PAD_NOTE_H + +#include "SynthNote.h" +#include "../globals.h" +#include "../Params/PADnoteParameters.h" +#include "../Params/Controller.h" +#include "Envelope.h" +#include "LFO.h" +#include "../Params/Controller.h" + +/**The "pad" synthesizer*/ +class PADnote:public SynthNote +{ + public: + PADnote(PADnoteParameters *parameters, + Controller *ctl_, + float freq, + float velocity, + int portamento_, + int midinote, + bool besilent); + ~PADnote(); + + void legatonote(float freq, float velocity, int portamento_, + int midinote, bool externcall); + + int noteout(float *outl, float *outr); + int finished() const; + void relasekey(); + private: + void setup(float freq, float velocity, int portamento_, + int midinote, bool legato = false); + void fadein(float *smps); + void computecurrentparameters(); + bool finished_; + PADnoteParameters *pars; + + int poshi_l, poshi_r; + float poslo; + + float basefreq; + bool firsttime, released; + + int nsample, portamento; + + int Compute_Linear(float *outl, + float *outr, + int freqhi, + float freqlo); + int Compute_Cubic(float *outl, + float *outr, + int freqhi, + float freqlo); + + + struct { + /****************************************** + * FREQUENCY GLOBAL PARAMETERS * + ******************************************/ + float Detune; //cents + + Envelope *FreqEnvelope; + LFO *FreqLfo; + + /******************************************** + * AMPLITUDE GLOBAL PARAMETERS * + ********************************************/ + float Volume; // [ 0 .. 1 ] + + float Panning; // [ 0 .. 1 ] + + Envelope *AmpEnvelope; + LFO *AmpLfo; + + struct { + int Enabled; + float initialvalue, dt, t; + } Punch; + + /****************************************** + * FILTER GLOBAL PARAMETERS * + ******************************************/ + class Filter * GlobalFilterL, *GlobalFilterR; + + float FilterCenterPitch; //octaves + float FilterQ; + float FilterFreqTracking; + + Envelope *FilterEnvelope; + + LFO *FilterLfo; + } NoteGlobalPar; + + + float globaloldamplitude, globalnewamplitude, velocity, realfreq; + Controller *ctl; +}; + + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Resonance.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Resonance.cpp new file mode 100644 index 000000000..4eb4e0197 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Resonance.cpp @@ -0,0 +1,276 @@ +/* + ZynAddSubFX - a software synthesizer + + Resonance.cpp - Resonance + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include "Resonance.h" + +Resonance::Resonance():Presets() +{ + setpresettype("Presonance"); + defaults(); +} + +Resonance::~Resonance() +{} + + +void Resonance::defaults() +{ + Penabled = 0; + PmaxdB = 20; + Pcenterfreq = 64; //1 kHz + Poctavesfreq = 64; + Pprotectthefundamental = 0; + ctlcenter = 1.0f; + ctlbw = 1.0f; + for(int i = 0; i < N_RES_POINTS; ++i) + Prespoints[i] = 64; +} + +/* + * Set a point of resonance function with a value + */ +void Resonance::setpoint(int n, unsigned char p) +{ + if((n < 0) || (n >= N_RES_POINTS)) + return; + Prespoints[n] = p; +} + +/* + * Apply the resonance to FFT data + */ +void Resonance::applyres(int n, fft_t *fftdata, float freq) +{ + if(Penabled == 0) + return; //if the resonance is disabled + float sum = 0.0f, + l1 = logf(getfreqx(0.0f) * ctlcenter), + l2 = logf(2.0f) * getoctavesfreq() * ctlbw; + + for(int i = 0; i < N_RES_POINTS; ++i) + if(sum < Prespoints[i]) + sum = Prespoints[i]; + if(sum < 1.0f) + sum = 1.0f; + + for(int i = 1; i < n; ++i) { + float x = (logf(freq * i) - l1) / l2; //compute where the n-th hamonics fits to the graph + if(x < 0.0f) + x = 0.0f; + + x *= N_RES_POINTS; + float dx = x - floor(x); + x = floor(x); + int kx1 = (int)x; + if(kx1 >= N_RES_POINTS) + kx1 = N_RES_POINTS - 1; + int kx2 = kx1 + 1; + if(kx2 >= N_RES_POINTS) + kx2 = N_RES_POINTS - 1; + float y = + (Prespoints[kx1] + * (1.0f - dx) + Prespoints[kx2] * dx) / 127.0f - sum / 127.0f; + + y = powf(10.0f, y * PmaxdB / 20.0f); + + if((Pprotectthefundamental != 0) && (i == 1)) + y = 1.0f; + + fftdata[i] *= y; + } +} + +/* + * Gets the response at the frequency "freq" + */ + +float Resonance::getfreqresponse(float freq) +{ + float l1 = logf(getfreqx(0.0f) * ctlcenter), + l2 = logf(2.0f) * getoctavesfreq() * ctlbw, sum = 0.0f; + + for(int i = 0; i < N_RES_POINTS; ++i) + if(sum < Prespoints[i]) + sum = Prespoints[i]; + if(sum < 1.0f) + sum = 1.0f; + + float x = (logf(freq) - l1) / l2; //compute where the n-th hamonics fits to the graph + if(x < 0.0f) + x = 0.0f; + x *= N_RES_POINTS; + float dx = x - floor(x); + x = floor(x); + int kx1 = (int)x; + if(kx1 >= N_RES_POINTS) + kx1 = N_RES_POINTS - 1; + int kx2 = kx1 + 1; + if(kx2 >= N_RES_POINTS) + kx2 = N_RES_POINTS - 1; + float result = + (Prespoints[kx1] + * (1.0f - dx) + Prespoints[kx2] * dx) / 127.0f - sum / 127.0f; + result = powf(10.0f, result * PmaxdB / 20.0f); + return result; +} + + +/* + * Smooth the resonance function + */ +void Resonance::smooth() +{ + float old = Prespoints[0]; + for(int i = 0; i < N_RES_POINTS; ++i) { + old = old * 0.4f + Prespoints[i] * 0.6f; + Prespoints[i] = (int) old; + } + old = Prespoints[N_RES_POINTS - 1]; + for(int i = N_RES_POINTS - 1; i > 0; i--) { + old = old * 0.4f + Prespoints[i] * 0.6f; + Prespoints[i] = (int) old + 1; + if(Prespoints[i] > 127) + Prespoints[i] = 127; + } +} + +/* + * Randomize the resonance function + */ +void Resonance::randomize(int type) +{ + int r = (int)(RND * 127.0f); + for(int i = 0; i < N_RES_POINTS; ++i) { + Prespoints[i] = r; + if((RND < 0.1f) && (type == 0)) + r = (int)(RND * 127.0f); + if((RND < 0.3f) && (type == 1)) + r = (int)(RND * 127.0f); + if(type == 2) + r = (int)(RND * 127.0f); + } + smooth(); +} + +/* + * Interpolate the peaks + */ +void Resonance::interpolatepeaks(int type) +{ + int x1 = 0, y1 = Prespoints[0]; + for(int i = 1; i < N_RES_POINTS; ++i) + if((Prespoints[i] != 64) || (i + 1 == N_RES_POINTS)) { + int y2 = Prespoints[i]; + for(int k = 0; k < i - x1; ++k) { + float x = (float) k / (i - x1); + if(type == 0) + x = (1 - cosf(x * PI)) * 0.5f; + Prespoints[x1 + k] = (int)(y1 * (1.0f - x) + y2 * x); + } + x1 = i; + y1 = y2; + } +} + +/* + * Get the frequency from x, where x is [0..1]; x is the x coordinate + */ +float Resonance::getfreqx(float x) +{ + if(x > 1.0f) + x = 1.0f; + float octf = powf(2.0f, getoctavesfreq()); + return getcenterfreq() / sqrt(octf) * powf(octf, x); +} + +/* + * Get the x coordinate from frequency (used by the UI) + */ +float Resonance::getfreqpos(float freq) +{ + return (logf(freq) - logf(getfreqx(0.0f))) / logf(2.0f) / getoctavesfreq(); +} + +/* + * Get the center frequency of the resonance graph + */ +float Resonance::getcenterfreq() +{ + return 10000.0f * powf(10, -(1.0f - Pcenterfreq / 127.0f) * 2.0f); +} + +/* + * Get the number of octave that the resonance functions applies to + */ +float Resonance::getoctavesfreq() +{ + return 0.25f + 10.0f * Poctavesfreq / 127.0f; +} + +void Resonance::sendcontroller(MidiControllers ctl, float par) +{ + if(ctl == C_resonance_center) + ctlcenter = par; + else + ctlbw = par; +} + + + + +void Resonance::add2XML(XMLwrapper *xml) +{ + xml->addparbool("enabled", Penabled); + + if((Penabled == 0) && (xml->minimal)) + return; + + xml->addpar("max_db", PmaxdB); + xml->addpar("center_freq", Pcenterfreq); + xml->addpar("octaves_freq", Poctavesfreq); + xml->addparbool("protect_fundamental_frequency", Pprotectthefundamental); + xml->addpar("resonance_points", N_RES_POINTS); + for(int i = 0; i < N_RES_POINTS; ++i) { + xml->beginbranch("RESPOINT", i); + xml->addpar("val", Prespoints[i]); + xml->endbranch(); + } +} + + +void Resonance::getfromXML(XMLwrapper *xml) +{ + Penabled = xml->getparbool("enabled", Penabled); + + PmaxdB = xml->getpar127("max_db", PmaxdB); + Pcenterfreq = xml->getpar127("center_freq", Pcenterfreq); + Poctavesfreq = xml->getpar127("octaves_freq", Poctavesfreq); + Pprotectthefundamental = xml->getparbool("protect_fundamental_frequency", + Pprotectthefundamental); + for(int i = 0; i < N_RES_POINTS; ++i) { + if(xml->enterbranch("RESPOINT", i) == 0) + continue; + Prespoints[i] = xml->getpar127("val", Prespoints[i]); + xml->exitbranch(); + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Resonance.h b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Resonance.h new file mode 100644 index 000000000..777857110 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/Resonance.h @@ -0,0 +1,70 @@ +/* + ZynAddSubFX - a software synthesizer + + Resonance.h - Resonance + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef RESONANCE_H +#define RESONANCE_H + +#include "../globals.h" +#include "../Misc/Util.h" +#include "../Misc/XMLwrapper.h" +#include "../Params/Presets.h" +#include "../DSP/FFTwrapper.h" + +#define N_RES_POINTS 256 + +class Resonance:public Presets +{ + public: + Resonance(); + ~Resonance(); + void setpoint(int n, unsigned char p); + void applyres(int n, fft_t *fftdata, float freq); + void smooth(); + void interpolatepeaks(int type); + void randomize(int type); + + void add2XML(XMLwrapper *xml); + void defaults(); + void getfromXML(XMLwrapper *xml); + + + float getfreqpos(float freq); + float getfreqx(float x); + float getfreqresponse(float freq); + float getcenterfreq(); + float getoctavesfreq(); + void sendcontroller(MidiControllers ctl, float par); + + //parameters + unsigned char Penabled; //if the ressonance is enabled + unsigned char Prespoints[N_RES_POINTS]; //how many points define the resonance function + unsigned char PmaxdB; //how many dB the signal may be amplified + unsigned char Pcenterfreq, Poctavesfreq; //the center frequency of the res. func., and the number of octaves + unsigned char Pprotectthefundamental; //the fundamental (1-st harmonic) is not damped, even it resonance function is low + + //controllers + float ctlcenter; //center frequency(relative) + float ctlbw; //bandwidth(relative) + + private: +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SUBnote.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SUBnote.cpp new file mode 100644 index 000000000..db54e6d84 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SUBnote.cpp @@ -0,0 +1,588 @@ +/* + ZynAddSubFX - a software synthesizer + + SUBnote.cpp - The "subtractive" synthesizer + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include +#include +#include "../globals.h" +#include "SUBnote.h" +#include "../Misc/Util.h" + +SUBnote::SUBnote(SUBnoteParameters *parameters, + Controller *ctl_, + float freq, + float velocity, + int portamento_, + int midinote, + bool besilent) + :SynthNote(freq, velocity, portamento_, midinote, besilent) +{ + pars = parameters; + ctl = ctl_; + NoteEnabled = ON; + setup(freq, velocity, portamento_, midinote); +} + +void SUBnote::setup(float freq, + float velocity, + int portamento_, + int midinote, + bool legato) +{ + portamento = portamento_; + NoteEnabled = ON; + volume = powf(0.1f, 3.0f * (1.0f - pars->PVolume / 96.0f)); //-60 dB .. 0 dB + volume *= VelF(velocity, pars->PAmpVelocityScaleFunction); + if(pars->PPanning != 0) + panning = pars->PPanning / 127.0f; + else + panning = RND; + if(!legato) { + numstages = pars->Pnumstages; + stereo = pars->Pstereo; + start = pars->Pstart; + firsttick = 1; + } + int pos[MAX_SUB_HARMONICS]; + + if(pars->Pfixedfreq == 0) + basefreq = freq; + else { + basefreq = 440.0f; + int fixedfreqET = pars->PfixedfreqET; + if(fixedfreqET != 0) { //if the frequency varies according the keyboard note + float tmp = + (midinote + - 69.0f) / 12.0f + * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f); + if(fixedfreqET <= 64) + basefreq *= powf(2.0f, tmp); + else + basefreq *= powf(3.0f, tmp); + } + } + float detune = getdetune(pars->PDetuneType, + pars->PCoarseDetune, + pars->PDetune); + basefreq *= powf(2.0f, detune / 1200.0f); //detune +// basefreq*=ctl->pitchwheel.relfreq;//pitch wheel + + //global filter + GlobalFilterCenterPitch = pars->GlobalFilter->getfreq() //center freq + + (pars->PGlobalFilterVelocityScale / 127.0f + * 6.0f) //velocity sensing + * (VelF(velocity, + pars->PGlobalFilterVelocityScaleFunction) + - 1); + + if(!legato) { + GlobalFilterL = NULL; + GlobalFilterR = NULL; + GlobalFilterEnvelope = NULL; + } + + //select only harmonics that desire to compute + int harmonics = 0; + for(int n = 0; n < MAX_SUB_HARMONICS; ++n) { + if(pars->Phmag[n] == 0) + continue; + if(n * basefreq > synth->samplerate_f / 2.0f) + break; //remove the freqs above the Nyquist freq + pos[harmonics++] = n; + } + if(!legato) + firstnumharmonics = numharmonics = harmonics; + else { + if(harmonics > firstnumharmonics) + numharmonics = firstnumharmonics; + else + numharmonics = harmonics; + } + + + if(numharmonics == 0) { + NoteEnabled = OFF; + return; + } + + + if(!legato) { + lfilter = new bpfilter[numstages * numharmonics]; + if(stereo != 0) + rfilter = new bpfilter[numstages * numharmonics]; + } + + //how much the amplitude is normalised (because the harmonics) + float reduceamp = 0.0f; + + for(int n = 0; n < numharmonics; ++n) { + float freq = basefreq * (pos[n] + 1); + + //the bandwidth is not absolute(Hz); it is relative to frequency + float bw = + powf(10, (pars->Pbandwidth - 127.0f) / 127.0f * 4) * numstages; + + //Bandwidth Scale + bw *= powf(1000 / freq, (pars->Pbwscale - 64.0f) / 64.0f * 3.0f); + + //Relative BandWidth + bw *= powf(100, (pars->Phrelbw[pos[n]] - 64.0f) / 64.0f); + + if(bw > 25.0f) + bw = 25.0f; + + //try to keep same amplitude on all freqs and bw. (empirically) + float gain = sqrt(1500.0f / (bw * freq)); + + float hmagnew = 1.0f - pars->Phmag[pos[n]] / 127.0f; + float hgain; + + switch(pars->Phmagtype) { + case 1: + hgain = expf(hmagnew * logf(0.01f)); + break; + case 2: + hgain = expf(hmagnew * logf(0.001f)); + break; + case 3: + hgain = expf(hmagnew * logf(0.0001f)); + break; + case 4: + hgain = expf(hmagnew * logf(0.00001f)); + break; + default: + hgain = 1.0f - hmagnew; + } + gain *= hgain; + reduceamp += hgain; + + for(int nph = 0; nph < numstages; ++nph) { + float amp = 1.0f; + if(nph == 0) + amp = gain; + initfilter(lfilter[nph + n * numstages], freq, bw, amp, hgain); + if(stereo != 0) + initfilter(rfilter[nph + n * numstages], freq, bw, amp, hgain); + } + } + + if(reduceamp < 0.001f) + reduceamp = 1.0f; + volume /= reduceamp; + + oldpitchwheel = 0; + oldbandwidth = 64; + if(!legato) { + if(pars->Pfixedfreq == 0) + initparameters(basefreq); + else + initparameters(basefreq / 440.0f * freq); + } + else { + if(pars->Pfixedfreq == 0) + freq = basefreq; + else + freq *= basefreq / 440.0f; + + if(pars->PGlobalFilterEnabled != 0) { + globalfiltercenterq = pars->GlobalFilter->getq(); + GlobalFilterFreqTracking = pars->GlobalFilter->getfreqtracking( + basefreq); + } + } + + oldamplitude = newamplitude; +} + +void SUBnote::legatonote(float freq, float velocity, int portamento_, + int midinote, bool externcall) +{ + // Manage legato stuff + if(legato.update(freq, velocity, portamento_, midinote, externcall)) + return; + + setup(freq, velocity, portamento_, midinote, true); +} + +SUBnote::~SUBnote() +{ + if(NoteEnabled != OFF) + KillNote(); +} + +/* + * Kill the note + */ +void SUBnote::KillNote() +{ + if(NoteEnabled != OFF) { + delete [] lfilter; + lfilter = NULL; + if(stereo != 0) + delete [] rfilter; + rfilter = NULL; + delete AmpEnvelope; + delete FreqEnvelope; + delete BandWidthEnvelope; + delete GlobalFilterL; + delete GlobalFilterR; + delete GlobalFilterEnvelope; + NoteEnabled = OFF; + } +} + + +/* + * Compute the filters coefficients + */ +void SUBnote::computefiltercoefs(bpfilter &filter, + float freq, + float bw, + float gain) +{ + if(freq > synth->samplerate_f / 2.0f - 200.0f) + freq = synth->samplerate_f / 2.0f - 200.0f; + + + float omega = 2.0f * PI * freq / synth->samplerate_f; + float sn = sinf(omega); + float cs = cosf(omega); + float alpha = sn * sinh(LOG_2 / 2.0f * bw * omega / sn); + + if(alpha > 1) + alpha = 1; + if(alpha > bw) + alpha = bw; + + filter.b0 = alpha / (1.0f + alpha) * filter.amp * gain; + filter.b2 = -alpha / (1.0f + alpha) * filter.amp * gain; + filter.a1 = -2.0f * cs / (1.0f + alpha); + filter.a2 = (1.0f - alpha) / (1.0f + alpha); +} + + +/* + * Initialise the filters + */ +void SUBnote::initfilter(bpfilter &filter, + float freq, + float bw, + float amp, + float mag) +{ + filter.xn1 = 0.0f; + filter.xn2 = 0.0f; + + if(start == 0) { + filter.yn1 = 0.0f; + filter.yn2 = 0.0f; + } + else { + float a = 0.1f * mag; //empirically + float p = RND * 2.0f * PI; + if(start == 1) + a *= RND; + filter.yn1 = a * cosf(p); + filter.yn2 = a * cosf(p + freq * 2.0f * PI / synth->samplerate_f); + + //correct the error of computation the start amplitude + //at very high frequencies + if(freq > synth->samplerate_f * 0.96f) { + filter.yn1 = 0.0f; + filter.yn2 = 0.0f; + } + } + + filter.amp = amp; + filter.freq = freq; + filter.bw = bw; + computefiltercoefs(filter, freq, bw, 1.0f); +} + +/* + * Do the filtering + */ + +inline void SubFilterA(const float coeff[4], float &src, float work[4]) +{ + work[3] = src*coeff[0]+work[1]*coeff[1]+work[2]*coeff[2]+work[3]*coeff[3]; + work[1] = src; + src = work[3]; +} + +inline void SubFilterB(const float coeff[4], float &src, float work[4]) +{ + work[2] = src*coeff[0]+work[0]*coeff[1]+work[3]*coeff[2]+work[2]*coeff[3]; + work[0] = src; + src = work[2]; +} + +//This dance is designed to minimize unneeded memory operations which can result +//in quite a bit of wasted time +void SUBnote::filter(bpfilter &filter, float *smps) +{ + assert(synth->buffersize % 8 == 0); + float coeff[4] = {filter.b0, filter.b2, -filter.a1, -filter.a2}; + float work[4] = {filter.xn1, filter.xn2, filter.yn1, filter.yn2}; + + for(int i = 0; i < synth->buffersize; i += 8) { + SubFilterA(coeff, smps[i + 0], work); + SubFilterB(coeff, smps[i + 1], work); + SubFilterA(coeff, smps[i + 2], work); + SubFilterB(coeff, smps[i + 3], work); + SubFilterA(coeff, smps[i + 4], work); + SubFilterB(coeff, smps[i + 5], work); + SubFilterA(coeff, smps[i + 6], work); + SubFilterB(coeff, smps[i + 7], work); + } + filter.xn1 = work[0]; + filter.xn2 = work[1]; + filter.yn1 = work[2]; + filter.yn2 = work[3]; +} + +/* + * Init Parameters + */ +void SUBnote::initparameters(float freq) +{ + AmpEnvelope = new Envelope(pars->AmpEnvelope, freq); + if(pars->PFreqEnvelopeEnabled != 0) + FreqEnvelope = new Envelope(pars->FreqEnvelope, freq); + else + FreqEnvelope = NULL; + if(pars->PBandWidthEnvelopeEnabled != 0) + BandWidthEnvelope = new Envelope(pars->BandWidthEnvelope, freq); + else + BandWidthEnvelope = NULL; + if(pars->PGlobalFilterEnabled != 0) { + globalfiltercenterq = pars->GlobalFilter->getq(); + GlobalFilterL = Filter::generate(pars->GlobalFilter); + if(stereo) + GlobalFilterR = Filter::generate(pars->GlobalFilter); + GlobalFilterEnvelope = new Envelope(pars->GlobalFilterEnvelope, + freq); + GlobalFilterFreqTracking = pars->GlobalFilter->getfreqtracking(basefreq); + } + computecurrentparameters(); +} + + +/* + * Compute Parameters of SUBnote for each tick + */ +void SUBnote::computecurrentparameters() +{ + if((FreqEnvelope != NULL) || (BandWidthEnvelope != NULL) + || (oldpitchwheel != ctl->pitchwheel.data) + || (oldbandwidth != ctl->bandwidth.data) + || (portamento != 0)) { + float envfreq = 1.0f; + float envbw = 1.0f; + float gain = 1.0f; + + if(FreqEnvelope != NULL) { + envfreq = FreqEnvelope->envout() / 1200; + envfreq = powf(2.0f, envfreq); + } + envfreq *= ctl->pitchwheel.relfreq; //pitch wheel + if(portamento != 0) { //portamento is used + envfreq *= ctl->portamento.freqrap; + if(ctl->portamento.used == 0) //the portamento has finished + portamento = 0; //this note is no longer "portamented" + ; + } + + if(BandWidthEnvelope != NULL) { + envbw = BandWidthEnvelope->envout(); + envbw = powf(2, envbw); + } + envbw *= ctl->bandwidth.relbw; //bandwidth controller + + float tmpgain = 1.0f / sqrt(envbw * envfreq); + + for(int n = 0; n < numharmonics; ++n) + for(int nph = 0; nph < numstages; ++nph) { + if(nph == 0) + gain = tmpgain; + else + gain = 1.0f; + computefiltercoefs(lfilter[nph + n * numstages], + lfilter[nph + n * numstages].freq * envfreq, + lfilter[nph + n * numstages].bw * envbw, + gain); + } + if(stereo != 0) + for(int n = 0; n < numharmonics; ++n) + for(int nph = 0; nph < numstages; ++nph) { + if(nph == 0) + gain = tmpgain; + else + gain = 1.0f; + computefiltercoefs( + rfilter[nph + n * numstages], + rfilter[nph + n + * numstages].freq * envfreq, + rfilter[nph + n * numstages].bw * envbw, + gain); + } + + + oldbandwidth = ctl->bandwidth.data; + oldpitchwheel = ctl->pitchwheel.data; + } + newamplitude = volume * AmpEnvelope->envout_dB() * 2.0f; + + //Filter + if(GlobalFilterL != NULL) { + float globalfilterpitch = GlobalFilterCenterPitch + + GlobalFilterEnvelope->envout(); + float filterfreq = globalfilterpitch + ctl->filtercutoff.relfreq + + GlobalFilterFreqTracking; + filterfreq = Filter::getrealfreq(filterfreq); + + GlobalFilterL->setfreq_and_q(filterfreq, + globalfiltercenterq * ctl->filterq.relq); + if(GlobalFilterR != NULL) + GlobalFilterR->setfreq_and_q( + filterfreq, + globalfiltercenterq + * ctl->filterq.relq); + } +} + +/* + * Note Output + */ +int SUBnote::noteout(float *outl, float *outr) +{ + memcpy(outl, denormalkillbuf, synth->bufferbytes); + memcpy(outr, denormalkillbuf, synth->bufferbytes); + + if(NoteEnabled == OFF) + return 0; + + float tmprnd[synth->buffersize]; + float tmpsmp[synth->buffersize]; + //left channel + for(int i = 0; i < synth->buffersize; ++i) + tmprnd[i] = RND * 2.0f - 1.0f; + for(int n = 0; n < numharmonics; ++n) { + memcpy(tmpsmp, tmprnd, synth->bufferbytes); + for(int nph = 0; nph < numstages; ++nph) + filter(lfilter[nph + n * numstages], tmpsmp); + for(int i = 0; i < synth->buffersize; ++i) + outl[i] += tmpsmp[i]; + } + + if(GlobalFilterL != NULL) + GlobalFilterL->filterout(&outl[0]); + + //right channel + if(stereo != 0) { + for(int i = 0; i < synth->buffersize; ++i) + tmprnd[i] = RND * 2.0f - 1.0f; + for(int n = 0; n < numharmonics; ++n) { + memcpy(tmpsmp, tmprnd, synth->bufferbytes); + for(int nph = 0; nph < numstages; ++nph) + filter(rfilter[nph + n * numstages], tmpsmp); + for(int i = 0; i < synth->buffersize; ++i) + outr[i] += tmpsmp[i]; + } + if(GlobalFilterR != NULL) + GlobalFilterR->filterout(&outr[0]); + } + else + memcpy(outr, outl, synth->bufferbytes); + + if(firsttick != 0) { + int n = 10; + if(n > synth->buffersize) + n = synth->buffersize; + for(int i = 0; i < n; ++i) { + float ampfadein = 0.5f - 0.5f * cosf( + (float) i / (float) n * PI); + outl[i] *= ampfadein; + outr[i] *= ampfadein; + } + firsttick = 0; + } + + if(ABOVE_AMPLITUDE_THRESHOLD(oldamplitude, newamplitude)) + // Amplitude interpolation + for(int i = 0; i < synth->buffersize; ++i) { + float tmpvol = INTERPOLATE_AMPLITUDE(oldamplitude, + newamplitude, + i, + synth->buffersize); + outl[i] *= tmpvol * panning; + outr[i] *= tmpvol * (1.0f - panning); + } + else + for(int i = 0; i < synth->buffersize; ++i) { + outl[i] *= newamplitude * panning; + outr[i] *= newamplitude * (1.0f - panning); + } + + oldamplitude = newamplitude; + computecurrentparameters(); + + // Apply legato-specific sound signal modifications + legato.apply(*this, outl, outr); + + // Check if the note needs to be computed more + if(AmpEnvelope->finished() != 0) { + for(int i = 0; i < synth->buffersize; ++i) { //fade-out + float tmp = 1.0f - (float)i / synth->buffersize_f; + outl[i] *= tmp; + outr[i] *= tmp; + } + KillNote(); + } + return 1; +} + +/* + * Relase Key (Note Off) + */ +void SUBnote::relasekey() +{ + AmpEnvelope->relasekey(); + if(FreqEnvelope) + FreqEnvelope->relasekey(); + if(BandWidthEnvelope) + BandWidthEnvelope->relasekey(); + if(GlobalFilterEnvelope) + GlobalFilterEnvelope->relasekey(); +} + +/* + * Check if the note is finished + */ +int SUBnote::finished() const +{ + if(NoteEnabled == OFF) + return 1; + else + return 0; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SUBnote.h b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SUBnote.h new file mode 100644 index 000000000..cd95b8104 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SUBnote.h @@ -0,0 +1,107 @@ +/* + ZynAddSubFX - a software synthesizer + + SUBnote.h - The subtractive synthesizer + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef SUB_NOTE_H +#define SUB_NOTE_H + +#include "SynthNote.h" +#include "../globals.h" +#include "../Params/SUBnoteParameters.h" +#include "../Params/Controller.h" +#include "Envelope.h" +#include "../DSP/Filter.h" + +class SUBnote:public SynthNote +{ + public: + SUBnote(SUBnoteParameters *parameters, Controller *ctl_, float freq, + float velocity, int portamento_, int midinote, bool besilent); + ~SUBnote(); + + void legatonote(float freq, float velocity, int portamento_, + int midinote, bool externcall); + + int noteout(float *outl, float *outr); //note output,return 0 if the note is finished + void relasekey(); + int finished() const; + private: + + void setup(float freq, + float velocity, + int portamento_, + int midinote, + bool legato = false); + void computecurrentparameters(); + void initparameters(float freq); + void KillNote(); + + SUBnoteParameters *pars; + + //parameters + int stereo; + int numstages; //number of stages of filters + int numharmonics; //number of harmonics (after the too higher hamonics are removed) + int firstnumharmonics; //To keep track of the first note's numharmonics value, useful in legato mode. + int start; //how the harmonics start + float basefreq; + float panning; + Envelope *AmpEnvelope; + Envelope *FreqEnvelope; + Envelope *BandWidthEnvelope; + + Filter *GlobalFilterL, *GlobalFilterR; + + Envelope *GlobalFilterEnvelope; + + //internal values + ONOFFTYPE NoteEnabled; + int firsttick, portamento; + float volume, oldamplitude, newamplitude; + + float GlobalFilterCenterPitch; //octaves + float GlobalFilterFreqTracking; + + struct bpfilter { + float freq, bw, amp; //filter parameters + float a1, a2, b0, b2; //filter coefs. b1=0 + float xn1, xn2, yn1, yn2; //filter internal values + }; + + void initfilter(bpfilter &filter, + float freq, + float bw, + float amp, + float mag); + void computefiltercoefs(bpfilter &filter, + float freq, + float bw, + float gain); + inline void filter(bpfilter &filter, float *smps); + + bpfilter *lfilter, *rfilter; + + Controller *ctl; + int oldpitchwheel, oldbandwidth; + float globalfiltercenterq; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SynthNote.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SynthNote.cpp new file mode 100644 index 000000000..578a11733 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SynthNote.cpp @@ -0,0 +1,134 @@ +#include "SynthNote.h" +#include "../globals.h" +#include + +SynthNote::SynthNote(float freq, float vel, int port, int note, bool quiet) + :legato(freq, vel, port, note, quiet) +{} + +SynthNote::Legato::Legato(float freq, float vel, int port, + int note, bool quiet) +{ + // Initialise some legato-specific vars + msg = LM_Norm; + fade.length = (int)(synth->samplerate_f * 0.005f); // 0.005f seems ok. + if(fade.length < 1) + fade.length = 1; // (if something's fishy) + fade.step = (1.0f / fade.length); + decounter = -10; + param.freq = freq; + param.vel = vel; + param.portamento = port; + param.midinote = note; + lastfreq = 0.0f; + silent = quiet; +} + +int SynthNote::Legato::update(float freq, float velocity, int portamento_, + int midinote_, bool externcall) +{ + if(externcall) + msg = LM_Norm; + if(msg != LM_CatchUp) { + lastfreq = param.freq; + param.freq = freq; + param.vel = velocity; + param.portamento = portamento_; + param.midinote = midinote_; + if(msg == LM_Norm) { + if(silent) { + fade.m = 0.0f; + msg = LM_FadeIn; + } + else { + fade.m = 1.0f; + msg = LM_FadeOut; + return 1; + } + } + if(msg == LM_ToNorm) + msg = LM_Norm; + } + return 0; +} + +void SynthNote::Legato::apply(SynthNote ¬e, float *outl, float *outr) +{ + if(silent) // Silencer + if(msg != LM_FadeIn) { + memset(outl, 0, synth->bufferbytes); + memset(outr, 0, synth->bufferbytes); + } + switch(msg) { + case LM_CatchUp: // Continue the catch-up... + if(decounter == -10) + decounter = fade.length; + //Yea, could be done without the loop... + for(int i = 0; i < synth->buffersize; ++i) { + decounter--; + if(decounter < 1) { + // Catching-up done, we can finally set + // the note to the actual parameters. + decounter = -10; + msg = LM_ToNorm; + note.legatonote(param.freq, param.vel, param.portamento, + param.midinote, false); + break; + } + } + break; + case LM_FadeIn: // Fade-in + if(decounter == -10) + decounter = fade.length; + silent = false; + for(int i = 0; i < synth->buffersize; ++i) { + decounter--; + if(decounter < 1) { + decounter = -10; + msg = LM_Norm; + break; + } + fade.m += fade.step; + outl[i] *= fade.m; + outr[i] *= fade.m; + } + break; + case LM_FadeOut: // Fade-out, then set the catch-up + if(decounter == -10) + decounter = fade.length; + for(int i = 0; i < synth->buffersize; ++i) { + decounter--; + if(decounter < 1) { + for(int j = i; j < synth->buffersize; ++j) { + outl[j] = 0.0f; + outr[j] = 0.0f; + } + decounter = -10; + silent = true; + // Fading-out done, now set the catch-up : + decounter = fade.length; + msg = LM_CatchUp; + //This freq should make this now silent note to catch-up/resync + //with the heard note for the same length it stayed at the + //previous freq during the fadeout. + float catchupfreq = param.freq * (param.freq / lastfreq); + note.legatonote(catchupfreq, param.vel, param.portamento, + param.midinote, false); + break; + } + fade.m -= fade.step; + outl[i] *= fade.m; + outr[i] *= fade.m; + } + break; + default: + break; + } +} + +void SynthNote::setVelocity(float velocity_) { + legato.setSilent(true); //Let legato.update(...) returns 0. + legatonote(legato.getFreq(), velocity_, + legato.getPortamento(), legato.getMidinote(), true); + legato.setDecounter(0); //avoid chopping sound due fade-in +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SynthNote.h b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SynthNote.h new file mode 100644 index 000000000..3058e5d3f --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Synth/SynthNote.h @@ -0,0 +1,86 @@ +/* + ZynAddSubFX - a software synthesizer + + Note.h - Abstract Base Class for synthesizers + Copyright (C) 2010-2010 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef SYNTH_NOTE_H +#define SYNTH_NOTE_H +#include "../globals.h" +#include "../Params/FilterParams.h" + +class SynthNote +{ + public: + SynthNote(float freq, float vel, int port, int note, bool quiet); + virtual ~SynthNote() {} + + /**Compute Output Samples + * @return 0 if note is finished*/ + virtual int noteout(float *outl, float *outr) = 0; + + //TODO fix this spelling error [noisey commit] + /**Release the key for the note and start release portion of envelopes.*/ + virtual void relasekey() = 0; + + /**Return if note is finished. + * @return finished=1 unfinished=0*/ + virtual int finished() const = 0; + + virtual void legatonote(float freq, float velocity, + int portamento_, int midinote_, + bool externcall) = 0; + /* For polyphonic aftertouch needed */ + void setVelocity(float velocity_); + protected: + // Legato transitions + class Legato + { + public: + Legato(float freq, float vel, int port, + int note, bool quiet); + + void apply(SynthNote ¬e, float *outl, float *outr); + int update(float freq, float velocity, int portamento_, + int midinote_, bool externalcall); + + private: + bool silent; + float lastfreq; + LegatoMsg msg; + int decounter; + struct { // Fade In/Out vars + int length; + float m, step; + } fade; + struct { // Note parameters + float freq, vel; + int portamento, midinote; + } param; + + public: /* Some get routines for legatonote calls (aftertouch feature)*/ + float getFreq() {return param.freq; } + float getVelocity() {return param.vel; } + int getPortamento() {return param.portamento; } + int getMidinote() {return param.midinote; } + void setSilent(bool silent_) {silent = silent_; } + void setDecounter(int decounter_) {decounter = decounter_; } + } legato; +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/AdNoteTest.h b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/AdNoteTest.h new file mode 100644 index 000000000..c296e9cb4 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/AdNoteTest.h @@ -0,0 +1,201 @@ +/* + ZynAddSubFX - a software synthesizer + + AdNoteTest.h - CxxTest for Synth/ADnote + Copyright (C) 2009-2011 Mark McCurry + Copyright (C) 2009 Harald Hvaal + Authors: Mark McCurry, Harald Hvaal + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + + +#include +#include +#include +#include +#include +#include "../Misc/Master.h" +#include "../Misc/Util.h" +#include "../Synth/ADnote.h" +#include "../Params/Presets.h" +#include "../DSP/FFTwrapper.h" +#include "../globals.h" +SYNTH_T *synth; + +using namespace std; + + +class AdNoteTest:public CxxTest::TestSuite +{ + public: + + ADnote *note; + Master *master; + FFTwrapper *fft; + Controller *controller; + unsigned char testnote; + + + float *outR, *outL; + + void setUp() { + //First the sensible settings and variables that have to be set: + synth = new SYNTH_T; + synth->buffersize = 256; + + outL = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + *(outL + i) = 0; + outR = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + *(outR + i) = 0; + + //next the bad global variables that for some reason have not been properly placed in some + //initialization routine, but rather exist as cryptic oneliners in main.cpp: + denormalkillbuf = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + denormalkillbuf[i] = 0; + + //phew, glad to get thouse out of my way. took me a lot of sweat and gdb to get this far... + + fft = new FFTwrapper(synth->oscilsize); + //prepare the default settings + ADnoteParameters *defaultPreset = new ADnoteParameters(fft); + + //Assert defaults + TS_ASSERT(!defaultPreset->VoicePar[1].Enabled); + + XMLwrapper *wrap = new XMLwrapper(); + cout << string(SOURCE_DIR) + string("/guitar-adnote.xmz") + << endl; + wrap->loadXMLfile(string(SOURCE_DIR) + + string("/guitar-adnote.xmz")); + TS_ASSERT(wrap->enterbranch("MASTER")); + TS_ASSERT(wrap->enterbranch("PART", 0)); + TS_ASSERT(wrap->enterbranch("INSTRUMENT")); + TS_ASSERT(wrap->enterbranch("INSTRUMENT_KIT")); + TS_ASSERT(wrap->enterbranch("INSTRUMENT_KIT_ITEM", 0)); + TS_ASSERT(wrap->enterbranch("ADD_SYNTH_PARAMETERS")); + defaultPreset->getfromXML(wrap); + //defaultPreset->defaults(); + + //verify xml was loaded + TS_ASSERT(defaultPreset->VoicePar[1].Enabled); + + + + controller = new Controller(); + + //lets go with.... 50! as a nice note + testnote = 50; + float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); + + note = new ADnote(defaultPreset, + controller, + freq, + 120, + 0, + testnote, + false); + + delete defaultPreset; + delete wrap; + } + + void willNoteBeRunButIsHereForLinkingReasonsHowsThisForCamelCaseEh() + { + master = new Master(); + } + + void tearDown() { + delete note; + delete controller; + delete fft; + delete [] outL; + delete [] outR; + delete [] denormalkillbuf; + FFT_cleanup(); + delete synth; + } + + void testDefaults() { + int sampleCount = 0; + +//#define WRITE_OUTPUT + +#ifdef WRITE_OUTPUT + ofstream file("adnoteout", ios::out); +#endif + note->noteout(outL, outR); +#ifdef WRITE_OUTPUT + for(int i = 0; i < synth->buffersize; ++i) + file << outL[i] << std::endl; + +#endif + sampleCount += synth->buffersize; + + TS_ASSERT_DELTA(outL[255], 0.254609f, 0.0001f); + + note->relasekey(); + + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], -0.102197f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], -0.111422f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], -0.021375f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], 0.149882f, 0.0001f); + + while(!note->finished()) { + note->noteout(outL, outR); +#ifdef WRITE_OUTPUT + for(int i = 0; i < synth->buffersize; ++i) + file << outL[i] << std::endl; + +#endif + sampleCount += synth->buffersize; + } +#ifdef WRITE_OUTPUT + file.close(); +#endif + + TS_ASSERT_EQUALS(sampleCount, 9472); + } + +#define OUTPUT_PROFILE +#ifdef OUTPUT_PROFILE + void testSpeed() { + const int samps = 15000; + + int t_on = clock(); // timer before calling func + for(int i = 0; i < samps; ++i) + note->noteout(outL, outR); + int t_off = clock(); // timer when func returns + + printf("AdNoteTest: %f seconds for %d Samples to be generated.\n", + (static_cast(t_off - t_on)) / CLOCKS_PER_SEC, samps); + } +#endif +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/CMakeLists.txt b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/CMakeLists.txt new file mode 100644 index 000000000..373fd40ba --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/CMakeLists.txt @@ -0,0 +1,35 @@ +#for tests looking for files stored in the source dir +add_definitions(-DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") + +CXXTEST_ADD_TEST(ControllerTest ControllerTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ControllerTest.h) +CXXTEST_ADD_TEST(EchoTest EchoTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/EchoTest.h) +#CXXTEST_ADD_TEST(SampleTest SampleTest.h) +CXXTEST_ADD_TEST(MicrotonalTest MicrotonalTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MicrotonalTest.h) +CXXTEST_ADD_TEST(XMLwrapperTest XMLwrapper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/XMLwrapperTest.h) +CXXTEST_ADD_TEST(ADnoteTest AdNoteTest.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/AdNoteTest.h) +CXXTEST_ADD_TEST(SUBnoteTest SubNoteTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/SubNoteTest.h) +CXXTEST_ADD_TEST(OscilGenTest OscilGenTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/OscilGenTest.h) +CXXTEST_ADD_TEST(RandTest RandTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RandTest.h) +CXXTEST_ADD_TEST(PADnoteTest PadNoteTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PadNoteTest.h) +CXXTEST_ADD_TEST(PluginTest PluginTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PluginTest.h) +CXXTEST_ADD_TEST(UnisonTest UnisonTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UnisonTest.h) + +#Extra libraries added to make test and full compilation use the same library +#links for quirky compilers +set(test_lib zynaddsubfx_core ${ZLIB_LIBRARY} ${FFTW_LIBRARIES} ${MXML_LIBRARIES} pthread) + +message(STATUS "Linking tests with: ${test_lib}") +target_link_libraries(ADnoteTest ${test_lib}) +target_link_libraries(SUBnoteTest ${test_lib}) +target_link_libraries(ControllerTest ${test_lib}) +target_link_libraries(EchoTest ${test_lib}) +target_link_libraries(MicrotonalTest ${test_lib}) +target_link_libraries(OscilGenTest ${test_lib}) +target_link_libraries(XMLwrapperTest ${test_lib}) +target_link_libraries(RandTest ${test_lib}) +target_link_libraries(PADnoteTest ${test_lib}) +target_link_libraries(PluginTest zynaddsubfx_core zynaddsubfx_nio + ${OS_LIBRARIES} ${AUDIO_LIBRARIES}) +target_link_libraries(UnisonTest ${test_lib}) + diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/ControllerTest.h b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/ControllerTest.h new file mode 100644 index 000000000..f9b97046b --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/ControllerTest.h @@ -0,0 +1,75 @@ +/* + ZynAddSubFX - a software synthesizer + + ControllerTest.h - CxxTest for Params/Controller + Copyright (C) 2009-2011 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include +#include +#include "../Params/Controller.h" +#include "../globals.h" +SYNTH_T *synth; + +class ControllerTest:public CxxTest::TestSuite +{ + public: + void setUp() { + synth = new SYNTH_T; + testCtl = new Controller(); + } + + void tearDown() { + delete testCtl; + delete synth; + } + + + void testPortamentoRange() { + //Initialize portamento + testCtl->setportamento(127); + testCtl->portamento.time = 127; + testCtl->initportamento(40.0f, 400.0f, false); + //Bounds Check + while(testCtl->portamento.used) { + TS_ASSERT((0.0f <= testCtl->portamento.x) + && (testCtl->portamento.x <= 1.0f)); + TS_ASSERT((0.1f <= testCtl->portamento.freqrap) + && (testCtl->portamento.freqrap <= 1.0f)); + testCtl->updateportamento(); + } + TS_ASSERT((0.0f <= testCtl->portamento.x) + && (testCtl->portamento.x <= 1.0f)); + TS_ASSERT((0.1f <= testCtl->portamento.freqrap) + && (testCtl->portamento.freqrap <= 1.0f)); + } + + void testPortamentoValue() { + testCtl->setportamento(127); + testCtl->portamento.time = 127; + testCtl->initportamento(40.0f, 400.0f, false); + int i; + for(i = 0; i < 10; ++i) + testCtl->updateportamento(); + //Assert that the numbers are the same as they were at release + TS_ASSERT_DELTA(testCtl->portamento.x, 0.0290249f, 0.000001f) + TS_ASSERT_DELTA(testCtl->portamento.freqrap, 0.126122f, 0.000001f) + } + + private: + Controller *testCtl; +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/EchoTest.h b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/EchoTest.h new file mode 100644 index 000000000..c0a295ed6 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/EchoTest.h @@ -0,0 +1,126 @@ +/* + ZynAddSubFX - a software synthesizer + + EchoTest.h - CxxTest for Effect/Echo + Copyright (C) 2009-2011 Mark McCurry + Copyright (C) 2009 Harald Hvaal + Authors: Mark McCurry, Harald Hvaal + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include +#include +#include +#include +#include "../Effects/Echo.h" +#include "../globals.h" +SYNTH_T *synth; + +using namespace std; + +class EchoTest:public CxxTest::TestSuite +{ + public: + void setUp() { + synth = new SYNTH_T; + outL = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + outL[i] = 0.0f; + outR = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + outR[i] = 0.0f; + input = new Stereo(new float[synth->buffersize], + new float[synth->buffersize]); + for(int i = 0; i < synth->buffersize; ++i) + input->l[i] = input->r[i] = 0.0f; + testFX = new Echo(true, outL, outR, 44100, 256); + } + + void tearDown() { + delete[] input->r; + delete[] input->l; + delete input; + delete[] outL; + delete[] outR; + delete testFX; + delete synth; + } + + + void testInit() { + //Make sure that the output will be zero at start + //(given a zero input) + testFX->out(*input); + for(int i = 0; i < synth->buffersize; ++i) { + TS_ASSERT_DELTA(outL[i], 0.0f, 0.0001f); + TS_ASSERT_DELTA(outR[i], 0.0f, 0.0001f); + } + } + + void testClear() { + char DELAY = 2; + testFX->changepar(DELAY, 127); + + //flood with high input + for(int i = 0; i < synth->buffersize; ++i) + input->r[i] = input->l[i] = 1.0f; + + for(int i = 0; i < 500; ++i) + testFX->out(*input); + for(int i = 0; i < synth->buffersize; ++i) { + TS_ASSERT_DIFFERS(outL[i], 0.0f); + TS_ASSERT_DIFFERS(outR[i], 0.0f) + } + //After making sure the internal buffer has a nonzero value + //cleanup + //Then get the next output, which should be zereoed out if DELAY + //is large enough + testFX->cleanup(); + testFX->out(*input); + for(int i = 0; i < synth->buffersize; ++i) { + TS_ASSERT_DELTA(outL[i], 0.0f, 0.0001f); + TS_ASSERT_DELTA(outR[i], 0.0f, 0.0001f); + } + } + //Insures that the proper decay occurs with high feedback + void testDecaywFb() { + //flood with high input + for(int i = 0; i < synth->buffersize; ++i) + input->r[i] = input->l[i] = 1.0f; + char FEEDBACK = 5; + testFX->changepar(FEEDBACK, 127); + for(int i = 0; i < 100; ++i) + testFX->out(*input); + for(int i = 0; i < synth->buffersize; ++i) { + TS_ASSERT_DIFFERS(outL[i], 0.0f); + TS_ASSERT_DIFFERS(outR[i], 0.0f) + } + float amp = abs(outL[0] + outR[0]) / 2; + //reset input to zero + for(int i = 0; i < synth->buffersize; ++i) + input->r[i] = input->l[i] = 0.0f; + + //give the echo time to fade based upon zero input and high feedback + for(int i = 0; i < 50; ++i) + testFX->out(*input); + TS_ASSERT_LESS_THAN_EQUALS(abs(outL[0] + outR[0]) / 2, amp); + } + + + private: + Stereo *input; + float *outR, *outL; + Echo *testFX; +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/MicrotonalTest.h b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/MicrotonalTest.h new file mode 100644 index 000000000..6ce1ca524 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/MicrotonalTest.h @@ -0,0 +1,136 @@ +/* + ZynAddSubFX - a software synthesizer + + MicrotonalTest.h - CxxTest for Misc/Microtonal + Copyright (C) 2009-2012 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include +#include +#include "../Misc/Microtonal.h" +#include +#include +#include +#include "../globals.h" +SYNTH_T *synth; + +using namespace std; + +class MicrotonalTest:public CxxTest::TestSuite +{ + public: + void setUp() { + synth = new SYNTH_T; + testMicro = new Microtonal(); + } + + void tearDown() { + delete testMicro; + delete synth; + } + + //Verifies that the object is initialized correctly + void testinit() { + TS_ASSERT_EQUALS(testMicro->Pinvertupdown, 0); + TS_ASSERT_EQUALS(testMicro->Pinvertupdowncenter, 60); + TS_ASSERT_EQUALS(testMicro->getoctavesize(), 12); + TS_ASSERT_EQUALS(testMicro->Penabled, 0); + TS_ASSERT_EQUALS(testMicro->PAnote, 69); + TS_ASSERT_EQUALS(testMicro->PAfreq, 440.0f); + TS_ASSERT_EQUALS(testMicro->Pscaleshift, 64); + TS_ASSERT_EQUALS(testMicro->Pfirstkey, 0); + TS_ASSERT_EQUALS(testMicro->Plastkey, 127); + TS_ASSERT_EQUALS(testMicro->Pmiddlenote, 60); + TS_ASSERT_EQUALS(testMicro->Pmapsize, 12); + TS_ASSERT_EQUALS(testMicro->Pmappingenabled, 0); + TS_ASSERT_EQUALS(testMicro->Pglobalfinedetune, 64); + + TS_ASSERT_EQUALS(string((const char *)testMicro->Pname), "12tET"); + TS_ASSERT_EQUALS(string( + (const char *)testMicro->Pcomment), + "Equal Temperament 12 notes per octave"); + + for(int i = 0; i < 128; ++i) + TS_ASSERT_EQUALS(testMicro->Pmapping[i], i); + + TS_ASSERT_DELTA(testMicro->getnotefreq(19, 0), 24.4997f, 0.0001f); + } + + //Tests saving/loading to XML + void testXML() { + //Gah, the XMLwrapper is a twisted maze + testMicro->Penabled = 1; + XMLwrapper xml; + xml.beginbranch("Dummy"); //this should not be needed, but odd behavior + //seems to exist from MICROTONAL being on the + //top of the stack + xml.beginbranch("MICROTONAL"); + testMicro->add2XML(&xml); + xml.endbranch(); + xml.endbranch(); + + char *tmp = xml.getXMLdata(); + Microtonal other; + + other.Penabled = 1; + strcpy((char *)other.Pname, "Myname"); //will be nicer with strings + + TS_ASSERT(*testMicro != other); //sanity check + + TS_ASSERT(xml.enterbranch("Dummy")); + TS_ASSERT(xml.enterbranch("MICROTONAL")); + + other.getfromXML(&xml); + xml.exitbranch(); + xml.exitbranch(); + char *tmpo = xml.getXMLdata(); + + TS_ASSERT(!strcmp(tmp, tmpo)); + free(tmp); + free(tmpo); + } + +#if 0 + /**\todo Test Saving/loading from file*/ + + //Test texttomapping TODO finish + void _testTextToMapping() { + //the mapping is from old documentation for "Intense Diatonic" scale + const char *mapping[12] = + {"0", "x", "1", "x", "2", "3", "x", "4", "x", "5", "x", "6"}; + //for(int i=0;i<20;++i) + // cout << i << ':' << testMicro->getnotefreq(i,0) << endl; + // + // octave size == 7 + // find dead notes + } + //Test texttotunings TODO finish + void _testTextToTunings() { + //the tuning is from old documentation for "Intense Diatonic" scale + const char *tuning[7] = + {"9/8", "5/4", "4/3", "3/2", "5/3", "15/8", "2/1"}; + const int numTunings = 7; + //for(int i=0;i<20;++i) + // cout << i << ':' << testMicro->getnotefreq(i,0) << endl; + // go to middle key and verify the proportions + } + /**\TODO test loading from scl and kbm files*/ +#endif + + private: + Microtonal *testMicro; +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/OscilGenTest.h b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/OscilGenTest.h new file mode 100644 index 000000000..b0e13b254 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/OscilGenTest.h @@ -0,0 +1,141 @@ +/* + ZynAddSubFX - a software synthesizer + + AdNoteTest.h - CxxTest for Synth/OscilGen + Copyright (C) 20011-2012 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include +#include +#include "../Synth/OscilGen.h" +#include "../globals.h" +SYNTH_T *synth; + +using namespace std; + +class OscilGenTest:public CxxTest::TestSuite +{ + public: + float freq; + float *outR, *outL; + FFTwrapper *fft; + OscilGen *oscil; + + void setUp() { + synth = new SYNTH_T; + //First the sensible settings and variables that have to be set: + synth->buffersize = 256; + synth->oscilsize = 1024; + + outL = new float[synth->oscilsize]; + outR = new float[synth->oscilsize]; + memset(outL, 0, sizeof(float) * synth->oscilsize); + memset(outR, 0, sizeof(float) * synth->oscilsize); + + //next the bad global variables that for some reason have not been properly placed in some + //initialization routine, but rather exist as cryptic oneliners in main.cpp: + denormalkillbuf = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + denormalkillbuf[i] = 0; + + //prepare the default settings + fft = new FFTwrapper(synth->oscilsize); + oscil = new OscilGen(fft, NULL); + + //Assert defaults [TODO] + + + XMLwrapper *wrap = new XMLwrapper(); + wrap->loadXMLfile(string(SOURCE_DIR) + + string("/guitar-adnote.xmz")); + TS_ASSERT(wrap->enterbranch("MASTER")); + TS_ASSERT(wrap->enterbranch("PART", 0)); + TS_ASSERT(wrap->enterbranch("INSTRUMENT")); + TS_ASSERT(wrap->enterbranch("INSTRUMENT_KIT")); + TS_ASSERT(wrap->enterbranch("INSTRUMENT_KIT_ITEM", 0)); + TS_ASSERT(wrap->enterbranch("ADD_SYNTH_PARAMETERS")); + TS_ASSERT(wrap->enterbranch("VOICE", 0)); + TS_ASSERT(wrap->enterbranch("OSCIL")); + oscil->getfromXML(wrap); + delete wrap; + + //verify xml was loaded [TODO] + + //lets go with.... 50! as a nice note + const char testnote = 50; + freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); + } + + void tearDown() { + delete oscil; + delete fft; + delete[] outL; + delete[] outR; + delete[] denormalkillbuf; + FFT_cleanup(); + delete synth; + } + + //verifies that initialization occurs + void testInit(void) + { + oscil->get(outL, freq); + } + + void testOutput(void) + { + oscil->get(outL, freq); + TS_ASSERT_DELTA(outL[23], -0.044547f, 0.0001f); + TS_ASSERT_DELTA(outL[129], -0.018169f, 0.0001f); + TS_ASSERT_DELTA(outL[586], 0.045647f, 0.0001f); + TS_ASSERT_DELTA(outL[1023], -0.038334f, 0.0001f); + } + + void testSpectrum(void) + { + oscil->getspectrum(synth->oscilsize / 2, outR, 1); + TS_ASSERT_DELTA(outR[0], 350.698059f, 0.0001f); + TS_ASSERT_DELTA(outR[1], 228.889267f, 0.0001f); + TS_ASSERT_DELTA(outR[2], 62.187931f, 0.0001f); + TS_ASSERT_DELTA(outR[3], 22.295225f, 0.0001f); + TS_ASSERT_DELTA(outR[4], 6.942001f, 0.0001f); + TS_ASSERT_DELTA(outR[26], 0.015110f, 0.0001f); + TS_ASSERT_DELTA(outR[47], 0.003425f, 0.0001f); + TS_ASSERT_DELTA(outR[65], 0.001293f, 0.0001f); + } + + //performance testing + void testSpeed() { + const int samps = 15000; + + int t_on = clock(); // timer before calling func + for(int i = 0; i < samps; ++i) + oscil->prepare(); + int t_off = clock(); // timer when func returns + + printf("OscilGenTest: %f seconds for %d prepares.\n", + (static_cast(t_off - t_on)) / CLOCKS_PER_SEC, samps); + + t_on = clock(); // timer before calling func + for(int i = 0; i < samps; ++i) + oscil->get(outL, freq); + t_off = clock(); // timer when func returns + + printf("OscilGenTest: %f seconds for %d gets.\n", + (static_cast(t_off - t_on)) / CLOCKS_PER_SEC, samps); + } +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/PadNoteTest.h b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/PadNoteTest.h new file mode 100644 index 000000000..5e202b362 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/PadNoteTest.h @@ -0,0 +1,207 @@ +/* + ZynAddSubFX - a software synthesizer + + PadNoteTest.h - CxxTest for Synth/PADnote + Copyright (C) 20012 zco + Author: zco + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + + +//Based Upon AdNoteTest.h and SubNoteTest.h +#include +#include +#include +#include +#include +#include "../Misc/Master.h" +#include "../Misc/Util.h" +#include "../Synth/PADnote.h" +#include "../Params/Presets.h" +#include "../DSP/FFTwrapper.h" +#include "../globals.h" +SYNTH_T *synth; + +using namespace std; + +class PadNoteTest:public CxxTest::TestSuite +{ + public: + PADnote *note; + Master *master; + FFTwrapper *fft; + Controller *controller; + unsigned char testnote; + + + float *outR, *outL; + + void setUp() { + synth = new SYNTH_T; + //First the sensible settings and variables that have to be set: + synth->buffersize = 256; + + outL = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + *(outL + i) = 0; + outR = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + *(outR + i) = 0; + + //next the bad global variables that for some reason have not been properly placed in some + //initialization routine, but rather exist as cryptic oneliners in main.cpp: + denormalkillbuf = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + denormalkillbuf[i] = 0; + + //phew, glad to get thouse out of my way. took me a lot of sweat and gdb to get this far... + + fft = new FFTwrapper(synth->oscilsize); + //prepare the default settings + PADnoteParameters *defaultPreset = new PADnoteParameters(fft,NULL); + + + //Assert defaults + ///TS_ASSERT(!defaultPreset->VoicePar[1].Enabled); + + XMLwrapper *wrap = new XMLwrapper(); + cout << string(SOURCE_DIR) + string("/guitar-adnote.xmz") + << endl; + wrap->loadXMLfile(string(SOURCE_DIR) + + string("/guitar-adnote.xmz")); + TS_ASSERT(wrap->enterbranch("MASTER")); + TS_ASSERT(wrap->enterbranch("PART", 2)); + TS_ASSERT(wrap->enterbranch("INSTRUMENT")); + TS_ASSERT(wrap->enterbranch("INSTRUMENT_KIT")); + TS_ASSERT(wrap->enterbranch("INSTRUMENT_KIT_ITEM", 0)); + TS_ASSERT(wrap->enterbranch("PAD_SYNTH_PARAMETERS")); + defaultPreset->getfromXML(wrap); + + + //defaultPreset->defaults(); + defaultPreset->applyparameters(false); + + //verify xml was loaded + ///TS_ASSERT(defaultPreset->VoicePar[1].Enabled); + + + + controller = new Controller(); + + //lets go with.... 50! as a nice note + testnote = 50; + float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); + + note = new PADnote(defaultPreset, + controller, + freq, + 120, + 0, + testnote, + false); + + //delete defaultPreset; + delete wrap; + } + + void willNoteBeRunButIsHereForLinkingReasonsHowsThisForCamelCaseEh() + { + master = new Master(); + } + + void tearDown() { + delete note; + delete controller; + delete fft; + delete [] outL; + delete [] outR; + delete [] denormalkillbuf; + FFT_cleanup(); + delete synth; + } + + void testDefaults() { + int sampleCount = 0; + + +//#define WRITE_OUTPUT + +#ifdef WRITE_OUTPUT + ofstream file("padnoteout", ios::out); +#endif + note->noteout(outL, outR); + +#ifdef WRITE_OUTPUT + for(int i = 0; i < synth->buffersize; ++i) + file << outL[i] << std::endl; + +#endif + sampleCount += synth->buffersize; + + TS_ASSERT_DELTA(outL[255], 0.0660f, 0.0001f); + + + note->relasekey(); + + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], -0.0729f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], 0.0613f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], 0.0378f, 0.0005f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], -0.0070f, 0.0001f); + + while(!note->finished()) { + note->noteout(outL, outR); + +#ifdef WRITE_OUTPUT + for(int i = 0; i < synth->buffersize; ++i) + file << outL[i] << std::endl; + +#endif + sampleCount += synth->buffersize; + } +#ifdef WRITE_OUTPUT + file.close(); +#endif + + TS_ASSERT_EQUALS(sampleCount, 2304); + } + +#define OUTPUT_PROFILE +#ifdef OUTPUT_PROFILE + void testSpeed() { + const int samps = 15000; + + int t_on = clock(); // timer before calling func + for(int i = 0; i < samps; ++i) + note->noteout(outL, outR); + int t_off = clock(); // timer when func returns + + printf("PadNoteTest: %f seconds for %d Samples to be generated.\n", + (static_cast(t_off - t_on)) / CLOCKS_PER_SEC, samps); + } +#endif +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/PluginTest.h b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/PluginTest.h new file mode 100644 index 000000000..b88e9ba40 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/PluginTest.h @@ -0,0 +1,119 @@ +/* + ZynAddSubFX - a software synthesizer + + PluginTest.h - CxxTest for embedding zyn + Copyright (C) 2013-2013 Mark McCurry + Authors: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include +#include +#include +#include +#include +#include +#include "../Misc/Master.h" +#include "../Misc/Util.h" +#include "../globals.h" +SYNTH_T *synth; + +using namespace std; + +char *instance_name=(char*)""; + +class PluginTest:public CxxTest::TestSuite +{ + public: + void setUp() { + synth = new SYNTH_T; + synth->buffersize = 256; + synth->samplerate = 48000; + synth->alias(); + + outL = new float[1024]; + for(int i = 0; i < synth->buffersize; ++i) + outL[i] = 0.0f; + outR = new float[1024]; + for(int i = 0; i < synth->buffersize; ++i) + outR[i] = 0.0f; + + //next the bad global variables that for some reason have not been properly placed in some + //initialization routine, but rather exist as cryptic oneliners in main.cpp: + denormalkillbuf = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + denormalkillbuf[i] = 0; + + for(int i = 0; i < 16; ++i) + master[i] = new Master(); + } + + void tearDown() { + for(int i = 0; i < 16; ++i) + delete master[i]; + + delete[] outL; + delete[] outR; + delete synth; + } + + + void testInit() { + + for(int x=0; x<100; ++x) + for(int i=0; i<16; ++i) + master[i]->GetAudioOutSamples(rand()%1025, + synth->samplerate, outL, outR); + } + + void testPanic() + { + master[0]->setController(0, 0x64, 0); + master[0]->noteOn(0,64,64); + master[0]->AudioOut(outL, outR); + + float sum = 0.0f; + for(int i = 0; i < synth->buffersize; ++i) + sum += fabs(outL[i]); + + TS_ASSERT_LESS_THAN(0.1f, sum); + } + + string loadfile(string fname) const + { + std::ifstream t(fname.c_str()); + std::string str((std::istreambuf_iterator(t)), + std::istreambuf_iterator()); + return str; + } + + + void testLoadSave(void) + { + const string fname = string(SOURCE_DIR) + "/guitar-adnote.xmz"; + const string fdata = string("\n") + loadfile(fname); + char *result = NULL; + master[0]->putalldata((char*)fdata.c_str(), fdata.length()); + int res = master[0]->getalldata(&result); + + TS_ASSERT_EQUALS(fdata.length()+1, res); + TS_ASSERT(fdata == result); + } + + + private: + float *outR, *outL; + Master *master[16]; +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/RandTest.h b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/RandTest.h new file mode 100644 index 000000000..2a51e952c --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/RandTest.h @@ -0,0 +1,41 @@ +/* + ZynAddSubFX - a software synthesizer + + RandTest.h - CxxTest for Pseudo-Random Number Generator + Copyright (C) 2009-2009 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "../Misc/Util.h" +SYNTH_T *synth; + +#include +#include +#include + +class RandTest:public CxxTest::TestSuite +{ + public: + void testPRNG(void) { + //verify RND returns expected pattern when unseeded + TS_ASSERT_DELTA(RND, 0.607781, 0.00001); + TS_ASSERT_DELTA(RND, 0.591761, 0.00001); + TS_ASSERT_DELTA(RND, 0.186133, 0.00001); + TS_ASSERT_DELTA(RND, 0.286319, 0.00001); + TS_ASSERT_DELTA(RND, 0.511766, 0.00001); + } +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/SubNoteTest.h b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/SubNoteTest.h new file mode 100644 index 000000000..e5cefd860 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/SubNoteTest.h @@ -0,0 +1,182 @@ +/* + ZynAddSubFX - a software synthesizer + + AdNoteTest.h - CxxTest for Synth/SUBnote + Copyright (C) 2009-2011 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +//Based Upon AdNoteTest.h +#include +#include +#include +#include +#include +#include "../Misc/Master.h" +#include "../Misc/Util.h" +#include "../Synth/SUBnote.h" +#include "../Params/Presets.h" +#include "../globals.h" +SYNTH_T *synth; + +using namespace std; + +class SubNoteTest:public CxxTest::TestSuite +{ + public: + + SUBnote *note; + Master *master; + Controller *controller; + unsigned char testnote; + + + float *outR, *outL; + + void setUp() { + synth = new SYNTH_T; + //First the sensible settings and variables that have to be set: + synth->buffersize = 256; + + outL = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + *(outL + i) = 0; + outR = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + *(outR + i) = 0; + + //next the bad global variables that for some reason have not been properly placed in some + //initialization routine, but rather exist as cryptic oneliners in main.cpp: + denormalkillbuf = new float[synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + denormalkillbuf[i] = 0; + + //prepare the default settings + SUBnoteParameters *defaultPreset = new SUBnoteParameters(); + XMLwrapper *wrap = new XMLwrapper(); + wrap->loadXMLfile(string(SOURCE_DIR) + + string("/guitar-adnote.xmz")); + TS_ASSERT(wrap->enterbranch("MASTER")); + TS_ASSERT(wrap->enterbranch("PART", 1)); + TS_ASSERT(wrap->enterbranch("INSTRUMENT")); + TS_ASSERT(wrap->enterbranch("INSTRUMENT_KIT")); + TS_ASSERT(wrap->enterbranch("INSTRUMENT_KIT_ITEM", 0)); + TS_ASSERT(wrap->enterbranch("SUB_SYNTH_PARAMETERS")); + defaultPreset->getfromXML(wrap); + + controller = new Controller(); + + //lets go with.... 50! as a nice note + testnote = 50; + float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); + + note = new SUBnote(defaultPreset, + controller, + freq, + 120, + 0, + testnote, + false); + delete wrap; + delete defaultPreset; + } + + void willNoteBeRunButIsHereForLinkingReasonsHowsThisForCamelCaseEh() + { + master = new Master(); + } + + void tearDown() { + delete controller; + delete note; + delete [] outL; + delete [] outR; + delete [] denormalkillbuf; + delete synth; + } + + void testDefaults() { + //Note: if these tests fail it is due to the relationship between + //global.h::RND and SUBnote.cpp + + int sampleCount = 0; + +//#define WRITE_OUTPUT + +#ifdef WRITE_OUTPUT + ofstream file("subnoteout", ios::out); +#endif + note->noteout(outL, outR); +#ifdef WRITE_OUTPUT + for(int i = 0; i < synth->buffersize; ++i) + file << outL[i] << std::endl; + +#endif + sampleCount += synth->buffersize; + + TS_ASSERT_DELTA(outL[255], 0.0000f, 0.0001f); + + note->relasekey(); + + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], 0.0016f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], -0.0000f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], -0.0013f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], -0.0002f, 0.0001f); + + while(!note->finished()) { + note->noteout(outL, outR); +#ifdef WRITE_OUTPUT + for(int i = 0; i < synth->buffersize; ++i) + file << outL[i] << std::endl; + +#endif + sampleCount += synth->buffersize; + } +#ifdef WRITE_OUTPUT + file.close(); +#endif + + TS_ASSERT_EQUALS(sampleCount, 2304); + } + +#define OUTPUT_PROFILE +#ifdef OUTPUT_PROFILE + void testSpeed() { + const int samps = 15000; + + int t_on = clock(); // timer before calling func + for(int i = 0; i < samps; ++i) + note->noteout(outL, outR); + int t_off = clock(); // timer when func returns + + printf("SubNoteTest: %f seconds for %d Samples to be generated.\n", + (static_cast(t_off - t_on)) / CLOCKS_PER_SEC, samps); + } +#endif +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/UnisonTest.h b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/UnisonTest.h new file mode 100644 index 000000000..86635004c --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/UnisonTest.h @@ -0,0 +1,183 @@ +/* + ZynAddSubFX - a software synthesizer + + AdNoteTest.h - CxxTest for Synth/ADnote + Copyright (C) 2009-2011 Mark McCurry + Copyright (C) 2009 Harald Hvaal + Authors: Mark McCurry, Harald Hvaal + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + + +#include +#include +#include +#include +#include +#include "../Misc/Util.h" +#include "../Synth/ADnote.h" +#include "../Synth/OscilGen.h" +#include "../Params/Presets.h" +#include "../DSP/FFTwrapper.h" +#include "../globals.h" +SYNTH_T *synth; + +using namespace std; + + +#define BUF 256 +class AdNoteTest:public CxxTest::TestSuite +{ + public: + + ADnote *note; + FFTwrapper *fft; + Controller *controller; + unsigned char testnote; + ADnoteParameters *params; + float freq; + + + float outR[BUF], outL[BUF]; + + void setUp() { + //First the sensible settings and variables that have to be set: + synth = new SYNTH_T; + synth->buffersize = BUF; + + memset(outL,0,sizeof(outL)); + memset(outR,0,sizeof(outR)); + + //next the bad global variables that for some reason have not been properly placed in some + //initialization routine, but rather exist as cryptic oneliners in main.cpp: + denormalkillbuf = new float[BUF]; + memset(denormalkillbuf, 0, sizeof(float)*BUF); + + fft = new FFTwrapper(BUF); + //prepare the default settings + params = new ADnoteParameters(fft); + + //sawtooth to make things a bit more interesting + params->VoicePar[0].OscilSmp->Pcurrentbasefunc = 3; + + controller = new Controller(); + + //lets go with.... 50! as a nice note + testnote = 50; + freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); + + } + + void tearDown() { + delete note; + delete controller; + delete fft; + delete [] denormalkillbuf; + FFT_cleanup(); + delete synth; + delete params; + } + + void run_test(int a, int b, int c, int d, int e, int f, float values[4]) + { + sprng(0); + params->set_unison_size_index(0,a); + params->VoicePar[0].Unison_frequency_spread = b; + params->VoicePar[0].Unison_stereo_spread = c; + params->VoicePar[0].Unison_vibratto = d; + params->VoicePar[0].Unison_vibratto_speed = e; + params->VoicePar[0].Unison_invert_phase = f; + + note = new ADnote(params, controller, freq, 120, 0, testnote, false); + note->noteout(outL, outR); + TS_ASSERT_DELTA(outL[80], values[0], 1e-5); + //printf("{%f,", outL[80]); + note->noteout(outL, outR); + TS_ASSERT_DELTA(outR[90], values[1], 1e-5); + //printf("%f,", outR[90]); + note->noteout(outL, outR); + TS_ASSERT_DELTA(outL[20], values[2], 1e-5); + //printf("%f,", outL[20]); + note->noteout(outL, outR); + TS_ASSERT_DELTA(outR[200], values[3], 1e-5); + //printf("%f},\n", outR[200]); + } + + void testUnison() { + sprng(0xbeef); + + float data[][4] = { + {-0.034547,0.034349,-0.000000,0.138284}, + {0.023612,-0.093842,0.000000,-0.040384}, + {-0.015980,0.001871,-0.014463,-0.000726}, + {-0.040970,-0.000275,0.000000,-0.121016}, + {0.019250,-0.045252,0.000270,0.105372}, + {-0.086575,0.001130,-0.018921,0.001329}, + {0.009203,-0.006176,0.017344,-0.003316}, + {0.029411,-0.000248,-0.112797,-0.012883}, + {0.043657,-0.014062,-0.003374,-0.071821}, + {0.007973,0.068019,-0.038900,0.047639}, + {-0.002055,0.011170,-0.058152,-0.043493}, + {-0.005298,0.000605,-0.070932,-0.005678}, + {0.025028,-0.027742,0.020985,-0.015417}, + {0.074349,0.000640,0.080613,0.066636}, + {-0.045721,0.000279,0.009819,0.032202}, + }; + + int freq_spread[15]; + int stereo_spread[15]; + int vibrato[15]; + int vibrato_speed[15]; + int inv_phase[15]; + for(int i=0; i<15; ++i) + { + freq_spread[i] = prng()%0x7f; + stereo_spread[i] = prng()%0x7f; + vibrato[i] = prng()%0x7f; + vibrato_speed[i] = prng()%0x7f; + inv_phase[i] = prng()%5; + } + + for(int i=0; i<15; ++i) + { + run_test(i, freq_spread[i], stereo_spread[i], + vibrato[i], vibrato_speed[i], inv_phase[i], data[i]); + } +#if 0 + int sampleCount = 0; + + sampleCount += synth->buffersize; + + TS_ASSERT_DELTA(outL[255], 0.254609f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], -0.102197f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], -0.111422f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], -0.021375f, 0.0001f); + + note->noteout(outL, outR); + sampleCount += synth->buffersize; + TS_ASSERT_DELTA(outL[255], 0.149882f, 0.0001f); +#endif + } +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/XMLwrapperTest.h b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/XMLwrapperTest.h new file mode 100644 index 000000000..bf31ae085 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/XMLwrapperTest.h @@ -0,0 +1,70 @@ +/* + ZynAddSubFX - a software synthesizer + + XMLwrapperTest.h - CxxTest for Misc/XMLwrapper + Copyright (C) 2009-2009 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include +#include "../Misc/XMLwrapper.h" +#include +#include "../globals.h" +SYNTH_T *synth; +using namespace std; + +class XMLwrapperTest:public CxxTest::TestSuite +{ + public: + void setUp() { + xmla = new XMLwrapper; + xmlb = new XMLwrapper; + } + + + void testAddPar() { + xmla->addpar("my Pa*_ramet@er", 75); + TS_ASSERT_EQUALS(xmla->getpar("my Pa*_ramet@er", 0, -200, 200), 75); + } + + //here to verify that no leaks occur + void testLoad() { + string location = string(SOURCE_DIR) + string( + "/Tests/guitar-adnote.xmz"); + xmla->loadXMLfile(location); + } + + void testAnotherLoad() + { + string dat = + "\n\n\ +\n\ +\n\ +\n"; + xmlb->putXMLdata(dat.c_str()); + } + + void tearDown() { + delete xmla; + delete xmlb; + } + + + private: + XMLwrapper *xmla; + XMLwrapper *xmlb; +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Tests/guitar-adnote.xmz b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/guitar-adnote.xmz new file mode 100644 index 000000000..9922ba96b --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Tests/guitar-adnote.xmz @@ -0,0 +1,3834 @@ + + + + + + + + + + + + + + + + + + + +12tET +Equal Temperament 12 notes per octave + + + + + + + + + + + + + + + + + + + + + + + +Dist Guitar 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/ADnoteUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/ADnoteUI.fl new file mode 100644 index 000000000..cf510f40a --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/ADnoteUI.fl @@ -0,0 +1,1162 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include "../Params/ADnoteParameters.h"} {public +} + +decl {\#include "../Misc/Util.h"} {public +} + +decl {\#include "../Misc/Master.h"} {public +} + +decl {\#include "ResonanceUI.h"} {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include "EnvelopeUI.h"} {public +} + +decl {\#include "LFOUI.h"} {public +} + +decl {\#include "FilterUI.h"} {public +} + +decl {\#include "OscilGenUI.h"} {public +} + +decl {\#include "PresetsUI.h"} {public +} + +class ADvoicelistitem {open : {public Fl_Group} +} { + Function {make_window()} {open private + } { + Fl_Window ADnoteVoiceListItem {open + private xywh {262 736 615 100} type Double box UP_FRAME + class Fl_Group visible + } { + Fl_Group voicelistitemgroup { + private xywh {50 0 570 25} + code0 {if (pars->VoicePar[nvoice].Enabled==0) o->deactivate();} + } { + Fl_Value_Slider voicevolume { + callback {pars->VoicePar[nvoice].PVolume=(int)o->value();} + tooltip Volume xywh {90 5 115 20} type {Horz Knob} box NO_BOX labelsize 8 align 5 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].PVolume);} + } + Fl_Check_Button voiceresonanceenabled { + callback {pars->VoicePar[nvoice].Presonance=(int)o->value();} + tooltip {Resonance On/Off} xywh {245 7 15 17} down_box DOWN_BOX labeltype EMBOSSED_LABEL labelfont 1 labelsize 11 align 4 + code0 {o->value(pars->VoicePar[nvoice].Presonance);} + } + Fl_Value_Slider voicelfofreq { + callback {pars->VoicePar[nvoice].FreqLfo->Pintensity=(int)o->value();} + tooltip {Frequency LFO amount} xywh {500 5 115 20} type {Horz Knob} box NO_BOX labelsize 8 align 5 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].FreqLfo->Pintensity);} + } + Fl_Dial voicepanning { + callback {pars->VoicePar[nvoice].PPanning=(int) o->value();} + tooltip {Panning (leftmost is Random)} xywh {215 5 20 20} box ROUND_UP_BOX labelsize 10 align 4 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].PPanning);} + class WidgetPDial + } + Fl_Group voiceoscil {open + xywh {60 5 30 20} box THIN_DOWN_BOX color 32 selection_color 71 labelcolor 179 + code0 {osc=new Oscilloscope(o->x(),o->y(),o->w(),o->h(),"");} + code1 {osc->init(pars->VoicePar[nvoice].OscilSmp,0,pars->VoicePar[nvoice].Poscilphase,master);} + code2 {if (pars->VoicePar[nvoice].Pextoscil != -1) {osc->init(pars->VoicePar[pars->VoicePar[nvoice].Pextoscil].OscilSmp,master);}} + } {} + Fl_Value_Output detunevalueoutput { + callback {o->value(getdetune((pars->VoicePar[nvoice].PDetuneType==0)?(pars->GlobalPar.PDetuneType) : (pars->VoicePar[nvoice].PDetuneType),0,pars->VoicePar[nvoice].PDetune)*pars->getBandwidthDetuneMultiplier());} + xywh {265 5 45 20} labelsize 10 align 5 minimum -5000 maximum 5000 step 0.01 textfont 1 textsize 10 + code0 {o->value(getdetune(pars->VoicePar[nvoice].PDetuneType,0,pars->VoicePar[nvoice].PDetune)*pars->getBandwidthDetuneMultiplier());} + } + Fl_Slider voicedetune { + callback {pars->VoicePar[nvoice].PDetune=(int)o->value()+8192; +detunevalueoutput->do_callback();} + tooltip {Fine Detune (cents)} xywh {315 5 185 20} type {Horz Knob} box NO_BOX minimum -8192 maximum 8191 step 1 + code0 {o->value(pars->VoicePar[nvoice].PDetune-8192);} + } + Fl_Box noiselabel { + label N + callback {if (pars->VoicePar[nvoice].Type==0) { + o->hide(); + voiceresonanceenabled->activate(); + detunevalueoutput->activate(); + voicedetune->activate(); + voicelfofreq->activate(); + voiceoscil->activate(); +} else { + o->show(); + voiceresonanceenabled->deactivate(); + detunevalueoutput->deactivate(); + voicedetune->deactivate(); + voicelfofreq->deactivate(); + voiceoscil->deactivate(); +};} + xywh {65 5 20 20} labelfont 1 labelsize 13 labelcolor 7 + code0 {if (pars->VoicePar[nvoice].Type==0) o->hide();} + } + } + Fl_Check_Button voiceenabled { + label 01 + callback {pars->VoicePar[nvoice].Enabled=(int)o->value(); +if (o->value()==0) voicelistitemgroup->deactivate(); +else voicelistitemgroup->activate(); +o->redraw();} + private xywh {30 5 20 20} down_box DOWN_BOX labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 4 + code0 {char tmp[10];snprintf(tmp,10,"%d",nvoice+1);o->label(strdup(tmp));} + code1 {o->value(pars->VoicePar[nvoice].Enabled);} + } + } + } + Function {ADvoicelistitem(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {nvoice=0; +pars=NULL;} {} + } + Function {init(ADnoteParameters *parameters,int nvoice_,Master *master_)} {} { + code {pars=parameters; +nvoice=nvoice_; +master=master_; +make_window(); +ADnoteVoiceListItem->show(); +end();} {} + } + Function {refreshlist()} {} { + code {voiceenabled->value(pars->VoicePar[nvoice].Enabled); +voiceresonanceenabled->value(pars->VoicePar[nvoice].Presonance); +voicevolume->value(pars->VoicePar[nvoice].PVolume); +voicedetune->value(pars->VoicePar[nvoice].PDetune-8192); +voicepanning->value(pars->VoicePar[nvoice].PPanning); +voicelfofreq->value(pars->VoicePar[nvoice].FreqLfo->Pintensity); +if (pars->VoicePar[nvoice].Pextoscil != -1) { + osc->init(pars->VoicePar[pars->VoicePar[nvoice].Pextoscil].OscilSmp,0,pars->VoicePar[nvoice].Poscilphase,master); +} else + osc->init(pars->VoicePar[nvoice].OscilSmp,0,pars->VoicePar[nvoice].Poscilphase,master); +if (pars->VoicePar[nvoice].Enabled==0) voicelistitemgroup->deactivate(); + else voicelistitemgroup->activate(); +detunevalueoutput->do_callback(); +noiselabel->do_callback(); +ADnoteVoiceListItem->redraw();} {} + } + Function {~ADvoicelistitem()} {} { + code {ADnoteVoiceListItem->hide(); +//delete(ADnoteVoiceListItem);} {} + } + decl {ADnoteParameters *pars;} {} + decl {int nvoice;} {} + decl {Oscilloscope *osc;} {} + decl {Master *master;} {} +} + +class ADvoiceUI {open : {public Fl_Group} +} { + Function {make_window()} {open + } { + Fl_Window ADnoteVoiceParameters { + label Voice open + xywh {84 305 765 590} type Double box NO_BOX + class Fl_Group visible + } { + Fl_Group voiceparametersgroup {open + xywh {0 0 765 595} color 48 + code0 {if (pars->VoicePar[nvoice].Enabled==0) o->deactivate();} + } { + Fl_Group voicemodegroup {open + xywh {0 5 765 590} color 64 + } { + Fl_Group voiceFMparametersgroup { + label MODULATOR open + xywh {530 5 230 580} box UP_FRAME color 48 labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + code0 {if (pars->VoicePar[nvoice].PFMEnabled==0) o->deactivate();} + } { + Fl_Group modfrequency { + label {Mod.FREQUENCY} + xywh {535 220 220 155} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + } { + Fl_Group voiceFMfreqenvgroup { + label {ADSynth Modulator - Frequency Envelope} + xywh {540 300 210 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->VoicePar[nvoice].FMFreqEnvelope);} + code1 {if (pars->VoicePar[nvoice].PFMFreqEnvelopeEnabled==0) o->deactivate();} + class EnvelopeUI + } {} + Fl_Check_Button {} { + label On + callback {pars->VoicePar[nvoice].PFMFreqEnvelopeEnabled=(int)o->value(); +if (o->value()==0) voiceFMfreqenvgroup->deactivate(); +else voiceFMfreqenvgroup->activate(); +o->redraw();} + tooltip {Forced Relase} xywh {545 305 50 10} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->VoicePar[nvoice].PFMFreqEnvelopeEnabled);} + } + Fl_Counter {} { + label {Coarse Det.} + callback {int k=(int) o->value(); +if (k<0) k+=1024; +pars->VoicePar[nvoice].PFMCoarseDetune = k+ + (pars->VoicePar[nvoice].PFMCoarseDetune/1024)*1024;} + tooltip {Coarse Detune} xywh {685 280 60 15} labelsize 10 align 1 minimum -64 maximum 63 step 1 textfont 1 textsize 11 + code0 {int k=pars->VoicePar[nvoice].PFMCoarseDetune%1024;} + code1 {if (k>=512) k-=1024;} + code2 {o->value(k);} + code3 {o->lstep(10);} + } + Fl_Counter {} { + label Octave + callback {int k=(int) o->value(); +if (k<0) k+=16; +pars->VoicePar[nvoice].PFMCoarseDetune = k*1024+ + pars->VoicePar[nvoice].PFMCoarseDetune%1024;} + tooltip Octave xywh {625 280 45 15} type Simple labelsize 10 align 1 minimum -8 maximum 7 step 1 textfont 1 textsize 11 + code0 {int k=pars->VoicePar[nvoice].PFMCoarseDetune/1024;} + code1 {if (k>=8) k-=16;} + code2 {o->value(k);} + } + Fl_Slider {} { + callback {pars->VoicePar[nvoice].PFMDetune=(int)o->value()+8192; +fmdetunevalueoutput->do_callback();} + tooltip {Fine Detune (cents)} xywh {590 245 155 15} type {Horz Knob} box NO_BOX minimum -8192 maximum 8191 step 1 + code0 {o->value(pars->VoicePar[nvoice].PFMDetune-8192);} + } + Fl_Value_Output fmdetunevalueoutput { + label Detune + callback {o->value(getdetune((pars->VoicePar[nvoice].PFMDetuneType==0)?(pars->GlobalPar.PDetuneType) : (pars->VoicePar[nvoice].PFMDetuneType),0,pars->VoicePar[nvoice].PFMDetune));} + xywh {540 245 45 18} labelsize 8 align 5 minimum -5000 maximum 5000 step 0.01 textfont 1 textsize 8 + code0 {o->value(getdetune((pars->VoicePar[nvoice].PFMDetuneType==0)?(pars->GlobalPar.PDetuneType) : (pars->VoicePar[nvoice].PFMDetuneType),0,pars->VoicePar[nvoice].PFMDetune));} + code1 {//o->value(getdetune(pars->VoicePar[nvoice].PFMDetuneType,0,pars->VoicePar[nvoice].PFMDetune));} + } + Fl_Choice {} { + label {Detune Type} + callback {pars->VoicePar[nvoice].PFMDetuneType=(int) o->value(); +fmdetunevalueoutput->do_callback();} open + xywh {540 280 75 15} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 + code0 {o->add("Default");o->add("L35cents");o->add("L10cents");o->add("E100cents");o->add("E1200cents");} + code1 {o->value(pars->VoicePar[nvoice].PFMDetuneType);} + } {} + } + Fl_Group {} { + label {Mod.AMPLITUDE} + xywh {535 60 220 160} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + } { + Fl_Value_Slider {} { + label Vol + callback {pars->VoicePar[nvoice].PFMVolume=(int)o->value();} + tooltip Volume xywh {540 80 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].PFMVolume);} + } + Fl_Value_Slider {} { + label {V.Sns} + callback {pars->VoicePar[nvoice].PFMVelocityScaleFunction=(int) o->value();} + tooltip {Velocity Sensing Function (rightmost to disable)} xywh {540 100 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].PFMVelocityScaleFunction);} + } + Fl_Group voiceFMampenvgroup { + label {ADSynth Modulator - Amplitude Envelope} open + xywh {540 145 205 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->VoicePar[nvoice].FMAmpEnvelope);} + code1 {if (pars->VoicePar[nvoice].PFMAmpEnvelopeEnabled==0) o->deactivate();} + class EnvelopeUI + } {} + Fl_Check_Button {} { + label On + callback {pars->VoicePar[nvoice].PFMAmpEnvelopeEnabled=(int)o->value(); +if (o->value()==0) voiceFMampenvgroup->deactivate(); +else voiceFMampenvgroup->activate(); +o->redraw();} + tooltip {Forced Relase} xywh {545 150 50 10} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->VoicePar[nvoice].PFMAmpEnvelopeEnabled);} + } + Fl_Value_Slider {} { + label {F.Damp} + callback {pars->VoicePar[nvoice].PFMVolumeDamp=(int) o->value()+64;} + tooltip {Modulator Damp at Higher frequency} xywh {540 120 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 minimum -64 maximum 63 step 1 + code0 {o->value(pars->VoicePar[nvoice].PFMVolumeDamp-64);} + } + } + Fl_Group modoscil {open + xywh {535 365 220 220} + } { + Fl_Group fmoscil {open + xywh {535 440 220 140} box THIN_DOWN_BOX color 32 selection_color 71 labelcolor 179 + code0 {oscFM=new Oscilloscope(o->x(),o->y(),o->w(),o->h(),"");} + code1 {int nv=nvoice; if (pars->VoicePar[nvoice].PextFMoscil>=0) nv=pars->VoicePar[nvoice].PextFMoscil;} + code2 {oscFM->init(pars->VoicePar[nv].FMSmp,0,pars->VoicePar[nvoice].PFMoscilphase,master);} + } {} + Fl_Box {} { + label {Mod.Oscillator} + xywh {535 375 155 20} labelfont 1 align 20 + } + Fl_Button changeFMoscilbutton { + label Change + callback {if (oscedit!=NULL) delete(oscedit); + +int nv=nvoice; +if (pars->VoicePar[nvoice].PextFMoscil>=0) nv=pars->VoicePar[nvoice].PextFMoscil; + +oscedit=new OscilEditor(pars->VoicePar[nv].FMSmp,fmoscil,NULL,NULL,master);} + xywh {700 380 55 15} box THIN_UP_BOX labelfont 1 labelsize 11 + code0 {if (pars->VoicePar[nvoice].PextFMoscil>=0) o->labelcolor(FL_BLUE);} + } + Fl_Slider {} { + label Phase + callback {pars->VoicePar[nvoice].PFMoscilphase=64-(int)o->value(); +oscFM->phase=64-(int) o->value(); +fmoscil->redraw();} + xywh {645 415 105 15} type {Horz Knob} box NO_BOX labelsize 10 align 5 minimum -64 maximum 63 step 1 + code0 {o->value(64-pars->VoicePar[nvoice].PFMoscilphase);} + } + Fl_Choice {} { + label Use + callback {pars->VoicePar[nvoice].PextFMoscil=(int)o->value()-1; +if ((int) o->value() != 0) { + oscFM->init(pars->VoicePar[(int) o->value()-1].FMSmp,master); + changeFMoscilbutton->labelcolor(FL_BLUE); +} else { + oscFM->init(pars->VoicePar[nvoice].FMSmp,master); + changeFMoscilbutton->labelcolor(FL_BLACK); +}; +voiceFMparametersgroup->redraw();} open + xywh {560 410 75 20} down_box BORDER_BOX labelsize 10 textfont 1 textsize 10 + code0 {o->add("Internal");} + code1 {char tmp[50]; for (int i=0;iadd(tmp);};} + code3 {o->value(pars->VoicePar[nvoice].PextFMoscil+1);} + } {} + } + Fl_Choice {} { + label {External Mod.} + callback {pars->VoicePar[nvoice].PFMVoice=(int)o->value()-1; +if ((int) o->value() != 0) { + modoscil->deactivate(); + modfrequency->deactivate(); +} else { + modoscil->activate(); + modfrequency->activate(); +}; +voiceFMparametersgroup->redraw();} open + xywh {635 40 85 20} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 + code0 {o->add("OFF");} + code1 {char tmp[50]; for (int i=0;iadd(tmp);};} + code2 {o->value(pars->VoicePar[nvoice].PFMVoice+1);} + code3 {if ((int) o->value() != 0) {modoscil->deactivate();modfrequency->deactivate();}} + } {} + } + Fl_Choice {} { + label {Type:} + callback {pars->VoicePar[nvoice].PFMEnabled=(int)o->value(); +if (o->value()==0) voiceFMparametersgroup->deactivate(); +else voiceFMparametersgroup->activate(); +o->redraw();} + xywh {535 40 80 20} down_box BORDER_BOX align 5 + code0 {o->value(pars->VoicePar[nvoice].PFMEnabled);} + } { + MenuItem {} { + label OFF + xywh {40 40 100 20} labelfont 1 + } + MenuItem {} { + label MORPH + xywh {50 50 100 20} labelfont 1 + } + MenuItem {} { + label RING + xywh {60 60 100 20} labelfont 1 + } + MenuItem {} { + label PM + xywh {70 70 100 20} labelfont 1 + } + MenuItem {} { + label FM + xywh {80 80 100 20} labelfont 1 + } + MenuItem {} { + label PITCH + xywh {90 90 100 20} labelfont 1 deactivate + } + } + Fl_Group {} { + label FREQUENCY + xywh {5 265 525 120} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + } { + Fl_Group voicefreqenvgroup { + label {ADSynth Voice - Frequency Envelope} open + xywh {10 305 205 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->VoicePar[nvoice].FreqEnvelope);} + code1 {if (pars->VoicePar[nvoice].PFreqEnvelopeEnabled==0) o->deactivate();} + class EnvelopeUI + } {} + Fl_Check_Button {} { + label On + callback {pars->VoicePar[nvoice].PFreqEnvelopeEnabled=(int)o->value(); +if (o->value()==0) voicefreqenvgroup->deactivate(); +else voicefreqenvgroup->activate(); +o->redraw();} + tooltip {Forced Relase} xywh {15 310 50 10} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->VoicePar[nvoice].PFreqEnvelopeEnabled);} + } + Fl_Group voicefreqlfogroup { + label {Frequency LFO } open + xywh {220 305 230 70} box FLAT_BOX color 47 align 144 + code0 {o->init(pars->VoicePar[nvoice].FreqLfo);} + code1 {if (pars->VoicePar[nvoice].PFreqLfoEnabled==0) o->deactivate();} + class LFOUI + } {} + Fl_Check_Button {} { + label On + callback {pars->VoicePar[nvoice].PFreqLfoEnabled=(int)o->value(); +if (o->value()==0) voicefreqlfogroup->deactivate(); +else voicefreqlfogroup->activate(); +o->redraw();} + tooltip {Forced Relase} xywh {225 311 55 10} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->VoicePar[nvoice].PFreqLfoEnabled);} + } + Fl_Counter {} { + label Octave + callback {int k=(int) o->value(); +if (k<0) k+=16; +pars->VoicePar[nvoice].PCoarseDetune = k*1024+ + pars->VoicePar[nvoice].PCoarseDetune%1024;} + tooltip Octave xywh {470 285 45 15} type Simple labelsize 10 align 1 minimum -8 maximum 7 step 1 textfont 1 textsize 11 + code0 {int k=pars->VoicePar[nvoice].PCoarseDetune/1024;} + code1 {if (k>=8) k-=16;} + code2 {o->value(k);} + } + Fl_Counter {} { + label {Coarse Det.} + callback {int k=(int) o->value(); +if (k<0) k+=1024; +pars->VoicePar[nvoice].PCoarseDetune = k+ + (pars->VoicePar[nvoice].PCoarseDetune/1024)*1024;} + tooltip {Coarse Detune} xywh {455 355 60 20} labelsize 10 align 1 minimum -64 maximum 63 step 1 textfont 1 textsize 11 + code0 {int k=pars->VoicePar[nvoice].PCoarseDetune%1024;} + code1 {if (k>=512) k-=1024;} + code2 {o->value(k);} + code3 {o->lstep(10);} + } + Fl_Slider {} { + callback {pars->VoicePar[nvoice].PDetune=(int)o->value()+8192; +detunevalueoutput->do_callback();} + tooltip {Fine Detune (cents)} xywh {58 287 392 13} type {Horz Knob} box NO_BOX minimum -8192 maximum 8191 step 1 + code0 {o->value(pars->VoicePar[nvoice].PDetune-8192);} + } + Fl_Value_Output detunevalueoutput { + label Detune + callback {o->value(getdetune((pars->VoicePar[nvoice].PDetuneType==0)?(pars->GlobalPar.PDetuneType) : (pars->VoicePar[nvoice].PDetuneType),0,pars->VoicePar[nvoice].PDetune)*pars->getBandwidthDetuneMultiplier());} + xywh {10 287 45 15} labelsize 10 align 5 minimum -5000 maximum 5000 step 0.01 textfont 1 textsize 10 + code0 {o->value(getdetune((pars->VoicePar[nvoice].PDetuneType==0)?(pars->GlobalPar.PDetuneType) : (pars->VoicePar[nvoice].PDetuneType),0,pars->VoicePar[nvoice].PDetune)*pars->getBandwidthDetuneMultiplier());} + } + Fl_Check_Button {} { + label 440Hz + callback {int x=(int) o->value(); +pars->VoicePar[nvoice].Pfixedfreq=x; +if (x==0) fixedfreqetdial->deactivate(); + else fixedfreqetdial->activate();} + tooltip {Set the voice base frequency to 440Hz} xywh {345 268 55 15} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->VoicePar[nvoice].Pfixedfreq);} + } + Fl_Dial fixedfreqetdial { + label {Eq.T.} + callback {pars->VoicePar[nvoice].PfixedfreqET=(int) o->value();} + tooltip {How the frequency varies acording to the keyboard (leftmost for fixed frequency)} xywh {405 270 15 15} box ROUND_UP_BOX labelsize 10 align 8 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].PfixedfreqET);} + code1 {if (pars->VoicePar[nvoice].Pfixedfreq==0) o->deactivate();} + class WidgetPDial + } + Fl_Choice {} { + label {Detune Type} + callback {pars->VoicePar[nvoice].PDetuneType=(int) o->value(); +detunevalueoutput->do_callback();} open + xywh {455 320 70 15} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 + code0 {o->add("Default");o->add("L35cents");o->add("L10cents");o->add("E100cents");o->add("E1200cents");} + code1 {o->value(pars->VoicePar[nvoice].PDetuneType);} + } {} + } + Fl_Group voiceoscil { + xywh {80 390 445 145} box THIN_DOWN_BOX color 32 selection_color 71 labelcolor 179 + code0 {osc=new Oscilloscope(o->x(),o->y(),o->w(),o->h(),"");} + code1 {int nv=nvoice; if (pars->VoicePar[nvoice].Pextoscil>=0) nv=pars->VoicePar[nvoice].Pextoscil;} + code2 {osc->init(pars->VoicePar[nv].OscilSmp,0,pars->VoicePar[nvoice].Poscilphase,master);} + } {} + Fl_Button changevoiceoscilbutton { + label Change + callback {if (oscedit!=NULL) delete(oscedit); + +int nv=nvoice; +if (pars->VoicePar[nvoice].Pextoscil>=0) nv=pars->VoicePar[nvoice].Pextoscil; + +oscedit=new OscilEditor(pars->VoicePar[nv].OscilSmp,voiceoscil,NULL,NULL,master);} + xywh {5 490 65 20} box THIN_UP_BOX labelfont 1 labelsize 11 + code0 {if (pars->VoicePar[nvoice].Pextoscil>=0) o->labelcolor(FL_BLUE);} + } + Fl_Box {} { + label {Voice Oscillator} + xywh {5 390 75 35} labelfont 1 labelsize 12 align 128 + } + Fl_Slider {} { + label Phase + callback {pars->VoicePar[nvoice].Poscilphase=64-(int)o->value(); +osc->phase=64-(int) o->value(); +voiceoscil->redraw();} + xywh {10 435 65 10} type {Horz Knob} box NO_BOX labelsize 10 align 5 minimum -64 maximum 63 step 1 + code0 {o->value(64-pars->VoicePar[nvoice].Poscilphase);} + } + Fl_Check_Button {} { + label {R.} + callback {pars->VoicePar[nvoice].Presonance=(int) o->value();} + tooltip {Resonance On/Off} xywh {210 5 35 35} box THIN_UP_BOX down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->VoicePar[nvoice].Presonance);} + } + Fl_Choice {} { + label {Use Oscil.} + callback {pars->VoicePar[nvoice].Pextoscil=(int)o->value()-1; +if ((int) o->value() != 0) { + osc->init(pars->VoicePar[(int) o->value()-1].OscilSmp,master); + changevoiceoscilbutton->labelcolor(FL_BLUE); +} else { + osc->init(pars->VoicePar[nvoice].OscilSmp,master); + changevoiceoscilbutton->labelcolor(FL_BLACK); +}; + +voiceparametersgroup->redraw(); +voiceonbutton->redraw();} open + xywh {5 470 65 15} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 + code0 {o->add("Internal");} + code1 {char tmp[50]; for (int i=0;iadd(tmp);};} + code3 {o->value(pars->VoicePar[nvoice].Pextoscil+1);} + } {} + Fl_Group {} {open + xywh {5 540 515 45} box UP_FRAME + } { + Fl_Dial {} { + label Stereo + callback {pars->VoicePar[nvoice].Unison_stereo_spread=(int)o->value();} + tooltip {Stereo Spread} xywh {285 555 25 30} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].Unison_stereo_spread);} + class WidgetPDial + } + Fl_Choice {} { + label Unison + callback {pars->set_unison_size_index(nvoice,(int) o->value());} open + tooltip {Unison size} xywh {10 560 75 20} down_box BORDER_BOX labelfont 1 align 5 textfont 1 textsize 10 + code0 {o->add("OFF");char tmp[100];for (int i=1;ADnote_unison_sizes[i];i++){snprintf(tmp,100,"size %d",ADnote_unison_sizes[i]);o->add(tmp);};} + code1 {o->value(pars->get_unison_size_index(nvoice));} + } {} + Fl_Dial {} { + label Vibrato + callback {pars->VoicePar[nvoice].Unison_vibratto=(int)o->value();} + tooltip Vibrato xywh {340 555 25 30} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].Unison_vibratto);} + class WidgetPDial + } + Fl_Choice {} { + label Invert + callback {pars->VoicePar[nvoice].Unison_invert_phase=(int) o->value();} open + tooltip {Phase Invert} xywh {445 560 65 15} down_box BORDER_BOX labelsize 11 align 5 textfont 1 textsize 10 + code0 {o->add("None");o->add("Random");char tmp[100];for (int i=2;i<=5;i++){snprintf(tmp,100,"%d %%",100/i);o->add(tmp);};} + code1 {o->value(pars->VoicePar[nvoice].Unison_invert_phase);} + } {} + Fl_Slider {} { + label {Frequency Spread} + callback {pars->VoicePar[nvoice].Unison_frequency_spread=(int)o->value(); +unisonspreadoutput->do_callback();} + tooltip {Frequency Spread of the Unison} xywh {95 562 125 13} type {Horz Knob} box NO_BOX labelsize 12 align 1 maximum 127 step 1 value 64 + code0 {o->value(pars->VoicePar[nvoice].Unison_frequency_spread);} + } + Fl_Value_Output unisonspreadoutput { + label {(cents)} + callback {o->value(pars->getUnisonFrequencySpreadCents(nvoice));} + xywh {225 560 40 15} labelsize 10 align 5 maximum 1000 step 0.1 textfont 1 textsize 10 + code0 {o->value(pars->getUnisonFrequencySpreadCents(nvoice));} + } + Fl_Dial {} { + label {Vib.speed} + callback {pars->VoicePar[nvoice].Unison_vibratto_speed=(int)o->value();} + tooltip {Vibrato Average Speed} xywh {390 555 25 30} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].Unison_vibratto_speed);} + class WidgetPDial + } + } + } + Fl_Group {} { + label AMPLITUDE open + xywh {5 40 240 220} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + } { + Fl_Value_Slider {} { + label Vol + callback {pars->VoicePar[nvoice].PVolume=(int)o->value();} + tooltip Volume xywh {10 60 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].PVolume);} + } + Fl_Value_Slider {} { + label {V.Sns} + callback {pars->VoicePar[nvoice].PAmpVelocityScaleFunction=(int) o->value();} + tooltip {Velocity Sensing Function (rightmost to disable)} xywh {10 80 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].PAmpVelocityScaleFunction);} + } + Fl_Group voiceampenvgroup { + label {ADSynth Voice - Amplitude Envelope} open + xywh {10 105 205 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->VoicePar[nvoice].AmpEnvelope);} + code1 {if (pars->VoicePar[nvoice].PAmpEnvelopeEnabled==0) o->deactivate();} + class EnvelopeUI + } {} + Fl_Dial {} { + label Pan + callback {pars->VoicePar[nvoice].PPanning=(int) o->value();} + tooltip {Panning (leftmost is Random)} xywh {210 60 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].PPanning);} + class WidgetPDial + } + Fl_Check_Button {} { + label On + callback {pars->VoicePar[nvoice].PAmpEnvelopeEnabled=(int)o->value(); +if (o->value()==0) voiceampenvgroup->deactivate(); +else voiceampenvgroup->activate(); +o->redraw();} + tooltip {Forced Relase} xywh {15 110 50 10} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->VoicePar[nvoice].PAmpEnvelopeEnabled);} + } + Fl_Group voiceamplfogroup { + label {Amplitude LFO } open + xywh {10 180 230 75} box FLAT_BOX color 47 align 144 + code0 {o->init(pars->VoicePar[nvoice].AmpLfo);} + code1 {if (pars->VoicePar[nvoice].PAmpLfoEnabled==0) o->deactivate();} + class LFOUI + } {} + Fl_Check_Button {} { + label On + callback {pars->VoicePar[nvoice].PAmpLfoEnabled=(int)o->value(); +if (o->value()==0) voiceamplfogroup->deactivate(); +else voiceamplfogroup->activate(); +o->redraw();} + tooltip {Forced Relase} xywh {15 185 55 10} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->VoicePar[nvoice].PAmpLfoEnabled);} + } + Fl_Check_Button {} { + label Minus + callback {pars->VoicePar[nvoice].PVolumeminus=(int)o->value();} + xywh {10 45 50 10} down_box DOWN_BOX labelfont 1 labelsize 10 + code0 {o->value(pars->VoicePar[nvoice].PVolumeminus);} + } + } + Fl_Group voicefiltergroup { + label FILTER open + xywh {245 5 285 260} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + code0 {if (pars->VoicePar[nvoice].PFilterEnabled==0) o->deactivate();} + } { + Fl_Group {} { + label {ADsynth Voice - Filter} open + xywh {250 30 275 75} box FLAT_BOX color 50 align 144 + code0 {o->init(pars->VoicePar[nvoice].VoiceFilter,NULL,NULL);} + class FilterUI + } {} + Fl_Group voicefilterenvgroup { + label {ADSynth Voice - Filter Envelope} open + xywh {250 115 275 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->VoicePar[nvoice].FilterEnvelope);} + code1 {if (pars->VoicePar[nvoice].PFilterEnvelopeEnabled==0) o->deactivate();} + class EnvelopeUI + } {} + Fl_Check_Button {} { + label On + callback {pars->VoicePar[nvoice].PFilterEnvelopeEnabled=(int)o->value(); +if (o->value()==0) voicefilterenvgroup->deactivate(); +else voicefilterenvgroup->activate(); +o->redraw();} + tooltip {Forced Relase} xywh {255 119 55 10} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->VoicePar[nvoice].PFilterEnvelopeEnabled);} + } + Fl_Group voicefilterlfogroup { + label {Filter LFO } open + xywh {250 190 230 70} box FLAT_BOX color 47 align 144 + code0 {o->init(pars->VoicePar[nvoice].FilterLfo);} + code1 {if (pars->VoicePar[nvoice].PFilterLfoEnabled==0) o->deactivate();} + class LFOUI + } {} + Fl_Check_Button {} { + label On + callback {pars->VoicePar[nvoice].PFilterLfoEnabled=(int)o->value(); +if (o->value()==0) voicefilterlfogroup->deactivate(); +else voicefilterlfogroup->activate(); +o->redraw();} + tooltip {Forced Relase} xywh {255 196 55 10} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->VoicePar[nvoice].PFilterLfoEnabled);} + } + } + Fl_Group {} { + label 01 + xywh {5 5 55 35} box THIN_UP_BOX labeltype EMBOSSED_LABEL labelfont 1 labelsize 22 align 16 + code0 {char tmp[10];snprintf(tmp,10,"%d",nvoice+1);o->label(strdup(tmp));} + } {} + Fl_Choice {} { + callback {int x=(int) o->value(); +pars->VoicePar[nvoice].Type=x; +if (x==0) voicemodegroup->activate(); + else voicemodegroup->deactivate(); +noiselabel->do_callback();} + tooltip {Oscillator Type (sound/noise)} xywh {5 515 65 20} down_box BORDER_BOX labelsize 10 textfont 1 textsize 10 + code0 {o->value(pars->VoicePar[nvoice].Type);} + code1 {if (pars->VoicePar[nvoice].Type!=0) voicemodegroup->deactivate();} + } { + MenuItem {} { + label Sound + xywh {5 5 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label NOISE + xywh {15 15 100 20} labelfont 1 labelsize 11 labelcolor 1 + } + } + Fl_Check_Button bypassfiltercheckbutton { + label {Bypass Global F.} + callback {pars->VoicePar[nvoice].Pfilterbypass=(int)o->value();} + xywh {425 10 100 20} down_box DOWN_BOX labelfont 1 labelsize 10 align 148 + code0 {o->value(pars->VoicePar[nvoice].Pfilterbypass);} + } + Fl_Group {} {open + xywh {115 5 95 35} box THIN_UP_BOX + } { + Fl_Value_Slider {} { + label Delay + callback {pars->VoicePar[nvoice].PDelay=(int)o->value();} + tooltip Volume xywh {120 21 84 12} type {Horz Knob} box NO_BOX labelsize 11 align 5 maximum 127 step 1 + code0 {o->value(pars->VoicePar[nvoice].PDelay);} + } + } + Fl_Check_Button {} { + label On + callback {pars->VoicePar[nvoice].PFilterEnabled=(int)o->value(); +if (o->value()==0) voicefiltergroup->deactivate(); +else voicefiltergroup->activate(); +o->redraw(); +bypassfiltercheckbutton->redraw();} + tooltip {Enable Filter} xywh {250 15 60 15} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->VoicePar[nvoice].PFilterEnabled);} + } + Fl_Box noiselabel { + label {White Noise} + callback {if (pars->VoicePar[nvoice].Type==0) o->hide(); else o->show();} + xywh {150 430 300 65} labelfont 1 labelsize 50 labelcolor 7 + code0 {if (pars->VoicePar[nvoice].Type==0) o->hide(); else o->show();} + } + } + Fl_Check_Button voiceonbutton { + label On + callback {pars->VoicePar[nvoice].Enabled=(int)o->value(); +if (o->value()==0) voiceparametersgroup->deactivate(); +else voiceparametersgroup->activate(); +o->redraw();} + xywh {60 5 55 35} box THIN_UP_BOX down_box DOWN_BOX labelfont 1 labelsize 13 + code0 {o->value(pars->VoicePar[nvoice].Enabled);} + } + } + } + Function {ADvoiceUI(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {nvoice=0; +pars=NULL; +oscedit=NULL;} {} + } + Function {init(ADnoteParameters *parameters,int nvoice_,Master *master_)} {open + } { + code {pars=parameters; +nvoice=nvoice_; +master=master_; +make_window(); +end(); +ADnoteVoiceParameters->show();} {} + } + Function {~ADvoiceUI()} {open + } { + code {ADnoteVoiceParameters->hide(); +hide(); +if (oscedit!=NULL) { + delete(oscedit); +}; +//delete (ADnoteVoiceParameters);} {} + } + decl {int nvoice;} {} + decl {ADnoteParameters *pars;} {} + decl {OscilEditor *oscedit;} {} + decl {Oscilloscope *osc;} {} + decl {Oscilloscope *oscFM;} {} + decl {Master *master;} {} +} + +class ADnoteUI {open : {public PresetsUI_} +} { + Function {make_window()} {open private + } { + Fl_Window ADnoteGlobalParameters { + label {ADsynth Global Parameters of the Instrument} open + xywh {457 319 540 430} type Double visible + } { + Fl_Group {} { + label FREQUENCY open + xywh {5 280 530 115} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + } { + Fl_Group freqenv { + label {ADSynth Global - Frequency Envelope} open + xywh {10 320 205 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->GlobalPar.FreqEnvelope);} + class EnvelopeUI + } {} + Fl_Counter octave { + label Octave + callback {int k=(int) o->value(); +if (k<0) k+=16; +pars->GlobalPar.PCoarseDetune = k*1024+ + pars->GlobalPar.PCoarseDetune%1024;} + tooltip Octave xywh {455 300 45 15} type Simple labelsize 10 align 1 minimum -8 maximum 7 step 1 textfont 1 textsize 11 + code0 {int k=pars->GlobalPar.PCoarseDetune/1024;if (k>=8) k-=16;} + code2 {o->value(k);} + } + Fl_Counter coarsedet { + label {Coarse det.} + callback {int k=(int) o->value(); +if (k<0) k+=1024; +pars->GlobalPar.PCoarseDetune = k+ + (pars->GlobalPar.PCoarseDetune/1024)*1024;} + tooltip {Coarse Detune} xywh {460 370 60 20} type Simple labelsize 10 align 5 minimum -64 maximum 63 step 1 textfont 1 textsize 11 + code0 {int k=pars->GlobalPar.PCoarseDetune%1024;if (k>=512) k-=1024;} + code2 {o->value(k);} + code3 {o->lstep(10);} + } + Fl_Group freqlfo { + label {Frequency LFO } open + xywh {220 320 230 70} box FLAT_BOX color 47 align 144 + code0 {o->init(pars->GlobalPar.FreqLfo);} + class LFOUI + } {} + Fl_Slider freq { + callback {pars->GlobalPar.PDetune=(int)o->value()+8192; +detunevalueoutput->do_callback();} + tooltip {Fine Detune (cents)} xywh {60 300 385 15} type {Horz Knob} box NO_BOX minimum -8192 maximum 8191 step 1 + code0 {o->value(pars->GlobalPar.PDetune-8192);} + } + Fl_Value_Output detunevalueoutput { + label Detune + callback {o->value(getdetune(pars->GlobalPar.PDetuneType,0,pars->GlobalPar.PDetune));} + xywh {12 300 45 15} labelsize 10 align 5 minimum -5000 maximum 5000 step 0.01 textfont 1 textsize 10 + code0 {o->value(getdetune(pars->GlobalPar.PDetuneType,0,pars->GlobalPar.PDetune));} + } + Fl_Choice detunetype { + label {Detune Type} + callback {pars->GlobalPar.PDetuneType=(int) o->value()+1; +detunevalueoutput->do_callback();} open + xywh {455 340 75 15} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 + code0 {o->add("L35cents");o->add("L10cents");o->add("E100cents");o->add("E1200cents");} + code1 {o->value(pars->GlobalPar.PDetuneType-1);} + } {} + Fl_Dial {} { + label relBW + callback {pars->GlobalPar.PBandwidth=(int) o->value(); + +pars->getBandwidthDetuneMultiplier(); + +for (int i=0;irefreshlist(); +};} + tooltip {Bandwidth - how the relative fine detune of the voice are changed} xywh {505 295 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->GlobalPar.PBandwidth);} + class WidgetPDial + } + } + Fl_Group {} { + label AMPLITUDE + xywh {5 5 240 260} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + } { + Fl_Value_Slider volume { + label Vol + callback {pars->GlobalPar.PVolume=(int)o->value();} + tooltip Volume xywh {10 30 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 + code0 {o->value(pars->GlobalPar.PVolume);} + } + Fl_Value_Slider vsns { + label {V.Sns} + callback {pars->GlobalPar.PAmpVelocityScaleFunction=(int) o->value();} + tooltip {Velocity Sensing Function (rightmost to disable)} xywh {10 50 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 + code0 {o->value(pars->GlobalPar.PAmpVelocityScaleFunction);} + } + Fl_Dial pan { + label Pan + callback {pars->GlobalPar.PPanning=(int) o->value();} + tooltip {Panning (leftmost is Random)} xywh {210 25 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(pars->GlobalPar.PPanning);} + class WidgetPDial + } + Fl_Dial pstr { + label {P.Str.} + callback {pars->GlobalPar.PPunchStrength=(int) o->value();} + tooltip {Punch Strength} xywh {125 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->GlobalPar.PPunchStrength);} + class WidgetPDial + } + Fl_Dial pt { + label {P.t.} + callback {pars->GlobalPar.PPunchTime=(int) o->value();} + tooltip {Punch Time (duration)} xywh {155 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->GlobalPar.PPunchTime);} + class WidgetPDial + } + Fl_Dial pstc { + label {P.Stc.} + callback {pars->GlobalPar.PPunchStretch=(int) o->value();} + tooltip {Punch Stretch} xywh {185 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->GlobalPar.PPunchStretch);} + class WidgetPDial + } + Fl_Dial pvel { + label {P.Vel.} + callback {pars->GlobalPar.PPunchVelocitySensing=(int) o->value();} + tooltip {Punch Velocity Sensing} xywh {215 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->GlobalPar.PPunchVelocitySensing);} + class WidgetPDial + } + Fl_Group ampenv { + label {ADSynth Global - Amplitude Envelope} open + xywh {10 75 205 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->GlobalPar.AmpEnvelope);} + class EnvelopeUI + } {} + Fl_Group amplfo { + label {Amplitude LFO } open + xywh {10 150 230 70} box FLAT_BOX color 47 align 144 + code0 {o->init(pars->GlobalPar.AmpLfo);} + class LFOUI + } {} + Fl_Check_Button rndgrp { + label {Rnd Grp} + callback {pars->GlobalPar.Hrandgrouping=(int) o->value();} + tooltip {How the Harmonic Amplitude is applied to voices that use the same oscillator} xywh {70 235 40 25} down_box DOWN_BOX labelsize 10 align 148 + code0 {o->value(pars->GlobalPar.Hrandgrouping);} + } + } + Fl_Group {} { + label FILTER open selected + xywh {250 5 285 265} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + } { + Fl_Group filterenv { + label {ADSynth Global - Filter Envelope} open + xywh {255 118 275 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->GlobalPar.FilterEnvelope);} + class EnvelopeUI + } {} + Fl_Group filterlfo { + label {Filter LFO} open + xywh {255 195 230 70} box FLAT_BOX color 47 align 144 + code0 {o->init(pars->GlobalPar.FilterLfo);} + class LFOUI + } {} + Fl_Group filterui { + label {ADsynth Global - Filter} open + xywh {255 35 275 75} box FLAT_BOX color 50 align 144 + code0 {o->init(pars->GlobalPar.GlobalFilter,&pars->GlobalPar.PFilterVelocityScale,&pars->GlobalPar.PFilterVelocityScaleFunction);} + class FilterUI + } {} + } + Fl_Check_Button stereo { + label Stereo + callback {pars->GlobalPar.PStereo=(int) o->value();} + xywh {5 230 65 35} down_box DOWN_BOX labelsize 11 + code0 {o->value(pars->GlobalPar.PStereo);} + } + Fl_Button {} { + label {Show Voice List} + callback {for (int i=0;irefreshlist(); +} +ADnoteVoiceList->show();} + xywh {180 400 125 25} labelsize 12 + } + Fl_Button {} { + label {Show Voice Parameters} + callback {ADnoteVoice->show();} + xywh {5 400 170 25} labelsize 12 + } + Fl_Button {} { + label Close + callback {ADnoteGlobalParameters->hide();} + xywh {475 400 60 25} box THIN_UP_BOX + } + Fl_Button {} { + label Resonance + callback {resui->resonancewindow->redraw(); +resui->resonancewindow->show();} + tooltip Resonance xywh {309 400 86 25} box THIN_UP_BOX labelsize 12 + } + Fl_Button {} { + label C + callback {presetsui->copyArray(pars);} + xywh {405 405 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->pasteArray(pars,this);} + xywh {435 405 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + } + Fl_Window ADnoteVoice { + label {ADsynth Voice Parameters} open + xywh {512 361 765 620} type Double visible + } { + Fl_Group advoice { + xywh {0 0 765 585} + code0 {o->init(pars,nvoice,master);} + code1 {o->show();} + class ADvoiceUI + } {} + Fl_Button {} { + label {Close Window} + callback {ADnoteVoice->hide();} + xywh {305 590 195 25} box THIN_UP_BOX labelfont 1 + } + Fl_Counter currentvoicecounter { + label {Current Voice} + callback {nvoice=(int)o->value()-1; +advoice->hide(); +ADnoteVoice->remove(advoice); +delete advoice; +advoice=new ADvoiceUI(0,0,765,585); +ADnoteVoice->add(advoice); +advoice->init(pars,nvoice,master); +advoice->show(); +ADnoteVoice->redraw();} + xywh {10 590 130 25} type Simple labelfont 1 align 8 minimum 0 maximum 2 step 1 value 1 textfont 1 textsize 13 + code0 {o->bounds(1,NUM_VOICES);} + } + Fl_Button {} { + label C + callback {presetsui->copy(pars,nvoice);} + xywh {705 595 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(pars,this,nvoice);} + xywh {735 595 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + } + Fl_Window ADnoteVoiceList { + label {ADsynth Voices list} + xywh {32 266 650 260} type Double hide + } { + Fl_Text_Display {} { + label {No.} + xywh {10 15 30 10} box NO_BOX labelfont 1 labelsize 11 + } + Fl_Text_Display {} { + label Vol + xywh {145 15 30 10} box NO_BOX labelfont 1 labelsize 11 + } + Fl_Text_Display {} { + label Detune + xywh {384 15 25 10} box NO_BOX labelfont 1 labelsize 11 + } + Fl_Text_Display {} { + label Pan + xywh {210 15 30 10} box NO_BOX labelfont 1 labelsize 11 + } + Fl_Text_Display {} { + label {Vib. Depth} + xywh {560 15 30 10} box NO_BOX labelfont 1 labelsize 11 + } + Fl_Text_Display {} { + label {R.} + xywh {245 15 25 10} box NO_BOX labelfont 1 labelsize 11 + } + Fl_Button {} { + label {Hide Voice List} + callback {ADnoteVoiceList->hide();} + xywh {255 237 125 20} + } + Fl_Scroll {} {open + xywh {0 15 640 220} type VERTICAL box THIN_UP_BOX + } { + Fl_Pack {} {open + xywh {0 20 620 210} + code0 {for (int i=0;iinit(pars,i,master);}} + } {} + } + } + } + Function {ADnoteUI(ADnoteParameters *parameters,Master *master_)} {} { + code {pars=parameters; +master=master_; +nvoice=0; +resui=new ResonanceUI(pars->GlobalPar.Reson); +make_window();} {} + } + Function {~ADnoteUI()} {} { + code {ADnoteVoiceList->hide(); +ADnoteGlobalParameters->hide(); +ADnoteVoice->hide(); +delete(ADnoteVoiceList); +delete(ADnoteGlobalParameters); +delete(ADnoteVoice); +delete(resui);} {} + } + Function {refresh()} {} { + code {volume->value(pars->GlobalPar.PVolume); +vsns->value(pars->GlobalPar.PAmpVelocityScaleFunction); +pan->value(pars->GlobalPar.PPanning); + +stereo->value(pars->GlobalPar.PStereo); +rndgrp->value(pars->GlobalPar.Hrandgrouping); + +pstr->value(pars->GlobalPar.PPunchStrength); +pt->value(pars->GlobalPar.PPunchTime); +pstc->value(pars->GlobalPar.PPunchStretch); +pvel->value(pars->GlobalPar.PPunchVelocitySensing); + +detunevalueoutput->value(getdetune(pars->GlobalPar.PDetuneType,0,pars->GlobalPar.PDetune)); +freq->value(pars->GlobalPar.PDetune-8192); + +int k=pars->GlobalPar.PCoarseDetune/1024;if (k>=8) k-=16; +octave->value(k); + +detunetype->value(pars->GlobalPar.PDetuneType-1); +k=pars->GlobalPar.PCoarseDetune%1024;if (k>=512) k-=1024; +coarsedet->value(k); +amplfo->refresh(); +freqlfo->refresh(); +filterlfo->refresh(); + +ampenv->refresh(); +freqenv->refresh(); +filterenv->refresh(); +filterui->refresh(); + +for (int i=0;irefreshlist(); + +resui->refresh(); +currentvoicecounter->do_callback();} {} + } + decl {ADnoteParameters *pars;} {} + decl {ResonanceUI *resui;} {} + decl {Master *master;} {} + decl {int nvoice;} {} + decl {ADvoicelistitem *voicelistitem[NUM_VOICES];} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/BankUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/BankUI.fl new file mode 100644 index 000000000..70c273a27 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/BankUI.fl @@ -0,0 +1,368 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "../Misc/Master.h"} {public +} + +decl {\#include "../Misc/Part.h"} {public +} + +decl {\#include "../Misc/Bank.h"} {public +} + +decl {\#include "../Misc/Config.h"} {public +} + +decl {\#include "../Misc/Util.h"} {public +} + +class BankProcess_ {} { + Function {process()} {open return_type {virtual void} + } { + code {;} {} + } + decl {Bank *bank;} {public + } +} + +class BankSlot {open : {public Fl_Button,BankProcess_} +} { + Function {BankSlot(int x,int y, int w, int h, const char *label=0):Fl_Button(x,y,w,h,label)} {open + } { + code {what=NULL; +whatslot=NULL; +nslot=0; +nselected=NULL;} {selected + } + } + Function {handle(int event)} {return_type int + } { + code {if (what==NULL) return(0); +if (Fl::event_inside(this)){ + *what=0;*whatslot=nslot; + if ((event==FL_RELEASE)&&(Fl::event_button()==1))*what=1; + if ((event==FL_RELEASE)&&(Fl::event_button()==3))*what=2; + if (event==FL_PUSH) highlight=1; +}else highlight=0; + +int tmp=Fl_Button::handle(event); +if ((*what!=0) && Fl::event_inside(this)) (bp->*fnc)(); +return(tmp);} {} + } + Function {init(int nslot_, int *what_, int *whatslot_,void (BankProcess_:: *fnc_)(void),BankProcess_ *bp_,Bank *bank_,int *nselected_)} {} { + code {nslot=nslot_; +what=what_; +whatslot=whatslot_; +fnc=fnc_; +bp=bp_; +bank=bank_; +nselected=nselected_; +box(FL_THIN_UP_BOX); +labelfont(0); +labelsize(13); +align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_CLIP); + +highlight=0; +refresh();} {} + } + Function {refresh()} {} { + code {if (bank->emptyslot(nslot)) + color(46); +else if (bank->isPADsynth_used(nslot)) + color(26); +else + color(51); + + +if (*nselected==nslot) + color(6); + + +copy_label(bank->getnamenumbered(nslot).c_str());} {} + } + decl {int *what,*whatslot,nslot,highlight, *nselected;} {} + decl {void (BankProcess_:: *fnc)(void);} {} + decl {BankProcess_ *bp;} {} +} + +class BankUI {open : {public BankProcess_} +} { + Function {make_window()} {open + } { + Fl_Window bankuiwindow { + label Bank + xywh {492 406 785 575} type Double + code0 {o->label(bank->bankfiletitle.c_str());} + code1 {if (bank->bankfiletitle.empty()) o->label ("Choose a bank from the bank list on the left (or go to settings if to configure the bank location) or choose 'New Bank...' to make a new bank.");} visible + } { + Fl_Button {} { + label Close + callback {bankuiwindow->hide();} + xywh {705 546 70 24} box THIN_UP_BOX + } + Fl_Group {} { + xywh {5 34 772 491} box ENGRAVED_FRAME + } { + Fl_Pack {} { + xywh {10 39 150 481} box BORDER_FRAME + code0 {o->box(FL_NO_BOX);} + code1 {for (int i=0;i<32;i++){bs[i]=new BankSlot (0,0,o->w(),15," ");bs[i]->init(i,&what,&slot,&BankProcess_::process,(BankProcess_ *)this,bank,&nselected);};} + } {} + Fl_Pack {} { + xywh {163 39 150 481} box BORDER_FRAME + code0 {o->box(FL_NO_BOX);} + code1 {for (int i=32;i<64;i++){bs[i]=new BankSlot (0,0,o->w(),15," ");bs[i]->init(i,&what,&slot,&BankProcess_::process,(BankProcess_ *)this,bank,&nselected);};} + } {} + Fl_Pack {} { + xywh {316 39 150 481} box BORDER_FRAME + code0 {o->box(FL_NO_BOX);} + code1 {for (int i=64;i<96;i++){bs[i]=new BankSlot (0,0,o->w(),15," ");bs[i]->init(i,&what,&slot,&BankProcess_::process,(BankProcess_ *)this,bank,&nselected);};} + } {} + Fl_Pack {} { + xywh {469 39 150 481} box BORDER_FRAME + code0 {o->box(FL_NO_BOX);} + code1 {for (int i=96;i<128;i++){bs[i]=new BankSlot (0,0,o->w(),15," ");bs[i]->init(i,&what,&slot,&BankProcess_::process,(BankProcess_ *)this,bank,&nselected);};} + } {} + Fl_Pack {} { + xywh {622 39 150 481} box BORDER_FRAME + code0 {o->box(FL_NO_BOX);} + code1 {for (int i=128;i<160;i++){bs[i]=new BankSlot (0,0,o->w(),15," ");bs[i]->init(i,&what,&slot,&BankProcess_::process,(BankProcess_ *)this,bank,&nselected);};} + } {} + } + Fl_Group modeselect { + xywh {5 528 425 42} box ENGRAVED_BOX + } { + Fl_Light_Button writebutton { + label WRITE + callback {if (o->value()>0.5) mode=2; +removeselection();} + xywh {116 534 99 30} type Radio down_box THIN_DOWN_BOX selection_color 1 labeltype ENGRAVED_LABEL labelfont 1 labelsize 13 + code0 {if (bank->locked()) o->deactivate();} + } + Fl_Light_Button readbutton { + label READ + callback {if (o->value()>0.5) mode=1; +removeselection();} + xywh {11 534 99 30} type Radio down_box THIN_DOWN_BOX selection_color 101 labeltype ENGRAVED_LABEL labelfont 1 labelsize 13 + code0 {o->value(1);} + } + Fl_Light_Button clearbutton { + label CLEAR + callback {if (o->value()>0.5) mode=3; +removeselection();} + xywh {221 534 99 30} type Radio down_box THIN_DOWN_BOX selection_color 0 labeltype ENGRAVED_LABEL labelfont 1 labelsize 13 + code0 {if (bank->locked()) o->deactivate();} + } + Fl_Light_Button swapbutton { + label SWAP + callback {if (o->value()>0.5) mode=4; +removeselection();} + xywh {325 534 99 30} type Radio down_box THIN_DOWN_BOX selection_color 227 labeltype ENGRAVED_LABEL labelfont 1 labelsize 13 + code0 {if (bank->locked()) o->deactivate();} + } + } + Fl_Button {} { + label {New Bank...} + callback {const char *dirname; + +dirname=fl_input("New empty Bank:"); +if (dirname==NULL) return; + + +int result=bank->newbank(dirname); + +if (result!=0) fl_alert("Error: Could not make a new bank (directory).."); + +refreshmainwindow();} + xywh {685 5 93 25} labelfont 1 labelsize 11 align 128 + } + Fl_Check_Button {} { + label {auto close} + callback {config.cfg.BankUIAutoClose=(int) o->value();} + tooltip {automatically close the bank window if the instrument is loaded} xywh {705 529 60 15} down_box DOWN_BOX labelsize 10 + code0 {o->value(config.cfg.BankUIAutoClose);} + } + Fl_Choice banklist { + callback {int n=o->value(); +std::string dirname=bank->banks[n].dir; +if (dirname.empty()) return; + +if (bank->loadbank(dirname)==2) + fl_alert("Error: Could not load the bank from the directory\\n%s.",dirname.c_str()); +for (int i=0;irefresh(); +refreshmainwindow();} + xywh {5 8 220 20} down_box BORDER_BOX labelfont 1 align 0 textfont 1 textsize 11 + } {} + Fl_Button {} { + label {Refresh bank list} + callback {rescan_for_banks(); +banklist->value(0);} + tooltip {Refresh the bank list (rescan)} xywh {230 8 105 20} box THIN_UP_BOX color 50 labelsize 11 + } + Fl_Check_Button {} { + label {Show PADsynth status} + callback {config.cfg.CheckPADsynth=(int) o->value(); +refreshmainwindow();} + xywh {435 530 150 15} down_box DOWN_BOX labelsize 11 + code0 {o->value(config.cfg.CheckPADsynth);} + } + } + } + Function {BankUI(Master *master_,int *npart_)} {} { + code {fnc=&BankProcess_::process; +master=master_; +npart=npart_; +bank=&master_->bank; +what=0; +nselected=-1; +make_window(); +mode=1;} {} + } + Function {~BankUI()} {return_type virtual + } { + code {bankuiwindow->hide(); +delete(bankuiwindow);} {} + } + Function {show()} {} { + code {bankuiwindow->show(); +simplesetmode(config.cfg.UserInterfaceMode==2);} {} + } + Function {hide()} {} { + code {bankuiwindow->hide();} {} + } + Function {init(Fl_Valuator *cbwig_)} {} { + code {cbwig=cbwig_; +rescan_for_banks();} {} + } + Function {process()} {return_type void + } { + code {int slot=this->slot; + +if ((what==2)&&(bank->emptyslot(slot)==0)&&(mode!=4)) {//Rename slot + const char *tmp=fl_input("Slot (instrument) name:",bank->getname(slot).c_str()); + if (tmp!=NULL) bank->setname(slot,tmp,-1); + bs[slot]->refresh(); +}; + +if ((what==1)&&(mode==1)&&(!bank->emptyslot(slot))){//Reads from slot + pthread_mutex_lock(&master->part[*npart]->load_mutex); + bank->loadfromslot(slot,master->part[*npart]); + pthread_mutex_unlock(&master->part[*npart]->load_mutex); + master->part[*npart]->applyparameters(); + snprintf((char *)master->part[*npart]->Pname,PART_MAX_NAME_LEN,"%s",bank->getname(slot).c_str()); + cbwig->do_callback(); + + if (config.cfg.BankUIAutoClose!=0) + bankuiwindow->hide(); + +}; + +if ((what==1)&&(mode==2)){//save(write) to slot + if (!bank->emptyslot(slot)){ + if (!fl_choice("Overwrite the slot no. %d ?","No","Yes",NULL,slot+1)) goto nooverwriteslot; + }; + pthread_mutex_lock(&master->part[*npart]->load_mutex); + bank->savetoslot(slot,master->part[*npart]); + pthread_mutex_unlock(&master->part[*npart]->load_mutex); + + bs[slot]->refresh(); + mode=1;readbutton->value(1);writebutton->value(0); + nooverwriteslot:; +}; + + + +if ((what==1)&&(mode==3)&&(!bank->emptyslot(slot))){//Clears the slot + if (fl_choice("Clear the slot no. %d ?","No","Yes",NULL,slot+1)){ + bank->clearslot(slot); + bs[slot]->refresh(); + }; +}; + +if (mode==4){//swap + bool done=false; + if ((what==1)&&(nselected>=0)){ + bank->swapslot(nselected,slot); + int ns=nselected; + nselected=-1; + bs[slot]->refresh(); + bs[ns]->refresh(); + done=true; + }; + if (((nselected<0)||(what==2))&&(!done)){ + int ns=nselected; + nselected=slot; + if (ns>0) bs[ns]->refresh(); + bs[slot]->refresh(); + }; +}; +if (mode!=4) refreshmainwindow();} {} + } + Function {refreshmainwindow()} {} { + code {bankuiwindow->label(bank->bankfiletitle.c_str()); +mode=1;readbutton->value(1);writebutton->value(0);clearbutton->value(0);swapbutton->value(0); +nselected=-1; +if (bank->locked()){ + writebutton->deactivate(); + clearbutton->deactivate(); + swapbutton->deactivate(); +} else { + writebutton->activate(); + clearbutton->activate(); + swapbutton->activate(); +}; +for (int i=0;irefresh();} {} + } + Function {removeselection()} {} { + code {if (nselected>=0) { + int ns=nselected; + nselected=-1; + bs[ns]->refresh(); +};} {} + } + Function {rescan_for_banks()} {} { + code {banklist->clear(); +bank->rescanforbanks(); + +for (unsigned int i=0;ibanks.size();i++) { + banklist->add(bank->banks[i].name.c_str()); +} +if (banklist->size() == 0) + banklist->add(" ");} {} + } + Function {simplesetmode(bool beginnerui)} {} { + code {readbutton->value(1); +mode=1; +removeselection(); +if (beginnerui) modeselect->hide(); + else modeselect->show();} {} + } + decl {BankSlot *bs[BANK_SIZE];} {} + decl {int slot,what;//"what"=what button is pressed} {} + decl {int mode,*npart,nselected;} {} + decl {Master *master;} {} + decl {void (BankProcess_::* fnc)(void);} {} + decl {Fl_Valuator *cbwig;} {public + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/CMakeLists.txt b/plugins/zynaddsubfx/zynaddsubfx/src/UI/CMakeLists.txt new file mode 100644 index 000000000..38ea1e113 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/CMakeLists.txt @@ -0,0 +1,45 @@ +set(UI_fl_files + ADnoteUI.fl + BankUI.fl + ConfigUI.fl + EffUI.fl + EnvelopeUI.fl + FilterUI.fl + LFOUI.fl + MasterUI.fl + MicrotonalUI.fl + OscilGenUI.fl + PADnoteUI.fl + PartUI.fl + PresetsUI.fl + ResonanceUI.fl + SUBnoteUI.fl + VirKeyboard.fl +) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +set_source_files_properties(UI/MasterUI.h PROPERTIES GENERATED 1) +fltk_wrap_ui(zynaddsubfx_gui ${UI_fl_files}) + +add_definitions(-DPIXMAP_PATH="${CMAKE_INSTALL_PREFIX}/share/zynaddsubfx/pixmaps/") +add_definitions(-DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") + +if(LibloEnable) + set(zynaddsubfx_gui_FLTK_UI_SRCS ${zynaddsubfx_gui_FLTK_UI_SRCS} NSM.C NSM/Client.C) +endif() + +add_library(zynaddsubfx_gui STATIC + ${UI_objs} + ${zynaddsubfx_gui_FLTK_UI_SRCS} + NioUI.cpp + WidgetPDial.cpp + ) + +if(NtkGui) + target_link_libraries(zynaddsubfx_gui ${NTK_LIBRARIES}) +endif(NtkGui) + +if(FltkGui) + target_link_libraries(zynaddsubfx_gui ${FLTK_LIBRARIES}) +endif(FltkGui) diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/ConfigUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/ConfigUI.fl new file mode 100644 index 000000000..83dc2fd1d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/ConfigUI.fl @@ -0,0 +1,458 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0300 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {private local +} + +decl {//License: GNU GPL version 2 or later} {private local +} + +decl {\#include } {public local +} + +decl {\#include } {public local +} + +decl {\#include } {public local +} + +decl {\#include } {public local +} + +decl {\#include "../globals.h"} {public local +} + +decl {\#include "../Misc/Util.h"} {public local +} + +decl {\#include "../Misc/Dump.h"} {public local +} + +decl {extern Dump dump;} {public local +} + +class ConfigUI {} { + Function {make_window()} {} { + Fl_Window configwindow { + label {ZynAddSubFX Settings} + callback {writebankcfg(); +o->hide();} + xywh {554 443 510 370} type Double visible + } { + Fl_Tabs {} { + xywh {5 5 500 330} + } { + Fl_Group {} { + label {Main settings} + xywh {5 25 500 310} + } { + Fl_Group {} { + label {Sample Rate} + xywh {15 45 165 30} box ENGRAVED_FRAME + } { + Fl_Choice {} { + callback {if ((int)o->value()==0) samplerateinput->activate(); + else samplerateinput->deactivate(); + +int samplerates[8]={44100,16000,22050,32000,44100,48000,88200,96000}; +config.cfg.SampleRate=samplerates[(int)o->value()]; + +setsamplerateinput();} + xywh {20 50 85 20} down_box BORDER_BOX textsize 10 + code0 {o->value(getsamplerateorder());} + } { + MenuItem {} { + label Custom + xywh {10 10 100 20} labelfont 1 + } + MenuItem {} { + label 16000Hz + xywh {30 30 100 20} labelfont 1 + } + MenuItem {} { + label 22050Hz + xywh {20 20 100 20} labelfont 1 + } + MenuItem {} { + label 32000Hz + xywh {30 30 100 20} labelfont 1 + } + MenuItem {} { + label 44100Hz + xywh {40 40 100 20} labelfont 1 + } + MenuItem {} { + label 48000Hz + xywh {50 50 100 20} labelfont 1 + } + MenuItem {} { + label 88200Hz + xywh {60 60 100 20} labelfont 1 + } + MenuItem {} { + label 96000Hz + xywh {70 70 100 20} labelfont 1 + } + } + Fl_Input samplerateinput { + callback {char *tmp; +config.cfg.SampleRate=strtoul(o->value(),&tmp,10);} + xywh {115 50 60 20} type Int textfont 1 + code0 {setsamplerateinput();} + code1 {if (getsamplerateorder()!=0) o->deactivate();} + } + } + Fl_Input {} { + label {Buffer Size} + callback {char *tmp; +config.cfg.SoundBufferSize=strtoul(o->value(),&tmp,10);} + tooltip {Internal Sound Buffer Size (samples)} xywh {190 45 60 20} type Int labelsize 11 align 129 textfont 1 + code0 {char *tmpbuf=new char[100];o->cut(0,o->maximum_size());} + code1 {snprintf(tmpbuf,100,"%d",config.cfg.SoundBufferSize);o->insert(tmpbuf);} + code2 {delete []tmpbuf;} + } + Fl_Light_Button {} { + label {Swap Stereo } + callback {config.cfg.SwapStereo=(int) o->value();} + xywh {20 80 85 20} box THIN_UP_BOX labelsize 10 + code0 {o->value(config.cfg.SwapStereo);} + } + Fl_Choice {} { + label OscilSize + callback {config.cfg.OscilSize=128<value();} + tooltip {ADSynth Oscillator Size (samples)} xywh {175 80 75 20} down_box BORDER_BOX labelfont 1 labelsize 11 textsize 10 + code0 {o->value( (int) (log(config.cfg.OscilSize/128.0-1.0)/log(2)) +1);} + } { + MenuItem {} { + label 128 + xywh {25 25 100 20} labelfont 1 + } + MenuItem {} { + label 256 + xywh {35 35 100 20} labelfont 1 + } + MenuItem {} { + label 512 + xywh {45 45 100 20} labelfont 1 + } + MenuItem {} { + label 1024 + xywh {45 45 100 20} labelfont 1 + } + MenuItem {} { + label 2048 + xywh {55 55 100 20} labelfont 1 + } + MenuItem {} { + label 4096 + xywh {55 55 100 20} labelfont 1 + } + MenuItem {} { + label 8192 + xywh {65 65 100 20} labelfont 1 + } + MenuItem {} { + label 16384 + xywh {75 75 100 20} labelfont 1 + } + } + Fl_Box {} { + label {Most settings has effect only after ZynAddSubFX is restarted.} + xywh {10 300 235 30} labelfont 1 labelsize 11 align 128 + } + Fl_Box {} { + label {Read the Readme.txt for other settings} + xywh {10 280 240 15} labelfont 1 labelsize 11 align 128 + } + Fl_Group {} { + xywh {15 125 230 85} box ENGRAVED_BOX + } { + Fl_File_Input {} { + label {Dump File} + callback {config.cfg.DumpFile = o->value();} + xywh {20 170 220 35} align 5 + code0 {o->insert(config.cfg.DumpFile.c_str());} + } + Fl_Check_Button {} { + label {Dump notes} + callback {config.cfg.DumpNotesToFile=(int) o->value(); +dump.startnow();//this has effect only if this option was disabled} + xywh {20 130 100 20} down_box DOWN_BOX + code0 {o->value(config.cfg.DumpNotesToFile);} + } + Fl_Check_Button {} { + label Append + callback {config.cfg.DumpAppend=(int) o->value();} + xywh {160 130 80 20} down_box DOWN_BOX + code0 {o->value(config.cfg.DumpAppend);} + } + } + Fl_Group {} { + xywh {255 45 245 260} box ENGRAVED_FRAME + } { + Fl_Box {} { + label {Note: Not all the following settings are used (this depends on the operating system, etc..)} + xywh {260 50 235 45} labelfont 1 labelsize 11 align 128 + } + Fl_Group {} { + label Linux + xywh {260 110 235 115} box ENGRAVED_BOX labelfont 1 labelsize 13 align 5 + } { + Fl_File_Input {} { + label {OSS Sequencer Device (/dev/...)} + callback {snprintf(config.cfg.LinuxOSSSeqInDev,config.maxstringsize,"%s",o->value());} + xywh {265 180 225 35} align 5 + code0 {o->insert(config.cfg.LinuxOSSSeqInDev);} + } + Fl_File_Input {} { + label {OSS Wave Out Device (/dev/...)} + callback {snprintf(config.cfg.LinuxOSSWaveOutDev,config.maxstringsize,"%s",o->value());} + xywh {265 130 225 35} align 5 + code0 {o->insert(config.cfg.LinuxOSSWaveOutDev);} + } + } + Fl_Group {} { + label Windows + xywh {260 250 235 50} box ENGRAVED_BOX labelfont 1 labelsize 13 align 5 + } { + Fl_Counter {} { + label {Midi In Dev} + callback {config.cfg.WindowsMidiInId=(int) o->value(); +midiinputnamebox->label(config.winmididevices[config.cfg.WindowsMidiInId].name);} + xywh {270 270 65 20} type Simple labelsize 11 align 1 minimum 0 maximum 100 step 1 + code0 {o->maximum(config.winmidimax-1);} + code1 {o->value(config.cfg.WindowsMidiInId);} + } + Fl_Box midiinputnamebox { + label {Midi input device name} + xywh {340 260 150 35} labelfont 1 labelsize 11 align 212 + code0 {o->label(config.winmididevices[config.cfg.WindowsMidiInId].name);} + } + } + } + Fl_Counter {} { + label {XML compression level} + callback {config.cfg.GzipCompression=(int) o->value();} + tooltip {gzip compression level (0 - uncompressed)} xywh {20 215 65 15} type Simple labelsize 11 align 8 minimum 0 maximum 9 step 1 + code0 {o->value(config.cfg.GzipCompression);} + } + Fl_Choice {} { + label {PADsynth Interpolation} + callback {config.cfg.Interpolation=(int) o->value();} + xywh {175 105 75 15} down_box BORDER_BOX labelsize 10 textsize 11 + code0 {o->value(config.cfg.Interpolation);} + } { + MenuItem {} { + label {Linear(fast)} + xywh {0 0 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Cubic(slow)} + xywh {10 10 100 20} labelfont 1 labelsize 10 + } + } + Fl_Choice {} { + label {Virtual Keyboard Layout} + callback {config.cfg.VirKeybLayout=(int) o->value();;} + xywh {155 235 85 20} down_box BORDER_BOX labelsize 12 textfont 1 textsize 11 + code0 {o->value(config.cfg.VirKeybLayout);} + } { + MenuItem {} { + label { } + xywh {5 5 100 20} labelfont 1 labelsize 11 deactivate + } + MenuItem {} { + label QWERTY + xywh {15 15 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Dvorak + xywh {25 25 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label QWERTZ + xywh {35 35 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label AZERTY + xywh {45 45 100 20} labelfont 1 labelsize 11 + } + } + Fl_Check_Button {} { + label {Ignore MIDI Program Change} + callback {config.cfg.IgnoreProgramChange=(int) o->value();} + xywh {10 255 230 20} down_box DOWN_BOX + code0 {o->value(config.cfg.IgnoreProgramChange);} + } + } + Fl_Group {} { + label {Bank root dirs} + xywh {5 25 500 285} hide + } { + Fl_Browser rootsbrowse { + callback {activatebutton_rootdir(o->value()!=0);} + xywh {15 35 485 220} type Hold + } + Fl_Button {} { + label {Add root directory...} + callback {const char *dirname; +dirname=fl_dir_chooser("Add a root directory for banks:",NULL,0); +if (dirname==NULL) return; + +rootsbrowse->add(dirname);} + xywh {15 265 80 35} box THIN_UP_BOX align 128 + } + Fl_Button removerootdirbutton { + label {Remove root dir...} + callback {if (rootsbrowse->value()!=0) { + rootsbrowse->remove(rootsbrowse->value()); +}; +activatebutton_rootdir(false);} + xywh {105 265 80 35} box THIN_UP_BOX align 128 + code0 {o->deactivate();} + } + Fl_Button makedefaultrootdirbutton { + label {Make default} + callback {int n=rootsbrowse->value(); + +if (n!=0) { + rootsbrowse->move(1,n); + rootsbrowse->value(1); + rootsbrowse->redraw(); +}; +activatebutton_rootdir(true);} + xywh {190 265 80 35} box THIN_UP_BOX align 128 + code0 {o->deactivate();} + } + } + Fl_Group {} { + label {Presets dirs} + xywh {5 25 500 285} hide + } { + Fl_Browser presetbrowse { + callback {activatebutton_presetdir(o->value()!=0);} + xywh {15 35 485 220} type Hold + } + Fl_Button {} { + label {Add preset directory...} + callback {const char *dirname; +dirname=fl_dir_chooser("Add a preset directory :",NULL,0); +if (dirname==NULL) return; + +presetbrowse->add(dirname);} + xywh {15 265 80 35} box THIN_UP_BOX align 128 + } + Fl_Button removepresetbutton { + label {Remove preset dir...} + callback {if (presetbrowse->value()!=0) { + presetbrowse->remove(presetbrowse->value()); +}; +activatebutton_presetdir(false);} + xywh {105 265 80 35} box THIN_UP_BOX align 128 + code0 {o->deactivate();} + } + Fl_Button makedefaultpresetbutton { + label {Make default} + callback {int n=presetbrowse->value(); + +if (n!=0) { + presetbrowse->move(1,n); + presetbrowse->value(1); + presetbrowse->redraw(); +}; +activatebutton_presetdir(true);} + xywh {190 265 80 35} box THIN_UP_BOX align 128 + code0 {o->deactivate();} + } + } + } + Fl_Button {} { + label Close + callback {configwindow->hide(); +writebankcfg(); +writepresetcfg();} + xywh {200 345 105 20} box THIN_UP_BOX + } + } + } + Function {ConfigUI()} {} { + code {make_window(); +readbankcfg(); +readpresetcfg();} {} + } + Function {activatebutton_rootdir(bool active)} {} { + code {if (active) { + removerootdirbutton->activate(); + makedefaultrootdirbutton->activate(); +}else{ + removerootdirbutton->deactivate(); + makedefaultrootdirbutton->deactivate(); +};} {} + } + Function {activatebutton_presetdir(bool active)} {} { + code {if (active) { + removepresetbutton->activate(); + makedefaultpresetbutton->activate(); +}else{ + removepresetbutton->deactivate(); + makedefaultpresetbutton->deactivate(); +};} {} + } + Function {readbankcfg()} {} { + code {rootsbrowse->clear(); + +for (int i=0;iadd(config.cfg.bankRootDirList[i].c_str()); +};} {} + } + Function {writebankcfg()} {} { + code {config.clearbankrootdirlist(); + +for (int n=0;nsize();n++){ + config.cfg.bankRootDirList[n] = rootsbrowse->text(n+1); +};} {} + } + Function {readpresetcfg()} {} { + code {presetbrowse->clear(); + +for(int i=0;iadd(config.cfg.presetsDirList[i].c_str()); +};} {} + } + Function {writepresetcfg()} {} { + code {config.clearpresetsdirlist(); + +for (int n=0;nsize();n++) + config.cfg.presetsDirList[n] = presetbrowse->text(n+1);} {} + } + Function {getsamplerateorder()} {return_type int + } { + code {int smpr=config.cfg.SampleRate; +int order=0; +switch(smpr){ + case 16000:order=1;break; + case 22050:order=2;break; + case 32000:order=3;break; + case 44100:order=4;break; + case 48000:order=5;break; + case 88200:order=6;break; + case 96000:order=7;break; + default:order=0;break; +}; +return(order);} {} + } + Function {setsamplerateinput()} {return_type void + } { + code {char *tmpbuf=new char[100]; +samplerateinput->cut(0,samplerateinput->maximum_size()); +snprintf(tmpbuf,100,"%d",config.cfg.SampleRate); +samplerateinput->insert(tmpbuf); +delete []tmpbuf;} {} + } + Function {show()} {} { + code {configwindow->show();} {} + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/EffUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/EffUI.fl new file mode 100644 index 000000000..3df5093a1 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/EffUI.fl @@ -0,0 +1,2351 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "../globals.h"} {public +} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include "EnvelopeUI.h"} {public +} + +decl {\#include "FilterUI.h"} {public +} + +decl {\#include "../Misc/Util.h"} {public +} + +decl {\#include "../Effects/EffectMgr.h"} {public +} + +decl {\#include "PresetsUI.h"} {public +} + +decl {\#include "common.H"} {public +} + +class EQGraph {: {public Fl_Box} +} { + Function {EQGraph(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {} { + code {eff=NULL; +maxdB=30;} {} + } + Function {init(EffectMgr *eff_)} {} { + code {eff=eff_; +oldx=-1; +khzval=-1;} {} + } + Function {draw_freq_line(float freq,int type)} {} { + code {fl_color(FL_GRAY); +float freqx=getfreqpos(freq); +switch(type){ + case 0:if (active_r()) fl_color(FL_WHITE); + else fl_color(205,205,205); + fl_line_style(FL_SOLID); + break; + case 1:fl_line_style(FL_DOT);break; + case 2:fl_line_style(FL_DASH);break; +}; + + +if ((freqx>0.0)&&(freqx<1.0)) + fl_line(x()+(int) (freqx*w()),y(), + x()+(int) (freqx*w()),y()+h());} {} + } + Function {draw()} {} { + code {int ox=x(),oy=y(),lx=w(),ly=h(),i; + double iy,oiy; +float freqx; + +if (active_r()) fl_color(fl_darker(FL_GRAY)); + else fl_color(FL_GRAY); +fl_rectf(ox,oy,lx,ly); + + +//draw the lines +fl_color(fl_lighter( FL_GRAY)); + +fl_line_style(FL_SOLID); +fl_line(ox+2,oy+ly/2,ox+lx-2,oy+ly/2); + +freqx=getfreqpos(1000.0); +if ((freqx>0.0)&&(freqx<1.0)) + fl_line(ox+(int) (freqx*lx),oy, + ox+(int) (freqx*lx),oy+ly); + +for (i=1;i<10;i++){ + if(i==1){ + draw_freq_line(i*100.0,0); + draw_freq_line(i*1000.0,0); + }else + if (i==5){ + draw_freq_line(i*10.0,2); + draw_freq_line(i*100.0,2); + draw_freq_line(i*1000.0,2); + }else{ + draw_freq_line(i*10.0,1); + draw_freq_line(i*100.0,1); + draw_freq_line(i*1000.0,1); + }; +}; + +draw_freq_line(10000.0,0); +draw_freq_line(20000.0,1); + + +fl_line_style(FL_DOT); +int GY=6;if (lysynth->samplerate/2) break; + iy=getresponse(ly,frq); + if ((oiy>=0) && (oiy=0) && (iygetEQfreqresponse(freq); +int idbresp=(int) ((dbresp/maxdB+1.0)*maxy/2.0); + + +//fprintf(stderr,"%.5f\\n",(dbresp/maxdB+1.0)*maxy/2.0); + + +return(idbresp);} {} + } + Function {getfreqx(float x)} {return_type float + } { + code {if (x>1.0) x=1.0; +return(20.0*pow((float)1000.0,x));} {} + } + Function {getfreqpos(float freq)} {return_type float + } { + code {if (freq<0.00001) freq=0.00001; +return(log(freq/20.0)/log(1000.0));} {} + } + decl {int oldx;} {} + decl {float khzval;} {public + } + decl {EffectMgr *eff;} {} + decl {int maxdB;} {} +} + +class EffUI {open : {public Fl_Group,public PresetsUI_} +} { + Function {EffUI(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {eff=NULL; +filterwindow=NULL;} {} + } + Function {~EffUI()} {} { + code {effnullwindow->hide();//delete (effnullwindow); +effreverbwindow->hide();//delete (effreverbwindow); +effechowindow->hide();//delete (effechowindow); +effchoruswindow->hide();//delete (effchoruswindow); +effphaserwindow->hide();//delete (effphaserwindow); +effalienwahwindow->hide();//delete (effalienwahwindow); +effdistorsionwindow->hide();//delete (effdistorsionwindow); +effeqwindow->hide();//delete (effeqwindow); +effdynamicfilterwindow->hide();//delete (effdynamicfilterwindow); + +if (filterwindow!=NULL){ + filterwindow->hide(); + delete(filterwindow); +};} {} + } + Function {make_null_window()} {open + } { + Fl_Window effnullwindow { + label {No Effect} + xywh {612 881 380 100} type Double box UP_BOX color 221 labelfont 1 labelsize 19 align 16 + code0 {set_module_parameters(o);} + class Fl_Group visible + } {} + } + Function {make_reverb_window()} {open + } { + Fl_Window effreverbwindow { + label Reverb open + xywh {377 636 380 100} type Double box UP_BOX color 221 labelfont 1 labelsize 19 align 25 + code0 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice revp { + label Preset + callback {eff->changepreset((int)o->value()); + +refresh(eff);} + xywh {10 15 90 15} box UP_BOX down_box BORDER_BOX color 14 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label {Cathedral 1} + xywh {10 10 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Cathedral 2} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Cathedral 3} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Hall 1} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Hall 2} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Room 1} + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Room 2} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Basement + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Tunnel + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Echoed 1} + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Echoed 2} + xywh {110 110 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Very Long 1} + xywh {120 120 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Very Long 2} + xywh {130 130 100 20} labelfont 1 labelsize 10 + } + } + Fl_Choice revp10 { + label Type + callback {eff->seteffectpar(10,(int) o->value()); +if (eff->geteffectpar(10)==2) revp12->activate(); + else revp12->deactivate();} + xywh {110 15 85 15} down_box BORDER_BOX color 14 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label Random + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Freeverb + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Bandwidth + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial revp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial revp1 { + label Pan + callback {eff->seteffectpar(1,(int) o->value());} + xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial revp2 { + label Time + callback {eff->seteffectpar(2,(int) o->value());} + tooltip {Duration of Effect} xywh {80 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial revp3 { + label {I.del} + callback {eff->seteffectpar(3,(int) o->value());} + tooltip {Initial Delay} xywh {120 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Dial revp4 { + label {I.delfb} + callback {eff->seteffectpar(4,(int) o->value());} + tooltip {Initial Delay Feedback} xywh {155 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial revp12 { + label bw + callback {eff->seteffectpar(12,(int) o->value());} + xywh {200 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 deactivate + code0 {if (eff->geteffectpar(10)==2) o->activate();} + class WidgetPDial + } + Fl_Dial revp6 { + label {E/R} + callback {eff->seteffectpar(6,(int) o->value());} + xywh {235 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 deactivate + class WidgetPDial + } + Fl_Dial revp7 { + label LPF + callback {eff->seteffectpar(7,(int) o->value());} + tooltip {Low Pass Filter} xywh {270 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial revp8 { + label HPF + callback {eff->seteffectpar(8,(int) o->value());} + tooltip {High Pass Filter} xywh {305 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial revp9 { + label Damp + callback {eff->seteffectpar(9,(int) o->value());} + tooltip Dampening xywh {340 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 minimum 64 maximum 127 step 1 + class WidgetPDial + } + Fl_Dial revp11 { + label {R.S.} + callback {int x=64; +if (Fl::event_button1()) x=(int)o->value(); + else o->value(x); +eff->seteffectpar(11,x);} + tooltip RoomSize xywh {200 10 25 25} box ROUND_UP_BOX labelfont 1 labelsize 8 align 8 minimum 1 maximum 127 step 1 + class WidgetPDial + } + } + } + Function {make_echo_window()} {open + } { + Fl_Window effechowindow { + label Echo + xywh {897 611 380 100} type Double box UP_BOX color 221 labelfont 1 labelsize 19 align 25 + code0 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice echop { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {11 15 95 15} box UP_BOX down_box BORDER_BOX color 14 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label {Echo 1} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Echo 2} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Echo 3} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Simple Echo} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Canyon + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Panning Echo 1} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Panning Echo 2} + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Panning Echo 3} + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Feedback Echo} + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial echop0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial echop1 { + label Pan + callback {eff->seteffectpar(1,(int) o->value());} + xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial echop2 { + label Delay + callback {eff->seteffectpar(2,(int) o->value());} + xywh {80 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Dial echop3 { + label {LRdl.} + callback {eff->seteffectpar(3,(int) o->value());} + tooltip {Delay Between L/R} xywh {120 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Dial echop4 { + label {LRc.} + callback {eff->seteffectpar(4,(int) o->value());} + tooltip {L/R Crossover} xywh {155 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial echop5 { + label {Fb.} + callback {eff->seteffectpar(5,(int) o->value());} + tooltip Feedback xywh {195 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial echop6 { + label Damp + callback {eff->seteffectpar(6,(int) o->value());} + tooltip Dampening xywh {235 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + } + } + Function {make_chorus_window()} {open + } { + Fl_Window effchoruswindow { + label Chorus open + xywh {467 742 380 100} type Double box UP_BOX color 221 labelfont 1 labelsize 19 align 25 + code0 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice chorusp { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {10 15 90 15} box UP_BOX down_box BORDER_BOX color 14 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label {Chorus 1} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Chorus 2} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Chorus 3} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Celeste 1} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Celeste 2} + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Flange 1} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Flange 2} + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Flange 3} + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Flange 4} + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Flange 5} + xywh {110 110 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial chorusp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp1 { + label Pan + callback {eff->seteffectpar(1,(int) o->value());} + xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp2 { + label Freq + callback {eff->seteffectpar(2,(int) o->value());} + tooltip {LFO Frequency} xywh {85 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp3 { + label Rnd + callback {eff->seteffectpar(3,(int) o->value());} + tooltip {LFO Randomness} xywh {120 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp5 { + label {St.df} + callback {eff->seteffectpar(5,(int) o->value());} + tooltip {L/R Phase Shift} xywh {200 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp6 { + label Dpth + callback {eff->seteffectpar(6,(int) o->value());} + tooltip {LFO Depth} xywh {235 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp7 { + label Delay + callback {eff->seteffectpar(7,(int) o->value());} + xywh {270 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp8 { + label Fb + callback {eff->seteffectpar(8,(int) o->value());} + tooltip Feedback xywh {305 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp9 { + label {L/R} + callback {eff->seteffectpar(9,(int) o->value());} + tooltip {Channel Routing} xywh {340 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Check_Button {} { + label Flange + callback {eff->seteffectpar(10,(int) o->value());} + xywh {120 10 55 20} box THIN_UP_BOX down_box DOWN_BOX color 230 labelfont 1 labelsize 10 hide deactivate + code0 {o->value(eff->geteffectpar(10));} + } + Fl_Check_Button chorusp11 { + label Substract + callback {eff->seteffectpar(11,(int) o->value());} + tooltip {inverts the output} xywh {185 10 70 20} box THIN_UP_BOX down_box DOWN_BOX color 51 labelsize 10 + } + Fl_Choice chorusp4 { + label {LFO type} + callback {eff->seteffectpar(4,(int) o->value());} + tooltip {LFO function} xywh {155 50 40 15} down_box BORDER_BOX labelfont 1 labelsize 10 align 130 textsize 8 + } { + MenuItem {} { + label SINE + xywh {15 15 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label TRI + xywh {25 25 100 20} labelfont 1 labelsize 10 + } + } + } + } + Function {make_phaser_window()} {open + } { + Fl_Window effphaserwindow { + label Phaser open + xywh {101 232 380 95} type Double box UP_BOX color 221 labelfont 1 labelsize 19 align 25 + code0 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice phaserp { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {10 15 100 15} box UP_BOX down_box BORDER_BOX color 14 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label {Phaser 1} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Phaser 2} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Phaser 3} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Phaser 4} + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Phaser 5} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Phaser 6} + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {APhaser 1} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {APhaser 2} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {APhaser 3} + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {APhaser 4} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {APhaser 5} + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {APhaser 6} + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial phaserp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial phaserp1 { + label Pan + callback {eff->seteffectpar(1,(int) o->value());} + xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial phaserp2 { + label Freq + callback {eff->seteffectpar(2,(int) o->value());} + tooltip {LFO frequency} xywh {85 45 25 25} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial phaserp3 { + label Rnd + callback {eff->seteffectpar(3,(int) o->value());} + tooltip {LFO randomness} xywh {120 45 25 25} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Choice phaserp4 { + label LFO + callback {eff->seteffectpar(4,(int) o->value());} + tooltip {LFO function} xywh {245 55 40 15} down_box BORDER_BOX labelfont 1 labelsize 10 align 130 textsize 8 + } { + MenuItem {} { + label SIN + xywh {15 15 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label TRI + xywh {25 25 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial phaserp5 { + label {St.df} + callback {eff->seteffectpar(5,(int) o->value());} + tooltip {Left/Right Channel Phase Shift} xywh {155 45 25 25} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial phaserp6 { + label Dpth + callback {eff->seteffectpar(6,(int) o->value());} + tooltip {LFO Depth} xywh {120 5 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 maximum 127 + class WidgetPDial + } + Fl_Dial phaserp7 { + label Fb + callback {eff->seteffectpar(7,(int) o->value());} + tooltip Feedback xywh {185 45 25 25} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Counter phaserp8 { + label Stages + callback {eff->seteffectpar(8,(int) o->value());} + xywh {290 55 35 15} type Simple labelfont 1 labelsize 11 minimum 0 maximum 127 step 1 + code0 {o->range(1,MAX_PHASER_STAGES);} + } + Fl_Dial phaserp9 { + label {L/R} + callback {eff->seteffectpar(9,(int) o->value());} + tooltip {Channel Routing} xywh {215 45 25 25} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Check_Button phaserp10 { + label Substract + callback {eff->seteffectpar(10,(int) o->value());} selected + tooltip {inverts output} xywh {200 10 74 20} box THIN_UP_BOX down_box DOWN_BOX color 51 labelfont 1 labelsize 10 + } + Fl_Dial phaserp11 { + label Phase + callback {eff->seteffectpar(11,(int) o->value());} + xywh {155 5 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 maximum 127 + class WidgetPDial + } + Fl_Check_Button phaserp12 { + label {hyp.} + callback {eff->seteffectpar(12,(int) o->value());} + tooltip hyper xywh {245 35 55 15} down_box DOWN_BOX + } + Fl_Dial phaserp13 { + label dist + callback {eff->seteffectpar(13,(int) o->value());} + tooltip Distortion xywh {340 50 25 25} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Check_Button phaserp14 { + label Analog + callback {eff->seteffectpar(14,(int) o->value());} + xywh {305 35 70 15} down_box DOWN_BOX + } + } + } + Function {make_alienwah_window()} {open + } { + Fl_Window effalienwahwindow { + label AlienWah + xywh {253 353 380 100} type Double box UP_BOX color 221 labelfont 1 labelsize 19 align 25 + code0 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice awp { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {10 15 90 15} box UP_BOX down_box BORDER_BOX color 14 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label {Alienwah 1} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Alienwah 2} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Alienwah 3} + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Alienwah 4} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial awp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial awp1 { + label Pan + callback {eff->seteffectpar(1,(int) o->value());} + xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial awp2 { + label Freq + callback {eff->seteffectpar(2,(int) o->value());} + tooltip {LFO Frequency} xywh {85 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial awp3 { + label Rnd + callback {eff->seteffectpar(3,(int) o->value());} + tooltip {LFO Randomness} xywh {120 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Dial awp5 { + label {St.df} + callback {eff->seteffectpar(5,(int) o->value());} + tooltip {Left/Right Channel Phase Shift} xywh {200 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial awp6 { + label Dpth + callback {eff->seteffectpar(6,(int) o->value());} + tooltip Depth xywh {235 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial awp7 { + label Fb + callback {eff->seteffectpar(7,(int) o->value());} + tooltip Feedback xywh {270 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial awp9 { + label {L/R} + callback {eff->seteffectpar(9,(int) o->value());} + xywh {345 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Choice awp4 { + label {LFO type} + callback {eff->seteffectpar(4,(int) o->value());} + tooltip {LFO function} xywh {155 50 40 15} down_box BORDER_BOX labelfont 1 labelsize 10 align 130 textsize 8 + } { + MenuItem {} { + label SINE + xywh {15 15 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label TRI + xywh {25 25 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial awp10 { + label Phase + callback {eff->seteffectpar(10,(int) o->value());} + xywh {160 5 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Counter awp8 { + label Delay + callback {eff->seteffectpar(8,(int) o->value());} + xywh {305 55 35 15} type Simple labelfont 1 labelsize 11 minimum 0 maximum 127 step 1 + code0 {o->range(1,MAX_ALIENWAH_DELAY);} + } + } + } + Function {make_distorsion_window()} {open + } { + Fl_Window effdistorsionwindow { + label Distortion open + xywh {544 217 380 100} type Double box UP_BOX color 221 labelfont 1 labelsize 19 align 25 + code0 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice distp { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {11 15 95 15} box UP_BOX down_box BORDER_BOX color 14 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label {Overdrive 1} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Overdrive 2} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {A. Exciter 1} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {A. Exciter 2} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Guitar Amp} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Quantisize + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial distp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial distp1 { + label Pan + callback {eff->seteffectpar(1,(int) o->value());} + xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial distp2 { + label {LRc.} + callback {eff->seteffectpar(2,(int) o->value());} + tooltip {L/R Mix} xywh {80 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Dial distp3 { + label Drive + callback {eff->seteffectpar(3,(int) o->value());} + tooltip {Input Amplification} xywh {120 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Dial distp4 { + label Level + callback {eff->seteffectpar(4,(int) o->value());} + tooltip {Output Amplification} xywh {155 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial distp7 { + label LPF + callback {eff->seteffectpar(7,(int) o->value());} + tooltip {Low Pass Filter} xywh {285 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial distp8 { + label HPF + callback {eff->seteffectpar(8,(int) o->value());} + tooltip {High Pass Filter} xywh {320 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Choice distp5 { + label Type + callback {eff->seteffectpar(5,(int) o->value());} + xywh {190 50 60 20} box UP_BOX down_box BORDER_BOX labelfont 1 labelsize 11 align 2 textsize 10 + } { + MenuItem {} { + label Atan + xywh {55 55 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Asym1 + xywh {65 65 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pow + xywh {75 75 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sine + xywh {85 85 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Qnts + xywh {95 95 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Zigzg + xywh {105 105 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Lmt + xywh {115 115 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LmtU + xywh {125 125 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LmtL + xywh {135 135 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label ILmt + xywh {147 147 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Clip + xywh {157 157 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Asym2 + xywh {75 75 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pow2 + xywh {85 85 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sgm + xywh {95 95 100 20} labelfont 1 labelsize 10 + } + } + Fl_Check_Button distp6 { + label {Neg.} + callback {eff->seteffectpar(6,(int) o->value());} + xywh {260 55 15 15} down_box DOWN_BOX labelfont 1 labelsize 11 align 2 + } + Fl_Check_Button distp9 { + label {St.} + callback {eff->seteffectpar(9,(int) o->value());} + tooltip Stereo xywh {355 60 15 15} down_box DOWN_BOX labelfont 1 labelsize 11 align 2 + } + Fl_Check_Button distp10 { + label PF + callback {eff->seteffectpar(10,(int) o->value());} + tooltip {Applies the filters(before or after) the distorsion} xywh {355 44 15 15} down_box DOWN_BOX labelfont 1 labelsize 11 align 1 + } + } + } + Function {make_eq_window()} {open + } { + Fl_Window effeqwindow { + label EQ open + xywh {682 881 380 100} type Double box UP_BOX color 221 labelfont 1 labelsize 19 align 25 + code0 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Dial eqp0 { + label Gain + callback {eff->seteffectpar(0,(int) o->value()); +eqgraph->redraw();} + xywh {10 35 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Counter bandcounter { + label {B.} + callback {eqband=(int) o->value(); +int npb=eqband*5+10; + +int type=eff->geteffectpar(npb); +typechoice->value(type); + +if (type>6) gaindial->activate(); + else gaindial->deactivate(); + +if (type==0) bandgroup->deactivate(); +else bandgroup->activate(); + +int freq=eff->geteffectpar(npb+1); +freqdial->value(freq); + +int gain=eff->geteffectpar(npb+2); +gaindial->value(gain); + +int q=eff->geteffectpar(npb+3); +qdial->value(q); + +int dbl=eff->geteffectpar(npb+4); +stagescounter->value(dbl);} + tooltip {Band no.} xywh {240 20 45 15} type Simple labelfont 1 labelsize 11 align 1 minimum 0 maximum 1 step 1 textfont 1 textsize 11 + code0 {o->bounds(0,MAX_EQ_BANDS-1);} + } + Fl_Group bandgroup { + xywh {245 40 130 50} box ENGRAVED_FRAME + code0 {if (eff->geteffectpar(10)==0) o->deactivate();} + } { + Fl_Dial freqdial { + label Freq + callback {int np=eqband*5+11; +eff->seteffectpar(np,(int) o->value()); +eqgraph->redraw();} + xywh {250 50 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 when 3 maximum 127 + class WidgetPDial + } + Fl_Dial gaindial { + label Gain + callback {int np=eqband*5+12; +eff->seteffectpar(np,(int) o->value()); +eqgraph->redraw();} + xywh {280 50 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 when 3 maximum 127 step 1 + class WidgetPDial + } + Fl_Dial qdial { + label Q + callback {int np=eqband*5+13; +eff->seteffectpar(np,(int) o->value()); +eqgraph->redraw();} + tooltip {Resonance/Bandwidth} xywh {310 50 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 when 3 maximum 127 + class WidgetPDial + } + Fl_Counter stagescounter { + label {St.} + callback {int np=eqband*5+14; +eff->seteffectpar(np,(int) o->value()); +eqgraph->redraw();} + tooltip {Additional filter stages} xywh {340 60 30 15} type Simple labelfont 1 labelsize 10 minimum 1 maximum 127 step 1 textfont 1 textsize 11 + code0 {o->bounds(0,MAX_FILTER_STAGES-1);} + } + } + Fl_Choice typechoice { + label {T.} + callback {int np=eqband*5+10; +eff->seteffectpar(np,(int) o->value()); +bandcounter->do_callback(); +eqgraph->redraw();} + tooltip Type xywh {290 20 40 15} down_box BORDER_BOX labelfont 1 labelsize 10 align 1 when 6 textsize 10 + } { + MenuItem {} { + label OFF + xywh {0 0 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Lp1 + xywh {10 10 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Hp1 + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Lp2 + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Hp2 + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Bp2 + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label N2 + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pk + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LSh + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label HSh + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + } + Fl_Box eqgraph { + xywh {45 10 190 75} box BORDER_BOX color 50 + code0 {o->init(eff);} + class EQGraph + } + } + } + Function {make_dynamicfilter_window()} {open + } { + Fl_Window effdynamicfilterwindow { + label DynFilter open + xywh {819 290 380 100} type Double box UP_BOX color 221 labelfont 1 labelsize 19 align 25 + code0 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice dfp { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {10 15 90 15} box UP_BOX down_box BORDER_BOX color 14 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label WahWah + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label AutoWah + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sweep + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label VocalMorph1 + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label VocalMorph2 + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial dfp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial dfp1 { + label Pan + callback {eff->seteffectpar(1,(int) o->value());} + xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial dfp2 { + label Freq + callback {eff->seteffectpar(2,(int) o->value());} + tooltip {LFO Frequency} xywh {85 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial dfp3 { + label Rnd + callback {eff->seteffectpar(3,(int) o->value());} + tooltip {LFO Randomness} xywh {120 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Dial dfp5 { + label {St.df} + callback {eff->seteffectpar(5,(int) o->value());} + tooltip {Left/Right Channel Phase Shift} xywh {200 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial dfp6 { + label LfoD + callback {eff->seteffectpar(6,(int) o->value());} + tooltip {LFO Depth} xywh {235 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Choice dfp4 { + label {LFO type} + callback {eff->seteffectpar(4,(int) o->value());} + tooltip {LFO function} xywh {155 50 40 15} down_box BORDER_BOX labelfont 1 labelsize 10 align 130 textsize 8 + } { + MenuItem {} { + label SINE + xywh {15 15 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label TRI + xywh {25 25 100 20} labelfont 1 labelsize 10 + } + } + Fl_Button {} { + label Filter + callback {filterwindow->show();} + xywh {115 10 55 25} box THIN_UP_BOX + } + Fl_Group {} { + xywh {270 40 105 45} box UP_FRAME color 51 + } { + Fl_Dial dfp7 { + label {A.S.} + callback {eff->seteffectpar(7,(int) o->value());} + tooltip {Filter vs Amplitude} xywh {275 45 25 25} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial dfp9 { + label {A.M} + callback {eff->seteffectpar(9,(int) o->value());} + tooltip {rate that amplitude changes the filter} xywh {305 45 25 25} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Check_Button dfp8 { + label {A.Inv.} + callback {eff->seteffectpar(8,(int) o->value());} + tooltip {enable for filter frequency to lower with higher input amplitude} xywh {345 55 15 15} down_box DOWN_BOX labelfont 1 labelsize 11 align 2 + } + } + } + } + Function {make_filter_window()} {open + } { + Fl_Window filterwindow { + label {Filter Parameters for DynFilter Eff.} + xywh {801 474 290 110} type Double + code0 {set_module_parameters(o);} visible + } { + Fl_Group {} { + label {DynFilter effect - Filter} + xywh {5 5 275 75} box FLAT_BOX color 50 align 144 + code0 {o->init(eff->filterpars,NULL,NULL);} + code1 {o->use_for_dynamic_filter();} + class FilterUI + } {} + Fl_Button {} { + label Close + callback {filterwindow->hide();} + xywh {105 85 70 20} box THIN_UP_BOX + } + } + } + Function {init(EffectMgr *eff_)} {} { + code {eff=eff_; + +make_null_window(); +make_reverb_window(); +make_echo_window(); +make_chorus_window(); +make_phaser_window(); +make_alienwah_window(); +make_distorsion_window(); +make_eq_window(); +make_dynamicfilter_window(); + +int px=this->parent()->x(); +int py=this->parent()->y(); + +effnullwindow->position(px,py); +effreverbwindow->position(px,py); +effechowindow->position(px,py); +effchoruswindow->position(px,py); +effphaserwindow->position(px,py); +effalienwahwindow->position(px,py); +effdistorsionwindow->position(px,py); +effeqwindow->position(px,py); +effdynamicfilterwindow->position(px,py); + +refresh(eff);} {} + } + Function {refresh(EffectMgr *eff_)} {open + } { + code {eff=eff_; +this->hide(); + +effnullwindow->hide(); +effreverbwindow->hide(); +effechowindow->hide(); +effchoruswindow->hide(); +effphaserwindow->hide(); +effalienwahwindow->hide(); +effdistorsionwindow->hide(); +effeqwindow->hide(); +effdynamicfilterwindow->hide(); + +eqband=0; + +if (filterwindow!=NULL){ + filterwindow->hide(); + delete(filterwindow); + filterwindow=NULL; +}; + +switch(eff->geteffect()){ + case 1: + revp->value(eff->getpreset()); + revp0->value(eff->geteffectpar(0));if (eff->insertion!=0) revp0->label("D/W"); + revp1->value(eff->geteffectpar(1)); + revp2->value(eff->geteffectpar(2)); + revp3->value(eff->geteffectpar(3)); + revp4->value(eff->geteffectpar(4)); + //revp5->value(eff->geteffectpar(5)); + revp6->value(eff->geteffectpar(6)); + revp7->value(eff->geteffectpar(7)); + revp8->value(eff->geteffectpar(8)); + revp9->value(eff->geteffectpar(9)); + revp10->value(eff->geteffectpar(10)); + revp11->value(eff->geteffectpar(11)); + revp12->value(eff->geteffectpar(12)); + + effreverbwindow->show(); + break; + case 2: + echop->value(eff->getpreset()); + echop0->value(eff->geteffectpar(0));if (eff->insertion!=0) echop0->label("D/W"); + echop1->value(eff->geteffectpar(1)); + echop2->value(eff->geteffectpar(2)); + echop3->value(eff->geteffectpar(3)); + echop4->value(eff->geteffectpar(4)); + echop5->value(eff->geteffectpar(5)); + echop6->value(eff->geteffectpar(6)); + effechowindow->show(); + break; + case 3: + chorusp->value(eff->getpreset()); + chorusp0->value(eff->geteffectpar(0));if (eff->insertion!=0) chorusp0->label("D/W"); + chorusp1->value(eff->geteffectpar(1)); + chorusp2->value(eff->geteffectpar(2)); + chorusp3->value(eff->geteffectpar(3)); + chorusp4->value(eff->geteffectpar(4)); + chorusp5->value(eff->geteffectpar(5)); + chorusp6->value(eff->geteffectpar(6)); + chorusp7->value(eff->geteffectpar(7)); + chorusp8->value(eff->geteffectpar(8)); + chorusp9->value(eff->geteffectpar(9)); + chorusp11->value(eff->geteffectpar(11)); + effchoruswindow->show(); + break; + case 4: + phaserp->value(eff->getpreset()); + phaserp0->value(eff->geteffectpar(0));if (eff->insertion!=0) phaserp0->label("D/W"); + phaserp1->value(eff->geteffectpar(1)); + phaserp2->value(eff->geteffectpar(2)); + phaserp3->value(eff->geteffectpar(3)); + phaserp4->value(eff->geteffectpar(4)); + phaserp5->value(eff->geteffectpar(5)); + phaserp6->value(eff->geteffectpar(6)); + phaserp7->value(eff->geteffectpar(7)); + phaserp8->value(eff->geteffectpar(8)); + phaserp9->value(eff->geteffectpar(9)); + phaserp10->value(eff->geteffectpar(10)); + phaserp11->value(eff->geteffectpar(11)); + phaserp12->value(eff->geteffectpar(12)); + phaserp13->value(eff->geteffectpar(13)); + phaserp14->value(eff->geteffectpar(14)); + effphaserwindow->show(); + break; + case 5: + awp->value(eff->getpreset()); + awp0->value(eff->geteffectpar(0));if (eff->insertion!=0) awp0->label("D/W"); + awp1->value(eff->geteffectpar(1)); + awp2->value(eff->geteffectpar(2)); + awp3->value(eff->geteffectpar(3)); + awp4->value(eff->geteffectpar(4)); + awp5->value(eff->geteffectpar(5)); + awp6->value(eff->geteffectpar(6)); + awp7->value(eff->geteffectpar(7)); + awp8->value(eff->geteffectpar(8)); + awp9->value(eff->geteffectpar(9)); + awp10->value(eff->geteffectpar(10)); + + effalienwahwindow->show(); + break; + case 6: + distp->value(eff->getpreset()); + distp0->value(eff->geteffectpar(0));if (eff->insertion!=0) distp0->label("D/W"); + distp1->value(eff->geteffectpar(1)); + distp2->value(eff->geteffectpar(2)); + distp3->value(eff->geteffectpar(3)); + distp4->value(eff->geteffectpar(4)); + distp5->value(eff->geteffectpar(5)); + distp6->value(eff->geteffectpar(6)); + distp7->value(eff->geteffectpar(7)); + distp8->value(eff->geteffectpar(8)); + distp9->value(eff->geteffectpar(9)); + distp10->value(eff->geteffectpar(10)); + effdistorsionwindow->show(); + break; + case 7:eqband=0; + eqp0->value(eff->geteffectpar(0)); + bandcounter->value(eqband); + bandcounter->do_callback(); + typechoice->value(eff->geteffectpar(10)); + eqgraph->redraw(); + freqdial->value(eff->geteffectpar(11)); + gaindial->value(eff->geteffectpar(12)); + if (eff->geteffectpar(10)<6) gaindial->deactivate(); + qdial->value(eff->geteffectpar(13)); + stagescounter->value(eff->geteffectpar(14)); + eqgraph->init(eff); + effeqwindow->show(); + break; + case 8:make_filter_window(); + dfp->value(eff->getpreset()); + dfp0->value(eff->geteffectpar(0));if (eff->insertion!=0) dfp0->label("D/W"); + dfp1->value(eff->geteffectpar(1)); + dfp2->value(eff->geteffectpar(2)); + dfp3->value(eff->geteffectpar(3)); + dfp4->value(eff->geteffectpar(4)); + dfp5->value(eff->geteffectpar(5)); + dfp6->value(eff->geteffectpar(6)); + dfp7->value(eff->geteffectpar(7)); + dfp8->value(eff->geteffectpar(8)); + dfp9->value(eff->geteffectpar(9)); + + + effdynamicfilterwindow->show(); + break; + default:effnullwindow->show(); + break; +}; + +this->show();} {} + } + Function {refresh()} {open + } { + code {refresh(eff);} {} + } + decl {EffectMgr *eff;} {} + decl {int eqband;} {} +} + +class SimpleEffUI {open : {public Fl_Group,public PresetsUI_} +} { + Function {SimpleEffUI(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {eff=NULL;} {} + } + Function {~SimpleEffUI()} {} { + code {effnullwindow->hide();//delete (effnullwindow); +effreverbwindow->hide();//delete (effreverbwindow); +effechowindow->hide();//delete (effechowindow); +effchoruswindow->hide();//delete (effchoruswindow); +effphaserwindow->hide();//delete (effphaserwindow); +effalienwahwindow->hide();//delete (effalienwahwindow); +effdistorsionwindow->hide();//delete (effdistorsionwindow); +effeqwindow->hide();//delete (effeqwindow); +effdynamicfilterwindow->hide();//delete (effdynamicfilterwindow);} {} + } + Function {make_null_window()} {open + } { + Fl_Window effnullwindow { + label {No Effect} open + xywh {1047 755 230 95} type Double box UP_BOX color 221 labelfont 1 labelsize 19 align 16 + code0 {set_module_parameters(o);} + class Fl_Group visible + } {} + } + Function {make_reverb_window()} {open + } { + Fl_Window effreverbwindow { + label Reverb open + xywh {1047 463 230 100} type Double box UP_BOX color 51 labelfont 1 labelsize 19 align 25 + code3 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice revp { + label Preset + callback {eff->changepreset((int)o->value()); + +refresh(eff);} + xywh {10 15 90 15} down_box BORDER_BOX color 47 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label {Cathedral 1} + xywh {10 10 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Cathedral 2} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Cathedral 3} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Hall 1} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Hall 2} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Room 1} + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Room 2} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Basement + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Tunnel + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Echoed 1} + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Echoed 2} + xywh {110 110 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Very Long 1} + xywh {120 120 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Very Long 2} + xywh {130 130 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial revp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial revp2 { + label Time + callback {eff->seteffectpar(2,(int) o->value());} + tooltip {Duration of Reverb} xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial revp3 { + label {I.del} + callback {eff->seteffectpar(3,(int) o->value());} + tooltip {Initial Delay} xywh {85 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Dial revp9 { + label Damp + callback {eff->seteffectpar(9,(int) o->value());} + tooltip Dampening xywh {120 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 minimum 64 maximum 127 step 1 + class WidgetPDial + } + } + } + Function {make_echo_window()} {open + } { + Fl_Window effechowindow { + label Echo open + xywh {428 823 230 100} type Double box UP_BOX color 51 labelfont 1 labelsize 19 align 25 + code3 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice echop { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {11 15 95 15} box UP_BOX down_box BORDER_BOX color 47 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label {Echo 1} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Echo 2} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Echo 3} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Simple Echo} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Canyon + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Panning Echo 1} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Panning Echo 2} + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Panning Echo 3} + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Feedback Echo} + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial echop0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial echop2 { + label Delay + callback {eff->seteffectpar(2,(int) o->value());} + xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Dial echop5 { + label {Fb.} + callback {eff->seteffectpar(5,(int) o->value());} + tooltip Feedback xywh {80 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + } + } + Function {make_chorus_window()} {open + } { + Fl_Window effchoruswindow { + label Chorus open + xywh {719 588 230 100} type Double box UP_BOX color 51 labelfont 1 labelsize 19 align 25 + code3 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice chorusp { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {10 15 90 15} box UP_BOX down_box BORDER_BOX color 47 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label {Chorus 1} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Chorus 2} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Chorus 3} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Celeste 1} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Celeste 2} + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Flange 1} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Flange 2} + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Flange 3} + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Flange 4} + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Flange 5} + xywh {110 110 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial chorusp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp2 { + label Freq + callback {eff->seteffectpar(2,(int) o->value());} + tooltip {LFO Frequency} xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp6 { + label Dpth + callback {eff->seteffectpar(6,(int) o->value());} + tooltip Depth xywh {80 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp7 { + label Delay + callback {eff->seteffectpar(7,(int) o->value());} + xywh {115 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial chorusp8 { + label Fb + callback {eff->seteffectpar(8,(int) o->value());} + tooltip Feedback xywh {150 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Check_Button {} { + label Flange + callback {eff->seteffectpar(10,(int) o->value());} + xywh {120 10 55 20} box THIN_UP_BOX down_box DOWN_BOX color 230 labelfont 1 labelsize 10 hide deactivate + code0 {o->value(eff->geteffectpar(10));} + } + } + } + Function {make_phaser_window()} {open + } { + Fl_Window effphaserwindow { + label Phaser open + xywh {1047 831 230 100} type Double box UP_BOX color 51 labelfont 1 labelsize 19 align 25 + code3 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice phaserp { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {10 15 90 15} box UP_BOX down_box BORDER_BOX color 47 selection_color 0 labelfont 1 labelsize 10 labelcolor 55 align 5 textfont 1 textsize 10 textcolor 7 + } { + MenuItem {} { + label {Phaser 1} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Phaser 2} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Phaser 3} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Phaser 4} + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Phaser 5} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Phaser 6} + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial phaserp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial phaserp2 { + label Freq + callback {eff->seteffectpar(2,(int) o->value());} + tooltip {LFO frequency} xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial phaserp5 { + label {St.df} + callback {eff->seteffectpar(5,(int) o->value());} + tooltip {Left/Right Channel Phase Shift} xywh {80 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial phaserp6 { + label Dpth + callback {eff->seteffectpar(6,(int) o->value());} + tooltip Depth xywh {115 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial phaserp7 { + label Fb + callback {eff->seteffectpar(7,(int) o->value());} + tooltip Feedback xywh {150 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Counter phaserp8 { + label Stages + callback {eff->seteffectpar(8,(int) o->value());} + xywh {185 55 35 15} type Simple labelfont 1 labelsize 11 minimum 0 maximum 127 step 1 + code0 {o->range(1,MAX_PHASER_STAGES);} + } + } + } + Function {make_alienwah_window()} {open + } { + Fl_Window effalienwahwindow { + label AlienWah open + xywh {403 480 230 100} type Double box UP_BOX color 51 labelfont 1 labelsize 19 align 25 + code3 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice awp { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {10 15 90 15} box UP_BOX down_box BORDER_BOX color 47 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label {Alienwah 1} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Alienwah 2} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Alienwah 3} + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Alienwah 4} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial awp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial awp2 { + label Freq + callback {eff->seteffectpar(2,(int) o->value());} + tooltip {LFO frequency} xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial awp6 { + label Dpth + callback {eff->seteffectpar(6,(int) o->value());} + tooltip Depth xywh {85 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Counter awp8 { + label Delay + callback {eff->seteffectpar(8,(int) o->value());} + xywh {125 55 35 15} type Simple labelfont 1 labelsize 11 minimum 0 maximum 127 step 1 + code0 {o->range(1,MAX_ALIENWAH_DELAY);} + } + } + } + Function {make_distorsion_window()} {open + } { + Fl_Window effdistorsionwindow { + label Distortion open + xywh {353 881 230 100} type Double box UP_BOX color 51 labelfont 1 labelsize 19 align 25 + code3 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice distp { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {11 15 95 15} box UP_BOX down_box BORDER_BOX color 47 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label {Overdrive 1} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Overdrive 2} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {A. Exciter 1} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {A. Exciter 2} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Guitar Amp} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Quantisize + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial distp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial distp3 { + label Drive + callback {eff->seteffectpar(3,(int) o->value());} + tooltip {Input amplification} xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 when 4 maximum 127 + class WidgetPDial + } + Fl_Dial distp4 { + label Level + callback {eff->seteffectpar(4,(int) o->value());} + tooltip {Output Amplification} xywh {80 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial distp7 { + label LPF + callback {eff->seteffectpar(7,(int) o->value());} + tooltip {Low Pass Filter} xywh {190 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Choice distp5 { + label Type + callback {eff->seteffectpar(5,(int) o->value());} + xywh {120 50 60 20} box UP_BOX down_box BORDER_BOX labelfont 1 labelsize 11 align 2 textsize 10 + } { + MenuItem {} { + label Atan + xywh {55 55 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Asym1 + xywh {65 65 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pow + xywh {75 75 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sine + xywh {85 85 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Qnts + xywh {95 95 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Zigzg + xywh {105 105 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Lmt + xywh {115 115 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LmtU + xywh {125 125 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LmtL + xywh {135 135 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label ILmt + xywh {147 147 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Clip + xywh {157 157 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Asym2 + xywh {75 75 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pow2 + xywh {85 85 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sgm + xywh {95 95 100 20} labelfont 1 labelsize 10 + } + } + } + } + Function {make_eq_window()} {open + } { + Fl_Window effeqwindow { + label EQ open + xywh {1047 881 230 100} type Double box UP_BOX color 51 labelfont 1 labelsize 19 align 25 + code3 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Counter bandcounter { + label Band + callback {eqband=(int) o->value(); +int npb=eqband*5+10; + +int type=eff->geteffectpar(npb); +typechoice->value(type); + +if (type>6) gaindial->activate(); + else gaindial->deactivate(); + +if (type==0) bandgroup->deactivate(); +else bandgroup->activate(); + +int freq=eff->geteffectpar(npb+1); +freqdial->value(freq); + +int gain=eff->geteffectpar(npb+2); +gaindial->value(gain); + +int q=eff->geteffectpar(npb+3); +qdial->value(q); + +int dbl=eff->geteffectpar(npb+4); +stagescounter->value(dbl);} + tooltip {Band no.} xywh {85 15 45 15} type Simple labelfont 1 labelsize 11 align 1 minimum 0 maximum 1 step 1 textfont 1 textsize 11 + code0 {o->bounds(0,MAX_EQ_BANDS-1);} + } + Fl_Group bandgroup { + xywh {5 5 75 85} box UP_FRAME + code0 {if (eff->geteffectpar(10)==0) o->deactivate();} + } { + Fl_Dial freqdial { + label Freq + callback {int np=eqband*5+11; +eff->seteffectpar(np,(int) o->value()); +eqgraph->redraw();} + xywh {10 10 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 when 3 maximum 127 + class WidgetPDial + } + Fl_Dial gaindial { + label Gain + callback {int np=eqband*5+12; +eff->seteffectpar(np,(int) o->value()); +eqgraph->redraw();} + xywh {45 10 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 when 3 maximum 127 step 1 + class WidgetPDial + } + Fl_Dial qdial { + label Q + callback {int np=eqband*5+13; +eff->seteffectpar(np,(int) o->value()); +eqgraph->redraw();} + tooltip {Bandwidth/Resonance} xywh {10 50 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 when 3 maximum 127 + class WidgetPDial + } + Fl_Counter stagescounter { + label Stages + callback {int np=eqband*5+14; +eff->seteffectpar(np,(int) o->value()); +eqgraph->redraw();} + tooltip {Additional filter stages} xywh {40 55 30 15} type Simple labelfont 1 labelsize 10 minimum 1 maximum 127 step 1 textfont 1 textsize 11 + code0 {o->bounds(0,MAX_FILTER_STAGES-1);} + } + } + Fl_Choice typechoice { + label Type + callback {int np=eqband*5+10; +eff->seteffectpar(np,(int) o->value()); +bandcounter->do_callback(); +eqgraph->redraw();} + tooltip Type xywh {135 15 40 15} down_box BORDER_BOX labelfont 1 labelsize 10 align 1 when 6 textsize 10 + } { + MenuItem {} { + label OFF + xywh {10 10 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Lp1 + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Hp1 + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Lp2 + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Hp2 + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Bp2 + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label N2 + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pk + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LSh + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label HSh + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + } + Fl_Box eqgraph { + xywh {85 35 140 55} box BORDER_BOX color 50 + code0 {o->init(eff);} + class EQGraph + } + } + } + Function {make_dynamicfilter_window()} {open + } { + Fl_Window effdynamicfilterwindow { + label DynFilter open + xywh {965 527 230 100} type Double box UP_BOX color 51 labelfont 1 labelsize 19 align 25 + code3 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Choice dfp { + label Preset + callback {eff->changepreset((int)o->value()); +refresh(eff);} + xywh {10 15 90 15} box UP_BOX down_box BORDER_BOX color 47 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + } { + MenuItem {} { + label WahWah + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label AutoWah + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sweep + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label VocalMorph1 + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label VocalMorph2 + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial dfp0 { + label Vol + callback {eff->seteffectpar(0,(int) o->value());} + tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial dfp2 { + label Freq + callback {eff->seteffectpar(2,(int) o->value());} + tooltip {LFO frequency} xywh {45 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial dfp6 { + label LfoD + callback {eff->seteffectpar(6,(int) o->value());} + tooltip {LFO depth} xywh {80 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Group {} { + xywh {115 40 65 45} box UP_FRAME + } { + Fl_Dial dfp7 { + label {A.S.} + callback {eff->seteffectpar(7,(int) o->value());} + tooltip {how filter varies with amplitude} xywh {120 45 25 25} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + Fl_Dial dfp9 { + label {A.M} + callback {eff->seteffectpar(9,(int) o->value());} + tooltip {how quickly the filter varies with amplitude} xywh {150 45 25 25} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + class WidgetPDial + } + } + } + } + Function {init(EffectMgr *eff_)} {open + } { + code {eff=eff_; + +make_null_window(); +make_reverb_window(); +make_echo_window(); +make_chorus_window(); +make_phaser_window(); +make_alienwah_window(); +make_distorsion_window(); +make_eq_window(); +make_dynamicfilter_window(); + +int px=this->parent()->x(); +int py=this->parent()->y(); + +effnullwindow->position(px,py); +effreverbwindow->position(px,py); +effechowindow->position(px,py); +effchoruswindow->position(px,py); +effphaserwindow->position(px,py); +effalienwahwindow->position(px,py); +effdistorsionwindow->position(px,py); +effeqwindow->position(px,py); +effdynamicfilterwindow->position(px,py); + +refresh(eff);} {} + } + Function {refresh(EffectMgr *eff_)} {} { + code {eff=eff_; +this->hide(); + +effnullwindow->hide(); +effreverbwindow->hide(); +effechowindow->hide(); +effchoruswindow->hide(); +effphaserwindow->hide(); +effalienwahwindow->hide(); +effdistorsionwindow->hide(); +effeqwindow->hide(); +effdynamicfilterwindow->hide(); + +eqband=0; + + +switch(eff->geteffect()){ + case 1: + revp->value(eff->getpreset()); + revp0->value(eff->geteffectpar(0));if (eff->insertion!=0) revp0->label("D/W"); + revp2->value(eff->geteffectpar(2)); + revp3->value(eff->geteffectpar(3)); + revp9->value(eff->geteffectpar(9)); + effreverbwindow->show(); + break; + case 2: + echop->value(eff->getpreset()); + echop0->value(eff->geteffectpar(0));if (eff->insertion!=0) echop0->label("D/W"); + echop2->value(eff->geteffectpar(2)); + echop5->value(eff->geteffectpar(5)); + effechowindow->show(); + break; + case 3: + chorusp->value(eff->getpreset()); + chorusp0->value(eff->geteffectpar(0));if (eff->insertion!=0) chorusp0->label("D/W"); + chorusp2->value(eff->geteffectpar(2)); + chorusp6->value(eff->geteffectpar(6)); + chorusp7->value(eff->geteffectpar(7)); + chorusp8->value(eff->geteffectpar(8)); + effchoruswindow->show(); + break; + case 4: + phaserp->value(eff->getpreset()); + phaserp0->value(eff->geteffectpar(0));if (eff->insertion!=0) phaserp0->label("D/W"); + phaserp2->value(eff->geteffectpar(2)); + phaserp5->value(eff->geteffectpar(5)); + phaserp6->value(eff->geteffectpar(6)); + phaserp7->value(eff->geteffectpar(7)); + phaserp8->value(eff->geteffectpar(8)); + effphaserwindow->show(); + break; + case 5: + awp->value(eff->getpreset()); + awp0->value(eff->geteffectpar(0));if (eff->insertion!=0) awp0->label("D/W"); + awp2->value(eff->geteffectpar(2)); + awp6->value(eff->geteffectpar(6)); + awp8->value(eff->geteffectpar(8)); + effalienwahwindow->show(); + break; + case 6: + distp->value(eff->getpreset()); + distp0->value(eff->geteffectpar(0));if (eff->insertion!=0) distp0->label("D/W"); + distp3->value(eff->geteffectpar(3)); + distp4->value(eff->geteffectpar(4)); + distp5->value(eff->geteffectpar(5)); + distp7->value(eff->geteffectpar(7)); + effdistorsionwindow->show(); + break; + case 7: + bandcounter->value(eqband); + bandcounter->do_callback(); + typechoice->value(eff->geteffectpar(10)); + eqgraph->redraw(); + freqdial->value(eff->geteffectpar(11)); + gaindial->value(eff->geteffectpar(12)); + if (eff->geteffectpar(10)<6) gaindial->deactivate(); + qdial->value(eff->geteffectpar(13)); + stagescounter->value(eff->geteffectpar(14)); + eqgraph->init(eff); + effeqwindow->show(); + break; + case 8: + dfp->value(eff->getpreset()); + dfp0->value(eff->geteffectpar(0));if (eff->insertion!=0) dfp0->label("D/W"); + dfp2->value(eff->geteffectpar(2)); + dfp6->value(eff->geteffectpar(6)); + dfp7->value(eff->geteffectpar(7)); + dfp9->value(eff->geteffectpar(9)); + + + effdynamicfilterwindow->show(); + break; + default:effnullwindow->show(); + break; +}; + +this->show();} {} + } + Function {refresh()} {} { + code {refresh(eff);} {} + } + decl {EffectMgr *eff;} {} + decl {int eqband;} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/EnvelopeUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/EnvelopeUI.fl new file mode 100644 index 000000000..f9dcfff9d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/EnvelopeUI.fl @@ -0,0 +1,868 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "../globals.h"} {public +} + +decl {\#include } {public +} + +decl {\#include "../Params/EnvelopeParams.h"} {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "PresetsUI.h"} {public +} + +decl {\#include "common.H"} {public +} + +class EnvelopeFreeEdit {: {public Fl_Box} +} { + Function {EnvelopeFreeEdit(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {} { + code {env=NULL; +pair=NULL;} {} + } + Function {init(EnvelopeParams *env_)} {} { + code {env=env_; +oldx=-1; +currentpoint=-1; +cpx=0; +lastpoint=-1;} {} + } + Function {setpair(Fl_Box *pair_)} {} { + code {pair=pair_;} {} + } + Function {getpointx(int n)} {return_type int + } { + code {int lx=w()-10; +int npoints=env->Penvpoints; + +float sum=0; +for (int i=1;igetdt(i)+1; + +float sumbefore=0;//the sum of all points before the computed point +for (int i=1;i<=n;i++) sumbefore+=env->getdt(i)+1; + +return((int) (sumbefore/(float) sum*lx));} {} + } + Function {getpointy(int n)} {return_type int + } { + code {int ly=h()-10; + +return((int) ((1.0-env->Penvval[n]/127.0)*ly));} {} + } + Function {getnearest(int x,int y)} {return_type int + } { + code {x-=5;y-=5; + +int nearestpoint=0; +int nearestval=1000000;//a big value +for (int i=0;iPenvpoints;i++){ + int distance=abs(x-getpointx(i))+abs(y-getpointy(i)); + if (distancePfreemode==0) env->converttofree(); +int npoints=env->Penvpoints; + +if (active_r()) fl_color(FL_BLACK); + else fl_color(90,90,90); +if (!active_r()) currentpoint=-1; + +fl_rectf(ox,oy,lx,ly); + +ox+=5;oy+=5;lx-=10;ly-=10; + +//draw the lines +fl_color(FL_GRAY); + +fl_line_style(FL_SOLID); +fl_line(ox+2,oy+ly/2,ox+lx-2,oy+ly/2); + +//draws the evelope points and lines +Fl_Color alb=FL_WHITE; +if (!active_r()) alb=fl_rgb_color(180,180,180); +fl_color(alb); +int oldxx=0,xx=0,oldyy=0,yy=getpointy(0); +fl_rectf(ox-3,oy+yy-3,6,6); +for (int i=1;i=0){ + fl_color(FL_CYAN); + fl_rectf(ox+getpointx(lastpoint)-5,oy+getpointy(lastpoint)-5,10,10); +}; + +//draw the sustain position +if (env->Penvsustain>0){ + fl_color(FL_YELLOW); + xx=getpointx(env->Penvsustain); + fl_line(ox+xx,oy+0,ox+xx,oy+ly); +}; + +//Show the envelope duration and the current line duration +fl_font(FL_HELVETICA|FL_BOLD,10); +float time=0.0; +if (currentpoint<=0){ + fl_color(alb); + for (int i=1;igetdt(i); +} else { + fl_color(255,0,0); + time=env->getdt(currentpoint); +}; +char tmpstr[20]; +if (time<1000.0) snprintf((char *)&tmpstr,20,"%.1fms",time); + else snprintf((char *)&tmpstr,20,"%.2fs",time/1000.0); +fl_draw(tmpstr,ox+lx-20,oy+ly-10,20,10,FL_ALIGN_RIGHT,NULL,0);} {} + } + Function {handle(int event)} {return_type int + } { + code {int x_=Fl::event_x()-x(); +int y_=Fl::event_y()-y(); + +if (event==FL_PUSH) { + currentpoint=getnearest(x_,y_); + cpx=x_; + cpdt=env->Penvdt[currentpoint]; + lastpoint=currentpoint; + redraw(); + if (pair!=NULL) pair->redraw(); +}; + +if (event==FL_RELEASE){ + currentpoint=-1; + redraw(); + if (pair!=NULL) pair->redraw(); +}; + +if ((event==FL_DRAG)&&(currentpoint>=0)){ + int ny=127-(int) (y_*127.0/h()); + if (ny<0) ny=0;if (ny>127) ny=127; + env->Penvval[currentpoint]=ny; + + int dx=(int)((x_-cpx)*0.1); + int newdt=cpdt+dx; + if (newdt<0) newdt=0;if (newdt>127) newdt=127; + if (currentpoint!=0) env->Penvdt[currentpoint]=newdt; + else env->Penvdt[currentpoint]=0; + + redraw(); + if (pair!=NULL) pair->redraw(); +}; + + +return(1);} {} + } + decl {Fl_Box *pair;} {} + decl {EnvelopeParams *env;} {} + decl {int oldx;} {} + decl {int currentpoint,cpx,cpdt;} {} + decl {int lastpoint;} {public + } +} + +class EnvelopeUI {open : {public Fl_Group,PresetsUI_} +} { + Function {EnvelopeUI(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {env=NULL; +freemodeeditwindow=NULL; +envADSR=NULL; +envASR=NULL; +envADSRfilter=NULL; +envASRbw=NULL; +envfree=NULL;} {} + } + Function {~EnvelopeUI()} {} { + code {envwindow->hide(); +hide(); +freemodeeditwindow->hide(); +delete (freemodeeditwindow);} {} + } + Function {make_freemode_edit_window()} {open + } { + Fl_Window freemodeeditwindow { + label Envelope + xywh {702 269 575 180} type Double visible + } { + Fl_Box freeedit { + label Envelope + xywh {5 5 565 145} box FLAT_BOX color 0 + code0 {o->init(env);} + class EnvelopeFreeEdit + } + Fl_Button addpoint { + label {Add point} + callback {int curpoint=freeedit->lastpoint; +if (curpoint<0) return; +//if (curpoint>=env->Penvpoints-1) return; +if (env->Penvpoints>=MAX_ENVELOPE_POINTS) return; + +for (int i=env->Penvpoints;i>=curpoint+1;i--){ + env->Penvdt[i]=env->Penvdt[i-1]; + env->Penvval[i]=env->Penvval[i-1]; +}; + +if (curpoint==0) { + env->Penvdt[1]=64; +}; + +env->Penvpoints++; +if (curpoint<=env->Penvsustain) env->Penvsustain++; + +freeedit->lastpoint+=1; +freeedit->redraw(); +envfree->redraw(); + +sustaincounter->value(env->Penvsustain); +sustaincounter->maximum(env->Penvpoints-2);} + xywh {115 155 80 20} box THIN_UP_BOX labelsize 11 + code0 {if (env->Pfreemode==0) o->hide();} + } + Fl_Button deletepoint { + label {Delete point} + callback {int curpoint=freeedit->lastpoint; +if (curpoint<1) return; +if (curpoint>=env->Penvpoints-1) return; +if (env->Penvpoints<=3) return; + +for (int i=curpoint+1;iPenvpoints;i++){ + env->Penvdt[i-1]=env->Penvdt[i]; + env->Penvval[i-1]=env->Penvval[i]; +}; + +env->Penvpoints--; + +if (curpoint<=env->Penvsustain) env->Penvsustain--; + + +freeedit->lastpoint-=1; +freeedit->redraw(); +envfree->redraw(); + +sustaincounter->value(env->Penvsustain); +sustaincounter->maximum(env->Penvpoints-2);} + xywh {200 155 80 20} box THIN_UP_BOX labelsize 11 + code0 {if (env->Pfreemode==0) o->hide();} + } + Fl_Light_Button freemodebutton { + label FreeMode + callback {reinit(); + +freeedit->lastpoint=-1; +freeedit->redraw();} + tooltip {Enable or disable the freemode} xywh {10 155 95 20} labelsize 11 + } + Fl_Check_Button forcedreleasecheck { + label frcR + callback {env->Pforcedrelease=(int)o->value();} + tooltip {Forced Relase} xywh {410 165 40 15} down_box DOWN_BOX labelsize 10 + code0 {o->value(env->Pforcedrelease);} + code1 {if (env->Pfreemode==0) o->hide();} + } + Fl_Dial envstretchdial { + label {Str.} + callback {env->Penvstretch=(int)o->value();} + tooltip {Envelope stretch (on lower notes make the envelope longer)} xywh {380 155 25 25} box ROUND_UP_BOX labelsize 10 align 4 maximum 127 step 1 + code0 {o->value(env->Penvstretch);} + code1 {if (env->Pfreemode==0) o->hide();} + class WidgetPDial + } + Fl_Button {} { + label Close + callback {freemodeeditwindow->hide();} + xywh {510 155 60 20} box THIN_UP_BOX + } + Fl_Check_Button linearenvelopecheck { + label L + callback {env->Plinearenvelope=(int)o->value();} + tooltip {Linear Envelope} xywh {410 151 30 15} down_box DOWN_BOX labelsize 10 + code0 {o->value(env->Plinearenvelope);} + code1 {if ((env->Pfreemode==0)||(env->Envmode>2)) o->hide();} + } + Fl_Counter sustaincounter { + label Sust + callback {env->Penvsustain=(int) o->value(); +freeedit->redraw(); +envfree->redraw();} + tooltip {Sustain (0 is disabled)} xywh {315 155 40 15} type Simple labelsize 11 align 4 minimum 0 maximum 127 step 1 + code0 {o->value(env->Penvsustain);} + code1 {if (env->Pfreemode==0) o->hide();} + code2 {o->maximum(env->Penvpoints-2);} + } + Fl_Button {} { + label C + callback {presetsui->copy(env);} + xywh {465 160 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(env,this);} + xywh {482 160 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + } + } + Function {make_ADSR_window()} {open + } { + Fl_Window envADSR {open + xywh {344 788 205 70} type Double color 50 labelfont 1 + class Fl_Group visible + } { + Fl_Group {} { + label {Amplitude Envelope} + xywh {0 0 205 70} box UP_BOX color 223 labeltype ENGRAVED_LABEL labelsize 10 align 17 + code0 {set_module_parameters(o);} + } { + Fl_Dial e1adt { + label {A.dt} + callback {env->PA_dt=(int)o->value(); +freeedit->redraw();} + tooltip {Attack time} xywh {5 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PA_dt);} + class WidgetPDial + } + Fl_Dial e1ddt { + label {D.dt} + callback {env->PD_dt=(int)o->value(); +freeedit->redraw();} + tooltip {Decay time} xywh {40 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PD_dt);} + class WidgetPDial + } + Fl_Dial e1rdt { + label {R.dt} + callback {env->PR_dt=(int)o->value(); +freeedit->redraw();} + tooltip {Release time} xywh {110 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PR_dt);} + class WidgetPDial + } + Fl_Dial e1sval { + label {S.val} + callback {env->PS_val=(int)o->value(); +freeedit->redraw();} + tooltip {Sustain value} xywh {75 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PS_val);} + class WidgetPDial + } + Fl_Check_Button e1forcedrelease { + label frcR + callback {env->Pforcedrelease=(int)o->value();} + tooltip {Forced Relase} xywh {180 35 20 15} down_box DOWN_BOX labelsize 10 align 6 + code0 {o->value(env->Pforcedrelease);} + } + Fl_Dial e1envstretch { + label Stretch + callback {env->Penvstretch=(int)o->value();} + tooltip {Envelope stretch (on lower notes makes the envelope longer)} xywh {145 25 25 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->Penvstretch);} + class WidgetPDial + } + Fl_Button {} { + label E + callback {freemodeeditwindow->show();} + tooltip {Envelope window} xywh {185 5 15 15} labelfont 1 labelsize 10 + } + Fl_Check_Button e1linearenvelope { + label L + callback {env->Plinearenvelope=(int)o->value();} + tooltip {The evelope is linear} xywh {180 20 15 15} down_box DOWN_BOX labelsize 10 align 4 + code0 {o->value(env->Plinearenvelope);} + } + Fl_Button {} { + label C + callback {presetsui->copy(env);} + xywh {150 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(env,this);} + xywh {167 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + } + } + } + Function {make_ASR_window()} {open + } { + Fl_Window envASR {open + xywh {648 667 210 70} type Double + class Fl_Group visible + } { + Fl_Group {} { + label {Frequency Envelope} + xywh {0 0 210 70} box UP_BOX color 223 labeltype ENGRAVED_LABEL labelsize 10 align 17 + code0 {set_module_parameters(o);} + } { + Fl_Dial e2aval { + label {A.val} + callback {env->PA_val=(int)o->value(); +freeedit->redraw();} + tooltip {Starting value} xywh {5 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PA_val);} + class WidgetPDial + } + Fl_Dial e2adt { + label {A.dt} + callback {env->PA_dt=(int)o->value(); +freeedit->redraw();} + tooltip {Attack time} xywh {40 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PA_dt);} + class WidgetPDial + } + Fl_Dial e2rval { + label {R.val} + callback {env->PR_val=(int)o->value(); +freeedit->redraw();} + tooltip {Release value} xywh {110 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PR_val);} + class WidgetPDial + } + Fl_Dial e2rdt { + label {R.dt} + callback {env->PR_dt=(int)o->value(); +freeedit->redraw();} + tooltip {Release time} xywh {75 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PR_dt);} + class WidgetPDial + } + Fl_Dial e2envstretch { + label Stretch + callback {env->Penvstretch=(int)o->value();} + tooltip {Envelope stretch (on lower notes makes the envelope longer)} xywh {145 25 25 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->Penvstretch);} + class WidgetPDial + } + Fl_Check_Button e2forcedrelease { + label frcR + callback {env->Pforcedrelease=(int)o->value();} + tooltip {Forced release} xywh {180 25 15 25} down_box DOWN_BOX labelsize 10 align 6 + code0 {o->value(env->Pforcedrelease);} + } + Fl_Button {} { + label C + callback {presetsui->copy(env);} + xywh {155 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(env,this);} + xywh {172 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + } + Fl_Button {} { + label E + callback {freemodeeditwindow->show();} + tooltip {Envelope window} xywh {190 5 15 15} labelfont 1 labelsize 10 + } + } + } + Function {make_ADSRfilter_window()} {open + } { + Fl_Window envADSRfilter {open selected + xywh {627 569 275 70} type Double color 50 labelfont 1 + class Fl_Group visible + } { + Fl_Group {} { + label {Filter Envelope} + xywh {0 0 275 70} box UP_BOX color 223 labeltype ENGRAVED_LABEL labelsize 10 align 17 + code0 {set_module_parameters(o);} + } { + Fl_Dial e3aval { + label {A.val} + callback {env->PA_val=(int)o->value(); +freeedit->redraw();} + tooltip {Starting value} xywh {5 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PA_val);} + class WidgetPDial + } + Fl_Dial e3adt { + label {A.dt} + callback {env->PA_dt=(int)o->value(); +freeedit->redraw();} + tooltip {Attack time} xywh {40 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PA_dt);} + class WidgetPDial + } + Fl_Dial e3dval { + label {D.val} + callback {env->PD_val=(int)o->value(); +freeedit->redraw();} + tooltip {decay value} xywh {75 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PD_val);} + class WidgetPDial + } + Fl_Dial e3ddt { + label {D.dt} + callback {env->PD_dt=(int)o->value(); +freeedit->redraw();} + tooltip {decay time} xywh {110 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PD_dt);} + class WidgetPDial + } + Fl_Dial e3rdt { + label {R.dt} + callback {env->PR_dt=(int)o->value(); +freeedit->redraw();} + tooltip {Release time} xywh {145 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PR_dt);} + class WidgetPDial + } + Fl_Dial e3rval { + label {R.val} + callback {env->PR_val=(int)o->value(); +freeedit->redraw();} + tooltip {Release value} xywh {180 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PR_val);} + class WidgetPDial + } + Fl_Dial e3envstretch { + label Stretch + callback {env->Penvstretch=(int)o->value();} + tooltip {Envelope stretch (on lower notes makes the envelope longer)} xywh {215 25 25 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->Penvstretch);} + class WidgetPDial + } + Fl_Check_Button e3forcedrelease { + label frcR + callback {env->Pforcedrelease=(int)o->value();} + tooltip {Forced Relase} xywh {250 30 15 20} down_box DOWN_BOX labelsize 10 align 6 + code0 {o->value(env->Pforcedrelease);} + } + Fl_Button {} { + label E + callback {freemodeeditwindow->show();} + xywh {255 5 15 15} labelfont 1 labelsize 10 + } + Fl_Button {} { + label C + callback {presetsui->copy(env);} + xywh {220 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(env,this);} + xywh {237 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + } + } + } + Function {make_ASRbw_window()} {open + } { + Fl_Window envASRbw {open + xywh {362 642 210 70} type Double + code0 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Group {} { + label {BandWidth Envelope} + xywh {0 0 210 70} box UP_BOX color 223 labeltype ENGRAVED_LABEL labelsize 10 align 17 + code0 {set_module_parameters(o);} + } { + Fl_Dial e4aval { + label {A.val} + callback {env->PA_val=(int)o->value(); +freeedit->redraw();} + tooltip {Starting value} xywh {5 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PA_val);} + class WidgetPDial + } + Fl_Dial e4adt { + label {A.dt} + callback {env->PA_dt=(int)o->value(); +freeedit->redraw();} + tooltip {Attack time} xywh {40 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PA_dt);} + class WidgetPDial + } + Fl_Dial e4rval { + label {R.val} + callback {env->PR_val=(int)o->value(); +freeedit->redraw();} + tooltip {Release value} xywh {110 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PR_val);} + class WidgetPDial + } + Fl_Dial e4rdt { + label {R.dt} + callback {env->PR_dt=(int)o->value(); +freeedit->redraw();} + tooltip {Release time} xywh {75 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->PR_dt);} + class WidgetPDial + } + Fl_Dial e4envstretch { + label Stretch + callback {env->Penvstretch=(int)o->value();} + tooltip {Envelope stretch (on lower notes makes the envelope longer)} xywh {145 25 25 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(env->Penvstretch);} + class WidgetPDial + } + Fl_Check_Button e4forcedrelease { + label frcR + callback {env->Pforcedrelease=(int)o->value();} + tooltip {Forced release} xywh {180 25 15 25} down_box DOWN_BOX labelsize 10 align 6 + code0 {o->value(env->Pforcedrelease);} + } + Fl_Button {} { + label C + callback {presetsui->copy(env);} + xywh {155 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(env,this);} + xywh {172 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + } + Fl_Button {} { + label E + callback {freemodeeditwindow->show();} + xywh {190 5 15 15} labelfont 1 labelsize 10 + } + } + } + Function {make_free_window()} {open + } { + Fl_Window envfree {open + xywh {376 436 205 70} type Double color 50 labelfont 1 resizable + code0 {set_module_parameters(o);} + class Fl_Group visible + } { + Fl_Group envfreegroup { + label {Amplitude Envelope} + xywh {0 0 205 70} box UP_BOX color 223 labeltype ENGRAVED_LABEL labelsize 10 align 17 resizable + code0 {set_module_parameters(o);} + } { + Fl_Box freeeditsmall { + label Envelope + callback {envfree->redraw();} + xywh {5 20 195 45} box FLAT_BOX color 0 resizable + code0 {o->init(env);} + class EnvelopeFreeEdit + } + Fl_Button {} { + label E + callback {freemodeeditwindow->show();} + xywh {185 5 15 15} labelfont 1 labelsize 10 + } + Fl_Button {} { + label C + callback {presetsui->copy(env);} + xywh {150 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(env,this);} + xywh {167 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + } + } + } + Function {init(EnvelopeParams *env_)} {open + } { + code {env=env_; +make_ADSR_window(); +make_ASR_window(); +make_ADSRfilter_window(); +make_ASRbw_window(); +make_free_window(); + +make_freemode_edit_window(); + +envwindow=NULL; +if (env->Envmode==3) envfreegroup->label("Frequency Envelope"); +if (env->Envmode==4) envfreegroup->label("Filter Envelope"); +if (env->Envmode==5) envfreegroup->label("Bandwidth Envelope"); + +freemodeeditwindow->label(this->label()); + + +freeeditsmall->setpair(freeedit); +freeedit->setpair(freeeditsmall); + + +refresh();} {} + } + Function {reinit()} {} { + code {if (env->Pfreemode!=0){ + int answer=fl_choice("Disable the free mode of the Envelope?","No","Yes",NULL); + if (env->Pfreemode!=0) freemodebutton->value(1); + else freemodebutton->value(0); + if (answer==0) return; +}; + +if (env->Pfreemode==0) env->Pfreemode=1; + else env->Pfreemode=0; + +hide(); +int winx=freemodeeditwindow->x(); +int winy=freemodeeditwindow->y(); + +freemodeeditwindow->hide(); + +envwindow->hide(); +Fl_Group *par=envwindow->parent(); +par->hide(); + + +refresh(); +envwindow->show(); +par->redraw(); + +par->show(); +show(); +freemodeeditwindow->position(winx,winy); +freemodeeditwindow->show(); + +if (env->Pfreemode!=0) { + freemodebutton->value(1); + addpoint->show(); + deletepoint->show(); + forcedreleasecheck->show(); +}else{ + freemodebutton->value(0); + addpoint->hide(); + deletepoint->hide(); + forcedreleasecheck->hide(); +};} {} + } + Function {refresh()} {open + } { + code {freemodebutton->value(env->Pfreemode); + +sustaincounter->value(env->Penvsustain); +if (env->Pfreemode==0) sustaincounter->hide(); + else sustaincounter->show(); +sustaincounter->maximum(env->Penvpoints-2); + +envstretchdial->value(env->Penvstretch); +if (env->Pfreemode==0) envstretchdial->hide(); + else envstretchdial->show(); + +linearenvelopecheck->value(env->Plinearenvelope); +if ((env->Pfreemode==0)||(env->Envmode>2)) linearenvelopecheck->hide(); + else linearenvelopecheck->show(); + +forcedreleasecheck->value(env->Pforcedrelease); +if (env->Pfreemode==0) forcedreleasecheck->hide(); + +freeedit->redraw(); + + +if (env->Pfreemode==0){ + switch(env->Envmode){ + case(1): + case(2): + e1adt->value(env->PA_dt); + e1ddt->value(env->PD_dt); + e1sval->value(env->PS_val); + e1rdt->value(env->PR_dt); + e1envstretch->value(env->Penvstretch); + e1linearenvelope->value(env->Plinearenvelope); + e1forcedrelease->value(env->Pforcedrelease); + break; + case(3): + e2aval->value(env->PA_val); + e2adt->value(env->PA_dt); + e2rdt->value(env->PR_dt); + e2rval->value(env->PR_val); + e2envstretch->value(env->Penvstretch); + e2forcedrelease->value(env->Pforcedrelease); + break; + case(4): + e3aval->value(env->PA_val); + e3adt->value(env->PA_dt); + e3dval->value(env->PD_val); + e3ddt->value(env->PD_dt); + e3rdt->value(env->PR_dt); + e3rval->value(env->PR_val); + e3envstretch->value(env->Penvstretch); + e3forcedrelease->value(env->Pforcedrelease); + break; + case(5): + e4aval->value(env->PA_val); + e4adt->value(env->PA_dt); + e4rdt->value(env->PR_dt); + e4rval->value(env->PR_val); + e4envstretch->value(env->Penvstretch); + e4forcedrelease->value(env->Pforcedrelease); + break; + default: + break; + }; +}else{ + envfree->redraw(); +}; + + +envADSR->hide(); +envASR->hide(); +envADSRfilter->hide(); +envASRbw->hide(); +envfree->hide(); + +if (env->Pfreemode==0){ + switch(env->Envmode){ + case(1): + case(2): + envwindow=envADSR; + break; + case(3): + envwindow=envASR; + break; + case(4): + envwindow=envADSRfilter; + break; + case(5): + envwindow=envASRbw; + break; + default: + break; + }; +}else{ + envwindow=envfree; +}; + +envwindow->resize(this->x(),this->y(),this->w(),this->h()); + +envwindow->show();} {} + } + decl {EnvelopeParams *env;} {} + decl {Fl_Group *envwindow;} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/FilterUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/FilterUI.fl new file mode 100644 index 000000000..83fa1ef2d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/FilterUI.fl @@ -0,0 +1,638 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include } {} + +decl {\#include } {global +} + +decl {\#include } {global +} + +decl {\#include "../globals.h"} {public +} + +decl {\#include } {public +} + +decl {\#include "../Params/FilterParams.h"} {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "PresetsUI.h"} {public +} + +decl {\#include "common.H"} {public +} + +class FormantFilterGraph {open : {public Fl_Box} +} { + Function {FormantFilterGraph(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {} { + code {pars=NULL; +nvowel=NULL; +nformant=NULL; +graphpoints=NULL;} {} + } + Function {init(FilterParams *pars_,int *nvowel_,int *nformant_)} {} { + code {pars=pars_; +nvowel=nvowel_; +nformant=nformant_; +oldx=-1; +graphpoints=new float [w()];} {} + } + Function {draw_freq_line(float freq,int type)} {} { + code {float freqx=pars->getfreqpos(freq); +switch(type){ + case 0:fl_line_style(FL_SOLID);break; + case 1:fl_line_style(FL_DOT);break; + case 2:fl_line_style(FL_DASH);break; +}; + + +if ((freqx>0.0)&&(freqx<1.0)) + fl_line(x()+(int) (freqx*w()),y(), + x()+(int) (freqx*w()),y()+h());} {} + } + Function {draw()} {open + } { + code {int maxdB=30; +int ox=x(),oy=y(),lx=w(),ly=h(),i,oiy; +float freqx; + +fl_color(FL_BLACK); +fl_rectf(ox,oy,lx,ly); + + +//draw the lines +fl_color(FL_GRAY); + +fl_line_style(FL_SOLID); +//fl_line(ox+2,oy+ly/2,ox+lx-2,oy+ly/2); + +freqx=pars->getfreqpos(1000.0); +if ((freqx>0.0)&&(freqx<1.0)) + fl_line(ox+(int) (freqx*lx),oy, + ox+(int) (freqx*lx),oy+ly); + +for (i=1;i<10;i++){ + if(i==1){ + draw_freq_line(i*100.0,0); + draw_freq_line(i*1000.0,0); + }else + if (i==5){ + draw_freq_line(i*100.0,2); + draw_freq_line(i*1000.0,2); + }else{ + draw_freq_line(i*100.0,1); + draw_freq_line(i*1000.0,1); + }; +}; + +draw_freq_line(10000.0,0); +draw_freq_line(20000.0,1); + +fl_line_style(FL_DOT); +int GY=10;if (lyPnumformants){ + draw_freq_line(pars->getformantfreq(pars->Pvowels[*nvowel].formants[*nformant].freq),2); + +//show some information (like current formant frequency,amplitude) + char tmpstr[20]; + + snprintf(tmpstr,20,"%.2f kHz",pars->getformantfreq(pars->Pvowels[*nvowel].formants[*nformant].freq)*0.001); + fl_draw(tmpstr,ox+1,oy+1,40,12,FL_ALIGN_LEFT,NULL,0); + + snprintf(tmpstr,20,"%d dB",(int)( rap2dB(1e-9 + pars->getformantamp(pars->Pvowels[*nvowel].formants[*nformant].amp)) + pars->getgain() )); + fl_draw(tmpstr,ox+1,oy+15,40,12,FL_ALIGN_LEFT,NULL,0); + +}; + +//draw the data + +fl_color(FL_RED); +fl_line_style(FL_SOLID); + +pars->formantfilterH(*nvowel,lx,graphpoints); + +fl_line_style( FL_SOLID, 2 ); +fl_begin_line(); +oiy=(int) ((graphpoints[0]/maxdB+1.0)*ly/2.0); +for (i=1;i=0)&&(oiy>=0)&&(iyhide(); +formantparswindow->hide(); +hide(); +//delete (filterui); +delete (formantparswindow);} {} + } + Function {make_window()} {open + } { + Fl_Window filterui {open + xywh {498 346 275 70} type Double color 50 labelfont 1 + class Fl_Group visible + } { + Fl_Group filterparamswindow { + label {Filter Parameters} + xywh {0 0 275 75} box UP_FRAME color 183 labeltype ENGRAVED_LABEL labelsize 10 align 17 + code0 {set_module_parameters( o );} + } { + Fl_Choice analogfiltertypechoice { + label FilterType + callback {pars->Ptype=(int)o->value(); +pars->changed=true;} + tooltip {The Filter type} xywh {10 50 50 15} down_box BORDER_BOX labelsize 10 align 5 textsize 10 + code1 {o->value(pars->Ptype);} + } { + MenuItem {} { + label LPF1 + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label HPF1 + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LPF2 + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label HPF2 + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label BPF2 + xywh {82 82 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label NF2 + xywh {94 94 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label PkF2 + xywh {104 104 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LSh2 + xywh {114 114 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label HSh2 + xywh {124 124 100 20} labelfont 1 labelsize 10 + } + } + Fl_Choice svfiltertypechoice { + label FilterType + callback {pars->Ptype=(int)o->value(); +pars->changed=true;} + tooltip {The Filter type} xywh {10 50 50 15} down_box BORDER_BOX labelsize 10 align 5 textsize 10 + code1 {o->value(pars->Ptype);} + } { + MenuItem {} { + label 1LPF + xywh {134 134 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label 1HPF + xywh {144 144 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label 1BPF + xywh {154 154 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label 1NF + xywh {164 164 100 20} labelfont 1 labelsize 10 + } + } + Fl_Choice filtertype { + label Category + callback {switchcategory((int)o->value()); +pars->changed=true;} + tooltip {The Category of the Filter (Analog/Formantic/etc.)} xywh {10 20 60 15} down_box BORDER_BOX labelsize 10 align 5 textsize 10 + code0 {o->value(pars->Pcategory);} + } { + MenuItem {} { + label Analog + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Formant + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label StVarF + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial cfreqdial { + label {C.Freq} + callback {pars->Pfreq=(int)o->value();} + tooltip {Center Frequency of the Filter or the base position in the vowel's sequence} xywh {75 25 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(pars->Pfreq);} + class WidgetPDial + } + Fl_Dial qdial { + label Q + callback {pars->Pq=(int)o->value(); +formantfiltergraph->redraw();} + tooltip {Filter resonance or bandwidth} xywh {110 25 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(pars->Pq);} + class WidgetPDial + } + Fl_Dial freqtrdial { + label {freq.tr.} + callback {pars->Pfreqtrack=(int) o->value();} + tooltip {Filter frequency tracking (left is negative, middle is 0, and right is positive)} xywh {215 25 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(pars->Pfreqtrack);} + class WidgetPDial + } + Fl_Dial vsnsadial { + label {V.SnsA.} + callback {if (velsnsamp!=NULL) *velsnsamp=(int)o->value();} + tooltip {Velocity sensing amount of the Filter} xywh {145 25 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + class WidgetPDial + } + Fl_Dial vsnsdial { + label {V.Sns.} + callback {if (velsns!=NULL) *velsns=(int)o->value();} + tooltip {Velocity Sensing Function of the Filter} xywh {180 25 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + class WidgetPDial + } + Fl_Dial gaindial { + label gain + callback {pars->Pgain=(int)o->value(); +formantfiltergraph->redraw(); +pars->changed=true;} + tooltip {Filter output gain/damp} xywh {250 35 20 20} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(pars->Pgain);} + class WidgetPDial + } + Fl_Choice stcounter { + label St + callback {pars->Pstages=(int)o->value(); +formantfiltergraph->redraw(); +pars->changed=true;} open + tooltip {Filter stages (in order to increase dB/oct. value and the order of the filter)} xywh {235 5 35 15} down_box BORDER_BOX labelsize 10 textfont 1 textsize 10 + code1 {for (int i=0;iadd(tmp);};} + code2 {o->value(pars->Pstages);} + } {} + } + Fl_Button editbutton { + label Edit + callback {formantparswindow->show();} + xywh {15 40 50 25} labelfont 1 labelsize 11 + } + Fl_Button {} { + label C + callback {presetsui->copyArray(pars);} + xywh {186 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->pasteArray(pars,this);} + xywh {203 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + } + } + Function {make_formant_window()} {open + } { + Fl_Window formantparswindow { + label {Formant Filter Parameters} selected + xywh {518 473 700 205} type Double visible + } { + Fl_Group {} { + xywh {485 47 105 113} box THIN_UP_BOX + } { + Fl_Counter {} { + label {Formant } + callback {nformant=(int) o->value(); +update_formant_window(); +formantfiltergraph->redraw();} + xywh {545 80 40 15} type Simple labelfont 1 labelsize 10 align 4 minimum 0 maximum 127 step 1 textsize 10 + code0 {o->bounds(0,FF_MAX_FORMANTS-1);} + code1 {o->value(nformant);} + } + Fl_Counter {} { + label {Vowel no.} + callback {nvowel=(int) o->value(); +update_formant_window(); +formantfiltergraph->redraw();} + xywh {545 55 40 20} type Simple labelfont 1 labelsize 10 align 4 minimum 0 maximum 127 step 1 textfont 1 textsize 11 + code0 {o->bounds(0,FF_MAX_VOWELS-1);} + code1 {o->value(nvowel);} + } + Fl_Group formantparsgroup { + xywh {490 105 95 50} box ENGRAVED_FRAME + } { + Fl_Dial formant_freq_dial { + label freq + callback {pars->Pvowels[nvowel].formants[nformant].freq=(int) o->value(); +formantfiltergraph->redraw(); +pars->changed=true;} + tooltip {Formant frequency} xywh {495 115 25 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + class WidgetPDial + } + Fl_Dial formant_q_dial { + label Q + callback {pars->Pvowels[nvowel].formants[nformant].q=(int) o->value(); +formantfiltergraph->redraw(); +pars->changed=true;} + tooltip {Formant's Q} xywh {525 115 24 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + class WidgetPDial + } + Fl_Dial formant_amp_dial { + label amp + callback {pars->Pvowels[nvowel].formants[nformant].amp=(int) o->value(); +formantfiltergraph->redraw(); +pars->changed=true;} + tooltip {Formant amplitude} xywh {555 115 24 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + class WidgetPDial + } + } + } + Fl_Group {} { + xywh {590 47 100 113} box THIN_UP_BOX + } { + Fl_Counter {} { + label {Seq.Size} + callback {pars->Psequencesize=(int) o->value(); +update_formant_window(); +pars->changed=true;} + xywh {595 62 55 20} type Simple labelfont 1 labelsize 10 align 5 minimum 0 maximum 127 step 1 textfont 1 textsize 11 + code0 {o->bounds(1,FF_MAX_SEQUENCE-1);} + code1 {o->value(pars->Psequencesize);} + } + Fl_Counter {} { + label {S.Pos.} + callback {nseqpos=(int) o->value(); +update_formant_window(); +pars->changed=true;} + tooltip {Current position from the sequence} xywh {595 97 40 15} type Simple labelfont 1 labelsize 10 align 9 minimum 0 maximum 127 step 1 textsize 10 + code0 {o->bounds(0,FF_MAX_SEQUENCE-2);} + code1 {o->value(nseqpos);} + } + Fl_Counter vowel_counter { + label Vowel + callback {pars->Psequence[nseqpos].nvowel=(int) o->value(); +pars->changed=true;} + xywh {640 97 40 15} type Simple labelsize 10 align 1 minimum 0 maximum 127 step 1 textsize 10 + code0 {o->bounds(0,FF_MAX_VOWELS-1);} + } + Fl_Check_Button {} { + label {Neg.Input} + callback {pars->Psequencereversed=(int) o->value(); +pars->changed=true;} + tooltip {Negate the input from LFO/envelopes/etc.} xywh {625 132 60 20} down_box DOWN_BOX labelsize 10 + code0 {o->value(pars->Psequencereversed);} + } + Fl_Dial strchdial { + label Strch + callback {pars->Psequencestretch=(int) o->value(); +pars->changed=true;} + tooltip {Sequence Stretch} xywh {595 130 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->Psequencestretch);} + class WidgetPDial + } + } + Fl_Counter {} { + label {Num.Formants} + callback {pars->Pnumformants=(int) o->value(); +update_formant_window(); +pars->changed=true; +formantfiltergraph->redraw();} + xywh {485 15 65 20} type Simple labelfont 1 labelsize 10 align 5 minimum 0 maximum 127 step 1 + code0 {o->bounds(1,FF_MAX_FORMANTS);} + code1 {o->value(pars->Pnumformants);} + } + Fl_Dial frsldial { + label {Fr.Sl.} + callback {pars->Pformantslowness=(int) o->value(); +pars->changed=true;} + tooltip {Formant's Slowness (Morphing)} xywh {565 15 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->Pformantslowness);} + class WidgetPDial + } + Fl_Value_Output centerfreqvo { + label {C.f.} + callback {o->value(pars->getcenterfreq()/1000.0);} + tooltip {Center Frequency (kHz)} xywh {515 164 33 18} when 3 minimum 1 maximum 10 step 0.01 value 1 textfont 1 + code0 {o->value(pars->getcenterfreq()/1000.0);} + } + Fl_Value_Output octavesfreqvo { + label {Oct.} + callback {o->value(pars->getoctavesfreq());} + tooltip {No. of octaves} xywh {515 182 33 18} when 3 minimum 1 maximum 127 step 1 value 5 textfont 1 + code0 {o->value(pars->getoctavesfreq());} + } + Fl_Slider cfknob { + callback {pars->Pcenterfreq=(int)o->value(); +centerfreqvo->do_callback(); +formantfiltergraph->redraw(); +pars->changed=true;} + xywh {551 167 84 15} type {Horz Knob} box FLAT_BOX maximum 127 + code0 {o->value(pars->Pcenterfreq);} + } + Fl_Slider octknob { + callback {pars->Poctavesfreq=(int)o->value(); +octavesfreqvo->do_callback(); +formantfiltergraph->redraw();} + xywh {551 185 84 15} type {Horz Knob} box FLAT_BOX maximum 127 + code0 {o->value(pars->Poctavesfreq);} + } + Fl_Box formantfiltergraph { + xywh {5 5 475 195} box BORDER_BOX + code0 {o->init(pars,&nvowel,&nformant);} + class FormantFilterGraph + } + Fl_Dial wvknob { + label {Vw.Cl.} + callback {pars->Pvowelclearness=(int) o->value(); +pars->changed=true;} + tooltip {Vowel "clearness" (how the mixed vowels are avoided)} xywh {600 15 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->Pvowelclearness);} + class WidgetPDial + } + Fl_Button {} { + label Close + callback {formantparswindow->hide();} + xywh {645 180 50 25} box THIN_UP_BOX + } + Fl_Button {} { + label C + callback {presetsui->copy(pars,nvowel);} + xywh {635 25 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(pars,this,nvowel);} + xywh {665 25 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Box {} { + label Vowel + xywh {635 10 55 15} + } + } + } + Function {update_formant_window()} {open + } { + code {formant_freq_dial->value(pars->Pvowels[nvowel].formants[nformant].freq); +formant_q_dial->value(pars->Pvowels[nvowel].formants[nformant].q); +formant_amp_dial->value(pars->Pvowels[nvowel].formants[nformant].amp); +if (nformantPnumformants) formantparsgroup->activate(); + else formantparsgroup->deactivate(); + +if (nseqposPsequencesize) vowel_counter->activate(); + else vowel_counter->deactivate(); + + +vowel_counter->value(pars->Psequence[nseqpos].nvowel);} {} + } + Function {refresh()} {} { + code {update_formant_window(); +formantfiltergraph->redraw(); + +if (pars->Pcategory==0) svfiltertypechoice->value(pars->Ptype); +if (pars->Pcategory==2) analogfiltertypechoice->value(pars->Ptype); + +filtertype->value(pars->Pcategory); + +cfreqdial->value(pars->Pfreq); +qdial->value(pars->Pq); + +freqtrdial->value(pars->Pfreqtrack); +gaindial->value(pars->Pgain); + +stcounter->value(pars->Pstages); + +int categ=pars->Pcategory; +if ((categ==0)||(categ==2)) { + if (categ==0) { + analogfiltertypechoice->show(); + svfiltertypechoice->hide(); + } else { + svfiltertypechoice->show(); + analogfiltertypechoice->hide(); + }; + editbutton->hide(); + formantparswindow->hide(); + cfreqdial->label("C.freq"); +} else { + analogfiltertypechoice->hide(); + svfiltertypechoice->hide(); + editbutton->show(); + cfreqdial->label("BS.pos"); +}; + +filterparamswindow->redraw();} {} + } + Function {init(FilterParams *filterpars_,unsigned char *velsnsamp_,unsigned char *velsns_)} {} { + code {pars=filterpars_; +velsnsamp=velsnsamp_; +velsns=velsns_; + +make_window(); +end(); +make_formant_window(); + + +filterui->resize(this->x(),this->y(),this->w(),this->h()); + + +if (velsnsamp==NULL){ + vsnsadial->deactivate(); + vsnsadial->value(127); + } else vsnsadial->value(*velsnsamp); + +if (velsns==NULL){ + vsnsdial->deactivate(); + vsnsdial->value(127); + } else vsnsdial->value(*velsns); + +switchcategory(pars->Pcategory); + + +formantparswindow->label(this->label()); + +update_formant_window();} {} + } + Function {switchcategory(int newcat)} {open + } { + code {if (pars->Pcategory!=newcat){ + pars->Pgain=64; + gaindial->value(64); + analogfiltertypechoice->value(0); + analogfiltertypechoice->do_callback(); + svfiltertypechoice->value(0); + svfiltertypechoice->do_callback(); +}; +pars->Pcategory=newcat; + +refresh();} {} + } + Function {use_for_dynamic_filter()} {open + } { + code {freqtrdial->deactivate(); +gaindial->when(0); + +cfknob->when(FL_WHEN_RELEASE); +octknob->when(FL_WHEN_RELEASE); + +frsldial->when(0); +wvknob->when(0); +formant_freq_dial->when(0); +formant_q_dial->when(0); +formant_amp_dial->when(0); +strchdial->when(0);} {} + } + decl {FilterParams *pars;} {} + decl {unsigned char *velsnsamp,*velsns;} {} + decl {int nvowel,nformant,nseqpos;} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/LFOUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/LFOUI.fl new file mode 100644 index 000000000..889a38611 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/LFOUI.fl @@ -0,0 +1,181 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "../globals.h"} {public +} + +decl {\#include } {public +} + +decl {\#include "../Params/LFOParams.h"} {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "PresetsUI.h"} {public +} + +decl {\#include "common.H"} {public +} + +class LFOUI {open : {public Fl_Group, PresetsUI_} +} { + Function {LFOUI(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {pars=NULL;} {} + } + Function {~LFOUI()} {} { + code {lfoui->hide(); +hide(); +//delete (lfoui);} {} + } + Function {make_window()} {open + } { + Fl_Window lfoui {open selected + xywh {630 351 230 70} type Double color 50 labelfont 1 + class Fl_Group visible + } { + Fl_Group lfoparamswindow { + label LFO + xywh {0 0 230 70} box UP_BOX color 223 labeltype ENGRAVED_LABEL labelsize 10 align 17 + code0 {set_module_parameters(o);} + } { + Fl_Dial freq { + label {Freq.} + callback {pars->Pfreq=o->value();} + tooltip {LFO Frequency} xywh {5 20 30 30} box ROUND_UP_BOX labelsize 10 step 1e-05 + class WidgetPDial + } + Fl_Dial intensity { + label Depth + callback {pars->Pintensity=(int)o->value();} + tooltip {LFO Amount} xywh {40 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + class WidgetPDial + } + Fl_Dial delay { + label Delay + callback {pars->Pdelay=(int)o->value();} + tooltip {LFO delay} xywh {110 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + class WidgetPDial + } + Fl_Dial startphase { + label Start + callback {pars->Pstartphase=(int)o->value();} + tooltip {LFO Startphase (leftmost is Random)} xywh {75 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + class WidgetPDial + } + Fl_Dial randomness { + label {A.R.} + callback {pars->Prandomness=(int)o->value();} + tooltip {LFO Amplitude Randomness} xywh {180 7 20 20} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + class WidgetPDial + } + Fl_Choice LFOtype { + label Type + callback {pars->PLFOtype=(int)o->value();} + tooltip {LFO function} xywh {180 40 45 15} down_box BORDER_BOX labelsize 10 align 2 textsize 8 + } { + MenuItem {} { + label SINE + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label TRI + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label SQR + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {R.up} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {R.dn} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label E1dn + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label E2dn + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + } + Fl_Check_Button continous { + label {C.} + callback {pars->Pcontinous=(int)o->value();} + tooltip {Continous LFO} xywh {165 35 15 15} down_box DOWN_BOX labelsize 10 align 2 + } + Fl_Dial freqrand { + label {F.R.} + callback {pars->Pfreqrand=(int)o->value();} + tooltip {LFO Frequency Randomness} xywh {205 7 20 20} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + class WidgetPDial + } + Fl_Dial stretch { + label {Str.} + callback {pars->Pstretch=(int)o->value();} + tooltip {LFO stretch} xywh {144 30 20 20} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + class WidgetPDial + } + Fl_Button {} { + label C + callback {presetsui->copy(pars);} + xywh {145 10 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(pars,this);} + xywh {162 10 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + } + } + } + } + Function {refresh()} {} { + code {freq->value(pars->Pfreq); +intensity->value(pars->Pintensity); +startphase->value(pars->Pstartphase); +delay->value(pars->Pdelay); +continous->value(pars->Pcontinous); +stretch->value(pars->Pstretch); +randomness->value(pars->Prandomness); +freqrand->value(pars->Pfreqrand); +LFOtype->value(pars->PLFOtype);} {} + } + Function {init(LFOParams *lfopars_)} {} { + code {pars=lfopars_; + +make_window(); +end(); + +refresh(); + +lfoui->resize(this->x(),this->y(),this->w(),this->h()); + +lfoparamswindow->label(this->label());} {} + } + decl {LFOParams *pars;} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/MasterUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/MasterUI.fl new file mode 100644 index 000000000..0a163a9fa --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/MasterUI.fl @@ -0,0 +1,1889 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2009 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include "ADnoteUI.h"} {public +} + +decl {\#include "SUBnoteUI.h"} {public +} + +decl {\#include "EffUI.h"} {public +} + +decl {\#include "VirKeyboard.h"} {public +} + +decl {\#include "ConfigUI.h"} {public +} + +decl {\#include "BankUI.h"} {public +} + +decl {\#include "PartUI.h"} {public +} + +decl {\#include "MicrotonalUI.h"} {public +} + +decl {\#include "PresetsUI.h"} {public +} + +decl {\#include "NioUI.h"} {public global +} + +decl {\#include "../Misc/Master.h"} {public +} + +decl {\#include "../Misc/Part.h"} {public +} + +decl {\#include "../Misc/Util.h"} {public +} + +decl {\#include "common.H"} {public +} + +decl {\#if USE_NSM +\#include "NSM.H" +extern NSM_Client *nsm; +\#endif} {public +} + +decl {\#include "../globals.h"} {public +} + +class VUMeter {: {public Fl_Box} +} { + Function {VUMeter(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {} { + code {master=NULL; +npart=-1;} {} + } + Function {init(Master *master_,int part_)} {} { + code {//the "part_" parameters sets the part (if it is >=0), else it sets the master +master=master_; +label(NULL); +npart=part_; +olddbl=0.0; +olddbr=0.0; +oldrmsdbl=0.0; +oldrmsdbr=0.0;} {} + } + Function {draw_master()} {} { + code {\#define MIN_DB (-48) + +int ox=x(); int oy=y(); int lx=w(); int ly=h(); + +vuData data = master->getVuData(); + +//pthread_mutex_lock(&master->mutex); +float dbl=rap2dB(data.outpeakl); +float dbr=rap2dB(data.outpeakr); +float rmsdbl=rap2dB(data.rmspeakl); +float rmsdbr=rap2dB(data.rmspeakr); +float maxdbl=rap2dB(data.maxoutpeakl); +float maxdbr=rap2dB(data.maxoutpeakr); +int clipped=data.clipped; +//pthread_mutex_unlock(&master->mutex); + +dbl=(MIN_DB-dbl)/MIN_DB; +if (dbl<0.0) dbl=0.0; + else if (dbl>1.0)dbl=1.0; + +dbr=(MIN_DB-dbr)/MIN_DB; +if (dbr<0.0) dbr=0.0; + else if (dbr>1.0) dbr=1.0; + +dbl=dbl*0.4+olddbl*0.6; +dbr=dbr*0.4+olddbr*0.6; + +if ( damage() & FL_DAMAGE_USER1 ) +{ + if ( olddbl == dbl && olddbr == dbr ) + return; +} + +olddbl=dbl; +olddbr=dbr; + +\#define VULENX (lx-35) +\#define VULENY (ly/2-3) + +dbl*=VULENX;dbr*=VULENX; + +int idbl=(int) dbl; +int idbr=(int) dbr; + +//compute RMS - start +rmsdbl=(MIN_DB-rmsdbl)/MIN_DB; +if (rmsdbl<0.0) rmsdbl=0.0; + else if (rmsdbl>1.0) rmsdbl=1.0; + +rmsdbr=(MIN_DB-rmsdbr)/MIN_DB; +if (rmsdbr<0.0) rmsdbr=0.0; + else if (rmsdbr>1.0) rmsdbr=1.0; + +rmsdbl=rmsdbl*0.4+oldrmsdbl*0.6; +rmsdbr=rmsdbr*0.4+oldrmsdbr*0.6; + +oldrmsdbl=rmsdbl; +oldrmsdbr=rmsdbr; + + +rmsdbl*=VULENX;rmsdbr*=VULENX; + +int irmsdbl=(int) rmsdbl; +int irmsdbr=(int) rmsdbr; +//compute RMS - end + + + +//draw the vu-meter lines +//db +fl_rectf(ox,oy,idbr,VULENY,0,200,255); +fl_rectf(ox,oy+ly/2,idbl,VULENY,0,200,255); +//black +fl_rectf(ox+idbr,oy,VULENX-idbr,VULENY,0,0,0); +fl_rectf(ox+idbl,oy+ly/2,VULENX-idbl,VULENY,0,0,0); + +//draw the scales +float tmp=VULENX*1.0/MIN_DB; +for (int i=1;i<1-MIN_DB;i++){ + int tx=VULENX+(int) (tmp*i); + fl_rectf(ox+tx,oy,1,VULENY+ly/2,0,160,200); + if (i%5==0) fl_rectf(ox+tx,oy,1,VULENY+ly/2,0,230,240); + if (i%10==0) fl_rectf(ox+tx-1,oy,2,VULENY+ly/2,0,225,255); +}; + +//rms +if (irmsdbr>2) fl_rectf(ox+irmsdbr-1,oy,3,VULENY,255,255,0); +if (irmsdbl>2) fl_rectf(ox+irmsdbl-1,oy+ly/2,3,VULENY,255,255,0); + + +//draw the red box if clipping has occured +if (clipped==0) fl_rectf(ox+VULENX+2,oy+1,lx-VULENX-3,ly-4,0,0,10); + else fl_rectf(ox+VULENX+2,oy+1,lx-VULENX-3,ly-4,250,10,10); + +//draw the maxdB +fl_font(FL_HELVETICA|FL_BOLD,10); +fl_color(255,255,255); +char tmpstr[10]; +if ((maxdbl>MIN_DB-20)){ + snprintf((char *)&tmpstr,10,"%ddB",(int)maxdbr); + fl_draw(tmpstr,ox+VULENX+1,oy+1,lx-VULENX-1,VULENY,FL_ALIGN_RIGHT,NULL,0); +}; +if ((maxdbr>MIN_DB-20)){ + snprintf((char *)&tmpstr,10,"%ddB",(int)maxdbl); + fl_draw(tmpstr,ox+VULENX+1,oy+ly/2+1,lx-VULENX-1,VULENY,FL_ALIGN_RIGHT,NULL,0); +};} {} + } + Function {draw_part()} {} { + code {\#define MIN_DB (-48) +int ox=x(); int oy=y(); int lx=w(); int ly=h(); + +if (!active_r()){ + pthread_mutex_lock(&master->vumutex); + int fakedb=master->fakepeakpart[npart]; + pthread_mutex_unlock(&master->vumutex); + fl_rectf(ox,oy,lx,ly,140,140,140); + if (fakedb>0){ + fakedb=(int)(fakedb/255.0*ly)+4; + fl_rectf(ox+2,oy+ly-fakedb,lx-4,fakedb,0,0,0); + }; + + return; +}; + +//draw the vu lines +pthread_mutex_lock(&master->vumutex); + float db=rap2dB(master->vuoutpeakpart[npart]); +pthread_mutex_unlock(&master->vumutex); + +db=(MIN_DB-db)/MIN_DB; +if (db<0.0) db=0.0; + else if (db>1.0) db=1.0; + +db*=ly-2; + +int idb=(int) db; + +fl_rectf(ox,oy+ly-idb,lx,idb,0,200,255); +fl_rectf(ox,oy,lx,ly-idb,0,0,0); + + +//draw the scales +float tmp=ly*1.0/MIN_DB; + for (int i=1;i<1-MIN_DB;i++){ + int ty=ly+(int) (tmp*i); + if (i%5==0) fl_rectf(ox,oy+ly-ty,lx,1,0,160,200); + if (i%10==0) fl_rectf(ox,oy+ly-ty,lx,1,0,230,240); +};} {} + } + Function {draw()} {} { + code {if (npart>=0) draw_part(); + else draw_master();} {} + } + Function {tickdraw(VUMeter *o)} {return_type {static void} + } { + code {o->damage(FL_DAMAGE_USER1);} {} + } + Function {tick(void *v)} {return_type {static void} + } { + code {tickdraw((VUMeter *) v); + Fl::repeat_timeout(1.0/18.0,tick,v);//18 fps} {} + } + Function {handle(int event)} {return_type int + } { + code {switch(event){ + case FL_SHOW: + Fl::add_timeout(1.0/18.0,tick,this); + break; + case FL_HIDE: + Fl::remove_timeout(tick,this); + break; + case FL_PUSH: + if (npart>=0) break; + pthread_mutex_lock(&master->mutex); + master->vuresetpeaks(); + pthread_mutex_unlock(&master->mutex); + break; +}; +return(1);} {} + } + decl {Master *master;} {} + decl {int npart;} {} + decl {float olddbl,olddbr;} {} + decl {float oldrmsdbl,oldrmsdbr;} {} +} + +class SysEffSend {open : {public WidgetPDial} +} { + Function {SysEffSend(int x,int y, int w, int h, const char *label=0):WidgetPDial(x,y,w,h,label)} {} { + code {master=NULL; +neff1=0; +neff2=0;} {} + } + Function {init(Master *master_,int neff1_,int neff2_)} {} { + code {neff1=neff1_; +neff2=neff2_; +master=master_; +minimum(0); +maximum(127); +step(1); +labelfont(1); +labelsize(10); +align(FL_ALIGN_TOP); + +value(master->Psysefxsend[neff1][neff2]); +char tmp[20];snprintf(tmp,20,"%d->%d",neff1+1,neff2+1); +this->copy_label(tmp);} {} + } + Function {~SysEffSend()} {} { + code {hide();} {} + } + Function {handle(int event)} {return_type int + } { + code {if ((event==FL_PUSH) || (event==FL_DRAG)){ + master->setPsysefxsend(neff1,neff2,(int) value()); +}; + +return(WidgetPDial::handle(event));} {} + } + decl {Master *master;} {} + decl {int neff1;} {} + decl {int neff2;} {} +} + +class Panellistitem {open : {public Fl_Group} +} { + Function {make_window()} {open private + } { + Fl_Window panellistitem {open + private xywh {608 711 100 260} type Double box NO_BOX + class Fl_Group visible + } { + Fl_Group panellistitemgroup {open + private xywh {0 20 70 240} box UP_FRAME + code0 {if (master->part[npart]->Penabled==0) o->deactivate();} + code1 {set_module_parameters( o );} + } { + Fl_Group {} { + xywh {45 65 15 110} box ENGRAVED_FRAME + } { + Fl_Box {} { + label {V U} + xywh {45 65 15 110} box FLAT_BOX color 0 selection_color 75 labelcolor 55 align 128 + code0 {o->init(master,npart);} + class VUMeter + } + } + Fl_Button partname { + label { } + callback {if ((int)bankui->cbwig->value()!=(npart+1)){ + bankui->cbwig->value(npart+1); + bankui->cbwig->do_callback(); +}; +bankui->show();} + xywh {5 27 60 30} box THIN_DOWN_BOX down_box FLAT_BOX labelfont 1 labelsize 10 align 208 + } + Fl_Slider partvolume { + callback {master->part[npart]->setPvolume((int) o->value());} + xywh {10 65 30 110} type {Vert Knob} box NO_BOX minimum 127 maximum 0 step 1 value 127 + code0 {o->value(master->part[npart]->Pvolume);} + } + Fl_Dial partpanning { + callback {master->part[npart]->setPpanning((int) o->value());} + xywh {20 180 30 30} maximum 127 step 1 + code0 {o->value(master->part[npart]->Ppanning);} + class WidgetPDial + } + Fl_Button {} { + label edit + callback {if ((int)bankui->cbwig->value()!=(npart+1)){ + bankui->cbwig->value(npart+1); + bankui->cbwig->do_callback(); +};} + xywh {15 235 40 20} labelsize 10 + } + Fl_Choice partrcv { + callback {master->part[npart]->Prcvchn=(int) o->value();} + tooltip {receive from Midi channel} xywh {10 213 50 15} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 + code0 {char nrstr[10]; for(int i=0;iadd(nrstr); else o->add("Dr10");};} + code1 {o->value(master->part[npart]->Prcvchn);} + } {} + } + Fl_Check_Button partenabled { + label 01 + callback {pthread_mutex_lock(&master->mutex); + master->partonoff(npart,(int) o->value()); +pthread_mutex_unlock(&master->mutex); + +if ((int) o->value()==0) panellistitemgroup->deactivate(); + else { + panellistitemgroup->activate(); + if ((int)bankui->cbwig->value()!=(npart+1)){ + bankui->cbwig->value(npart+1); + bankui->cbwig->do_callback(); + }; +}; + +o->redraw();} + private xywh {5 0 45 20} down_box DOWN_BOX labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 24 + code0 {char tmp[10];snprintf(tmp,10,"%d",npart+1);o->copy_label(tmp);} + code1 {o->value(master->part[npart]->Penabled);} + } + } + } + Function {Panellistitem(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {npart=0; +master=NULL; +bankui=NULL;} {} + } + Function {init(Master *master_, int npart_,BankUI *bankui_)} {} { + code {npart=npart_; +master=master_; +bankui=bankui_; + +make_window(); +panellistitem->show(); +end();} {} + } + Function {refresh()} {} { + code {partenabled->value(master->part[npart]->Penabled); +if (master->part[npart]->Penabled!=0) panellistitemgroup->activate(); + else panellistitemgroup->deactivate(); + +partvolume->value(master->part[npart]->Pvolume); +partpanning->value(master->part[npart]->Ppanning); +partrcv->value(master->part[npart]->Prcvchn); + +partname->label((char *)master->part[npart]->Pname); + +if ((int)bankui->cbwig->value()!=(npart+1)) + panellistitemgroup->color(fl_rgb_color(160,160,160)); +else + panellistitemgroup->color(fl_rgb_color(50,190,240)); + +panellistitemgroup->redraw();} {} + } + Function {~Panellistitem()} {} { + code {panellistitem->hide(); +//delete(panellistitem);} {} + } + decl {int npart;} {} + decl {Master *master;} {} + decl {BankUI *bankui;} {} +} + +class MasterUI {open +} { + Function {make_window()} {open + } { + Fl_Window masterwindow { + label zynaddsubfx + callback {if (( +\#ifdef PLUGINVERSION +1 +\#elif USE_NSM +(nsm && nsm->is_active()) +\#else +0 +\#endif + || fl_choice("Exit and leave the unsaved data?","No","Yes",NULL))) { + config.save(); + *exitprogram=1; +}; +} open + xywh {80 370 390 525} type Double xclass zynaddsubfx visible + } { + Fl_Menu_Bar mastermenu { + xywh {-5 0 690 25} + } { + Submenu {} { + label {&File} + xywh {0 0 100 20} + } { + MenuItem {} { + label {&New (erase all)...} + callback {do_new_master();} + xywh {20 20 100 20} + } + MenuItem {} { + label {&Open Parameters...} + callback {\#if USE_NSM + if ( nsm && nsm->is_active() ) + { + do_load_master(); + do_save_master( nsm->project_filename ); + } + else +\#endif + { + do_load_master(); + }} + xywh {20 20 100 20} + } + MenuItem {} { + label {&Save All Parameters...} + callback {\#if USE_NSM + if ( nsm && nsm->is_active() ) + { + do_save_master( nsm->project_filename ); + } + else +\#endif + { + do_save_master(); + }} + xywh {10 10 100 20} divider + } + MenuItem {} { + label {&Load Scale Settings...} + callback {char *filename; +filename=fl_file_chooser("Open:","({*.xsz})",NULL,0); +if (filename==NULL) return; + +pthread_mutex_lock(&master->mutex); + //clear all parameters + master->microtonal.defaults(); + + //load the data + int result=master->microtonal.loadXML(filename); +pthread_mutex_unlock(&master->mutex); + + + delete microtonalui; + microtonalui=new MicrotonalUI(&master->microtonal); + +if (result==-10) fl_alert("Error: Could not load the file\\nbecause it is not a scale file."); + else if (result<0) fl_alert("Error: Could not load the file.");} + xywh {35 35 100 20} + } + MenuItem {} { + label {Save Sc&ale Settings ..} + callback {char *filename; +int result=0; + +filename=fl_file_chooser("Save:","({*.xsz})",NULL,0); +if (filename==NULL) return; +filename=fl_filename_setext(filename,".xsz"); + +result=fileexists(filename); +if (result) { + result=0; + if (!fl_choice("The file exists. \\nOverwrite it?","No","Yes",NULL)) return; + +}; + + +pthread_mutex_lock(&master->mutex); +result=master->microtonal.saveXML(filename); +pthread_mutex_unlock(&master->mutex); + +if (result<0) fl_alert("Error: Could not save the file."); + + +updatepanel();} + xywh {25 25 100 20} + } + MenuItem {} { + label {Show Scale Settings...} + callback {microtonalui->show();} + xywh {0 0 100 20} divider + } + MenuItem {} { + label {&Settings...} + callback {configui->show();} + xywh {25 25 100 20} + } + MenuItem {} { + label {N&io Settings} + callback {nioui.refresh(); +nioui.show();} + xywh {0 0 36 21} divider + } + MenuItem {} { + label {&Copyright...} + callback {aboutwindow->show();} + xywh {15 15 100 20} divider + } + MenuItem {} { + label {E&xit} + callback {masterwindow->do_callback();} + xywh {10 10 100 20} + } + } + Submenu {} { + label {&Instrument} + xywh {10 10 100 20} + } { + MenuItem {} { + label {&Clear Instrument...} + callback {if (fl_choice("Clear instrument's parameters ?","No","Yes",NULL)){ +// int npart=(int)npartcounter->value()-1; + pthread_mutex_lock(&master->mutex); + master->part[npart]->defaultsinstrument(); + pthread_mutex_unlock(&master->mutex); + + npartcounter->do_callback(); +}; + +updatepanel();} + xywh {35 35 100 20} + } + MenuItem {} { + label {&Open Instrument...} + callback {const char *filename; +filename=fl_file_chooser("Load:","({*.xiz})",NULL,0); +if (filename==NULL) return; + + +pthread_mutex_lock(&master->mutex); +// int npart=(int)npartcounter->value()-1; + + //clear all instrument parameters, first + master->part[npart]->defaultsinstrument(); + + //load the instr. parameters + int result=master->part[npart]->loadXMLinstrument(filename); + +pthread_mutex_unlock(&master->mutex); +master->part[npart]->applyparameters(); + +npartcounter->do_callback(); +updatepanel(); + +if (result==-10) fl_alert("Error: Could not load the file\\nbecause it is not an instrument file."); + else if (result<0) fl_alert("Error: Could not load the file.");} + xywh {30 30 100 20} + } + MenuItem {} { + label {&Save Instrument ...} + callback {char *filename; + +filename=fl_file_chooser("Save:","({*.xiz})",NULL,0); +if (filename==NULL) return; +filename=fl_filename_setext(filename,".xiz"); + +int result=fileexists(filename); +if (result) { + result=0; + if (!fl_choice("The file exists. \\nOverwrite it?","No","Yes",NULL)) return; + +}; + + +pthread_mutex_lock(&master->mutex); +result=master->part[npart]->saveXML(filename); +pthread_mutex_unlock(&master->mutex); + +if (result<0) fl_alert("Error: Could not save the file."); + +updatepanel();} + xywh {20 20 100 20} divider + } + MenuItem {} { + label {Show Instrument &Bank...} + callback {bankui->show();} + xywh {0 0 100 20} divider + } + MenuItem {} { + label {&Virtual Keyboard...} + callback {virkeyboard->show();} + xywh {10 10 100 20} + } + } + Submenu recordmenu { + label {&Record} + xywh {0 0 100 20} + } { + MenuItem {} { + label {&Choose WAV file...} + callback {char *filename; +recordbutton->deactivate(); +pausebutton->deactivate(); +pauselabel->deactivate(); +stopbutton->deactivate(); +filename=fl_file_chooser("Record to audio file:","(*.wav)",NULL,0); +if (filename==NULL) return; +fl_filename_setext(filename,".wav"); + +int result=master->HDDRecorder.preparefile(filename,0); +if (result==1) { + result=0; + if (fl_choice("The file exists. \\nOverwrite it?","No","Yes",NULL)) + master->HDDRecorder.preparefile(filename,1); +}; +if (result==0) recordbutton->activate(); + +if (result!=0) fl_alert("Error: Could not save the file.");} + xywh {0 0 100 20} + } + } + Submenu {} { + label Misc + xywh {10 10 100 20} + } { + MenuItem {} { + label {Switch User Interface Mode} + callback {if (fl_choice("Switch the User Interface to Beginner mode ?","No","Yes",NULL)){ + masterwindow->hide(); + refresh_master_ui(); + simplemasterwindow->show(); + config.cfg.UserInterfaceMode=2; +};} + xywh {10 10 100 20} + } + } + } + Fl_Dial mastervolumedial { + label {Master Volume} + callback {master->setPvolume((int) o->value());} + tooltip {Master Volume} xywh {15 32 55 55} box ROUND_UP_BOX labelsize 9 align 130 maximum 127 step 1 + code0 {o->value(master->Pvolume);} + class WidgetPDial + } + Fl_Counter masterkeyshiftcounter { + label {Master KeyShift} + callback {master->setPkeyshift((int) o->value()+64);} + xywh {150 97 120 23} type Simple labelsize 9 minimum -64 maximum 64 step 1 + code0 {o->lstep(12);} + code1 {o->value(master->Pkeyshift-64);} + } + Fl_Button {} { + label {Panic!} + callback {virkeyboard->relaseallkeys(); +pthread_mutex_lock(&master->mutex); +master->shutup=1; +pthread_mutex_unlock(&master->mutex);} + xywh {280 29 105 53} color 90 labelfont 1 + } + Fl_Group partuigroup {open + xywh {0 310 390 205} + } { + Fl_Group partui {open selected + xywh {0 310 383 175} + code0 {o->init(master->part[0],master,0,bankui);} + code1 {o->show();} + class PartUI + } {} + } + Fl_Tabs {} {open + xywh {0 145 390 165} box UP_FRAME + } { + Fl_Group {} { + label {System Effects} open + xywh {0 162 390 145} labelsize 15 align 9 + } { + Fl_Counter syseffnocounter { + label {Sys.Effect No.} + callback {nsyseff=(int) o->value()-1; +sysefftype->value(master->sysefx[nsyseff]->geteffect()); +syseffectui->refresh(master->sysefx[nsyseff]);} + xywh {5 181 80 22} type Simple labelfont 1 labelsize 10 align 1 minimum 0 maximum 127 step 1 value 1 textfont 1 + code0 {o->bounds(1,NUM_SYS_EFX);} + code1 {o->value(nsyseff+1);} + } + Fl_Choice sysefftype { + label EffType + callback {pthread_mutex_lock(&master->mutex); +master->sysefx[nsyseff]->changeeffect((int) o->value()); +pthread_mutex_unlock(&master->mutex); +syseffectui->refresh(master->sysefx[nsyseff]);} + xywh {285 176 100 22} down_box BORDER_BOX labelsize 10 + code0 {o->value(master->sysefx[nsyseff]->geteffect());} + } { + MenuItem {} { + label {No Effect} + xywh {10 10 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Reverb + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Echo + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Chorus + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Phaser + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label AlienWah + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Distortion + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label EQ + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label DynFilter + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + } + Fl_Group syseffectuigroup {open + xywh {5 203 380 95} color 48 + } { + Fl_Group syseffectui { + xywh {5 203 380 95} + code0 {o->init(master->sysefx[nsyseff]);} + class EffUI + } {} + } + Fl_Button {} { + label {Send to...} + callback {syseffsendwindow->show();} + xywh {90 181 85 22} box THIN_UP_BOX labelfont 1 labelsize 11 + } + Fl_Button {} { + label C + callback {presetsui->copy(master->sysefx[nsyseff]);} + xywh {180 187 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button {} { + label P + callback {pthread_mutex_lock(&master->mutex); +presetsui->paste(master->sysefx[nsyseff],syseffectui); +pthread_mutex_unlock(&master->mutex);} + xywh {210 187 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + } + Fl_Group {} { + label {Insertion Effects} open + xywh {0 165 390 145} labelsize 15 align 9 hide + } { + Fl_Counter inseffnocounter { + label {Ins.Effect No.} + callback {ninseff=(int) o->value()-1; +insefftype->value(master->insefx[ninseff]->geteffect()); +inseffpart->value(master->Pinsparts[ninseff]+2); +inseffectui->refresh(master->insefx[ninseff]); + +if (master->Pinsparts[ninseff]!=-1) { + insefftype->activate(); + inseffectui->activate(); + inseffectuigroup->activate(); +} else { + insefftype->deactivate(); + inseffectui->deactivate(); + inseffectuigroup->deactivate(); +};} + xywh {5 183 80 22} type Simple labelfont 1 labelsize 10 align 1 minimum 0 maximum 127 step 1 value 1 textfont 1 + code0 {o->bounds(1,NUM_INS_EFX);} + code1 {o->value(ninseff+1);} + } + Fl_Choice insefftype { + label EffType + callback {pthread_mutex_lock(&master->mutex); +master->insefx[ninseff]->changeeffect((int) o->value()); +pthread_mutex_unlock(&master->mutex); +inseffectui->refresh(master->insefx[ninseff]); +inseffectui->show();} + xywh {285 173 100 22} down_box BORDER_BOX labelsize 10 + code0 {o->value(master->insefx[ninseff]->geteffect());} + code1 {if (master->Pinsparts[ninseff]== -1) o->deactivate();} + } { + MenuItem {} { + label {No Effect} + xywh {25 25 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Reverb + xywh {35 35 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Echo + xywh {45 45 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Chorus + xywh {55 55 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Phaser + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label AlienWah + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Distortion + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label EQ + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label DynFilter + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + } + Fl_Group inseffectuigroup {open + xywh {5 205 380 95} box FLAT_BOX color 48 + } { + Fl_Group inseffectui { + xywh {5 205 380 90} box UP_FRAME + code0 {o->init(master->insefx[ninseff]);} + code1 {if (master->Pinsparts[ninseff]== -1) o->deactivate();} + class EffUI + } {} + } + Fl_Choice inseffpart { + label {Insert To.} + callback {master->Pinsparts[ninseff]=(int) o->value()-2; +if ((int) o->value()==1){ + inseffectuigroup->deactivate(); + insefftype->deactivate(); + inseffectui->deactivate(); +} else { + inseffectuigroup->activate(); + insefftype->activate(); + inseffectui->activate(); +}; +master->insefx[ninseff]->cleanup();} open + xywh {95 183 80 22} down_box BORDER_BOX labelfont 1 labelsize 10 align 5 textsize 10 + code0 {o->add("Master Out");o->add("Off");} + code1 {char tmp[50]; for (int i=0;iadd(tmp);};} + code3 {o->value(master->Pinsparts[ninseff]+2);} + } {} + Fl_Button {} { + label C + callback {presetsui->copy(master->insefx[ninseff]);} + xywh {180 185 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button {} { + label P + callback {pthread_mutex_lock(&master->mutex); +presetsui->paste(master->insefx[ninseff],inseffectui); +pthread_mutex_unlock(&master->mutex);} + xywh {210 185 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + } + } + Fl_Button {} { + label Scales + callback {microtonalui->show();} + xywh {320 87 65 23} color 51 labelfont 1 + } + Fl_Group {} { + xywh {150 40 117 45} box UP_FRAME + } { + Fl_Button recordbutton { + label {Rec.} + callback {o->deactivate(); +recordmenu->deactivate(); +recordmenu->label("&Record(*)"); +stopbutton->activate(); +pausebutton->activate(); +pauselabel->activate(); +master->HDDRecorder.start(); +master->vuresetpeaks(); +mastermenu->redraw();} + tooltip {Start Recording} xywh {159 46 21 21} box ROUND_UP_BOX color 88 labelfont 1 labelsize 10 align 2 deactivate + } + Fl_Button stopbutton { + label Stop + callback {o->deactivate(); +master->HDDRecorder.stop(); +recordbutton->deactivate(); +pausebutton->deactivate(); +pauselabel->deactivate(); +recordmenu->activate(); +recordmenu->label("&Record"); +mastermenu->redraw();} + tooltip {Stop Recording and close the audio file} xywh {237 46 21 21} box THIN_UP_BOX color 4 labelfont 1 labelsize 10 align 2 deactivate + } + Fl_Button pausebutton { + label {@||} + callback {o->deactivate(); +master->HDDRecorder.pause(); +recordbutton->activate(); +mastermenu->redraw();} + tooltip {Pause Recording} xywh {198 46 21 21} box THIN_UP_BOX color 4 selection_color 4 labelfont 1 labelcolor 3 align 16 deactivate + } + Fl_Box pauselabel { + label Pause + xywh {192 66 30 15} labelfont 1 labelsize 10 deactivate + } + } + Fl_Group {} {open + xywh {1 490 389 55} + } { + Fl_Box {} { + label {VU-Meter} + xywh {5 490 380 30} box FLAT_BOX color 48 selection_color 75 + code0 {o->init(master,-1);} + class VUMeter + } + } + Fl_Check_Button nrpnbutton { + label NRPN + callback {master->ctl.NRPN.receive=(int) o->value();} + tooltip {Receive NRPNs} xywh {10 115 60 25} down_box DOWN_BOX labelsize 12 + code0 {o->value(master->ctl.NRPN.receive);} + } + Fl_Counter npartcounter { + callback {int nval=(int) o->value()-1; +partuigroup->remove(partui); +delete partui; +partui=new PartUI(0,0,765,525); +partuigroup->add(partui); +partui->init(master->part[nval],master,nval,bankui); +partui->redraw(); +o->redraw(); +npart=nval; + +updatepanel(); +simplenpartcounter->value(nval+1); +simplenpartcounter->do_callback();} + tooltip {The part number} xywh {5 312 50 18} type Simple labelfont 1 minimum 0 maximum 127 step 1 value 1 textfont 1 + code0 {o->bounds(1,NUM_MIDI_PARTS);} + code1 {bankui->init(o);} + } + Fl_Button {} { + label vK + callback {virkeyboard->show();} + tooltip {Virtual Keyboard} xywh {280 87 40 23} color 51 labelfont 1 + } + Fl_Group {} {open + xywh {85 32 55 110} box UP_FRAME + } { + Fl_Button {} { + label Reset + callback {globalfinedetuneslider->value(64.0); +globalfinedetuneslider->do_callback();} + tooltip {Master fine detune reset} xywh {90 37 45 23} box THIN_UP_BOX labelsize 10 + } + Fl_Dial globalfinedetuneslider { + label {Fine Detune} + callback {master->microtonal.Pglobalfinedetune=(int) o->value();} + tooltip {global fine detune} xywh {90 68 45 45} box ROUND_UP_BOX labelsize 9 align 130 maximum 127 step 1 value 64 + code0 {o->value(master->microtonal.Pglobalfinedetune);} + class WidgetPDial + } + } + Fl_Button {} { + label {Panel Window} + callback {updatepanel(); +panelwindow->show();} + tooltip {Panel Window} xywh {280 112 105 23} color 51 labelfont 1 labelsize 10 + } + Fl_Button sm_indicator1 { + label SM + xywh {350 5 35 15} box ROUNDED_BOX down_box ROUNDED_BOX color 45 selection_color 93 labelfont 3 labelcolor 39 deactivate + } + } + Fl_Window aboutwindow { + label {Copyright...} + xywh {411 344 365 280} type Double hide + } { + Fl_Box {} { + label {Copyright (c) 2002-2009 Nasca O. PAUL and others. Please read AUTHORS.txt} + xywh {15 35 335 55} labeltype EMBOSSED_LABEL labelsize 15 align 208 + } + Fl_Box {} { + label {This is free software; you may redistribute it and/or modify it under the terms of the +version 2 (or any later version) of the GNU General Public License as published by the Free Software Fundation. + This program comes with + ABSOLUTELY NO WARRANTY. + See the version 2 (or any later version) of the +GNU General Public License for details.} + xywh {15 90 335 145} labelfont 1 labelsize 11 align 144 + } + Fl_Button {} { + label {Close this window} + callback {aboutwindow->hide();} + xywh {80 245 190 25} box THIN_UP_BOX labelsize 11 + } + Fl_Box {} { + label ZynAddSubFX + xywh {15 5 335 30} labeltype EMBOSSED_LABEL labelfont 1 labelsize 20 align 16 + } + } + Fl_Window syseffsendwindow { + label {System Effects Send} + xywh {171 234 120 250} type Double hide resizable + } { + Fl_Scroll {} {open + xywh {0 45 120 170} box FLAT_BOX resizable + code0 {for (int neff1=0;neff1x()+(neff2-1)*35,o->y()+15+neff1*50,30,30);syseffsend[neff1][neff2]->label("aaa");syseffsend[neff1][neff2]->init(master,neff1,neff2);};} + } {} + Fl_Button {} { + label Close + callback {syseffsendwindow->hide();} + xywh {25 220 80 25} box THIN_UP_BOX + } + Fl_Box {} { + label {Send system effect's output to other system effects} + xywh {5 5 110 35} labelsize 10 align 192 + } + } + Fl_Window panelwindow { + label {ZynAddSubFX Panel} + xywh {89 59 630 635} type Double hide + } { + Fl_Scroll {} { + xywh {0 5 570 310} type HORIZONTAL box THIN_UP_BOX + } { + Fl_Pack {} { + xywh {5 10 560 285} type HORIZONTAL + code0 {for (int i=0;iinit(master,i,bankui);}} + } {} + } + Fl_Scroll {} { + xywh {0 320 570 310} type HORIZONTAL box THIN_UP_BOX + } { + Fl_Pack {} { + xywh {5 325 560 285} type HORIZONTAL + code0 {for (int i=NUM_MIDI_PARTS/2;iinit(master,i,bankui);}} + } {} + } + Fl_Button {} { + label Close + callback {panelwindow->hide(); +updatepanel();} + xywh {575 605 50 25} box THIN_UP_BOX labelsize 13 + } + Fl_Button {} { + label Refresh + callback {updatepanel();} + xywh {575 570 55 25} box THIN_UP_BOX labelsize 13 + } + } + Fl_Window simplemasterwindow { + label ZynAddSubFX + callback {\#ifndef PLUGINVERSION +if (fl_choice("Exit and leave the unsaved data?","No","Yes",NULL)) +\#endif +{ + config.save(); + *exitprogram=1; +}; +} open + xywh {283 262 600 335} type Double visible + } { + Fl_Menu_Bar simplemastermenu { + xywh {0 0 690 25} + } { + Submenu {} { + label {&File} + xywh {10 10 100 20} + } { + MenuItem {} { + label {&New (erase all)...} + callback {do_new_master();} + xywh {30 30 100 20} + } + MenuItem {} { + label {&Open Parameters...} + callback {do_load_master();} + xywh {30 30 100 20} + } + MenuItem {} { + label {&Save All Parameters...} + callback {\#if USE_NSM + if ( nsm && nsm->is_active() ) + { + do_save_master( nsm->project_filename ); + } + else +\#endif + { + do_save_master(); + }} + xywh {20 20 100 20} divider + } + MenuItem {} { + label {&Settings...} + callback {configui->show();} + xywh {35 35 100 20} divider + } + MenuItem {} { + label {&Copyright...} + callback {aboutwindow->show();} + xywh {25 25 100 20} divider + } + MenuItem {} { + label {E&xit} + callback {masterwindow->do_callback();} + xywh {20 20 100 20} + } + } + Submenu {} { + label {&Instrument} + xywh {20 20 100 20} + } { + MenuItem {} { + label {&Open Instrument...} + callback {const char *filename; +filename=fl_file_chooser("Load:","({*.xiz})",NULL,0); +if (filename==NULL) return; + + +pthread_mutex_lock(&master->mutex); +// int npart=(int)npartcounter->value()-1; + + //clear all instrument parameters, first + master->part[npart]->defaultsinstrument(); + + //load the instr. parameters + int result=master->part[npart]->loadXMLinstrument(filename); + +pthread_mutex_unlock(&master->mutex); +master->part[npart]->applyparameters(); + +simplenpartcounter->do_callback(); + +if (result==-10) fl_alert("Error: Could not load the file\\nbecause it is not an instrument file."); + else if (result<0) fl_alert("Error: Could not load the file.");} + xywh {40 40 100 20} + } + MenuItem {} { + label {Show Instrument &Bank...} + callback {bankui->show();} + xywh {10 10 100 20} divider + } + } + Submenu {} { + label Misc + xywh {0 0 100 20} + } { + MenuItem {} { + label {Switch User Interface Mode} + callback {if (fl_choice("Switch the User Interface to Advanced mode ?","No","Yes",NULL)){ + simplemasterwindow->hide(); + refresh_master_ui(); + masterwindow->show(); + config.cfg.UserInterfaceMode=1; +};} + xywh {0 0 100 20} + } + } + } + Fl_Group simplelistitemgroup {open + private xywh {125 65 215 145} box UP_FRAME + code0 {if (master->part[npart]->Penabled==0) o->deactivate();} + } { + Fl_Button partname { + callback {if ((int)bankui->cbwig->value()!=(npart+1)){ + bankui->cbwig->value(npart+1); + bankui->cbwig->do_callback(); +}; +bankui->show();} + xywh {130 72 205 18} box THIN_DOWN_BOX down_box FLAT_BOX color 50 labelfont 1 labelsize 11 align 208 + } + Fl_Slider partpanning { + label Pan + callback {master->part[npart]->setPpanning((int) o->value());} + xywh {185 95 145 15} type {Horz Knob} box NO_BOX labelsize 11 maximum 127 step 1 value 64 + code0 {o->value(master->part[npart]->Ppanning);} + } + Fl_Choice partrcv { + label {Midi Channel Receive} + callback {virkeys->relaseallkeys(0); +master->part[npart]->Prcvchn=(int) o->value(); +virkeys->midich=(int) o->value();} open + tooltip {receive from Midi channel} xywh {140 157 65 18} down_box BORDER_BOX labelsize 10 align 130 textfont 1 + code0 {char nrstr[10]; for(int i=0;iadd(nrstr); else o->add("Dr10");};} + code1 {o->value(master->part[npart]->Prcvchn);} + } {} + Fl_Dial partvolume { + callback {master->part[npart]->setPvolume((int) o->value());} + xywh {135 95 45 40} labelsize 9 maximum 127 step 1 + code0 {o->value(master->part[npart]->Pvolume);} + class WidgetPDial + } + Fl_Box {} { + label Volume + xywh {130 130 55 20} labelsize 10 + } + Fl_Check_Button simplepartportamento { + label Portamento + callback {master->part[npart]->ctl.portamento.portamento=(int) o->value();} + tooltip {Enable/Disable the portamento} xywh {193 127 79 23} down_box DOWN_BOX labelsize 9 + code0 {o->value(master->part[npart]->ctl.portamento.portamento);} + } + Fl_Counter simpleminkcounter { + label {Min.key} + callback {master->part[npart]->Pminkey=(int) o->value(); +if (master->part[npart]->Pminkey>master->part[npart]->Pmaxkey) o->textcolor(FL_RED); + else o->textcolor(FL_BLACK);} + tooltip {Minimum key (that the part receives NoteOn messages)} xywh {210 158 40 15} type Simple labelsize 10 minimum 0 maximum 127 step 1 textsize 10 + code0 {o->value(master->part[npart]->Pminkey);} + } + Fl_Counter simplemaxkcounter { + label {Max.key} + callback {master->part[npart]->Pmaxkey=(int) o->value(); + +if (master->part[npart]->Pminkey>master->part[npart]->Pmaxkey) o->textcolor(FL_RED); + else o->textcolor(FL_BLACK);} + tooltip {Maximum key (that the part receives NoteOn messages)} xywh {255 158 40 15} type Simple labelsize 10 minimum 0 maximum 127 step 1 textsize 10 + code0 {o->value(master->part[npart]->Pmaxkey);} + } + Fl_Button {} { + label m + callback {if (master->part[npart]->lastnote>=0) simpleminkcounter->value(master->part[npart]->lastnote); +simpleminkcounter->do_callback(); +simplemaxkcounter->do_callback();} + tooltip {set the minimum key to the last pressed key} xywh {230 188 15 12} box THIN_UP_BOX labelsize 10 + } + Fl_Button {} { + label M + callback {if (master->part[npart]->lastnote>=0) simplemaxkcounter->value(master->part[npart]->lastnote); +simplemaxkcounter->do_callback(); +simpleminkcounter->do_callback();} + tooltip {set the maximum key to the last pressed key} xywh {260 188 15 12} box THIN_UP_BOX labelsize 10 + } + Fl_Button {} { + label R + callback {simpleminkcounter->value(0); +simpleminkcounter->do_callback(); +simplemaxkcounter->value(127); +simplemaxkcounter->do_callback();} + tooltip {reset the minimum key to 0 and maximum key to 127} xywh {245 188 15 12} box THIN_UP_BOX labelfont 1 labelsize 10 + } + Fl_Counter simplepartkeyshiftcounter { + label KeyShift + callback {master->part[npart]->Pkeyshift=(int) o->value()+64;} + xywh {280 120 50 20} type Simple labelsize 11 minimum -64 maximum 64 step 1 + code0 {o->lstep(12);} + code1 {o->value(master->part[npart]->Pkeyshift-64);} + } + Fl_Dial simplesyseffsend { + callback {master->setPsysefxvol(npart,nsyseff,(int) o->value());} + xywh {300 160 30 30} maximum 127 step 1 + class WidgetPDial + } + Fl_Box {} { + label Effect + xywh {295 190 40 15} labelsize 10 + } + } + Fl_Check_Button partenabled { + label Enabled + callback {pthread_mutex_lock(&master->mutex); + master->partonoff(npart,(int) o->value()); +pthread_mutex_unlock(&master->mutex); + +if ((int) o->value()==0) simplelistitemgroup->deactivate(); + else { + simplelistitemgroup->activate(); + if ((int)bankui->cbwig->value()!=(npart+1)){ + bankui->cbwig->value(npart+1); + bankui->cbwig->do_callback(); + }; +}; + +o->redraw();} + private xywh {250 40 85 20} down_box DOWN_BOX labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 24 + code0 {//char tmp[10];snprintf(tmp,10,"%d",npart+1);o->copy_label(tmp);} + code1 {o->value(master->part[npart]->Penabled);} + } + Fl_Box virkeys { + label Keyboard + xywh {5 215 590 80} box BORDER_BOX color 17 + code0 {o->init(master);} + class VirKeys + } + Fl_Group {} {open + xywh {340 30 255 185} + } { + Fl_Tabs {} {open + xywh {345 35 245 175} box UP_FRAME align 18 + } { + Fl_Group {} { + label {System Effects} open + xywh {345 55 245 155} box UP_FRAME labelfont 1 labelsize 12 align 18 + } { + Fl_Counter simplesyseffnocounter { + label {Sys.Effect No.} + callback {nsyseff=(int) o->value()-1; +simplesysefftype->value(master->sysefx[nsyseff]->geteffect()); +simplesyseffectui->refresh(master->sysefx[nsyseff]); +simplerefresh();} + xywh {350 75 80 20} type Simple labelfont 1 labelsize 10 align 1 minimum 0 maximum 127 step 1 value 1 textfont 1 + code0 {o->bounds(1,NUM_SYS_EFX);} + code1 {o->value(nsyseff+1);} + } + Fl_Choice simplesysefftype { + label EffType + callback {pthread_mutex_lock(&master->mutex); +master->sysefx[nsyseff]->changeeffect((int) o->value()); +pthread_mutex_unlock(&master->mutex); +simplesyseffectui->refresh(master->sysefx[nsyseff]);} + xywh {515 80 70 15} down_box BORDER_BOX labelsize 10 align 5 + code0 {o->value(master->sysefx[nsyseff]->geteffect());} + } { + MenuItem {} { + label {No Effect} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Reverb + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Echo + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Chorus + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Phaser + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label AlienWah + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Distortion + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label EQ + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label DynFilter + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + } + Fl_Group simplesyseffectuigroup {open + xywh {350 95 235 95} color 48 + } { + Fl_Group simplesyseffectui { + xywh {350 95 234 95} + code0 {o->init(master->sysefx[nsyseff]);} + class SimpleEffUI + } {} + } + Fl_Button {} { + label {Send to...} + callback {syseffsendwindow->show();} + xywh {435 75 75 20} box THIN_UP_BOX labelfont 1 labelsize 11 + } + Fl_Button {} { + label P + callback {pthread_mutex_lock(&master->mutex); +presetsui->paste(master->sysefx[nsyseff],simplesyseffectui); +pthread_mutex_unlock(&master->mutex);} + xywh {560 65 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + } + Fl_Group {} { + label {Insertion Effects} + xywh {345 55 245 155} box UP_FRAME labelfont 1 labelsize 12 align 18 hide + } { + Fl_Counter simpleinseffnocounter { + label {Ins.Effect No.} + callback {ninseff=(int) o->value()-1; +simpleinsefftype->value(master->insefx[ninseff]->geteffect()); +simpleinseffpart->value(master->Pinsparts[ninseff]+2); +simpleinseffectui->refresh(master->insefx[ninseff]); + +if (master->Pinsparts[ninseff]!=-1) { + simpleinsefftype->activate(); + simpleinseffectui->activate(); + simpleinseffectuigroup->activate(); +} else { + simpleinsefftype->deactivate(); + simpleinseffectui->deactivate(); + simpleinseffectuigroup->deactivate(); +};} + xywh {350 75 80 20} type Simple labelfont 1 labelsize 10 align 1 minimum 0 maximum 127 step 1 value 1 textfont 1 + code0 {o->bounds(1,NUM_INS_EFX);} + code1 {o->value(ninseff+1);} + } + Fl_Choice simpleinsefftype { + label EffType + callback {pthread_mutex_lock(&master->mutex); +master->insefx[ninseff]->changeeffect((int) o->value()); +pthread_mutex_unlock(&master->mutex); +simpleinseffectui->refresh(master->insefx[ninseff]); +simpleinseffectui->show();} + xywh {515 80 70 15} down_box BORDER_BOX labelsize 10 align 5 + code0 {o->value(master->insefx[ninseff]->geteffect());} + code1 {if (master->Pinsparts[ninseff]== -1) o->deactivate();} + } { + MenuItem {} { + label {No Effect} + xywh {35 35 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Reverb + xywh {45 45 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Echo + xywh {55 55 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Chorus + xywh {65 65 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Phaser + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label AlienWah + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Distortion + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label EQ + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label DynFilter + xywh {110 110 100 20} labelfont 1 labelsize 10 + } + } + Fl_Group simpleinseffectuigroup { + xywh {350 95 234 95} box FLAT_BOX color 48 + } { + Fl_Group simpleinseffectui { + xywh {350 95 234 95} + code0 {o->init(master->insefx[ninseff]);} + code1 {if (master->Pinsparts[ninseff]== -1) o->deactivate();} + class SimpleEffUI + } {} + } + Fl_Choice simpleinseffpart { + label {Insert To.} + callback {master->Pinsparts[ninseff]=(int) o->value()-2; +if ((int) o->value()==1){ + simpleinseffectuigroup->deactivate(); + simpleinsefftype->deactivate(); + simpleinseffectui->deactivate(); +} else { + simpleinseffectuigroup->activate(); + simpleinsefftype->activate(); + simpleinseffectui->activate(); +}; +master->insefx[ninseff]->cleanup();} open + xywh {435 75 80 20} down_box BORDER_BOX labelfont 1 labelsize 10 align 5 textsize 10 + code0 {o->add("Master Out");o->add("Off");} + code1 {char tmp[50]; for (int i=0;iadd(tmp);};} + code3 {o->value(master->Pinsparts[ninseff]+2);} + } {} + Fl_Button {} { + label P + callback {pthread_mutex_lock(&master->mutex); +presetsui->paste(master->insefx[ninseff],simpleinseffectui); +pthread_mutex_unlock(&master->mutex);} + xywh {560 65 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + } + } + } + Fl_Group {} {open + xywh {5 300 590 30} box ENGRAVED_FRAME + } { + Fl_Box {} { + label {VU-Meter} + xywh {5 300 590 30} box FLAT_BOX color 41 selection_color 75 + code0 {o->init(master,-1);} + class VUMeter + } + } + Fl_Dial simplemastervolumedial { + label {Master Volume} + callback {master->setPvolume((int) o->value());} + tooltip {Master Volume} xywh {10 35 40 40} box ROUND_UP_BOX labelfont 1 labelsize 11 align 130 maximum 127 step 1 + code0 {o->value(master->Pvolume);} + class WidgetPDial + } + Fl_Counter simplemasterkeyshiftcounter { + label {Master KeyShift} + callback {master->setPkeyshift((int) o->value()+64);} + xywh {15 110 90 20} labelsize 11 minimum -64 maximum 64 step 1 + code0 {o->lstep(12);} + code1 {o->value(master->Pkeyshift-64);} + } + Fl_Button {} { + label {Stop ALL sounds!} + callback {virkeyboard->relaseallkeys(); +pthread_mutex_lock(&master->mutex); +master->shutup=1; +pthread_mutex_unlock(&master->mutex);} + xywh {5 149 115 31} color 90 labelfont 1 labelsize 10 + } + Fl_Button {} { + label Reset + callback {simpleglobalfinedetuneslider->value(64.0); +simpleglobalfinedetuneslider->do_callback();} + tooltip {Master fine detune reset} xywh {70 30 50 17} box THIN_UP_BOX labelsize 11 align 128 + } + Fl_Dial simpleglobalfinedetuneslider { + label {Fine Detune} + callback {master->microtonal.Pglobalfinedetune=(int) o->value();} + tooltip {global fine detune} xywh {80 50 30 30} box ROUND_UP_BOX labelsize 11 align 130 maximum 127 step 1 value 64 + code0 {o->value(master->microtonal.Pglobalfinedetune);} + class WidgetPDial + } + Fl_Counter simplenpartcounter { + label Part + callback {virkeys->relaseallkeys(0); +npartcounter->value(o->value()); +npart=(int) o->value()-1; + +simplerefresh(); +virkeys->midich=master->part[npart]->Prcvchn;} + tooltip {The part number} xywh {170 40 70 20} type Simple labelfont 1 align 4 minimum 0 maximum 127 step 1 value 1 textfont 1 + code0 {o->bounds(1,NUM_MIDI_PARTS);} + } + Fl_Counter {} { + label {Keyb.Oct.} + callback {virkeys->relaseallkeys(0); +virkeys->midioct=(int) o->value(); +virkeys->take_focus();} + tooltip {Midi Octave} xywh {5 190 55 20} type Simple labelsize 11 align 8 when 6 minimum 0 maximum 5 step 1 textfont 1 textsize 11 + code0 {o->value(virkeys->midioct);} + } + Fl_Button sm_indicator2 { + label SM + xywh {560 5 35 15} box ROUNDED_BOX down_box ROUNDED_BOX color 45 selection_color 93 labelfont 3 labelcolor 39 deactivate + } + } + Fl_Window selectuiwindow { + label {User Interface mode} + callback {*exitprogram=1;} + xywh {342 246 430 250} type Double hide non_modal + } { + Fl_Box {} { + label {Welcome to ZynAddSubFX} + xywh {5 5 425 40} labeltype SHADOW_LABEL labelfont 1 labelsize 26 + } + Fl_Box {} { + label {Please choose the interface mode:} + xywh {10 50 265 25} labelfont 1 labelsize 13 + } + Fl_Button {} { + label Advanced + callback {config.cfg.UserInterfaceMode=1; +masterwindow->show(); +selectuiwindow->hide();} + xywh {10 165 100 35} color 229 labelfont 1 labelsize 16 + } + Fl_Box {} { + label {.. if you have used ZynAddSubFX before, or you like to have full controll to all parameters.} + xywh {110 165 310 35} labelfont 1 labelsize 11 align 144 + } + Fl_Button {} { + label Beginner + callback {simplemasterwindow->show(); +selectuiwindow->hide(); +config.cfg.UserInterfaceMode=2;} + xywh {10 80 100 65} color 238 labelfont 1 labelsize 16 + } + Fl_Box {} { + label {..if you are a beginner, you prefer using presets or you prefer to use simpler user interfaces. Most functionality of ZynAddSubFX will be hidden in this mode to make simple the learning/using it.} + xywh {110 75 320 75} labelfont 1 labelsize 11 align 144 + } + Fl_Box {} { + label {You can switch the interface modes anytime you want.} + xywh {30 215 360 25} box BORDER_BOX color 51 labelfont 1 labelsize 11 align 144 + } + } + } + Function {updatesendwindow()} {} { + code {for (int neff1=0;neff1value(master->Psysefxsend[neff1][neff2]);} {} + } + Function {updatepanel()} {} { + code {for (int npart=0;npartrefresh(); +};} {} + } + Function {setfilelabel(const char *filename)} {} { + code {if (filename!=NULL) snprintf(&masterwindowlabel[0],100,"%s - ZynAddSubFX",fl_filename_name(filename)); + else snprintf(&masterwindowlabel[0],100,"%s","ZynAddSubFX"); +masterwindowlabel[99]='\\0'; +masterwindow->label(&masterwindowlabel[0]); +simplemasterwindow->label(&masterwindowlabel[0]);} {} + } + Function {MasterUI(Master *master_,int *exitprogram_)} {} { + code {master=master_; +exitprogram=exitprogram_; +ninseff=0; +nsyseff=0; +npart=0; + +for (int i=0;imicrotonal); +virkeyboard=new VirKeyboard(master); +bankui=new BankUI(master,&npart); +configui=new ConfigUI(); + +make_window(); +\#ifdef OS_WINDOWS +masterwindow->icon((char *)LoadIcon(GetModuleHandle(NULL), "zynaddsubfx_icon")); +\#endif + +presetsui=new PresetsUI(); +setfilelabel(NULL); +swapefftype=0; +simplerefresh();} {} + } + Function {~MasterUI()} {} { + code {masterwindow->hide(); +delete masterwindow; +simplemasterwindow->hide(); +delete simplemasterwindow; +aboutwindow->hide(); +delete aboutwindow; +syseffsendwindow->hide(); +delete syseffsendwindow; + +delete virkeyboard; +delete microtonalui; +delete bankui; +delete configui; + +delete presetsui; +delete panelwindow; +delete selectuiwindow;} {} + } + Function {showUI()} {} { + code {switch (config.cfg.UserInterfaceMode){ + case 0:selectuiwindow->show(); + break; + case 1:masterwindow->show(); + break; + case 2:simplemasterwindow->show(); + break; +};} {} + } + Function {simplerefresh()} {} { + code {partenabled->value(master->part[npart]->Penabled); +if (master->part[npart]->Penabled!=0) simplelistitemgroup->activate(); + else simplelistitemgroup->deactivate(); + +partvolume->value(master->part[npart]->Pvolume); +partpanning->value(master->part[npart]->Ppanning); +partrcv->value(master->part[npart]->Prcvchn); + +if (master->part[npart]->Pname[0]!=0) partname->label((char *)master->part[npart]->Pname); + else partname->label("Click here to load a instrument"); + +simplelistitemgroup->redraw(); +simplepartportamento->value(master->part[npart]->ctl.portamento.portamento); +simpleminkcounter->value(master->part[npart]->Pminkey); +simplemaxkcounter->value(master->part[npart]->Pmaxkey); + +simplepartkeyshiftcounter->value(master->part[npart]->Pkeyshift-64); +simplesyseffsend->value(master->Psysefxvol[nsyseff][npart]);} {} + } + Function {do_new_master_unconditional()} {} { + code {delete microtonalui; + + pthread_mutex_lock(&master->mutex); + master->defaults(); + pthread_mutex_unlock(&master->mutex); + + npartcounter->value(1); + refresh_master_ui(); + updatepanel();} {} + } + Function {do_new_master()} {} { + code {if (fl_choice("Clear *ALL* the parameters ?","No","Yes",NULL)){ + do_new_master_unconditional(); + }} {} + } + Function {do_load_master_unconditional(const char *filename, const char *display_name)} {return_type int + } { + code {pthread_mutex_lock(&master->mutex); + //clear all parameters + master->defaults(); + + //load the data + int result=master->loadXML(filename); + + master->applyparameters(false); + pthread_mutex_unlock(&master->mutex); + + npartcounter->value(1); + refresh_master_ui(); + updatepanel(); + + if (result>=0) setfilelabel(display_name); + + return result;} {} + } + Function {do_load_master(const char* file = NULL)} {} { + code {const char *filename; + if (file == NULL) { + filename=fl_file_chooser("Open:","({*.xmz})",NULL,0); + if (filename==NULL) return; + } + else { + filename = file; + } + + int result = do_load_master_unconditional( filename, filename ); + +if (result==-10) fl_alert("Error: Could not load the file\\nbecause it is not a zynaddsubfx parameters file."); + else if (result<0) fl_alert("Error: Could not load the file.");} {} + } + Function {do_save_master(const char* file = NULL)} {} { + code {const char *filename; +char *tmp; + int result=0; + if (file == NULL) { + tmp=fl_file_chooser("Save:","({*.xmz})",NULL,0); + if (tmp==NULL) return; + tmp=fl_filename_setext(tmp,".xmz"); + filename=tmp; + result=fileexists(tmp); + if (result) { + result=0; + if (!fl_choice("The file exists. Overwrite it?","No","Yes",NULL)) return; + + } + } + else { + filename = file; + } + + +pthread_mutex_lock(&master->mutex); +result=master->saveXML(filename); +pthread_mutex_unlock(&master->mutex); + +if (result<0) fl_alert("Error: Could not save the file."); + else +{ +\#if USE_NSM + if ( nsm && nsm->is_active() ) + setfilelabel( nsm->display_name ); + else +\#endif + setfilelabel(filename); +} +updatepanel();} {} + } + Function {refresh_master_ui()} {} { + code {ninseff=0; +nsyseff=0; +npart=0; + +//the Master UI +npartcounter->do_callback(); +syseffnocounter->do_callback(); +inseffnocounter->do_callback(); +masterkeyshiftcounter->value(master->Pkeyshift-64); +mastervolumedial->value(master->Pvolume); +globalfinedetuneslider->value(master->microtonal.Pglobalfinedetune); +microtonalui=new MicrotonalUI(&master->microtonal); +nrpnbutton->value(master->ctl.NRPN.receive); +updatesendwindow(); +updatepanel(); + +//the simle MasterUI +simplenpartcounter->value(1); +simplesyseffnocounter->value(1); +simpleinseffnocounter->value(1); +simplenpartcounter->do_callback(); +simplesyseffnocounter->do_callback(); +simpleinseffnocounter->do_callback(); +simplemasterkeyshiftcounter->value(master->Pkeyshift-64); +simplemastervolumedial->value(master->Pvolume); +simpleglobalfinedetuneslider->value(master->microtonal.Pglobalfinedetune); +virkeys->midich=master->part[npart]->Prcvchn; + +simplerefresh(); +bankui->hide();} {} + } + decl {Master *master;} {} + decl {MicrotonalUI *microtonalui;} {} + decl {BankUI *bankui;} {} + decl {int ninseff,npart;} {} + decl {int nsyseff;} {} + decl {int *exitprogram;} {} + decl {SysEffSend *syseffsend[NUM_SYS_EFX][NUM_SYS_EFX];} {} + decl {VirKeyboard *virkeyboard;} {} + decl {ConfigUI *configui;} {} + decl {int swapefftype;} {} + decl {char masterwindowlabel[100];} {} + decl {Panellistitem *panellistitem[NUM_MIDI_PARTS];} {} + decl {NioUI nioui;} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/MicrotonalUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/MicrotonalUI.fl new file mode 100644 index 000000000..1f1af9583 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/MicrotonalUI.fl @@ -0,0 +1,270 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0106 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "../Misc/Microtonal.h"} {public +} + +class MicrotonalUI {} { + Function {make_window()} {} { + Fl_Window microtonaluiwindow { + label Scales + xywh {99 164 405 450} type Double hide + } { + Fl_Group {} { + tooltip {Center where the note's freqs. are turned upside-down} xywh {249 2 155 45} box ENGRAVED_FRAME + } { + Fl_Check_Button {} { + label {Invert keys} + callback {microtonal->Pinvertupdown=(int) o->value(); +if (microtonal->Pinvertupdown==0) centerinvertcounter->deactivate(); + else centerinvertcounter->activate();} + tooltip {Turn upside-down the note frequencies} xywh {254 13 55 30} down_box DOWN_BOX labelfont 1 labelsize 11 align 148 + code0 {o->value(microtonal->Pinvertupdown);} + } + Fl_Counter centerinvertcounter { + label Center + callback {microtonal->Pinvertupdowncenter=(int) o->value();} + xywh {319 13 80 20} labelfont 1 labelsize 11 align 130 minimum 0 maximum 127 step 1 textfont 1 + code0 {o->lstep(microtonal->getoctavesize());} + code1 {o->value(microtonal->Pinvertupdowncenter);} + code2 {if (microtonal->Pinvertupdown==0) o->deactivate();} + } + } + Fl_Group microtonalgroup {selected + xywh {3 49 402 398} box ENGRAVED_FRAME + code0 {if (microtonal->Penabled==0) o->deactivate();} + } { + Fl_Button applybutton { + label Retune + callback {apply();} + tooltip {Retune the synth accorging to the inputs from "Tunnings" and "Keyboard Mappings"} xywh {8 413 107 28} box THIN_UP_BOX labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 + } + Fl_Value_Output octavesizeoutput { + label {nts./oct.} + callback {o->value(microtonal->getoctavesize());} + tooltip {Notes/Octave} xywh {150 423 35 17} labelsize 10 align 5 maximum 500 step 1 value 12 textfont 1 + code0 {o->value(microtonal->getoctavesize());} + } + Fl_Input nameinput { + label {Name:} + callback {snprintf((char *)microtonal->Pname,MICROTONAL_MAX_NAME_LEN,"%s",o->value());} + xywh {8 64 285 25} labelfont 1 labelsize 11 align 5 + code0 {o->insert((char *)microtonal->Pname);} + } + Fl_Input tuningsinput { + label {Tunings:} + xywh {8 144 182 264} type Multiline labelfont 1 labelsize 11 align 5 when 2 + code0 {updateTuningsInput();} + } + Fl_Input commentinput { + label {Comment:} + callback {snprintf((char *)microtonal->Pcomment,MICROTONAL_MAX_NAME_LEN,"%s",o->value());} + xywh {8 104 391 25} labelfont 1 labelsize 11 align 5 + code0 {o->insert((char *)microtonal->Pcomment);} + } + Fl_Counter {} { + label Shift + callback {microtonal->Pscaleshift=(int) o->value()+64;} + xywh {313 69 70 20} type Simple labelsize 11 align 1 minimum -63 maximum 64 step 1 textfont 1 + code0 {o->value(microtonal->Pscaleshift-64);} + } + Fl_Button {} { + label {Import .SCL file} + callback {const char *filename; +filename=fl_file_chooser("Open:","(*.scl)",NULL,0); +if (filename==NULL) return; +int result=microtonal->loadscl(filename); +if (result==0) { + updateTuningsInput(); + nameinput->cut(0,nameinput->maximum_size()); + nameinput->insert((char *)microtonal->Pname); + nameinput->position(0); + commentinput->cut(0,commentinput->maximum_size()); + commentinput->insert((char *)microtonal->Pname); + commentinput->position(0); + tuningsinput->position(0); + octavesizeoutput->do_callback(); + } else { + fl_alert("Error: Could not load the file."); + };} + tooltip {Inport Scala .scl file (tunnings)} xywh {243 411 84 15} box THIN_UP_BOX labelfont 1 labelsize 10 + } + Fl_Group keymappinggroup { + label {Keyboard Mapping} open + xywh {193 144 206 264} box ENGRAVED_BOX labelfont 1 labelsize 11 + } { + Fl_Input mappinginput { + xywh {250 147 146 258} type Multiline labelfont 1 labelsize 11 align 5 when 2 + code0 {updateMappingInput();} + } + Fl_Counter firstnotecounter { + label {First note} + callback {microtonal->Pfirstkey=(int) o->value();} + tooltip {First MIDI note number} xywh {199 195 42 18} type Simple labelsize 10 align 5 minimum 0 maximum 127 step 1 textfont 1 textsize 11 + code0 {o->value(microtonal->Pfirstkey);} + } + Fl_Counter lastnotecounter { + label {Last note} + callback {microtonal->Plastkey=(int) o->value();} + tooltip {Last MIDI note number} xywh {199 225 42 18} type Simple labelsize 10 align 5 minimum 0 maximum 127 step 1 value 127 textfont 1 textsize 11 + code0 {o->value(microtonal->Plastkey);} + } + Fl_Counter middlenotecounter { + label {Midle note} + callback {microtonal->Pmiddlenote=(int) o->value();} + tooltip {Midle note (where scale degree 0 is mapped to)} xywh {199 267 42 18} type Simple labelsize 10 align 5 minimum 0 maximum 127 step 1 value 60 textfont 1 textsize 11 + code0 {o->value(microtonal->Pmiddlenote);} + } + Fl_Value_Output mapsizeoutput { + label {Map Size} + callback {o->value(microtonal->Pmapsize);} + xywh {201 382 44 20} labelsize 10 align 5 maximum 500 step 1 value 12 textfont 1 + code0 {o->value(microtonal->Pmapsize);} + } + } + Fl_Check_Button mappingenabledbutton { + label ON + callback {int x=(int) o->value(); +microtonal->Pmappingenabled=x; +if (x==0) keymappinggroup->deactivate(); + else keymappinggroup->activate(); +o->show();} + tooltip {Enable the Mapping (otherwise the mapping is linear)} xywh {198 150 48 21} box FLAT_BOX down_box DOWN_BOX labelfont 1 + code0 {o->value(microtonal->Pmappingenabled);} + code1 {if (microtonal->Pmappingenabled==0) keymappinggroup->deactivate();} + } + Fl_Button {} { + label {Import .kbm file} + callback {const char *filename; +filename=fl_file_chooser("Open:","(*.kbm)",NULL,0); +if (filename==NULL) return; +int result=microtonal->loadkbm(filename); +if (result==0) { + updateMappingInput(); + mappinginput->position(0); + mapsizeoutput->do_callback(); + firstnotecounter->value(microtonal->Pfirstkey); + lastnotecounter->value(microtonal->Plastkey); + middlenotecounter->value(microtonal->Pmiddlenote); + mapsizeoutput->do_callback(); + mappingenabledbutton->value(microtonal->Pmappingenabled); + mappingenabledbutton->do_callback(); + afreqinput->value(microtonal->PAfreq); + anotecounter->value(microtonal->PAnote); + anotecounter->do_callback(); + } else { + fl_alert("Error: Could not load the file."); + };} + tooltip {Inport Scala .kbm file (keyboard mapping)} xywh {243 428 84 16} box THIN_UP_BOX labelfont 1 labelsize 10 + } + } + Fl_Group {} { + xywh {108 2 140 45} box ENGRAVED_FRAME + } { + Fl_Counter anotecounter { + label {"A" Note} + callback {microtonal->PAnote=(int) o->value(); +if (microtonal->getnotefreq(microtonal->PAnote,0)<0.0) o->textcolor(FL_RED); + else o->textcolor(FL_BLACK); + +o->redraw();} + tooltip {The "A" note (the reference note for which freq. ("A" freq) is given)} xywh {173 17 65 20} labelfont 1 labelsize 10 align 129 minimum 0 maximum 127 step 1 value 69 textfont 1 textsize 10 + code0 {o->lstep(12);} + code1 {o->value(microtonal->PAnote);} + } + Fl_Value_Input afreqinput { + label {"A" Freq.} + callback {microtonal->PAfreq=o->value();} + tooltip {The freq. of "A" note (default=440.0)} xywh {118 17 45 20} labelfont 1 labelsize 10 align 1 minimum 1 maximum 20000 step 0.001 value 440 textfont 1 textsize 10 + code0 {o->value(microtonal->PAfreq);} + } + } + Fl_Button {} { + label Close + callback {microtonaluiwindow->hide();} + xywh {333 413 67 28} box THIN_UP_BOX + } + Fl_Check_Button {} { + label {Enable Microtonal} + callback {microtonal->Penabled=(int) o->value(); +if (microtonal->Penabled==0) microtonalgroup->deactivate(); + else microtonalgroup->activate();} + xywh {3 3 102 45} box UP_BOX down_box DOWN_BOX labelfont 1 labelsize 11 align 148 + code0 {o->value(microtonal->Penabled);} + } + } + } + Function {updateTuningsInput()} {} { + code {char *tmpbuf=new char[100]; + +tuningsinput->cut(0,tuningsinput->maximum_size()); + +for (int i=0;igetoctavesize();i++){ + if (i!=0) tuningsinput->insert("\\n"); + microtonal->tuningtoline(i,tmpbuf,100); + tuningsinput->insert(tmpbuf); +}; + +delete []tmpbuf;} {} + } + Function {updateMappingInput()} {} { + code {char *tmpbuf=new char[100]; + +mappinginput->cut(0,tuningsinput->maximum_size()); + +for (int i=0;iPmapsize;i++){ + if (i!=0) mappinginput->insert("\\n"); + if ((microtonal->Pmapping[i])==-1) + snprintf(tmpbuf,100,"x"); + else snprintf(tmpbuf,100,"%d",microtonal->Pmapping[i]); + mappinginput->insert(tmpbuf); +}; + +delete []tmpbuf;} {} + } + Function {MicrotonalUI(Microtonal *microtonal_)} {} { + code {microtonal=microtonal_; + +make_window();} {} + } + Function {~MicrotonalUI()} {} { + code {microtonaluiwindow->hide(); +delete(microtonaluiwindow);} {} + } + Function {show()} {} { + code {microtonaluiwindow->show();} {} + } + Function {apply()} {} { + code {int err=microtonal->texttotunings(tuningsinput->value()); +if (err>=0) fl_alert("Parse Error: The input may contain only numbers (like 232.59)\\n or divisions (like 121/64)."); +if (err==-2) fl_alert("Parse Error: The input is empty."); +octavesizeoutput->do_callback(); + +microtonal->texttomapping(mappinginput->value()); +mapsizeoutput->do_callback(); +anotecounter->do_callback(); + +//applybutton->color(FL_GRAY);} {} + } + decl {Microtonal *microtonal;} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM.C b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM.C new file mode 100644 index 000000000..7b7e6fc2e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM.C @@ -0,0 +1,163 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + + +#include "NSM.H" + +#include "../Nio/Nio.h" + +#include "MasterUI.h" +#include +#include +#include +#include +#include + +extern int Pexitprogram; +extern MasterUI *ui; + +extern NSM_Client *nsm; +extern char *instance_name; + +NSM_Client::NSM_Client() +{ + project_filename = 0; + display_name = 0; +} + +int command_open(const char *name, + const char *display_name, + const char *client_id, + char **out_msg); +int command_save(char **out_msg); + +int +NSM_Client::command_save(char **out_msg) +{ + (void) out_msg; + int r = ERR_OK; + + ui->do_save_master(project_filename); + + return r; +} + +int +NSM_Client::command_open(const char *name, + const char *display_name, + const char *client_id, + char **out_msg) +{ + Nio::stop(); + + if(instance_name) + free(instance_name); + + instance_name = strdup(client_id); + + Nio::start(); + + char *new_filename; + + asprintf(&new_filename, "%s.xmz", name); + + struct stat st; + + int r = ERR_OK; + + if(0 == stat(new_filename, &st)) { + if(ui->do_load_master_unconditional(new_filename, display_name) < 0) { + *out_msg = strdup("Failed to load for unknown reason"); + r = ERR_GENERAL; + + return r; + } + } + else + ui->do_new_master_unconditional(); + + if(project_filename) + free(project_filename); + + if(this->display_name) + free(this->display_name); + + project_filename = new_filename; + + this->display_name = strdup(display_name); + + return r; +} + +static void save_callback(Fl_Widget *, void *v) +{ + MasterUI *ui = static_cast(v); + ui->do_save_master(); +} + +void +NSM_Client::command_active(bool active) +{ + if(active) { + Fl_Menu_Item *m; + //TODO see if there is a cleaner way of doing this without voiding + //constness + if((m=const_cast(ui->mastermenu->find_item( + "&File/&Open Parameters...")))) + m->label("&Import Parameters..."); + if((m=const_cast(ui->simplemastermenu->find_item( + "&File/&Open Parameters...")))) + m->label("&Import Parameters..."); + + //TODO get this menu entry inserted at the right point + if((m=const_cast(ui->mastermenu->find_item("&File/&Export Parameters...")))) + m->show(); + else + ui->mastermenu->add("&File/&Export Parameters...",0,save_callback,ui); + + if((m=const_cast(ui->simplemastermenu->find_item("&File/&Export Parameters...")))) + m->show(); + else + ui->simplemastermenu->add("&File/&Export Parameters...",0,save_callback,ui); + + ui->sm_indicator1->value(1); + ui->sm_indicator2->value(1); + ui->sm_indicator1->tooltip(session_manager_name()); + ui->sm_indicator2->tooltip(session_manager_name()); + } + else { + Fl_Menu_Item *m; + if((m=const_cast(ui->mastermenu->find_item( + "&File/&Import Parameters...")))) + m->label("&Open Parameters..."); + if((m=const_cast(ui->simplemastermenu->find_item( + "&File/&Open Parameters...")))) + m->label("&Open Parameters..."); + + if((m=const_cast(ui->mastermenu->find_item("&File/&Export Parameters...")))) + m->hide(); + if((m=const_cast(ui->simplemastermenu->find_item("&File/&Export Parameters...")))) + m->hide(); + + ui->sm_indicator1->value(0); + ui->sm_indicator2->value(0); + ui->sm_indicator1->tooltip(NULL); + ui->sm_indicator2->tooltip(NULL); + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM.H b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM.H new file mode 100644 index 000000000..1c70de9d6 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM.H @@ -0,0 +1,45 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +#pragma once + +#if USE_NSM +#include "NSM/Client.H" + +class NSM_Client:public NSM::Client +{ + public: + + char *project_filename; + char *display_name; + + NSM_Client(); + ~NSM_Client() { } + + protected: + + int command_open(const char *name, + const char *display_name, + const char *client_id, + char **out_msg); + int command_save(char **out_msg); + + void command_active(bool active); +}; +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM/Client.C b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM/Client.C new file mode 100644 index 000000000..83141e27e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM/Client.C @@ -0,0 +1,403 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +#include "Client.H" +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wunused-parameter" + +namespace NSM +{ +/************************/ +/* OSC Message Handlers */ +/************************/ + +#undef OSC_REPLY +#undef OSC_REPLY_ERR + +#define OSC_REPLY(value) lo_send_from(((NSM::Client *)user_data)->nsm_addr, \ + ((NSM::Client *)user_data)->_server, \ + LO_TT_IMMEDIATE, \ + "/reply", \ + "ss", \ + path, \ + value) + +#define OSC_REPLY_ERR(errcode, value) lo_send_from( \ + ((NSM::Client *)user_data)->nsm_addr, \ + ((NSM::Client *)user_data)->_server, \ + LO_TT_IMMEDIATE, \ + "/error", \ + "sis", \ + path, \ + errcode, \ + value) + + Client::Client() + { + nsm_addr = 0; + nsm_client_id = 0; + _session_manager_name = 0; + nsm_is_active = false; + _server = 0; + _st = 0; + } + + Client::~Client() + { + if(_st) + stop(); + + if(_st) + lo_server_thread_free(_st); + else + lo_server_free(_server); + } + + void + Client::announce(const char *application_name, + const char *capabilities, + const char *process_name) + { + // MESSAGE( "Announcing to NSM" ); + + lo_address to = lo_address_new_from_url(nsm_url); + + if(!to) + // MESSAGE( "Bad address" ); + return; + + int pid = (int)getpid(); + + lo_send_from(to, + _server, + LO_TT_IMMEDIATE, + "/nsm/server/announce", + "sssiii", + application_name, + capabilities, + process_name, + 1, + /* api_major_version */ + 0, + /* api_minor_version */ + pid); + + lo_address_free(to); + } + + void + Client::progress(float p) + { + if(nsm_is_active) + lo_send_from(nsm_addr, + _server, + LO_TT_IMMEDIATE, + "/nsm/client/progress", + "f", + p); + } + + void + Client::is_dirty(void) + { + if(nsm_is_active) + lo_send_from(nsm_addr, + _server, + LO_TT_IMMEDIATE, + "/nsm/client/is_dirty", + ""); + } + + void + Client::is_clean(void) + { + if(nsm_is_active) + lo_send_from(nsm_addr, + _server, + LO_TT_IMMEDIATE, + "/nsm/client/is_clean", + ""); + } + + void + Client::message(int priority, const char *msg) + { + if(nsm_is_active) + lo_send_from(nsm_addr, + _server, + LO_TT_IMMEDIATE, + "/nsm/client/message", + "is", + priority, + msg); + } + + + void + Client::broadcast(lo_message msg) + { + if(nsm_is_active) + lo_send_message_from(nsm_addr, + _server, + "/nsm/server/broadcast", + msg); + } + + void + Client::check(int timeout) + { + if(lo_server_wait(_server, timeout)) + while(lo_server_recv_noblock(_server, 0)) {} + } + + void + Client::start() + { + lo_server_thread_start(_st); + } + + void + Client::stop() + { + lo_server_thread_stop(_st); + } + + int + Client::init(const char *nsm_url) + { + this->nsm_url = nsm_url; + + lo_address addr = lo_address_new_from_url(nsm_url); + int proto = lo_address_get_protocol(addr); + lo_address_free(addr); + + _server = lo_server_new_with_proto(NULL, proto, NULL); + + if(!_server) + return -1; + + lo_server_add_method(_server, "/error", "sis", &Client::osc_error, this); + lo_server_add_method(_server, + "/reply", + "ssss", + &Client::osc_announce_reply, + this); + lo_server_add_method(_server, + "/nsm/client/open", + "sss", + &Client::osc_open, + this); + lo_server_add_method(_server, + "/nsm/client/save", + "", + &Client::osc_save, + this); + lo_server_add_method(_server, + "/nsm/client/session_is_loaded", + "", + &Client::osc_session_is_loaded, + this); + lo_server_add_method(_server, NULL, NULL, &Client::osc_broadcast, this); + + return 0; + } + + int + Client::init_thread(const char *nsm_url) + { + this->nsm_url = nsm_url; + + lo_address addr = lo_address_new_from_url(nsm_url); + int proto = lo_address_get_protocol(addr); + lo_address_free(addr); + + _st = lo_server_thread_new_with_proto(NULL, proto, NULL); + _server = lo_server_thread_get_server(_st); + + if(!_server || !_st) + return -1; + + lo_server_thread_add_method(_st, + "/error", + "sis", + &Client::osc_error, + this); + lo_server_thread_add_method(_st, + "/reply", + "ssss", + &Client::osc_announce_reply, + this); + lo_server_thread_add_method(_st, + "/nsm/client/open", + "sss", + &Client::osc_open, + this); + lo_server_thread_add_method(_st, + "/nsm/client/save", + "", + &Client::osc_save, + this); + lo_server_thread_add_method(_st, + "/nsm/client/session_is_loaded", + "", + &Client::osc_session_is_loaded, + this); + lo_server_thread_add_method(_st, + NULL, + NULL, + &Client::osc_broadcast, + this); + + return 0; + } + +/************************/ +/* OSC Message Handlers */ +/************************/ + + int + Client::osc_broadcast(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data) + { + return ((NSM::Client *)user_data)->command_broadcast(path, msg); + } + + int + Client::osc_save(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data) + { + char *out_msg = NULL; + + int r = ((NSM::Client *)user_data)->command_save(&out_msg); + + if(r) + OSC_REPLY_ERR(r, (out_msg ? out_msg : "")); + else + OSC_REPLY("OK"); + + if(out_msg) + free(out_msg); + + return 0; + } + + int + Client::osc_open(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data) + { + char *out_msg = NULL; + + NSM::Client *nsm = (NSM::Client *)user_data; + + nsm->nsm_client_id = strdup(&argv[2]->s); + + int r = ((NSM::Client *)user_data)->command_open(&argv[0]->s, + &argv[1]->s, + &argv[2]->s, + &out_msg); + + if(r) + OSC_REPLY_ERR(r, (out_msg ? out_msg : "")); + else + OSC_REPLY("OK"); + + if(out_msg) + free(out_msg); + + return 0; + } + + int + Client::osc_session_is_loaded(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data) + { + NSM::Client *nsm = (NSM::Client *)user_data; + + nsm->command_session_is_loaded(); + + return 0; + } + + int + Client::osc_error(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data) + { + if(strcmp(&argv[0]->s, "/nsm/server/announce")) + return -1; + + NSM::Client *nsm = (NSM::Client *)user_data; + + +// WARNING( "Failed to register with NSM: %s", &argv[2]->s ); + nsm->nsm_is_active = false; + + nsm->command_active(nsm->nsm_is_active); + + return 0; + } + + int + Client::osc_announce_reply(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data) + { + if(strcmp(&argv[0]->s, "/nsm/server/announce")) + return -1; + + NSM::Client *nsm = (NSM::Client *)user_data; + +// MESSAGE( "Successfully registered. NSM says: %s", &argv[1]->s ); + nsm->nsm_is_active = true; + nsm->_session_manager_name = strdup(&argv[2]->s); + nsm->nsm_addr = + lo_address_new_from_url(lo_address_get_url(lo_message_get_source( + msg))); + + nsm->command_active(nsm->nsm_is_active); + + return 0; + } +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM/Client.H b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM/Client.H new file mode 100644 index 000000000..e515c717e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NSM/Client.H @@ -0,0 +1,143 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +#pragma once + +#include + +namespace NSM +{ + class Client + { + private: + + const char *nsm_url; + + lo_server _server; + lo_server_thread _st; + lo_address nsm_addr; + + bool nsm_is_active; + char *nsm_client_id; + char *_session_manager_name; + + public: + + enum { + ERR_OK = 0, + ERR_GENERAL = -1, + ERR_INCOMPATIBLE_API = -2, + ERR_BLACKLISTED = -3, + ERR_LAUNCH_FAILED = -4, + ERR_NO_SUCH_FILE = -5, + ERR_NO_SESSION_OPEN = -6, + ERR_UNSAVED_CHANGES = -7, + ERR_NOT_NOW = -8 + }; + + Client(); + virtual ~Client(); + + bool is_active(void) { return nsm_is_active; } + + const char *session_manager_name(void) { + return + _session_manager_name; + } + + /* Client->Server methods */ + void is_dirty(void); + void is_clean(void); + void progress(float f); + void message(int priority, const char *msg); + void announce(const char *appliction_name, + const char *capabilities, + const char *process_name); + + void broadcast(lo_message msg); + + /* init without threading */ + int init(const char *nsm_url); + /* init with threading */ + int init_thread(const char *nsm_url); + + /* call this periodically to check for new messages */ + void check(int timeout = 0); + + /* or call these to start and stop a thread (must do your own locking in handler!) */ + void start(void); + void stop(void); + + protected: + + /* Server->Client methods */ + virtual int command_open(const char *name, + const char *display_name, + const char *client_id, + char **out_msg) = 0; + virtual int command_save(char **out_msg) = 0; + + virtual void command_active(bool) { } + + virtual void command_session_is_loaded(void) { } + + /* invoked when an unrecognized message is received. Should return 0 if you handled it, -1 otherwise. */ + virtual int command_broadcast(const char *, lo_message) { return -1; } + + private: + + /* osc handlers */ + static int osc_open(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data); + static int osc_save(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data); + static int osc_announce_reply(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data); + static int osc_error(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data); + static int osc_session_is_loaded(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data); + static int osc_broadcast(const char *path, + const char *types, + lo_arg **argv, + int argc, + lo_message msg, + void *user_data); + }; +}; diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/NioUI.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NioUI.cpp new file mode 100644 index 000000000..8e253b395 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NioUI.cpp @@ -0,0 +1,76 @@ +#include "NioUI.h" +#include "../Nio/Nio.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +NioUI::NioUI() + :Fl_Window(200, 100, 400, 400, "New IO Controls") +{ + //hm, I appear to be leaking memory + Fl_Group *settings = new Fl_Group(0, 20, 400, 400 - 35, "Settings"); + { + audio = new Fl_Choice(60, 80, 100, 25, "Audio"); + audio->callback(audioCallback); + midi = new Fl_Choice(60, 100, 100, 25, "Midi"); + midi->callback(midiCallback); + } + settings->end(); + + //initialize midi list + { + set midiList = Nio::getSources(); + string source = Nio::getSource(); + int midival = 0; + for(set::iterator itr = midiList.begin(); + itr != midiList.end(); ++itr) { + midi->add(itr->c_str()); + if(*itr == source) + midival = midi->size() - 2; + } + midi->value(midival); + } + + //initialize audio list + { + set audioList = Nio::getSinks(); + string sink = Nio::getSink(); + int audioval = 0; + for(set::iterator itr = audioList.begin(); + itr != audioList.end(); ++itr) { + audio->add(itr->c_str()); + if(*itr == sink) + audioval = audio->size() - 2; + } + audio->value(audioval); + } + resizable(this); + size_range(400, 300); +} + +NioUI::~NioUI() +{} + +void NioUI::refresh() +{} + +void NioUI::midiCallback(Fl_Widget *c) +{ + bool good = Nio::setSource(static_cast(c)->text()); + static_cast(c)->textcolor(fl_rgb_color(255 * !good, 0, 0)); +} + +void NioUI::audioCallback(Fl_Widget *c) +{ + bool good = Nio::setSink(static_cast(c)->text()); + static_cast(c)->textcolor(fl_rgb_color(255 * !good, 0, 0)); +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/NioUI.h b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NioUI.h new file mode 100644 index 000000000..242a49b90 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/NioUI.h @@ -0,0 +1,20 @@ +#ifndef NIOUI_H +#define NIOUI_H + +#include +#include + +class NioUI:public Fl_Window +{ + public: + NioUI(); + ~NioUI(); + void refresh(); + private: + class Fl_Choice * midi; + class Fl_Choice * audio; + static void midiCallback(Fl_Widget *c); + static void audioCallback(Fl_Widget *c); +}; + +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/OscilGenUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/OscilGenUI.fl new file mode 100644 index 000000000..a77a041e3 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/OscilGenUI.fl @@ -0,0 +1,1148 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include "../Synth/OscilGen.h"} {public +} + +decl {\#include "../Misc/Util.h"} {public +} + +decl {\#include "../Misc/Master.h"} {public +} + +decl {\#include "ResonanceUI.h"} {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include "EnvelopeUI.h"} {public +} + +decl {\#include "LFOUI.h"} {public +} + +decl {\#include "FilterUI.h"} {public +} + +decl {\#include "PresetsUI.h"} {public +} + +decl {\#include } {public +} + +class OscilSpectrum {: {public Fl_Box} +} { + Function {OscilSpectrum(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {} { + code {oscil=NULL;} {} + } + Function {init(OscilGen *oscil_,int oscbase_,Master *master_)} {} { + code {oscil=oscil_; +oscbase=oscbase_; +master=master_;} {} + } + Function {draw()} {} { + code {int ox=x(),oy=y(),lx=w(),ly=h(),i; +const int maxdb=60;//must be multiple of 10 +int GX=2; +int n=lx/GX-1; +if (n>synth->oscilsize/2) n=synth->oscilsize/2; + +float x; +float* spc=new float[n]; +for (i=0;imutex); +if (oscbase==0) oscil->getspectrum(n,spc,0); + else oscil->getspectrum(n,spc,1); +pthread_mutex_unlock(&master->mutex); + +//normalize +float max=0; +for (i=0;iactive_r()) fl_color(this->parent()->selection_color()); + else fl_color(this->parent()->color()); +fl_line_style(FL_DOT); + +for (i=1;iactive_r()) fl_color(this->parent()->labelcolor()); + else fl_color(this->parent()->color()); +fl_line_style(0); + +//draws the spectrum +for (i=0;idB2rap(-maxdb)) x=rap2dB(x)/maxdb+1; + else x=0; + + int val=(int) ((ly-2)*x); + if (val>0) fl_line(ox+tmp,oy+ly-2-val,ox+tmp,oy+ly-2); +} +delete [] spc;} {} + } + decl {OscilGen *oscil;} {} + decl {int oscbase;} {} + decl {Master *master;} {} +} + +class PSlider {: {public Fl_Slider} +} { + Function {PSlider(int x,int y, int w, int h, const char *label=0):Fl_Slider(x,y,w,h,label)} {} { + code {;} {} + } + Function {handle(int event)} {return_type int + } { + code {int X=x(),Y=y(),W=w(),H=h(); + +if ((!Fl::event_buttons())|| (event==0)||(Fl::event_shift()==0)) return(Fl_Slider::handle(event)); + +if (!Fl::event_inside(X,Y,W,H)) { + if (event==FL_DRAG){ + Fl_Slider::handle(FL_RELEASE); + Fl_Slider::handle(FL_LEAVE); + deactivate(); + activate(); + return(1); + }else{ + return(Fl_Slider::handle(event)); + }; +} else { + //Fl_Slider::handle(FL_FOCUS); + Fl_Slider::handle(FL_PUSH); +}; + +return(1);} {} + } +} + +class Oscilloscope {open : {public Fl_Box} +} { + Function {Oscilloscope(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {open + } { + code {oscil=NULL; +phase=64; +oscbase=0; +box(FL_FLAT_BOX);} {} + } + Function {init(OscilGen *oscil_,Master *master_)} {} { + code {oscil=oscil_; +master=master_;} {} + } + Function {init(OscilGen *oscil_,int oscbase_,Master *master_)} {} { + code {oscil=oscil_; +oscbase=oscbase_; +master=master_;} {} + } + Function {init(OscilGen *oscil_,int oscbase_,int phase_,Master *master_)} {} { + code {oscil=oscil_; +oscbase=oscbase_; +phase=phase_; +master=master_;} {} + } + Function {draw()} {open + } { + code {int ox=x(),oy=y(),lx=w(),ly=h()-1,i; +float smps[synth->oscilsize]; +pthread_mutex_lock(&master->mutex); +if (oscbase==0) oscil->get(smps,-1.0); + else oscil->getcurrentbasefunction(smps); +pthread_mutex_unlock(&master->mutex); + +if (damage()!=1){ + fl_color( fl_color_average( FL_BLACK, FL_BACKGROUND_COLOR, 0.5 )); + fl_rectf(ox,oy,lx,ly); +}; + +//normalize +float max=0; +for (i=0;ioscilsize;i++) + if (maxactive_r()) fl_color(this->parent()->labelcolor()); + else fl_color(this->parent()->color()); +int GX=16;if (lxactive_r()) fl_color(this->parent()->selection_color()); + else fl_color(this->parent()->labelcolor()); + +int lw=2; +//if ((lx<135)||(ly<135)) lw=1; +fl_line_style(FL_SOLID,lw); +fl_begin_line(); +double ph=((phase-64.0)/128.0*synth->oscilsize+synth->oscilsize); +for (i=1;ioscilsize*i/lx)+ph; + double y2=smps[k2%synth->oscilsize]/max; + fl_vertex(i+ox,y2*ly/2.0+oy+ly/2); +}; +fl_end_line(); + +fl_line_style(FL_SOLID,0);} {} + } + decl {OscilGen *oscil;} {} + decl {int oscbase;} {} + decl {int phase;} {public + } + decl {Master *master;} {} +} + +class Oscilharmonic {: {public Fl_Group} +} { + Function {make_window()} {open private + } { + Fl_Window harmonic {open + private xywh {338 259 100 225} type Double box NO_BOX + class Fl_Group visible + } { + Fl_Slider mag { + callback {int x=64; +if (Fl::event_button3()) o->value(x); + else x=127-(int)o->value(); +if (x==64) o->selection_color(0); + else o->selection_color(222); + +pthread_mutex_lock(&master->mutex); + oscil->Phmag[n]=x; + if (x==64) { + oscil->Phphase[n]=64; + phase->value(64); + }; + oscil->prepare(); +pthread_mutex_unlock(&master->mutex); + +display->redraw(); +oldosc->redraw(); +if (cbwidget!=NULL) { + cbwidget->do_callback(); + applybutton->color(FL_RED); + applybutton->redraw(); +};} + xywh {0 15 15 115} type {Vert Knob} box NO_BOX selection_color 222 maximum 127 step 1 value 64 + code0 {o->value(127-oscil->Phmag[n]);} + code1 {if (oscil->Phmag[n]==64) o->selection_color(0);} + class PSlider + } + Fl_Slider phase { + callback {int x=64; +if (Fl::event_button3()) o->value(x); + else x=(int)o->value(); + +pthread_mutex_lock(&master->mutex); + oscil->Phphase[n]=x; + oscil->prepare(); +pthread_mutex_unlock(&master->mutex); + +display->redraw(); +oldosc->redraw(); +if (cbwidget!=NULL) { + cbwidget->do_callback(); + applybutton->color(FL_RED); + applybutton->redraw(); +};} + xywh {0 135 15 75} type {Vert Knob} box NO_BOX selection_color 222 maximum 127 step 1 value 64 + code0 {o->value(oscil->Phphase[n]);} + class PSlider + } + Fl_Box {} { + xywh {15 70 5 5} box FLAT_BOX color 45 + } + Fl_Box {} { + xywh {15 170 5 5} box FLAT_BOX color 45 + } + Fl_Box {} { + label 01 + xywh {0 210 20 15} labelfont 1 labelsize 9 align 20 + code0 {char tmp[10];snprintf(tmp,10,"%d",n+1);o->label(strdup(tmp));} + } + Fl_Box {} { + label 01 + xywh {0 0 20 15} labelfont 1 labelsize 9 align 20 + code0 {char tmp[10];snprintf(tmp,10,"%d",n+1);o->label(strdup(tmp));} + } + } + } + Function {Oscilharmonic(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {n=0; +oscil=NULL; +display=NULL; +applybutton=NULL; +cbwidget=NULL;} {} + } + Function {init(OscilGen *oscil_,int n_,Fl_Group *display_,Fl_Widget *oldosc_,Fl_Widget *cbwidget_,Fl_Widget *applybutton_, Master *master_)} {} { + code {oscil=oscil_; +n=n_; +display=display_; +master=master_; +oldosc=oldosc_; +cbwidget=cbwidget_; +applybutton=applybutton_; +make_window(); +end(); +harmonic->show();} {} + } + Function {refresh()} {} { + code {mag->value(127-oscil->Phmag[n]); +phase->value(oscil->Phphase[n]); + +if (oscil->Phmag[n]==64) mag->selection_color(0); + else mag->selection_color(222);} {} + } + Function {~Oscilharmonic()} {} { + code {harmonic->hide(); +//delete(harmonic);} {} + } + decl {OscilGen *oscil;} {} + decl {Fl_Group *display;} {} + decl {int n;} {} + decl {Fl_Widget *oldosc,*cbwidget,*applybutton;} {} + decl {Master *master;} {} +} + +class OscilEditor {open : {public PresetsUI_} +} { + Function {make_window()} {open + } { + Fl_Window osceditUI { + label {ADsynth Oscillator Editor} open + xywh {542 193 735 595} type Double + code0 {if (oscil->ADvsPAD) o->label("PADsynth Harmonic Content Editor");} visible + } { + Fl_Button applybutton { + label Apply + callback {applybutton->color(FL_GRAY); +applybutton->redraw(); +if (cbapplywidget!=NULL) { + cbapplywidget->do_callback(); + cbapplywidget->color(FL_GRAY); + cbapplywidget->redraw(); +};} + xywh {300 280 60 20} box THIN_UP_BOX labelfont 1 + code0 {if (!oscil->ADvsPAD) o->hide();} + } + Fl_Group oscildisplaygroup { + xywh {5 5 360 300} box UP_FRAME + } { + Fl_Group {} {open + xywh {10 85 350 190} box THIN_DOWN_BOX color 32 selection_color 71 labelcolor 179 + code0 {Oscilloscope *osc=new Oscilloscope(o->x(),o->y(),o->w(),o->h(),"");} + code1 {osc->init(oscil,master);} + } {} + Fl_Box {} { + label Oscillator + xywh {120 10 110 20} labelfont 1 + } + Fl_Value_Slider rndslider { + label rnd + callback {oscil->Prand=(int)o->value()+64; +oscildisplaygroup->redraw(); +oldosc->redraw();} + tooltip {Oscilator Phase Randomness: smaller than 0 is "group", larger than 0 is for each harmonic} xywh {140 285 100 10} type {Horz Knob} box NO_BOX labelsize 10 align 5 minimum -64 maximum 63 step 1 + code0 {if (oscil->ADvsPAD) o->hide();} + } + Fl_Group {} {open + xywh {10 30 350 50} box THIN_DOWN_BOX color 32 selection_color 218 labelcolor 63 + code0 {OscilSpectrum *spc=new OscilSpectrum(o->x(),o->y(),o->w(),o->h(),"");} + code1 {spc->init(oscil,0,master);} + } {} + Fl_Group {} { + xywh {246 277 115 25} box UP_FRAME + code0 {if (oscil->ADvsPAD) o->hide();} + } { + Fl_Choice hrndtype { + label {H.rnd} + callback {oscil->Pamprandtype=(int) o->value();} + tooltip {Harmonic Amplitude Randomness} xywh {281 282 50 15} down_box BORDER_BOX labelsize 10 textsize 10 + } { + MenuItem {} { + label None + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pow + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sin + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial hrnddial { + callback {oscil->Pamprandpower=(int) o->value();} + tooltip {Oscillator's spectrum adjust parameter} xywh {338 280 18 18} maximum 127 step 1 + class WidgetPDial + } + } + } + Fl_Group basefuncdisplaygroup {open selected + xywh {365 5 360 300} box UP_FRAME + } { + Fl_Group {} { + xywh {370 85 350 190} box THIN_DOWN_BOX color 32 selection_color 71 labelcolor 179 + code0 {Oscilloscope *osc=new Oscilloscope(o->x(),o->y(),o->w(),o->h(),"");} + code1 {osc->init(oscil,1,master);} + } {} + Fl_Dial bfslider { + callback {oscil->Pbasefuncpar=(int)o->value()+64; +basefuncdisplaygroup->redraw(); +bfparval->value(oscil->Pbasefuncpar-64); + +redrawoscil();} + tooltip {Base Function Parameter} xywh {520 280 20 20} minimum -64 maximum 63 step 1 + class WidgetPDial + } + Fl_Choice bftype { + label {Base.F..} + callback {oscil->Pcurrentbasefunc=(int) o->value(); + +basefuncdisplaygroup->redraw(); +redrawoscil(); + +if ((oscil->Pcurrentbasefunc==0)||(oscil->Pcurrentbasefunc==127)) basefuncmodulation->deactivate(); + else basefuncmodulation->activate();} + xywh {370 285 90 15} down_box BORDER_BOX labelsize 10 align 5 textsize 11 + } { + MenuItem {} { + label Sine + xywh {10 10 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Triangle + xywh {20 20 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Pulse + xywh {30 30 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Saw + xywh {40 40 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Power + xywh {50 50 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Gauss + xywh {50 50 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Diode + xywh {60 60 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label AbsSine + xywh {70 70 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label PulseSine + xywh {80 80 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label StrchSine + xywh {90 90 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Chirp + xywh {100 100 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label AbsStrSine + xywh {102 102 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Chebyshev + xywh {112 112 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Sqr + xywh {122 122 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Spike + xywh {122 122 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Circle + xywh {122 122 100 20} labelfont 1 labelsize 11 + } + } + Fl_Box {} { + label {Base Func.} + xywh {480 10 110 20} labelfont 1 + } + Fl_Group {} {open + xywh {370 30 350 50} box THIN_DOWN_BOX color 32 selection_color 218 labelcolor 63 + code0 {OscilSpectrum *spc=new OscilSpectrum (o->x(),o->y(),o->w(),o->h(),"");} + code1 {spc->init(oscil,1,master);} + } {} + Fl_Value_Output bfparval { + label {Par.} + xywh {490 285 25 15} labelsize 12 minimum -63 maximum 63 step 1 + } + Fl_Group basefuncmodulation { + xywh {550 276 169 25} box UP_FRAME + code0 {if ((oscil->Pcurrentbasefunc==0)||(oscil->Pcurrentbasefunc==127)) basefuncmodulation->deactivate();} + } { + Fl_Choice bfmodtype { + label {B.F.Mod.} + callback {oscil->Pbasefuncmodulation=(int) o->value(); +basefuncdisplaygroup->redraw(); +redrawoscil();} + tooltip {Base function modulation} xywh {599 281 50 15} down_box BORDER_BOX labelsize 10 textsize 10 + } { + MenuItem {} { + label None + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Rev + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sine + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pow + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial bfmodpar1 { + callback {oscil->Pbasefuncmodulationpar1=(int)o->value(); +basefuncdisplaygroup->redraw(); +redrawoscil();} + tooltip {Oscillator's modulation parameter 1} xywh {659 281 15 15} maximum 127 step 1 + class WidgetPDial + } + Fl_Dial bfmodpar2 { + callback {oscil->Pbasefuncmodulationpar2=(int)o->value(); +basefuncdisplaygroup->redraw(); +redrawoscil();} + tooltip {Oscillator's modulation parameter 2} xywh {679 281 15 15} maximum 127 step 1 + class WidgetPDial + } + Fl_Dial bfmodpar3 { + callback {oscil->Pbasefuncmodulationpar3=(int)o->value(); +basefuncdisplaygroup->redraw(); +redrawoscil();} + tooltip {Oscillator's modulation parameter 3} xywh {699 281 15 15} maximum 127 step 1 + class WidgetPDial + } + } + } + Fl_Choice magtype { + label {Mag.Type} + callback {oscil->Phmagtype=(int) o->value(); +basefuncdisplaygroup->redraw(); + +redrawoscil();} + xywh {70 280 65 20} down_box BORDER_BOX labelsize 11 textsize 11 + } { + MenuItem {} { + label Linear + xywh {0 0 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {-40dB} + xywh {10 10 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {-60dB} + xywh {20 20 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {-80dB} + xywh {30 30 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {-100dB} + xywh {40 40 100 20} labelfont 1 labelsize 11 + } + } + Fl_Button {} { + label {Use as base} + callback {oscil->useasbase(); +if (autoclearbutton->value()){ + for (int i=0;imag->value(64); + oscil->Phmag[i]=64; + h[i]->phase->value(64); + oscil->Phphase[i]=64; + }; + oscil->Phmag[0]=127; + + oscil->Pharmonicshift=0; + harmonicshiftcounter->value(0); + + h[0]->mag->value(0); + wshbutton->value(0); + wshbutton->do_callback(); + fltbutton->value(0); + fltbutton->do_callback(); + sabutton->value(0); + sabutton->do_callback(); +}; + +pthread_mutex_lock(&master->mutex); + for (int i=0;iPhmag[i]==64) h[i]->mag->selection_color(0); + else h[i]->mag->selection_color(222); + }; + oscil->prepare(); +pthread_mutex_unlock(&master->mutex); + +basefuncdisplaygroup->redraw(); +redrawoscil();} + tooltip {Use this Oscillator as base function} xywh {5 313 85 20} box THIN_UP_BOX labelfont 1 labelsize 11 + } + Fl_Button {} { + label Close + callback {osceditUI->hide();} + xywh {668 565 62 25} box THIN_UP_BOX + } + Fl_Button {} { + label Clear + callback {if (!fl_choice("Clear the harmonics settings?","No","Yes",NULL)) return; + +for (int i=0;imag->value(64); + oscil->Phmag[i]=64; + h[i]->phase->value(64); + oscil->Phphase[i]=64; +}; +oscil->Phmag[0]=127; +h[0]->mag->value(0); + +for (int i=0;iPhmag[i]==64) h[i]->mag->selection_color(0); + else h[i]->mag->selection_color(222); +}; + +//harmonics->redraw(); + +pthread_mutex_lock(&master->mutex); + oscil->prepare(); +pthread_mutex_unlock(&master->mutex); + +redrawoscil();} + xywh {670 505 55 15} box THIN_UP_BOX labelfont 1 labelsize 11 + } + Fl_Group {} { + xywh {135 308 150 30} box UP_FRAME + } { + Fl_Choice wshbutton { + label {Wsh.} + callback {oscil->Pwaveshapingfunction=(int) o->value(); +basefuncdisplaygroup->redraw(); +redrawoscil();} open + tooltip {Waveshaping function} xywh {165 313 55 20} down_box BORDER_BOX labelsize 10 textsize 10 + } { + MenuItem {} { + label None + xywh {25 25 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Atan + xywh {35 35 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Asym1 + xywh {45 45 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pow + xywh {55 55 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sine + xywh {65 65 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Qnts + xywh {75 75 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Zigzg + xywh {85 85 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Lmt + xywh {95 95 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LmtU + xywh {105 105 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LmtL + xywh {115 115 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label ILmt + xywh {127 127 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Clip + xywh {137 137 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Asym2 + xywh {85 85 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pow2 + xywh {95 95 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sgm + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial wshpar { + callback {oscil->Pwaveshaping=(int)o->value()+64; +wsparval->value(oscil->Pwaveshaping-64); +redrawoscil();} + tooltip {Waveshaping Parameter} xywh {260 313 20 20} minimum -64 maximum 63 step 1 + class WidgetPDial + } + Fl_Value_Output wsparval { + xywh {228 316 25 15} labelsize 12 minimum -63 maximum 63 step 1 + } + } + Fl_Light_Button autoclearbutton { + label {Clr.} + tooltip {Auto clear when using the oscillator as base function} xywh {95 313 35 20} box THIN_UP_BOX value 1 labelfont 1 labelsize 10 + } + Fl_Group {} { + xywh {285 308 155 30} box UP_FRAME + } { + Fl_Choice fltbutton { + label Filter + callback {oscil->Pfiltertype=(int) o->value(); + +redrawoscil();} + tooltip {Oscillator's filter type} xywh {315 313 50 20} down_box BORDER_BOX labelsize 10 textsize 10 + } { + MenuItem {} { + label None + xywh {35 35 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LP1 + xywh {45 45 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label HP1a + xywh {55 55 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label HP1b + xywh {65 65 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label BP1 + xywh {75 75 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label BS1 + xywh {85 85 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LP2 + xywh {55 55 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label HP2 + xywh {65 65 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label BP2 + xywh {65 65 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label BS2 + xywh {75 75 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Cos + xywh {75 75 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sin + xywh {85 85 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label LSh + xywh {95 95 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label S + xywh {105 105 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial filtervalue1 { + callback {oscil->Pfilterpar1=(int)o->value(); + +redrawoscil();} + tooltip {Oscillator's filter parameter1} xywh {367 313 20 20} maximum 127 step 1 + class WidgetPDial + } + Fl_Check_Button filterpref { + label p + callback {oscil->Pfilterbeforews=(int)o->value(); + +redrawoscil();} + tooltip {Apply the filter before the waveshaping} xywh {415 313 20 20} down_box DOWN_BOX labelsize 10 align 24 + } + Fl_Dial filtervalue2 { + callback {oscil->Pfilterpar2=(int)o->value(); + +redrawoscil();} + tooltip {Oscillator's filter parameter2} xywh {392 313 20 20} maximum 127 step 1 + class WidgetPDial + } + } + Fl_Group {} { + xywh {590 308 135 30} box UP_FRAME + } { + Fl_Choice sabutton { + label {Sp.adj.} + callback {oscil->Psatype=(int) o->value(); +redrawoscil();} + tooltip {Oscillator's spectrum adjust} xywh {630 313 60 20} down_box BORDER_BOX labelsize 10 textsize 10 + } { + MenuItem {} { + label None + xywh {55 55 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pow + xywh {65 65 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label ThrsD + xywh {75 75 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label ThrsU + xywh {85 85 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial sadjpar { + callback {oscil->Psapar=(int)o->value(); +redrawoscil();} + tooltip {Oscillator's spectrum adjust parameter} xywh {695 313 20 20} maximum 127 step 1 + class WidgetPDial + } + } + Fl_Group {} { + xywh {665 340 65 65} box UP_FRAME + } { + Fl_Counter harmonicshiftcounter { + label {Harmonic Shift} + callback {oscil->Pharmonicshift=(int)o->value(); +redrawoscil();} + xywh {670 365 55 15} type Simple labelsize 10 align 129 minimum -64 maximum 64 step 1 textfont 1 textsize 10 + } + Fl_Check_Button harmonicshiftpre { + label preH + callback {oscil->Pharmonicshiftfirst=(int)o->value(); +redrawoscil();} + tooltip {Apply the harmonic shift before the waveshaping and filtering} xywh {690 385 34 15} down_box DOWN_BOX labelsize 10 align 24 + } + Fl_Button {} { + label R + callback {oscil->Pharmonicshift=0; +harmonicshiftcounter->value(0); +redrawoscil();} + xywh {670 385 20 15} box THIN_UP_BOX labelfont 1 labelsize 10 + } + } + Fl_Group {} {open + xywh {665 410 65 90} box UP_FRAME + } { + Fl_Choice adhrtype { + label {Adpt.Harm.} + callback {oscil->Padaptiveharmonics=(int) o->value(); +redrawoscil();} + tooltip {The type of the addaptive harmonics} xywh {670 425 55 15} down_box BORDER_BOX labelsize 10 align 129 when 6 textsize 10 + } { + MenuItem {} { + label OFF + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label ON + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Square + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label 2xSub + xywh {110 110 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label 2xAdd + xywh {120 120 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label 3xSub + xywh {120 120 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label 3xAdd + xywh {130 130 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label 4xSub + xywh {130 130 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label 4xAdd + xywh {140 140 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial adhrpow { + label pow + callback {oscil->Padaptiveharmonicspower=(int)o->value(); +redrawoscil();} + tooltip {Adaptive harmonics power} xywh {700 460 25 25} labelsize 10 maximum 200 step 1 + class WidgetPDial + } + Fl_Dial adhrbf { + label baseF + callback {oscil->Padaptiveharmonicsbasefreq=(int)o->value(); +redrawoscil();} + tooltip {Adaptive harmonics base frequency} xywh {670 460 25 25} labelsize 10 maximum 255 step 1 + class WidgetPDial + } + Fl_Slider adhrpar { + callback {oscil->Padaptiveharmonicspar=(int)o->value(); +redrawoscil();} + xywh {670 445 55 10} type {Horz Knob} box NO_BOX maximum 100 step 1 value 50 + } + } + Fl_Group {} { + xywh {440 308 150 30} box UP_FRAME + } { + Fl_Choice modtype { + label {Mod.} + callback {oscil->Pmodulation=(int) o->value(); + +redrawoscil();} + tooltip modulation xywh {470 315 50 15} down_box BORDER_BOX labelsize 10 textsize 10 + } { + MenuItem {} { + label None + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Rev + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sine + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Pow + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial modpar1 { + callback {oscil->Pmodulationpar1=(int)o->value(); + +redrawoscil();} + tooltip {Oscillator's modulation parameter 1} xywh {530 315 15 15} maximum 127 step 1 + class WidgetPDial + } + Fl_Dial modpar2 { + callback {oscil->Pmodulationpar2=(int)o->value(); + +redrawoscil();} + tooltip {Oscillator's modulation parameter 2} xywh {550 315 15 15} maximum 127 step 1 + class WidgetPDial + } + Fl_Dial modpar3 { + callback {oscil->Pmodulationpar3=(int)o->value(); +redrawoscil();} + tooltip {Oscillator's modulation parameter 3} xywh {570 315 15 15} maximum 127 step 1 + class WidgetPDial + } + } + Fl_Button {} { + label Sine + callback {if (!fl_choice("Convert to SINE?","No","Yes",NULL)) return; + +pthread_mutex_lock(&master->mutex); + oscil->convert2sine(); +pthread_mutex_unlock(&master->mutex); + +redrawoscil(); +refresh();} + xywh {670 525 55 15} box THIN_UP_BOX labelfont 1 labelsize 11 + } + Fl_Button {} { + label C + callback {presetsui->copy(oscil);} + xywh {670 545 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(oscil,this);} + xywh {700 545 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Scroll _this_has_to_be_the_last { + xywh {5 340 660 250} type HORIZONTAL box FLAT_BOX + } { + Fl_Pack harmonics {open + xywh {10 345 650 225} type HORIZONTAL + code0 {for (int i=0;ih(),"");h[i]->init(oscil,i,oscildisplaygroup,oldosc,cbwidget,applybutton,master);}} + } {} + } + } + } + Function {OscilEditor(OscilGen *oscil_,Fl_Widget *oldosc_,Fl_Widget *cbwidget_,Fl_Widget *cbapplywidget_,Master *master_)} {} { + code {oscil=oscil_; +oldosc=oldosc_; +cbwidget=cbwidget_; +cbapplywidget=cbapplywidget_; +master=master_; + +make_window(); + +refresh(); +osceditUI->show();} {} + } + Function {~OscilEditor()} {} { + code {osceditUI->hide(); +//for (int i=0;ivalue(oscil->Phmagtype); +rndslider->value(oscil->Prand-64); + +hrndtype->value(oscil->Pamprandtype); +hrnddial->value(oscil->Pamprandpower); + +bftype->value(oscil->Pcurrentbasefunc); +bfparval->value(oscil->Pbasefuncpar-64); +bfslider->value(oscil->Pbasefuncpar-64); + +bfmodtype->value(oscil->Pbasefuncmodulation); +bfmodpar1->value(oscil->Pbasefuncmodulationpar1); +bfmodpar2->value(oscil->Pbasefuncmodulationpar2); +bfmodpar3->value(oscil->Pbasefuncmodulationpar3); + +wshbutton->value(oscil->Pwaveshapingfunction); +wsparval->value(oscil->Pwaveshaping-64); +wshpar->value(oscil->Pwaveshaping-64); + +fltbutton->value(oscil->Pfiltertype); +filtervalue1->value(oscil->Pfilterpar1); +filtervalue2->value(oscil->Pfilterpar2); +filterpref->value(oscil->Pfilterbeforews); + +modtype->value(oscil->Pmodulation); +modpar1->value(oscil->Pmodulationpar1); +modpar2->value(oscil->Pmodulationpar2); +modpar3->value(oscil->Pmodulationpar3); + +sabutton->value(oscil->Psatype); +sadjpar->value(oscil->Psapar); + +harmonicshiftcounter->value(oscil->Pharmonicshift); +harmonicshiftpre->value(oscil->Pharmonicshiftfirst); + +adhrtype->value(oscil->Padaptiveharmonics); +adhrbf->value(oscil->Padaptiveharmonicsbasefreq); +adhrpow->value(oscil->Padaptiveharmonicspower); +adhrtype->value(oscil->Padaptiveharmonicspar); + +for (int i=0;irefresh(); + +pthread_mutex_lock(&master->mutex); + oscil->prepare(); +pthread_mutex_unlock(&master->mutex); + +basefuncdisplaygroup->redraw(); +redrawoscil();} {} + } + Function {redrawoscil()} {} { + code {oscildisplaygroup->redraw(); +oldosc->redraw(); +if (cbwidget!=NULL) { + cbwidget->do_callback(); + applybutton->color(FL_RED); + applybutton->redraw(); +};} {} + } + decl {OscilGen *oscil;} {} + decl {Fl_Widget *oldosc,*cbwidget,*cbapplywidget;} {} + decl {Oscilharmonic *h[MAX_AD_HARMONICS];} {} + decl {Master *master;} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/PADnoteUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/PADnoteUI.fl new file mode 100644 index 000000000..a47ec6025 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/PADnoteUI.fl @@ -0,0 +1,1114 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {\#include "../Params/PADnoteParameters.h"} {public +} + +decl {\#include "../Misc/Util.h"} {public +} + +decl {\#include "../Misc/Master.h"} {public +} + +decl {\#include "ResonanceUI.h"} {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include "EnvelopeUI.h"} {public +} + +decl {\#include "LFOUI.h"} {public +} + +decl {\#include "FilterUI.h"} {public +} + +decl {\#include "OscilGenUI.h"} {public +} + +decl {\#include "PresetsUI.h"} {public +} + +class PADnoteHarmonicProfile {: {public Fl_Box} +} { + Function {PADnoteHarmonicProfile(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {} { + code {pars=NULL;} {} + } + Function {init(PADnoteParameters *pars,Master *master_)} {} { + code {master=master_; +this->pars=pars;} {} + } + Function {draw()} {} { + code {int ox=x(),oy=y(),lx=w(),ly=h(); +if (!visible()) return; +float smps[lx]; + +float realbw=pars->getprofile(smps,lx); +bool active=active_r(); + +//draw the equivalent bandwidth +if (active) fl_color(220,220,220); + else fl_color(160,165,165); +fl_line_style(FL_DASH); +int rbw=(int)(realbw*(lx-1.0)/2.0); +fl_begin_line(); +for (int i=lx/2-rbw;i<(lx/2+rbw);i++) { fl_vertex(ox+i,oy); } +fl_end_line(); + +fl_line_style(FL_DASH); +if (active) fl_color(200,200,200); + else fl_color(160,160,160); +for (int i=1;i<10;i++){ + int kx=(int)(lx/10.0*i); + fl_line( ox + kx, oy, ox + kx, oy + ly - 1 ); +}; +for (int i=1;i<5;i++){ + int ky=(int)(ly/5.0*i); + fl_line(ox,oy+ly-ky,ox+lx,oy+ly-ky-1); +}; + + +fl_color(120,120,120); +fl_line_style(FL_DASH); +fl_line(ox+lx/2,oy,ox+lx/2,oy+ly); + +//draw the graph +fl_line_style(FL_SOLID); + if (active) fl_color(180,210,240); + else fl_color(150,150,155); + +fl_begin_polygon(); +fl_vertex( ox, oy + h() ); +for (int i=0;i0) +// { +// fl_vertex(ox+i-1,oy+ly-2-old); +// fl_vertex(ox+i,oy+ly-2-val); +// } +}; +fl_vertex( ox + w(), oy + h() ); +fl_end_polygon(); + + +fl_line_style(FL_DASH); +if (active) fl_color(0,100,220); + else fl_color(150,160,170); +fl_line(ox+lx/2-rbw,oy,ox+lx/2-rbw,oy+ly-1); +fl_line(ox+lx/2+rbw,oy,ox+lx/2+rbw,oy+ly-1); + +fl_line_style(0);} {} + } + decl {Master *master;} {} + decl {PADnoteParameters *pars;} {public + } +} + +class PADnoteOvertonePosition {: {public Fl_Box} +} { + Function {PADnoteOvertonePosition(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {} { + code {pars=NULL;} {} + } + Function {init(PADnoteParameters *pars,Master *master_)} {} { + code {master=master_; +this->pars=pars;} {} + } + Function {draw()} {} { + code {if (!visible()) return; +const int maxdb=60; + +int ox=x(),oy=y(),lx=w(),ly=h(); +const int maxharmonic=64; + + +for (int i=1;ioscilsize/2; +float spc[n]; +for (int i=0;imutex); +pars->oscilgen->getspectrum(n,spc,0); +pthread_mutex_unlock(&master->mutex); + + +//normalize +float max=0; +for (int i=0;igetNhr(i); + int kx=(int)(lx/(float)maxharmonic*nhr); + if ((kx<0)||(kx>lx)) continue; + + spectrum[kx]=spc[i-1]/max+1e-9; + +}; + +fl_color(180,0,0); +fl_line_style(0); + +if (pars->Pmode==2){ + int old=0; + for (int i=1;i1e-10)||(i==(lx-1))){ + int delta=i-old; + float val1=spectrum[old]; + float val2=spectrum[i]; + + float idelta=1.0/delta; + for (int j=0;jdB2rap(-maxdb)) x=rap2dB(x)/maxdb+1; + else continue; + int yy=(int)(x*ly); + fl_line(ox+i,oy+ly-1-yy,ox+i,oy+ly-1); + +};} {} + } + decl {Master *master;} {} + decl {PADnoteParameters *pars;} {public + } +} + +class PADnoteUI {open : {public PresetsUI_} +} { + Function {PADnoteUI(PADnoteParameters *parameters,Master *master_)} {open + } { + code {pars=parameters; +master=master_; +oscui=NULL; +resui=new ResonanceUI(pars->resonance); +make_window();} {} + } + Function {make_window()} {open + } { + Fl_Window padnotewindow { + label {PAD synth Parameters} open + xywh {294 392 535 435} type Double visible + } { + Fl_Tabs {} { + callback {if (o->value()!=harmonicstructuregroup) applybutton->hide(); + else applybutton->show();} open + xywh {0 0 535 395} box UP_FRAME + } { + Fl_Group harmonicstructuregroup { + label {Harmonic Structure} open selected + xywh {0 20 535 375} box UP_FRAME + } { + Fl_Group bwprofilegroup { + xywh {5 30 90 260} box UP_FRAME + code0 {if (pars->Pmode!=0) o->deactivate();} + } { + Fl_Dial hpbasepar1 { + label Width + callback {pars->Php.base.par1=(int) o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {20 75 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->Php.base.par1);} + class WidgetPDial + } + Fl_Choice hpbasetype { + label {Base Type} + callback {pars->Php.base.type=o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {15 45 75 15} down_box BORDER_BOX labelsize 10 align 5 textsize 10 + code0 {o->value(pars->Php.base.type);} + } { + MenuItem {} { + label Gauss + xywh {15 15 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Square + xywh {25 25 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label DoubleExp + xywh {35 35 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial hpfreqmult { + label FreqMlt + callback {pars->Php.freqmult=(int) o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {55 75 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->Php.freqmult);} + class WidgetPDial + } + Fl_Dial hpmpar1 { + label Str + callback {pars->Php.modulator.par1=(int) o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {15 115 20 20} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->Php.modulator.par1);} + class WidgetPDial + } + Fl_Dial hpmfreq { + label SFreq + callback {pars->Php.modulator.freq=(int) o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {40 115 20 20} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->Php.modulator.freq);} + class WidgetPDial + } + Fl_Group {} { + xywh {10 160 80 105} box BORDER_BOX + } { + Fl_Choice hpamptype { + label AmpMultiplier + callback {pars->Php.amp.type=o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {15 175 70 15} down_box BORDER_BOX labelsize 10 align 5 textsize 10 + code0 {o->value(pars->Php.amp.type);} + } { + MenuItem {} { + label OFF + xywh {45 45 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Gauss + xywh {55 55 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Sine + xywh {65 65 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Flat + xywh {75 75 100 20} labelfont 1 labelsize 10 + } + } + Fl_Choice hpampmode { + label AmpMode + callback {pars->Php.amp.mode=o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {15 205 70 15} down_box BORDER_BOX labelsize 10 align 5 textsize 10 + code0 {o->value(pars->Php.amp.mode);} + } { + MenuItem {} { + label Sum + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Mult + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Div1 + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Div2 + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial hpamppar1 { + label Par1 + callback {pars->Php.amp.par1=(int) o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {15 235 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->Php.amp.par1);} + class WidgetPDial + } + Fl_Dial hpamppar2 { + label Par2 + callback {pars->Php.amp.par2=(int) o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {55 235 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->Php.amp.par2);} + class WidgetPDial + } + } + Fl_Check_Button hpautoscale { + label autoscale + callback {pars->Php.autoscale=(int) o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {10 270 60 15} down_box DOWN_BOX labelsize 10 + code0 {o->value(pars->Php.autoscale);} + } + Fl_Choice hponehalf { + callback {pars->Php.onehalf=o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {10 143 80 15} down_box BORDER_BOX labelsize 10 align 5 textsize 10 + code0 {o->value(pars->Php.onehalf);} + } { + MenuItem {} { + label Full + xywh {25 25 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Upper Half} + xywh {45 45 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Lower Half} + xywh {35 35 100 20} labelfont 1 labelsize 10 + } + } + Fl_Dial hpwidth { + label Size + callback {pars->Php.width=(int) o->value(); +hprofile->redraw(); +cbwidget->do_callback();} + xywh {65 115 20 20} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->Php.width);} + class WidgetPDial + } + } + Fl_Group {} { + xywh {100 155 270 135} box THIN_DOWN_BOX color 32 selection_color 71 labelcolor 179 align 6 + code0 {osc=new Oscilloscope(o->x(),o->y(),o->w(),o->h(),"");} + code1 {osc->init(pars->oscilgen,master);} + } {} + Fl_Button {} { + label Change + callback {if (oscui!=NULL) delete (oscui); +oscui=new OscilEditor(pars->oscilgen,osc,cbwidget,applybutton,master);} + xywh {375 270 60 20} box THIN_UP_BOX labelfont 1 labelsize 11 + } + Fl_Box cbwidget { + label {Harmonic Content} + callback {overtonepos->redraw(); +applybutton->color(FL_RED); +applybutton->redraw();} + xywh {125 135 205 20} align 16 + } + Fl_Button {} { + label Resonance + callback {resui->resonancewindow->redraw(); +resui->resonancewindow->show(); +resui->setcbwidget(cbwidget,applybutton);} + xywh {375 225 80 20} box THIN_UP_BOX + } + Fl_Dial bwdial { + label BandWidth + callback {bwcents->value(pars->setPbandwidth((int) o->value())); +cbwidget->do_callback();} + xywh {15 295 35 35} box ROUND_UP_BOX labelsize 10 maximum 1000 step 1 + code0 {o->value(pars->Pbandwidth);} + code1 {if (pars->Pmode!=0) o->deactivate();} + class WidgetPDial + } + Fl_Value_Output bwcents { + label cents + xywh {55 305 55 15} labelsize 10 align 6 maximum 10000 step 0.1 + code0 {o->value(pars->setPbandwidth(pars->Pbandwidth));} + code1 {if (pars->Pmode!=0) o->deactivate();} + } + Fl_Group {} { + xywh {315 295 215 45} box UP_FRAME + } { + Fl_Choice hrpostype { + label OvertonesPosition + callback {pars->Phrpos.type=o->value(); +overtonepos->redraw(); +cbwidget->do_callback();} + xywh {325 310 80 20} down_box BORDER_BOX labelsize 10 align 5 textsize 11 + code0 {o->value(pars->Phrpos.type);} + } { + MenuItem {} { + label Harmonic + xywh {70 70 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label ShiftU + xywh {80 80 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label ShiftL + xywh {90 90 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label PowerU + xywh {90 90 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label PowerL + xywh {100 100 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Sine + xywh {110 110 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Power + xywh {120 120 100 20} labelfont 1 labelsize 11 + } + } + Fl_Dial hrpospar1 { + label Par1 + callback {pars->Phrpos.par1=(int) o->value(); +overtonepos->redraw(); +cbwidget->do_callback();} + xywh {425 310 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 255 step 1 + code0 {o->value(pars->Phrpos.par1);} + class WidgetPDial + } + Fl_Dial hrpospar2 { + label Par2 + callback {pars->Phrpos.par2=(int) o->value(); +overtonepos->redraw(); +cbwidget->do_callback();} + xywh {460 310 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 255 step 1 + code0 {o->value(pars->Phrpos.par2);} + class WidgetPDial + } + Fl_Dial hrpospar3 { + label ForceH + callback {pars->Phrpos.par3=(int) o->value(); +overtonepos->redraw(); +cbwidget->do_callback();} + xywh {495 310 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 255 step 1 + code0 {o->value(pars->Phrpos.par3);} + class WidgetPDial + } + } + Fl_Choice bwscale { + label {Bandwidth Scale} + callback {pars->Pbwscale=(int) o->value(); +cbwidget->do_callback();} + xywh {120 305 80 20} down_box BORDER_BOX labelsize 10 align 5 textsize 11 + code0 {o->value(pars->Pbwscale);} + code1 {if (pars->Pmode!=0) o->deactivate();} + } { + MenuItem {} { + label Normal + xywh {95 95 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label EqualHz + xywh {105 105 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Quater + xywh {115 115 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Half + xywh {125 125 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {75%} + xywh {135 135 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {150%} + xywh {145 145 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Double + xywh {145 145 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {Inv.Half} + xywh {155 155 100 20} labelfont 1 labelsize 11 + } + } + Fl_Group overtonepos { + xywh {5 345 525 45} box FLAT_BOX color 51 selection_color 218 labelcolor 63 + code0 {PADnoteOvertonePosition *opui=new PADnoteOvertonePosition(o->x(),o->y(),o->w(),o->h(),"");} + code1 {opui->init(pars,master);} + } {} + Fl_Choice qsamplesize { + label {Sample Size} + callback {pars->Pquality.samplesize=(int) o->value(); +cbwidget->do_callback();} + xywh {375 190 115 20} down_box BORDER_BOX labelsize 10 align 5 textsize 11 + code0 {o->value(pars->Pquality.samplesize);} + } { + MenuItem {} { + label {16k (Tiny)} + xywh {155 155 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 32k + xywh {165 165 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {64k (Small)} + xywh {175 175 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 128k + xywh {185 185 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {256k (Normal)} + xywh {205 205 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 512k + xywh {200 200 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {1M (Big)} + xywh {205 205 100 20} labelfont 1 labelsize 11 + } + } + Fl_Choice qsmpoct { + label {smp/oct} + callback {pars->Pquality.smpoct=(int) o->value(); +cbwidget->do_callback();} + xywh {430 155 45 20} down_box BORDER_BOX labelsize 11 align 5 textsize 11 + code0 {o->value(pars->Pquality.smpoct);} + } { + MenuItem {} { + label {0.5} + xywh {10 10 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 1 + xywh {0 0 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 2 + xywh {10 10 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 3 + xywh {20 20 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 4 + xywh {30 30 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 6 + xywh {40 40 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 12 + xywh {50 50 100 20} labelfont 1 labelsize 11 + } + } + Fl_Choice qoct { + label {no.oct} + callback {pars->Pquality.oct=(int) o->value(); +cbwidget->do_callback();} + xywh {480 155 45 20} down_box BORDER_BOX labelsize 11 align 5 textsize 11 + code0 {o->value(pars->Pquality.oct);} + } { + MenuItem {} { + label 1 + xywh {10 10 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 2 + xywh {20 20 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 3 + xywh {30 30 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 4 + xywh {40 40 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 5 + xywh {50 50 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 6 + xywh {60 60 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 7 + xywh {70 70 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label 8 + xywh {80 80 100 20} labelfont 1 labelsize 11 + } + } + Fl_Choice qbasenote { + label base + callback {pars->Pquality.basenote=(int) o->value(); +cbwidget->do_callback();} + xywh {375 155 50 20} down_box BORDER_BOX labelsize 11 align 5 textsize 11 + code0 {o->value(pars->Pquality.basenote);} + } { + MenuItem {} { + label {C-2} + xywh {10 10 100 20} labelfont 1 + } + MenuItem {} { + label {G-2} + xywh {20 20 100 20} labelfont 1 + } + MenuItem {} { + label {C-3} + xywh {20 20 100 20} labelfont 1 + } + MenuItem {} { + label {G-3} + xywh {30 30 100 20} labelfont 1 + } + MenuItem {} { + label {C-4} + xywh {30 30 100 20} labelfont 1 + } + MenuItem {} { + label {G-4} + xywh {40 40 100 20} labelfont 1 + } + MenuItem {} { + label {C-5} + xywh {40 40 100 20} labelfont 1 + } + MenuItem {} { + label {G-5} + xywh {50 50 100 20} labelfont 1 + } + MenuItem {} { + label {G-6} + xywh {60 60 100 20} labelfont 1 + } + } + Fl_Group hprofile { + xywh {100 45 430 90} box FLAT_BOX color 51 selection_color 218 labelcolor 63 + code0 {PADnoteHarmonicProfile *hpui=new PADnoteHarmonicProfile(o->x(),o->y(),o->w(),o->h(),"");} + code1 {hpui->init(pars,master);} + code2 {if (pars->Pmode!=0) { o->deactivate(); o->color(48);};} + } {} + Fl_Box {} { + label {Profile of One Harmonic (Frequency Distribution)} + xywh {160 25 315 20} + } + Fl_Choice spectrummode { + label {Spectrum Mode} + callback {pars->Pmode=(int) o->value(); + +if (pars->Pmode==0){ + bwprofilegroup->activate(); + bwdial->activate(); + bwcents->activate(); + hprofile->activate(); + hprofile->color(54); + bwscale->activate(); +} else { + bwprofilegroup->deactivate(); + bwdial->deactivate(); + bwcents->deactivate(); + hprofile->deactivate(); + hprofile->color(48); + bwscale->deactivate(); +}; + +cbwidget->do_callback();} + xywh {220 305 90 20} down_box BORDER_BOX labelfont 1 labelsize 10 align 5 textsize 11 + code0 {o->value(pars->Pmode);} + } { + MenuItem {} { + label Bandwidth + xywh {105 105 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Discrete + xywh {125 125 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Continous + xywh {115 115 100 20} labelfont 1 labelsize 11 + } + } + } + Fl_Group {} { + label {Envelopes&LFOs} open + xywh {0 20 535 375} box UP_FRAME hide + } { + Fl_Group {} { + label FREQUENCY + xywh {5 275 525 115} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + } { + Fl_Group freqenv { + label {PADSynth - Frequency Envelope} open + xywh {10 315 205 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->FreqEnvelope);} + class EnvelopeUI + } {} + Fl_Counter octave { + label Octave + callback {int k=(int) o->value(); +if (k<0) k+=16; +pars->PCoarseDetune = k*1024+ + pars->PCoarseDetune%1024;} + tooltip Octave xywh {470 295 45 15} type Simple labelsize 10 align 1 minimum -8 maximum 7 step 1 textfont 1 textsize 11 + code0 {int k=pars->PCoarseDetune/1024;} + code1 {if (k>=8) k-=16;} + code2 {o->value(k);} + } + Fl_Counter coarsedet { + label {Coarse det.} + callback {int k=(int) o->value(); +if (k<0) k+=1024; +pars->PCoarseDetune = k+ + (pars->PCoarseDetune/1024)*1024;} + tooltip {Coarse Detune} xywh {455 365 70 20} type Simple labelsize 10 align 5 minimum -64 maximum 63 step 1 textfont 1 textsize 11 + code0 {int k=pars->PCoarseDetune%1024;} + code1 {if (k>=512) k-=1024;} + code2 {o->value(k);} + code3 {o->lstep(10);} + } + Fl_Group freqlfo { + label {Frequency LFO } open + xywh {215 315 230 70} box FLAT_BOX color 47 align 144 + code0 {o->init(pars->FreqLfo);} + class LFOUI + } {} + Fl_Slider detune { + callback {pars->PDetune=(int)o->value()+8192; +detunevalueoutput->do_callback();} + tooltip {Fine Detune (cents)} xywh {60 295 295 15} type {Horz Knob} box NO_BOX minimum -8192 maximum 8191 step 1 + code0 {o->value(pars->PDetune-8192);} + } + Fl_Value_Output detunevalueoutput { + label Detune + callback {o->value(getdetune(pars->PDetuneType,0,pars->PDetune));} + xywh {12 295 45 15} labelsize 10 align 5 minimum -5000 maximum 5000 step 0.01 textfont 1 textsize 10 + code0 {o->value(getdetune(pars->PDetuneType,0,pars->PDetune));} + } + Fl_Choice detunetype { + label {Detune Type} + callback {pars->PDetuneType=(int) o->value()+1; +detunevalueoutput->do_callback();} open + xywh {450 330 75 20} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 + code0 {o->add("L35cents");o->add("L10cents");o->add("E100cents");o->add("E1200cents");} + code1 {o->value(pars->PDetuneType-1);} + } {} + Fl_Check_Button hz440 { + label 440Hz + callback {int x=(int) o->value(); +pars->Pfixedfreq=x; +if (x==0) fixedfreqetdial->deactivate(); + else fixedfreqetdial->activate();} + tooltip {set the base frequency to 440Hz} xywh {365 295 50 15} down_box DOWN_BOX labelfont 1 labelsize 10 + code0 {o->value(pars->Pfixedfreq);} + } + Fl_Dial fixedfreqetdial { + label {Eq.T.} + callback {pars->PfixedfreqET=(int) o->value();} + tooltip {How the frequency varies acording to the keyboard (leftmost for fixed frequency)} xywh {420 295 15 15} box ROUND_UP_BOX labelsize 10 align 8 maximum 127 step 1 + code0 {o->value(pars->PfixedfreqET);} + code1 {if (pars->Pfixedfreq==0) o->deactivate();} + class WidgetPDial + } + } + Fl_Group {} { + label AMPLITUDE + xywh {5 25 240 250} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + } { + Fl_Value_Slider volume { + label Vol + callback {pars->PVolume=(int)o->value();} + tooltip Volume xywh {10 50 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 + code0 {o->value(pars->PVolume);} + } + Fl_Value_Slider vsns { + label {V.Sns} + callback {pars->PAmpVelocityScaleFunction=(int) o->value();} + tooltip {Velocity Sensing Function (rightmost to disable)} xywh {10 70 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 + code0 {o->value(pars->PAmpVelocityScaleFunction);} + } + Fl_Dial pan { + label Pan + callback {pars->PPanning=(int) o->value();} + tooltip {Panning (leftmost is Random)} xywh {210 45 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(pars->PPanning);} + class WidgetPDial + } + Fl_Dial pstr { + label {P.Str.} + callback {pars->PPunchStrength=(int) o->value();} + tooltip {Punch Strength} xywh {125 247 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->PPunchStrength);} + class WidgetPDial + } + Fl_Dial pt { + label {P.t.} + callback {pars->PPunchTime=(int) o->value();} + tooltip {Punch Time (duration)} xywh {155 247 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->PPunchTime);} + class WidgetPDial + } + Fl_Dial pstc { + label {P.Stc.} + callback {pars->PPunchStretch=(int) o->value();} + tooltip {Punch Stretch} xywh {185 247 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->PPunchStretch);} + class WidgetPDial + } + Fl_Dial pvel { + label {P.Vel.} + callback {pars->PPunchVelocitySensing=(int) o->value();} + tooltip {Punch Velocity Sensing} xywh {215 247 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->PPunchVelocitySensing);} + class WidgetPDial + } + Fl_Group ampenv { + label {PADSynth - Amplitude Envelope} open + xywh {10 95 205 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->AmpEnvelope);} + class EnvelopeUI + } {} + Fl_Group amplfo { + label {Amplitude LFO } open + xywh {10 165 230 70} box FLAT_BOX color 47 align 144 + code0 {o->init(pars->AmpLfo);} + class LFOUI + } {} + Fl_Check_Button stereo { + label Stereo + callback {pars->PStereo=(int) o->value(); +hprofile->redraw();} + xywh {15 245 70 25} down_box DOWN_BOX + code0 {o->value(pars->PStereo);} + } + } + Fl_Group {} { + label FILTER + xywh {245 25 285 250} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + } { + Fl_Group filterenv { + label {PADSynth - Filter Envelope} open + xywh {250 130 275 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->FilterEnvelope);} + class EnvelopeUI + } {} + Fl_Group filterlfo { + label {Filter LFO } open + xywh {250 200 230 70} box FLAT_BOX color 47 align 144 + code0 {o->init(pars->FilterLfo);} + class LFOUI + } {} + Fl_Group filterui { + label {PADsynth - Filter} open + xywh {250 55 275 75} box FLAT_BOX color 50 align 144 + code0 {o->init(pars->GlobalFilter,&pars->PFilterVelocityScale,&pars->PFilterVelocityScaleFunction);} + class FilterUI + } {} + } + } + } + Fl_Button applybutton { + label {Apply Changes} + callback {pars->applyparameters(true); +o->color(FL_GRAY); +if (oscui!=NULL) { + oscui->applybutton->color(FL_GRAY); + oscui->applybutton->redraw(); +}; +if (resui!=NULL) { + resui->applybutton->color(FL_GRAY); + resui->applybutton->redraw(); +};} + xywh {300 400 135 30} box THIN_UP_BOX + code0 {o->color(FL_RED);} + } + Fl_Button {} { + label Close + callback {padnotewindow->hide();} + xywh {440 400 90 30} box THIN_UP_BOX + } + Fl_Button {} { + label C + callback {presetsui->copy(pars);} + xywh {65 400 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(pars,this);} + xywh {95 400 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button {} { + label export + callback {char *filename; +filename=fl_file_chooser("Export samples:","(*.wav)",NULL,0); +if (filename==NULL) return; +fl_filename_setext(filename,""); + + + +pars->export2wav(filename);} + tooltip {export samples as wav file} xywh {5 400 55 30} box THIN_UP_BOX color 51 labelsize 11 align 128 + } + } + } + Function {refresh()} {} { + code {volume->value(pars->PVolume); +vsns->value(pars->PAmpVelocityScaleFunction); +pan->value(pars->PPanning); + +stereo->value(pars->PStereo); + + +pstr->value(pars->PPunchStrength); +pt->value(pars->PPunchTime); +pstc->value(pars->PPunchStretch); +pvel->value(pars->PPunchVelocitySensing); + +detunevalueoutput->value(getdetune(pars->PDetuneType,0,pars->PDetune)); +detune->value(pars->PDetune-8192); + +int k=pars->PCoarseDetune/1024;if (k>=8) k-=16; +octave->value(k); + +detunetype->value(pars->PDetuneType-1); +k=pars->PCoarseDetune%1024;if (k>=512) k-=1024; +coarsedet->value(k); + +hz440->value(pars->Pfixedfreq); +fixedfreqetdial->value(pars->PfixedfreqET); + +amplfo->refresh(); +freqlfo->refresh(); +filterlfo->refresh(); + +ampenv->refresh(); +freqenv->refresh(); +filterenv->refresh(); +filterui->refresh(); + + +/* harmonic structure parametrs */ + +resui->refresh(); +if (oscui!=NULL) oscui->refresh(); + +hpbasetype->value(pars->Php.base.type); +hpbasepar1->value(pars->Php.base.par1); +hpfreqmult->value(pars->Php.freqmult); + +hpmpar1->value(pars->Php.modulator.par1); +hpmfreq->value(pars->Php.modulator.freq); +hpwidth->value(pars->Php.width); + +hponehalf->value(pars->Php.onehalf); +hpamptype->value(pars->Php.amp.type); +hpampmode->value(pars->Php.amp.mode); +hpamppar1->value(pars->Php.amp.par1); +hpamppar2->value(pars->Php.amp.par2); +hpautoscale->value(pars->Php.autoscale); + +bwdial->value(pars->Pbandwidth); +if (pars->Pmode==0){ + bwprofilegroup->activate(); + bwdial->activate(); + bwcents->activate(); + hprofile->activate(); + hprofile->color(54); + bwscale->activate(); +} else { + bwprofilegroup->deactivate(); + bwdial->deactivate(); + bwcents->deactivate(); + hprofile->deactivate(); + hprofile->color(48); + bwscale->activate(); +}; + +spectrummode->value(pars->Pmode); + +qbasenote->value(pars->Pquality.basenote); +qsmpoct->value(pars->Pquality.smpoct); +qoct->value(pars->Pquality.oct); +qsamplesize->value(pars->Pquality.samplesize); + +hrpostype->value(pars->Phrpos.type); +hrpospar1->value(pars->Phrpos.par1); +hrpospar2->value(pars->Phrpos.par2); +hrpospar3->value(pars->Phrpos.par3); + +hprofile->redraw(); +overtonepos->redraw(); + +osc->redraw(); +pars->applyparameters(true); +applybutton->color(FL_GRAY); +applybutton->parent()->redraw();} {} + } + Function {~PADnoteUI()} {} { + code {delete(oscui); +delete(resui); + +padnotewindow->hide(); +delete(padnotewindow);} {} + } + decl {PADnoteParameters *pars;} {public + } + decl {Master *master;} {public + } + decl {OscilEditor *oscui;} {public + } + decl {Oscilloscope *osc;} {public + } + decl {ResonanceUI *resui;} {public + } +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/PartUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/PartUI.fl new file mode 100644 index 000000000..556ae13ef --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/PartUI.fl @@ -0,0 +1,1125 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include "EffUI.h"} {public +} + +decl {\#include "BankUI.h"} {public +} + +decl {\#include "ADnoteUI.h"} {public +} + +decl {\#include "SUBnoteUI.h"} {public +} + +decl {\#include "PADnoteUI.h"} {public +} + +decl {\#include "../Misc/Config.h"} {public +} + +decl {\#include "../Misc/Master.h"} {public +} + +decl {\#include "../Misc/Part.h"} {public +} + +class PartSysEffSend {open : {public Fl_Group} +} { + Function {make_window()} {open private + } { + Fl_Window syseffsend { + private xywh {589 129 100 100} type Double box NO_BOX + class Fl_Group visible + } { + Fl_Dial {} { + label 01 + callback {master->setPsysefxvol(npart,neff,(int) o->value());} + xywh {0 0 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 align 130 maximum 127 step 1 + code0 {o->size(25,25);} + code1 {o->value(master->Psysefxvol[neff][npart]);} + code2 {char tmp[10];snprintf(tmp,10,"%d",neff+1);o->copy_label(tmp);} + class WidgetPDial + } + } + } + Function {PartSysEffSend(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {master=NULL; +neff=0; +npart=0;} {} + } + Function {init(Master *master_,int npart_,int neff_)} {} { + code {npart=npart_; +neff=neff_; +master=master_; +make_window(); +syseffsend->show(); +end();} {} + } + Function {~PartSysEffSend()} {} { + code {syseffsend->hide(); +//delete(syseffsend);} {} + } + decl {Master *master;} {} + decl {int neff;} {} + decl {int npart;} {} +} + +class PartKitItem {open : {public Fl_Group} +} { + Function {make_window()} {open private + } { + Fl_Window partkititem { + private xywh {473 406 670 100} type Double box NO_BOX + class Fl_Group visible + } { + Fl_Group partkititemgroup { + private xywh {55 0 605 20} + code0 {if (part->kit[n].Penabled==0) o->deactivate();} + } { + Fl_Counter minkcounter { + callback {part->kit[n].Pminkey=(int)o->value();} + xywh {225 0 55 15} type Simple minimum 0 maximum 128 step 1 + code0 {o->value(part->kit[n].Pminkey);} + } + Fl_Button {} { + label m + callback {if (part->lastnote>=0) minkcounter->value(part->lastnote); +minkcounter->do_callback(); +maxkcounter->do_callback();} + tooltip {set the minimum key to the last pressed key} xywh {285 3 15 12} box THIN_UP_BOX labelsize 10 + } + Fl_Button {} { + label M + callback {if (part->lastnote>=0) maxkcounter->value(part->lastnote); +maxkcounter->do_callback(); +minkcounter->do_callback();} + tooltip {set the maximum key to the last pressed key} xywh {315 3 15 12} box THIN_UP_BOX labelsize 10 + } + Fl_Button {} { + label R + callback {minkcounter->value(0); +minkcounter->do_callback(); +maxkcounter->value(127); +maxkcounter->do_callback();} + tooltip {reset the minimum key to 0 and maximum key to 127} xywh {300 3 15 12} box THIN_UP_BOX labelfont 1 labelsize 10 + } + Fl_Button adeditbutton { + label edit + callback {partui->showparameters(n,0);} + xywh {420 0 40 15} box THIN_UP_BOX labelsize 11 + code0 {if (part->kit[n].Padenabled==0) o->deactivate();} + code1 {if (n==0) o->hide();} + } + Fl_Button subeditbutton { + label edit + callback {partui->showparameters(n,1);} + xywh {490 0 40 15} box THIN_UP_BOX labelsize 11 + code0 {if (part->kit[n].Psubenabled==0) o->deactivate();} + code1 {if (n==0) o->hide();} + } + Fl_Check_Button mutedcheck { + callback {part->kit[n].Pmuted=(int)o->value();} + private xywh {60 0 20 15} down_box DOWN_BOX labelfont 1 labelsize 11 align 4 + code0 {o->value(part->kit[n].Pmuted);} + } + Fl_Counter maxkcounter { + callback {part->kit[n].Pmaxkey=(int)o->value();} + xywh {335 0 55 15} type Simple minimum 0 maximum 128 step 1 + code0 {o->value(part->kit[n].Pmaxkey);} + } + Fl_Button labelbutton { + label {Bass Drum} + callback {const char *tmp=fl_input("Kit item name:",(const char *)part->kit[n].Pname); +if (tmp!=NULL) snprintf((char *)part->kit[n].Pname,PART_MAX_NAME_LEN,"%s",tmp);} + xywh {90 0 130 15} box THIN_DOWN_BOX down_box FLAT_BOX labelfont 1 labelsize 10 align 20 + code0 {o->label((char *)part->kit[n].Pname);} + } + Fl_Check_Button adcheck { + callback {part->kit[n].Padenabled=(int)o->value(); +if (part->kit[n].Padenabled!=0) adeditbutton->activate(); + else adeditbutton->deactivate();} + private xywh {400 0 20 15} down_box DOWN_BOX labelfont 1 labelsize 11 align 4 + code0 {o->value(part->kit[n].Padenabled);} + code1 {if (n==0) o->hide();} + } + Fl_Check_Button subcheck { + callback {part->kit[n].Psubenabled=(int)o->value(); +if (part->kit[n].Psubenabled!=0) subeditbutton->activate(); + else subeditbutton->deactivate();} + private xywh {470 0 20 15} down_box DOWN_BOX labelfont 1 labelsize 11 align 4 + code0 {o->value(part->kit[n].Psubenabled);} + code1 {if (n==0) o->hide();} + } + Fl_Choice sendtoeffect { + callback {if (o->value()!=0) part->kit[n].Psendtoparteffect=(int)o->value()-1; + else part->kit[n].Psendtoparteffect=127;} open + xywh {615 0 45 15} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 + code0 {o->add("OFF");char nrstr[10]; for(int i=0;iadd(nrstr);};} + code1 {o->value(part->kit[n].Psendtoparteffect+1);if (part->kit[n].Psendtoparteffect==127) o->value(0);} + } {} + Fl_Button padeditbutton { + label edit + callback {partui->showparameters(n,2);} + xywh {560 0 40 15} box THIN_UP_BOX labelsize 11 + code0 {if (part->kit[n].Ppadenabled==0) o->deactivate();} + code1 {if (n==0) o->hide();} + } + Fl_Check_Button padcheck { + callback {part->kit[n].Ppadenabled=(int)o->value(); +if (part->kit[n].Ppadenabled!=0) padeditbutton->activate(); + else padeditbutton->deactivate();} + private xywh {540 0 20 15} down_box DOWN_BOX labelfont 1 labelsize 11 align 4 + code0 {o->value(part->kit[n].Ppadenabled);} + code1 {if (n==0) o->hide();} + } + } + Fl_Check_Button enabledcheck { + label 01 + callback {int answer=1; +if (o->value()==0) answer=fl_choice("Delete the item?","No","Yes",NULL); +if (answer!=0){ +pthread_mutex_lock(&master->mutex); + part->setkititemstatus(n,(int) o->value()); +pthread_mutex_unlock(&master->mutex); + +if (o->value()==0) partkititemgroup->deactivate(); +else partkititemgroup->activate(); +o->redraw(); +partui->showparameters(n,-1);//use to delete the ui, if it is not to item 0 +} else o->value(1);} + private xywh {30 0 20 15} down_box DOWN_BOX labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 4 + code0 {snprintf(label,10,"%d",n+1);o->label(label);} + code1 {o->value(part->kit[n].Penabled);} + code2 {if (n==0) o->deactivate();} + } + } + } + Function {PartKitItem(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {n=0; +part=NULL;} {} + } + Function {refresh()} {} { + code {enabledcheck->value(part->kit[n].Penabled); +if (part->kit[n].Penabled==0) partkititemgroup->deactivate(); +else partkititemgroup->activate(); + +mutedcheck->value(part->kit[n].Pmuted); +labelbutton->label((char *)part->kit[n].Pname); +minkcounter->value(part->kit[n].Pminkey); +maxkcounter->value(part->kit[n].Pmaxkey); +adcheck->value(part->kit[n].Padenabled); +adcheck->do_callback(); +subcheck->value(part->kit[n].Psubenabled); +subcheck->do_callback(); + +sendtoeffect->value(part->kit[n].Psendtoparteffect+1); +if (part->kit[n].Psendtoparteffect==127) sendtoeffect->value(0); + +this->redraw();} { + callback {int answer=1; +if (o->value()==0) answer=fl_choice("Delete the item?","No","Yes",NULL); +if (answer!=0){ +pthread_mutex_lock(&master->mutex); + part->setkititemstatus(n,(int) o->value()); +pthread_mutex_unlock(&master->mutex); + +if (o->value()==0) partkititemgroup->deactivate(); +else partkititemgroup->activate(); +o->redraw(); +partui->showparameters(n,-1);//use to delete the ui, if it is not to item 0 +} else o->value(1);} + } + } + Function {init(Part *part_,int n_,Master *master_,class PartUI *partui_)} {} { + code {part=part_; +n=n_; +partui=partui_; +master=master_; +make_window(); +//partkititem->show(); +end();} {} + } + Function {~PartKitItem()} {} { + code {partkititem->hide(); +//delete(partkititem);} {} + } + decl {Part *part;} {} + decl {int n;} {} + decl {Master *master;} {} + decl {char label[10];} {} + decl {class PartUI *partui;} {} +} + +class PartUI {open : {public Fl_Group} +} { + Function {make_window()} {open private + } { + Fl_Window partgroup {open + private xywh {688 264 385 180} type Double box NO_BOX + class Fl_Group visible + } { + Fl_Group partgroupui {open + xywh {0 0 385 180} + code0 {if (part->Penabled==0) o->deactivate();} + } { + Fl_Dial {} { + label Pan + callback {part->setPpanning((int) o->value());} + xywh {50 40 25 25} box ROUND_UP_BOX labelsize 11 maximum 127 step 1 + code0 {o->value(part->Ppanning);} + class WidgetPDial + } + Fl_Counter {} { + label KeyShift + callback {part->Pkeyshift=(int) o->value()+64;} + xywh {195 45 90 20} labelsize 11 align 1 minimum -64 maximum 64 step 1 + code0 {o->lstep(12);} + code1 {o->value(part->Pkeyshift-64);} + } + Fl_Scroll {} {open + xywh {166 91 125 49} box UP_BOX labelfont 1 labelsize 10 align 21 + } { + Fl_Pack {} {open + xywh {171 96 115 35} type HORIZONTAL + code0 {o->spacing(5);} + code1 {for (int i=0;iinit(master,npart,i);}} + } {} + } + Fl_Button {} { + label {Grand Piano} + callback {int event=Fl::event_button(); +if (event==FL_RIGHT_MOUSE){ + const char *tmp=fl_input("Instrument name:",(const char *)part->Pname); + if (tmp!=NULL) snprintf((char *)part->Pname,PART_MAX_NAME_LEN,"%s",tmp); +} else { + if (event==FL_LEFT_MOUSE) bankui->show(); + else instrumenteditwindow->show(); +};} + tooltip {left mousebutton - to choose/save/.. from/to bank or right mousebutton to change the name or middle button to change the instrument information} xywh {195 5 185 20} box UP_FRAME down_box DOWN_FRAME labelfont 1 labelsize 11 align 84 + code0 {o->label((char *)part->Pname);} + } + Fl_Box {} { + label {To Sys.Efx.} + xywh {166 81 95 10} labelfont 1 labelsize 10 + } + Fl_Check_Button {} { + label NoteOn + callback {part->Pnoteon=(int) o->value();} + tooltip {set if the part receives NoteOn messages} xywh {10 155 65 20} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(part->Pnoteon);} + } + Fl_Counter minkcounter { + label {Min.k} + callback {part->Pminkey=(int) o->value(); +if (part->Pminkey>part->Pmaxkey) o->textcolor(FL_RED); + else o->textcolor(FL_BLACK);} + tooltip {Minimum key (that the part receives NoteOn messages)} xywh {295 125 40 15} type Simple labelfont 1 labelsize 10 minimum 0 maximum 127 step 1 textsize 10 + code0 {o->value(part->Pminkey);} + } + Fl_Counter maxkcounter { + label {Max.k} + callback {part->Pmaxkey=(int) o->value(); + +if (part->Pminkey>part->Pmaxkey) o->textcolor(FL_RED); + else o->textcolor(FL_BLACK);} + tooltip {Maximum key (that the part receives NoteOn messages)} xywh {340 125 40 15} type Simple labelfont 1 labelsize 10 minimum 0 maximum 127 step 1 textsize 10 + code0 {o->value(part->Pmaxkey);} + } + Fl_Dial {} { + label Volume + callback {part->setPvolume((int) o->value());} + tooltip {Part Volume} xywh {10 35 30 30} box ROUND_UP_BOX labelsize 11 maximum 127 step 1 + code0 {o->value(part->Pvolume);} + class WidgetPDial + } + Fl_Dial {} { + label {Vel.Ofs.} + callback {part->Pveloffs=(int) o->value();} + tooltip {Velocity Offset} xywh {135 40 25 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(part->Pveloffs);} + class WidgetPDial + } + Fl_Dial {} { + label {Vel.Sns.} + callback {part->Pvelsns=(int) o->value();} + tooltip {Velocity Sensing Function} xywh {95 40 25 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(part->Pvelsns);} + class WidgetPDial + } + Fl_Button {} { + label Controllers + callback {ctlwindow->show();} + xywh {295 90 85 30} labelfont 1 labelsize 11 + } + Fl_Check_Button {} { + label Portamento + callback {part->ctl.portamento.portamento=(int) o->value();} + tooltip {Enable/Disable the portamento} xywh {95 155 88 20} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(part->ctl.portamento.portamento);} + } + Fl_Button {} { + label {Edit instrument} + callback {instrumenteditwindow->show();} + xywh {15 90 130 30} color 52 labelfont 1 labelsize 13 + } + Fl_Button {} { + label m + callback {if (part->lastnote>=0) minkcounter->value(part->lastnote); +minkcounter->do_callback(); +maxkcounter->do_callback();} + tooltip {set the minimum key to the last pressed key} xywh {315 155 15 12} box THIN_UP_BOX labelsize 10 + } + Fl_Button {} { + label M + callback {if (part->lastnote>=0) maxkcounter->value(part->lastnote); +maxkcounter->do_callback(); +minkcounter->do_callback();} + tooltip {set the maximum key to the last pressed key} xywh {345 155 15 12} box THIN_UP_BOX labelsize 10 + } + Fl_Button {} { + label R + callback {minkcounter->value(0); +minkcounter->do_callback(); +maxkcounter->value(127); +maxkcounter->do_callback();} + tooltip {reset the minimum key to 0 and maximum key to 127} xywh {330 155 15 12} box THIN_UP_BOX labelfont 1 labelsize 10 + } + Fl_Choice {} { + label {MIDI Chn.Rcv.} + callback {part->Prcvchn=(int) o->value();} open + tooltip {receive from Midi channel} xywh {310 45 70 20} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 + code0 {char nrstr[10]; for(int i=0;iadd(nrstr); else o->add("Drms10");};} + code1 {o->value(part->Prcvchn);} + } {} + Fl_Choice keylimitlist { + label KLmt + callback {int val=0; +val=atoi(o->text()); +part->setkeylimit(val);} open + tooltip {Key Limit} xywh {215 155 50 20} down_box BORDER_BOX labelsize 10 align 8 textfont 1 textsize 10 + } {} + Fl_Choice {} { + label {Mode :} + callback {if ((int) o->value()==0){ /* Poly (implies no legato) */ + part->Ppolymode=1; + part->Plegatomode=0; +} else { + if ((int) o->value()==1){ /* Mono (implies no legato) */ + part->Ppolymode=0; + part->Plegatomode=0; + } else { + if ((int) o->value()==2){ /* Legato (implies mono) */ + part->Ppolymode=0; + part->Plegatomode=1; + }; + }; +};} open + tooltip {Poly, Mono or Legato mode} xywh {80 130 64 18} down_box BORDER_BOX labelfont 1 labelsize 11 textfont 1 textsize 10 + code0 {o->add("Poly"); o->add("Mono"); o->add("Legato");} + code1 {if (part->Ppolymode!=0) o->value(0); else o->value(1);} + code2 {if (part->Ppolymode==0 && part->Plegatomode!=0) o->value(2);} + } {} + } + Fl_Check_Button {} { + label Enabled + callback {pthread_mutex_lock(&master->mutex); +master->partonoff(npart,(int) o->value()); +pthread_mutex_unlock(&master->mutex); +if (part->Penabled==0) partgroupui->deactivate(); + else partgroupui->activate();} + xywh {90 5 75 20} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(part->Penabled);} + } + } + Fl_Window ctlwindow { + label Controllers open + private xywh {777 261 500 130} type Double box NO_BOX visible + } { + Fl_Check_Button {} { + label Expr + callback {part->ctl.expression.receive=(int) o->value();} + tooltip {Expression enable} xywh {155 55 45 20} box THIN_UP_BOX down_box DOWN_BOX labelsize 10 + code0 {o->value(part->ctl.expression.receive);} + } + Fl_Dial {} { + label PanDpth + callback {part->ctl.panning.depth=(int) o->value();} + tooltip {Panning Depth} xywh {10 55 30 30} labelsize 10 maximum 127 step 1 + code0 {o->value(part->ctl.panning.depth);} + class WidgetPDial + } + Fl_Dial {} { + label FltCut + callback {part->ctl.filtercutoff.depth=(int) o->value();} + tooltip {Filter Cutoff depth} xywh {90 55 30 30} labelsize 10 maximum 127 step 1 + code0 {o->value(part->ctl.filtercutoff.depth);} + class WidgetPDial + } + Fl_Dial {} { + label FltQ + callback {part->ctl.filterq.depth=(int) o->value();} + tooltip {Filter Q depth} xywh {50 55 30 30} labelsize 10 maximum 127 step 1 + code0 {o->value(part->ctl.filterq.depth);} + class WidgetPDial + } + Fl_Dial {} { + label BwDpth + callback {part->ctl.bandwidth.depth=(int) o->value();} + tooltip {BandWidth depth} xywh {125 10 30 30} labelsize 10 maximum 127 step 1 + code0 {o->value(part->ctl.bandwidth.depth);} + class WidgetPDial + } + Fl_Dial {} { + label ModWh + callback {part->ctl.modwheel.depth=(int) o->value();} + tooltip {Modulation Wheel depth} xywh {50 10 30 30} labelsize 10 maximum 127 step 1 + code0 {o->value(part->ctl.modwheel.depth);} + class WidgetPDial + } + Fl_Counter {} { + label {PWheelB.Rng (cents)} + callback {part->ctl.pitchwheel.bendrange=(int) o->value();} + tooltip {Pitch Wheel Bend Range (cents)} xywh {165 15 110 20} labelsize 10 align 1 minimum -6400 maximum 6400 step 1 + code0 {o->value(part->ctl.pitchwheel.bendrange);} + code1 {o->lstep(100);} + } + Fl_Check_Button {} { + label FMamp + callback {part->ctl.fmamp.receive=(int) o->value();} + tooltip {FM amplitude enable} xywh {205 55 60 20} box THIN_UP_BOX down_box DOWN_BOX labelsize 10 + code0 {o->value(part->ctl.fmamp.receive);} + } + Fl_Check_Button {} { + label Vol + callback {part->ctl.volume.receive=(int) o->value();} + tooltip {Volume enable} xywh {155 80 45 20} box THIN_UP_BOX down_box DOWN_BOX labelsize 10 + code0 {o->value(part->ctl.volume.receive);} + } + Fl_Check_Button {} { + label Sustain + callback {part->ctl.sustain.receive=(int) o->value(); +if (part->ctl.sustain.receive==0) { + part->RelaseSustainedKeys(); + part->ctl.setsustain(0); +};} + tooltip {Sustain pedal enable} xywh {205 80 60 20} box THIN_UP_BOX down_box DOWN_BOX labelsize 10 + code0 {o->value(part->ctl.sustain.receive);} + } + Fl_Button {} { + label Close + callback {ctlwindow->hide();} + xywh {400 107 95 20} box THIN_UP_BOX + } + Fl_Button {} { + label {Reset all controllers} + callback {part->SetController(C_resetallcontrollers,0);} + xywh {5 107 210 20} box THIN_UP_BOX + } + Fl_Group {} { + label Portamento + xywh {280 15 160 90} box UP_FRAME labelsize 10 + } { + Fl_Check_Button {} { + label Rcv + callback {part->ctl.portamento.receive=(int) o->value();} + tooltip {Receive Portamento Controllers} xywh {285 20 40 20} box THIN_UP_BOX down_box DOWN_BOX labelsize 10 + code0 {o->value(part->ctl.portamento.receive);} + } + Fl_Dial {} { + label time + callback {part->ctl.portamento.time=(int) o->value();} + tooltip {Portamento time} xywh {285 60 25 25} labelsize 10 maximum 127 step 1 + code0 {o->value(part->ctl.portamento.time);} + class WidgetPDial + } + Fl_Counter {} { + label thresh + callback {part->ctl.portamento.pitchthresh=(int) o->value();} + tooltip {Minimum or max. difference of the notes in order to do the portamento (x 100 cents)} xywh {340 20 50 20} type Simple labelsize 10 minimum 0 maximum 127 step 1 + code0 {o->value(part->ctl.portamento.pitchthresh);} + } + Fl_Check_Button {} { + label {th.type} + callback {part->ctl.portamento.pitchthreshtype=(int) o->value();} + tooltip {Threshold type (min/max)} xywh {365 70 15 15} down_box DOWN_BOX labelsize 10 align 2 + code0 {o->value(part->ctl.portamento.pitchthreshtype);} + } + Fl_Box {} { + label {x100 cnt.} + xywh {340 50 55 15} labelsize 10 align 16 + } + Fl_Dial {} { + label {t.dn/up} + callback {int x=(int) o->value(); + +part->ctl.portamento.updowntimestretch=x;} + tooltip {Portamento time stretch (up/down)} xywh {315 60 25 25} labelsize 10 maximum 127 step 1 + code0 {o->value(part->ctl.portamento.updowntimestretch);} + class WidgetPDial + } + Fl_Dial propta { + label {Prp.Rate} + callback {part->ctl.portamento.propRate=(int) o->value();} + tooltip {Distance required to double change from nonpropotinal portamento time} xywh {405 20 25 25} labelsize 9 maximum 127 step 1 + code0 {o->value(part->ctl.portamento.propRate);} + class WidgetPDial + } + Fl_Dial proptb { + label {Prp.Dpth} + callback {part->ctl.portamento.propDepth=(int) o->value();} + tooltip {The difference from nonproportinal portamento} xywh {405 60 25 25} labelsize 9 maximum 127 step 1 + code0 {o->value(part->ctl.portamento.propDepth);} + class WidgetPDial + } + Fl_Check_Button {} { + label {Proprt.} + callback {part->ctl.portamento.proportional=(int) o->value(); +if(o->value()){propta->activate();proptb->activate();} +else {propta->deactivate();proptb->deactivate();}} + tooltip {Enable Proportinal Portamento (over fixed Portamento)} xywh {285 40 50 15} box THIN_UP_BOX down_box DOWN_BOX labelsize 9 + code0 {o->value(part->ctl.portamento.proportional);} + code1 {if(o->value()){propta->activate();proptb->activate();}} + code2 {else {propta->deactivate();proptb->deactivate();}} + } + } + Fl_Group {} { + label Resonance + xywh {445 15 50 90} box UP_FRAME labelsize 10 + } { + Fl_Dial {} { + label BWdpth + callback {part->ctl.resonancebandwidth.depth=(int) o->value();} + tooltip {BandWidth controller depth} xywh {455 60 25 25} labelsize 10 maximum 127 step 1 + code0 {o->value(part->ctl.resonancebandwidth.depth);} + class WidgetPDial + } + Fl_Dial {} { + label CFdpth + callback {part->ctl.resonancecenter.depth=(int) o->value();} + tooltip {Center Frequency controller Depth} xywh {455 20 25 25} labelsize 10 maximum 127 step 1 + code0 {o->value(part->ctl.resonancecenter.depth);} + class WidgetPDial + } + } + Fl_Check_Button {} { + label {Exp MWh} + callback {part->ctl.modwheel.exponential=(int) o->value();} + tooltip {Exponential modulation wheel} xywh {10 15 40 25} down_box DOWN_BOX labelsize 10 align 148 + code0 {o->value(part->ctl.modwheel.exponential);} + } + Fl_Check_Button {} { + label {Exp BW} + callback {part->ctl.bandwidth.exponential=(int) o->value();} + tooltip {Exponential BandWidth Controller} xywh {85 15 35 25} down_box DOWN_BOX labelsize 10 align 148 + code0 {o->value(part->ctl.bandwidth.exponential);} + } + } + Fl_Window partfx { + label {Part's Insert Effects} selected + private xywh {554 660 390 145} type Double box NO_BOX visible + } { + Fl_Counter inseffnocounter { + label {FX No.} + callback {ninseff=(int) o->value()-1; +insefftype->value(part->partefx[ninseff]->geteffect()); +//insefftype->do_callback(); +inseffectui->refresh(part->partefx[ninseff]); +int x=part->Pefxroute[ninseff]; +if (x==127) x=1; +bypasseff->value(part->Pefxbypass[ninseff]); + +sendtochoice->value(x);} + xywh {5 110 80 20} type Simple labelfont 1 align 6 minimum 1 maximum 127 step 1 textfont 1 + code0 {o->bounds(1,NUM_PART_EFX);} + code1 {o->value(ninseff+1);} + } + Fl_Choice insefftype { + label EffType + callback {pthread_mutex_lock(part->mutex); +part->partefx[ninseff]->changeeffect((int) o->value()); +pthread_mutex_unlock(part->mutex); +inseffectui->refresh(part->partefx[ninseff]);} + xywh {155 110 70 15} down_box BORDER_BOX labelsize 10 align 6 + code0 {o->value(part->partefx[ninseff]->geteffect());} + } { + MenuItem {} { + label {No Effect} + xywh {35 35 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Reverb + xywh {45 45 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Echo + xywh {55 55 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Chorus + xywh {65 65 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Phaser + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label AlienWah + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Distortion + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label EQ + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label DynFilter + xywh {110 110 100 20} labelfont 1 labelsize 10 + } + } + Fl_Group inseffectuigroup { + xywh {5 5 380 100} box FLAT_BOX color 48 + } { + Fl_Group inseffectui { + xywh {5 5 380 95} + code0 {o->init(part->partefx[ninseff]);} + class EffUI + } {} + } + Fl_Button {} { + label Close + callback {partfx->hide();} + xywh {325 115 60 20} box THIN_UP_BOX + } + Fl_Choice sendtochoice { + label {Send To.} + callback {int x=(int) o->value(); +part->Pefxroute[ninseff]=x; +if (x==2) part->partefx[ninseff]->setdryonly(true); + else part->partefx[ninseff]->setdryonly(false);} + xywh {235 110 80 15} down_box BORDER_BOX labelsize 10 align 6 + code0 {int x=part->Pefxroute[ninseff]; if (x==127) x=1;} + code1 {o->value(x);} + } { + MenuItem {} { + label {Next Effect} + xywh {45 45 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Part Out} + xywh {55 55 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Dry Out} + xywh {65 65 100 20} labelfont 1 labelsize 10 + } + } + Fl_Check_Button bypasseff { + label bypass + callback {part->Pefxbypass[ninseff]=(((int)o->value())!=0);} + tooltip {if the effect is not used (is bypassed)} xywh {90 110 60 15} down_box DOWN_BOX labelsize 11 + code0 {int x=part->Pefxbypass[ninseff];o->value(x);} + } + Fl_Button {} { + label C + callback {presetsui->copy(part->partefx[ninseff]);} + xywh {90 127 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button {} { + label P + callback {pthread_mutex_lock(&master->mutex); +presetsui->paste(part->partefx[ninseff],inseffectui); +pthread_mutex_unlock(&master->mutex);} + xywh {120 127 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + } + Fl_Window instrumentkitlist { + label {Instrument Kit} open + xywh {586 566 670 370} type Double box NO_BOX visible + } { + Fl_Button {} { + label {Close Window} + callback {instrumentkitlist->hide();} + xywh {375 350 160 20} box THIN_UP_BOX + } + Fl_Scroll kitlist {open + xywh {0 15 670 330} type VERTICAL box UP_FRAME + code0 {if (part->Pkitmode==0) o->deactivate();} + } { + Fl_Pack {} { + xywh {0 20 670 320} + code0 {for (int i=0;iinit(part,i,master,this);}} + } {} + } + Fl_Box {} { + label {No.} + xywh {5 0 25 15} labelfont 1 labelsize 11 align 18 + } + Fl_Box {} { + label {M.} + xywh {55 0 25 15} labelfont 1 labelsize 11 align 18 + } + Fl_Box {} { + label {Min.k} + xywh {235 0 40 15} labelfont 1 labelsize 11 align 18 + } + Fl_Box {} { + label {Max.k} + xywh {345 0 40 15} labelfont 1 labelsize 11 align 18 + } + Fl_Box {} { + label ADsynth + xywh {405 0 50 15} labelfont 1 labelsize 11 align 18 + } + Fl_Box {} { + label SUBsynth + xywh {470 0 60 15} labelfont 1 labelsize 11 align 18 + } + Fl_Choice {} { + label Mode + callback {part->Pkitmode=(int) o->value(); +if (part->Pkitmode==0) { + kitlist->deactivate(); + } else { + kitlist->activate(); +};} + xywh {35 350 70 15} down_box BORDER_BOX labelsize 11 textfont 1 textsize 11 + code0 {o->value(part->Pkitmode);} + } { + MenuItem {} { + label OFF + xywh {0 0 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label MULTI + xywh {10 10 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label SINGLE + xywh {20 20 100 20} labelfont 1 labelsize 11 + } + } + Fl_Check_Button {} { + label {Drum mode} + callback {part->Pdrummode=(int) o->value();} + xywh {285 350 70 15} down_box DOWN_BOX labelsize 10 + code0 {o->value(part->Pdrummode);} + } + Fl_Box {} { + label {FX.r.} + xywh {620 0 30 15} labelfont 1 labelsize 11 align 18 + } + Fl_Box {} { + label PADsynth + xywh {540 0 60 15} labelfont 1 labelsize 11 align 18 + } + } + Fl_Window instrumenteditwindow { + label {Instrument Edit} open + xywh {247 621 395 360} type Double box NO_BOX visible + } { + Fl_Group {} { + xywh {0 220 395 110} box UP_FRAME + } { + Fl_Group {} { + label PADsynth + xywh {205 245 100 80} box ENGRAVED_FRAME labelfont 1 + } { + Fl_Button padeditbutton { + label Edit + callback {showparameters(0,2);} + xywh {215 280 80 35} color 51 selection_color 51 labelfont 1 labelsize 13 align 128 + code0 {if (part->kit[0].Ppadenabled==0) o->deactivate();} + } + Fl_Check_Button padsynenabledcheck { + label Enabled + callback {int x=(int) o->value(); +part->kit[0].Ppadenabled=x; +if (x==0) padeditbutton->deactivate(); + else padeditbutton->activate();} + tooltip {enable/disable PADsynth} xywh {215 255 80 20} box UP_BOX down_box DOWN_BOX color 51 selection_color 51 labelfont 1 labelsize 11 + code1 {o->value(part->kit[0].Ppadenabled);} + } + } + Fl_Group {} { + label ADDsynth + xywh {5 245 100 80} box ENGRAVED_FRAME labelfont 1 + } { + Fl_Check_Button adsynenabledcheck { + label Enabled + callback {int x=(int) o->value(); +part->kit[0].Padenabled=x; +if (x==0) adeditbutton->deactivate(); + else adeditbutton->activate();} + tooltip {enable/disable ADsynth} xywh {15 255 80 20} box UP_BOX down_box DOWN_BOX color 51 selection_color 51 labelfont 1 labelsize 11 + code1 {o->value(part->kit[0].Padenabled);} + } + Fl_Button adeditbutton { + label Edit + callback {showparameters(0,0);} + xywh {15 281 80 34} color 51 selection_color 51 labelfont 1 labelsize 13 align 128 + code0 {if (part->kit[0].Padenabled==0) o->deactivate();} + } + } + Fl_Group {} { + label SUBsynth + xywh {105 245 100 80} box ENGRAVED_FRAME labelfont 1 + } { + Fl_Check_Button subsynenabledcheck { + label Enabled + callback {int x=(int) o->value(); +part->kit[0].Psubenabled=x; +if (x==0) subeditbutton->deactivate(); + else subeditbutton->activate();} + tooltip {enable/disable SUBsynth} xywh {115 255 80 20} box UP_BOX down_box DOWN_BOX color 51 selection_color 51 labelfont 1 labelsize 11 + code1 {o->value(part->kit[0].Psubenabled);} + } + Fl_Button subeditbutton { + label Edit + callback {showparameters(0,1);} + xywh {115 280 80 35} color 51 selection_color 51 labelfont 1 labelsize 13 align 128 + code0 {if (part->kit[0].Psubenabled==0) o->deactivate();} + } + } + Fl_Button {} { + label {Kit Edit} + callback {instrumentkitlist->show();} + xywh {310 245 80 35} color 51 selection_color 51 labelfont 1 align 128 + } + Fl_Button {} { + label Effects + callback {partfx->show();} + xywh {310 290 80 35} color 51 selection_color 51 labelfont 1 labelsize 13 + } + } + Fl_Group {} { + xywh {0 5 395 215} box UP_FRAME + } { + Fl_Input {} { + label {Author and Copyright} + callback {snprintf((char *)part->info.Pauthor,MAX_INFO_TEXT_SIZE,"%s",o->value());} + xywh {5 60 385 50} type Multiline color 26 labelsize 10 align 5 + code0 {o->maximum_size(MAX_INFO_TEXT_SIZE);} + code1 {o->value((char *) &part->info.Pauthor);} + } + Fl_Input {} { + label Comments + callback {snprintf((char *)part->info.Pcomments,MAX_INFO_TEXT_SIZE,"%s",o->value());} + xywh {5 125 385 90} type Multiline color 26 labelsize 11 align 5 + code0 {o->maximum_size(MAX_INFO_TEXT_SIZE);} + code1 {o->value((char *) &part->info.Pcomments);} + } + Fl_Choice {} { + label {Type:} + callback {part->info.Ptype=o->value();} + xywh {5 25 155 20} down_box BORDER_BOX labelfont 1 labelsize 11 align 5 textsize 10 + code0 {o->value(part->info.Ptype);} + } { + MenuItem {} { + label {--------------------------} + xywh {20 20 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Piano + xywh {10 10 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {Chromatic Percussion} + xywh {20 20 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Organ + xywh {30 30 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Guitar + xywh {40 40 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Bass + xywh {50 50 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {Solo Strings} + xywh {60 60 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Ensemble + xywh {70 70 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Brass + xywh {80 80 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Reed + xywh {90 90 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Pipe + xywh {100 100 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {Synth Lead} + xywh {110 110 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {Synth Pad} + xywh {120 120 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {Synth Effects} + xywh {130 130 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Ethnic + xywh {140 140 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label Percussive + xywh {150 150 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {Sound Effects} + xywh {160 160 100 20} labelfont 1 labelsize 11 + } + } + } + Fl_Button {} { + label Close + callback {instrumenteditwindow->hide();} + xywh {150 335 95 25} box THIN_UP_BOX + } + } + } + Function {PartUI(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {part=NULL; +adnoteui=NULL; +subnoteui=NULL; +padnoteui=NULL; +lastkititem=-1;} {} + } + Function {init(Part *part_,Master *master_,int npart_,BankUI *bankui_)} {} { + code {bankui=bankui_; +part=part_; +npart=npart_; +master=master_; +ninseff=0; + +make_window(); +\#ifdef NTK_GUI +adsynenabledcheck->selection_color(55); +subsynenabledcheck->selection_color(55); +padsynenabledcheck->selection_color(55); +\#endif +partgroup->position(this->parent()->x()+2,this->parent()->y()+2); +partgroup->show(); +end(); + + +//if (config.ui.showinstrumentinfo!=0) instrumenteditwindow->show(); + +int klimits[]={1,2,3,4,5,6,7,8,9,10,15,20,30,50,100,0}; + +keylimitlist->add("OFF"); +int k=0; +int val=-1; +char tmp[10]; +while (klimits[k]!=0){ + sprintf(tmp,"%d",klimits[k]); + keylimitlist->add(tmp); + if (val==-1){ + if (klimits[k]>part->Pkeylimit) val=k; + }; + k++; +}; + +if (val==-1) val=k; +keylimitlist->value(val);} {} + } + Function {showparameters(int kititem,int engine)} {} { + code {if (engine==-1){//this is used if I want to clear the engine from the part + if (kititem==lastkititem) kititem=-1; + else kititem=lastkititem; +}; + +if (kititem!=lastkititem){ + if (adnoteui!=NULL) delete (adnoteui); + if (subnoteui!=NULL) delete (subnoteui); + if (padnoteui!=NULL) delete (padnoteui); + adnoteui=NULL;subnoteui=NULL;padnoteui=NULL; + lastkititem=kititem; + + if (kititem>=NUM_KIT_ITEMS) return;//bad kit item + if (kititem<0) return; + + if (part->kit[kititem].adpars!=NULL) + adnoteui=new ADnoteUI(part->kit[kititem].adpars,master); + + if (part->kit[kititem].subpars!=NULL) + subnoteui=new SUBnoteUI(part->kit[kititem].subpars); + + if (part->kit[kititem].padpars!=NULL) + padnoteui=new PADnoteUI(part->kit[kititem].padpars,master); + +}; + + + +if ((engine==0)&&(adnoteui!=NULL)) adnoteui->ADnoteGlobalParameters->show(); +if ((engine==1)&&(subnoteui!=NULL)) subnoteui->SUBparameters->show(); +if ((engine==2)&&(adnoteui!=NULL)) padnoteui->padnotewindow->show();} {} + } + Function {~PartUI()} {} { + code {if (adnoteui!=NULL) delete (adnoteui); +if (subnoteui!=NULL) delete (subnoteui); +if (padnoteui!=NULL) delete (padnoteui); + +partgroup->hide(); +//delete(partgroup); + +ctlwindow->hide(); +delete(ctlwindow); + +partfx->hide(); +delete(partfx); + +instrumentkitlist->hide(); +delete(instrumentkitlist); + +instrumenteditwindow->hide(); +delete(instrumenteditwindow);} {} + } + decl {Part *part;} {} + decl {Master *master;} {} + decl {BankUI *bankui;} {} + decl {ADnoteUI *adnoteui;} {} + decl {SUBnoteUI *subnoteui;} {} + decl {PADnoteUI *padnoteui;} {} + decl {PartSysEffSend *psyef[NUM_SYS_EFX];} {} + decl {int npart;} {} + decl {int ninseff;} {} + decl {int lastkititem;} {} + decl {PartKitItem *partkititem[NUM_KIT_ITEMS];} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/PresetsUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/PresetsUI.fl new file mode 100644 index 000000000..5eafc4723 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/PresetsUI.fl @@ -0,0 +1,278 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {\#include } {selected public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "../Params/PresetsArray.h"} {} + +decl {\#include "../Params/Presets.h"} {public +} + +class PresetsUI_ {} { + Function {refresh()} {open return_type {virtual void} + } { + code {;} {} + } + Function {~PresetsUI_()} {open return_type virtual + } { + code {;} {} + } +} + +class PresetsUI {} { + Function {PresetsUI()} {} { + code {p=NULL; +make_window();} {} + } + Function {~PresetsUI()} {} { + code {copywin->hide();delete(copywin); +pastewin->hide();delete(pastewin);} {} + } + Function {make_window()} {} { + Fl_Window copywin { + label {Copy to Clipboard/Preset} + xywh {190 173 265 430} type Double box THIN_UP_BOX color 238 hide modal + } { + Fl_Browser copybrowse { + callback {int val=o->value(); +if (val!=0){ + presetname->cut(0,presetname->maximum_size()); + presetname->insert(o->text(val)); +};} + xywh {10 25 245 320} type Select + } + Fl_Button copypbutton { + label {Copy to Preset} + callback {const char *tmp=presetname->value(); +if (tmp!=NULL) { + if (strlen(tmp)>0){ + p->copy(tmp); + copywin->hide(); + }; +};} + xywh {145 355 110 20} box THIN_UP_BOX + } + Fl_Button copybutton { + label {Copy to Clipboard} + callback {p->copy(NULL); +copywin->hide();} + xywh {25 385 90 35} box THIN_UP_BOX align 192 + } + Fl_Button {} { + label Cancel + callback {copywin->hide();} + xywh {160 385 80 35} box THIN_UP_BOX align 192 + } + Fl_Box {} { + label {Type:} + xywh {10 5 40 15} labelsize 11 align 20 + } + Fl_Box copytypetext { + xywh {50 5 205 15} box FLAT_BOX color 238 labelfont 1 labelsize 11 align 20 + } + Fl_Input presetname { + callback {const char *tmp=o->value(); +if (tmp==NULL) tmp=""; +if (strlen(tmp)>0) { + copybutton->deactivate(); + copypbutton->activate(); +} else { + copybutton->activate(); + copypbutton->deactivate(); +};} + xywh {10 355 130 20} when 1 + } + } + Fl_Window pastewin { + label {Paste from Clipboard/Preset} + xywh {463 173 265 430} type Double box THIN_UP_BOX color 238 hide modal + } { + Fl_Browser pastebrowse { + callback {if (o->value()==0) { + pastepbutton->deactivate(); + deletepbutton->deactivate(); +}else{ + pastepbutton->activate(); + deletepbutton->activate(); +};} + xywh {10 25 245 320} type Hold + } + Fl_Button pastepbutton { + label {Paste from Preset} + callback {int n=pastebrowse->value(); +if (n!=0) p->paste(n); +pastewin->hide(); +pui->refresh();} + xywh {10 355 160 20} box THIN_UP_BOX + } + Fl_Button pastebutton { + label {Paste from Clipboard} + callback {p->paste(0); +pastewin->hide(); +pui->refresh();} + xywh {25 385 90 35} box THIN_UP_BOX align 192 + } + Fl_Button {} { + label Cancel + callback {pastewin->hide();} + xywh {160 385 80 35} box THIN_UP_BOX align 192 + } + Fl_Box pastetypetext { + xywh {55 5 200 15} box FLAT_BOX color 238 labelfont 1 labelsize 11 align 20 + } + Fl_Box {} { + label {Type:} + xywh {15 5 40 15} labelsize 11 align 20 + } + Fl_Button deletepbutton { + label Delete + callback {int n=pastebrowse->value(); + +if (this->p_is_PresetArray) { + PresetsArray *pre = dynamic_cast(p); + if (n!=0) pre->deletepreset(n); + rescanArray(); +} else { + if (n!=0) p->deletepreset(n); + rescan(); +}} selected + xywh {180 355 75 20} box THIN_UP_BOX + } + } + } + Function {copy(Presets *p)} {} { + code {copybutton->activate(); +copypbutton->deactivate(); + + +this->p=p; +this->pui=NULL; +bool but=(Fl::event_button()!=FL_LEFT_MOUSE); +presetname->cut(0,presetname->maximum_size()); + +if (but) p->copy(NULL); + else { + rescan(); + copytypetext->label(&p->type[1]); + copywin->show(); + };} {} + } + Function {paste(Presets *p,PresetsUI_ *pui)} {} { + code {this->p=p; +this->pui=pui; +bool but=(Fl::event_button()!=FL_LEFT_MOUSE); +pastepbutton->deactivate(); +deletepbutton->deactivate(); + +if (but) { + p->paste(0); + pui->refresh(); +} else { + rescan(); + pastetypetext->label(&p->type[1]); + if (p->checkclipboardtype()) pastebutton->activate(); + else pastebutton->deactivate(); + this->p_is_PresetArray = false; + pastewin->show(); + };} {} + } + Function {copy(Presets *p,int n)} {} { + code {PresetsArray *pre = dynamic_cast(p); +if(pre) + pre->setelement(n); +copyArray(p);} {} + } + Function {paste(Presets *p,PresetsUI_ *pui,int n)} {} { + code {PresetsArray *pre = dynamic_cast(p); +if(pre) + pre->setelement(n); +pasteArray(p,pui);} {} + } + Function {rescan()} {} { + code {copybrowse->clear(); +pastebrowse->clear(); +p->rescanforpresets(); + +for (unsigned int i=0;iadd(name.c_str()); + pastebrowse->add(name.c_str()); +};} {} + } + decl {Presets *p;} {public local + } + decl {PresetsUI_ *pui;} {public local + } + Function {copyArray(Presets *p)} {open + } { + code {PresetsArray *pre = dynamic_cast(p); + +copybutton->activate(); +copypbutton->deactivate(); + +this->p=p; +this->pui=NULL; +bool but=(Fl::event_button()!=FL_LEFT_MOUSE); +presetname->cut(0,presetname->maximum_size()); + +if (but) pre->copy(NULL); + else { + rescanArray(); + copytypetext->label(&pre->type[1]); + copywin->show(); + };} {} + } + Function {pasteArray(Presets *p,PresetsUI_ *pui)} {open + } { + code {PresetsArray *pre = dynamic_cast(p); + +this->p=p; +this->pui=pui; +bool but=(Fl::event_button()!=FL_LEFT_MOUSE); +pastepbutton->deactivate(); +deletepbutton->deactivate(); + +if (but) { + pre->paste(0); + pui->refresh(); +} else { + rescanArray(); + pastetypetext->label(&pre->type[1]); + if (pre->checkclipboardtype()) pastebutton->activate(); + else pastebutton->deactivate(); + this->p_is_PresetArray = true; + pastewin->show(); + };} {} + } + Function {rescanArray()} {open + } { + code {PresetsArray *pre = dynamic_cast(p); + +copybrowse->clear(); +pastebrowse->clear(); +pre->rescanforpresets(); + +for (unsigned int i=0;iadd(name.c_str()); + pastebrowse->add(name.c_str()); +};} {} + } + decl {bool p_is_PresetArray;} {public + } +} + +decl {PresetsUI *presetsui;} {public +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/ResonanceUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/ResonanceUI.fl new file mode 100644 index 000000000..12e591ab5 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/ResonanceUI.fl @@ -0,0 +1,398 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include } {} + +decl {\#include "../Synth/Resonance.h"} {public +} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include "PresetsUI.h"} {public +} + +class ResonanceGraph {open : {public Fl_Box} +} { + Function {ResonanceGraph(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {} { + code {respar=NULL; +cbwidget=NULL; +applybutton=NULL;} {} + } + Function {init(Resonance *respar_,Fl_Value_Output *khzvalue_,Fl_Value_Output *dbvalue_)} {} { + code {respar=respar_; +khzvalue=khzvalue_; +dbvalue=dbvalue_; +oldx=-1; +khzval=-1;} {} + } + Function {draw_freq_line(float freq,int type)} {open + } { + code {float freqx=respar->getfreqpos(freq); +switch(type){ + case 0:fl_line_style(FL_SOLID);break; + case 1:fl_line_style(FL_DOT);break; + case 2:fl_line_style(FL_DASH);break; +}; + + +if ((freqx>0.0)&&(freqx<1.0)) + fl_line(x()+(int) (freqx*w()),y(), + x()+(int) (freqx*w()),y()+h());} {} + } + Function {draw()} {open + } { + code {int ox=x(),oy=y(),lx=w(),ly=h(),i,ix,iy,oiy; +float freqx; + +fl_color(FL_DARK1); +fl_rectf(ox,oy,lx,ly); + + +//draw the lines +fl_color(FL_GRAY); + +fl_line_style(FL_SOLID); +fl_line(ox+2,oy+ly/2,ox+lx-2,oy+ly/2); + +freqx=respar->getfreqpos(1000.0); +if ((freqx>0.0)&&(freqx<1.0)) + fl_line(ox+(int) (freqx*lx),oy, + ox+(int) (freqx*lx),oy+ly); + +for (i=1;i<10;i++){ + if(i==1){ + draw_freq_line(i*100.0,0); + draw_freq_line(i*1000.0,0); + }else + if (i==5){ + draw_freq_line(i*100.0,2); + draw_freq_line(i*1000.0,2); + }else{ + draw_freq_line(i*100.0,1); + draw_freq_line(i*1000.0,1); + }; +}; + +draw_freq_line(10000.0,0); +draw_freq_line(20000.0,1); + +fl_line_style(FL_DOT); +int GY=10;if (lyPrespoints[0]/128.0*ly); +for (i=1;iPrespoints[i]/128.0*ly); + fl_vertex(ox+ix,oy+ly-oiy); + oiy=iy; +}; +fl_end_line(); +fl_line_style(FL_SOLID,0);} {selected + } + } + Function {handle(int event)} {return_type int + } { + code {int x_=Fl::event_x()-x(); +int y_=Fl::event_y()-y(); +if ( (x_>=0)&&(x_=0)&&(y_value(respar->getfreqx(x_*1.0/w())/1000.0); + dbvalue->value((1.0-y_*2.0/h())*respar->PmaxdB); +}; + +if ((event==FL_PUSH)||(event==FL_DRAG)){ + int leftbutton=1; + if (Fl::event_button()==FL_RIGHT_MOUSE) leftbutton=0; + if (x_<0) x_=0;if (y_<0) y_=0; + if (x_>=w()) x_=w();if (y_>=h()-1) y_=h()-1; + + if ((oldx<0)||(oldx==x_)){ + int sn=(int)(x_*1.0/w()*N_RES_POINTS); + int sp=127-(int)(y_*1.0/h()*127); + if (leftbutton!=0) respar->setpoint(sn,sp); + else respar->setpoint(sn,64); + } else { + int x1=oldx; + int x2=x_; + int y1=oldy; + int y2=y_; + if (oldx>x_){ + x1=x_;y1=y_; + x2=oldx;y2=oldy; + }; + for (int i=0;isetpoint(sn,sp); + else respar->setpoint(sn,64); + }; + }; + + oldx=x_;oldy=y_; + redraw(); +}; + +if (event==FL_RELEASE) { + oldx=-1; + if (cbwidget!=NULL) { + cbwidget->do_callback(); + if (applybutton!=NULL) { + applybutton->color(FL_RED); + applybutton->redraw(); + + }; + }; +}; + +return(1);} {} + } + Function {setcbwidget(Fl_Widget *cbwidget,Fl_Widget *applybutton)} {} { + code {this->cbwidget=cbwidget; +this->applybutton=applybutton;} {} + } + decl {Fl_Value_Output *khzvalue;} {} + decl {Fl_Value_Output *dbvalue;} {} + decl {Resonance *respar;} {} + decl {int oldx,oldy;} {} + decl {float khzval;} {public + } + decl {Fl_Widget *cbwidget,*applybutton;} {} +} + +class ResonanceUI {open : PresetsUI_ +} { + Function {make_window()} {open + } { + Fl_Window resonancewindow { + label Resonance open + xywh {120 70 780 305} type Double hide + } { + Fl_Value_Output khzvalue { + label kHz + xywh {415 264 45 18} labelsize 12 align 8 minimum 0.001 maximum 48 step 0.01 textfont 1 textsize 12 + code0 {//this widget must be before the calling widgets} + } + Fl_Value_Output dbvalue { + label dB + xywh {415 282 45 18} labelsize 12 align 8 minimum -150 maximum 150 step 0.1 textfont 1 textsize 12 + code0 {//this widget must be before the calling widgets} + } + Fl_Group {} { + xywh {6 5 768 256} box BORDER_BOX + code0 {rg=new ResonanceGraph(o->x(),o->y(),o->w(),o->h(),"");} + code1 {rg->init(respar,khzvalue,dbvalue);} + code2 {rg->show();} + } {} + Fl_Button {} { + label Close + callback {resonancewindow->hide();} + xywh {690 283 84 17} box THIN_UP_BOX + } + Fl_Button {} { + label Zero + callback {for (int i=0;isetpoint(i,64); +resonancewindow->redraw(); +redrawPADnoteApply();} + tooltip {Clear the resonance function} xywh {491 264 66 15} box THIN_UP_BOX labelfont 1 labelsize 12 + } + Fl_Button {} { + label Smooth + callback {respar->smooth(); +resonancewindow->redraw(); +redrawPADnoteApply();} + tooltip {Smooth the resonance function} xywh {491 282 66 18} box THIN_UP_BOX labelfont 1 labelsize 12 + } + Fl_Check_Button enabled { + label Enable + callback {respar->Penabled=(int) o->value(); +redrawPADnoteApply();} + xywh {6 270 78 27} box THIN_UP_BOX down_box DOWN_BOX + code0 {o->value(respar->Penabled);} + } + Fl_Roller maxdb { + callback {maxdbvo->value(o->value()); +respar->PmaxdB=(int) o->value(); +redrawPADnoteApply();} + xywh {90 282 84 15} type Horizontal minimum 1 maximum 90 step 1 value 30 + } + Fl_Value_Output maxdbvo { + label {Max.} + callback {o->value(respar->PmaxdB);} + tooltip {The Maximum amplitude (dB)} xywh {126 264 24 18} labelsize 12 minimum 1 maximum 127 step 1 value 30 textfont 1 textsize 12 + code0 {o->value(respar->PmaxdB);} + } + Fl_Box {} { + label dB + xywh {150 264 24 18} + } + Fl_Value_Output centerfreqvo { + label {C.f.} + callback {o->value(respar->getcenterfreq()/1000.0);} + tooltip {Center Frequency (kHz)} xywh {210 264 33 18} labelsize 12 when 3 minimum 1 maximum 10 step 0.01 value 1 textfont 1 textsize 12 + code0 {o->value(respar->getcenterfreq()/1000.0);} + } + Fl_Value_Output octavesfreqvo { + label {Oct.} + callback {o->value(respar->getoctavesfreq());} + tooltip {No. of octaves} xywh {210 282 33 18} labelsize 12 when 3 minimum 1 maximum 127 step 1 value 30 textfont 1 textsize 12 + code0 {o->value(respar->getoctavesfreq());} + } + Fl_Button {} { + label RND2 + callback {respar->randomize(1); +resonancewindow->redraw(); +redrawPADnoteApply();} + tooltip {Randomize the resonance function} xywh {566 276 42 12} box THIN_UP_BOX labelfont 1 labelsize 10 + } + Fl_Button {} { + label RND1 + callback {respar->randomize(0); +resonancewindow->redraw(); +redrawPADnoteApply();} + tooltip {Randomize the resonance function} xywh {566 264 42 12} box THIN_UP_BOX labelfont 1 labelsize 10 + } + Fl_Button {} { + label RND3 + callback {respar->randomize(2); +resonancewindow->redraw(); +redrawPADnoteApply();} + tooltip {Randomize the resonance function} xywh {566 288 42 12} box THIN_UP_BOX labelfont 1 labelsize 10 + } + Fl_Check_Button p1st { + label {P.1st} + callback {respar->Pprotectthefundamental=(int) o->value(); +redrawPADnoteApply();} + tooltip {Protect the fundamental frequency (do not damp the first harmonic)} xywh {365 285 45 15} down_box DOWN_BOX labelsize 10 + code0 {o->value(respar->Pprotectthefundamental);} + } + Fl_Button {} { + label InterpP + callback {int type; +if (Fl::event_button()==FL_LEFT_MOUSE) type=0; + else type=1; +respar->interpolatepeaks(type); +resonancewindow->redraw(); +redrawPADnoteApply();} + tooltip {Interpolate the peaks} xywh {365 265 46 15} box THIN_UP_BOX labelfont 1 labelsize 10 + } + Fl_Dial centerfreq { + label {C.f.} + callback {respar->Pcenterfreq=(int)o->value(); +centerfreqvo->do_callback(); +rg->redraw(); +redrawPADnoteApply();} + xywh {245 265 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(respar->Pcenterfreq);} + class WidgetPDial + } + Fl_Dial octavesfreq { + label {Oct.} + callback {respar->Poctavesfreq=(int)o->value(); +octavesfreqvo->do_callback(); +rg->redraw(); +redrawPADnoteApply();} + xywh {280 265 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(respar->Poctavesfreq);} + class WidgetPDial + } + Fl_Button {} { + label C + callback {presetsui->copy(respar);} + xywh {625 275 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(respar,this);} + xywh {655 275 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button applybutton { + label Apply + callback {applybutton->color(FL_GRAY); +applybutton->redraw(); +if (cbapplywidget!=NULL) { + cbapplywidget->do_callback(); + cbapplywidget->color(FL_GRAY); + cbapplywidget->redraw(); +};} + xywh {690 265 85 15} box THIN_UP_BOX labelfont 1 labelsize 11 + } + } + } + Function {ResonanceUI(Resonance *respar_)} {} { + code {respar=respar_; +cbwidget=NULL; +cbapplywidget=NULL; +make_window(); +applybutton->hide();} {} + } + Function {~ResonanceUI()} {} { + code {resonancewindow->hide();} {} + } + Function {redrawPADnoteApply()} {} { + code {if (cbwidget!=NULL) { + cbwidget->do_callback(); + applybutton->color(FL_RED); + applybutton->redraw(); +};} {} + } + Function {setcbwidget(Fl_Widget *cbwidget,Fl_Widget *cbapplywidget)} {} { + code {this->cbwidget=cbwidget; +this->cbapplywidget=cbapplywidget; +rg->setcbwidget(cbwidget,applybutton); +applybutton->show();} {} + } + Function {refresh()} {} { + code {redrawPADnoteApply(); + +enabled->value(respar->Penabled); + +maxdb->value(respar->PmaxdB); +maxdbvo->value(respar->PmaxdB); + +centerfreqvo->value(respar->getcenterfreq()/1000.0); +octavesfreqvo->value(respar->getoctavesfreq()); + +centerfreq->value(respar->Pcenterfreq); +octavesfreq->value(respar->Poctavesfreq); + +p1st->value(respar->Pprotectthefundamental); + +rg->redraw();} {} + } + decl {Resonance *respar;} {public + } + decl {ResonanceGraph *rg;} {} + decl {Fl_Widget *cbwidget,*cbapplywidget;} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/SUBnoteUI.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/SUBnoteUI.fl new file mode 100644 index 000000000..d7807fc84 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/SUBnoteUI.fl @@ -0,0 +1,450 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "../globals.h"} {public +} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include "EnvelopeUI.h"} {public +} + +decl {\#include "FilterUI.h"} {public +} + +decl {\#include "../Misc/Util.h"} {public +} + +decl {\#include "../Params/SUBnoteParameters.h"} {public +} + +decl {\#include "PresetsUI.h"} {public +} + +class SUBnoteharmonic {: {public Fl_Group} +} { + Function {make_window()} {private + } { + Fl_Window harmonic { + xywh {329 403 90 225} type Double hide + class Fl_Group + } { + Fl_Slider mag { + callback {int x=0; +if (Fl::event_button1() || Fl::event() == FL_MOUSEWHEEL) x=127-(int)o->value(); + else o->value(127-x); +pars->Phmag[n]=x; +if (pars->Phmag[n]==0) o->selection_color(0); + else o->selection_color(222);} + tooltip {harmonic's magnitude} xywh {0 15 10 115} type {Vert Knob} box FLAT_BOX selection_color 222 maximum 127 step 1 value 127 + code0 {o->value(127-pars->Phmag[n]);} + code1 {if (pars->Phmag[n]==0) o->selection_color(0);} + } + Fl_Slider bw { + callback {int x=64; +if (Fl::event_button1() || Fl::event() == FL_MOUSEWHEEL) x=127-(int)o->value(); + else o->value(x); +pars->Phrelbw[n]=x;} + tooltip {harmonic's bandwidth} xywh {0 135 10 75} type {Vert Knob} box FLAT_BOX selection_color 222 maximum 127 step 1 value 64 + code0 {o->value(127-pars->Phrelbw[n]);} + } + Fl_Box {} { + xywh {10 170 5 5} box FLAT_BOX color 45 + code0 {if (n+1==MAX_SUB_HARMONICS) o->hide();} + } + Fl_Box {} { + label 01 + xywh {0 210 10 15} labelfont 1 labelsize 9 align 20 + code0 {char tmp[10];snprintf(tmp,10,"%d",n+1);o->label(strdup(tmp));} + } + Fl_Box {} { + label 01 + xywh {0 0 10 15} labelfont 1 labelsize 9 align 20 + code0 {char tmp[10];snprintf(tmp,10,"%d",n+1);o->label(strdup(tmp));} + } + } + } + Function {SUBnoteharmonic(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} { + code {n=0;} {} + } + Function {init(SUBnoteParameters *pars_,int n_)} {} { + code {pars=pars_; +n=n_; +make_window(); +harmonic->show(); +end();} {} + } + Function {refresh()} {} { + code {mag->value(127-pars->Phmag[n]); +if (pars->Phmag[n]==0) mag->selection_color(0); +bw->value(127-pars->Phrelbw[n]);} {} + } + Function {~SUBnoteharmonic()} {} { + code {harmonic->hide(); +hide(); +//delete(harmonic);} {} + } + decl {SUBnoteParameters *pars;} {} + decl {int n;} {} +} + +class SUBnoteUI {open : {public PresetsUI_} +} { + Function {make_window()} {open + } { + Fl_Window SUBparameters { + label {SUBsynth Parameters} open + xywh {542 489 735 390} type Double visible + } { + Fl_Scroll {} { + label scroll open + xywh {5 140 435 245} type HORIZONTAL box FLAT_BOX labeltype NO_LABEL + } { + Fl_Pack harmonics {open + xywh {10 145 425 235} type HORIZONTAL + code0 {for (int i=0;ih(),"");h[i]->init(pars,i);}} + } {} + } + Fl_Button {} { + label Close + callback {SUBparameters->hide();} + xywh {625 365 105 20} box THIN_UP_BOX labelfont 1 labelsize 11 + } + Fl_Group {} { + label AMPLITUDE + xywh {5 5 215 135} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 align 17 + } { + Fl_Value_Slider vol { + label Vol + callback {pars->PVolume=(int)o->value();} + tooltip Volume xywh {10 25 140 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 + code0 {o->value(pars->PVolume);} + } + Fl_Value_Slider vsns { + label {V.Sns} + callback {pars->PAmpVelocityScaleFunction=(int) o->value();} + tooltip {Velocity Sensing Function (rightmost to disable)} xywh {10 45 140 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 + code0 {o->value(pars->PAmpVelocityScaleFunction);} + } + Fl_Dial pan { + label Pan + callback {pars->PPanning=(int) o->value();} + tooltip {Panning (leftmost is Random)} xywh {185 20 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 + code0 {o->value(pars->PPanning);} + class WidgetPDial + } + Fl_Group ampenv { + label {SUBsynth - Amplitude Envelope} open + xywh {10 65 205 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->AmpEnvelope);} + class EnvelopeUI + } {} + } + Fl_Group {} { + xywh {495 325 235 35} box UP_FRAME + } { + Fl_Counter filterstages { + label {Filter Stages} + callback {pars->Pnumstages=(int) o->value();} + tooltip {How many times the noise is filtered} xywh {515 340 45 15} type Simple labelfont 1 labelsize 10 align 1 minimum 1 maximum 5 step 1 textsize 10 + code0 {o->value(pars->Pnumstages);} + } + Fl_Choice magtype { + label {Mag.Type} + callback {pars->Phmagtype=(int) o->value();} + xywh {585 340 65 15} down_box BORDER_BOX labelfont 1 labelsize 10 align 1 textsize 11 + code0 {o->value(pars->Phmagtype);} + } { + MenuItem {} { + label Linear + xywh {20 20 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {-40dB} + xywh {30 30 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {-60dB} + xywh {40 40 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {-80dB} + xywh {50 50 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {-100dB} + xywh {60 60 100 20} labelfont 1 labelsize 11 + } + } + Fl_Choice start { + label Start + callback {pars->Pstart=(int) o->value();} open + xywh {670 340 50 15} down_box BORDER_BOX labelfont 1 labelsize 10 align 1 textsize 11 + code0 {o->value(pars->Pstart);} + } { + MenuItem {} { + label Zero + xywh {30 30 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label RND + xywh {40 40 100 20} labelfont 1 labelsize 11 + } + MenuItem {} { + label {Max.} + xywh {50 50 100 20} labelfont 1 labelsize 11 + } + } + } + Fl_Group freqsettingsui { + label FREQUENCY + xywh {440 5 290 135} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 align 17 + } { + Fl_Group freqenvelopegroup { + label {SUBsynth - Frequency Envelope} open + xywh {445 65 205 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->FreqEnvelope);} + code1 {if (pars->PFreqEnvelopeEnabled==0) o->deactivate();} + class EnvelopeUI + } {} + Fl_Check_Button freqee { + label Enabled + callback {pars->PFreqEnvelopeEnabled=o->value(); +if (o->value()==0) freqenvelopegroup->deactivate(); + else freqenvelopegroup->activate(); +o->show(); +freqsettingsui->redraw();} + xywh {445 68 55 15} down_box DOWN_BOX labelfont 1 labelsize 10 + code0 {o->value(pars->PFreqEnvelopeEnabled);} + } + Fl_Counter octave { + label Octave + callback {int k=(int) o->value(); +if (k<0) k+=16; +pars->PCoarseDetune = k*1024+ + pars->PCoarseDetune%1024;} + tooltip Octave xywh {670 50 45 15} type Simple labelsize 10 align 1 minimum -8 maximum 7 step 1 textfont 1 textsize 11 + code0 {int k=pars->PCoarseDetune/1024;if (k>=8) k-=16;} + code2 {o->value(k);} + } + Fl_Counter coarsedet { + label {Coarse Det.} + callback {int k=(int) o->value(); +if (k<0) k+=1024; +pars->PCoarseDetune = k+ + (pars->PCoarseDetune/1024)*1024;} + tooltip {Coarse Detune} xywh {655 115 60 20} labelsize 10 align 1 minimum -64 maximum 63 step 1 textfont 1 textsize 11 + code0 {int k=pars->PCoarseDetune%1024;if (k>=512) k-=1024;} + code2 {o->value(k);} + code3 {o->lstep(10);} + } + Fl_Slider detune { + callback {pars->PDetune=(int)o->value()+8192; +detunevalueoutput->do_callback();} + tooltip {Fine Detune (cents)} xywh {495 25 230 15} type {Horz Knob} box NO_BOX minimum -8192 maximum 8191 step 1 + code0 {o->value(pars->PDetune-8192);} + } + Fl_Value_Output detunevalueoutput { + label Detune + callback {o->value(getdetune(pars->PDetuneType,0,pars->PDetune));} + xywh {448 25 45 15} labelsize 10 align 5 minimum -5000 maximum 5000 step 0.01 textfont 1 textsize 10 + code0 {o->value(getdetune(pars->PDetuneType,0,pars->PDetune));} + } + Fl_Check_Button hz440 { + label 440Hz + callback {int x=(int) o->value(); +pars->Pfixedfreq=x; +if (x==0) fixedfreqetdial->deactivate(); + else fixedfreqetdial->activate();} + tooltip {set the base frequency to 440Hz} xywh {555 45 50 15} down_box DOWN_BOX labelfont 1 labelsize 10 + code0 {o->value(pars->Pfixedfreq);} + } + Fl_Dial fixedfreqetdial { + label {Eq.T.} + callback {pars->PfixedfreqET=(int) o->value();} + tooltip {How the frequency varies acording to the keyboard (leftmost for fixed frequency)} xywh {610 45 15 15} box ROUND_UP_BOX labelsize 10 align 8 maximum 127 step 1 + code0 {o->value(pars->PfixedfreqET);} + code1 {if (pars->Pfixedfreq==0) o->deactivate();} + class WidgetPDial + } + Fl_Choice detunetype { + label {Detune Type} + callback {pars->PDetuneType=(int) o->value()+1; +detunevalueoutput->do_callback();} open + xywh {655 85 70 15} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 + code0 {o->add("L35cents");o->add("L10cents");o->add("E100cents");o->add("E1200cents");} + code1 {o->value(pars->PDetuneType-1);} + } {} + } + Fl_Check_Button stereo { + label Stereo + callback {pars->Pstereo=(int) o->value();} selected + xywh {440 325 55 35} box THIN_UP_BOX down_box DOWN_BOX labelsize 10 + code0 {o->value(pars->Pstereo);} + } + Fl_Button {} { + label Clear + callback {for (int i=0;imag->value(127); + pars->Phmag[i]=0; + h[i]->bw->value(64); + pars->Phrelbw[i]=64; +}; +pars->Phmag[0]=127; +h[0]->mag->value(0); +SUBparameters->redraw();} + tooltip {Clear the harmonics} xywh {445 365 70 20} box THIN_UP_BOX labelfont 1 labelsize 11 + } + Fl_Group bandwidthsettingsui { + label BANDWIDTH + xywh {220 5 220 135} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 align 17 + } { + Fl_Group bandwidthenvelopegroup { + label {SUBsynth - BandWidth Envelope} open + xywh {225 65 205 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->BandWidthEnvelope);} + code1 {if (pars->PBandWidthEnvelopeEnabled==0) o->deactivate();} + class EnvelopeUI + } {} + Fl_Check_Button bwee { + label Enabled + callback {pars->PBandWidthEnvelopeEnabled=o->value(); +if (o->value()==0) bandwidthenvelopegroup->deactivate(); + else bandwidthenvelopegroup->activate(); +o->show(); +bandwidthsettingsui->redraw();} + xywh {225 67 55 15} down_box DOWN_BOX labelfont 1 labelsize 10 + code0 {o->value(pars->PBandWidthEnvelopeEnabled);} + } + Fl_Value_Slider bandwidth { + label {Band Width} + callback {pars->Pbandwidth=(int) o->value();} + xywh {225 40 115 15} type {Horz Knob} box NO_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->value(pars->Pbandwidth);} + } + Fl_Value_Slider bwidthscale { + label {B.Width Scale} + callback {pars->Pbwscale=(int) o->value()+64;} + tooltip {How much I increase the BandWidth according to lower/higher harmonics} xywh {345 40 90 15} type {Horz Knob} box NO_BOX labelsize 10 align 1 minimum -64 maximum 63 step 1 + code0 {o->value(pars->Pbwscale-64);} + } + } + Fl_Group globalfiltergroup { + label FILTER + xywh {440 140 290 185} box UP_FRAME labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 17 + code0 {if (pars->PGlobalFilterEnabled==0) o->deactivate();} + } { + Fl_Group filterenv { + label {SUBsynth - Filter Envelope} open + xywh {445 250 275 70} box FLAT_BOX color 51 align 144 + code0 {o->init(pars->GlobalFilterEnvelope);} + class EnvelopeUI + } {} + Fl_Group filterui { + label {SUBsynthl - Filter} open + xywh {445 165 275 75} box FLAT_BOX color 50 align 144 + code0 {o->init(pars->GlobalFilter,&pars->PGlobalFilterVelocityScale,&pars->PGlobalFilterVelocityScaleFunction);} + class FilterUI + } {} + } + Fl_Check_Button filtere { + label Enabled + callback {pars->PGlobalFilterEnabled=o->value(); +if (o->value()==0) globalfiltergroup->deactivate(); + else globalfiltergroup->activate(); +o->show(); +globalfiltergroup->redraw();} + xywh {445 145 85 20} down_box DOWN_BOX labelfont 1 labelsize 11 + code0 {o->value(pars->PGlobalFilterEnabled);} + } + Fl_Button {} { + label C + callback {presetsui->copy(pars);} + xywh {540 370 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + Fl_Button {} { + label P + callback {presetsui->paste(pars,this);} + xywh {570 370 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + } + } + } + Function {refresh()} {} { + code {for (int i=0;irefresh(); +vol->value(pars->PVolume); +vsns->value(pars->PAmpVelocityScaleFunction); +pan->value(pars->PPanning); + + +bandwidth->value(pars->Pbandwidth); +bwidthscale->value(pars->Pbwscale-64); +bwee->value(pars->PBandWidthEnvelopeEnabled); +if (pars->PBandWidthEnvelopeEnabled==0) bandwidthenvelopegroup->deactivate(); + else bandwidthenvelopegroup->activate(); +bwee->show(); +bandwidthsettingsui->redraw(); + +detunevalueoutput->value(getdetune(pars->PDetuneType,0,pars->PDetune)); +freqee->value(pars->PFreqEnvelopeEnabled); +if (pars->PFreqEnvelopeEnabled==0) freqenvelopegroup->deactivate(); + else freqenvelopegroup->activate(); +freqee->show(); +freqsettingsui->redraw(); + +detune->value(pars->PDetune-8192); +hz440->value(pars->Pfixedfreq); + +fixedfreqetdial->value(pars->PfixedfreqET); + +int k=pars->PCoarseDetune/1024;if (k>=8) k-=16; +octave->value(k); + +detunetype->value(pars->PDetuneType-1); + +k=pars->PCoarseDetune%1024;if (k>=512) k-=1024; +coarsedet->value(k); + +filtere->value(pars->PGlobalFilterEnabled); +if (pars->PGlobalFilterEnabled==0) globalfiltergroup->deactivate(); + else globalfiltergroup->activate(); +filtere->show(); +globalfiltergroup->redraw(); + +stereo->value(pars->Pstereo); +filterstages->value(pars->Pnumstages); +magtype->value(pars->Phmagtype); +start->value(pars->Pstart); + +ampenv->refresh(); +bandwidthenvelopegroup->refresh(); +freqenvelopegroup->refresh(); +filterui->refresh(); +filterenv->refresh();} {} + } + Function {SUBnoteUI(SUBnoteParameters *parameters)} {} { + code {pars=parameters; +make_window();} {} + } + Function {~SUBnoteUI()} {} { + code {//for (int i=0;ihide(); +delete(SUBparameters);} {} + } + decl {SUBnoteParameters *pars;} {} + decl {SUBnoteharmonic *h[MAX_SUB_HARMONICS];} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/VirKeyboard.fl b/plugins/zynaddsubfx/zynaddsubfx/src/UI/VirKeyboard.fl new file mode 100644 index 000000000..6bbaa870e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/VirKeyboard.fl @@ -0,0 +1,487 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0110 +header_name {.h} +code_name {.cc} +decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} + +decl {//License: GNU GPL version 2 or later} {} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include } {public +} + +decl {\#include "../globals.h"} {public +} + +decl {\#include "../Misc/Master.h"} {public +} + +decl {\#include "../Misc/Util.h"} {public +} + +decl {\#include "WidgetPDial.h"} {public +} + +decl {\#include "common.H"} {public +} + +decl {\#ifdef NTK_GUI + \#include "FL/Fl_Shared_Image.H" + \#endif} {public +} + +decl {const int keyspos[12]={0,-1,1,-2,2,3,-4,4,-5,5,-6,6};} {} + +decl {const int keysoct1qwerty[]={'q','2','w','3','e','r','5','t','6','y','7','u','i','9','o','0','p','[','=',']','\\\\',FL_Enter,0};} {} + +decl {const int keysoct2qwerty[]={'z','s','x','d','c','v','g','b','h','n','j','m',',','l','.',';','/',0};} {} + +decl {const int keysoct1dw[]={'\\'','2',',','3','.','p','5','y','6','f','7','g','c','9','r','0','l','/',']','=','\\\\',FL_Enter,0};} {} + +decl {const int keysoct2dw[]={';','o','q','e','j','k','i','x','d','b','h','m','w','n','v','s','z',0};} {} + +decl {const int keysoct1qwertz[]={'q','2','w','3','e','r','5','t','6','z','7','u','i','9','o','0','p',252,'\\'','+','\\\\',FL_Enter,0};} {} + +decl {const int keysoct2qwertz[]={'y','s','x','d','c','v','g','b','h','n','j','m',',','l','.',246,'-',0};} {} + +decl {const int keysoct1az[]={'a',233,'z','\\"','e','r','(','t','-','y',232,'u','i',231,'o',224,'p',65106,'=','$',0};} {} + +decl {const int keysoct2az[]={'w','s','x','d','c','v','g','b','h','n','j',',',';','l',':','m','!',0};} {} + +class VirKeys {: {public Fl_Box} +} { + decl {static const int N_OCT=6;} {} + decl {static const int SIZE_WHITE=14;} {} + decl {static const int SIZE_BLACK=8;} {} + Function {VirKeys(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {} { + code {master=NULL;} {} + } + Function {init(Master *master_)} {} { + code {master=master_; +for (int i=0;i=0){//white keys + if (pressed[i]==0) + key = white_up; + else + key = white_down; + + key->draw( ox + (kv + 7 * noct ) * white_up->w() + 3, oy ); + } +} + +for (i=0;idraw( ox + (kv + 7 * noct ) * white_up->w() - black_up->w() / 2 + 2, oy ); + } +} +\#else +if (damage()!=1){ + fl_color(250,240,230); + fl_rectf(ox,oy,lx,ly); + + fl_color(FL_BLACK); + fl_line(ox,oy,ox+lx,oy); + fl_line(ox,oy+ly,ox+lx,oy+ly); + for (i=0;i=0){//white keys + if (pressed[i]==0) fl_color(250,240,230); + else fl_color(FL_BLUE); + fl_rectf(ox+(kv+7*noct)*SIZE_WHITE+3,oy+ly*3/5+2, + SIZE_WHITE-4,ly*2/5-3); + } else {//black keys + kv=keyspos[(i+1)%12]; + if (pressed[i]==0) fl_color(FL_BLACK); + else fl_color(FL_BLUE); + fl_rectf(ox+(kv+7*noct)*SIZE_WHITE-SIZE_BLACK/2+2,oy+2, + SIZE_BLACK-3,ly*3/5-5); + } +} +\#endif} {} + } + Function {handle(int event)} {return_type int + } { + code {int i; +int ly=h(); +int x_=Fl::event_x()-x(); +int y_=Fl::event_y()-y(); +if ( (x_<0)&&(x_>w()) && (y_<0)&&(y_>h())){ + return(0); +}; + + +if ((event==FL_PUSH)||(event==FL_DRAG)||(event==FL_RELEASE)){ + int kpos=-1; + + if (y_>ly*3/5){//white keys + int pos=x_/SIZE_WHITE; + if (pos<0) return(1); + for (i=0;i<12;i++) { + if (pos%7==keyspos[i]) { + kpos=pos/7*12+i; + break; + }; + }; + } else {//black keys + int pos=(x_+SIZE_WHITE/2)/SIZE_WHITE; + if (pos<0) return(1); + for (i=1;i<12;i++) { + if (pos%7==-keyspos[i]) { + kpos=pos/7*12+i; + break; + }; + }; + }; + + if ((kpos!=-1)&&((event==FL_PUSH)||(event==FL_DRAG))&& + (Fl::event_shift()==0)) { + presskey(kpos,1,1); + }; + + if ((event==FL_PUSH)&&(Fl::event_shift()!=0)) { + if (pressed[kpos]==0) presskey(kpos,0,1); + else relasekey(kpos,1); + }; + if ((event==FL_RELEASE)&&(Fl::event_shift()==0)) + relaseallkeys(1); + take_focus(); +}; + + +const int *keysoct1=keysoct1qwerty; +const int *keysoct2=keysoct2qwerty; + +if (config.cfg.VirKeybLayout==2) { + keysoct1=keysoct1dw; + keysoct2=keysoct2dw; +}else if (config.cfg.VirKeybLayout==3) { + keysoct1=keysoct1qwertz; + keysoct2=keysoct2qwertz; +}else if (config.cfg.VirKeybLayout==4) { + keysoct1=keysoct1az; + keysoct2=keysoct2az; +}; + +if ((event==FL_KEYDOWN)||(event==FL_KEYUP)){ + int key=Fl::event_key(); + int kpos=-1; + for (i=0;keysoct1[i]!=0;i++) if (key==keysoct1[i]) kpos=i+12*keyoct1; + for (i=0;keysoct2[i]!=0;i++) if (key==keysoct2[i]) kpos=i+12*keyoct2; + + + + + if (kpos==-1) return(0); + if ((event==FL_KEYUP) && (Fl::event_key(key)==0) && (Fl::get_key(key)!=0)) return(0); + if (event==FL_KEYDOWN) presskey(kpos,0,2); + else relasekey(kpos,2); +}; + +return(1);} {} + } + Function {presskey(int nk,int exclusive,int type)} {} { + code {//Exclusive means that multiple keys can be pressed at once +//when the user uses the shift key +if (nk>=N_OCT*12) return; +if ((nk<0)&&(exclusive==0)) { + relaseallkeys(type); + return; +}; +if (nk<0) return; +if (pressed[nk]!=0) return;//the key is already pressed + +if (exclusive!=0) relaseallkeys(type); +pressed[nk]=type; + +damage(1); +float vel=midivel; +if (rndvelocity!=0){ + vel=midivel*(127.0-rndvelocity)/127.0+RND*rndvelocity; +}; + +pthread_mutex_lock(&master->mutex); +master->noteOn(midich,nk+midioct*12,(int)vel); +pthread_mutex_unlock(&master->mutex);} {} + } + Function {relasekey(int nk,int type)} {} { + code {if ((nk<0)||(nk>=N_OCT*12)) return; +if (pressed[nk]==0) return;//the key is not pressed +if ((type!=0)&&(pressed[nk]!=type)) return; + +pressed[nk]=0; + + +damage(1); + +pthread_mutex_lock(&master->mutex); +master->noteOff(midich,nk+12*midioct); +pthread_mutex_unlock(&master->mutex);} {} + } + Function {relaseallkeys(int type)} {} { + code {for (int i=0;ihide();} open + xywh {100 597 650 130} type Double visible + } { + Fl_Box virkeys { + label Keyboard + xywh {10 10 590 80} box FLAT_BOX color 17 + code0 {o->init(master);} + class VirKeys + } + Fl_Counter {} { + label {"qwer.." Oct} + callback {relaseallkeys(); +virkeys->keyoct1=(int) o->value(); +virkeys->take_focus();} + tooltip {keys "q2w3er5t6y..." octave} xywh {380 95 45 15} type Simple labelsize 10 align 4 when 6 minimum 0 maximum 5 step 1 textfont 1 textsize 10 + code0 {o->value(virkeys->keyoct1);} + } + Fl_Counter {} { + label {"zxcv.." Oct} + callback {relaseallkeys(); +virkeys->keyoct2=(int) o->value(); +virkeys->take_focus();} + tooltip {keys "zsxdcvgbh..." octave} xywh {380 110 45 15} type Simple labelsize 10 align 4 when 6 minimum 0 maximum 5 step 1 textfont 1 textsize 10 + code0 {o->value(virkeys->keyoct2);} + } + Fl_Value_Slider {} { + label Vel + callback {virkeys->midivel=(int) o->value(); +virkeys->take_focus();} selected + tooltip Velocity xywh {95 105 100 15} type {Horz Knob} box NO_BOX labelsize 10 align 5 minimum 1 maximum 127 step 1 + code0 {o->value(virkeys->midivel);} + } + Fl_Counter {} { + label {Oct.} + callback {relaseallkeys(); +virkeys->midioct=(int) o->value(); +virkeys->take_focus();} + tooltip {Midi Octave} xywh {255 100 55 20} type Simple labelsize 11 align 4 when 6 minimum 0 maximum 5 step 1 textfont 1 textsize 11 + code0 {o->value(virkeys->midioct);} + } + Fl_Button {} { + label Close + callback {relaseallkeys(); +virkeyboardwindow->hide();} + xywh {545 105 55 20} box THIN_UP_BOX + } + Fl_Value_Slider {} { + label Cval + callback {int ctl=midictl; + +pthread_mutex_lock(&master->mutex); +master->setController(virkeys->midich,ctl,(int) o->value()); +pthread_mutex_unlock(&master->mutex); +virkeys->take_focus();} + tooltip {Controller value} xywh {605 10 15 115} type {Vert Fill} box ENGRAVED_BOX selection_color 229 labelsize 8 align 5 minimum 127 maximum 0 step 1 value 64 textsize 7 + } + Fl_Choice {} { + label Controller + callback {switch((int) o->value()+1){ + case 1: midictl=C_modwheel; break; + case 2: midictl=C_volume; break; + case 3: midictl=C_panning; break; + case 4: midictl=C_expression; break; + case 5: midictl=C_sustain; break; + case 6: midictl=C_portamento; break; + case 7: midictl=C_filterq; break; + case 8: midictl=C_filtercutoff; break; + case 9: midictl=C_bandwidth; break; + case 10: midictl=C_fmamp; break; + case 11: midictl=C_resonance_center; break; + case 12: midictl=C_resonance_bandwidth; break; + default: midictl=C_NULL; break; + +}; + + + +virkeys->take_focus();} + xywh {435 105 100 15} down_box BORDER_BOX labelsize 10 align 5 when 6 textfont 1 textsize 10 + code0 {midictl=C_filtercutoff;o->value(7);} + } { + MenuItem {} { + label {01: Mod.Wheel} + xywh {0 0 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {07: Volume} + xywh {10 10 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {10: Panning} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {11: Expression} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {64: Sustain} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {65: Portamento} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {71: Filter Q} + xywh {60 60 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {74: Filter Freq.} + xywh {70 70 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {75: Bandwidth} + xywh {80 80 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {76: FM Gain} + xywh {90 90 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {77: Res. c. freq} + xywh {100 100 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {78: Res. bw.} + xywh {110 110 100 20} labelfont 1 labelsize 10 + } + } + Fl_Roller pitchwheelroller { + label Pwh + callback {pthread_mutex_lock(&master->mutex); +master->setController(virkeys->midich,C_pitchwheel,-(int) o->value()); +pthread_mutex_unlock(&master->mutex); +virkeys->take_focus();} + tooltip {Pitch Wheel} xywh {625 10 20 95} labelsize 8 align 1 when 3 minimum -8192 maximum 8192 step 64 + } + Fl_Button {} { + label R + callback {pitchwheelroller->value(0); +pitchwheelroller->do_callback();} + tooltip {Reset Pitch Bend} xywh {625 110 20 15} box THIN_UP_BOX labelfont 1 + } + Fl_Dial {} { + label Vrnd + callback {virkeys->rndvelocity=(int) o->value();} + tooltip {Velocity Randomness} xywh {205 105 20 20} box ROUND_UP_BOX labelsize 10 align 129 maximum 127 step 1 + code0 {o->value(virkeys->rndvelocity);} + class WidgetPDial + } + Fl_Choice partrcv { + label {MIDI Ch.} + callback {relaseallkeys(); +virkeys->midich=(int) o->value(); +virkeys->take_focus();} open + tooltip {Send to Midi Channel} xywh {20 105 65 20} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 + code0 {char nrstr[10]; for(int i=0;iadd(nrstr); else o->add("Drum10");};} + code1 {o->value(virkeys->midich);} + } {} + } + } + Function {VirKeyboard(Master *master_)} {} { + code {master=master_; +midictl=75; +make_window();} {} + } + Function {~VirKeyboard()} {} { + code {delete virkeyboardwindow;} {} + } + Function {show()} {} { + code {virkeyboardwindow->show();} {} + } + Function {relaseallkeys()} {} { + code {virkeys->relaseallkeys(0);} {} + } + decl {Master *master;} {} + decl {int midictl;} {} +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/WidgetPDial.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/UI/WidgetPDial.cpp new file mode 100644 index 000000000..b4f86c4d6 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/WidgetPDial.cpp @@ -0,0 +1,283 @@ +// generated by Fast Light User Interface Designer (fluid) version 1.0107f + +#include "WidgetPDial.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "../Misc/Util.h" +//Copyright (c) 2003-2005 Nasca Octavian Paul +//License: GNU GPL version 2 or later + +using namespace std; + +class TipWin:public Fl_Menu_Window +{ + public: + TipWin(); + void draw(); + void showValue(float f); + void setText(const char *c); + void showText(); + private: + void redraw(); + const char *getStr() const; + string tip; + string text; + bool textmode; +}; + +TipWin::TipWin():Fl_Menu_Window(1, 1) +{ + set_override(); + end(); +} + +void TipWin::draw() +{ + //setup window + draw_box(FL_BORDER_BOX, 0, 0, w(), h(), Fl_Color(175)); + fl_color(Fl_Tooltip::textcolor()); + fl_font(labelfont(), labelsize()); + + //Draw the current string + fl_draw(getStr(), 3, 3, w() - 6, h() - 6, + Fl_Align(FL_ALIGN_LEFT | FL_ALIGN_WRAP)); +} + +void TipWin::showValue(float f) +{ + //convert the value to a string + char tmp[10]; + snprintf(tmp, 9, "%.2f", f); + tip = tmp; + + textmode = false; + redraw(); + show(); +} + +void TipWin::setText(const char *c) +{ + text = c; + textmode = true; + redraw(); +} + +void TipWin::showText() +{ + if(!text.empty()) { + textmode = true; + redraw(); + show(); + } +} + +void TipWin::redraw() +{ + // Recalc size of window + fl_font(labelfont(), labelsize()); + int W = 0, H = 0; + fl_measure(getStr(), W, H, 0); + //provide a bit of extra space + W += 8; + H += 4; + size(W, H); + Fl_Menu_Window::redraw(); +} + +const char *TipWin::getStr() const +{ + return (textmode ? text : tip).c_str(); +} + +//static int numobj = 0; + +WidgetPDial::WidgetPDial(int x, int y, int w, int h, const char *label) + :Fl_Dial(x, y, w, h, label), oldvalue(0.0f), pos(false), textset(false) +{ + //cout << "[" << label << "] There are now " << ++numobj << endl; + Fl_Group *save = Fl_Group::current(); + tipwin = new TipWin(); + tipwin->hide(); + Fl_Group::current(save); +} + +WidgetPDial::~WidgetPDial() +{ + //cout << "There are now " << --numobj << endl; + delete tipwin; +} + +int WidgetPDial::handle(int event) +{ +//#ifdef NTK_GUI +// return Fl_Dial::handle( event ); +//#else + double dragsize, min = minimum(), max = maximum(); + int my; + + switch(event) { + case FL_PUSH: + oldvalue = value(); + case FL_DRAG: + getPos(); + my = -(Fl::event_y() - y() - h() / 2); + + dragsize = 200.0f; + if(Fl::event_state(FL_BUTTON1) == 0) + dragsize *= 10; + + value(limit(oldvalue + my / dragsize * (max - min), min, max)); + tipwin->showValue(value()); + value_damage(); + if(this->when() != 0) + do_callback(); + return 1; + case FL_MOUSEWHEEL: + if (Fl::belowmouse() != this) + return 1; + my = - Fl::event_dy(); + + dragsize = 200.0f; + if(Fl::event_state(FL_CTRL) != 0) + dragsize *= 10; + + value(limit(value() + my / dragsize * (max - min), min, max)); + tipwin->showValue(value()); + value_damage(); + if(this->when() != 0) + do_callback(); + return 1; + case FL_ENTER: + getPos(); + tipwin->showText(); + return 1; + case FL_HIDE: + case FL_LEAVE: + tipwin->hide(); + resetPos(); + break; + case FL_RELEASE: + tipwin->hide(); + resetPos(); + if(this->when() == 0) + do_callback(); + return 1; + break; + } + return 0; +//#endif +} + +void WidgetPDial::drawgradient(int cx, int cy, int sx, double m1, double m2) +{ +#ifdef NTK_GUI + return; +#else + for(int i = (int)(m1 * sx); i < (int)(m2 * sx); i++) { + double tmp = 1.0f - powf(i * 1.0f / sx, 2.0f); + pdialcolor(140 + + (int) (tmp + * 90), 140 + + (int)(tmp * 90), 140 + (int) (tmp * 100)); + fl_arc(cx + sx / 2 - i / 2, cy + sx / 2 - i / 2, i, i, 0, 360 ); + } +#endif +} + +void WidgetPDial::draw() +{ +#ifdef NTK_GUI + box( FL_NO_BOX ); + + Fl_Dial::draw(); + + return; +#else + int cx = x(), cy = y(), sx = w(), sy = h(); + + //clears the button face + pdialcolor(190, 190, 200); + fl_pie(cx - 1, cy - 1, sx + 2, sy + 2, 0, 360); + + /* //Draws the button face (gradinet) */ + drawgradient(cx, cy, sx, 0.5f, 1.0f); + + double val = (value() - minimum()) / (maximum() - minimum()); + + //draws the scale + pdialcolor(220, 220, 250); + double a1 = angle1(), a2 = angle2(); + for(int i = 0; i < 12; i++) { + double a = -i / 12.0f * 360.0f - val * (a2 - a1) - a1; + fl_pie(cx, cy, sx, sy, a + 270 - 3, a + 3 + 270); + } + + drawgradient(cx, cy, sx, 0.0f, 0.75f); + + + + //draws the value + double a = -(a2 - a1) * val - a1; + + //draws the max and min points + pdialcolor(0, 100, 200); + int xp = + (int)(cx + sx / 2.0f + sx / 2.0f * sinf(angle1() / 180.0f * 3.141592f)); + int yp = + (int)(cy + sy / 2.0f + sy / 2.0f * cosf(angle1() / 180.0f * 3.141592f)); + fl_pie(xp - 2, yp - 2, 4, 4, 0, 360); + + xp = (int)(cx + sx / 2.0f + sx / 2.0f * sinf(angle2() / 180.0f * 3.141592f)); + yp = (int)(cy + sy / 2.0f + sy / 2.0f * cosf(angle2() / 180.0f * 3.141592f)); + fl_pie(xp - 2, yp - 2, 4, 4, 0, 360); + + fl_push_matrix(); + + fl_translate(cx + sx / 2, cy + sy / 2); + fl_rotate(a - 90.0f); + + fl_translate(sx / 2, 0); + + fl_begin_polygon(); + pdialcolor(0, 0, 0); + fl_vertex(-10, -4); + fl_vertex(-10, 4); + fl_vertex(0, 0); + fl_end_polygon(); + + fl_pop_matrix(); +#endif +} + +void WidgetPDial::pdialcolor(int r, int g, int b) +{ + if(active_r()) + fl_color(r, g, b); + else + fl_color(160 - (160 - r) / 3, 160 - (160 - b) / 3, 160 - (160 - b) / 3); +} + +void WidgetPDial::tooltip(const char *c) +{ + tipwin->setText(c); + textset = true; +} + +void WidgetPDial::getPos() +{ + if(!pos) { + tipwin->position(Fl::event_x_root(), Fl::event_y_root() + 20); + pos = true; + } +} + +void WidgetPDial::resetPos() +{ + pos = false; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/WidgetPDial.h b/plugins/zynaddsubfx/zynaddsubfx/src/UI/WidgetPDial.h new file mode 100644 index 000000000..3f8cd711c --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/WidgetPDial.h @@ -0,0 +1,26 @@ +// generated by Fast Light User Interface Designer (fluid) version 1.0107f + +#ifndef WIDGETPDIAL_h +#define WIDGETPDIAL_h +#include + + +class WidgetPDial:public Fl_Dial +{ + public: + WidgetPDial(int x, int y, int w, int h, const char *label = 0); + ~WidgetPDial(); + int handle(int event); + void drawgradient(int cx, int cy, int sx, double m1, double m2); + void draw(); + void pdialcolor(int r, int g, int b); + void tooltip(const char *c); + private: + void getPos(); + void resetPos(); + double oldvalue; + bool pos; + bool textset; + class TipWin * tipwin; +}; +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/UI/common.H b/plugins/zynaddsubfx/zynaddsubfx/src/UI/common.H new file mode 100644 index 000000000..4a077e32b --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/UI/common.H @@ -0,0 +1,29 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +class Fl_Widget; +extern void set_module_parameters ( Fl_Widget * ); + +#ifdef FLTK_GUI +#define fl_color_add_alpha( x,y ) x +#undef FL_NO_BOX +#undef FL_UP_FRAME +#define FL_NO_BOX FL_FLAT_BOX +#define FL_UP_FRAME FL_UP_BOX +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/globals.h b/plugins/zynaddsubfx/zynaddsubfx/src/globals.h new file mode 100644 index 000000000..d98e8babd --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/globals.h @@ -0,0 +1,249 @@ +/* + ZynAddSubFX - a software synthesizer + + globals.h - it contains program settings and the program capabilities + like number of parts, of effects + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + + +#ifndef GLOBALS_H +#define GLOBALS_H +#include + + +/** + * The number of harmonics of additive synth + * This must be smaller than OSCIL_SIZE/2 + */ +#define MAX_AD_HARMONICS 128 + + +/** + * The number of harmonics of substractive + */ +#define MAX_SUB_HARMONICS 64 + + +/* + * The maximum number of samples that are used for 1 PADsynth instrument(or item) + */ +#define PAD_MAX_SAMPLES 64 + + +/* + * Number of parts + */ +#define NUM_MIDI_PARTS 16 + +/* + * Number of Midi channes + */ +#define NUM_MIDI_CHANNELS 16 + +/* + * The number of voices of additive synth for a single note + */ +#define NUM_VOICES 8 + +/* + * The poliphony (notes) + */ +#define POLIPHONY 128 + +/* + * Number of system effects + */ +#define NUM_SYS_EFX 4 + + +/* + * Number of insertion effects + */ +#define NUM_INS_EFX 8 + +/* + * Number of part's insertion effects + */ +#define NUM_PART_EFX 3 + +/* + * Maximum number of the instrument on a part + */ +#define NUM_KIT_ITEMS 16 + + +/* + * How is applied the velocity sensing + */ +#define VELOCITY_MAX_SCALE 8.0f + +/* + * The maximum length of instrument's name + */ +#define PART_MAX_NAME_LEN 30 + +/* + * The maximum number of bands of the equaliser + */ +#define MAX_EQ_BANDS 8 +#if (MAX_EQ_BANDS >= 20) +#error "Too many EQ bands in globals.h" +#endif + + +/* + * Maximum filter stages + */ +#define MAX_FILTER_STAGES 5 + +/* + * Formant filter (FF) limits + */ +#define FF_MAX_VOWELS 6 +#define FF_MAX_FORMANTS 12 +#define FF_MAX_SEQUENCE 8 + +#define LOG_2 0.693147181f +#define PI 3.1415926536f +#define LOG_10 2.302585093f + +/* + * The threshold for the amplitude interpolation used if the amplitude + * is changed (by LFO's or Envelope's). If the change of the amplitude + * is below this, the amplitude is not interpolated + */ +#define AMPLITUDE_INTERPOLATION_THRESHOLD 0.0001f + +/* + * How the amplitude threshold is computed + */ +#define ABOVE_AMPLITUDE_THRESHOLD(a, b) ((2.0f * fabs((b) - (a)) \ + / (fabs((b) + (a) \ + + 0.0000000001f))) > \ + AMPLITUDE_INTERPOLATION_THRESHOLD) + +/* + * Interpolate Amplitude + */ +#define INTERPOLATE_AMPLITUDE(a, b, x, size) ((a) \ + + ((b) \ + - (a)) * (float)(x) \ + / (float) (size)) + + +/* + * dB + */ +#define dB2rap(dB) ((expf((dB) * LOG_10 / 20.0f))) +#define rap2dB(rap) ((20 * logf(rap) / LOG_10)) + +#define ZERO(data, size) {char *data_ = (char *) data; for(int i = 0; \ + i < size; \ + i++) \ + data_[i] = 0; } +#define ZERO_float(data, size) {float *data_ = (float *) data; \ + for(int i = 0; \ + i < size; \ + i++) \ + data_[i] = 0.0f; } + +enum ONOFFTYPE { + OFF = 0, ON = 1 +}; + +enum MidiControllers { + C_bankselectmsb = 0, C_pitchwheel = 1000, C_NULL = 1001, + C_expression = 11, C_panning = 10, C_bankselectlsb = 32, + C_filtercutoff = 74, C_filterq = 71, C_bandwidth = 75, C_modwheel = 1, + C_fmamp = 76, + C_volume = 7, C_sustain = 64, C_allnotesoff = 123, C_allsoundsoff = 120, + C_resetallcontrollers = 121, + C_portamento = 65, C_resonance_center = 77, C_resonance_bandwidth = 78, + + C_dataentryhi = 0x06, C_dataentrylo = 0x26, C_nrpnhi = 99, C_nrpnlo = 98 +}; + +enum LegatoMsg { + LM_Norm, LM_FadeIn, LM_FadeOut, LM_CatchUp, LM_ToNorm +}; + +//is like i=(int)(floor(f)) +#ifdef ASM_F2I_YES +#define F2I(f, \ + i) __asm__ __volatile__ ("fistpl %0" : "=m" (i) : "t" (f \ + - \ + 0.49999999f) \ + : "st"); +#else +#define F2I(f, i) (i) = ((f > 0) ? ((int)(f)) : ((int)(f - 1.0f))); +#endif + + + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +//temporary include for synth->{samplerate/buffersize} members +struct SYNTH_T { + SYNTH_T(void) + :samplerate(44100), buffersize(256), oscilsize(1024) + { + alias(); + } + + /**Sampling rate*/ + unsigned int samplerate; + + /** + * The size of a sound buffer (or the granularity) + * All internal transfer of sound data use buffer of this size + * All parameters are constant during this period of time, exception + * some parameters(like amplitudes) which are linear interpolated. + * If you increase this you'll ecounter big latencies, but if you + * decrease this the CPU requirements gets high. + */ + int buffersize; + + /** + * The size of ADnote Oscillator + * Decrease this => poor quality + * Increase this => CPU requirements gets high (only at start of the note) + */ + int oscilsize; + + //Alias for above terms + float samplerate_f; + float halfsamplerate_f; + float buffersize_f; + int bufferbytes; + float oscilsize_f; + + inline void alias(void) + { + halfsamplerate_f = (samplerate_f = samplerate) / 2.0f; + buffersize_f = buffersize; + bufferbytes = buffersize * sizeof(float); + oscilsize_f = oscilsize; + } + static float numRandom(void); //defined in Util.cpp for now +}; + +extern SYNTH_T *synth; +#endif diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/main.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/main.cpp new file mode 100644 index 000000000..d0adfd938 --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/src/main.cpp @@ -0,0 +1,551 @@ +/* + ZynAddSubFX - a software synthesizer + + main.cpp - Main file of the synthesizer + Copyright (C) 2002-2005 Nasca Octavian Paul + Author: Nasca Octavian Paul + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include + +#include "UI/common.H" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "DSP/FFTwrapper.h" +#include "Misc/Master.h" +#include "Misc/Part.h" +#include "Misc/Util.h" +#include "Misc/Dump.h" +extern Dump dump; + + +//Nio System +#include "Nio/Nio.h" + +#ifndef DISABLE_GUI +#ifdef QT_GUI + +#include +#include "masterui.h" +QApplication *app; + +#elif defined FLTK_GUI +#include "UI/MasterUI.h" +#elif defined NTK_GUI +#include "UI/MasterUI.h" +#include +#include +#include +#include +#endif // FLTK_GUI + +MasterUI *ui; + +#endif //DISABLE_GUI + +using namespace std; + +pthread_t thr4; +Master *master; +SYNTH_T *synth; +int swaplr = 0; //1 for left-right swapping + +int Pexitprogram = 0; //if the UI set this to 1, the program will exit + +#if LASH +#include "Misc/LASHClient.h" +LASHClient *lash = NULL; +#endif + +#if USE_NSM +#include "UI/NSM.H" + +NSM_Client *nsm = 0; +#endif + +char *instance_name = 0; + +void exitprogram(); + +//cleanup on signaled exit +void sigterm_exit(int /*sig*/) +{ + Pexitprogram = 1; +} + + +#ifndef DISABLE_GUI + +#ifdef NTK_GUI +static Fl_Tiled_Image *module_backdrop; +#endif + +void +set_module_parameters ( Fl_Widget *o ) +{ +#ifdef NTK_GUI + o->box( FL_DOWN_FRAME ); + o->align( o->align() | FL_ALIGN_IMAGE_BACKDROP ); + o->color( FL_BLACK ); + o->image( module_backdrop ); + o->labeltype( FL_SHADOW_LABEL ); +#else + o->box( FL_PLASTIC_UP_BOX ); + o->color( FL_CYAN ); + o->labeltype( FL_EMBOSSED_LABEL ); +#endif +} +#endif + +/* + * Program initialisation + */ +void initprogram(void) +{ + cerr.precision(1); + cerr << std::fixed; + cerr << "\nSample Rate = \t\t" << synth->samplerate << endl; + cerr << "Sound Buffer Size = \t" << synth->buffersize << " samples" << endl; + cerr << "Internal latency = \t" << synth->buffersize_f * 1000.0f + / synth->samplerate_f << " ms" << endl; + cerr << "ADsynth Oscil.Size = \t" << synth->oscilsize << " samples" << endl; + + + master = &Master::getInstance(); + master->swaplr = swaplr; + + signal(SIGINT, sigterm_exit); + signal(SIGTERM, sigterm_exit); +} + +/* + * Program exit + */ +void exitprogram() +{ + //ensure that everything has stopped with the mutex wait + pthread_mutex_lock(&master->mutex); + pthread_mutex_unlock(&master->mutex); + + Nio::stop(); + +#ifndef DISABLE_GUI + delete ui; +#endif +#if LASH + if(lash) + delete lash; +#endif +#if USE_NSM + if(nsm) + delete nsm; +#endif + + delete [] denormalkillbuf; + FFT_cleanup(); + Master::deleteInstance(); +} + +int main(int argc, char *argv[]) +{ + synth = new SYNTH_T; + config.init(); + dump.startnow(); + int noui = 0; + cerr + << "\nZynAddSubFX - Copyright (c) 2002-2011 Nasca Octavian Paul and others" + << endl; + cerr << "Compiled: " << __DATE__ << " " << __TIME__ << endl; + cerr << "This program is free software (GNU GPL v.2 or later) and \n"; + cerr << "it comes with ABSOLUTELY NO WARRANTY.\n" << endl; + if(argc == 1) + cerr << "Try 'zynaddsubfx --help' for command-line options." << endl; + + /* Get the settings from the Config*/ + synth->samplerate = config.cfg.SampleRate; + synth->buffersize = config.cfg.SoundBufferSize; + synth->oscilsize = config.cfg.OscilSize; + swaplr = config.cfg.SwapStereo; + + Nio::preferedSampleRate(synth->samplerate); + + synth->alias(); //build aliases + + sprng(time(NULL)); + + /* Parse command-line options */ + struct option opts[] = { + { + "load", 2, NULL, 'l' + }, + { + "load-instrument", 2, NULL, 'L' + }, + { + "sample-rate", 2, NULL, 'r' + }, + { + "buffer-size", 2, NULL, 'b' + }, + { + "oscil-size", 2, NULL, 'o' + }, + { + "dump", 2, NULL, 'D' + }, + { + "swap", 2, NULL, 'S' + }, + { + "no-gui", 2, NULL, 'U' + }, + { + "dummy", 2, NULL, 'Y' + }, + { + "help", 2, NULL, 'h' + }, + { + "version", 2, NULL, 'v' + }, + { + "named", 1, NULL, 'N' + }, + { + "auto-connect", 0, NULL, 'a' + }, + { + "output", 1, NULL, 'O' + }, + { + "input", 1, NULL, 'I' + }, + { + "exec-after-init", 1, NULL, 'e' + }, + { + 0, 0, 0, 0 + } + }; + opterr = 0; + int option_index = 0, opt, exitwithhelp = 0, exitwithversion = 0; + + string loadfile, loadinstrument, execAfterInit; + + while(1) { + int tmp = 0; + + /**\todo check this process for a small memory leak*/ + opt = getopt_long(argc, + argv, + "l:L:r:b:o:I:O:N:e:hvaSDUY", + opts, + &option_index); + char *optarguments = optarg; + +#define GETOP(x) if(optarguments) \ + x = optarguments +#define GETOPNUM(x) if(optarguments) \ + x = atoi(optarguments) + + + if(opt == -1) + break; + + switch(opt) { + case 'h': + exitwithhelp = 1; + break; + case 'v': + exitwithversion = 1; + break; + case 'Y': /* this command a dummy command (has NO effect) + and is used because I need for NSIS installer + (NSIS sometimes forces a command line for a + program, even if I don't need that; eg. when + I want to add a icon to a shortcut. + */ + break; + case 'U': + noui = 1; + break; + case 'l': + GETOP(loadfile); + break; + case 'L': + GETOP(loadinstrument); + break; + case 'r': + GETOPNUM(synth->samplerate); + if(synth->samplerate < 4000) { + cerr << "ERROR:Incorrect sample rate: " << optarguments + << endl; + exit(1); + } + break; + case 'b': + GETOPNUM(synth->buffersize); + if(synth->buffersize < 2) { + cerr << "ERROR:Incorrect buffer size: " << optarguments + << endl; + exit(1); + } + break; + case 'o': + if(optarguments) + synth->oscilsize = tmp = atoi(optarguments); + if(synth->oscilsize < MAX_AD_HARMONICS * 2) + synth->oscilsize = MAX_AD_HARMONICS * 2; + synth->oscilsize = + (int) powf(2, + ceil(logf(synth->oscilsize - 1.0f) / logf(2.0f))); + if(tmp != synth->oscilsize) + cerr + << + "synth->oscilsize is wrong (must be 2^n) or too small. Adjusting to " + << synth->oscilsize << "." << endl; + break; + case 'S': + swaplr = 1; + break; + case 'D': + dump.startnow(); + break; + case 'N': + Nio::setPostfix(optarguments); + break; + case 'I': + if(optarguments) + Nio::setDefaultSource(optarguments); + break; + case 'O': + if(optarguments) + Nio::setDefaultSink(optarguments); + break; + case 'a': + Nio::autoConnect = true; + break; + case 'e': + GETOP(execAfterInit); + break; + case '?': + cerr << "ERROR:Bad option or parameter.\n" << endl; + exitwithhelp = 1; + break; + } + } + + synth->alias(); + + if(exitwithversion) { + cout << "Version: " << VERSION << endl; + return 0; + } + if(exitwithhelp != 0) { + cout << "Usage: zynaddsubfx [OPTION]\n\n" + << " -h , --help \t\t\t\t Display command-line help and exit\n" + << " -v , --version \t\t\t Display version and exit\n" + << " -l file, --load=FILE\t\t\t Loads a .xmz file\n" + << " -L file, --load-instrument=FILE\t Loads a .xiz file\n" + << " -r SR, --sample-rate=SR\t\t Set the sample rate SR\n" + << + " -b BS, --buffer-size=SR\t\t Set the buffer size (granularity)\n" + << " -o OS, --oscil-size=OS\t\t Set the ADsynth oscil. size\n" + << " -S , --swap\t\t\t\t Swap Left <--> Right\n" + << " -D , --dump\t\t\t\t Dumps midi note ON/OFF commands\n" + << + " -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n" + << " -N , --named\t\t\t\t Postfix IO Name when possible\n" + << " -a , --auto-connect\t\t\t AutoConnect when using JACK\n" + << " -O , --output\t\t\t\t Set Output Engine\n" + << " -I , --input\t\t\t\t Set Input Engine\n" + << " -e , --exec-after-init\t\t Run post-initialization script\n" + << endl; + + return 0; + } + + //produce denormal buf + denormalkillbuf = new float [synth->buffersize]; + for(int i = 0; i < synth->buffersize; ++i) + denormalkillbuf[i] = (RND - 0.5f) * 1e-16; + + initprogram(); + + if(!loadfile.empty()) { + int tmp = master->loadXML(loadfile.c_str()); + if(tmp < 0) { + cerr << "ERROR: Could not load master file " << loadfile + << "." << endl; + exit(1); + } + else { + master->applyparameters(); + cout << "Master file loaded." << endl; + } + } + + if(!loadinstrument.empty()) { + int loadtopart = 0; + int tmp = master->part[loadtopart]->loadXMLinstrument( + loadinstrument.c_str()); + if(tmp < 0) { + cerr << "ERROR: Could not load instrument file " + << loadinstrument << '.' << endl; + exit(1); + } + else { + master->part[loadtopart]->applyparameters(); + cout << "Instrument file loaded." << endl; + } + } + + //Run the Nio system + bool ioGood = Nio::start(); + + if(!execAfterInit.empty()) { + cout << "Executing user supplied command: " << execAfterInit << endl; + if(system(execAfterInit.c_str()) == -1) + cerr << "Command Failed..." << endl; + } + + +#ifndef DISABLE_GUI + +#ifdef NTK_GUI + fl_register_images(); + + Fl_Tooltip::textcolor(0x0); + + Fl_Dial::default_style(Fl_Dial::PIXMAP_DIAL); + + if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/knob.png")) + Fl_Dial::default_image(img); + else + Fl_Dial::default_image(Fl_Shared_Image::get(SOURCE_DIR "/../pixmaps/knob.png")); + + if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/window_backdrop.png")) + Fl::scheme_bg(new Fl_Tiled_Image(img)); + else + Fl::scheme_bg(new Fl_Tiled_Image(Fl_Shared_Image::get(SOURCE_DIR "/../pixmaps/window_backdrop.png"))); + + if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/module_backdrop.png")) + module_backdrop = new Fl_Tiled_Image(img); + else + module_backdrop = new Fl_Tiled_Image(Fl_Shared_Image::get(SOURCE_DIR "/../pixmaps/module_backdrop.png")); + + Fl::background( 50, 50, 50 ); + Fl::background2( 70, 70, 70 ); + Fl::foreground( 255,255,255 ); +#endif + + ui = new MasterUI(master, &Pexitprogram); + + if ( !noui) + { + ui->showUI(); + + if(!ioGood) + fl_alert( + "Default IO did not initialize.\nDefaulting to NULL backend."); + } + +#endif + +#ifndef DISABLE_GUI +#if USE_NSM + char *nsm_url = getenv("NSM_URL"); + + if(nsm_url) { + nsm = new NSM_Client; + + if(!nsm->init(nsm_url)) + nsm->announce("ZynAddSubFX", ":switch:", argv[0]); + else { + delete nsm; + nsm = NULL; + } + } +#endif +#endif + +#if USE_NSM + if(!nsm) +#endif + { +#if LASH + lash = new LASHClient(&argc, &argv); +#ifndef DISABLE_GUI + ui->sm_indicator1->value(1); + ui->sm_indicator2->value(1); + ui->sm_indicator1->tooltip("LASH"); + ui->sm_indicator2->tooltip("LASH"); +#endif +#endif + } + + while(Pexitprogram == 0) { +#ifndef DISABLE_GUI +#if USE_NSM + if(nsm) { + nsm->check(); + goto done; + } +#endif +#if LASH + { + string filename; + switch(lash->checkevents(filename)) { + case LASHClient::Save: + ui->do_save_master(filename.c_str()); + lash->confirmevent(LASHClient::Save); + break; + case LASHClient::Restore: + ui->do_load_master(filename.c_str()); + lash->confirmevent(LASHClient::Restore); + break; + case LASHClient::Quit: + Pexitprogram = 1; + default: + break; + } + } +#endif //LASH + +#if USE_NSM +done: +#endif + + Fl::wait(0.02f); +#else + usleep(100000); +#endif + } + + exitprogram(); + return 0; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/style.cfg b/plugins/zynaddsubfx/zynaddsubfx/style.cfg new file mode 100644 index 000000000..ba66d990d --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/style.cfg @@ -0,0 +1,1218 @@ +# Uncrustify 0.53 + +# +# General options +# + +# The type of line endings +newlines = lf # auto/lf/crlf/cr + +# The original size of tabs in the input +input_tab_size = 4 # number + +# The size of tabs in the output (only used if align_with_tabs=true) +output_tab_size = 4 # number + +# The ascii value of the string escape char, usually 92 (\) or 94 (^). (Pawn) +string_escape_char = 92 # number + +# Alternate string escape char for Pawn. Only works right before the quote char. +string_escape_char2 = 0 # number + +# +# Indenting +# + +# The number of columns to indent per level. +# Usually 2, 3, 4, or 8. +indent_columns = 4 # number + +# How to use tabs when indenting code +# 0=spaces only +# 1=indent with tabs, align with spaces +# 2=indent and align with tabs +indent_with_tabs = 0 # number + +# Whether to indent strings broken by '\' so that they line up +indent_align_string = true # false/true + +# The number of spaces to indent multi-line XML strings. +# Requires indent_align_string=True +indent_xml_string = 0 # number + +# Spaces to indent '{' from level +indent_brace = 0 # number + +# Whether braces are indented to the body level +indent_braces = false # false/true + +# Disabled indenting function braces if indent_braces is true +indent_braces_no_func = false # false/true + +# Indent based on the size of the brace parent, ie 'if' => 3 spaces, 'for' => 4 spaces, etc. +indent_brace_parent = false # false/true + +# Whether the 'namespace' body is indented +indent_namespace = true # false/true + +# Whether the 'extern "C"' body is indented +indent_extern = true # false/true + +# Whether the 'class' body is indented +indent_class = true # false/true + +# Whether to indent the stuff after a leading class colon +indent_class_colon = true # false/true + +# False=treat 'else\nif' as 'else if' for indenting purposes +# True=indent the 'if' one level +indent_else_if = false # false/true + +# Amount to indent variable declarations after a open brace. neg=relative, pos=absolute +indent_var_def_blk = 0 # number + +# True: indent continued function call parameters one indent level +# False: align parameters under the open paren +indent_func_call_param = false # false/true + +# Same as indent_func_call_param, but for function defs +indent_func_def_param = false # false/true + +# Same as indent_func_call_param, but for function protos +indent_func_proto_param = false # false/true + +# Same as indent_func_call_param, but for class declarations +indent_func_class_param = false # false/true + +# Same as indent_func_call_param, but for class variable constructors +indent_func_ctor_var_param = false # false/true + +# Same as indent_func_call_param, but for templates +indent_template_param = false # false/true + +# Double the indent for indent_func_xxx_param options +indent_func_param_double = false # false/true + +# Indentation column for standalone 'const' function decl/proto qualifier +indent_func_const = 0 # number + +# Indentation column for standalone 'throw' function decl/proto qualifier +indent_func_throw = 0 # number + +# The number of spaces to indent a continued '->' or '.' +# Usually set to 0, 1, or indent_columns. +indent_member = 0 # number + +# Spaces to indent single line ('//') comments on lines before code +indent_sing_line_comments = 0 # number + +# If set, will indent trailing single line ('//') comments relative +# to the code instead of trying to keep the same absolute column +indent_relative_single_line_comments = false # false/true + +# Spaces to indent 'case' from 'switch' +# Usually 0 or indent_columns. +indent_switch_case = 4 # number + +# Spaces to shift the 'case' line, without affecting any other lines +# Usually 0. +indent_case_shift = 0 # number + +# Spaces to indent '{' from 'case'. +# By default, the brace will appear under the 'c' in case. +# Usually set to 0 or indent_columns. +indent_case_brace = 0 # number + +# Whether to indent comments found in first column +indent_col1_comment = false # false/true + +# How to indent goto labels +# >0 : absolute column where 1 is the leftmost column +# <=0 : subtract from brace indent +indent_label = 1 # number + +# Same as indent_label, but for access specifiers that are followed by a colon +indent_access_spec = 1 # number + +# Indent the code after an access specifier by one level. +# If set, this option forces 'indent_access_spec=0' +indent_access_spec_body = true # false/true + +# If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended) +indent_paren_nl = false # false/true + +# Controls the indent of a close paren after a newline. +# 0: Indent to body level +# 1: Align under the open paren +# 2: Indent to the brace level +indent_paren_close = 1 # number + +# Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren +indent_comma_paren = false # false/true + +# Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren +indent_bool_paren = false # false/true + +# If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended) +indent_square_nl = false # false/true + +# Don't change the relative indent of ESQL/C 'EXEC SQL' bodies +indent_preserve_sql = false # false/true + +# Align continued statements at the '='. Default=True +# If FALSE or the '=' is followed by a newline, the next line is indent one tab. +indent_align_assign = true # false/true + +# +# Spacing options +# + +# Add or remove space around arithmetic operator '+', '-', '/', '*', etc +sp_arith = force # ignore/add/remove/force + +# Add or remove space around assignment operator '=', '+=', etc +sp_assign = force # ignore/add/remove/force + +# Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign. +sp_before_assign = ignore # ignore/add/remove/force + +# Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign. +sp_after_assign = ignore # ignore/add/remove/force + +# Add or remove space around assignment '=' in enum +sp_enum_assign = force # ignore/add/remove/force + +# Add or remove space before assignment '=' in enum. Overrides sp_enum_assign. +sp_enum_before_assign = ignore # ignore/add/remove/force + +# Add or remove space after assignment '=' in enum. Overrides sp_enum_assign. +sp_enum_after_assign = ignore # ignore/add/remove/force + +# Add or remove space around preprocessor '##' concatenation operator +sp_pp_concat = add # ignore/add/remove/force + +# Add or remove space after preprocessor '#' stringify operator +sp_pp_stringify = add # ignore/add/remove/force + +# Add or remove space around boolean operators '&&' and '||' +sp_bool = force # ignore/add/remove/force + +# Add or remove space around compare operator '<', '>', '==', etc +sp_compare = force # ignore/add/remove/force + +# Add or remove space inside '(' and ')' +sp_inside_paren = remove # ignore/add/remove/force + +# Add or remove space between nested parens +sp_paren_paren = remove # ignore/add/remove/force + +# Whether to balance spaces inside nested parens +sp_balance_nested_parens = false # false/true + +# Add or remove space between ')' and '{' +sp_paren_brace = force # ignore/add/remove/force + +# Add or remove space before pointer star '*' +sp_before_ptr_star = force # ignore/add/remove/force + +# Add or remove space before pointer star '*' that isn't followed by a variable name +# If set to 'ignore', sp_before_ptr_star is used instead. +sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force + +# Add or remove space between pointer stars '*' +sp_between_ptr_star = remove # ignore/add/remove/force + +# Add or remove space after pointer star '*', if followed by a word. +sp_after_ptr_star = remove # ignore/add/remove/force + +# Add or remove space after a pointer star '*', if followed by a func proto/def. +sp_after_ptr_star_func = remove # ignore/add/remove/force + +# Add or remove space before a pointer star '*', if followed by a func proto/def. +sp_before_ptr_star_func = force # ignore/add/remove/force + +# Add or remove space before a reference sign '&' +sp_before_byref = force # ignore/add/remove/force + +# Add or remove space before a reference sign '&' that isn't followed by a variable name +# If set to 'ignore', sp_before_byref is used instead. +sp_before_unnamed_byref = ignore # ignore/add/remove/force + +# Add or remove space after reference sign '&', if followed by a word. +sp_after_byref = remove # ignore/add/remove/force + +# Add or remove space after a reference sign '&', if followed by a func proto/def. +sp_after_byref_func = ignore # ignore/add/remove/force + +# Add or remove space before a reference sign '&', if followed by a func proto/def. +sp_before_byref_func = ignore # ignore/add/remove/force + +# Add or remove space between type and word +sp_after_type = remove # ignore/add/remove/force + +# Add or remove space in 'template <' vs 'template<'. +# If set to ignore, sp_before_angle is used. +sp_template_angle = ignore # ignore/add/remove/force + +# Add or remove space before '<>' +sp_before_angle = remove # ignore/add/remove/force + +# Add or remove space inside '<' and '>' +sp_inside_angle = remove # ignore/add/remove/force + +# Add or remove space after '<>' +sp_after_angle = ignore # ignore/add/remove/force + +# Add or remove space between '<>' and '(' as found in 'new List();' +sp_angle_paren = remove # ignore/add/remove/force + +# Add or remove space between '<>' and a word as in 'List m;' +sp_angle_word = ignore # ignore/add/remove/force + +# Add or remove space before '(' of 'if', 'for', 'switch', and 'while' +sp_before_sparen = remove # ignore/add/remove/force + +# Add or remove space inside if-condition '(' and ')' +sp_inside_sparen = remove # ignore/add/remove/force + +# Add or remove space before if-condition ')'. Overrides sp_inside_sparen. +sp_inside_sparen_close = remove # ignore/add/remove/force + +# Add or remove space after ')' of 'if', 'for', 'switch', and 'while' +sp_after_sparen = remove # ignore/add/remove/force + +# Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while' +sp_sparen_brace = force # ignore/add/remove/force + +# Add or remove space between 'invariant' and '(' in the D language. +sp_invariant_paren = ignore # ignore/add/remove/force + +# Add or remove space after the ')' in 'invariant (C) c' in the D language. +sp_after_invariant_paren = ignore # ignore/add/remove/force + +# Add or remove space before empty statement ';' on 'if', 'for' and 'while' +sp_special_semi = ignore # ignore/add/remove/force + +# Add or remove space before ';' +sp_before_semi = remove # ignore/add/remove/force + +# Add or remove space before ';' in non-empty 'for' statements +sp_before_semi_for = remove # ignore/add/remove/force + +# Add or remove space before a semicolon of an empty part of a for statment. +sp_before_semi_for_empty = remove # ignore/add/remove/force + +# Add or remove space after the final semicolon of an empty part of a for statment: for ( ; ; ). +sp_after_semi_for_empty = remove # ignore/add/remove/force + +# Add or remove space before '[' (except '[]') +sp_before_square = ignore # ignore/add/remove/force + +# Add or remove space before '[]' +sp_before_squares = ignore # ignore/add/remove/force + +# Add or remove space inside '[' and ']' +sp_inside_square = remove # ignore/add/remove/force + +# Add or remove space after ',' +sp_after_comma = force # ignore/add/remove/force + +# Add or remove space before ',' +sp_before_comma = remove # ignore/add/remove/force + +# Add or remove space after class ':' +sp_after_class_colon = remove # ignore/add/remove/force + +# Add or remove space before class ':' +sp_before_class_colon = remove # ignore/add/remove/force + +# Add or remove space before case ':' +sp_before_case_colon = remove # ignore/add/remove/force + +# Add or remove space between 'operator' and operator sign +sp_after_operator = ignore # ignore/add/remove/force + +# Add or remove space between the operator symbol and the open paren, as in 'operator ++(' +sp_after_operator_sym = ignore # ignore/add/remove/force + +# Add or remove space after C/D cast, ie 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a' +sp_after_cast = ignore # ignore/add/remove/force + +# Add or remove spaces inside cast parens +sp_inside_paren_cast = remove # ignore/add/remove/force + +# Add or remove space between the type and open paren in a C++ cast, ie 'int(exp)' vs 'int (exp)' +sp_cpp_cast_paren = remove # ignore/add/remove/force + +# Add or remove space between 'sizeof' and '(' +sp_sizeof_paren = remove # ignore/add/remove/force + +# Add or remove space after the tag keyword (Pawn) +sp_after_tag = ignore # ignore/add/remove/force + +# Add or remove space inside enum '{' and '}' +sp_inside_braces_enum = ignore # ignore/add/remove/force + +# Add or remove space inside struct/union '{' and '}' +sp_inside_braces_struct = remove # ignore/add/remove/force + +# Add or remove space inside '{' and '}' +sp_inside_braces = ignore # ignore/add/remove/force + +# Add or remove space inside '{}' +sp_inside_braces_empty = ignore # ignore/add/remove/force + +# Add or remove space between return type and function name +# A minimum of 1 is forced except for pointer return types. +sp_type_func = ignore # ignore/add/remove/force + +# Add or remove space between function name and '(' on function declaration +sp_func_proto_paren = remove # ignore/add/remove/force + +# Add or remove space between function name and '(' on function definition +sp_func_def_paren = remove # ignore/add/remove/force + +# Add or remove space inside empty function '()' +sp_inside_fparens = remove # ignore/add/remove/force + +# Add or remove space inside function '(' and ')' +sp_inside_fparen = remove # ignore/add/remove/force + +# Add or remove space between ']' and '(' when part of a function call. +sp_square_fparen = ignore # ignore/add/remove/force + +# Add or remove space between ')' and '{' of function +sp_fparen_brace = force # ignore/add/remove/force + +# Add or remove space between function name and '(' on function calls +sp_func_call_paren = remove # ignore/add/remove/force + +# Add or remove space between the user function name and '(' on function calls +# You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file. +sp_func_call_user_paren = ignore # ignore/add/remove/force + +# Add or remove space between a constructor/destructor and the open paren +sp_func_class_paren = remove # ignore/add/remove/force + +# Add or remove space between 'return' and '(' +sp_return_paren = remove # ignore/add/remove/force + +# Add or remove space between '__attribute__' and '(' +sp_attribute_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'defined' and '(' in '#if defined (FOO)' +sp_defined_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'throw' and '(' in 'throw (something)' +sp_throw_paren = ignore # ignore/add/remove/force + +# Add or remove space between macro and value +sp_macro = ignore # ignore/add/remove/force + +# Add or remove space between macro function ')' and value +sp_macro_func = ignore # ignore/add/remove/force + +# Add or remove space between 'else' and '{' if on the same line +sp_else_brace = force # ignore/add/remove/force + +# Add or remove space between '}' and 'else' if on the same line +sp_brace_else = ignore # ignore/add/remove/force + +# Add or remove space between '}' and the name of a typedef on the same line +sp_brace_typedef = ignore # ignore/add/remove/force + +# Add or remove space between 'catch' and '{' if on the same line +sp_catch_brace = ignore # ignore/add/remove/force + +# Add or remove space between '}' and 'catch' if on the same line +sp_brace_catch = ignore # ignore/add/remove/force + +# Add or remove space between 'finally' and '{' if on the same line +sp_finally_brace = ignore # ignore/add/remove/force + +# Add or remove space between '}' and 'finally' if on the same line +sp_brace_finally = ignore # ignore/add/remove/force + +# Add or remove space between 'try' and '{' if on the same line +sp_try_brace = ignore # ignore/add/remove/force + +# Add or remove space between get/set and '{' if on the same line +sp_getset_brace = ignore # ignore/add/remove/force + +# Add or remove space before the '::' operator +sp_before_dc = remove # ignore/add/remove/force + +# Add or remove space after the '::' operator +sp_after_dc = remove # ignore/add/remove/force + +# Add or remove around the D named array initializer ':' operator +sp_d_array_colon = ignore # ignore/add/remove/force + +# Add or remove space after the '!' (not) operator. +sp_not = remove # ignore/add/remove/force + +# Add or remove space after the '~' (invert) operator. +sp_inv = remove # ignore/add/remove/force + +# Add or remove space after the '&' (address-of) operator. +# This does not affect the spacing after a '&' that is part of a type. +sp_addr = remove # ignore/add/remove/force + +# Add or remove space around the '.' or '->' operators +sp_member = remove # ignore/add/remove/force + +# Add or remove space after the '*' (dereference) operator. +# This does not affect the spacing after a '*' that is part of a type. +sp_deref = remove # ignore/add/remove/force + +# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7' +sp_sign = remove # ignore/add/remove/force + +# Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;' +sp_incdec = remove # ignore/add/remove/force + +# Add or remove space before a backslash-newline at the end of a line +sp_before_nl_cont = force # ignore/add/remove/force + +# Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;' +sp_after_oc_scope = ignore # ignore/add/remove/force + +# Add or remove space after the colon in message specs +# '-(int) f:(int) x;' vs '-(int) f: (int) x;' +sp_after_oc_colon = ignore # ignore/add/remove/force + +# Add or remove space before the colon in message specs +# '-(int) f: (int) x;' vs '-(int) f : (int) x;' +sp_before_oc_colon = ignore # ignore/add/remove/force + +# Add or remove space after the colon in message specs +# '[object setValue:1];' vs '[object setValue: 1];' +sp_after_send_oc_colon = ignore # ignore/add/remove/force + +# Add or remove space before the colon in message specs +# '[object setValue:1];' vs '[object setValue :1];' +sp_before_send_oc_colon = ignore # ignore/add/remove/force + +# Add or remove space after the (type) in message specs +# '-(int) f: (int) x;' vs '-(int) f: (int)x;' +sp_after_oc_type = ignore # ignore/add/remove/force + +# Add or remove space around the ':' in 'b ? t : f' +sp_cond_colon = ignore # ignore/add/remove/force + +# Add or remove space around the '?' in 'b ? t : f' +sp_cond_question = ignore # ignore/add/remove/force + +# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here. +sp_case_label = ignore # ignore/add/remove/force + +# Control the space around the D '..' operator. +sp_range = ignore # ignore/add/remove/force + +# Control the space after the opening of a C++ comment '// A' vs '//A' +sp_cmt_cpp_start = ignore # ignore/add/remove/force + +# +# Code alignment (not left column spaces/tabs) +# + +# Whether to keep non-indenting tabs +align_keep_tabs = false # false/true + +# Whether to use tabs for alinging +align_with_tabs = false # false/true + +# Whether to bump out to the next tab when aligning +align_on_tabstop = false # false/true + +# Whether to left-align numbers +align_number_left = false # false/true + +# Align variable definitions in prototypes and functions +align_func_params = false # false/true + +# Align parameters in single-line functions that have the same name. +# The function names must already be aligned with each other. +align_same_func_call_params = false # false/true + +# The span for aligning variable definitions (0=don't align) +align_var_def_span = 1 # number + +# How to align the star in variable definitions. +# 0=Part of the type 'void * foo;' +# 1=Part of the variable 'void *foo;' +# 2=Dangling 'void *foo;' +align_var_def_star_style = 2 # number + +# How to align the '&' in variable definitions. +# 0=Part of the type +# 1=Part of the variable +# 2=Dangling +align_var_def_amp_style = 2 # number + +# The threshold for aligning variable definitions (0=no limit) +align_var_def_thresh = 5 # number + +# The gap for aligning variable definitions +align_var_def_gap = 1 # number + +# Whether to align the colon in struct bit fields +align_var_def_colon = false # false/true + +# Whether to align any attribute after the variable name +align_var_def_attribute = false # false/true + +# Whether to align inline struct/enum/union variable definitions +align_var_def_inline = false # false/true + +# The span for aligning on '=' in assignments (0=don't align) +align_assign_span = 1 # number + +# The threshold for aligning on '=' in assignments (0=no limit) +align_assign_thresh = 5 # number + +# The span for aligning on '=' in enums (0=don't align) +align_enum_equ_span = 1 # number + +# The threshold for aligning on '=' in enums (0=no limit) +align_enum_equ_thresh = 5 # number + +# The span for aligning struct/union (0=don't align) +align_var_struct_span = 1 # number + +# The threshold for aligning struct/union member definitions (0=no limit) +align_var_struct_thresh = 5 # number + +# The gap for aligning struct/union member definitions +align_var_struct_gap = 0 # number + +# The span for aligning struct initializer values (0=don't align) +align_struct_init_span = 1 # number + +# The minimum space between the type and the synonym of a typedef +align_typedef_gap = 0 # number + +# The span for aligning single-line typedefs (0=don't align) +align_typedef_span = 1 # number + +# How to align typedef'd functions with other typedefs +# 0: Don't mix them at all +# 1: align the open paren with the types +# 2: align the function type name with the other type names +align_typedef_func = 0 # number + +# Controls the positioning of the '*' in typedefs. Just try it. +# 0: Align on typdef type, ignore '*' +# 1: The '*' is part of type name: typedef int *pint; +# 2: The '*' is part of the type, but dangling: typedef int *pint; +align_typedef_star_style = 0 # number + +# Controls the positioning of the '&' in typedefs. Just try it. +# 0: Align on typdef type, ignore '&' +# 1: The '&' is part of type name: typedef int &pint; +# 2: The '&' is part of the type, but dangling: typedef int &pint; +align_typedef_amp_style = 0 # number + +# The span for aligning comments that end lines (0=don't align) +align_right_cmt_span = 0 # number + +# If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment +align_right_cmt_mix = false # false/true + +# If a trailing comment is more than this number of columns away from the text it follows, +# it will qualify for being aligned. +align_right_cmt_gap = 0 # number + +# Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore) +align_right_cmt_at_col = 0 # number + +# The span for aligning function prototypes (0=don't align) +align_func_proto_span = 0 # number + +# Minimum gap between the return type and the function name. +align_func_proto_gap = 0 # number + +# Align function protos on the 'operator' keyword instead of what follows +align_on_operator = false # false/true + +# Whether to mix aligning prototype and variable declarations. +# If true, align_var_def_XXX options are used instead of align_func_proto_XXX options. +align_mix_var_proto = false # false/true + +# Align single-line functions with function prototypes, uses align_func_proto_span +align_single_line_func = false # false/true + +# Aligning the open brace of single-line functions. +# Requires align_single_line_func=true, uses align_func_proto_span +align_single_line_brace = false # false/true + +# Gap for align_single_line_brace. +align_single_line_brace_gap = 0 # number + +# The span for aligning ObjC msg spec (0=don't align) +align_oc_msg_spec_span = 0 # number + +# Whether to align macros wrapped with a backslash and a newline. +# This will not work right if the macro contains a multi-line comment. +align_nl_cont = false # false/true + +# The minimum space between label and value of a preprocessor define +align_pp_define_gap = 0 # number + +# The span for aligning on '#define' bodies (0=don't align) +align_pp_define_span = 0 # number + +# Align lines that start with '<<' with previous '<<'. Default=true +align_left_shift = true # false/true + +# +# Newline adding and removing options +# + +# Whether to collapse empty blocks between '{' and '}' +nl_collapse_empty_body = true # false/true + +# Don't split one-line braced assignments - 'foo_t f = { 1, 2 };' +nl_assign_leave_one_liners = true # false/true + +# Don't split one-line braced statements inside a class xx { } body +nl_class_leave_one_liners = false # false/true + +# Don't split one-line enums: 'enum foo { BAR = 15 };' +nl_enum_leave_one_liners = false # false/true + +# Don't split one-line get or set functions +nl_getset_leave_one_liners = true # false/true + +# Don't split one-line function definitions - 'int foo() { return 0; }' +nl_func_leave_one_liners = true # false/true + +# Don't split one-line if/else statements - 'if(a) b++;' +nl_if_leave_one_liners = false # false/true + +# Add or remove newlines at the start of the file +nl_start_of_file = ignore # ignore/add/remove/force + +# The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force' +nl_start_of_file_min = 0 # number + +# Add or remove newline at the end of the file +nl_end_of_file = force # ignore/add/remove/force + +# The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force') +nl_end_of_file_min = 1 # number + +# Add or remove newline between '=' and '{' +nl_assign_brace = ignore # ignore/add/remove/force + +# Add or remove newline between '=' and '[' (D only) +nl_assign_square = ignore # ignore/add/remove/force + +# Add or remove newline after '= [' (D only). Will also affect the newline before the ']' +nl_after_square_assign = ignore # ignore/add/remove/force + +# The number of newlines after a block of variable definitions +nl_func_var_def_blk = 0 # number + +# Add or remove newline between a function call's ')' and '{', as in: +# list_for_each(item, &list) { } +nl_fcall_brace = force # ignore/add/remove/force + +# Add or remove newline between 'enum' and '{' +nl_enum_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'struct and '{' +nl_struct_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'union' and '{' +nl_union_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'if' and '{' +nl_if_brace = remove # ignore/add/remove/force + +# Add or remove newline between '}' and 'else' +nl_brace_else = force # ignore/add/remove/force + +# Add or remove newline between 'else if' and '{' +# If set to ignore, nl_if_brace is used instead +nl_elseif_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'else' and '{' +nl_else_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'else' and 'if' +nl_else_if = add # ignore/add/remove/force + +# Add or remove newline between '}' and 'finally' +nl_brace_finally = force # ignore/add/remove/force + +# Add or remove newline between 'finally' and '{' +nl_finally_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'try' and '{' +nl_try_brace = remove # ignore/add/remove/force + +# Add or remove newline between get/set and '{' +nl_getset_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'for' and '{' +nl_for_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'catch' and '{' +nl_catch_brace = remove # ignore/add/remove/force + +# Add or remove newline between '}' and 'catch' +nl_brace_catch = force # ignore/add/remove/force + +# Add or remove newline between 'while' and '{' +nl_while_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'do' and '{' +nl_do_brace = remove # ignore/add/remove/force + +# Add or remove newline between '}' and 'while' of 'do' statement +nl_brace_while = remove # ignore/add/remove/force + +# Add or remove newline between 'switch' and '{' +nl_switch_brace = remove # ignore/add/remove/force + +# Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc. +# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace. +nl_multi_line_cond = false # false/true + +# Force a newline in a define after the macro name for multi-line defines. +nl_multi_line_define = false # false/true + +# Whether to put a newline before 'case' statement +nl_before_case = false # false/true + +# Add or remove newline between ')' and 'throw' +nl_before_throw = ignore # ignore/add/remove/force + +# Whether to put a newline after 'case' statement +nl_after_case = false # false/true + +# Newline between namespace and { +nl_namespace_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'template<>' and whatever follows. +nl_template_class = ignore # ignore/add/remove/force + +# Add or remove newline between 'class' and '{' +nl_class_brace = force # ignore/add/remove/force + +# Add or remove newline after each ',' in the constructor member initialization +nl_class_init_args = ignore # ignore/add/remove/force + +# Add or remove newline between return type and function name in definition +nl_func_type_name = ignore # ignore/add/remove/force + +# Add or remove newline between function scope and name in a definition +# Controls the newline after '::' in 'void A::f() { }' +nl_func_scope_name = ignore # ignore/add/remove/force + +# Add or remove newline between return type and function name in a prototype +nl_func_proto_type_name = ignore # ignore/add/remove/force + +# Add or remove newline between a function name and the opening '(' +nl_func_paren = remove # ignore/add/remove/force + +# Add or remove newline after '(' in a function declaration +nl_func_decl_start = ignore # ignore/add/remove/force + +# Add or remove newline after each ',' in a function declaration +nl_func_decl_args = ignore # ignore/add/remove/force + +# Add or remove newline before the ')' in a function declaration +nl_func_decl_end = ignore # ignore/add/remove/force + +# Add or remove newline between function signature and '{' +nl_fdef_brace = ignore # ignore/add/remove/force + +# Whether to put a newline after 'return' statement +nl_after_return = false # false/true + +# Add or remove a newline between the return keyword and return expression. +nl_return_expr = ignore # ignore/add/remove/force + +# Whether to put a newline after semicolons, except in 'for' statements +nl_after_semicolon = false # false/true + +# Whether to put a newline after brace open. +# This also adds a newline before the matching brace close. +nl_after_brace_open = true # false/true + +# If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is +# placed between the open brace and a trailing single-line comment. +nl_after_brace_open_cmt = false # false/true + +# Whether to put a newline after a virtual brace open. +# These occur in un-braced if/while/do/for statement bodies. +nl_after_vbrace_open = true # false/true + +# Whether to put a newline after a brace close. +# Does not apply if followed by a necessary ';'. +nl_after_brace_close = false # false/true + +# Whether to alter newlines in '#define' macros +nl_define_macro = false # false/true + +# Whether to not put blanks after '#ifxx', '#elxx', or before '#endif' +nl_squeeze_ifdef = false # false/true + +# Add or remove newline before 'if' +nl_before_if = ignore # ignore/add/remove/force + +# Add or remove newline after 'if' +nl_after_if = ignore # ignore/add/remove/force + +# Add or remove newline before 'for' +nl_before_for = ignore # ignore/add/remove/force + +# Add or remove newline after 'for' +nl_after_for = ignore # ignore/add/remove/force + +# Add or remove newline before 'while' +nl_before_while = ignore # ignore/add/remove/force + +# Add or remove newline after 'while' +nl_after_while = ignore # ignore/add/remove/force + +# Add or remove newline before 'switch' +nl_before_switch = ignore # ignore/add/remove/force + +# Add or remove newline after 'switch' +nl_after_switch = ignore # ignore/add/remove/force + +# Add or remove newline before 'do' +nl_before_do = ignore # ignore/add/remove/force + +# Add or remove newline after 'do' +nl_after_do = ignore # ignore/add/remove/force + +# Whether to double-space commented-entries in struct/enum +nl_ds_struct_enum_cmt = false # false/true + +# Whether to double-space before the close brace of a struct/union/enum +nl_ds_struct_enum_close_brace = false # false/true + +# Add or remove a newline around a class colon. +# Related to pos_class_colon, nl_class_init_args, and pos_comma. +nl_class_colon = ignore # ignore/add/remove/force + +# Change simple unbraced if statements into a one-liner +# 'if(b)\n i++;' => 'if(b) i++;' +nl_create_if_one_liner = false # false/true + +# Change simple unbraced for statements into a one-liner +# 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);' +nl_create_for_one_liner = false # false/true + +# Change simple unbraced while statements into a one-liner +# 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);' +nl_create_while_one_liner = false # false/true + +# +# Positioning options +# + +# The position of arithmetic operators in wrapped expressions +pos_arith = lead # ignore/lead/trail + +# The position of assignment in wrapped expressions +pos_assign = trail # ignore/lead/trail + +# The position of boolean operators in wrapped expressions +pos_bool = lead # ignore/lead/trail + +# The position of the comma in wrapped expressions +pos_comma = trail # ignore/lead/trail + +# The position of the comma in the constructor initialization list +pos_class_comma = trail # ignore/lead/trail + +# The position of colons between constructor and member initialization +pos_class_colon = lead # ignore/lead/trail + +# +# Line Splitting options +# + +# Try to limit code width to N number of columns +code_width = 80 # number + +# Whether to fully split long 'for' statements at semi-colons +ls_for_split_full = true # false/true + +# Whether to fully split long function protos/calls at commas +ls_func_split_full = true # false/true + +# +# Blank line options +# + +# The maximum consecutive newlines +nl_max = 0 # number + +# The number of newlines after a function prototype, if followed by another function prototype +nl_after_func_proto = 0 # number + +# The number of newlines after a function prototype, if not followed by another function prototype +nl_after_func_proto_group = 0 # number + +# The number of newlines after '}' of a multi-line function body +nl_after_func_body = 0 # number + +# The number of newlines after '}' of a single line function body +nl_after_func_body_one_liner = 0 # number + +# The minimum number of newlines before a multi-line comment. +# Doesn't apply if after a brace open or another multi-line comment. +nl_before_block_comment = 0 # number + +# The minimum number of newlines before a single-line C comment. +# Doesn't apply if after a brace open or other single-line C comments. +nl_before_c_comment = 0 # number + +# The minimum number of newlines before a CPP comment. +# Doesn't apply if after a brace open or other CPP comments. +nl_before_cpp_comment = 0 # number + +# Whether to force a newline after a mulit-line comment. +nl_after_multiline_comment = false # false/true + +# The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. +# Will not change the newline count if after a brace open. +# 0 = No change. +nl_before_access_spec = 0 # number + +# The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. +# 0 = No change. +nl_after_access_spec = 0 # number + +# The number of newlines between a function def and the function comment. +# 0 = No change. +nl_comment_func_def = 0 # number + +# The number of newlines after a try-catch-finally block that isn't followed by a brace close. +# 0 = No change. +nl_after_try_catch_finally = 0 # number + +# The number of newlines before and after a property, indexer or event decl. +# 0 = No change. +nl_around_cs_property = 0 # number + +# The number of newlines between the get/set/add/remove handlers in C#. +# 0 = No change. +nl_between_get_set = 0 # number + +# Whether to remove blank lines after '{' +eat_blanks_after_open_brace = true # false/true + +# Whether to remove blank lines before '}' +eat_blanks_before_close_brace = true # false/true + +# +# Code modifying options (non-whitespace) +# + +# Add or remove braces on single-line 'do' statement +mod_full_brace_do = ignore # ignore/add/remove/force + +# Add or remove braces on single-line 'for' statement +mod_full_brace_for = remove # ignore/add/remove/force + +# Add or remove braces on single-line function defintions. (Pawn) +mod_full_brace_function = ignore # ignore/add/remove/force + +# Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'. +mod_full_brace_if = remove # ignore/add/remove/force + +# Don't remove braces around statements that span N newlines +mod_full_brace_nl = 0 # number + +# Add or remove braces on single-line 'while' statement +mod_full_brace_while = ignore # ignore/add/remove/force + +# Add or remove unnecessary paren on 'return' statement +mod_paren_on_return = remove # ignore/add/remove/force + +# Whether to change optional semicolons to real semicolons +mod_pawn_semicolon = false # false/true + +# Add parens on 'while' and 'if' statement around bools +mod_full_paren_if_bool = true # false/true + +# Whether to remove superfluous semicolons +mod_remove_extra_semicolon = true # false/true + +# If a function body exceeds the specified number of newlines and doesn't have a comment after +# the close brace, a comment will be added. +mod_add_long_function_closebrace_comment = 0 # number + +# If a switch body exceeds the specified number of newlines and doesn't have a comment after +# the close brace, a comment will be added. +mod_add_long_switch_closebrace_comment = 0 # number + +# If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after +# the #else, a comment will be added. +mod_add_long_ifdef_endif_comment = 0 # number + +# If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after +# the #endif, a comment will be added. +mod_add_long_ifdef_else_comment = 0 # number + +# If TRUE, will sort consecutive single-line 'import' statements [Java, D] +mod_sort_import = false # false/true + +# If TRUE, will sort consecutive single-line 'using' statements [C#] +mod_sort_using = false # false/true + +# If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] +# This is generally a bad idea, as it may break your code. +mod_sort_include = false # false/true + +# If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace. +mod_move_case_break = false # false/true + +# If TRUE, it will remove a void 'return;' that appears as the last statement in a function. +mod_remove_empty_return = false # false/true + +# +# Comment modifications +# + +# Try to wrap comments at cmt_width columns +cmt_width = 0 # number + +# If false, disable all multi-line comment changes, including cmt_width and leading chars. +# Default is true. +cmt_indent_multi = false # false/true + +# Whether to group c-comments that look like they are in a block +cmt_c_group = false # false/true + +# Whether to put an empty '/*' on the first line of the combined c-comment +cmt_c_nl_start = false # false/true + +# Whether to put a newline before the closing '*/' of the combined c-comment +cmt_c_nl_end = false # false/true + +# Whether to group cpp-comments that look like they are in a block +cmt_cpp_group = false # false/true + +# Whether to put an empty '/*' on the first line of the combined cpp-comment +cmt_cpp_nl_start = false # false/true + +# Whether to put a newline before the closing '*/' of the combined cpp-comment +cmt_cpp_nl_end = false # false/true + +# Whether to change cpp-comments into c-comments +cmt_cpp_to_c = false # false/true + +# Whether to put a star on subsequent comment lines +cmt_star_cont = false # false/true + +# The number of spaces to insert at the start of subsequent comment lines +cmt_sp_before_star_cont = 0 # number + +# The number of spaces to insert after the star on subsequent comment lines +cmt_sp_after_star_cont = 1 # number + +# For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of +# the comment are the same length. Default=True +cmt_multi_check_last = true # false/true + +# The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment. +# Will substitue $(filename) with the current file's name. +cmt_insert_file_header = "" # string + +# The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment. +# Will substitue $(filename) with the current file's name. +cmt_insert_file_footer = "" # string + +# The filename that contains text to insert before a function implementation if the function isn't preceeded with a C/C++ comment. +# Will substitue $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff. +# Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... } +cmt_insert_func_header = "" # string + +# The filename that contains text to insert before a class if the class isn't preceeded with a C/C++ comment. +# Will substitue $(class) with the class name. +cmt_insert_class_header = "" # string + +# If a preprocessor is encountered when stepping backwards from a function name, then +# this option decides whether the comment should be inserted. +# Affects cmt_insert_func_header and cmt_insert_class_header. +cmt_insert_before_preproc = false # false/true + +# +# Preprocessor options +# + +# Control indent of preprocessors inside #if blocks at brace level 0 +pp_indent = ignore # ignore/add/remove/force + +# Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false) +pp_indent_at_level = false # false/true + +# If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1. +pp_indent_count = 1 # number + +# Add or remove space after # based on pp_level of #if blocks +pp_space = ignore # ignore/add/remove/force + +# Sets the number of spaces added with pp_space +pp_space_count = 0 # number + +# The indent for #region and #endregion in C# and '#pragma region' in C/C++ +pp_indent_region = 0 # number + +# Whether to indent the code between #region and #endregion +pp_region_indent_code = false # false/true + +# If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level +pp_indent_if = 0 # number + +# Control whether to indent the code between #if, #else and #endif when not at file-level +pp_if_indent_code = false # false/true + +# Whether to indent '#define' at the brace level (true) or from column 1 (false) +pp_define_at_level = false # false/true + +# You can force a token to be a type with the 'type' option. +# Example: +# type myfoo1 myfoo2 +# +# You can create custom macro-based indentation using macro-open, +# macro-else and macro-close. +# Example: +# macro-open BEGIN_TEMPLATE_MESSAGE_MAP +# macro-open BEGIN_MESSAGE_MAP +# macro-close END_MESSAGE_MAP +# +# You can assign any keyword to any type with the set option. +# set func_call_user _ N_ +# +# The full syntax description of all custom definition config entries +# is shown below: +# +# define custom tokens as: +# - embed whitespace in token using '' escape character, or +# put token in quotes +# - these: ' " and ` are recognized as quote delimiters +# +# type token1 token2 token3 ... +# ^ optionally specify multiple tokens on a single line +# define def_token output_token +# ^ output_token is optional, then NULL is assumed +# macro-open token +# macro-close token +# macro-else token +# set id token1 token2 ... +# ^ optionally specify multiple tokens on a single line +# ^ id is one of the names in token_enum.h sans the CT_ prefix, +# e.g. PP_PRAGMA +# +# all tokens are separated by any mix of ',' commas, '=' equal signs +# and whitespace (space, tab) +# diff --git a/plugins/zynaddsubfx/zynaddsubfx/style.sh b/plugins/zynaddsubfx/zynaddsubfx/style.sh new file mode 100755 index 000000000..08d93ee2e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/style.sh @@ -0,0 +1,6 @@ +#!/bin/sh +uncrustify -c style.cfg --no-backup -l CPP `find . | grep -e "\.h$"` +uncrustify -c style.cfg --no-backup -l CPP `find . | grep -e "\.cpp$"` +#cover nsm code +uncrustify -c style.cfg --no-backup -l CPP `find . | grep -e "\.H$"` +uncrustify -c style.cfg --no-backup -l CPP `find . | grep -e "\.C$"` diff --git a/plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx-alsa.desktop b/plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx-alsa.desktop new file mode 100644 index 000000000..1a63e9d9e --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx-alsa.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=ZynAddSubFX - Alsa +Comment=A powerful realtime software synthesizer +Exec=zynaddsubfx -I alsa -O alsa +Icon=zynaddsubfx +Terminal=false +Type=Application +Categories=AudioVideo;Audio; diff --git a/plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx-jack.desktop b/plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx-jack.desktop new file mode 100644 index 000000000..a16a2e7fd --- /dev/null +++ b/plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx-jack.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=ZynAddSubFX - Jack +Comment=A powerful realtime software synthesizer +Exec=zynaddsubfx -I jack -O jack +Icon=zynaddsubfx +Terminal=false +Type=Application +Categories=AudioVideo;Audio; diff --git a/plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx.ico b/plugins/zynaddsubfx/zynaddsubfx/zynaddsubfx.ico new file mode 100644 index 0000000000000000000000000000000000000000..80f0519d3e48071cd514952a62d9b04561d6c4db GIT binary patch literal 9662 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

A>g&U6X3&osR|E!FJiVK zLV^=r6X;fJRBYq75vCXjBZxRLZ(n^xm^X-EVBOTg2gUj?BF4#@mL z2DA|;{ozpRr-pBpi zm(Ahi8+)=Y>0fqlo>{v6zpy9(&j=1#T9W77kPDs`4$bal*)V})`OuRgG5!BR(a@^b z7OC7s$dHnr%;1<>{kEdj!LoSi*&BiTx0E}6x>4^|kQpwOVmQCxu@WoKHQrnn+)eL2 zrp!L&20-&7av7vJ!yu%A`U*hvH{dmxLZ|{NLlqCG%=R|k96@FRkMun-G4P2Id>c~= zae+0Z6$Hfh!8L52$$#O?xXkcRGBM7jT$|-&@`b7vy*BFK-|6(CFY4N#On)}{?B|Uu zk8hf){+LLSwNPJeOP*`+T?mhkcXxR*nX}6)(q1}c(s2fhcfUpb6QpVi_WuJ{_f_(NY686J#3yb z7&KHfOJ_F<7*B2UJ~*CwydHE!{b zx)AABz9)&>M&KE_-9+4MtWnR_@@L;RM7#IG3ZSsv{kp&HT&9Prs_7eX;0OuY27oR| z2OnN%R_vMDl<$lA#BjycMfe8YyO`}6eJtUu;@XrfO+_xovbrD^#xH^_@tYAJ#3#jOSNxzVeS6ZDqBIv{TYSs|_(l zg>p58`lR>aqCXESn3Ki-z7;KA`reF@l5^X=H7}So-!Vg0+ZAg2och{DHnZmOc$Ski zX)bR3yOp<$e$dh^h(GjXICI(!vL6u58#^cEzbfd~|DcYCm`Cmp1E)*N2D7?~Z4>5g~c<0~P zNAH$nE;S76RmuvHmPZeVS}^UVy$jE{w|Pv=JK|&nYxHFm-3Jd;&#C_HIM6L=FFCpB zc_YsLXLQX0ofox-LnK~Ck=;zrh~^l|Xn!k}TF5OV+RqzH+1NIg$)F+|$w+PZF`X^> zXKKFF>g+f2y~9VtExEoXb~W3q#c7}E5AFCj_++=ay zzha}eTQk@dQtvduRbXaiCC%XB5yQm&@X%* zI8}Y(_lBeUs_*h@dC@{$|E|2xr=&w7FKs>2%kq%gaKs}#%Vz3<lffrlbC3O0KN&*#>FIbrZ#xT7qr%`yf14HoYJ?_w)%L+;L*ZcYdJ4n8QGWe z6s28x=7vuUQ?+%zR?JfQsh5gE-)O~p8x769g!*YD3gE3m<=FxRJuh#x0CDqV1T9}r zPmh|B6Z1&NY;E@tIdXero1*yx~Z`wwLg*q5wqYzkGQzw_(9NL}0C`qR6IT`;M$t&ocL-Ldw5wR|0S z|GdN6iy8e@Cnp%5-quw*_vo;8lEwWBM~fF(HUW$}<&XMPYd&8dN+ca7G{*xd$jM)i zak%q5{`1_ZHfOH=wWQ#$k7dhCn(L8qt@f$+?mZ~3v_E_H14#&psdD2+FJ|~bK%pc z^=Rg!Zo=T1I+T|#bn-5ShDs+18LHeU8Dn@qQz_?Ve6n*Wfxh}Mm4KlpOLefT+nzVp zf8_bx^gUuvCr*DID&{AM+u-R&drOe=ii!lO85tR!DiqSS;?ckUo4~;gifAWNpR9IAGDmDnDh?Nc^i^?gT zSSP9|sk#sS(~U#-J*Dw;>Y;#5bkRXy0I>?>_3WnyUWxpW-OtDvBxR$dw#IO$j$bEA z7S#I@P0IVR=e7DpRrJoJOk+&O8JbtMwHv6~R=$6rCZYC?GhPL$8w>P=wzesBtk4l; zJ?`9}(+S3tshANalh9Ey726rrLJgCS zb0Dy(Z<<%y+esgPFm=A}T>kE#$295bWHNVCb-EZ{=E5y80vKUnh!fcr1$83MsImi+?1rf|GpvaL0LW3cAb+;ucYevb-aBQV>`va%+u0(yH!Y@)!yOw)mS^o z+Ar!oo1Xj4&^IzPg5?t zwnUmiUE44|KlpIfg4wN^O0u@o!4Inoml@3>UQmALrc^r}tQh6cS?4m4bSCTHk=pLd z4%%db{DGzySYx`RNrGqc@~s4S-1OXbllSq%@oUf3jw<9U3USO?{~K6Knl?}+XS=)a z&c6kB5273~6)`&L3A(J7`QAowHIHtVPFH0Op zb8H#x)GpXP&gxUShY{3GtF09;{v0_&A)mPSMq{?%>xH4)w<#AXRe&kzMlG~u z--wGFfYau$qP;is=>2)Vkyw(vZCY!yF6K#z9@`niBdX+NRxSIhAWNOCjHmkGT^Mv3 zYn?p#E<&h8DujvFFdn8IH|JlOzm+8~V;Re|=|6f|&~Avy(VVwRBUDb}MV(YgVT^9z zR?o){uiOKo$R6;~z3s7coV#K&?!)oPnxjIO{k{u_CX2M-*of}H{iILF(-<#uecdwY z353@Gt#_DK78MqPZYSqFlBaUHqoc#g$*D}3r6Sl_LS`k=w}$0SH*#ic?ZmkE-x;6|M~F{)WuNJ>Wn2jr5XqQ3o?#izQ~lU zF1EOz64Hf7ruGj1NT!cmz^_kCP9l8y`uKo{9)KDhFaeQ~wdLdB|GT#4T4gRQoC_hh z|M86j{;7sFVa|_&bmMS?ZSly+2gfr&$~g^v0!}|M z0SW9wZH7laGK*W}evJ+#3yMn2(cRdqy=0O7%K!G`J%WZ5U0Y?BPrqY0oFRK&+CNmc5@XFC5>9sN*>)en5b{N;srrjFE7-nii(QfzP_mR3kcl^z;PI! zLSq6A`vZ`<1l|w{hUlS(7#-(1#p=I^qoCLg%J_gdL*0mCnkw8p+C zTiiKzVV|$X&HY~94=nODV~14CG2T@F&$7;M6__Px;NU(EW1u8jYZ)wFC>uxNYQT$v zg7}0hK|1`hv^rrd7C(3VJ@V$Ep&?9MCnhCLPECWaU!hAT)tFyCbZG>ql)jl3; z8Me(zIj8l4gW}k}_DSmrkaXCCn?!4n~lO*pf0jnjT zxcHOooSbu?KQAItMK%C_!46hdaFW{Jy?giar443(K_LV418$pYSo&wq#NEHolG6DR zX$)q)alzwXznYnt`~kWQ?70Fp1;C>pgFT;@RxvpHV{s7+Yz!oGjP~N$I$XYt;e0i- zr;i`M>Fhj{Tu)5`j0L_A2d`RBg22RmePPsi%?biXUiQ(Q~x|7Bv>eqtZHpS?(aQnJq( zgCf7quC6Y^DHP*t@nWVW7rN64+d{yUwxz=WfuO+ZJXknvY;~Q$(J+_|2#+EV(FYVj zF)=alPyq@)IZfb}z=a~arwLm`^@sNwKljWsms*>+li#=NdoR&D)TqhY`nIOcmg#x| zvsM7-% z{@I^c<4CO{I1R(Bnx-^HuCGgO+p$vfT| z7RZ-621o0$_ISF9J~ff~n8zx3tD9a>*6{wzMHSudcOwsvP%8}p!YeDj zpJW`geR4s{If;qZ3m)a) z{whavqUcUS@N3(%XU|?b!!!JU*&AYWa*6IO{l3?Bk0V06Z>6}7v4>3fAnz_Le0yK| zt9r})%U|Xgu@imRnHMmsF&$mGC~9T4 zmT+P0{L@o+U5e_AUJjq5IsTgAh<1?HdliigW+>%{@Y1|8(d}QdxoKf9t!vdCg!w%- zYN0=C329F_diP0x@rA)B(@}1Qum!%v)3H(79`UF0;-0d@u~SW^A-wc(KaJ$0!UMBn z6#54HWZ6^esfoOW>FBk@)?`n^namq|*xwO#%{E!$b2NBW_dZb3rLhm8UYzHo4UyiSc|u9lxBYp{{=Oc}bAS{mEh4ew4S6Evd@aOfrK`tgxh{*w_No(zyugd6rlvR-Kp;gb;Y%%Mm3Y)>f?|Mv}4aW_}jy!w|Q=wjIu@+Fuh#4XVlG9=waZgVP) z`Ox5FAr8EPB(V1d1q5bz>+V?lu{%qa+}WC!QZEd_2WYVkJ+nF8h7LQbQGB7jE%R9P zCYv0Zgg_#IP>`%IT$uj)H90X6&gyJjTqizGqL{*&2tdQ6?BXeqC;?@7d3oVn*M-@Q zL;h=1k^*!^@lSBz#72F4sxaW+CSvoGf`b2n_xPVazP`q1&WM|q+&-Q6^r_Vg_gPen z*n5v3KNet3k-apSrF|d0cQ=R=keC)?k5b2p7CshwyNwW6M<^!jj1wgZyHB+Az%$1K z8-t)JFl?<8vt+Rn06>j#xo7|)bPuo_9JM=-g|d4rGg!CW@v*S}^WH{DL66h-U#|L= zsdkrRV!z10bq|dj!fO@X(^6P_&lKd-Hqaiik_{( zwsy&@tFtpr{z^J%4tP|UpO$r<(9)42nP3K~yxap%7h^yeWO=Q8#>hy6>yY0c{rp#t z9i?5q95wJqT7%;S6#5;cKYsogg1}rR+MQGL^L2>dU=o7MuS0K*sSW|17uOd2F(v%x zkJrvHS@e2|Kjn0AJA-=PD%*`HN|xVjv>eZTTXhG~r5YU>`7f8pqlV^#`K3#jNQp8I zF@FwXa(j)yw*D-hyK(r_#Kc=%H2N+|N=mdruP~wj2;wJ>W^;*m?d{*7urSk?O_wIb zbej)v!)fRl#6mydwEKfZ48{ljsICn!pkiGEM1kL;IDGl?WjuJYhSAZqp`Cpf@XP>M zpn%*;f);;kSC{j%Q3}E?L+8Ya@92j(I({XVK$o0D-|P1Fu+wL2eSU$vzqjeO`e*bf z!P9#+=?aWwb$Qkh`o~EpdQO`Np2%~B&IuAJSA|sk5c956uvZTW#VG5UQ-Ua6o_NvW0lIChFt-0eDRDz;g9J(_z$4sHo(13 zrm472glL@J^XKp1mZm0r?`cj>GjIoo>pI1FZGVA=01lhHs(@ZMl0&7z+dUZkz#smH ziXZ))?|2Q?)(;Nz^5(YQSSCRq;q;5!ji2wGJ8+;p-5)5-Sxglb7!|$UzUKtCtCv@$ zi&E>PW{Qse)tq`G({b6|qWaOJ&1`}mW`^}TqDRjKfWl}5=mvjPaGxw@cCx;Kl|U1u z&K*?&nF1u)SB6U_kyB#e3Ca+!XCOIo5_1yJ1o-~-t0*fiMyk$SR+qrYi2feOZTU%p);QD_RMbl%=B9OiNl%RIvM8rXG6~{ehdOjoJa=f_0gAd=v zeULz*|9q@8XxB^73D3u&Zt?8~)sd+?BWtk^_LNjeD_YFhPK48mYLA!kNY`8lFF_)epx z7fxbGXW*S7FH@m;70THJ@*59ehJ!VFkh>dig9s$hbq66J6XO@Qws2cd=>+ds0~lQ1 znB1j7-7ke0`^454K9MZI9J&VnP=WD&*@4~YNfU!)5PZ2)&e2j6nON1Z%x;Vg+xrdLRD%JXrj~TiviTFBO?Tp=5!}2!sukEMD*TrB2%FkjCA-vi8(pihoyMIDGc$B>jgZuTHI=rxzX z2YQ^JZ&Pf71}xDASY5@82e+!<5aRfwM|&RXE8xRPCeNMNlcEmnrFVHw*XTpfIJsYu zJBpg*r)nj_8nbQw?p@kNLrT%N!8LSt{v|{2-^0}UGpeg=^CNBD-QBqIj*i0oe3z?N zA7t)J6LJquN@ADYewb$KRwOUW2;L9_heHOS0pmiG=(M!XNmq<*v2$}Xk`QFVA|kGD zu6rZwU^$(?a^>Wd4}>1PJ~fcRHHwhzD9X;B#MzC_gYoHmkct7z9uz^;v*ISt0@?Oq zV-ff#2GWO|JaH0Db!XILwSWS#-?Aq0Vx`Y7M;u7!n(1|>L8C&jh~$vHo9K+twQE5S zf{FI__NJyuI3BU(v;$_{-H<5ZY98n1UG?yQScKAIxAscfl>WQHLDhiGRJLjq(ug;B zv?Uc4!YdCutUrS<@a&nSvGF_LqnI_+JIJ_#Jn+lsV$!G2pIx9|HgJWEz07gI5v_1$ z)ISngk!UzT%joc8>6c5-v%7Igj*!pb=w%=nX-x?{ABKh?Oa^oI)Kr0;kC3$+-ZeEd z!&(27`O#}-L4RFCo3-SI4}$qv-a9Hz9M{oN;OrvgUn~uMNZH46LHLSw%wy)}=0*kx z{hbty2|)e|3h2zJBiY&ie|=qfG?j0({)`zTgd|bOJV*QxnkX_>lFHNxWyny*2$>>N z5-Le#E=eL$I-yV*A~IwsAyX+s;yz!$b?>_Cu6wW59}TB-&iB3F`|iD;=h@GGHQ){n zLoaO7h2qHR6?QM9aqc2F)CyV-KpP*7oJ8Z^0cSQJ_=qS?x*B8#Y;)p$(R;7`)9J0; zdlsk{A|h51?HwJY$A(_!G~x>I7}{VoE9|S9Pez4$!q#>TQe60E-G&L0s|kzZkUy*Ee(Z1WWaafrxpM#6vuIGHxOphhx+ z&chbzWvc^=$X$?TXqFvAq}BHSB_k$w(E|GSNyt0Tp^X(TUGY_P;A!48GzkCF4GjE) zoCtk&Qhi}ho;=CQvOj(L8``*!)tr~je1^n=l<>sIn;boQs>cfgCI>8u%I~GXirSP7MM<>hA7N9760tw-R^ytGYTyYV8HU4$4YOkRBS; zSRXw~a|pgP?1jczkxk3RE4U{_U)+p!tDSSTJjA!}EPeAGD{aW;>1HFn=qgn zZn=-Rh_1==5SL+NomTaSo=@tWK6Y-29Wr?YXzp_vF@ox?O-(T`?Rua3m*2nd4#EpZ zJnDP&9Am}Me89^@4|)?oXJ20*5+946FhdcG9aQiBq9baF;ls$%kXIwuD8s%{Rvsr= z9~JBXgbQj~Q$xf3^70g>&7Ws{z++;^N*^rbSG1!-YsbRMnpcX7O1KEEMc1`{_fzu+ zU1@cojRI0-Dbs;CKQS?}QOgg(Zvtxr8BeKYAuxl;f39A!b9LR{`m`ZzXK68>G7J+z z-%5tk36`p#Otb`)_|vS3(;s+ct#BTDBTKwvB5$6gCt~a?7Wl$Y?4sUHoaL^q>sv3R zjZ#W1htI>X1j@$t7ZDt2I@G_Cs4waaFRc7>bcUCb_wvx-Wl#I?WA5S3$2KzV%$7L! zOF_p#t6k;mybzE&nnjS8PX=qd75;lDI8H5G(j7i9PQBzP(-D%b^7 zBuIS_0z?}g1U!}S5OlAwK7PC(tChd5A@2bA$emS_gEWfmVMlS>TwEq_M!yPTpw-1u zh2{T@m~=p#Ta6`NGEC6i{5+^*sxf9I0BhXz=exLp5oy+iPDwOH=$E&`CvN3;&B|sj zG~z&=xfs>7iiTKA3=9ujn3?Hee~`wBi1SyIlh16sj)Lppp+hMtDNiaZ&1h_m(o{@) z&Ygj?C60c2+6#!~_wG$$L!tL@gPkvcNjo_f)zq1DQUCJMB{6X2cuHG^@xYw#Lbkp#+iH zB7o&MZPda%)-B<&=4dHC-=Z*z{nQVo}nD9Ci2bP3A*aUF@+5ghU6bbV^JV9=mbQ3t z4p6dOQP?nIKWw+bqeap@2gz#jk@7e6Lut&T$8S#5SSc$DbZ4!M(KycXOFWj(I|$ur zC|q7~s3Qyky#w+M@&k^!&j#FAZ`2m#;_^UQ6BEPsdJ<4uGtOb0S8#5?MpX%zclXsB zeyNFpoPDoekw_c>^^>JtIGDuDkK(CC#eMvNg8??ULBpkO2yL6yJ&#&j!!-z222$E< z9^sXEqx+VHhzyC@XT2&9A3yG9YpZMa@hJML5T@?r=!;ecEO?+Y#BE<$9G7q1lLiY> zO?CCuCO&#b#>B&>EDXY5Y6Ter4!bX?Zhb@|jO z*@nfuFU^5Jz88mR3-UKvB&J%eX}G#!T2(4RM>8@E$PQYoXEMn7Z4ei7d5E5^O;fO? zJ=|s!mdjO_P#u0#ZI`jBWWKLojEq;E@D+`59Onj<#JZji0k~#A$o6B9b z=r0h`)O#0=)H2J*5iOmzpM`lFo~PZHv7e2T5n@PZ)K;9{YuxlTD|?!oWmQ!VT1BR( zr=!omsc;Ep3-apiWY zdDhV}`D>z0%P)+F#{s38nRj7fAx&^&84(;4^TJwAe#@3p`#sw;`Z$9;k=k{C z{O8M;Wd>rZravR!-f7`WCCpsY8b2doC8yB&1{HKw>Po*dERtYbeD0_dr_D&2wawWO zo*U*7^cHNQ*R6=l@Y_n#AGfq6lGzNnkP#E3aX~joFX>>lyyHTWY=R|G3hnhZ5ihq*@?;APr@iQ6e)KD=f zZ>~~Qj_H4Qe*N=7u88@2=R^(1DyPM*KR>Aa0Zox8>I-GPD6oLYmCoEje-BU3UKQ#^opz=q6HSj>f3 zecfEm5~2L9FQq4>rGXdxVdeXEB_(B4#^Pe?UL|wsRAgi!4rRgxn>nCoKnWYLKtf#O zyJ?smw7J<9TlR=U3idp#?MPjiX_pqElYKsARr2=_*WlrD`O>A$_3(y-T77T*a~+6m zJ$-%EZ|FrMhMqie0$X9LTi^B5{Y7+<^&!zlnYYi#$~fFpg$|UPi5^{i-ri;9ZW(w^ z#Pl8tM4V({guD+p1Wh%{xAOe?ur1RCBe)IVLM31A87n zlxb$mgD+XH@{3+7Y0}%d4?QdR89l-(i)KxDdy1CmMmOn2gP+CHBDKJ+WfM@*|Dvbt zvcss&(sKH-^(Ftg@7U)}jnZ%Mt2uCa+IHZn|AKWfLtH(S`O0b|i6{2virSYYMj)mj zi*I;j&4W$t@;-lUO5?=4OuAQc&@XR5mFLh9k4$IArwaDYO2>IOZtTx7S}&JEYNV4@ z?<@1S?{C|*x#{rl!zYIYw8YZ|>dE8QIXu!tLQuoY5xW9hBevpiKyC2+hOc4RYu(N} z2HX$wuFi)uE3vZEzK*K3bSTc{i&!|y8L_RvVYd(yeFy-Bh>I7a?%H2HEb#^<2LXo} zxS_;1Q{WZj2j>|pvzy|$+Z2kRYC0VubCroets40Bd~#yg-$#H2Uc`vJpv4@ZC%iL^ z*`V+hCB^Kh=}NsuTXAOhKlM+!4jj7`kPD0_tC1@=H0USjtJv0t;bA0MTO{-w6zz7x zp}a|SbT*-3YyOJKcB8N}`hI~A1(R<#wAtrpDu-v@(X%M^2c8G8ESzoK1GKOcK7i+N zoOBWuXO7g=i_Glvjr}!2H5#uS*Jq#Qc=0pE2DvHVASGh})OYYU z$W^<#x(Jhl2Y&$|Lz!n@`R>hRO<^@4K;qfRj}%-PzA~yPR9cf{httGGlZMTW2}}+& zk5t-)uC~U1vL=MpBg<(19<3^XEKnvd0jg_QZ9T0T_yczY(aaQMis5+74D)Vt)eupoekVIS577y!k=LzlVxT}#MS&2*(&ljQX>BVU4E zx$UR^CVIRxNlNOqK|ryABo3>u%FrMHG6q^JRb-(0M@-H=(?tJfJmiXZYC{`-opsg| z3a@jR<@B;yywze)b=>x;uDLn(w4=_1rr14`0{wJt_TPQoLAss+aKh6_AMg* zKVDx60w7Hn?m7Ml{v}K=(@P|;2RSlO%yUT6kX-@l+y(^R%1YqU%S}JM<8*`-l)2kG zJ&9*cP1c1~C`O?P3xC@g6m9$WWOb;el)@m?*kufsGP4k@!Qb&Ad`v-MSj(Nmv_7RW zb~ZLxVK@Ot1CWb)vjqN*a2buwr3z>5$+8u6vkzpvbFam5{1jt6=O(Sw?7RO4eW-1X zksA{vB#Lu?73txPb&Z@V8=jn$yRpfblKH45GA?DIE0AY+N<^WCRmBMlhD35;Vhfp1 zzIt<9iB0ba>QIS-`I1FV50I0;p?i~9)HP^23p?=aGbvzynY^+}niZ z1Av?!x%Mu?(c&_{V(qI}c>o>9PS^E;?M*Ex$4!_`bv13X6%r+dSM@O2jnm40hyr|4m3WJrGzeLKUPLfC+|>GafIcu&-@z(lLDg zRU}G-x?wwYGV_YOlM= z6U7G;q0vxP|2HGq78Cd=-b5rdUa&2#@@XnviBEe}!@^Ygl4UTB@olC@Wj~?tzL>8V^qKrOHYxPtRf>qp>n}LR1rL)AAy#boF(uzgccn zR*Jt_=+_Z{&2L0}JJrN4L4|JmF3!ymu_@Roq0mR(=;!jaCpMhGTpcP|- zF@f*WHzx94Igruc&QgEgpL1tLuf2p214Byy7yl{ujkPMRb<;FD62l3CexiK`uSZ9F z5EAn8n^)Lb(h^h7{fvp&-{13ZM(=##@JW>e+{qRkVrPvJ{!sq9auz3`(r>v}6|{05 z#E93y%^JZ~QK*;_5BcnW6LfBauZw4eCLUCW;|zgMAElDzydEd@7IJESzxkVX?QminrrUe2Ox0h;JLj?ps4>+V{{7!AqK-4dOi8x3 zk4RC2jz5F0A%Xuki~ffYg7~zo$z(T+{3^5NKR=+eWHWhPc2c)`p=y94AupIzXX1A` zwB4jcfhpt~OX7IN($bQO7>Wx&RFr9nbq=uHRgq}!?iL7bHez94ZV|~bKw)WZeVwv# zCzD-agJu2A^z?%29<=;;C?J3TnXQP&;or zFblZO;f|}!m*+61X*+YRS(A{SzW!h7eBij9heE*gds8<6CQt*w(EVeHeT1sl4$ds9 z-?8vTJw0#f!`B(RwH#-2qcw6kGTv^7QX5A__8@=FVTNKiw;YyJS&0dsSE02X&mO6eIGdLeK_IuB?INB??B zLnEWLO!UKrX3P*o@kZ^u(j4Z<%id={)|S1GkL>+GCWeU582s(qX%8tFIHJRWfV&Sn zG5zZoW0e{wz3H0d2_Z%SEh7HLjiVs-p*%tEGJ1q>m&&u*1+<4Yu#O^}2`N)z@B%>3 zpxGc7VIu(V0x}T*tAk)cV*^PV)bca4vpo-;xT|i^@+&z+LWF?00_l9v%Aa$o7eNc) z>ak*wFj&h$lvWHb96Xs=%Y`LU+2HExiho2Z36PldO#+1pt_KYV1OYyExbGU23)Ds> z$VdR!qACY5bNky(0LL_>rJkmi77isI(#H-62#}y5j|xn-AWiXo8EBtD;z1oz0*XQQwchFMKlX)iqibKAvahLcyBe2m22S7K&-}{%DCk*@`lf zlIXzZBH$5)`9VI{r$U#$>&!0ZP&4`wMW!A7S4e~ z!kI>G-m!eKd|4>9q3RHg4Faef7QG!5 zAV^sB5WzVTlTZ%zT!APHV=T%`OD{!5y+Y0W`ZaAZTnFOkg}^T&ZEtK7!88>lZvood zwX_0}#8bQMk<%errPBcn;NhV}kiLTOR>gh+_lI&EbY*pQHP>wzzN7JWH32tJYj{dW zyvW17&&|En#k!L=ZH`}eCRs{C0w)M=4{jAz0F!jU!Q>xH1Dp!wFnO?5R;F31<73GD z8mkQkB}xqN^am1n@Oxc#2ofZ8im?#@zSF>;V{;>rLSc5JxB|EcG_Ab6eO10~&z}#V zXa>n`d{a~8m;~YYK0g^chiLma;kwYb{;C7#i{E?Kt`t39d>qC>T-r~YlJ$!DtM>@& zF^i6_nQbjJQw!yf`fvQKvi0~n7PdQ%lBs@EfZ1ZKqbQvuC9eU8l&SI@S?xs5%z+IB zt~g+6UXh45KEi?pA7pbsbB2cC+PfI8YBb8Z&nMaFp-2Wb%WJj zH@(5{Br&Jx70$77|MI>nZ4{l^+hdsQ0%o0^RuL&e$!oGAOpeI6+i*7v%2uU!&(66M z!CSJovR~cN*`EYydrll<=IE3Y&e}_4-Fc|kdI9Q^Z0Y!Qa+%Z3PY;-i*ozU^w>VtgM*WXG8!U`P&sV5;!w2wED+rqbLM@ zf$1C{P;Ud?CxIGe2{Vr6m)A zaaz^h;|J+xoiL7G-4SYI%y7}W3^(EvR;I1-hgg|=OrN1rMvuBU4+|Xa)ChMkFVX(C zK^EvmNvIM}f72e5&w~&jB+Si1>%k{u`HWIpP*8xPuZSXV$eTTP&F93+LjVY0*Vb-Q zQc7$WSzsmm0#E}daHT=7tURCI(z{W?`M$$bv18U?@80g3pzRBs^ym|XwE#H1G!>`Y#$^tfV}8Xb z;rMuXP)?z+C27aufB1=pv9%xzy1at0T+Z42!HU8|7d7@ z85tS*@&$uR;GkeI`qtpuF02KlTd}dtI9279M}eE{!>J0`5i}5C(-1F0wV@5BG^as7 z2;)MWd~;CcK*fTq{g-oY_2}vC-3~=IkS$V#AuU~iI|%8;%3RP2#y#42``NyhsII4) z24n+bz&Y0fC;)*8JY*8{EfndhpBTqmvm3#-kB^TJ6h+y)xby-IRNA`x@_`8oL>?Gr zg+r1AC$Q;A^oWB|<0u`$$CrY)0ltKjII-yg82bg}4+Lavr_OBW4Y?-V$tv~eB+vPl z7ZcxQ`ZuKRTGFr$;`lvZ!@{l1lDu+FXp37)Rc@jAG2?Yo^@57ZQ`7hgur$bfPpbFv zgUeiezc-CB=4MvbP&oT~2!X$ij$)A|c~~HfBo*jf)Biw0iSOWE(Syjaih%+80yV=D zEYzM|yD;eL3j!dHU_krpHM}Hl=o})L*w|+0=0;|Gke|SL)7U7s$LY{alx$SlBxz#< zlXhG(P$^`?7>$C{KypL7;XLf?k>kQc;TQ&SUMv_*}|mEU27+*c&NK^Omm>sJ6`H^gg$?%N5K?^WD9>4r~YXrZ5K)q)^th^FRsDv3fNl;X9P$RY(;XsxutoYT-gS0>ix6 zL92NB%A`SI{n`S|X74H^6CTW#r1lF@xJA@r9BLjnHt?Pqu`@C()bm5Lnd z91I^1S#W8iLmBuNmAjmtyNt7=qm`38NXE^|+}-Lmt(Tp0)RK9C;T*BcNf z#0>^=#YPBCl*`sL>Jkz-YPhl15>E9QsNYYO3xh^|ltD2VyI$F)O-jgWT!t@;+blAR`q@pTk?&9DR%jlFhqLOY~W1Niz-Uma{!BNsCakP()ON>qBa zA^X1(Zf0gw!})3?(9NGiZPv69JwgyVCL8o4NM2&zp%z-F0v2GK5QEk|H#ZM~#4c1D zG?BL&F;ZbimGn=Ik5{ahzaofuM9YVvOXanD)CVpLk&;vBzObTIffe#sz88XyHj=%> zV}bwb)(D4WCWnLu&3#&@#evIkfok5re{T_l%nCcwt?s}1q>1Ny#X(%F2PV+`rfAND ze~UEQ92G%IBV+&ze*cyqw`vjlm<48>x)N#a;$0LuEb&J&{Q?>dpDCpQfef8AtY^g^HK!z zkxtFv4XY*y6f~WmaI>iA(q`naQja9~W?nXkoBPAZj|4D8>X4b@`P21v^tIAHs;r@8 z<)IivCU~-P@^q}M=qLI4pkN=R^xn}yhR03d`o(T4LHqBa&eQpBwV2ULEUxI8DZ~UxSTvoaD_uz(Vt2k^T}3MgtqoBRYP-qNP4=?7>Rlz=3!>mSOOm{Vee%z z6%Dk`3`JwHmj`7E9u8qp0H;ag`QP2(3DeQ>xGeI7Y)=q#z@?3hesYIR47EEho9VQ0 z-TC;^adIXgKzVc(k&SrO+P=XO+Mk z92wfYj3FS!!$oYZ?0FoWiSyR49ATtMb>01$HI*L)7LT8S*uo$0R4Q-WP2@LKWP4?+7uIP+yke4-}NG6W7bO=ik3*4Njuc*t<+}tYSqWL@5ihZ=x zri+MPhYvrwfDBGge5 zvW2X)$Z%n15sVSg zKK%8bAO$aO?xI7rTp=u=d6IPm74h*n&lW3N!JjHZvJv5iB8|4#6d@#{s4SYF~ux{`QawJ{`LK-Z6sa`|W*TAfHw)KPG z`|mA!z!Y+1OkpdZ?Q8|nov$}7BVz;tRDzVY150iui$(-vqDIEB*1A=r300XS%nspC zYiZ?61jb7WG-no8Vm`DuQ5iJ8fjZ;he9$Sx+=+M!jnAaYZqQK$vrHBSj)mBbZzD;q zo`2X-h?K-M)nDD-68K0vy-;XD_>UJ_t=pYV@!cj+7ZbZ^X{HS^4~LL?a{=Me8qUY6>-G`N zJI01dmGI}C_;L&V&k-CQmcpua#~({gw%@)$)=pL@R`#P?!`Y~zJX_rcI^y`GKy~cf ze=jOm$1Xyex3Yq0(BXVn<8?TPu3e#Hl?G94AmK9XAUc`CRF1Uw>gq_z;O z@L4S#o05U0A@zT0Iy?UrKOskms4by<^yNQQwG1?9s!lp^w4ORT2g|EeW0FB^m`{5$ z$e0S`4mS@b=7hbg6{~P8Ezq@ra$3!Et}Hh+y-w5a>x*9!{PmDazl%J5o@!%zwhM?) zHzYg&9#rq0fE-5qH>BW$dQ_n-N4MLqC4s)N+>*y@kN1l~e94BFm*#8jM0 z0a7C3Bj}|W;;ASld;#(T=*5Ji0q(2gX%@9ugv3ZPGAyj@aopvYzHR@y+!(7I9X1D^ zx~gqyA8k+wVe(_h89!y*$lRa0^Y4D|#9=^d*;Wa5-0zdmvJl4)S!q*i$NR@SB0?kJUkC zli5%idiuL3I8_0$GTS}?IbS>7JR8&@1s1B02}C_1zix0VaQRZ2om`#ypEvO^%<$}N za(YG3sHyWy<(J7U(3vSBo*?~hzJrC;u9bjyz~%xgc7w76)_|DON>|#F0)YmNXKsE* zBVptK?R`9ZpqGcMnf7dD4AMe_BMX7N?wudQlQ>ybWC>H;eL7nN=o_r?eQWsF+v?Xk zcxU+2(e+YBb~ukGQxvbQt<#-R-XlZN)V~B#g&FCQHLYARh&Z)Q znJx6DRU{XN-WMGVs-J|HrnP300+Ii%3}knV|HA7h-=e7Kz;YLRp#P)E^QUNHNrCj% zb?Rgrfx{s?YIw-bE75Bb66H>|qiE1G5}>@=>Q*x4>8C}9^4a|46KQSj|L7;okXHJH zovNkep88_lxGI>h8cjn}qb}&$X(`4g{GnX6AaSw3E0SRYe_YFKF_%$kLI(xdTt^sa zAT0L3Mt30#9y+b%u#YvT7UaF;rM;h~qs%3)A1jVXm^t~0cuZ2ska>4uBQndfP& z<-T;$N9SH6(;)=N0^<|IB-{+A^5=(^BLj;8TG(+f&$~c& z#bFhhsH+n>3JeSZL1CMRO^knmXgOzNpgpg**mic6mKT1oR88k3085+iw zYO}kc@2*E0)ZmX~&~XHD8X;%Y7;kQ`X$LY_BV@|YYVqbh z;*Y=sTkZl~KJ&*zlZ*NT#7$V3@;MDG>@ZSVaGU|kaDS7Sv$=WjYmMp+IXUB(M{&y+ z-;zWHpR8HwAEXrWExwVWCW>m~hfrl=l5~TQN#-VRr&nNAavwCn#m{o{)kdD_{sjr>ZrNfw1 zlGEOWWFFWu1gOboP@817SO{KXR)j&$#Gg^G)g&UcHN)f-3}0JWZOy%tv0W?U)Nw#Z z0sZ>XiFfTJS1gsnG&=TWI46S}QKIAcOO(Q(!s4AmL#eD zq0>HXjZincW6;gHIG*|bbkQkoP_9TF4X_N(dSWvx`=~#D*k=!q8>)oX$nk?O3)Vz@ zpF@C6^J{Sk&5lG-U5^f}s9>utL3ee9OwX+i6CYhsgIH*vabb%)ELooJC1mpWQ5S)v z!BuR*pDSJkBs}4$b63~1U#o@!6q&f5JP+SCGq~xG7r18u)XN{KQqY5IEGD+`Z3=6h zfIzkkRJH(NN+J(2HWbJY={lyNPA#(b+0fYdWPkFSiLH|X)szaRnR*LxbCo`;i(1c#(_(`aK_^g~R`h5L z7i!pkKUbv@hcw(_@%%nzyt(>=akAcRys2bsZ)&49op<$LlhaVdWvCStIt|D( zSd#8A>w1~Qe3M3@w!)Vx5yQUDJ*n9_tl$VB$lYm%_AvzNU3 zO-#z`L??}XGHv==TRr5Xm!#IGiUeoVZn%OjiBO_ytLN?eUU9L2yS^XQ6aoT*MU@{- z-$+tFdeNMkbjjkJK7fMr@BQ(750L{ue#H1gL7JUOd#uZL8u+;UnabIuySG&;@L;iy z>tDOa@Nxz>^4d3Gmq`~_u_W^HHmrZZguZZ*N|^H%fu6mfXa9>d_Qou;yr)uY;Xm;G zE+hm|+dHf@>)(E%T5HmdETfRXMYTXyNAkJ8mC3(D&bsi^+x?KRV(89cr2MZ%xSYSP z2Zg}K_wO}2Snvl4O_WG^@4ttP)Q{IR{oE!S-yiexBRf0)rN4dqme=d6cP`d*lyBKh zh|)LOgR%-G#vw`%C|-dE1jUO-{}@kMjJx{f(;NdD@#a zC|iCu`Re^gP}y!82g<~n1<>Omu-h1${#v8HaV!c3S{6C0y?@ZgDR}a&l=j_=-qh!B z!G&5@w8zHM&yAe2JOl-sX1sc7y>TcxT-dpUY0Ms?((0ZQ(*sKX`f zZG55%M?;m$S@>vOzaciKglmoCqy{33Dw>;J$_JAJ{3hBxN}VTYhkAL0QsL%Vd^TJw zepk{xPR8$WX+{jpNK=OT+mYzFjKjN&@G#@!)w?#50z;VR_Hk$swd$7n_@k4PgnBgP zXIZfP&HkS$g%=ktkD8eIBab`x&t3n#I9ZJy%t)dc_n!l@nwQ;0;LSX*4A2bJOwR>GG754KUe|Dy$`(Zl^UIZ0L`X18_wVoxkDM=KbdvYpW60Uae6WHa7NR{WG*2-P+)H_2`Mm z>lhv$SeyAVwMfrUuZkL?Y+|}TW;Zz`+$H4D?ra1qXY zovh1sMJCj353-gMCXbBYzCAnnuCyrfd|~ zbB+G94QWLqX>=%0OWv!YKdC?pe}t;$S240Ofvjy5EDoic0zi%^ngWx|07KA_N20Kf>3)1E_o9xuiVUpXnWYR;-cV--~1 z_2To}dq3bo%wBGI$7k}8ZT!}<2W~(vU^jmKOY!MmU%!2w8UGsV3Z*q}VAQnCDPBPR zOiZiQh4D-wNFnI1uAt;&ZNb^$(Zb<-1PJ~RT4>c^-AYIn^TOI1zX-c!izd`m?@qGT%R8Y4E;4+0P*w@WQXjp`fHt^mu*YYW?osw z%ER5ug$J14wphWh5TKx+-_~`@Sb~esWtGv!2Yamdb7EFHwnp24*vm;ambS~aV%G%~ zfq0YN51G}cGH%BW?fwYCAecrbc3|!!hNkX|c6xtt0N6a8MP2iPPvM1ipGhYjYJ$=E zZTcE&h40$DcDpcQGy$%smkm0qSp%{|i5!!u0S)gcYTTl+9qw#)`OQ{18VcCYhMp%( zhf^Wm5_6kR4@k#a)3(zbo%7p(DY5T5yoNR*0If-j{4_lS1TX;qXrXAl=H}sO;kR{r zJ3D4?5xB!Dd2Ee^b??|WcFwn7vaX!K%UFZ&??Z;CZ!q%LBn}q7W1aKXheN`F_51~< zE`W`V(G13o8lr=|;Qk5FVDTy{=HKf}v~08iE{{WaCb~MSy2=`pLh_#5rB^D*OBmQ= zFaRyn@*$b=uQM27v~hjmknuN~ujSy}eO`fESwDgvr@Eu$prIBP76y0C!^x5v2p$HO z?Xqd)sFYevY$6tv?{yL+UTh1VQB}di+)PcdU~mC z3yJ-iA%p(9V9m0Sfz959E{r{nzovmGZJlBWLTD`H;^G3nt9~1#Nyri_O`Z1WjF20_ zc;pgG*F|KEPar|jjMUCsw3m96o)35E);9wm2d(wI#i;ohd!n{WL^Uyi6!G~~?L5D9 z+5fbDb8_r>ayOmd*(g@xMMf9YQpP2ZX{`@Q^l~; z_lnR^x3>4gGo;zid5jLZM!+Z*%fxAzmd&JHwH~|kB%NKU!_B@uXJRR=^MAO(%X4&e zxgJt7L1D8~2Yp~-`D%)3UA$_OM7?*~KXy?S`vWIuha_yh%U8d#k_GyqjA7eTR)lU~ z0GEt9GF~!hW_FfL=u6GP!9o1t@NhT;4f-+x0y5Kh%#ZP+>%S1rvy?#yRU5~FWujoE z$P9Ld#(XwHk*FyBZIw_wSH-jiXcQ9$n@5I{`@(FxvBDJVS|mD`_(UW z(9O*i7G14LUxX%eo%bEXT$LsXRbH++u&!n*2R2OiRMIEbJ)#Iy#E)|L^N6{V(1PO4 z3;qaz;#h2Q45PrI?w?uA1h`jrkoh6=;Bb1E(x*ul&`$*Dajg6ZuddmPuwtsJibJgP zEyX+CT3S9a%!2hE8N;K*I#lRIH!T$w>x-|SME_taq^j>ftXymFG5laUd3+X8=yl57TwW_n2^;Dx>Ca# zA?nt!PpquA>s72=E3N1azJFdPLc0C`A=7JWmRD5pi4?LJG#Kg&A-!_YDGlZ-vXMzF z_M@SxUoK^V)fPw&RG;z4nskMZT%Qz5rC8tEW2kc3+RajrkB?u-fCn)L`-Ktiu#Lz4 z*QVWq5GJUVnhOu5v9Xdi&n>v{_*r2WzWmWMbi2`x+bZ=KU;S z^OI>{;2!LRlIOJaCilkN{QfI^vfdtJxM{A(V@HXft4ATs33c1oVOY8RQ=~K8Z)9bJS@RN+ ziGZX%)9w8A136?4lCC)YQ4U$$itQ20tu9|KnV1mG?|J zh&|&PhC)F9^8&b5ccQ#?bE{)yK?{NAOuVw^Z7r5+G8=Xu92$;x3Hq_fn3{%6Ni=t~ zbMow>u$YDEj%~?;r-Tr$?LVOzg%kT=7}H8Rx$E|n%knCDF#-FvwEuQdF-gUVUDu0w ztqwpU0zw1v?_^x=X21-o4V4a7>x`)?Ff+Fdgh2{1IV|NrWKrx=uNM#Y*c!$Ag}xTq zpgVvLWdT9YGL#Uu#Nq%}SFvHFjONJN6# zRi>Sws|0(?#unx)XOkqaS`#f3Sn|0MxOhg~xJ|=)tFJaZGPIb*{cuTTj}_k2{wy^$ zEuy=l6FIW}dF5SN_IN7eVm7~i1Ql_0Ay_%oN5y1GG<@qkYg>;QHV1Bm>jcdH7*RVER=W15Oi<&2iCFTu8R zRb&7oW2FFoX@Q7|`3^{2Y`H(i!^omJ84-Hiw>Iw)fE25sqUH1byKt~!ZMVzOcM?Dj zC&ewoc1U$y#wSkNUESSuP)6c1*tS0|)*2@i?+_SEw?%d^8LC)E*A|rklTI z(1Z_AM96Pm`Sa>cdda=gLx|nop{*5c4sB@IG-IlR7u9#5n22Wr}tt*y!B!WDIIzm^6nEWw!@ET)xAHwlO-6QRmLIvIFAIQ*Uto?2taSK z%Rsz1jr}kF|6xq6DqtuEKbi?REnyOrO()v{EC=h@Sh+?MTWV_S`%)0uTlCVmkCuL3 z1Ab??5tx{k7sk6gJ7=S%y<&ZOJ7E^UG1^*VBNwVdsJZktq5Yh0kmBv&??8LTX-OY4q+j*>8$27Bu4*_U50~@ic8&SR|+Aab6Z$kdu)8 z2y9P9(O>q>7-xo@FTXB;WCCTkRL49mc(f-il_l88)P9avhURfouW28*-y~?bD($GS z=u<@XqakKSnKjzMGQFhd<}SuCGl#M>kqWOgICXS8`(1asP6ocfdAcGp(NqQg*0$L~ znP82iH`+kIvMHZ3*nta0DFMQe$#*e*x<13gz#aKkIOkvZc5Bo!!xTPLIC#-Z) zhm=S2$jXI36BZ_!Pvp7{qRcHUaW15_rafnc<6HX0nv%)VPFB77I}9-8OwwNO?O?bj zf6@0~dO|kXTiX_71cYU*aPqjmQgu*Jo7+hhT0kiq6nfTE$z{@I6%MhTlxAe8jfm^} z?*3j4s zO6P9pmy!otKa`wU;Y)3h&wTvnXI~s;BTS{VWJxW${pCd|MD?EUb=1#S7@7{V!kYmB zjcger3%Ai}vhm~jm6u1R4Vlz{-*OcxTpdffY2g13Ej9y6fkkl-J`8tjzJK698$+$+-PlJqF z%0S$YyIVJtk&!Ld`w(EC?NTHUDu=v+*;<;9{U)uLoGwdB2JAO5N5`TK%u*Th2%C{+H`-#cE7-WZxXXQ;ckJ7B z1ME$d%U0~6Zpgc%-Ccm(j?Zb(l%9!iwuJei=o6GHL&-?mPOHYrfn!b~y7&i~ofA&q zaIl7kPR(UppF_Z0?`ENEoQ~~EClLy&>TtoO`#-NElw!IXvxnAqFCWgSj*qF#j}Kz6 z61hqheE22WQo33}FD?|*D$v+)Q*(1E9gIoB1r3|WsDL)dUbMgY^B7B9;xv3*7F@go z^M1Hdj}e6Emw@b+TQEof2BVI)w$!Mws1&-a>Qr~Ug8~9r5vck?KQ5Uk`VsM$$dGru zRg}5r$2+k?R1Uu_Nj|86`HDe`_PY9dQqKwSLjj8Lz}9)Xy|6Az*n2b+l=Ts$)3Qcn z-n_B+L!}r7&j(QBDo(9wUzL=MG}sJ*65iR{s^jR5MGC0}%OvOI#F5~fJ{1^I9qy5i zjOm$TV?RC7-EYzZ)>CAqARj)YG;I}@YxUUTI=kaH$f{X%2(|1Gb9Yp2^E9is`?Svz zt;Z!^aiVBqw$`R=C&^zVY<=g-y;CSAq*Vft>D|)WnpA7MLr1u@=Wg2ky9au{4;d$?tAd%KPef3hNVMR-C`Tc;GDV zA8=u&!^Cl3m#y~wW4?O;V^<()Y;27D92|*J1KTi7Zk7>vp~gsFl-*wIlYur9prfF}02Z2RxdnAC>>uDUDI)?jL$is-XY+o= z=Xe^({I~;(@XYJfytD}B>r+~`f3Oo1jHkuTmW6RvVK%F zkI!~76nV{kydSBl@4ZY54a_0#Ft~{cHNd9FeLw~#+&YJoT>PepfSW=qrFWUQIOgQZ z8OG8+q5b_jvxFdaQNJPyhj`g2C4h)HU!{E4^D~!+WPF^fsDGTDf`GWp3!TUmJvH#- z-Bk7fw;A0$jcC}+J`L~dukOH&yjKNOaA{hz=_ApST*nQF%#m=*V+YhV>=ATeQJVp%+2dOJk8yk{{bW> zuh>{7pU zFh_H(Y1EfhaYTz0QW16F-#d?k-6M>E`@{IoxoMiUZ5s`INSU&XzXK*9j)mSYH=Emx zN^S(xqrwXP89}h}it4EYIAHzkM{s*h8eK>>HZ=Eryms~pf4OfQkuL_`CKsIj=4{sX zCo(<{ZJWGFPZ+7o@BXv094&W-7{+D*I;4z%sQ zK7F4wZ8GPpV$3a(%&jcLa(_9vpC5$i$7RrR3*#tR?=xp6brOdVVeDJ#nb8ib<$zT< zRo1#5|2-rGhXl^dej_kI2L&q>bkEGsn-f+7rOkY;LNIr={h94PHDILJ*!uac5VX=o z6cdtva^`Uw+Lkg*-(hCI3MiL-wH*}xs~u*C<(q||+!vn6VPdPByS8yzmJ0Oz`~cMxV;Req%}V^6D=0f?tK*s;Mb~OlrvS@_6SfJ_O*gR z2-*4Z#J*k^?K-A603ps?n?~wPs55zeMF85*^`T%}q%{Cz80)NWL5;@MVOkaQ=m%9m zo!V#B#cBVxDKcgDQ>OeC_p6HI+@=}&I6M3O<1<~p z)>o9lsOs;^)kiQ-0{(8UR+3~2zIoZ!1xQ$lrmSP}nIWVAV=5F=(s4^4WXzQjXYI(b zA?f;}Ik!EWKWVLSI$1sX?;Tk~Ru z?Mk|T7mT?te33tW`UJ=vWS|Y;n@A-Cn8*d~#*czjRk6PYmG<>`sUa(8!B7`e8ux0<$V8|MhtKv?o}^u9OSRsI@%3ol9h7+*vV8f zP2@uBUyYRf_s@`a8E}9Btv*b5#k%#K%SU2&#eh4hZzJ%j%X+ zteYfaCD-UCsbF$>t*CBr#DC!M$G3Oc7X;Y`+B}wWxR@%+t_JGRI7R57f1-M6@M?t9IRsu@eE&q&$FOq#y~;}7zWH-RvU2QjK_v5r?MvQ2)HNj3v<~9BfcqDn0r%u7Hd9i- zX2f4i6@YE5U&D=!yLP%s*~Ss-+Hyvd!b`0;ce_uz5b7a0c~x7uwT+F8z8sXhKtk$` zB47G+US6%4rR6#QPgG^V2u?9^#lt5EjiCEB(Xmil$7w4lLWzxUVd=BS+p|gU`udt{ z(y$GMWIDJ1=TVEkJ_gHCzvny^sRugn#)V$JBXf7)9pG4?ue07X>h!-rv$UDy`u*7i z3FTQ~kn^IKXPClzV7X=w15_gD280^c5e0(M5<+lq#uiJc-{~>=!u}lHUA3jXyLv~Kzr)x*4Q-B%bU7pc1OVkeqLLXLEHeb zk(~8TeMTN6uUY^^icTw$Cprbm)KI^QD5)MsvXhw}R)g=st|?B}KLuK5QHtq@j<^my z^TkG6uI|pDxcPaZI(L`Iv@nb}8TIKy^EJ(-Y@u_hgp?~?7&Zg7v!3;$pMz+y3&f-L zpdBd^0hhQKHp<(qp@ZQfTEYU-+K!I?aSyTa>p_JzMInGKzp=?7DZ9zuav^=HQ;15` zW4Ld%V`gn08}PNfR8^D)h=lS_m6jv3+oYo2AU^&E9^1i}MbJ`RNz*;u>p1lBrilz5 z&m~OYLjzTTNR!6Rp}N{~+CC=D*tcjC7B-P!`-I%=LWv>lijzNuv|1)Z{E=+FvuWb z3P?Ii`yf0?n=+oO}x(!z@Uw>E>V$sFI1rI$_70IA0*Mt z%(fA{5b@|=lsGv!_zGPO_*eL$D*og3=~h8N$JunZ@hG6k$0^_tI%OcNh=>}So2A&< zDdIDEaadnTx}5rooEZ1S%H)I+hLe4?>g2I+G*8!MRBy z5BnJcM(eBW;iyxM!#B6J8x4HX`8L%u+j9i^Wrd5l> zcb93z@4PK%FIhzhef8-GdxK)_7|%HG*b`5du>YzWKhYlm|5~i#XAis=QYp09U?=X2 zMH)XK8dmfnmAm*OHWi$G;;MYT6ySf z+e=xxY7L6oodNHhhj(4tW@hFF!*7*<6P^^6S0^M^p$r?q^C|=?|FRB;l#>Y;3#dWI z8UAa5Tg2-ZEz{*`j5tZHjEoWn0SO1RrB|{*^WYlt#chnk_+si-a zf0R~YT0Ew>2O zhM7m1*r5`1b~p4kJYNFJ$jDVeK{n8-Z7<-w8mY@E&WWXPeKH)?;%xT%O{~d1YHyAbgQ5T-IuE2;e(a<6rv*1MRFE_tMsf1K-opRJ@BzpTl5#Zr_ z=6OI7|1QS{P-h!@Xy^$Mwilw0YG@`BKH;66VgmNxQK;AMfhjGtaZc;kN*ytfD?p13 zS*$gk`O&~EeRVtxXkvvGWhmDY61KC=RG%9fT(1`K1_R>c*QI3Mk6v6N{}mfM!#`#9A{{rVYW68aW0S?Iw?$B^txrz znaYQO&Zs3&e*Sl9gmH$Gn;N*36=c3NIXT*!($V4~Oow@06Y&fHe#gL*t<4evhsaZ- zZ^*&OGgWJ|)n#v<=MziI`G=$tcC~&BK)lD3HNZvg;mykW8}Pg^Uoc%_Rg|cIqZ2*Y zMtzhrmO)C$A^u@$+T#yqcMN#zoUJa-s@eNfwrB%b7i% zr|!HiFRzdWv>QOD8t7{xm=h2HoOXp328}OMQc{p=-fP@CnP^0#d;i{gKF?ti9QZJR z+#H^s5pN#bDv7ZBI8FIn4~~41!@eU|-SzY-T3Sw@`O{s;csGu``PL*hZ)6Kz=V zO@C)KBf#>{7V5vHTqF+;^t;WTh0W`j7HSF6!To~aJYDh#wJc>Vg( zoaV*B&W^FRx`Ny2{|R2wO?{F{%p+fTel8PbW~QbFUZ{tDD=W@%R0#34Pv?k?lQRUQ z_Om6iMjH}JgO*+ZH>k_9GMg_HlhuUh?~BiciK^SK*GXJC~w|QB)JtXNrPN#%t#xI!JIUb9q|^rc09ZrSMM^83{IJPXS~d7|LbLb z4rlV|i*}a_?|(0qH?S%I^;hqOC9K=ud%E)aV$71GK#)p}W@X>6WftwukLyHXjitEJ zw5Klc9AebLLORoW8>HlNa)Sr{J3G6&?0Ps87~*S_L^HgZ7mA+kWYQKsw^IPTU$#l* z=PnEzx%vfXEAZQ0CYg4<=rv&&9KJZgqqrxs!KUf8>9%>LdKZGhEOU0Ui?LQe8INfs z@fN@KHjo{JCK*JmcCw0hFdfw5v|&ES3D#T$8u{7Al}d;&jY-eo{U`;g^hiKx_I`8b2@F(2j+z*%8~er^a7*reNfX_eN*D||B;puo{AdR=`F3nipSmvv>@wC03-NNcXB9o$<9?)B5sO_p z1o|nDW#9T4NAvAUcvi!P@aytkM^sFTM0Elt*k%7z3F@$NE{98Xm_0Kl4ka^g-CTvE zC?**JNXF7~t=<=lqR(p-G51Ng;2Hs#qt3{7lFFtG&rXVpK838^-_Lx`gVs{(zCu6g z@-68~a16m}7n~Cm*!Vg{H4impCY(Puv%wir&4b?A0%X9iTw|zpq2N(wQMCsu06fnR z$;c^6e2#=VZHhnutSAl~kS{MbZv6HMcnAQ)keiu z!0v8wB64ihOmL}&THr9y&!4~q{Fm`8Lf-Rtaj|n-k@oGuAq#kB@y{?NZM2oRv~K7U<8eKfwL{e`f=wF6?in zq&X(_D(eNx--}Ig>jd&pcAL11ut&vLees)NF8-&gArh*8uI0`utRVD3CL1gqc|uYy zTY_FpOgMy;JBL^m{$bpaB9ihB)tI&GWxWJFJBZuAo1VHBVxZX7T1vW2xO6daVo8XHI=gHvF7KX>9APi3-2 zl6>MRm*28XWW-*cK-M zK>gz6{A!3_B%91k4e`!$WcoDbV5Jpk+PMs8f4!Xb<*s7j|KAHh!~gFuy4b`4bsp{p zC?5dM7e){Fqsz~gY*(Z_d6$! zm_O(6HXrl1SWQ%_D_Q}MZLXN_W9QSgw+0{KXp(LfZzF9}d z)}HY|)U?)IhGV_%)rOu2y=B!3Ifsj~oE((G^OU{KwlmNY9ZSsSHWr`F_g}m|;~^IL zbMi3xMB?(Ll3EC%=C|`KsX>kA)%IiUukgU*mG3Z}bqVdZ=R#e2sT~nvc z94c^`aGrDYDaa_!88-VE#@G#q|M<;gekB&t9yW|@F_Q@oxmxM;?VGC1=&cu;r#cf* zchS))@ucc}smRW|>wR`C>Rg|6t?qwg)H{F*@_hL19Ag<&yB`0XeI9t9JkN{6WsedQ zo0NM)ZS9*gZf=K9Z!q`!h3*sz)%80Z6#tZHrYXa%f*dcva>}rN_Z`cX;~%m6+0xJ@IhF zvIC*A9>n?!`#FU-4n@S=4nv5gv9C{w`tNjvCU%fk+?42u_u2eh znGltvD>#iX6m2VZDq({3NPU|>PR>gkibI7214;tm=aK217;e?!T=e*fvU(UP7f@wF zrIX0TQ=y)ss|E}b)m1P0lerh_-@gamaB2xwm(ubzUj-DD{tr5iA^%6yRYpbmecd63 zM!G~oX&72cx>UML8blhTe@J&D(%m85-5}i^N=iz1!+ZI!^|Jgj3uK0KpL^~(`|Q21 z=iWd)O_;%&?l8ujt_5C61lA2ysiD}cmXC&?u;H+N>bZsQ9V5du8aSZ zP0HCj~0gAcwrv$v}&bL-)`FBiPY$xCXI?p!*0v~>1x8fZs&OSBGLbmE^0*QwBG zc3D*@<)H(Ej2Li^h{Ap*l;RdZs$}T1Ag(}i8Co^00_j=q#zVvQc%JF9<-j)QXkrz; zjg%oVW4W^QB5m~E)RCs9Uu1yx&bwq2&A`J-#RMCuLbX&N%K6^X6~y3&*d3t#2mX@M zG5nj-AK7^-w?ndIhj>%_5 z_Ui8_G&pFP{sIGo=}h?D?Uv?F3`&c!ov}Y)ZA{$s-ndmNUHReo-|RoI^CC$rK_)Lz zjI68@n0Dbqy@jB`OHzOmtakf=b+qX7G?GG9mswFsD1y51x>slx-;V;qE^KAQ07318 zR&W>^{(#?+J*2KC6!F<@>72}bmKwnO0a^G!$%bx2HcF^~vWVfp@bIrgX3`T-H29w# z2r;POQv$V>2U@0JvC$Mp?nitOlv5Ct&&JH&xaK+uoZMMH}r z{-vTiVo7}#O-RVN#JwGTK@nH_NVQd25K2kcxbT9El#B-fdR_&4zWiD=Pk%4GY36wLu;<3fm)LBp;pd!^$2YeXb=$F@lsh9r{) zzf&R~pM~=4QB%g1y`jWjaGwJVl2-q43FBP;T5y=CkWvXx7a&McXCZ14wnHUIho`P2 zq{M|PzVVvZWY->SmZ5(Z=H@{P%c0|5EG#)`u|7SkRkuw}BaGq6yh#ZpJM+s+!8f-A zdJpa=H{EUmEiMTS{$fomS;|=uZUTs0Owz}8f-J~FCL~bcfm#ZV2H&Tp*QOa z4aP7e;Jf`3G#U#a?Ei%Unio%F4kt4~-Pa_1e64KnuG7mI8G$AjECAd?BZb?%um*%< z;2=d77Q{j}xM1ZnH1S3HFn-PVeZu*loj?|ZLD4(A$v@PnZiA5#iXXq4$wmCu;w`8H zL;opxf1scsPDwufcXN%^)9vPRveo18l2!E_u;GPg-{X@=`98#N_%ipGP&M^0>D`G5~e z`ie@B!a@L~xihF?(+nalJ*%O#E`ws|X!8_`vh~dlf8U*x@e(h-Yoa0d+&^!Zp1vaE z#vLskIe9!Fowl40wnK}GhtHIQvtk57lV`cNoKH^g>S7Q2_3f^4k}xJIzGBiG{O2AF zRfhk?mkOV<`1wOHX$a`VGJ}2HuI#loK6*ebAM$0tu#WrqJn0tQwH)7zW)et8~2|6U!uh9z>#55GJ)axz@+Qz}aV^!hX z%RvWJGVr*|lfU!MJqp=Zf=q=<0-AnrPy|qhjABUWh?%6q&tqcXT{Jd?dwEk=5bLNf zJm)4t>h!`LC%WFA*Gx>+=XXS}o`D=2J;6%1GbAxMkmf;XY0$9K4zq(VSgA}WUZ2Zy~a>kq|RZTeVj#*1wU4RsZ~>bWtZ0OkEBiz2fz3417taIw+Cb4_1IAMJ z&S+Qj(YW!Olt)p3k^<;IF?3_9Nzx?b6pHH>b{&kU&;~r_e zrsUxBY`J2qB?B?}tu(#JAwBTG0DR~WF*qyPLy6=HMH6hFX`shxVR*Cv52(Rv{{(=6 zOQPVfvFM9`9^)>cPtAeJQb((czLjokG;pV|M>7C^1i-QdY#^gX|H-63sM|t}sGLzK z4a8>`OkLt*#vT0=Vt^+X((*Demtw{I!fASXm)GI^>#iF<@NHN#t>f6jMFWp+bU_E3 zd6LgY20U?M#YZ1_X&U%KMM%V8p4#t5c9hl!r0nF#NWgT}zkM5UPl=C3$h%~!>E~!HH#Nxe?c**eBLrr;M$0rA* zi&qryJm0)w(d)RID+d+{>CMdMg;;CkjWl^`p9Y` z%ix(rH5f;SMlayz2-4BQLN{P?{eb2*%ta}VseV-ksA*2Ya7VJ_+2+*qhGj&4t}`(} z45w&f<@F_(Jya*3-f4^dHU z*7L~X*Z7pO^zA|tNIJVl(?B8(!lz^cT%Q(ma4x`iC+0&wg^DD9$y~o`cih4U;za}N zBYJ0jtb@f!TmAN6?=Z=L?lpt1RH$bC=hyc3_CU9jOG*r6G0)Zyhq!3L+^DOAIRFqwScXrW;rbc54K%7EAo)r2oP zO%&+k$u(vfGLNSL5O{sIjis|BNSstk)7$r&pEHIV8asQqvd3+Bw1o-!bs8!PZjiVB z=X?~+ByvBHc0Bfr;DaKyC|rOE5NTpi2#0K+G8~G{1BK_w-IeY8x}cw0eHdV_+qG|( z^CKmN36!n>bEE>lMi33#`qLl=^rIVl!<(k<&}qtUd_|?t$A9mxj&J_DogW|4y;CP( zDq9jzTpAC?DS?Nq;N)Z#6$N@|bBDs{qqz|NXBLzV%;#W8F~FWHmPQzd?EqIg0iLmm zjn|AT5HZ}ZDin>A(-KXy9p+X3I{%9r7%_&;+V)=779Z~(X#Y=O05W-%I3RJN0oOU> zmyB1vxcQ*^dAwi}Xut~)@(T=R{po&1{4}SNs8%tDy+lzX|A0iyZqB$R|GyD&7Q|fn z*@B4_xO*{(XmTo=xS-~laVr{*8r6XU%fs^bT&d5|@sDTew{5!Di>Wj4I?07BRFZ9nI7Tydb*trmHeD4aP zU=J_p79P2Y26ob;P!?!#31t2rlR67R{kj~ZjVW|11a0X94QQ08Sq_0+L+d?~*awa8 z&x%^@rG8>Y3yW|?krU-|CA{nJWZ!S_`hZYi_SwG#==Q+DzP^oSpo|*Zeny+b<*4nF!O9)_oz*Yo7lWEVQ(j-#^@*2$=L#9C2 z#_rkLv!@0Ay$bpS)<`cD^W3~2%J`L}%{_-Ldd=mfbJx0m7w0T>o1ecBB-ybEcB5?O zS13BFm?)bo(0}re{iV7)Y&jckar@GCCb&J!m#D2ZWTio4$rYtDjSbIN#J%wg{`3Xp z<}+NCGQx(q-sS-O=Tmplbn1r!RfGX!8zz}41g%#XF^Drp**Oc%u2g<~;R&xi@&rg) z>~3&{JdUF+-7_aQ2RXkEBle!}%IjE_ymC=POBkn!gyL7~zy-Ln$$laz&sDgY(G|8|PhMni;rCGL%e_l5z(5vGg~iD#JzC2PX?QEgIUVt-w2F|mNNSX+2!y z-yn%VuQ)#R{<2}6y3p}eO7+n@eg!~?hO07x3k7adeul!;GG=<-l4^t&lv)sx_k3-( zSeJRJUkjDh)&oyDj&pN!Vf^PU;}%ls`m%BnoBWrP#aama()<(o+GApH5OX5$FuF+P zd@-7mHank@NN7I-#Jb_ z>IrIWMMbVy#G>W`AIja`zk!?@vWzMYq+VLyCRLGw2EAp^VqXKe6)1#6;K{TCit>4W zF4dcItk-}9McTrGF+~Gre7kPK0rlShH(N4Cx#Ig~X)}j&Z9eS;)h5X$!7y)=;3p!6Tp%uNX!~zup=HU|{9W z*o114TF}ggQP1j%mDQC2qLbi*4oCu8)kfj*^tvt5-KvcI@o;t)Bcddpz+2U9?!n z6*?lTreF2?xBraJSpKZg0_*0rPW-MX?C)YlrH-bznO3903+pCFqQ%!}Vv5pnPKJj? znBjZBV+fkDl(Q@@;FS;t++|lY(c?h9{n;$46aPJVxye1ZV%KZ@us@$y6!XZ$}{_Lu@k2f5<)Iop2BPufv zd&T-0R;B(Pj5c4|K!+z$cxIsZwaW>_C}@kwn}FiX72fBb9HV%y1X>+m{>AaAtU=f_ zQ6@!_M?=Kj3g$o;o}HCL<76!}UT-44{H+CBTSsFwO{!a2eY&2<_>Zlw*6CcvaK_vB znx;#_4(7Y-B1_S<)6C{wHLpEFB7^(yJ=b1sMLhZO{e7=EHtYXGcM&q46^K4|xbm78 zn4#WXP}A^{@DREE(b->Uw<|q<6lrHYH~$obR7YE5U%9=Qgx7WDxHkVf(AlhWbY{qG z-%P$W`u)AFnZA*6b`rlcFQ!$5`MOze-4h}D@4NkrNcBy!;E)hPy=MDU`lUV9tjbmf zOl!GBj?6h1tU%ZHi;bD759bs-0cczn4&PSFbT$Wn{qapI=E=${9ZBbUdW|g-)lLBx zN2yEaPwV3K@6R%CBqb#SEG}N2F6`;HX@S(@{h0@q-kp&0$KB5stU#&70e%fuJR(w= zDei;MUGVnBZf4uu_a5$OG#Vn^@uk(?J|$Y-EfJkErt+ITId}Ul|M@Z53aM1SXMXQ! z{lQU{8q#;Qzq-+uRwC#g`a471Y@(70%=oEkIUKTF>hzC_qR)}+lBq&)nSnKTsp`S*`=j2t%Rix?&Skg zQmOnkMD;eUlf5hX2y2E}0!?;m*#RTdlcD|Ph9ZkA#gXaa8Y|wLUUJzQt&EPvYUwh4 zFGBbw!BbLHP3q-nB|7(V)E|n>anOF@Y6RE+pP@>FKj9nn>n{!k$)xB}K`(cHuLHFh zMeFiJo%VNu8_jLW_O#TnImNbg);0Mo!=c%VSMRS52Zc%Nm8eJ&oU<0-+wjhMSGMs0 zNtQRBnXV=OQwcFReXdU@NI#x=;>_qG0L%mzkL0!0+0JNbNzxM!0F!n|Ug8qriJK{A z&I?|eq@KaFxnJb@6V=u@Db9^ke)9j3_~}#F{0qdLon1rPqQA(Fp?f$q_TSBJt`9Mp z-?=343XP}9Y==aKcNB`5+`9|GDdsr~${(#rMg+>)&)5bbL91!9!q%6|#}zKlst z9d^TVxh&Q1S)oEUuu;L?W;DN~19Y@^=OiDcEzrge|7;v?o`HV$-o4iVZ57`MhX-lf z)TWpaAL3qq*lQBDD{S=EqG$T^m6!t#{#74Vp?U#%@*5lPQ^MH&HkuEi9mq<1yK z9v{N96uw#IWxpa9Q_=qLQxC_QlZE#Ewa^6yMzE9y9;%w4(vL1qIG;BC>+P{hDEmTw2v`6WNnS&nXyj>fY+`@4TfJT3Cn}j{#-bu#wURD9S_oy30COnT|*0&Ok{Wgf`= z3dr%8x>ieh{P|miH~CIg{RE1w@`BE3?Y&7<$cgV9A$E3lxC%vGyu|8Vf8hV(=KpE= zDzC}Yzw*7FoRwa?5DgM%F}t>Y(%pq9h_j~O=(ZVMfw?k9p`@gi7apAP6me#ns}8lW z*o%rzm6gymBk$nwLhc4hpeGM6$iXBY(&+>Dr`g`e%Y|&~Umx~b`w)dM@Cx6atQ6<- z1t!9;n7+u<9uJu|VUok;LQQ04KVt7$TB%Z_p4e%rnq@DM3z#64X9%s&Hco*ejYIKd z@H5$PUniTn>K8sAZ(=I?<8G>5eVb2}vFQlNIeDeJnWFWx%zDAveJrt#UtwPfqn3ES z#H(8gI{o1L`(dJ94KLn$B`iR0z`fY#bO>I4e_^27&E;1aX42b;N-tJ}(FPm|f3KeT z>2QRUIhATpDjiZDgv^zl6AX*r;ja(0m5Wl*f$ZbgBG5s}ux{@Ag49?2NE~iCMJUkd zf%pB~5A^4xZXeDg!jm|Tc?Q+W-XHXi{QOkl$?}(IV~zQXwc)D2IfkQUN8;gu*>pYv zND3|AZiADGxlIb)4p;Rs!sULdmuFqe%W5U%lQr3YTakZvK^hMEC>y*DswnK-eB4*7 zV?f%4gMv8xsTs2X&Aj7gb&*E>K``Rh#=5e~0XS=iTBPIfsQ zsLMnD`8&JT7m5!-@3$?3xK={g)eExv$0-4RsEC$k{ z?%R77Wlj@3Z-+CEhrlGDR}Fl$&*4w}=1+k-(RVn9UiNLO|bGwJY{hL~?dn zmgoav>G7$1PfEfVgGwa_LZ8C>vGL%++JCu)vWi`UqvXw#Cck=fa}nb#nzD<_71+7B zl53BALBGo|Af>smX!yk>rdQfxPV|FC<`BR^t6Hyn@&0}ehdE*S5r##dyD z2)VUVu?wX)jc9Y8WNaEaBX;9{QI++|AR)8=>iimfCps_HzxPuA!5~p&FeUU~XT?4o z<#ux!IY0K^(|}t+bX%6vxL~;gEpz~$W6;;!>kTA-R9{u@vuVZua{&w>QA%a{ydN3| z)z~-~l+0kOZcZ7?t=;=4ITg%m$qeX(DqUU5TV1SUQgPAIVzQre6^{=79`qcI{5!e& z7cot=5W>h2$N>I@cUGXKF_7q8;y?gR3?#G7-e%dqYI>8RMFX1=R?e_HCufRqoU}*Pg85t zp!|4eI27q+tiZyeLiW$=s-K+y4Yq_B^LiMtdNJ!tC7lw4{TaRdts5tACnzuQttW^l zotlo$(D*30u{Huc<#2?VUr`@wwM3k`RMEv1pPSWca|C_`efk>RnGla=^YLr~bsB4Q zUCh{&%=vl#s>gnFd9wMTpXq5omC9dxJQ07`wP%qGg}S@N_*9~*wVxO;$P!XUopuJK zad3>at7SF$Gir*gRYFA5f2)lu(48eSt*%Bgt2&hW`Kct`qe=_eL(j=M-e_af4>rOt zI_?crx{cARw7tps=4|x&d!#^%he~!QlRfm~BN)3tB+I-h-UvvLYl_I%#+ob-=s2T? zgoIe>S4WE)({9)*Z&sK8)_9^5fHIY4;(v+awIteHc^Q@D4_CrU z#Tgh?OVw9lA~lZxrm(8d{ZFgj5yxwY4h+F7f@b%!)pL9N0F>06K4isDIo4c6@ng3X ztQ5l0Xt1*y%^@}dqera8{y=EId*!2vrpTj99cZXsLmp_Nl5`k`K4(RJHBCuQ9^8r> z^3TeL+T+Lm3B78r|J5ro6b#*vQ~b=X6c-z7_NR&L_n7YH3Ujlfy84<|H_~{%v{w8` zYUo|}-OAW6T4X}SugygQw2@CFn%?2Q!i1=Vlm(b!C0Zg`#Xg99f$$>vg}rsSp1Fnj`3*PTU_c$OHw4ORd=0IlJeF zU*T~&gv*Vvvvc+R5;%AY|B8r4Na+Qk|N6iEVWR0xnL?_%DVW9a&*s*a0f7T|3~U`X z$Mx8ZWT<<&E-3YfOG?b6$x~rmTybE@u{X9qfbK~Y&J)r7WUVJB2(iHH1tx}woGt>F zlT)l(EXb&mKtcmfir6@{XPJ$bB+1PA9^>wu+EjUQu#N`KtcIxMwZcXlY%Xr<5U;r4 z&8ZH-k8Pmx6#BsT(6sZwSoAZuKIKLWYVu>-7EJ)yGfm>kF6Oq@)#=9JrP1JFQBU%qi1By_Ra|v zp#%|0`xKGFX+D7#y2(}G3*f2=12{=~>7N23A}ENb%L^djsG#H=^c2I7X3lhv`-iXa zU6uilO9ICf?q*4i0bGW%->Lhup_VyQS8YNaABc7ov4nBkzxOh&#kV0kVsUYC0M`g?%BTGV3VzTT*_!!h zNudG>AsefCpRe^@r5$}w@Z|`~^{H0Xiu4*Tv>7xP4}TZA z-L*&g8^){f_{hkHOnm>03zvU_4NiW+=Mt;YFtKQOaFq%+VZSIT>Ykpk8{<(7&D)qs{$B}mmY+fyu`E%EiC^2>>Pr`v?4ah-ITW59 zjg2F|m{~-bkn;B8UjXQvADN*z6hG2EzP>eiSkbL@K zywL@J+FbmS&*WWQWZo%^J|K>_EmONOF~M&)$ht5DB^0C%P=j?H)cN(&&zG?B$TK5dIMMk zhn54!>%DOWOjE=;d{x5~uCbP35T&iu@~>htrm2mA$x*-em)b_BHAlE2A5S(yN~{az ztV+nhHyGA;%0>x_xZGdwC97SgkdPutSoH~`3fl5LataGWLPJA+p&!{_ zunSt1GwKMQaSU)9q*AHYF|U220co-GO12E*Xq%jz*c|IACJ{8n@xnS9YHFMc35)pg z8a=8rQ1S;;rlJFs6509b;4IFByKwSJ$n`4)_lro?67`(m3h({B7sQ-!q;AMbKYemd zK-;d&U=PJF1=e@NrMb>}U#E4Qs++2YR zwUzN;^CxSA!>!iTh4!#c=DpL6*^7)oC}-yAq*vl-wT9wFX{Ei6xw%G+my+d&&v~-C{I)H_9p*XrZK7;Zcd=< z2s?#)(LMO2#c+1#k9GF8)wsh!BxA>028uKL+nKK03Nq|Hr$tucN1m8*sxkYS&dXr9 zeL;wb(TRza8lpEiI${pSaM?t*1sRDC1Yj)iLqi*fF66#q2+8_g#ls>M=qN{iUXI+@ z-b%b_3uFY>XTT_*EW;IR{M>myrik$FhMN7idf_Ex z?v@`si4zoEtGxegTy)lkal|ljaXEd9Bp$l7{Qgl&e(UO1ZGp|}eN(dh8t!c?t7ng& z;baA*&_@JUbfLx_72$n^xRjRGt^8m=jp|EL^XVey_xbt4gy>OzGC$uUcgr8FFylao z0u(2zzQ9dNpjuDVx;$?FOF{leLc(d&X+N9RC@<#z>LF#?U?3ysX8qv8vfBF%n4ki{ zOTl$ylVq?8J2pXq{A{h1&bmG3KU!hmH@i$fdV6dxx*B&Y13f7TDXByUioth3NOq(LYw&P0@mhecmD!!SotMT3=+FiN-yh0SPCfBs!OOBF1WzR4fRQo{5> zp;P}N#B+v&=shKRf}$XT_-#bSV0ts_PtD>NU>EdVA6mOQK2%AZ-cnUST({7!Nw5Mp z>Gs3yJ$JIb!+&ws@66g7Oc=u9?x3@#^Gbi^K8%_~8N@omWt=IK7D1@zzWD;j;iVzz z4-Jk_CH4_`5om5^F=5xi=Dh44s(rPhDeUsjV^72IDA~jx3LVtdBWQ&8#4!G93mweC`OH$3%E(>=|X%+?1jt@jx{#IViIioSZ=$4@ZI@1y7!pX%b! z(XW?F0lf0|U^*>==!k7?Ma})&p41AGT9Qx&oeN7k)lG`P5Ql!r^V{$eMiHQ#CNfcW zj3o>e$d9}21iM#4T#5Z0+_G-pzsG0Fr)_=t@z|l!U%I;SICe-L5Zb3*X zlQ53PrJGdcF6egTs@N*3;tbOkDgQKTrT3P!{W}F(?EiKk3wl31qO0UTvSN+#d~F4c z2)WN~#e?PSy!Gt5{0`AknH~De;CX8!pdh-FsfI}Xu3TRku+Y)wT-n;`|gc^7b#(YjMPab_|w^u|M5H{AF zd-P+kulu2id0t!vnJAw4@k25liti8JH=2v6j@lygzS7%PYp=eiQV05ewv;m-d*&k^Wu@~$yk1>1mo>j? zn1GU%kn)qukeAHy&1#+j9BzkL?@aW9tJVX!X=(DljA4KQcc556Q^dw;=IvXv#Y%pM zda}85o%}aw*u|t zTVhf?@u9&{D?U5IK9119viyRtHFQBe^+1H)!N{01Ck@{b}Lg!1N2xY~=SR041Q zY3Ib}b3Z5%knV1bj0_oGL;6>5IHKkn=84I58aqZf`&=FWqM0zN<$3PC0A9TKcn&g2 zaNbrMwW|<_d7qkhW&7uet$oilzl`OUKRR5jnBdSzJVDk6<7-1_LNd2v9|@UkMLJW69$ESLPP6W%H5 z2i;IDPYgtI`c#u8f5r{QoX3xwoasxAosAy+S{!!NTQmnhH$G)ZJH1qF`$AUZ6h_1g z!G{tmW-5vOIBHg&s*12oN;*}aDu`&?isO%9 ztaGYr$OJ1ZnV`s2$efin!1B0{4kkSBG1bb|;I=U0) zNS7&`)^t1?VU+HGBBm4uBK#!onB&*d!3>ATESsq<$#~EQx%^!ZsM(o^TDpCJqDLKf z_PN*==pMC!E~mETu?2Wf{WUuI@4p077n|erStp_PvtMAO%h)tq5N(N&%BUt;tH};U zr}u?H;D!$~{32G}F!jKB=;UbXY?6_*19iPs&c%W4+T|xBT3I3YC(mry=Kc~fR4HE_S6vcXB(q))GcRo=dZ=(T< zE0dniX4`PQPhzEvhSxJcpY$K6#BB~iXQOVZkx+?~UNpE}{GRS}kWT^OefQdfGK zq7;yZzKMl+%A&KMSbo8nnw{#ab+yzgO{e0lz>oyEJ5KKA-L=5&w1L6N{x zMowN%SbW`Rc_~t~qM|1fAvQT-D0uC1_NBjz_|^VVP-Db_!U+6nyWs$gmQX!WQ&Xu} zi^;I{=P?@y&B^K4?ST=dS333KnXE$dv5bD=?;Jp!aDH=)8tF+(;=s_gF{!0=)ua$j zK!WrSF{ReEQC3!-Z}^PEz%WpAFoB-tsQ(w>)^=M1Oc3CouE}A{=*F@#IJJuK;0{vC zC+9<26;ao3N8Dlg;U83FNHLk^UJW&kyZs`TPSoyq*NC$vj7JuQ1D+Lee2_oyypl&` zwsJ~x!vSiFKr5f4{&!26*=p=)={+U3VxjZWQKZ*H3rWqtiJ;RN%YjsAFyVym-uG2p zMs)1p?_o3yuiOi)}yVy~8Y2Eil~TG%~)~+3j@uzTM(=lX`aNy!!WokaI7Z zH@4Wj)xmYxY9HuVfBor=G_tS&JJQgtQF^}*xCDLOL-H!up^Ia=y^NEIEmtSQQ{fz? z5R}+nLH=rVWYZ<)OLFCgRdTSeq^9zB=!0$V!?IuU1?dDh<-oX_mYen^O>AO|0ehbA zAbRJ{Y~C3?Q}OC-BU73u1*N+l*akqqS+KH$8q26HpWSKusVZtqx#9a(ol0<6L{FQn z7VuSshk?mA$S8x5cY9i0+lzmSm7{!6y+Et(xEL}QisN|>8Oh|w`INy46pdkVUpgQGtF z*Z;lg5}R5v#rXF!8EpO-@LxeG`0|ta4ON$~UoZxG_w?XPAnXNag@}bT+6!QfYO$AD zR3vTo6}xgmDe?C_+Dwx1)}stN*TuODQAm6w3!G5KI48HLiVXh>0DwbDi3cSyRij|5 zY#0b26yt6?|SZ-FLM0@xCotJXfE;_nJ1i#!5O0$?+Ye)vOkwYZ4Q0$1Twpe zwwYf)uol{{1BJUIDH|~!C+C+4a?x!e1hcsuo;|`5Eqe`n#beAo&q;H|4R0Mqi@ivb7CUt{&TfKO|z*j;_D+$Y1uiEUl5JtGnu^XO-v%Z@*@W0ecCx;~q#9#EW z_UkHg{A=Vke?1kz%*POt>yHSw8yuKsW=ebuPiOFGW_KO1fLU3cTwEG0=*MVQT@g2{ zv#dSFp~u=6e*Bmz979yT)w_TPNGfM*f~}9lV=>xu$N$}XZ{$+D3wy3+qy@3RW$2t_ zFgBkmrXa9rnk?`nD4MpE0838(ouXpBpHDjYTRBQDFxBNzuobKvwDwc_H6yZ?)md|6 z4Y3itv)i19+j^SFAal_}YsW%87MGGD$-(`yP?rKGZ>tFk#PMu+@Vp|LijWh#%0#W- z+0<3U$hYo*E`Y;Hs=e0e2c+`w9+QiTM0KzGyV~{#n+!B&1M+php;`hL{`IF5gtp}) z@aL_k-Kmt32?wuyU|rA7VP`cD%1fgnyo}Ubd}r|E1^#U>tuQTmKNaM&3F1O5L|C6C zO<;5Yc`Og!r#$O*gK*2(e3FBrx-#uMMqB{PRWNOyFGbSQ&{R40zU>kh7m-3;0c;@% zmW^J4(;Ru1t|JnCPhSsvgNKaz0e`6VE{AgKxHi$)noc2OAT2EiocF%8c>-FC`+YD# z2%O8cJIiET6u~?Jm@-t@c3a$rzZzP;C|>ZmAh)nc`nET_)?3AwLCzE9$sj zavOH|n;R|bth}%Fa6fzLHT;SGOO*9&B`E^~gCprFaugv=t!{>oM1a?32iS80Ro@TA zKZMnikP!@wn^&`QbDDPFll*z-5-N`nIIv-$x%uwG1SvkCY(-r;TkPQ5ulFC|q7tJz zQnOkoCK=>)9P#5XBQet2L`s(G4;3^Y<`BV|cD6Qye*3`ZH|Rua_g}ZPP>Ves`)eN& zeTuC^0pGH8IP~|IRD29L#+)*tg~jYUUT6Q^-LnJs!mrLo0ljj)aj#QZ#wPBl|72e# ze-iV)u9Nr>_;>FCPWb5R+STo3|B`ETe;FpbXl$N<1TL0+VQyf!9UK5i74Mpa3DB`O zkPt}-08ApfS0>2zmf`ix^3HJQ194(v#pheqwJs0$BbMIbM5-;^{M=lLB1*Mu*OlJd z#Ka^{hZ_qpY1!4}x301@x5OjGi`nD@mzjYy;m?J>&EfT+eu5X?i1BBB8iUJ;t5Lq_ zcw7-9JmTbLRZ>EeSOw`qY6X?fNP93u;b_H3qbZyi1(Lelo?_fw2VP;k_JK>x{Y~=j zf~^1kgvxxrN$}Yc`TMTJ#wcj>rtZcl%V=tbY+~_n9~7b^-2bB^>2(R z;qd`a{b*XYABcsh6crxfIVA&1K|Lnyem z+3-@8wtU_{pcKJS)aPEXfi7;3JNqn33CySP2p;1+iXS(72dHWuttoBOg}=<~rU0S@ zI)CHi5reE=BNWVQ#Sk8ncuKHjzH>(nG}MIcsWgcZNNfCzb{Y*t1gl8i{$20-Tl@B9 zBi^-Zd*4$G4|swT6SWPkY(joC1{~WOqsxSkFtfj{%}h*Qbm6C&%8Z9!fRl+l%mJ*E zS1Z*q4;n`w1-Sss*x7aQ`zyT)cWG(4;klJgknkf9Iv5y49xX_LsbZxIqqwFbc0H?p zZmu6y^5Q%9Zzr#gu59_598%aM9QSNmuU9G_pQo<|RdA-I4d7sHTX?~fP!==$q9C&dsWps1X6y$ok)Pf2T{3 zN^Yxp#(CZvT@~elojiiurVG1o2=5mEj0`WFEn_w?Y4TIG;5C@1z6Mu2BOb5K$OyH% zjc8xdiCxJ3XZCacA!GSlmEE#_*BFTYu5%E(FQGeU zGWG5sk4tyeXQ_dLK*|XF`rTO>6nJ=%v5qLwzfaUK>O{U`?ysBYsOiEjLB?99@rUz$ zex!3BrKNLa+C}$2HT!n|=ALRXCdz7Sh;`umQoh{Z*p2)#_Nhqn&K zBl}MiHR2ke31eGbEth^PA&8*Vj9fcy}C|^G*Me$JaUk zY5SUd%B4f|FSTSHL09OTH!WH7Lli*p%i_ctEZ*A4Hx_Y`Ocb#=Y1j13|ADs*nsg-R zkS;k)J1g+_B8{c*3|LT>E6UkGo=W8OMwN96>{q9Wj_V93fc#FfS0*VzDiu08_7q|a+inAp9$ z>-MASHQ$@JiR*#A=CW_dV6re6J4dyw+jgzZ|8oIMU&imGB!;HVMH8W8lV&`V2Wok= z{6;<8p8}0~rhgGin`68VAftJ!5W`5}i+jQatQrKsn6>MtKSRDcx7Pf?-5mLU?I*v+ z=l#LLpsQ(t&Z+AvhsQZ@NU=!AH5wPM(cwm5v8jnS-sv@mfGi49`r()+*Bz@m`8zdA zn27AAivf>~uG`T01Md_8z})6@m7bItI>44`Vg15sTGwcBZ{Bu%$8N7_&*|zO=V6ya4lCL3>i>i5@fUZ2 zZYJ7Z$KlrHg=ajDhblgb7(4de2(5(d{WcB+#Y*vMW#~maKqlS zFN~yQ9QP8ZOT^r0+Lq-cKM&I(%GHbuqjXYmy>=7p9i zfDQQS&|cPA$o!{CkCK`C@!B8M)_8#3<~;Hgh6SjScJ$iv!L#6~ z224f6zlvxPj<=*BrXLk4X<)E`vUG3*B=`<6>SO?*)vAaWK$Xz@VAv=q=r1II z1}bg++ne4}lvq{O2A9n@{2I*0fY%x;OHo`G49CRB9s&j=p&r{Es-CnW9Yhqe%%z(= zg`Uy$Sqb0CGwp(Ggb~sE3)7$IY=Ik8fSzc`$w|xA*8;1Vm(Q5mdZ)F~8>$MIGVi!n zDjdVCUlNiAN*p7tyz?tWW2WEf^Yi5!Eoyo_0L3ZEZ@$$2ytUqDi;&;FeJJ;j|vm67^Uxr}0Jj{pJpf zFz8+nu+nSOELkqAE!Gs!{UdiaemOGyk~6iTL38bZNa5Ay5qmc(;RJN0-7~;k{O)Wk zueoA*s1GX%Fs@w6KBv=OKHP_dJh<6C&KtglMnr}i$d1W_G%vQl;N@sJcA8|4w13%t z(K9`myxWb;;V_L|`Rz%TWGw0h^Dobz7hzkI1?FT~SBM>L5tchsH7_z+mr?h0d7XpF zFN#h!F6GunzS}Nks$de0QY>ena@z{-s&)sY6~~9buGeY`HA79N=)x`v)LfBiFLy&t zr1%MK7W@F`AH!-Ps!i@6^We%C8W;!4`SvI!|1Qy)tn9&w-x82czC{bi~vNk`CAu|)W87PdLOd|=ex zS*V-BS7Vr`*_vV+S#3L_T={LxMt^%}i_83!83LZpF8Bw&N@Ikz*PZxNEGvXsoGBKpun>pF0Bs*xlG$xDn!sSPcW7RCG$g*L6Ruz#*|odG02?=) z_?rL?tRo!Kq}?y%H(VHF1z0_&?0vlwe=i2pj-ytGM^u$@DFg+tU%sOLLUDZZP3EI_ z#(cG(M>{rcl^(o*5aJ0J^~e2Bj-Pq;{SbP}XnytKkdeqsTNDvPe)aVNicX2R=m2={IbackJIH)8231%1um9Y@Yqr(D~Dzrl#^} zpn7I;{*~M0bIc&(-OUCX%HYbnvQ&`l)Ao8`pp~G<4>;om|I|57VCw?dcr+-AOg zfoapI+YW~>z!q!7_dk_qQE9bUe^1PsEmh&!DqjgLG}(Hi4w#`|$`?N}Z2w|XLV?lUOh6i>Sad$se(^k(vJ5WD)B%xSh{C??@$GcOD2g?GKNJO8xDM6uY)>4Ez{dL?YOr)ZS zx0CFNlrJ;OEw8S*w`l*?QswMLmy0reBm|VMUjgP{uT{!r*dK{Rj-k9vKu(5othDww zIut;aD_S|^1t~VIdngqHwK7XP9s_t7O$~CXGJ0A!V?Za*IP$f*Et`_4pDn+ANQxtK zI*j?-FW?(4XFfbR$(gnB z|4L`KH|4=QAQd}xCWY@h&y#AZxndCmwk*(d4lks+LmivGCS z(nr|YCG~DtJ}MS7k@z0_B7jVRH+yJoZtCLTGJm>i-$)|1Z|w?v@z>IIfD03$+X9k6 zF1zbwIX`3|&Avwn!uNj?sIwcmTh6>Y!leDirmcMaB#ifkTm0CATR$c8FCVkSIO_HJ zks)G;ZU-tjcbwb&l+F#lJz;S(-F4mjfbgUtDl2_Vu@b<`2FgF=Hd5* zL+J*8?=n0KmG$gVERkBvXnB7Is8TPn*-qyJutP}t5!}YmaR}ZyyFMe~yYe6__HsXF zD>$_uoLYvPE!g$ldL863Y!8H6ZK39?#^3fiY0Fk>+Xyf3q6w^BB70Z1=@5K5~wu(kiRt<-R-uRwxDAJ zO9Lqxs;zqlug9vIpO!tEr;=Fv^C{Q;nN@G}+MRc&V)RSjUW)MTWr&9>mHP7ko({d- zZE|&45>zgA(DU76XHHG@-ne`Wc#b>Kfprm4bfl|CTX23 zN8#f~aDUAMR{`fA5Q|mFP7Q(9seq3U9Lsy#&THmhKSqAcsSke4w&j`g?p&C$EnI!R z9rR{Mq^5tR>&(yyMs?9T-D{%$f2J5X~8o4gagl!(1)S!al-KgYiPG?QXrOLbc`O zC;s^#75FI??6bn{w;omdi+Ud27WbI z5}PeonsP%1F`!#8Y`OPis92LvSfD0GLo5L6bQ%4?F+*7XOp@8csE8=!ME15jWukgZ zhzPT7vp_KNL5-y z@KqqbB5?;K3D0LhFQizU@3;^O4tTuqUn>i!2v)EZ@ssyP$$$YI;aFz*P8qk7UofYlca?|=n5{G%nc%OT*QHM($s z0hfJY)rG6$BEGi60KN&V4H5{eJ$M2HP9=9BT@HD$??}m&Bgsan1_p50nz7se|Ih42 z!AIEGZI>O~VLZAk02Ae4;6z*M^BV+MSVZj5QZ@en!oWXtg}{0`%IC{KMV^V3J_)nY zi|QRR{?OpjZ-}$eJENH)QqHa<0Hl7PfPB)@(jFZ_gMO)m_Xw51qggjJ;z4Yns@lVOj3#TbHQursM2!oP zc3+CW#R8;6m9j`Jr+~d^E4Z2h5H}nQJdd+FRM6=1yJ<&43*oUhTe^r&YQt(c!%GG= z>d>c6H1{a`GkCi2j*kBY>HuPC{`IS_%fXwsf4)4#?&-S{7aIN+M&{`-OP+Wlvkd{a zh%&M=2P@Gm|4K`X!X~X^h4)K$Mw&If%=RqQsg_S?L${=Z`ucY0Ko(1@cq)#0w@Gcj z9YRd30`@?B{1eQY!KTsH3T=p{nxhtDA1iOeoKLA4` zV;q;1zXgUqp-OrrEvEt6ci8U`V8a$$y??Hh(h64v-G4^Efayk+a?Cf97{$*+VZn(0 zfF`nEJE8H&2ehh2l$-uPd0O7;TBCC5{!SU1om-5qo^C%)ymV*e=SH;Qo|^Ax-Uout z_aq#UG9?)E*?ftckeKjPk(z6mSV=o;(TN;tYjVaFvTfY5_tD}{Wl3<@Dv$d^vT5HOqeg{ zoU8qn9Jsmz|3AV83~@;~-90_7Daa5KIBcg_-@^V9lNZe*3Zba0k&G z5QWM|f|B~f$EZAo_b3pmh+#6AJY1g?xR&rqOiGgnagL!9C5>ElzD5$-NHcs*9@0$F zN=a~}mvb7WAlZciyskkmw%oTjkN@;XHXm}dzG3WvjbekKechxF=07 zHP1eohJObQT=&ysY#2C{3?&+~uTqDPe%>Gti~v<@VehDbJW57Rlu$;`ddZ5=Lgwp^ zOdPXBTM23%A)5 zV*Q$~R%H=IUERPsdLJI#J{j57zssthNh#UGbCtrxih~~+Hg$I%c7e)VChRln!jbcqGDI?d4yN|%9xV*^$UfMF<9YzC^tfh=0Jt8Sr40qVv|~< z*w|Q${=9e~&cH7|dlW7fewse(t3_EHcj`T1?$-QfX@Gm{#I-3{YrRFq!oiA2CE0WJ zT=L|iTU5H4q`Pptz||VN*CBs|0-|hJN$|qq?eFNAEGXTbyM0Iv0pRnIQc)oKa<4L` zTeIdcs?N>}u5TS@*_Ejh)umw92Z1-_VD7hEt$R;fF${XJ)d^QRFlAnm#P^dVn-Q{_ zv{0RuELP}SXrYa@T4yH#pOPKm(pZghcxz~d`sBO0 z;>QRZeD)}SglF^V80~;<(%e$-dxV!03UOD>wf zL<4P8=8DE`;B!lNcq{x2j3ooQ+rVb)j9Ia#CnKkBM`L~a@8$uE75 z4Rt&C4p3i$-7a-NyIA4d3nt-a7XjMMzp~MCKe&17hR~D{lx8&Q5I@Wj;IyJ%w_Nen23P>nXMO*LxL&~CT`(MCruzQN=mJM&y$0Fe zzDR;(R@secP%=BozCW%=l;gwgpnWw(Q(S!6q>bVMoPJwerTF51e zI%BM~kqZ2}pAWmxtrTt1x7l%FMk19Y2*H5&Xw>b2eV<{O3JVfbt9*YaI}Jx37aLSX zGsZ0*D@2&P1si5`S7$JyXyxuj?F*WkUt*@L?*+>&e#}1@x1A>%Oz`x%qx6ExDE?qr zD}KHO!qyWpYIP!W8I|>6zmLSb&F2SjyYUV#WPsWw(H1la)^`}&H@V&CJf3?XkR0Xe z|F=N4DlB{p9P#E~8OidziWIK6qB7ek7EadJ!OEwfE>8efG|w{ zMXo!@H*sZ zrI#%CAE4fJkbE+!GyE-9xYEXh<|2LYO%V>yZIpZv0-+p%i9-sQCE|D z&m=Uo-v3RuFsZ+lnVIRk_K)bxCUBU4|M<7T#hAg2F#ZmLkDO`&#} z`lPKvM6ZvB{mwd;(cNG92|5&<4pRor2YtmKW7#;?E>VGfikVAq z@SKa&$N7};KFNNr4W_#1*?*7Yfn4}6)p9|Ds=nW!4hXf+5kT~W0H1xj()l&&$QC&K z=C<49gLb-Vhy$l|v}NlR3Ji-13wn=>`=+TJX=nYmsL$2A7Fhg$vzzfRSD9*|{nU0c z*4%S{KPB(X*K)2g4xMGFTYeu3DqQ$VTsQov@9I=3t5i?QU8a7d*VM)&VmsPkdD4%8 zIb{;(E~JeB?+p$NMA`Oho7RI8ft>qqLb;|AI~Sc~lEu=)ft0nI;#9FFuXO43sZq<3 z1Hg&5mmYs>@VoH;90BHr7#2+=T?%nmRhq4Sr4+08`~)2Dti=Weu-><~xV|>D1c44A zwt|kT7oSneyK~7vKI3bBDVnk;`BAfnO(PhBkW~-`wOM#&0xx^4iHx+SlIw%G(B?+1 zGTA<8mvE&Ir=68bj%`aXby||?S%j>A;2G|}ci2CdoIK96w{yJl2{c>M{H?=ak>=FV z5B(}`8bbD!rNuInRjmr!n7TmUolid5`P#J&FfgqY@A~M0CVTqBrjv^WTF8*BZfRvD zZ^7%Q_}54H0J}(e)W&w${I^#BLjRQ2gncaT;RC7&Kvpa^O0IOa?mjfG%K(C7#r3#~ zGDis|2V7OQfdK@vSpJKHtL43gk_RK;5ydtem!A?1#e7K-WMhBdUgZO$*llN)5K%+; z_E7c>hMg1dJmBcR9wkK`#ePm1~WHn3|dz0s`X zsj;wucyb!R3-P2$Ia?Qve;b@WgX9nB6quJ@GT!Ox&Mx=dd;)6N%3XAh#~}XE4H5hb zx=Q=W*kbHA*8Y69tMjSGr!H!mt!aDf=;JDu7kVAdN`s82LRqS!+`W=&-%Hn0%a9P++c(j+xoSzS33ir+f^4-7B^#~MQ=S*GO^hU)9SV_z8`QVdN%L(Fl;)@hk$)kHT1*= zYce47T|h&s-_%2QmwKG=cji-vhW4+F?kemlXeBMSf2$%V6AomQF~5JXOE>bL(@Q(I zdoZ8t5FPdF;h=F`%wwq$;djP z#KXI5{}(^#_k2XLm8$7ciH!Hv9jPatIPV8#-z$fwF#oqrzu4DONj{{Y7qLaNbamxk zQ&H11$;49OvjrR1)>*wmn#t>yQ$yES{>Tw)CnqyXiefDo8Q~EScZDy-*iI&2PW`O& zf=fFPr@SJ0+1csDV_qGUU6|AKG;I0ETemro(EJ|x)wP$c1~1-Y;d)DPUN47^n)!#_ zPo8uRJ|ktU7)-B~aOzH+rO`@FHhn#yq5SFgDP&pihAEHpbL{eW-6)!SL|)J6@iH>} zjEYVLCWNzn4r-#`g_}tYSq+sV9{<8c6JXoR|GLUw))MrGg)HAAeV1%AS1o61U99tj zib)`V<>y;EDCFA6oo*#t849WQ^JU^^E0}sex7N3D#Q#!xdZwdmkha4to_cF1)+Lc~ z`FW>XJI2LFEN@pbDyVqxYYvJ^s7z@j$&Dy;cUP@=qP(Um6#ffaCw4_%UiF3UZwi^d zAMc2>m&!=wE^*95~dZgJFlJv=vH#6lx22wkkK=(mCdoX zxydPEV%%Dh*`9Ug5Bl^FH1 z=N@OHyymwf#+tE{m*Lj{Lx3)#cgn81CGn7RXVwc{2esW#pE3ys-bHO>_U?8Tvo?BD zJR+X1gz7~_|eQIq^SbY}W0!Or92wnbTAryLg;q7WI zxn7yNd}zPCDM0>lsk9x_AGy4Hzp$pP;3+5E{o0TsOEg+kR!2P}h>Ektp(ZRe_a3(D z&x0dE_;}O|HSMDbFin}CX!P*-;6$C1%gRb>auyb57_+yhQ(m!QkycE5EiF~yd@84$ zm3i`(MZAfs{e#+1`j7cKMf44o9EeBKJZJyq+zeDTz@qk83N9l&o6v{e9|CAKT@KI5 zJ{TItd+g){?wY&d_^VZVxS)?k!*t#rSO+1e4WZRggnB^FCP8@=eq=nvlp1g3s^5GS z&%4xeCQj|RA>5i)O;86hqYFuANBp2{-z4}FwaZVXuW={T>mt=J8rV%*YW;(CAupui4-hwJBpTiVeh$jZ5rJ@BWm~{~y;egNm zVj*A1LoX;No%SNTg*EgTS5aMh(H}u%|ubRGMsV_cvE4L;`h1Pq<3*t zs#VqM8ZO1vgR8m-)ztUggbeh!gyj@MqzfDf&!%k*uDKp$5Ehl#cgEgPwE=pV-F>_+ zwO2u;SEV&j39&PWD*u?Q77P8=0XuQU;vpsLenXt~2q15`h2jpcU=nFPrgm1)=YKocbP-Vc(;NZ_+NcL9 zrMF|6hf6=ej(C=({%GN_N^26`oQI*AaFc%^t8SPhpTz7;fl`!o*5#Mrh?u;uolX0@ z7a*IEc1I#UD1`baYzPa&U0?)$8JToH`gUcCxOmo?(X(~eSd$K3+Hv6_dM_C z`Q&}yPpFcDBr+laA_N2kvb2<#3IqgX81T0x0vz!3(}Kw_;1~RNX?bz@X+#WkSlY~& z-D%(w@po~p@1pj$wx)L9Aw-=_4ZoY3khxiYw;)rqFeQ_emRCA6p|XR3AcK$=6IOFy zJV|rY!q&v>^}}TXhlNeTAW5Oqg#{q9xaLe0O0!c}^ORBdL3M9)F2#e&!dE1Z4AVW~58t6xa z4w9o*b#rpnKRn)?Y)?zn6h&23#9T@MDXXf+!SPwzXewwFQiFGh(2BPfV0KJ{5cTy6 zgKz28^?|GE5Qx}T-?ECY5%Y#V%-FP05n&aVe50pJjZYdFS+{=pVLsNa%j?E@J34Z- zwu}S@*NJ6*X61BsaCKa0b$Ga}FZK zhaoksqU3&C@TXMCAR;eS%My6tQdaQTXg%Uvu~@}s;e7@fmVM3Os=eijj?5N+!>Y>9 zKeiQ!Yh7$-IjxYb-@zgmcS`-fMgA`u&EUbRwc`E6o@32I3b5(5o2aO^<--2-OVF7`Tu zsuuj#Ecn2hWE1{X05i_e2ma&d*x>o*9-KnEda`NidL>R(_f>5l_1Z+0J2JIaFg^cM z!n{b&aqG{!EZ$YfA8n$m7GK-5ilSPxii(=Honq%jR?=*xZL%is$Y4{|qd;oS6^Y*6 zF13u^fE0U-TS5Q3?#eAmx_oyjn#;_p)0G6i2ok82qUS}h1ycT`f3=?P9FkEiW2#5P#T4z|`4SMwME43N z`qg3%uTS{Ol-7L5s2-y$Dyn$NGb3yeJ*lc50+NC>4+W{g>qz~IqTIfu3Sfp0d)wXJ zUf(|q&MD!mXy*%SVrb9tg;uR*V+*HckgkBi!q5Rek=O{ScwMA;QaYd;M@m7C?9;vz zq^we9(DWZHB0XKl?=A&vg!U{x19N}1wXpe~9KDGQ8|=n=OH`BQ68$%+AOruT7jI^g zRRuK<;Hc3G)a8OWs6vl41H|T(`*&nN_4Z9oHgPDFpI+mA{#BI0+)-@FmUpbFBZ(_} zl)tkY2!mA@4=Ob1Xv(jGrCZm1S_q(TiDsQtX>#O{GWqZ?k@Mud?1FSb7^(=UEuyL|3Ea(%TZ^mV4SkB)O@Wk7O|6Zw^f3IydIALOR%+!h zGGBgRIh3p02+Jt?Y}g1Rawqz7b8#h(AN_-6+`#xJ{9_sH5=T9fytRKAnNvb2DiQf_ zv!wmZa?RW|ruHkbnl@iYQ=}4s9bOmqZXkKu2JUp`xlvAd};k3|jBa(U#gdq|TgR#pe2ESlbUn zM=Ft<#j~r=&PXX{TJ!7-xMJ4qN>T?!z<2!L7n%RetH!cUgzx)pm>;o?sQ)H&XdfT% z7fTQesZVfDID^EEoIWG1ny6p9+Y@zY)Rv>|TGsHZevZG0jAA_C>tP_^R>gfd^z(DZ zBoa7bbhNrs$;rvw&COfehrN8H-oF6jQX>=KXsSsoxhvZU}qLoG0Y{X^4~UB2Nj54q(cRgA#gX-E=$@+;)mpf^C1&*F=1j^ZRh6yiOVKgrs3eh zhsSMTy(xxK0wOKfzor|Xv>LO!Z2{ z$A}$`zR({dBGj*jZ0Ob*f$dCr&mg^Uso3HdOxTdYe{w$&eQg24R3YP^{rNyAmDVDA z8TeLbAk^Ti3?dKD0$$QIvugR`4-rIeSsUSa-UPtM7Hr5lsFIE}nQ$S4?&l@v`C8V2 zM`x%&F&IpJV7FX_N|MtEf8?8)Tr;(@>fa&QxxJ%@M+EXii2nN`f|~nqSSKeD!!`x< zRLOS@Vd^8x9GqNorWge9?gl<4FV>04%b(;e*t@`|mW=v_4_O`^#%3gHo{F6}y@v^4 zf9z7+Yq#k?eN zVD_tW+2L7h?JEf`Hmtug_|bG9n;s5p<{wV(g2sv=l7Rk6MRs87VP3)N$EM&km`~} zveJ2aAt>1fPEM?F!HPov6y>Fb*E%v)#_Anz<#s{(*^?5N*?b9#XM+7ZPI;q6M+Zf8 z0|OjP>!}PlJuRQAgMie)&f0(_t9MQKn6LFFRT%uLuV<6R=`SMfWt)~N`zV*oor6}~ zAEahVd<0t+gdazPNs2oZDyum@KCaX#!7so~=!EINo%<>H`gDN&%{kr(>FKQ@yy+WA z*`xlP!d!B3d)oN+>=fVDpoyVbF^(>3z}Bj4}fe%V5zvZe%xOIJEhm(8C1> zbJtq+#Jt^3nRi$dJ=hC{e)uq)@rrMTgJWT3i9vi5{V*leRcEWyd~-O`dp7I9#g289 zFOSc)BN`zuhvpx?H(GUdx7I&AY;vyCwZA=8*1ywWqow#1N$lugr)Rwoz*>z`_LXvO zx)cN0iRll!au5D)y^0+jf4b5zBFoKonep*hdoM4+#H7HF;UB9{034~aPN&iH*Li6l>CM{E)0?G z^54e?5g>QU&eRwziscXMNI83jd>z60(*zHn$Qj1I>0~2UejHIwT@`d`((v-g5Ez17 z5Gt?Q_N^{p6%Xv-U4(C5g-|M_qKKi}#ehH*)SoxHS4S416g`s?>&O)5UVpg$xHp6Uxee+1bVQ zR9~9(q}t$LjpzT~dDw57K$s7;c)P znV6l}IJu)ib39`T4gJ8F&XESxt&7>hDw{`F?1qYvcTYHzPmM^*qk8c0_(VnR58>Mv z6_b%M))dzl8t&6wIG|eP;!y1Lbi#IzoCi_I>rc-5>>I;@T@J4&q9Z8@iD-8k@p^7d zq^sNWC>antSIC5`;yzD|0vi$&liu5k+I#sFTo>_TlHW<35GSf8#*IvvlvP&3a0oiK zla^PYxVSJLPSFattZ!;+5({4I8t=z5j)PNt`ff&z+I5y?Zt%-8%Y{fK7DHM_u48zx zd0TBIu>)ri6T?4x2zP>uJzLs&shUQ1Mx#$md}^k;x`wCGjcaPp??-t$i5pD_#uIID zaQ^S;p_!`cdN`QaxL6?Kx+j}MHwgm1biYs@FWsOf9pJ;j>cbd}t?_yI4Tf;nyFzv> z56Z+W#Kd*&X9ePOI$^!Qf%Q+AKILtHa}vlkpCZVh5vVKEgvS}Z|2cg9ebMbSK!;Cp zdS)in7jGcRSkj{of>wcq=cmgRj+mr`gTvjMR`n^o)0NY}&`6#huKhLgM|Zc)PwaKI z14Cnd3t;5H;NR;3YnJ(KT&cv83bCnwyR!sZJgl#|%k>er*W2GJzrRE+H+lwXlSUX9 zoFxq|dC+yXG`x(iyGdy9(RTAkY%j8jUrbNW^!)8y+M>ZJ^82K=PQcvUeng13xxXo4 zCBq7l2>q|vsRIkx}&sq|sXzSiaYgL}6U zfwq<--!}aT@6%_;**&pkq^+qK`;E;FanG;xFRR`ux2r4jjn51LP|qwJ4P?9&sp{=- zs=l4QNCrrQSfJWkOAW@F((bQ{P%b5&hZ{a#(h4$0N^U`3v=@$0Z_f|#Tlor@maZ`< zy60!NW$g~v?Qee@Oz@MxekI~LCxwbmN(#+S(5N~pEGo*Xs8AGhcRvC7C=27pW>vAU za@0AQOAU78G~XRX#s5YQxji-9xK@O?-|Co9Ia?dd$44do>1^^w!s~Vxs;(-I?Az2# z6iz+6$x+Q#GhcnH!#X+DNa&f8y1Z<}`;GXLx*kOVC_+`6Lmo3m?G^28)q{Ezjw~!N zxO8D|q06a6=vcw6Tm%K`r|k8WjQN^{ov-@nPwEvsHIV`;p5TF@2S2>gUL+c zn12E^H1PhxzX`9F{6ZFLj0xj{!T*-FsLyx%S;Qg6-tJjAOLa#l&-}zR*Zku`P-Ar* zd^hCBI@T?t70Z=PzkGFn@;Vyp!4nss7ZLF$`ZtEvBdaLe^{)q+Mw9EY{ehD$(K|4x zPCiIj7@LM8U2rAT1Mgov5~PKN1%1FtR)ZGjm9esftZYj^%(<(>XO@^4@5fag2otm1 zsVXmK6_wZ|o=4jVIl0fD*(LhNXf7Q2^;bMG94@)LT9-B!+n_PcMz>t0K2M;dXGfMs zeCNI0SN*|RB3Pdpp#ZWyDHy6wFl!IOWhzD@t;x&F%`Puj2w~FeNz<5}ZS-(QLnj%% ze)v{tacaZJ2-0qJ;!rgoF`M){8~@kBU=QXBQ>2dA>O6uzSj?f(*J~z&SQYTZ0N&G2 zUXfE?KAPDO%;G-3)aX$wTFzv?gPNlC_@Em}@*HIcew_YVpE*3inXYePVM$La1`<7L zQl+W1TKwoN%YcE2DJ3u8Q(J`(_@FKdx1@{{hIUetF7F;{Ts{UNaUhTuDTVH%;eHc1 zANG^4;_<+7xVVsriyK;*_2%qb*qjho*{ueIzncFF_0;NSV^E8Ep{Ja4J13N>38E3d zK!g@G>f8!=lsvsAy5}YJsrm&J6kH(P&>r9veRx>`i_!pT5NNWrSnS~#d{-2}kH~*p zj6l6Ngn!&4i<0mYZ#T|0nCE2-ThaA2!eUkhTHUO zzb(-8^z``bY*tL^JlpkcMA2L`{J2cZe8zgzn%N$}D3@uN@p^|V@b4J$s+ z4co=?>`CkR6qmulwIz2`9XUVaA6LERiQ&=o8Qjq-<15tVCK5ScUqJ{-w=;_4T^G5L z;SVUS9UbU74mZdB^`!0Q3&T;ssJ-XAZ`;~@(*YJO*t4rR#HS2bfY~DLA&V;lO0!zN z{=)tT%rnrVS&)l3GoHJkKeF}g_~J^MG^roO={Mdt=WPN4Z(g_bM*FRXH4NV~5+u!1 zth|L)S8sXk_RnBsYb4V8DDmt`=@Q)w5U@WOM;lM&orE;$0 zTM!iV=*CPQ1CB;m@*R*Pod)k{Pfr%7hBabnGGbGQFGS(1>Kgjm;*CBJ4vcPeoW2>_ z&A{Bbp7eNK3*3{!1xwW4coIZk@tw-*`;w$JyWrmGwRnei|LpsI=^?)C_SEaLX=LPZ zcj%uVOyQ1=?wbKrUGApFP*R*;fv^oF(jq*d;EJez`F+PU>WD;f~=> z6_%m+0Vx@GIR#hF-@_oK$G0OaD5(9BniCt-nB#L=>%-{vEjBqGm6l?CZ=vF8eC z6Z?h-5oz2|fpBW{I)Zbv*X^4q0}X_Jn4hozL_?Fp0BH`ii20Il?9bUy~826pKy*e%et#WIm&IZzn~Lhb{*!0Cvl$`^FDmXgdQnLWHLKHZ#d8{ z93AIEYVRJ&NkhPbI`2xJ0W#u6Z;RqfXZv&I52RP$RxZ1M#xp;@`m3>w_9^QQh^oew zs=&94lgS>S*8KXi;dlAzN-%x!ajn5n9|DUjpn_#9@B^b(lLSmy2r9{E+hM6*`6sQ^ zy8MDX7T^WkN1qx5CGYE33!*fgtEaZW&9;XOUoj00YMSy@w7W8`b6Cg%MT}=!VG|^s z*|MFAG9l;PUSh|}x4oz}tri;=KT^J+niM`Lhs}O+Gx_9>#kKm0oNsSXm4bawqVO!% z7b+4sxj7O)*Nmkam?F|1(KCFj&|t)EpM>>z$IZtLG;CjCm2ykkW*0Dt_4IyvL;8q{ z9&*|A1EBf#-j4C!HViD!`CZp(bV9O>Y=K4?J_QsDDy6i7Tz9hv5>R2#VSfC)=f)rv z%CBmI1$st=fG<`uh#xK-|FT>7*GDo1kVOL!{8ppZ$B)R^-=L77ns}N%b@yeJ ztLgr~AP^oN3Ws)p3*>PF018LLMFTG{51QuK?pHb<8kUmOV=4#=qy&ICb~lIZ>j}|T zHb+F&-C=k#AgJc}SN6xt;GH!~(JQ#J%{7P1k}JKZDim$^z=od$BlijfLb`p1%zQe< z+oz7X9Tk=vEN7fh&Q^Qe{9Z-7dd(n?mo8}dd>8wVE9S+?4SL_yh@C?$JwpC0`a_L$SK%DKRS{a zm$)ZOkI&)i@MoYkA?%i%g4tSq8M~W>c#6WRVqe)euBrY`j0#Q9&?l7F8e)zl0zqi~ zisw%80E~#uv%nC`+0J?dqmv5(Z8_JKvG&-scs;SR<%)#!iQ^`58MBBK*7z*@_Q3ex z6yoX-kEr+o=6j=mb+p208qVfJUkYBZo)Eut*3=oVSqcGQaM;{9*XQ*N%C>2HrD@3~ z+c)7_7nd;ptuW-2FoFBhpl>mMqm{($j{l$@2)Rqe1kF65k_6;9TQio~iY+j$cvZgM z**4bOG7R$YHkjS)W;nXqQ`T!6J0oe>zb4@XS(RBF9ah_{z?G(OFgwY9E7$t%2=)|D zH$6OV+TPv8aU=)x`BH$t5o&FNuZgdD>rqhl3Hfc$Mk}trXey$z$;w_3C@|T1KR+bG z9?MrX6qSKg_q-!ZB)u*OZsZQxEFT#c-91CO3iI+tenBavaoMu^_W4N5%J&TB)1izq zd2@R)CBUY+ZyjQ9o0wnyyDot4xdo+sXmN?tmQ4ufy*phVg>7f^zU-m6MzYzt|M$l2 zcrMf{_o6lJc2P>|XaACfz9rc4uSfk~b~${F#|Zr+vPaK~v@w+|tGY4Ud0)xdM92%=k3c59#Bv zhX>%vajVjfOKm1qRKn)9fDCjIWz_Gb;@NC)?s2~^=;6^sp#!in+p8x!x%t%LPWYBZ z%gwGqeU4Q@2RS0 zA%5Hy@@#*k2o%07a8y82L0;ZvdzldA_|;9;W}TbOSH(SjANckGNd6FGZPaBz-wM< zyf3ZE0xafaINyICa5%*T+d=Mg!dSrYlumS6V=z( zkFiMuhWP4g)`^KJ+!GLxi%W+;e*buCj|5~ zRGjn}dICNXa);p!_&4iD2V7KFm~Z-vz(||IU+V14bYE4o0dZ9nHYc z4fRez?CjFh`UFPXwg2L_z)OiHdll+}Mw51*%9)7~;?G9fU-;>@^?TbKwj*0iz zPXaYHNtc(r=$3_<39*#Y>Tv!Ds2sJA`zd4o+k3VX2568O&LZXPcIaV}n_YYlQ*%^g z0uG?tQ!Pp=RufPcUkHc|Tz;wK18&-L_LF%}A-!Y18>XUM@FOW-BuD#maF!z&ktKGq zO?Pj|1j)q(GJVhTVo@!e8xsP8MDtw>Ftb)-tfH#anZtVEb6|O3akSn3xtuSxlu$#X`0F$`BI09fYs+Mwm!cWt*8q>Ki2Sd7{Q%)ID4Zoj{ed})M8(z&Y0&O+ zkbVH54~2wCv!FDzDCYwZo1*DEancJuc>`@L?(@qzpD)(iR@=`t4RMRB@%l@~A*fNGcw$w#f!`4H-G*?!u%FlITiKWMP7s&P|-`CIIK zo-iNa#FUFTN^*I1)#OU2^barc$!TZP}mU<95}jBDC!$9VsJPaqUPaB#)c~rLrD2a1vQ1T@pGSA_YKK%l$yYBXk2L#f< z=wRSqQLc1HP4>WZ0!s4t}y;2^E8oNIE~I&g|HrcunQ zYw;SGp~<_RwsIjCSh&2r4C}@(httnAWiW4T_4x}vCe#&J47dIvq=T*iP@97F05?ii zuXGcad}xX^NCaZ}TV`+25r#}Sq=trtS7?9*va#;7kque^3=A+RET$am*K`2mEB3y` zv%rDcqj_!RO%Zp zt&>JMdl!-ex%zyjS}KTtVUwh1-S3k7wa@25XhYV{j!9q7+dc1_-!9WPX-elS8EAlJ zf9HY?kW@U22O&kJPpFt>g@?Ont66Kp5by{Hoi|E_06Xp<5E@LidZam%tHbL+h|kPl z`I>hh=k~+jSsu-x1MwD-dQDz`8#%KEsRnEt=?GGE)^|?2_UWe1_s2hg17Lr`P~FG^YaV-F zb2pBDy<6robB#HZR=0(yu5s;EHVH}f5w}LK?ff0CtwWYd~dOqhDmi1~)MODhldS;SV-1)&xKz*n&_8~w9y~pz9*ZH5eir$p2^?) zi+Z_EnXhLWAu@gAj{1>BK63K4%>j2ZJYAq$LUQXH?I>B?0Fc$P<~rscjAq=}KijI+ zz%|J#(fy2s z_xnlj2G}bs&Bn}7`hKNlpYYEZty&t$pJLQur_&>bMYo;cC!l%IUlnB3s)^J%wN=v8 z^z#)3lm*tGbfTd~0Z$(RZn>ahEdbKG0X@AggaRxVfU<7vZ+ll(7L4`OL?UK%5rW1TDrHbUNSM0r#?fv_tlCwJG?W zh-)0uHcI-0YB<#zagZ-0F2$(o|A zsCAUuW>r7LCgC6a)zK!0z?tRj7878kz#+u^9T|zj+sw-hm~?Zig8_Fe+_Ho#uwrts z3>mPgcik;~s_u$Xi5PFiwg{k@s;h>j6SxU#jHYMbogVkUx>~SbaJrmC+1}pXG@Zlp zFD;e(`Rwhh3&!xrPVvpQMB-zwV~;;!MA7r{i8xGBBmA68O@ryMi)D_~LH;^Hx#)J$JGB%d&1N>0YgvPcJsk)55%vH4 z2vApXeBNh0Y1r4k92_NKEVY$+sn4vIhy^MmKsOkrZfNv#q%OI?C%MU+6bFkFm8@#= z|5RDP<`(C50lvf`oP|>jD?CuMZOU^t#Cvmlg9acImS%I+B3RE0LiXQKd&lnH%kl^Q zy(pKOU3ZVC&mOx(%lv$qd1ZP#!4!D~6tJmB6gq%PtfUrPY!XYR7PpF68FYD#L&v5+ zAz`nl)G1mR9rNY8NiIdN5}Nt=>_iXHAqxXqmV;pV!KP|JFhg)4;#oxuuU^uZxG`V%cBVdW09deG1xMQMf2=L1zEG+uJS^xHJ>#o=Ms_QF+VZGF*34JZ^e zZSkJ8+R$)Sg1%@10x3fqVQuaAMv=*uU*`I&skyoHj);GD=IV!UA^}veb z3!3L^z?oaqm=RV0vIpRuz|)}V6v6`fodKM%0mL4+!T7Wh0S0FvxB=+V(#pE8EZW)# z;t@!`$@P^zz~&S=?{3YmBFTe*WvDg9NWG^xAl#24k%}JY16XMW82Fxi@X$xAUY@wU zF1ec@AHVM}{{D9sVY1Mh@tL*!Zn*}YPC(s>d`9~Hclf{~OUJZyCFol~~doBSf+J71@&%jimy@0Y24g#9(g|(ex z?*G@S{ZEY4O{Up5-{~C1gAJq1(}|S10en;^F$0Jr=&B$xyl>Id5_j*3vnW#yEF)R!|+-w+zvtzaQp69N-X*ntn$P4rPBGtLx4+(7iu&i?*^ zv55+)vvtbkP$|$j>?KeCgn_GceU*a{d@=*c>+k}a8o)a0D||{AN@_0f?+%v>PVnW7 z4{UkGcsu2BKtSTMq0_WCGox-VeF%PgsSoO_ii)6>R}CCMNP`gI->e&11K?5wTuDseM`@;9 zdK07~>CojyZ{Z95sVP7Vn#PnXQwgh%#I1=<`{xt0pU1logKU>twou;(^d!07dwk~y z!vzYI7kirY70>ar-y5d-e!Ke{_oRFg$;kv6AZ_V(F3Dd=+}L#2R0u!UntA|`3+YSd zerr+Xac&b5-u3EVin)Q20jm8}Qfu7No$5Dyrl$Pbi;L{lHUTM`=D@riRv&2>^RxoKp$TAy1JcV#?t|<& zeXWd<^qK8cHFIcqSm$(S>_o;0=yshY29?|E7%giq;8!F9pq~2x;Uh|EbA0T0aVY1N zLDJ8`9v0XXdrRoe;$I_)>TqU;AbsIzVs4(3S0*3WyIbpECEurCVKmcOF2+%Sdi?Kf zTo6J~yDb6m$W1>BQp34GX=%AHdgYO!mc}+|dIytkWH>>#6`!8K&M8PVkq&o{97YtR z(dn@Pa`M530+=?(X%0uz*bR|-83`A3xn#`DQQdZ-n&CdQD@ojH`k$lSn>dT$;p^L*34|q zX0gpJ__v`@iVvY9tG(f!@C>X}nq^K7Ns8yN<$YS;-xUVfIdsD;qUR^Y$m7L-%q59c zR(<-6Hp!v9{LO8h!4B#}f&Ad}>UzHEpUY?Gqec$Y0S(75-m+o+?YW{S?%=)kd zQnt8e0sw+$l%RXtBobxZ9Ydrps6(m06T&&bghvbPb3YDf0P`eJg!#Z5_-K<^d)pN&c6N>hr}IysukE$=Bq8O0C~#2( z?^txVaS}d0@Gd8+bw)|IH+g+KCX&^a`{x!?KqqdPrz_Rsx70bumQN8k@VCxN033qC ztjxrJjiVZJO9_BQ6;T9Z)8gN7n;u9;2muf}C>THvIbVL5H>CHhirP$z_TC_c>E&uMuX~pn6<_Tp7oX5Z-rZ%0$bH zVS7D&Vr=D_@UbL)Q@KIJ46lzBcnA>7VEoQL(Z}fIKT>QV8l@{}~G*37>A z{GkjsA|?|O%CI+=-|4vh#!_nDi7Gi7e&_wX)~?0r!U)k0zuetjTLn~HVVkU0+ktyS z^X;7{r6D1pJAYy#M9X3v;WYPH> zYh8ZFh}(*5dgZ%Z=Jwu>`P^>KtNq2m(~9<%5WF|| z9U~n`d5g`)I*KSZZ}Hnd`|gRCMb|dM27U#4@f~upQVLRs9oXOI`P>PzSGja7=i~oQ znIF%rYLcmfRsYDXnE?7zp!pmLXrSAd7eoPw^qYcmHzvUH0>r{bpFIvPCJqcFka$?x zCbd7C619`uboJ-@hR&&JYWB@e;Oj{48Qn#rk@o!nJl^mEHgre>AgicW zk3zmN8m_k(hYf%1I$Jh-yIq@ab0l_NBmK9oyp_c6#{y{mJ5R7lG39~ysRH@xv{Wy1 zx?CJ1e36jgZT5WZiR7ZE3clDDtaZWO+cq;#bPps2>G#xKnTP%k+fKn()jHog+pGeu zF>c=0kpVS0>1rk4=mf`gtB8-OhhbA>+(Xl4AZ5aSGgUUeh6qW}JKxOhuhpLS8^*65U!OFa&PJPjBw@aYs>)8= zTbYiaN~k!l7_fGD_E&OVafRemGJy{B2lnQBHP>on4$q0%i!SPP>zkt z&GaExY|s=n#v3gh01~GPEMgM5w_QV4gK;&-GAKED!>BUk%nlNS7(V9S9C8`V-_yaQ zU2RSO@adKi*nW(ct+kp`D4GwD%MW%5PmoKkL2E$K`^$D~>-t*zWB-=}j@|NC`t?8c zN$ge;$^w|N7$mA|wJd9=&lQiWK`O4)nf4~##~g$Y_amA-J_gKB4Yqk_I%jCxe#y2+ z*u(altreB@QgF1b93b{}6e5mvy9#9WN8fm``OvOn;1m~=Uanq4dfs3#9K3))#h*;h zj*%f;OHGU=v~&{q_cn8Uvxs3NKM&I!;}ZZ9){Whos5023i+pIGMEOIjsQ^!_`Wm|0 z*^YRqE^{lbRGC`W89!0--7CIX$DnWbCY8qt!ru}pvHEdLuati8ZbEYilH0?8w8eKp zHy0dvx*%@k?cH?eB|sZp0%;B@4yO>eTW<;_X30dQm_fq0+=%VclsXl1TXWuZ2S`3O zU%?+T%kxM7z=oMO16@TiPeycFyn6l$0BnWhi_u!@1lgAEZ^Tw?Zq9^q(a6CXoIK+% zw>pJVP4!YDK&4k)8+Ei8hX=_2t5~uYb+lU2cU;|@`oQbeth*#FEhB~olXuc^M@T6) zTWfa9PI0zEr+jAmruzJ_!Ua)#d;C+UuRITGYSAMDjo7!MTSBPz3RU6wK?l+|N9fDi z{(%Zq#qV@~@fsWm#uVO!O2U6Tw;6xBe^S^cr2hS4r*D_#mNkF<@wdgnV(^a~p^g>^ zhn&u{*z{zTw%-D5>HPM7%ynZ7D{C3X5m+*Kltqgy)p8QbA5DR2ibG{E+e=imyvlBa zAEH(NV4$RH6?8`cHGcOS{r+_2_gV~e>4OmtQ#!Gwl{soh>%|$Qftjg1o0lYk&R0VN zT~vUV2Tm19dlQY_$l~_cP)Rn_d)M1=z)9SMeasYrZDT(7%)@n_voZO%%|f*Fg1Xr3 zIec#hVoj1eU~DD{T+P%MRpn$#AA9qChzaC->+WhH1Qcn;y6upMhfb4ID$>7vx?je1 z-feA&S-|3b*|(!GCi`k9%mXKISkt7-zcX}VEi8vW9|*MC@0p(FTLnYiZ=jvfe(h_w z(E~>aw7+!I*k9*JBk}3BHX9gOg|)@70KG--Cu%f8Y-z22j?+~Hv$HZhc0|x66++$= zvJa)P#wmYC^1>7Xx9cw!L0ii=8GDKR*R%NKzxn;of{FcaVn6%dP5aUkzIrls zMNr$_`J1bg&~<{52B#4gGGPjxEv+NV#B<9HO;AZr9<<;KJl(CNBsvMsAkg_zoCU?; zkt%)##ERP5zyO(DV*bTvN`l?eP_m@{MAj6;&0fFH?TiT~Q}k|izNIm7$&BW8i4xAy z=dnHVcbQM#s_1>14oo_a^!5rLf25BXaOTDdnaA@ncpsm@$Z#qmA4FVbJ44_jx<#>P z_i}6P>6ufQ3-&RL;^=D>RXp}V>gW{w*l!& z_RmGZrP%oZD`Y1CQL$4hEFO@2I`y$w11qA-kOQnRpu-)^RGP$b+@lV4jPjnr*xW*a zeasl}CO;?&0L~uKw>MmeGo+MLtJRLA%b%6dgR$fr7*E8q=vT!*zz0zr%!m{L=bxJ4 zv)sMZXIk9r!GqOndzLlTC~Bdr7(cPbC~1AgSH)*;-RK1Bc7MQhB>B9F^$J9A7rO#_ z^^rAZZM5i^808z{5)28I4HPo9T;q7}ygf+Rtgr|%GCZ3e=V;8&I z!kQvV5j#FCCB_za(&DUk7I)~g!1gG9DdA#-3;RlfY zs&hA?nn{cqFfjw@kQv(>K3^w6ZQA7&c#ZA202GdCWv=GeP1tu6%+R8qjhgo!rb z;HD%eC#N{f&9m;GYkbi$2^hc$vB?_NH+W+2KYPg1wnMp21+j7wgMj}7UJv7D literal 0 HcmV?d00001 diff --git a/plugins/zynaddsubfx/zynaddsubfx/doc/images/envelope3.png b/plugins/zynaddsubfx/zynaddsubfx/doc/images/envelope3.png new file mode 100644 index 0000000000000000000000000000000000000000..46de2438fd464fc1d5e749d71363916512aa087a GIT binary patch literal 20588 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