zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit 788045ba80f4ea84a02e08ab28aec8233c1c4dd3
parent 15e4323e623c4bcd3363776a630bdcf59e50977c
Author: Johannes Lorenz <johannes89@ist-einmalig.de>
Date:   Tue, 12 Sep 2017 21:05:07 +0200

Merge branch 'master' of ssh://git.code.sf.net/p/zynaddsubfx/code into pad_synth_par

Diffstat:
M.gitmodules | 1+
M.travis.yml | 8--------
MCMakeLists.txt | 13+++++++++++--
MHISTORY.txt | 11+++++++++++
Adoc/automation-proposal/automation1.png | 0
Adoc/automation-proposal/current_slot.png | 0
Adoc/automation-proposal/macro.png | 0
Adoc/automation-proposal/macro_view.png | 0
Adoc/automation-proposal/parameter-automation-documentation.txt | 208+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Effects/Alienwah.cpp | 29+++++++++++++++++++----------
Msrc/Effects/Chorus.cpp | 35+++++++++++++++++++++++++----------
Msrc/Effects/Distorsion.cpp | 32++++++++++++++++++++------------
Msrc/Effects/DynamicFilter.cpp | 28++++++++++++++++++----------
Msrc/Effects/EQ.cpp | 4++--
Msrc/Effects/Echo.cpp | 23+++++++++++++++++------
Msrc/Effects/Effect.h | 29+++++++++++++++++++++++++++--
Msrc/Effects/EffectMgr.cpp | 33++++++++++++++++-----------------
Msrc/Effects/EffectMgr.h | 24++++++++++++++++++++++++
Msrc/Effects/Phaser.cpp | 64+++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/Effects/Reverb.cpp | 42+++++++++++++++++++++++++++++++-----------
Msrc/Misc/Bank.cpp | 17++++++++++++++++-
Msrc/Misc/BankDb.cpp | 32++++++++++++++++++++------------
Msrc/Misc/Config.cpp | 39++++++++++++++++++++++++++++-----------
Msrc/Misc/Master.cpp | 300+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/Misc/Master.h | 16++++++++++++++++
Msrc/Misc/Microtonal.cpp | 43++++++++++++++++++++++++++++---------------
Msrc/Misc/MiddleWare.cpp | 135++++++++++++++++++++++++++++++-------------------------------------------------
Msrc/Misc/Part.cpp | 82++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/Misc/Part.h | 4+++-
Msrc/Misc/Schema.cpp | 38+++++++++++++++++++++++++++++++++++++-
Msrc/Misc/Util.cpp | 23++++++++++++++++++++++-
Msrc/Misc/Util.h | 7+------
Msrc/Misc/XMLwrapper.cpp | 8+++++++-
Msrc/Nio/NulEngine.cpp | 4++--
Msrc/Output/DSSIaudiooutput.cpp | 2--
Msrc/Params/ADnoteParameters.cpp | 165+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/Params/ADnoteParameters.h | 8++++----
Msrc/Params/Controller.cpp | 78++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/Params/EnvelopeParams.cpp | 95+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/Params/EnvelopeParams.h | 42++++++++++++++++++++++++++++++++----------
Msrc/Params/FilterParams.cpp | 6+++---
Msrc/Params/LFOParams.cpp | 9++++++---
Msrc/Params/PADnoteParameters.cpp | 111+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/Params/SUBnoteParameters.cpp | 82++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/Plugin/AlienWah/CMakeLists.txt | 2++
Msrc/Plugin/Chorus/CMakeLists.txt | 2++
Msrc/Plugin/Distortion/CMakeLists.txt | 2++
Msrc/Plugin/DynamicFilter/CMakeLists.txt | 2++
Msrc/Plugin/Echo/CMakeLists.txt | 2++
Msrc/Plugin/Phaser/CMakeLists.txt | 2++
Msrc/Plugin/Reverb/CMakeLists.txt | 2++
Msrc/Plugin/ZynAddSubFX/CMakeLists.txt | 41++++++++++++++++++++++++++++-------------
Msrc/Plugin/ZynAddSubFX/ZynAddSubFX-UI-Zest.cpp | 48++++++++++++++++++++++++++++++++++++++++++++----
Msrc/Plugin/ZynAddSubFX/ZynAddSubFX.cpp | 10+++-------
Msrc/Synth/OscilGen.cpp | 58+++++++++++++++++++++++++++++++++++-----------------------
Msrc/Synth/Resonance.cpp | 17+++++++++++------
Msrc/Tests/CMakeLists.txt | 12++++++++++--
Msrc/Tests/InstrumentStats.cpp | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/Tests/MessageTest.h | 43+++++++++++++++++++++++++++++++++++++++----
Msrc/Tests/PluginTest.h | 70+++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Asrc/Tests/SaveOSC.cpp | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Tests/guitar-adnote.xmz | 5++++-
Msrc/UI/BankUI.fl | 21+++++++++++++++++++++
Msrc/UI/BankView.cpp | 31+++++++++++++++++++++++++------
Msrc/UI/BankView.h | 3---
Msrc/UI/Fl_Osc_Tree.H | 2+-
Msrc/main.cpp | 40++++++++++++++++++++++++++++++++++++----
Azynaddsubfx-oss.desktop | 10++++++++++
68 files changed, 2104 insertions(+), 615 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -4,6 +4,7 @@ [submodule "rtosc"] path = rtosc url = https://github.com/fundamental/rtosc + branch = default_values [submodule "DPF"] path = DPF url = git://github.com/DISTRHO/DPF diff --git a/.travis.yml b/.travis.yml @@ -4,14 +4,6 @@ compiler: - gcc before_install: - - sudo apt-get --purge remove gcc - - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - - sudo apt-add-repository -y ppa:dhart/ppa - - sudo apt-get -qq update - - sudo apt-get -qq install g++-4.8 - - sudo apt-get -qq install gcc-4.8 - - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90 - - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 90 - sudo apt-get install zlib1g-dev libmxml-dev libfftw3-dev dssi-dev libfltk1.3-dev libxpm-dev - sudo apt-get install --force-yes cxxtest - wget http://downloads.sourceforge.net/liblo/liblo-0.28.tar.gz diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,8 +3,12 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") project(zynaddsubfx) set(VERSION_MAJOR "3") set(VERSION_MINOR "0") -set(VERSION_REVISION "1") +set(VERSION_REVISION "2") +#Set data directory, if any +if(DEFINED ZYN_DATADIR) +add_definitions(-DZYN_DATADIR="${ZYN_DATADIR}") +endif() #Include RTOSC if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/rtosc/CMakeLists.txt") @@ -34,14 +38,19 @@ add_subdirectory(doc) # Doxygen only install(FILES AUTHORS.txt COPYING HISTORY.txt README.adoc DESTINATION share/doc/zynaddsubfx ) -install(FILES zynaddsubfx-jack.desktop zynaddsubfx-alsa.desktop +install(FILES zynaddsubfx-jack.desktop zynaddsubfx-alsa.desktop zynaddsubfx-oss.desktop DESTINATION share/applications) install(FILES zynaddsubfx.svg DESTINATION share/pixmaps) install(DIRECTORY instruments/banks DESTINATION share/zynaddsubfx) +if(DEFINED ZYN_EXAMPLESDIR) +install(DIRECTORY instruments/examples + DESTINATION ${ZYN_EXAMPLESDIR}) +else() install(DIRECTORY instruments/examples DESTINATION share/zynaddsubfx) +endif() install(DIRECTORY instruments/ZynAddSubFX.lv2presets DESTINATION ${PluginLibDir}/lv2) diff --git a/HISTORY.txt b/HISTORY.txt @@ -1,3 +1,14 @@ +3.0.2 (21 July 2017) + - Upgrade MIDI learn system to include host automations and macro + learned controls + - Upgrade analog filter parameters to floating point resolution + - Add default values to OSC metadata + - Fix exit when closing zyn-fusion subprocess + - Fix crash with large number of pad synth samples + - Silence 0 volume effects + - Silence 0 volume add synth voices + - Fix minor bugs + 3.0.1 (28 November 2016) - Fix bank screen with Zyn-Fusion - Fix crash on startup with GL 2.1 to 3.1 diff --git a/doc/automation-proposal/automation1.png b/doc/automation-proposal/automation1.png Binary files differ. diff --git a/doc/automation-proposal/current_slot.png b/doc/automation-proposal/current_slot.png Binary files differ. diff --git a/doc/automation-proposal/macro.png b/doc/automation-proposal/macro.png Binary files differ. diff --git a/doc/automation-proposal/macro_view.png b/doc/automation-proposal/macro_view.png Binary files differ. diff --git a/doc/automation-proposal/parameter-automation-documentation.txt b/doc/automation-proposal/parameter-automation-documentation.txt @@ -0,0 +1,208 @@ +An Introduction to the ZynAddSubFX Macro System +=============================================== +:author: Mark McCurry, Spencer Jackson +:toc: + + +Motivation +---------- + +One of the major issues expressed by users around the time of the initial +3.0.0 launch was that they were dissatisfied with automations when using +ZynAddSubFX within their preferred VST host. This was a combination of: + +1. Hosts providing a poor workflow for sending MIDI CC events compared to VST + parameters +2. Confusion with the use of MIDI learn within Zyn +3. Issues regarding saving/loading parameters exposed through MIDI learn +4. Parameter resolution for MIDI CCs +5. Some parameters only produce updates on the next note + +This feature attempts to address points 1, 2, & 3. Issue 4 is a large undertaking +but is expected to become complete for the 3.1.x release. Issue 5 is being fixed +one set of parameters at a time and should be addressed in the 3.0.x series. + +Overview & Terminology +---------------------- + +The Macro System boils down to replacing the existing MIDI learn implementation +with a series of 'slots' for parameter automation and automation +macros. In its current state it does that and a little more but as it matures it +should become a powerful system for automation and live tweaking of sounds. + +What is an automation? +~~~~~~~~~~~~~~~~~~~~~~ +image::automation1.png[] + +- A single parameter, e.g. the addsynth base frequency in part 1, kit 1 +- A gain and offset to adjust the range of values the automation will reach +(e.g. -50 cents to +150 cents) +- A mapping function (currently only linear, log, custom, etc coming soon) + +What is a macro? +~~~~~~~~~~~~~~~~ + +- A group of automations that get assigned to an automation slot + +All automations in the macro are operated together. Thus turning one knob on +your MIDI controller or a single automation lane in a DAW can change up to +4 parameters (see <<FAQ>>) +at a time. Using the automation mappings allows for all the parameters to be adjusted +in different amounts and/or directions, but they are all moved by actuating the macro. + +Macros are assembled by adding automations to a macro slot. + +What is a macro slot? +~~~~~~~~~~~~~~~~~~~~~~ + +- A set of one or more automations (a single macro) +- An optional MIDI CC binding or plugin host automation + +In Zynaddsubfx 3.0.2 all macro slots are global. This means that loading new +instruments does not switch the automations in any way. Only loading and saving +Zyn master files will allow you to quickly switch between macro slot configurations. +In future versions there +will be global macro slots and instrument macro slots that can be saved +and loaded independently. + +The global macro slots are exposed to VST/LV2 hosts. +This keeps the total number of parameters exposed to a host somewhat +reasonable: 16 slots for automation of up to 64 parameters. + + +At the level of a Zyn master file (.xmz) the automation slots +can be bound to MIDI CCs; loading the file will reload the midi bindings. +Zyn MIDI-learn files (.xlz) can store the MIDI CC+Channel linkage to all +automation slots. + +The Macros View +--------------- + +image::macro_view.png[] + +The Macro view shows: + +. List of Slots +.. Slot state (active, inactive) - currently this button is disabled, all slots are always active +.. A "clear slot" button (the x) - removes all automations in the slot +.. Slot name - click name to edit +. Macro selector buttons +.. highlighted button is the slot currently selected +.. use these when macro learning to select which slot to add to +. Macro sliders - (green bars under names) +.. indicates current macro value +.. slide to adjust through the UI +. Learning Mode selectors +.. Normal Learn - each learned automation is added to the next available slot +.. Macro Learn - learned automations are added to the currently selected slot (if not full) + +image::current_slot.png[] +Currently selected slot is selected/indicated with these buttons. + +The selected macro shows: + +. MIDI binding if any e.g. "Chan 2, CC 14" (or "None" or "Learning Queue N") +. 4 Automations + +image::automation1.png[] + +Each automation shows + +. Parameter path/name - clicking a section of the path shows a dropdown menu allowing easy switching between parts, voices, engines, etc. +. Minimum, maximum, center value of parameter +. Graph of map function +.. X is the macro value +.. Y is value of parameter being automated +. Gain knob - adjust to change slope of map function +. Offset knob adjust to move map function up or down +. Remove button (X) - click to remove the automation + + +Workflow examples +----------------- + +Automating a parameter in plugin mode +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +. Learn mode should be set to Normal Learn (this is the default) +. Use CTRL or click the [Learn] button to start learning an automation +. Click on one or more controls (knobs/sliders/buttons) to assign them to + automation slots +.. After clicking, each control is allocated to the next free Master + automation slot +.. the automation slot is put into the unbound state (it can be put into + "MIDI learn" mode later. +. The automation can be adjusted in the automation view + +Binding parameters to MIDI in standalone mode +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +. Learn mode should be set to Normal Learn (this is the default) +. Use CTRL or click the [Learn] button to start learning an automation +. Click on one or more controls (knobs/sliders/buttons) to assign them to + automation slots +.. After clicking, each control is allocated to the next free Master + automation slot +.. Each automation slot is then placed into the "MIDI learn" mode, watching for + the next unbound MIDI CC that arrives in standalone mode + (MIDI CCs are identified by Channel+CC# in Master automation slots) +. The user optionally sends CCs to be bound by wiggling the controls on their + physical (or virtual) MIDI device +. The automation can be adjusted in the automation view + +Setting up a macro +~~~~~~~~~~~~~~~~~~ + +image::macro.png[] +A macro with 3 automations + +. Enter the Macro view +. Select the Macro Learn button (the Normal Learn button will turn off) +. Select the macro slot you would like to add new automations to +. Hold CTRL or double click the [Learn] button to enter learn mode +. Click the parameter(s) that you wish to add the the macro slot +. To add parameters to another macro slot switch the currently selected + macro slot in the Macro view + +Adjusting automation ranges +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +. Go to the macro view +. Find the right macro slot and automation +. Adjust gain +.. High gain will make the automation's parameter traverse the whole range of travel with just a little movement of the macro. +.. Low gain will make the automation's parameter travel less range through the knob than the knob in the UI +.. Negative gain makes the macro adjust the parameter down when the macro is turned up +. Adjust offset +.. This moves the automation's parameter range of travel up or down +. If an automation was unintentional the user can use the X button to remove that automation + +Learning/changing MIDI CC bindings after loading an instrument/binding +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +. Go to the [Macro] view +. Find the right macro slot and automation +. Single click on the MIDI CC widget +.. The automation switches to the "learning CC" state +. Wiggle some MIDI controller +. OR Double click on the MIDI CC widget +.. this shows a dialog to manually choose the channel and CC id +. The CC has been remapped + +FAQ +--- +Q: Why only 4 automations to a slot? + +A: Limiting it to only 4 allowed for an easier design in the UI. There is no +limitation in the software and if many users request more capability, we will +extend it to more. + + +Q: What happens if a single parameter is in multiple automation slots? + +A: Currently the engine will set the parameter to the value from the macro that +was most recently changed. Future versions may allow for macro fusion to combine +multiple values in different ways. + + +Q: Why didn't you add _feature X_ + +A: We love well documented feature requests. Please leave your idea in our bugtracker at https://sourceforge.net/p/zynaddsubfx/feature-requests/ diff --git a/src/Effects/Alienwah.cpp b/src/Effects/Alienwah.cpp @@ -36,17 +36,26 @@ rtosc::Ports Alienwah::ports = { else d.reply(d.loc, "i", o->Ppreset); rEnd}, - //Pvolume/Ppanning are common - rEffPar(Pfreq, 2, rShort("freq"), "Effect Frequency"), - rEffPar(Pfreqrnd, 3, rShort("rand"), "Frequency Randomness"), + rEffParVol(rDefault(127), rPresetsAt(3, 93)), + rEffParPan(), + rEffPar(Pfreq, 2, rShort("freq") rPresets(70, 73, 63, 25), + "Effect Frequency"), + rEffPar(Pfreqrnd, 3, rShort("rand"), rPreset(1, 106) rDefault(0), + "Frequency Randomness"), rEffPar(PLFOtype, 4, rShort("shape"), - rOptions(sine, triangle), "LFO Shape"), - rEffPar(PStereo, 5, rShort("stereo"), "Stereo Mode"), - rEffPar(Pdepth, 6, rShort("depth"), "LFO Depth"), - rEffPar(Pfeedback, 7, rShort("fb"), "Feedback"), - rEffPar(Pdelay, 8, rLinear(1,100), rShort("delay"), "Delay"), - rEffPar(Plrcross, 9, rShort("l/r"), "Left/Right Crossover"), - rEffPar(Pphase, 10, rShort("phase"), "Phase"), + rOptions(sine, triangle), rPresets(sine, sine, triangle, triangle), + "LFO Shape"), + rEffPar(PStereo, 5, rShort("stereo"), rPresets(62, 101, 100, 66), + "Stereo Mode"), + rEffPar(Pdepth, 6, rShort("depth"), rPresets(60, 60, 112, 101), + "LFO Depth"), + rEffPar(Pfeedback, 7, rShort("fb"), rPreset(3, 11), rDefault(105), + "Feedback"), + rEffPar(Pdelay, 8, rLinear(1,100), rPresets(25, 17, 31, 47), + rShort("delay"), "Delay"), + rEffPar(Plrcross, 9, rShort("l/r"), rDefault(0), "Left/Right Crossover"), + rEffPar(Pphase, 10, rShort("phase"), rDefault(64), rPreset(2, 42), + rPreset(3, 86), "Phase"), }; #undef rBegin #undef rEnd diff --git a/src/Effects/Chorus.cpp b/src/Effects/Chorus.cpp @@ -39,17 +39,32 @@ rtosc::Ports Chorus::ports = { rEnd}, //Pvolume/Ppanning are common - rEffPar(Pfreq, 2, rShort("freq"), "Effect Frequency"), - rEffPar(Pfreqrnd, 3, rShort("rand"), "Frequency Randomness"), + rEffParVol(rDefault(64)), + rEffParPan(), + rEffPar(Pfreq, 2, rShort("freq"), + rPresets(50, 45, 29, 26, 29, 57, 33, 53, 40, 55), + "Effect Frequency"), + rEffPar(Pfreqrnd, 3, rShort("rand"), + rPreset(4, 117) rPreset(6, 34) rPreset(7, 34) rPreset(9, 105) + rDefault(0), "Frequency Randomness"), rEffPar(PLFOtype, 4, rShort("shape"), - rOptions(sine, tri), "LFO Shape"), - rEffPar(PStereo, 5, rShort("stereo"), "Stereo Mode"), - rEffPar(Pdepth, 6, rShort("depth"), "LFO Depth"), - rEffPar(Pdelay, 7, rShort("delay"), "Delay"), - rEffPar(Pfeedback,8, rShort("fb"), "Feedback"), - rEffPar(Plrcross, 9, rShort("l/r"), "Left/Right Crossover"), - rEffParTF(Pflangemode, 10, rShort("flange"), "Flange Mode"), - rEffParTF(Poutsub, 11, rShort("sub"), "Output Subtraction"), + rOptions(sine, tri), + rPresets(sine, sine, tri, sine, sine, sine, tri, tri, tri, sine) + "LFO Shape"), + rEffPar(PStereo, 5, rShort("stereo"), + rPresets(90, 98, 42, 42, 50, 60, 40, 94, 62), "Stereo Mode"), + rEffPar(Pdepth, 6, rShort("depth"), + rPresets(40, 56, 97, 115, 115, 23, 35, 35, 12), "LFO Depth"), + rEffPar(Pdelay, 7, rShort("delay"), + rPresets(85, 90, 95, 18, 9, 3, 3, 3, 19), "Delay"), + rEffPar(Pfeedback,8, rShort("fb"), + rPresets(64, 64, 90, 90, 31, 62, 109, 54, 97), "Feedback"), + rEffPar(Plrcross, 9, rShort("l/r"), rPresets(119, 19, 127, 127, 127), + rDefault(0), "Left/Right Crossover"), + rEffParTF(Pflangemode, 10, rShort("flange"), rDefault(false), + "Flange Mode"), + rEffParTF(Poutsub, 11, rShort("sub"), rPreset(4, true), rPreset(7, true), + rDefault(false), "Output Subtraction"), }; #undef rBegin #undef rEnd diff --git a/src/Effects/Distorsion.cpp b/src/Effects/Distorsion.cpp @@ -37,21 +37,29 @@ rtosc::Ports Distorsion::ports = { else d.reply(d.loc, "i", o->Ppreset); rEnd}, - //Pvolume/Ppanning are common - rEffPar(Plrcross, 2, rShort("l/r"), "Left/Right Crossover"), - rEffPar(Pdrive, 3, rShort("drive"), "Input amplification"), - rEffPar(Plevel, 4, rShort("output"), "Output amplification"), + rEffParVol(rDefault(127), rPresetsAt(2, 64, 64)), + rEffParPan(), + rEffPar(Plrcross, 2, rShort("l/r"), rDefault(35), "Left/Right Crossover"), + rEffPar(Pdrive, 3, rShort("drive"), rPresets(56, 29, 75, 85, 63, 88), + "Input amplification"), + rEffPar(Plevel, 4, rShort("output"), rPresets(70, 75, 80, 62, 75, 75), + "Output amplification"), rEffPar(Ptype, 5, rShort("type"), rOptions(Arctangent, Asymmetric, Pow, Sine, Quantisize, - Zigzag, Limiter, Upper Limiter, Lower Limiter, - Inverse Limiter, Clip, Asym2, Pow2, sigmoid), + Zigzag, Limiter, Upper Limiter, Lower Limiter, + Inverse Limiter, Clip, Asym2, Pow2, sigmoid), + rPresets(Arctangent, Asymmetric, Zigzag, + Asymmetric, Pow, Quantisize), "Distortion Shape"), - rEffParTF(Pnegate, 6, rShort("neg"), "Negate Signal"), - rEffPar(Plpf, 7, rShort("lpf"), "Low Pass Cutoff"), - rEffPar(Phpf, 8, rShort("hpf"), "High Pass Cutoff"), - rEffParTF(Pstereo, 9, rShort("stereo"), "Stereo"), - rEffParTF(Pprefiltering, 10, rShort("p.filt"), - "Filtering before/after non-linearity"), + rEffParTF(Pnegate, 6, rShort("neg"), rDefault(false), "Negate Signal"), + rEffPar(Plpf, 7, rShort("lpf"), + rPreset(0, 96), rPreset(4, 55), rDefault(127), "Low Pass Cutoff"), + rEffPar(Phpf, 8, rShort("hpf"), + rPreset(2, 105), rPreset(3, 118), rDefault(0), "High Pass Cutoff"), + rEffParTF(Pstereo, 9, rShort("stereo"), + rPresets(false, false, true, true, false, true), "Stereo"), + rEffParTF(Pprefiltering, 10, rShort("p.filt"), rDefault(false), + "Filtering before/after non-linearity"), {"waveform:", 0, 0, [](const char *, rtosc::RtData &d) { Distorsion &dd = *(Distorsion*)d.obj; diff --git a/src/Effects/DynamicFilter.cpp b/src/Effects/DynamicFilter.cpp @@ -36,16 +36,24 @@ rtosc::Ports DynamicFilter::ports = { else d.reply(d.loc, "i", o->Ppreset); rEnd}, - //Pvolume/Ppanning are common - rEffPar(Pfreq, 2, rShort("freq"), "Effect Frequency"), - rEffPar(Pfreqrnd, 3, rShort("rand"), "Frequency Randomness"), - rEffPar(PLFOtype, 4, rShort("shape"), - rOptions(sin, tri), "LFO Shape"), - rEffPar(PStereo, 5, rShort("stereo"), "Stereo Mode"), - rEffPar(Pdepth, 6, rShort("depth"), "LFO Depth"), - rEffPar(Pampsns, 7, rShort("sense"), "how the filter varies according to the input amplitude"), - rEffPar(Pampsnsinv, 8, rShort("sns.inv"), "Sense Inversion"), - rEffPar(Pampsmooth, 9, rShort("smooth"), "how smooth the input amplitude changes the filter"), + rEffParVol(rDefault(110), rPreset(2, 110), rPreset(4, 127)), + rEffParPan(), + rEffPar(Pfreq, 2, rShort("freq"), rPresets(80, 70, 30, 80, 50), + "Effect Frequency"), + rEffPar(Pfreqrnd, 3, rShort("rand"), rDefault(0), + "Frequency Randomness"), + rEffPar(PLFOtype, 4, rShort("shape"), rOptions(sin, tri), rDefault(sin), + "LFO Shape"), + rEffPar(PStereo, 5, rShort("stereo"), rPresets(64, 80, 50, 64, 96), + "Stereo Mode"), + rEffPar(Pdepth, 6, rShort("depth"), rPresets(0, 70, 80, 0, 64), + "LFO Depth"), + rEffPar(Pampsns, 7, rShort("sense"), + rPreset(0, 90) rPreset(3, 64) rDefault(0), + "how the filter varies according to the input amplitude"), + rEffPar(Pampsnsinv, 8, rShort("sns.inv"), rDefault(0), "Sense Inversion"), + rEffPar(Pampsmooth, 9, rShort("smooth"), rDefault(60), + "how smooth the input amplitude changes the filter"), }; #undef rBegin #undef rEnd diff --git a/src/Effects/EQ.cpp b/src/Effects/EQ.cpp @@ -80,8 +80,8 @@ rtosc::Ports EQ::ports = { memset(b, 0, sizeof(b)); eq->getFilter(a,b); - char type[MAX_EQ_BANDS*MAX_FILTER_STAGES*3*2+1] = {0}; - rtosc_arg_t val[MAX_EQ_BANDS*MAX_FILTER_STAGES*3*2] = {0}; + char type[MAX_EQ_BANDS*MAX_FILTER_STAGES*3*2+1] = {}; + rtosc_arg_t val[MAX_EQ_BANDS*MAX_FILTER_STAGES*3*2] = {}; for(int i=0; i<MAX_EQ_BANDS*MAX_FILTER_STAGES*3; ++i) { int stride = MAX_EQ_BANDS*MAX_FILTER_STAGES*3; type[i] = type[i+stride] = 'f'; diff --git a/src/Effects/Echo.cpp b/src/Effects/Echo.cpp @@ -39,12 +39,23 @@ rtosc::Ports Echo::ports = { else d.reply(d.loc, "i", o->Ppreset); rEnd}, - //Pvolume/Ppanning are common - rEffPar(Pdelay, 2, rShort("delay"), "Length of Echo"), - rEffPar(Plrdelay, 3, rShort("lr delay"), "Difference In Left/Right Delay"), - rEffPar(Plrcross, 4, rShort("cross"), "Left/Right Crossover"), - rEffPar(Pfb, 5, rShort("feedback"), "Echo Feedback"), - rEffPar(Phidamp, 6, rShort("damp"), "Dampen High Frequencies"), + rEffParVol(rDefault(67), rPresetsAt(6, 81, 81, 62)), + rEffParPan(rPresetsAt(2, 75, 60, 60, 64, 60, 60, 64)), + rEffPar(Pdelay, 2, rShort("delay"), + rPresets(35, 21, 60, 44, 102, 44, 46, 26, 28), + "Length of Echo"), + rEffPar(Plrdelay, 3, rShort("lr delay"), + rPresetsAt(4, 50, 17, 118, 100, 64), rDefault(64), + "Difference In Left/Right Delay"), + rEffPar(Plrcross, 4, rShort("cross"), + rPresetsAt(5, 0, 100, 127, 100), rDefault(30), + "Left/Right Crossover"), + rEffPar(Pfb, 5, rShort("feedback"), + rPresets(59, 59, 59, 0, 82, 82, 68, 67, 90), + "Echo Feedback"), + rEffPar(Phidamp, 6, rShort("damp"), + rPresets(0, 0, 10, 0, 48, 24, 18, 36, 55), + "Dampen High Frequencies"), }; #undef rBegin #undef rEnd diff --git a/src/Effects/Effect.h b/src/Effects/Effect.h @@ -19,11 +19,15 @@ #include "../Params/FilterParams.h" #include "../Misc/Stereo.h" +// bug: the effect parameters can currently be set, but such values +// will not be saved into XML files #ifndef rEffPar #define rEffPar(name, idx, ...) \ - {STRINGIFY(name) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rEffParCb(idx)} + {STRINGIFY(name) "::i", rProp(parameter) rDefaultDepends(preset) \ + DOC(__VA_ARGS__), NULL, rEffParCb(idx)} #define rEffParTF(name, idx, ...) \ - {STRINGIFY(name) "::T:F", rProp(parameter) DOC(__VA_ARGS__), NULL, rEffParTFCb(idx)} + {STRINGIFY(name) "::T:F", rProp(parameter) rDefaultDepends(preset) \ + DOC(__VA_ARGS__), NULL, rEffParTFCb(idx)} #define rEffParCb(idx) \ [](const char *msg, rtosc::RtData &d) {\ rObject &obj = *(rObject*)d.obj; \ @@ -40,6 +44,27 @@ d.reply(d.loc, obj.getpar(idx)?"T":"F");} #endif +#define rEffParCommon(pname, rshort, rdoc, idx, ...) \ +{STRINGIFY(pname) "::i", rProp(parameter) rLinear(0,127) \ + rShort(rshort) rDoc(rdoc), \ + 0, \ + [](const char *msg, rtosc::RtData &d) \ + { \ + rObject& eff = *(rObject*)d.obj; \ + if(!rtosc_narguments(msg)) \ + d.reply(d.loc, "i", eff.getpar(idx)); \ + else { \ + eff.changepar(0, rtosc_argument(msg, 0).i); \ + d.broadcast(d.loc, "i", eff.getpar(idx)); \ + } \ + }} + +#define rEffParVol(...) rEffParCommon(Pvolume, "amt", "amount of effect", 0, \ + __VA_ARGS__) +#define rEffParPan(...) rEffParCommon(Ppanning, "pan", "panning", 1, \ + __VA_ARGS__) + + namespace zyn { class FilterParams; diff --git a/src/Effects/EffectMgr.cpp b/src/Effects/EffectMgr.cpp @@ -38,15 +38,16 @@ namespace zyn { {STRINGIFY(name)"/", NULL, &name::ports,\ [](const char *msg, rtosc::RtData &data){\ rObject &o = *(rObject*)data.obj; \ - data.obj = o.efx; \ - if(!dynamic_cast<name*>(o.efx)) \ + data.obj = dynamic_cast<name*>(o.efx); \ + if(!data.obj) \ return; \ SNIP \ name::ports.dispatch(msg, data); \ }} static const rtosc::Ports local_ports = { - rSelf(EffectMgr), + rSelf(EffectMgr, rEnabledByCondition(self-enabled)), rPaste, + rEnabledCondition(self-enabled, obj->geteffect()), rRecurp(filterpars, "Filter Parameter for Dynamic Filter"), {"Pvolume::i", rProp(parameter) rLinear(0,127) rShort("amt") rDoc("amount of effect"), 0, @@ -93,7 +94,8 @@ static const rtosc::Ports local_ports = { d.broadcast(d.loc, "i", eff->geteffectparrt(atoi(mm))); } }}, - {"preset::i", rProp(parameter) rProp(alias) rDoc("Effect Preset Selector"), NULL, + {"preset::i", rProp(parameter) rProp(alias) rDoc("Effect Preset Selector") + rDefault(0), NULL, [](const char *msg, rtosc::RtData &d) { char loc[1024]; @@ -129,18 +131,10 @@ static const rtosc::Ports local_ports = { eq->getFilter(a,b); d.reply(d.loc, "bb", sizeof(a), a, sizeof(b), b); }}, - {"efftype::i", rOptions(Disabled, Reverb, Echo, Chorus, - Phaser, Alienwah, Distortion, EQ, DynFilter) - rProp(parameter) rDoc("Get Effect Type"), NULL, - [](const char *m, rtosc::RtData &d) - { - EffectMgr *eff = (EffectMgr*)d.obj; - if(rtosc_narguments(m)) { - eff->changeeffectrt(rtosc_argument(m,0).i); - d.broadcast(d.loc, "i", eff->nefx); - } else - d.reply(d.loc, "i", eff->nefx); - }}, + {"efftype::i:c:S", rOptions(Disabled, Reverb, Echo, Chorus, + Phaser, Alienwah, Distortion, EQ, DynFilter) rDefault(Disabled) + rProp(parameter) rDoc("Get Effect Type"), NULL, + rCOptionCb(obj->nefx, obj->changeeffectrt(var))}, {"efftype:b", rProp(internal) rDoc("Pointer swap EffectMgr"), NULL, [](const char *msg, rtosc::RtData &d) { @@ -472,7 +466,12 @@ void EffectMgr::add2XML(XMLwrapper& xml) xml.beginbranch("EFFECT_PARAMETERS"); for(int n = 0; n < 128; ++n) { - int par = geteffectpar(n); + int par = 0; + if(efx) + par = efx->getpar(n); + else if(n<128) + par = settings[n]; + if(par == 0) continue; xml.beginbranch("par_no", n); diff --git a/src/Effects/EffectMgr.h b/src/Effects/EffectMgr.h @@ -77,6 +77,30 @@ class EffectMgr:public Presets //Parameters Prior to initialization char preset; + + /** + * When loading an effect from XML the child effect cannot be loaded + * directly as it would require access to the realtime memory pool, + * which cannot be done outside of the realtime thread. + * Therefore, parameters are loaded to this array which can then be used + * to construct the full effect (via init()) once the object is in the + * realtime context. + * + * Additionally this structure is used in the case of pasting effects as + * pasted effect object are *not* fully initialized when they're put on + * the middleware -> backend ringbuffer, but settings has the values + * loaded from the XML serialization. + * The settings values can be pasted once they're on the realtime thread + * and then they can be applied. + * + * The requirement that the realtime memory pool is used to create the + * effect is in place as it is possible to change the effect type in the + * realtime thread and thus the new effect would draw from the realtime + * memory pool and the old object would be expected to be freed to the + * realtime memory pool. + * + * See also: PresetExtractor.cpp + */ char settings[128]; bool dryonly; diff --git a/src/Effects/Phaser.cpp b/src/Effects/Phaser.cpp @@ -37,7 +37,8 @@ namespace zyn { d.reply(d.loc, "i", p.P##pname); \ rEnd #define rParamPhaser(name, ...) \ - {STRINGIFY(P##name) "::i", rProp(parameter) rMap(min, 0) rMap(max, 127) DOC(__VA_ARGS__), NULL, ucharParamCb(name)} + {STRINGIFY(P##name) "::i", rProp(parameter) rMap(min, 0) rMap(max, 127) \ + rDefaultDepends(preset) DOC(__VA_ARGS__), NULL, ucharParamCb(name)} rtosc::Ports Phaser::ports = { {"preset::i", rProp(parameter) @@ -53,23 +54,52 @@ rtosc::Ports Phaser::ports = { else d.reply(d.loc, "i", o->Ppreset); rEnd}, - //Pvolume/Ppanning are common - rEffPar(lfo.Pfreq, 2, rShort("freq"), "LFO frequency"), - rEffPar(lfo.Prandomness, 3, rShort("rnd."), "LFO randomness"), + rEffParVol(rDefault(64), rPreset(3, 39), rPreset(10, 25)), + rEffParPan(), + rEffPar(lfo.Pfreq, 2, rShort("freq"), + rPresets(36, 35, 31, 22, 20, 53, 14, 14, 9, 14, 127, 1), + "LFO frequency"), + rEffPar(lfo.Prandomness, 3, rShort("rnd."), + rPreset(5, 100), rPreset(7, 5), rPresetsAt(9, 10, 10, 10), + rDefault(0), "LFO randomness"), rEffPar(lfo.PLFOtype, 4, rShort("type"), - rOptions(sine, tri), "lfo shape"), - rEffPar(lfo.Pstereo, 5, rShort("stereo"), "Left/right channel phase shift"), - rEffPar(Pdepth, 6, rShort("depth"), "LFP depth"), - rEffPar(Pfb, 7, rShort("fb"), "Feedback"), - rEffPar(Pstages, 8, rLinear(1,12), rShort("stages"), ""), - rParamPhaser(lrcross, rShort("cross"), "Channel routing"), - rParamPhaser(offset, rShort("off"), "Offset"), - rEffParTF(Poutsub, 10, rShort("sub"), "Invert output"), - rParamPhaser(phase, rShort("phase"), ""), - rParamPhaser(width, rShort("width"), ""), - rEffParTF(Phyper, 12, rShort("hyp."), "Square the LFO"), - rEffPar(Pdistortion, 13, rShort("distort"), "Distortion"), - rEffParTF(Panalog, 14, rShort("analog"), "Use analog phaser"), + rPreset(4, tri), rPresetsAt(6, tri, tri), rPreset(11, tri), + rDefault(sine), + rOptions(sine, tri), "lfo shape"), + rEffPar(lfo.Pstereo, 5, rShort("stereo"), + rPresetsAt(1, 88, 66, 66, 110, 58), rDefault(64), + "Left/right channel phase shift"), + rEffPar(Pdepth, 6, rShort("depth"), + rPresets(110, 40, 68, 67, 67, 37, 64, 70, 60, 45, 25, 70), + "LFP depth"), + rEffPar(Pfb, 7, rShort("fb"), + rPresets(64, 64, 107, 10, 78, 78, 40, 40, 40, 80, 16, 40), + "Feedback"), + rEffPar(Pstages, 8, rLinear(1,12), rShort("stages"), + rPresets(1, 3, 2, 5, 10, 3, 4, 6, 8, 7, 8, 12), + ""), + rParamPhaser(lrcross, rShort("cross"), + rPresetsAt(6, 10, 10, 10, 10, 100, 10) rDefault(0), + "Channel routing"), + rParamPhaser(offset, rShort("off"), + rPresetsAt(6, 10, 10, 10, 10, 100, 10) rDefault(0), + "Offset"), + rEffParTF(Poutsub, 10, rShort("sub"), + rPreset(3, true), rPreset(9, true), rDefault(false), + "Invert output"), + rParamPhaser(phase, rShort("phase"), + rPresets(20, 20, 20, 20, 20, 20, 110, 110, 40, 110, 25, 110), ""), + rParamPhaser(width, rShort("width"), + rPresets(20, 20, 20, 20, 20, 20, 110, 110, 40, 110, 25, 110), ""), + rEffParTF(Phyper, 12, rShort("hyp."), + rPresetsAt(6, true, true, false, true, false, true), + rDefault(false), "Square the LFO"), + rEffPar(Pdistortion, 13, rShort("distort"), + rPresetsAt(6, 20, 20, 20, 20, 20, 20), rDefault(0), + "Distortion"), + rEffParTF(Panalog, 14, rShort("analog"), + rPresetsAt(6, true, true, true, true, true, true), rDefault(false), + "Use analog phaser"), }; #undef rBegin #undef rEnd diff --git a/src/Effects/Reverb.cpp b/src/Effects/Reverb.cpp @@ -39,18 +39,38 @@ rtosc::Ports Reverb::ports = { else d.reply(d.loc, "i", o->Ppreset); rEnd}, - //Pvolume/Ppanning are common - rEffPar(Ptime, 2, rShort("time"), "Length of Reverb"), - rEffPar(Pidelay, 3, rShort("i.time"), "Delay for first impulse"), - rEffPar(Pidelayfb,4, rShort("i.fb"), "Feedback for first impulse"), - rEffPar(Plpf, 7, rShort("lpf"), "Low pass filter"), - rEffPar(Phpf, 8, rShort("hpf"), "High pass filter"), - rEffPar(Plohidamp,9, rShort("damp"), "Dampening"), + rEffParVol(rDefault(90), rPresets(80, 80, 80), + rPresetsAt(5, 100, 100, 110, 85, 95)), + rEffParPan(rPreset(8, 80)), + rEffPar(Ptime, 2, rShort("time"), + rPresets(63, 69, 69, 51, 53, 33, 21, 14, 84, 26, 40, 93, 111), + "Length of Reverb"), + rEffPar(Pidelay, 3, rShort("i.time"), + rPresets(24, 35, 24, 10, 20, 0, 26, 0, 20, 60, 88, 15, 30), + "Delay for first impulse"), + rEffPar(Pidelayfb,4, rShort("i.fb"), rPresetsAt(8, 42, 71, 71), rDefault(0), + "Feedback for first impulse"), + rEffPar(Plpf, 7, rShort("lpf"), + rPreset(1, 85), rPresetsAt(62, 127, 51, 114, 114, 114), + rDefault(127), "Low pass filter"), + rEffPar(Phpf, 8, rShort("hpf"), + rPresets(5), rPresetsAt(2, 75, 21, 75), rPreset(7, 50), + rPreset(12, 90), rDefault(0), "High pass filter"), + rEffPar(Plohidamp,9, rShort("damp"), rDefault(0), + rPresets(83, 71, 78, 78, 71, 106, 77, 71, 78, 64, 88, 77, 74) + "Dampening"), //Todo make this a selector - rEffPar(Ptype, 10,rShort("type"), - rOptions(Random, Freeverb, Bandwidth), "Type"), - rEffPar(Proomsize,11,rShort("size"), "Room Size"), - rEffPar(Pbandwidth,12,rShort("bw"), "Bandwidth"), + rEffPar(Ptype, 10, rShort("type"), + rOptions(Random, Freeverb, Bandwidth), + rPresets(Freeverb, Random, Freeverb, Freeverb, Freeverb, Random, + Freeverb, Random, Freeverb, Freeverb, Freeverb, Random, + Freeverb) + rDefault(Random), "Type"), + rEffPar(Proomsize,11,rShort("size"), + rPreset(2, 85), rPresetsAt(5, 30, 45, 25, 105), + rPresetsAt(11, 95, 80), rDefault(64), + "Room Size"), + rEffPar(Pbandwidth,12,rShort("bw"), rDefault(20), "Bandwidth"), }; #undef rBegin #undef rEnd diff --git a/src/Misc/Bank.cpp b/src/Misc/Bank.cpp @@ -30,6 +30,9 @@ #include "Util.h" #include "Part.h" #include "BankDb.h" +#ifdef WIN32 +#include <windows.h> +#endif using namespace std; @@ -362,6 +365,18 @@ void Bank::rescanforbanks() for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) if(!config->cfg.bankRootDirList[i].empty()) scanrootdir(config->cfg.bankRootDirList[i]); +#ifdef WIN32 + { + //Search the VST Directory for banks/preset/etc + char path[1024]; + GetModuleFileName(GetModuleHandle("ZynAddSubFX.dll"), path, sizeof(path)); + if(strstr(path, "ZynAddSubFX.dll")) { + strstr(path, "ZynAddSubFX.dll")[0] = 0; + strcat(path, "banks"); + scanrootdir(path); + } + } +#endif //sort the banks sort(banks.begin(), banks.end()); @@ -477,7 +492,7 @@ std::vector<std::string> Bank::search(std::string s) const std::vector<std::string> Bank::blist(std::string s) { std::vector<std::string> out; - int result = loadbank(s); + loadbank(s); for(int i=0; i<128; ++i) { if(ins[i].filename.empty()) out.push_back("Empty Preset"); diff --git a/src/Misc/BankDb.cpp b/src/Misc/BankDb.cpp @@ -80,17 +80,17 @@ static svec split(string s) return vec; } -static string line(string s) -{ - string ss; - for(char c:s) { - if(c != '\n') - ss.push_back(c); - else - return ss; - } - return ss; -} +//static string line(string s) +//{ +// string ss; +// for(char c:s) { +// if(c != '\n') +// ss.push_back(c); +// else +// return ss; +// } +// return ss; +//} bvec BankDb::search(std::string ss) const { @@ -231,10 +231,18 @@ BankEntry BankDb::processXiz(std::string filename, //Grab a timestamp struct stat st; - int ret = lstat(fname.c_str(), &st); int time = 0; + + //gah windows, just implement the darn standard APIs +#ifndef WIN32 + int ret = lstat(fname.c_str(), &st); if(ret != -1) time = st.st_mtim.tv_sec; +#else + int ret = 0; + time = rand(); +#endif + //quickly check if the file exists in the cache and if it is up-to-date if(cache.find(fname) != cache.end() && diff --git a/src/Misc/Config.cpp b/src/Misc/Config.cpp @@ -122,8 +122,8 @@ static const rtosc::Ports ports = { //rArrayS(cfg.presetsDirList,MAX_BANK_ROOT_DIRS), rToggle(cfg.CheckPADsynth, "Old Check For PADsynth functionality within a patch"), rToggle(cfg.IgnoreProgramChange, "Ignore MIDI Program Change Events"), - rParamI(cfg.UserInterfaceMode, "Beginner/Advanced Mode Select"), - rParamI(cfg.VirKeybLayout, "Keyboard Layout For Virtual Piano Keyboard"), + rParamI(cfg.UserInterfaceMode, "Beginner/Advanced Mode Select"), + rParamI(cfg.VirKeybLayout, "Keyboard Layout For Virtual Piano Keyboard"), //rParamS(cfg.LinuxALSAaudioDev), //rParamS(cfg.nameTag) {"cfg.OscilPower::i", rProp(parameter) rDoc("Size Of Oscillator Wavetable"), 0, @@ -138,19 +138,26 @@ static const rtosc::Ports ports = { c.cfg.OscilSize = val; d.broadcast(d.loc, "i", (int)(log(c.cfg.OscilSize*1.0)/log(2.0))); }}, + {"clear-favorites:", rDoc("Clear favorite directories"), 0, + [](const char *msg, rtosc::RtData &d) { + Config &c = *(Config*)d.obj; + for(int i=0; i<MAX_BANK_ROOT_DIRS; ++i) + c.cfg.favoriteList[i] = ""; + }}, {"add-favorite:s", rDoc("Add favorite directory"), 0, [](const char *msg, rtosc::RtData &d) { Config &c = *(Config*)d.obj; + const char *path = rtosc_argument(msg, 0).s; for(int i=0; i<MAX_BANK_ROOT_DIRS; ++i) { - if(c.cfg.favoriteList[i].empty()) { - c.cfg.favoriteList[i] = rtosc_argument(msg, 0).s; + if(c.cfg.favoriteList[i].empty() || c.cfg.favoriteList[i] == path) { + c.cfg.favoriteList[i] = path; return; } } }}, - {"favorites:", rProp(parameter), 0, + {"favorites:", /*rProp(parameter)*/ 0, 0, [](const char *msg, rtosc::RtData &d) { Config &c = *(Config*)d.obj; @@ -174,7 +181,9 @@ const rtosc::Ports &Config::ports = zyn::ports; #endif Config::Config() -{} +{ + init(); +} void Config::init() { @@ -225,14 +234,18 @@ void Config::init() //banks cfg.bankRootDirList[0] = "~/banks"; cfg.bankRootDirList[1] = "./"; - cfg.bankRootDirList[2] = "/usr/share/zynaddsubfx/banks"; - cfg.bankRootDirList[3] = "/usr/local/share/zynaddsubfx/banks"; #ifdef __APPLE__ - cfg.bankRootDirList[4] = "../Resources/banks"; + cfg.bankRootDirList[2] = "../Resources/banks"; #else - cfg.bankRootDirList[4] = "../banks"; + cfg.bankRootDirList[2] = "../banks"; +#endif + cfg.bankRootDirList[3] = "banks"; +#ifdef ZYN_DATADIR + cfg.bankRootDirList[4] = ZYN_DATADIR "/banks"; +#else + cfg.bankRootDirList[4] = "/usr/share/zynaddsubfx/banks"; + cfg.bankRootDirList[5] = "/usr/local/share/zynaddsubfx/banks"; #endif - cfg.bankRootDirList[5] = "banks"; } if(cfg.presetsDirList[0].empty()) { @@ -244,8 +257,12 @@ void Config::init() cfg.presetsDirList[1] = "../presets"; #endif cfg.presetsDirList[2] = "presets"; +#ifdef ZYN_DATADIR + cfg.presetsDirList[3] = ZYN_DATADIR "/presets"; +#else cfg.presetsDirList[3] = "/usr/share/zynaddsubfx/presets"; cfg.presetsDirList[4] = "/usr/local/share/zynaddsubfx/presets"; +#endif } cfg.LinuxALSAaudioDev = "default"; cfg.nameTag = ""; diff --git a/src/Misc/Master.cpp b/src/Misc/Master.cpp @@ -16,6 +16,7 @@ #include "Part.h" +#include "zyn-version.h" #include "../Misc/Stereo.h" #include "../Misc/Util.h" #include "../Params/LFOParams.h" @@ -32,6 +33,7 @@ #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> +#include <fstream> #include <iostream> #include <algorithm> #include <cmath> @@ -51,12 +53,18 @@ static const Ports sysefxPort = rDoc("gain on part to sysefx routing"), 0, [](const char *m, RtData&d) { - //ok, this is going to be an ugly workaround - //we know that if we are here the message previously MUST have - //matched Psysefxvol#/ - //and the number is one or two digits at most - const char *index_1 = m; - index_1 -=2; + //we know that if we are here the location must + //be ...Psysefxvol#N/part#M + //and the number "N" is one or two digits at most + + // go backto the '/' + const char* m_findslash = m + strlen(m), + * loc_findslash = d.loc + strlen(d.loc); + for(;*loc_findslash != '/'; --m_findslash, --loc_findslash) + assert(*loc_findslash == *m_findslash); + assert(m_findslash + 1 == m); + + const char *index_1 = loc_findslash-1; assert(isdigit(*index_1)); if(isdigit(index_1[-1])) index_1--; @@ -80,9 +88,15 @@ static const Ports sysefsendto = {"to#" STRINGIFY(NUM_SYS_EFX) "::i", rProp(parameter) rDoc("sysefx to sysefx routing gain"), 0, [](const char *m, RtData&d) { - //same ugly workaround as before - const char *index_1 = m; - index_1 -=2; + //same workaround as before + //go backto the '/' + const char* m_findslash = m + strlen(m), + * loc_findslash = d.loc + strlen(d.loc); + for(;*loc_findslash != '/'; --m_findslash, --loc_findslash) + assert(*loc_findslash == *m_findslash); + assert(m_findslash + 1 == m); + + const char *index_1 = loc_findslash-1; assert(isdigit(*index_1)); if(isdigit(index_1[-1])) index_1--; @@ -117,7 +131,7 @@ static int get_next_int(const char *msg) } static const Ports mapping_ports = { - {"offset::f", rProp(parameter) rShort("off") rLinear(-50, 50) rMap(unit, percent), 0, + {"offset::f", rProp(parameter) rDefault(0) rShort("off") rLinear(-50, 50) rMap(unit, percent), 0, rBegin; int slot = d.idx[1]; int param = d.idx[0]; @@ -128,7 +142,7 @@ static const Ports mapping_ports = { } else d.reply(d.loc, "f", a.getSlotSubOffset(slot, param)); rEnd}, - {"gain::f", rProp(parameter) rShort("gain") rLinear(-200, 200) rMap(unit, percent), 0, + {"gain::f", rProp(parameter) rDefault(100) rShort("gain") rLinear(-200, 200) rMap(unit, percent), 0, rBegin; int slot = d.idx[1]; int param = d.idx[0]; @@ -142,7 +156,7 @@ static const Ports mapping_ports = { }; static const Ports auto_param_ports = { - {"used:", rProp(parameter) rProp(read-only) rDoc("If automation is assigned to anything"), 0, + {"used::T:F", rProp(parameter) rProp(read-only) rDoc("If automation is assigned to anything"), 0, rBegin; int slot = d.idx[1]; int param = d.idx[0]; @@ -158,13 +172,13 @@ static const Ports auto_param_ports = { else d.reply(d.loc, a.slots[slot].automations[param].active ? "T" : "F"); rEnd}, - {"path:", rProp(parameter) rProp(read-only) rDoc("Path of parameter"), 0, + {"path::s", rProp(parameter) rProp(read-only) rDoc("Path of parameter"), 0, rBegin; int slot = d.idx[1]; int param = d.idx[0]; d.reply(d.loc, "s", a.slots[slot].automations[param].param_path); rEnd}, - {"clear:", 0, 0, + {"clear:", rDoc("Clear automation param"), 0, rBegin; int slot = d.idx[1]; int param = d.idx[0]; @@ -172,6 +186,7 @@ static const Ports auto_param_ports = { rEnd}, {"mapping/", 0, &mapping_ports, rBegin; + (void) a; SNIP; mapping_ports.dispatch(msg, d); rEnd}, @@ -205,7 +220,7 @@ static const Ports slot_ports = { // rtosc_argument(msg, 1).s, // rtosc_argument(msg, 2).T); // rEnd}, - {"value::f", rProp(parameter) rMap(default, 0.5) rLinear(0, 1) rDoc("Access current value in slot 'i' (0..1)"), 0, + {"value::f", rProp(no learn) rProp(parameter) rMap(default, 0.5) rLinear(0, 1) rDoc("Access current value in slot 'i' (0..1)"), 0, rBegin; int num = d.idx[0]; if(!strcmp("f",rtosc_argument_string(msg))) { @@ -241,12 +256,12 @@ static const Ports slot_ports = { else d.reply(d.loc, a.slots[slot].active ? "T" : "F"); rEnd}, - {"learning:", rProp(parameter) rMap(default, -1) rDoc("If slot is trying to find a midi learn binding"), 0, + {"learning::i", rProp(parameter) rMap(default, -1) rDoc("If slot is trying to find a midi learn binding"), 0, rBegin; int slot = d.idx[0]; d.reply(d.loc, "i", a.slots[slot].learning); rEnd}, - {"clear:", 0, 0, + {"clear:", rDoc("Clear automation slot"), 0, rBegin; int slot = d.idx[0]; a.clearSlot(slot); @@ -283,7 +298,8 @@ static const Ports automate_ports = { if(a.active_slot >= 0) a.createBinding(a.active_slot, rtosc_argument(msg, 0).s, true); rEnd}, - {"slot#16/", rDoc("Parameters of individual automation slots"), &slot_ports, + // TODO: remove rNoWalk + {"slot#16/", rNoWalk rDoc("Parameters of individual automation slots"), &slot_ports, rBegin; (void)a; d.push_index(get_next_int(msg)); @@ -291,6 +307,40 @@ static const Ports automate_ports = { slot_ports.dispatch(msg, d); d.pop_index(); rEnd}, + {"clear", rDoc("Clear all automation slots"), 0, + rBegin; + for(int i=0; i<a.nslots; ++i) + a.clearSlot(i); + rEnd}, + {"load-blob:b", rProp(internal) rDoc("Load blob from middleware"), 0, + rBegin; + auto &b = **(rtosc::AutomationMgr **)rtosc_argument(msg, 0).b.data; + //XXX this code should likely be in rtosc + for(int i=0; i<a.nslots; ++i) { + auto &slota = a.slots[i]; + auto &slotb = b.slots[i]; + std::swap(slota.learning, slotb.learning); + std::swap(slota.midi_cc, slotb.midi_cc); + std::swap(slota.used, slotb.used); + std::swap(slota.active, slotb.active); + for(int j=0; j<a.per_slot; ++j) { + auto &aa = slota.automations[j]; + auto &ab = slotb.automations[j]; + std::swap(aa.used, ab.used); + std::swap(aa.active, ab.active); + std::swap(aa.param_path, ab.param_path); + std::swap(aa.param_min, ab.param_min); + std::swap(aa.param_max, ab.param_max); + std::swap(aa.param_step, ab.param_step); + std::swap(aa.param_type, ab.param_type); + std::swap(aa.map.offset, ab.map.offset); + std::swap(aa.map.gain, ab.map.gain); + std::swap(aa.map.upoints, ab.map.upoints); + for(int k=0; k<aa.map.npoints; ++k) + std::swap(aa.map.control_points[k], ab.map.control_points[k]); + } + } + rEnd}, }; #undef rBegin @@ -312,14 +362,15 @@ static const Ports master_ports = { rRecursp(part, 16, "Part"),//NUM_MIDI_PARTS rRecursp(sysefx, 4, "System Effect"),//NUM_SYS_EFX rRecursp(insefx, 8, "Insertion Effect"),//NUM_INS_EFX - rRecur(microtonal, "Micrtonal Mapping Functionality"), + rRecur(microtonal, "Microtonal Mapping Functionality"), rRecur(ctl, "Controller"), - rArrayI(Pinsparts, NUM_INS_EFX, rOpt(-2, Master), rOpt(-1, Off) - rOptions(Part1, Part2, Part3, Part4, Part5, Part6, - Part7, Part8, Part9, Part10, Part11, Part12, - Part13, Part14, Part15, Part16), - "Part to insert part onto"), - {"Pkeyshift::i", rShort("key shift") rProp(parameter) rLinear(0,127) rDoc("Global Key Shift"), 0, [](const char *m, RtData&d) { + rArrayOption(Pinsparts, NUM_INS_EFX, rOpt(-2, Master), rOpt(-1, Off), + rOptions(Part1, Part2, Part3, Part4, Part5, Part6, + Part7, Part8, Part9, Part10, Part11, Part12, + Part13, Part14, Part15, Part16) rDefault(Off), + "Part to insert part onto"), + {"Pkeyshift::i", rShort("key shift") rProp(parameter) rLinear(0,127) + rDefault(64) rDoc("Global Key Shift"), 0, [](const char *m, RtData&d) { if(rtosc_narguments(m)==0) { d.reply(d.loc, "i", ((Master*)d.obj)->Pkeyshift); } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') { @@ -368,14 +419,16 @@ static const Ports master_ports = { keys[i] = m->activeNotes[i] ? 'T' : 'F'; d.broadcast(d.loc, keys); rEnd}, - {"Pvolume::i", rShort("volume") rProp(parameter) rLinear(0,127) rDoc("Master Volume"), 0, + {"Pvolume::i", rShort("volume") rProp(parameter) rLinear(0,127) + rDefault(80) rDoc("Master Volume"), 0, [](const char *m, rtosc::RtData &d) { if(rtosc_narguments(m)==0) { d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') { ((Master*)d.obj)->setPvolume(limit<char>(rtosc_argument(m,0).i,0,127)); d.broadcast(d.loc, "i", ((Master*)d.obj)->Pvolume);}}}, - {"volume::i", rShort("volume") rProp(parameter) rLinear(0,127) rDoc("Master Volume"), 0, + {"volume::i", rShort("volume") rProp(parameter) rLinear(0,127) + rDefault(80) rDoc("Master Volume"), 0, [](const char *m, rtosc::RtData &d) { if(rtosc_narguments(m)==0) { d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); @@ -460,8 +513,8 @@ static const Ports master_ports = { [](const char *, rtosc::RtData &d) {d.reply("/undo_pause", "");}}, {"undo_resume:",rProp(internal) rDoc("resume undo event recording"),0, [](const char *, rtosc::RtData &d) {d.reply("/undo_resume", "");}}, - {"config/", rDoc("Top Level Application Configuration Parameters"), &Config::ports, - [](const char *, rtosc::RtData &d){d.forward();}}, + {"config/", rNoWalk rDoc("Top Level Application Configuration Parameters"), + &Config::ports, [](const char *, rtosc::RtData &d){d.forward();}}, {"presets/", rDoc("Parameter Presets"), &preset_ports, rBOIL_BEGIN SNIP preset_ports.dispatch(msg, data); @@ -566,6 +619,81 @@ vuData::vuData(void) rmspeakl(0.0f), rmspeakr(0.0f), clipped(0) {} +void Master::saveAutomation(XMLwrapper &xml, const rtosc::AutomationMgr &midi) +{ + xml.beginbranch("automation"); + { + XmlNode metadata("mgr-info"); + metadata["nslots"] = to_s(midi.nslots); + metadata["nautomations"] = to_s(midi.per_slot); + metadata["ncontrol"] = to_s(midi.slots[0].automations[0].map.npoints); + xml.add(metadata); + + for(int i=0; i<midi.nslots; ++i) { + const auto &slot = midi.slots[i]; + if(!slot.used) + continue; + xml.beginbranch("slot", i); + XmlNode params("params"); + params["midi-cc"] = to_s(slot.midi_cc); + xml.add(params); + for(int j=0; j<midi.per_slot; ++j) { + const auto &au = slot.automations[j]; + if(!au.used) + continue; + xml.beginbranch("automation", j); + XmlNode automation("params"); + automation["path"] = au.param_path; + XmlNode mapping("mapping"); + mapping["gain"] = to_s(au.map.gain); + mapping["offset"] = to_s(au.map.offset); + xml.add(automation); + xml.add(mapping); + xml.endbranch(); + } + + xml.endbranch(); + } + } + xml.endbranch(); +} + +void Master::loadAutomation(XMLwrapper &xml, rtosc::AutomationMgr &midi) +{ + if(xml.enterbranch("automation")) { + for(int i=0; i<midi.nslots; ++i) { + auto &slot = midi.slots[i]; + if(xml.enterbranch("slot", i)) { + for(int j=0; j<midi.per_slot; ++j) { + if(xml.enterbranch("automation", j)) { + float gain = 1.0; + float offset = 0.0; + std::string path = ""; + for(auto node:xml.getBranch()) { + if(node.name == "params") + path = node["path"]; + else if(node.name == "mapping") { + gain = atof(node["gain"].c_str()); + offset = atof(node["offset"].c_str()); + } + } + printf("createBinding(%d, %s, false)\n", i, path.c_str()); + midi.createBinding(i, path.c_str(), false); + midi.setSlotSubGain(i, j, gain); + midi.setSlotSubOffset(i, j, offset); + xml.exitbranch(); + } + } + for(auto node:xml.getBranch()) + if(node.name == "params") + slot.midi_cc = atoi(node["midi-cc"].c_str()); + xml.exitbranch(); + } + } + xml.exitbranch(); + } +} + Master::Master(const SYNTH_T &synth_, Config* config) :HDDRecorder(synth_), time(synth_), ctl(synth_, &time), microtonal(config->cfg.GzipCompression), bank(config), @@ -651,6 +779,7 @@ void Master::defaults() for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { part[npart]->defaults(); + part[npart]->partno = npart % NUM_MIDI_CHANNELS; part[npart]->Prcvchn = npart % NUM_MIDI_CHANNELS; } @@ -1289,6 +1418,8 @@ void Master::add2XML(XMLwrapper& xml) microtonal.add2XML(xml); xml.endbranch(); + saveAutomation(xml, automate); + for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { xml.beginbranch("PART", npart); part[npart]->add2XML(xml); @@ -1413,6 +1544,8 @@ void Master::getfromXML(XMLwrapper& xml) xml.exitbranch(); } + loadAutomation(xml, automate); + sysefx[0]->changeeffect(0); if(xml.enterbranch("SYSTEM_EFFECTS")) { for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) { @@ -1464,4 +1597,113 @@ void Master::getfromXML(XMLwrapper& xml) } } +static rtosc_version version_in_rtosc_fmt() +{ + return rtosc_version + { + (unsigned char) version.get_major(), + (unsigned char) version.get_minor(), + (unsigned char) version.get_revision() + }; +} + +char* Master::getXMLData() +{ + XMLwrapper xml; + + xml.beginbranch("MASTER"); + add2XML(xml); + xml.endbranch(); + + return xml.getXMLdata(); +} + +int Master::saveOSC(const char *filename) +{ + std::string savefile = rtosc::save_to_file(ports, this, + "ZynAddSubFX", + version_in_rtosc_fmt()); + + zyn::Config config; + zyn::SYNTH_T* synth = new zyn::SYNTH_T; + synth->buffersize = 256; + synth->samplerate = 48000; + synth->alias(); + + zyn::Master master2(*synth, &config); + int rval = master2.loadOSCFromStr(savefile.c_str()); + + + if(rval < 0) + { + std::cerr << "invalid savefile!" << std::endl; + std::cerr << "complete savefile:" << std::endl; + std::cerr << savefile << std::endl; + std::cerr << "first entry that could not be parsed:" << std::endl; + + for(int i = -rval + 1; savefile[i]; ++i) + if(savefile[i] == '\n') + { + savefile.resize(i); + break; + } + std::cerr << (savefile.c_str() - rval) << std::endl; + + rval = -1; + } + else + { + char* xml = getXMLData(), + * xml2 = master2.getXMLData(); + + rval = strcmp(xml, xml2) ? -1 : 0; + + if(rval == 0) + { + if(filename) + { + std::ofstream ofs(filename); + ofs << savefile; + } + else if(!filename) + std::cout << savefile << std::endl; + } + else + { + std::cout << savefile << std::endl; + std::cerr << "Can not write OSC savefile!! (see tmp1.txt and tmp2.txt)" + << std::endl; + std::ofstream tmp1("tmp1.txt"), tmp2("tmp2.txt"); + tmp1 << xml; + tmp2 << xml2; + } + + free(xml); + free(xml2); + } + return rval; +} + +int Master::loadOSCFromStr(const char *filename) +{ + return rtosc::load_from_file(filename, + ports, this, + "ZynAddSubFX", version_in_rtosc_fmt()); +} + +string loadfile(string fname) +{ + std::ifstream t(fname.c_str()); + std::string str((std::istreambuf_iterator<char>(t)), + std::istreambuf_iterator<char>()); + return str; +} + +int Master::loadOSC(const char *filename) +{ + int rval = loadOSCFromStr(loadfile(filename).c_str()); + return rval < 0 ? rval : 0; +} + + } diff --git a/src/Misc/Master.h b/src/Misc/Master.h @@ -59,12 +59,23 @@ class Master /**This adds the parameters to the XML data*/ void add2XML(XMLwrapper& xml); + static void saveAutomation(XMLwrapper &xml, const rtosc::AutomationMgr &midi); + static void loadAutomation(XMLwrapper &xml, rtosc::AutomationMgr &midi); + 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); + /**Save all settings to an OSC file (as specified by RT OSC) + * @param filename File to save to or NULL (useful for testing) + * @return 0 for ok or <0 if there is an error*/ + int saveOSC(const char *filename); + /**loads all settings from an OSC file (as specified by RT OSC) + * @return 0 for ok or <0 if there is an error*/ + int loadOSC(const char *filename); + /**Regenerate PADsynth and other non-RT parameters * It is NOT SAFE to call this from a RT context*/ void applyparameters(void) NONREALTIME; @@ -199,6 +210,11 @@ class Master //Callback When Master changes void(*mastercb)(void*,Master*); void* mastercb_ptr; + + //Return XML data as string. Must be freed. + char* getXMLData(); + //Used by loadOSC and saveOSC + int loadOSCFromStr(const char *filename); }; } diff --git a/src/Misc/Microtonal.cpp b/src/Misc/Microtonal.cpp @@ -42,25 +42,37 @@ namespace zyn { * A good lookup table should be a good finalization of this */ const rtosc::Ports Microtonal::ports = { - rToggle(Pinvertupdown, rShort("inv."), "key mapping inverse"), - rParamZyn(Pinvertupdowncenter, rShort("center"), "center of the inversion"), - rToggle(Penabled, rShort("enable"), "Enable for microtonal mode"), - rParamZyn(PAnote, rShort("1/1 midi note"), "The note for 'A'"), - rParamF(PAfreq, rShort("ref freq"), "Frequency of the 'A' note"), - rParamZyn(Pscaleshift, rShort("shift"), "UNDOCUMENTED"), - rParamZyn(Pfirstkey, rShort("first key"), "First key to retune"), - rParamZyn(Plastkey, rShort("last key"), "Last key to retune"), - rParamZyn(Pmiddlenote, rShort("middle"), "Scale degree 0 note"), + rToggle(Pinvertupdown, rShort("inv."), rDefault(false), + "key mapping inverse"), + rParamZyn(Pinvertupdowncenter, rShort("center"), rDefault(60), + "center of the inversion"), + rToggle(Penabled, rShort("enable"), rDefault(false), + "Enable for microtonal mode"), + rParamZyn(PAnote, rShort("1/1 midi note"), rDefault(69), + "The note for 'A'"), + rParamF(PAfreq, rShort("ref freq"), rDefault(440.0f), + "Frequency of the 'A' note"), + rParamZyn(Pscaleshift, rShort("shift"), rDefault(64), + "UNDOCUMENTED"), + rParamZyn(Pfirstkey, rShort("first key"), rDefault(0), + "First key to retune"), + rParamZyn(Plastkey, rShort("last key"), rDefault(127), + "Last key to retune"), + rParamZyn(Pmiddlenote, rShort("middle"), rDefault(60), + "Scale degree 0 note"), //TODO check to see if this should be exposed - rParamZyn(Pmapsize, "Size of key map"), - rToggle(Pmappingenabled, "Mapping Enable"), + rParamZyn(Pmapsize, rDefault(12), "Size of key map"), + rToggle(Pmappingenabled, rDefault(false), "Mapping Enable"), - rParams(Pmapping, 128, "Mapping of keys"), - rParamZyn(Pglobalfinedetune, rShort("fine"), "Fine detune for all notes"), + rParams(Pmapping, 128, rDefaultMissing, "Mapping of keys"), + rParamZyn(Pglobalfinedetune, rShort("fine"), rDefault(64), + "Fine detune for all notes"), - rString(Pname, MICROTONAL_MAX_NAME_LEN, rShort("name"), "Microtonal Name"), - rString(Pcomment, MICROTONAL_MAX_NAME_LEN, rShort("comment"), "Microtonal comments"), + rString(Pname, MICROTONAL_MAX_NAME_LEN, rShort("name"), + rDefault("12tET"), "Microtonal Name"), + rString(Pcomment, MICROTONAL_MAX_NAME_LEN, rShort("comment"), + rDefault("Equal Temperament 12 notes per octave"), "Microtonal comments"), {"octavesize:", rDoc("Get octave size"), 0, [](const char*, RtData &d) { @@ -858,6 +870,7 @@ void Microtonal::apply(void) strncat(buf, tmpbuf, sizeof(buf)-1); } int err = texttotunings(buf); + (void) err; } } diff --git a/src/Misc/MiddleWare.cpp b/src/Misc/MiddleWare.cpp @@ -228,55 +228,6 @@ void preparePadSynth(string path, PADnoteParameters *p, rtosc::RtData &d) } /****************************************************************************** - * MIDI Serialization * - * * - ******************************************************************************/ -//void saveMidiLearn(XMLwrapper &xml, const rtosc::MidiMappernRT &midi) -//{ -// xml.beginbranch("midi-learn"); -// for(auto value:midi.inv_map) { -// XmlNode binding("midi-binding"); -// auto biject = std::get<3>(value.second); -// binding["osc-path"] = value.first; -// binding["coarse-CC"] = to_s(std::get<1>(value.second)); -// binding["fine-CC"] = to_s(std::get<2>(value.second)); -// binding["type"] = "i"; -// binding["minimum"] = to_s(biject.min); -// binding["maximum"] = to_s(biject.max); -// xml.add(binding); -// } -// xml.endbranch(); -//} -// -//void loadMidiLearn(XMLwrapper &xml, rtosc::MidiMappernRT &midi) -//{ -// using rtosc::Port; -// if(xml.enterbranch("midi-learn")) { -// auto nodes = xml.getBranch(); -// -// //TODO clear mapper -// -// for(auto node:nodes) { -// if(node.name != "midi-binding" || -// !node.has("osc-path") || -// !node.has("coarse-CC")) -// continue; -// const string path = node["osc-path"]; -// const int CC = atoi(node["coarse-CC"].c_str()); -// const Port *p = Master::ports.apropos(path.c_str()); -// if(p) { -// printf("loading midi port...\n"); -// midi.addNewMapper(CC, *p, path); -// } else { -// printf("unknown midi bindable <%s>\n", path.c_str()); -// } -// } -// xml.exitbranch(); -// } else -// printf("cannot find 'midi-learn' branch...\n"); -//} - -/****************************************************************************** * Non-RealTime Object Store * * * * * @@ -803,7 +754,7 @@ class MwDataObj:public rtosc::RtData //Chain calls repeat the call into handle() //Forward calls send the message directly to the realtime - virtual void reply(const char *path, const char *args, ...) + virtual void reply(const char *path, const char *args, ...) override { //printf("reply building '%s'\n", path); va_list va; @@ -829,7 +780,7 @@ class MwDataObj:public rtosc::RtData reply(buffer); } } - virtual void reply(const char *msg){ + virtual void reply(const char *msg) override{ mwi->sendToCurrentRemote(msg); }; //virtual void broadcast(const char *path, const char *args, ...){(void)path;(void)args;}; @@ -891,9 +842,9 @@ static std::vector<std::string> getFiles(const char *folder, bool finddir) } #else std::string darn_windows = folder + std::string("/") + std::string(fn->d_name); - printf("attr on <%s> => %x\n", darn_windows.c_str(), GetFileAttributes(darn_windows.c_str())); - printf("desired mask = %x\n", mask); - printf("error = %x\n", INVALID_FILE_ATTRIBUTES); + //printf("attr on <%s> => %x\n", darn_windows.c_str(), GetFileAttributes(darn_windows.c_str())); + //printf("desired mask = %x\n", mask); + //printf("error = %x\n", INVALID_FILE_ATTRIBUTES); bool is_dir = GetFileAttributes(darn_windows.c_str()) & FILE_ATTRIBUTE_DIRECTORY; #endif if(finddir == is_dir && strcmp(".", fn->d_name)) @@ -942,13 +893,28 @@ extern const rtosc::Ports bankPorts; const rtosc::Ports bankPorts = { {"rescan:", 0, 0, rBegin; + impl.bankpos = 0; impl.rescanforbanks(); //Send updated banks int i = 0; for(auto &elm : impl.banks) d.reply("/bank/bank_select", "iss", i++, elm.name.c_str(), elm.dir.c_str()); d.reply("/bank/bank_select", "i", impl.bankpos); - + if (i > 0) { + impl.loadbank(impl.banks[0].dir); + + //Reload bank slots + for(int i=0; i<BANK_SIZE; ++i) { + d.reply("/bankview", "iss", + i, impl.ins[i].name.c_str(), + impl.ins[i].filename.c_str()); + } + } else { + //Clear all bank slots + for(int i=0; i<BANK_SIZE; ++i) { + d.reply("/bankview", "iss", i, "", ""); + } + } rEnd}, {"bank_list:", 0, 0, rBegin; @@ -1096,8 +1062,8 @@ const rtosc::Ports bankPorts = { rBegin; auto res = impl.search(rtosc_argument(msg, 0).s); #define MAX_SEARCH 300 - char res_type[MAX_SEARCH+1] = {0}; - rtosc_arg_t res_dat[MAX_SEARCH] = {0}; + char res_type[MAX_SEARCH+1] = {}; + rtosc_arg_t res_dat[MAX_SEARCH] = {}; for(unsigned i=0; i<res.size() && i<MAX_SEARCH; ++i) { res_type[i] = 's'; res_dat[i].s = res[i].c_str(); @@ -1109,8 +1075,8 @@ const rtosc::Ports bankPorts = { rBegin; auto res = impl.blist(rtosc_argument(msg, 0).s); #define MAX_SEARCH 300 - char res_type[MAX_SEARCH+1] = {0}; - rtosc_arg_t res_dat[MAX_SEARCH] = {0}; + char res_type[MAX_SEARCH+1] = {}; + rtosc_arg_t res_dat[MAX_SEARCH] = {}; for(unsigned i=0; i<res.size() && i<MAX_SEARCH; ++i) { res_type[i] = 's'; res_dat[i].s = res[i].c_str(); @@ -1206,24 +1172,29 @@ static rtosc::Ports middwareSnoopPorts = { impl.kitEnable(msg); d.forward(); rEnd}, - //{"save_xlz:s", 0, 0, - // rBegin; - // const char *file = rtosc_argument(msg, 0).s; - // XMLwrapper xml; - // saveMidiLearn(xml, impl.midi_mapper); - // xml.saveXMLfile(file, impl.master->gzip_compression); - // rEnd}, - //{"load_xlz:s", 0, 0, - // rBegin; - // const char *file = rtosc_argument(msg, 0).s; - // XMLwrapper xml; - // xml.loadXMLfile(file); - // loadMidiLearn(xml, impl.midi_mapper); - // rEnd}, - //{"clear_xlz:", 0, 0, - // rBegin; - // impl.midi_mapper.clear(); - // rEnd}, + {"save_xlz:s", 0, 0, + rBegin; + impl.doReadOnlyOp([&]() { + const char *file = rtosc_argument(msg, 0).s; + XMLwrapper xml; + Master::saveAutomation(xml, impl.master->automate); + xml.saveXMLfile(file, impl.master->gzip_compression); + }); + rEnd}, + {"load_xlz:s", 0, 0, + rBegin; + const char *file = rtosc_argument(msg, 0).s; + XMLwrapper xml; + xml.loadXMLfile(file); + rtosc::AutomationMgr *mgr = new rtosc::AutomationMgr(16,4,8); + mgr->set_ports(Master::ports); + Master::loadAutomation(xml, *mgr); + d.chain("/automate/load-blob", "b", sizeof(void*), &mgr); + rEnd}, + {"clear_xlz:", 0, 0, + rBegin; + d.chain("/automate/clear", ""); + rEnd}, //scale file stuff {"load_xsz:s", 0, 0, rBegin; @@ -1402,7 +1373,7 @@ static rtosc::Ports middwareSnoopPorts = { // //cc-id, path, min, max //#define MAX_MIDI 32 // rtosc_arg_t args[MAX_MIDI*4]; - // char argt[MAX_MIDI*4+1] = {0}; + // char argt[MAX_MIDI*4+1] = {}; // int j=0; // for(unsigned i=0; i<key.size() && i<MAX_MIDI; ++i) { // auto val = midi.inv_map[key[i]]; @@ -1486,10 +1457,6 @@ static rtosc::Ports middlewareReplyPorts = { if(impl.recording_undo) impl.undo.recordEvent(msg); rEnd}, - //{"midi-use-CC:i", 0, 0, - // rBegin; - // impl.midi_mapper.useFreeID(rtosc_argument(msg, 0).i); - // rEnd}, {"broadcast:", 0, 0, rBegin; impl.broadcast = true; rEnd}, {"forward:", 0, 0, rBegin; impl.forward = true; rEnd}, }; @@ -1613,7 +1580,7 @@ void MiddleWareImpl::doReadOnlyOp(std::function<void()> read_only_fn) int tries = 0; while(tries++ < 10000) { if(!bToU->hasNext()) { - usleep(500); + os_usleep(500); continue; } const char *msg = bToU->read(); @@ -1726,7 +1693,7 @@ bool MiddleWareImpl::doReadOnlyOpNormal(std::function<void()> read_only_fn, bool int tries = 0; while(tries++ < 2000) { if(!bToU->hasNext()) { - usleep(500); + os_usleep(500); continue; } const char *msg = bToU->read(); diff --git a/src/Misc/Part.cpp b/src/Misc/Part.cpp @@ -47,41 +47,55 @@ static const Ports partPorts = { rRecurs(kit, 16, "Kit"),//NUM_KIT_ITEMS rRecursp(partefx, 3, "Part Effect"), rRecur(ctl, "Controller"), - rToggle(Penabled, rShort("enable"), "Part enable"), + rParamZyn(partno, rProp(internal), + "How many parts are before this in the Master"), +#undef rChangeCb +#define rChangeCb if(obj->Penabled == false) obj->AllNotesOff(); + rToggle(Penabled, rShort("enable"), rDefaultDepends(partno), + rPresets(true), rDefault(false), "Part enable"), +#undef rChangeCb +#define rChangeCb #undef rChangeCb #define rChangeCb obj->setPvolume(obj->Pvolume); - rParamZyn(Pvolume, rShort("Vol"), "Part Volume"), + rParamZyn(Pvolume, rShort("Vol"), rDefault(96),"Part Volume"), #undef rChangeCb #define rChangeCb obj->setPpanning(obj->Ppanning); - rParamZyn(Ppanning, rShort("pan"), "Set Panning"), + rParamZyn(Ppanning, rShort("pan"), rDefault(64), "Set Panning"), #undef rChangeCb #define rChangeCb obj->setkeylimit(obj->Pkeylimit); - rParamI(Pkeylimit, rShort("limit"), rProp(parameter), rMap(min,0), rMap(max, POLYPHONY), "Key limit per part"), + rParamI(Pkeylimit, rShort("limit"), rProp(parameter), + rMap(min,0), rMap(max, POLYPHONY), rDefault(15), "Key limit per part"), #undef rChangeCb #define rChangeCb - rParamZyn(Pminkey, rShort("min"), "Min Used Key"), - rParamZyn(Pmaxkey, rShort("max"), "Max Used Key"), - rParamZyn(Pkeyshift, rShort("shift"), "Part keyshift"), + rParamZyn(Pminkey, rShort("min"), rDefault(0), "Min Used Key"), + rParamZyn(Pmaxkey, rShort("max"), rDefault(127), "Max Used Key"), + rParamZyn(Pkeyshift, rShort("shift"), rDefault(64), "Part keyshift"), rParamZyn(Prcvchn, rOptions(ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8, ch9, ch10, ch11, ch12, ch13, ch14, ch15, ch16), - "Active MIDI channel"), - rParamZyn(Pvelsns, rShort("sense"), "Velocity sensing"), - rParamZyn(Pveloffs, rShort("offset"), "Velocity offset"), - rToggle(Pnoteon, "If the channel accepts note on events"), - rOption(Pkitmode, rOptions(Off, Multi-Kit, Single-Kit), "Kit mode/enable\n" + rPresets(ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8, ch9, ch10, ch11, ch12, ch13, ch14, ch15, ch16), + "Active MIDI channel"), + rParamZyn(Pvelsns, rShort("sense"), rDefault(64), "Velocity sensing"), + rParamZyn(Pveloffs, rShort("offset"), rDefault(64),"Velocity offset"), + rToggle(Pnoteon, rDefault(true), "If the channel accepts note on events"), + rOption(Pkitmode, rOptions(Off, Multi-Kit, Single-Kit), rDefault(Off), + "Kit mode/enable\n" "Off - Only the first kit is ever utilized\n" "Multi-kit - Every applicable kit is run for a note\n" "Single-kit - The first applicable kit is run for a given note"), - rToggle(Pdrummode, "Drum mode enable\n" + rToggle(Pdrummode, rDefault(false), "Drum mode enable\n" "When drum mode is enabled all keys are mapped to 12tET and legato is disabled"), - rToggle(Ppolymode, "Polyphony mode"), - rToggle(Plegatomode, "Legato mode"), - rParamZyn(info.Ptype, "Class of Instrument"), - rString(info.Pauthor, MAX_INFO_TEXT_SIZE, "Instrument author"), - rString(info.Pcomments, MAX_INFO_TEXT_SIZE, "Instrument comments"), - rString(Pname, PART_MAX_NAME_LEN, "User specified label"), + rToggle(Ppolymode, rDefault(true), "Polyphony mode"), + rToggle(Plegatomode, rDefault(false), "Legato mode"), + rParamZyn(info.Ptype, rDefault(0), "Class of Instrument"), + rString(info.Pauthor, MAX_INFO_TEXT_SIZE, rDefault(""), + "Instrument author"), + rString(info.Pcomments, MAX_INFO_TEXT_SIZE, rDefault(""), + "Instrument comments"), + rString(Pname, PART_MAX_NAME_LEN, rDefault(""), "User specified label"), rArrayI(Pefxroute, NUM_PART_EFX, - rOptions(Next Effect,Part Out,Dry Out), "Effect Routing"), - rArrayT(Pefxbypass, NUM_PART_EFX, "If an effect is bypassed"), + rOptions(Next Effect,Part Out,Dry Out), rDefaultId(Next Effect), + "Effect Routing"), + rArrayT(Pefxbypass, NUM_PART_EFX, rDefault(false), + "If an effect is bypassed"), {"captureMin:", rDoc("Capture minimum valid note"), NULL, [](const char *, RtData &r) {Part *p = (Part*)r.obj; p->Pminkey = p->lastnote;}}, @@ -158,20 +172,26 @@ static const Ports partPorts = { #undef rObject #define rObject Part::Kit static const Ports kitPorts = { + rSelf(Part::Kit, rEnabledBy(Penabled)), rRecurp(padpars, "Padnote parameters"), rRecurp(adpars, "Adnote parameters"), rRecurp(subpars, "Adnote parameters"), - rToggle(Penabled, "Kit item enable"), - rToggle(Pmuted, "Kit item mute"), - rParamZyn(Pminkey, "Kit item min key"), - rParamZyn(Pmaxkey, "Kit item max key"), - rToggle(Padenabled, "ADsynth enable"), - rToggle(Psubenabled, "SUBsynth enable"), - rToggle(Ppadenabled, "PADsynth enable"), + rToggle(firstkit, rProp(internal), "If this is the part's first kit"), + rToggle(Penabled, rDefaultDepends(firstkit), + rPreset(true, true), rPreset(false, false), + "Kit item enable"), + rToggle(Pmuted, rDefault(false), "Kit item mute"), + rParamZyn(Pminkey, rDefault(0), "Kit item min key"), + rParamZyn(Pmaxkey, rDefault(127) "Kit item max key"), + rToggle(Padenabled, rDefaultDepends(firstkit), + rPreset(true, true), rPreset(false, false) + "ADsynth enable"), + rToggle(Psubenabled, rDefault(false), "SUBsynth enable"), + rToggle(Ppadenabled, rDefault(false), "PADsynth enable"), rParamZyn(Psendtoparteffect, - rOptions(FX1, FX2, FX3, Off), + rOptions(FX1, FX2, FX3, Off), rDefault(FX1), "Effect Levels"), - rString(Pname, PART_MAX_NAME_LEN, "Kit User Specified Label"), + rString(Pname, PART_MAX_NAME_LEN, rDefault(""), "Kit User Specified Label"), {"captureMin:", rDoc("Capture minimum valid note"), NULL, [](const char *, RtData &r) {Part::Kit *p = (Part::Kit*)r.obj; p->Pminkey = p->parent->lastnote;}}, @@ -328,6 +348,7 @@ void Part::defaultsinstrument() for(int n = 0; n < NUM_KIT_ITEMS; ++n) { //kit[n].Penabled = false; + kit[n].firstkit = false; kit[n].Pmuted = false; kit[n].Pminkey = 0; kit[n].Pmaxkey = 127; @@ -339,6 +360,7 @@ void Part::defaultsinstrument() if(n != 0) setkititemstatus(n, 0); } + kit[0].firstkit = true; kit[0].Penabled = 1; kit[0].Padenabled = 1; kit[0].adpars->defaults(); diff --git a/src/Misc/Part.h b/src/Misc/Part.h @@ -85,6 +85,7 @@ class Part struct Kit { Kit(void); Part *parent; + bool firstkit; bool Penabled, Pmuted; unsigned char Pminkey, Pmaxkey; char *Pname; @@ -106,13 +107,14 @@ class Part void setkeylimit(unsigned char Pkeylimit); void setkititemstatus(unsigned kititem, bool Penabled_); + unsigned char partno; /**<if it's the Master's first part*/ bool Penabled; /**<if the part is enabled*/ unsigned char Pvolume; /**<part volume*/ unsigned char Pminkey; /**<the minimum key that the part receives noteon messages*/ unsigned char Pmaxkey; //the maximum key that the part receives noteon messages void setPvolume(char Pvolume); unsigned char Pkeyshift; //Part keyshift - unsigned char Prcvchn; //from what midi channel it receive commnads + unsigned char Prcvchn; //from what midi channel it receives commands unsigned char Ppanning; //part panning void setPpanning(char Ppanning); unsigned char Pvelsns; //velocity sensing (amplitude velocity scale) diff --git a/src/Misc/Schema.cpp b/src/Misc/Schema.cpp @@ -11,6 +11,30 @@ void walk_ports2(const rtosc::Ports *base, namespace zyn { +static const char *escape_string(const char *msg) +{ + if(!msg) + return NULL; + char *out = (char*)malloc(strlen(msg)*2+1); + memset(out, 0, strlen(msg)*2+1); + char *itr = out; + while(*msg) { + if(*msg == '"') { + *itr++ = '\\'; + *itr++ = '\"'; + } else if(*msg == '\\') { + *itr++ = '\\'; + *itr++ = '\\'; + } else { + *itr++ = *msg; + } + + msg++; + + } + return out; +} + /* * root : * - 'parameters' : [parameter...] @@ -25,6 +49,8 @@ namespace zyn { * - 'scale' : scale-type * - 'domain' : range [OPTIONAL] * - 'options' : [option...] [OPTIONAL] + * - 'default' : string + * - 'defaults' : defaults * type : {'int', 'float', 'boolean'} * action : * - 'path' : path-id @@ -35,10 +61,14 @@ namespace zyn { * option : * - 'id' : id-number * - 'value' : string-rep + * defaults : + * - 'id' : id-number + * - 'value' : string-rep */ using std::ostream; using std::string; +#if 0 static int enum_min(Port::MetaContainer meta) { int min = 0; @@ -96,6 +126,7 @@ static ostream &add_options(ostream &o, Port::MetaContainer meta) return o; } +#endif /* * parameter : @@ -107,7 +138,8 @@ static ostream &add_options(ostream &o, Port::MetaContainer meta) * - 'domain' : range [OPTIONAL] */ static bool first = true; -void dump_param_cb(const rtosc::Port *p, const char *full_name, void *v) +void dump_param_cb(const rtosc::Port *p, const char *full_name, const char*, + const Ports&,void *v, void*) { typedef std::vector<std::pair<int,string>> opts; std::ostream &o = *(std::ostream*)v; @@ -174,6 +206,8 @@ void dump_param_cb(const rtosc::Port *p, const char *full_name, void *v) const char *min = meta["min"]; const char *max = meta["max"]; + const char *def = meta["default"]; + def = escape_string(def); for(auto m:meta) { if(strlen(m.title) >= 5 && !memcmp(m.title, "map ", 4)) { @@ -201,6 +235,8 @@ void dump_param_cb(const rtosc::Port *p, const char *full_name, void *v) o << " \"type\" : \"" << type << "\""; if(min && max) o << ",\n \"range\" : [" << min << "," << max << "]"; + if(def) + o << ",\n \"default\" : \"" << def << "\"\n"; if(!options.empty()) { o << ",\n \"options\" : [\n"; int N = options.size(); diff --git a/src/Misc/Util.cpp b/src/Misc/Util.cpp @@ -129,10 +129,31 @@ void set_realtime() #endif } -void os_sleep(long length) + + +#ifdef WIN32 +#include <windows.h> + +//https://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw +void os_usleep(long usec) +{ + HANDLE timer; + LARGE_INTEGER ft; + + ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time + + timer = CreateWaitableTimer(NULL, TRUE, NULL); + SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); +} +#else + +void os_usleep(long length) { usleep(length); } +#endif //!< maximum lenght a pid has on any POSIX system //!< this is an estimation, but more than 12 looks insane diff --git a/src/Misc/Util.h b/src/Misc/Util.h @@ -46,7 +46,7 @@ extern float getdetune(unsigned char type, void set_realtime(); /**Os independent sleep in microsecond*/ -void os_sleep(long length); +void os_usleep(long length); //! returns pid padded to maximum pid lenght, posix conform std::string os_pid_as_padded_string(); @@ -153,11 +153,6 @@ char *rtosc_splat(const char *path, std::set<std::string>); #define rParamZyn(name, ...) \ {STRINGIFY(name) "::i", rProp(parameter) rMap(min, 0) rMap(max, 127) DOC(__VA_ARGS__), NULL, rParamICb(name)} -#define rSelf(type) \ -{"self:", rProp(internal) rMap(class, type) rDoc("port metadata"), 0, \ - [](const char *, rtosc::RtData &d){ \ - d.reply(d.loc, "b", sizeof(d.obj), &d.obj);}}\ - #define rPresetType \ {"preset-type:", rProp(internal) rDoc("clipboard type of object"), 0, \ [](const char *, rtosc::RtData &d){ \ diff --git a/src/Misc/XMLwrapper.cpp b/src/Misc/XMLwrapper.cpp @@ -253,7 +253,7 @@ void XMLwrapper::addparreal(const string &name, float val) union { float in; uint32_t out; } convert; char buf[11]; convert.in = val; - sprintf(buf, "0x%0.8X", convert.out); + sprintf(buf, "0x%.8X", convert.out); addparams("par_real", 3, "name", name.c_str(), "value", stringFrom<float>(val).c_str(), "exact_value", buf); } @@ -397,6 +397,12 @@ bool XMLwrapper::putXMLdata(const char *xmldata) if(root == NULL) return false; + //fetch version information + _fileversion.set_major(stringTo<int>(mxmlElementGetAttr(root, "version-major"))); + _fileversion.set_minor(stringTo<int>(mxmlElementGetAttr(root, "version-minor"))); + _fileversion.set_revision( + stringTo<int>(mxmlElementGetAttr(root, "version-revision"))); + return true; } diff --git a/src/Nio/NulEngine.cpp b/src/Nio/NulEngine.cpp @@ -13,8 +13,8 @@ #include "NulEngine.h" #include "../globals.h" +#include "../Misc/Util.h" -#include <unistd.h> #include <iostream> using namespace std; @@ -50,7 +50,7 @@ void *NulEngine::AudioThread() + (playing_until.tv_sec - now.tv_sec) * 1000000; if(remaining > 10000) //Don't sleep() less than 10ms. //This will add latency... - usleep(remaining - 10000); + os_usleep(remaining - 10000); if(remaining < 0) cerr << "WARNING - too late" << endl; } diff --git a/src/Output/DSSIaudiooutput.cpp b/src/Output/DSSIaudiooutput.cpp @@ -625,8 +625,6 @@ DSSIaudiooutput::DSSIaudiooutput(unsigned long sampleRate) : dssi_control{dssi_c this->sampleRate = sampleRate; this->banksInited = false; - config.init(); - zyn::sprng(time(NULL)); synth.alias(); diff --git a/src/Params/ADnoteParameters.cpp b/src/Params/ADnoteParameters.cpp @@ -70,19 +70,26 @@ static const Ports voicePorts = { rRecurp(FMAmpEnvelope, "Modulator Amplitude Envelope"), rRecurp(VoiceFilter, "Optional Voice Filter"), - rToggle(Enabled, rShort("enable"), "Voice Enable"), - rParamI(Unison_size, rShort("size"), rMap(min, 0), rMap(max, 50), "Number of subvoices"), - rParamZyn(Unison_phase_randomness, rShort("ph.rnd."), "Phase Randomness"), - rParamZyn(Unison_frequency_spread, rShort("detune"), "Subvoice detune"), - rParamZyn(Unison_stereo_spread, rShort("spread"), - "Subvoice L/R Separation"), - rParamZyn(Unison_vibratto, rShort("vib."), "Subvoice vibratto"), - rParamZyn(Unison_vibratto_speed, rShort("speed"), - "Subvoice vibratto speed"), +// rToggle(Enabled, rShort("enable"), "Voice Enable"), + rParamI(Unison_size, rShort("size"), rMap(min, 0), rMap(max, 50), + rDefault(1), "Number of subvoices"), + rParamZyn(Unison_phase_randomness, rShort("ph.rnd."), rDefault(127), + "Phase Randomness"), + rParamZyn(Unison_frequency_spread, rShort("detune"), rDefault(60), + "Subvoice detune"), + rParamZyn(Unison_stereo_spread, rShort("spread"), rDefault(64), + "Subvoice L/R Separation"), + rParamZyn(Unison_vibratto, rShort("vib."), rDefault(64), + "Subvoice vibratto"), + rParamZyn(Unison_vibratto_speed, rShort("speed"), rDefault(64), + "Subvoice vibratto speed"), rOption(Unison_invert_phase, rShort("inv."), - rOptions(none, random, 50%, 33%, 25%), "Subvoice Phases"), - rOption(Type, rShort("type"), rOptions(Sound,White,Pink,DC), "Type of Sound"), - rParamZyn(PDelay, rShort("delay"), "Voice Startup Delay"), + rOptions(none, random, 50%, 33%, 25%), rDefault(none), + "Subvoice Phases"), + rOption(Type, rShort("type"), rOptions(Sound,White,Pink,DC), + rDefault(Sound), "Type of Sound"), + rParamZyn(PDelay, rShort("delay"), rDefault(0), + "Voice Startup Delay"), rToggle(Presonance, rShort("enable"), "Resonance Enable"), rParamI(Pextoscil, rShort("ext."), rMap(min, -1), rMap(max, 16), "External Oscillator Selection"), @@ -109,37 +116,56 @@ static const Ports voicePorts = { //Amplitude Stuff rParamZyn(PPanning, rShort("pan."), "Panning"), - rParamZyn(PVolume, rShort("vol."), "Volume"), - rToggle(PVolumeminus, rShort("inv."), "Signal Inverter"), //do we really need this?? - rParamZyn(PAmpVelocityScaleFunction, rShort("sense"), "Velocity Sensing"), - rToggle(PAmpEnvelopeEnabled, rShort("enable"), "Amplitude Envelope Enable"), - rToggle(PAmpLfoEnabled, rShort("enable"), "Amplitude LFO Enable"), + rParamZyn(PVolume, rShort("vol."), rDefault(100), + "Volume"), + rToggle(PVolumeminus, rShort("inv."), rDefault(false), + "Signal Inverter"), //do we really need this?? + rParamZyn(PAmpVelocityScaleFunction, rShort("sense"), rDefault(127), + "Velocity Sensing"), + rToggle(PAmpEnvelopeEnabled, rShort("enable"), rDefault(false), + "Amplitude Envelope Enable"), + rToggle(PAmpLfoEnabled, rShort("enable"), rDefault(false), + "Amplitude LFO Enable"), //Filter Stuff - rToggle(PFilterEnabled, rShort("enable"), "Filter Enable"), - rToggle(PFilterEnvelopeEnabled, rShort("enable"), "Filter Envelope Enable"), - rToggle(PFilterLfoEnabled, rShort("enable"), "Filter LFO Enable"), - rParamZyn(PFilterVelocityScale, rShort("v.scale"), "Filter Velocity Magnitude"), - rParamZyn(PFilterVelocityScaleFunction, rShort("v.sense"), "Filter Velocity Function Shape"), + rToggle(PFilterEnabled, rShort("enable"), rDefault(false), + "Filter Enable"), + rToggle(PFilterEnvelopeEnabled, rShort("enable"), rDefault(false), + "Filter Envelope Enable"), + rToggle(PFilterLfoEnabled, rShort("enable"), rDefault(false), + "Filter LFO Enable"), + rParamZyn(PFilterVelocityScale, rShort("v.scale"), rDefault(0), + "Filter Velocity Magnitude"), + rParamZyn(PFilterVelocityScaleFunction, rShort("v.sense"), rDefault(64), + "Filter Velocity Function Shape"), //Modulator Stuff rOption(PFMEnabled, rShort("mode"), rOptions(none, morph, ring, phase, - frequency, pulse), "Modulator mode"), - rParamI(PFMVoice, rShort("voice"), "Modulator Oscillator Selection"), - rParamZyn(PFMVolume, rShort("vol."), "Modulator Magnitude"), - rParamZyn(PFMVolumeDamp, rShort("damp."), "Modulator HF dampening"), - rParamZyn(PFMVelocityScaleFunction, rShort("sense"), "Modulator Velocity Function"), + frequency, pulse), rDefault(none), "Modulator mode"), + rParamI(PFMVoice, rShort("voice"), rDefault(-1), + "Modulator Oscillator Selection"), + rParamZyn(PFMVolume, rShort("vol."), rDefault(90), + "Modulator Magnitude"), + rParamZyn(PFMVolumeDamp, rShort("damp."), rDefault(64), + "Modulator HF dampening"), + rParamZyn(PFMVelocityScaleFunction, rShort("sense"), rDefault(64), + "Modulator Velocity Function"), //nominally -8192..8191 rParamI(PFMDetune, rShort("fine"), - rLinear(0, 16383), "Modulator Fine Detune"), - rParamI(PFMCoarseDetune, rShort("coarse"), "Modulator Coarse Detune"), + rLinear(0, 16383), rDefault(8192), "Modulator Fine Detune"), + rParamI(PFMCoarseDetune, rShort("coarse"), rDefault(0), + "Modulator Coarse Detune"), rParamZyn(PFMDetuneType, rShort("type"), - rOptions(L35cents, L10cents, E100cents, E1200cents), - "Modulator Detune Magnitude"), - rToggle(PFMFixedFreq, rShort("fixed"), "Modulator Frequency Fixed"), - rToggle(PFMFreqEnvelopeEnabled, rShort("enable"), "Modulator Frequency Envelope"), - rToggle(PFMAmpEnvelopeEnabled, rShort("enable"), "Modulator Amplitude Envelope"), + rOptions(L35cents, L10cents, E100cents, E1200cents), + rDefault(L35cents), + "Modulator Detune Magnitude"), + rToggle(PFMFixedFreq, rShort("fixed"), rDefault(false), + "Modulator Frequency Fixed"), + rToggle(PFMFreqEnvelopeEnabled, rShort("enable"), rDefault(false), + "Modulator Frequency Envelope"), + rToggle(PFMAmpEnvelopeEnabled, rShort("enable"), rDefault(false), + "Modulator Amplitude Envelope"), //weird stuff for PCoarseDetune @@ -250,36 +276,48 @@ static const Ports globalPorts = { rRecurp(FilterEnvelope, "Frequency Envelope"), rRecurp(GlobalFilter, "Filter"), - rToggle(PStereo, rShort("stereo"), "Mono/Stereo Enable"), + rToggle(PStereo, rShort("stereo"), rDefault(true), "Mono/Stereo Enable"), //Frequency //nominally -8192..8191 rParamI(PDetune, rShort("fine"), - rLinear(0, 16383), "Fine Detune"), - rParamI(PCoarseDetune, rShort("coarse"), "Coarse Detune"), + rLinear(0, 16383), rDefault(8192), "Fine Detune"), + rParamI(PCoarseDetune, rShort("coarse"), rDefault(0), "Coarse Detune"), rParamZyn(PDetuneType, rShort("type"), - rOptions(L35cents, L10cents, E100cents, E1200cents), - "Detune Scaling Type"), - rParamZyn(PBandwidth, rShort("bw."), "Relative Fine Detune Gain"), + rOptions(L35cents, L10cents, E100cents, E1200cents), + rDefault(L10cents), + "Detune Scaling Type"), + rParamZyn(PBandwidth, rShort("bw."), rDefault(64), + "Relative Fine Detune Gain"), //Amplitude - rParamZyn(PPanning, rShort("pan"), "Panning of ADsynth (0 random, 1 left, 127 right)"), - rParamZyn(PVolume, rShort("vol"), "volume control"), - rParamZyn(PAmpVelocityScaleFunction, rShort("scale"), "Volume Velocity Control"), - - rParamZyn(Fadein_adjustment, "Adjustment for anti-pop strategy."), - rParamZyn(PPunchStrength, rShort("strength"), "Punch Strength"), - rParamZyn(PPunchTime, rShort("time"), "Length of Punch"), - rParamZyn(PPunchStretch, rShort("stretch"), "How Punch changes with note frequency"), - rParamZyn(PPunchVelocitySensing, rShort("v.sns"), "Punch Velocity control"), + rParamZyn(PPanning, rShort("pan"), rDefault(64), + "Panning of ADsynth (0 random, 1 left, 127 right)"), + rParamZyn(PVolume, rShort("vol"), rDefault(90), "volume control"), + rParamZyn(PAmpVelocityScaleFunction, rShort("scale"), rDefault(64), + "Volume Velocity Control"), + + rParamZyn(Fadein_adjustment, rDefault(FADEIN_ADJUSTMENT_SCALE), + "Adjustment for anti-pop strategy."), + rParamZyn(PPunchStrength, rShort("strength"), rDefault(0), + "Punch Strength"), + rParamZyn(PPunchTime, rShort("time"), rDefault(60), + "Length of Punch"), + rParamZyn(PPunchStretch, rShort("stretch"), rDefault(64), + "How Punch changes with note frequency"), + rParamZyn(PPunchVelocitySensing, rShort("v.sns"), rDefault(72), + "Punch Velocity control"), //Filter - rParamZyn(PFilterVelocityScale, rShort("scale"), "Filter Velocity Magnitude"), - rParamZyn(PFilterVelocityScaleFunction, rShort("sense"), "Filter Velocity Function Shape"), + rParamZyn(PFilterVelocityScale, rShort("scale"), rDefault(64), + "Filter Velocity Magnitude"), + rParamZyn(PFilterVelocityScaleFunction, rShort("sense"), rDefault(64), + "Filter Velocity Function Shape"), //Resonance - rToggle(Hrandgrouping, "How randomness is applied to multiple voices using the same oscil"), + rToggle(Hrandgrouping, rDefault(false), + "How randomness is applied to multiple voices using the same oscil"), //weird stuff for PCoarseDetune {"detunevalue:", rMap(unit,cents) rDoc("Get detune in cents"), NULL, @@ -325,12 +363,15 @@ static const Ports globalPorts = { #undef rObject #define rObject ADnoteParameters -#define rChangeCb obj->last_update_timestamp = obj->time.time(); +#define rChangeCb obj->last_update_timestamp = obj->time->time(); static const Ports adPorts = {//XXX 16 should not be hard coded rSelf(ADnoteParameters), rPaste, rArrayPaste, rRecurs(VoicePar, NUM_VOICES), + {"VoicePar#" STRINGIFY(NUM_VOICES) "/Enabled::T:F", + rProp(parameter) rShort("enable") rDoc("Voice Enable"), + NULL, rArrayTCbMember(VoicePar, Enabled)}, rRecur(GlobalPar, "Adnote Parameters"), }; #undef rChangeCb @@ -359,16 +400,16 @@ ADnoteGlobalParam::ADnoteGlobalParam(const AbsTime *time_) : time(time_), last_update_timestamp(0) { FreqEnvelope = new EnvelopeParams(0, 0, time_); - FreqEnvelope->ASRinit(64, 50, 64, 60); + FreqEnvelope->init(EnvelopeParams::ad_global_freq_env); FreqLfo = new LFOParams(70, 0, 64, 0, 0, 0, 0, 0, time_); AmpEnvelope = new EnvelopeParams(64, 1, time_); - AmpEnvelope->ADSRinit_dB(0, 40, 127, 25); + AmpEnvelope->init(EnvelopeParams::ad_global_amp_env); AmpLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 1, time_); GlobalFilter = new FilterParams(2, 94, 40, time_); FilterEnvelope = new EnvelopeParams(0, 1, time_); - FilterEnvelope->ADSRinit_filter(64, 40, 64, 70, 60, 64); + FilterEnvelope->init(EnvelopeParams::ad_global_filter_env); FilterLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 2, time_); Reson = new Resonance(); } @@ -515,22 +556,22 @@ void ADnoteVoiceParam::enable(const SYNTH_T &synth, FFTwrapper *fft, FMSmp = new OscilGen(synth, fft, NULL); AmpEnvelope = new EnvelopeParams(64, 1, time); - AmpEnvelope->ADSRinit_dB(0, 100, 127, 100); + AmpEnvelope->init(EnvelopeParams::ad_voice_amp_env); AmpLfo = new LFOParams(90, 32, 64, 0, 0, 30, 0, 1, time); FreqEnvelope = new EnvelopeParams(0, 0, time); - FreqEnvelope->ASRinit(30, 40, 64, 60); + FreqEnvelope->init(EnvelopeParams::ad_voice_freq_env); FreqLfo = new LFOParams(50, 40, 0, 0, 0, 0, 0, 0, time); VoiceFilter = new FilterParams(2, 50, 60, time); FilterEnvelope = new EnvelopeParams(0, 0, time); - FilterEnvelope->ADSRinit_filter(90, 70, 40, 70, 10, 40); + FilterEnvelope->init(EnvelopeParams::ad_voice_filter_env); FilterLfo = new LFOParams(50, 20, 64, 0, 0, 0, 0, 2, time); FMFreqEnvelope = new EnvelopeParams(0, 0, time); - FMFreqEnvelope->ASRinit(20, 90, 40, 80); + FMFreqEnvelope->init(EnvelopeParams::ad_voice_fm_freq_env); FMAmpEnvelope = new EnvelopeParams(64, 1, time); - FMAmpEnvelope->ADSRinit(80, 90, 127, 100); + FMAmpEnvelope->init(EnvelopeParams::ad_voice_fm_amp_env); } /* @@ -1114,7 +1155,7 @@ void ADnoteVoiceParam::getfromXML(XMLwrapper& xml, unsigned nvoice) Unison_invert_phase = xml.getpar127("unison_invert_phase", Unison_invert_phase); Unison_phase_randomness = xml.getpar127("unison_phase_randomness", - Unison_phase_randomness); + Unison_phase_randomness); Type = xml.getpar127("type", Type); PDelay = xml.getpar127("delay", PDelay); diff --git a/src/Params/ADnoteParameters.h b/src/Params/ADnoteParameters.h @@ -330,14 +330,14 @@ class ADnoteParameters:public PresetsArray void defaults(int n); //n is the nvoice void add2XMLsection(XMLwrapper& xml, int n); void getfromXMLsection(XMLwrapper& xml, int n); - private: + const AbsTime *time; + int64_t last_update_timestamp; + + private: void EnableVoice(const SYNTH_T &synth, int nvoice, const AbsTime* time); void KillVoice(int nvoice); FFTwrapper *fft; - - const AbsTime *time; - int64_t last_update_timestamp; }; } diff --git a/src/Params/Controller.cpp b/src/Params/Controller.cpp @@ -29,33 +29,59 @@ namespace zyn { #undef rChangeCb #define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } const rtosc::Ports Controller::ports = { - rParamZyn(panning.depth, rShort("pan.d"), "Depth of Panning MIDI Control"), - rParamZyn(filtercutoff.depth, rShort("fc.d"), "Depth of Filter Cutoff MIDI Control"), - rParamZyn(filterq.depth, rShort("fq.d"), "Depth of Filter Q MIDI Control"), - rParamZyn(bandwidth.depth, rShort("bw.d"), "Depth of Bandwidth MIDI Control"), - rToggle(bandwidth.exponential, rShort("bw.exp"), "Bandwidth Exponential Mode"), - rParamZyn(modwheel.depth, rShort("mdw.d"), "Depth of Modwheel MIDI Control"), - rToggle(modwheel.exponential, rShort("mdw.exp"), "Modwheel Exponential Mode"), - rToggle(pitchwheel.is_split, "If PitchWheel Has unified bendrange or not"), - rParamI(pitchwheel.bendrange, rShort("pch.d"), "Range of MIDI Pitch Wheel"), - rParamI(pitchwheel.bendrange_down, "Lower Range of MIDI Pitch Wheel"), - rToggle(expression.receive, rShort("exp.rcv"), "Expression MIDI Receive"), - rToggle(fmamp.receive, rShort("fma.rcv"), "FM amplitude MIDI Receive"), - rToggle(volume.receive, rShort("vol.rcv"), "Volume MIDI Receive"), - rToggle(sustain.receive, rShort("sus.rcv"), "Sustain MIDI Receive"), - rToggle(portamento.receive, rShort("prt.rcv"), "Portamento MIDI Receive"), - rToggle(portamento.portamento, "Portamento Enable"), - rParamZyn(portamento.time, rShort("time"), "Portamento Length"), - rToggle(portamento.proportional, rShort("propt."), "Whether the portamento time is proportional" + rParamZyn(panning.depth, rShort("pan.d"), rDefault(64), + "Depth of Panning MIDI Control"), + rParamZyn(filtercutoff.depth, rShort("fc.d"), rDefault(64), + "Depth of Filter Cutoff MIDI Control"), + rParamZyn(filterq.depth, rShort("fq.d"), rDefault(64), + "Depth of Filter Q MIDI Control"), + rParamZyn(bandwidth.depth, rShort("bw.d"), rDefault(64), + "Depth of Bandwidth MIDI Control"), + rToggle(bandwidth.exponential, rShort("bw.exp"), rDefault(false), + "Bandwidth Exponential Mode"), + rParamZyn(modwheel.depth, rShort("mdw.d"), rDefault(80), + "Depth of Modwheel MIDI Control"), + rToggle(modwheel.exponential, rShort("mdw.exp"), rDefault(false), + "Modwheel Exponential Mode"), + rToggle(pitchwheel.is_split, rDefault(false), + "If PitchWheel Has unified bendrange or not"), + rParamI(pitchwheel.bendrange, rShort("pch.d"), rDefault(200), + rLinear(-6400, 6400), + "Range of MIDI Pitch Wheel"), + rParamI(pitchwheel.bendrange_down, rDefault(0), + "Lower Range of MIDI Pitch Wheel"), + rToggle(expression.receive, rShort("exp.rcv"), rDefault(true), + "Expression MIDI Receive"), + rToggle(fmamp.receive, rShort("fma.rcv"), rDefault(true), + "FM amplitude MIDI Receive"), + rToggle(volume.receive, rShort("vol.rcv"), rDefault(true), + "Volume MIDI Receive"), + rToggle(sustain.receive, rShort("sus.rcv"), rDefault(true), + "Sustain MIDI Receive"), + rToggle(portamento.receive, rShort("prt.rcv"), rDefault(true), + "Portamento MIDI Receive"), + rToggle(portamento.portamento, rDefault(false), + "Portamento Enable"), + rParamZyn(portamento.time, rShort("time"), rDefault(64), + "Portamento Length"), + rToggle(portamento.proportional, rShort("propt."), rDefault(false), + "Whether the portamento time is proportional" "to the size of the interval between two notes."), - rParamZyn(portamento.propRate, rShort("scale"), "Portamento proportional scale"), - rParamZyn(portamento.propDepth, rShort("depth"), "Portamento proportional depth"), - rParamZyn(portamento.pitchthresh, rShort("thresh"), "Threshold for portamento"), - rToggle(portamento.pitchthreshtype, rShort("tr.type"), "Type of threshold"), - rParamZyn(portamento.updowntimestretch, rShort("up/dwn"), "Relative length of glide up vs glide down"), - rParamZyn(resonancecenter.depth, rShort("rfc.d"), "Resonance Center MIDI Depth"), - rParamZyn(resonancebandwidth.depth, rShort("rbw.d"), "Resonance Bandwidth MIDI Depth"), - rToggle(NRPN.receive, "NRPN MIDI Enable"), + rParamZyn(portamento.propRate, rShort("scale"), rDefault(80), + "Portamento proportional scale"), + rParamZyn(portamento.propDepth, rShort("depth"), rDefault(90), + "Portamento proportional depth"), + rParamZyn(portamento.pitchthresh, rShort("thresh"), rDefault(3), + "Threshold for portamento"), + rToggle(portamento.pitchthreshtype, rShort("tr.type"), rDefault(true), + "Type of threshold"), + rParamZyn(portamento.updowntimestretch, rShort("up/dwn"), rDefault(64), + "Relative length of glide up vs glide down"), + rParamZyn(resonancecenter.depth, rShort("rfc.d"), rDefault(64), + "Resonance Center MIDI Depth"), + rParamZyn(resonancebandwidth.depth, rShort("rbw.d"), rDefault(64), + "Resonance Bandwidth MIDI Depth"), + rToggle(NRPN.receive, rDefault(true), "NRPN MIDI Enable"), rAction(defaults), }; #undef rChangeCb diff --git a/src/Params/EnvelopeParams.cpp b/src/Params/EnvelopeParams.cpp @@ -37,27 +37,54 @@ static const rtosc::Ports localPorts = { #undef rChangeCb #define rChangeCb if(!obj->Pfreemode) obj->converttofree(); if (obj->time) { \ obj->last_update_timestamp = obj->time->time(); } - rToggle(Pfreemode, "Complex Envelope Definitions"), + rToggle(Pfreemode, rDefault(false), "Complex Envelope Definitions"), #undef rChangeCb #define rChangeCb if(!obj->Pfreemode) obj->converttofree(); \ if(obj->time) { obj->last_update_timestamp = obj->time->time(); } - rParamZyn(Penvpoints, rProp(internal), "Number of points in complex definition"), - rParamZyn(Penvsustain, "Location of the sustain point"), + rOption(envelope_type, rProp(internal), + rOptions(ad_global_amp, ad_global_freq, ad_global_filter, + ad_voice_amp, ad_voice_freq, ad_voice_filter, + ad_voice_fm_freq, ad_voice_fm_amp, + sub_freq_env, sub_bandwidth_env), "function of the envelope"), + rParamZyn(Penvpoints, rProp(internal), rDefaultDepends(envelope_type), + rPresets(4, 3, 4, 4, 3, 4, 3, 4, 3, 3), + "Number of points in complex definition"), + rParamZyn(Penvsustain, rDefaultDepends(envelope_type), + rPresets(2, 1, 2, 2, 1, 2, 1, 2, 1, 1), + "Location of the sustain point"), rParams(Penvdt, MAX_ENVELOPE_POINTS, "Envelope Delay Times"), rParams(Penvval, MAX_ENVELOPE_POINTS, "Envelope Values"), - rParamZyn(Penvstretch, rShort("stretch"), + rParamZyn(Penvstretch, rShort("stretch"), rDefaultDepends(envelope_type), + rPresets(64, 0, 0, 64, 0, 0, 0, 64, 64, 64), "Stretch with respect to frequency"), - rToggle(Pforcedrelease, rShort("frcr"), + rToggle(Pforcedrelease, rShort("frcr"), rDefaultDepends(envelope_type), + rPresets(true, false, true, true, false, + false, false, true, false, false), "Force Envelope to fully evaluate"), - rToggle(Plinearenvelope, rShort("lin/log"), + rToggle(Plinearenvelope, rShort("lin/log"), rDefault(false), "Linear or Logarithmic Envelopes"), - rParamZyn(PA_dt, rShort("a.dt"), "Attack Time"), - rParamZyn(PA_val, rShort("a.val"), "Attack Value"), - rParamZyn(PD_dt, rShort("d.dt"), "Decay Time"), - rParamZyn(PD_val, rShort("d.val"), "Decay Value"), - rParamZyn(PS_val, rShort("s.val"), "Sustain Value"), - rParamZyn(PR_dt, rShort("r.dt"), "Release Time"), - rParamZyn(PR_val, rShort("r.val"), "Release Value"), + rParamZyn(PA_dt, rShort("a.dt"), rDefaultDepends(envelope_type), + rPresets(0, 50, 40, 0, 40, 70, 90, 80, 50, 70), + "Attack Time"), + rParamZyn(PA_val, rShort("a.val"), rDefaultDepends(envelope_type), + rDefault(64), rPresetsAt(4, 30, 90, 20, 64, 30, 100), + "Attack Value"), + rParamZyn(PD_dt, rShort("d.dt"), rDefaultDepends(envelope_type), + rDefault(10), rPresets(40, 10, 70, 100, 10, 70, 10, 90), + "Decay Time"), + rParamZyn(PD_val, rShort("d.val"), rDefaultDepends(envelope_type), + rDefault(64), rPresetsAt(5, 40), + "Decay Value"), + rParamZyn(PS_val, rShort("s.val"), rDefaultDepends(envelope_type), + rDefault(64), + rPresets(127), rPresetsAt(3, 127), rPresetsAt(7, 127), + "Sustain Value"), + rParamZyn(PR_dt, rShort("r.dt"), rDefaultDepends(envelope_type), + rPresets(25, 60, 60, 100, 60, 10, 80, 100, 60, 60), + "Release Time"), + rParamZyn(PR_val, rShort("r.val"), rDefaultDepends(envelope_type), + rDefault(64), rPresetsAt(5, 40, 40), + "Release Value"), {"Envmode:", rDoc("Envelope variant type"), NULL, rBegin; @@ -202,6 +229,26 @@ void EnvelopeParams::paste(const EnvelopeParams &ep) } #undef COPY +void EnvelopeParams::init(EnvelopeParams::envelope_type_t etype) +{ + switch(etype) + { + case ad_global_amp_env: ADSRinit_dB(0, 40, 127, 25); break; + case ad_global_freq_env: ASRinit(64, 50, 64, 60); break; + case ad_global_filter_env: ADSRinit_filter(64, 40, 64, 70, 60, 64); + break; + case ad_voice_amp_env: ADSRinit_dB(0, 100, 127, 100); break; + case ad_voice_freq_env: ASRinit(30, 40, 64, 60); break; + case ad_voice_filter_env: ADSRinit_filter(90, 70, 40, 70, 10, 40); + break; + case ad_voice_fm_freq_env: ASRinit(20, 90, 40, 80); break; + case ad_voice_fm_amp_env: ADSRinit(80, 90, 127, 100); break; + case sub_freq_env: ASRinit(30, 50, 64, 60); break; + case sub_bandwidth_env: ASRinit_bw(100, 70, 64, 60); break; + }; + envelope_type = etype; +} + float EnvelopeParams::getdt(char i) const { return EnvelopeParams::dt(Penvdt[(int)i]); @@ -304,16 +351,6 @@ 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; @@ -326,6 +363,7 @@ void EnvelopeParams::converttofree() Penvval[3] = 0; break; case 3: + case 5: Penvpoints = 3; Penvsustain = 1; Penvval[0] = PA_val; @@ -345,15 +383,6 @@ void EnvelopeParams::converttofree() 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; } } @@ -413,7 +442,7 @@ public: // f^{-1} o (env_dB2rap^{-1}) o dB2rap o f // from the xml file. This results in the following formula: ? roundf(127.0f * (0.5f * - log10f( 0.01f + 0.99f * + log10f( 0.01f + 0.99f * powf(100, input/127.0f - 1)) + 1)) : input; diff --git a/src/Params/EnvelopeParams.h b/src/Params/EnvelopeParams.h @@ -23,21 +23,28 @@ namespace zyn { class EnvelopeParams:public Presets { public: + enum envelope_type_t + { + ad_global_amp_env, // ADSRinit_dB(0, 40, 127, 25); + ad_global_freq_env, // ASRinit(64, 50, 64, 60); + ad_global_filter_env, // ADSRinit_filter(64, 40, 64, 70, 60, 64) + + ad_voice_amp_env, // ADSRinit_dB(0, 100, 127, 100); + ad_voice_freq_env, // ASRinit(30, 40, 64, 60); + ad_voice_filter_env, // ADSRinit_filter(90, 70, 40, 70, 10, 40); + ad_voice_fm_freq_env, // ASRinit(20, 90, 40, 80); + ad_voice_fm_amp_env, // ADSRinit(80, 90, 127, 100) + sub_freq_env, // ASRinit(30, 50, 64, 60); + sub_bandwidth_env, // ASRinit_bw(100, 70, 64, 60) + }; + EnvelopeParams(unsigned char Penvstretch_=64, unsigned char Pforcedrelease_=0, const AbsTime *time_ = nullptr); ~EnvelopeParams(); void paste(const EnvelopeParams &ep); - 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 init(envelope_type_t etype); void converttofree(); void add2XML(XMLwrapper& xml); @@ -48,6 +55,10 @@ class EnvelopeParams:public Presets static float dt(char val); static char inv_dt(float val); + //! @brief defines where it is used and its default settings + //! corresponds to envelope_type_t + int envelope_type; + /* MIDI Parameters */ unsigned char Pfreemode; //1 for free mode, 0 otherwise unsigned char Penvpoints; @@ -78,6 +89,17 @@ class EnvelopeParams:public Presets static float env_dB2rap(float db); private: + 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 store2defaults(); /* Default parameters */ diff --git a/src/Params/FilterParams.cpp b/src/Params/FilterParams.cpp @@ -101,8 +101,9 @@ const rtosc::Ports FilterParams::ports = { unsigned idx = atoi(mm); if(rtosc_narguments(msg)) { obj->Psequence[idx].nvowel = rtosc_argument(msg, 0).i; - } else d.broadcast(d.loc, "i", obj->Psequence[idx].nvowel); + } else + d.reply(d.loc, "i", obj->Psequence[idx].nvowel); }}, {"type-svf::i", rProp(parameter) rShort("type") rOptions(low, high, band, notch) @@ -171,7 +172,6 @@ const rtosc::Ports FilterParams::ports = { 0.0, cf.d[1]); } } else if(obj->Pcategory == 2) { - int order = 0; float gain = dB2rap(obj->getgain()); auto cf = SVFilter::computeResponse(obj->Ptype, Filter::getrealfreq(obj->getfreq()), @@ -569,7 +569,7 @@ void FilterParams::getfromXML(XMLwrapper& xml) basefreq = xml.getparreal("basefreq", 1000); baseq = xml.getparreal("baseq", 10); gain = xml.getparreal("gain", 0); - freqtracking = xml.getparreal("freq_track", 0); + freqtracking = xml.getparreal("freq_tracking", 0); } //formant filter parameters diff --git a/src/Params/LFOParams.cpp b/src/Params/LFOParams.cpp @@ -42,13 +42,15 @@ static const rtosc::Ports _ports = { exp1, exp2), "Shape of LFO"), rParamZyn(Prandomness, rShort("a.r."), rSpecial(disable), "Amplitude Randomness (calculated uniformly at each cycle)"), - rParamZyn(Pfreqrand, rShort("f.r."), rSpecial(disable), + rParamZyn(Pfreqrand, rShort("f.r."), rSpecial(disable), rDefault(0), "Frequency Randomness (calculated uniformly at each cycle)"), rParamZyn(Pdelay, rShort("delay"), rSpecial(disable), "Delay before LFO start\n" "0..4 second delay"), rToggle(Pcontinous, rShort("c"), "Enable for global operation"), - rParamZyn(Pstretch, rShort("str"), rCentered, "Note frequency stretch"), - + rParamZyn(Pstretch, rShort("str"), rCentered, rDefault(64), + "Note frequency stretch"), +// these are currently not yet implemented at must be hidden therefore +#ifdef DEAD_PORTS //Float valued aliases {"delay::f", rProp(parameter) rMap(units, ms) rLog(0,4000), 0, rBegin; @@ -58,6 +60,7 @@ static const rtosc::Ports _ports = { {"period::f", rProp(parameter) rMap(units, ms) rPseudoLog(0.10, 1500.0), 0, rBegin; rEnd}, +#endif }; #undef rPseudoLog #undef rBegin diff --git a/src/Params/PADnoteParameters.cpp b/src/Params/PADnoteParameters.cpp @@ -43,33 +43,46 @@ static const rtosc::Ports realtime_ports = rRecurp(GlobalFilter, "Post Filter"), //Volume - rToggle(PStereo, rShort("stereo"), "Stereo/Mono Mode"), - rParamZyn(PPanning, rShort("panning"), "Left Right Panning"), - rParamZyn(PVolume, rShort("vol"), "Synth Volume"), - rParamZyn(PAmpVelocityScaleFunction, rShort("sense"), "Amplitude Velocity Sensing function"), + rToggle(PStereo, rShort("stereo"), rDefault(true), "Stereo/Mono Mode"), + rParamZyn(PPanning, rShort("panning"), rDefault(64), "Left Right Panning"), + rParamZyn(PVolume, rShort("vol"), rDefault(90), "Synth Volume"), + rParamZyn(PAmpVelocityScaleFunction, rShort("sense"), rDefault(64), + "Amplitude Velocity Sensing function"), - rParamZyn(Fadein_adjustment, rShort("a.pop."), "Adjustment for anti-pop strategy."), + rParamZyn(Fadein_adjustment, rShort("a.pop."), + rDefault(FADEIN_ADJUSTMENT_SCALE), "Adjustment for anti-pop strategy."), //Punch - rParamZyn(PPunchStrength, rShort("strength"), "Punch Strength"), - rParamZyn(PPunchTime, rShort("time"), "Length of punch"), - rParamZyn(PPunchStretch, rShort("stretch"), "How Punch changes with note frequency"), - rParamZyn(PPunchVelocitySensing, rShort("sense"), "Punch Velocity control"), + rParamZyn(PPunchStrength, rShort("strength"), rDefault(0), + "Punch Strength"), + rParamZyn(PPunchTime, rShort("time"), rDefault(60), + "Length of punch"), + rParamZyn(PPunchStretch, rShort("stretch"), rDefault(64), + "How Punch changes with note frequency"), + rParamZyn(PPunchVelocitySensing, rShort("sense"), rDefault(72), + "Punch Velocity control"), //Filter - rParamZyn(PFilterVelocityScale, rShort("scale"), "Filter Velocity Magnitude"), - rParamZyn(PFilterVelocityScaleFunction, rShort("sense"), "Filter Velocity Function Shape"), + rParamZyn(PFilterVelocityScale, rShort("scale"), rDefault(64), + "Filter Velocity Magnitude"), + rParamZyn(PFilterVelocityScaleFunction, rShort("sense"), rDefault(64), + "Filter Velocity Function Shape"), //Freq - rToggle(Pfixedfreq, rShort("fixed"), "Base frequency fixed frequency enable"), - rParamZyn(PfixedfreqET, rShort("f.ET"), "Equal temeperate control for fixed frequency operation"), - rParamZyn(PBendAdjust, "Pitch bend adjustment"), - rParamZyn(POffsetHz, rShort("offset"), "Voice constant offset"), - rParamI(PDetune, rShort("fine"), rLinear(0, 16383), "Fine Detune"), - rParamI(PCoarseDetune, rShort("coarse"), "Coarse Detune"), + rToggle(Pfixedfreq, rShort("fixed"), rDefault(false), + "Base frequency fixed frequency enable"), + rParamZyn(PfixedfreqET, rShort("f.ET"), rDefault(0), + "Equal temeperate control for fixed frequency operation"), + rParamZyn(PBendAdjust, rDefault(88), + "Pitch bend adjustment"), + rParamZyn(POffsetHz, rShort("offset"), rDefault(64), + "Voice constant offset"), + rParamI(PDetune, rShort("fine"), rLinear(0, 16383), rDefault(8192), + "Fine Detune"), + rParamI(PCoarseDetune, rShort("coarse"), rDefault(0), "Coarse Detune"), rParamZyn(PDetuneType, rShort("type"), rOptions(L35cents, L10cents, E100cents, E1200cents), - "Magnitude of Detune"), + rDefault(L10cents), "Magnitude of Detune"), {"sample#64:ifb", rProp(internal) rDoc("Nothing to see here"), 0, [](const char *m, rtosc::RtData &d) @@ -144,58 +157,78 @@ static const rtosc::Ports non_realtime_ports = rRecurp(resonance, "Resonance"), //Harmonic Shape - rOption(Pmode, rMap(min, 0), rMap(max, 2), rShort("distribution"), rOptions(bandwidth,discrete,continious), + rOption(Pmode, rMap(min, 0), rMap(max, 2), rShort("distribution"), + rOptions(bandwidth,discrete,continious), + rDefault(bandwidth), "Harmonic Distribution Model"), - rOption(Php.base.type, rOptions(Gaussian, Rectanglar, Double Exponential), rShort("shape"), + rOption(Php.base.type, rOptions(Gaussian, Rectanglar, Double Exponential), + rShort("shape"), rDefault(Gaussian), "Harmonic profile shape"), - rParamZyn(Php.base.par1, rShort("warp"), "Harmonic shape distribution parameter"), - rParamZyn(Php.freqmult, rShort("clone"), "Frequency multiplier on distribution"), - rParamZyn(Php.modulator.par1, rShort("p1"), "Distribution modulator parameter"), - rParamZyn(Php.modulator.freq, rShort("freq"), "Frequency of modulator parameter"), - rParamZyn(Php.width, rShort("bandwidth"), "Width of base harmonic"), - rOption(Php.amp.mode, rShort("mode"), rOptions(Sum, Mult, Div1, Div2), + rParamZyn(Php.base.par1, rShort("warp"), rDefault(80), + "Harmonic shape distribution parameter"), + rParamZyn(Php.freqmult, rShort("clone"), rDefault(0), + "Frequency multiplier on distribution"), + rParamZyn(Php.modulator.par1, rShort("p1"), rDefault(0), + "Distribution modulator parameter"), + rParamZyn(Php.modulator.freq, rShort("freq"), rDefault(30), + "Frequency of modulator parameter"), + rParamZyn(Php.width, rShort("bandwidth"), rDefault(127), + "Width of base harmonic"), + rOption(Php.amp.mode, rShort("mode"), + rOptions(Sum, Mult, Div1, Div2), rDefault(Sum), "Amplitude harmonic multiplier type"), //Harmonic Modulation rOption(Php.amp.type, rShort("mult"), rOptions(Off, Gauss, Sine, Flat), - "Type of amplitude multipler"), - rParamZyn(Php.amp.par1, rShort("p1"), "Amplitude multiplier parameter"), - rParamZyn(Php.amp.par2, rShort("p2"), "Amplitude multiplier parameter"), - rToggle(Php.autoscale, rShort("auto"), "Autoscaling Harmonics"), + rDefault(Off), "Type of amplitude multipler"), + rParamZyn(Php.amp.par1, rShort("p1"), rDefault(80), + "Amplitude multiplier parameter"), + rParamZyn(Php.amp.par2, rShort("p2"), rDefault(60), + "Amplitude multiplier parameter"), + rToggle(Php.autoscale, rShort("auto"), rDefault(true), + "Autoscaling Harmonics"), rOption(Php.onehalf, rShort("side"), - rOptions(Full, Upper Half, Lower Half), + rOptions(Full, Upper Half, Lower Half), rDefault(Full) "Harmonic cutoff model"), //Harmonic Bandwidth rOption(Pbwscale, rShort("bw scale"), rOptions(Normal, - EqualHz, Quater, + EqualHz, Quarter, Half, 75%, 150%, Double, Inv. Half), + rDefault(Normal), "Bandwidth scaling"), //Harmonic Position Modulation rOption(Phrpos.type, rOptions(Harmonic, ShiftU, ShiftL, PowerU, PowerL, Sine, Power, Shift), + rDefault(Harmonic) "Harmonic Overtone shifting mode"), - rParamI(Phrpos.par1, rShort("p1"), rLinear(0,255), "Harmonic position parameter"), - rParamI(Phrpos.par2, rShort("p2"), rLinear(0,255), "Harmonic position parameter"), - rParamI(Phrpos.par3, rShort("force h."), rLinear(0,255), "Harmonic position parameter"), + rParamI(Phrpos.par1, rShort("p1"), rLinear(0,255), rDefault(0), + "Harmonic position parameter"), + rParamI(Phrpos.par2, rShort("p2"), rLinear(0,255), rDefault(0), + "Harmonic position parameter"), + rParamI(Phrpos.par3, rShort("force h."), rLinear(0,255), rDefault(0), + "Harmonic position parameter"), //Quality rOption(Pquality.samplesize, rShort("quality"), rOptions(16k (Tiny), 32k, 64k (Small), 128k, 256k (Normal), 512k, 1M (Big)), + rDefaultId(128k), "Size of each wavetable element"), rOption(Pquality.basenote, rShort("basenote"), rOptions(C-2, G-2, C-3, G-3, C-4, G-4, C-5, G-5, G-6,), + rDefaultId(C-4), "Base note for wavetable"), rOption(Pquality.smpoct, rShort("smp/oct"), rOptions(0.5, 1, 2, 3, 4, 6, 12), + rDefault(2), "Samples per octave"), - rParamI(Pquality.oct, rShort("octaves"), rLinear(0,7), + rParamI(Pquality.oct, rShort("octaves"), rLinear(0,7), rDefault(3), "Number of octaves to sample (above the first sample"), {"Pbandwidth::i", rShort("bandwidth") rProp(parameter) rLinear(0,1000) rDoc("Bandwith Of Harmonics"), NULL, @@ -278,16 +311,16 @@ PADnoteParameters::PADnoteParameters(const SYNTH_T &synth_, FFTwrapper *fft_, oscilgen->ADvsPAD = true; FreqEnvelope = new EnvelopeParams(0, 0, time_); - FreqEnvelope->ASRinit(64, 50, 64, 60); + FreqEnvelope->init(EnvelopeParams::ad_global_freq_env); FreqLfo = new LFOParams(70, 0, 64, 0, 0, 0, 0, 0, time_); AmpEnvelope = new EnvelopeParams(64, 1, time_); - AmpEnvelope->ADSRinit_dB(0, 40, 127, 25); + AmpEnvelope->init(EnvelopeParams::ad_global_amp_env); AmpLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 1, time_); GlobalFilter = new FilterParams(2, 94, 40, time_); FilterEnvelope = new EnvelopeParams(0, 1, time_); - FilterEnvelope->ADSRinit_filter(64, 40, 64, 70, 60, 64); + FilterEnvelope->init(EnvelopeParams::ad_global_filter_env); FilterLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 2, time_); for(int i = 0; i < PAD_MAX_SAMPLES; ++i) diff --git a/src/Params/SUBnoteParameters.cpp b/src/Params/SUBnoteParameters.cpp @@ -37,52 +37,74 @@ namespace zyn { static const rtosc::Ports SUBnotePorts = { rSelf(SUBnoteParameters), rPaste, - rToggle(Pstereo, rShort("stereo"), "Stereo Enable"), - rParamZyn(PVolume, rShort("volume"), "Volume"), - rParamZyn(PPanning, rShort("panning"), "Left Right Panning"), - rParamZyn(PAmpVelocityScaleFunction, rShort("sense"), "Amplitude Velocity Sensing function"), - rParamI(PDetune, rShort("detune"), rLinear(0, 16383), "Detune in detune type units"), - rParamI(PCoarseDetune, rShort("cdetune"), "Coarse Detune"), + rToggle(Pstereo, rShort("stereo"), rDefault(true), "Stereo Enable"), + rParamZyn(PVolume, rShort("volume"), rDefault(96), "Volume"), + rParamZyn(PPanning, rShort("panning"), rDefault(64), "Left Right Panning"), + rParamZyn(PAmpVelocityScaleFunction, rShort("sense"), rDefault(90), + "Amplitude Velocity Sensing function"), + rParamI(PDetune, rShort("detune"), rLinear(0, 16383), rDefault(8192), + "Detune in detune type units"), + rParamI(PCoarseDetune, rShort("cdetune"), rDefault(0), "Coarse Detune"), //Real values needed rOption(PDetuneType, rShort("det. scl."), - rOptions(L35 cents, L10 cents, E100 cents, E1200 cents), "Detune Scale"), - rToggle(PFreqEnvelopeEnabled, rShort("enable"), "Enable for Frequency Envelope"), - rToggle(PBandWidthEnvelopeEnabled, rShort("enable"), "Enable for Bandwidth Envelope"), - rToggle(PGlobalFilterEnabled, rShort("enable"), "Enable for Global Filter"), - rParamZyn(PGlobalFilterVelocityScale, rShort("scale"), "Filter Velocity Magnitude"), - rParamZyn(PGlobalFilterVelocityScaleFunction, rShort("sense"), "Filter Velocity Function Shape"), + rOptions(L35 cents, L10 cents, E100 cents, E1200 cents), + rDefaultId(L10 cents), "Detune Scale"), + rToggle(PFreqEnvelopeEnabled, rShort("enable"), rDefault(false), + "Enable for Frequency Envelope"), + rToggle(PBandWidthEnvelopeEnabled, rShort("enable"), rDefault(false), + "Enable for Bandwidth Envelope"), + rToggle(PGlobalFilterEnabled, rShort("enable"), + rDefault(false), "Enable for Global Filter"), + rParamZyn(PGlobalFilterVelocityScale, rShort("scale"), rDefault(64), + "Filter Velocity Magnitude"), + rParamZyn(PGlobalFilterVelocityScaleFunction, rShort("sense"), rDefault(64), + "Filter Velocity Function Shape"), //rRecur(FreqEnvelope, EnvelopeParams), //rToggle(),//continue - rToggle(Pfixedfreq, rShort("fixed freq"), "Base frequency fixed frequency enable"), - rParamZyn(PfixedfreqET, rShort("fixed ET"), "Equal temeperate control for fixed frequency operation"), - rParamZyn(PBendAdjust, rShort("bend"), "Pitch bend adjustment"), - rParamZyn(POffsetHz, rShort("+ Hz"), "Voice constant offset"), + rToggle(Pfixedfreq, rShort("fixed freq"), rDefault(false), + "Base frequency fixed frequency enable"), + rParamZyn(PfixedfreqET, rShort("fixed ET"), rDefault(0), + "Equal temeperate control for fixed frequency operation"), + rParamZyn(PBendAdjust, rShort("bend"), rDefault(88), + "Pitch bend adjustment"), + rParamZyn(POffsetHz, rShort("+ Hz"), rDefault(64), + "Voice constant offset"), #undef rChangeCb #define rChangeCb obj->updateFrequencyMultipliers(); if (obj->time) { \ obj->last_update_timestamp = obj->time->time(); } rParamI(POvertoneSpread.type, rMap(min, 0), rMap(max, 7), rShort("spread type") rOptions(Harmonic, ShiftU, ShiftL, PowerU, PowerL, Sine, Power, Shift), + rDefault(Harmonic) "Spread of harmonic frequencies"), rParamI(POvertoneSpread.par1, rMap(min, 0), rMap(max, 255), rShort("p1"), - "Overtone Parameter"), + rDefault(0), "Overtone Parameter"), rParamI(POvertoneSpread.par2, rMap(min, 0), rMap(max, 255), rShort("p2"), - "Overtone Parameter"), + rDefault(0), "Overtone Parameter"), rParamI(POvertoneSpread.par3, rMap(min, 0), rMap(max, 255), rShort("forceH"), - "Force Overtones To Harmonics"), + rDefault(0), "Force Overtones To Harmonics"), #undef rChangeCb #define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } - rParamI(Pnumstages, rShort("stages"), rMap(min, 1), rMap(max, 5), "Number of filter stages"), - rParamZyn(Pbandwidth, rShort("bandwidth"), "Bandwidth of filters"), - rParamZyn(Phmagtype, rShort("mag. type"), rOptions(linear, -40dB, -60dB, -80dB, -100dB), "Magnitude scale"), - rArray(Phmag, MAX_SUB_HARMONICS, "Harmonic magnitudes"), - rArray(Phrelbw, MAX_SUB_HARMONICS, "Relative bandwidth"), - rParamZyn(Pbwscale, rShort("stretch"), "Bandwidth scaling with frequency"), + rParamI(Pnumstages, rShort("stages"), rMap(min, 1), rMap(max, 5), + rDefault(2), "Number of filter stages"), + rParamZyn(Pbandwidth, rShort("bandwidth"), rDefault(40), + "Bandwidth of filters"), + rParamZyn(Phmagtype, rShort("mag. type"), + rOptions(linear, -40dB, -60dB, -80dB, -100dB), + rDefault(linear), "Magnitude scale"), + rArray(Phmag, MAX_SUB_HARMONICS, rDefaultMissing, + "Harmonic magnitudes"), + rArray(Phrelbw, MAX_SUB_HARMONICS, rDefaultMissing, + "Relative bandwidth"), + rParamZyn(Pbwscale, rShort("stretch"), rDefault(64), + "Bandwidth scaling with frequency"), rRecurp(AmpEnvelope, "Amplitude envelope"), rRecurp(FreqEnvelope, "Frequency Envelope"), rRecurp(BandWidthEnvelope, "Bandwidth Envelope"), rRecurp(GlobalFilterEnvelope, "Post Filter Envelope"), rRecurp(GlobalFilter, "Post Filter"), - rOption(Pstart, rShort("initial"), rOptions(zero, random, ones), "How harmonics are initialized"), + rOption(Pstart, rShort("initial"), rOptions(zero, random, ones), + rDefault(random), + "How harmonics are initialized"), {"clear:", rDoc("Reset all harmonics to equal bandwidth/zero amplitude"), NULL, rBegin; @@ -179,15 +201,15 @@ SUBnoteParameters::SUBnoteParameters(const AbsTime *time_) { setpresettype("Psubsynth"); AmpEnvelope = new EnvelopeParams(64, 1, time_); - AmpEnvelope->ADSRinit_dB(0, 40, 127, 25); + AmpEnvelope->init(EnvelopeParams::ad_global_amp_env); FreqEnvelope = new EnvelopeParams(64, 0, time_); - FreqEnvelope->ASRinit(30, 50, 64, 60); + FreqEnvelope->init(EnvelopeParams::sub_freq_env); BandWidthEnvelope = new EnvelopeParams(64, 0, time_); - BandWidthEnvelope->ASRinit_bw(100, 70, 64, 60); + BandWidthEnvelope->init(EnvelopeParams::sub_bandwidth_env); GlobalFilter = new FilterParams(2, 80, 40, time_); GlobalFilterEnvelope = new EnvelopeParams(0, 1, time_); - GlobalFilterEnvelope->ADSRinit_filter(64, 40, 64, 70, 60, 64); + GlobalFilterEnvelope->init(EnvelopeParams::ad_global_filter_env); defaults(); } diff --git a/src/Plugin/AlienWah/CMakeLists.txt b/src/Plugin/AlienWah/CMakeLists.txt @@ -24,6 +24,8 @@ add_custom_command(TARGET ZynAlienWah_lv2 POST_BUILD COMMAND ../../lv2-ttl-generator $<TARGET_FILE:ZynAlienWah_lv2> WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lv2) +add_dependencies(ZynAlienWah_lv2 lv2-ttl-generator) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lv2/manifest.ttl ${CMAKE_CURRENT_BINARY_DIR}/lv2/presets.ttl diff --git a/src/Plugin/Chorus/CMakeLists.txt b/src/Plugin/Chorus/CMakeLists.txt @@ -24,6 +24,8 @@ add_custom_command(TARGET ZynChorus_lv2 POST_BUILD COMMAND ../../lv2-ttl-generator $<TARGET_FILE:ZynChorus_lv2> WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lv2) +add_dependencies(ZynChorus_lv2 lv2-ttl-generator) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lv2/manifest.ttl ${CMAKE_CURRENT_BINARY_DIR}/lv2/presets.ttl diff --git a/src/Plugin/Distortion/CMakeLists.txt b/src/Plugin/Distortion/CMakeLists.txt @@ -24,6 +24,8 @@ add_custom_command(TARGET ZynDistortion_lv2 POST_BUILD COMMAND ../../lv2-ttl-generator $<TARGET_FILE:ZynDistortion_lv2> WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lv2) +add_dependencies(ZynDistortion_lv2 lv2-ttl-generator) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lv2/manifest.ttl ${CMAKE_CURRENT_BINARY_DIR}/lv2/presets.ttl diff --git a/src/Plugin/DynamicFilter/CMakeLists.txt b/src/Plugin/DynamicFilter/CMakeLists.txt @@ -24,6 +24,8 @@ add_custom_command(TARGET ZynDynamicFilter_lv2 POST_BUILD COMMAND ../../lv2-ttl-generator $<TARGET_FILE:ZynDynamicFilter_lv2> WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lv2) +add_dependencies(ZynDynamicFilter_lv2 lv2-ttl-generator) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lv2/manifest.ttl ${CMAKE_CURRENT_BINARY_DIR}/lv2/presets.ttl diff --git a/src/Plugin/Echo/CMakeLists.txt b/src/Plugin/Echo/CMakeLists.txt @@ -24,6 +24,8 @@ add_custom_command(TARGET ZynEcho_lv2 POST_BUILD COMMAND ../../lv2-ttl-generator $<TARGET_FILE:ZynEcho_lv2> WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lv2) +add_dependencies(ZynEcho_lv2 lv2-ttl-generator) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lv2/manifest.ttl ${CMAKE_CURRENT_BINARY_DIR}/lv2/presets.ttl diff --git a/src/Plugin/Phaser/CMakeLists.txt b/src/Plugin/Phaser/CMakeLists.txt @@ -24,6 +24,8 @@ add_custom_command(TARGET ZynPhaser_lv2 POST_BUILD COMMAND ../../lv2-ttl-generator $<TARGET_FILE:ZynPhaser_lv2> WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lv2) +add_dependencies(ZynPhaser_lv2 lv2-ttl-generator) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lv2/manifest.ttl ${CMAKE_CURRENT_BINARY_DIR}/lv2/presets.ttl diff --git a/src/Plugin/Reverb/CMakeLists.txt b/src/Plugin/Reverb/CMakeLists.txt @@ -24,6 +24,8 @@ add_custom_command(TARGET ZynReverb_lv2 POST_BUILD COMMAND ../../lv2-ttl-generator $<TARGET_FILE:ZynReverb_lv2> WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lv2) +add_dependencies(ZynReverb_lv2 lv2-ttl-generator) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lv2/manifest.ttl ${CMAKE_CURRENT_BINARY_DIR}/lv2/presets.ttl diff --git a/src/Plugin/ZynAddSubFX/CMakeLists.txt b/src/Plugin/ZynAddSubFX/CMakeLists.txt @@ -48,6 +48,7 @@ add_library(ZynAddSubFX_vst SHARED elseif(ZestGui) # UI Enabled using Zest: internal only +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") add_library(ZynAddSubFX_lv2 SHARED ${CMAKE_SOURCE_DIR}/src/globals.cpp ${CMAKE_SOURCE_DIR}/src/UI/ConnectionDummy.cpp @@ -61,6 +62,7 @@ add_library(ZynAddSubFX_lv2_ui SHARED ${CMAKE_SOURCE_DIR}/DPF/dgl/src/Application.cpp ${CMAKE_SOURCE_DIR}/DPF/distrho/DistrhoUIMain.cpp ZynAddSubFX-UI-Zest.cpp) +endif() add_library(ZynAddSubFX_vst SHARED ${CMAKE_SOURCE_DIR}/src/globals.cpp @@ -91,10 +93,12 @@ add_library(ZynAddSubFX_vst SHARED endif() -set_target_properties(ZynAddSubFX_lv2 PROPERTIES COMPILE_DEFINITIONS "DISTRHO_PLUGIN_TARGET_LV2") -set_target_properties(ZynAddSubFX_lv2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "lv2") -set_target_properties(ZynAddSubFX_lv2 PROPERTIES OUTPUT_NAME "ZynAddSubFX") -set_target_properties(ZynAddSubFX_lv2 PROPERTIES PREFIX "") +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + set_target_properties(ZynAddSubFX_lv2 PROPERTIES COMPILE_DEFINITIONS "DISTRHO_PLUGIN_TARGET_LV2") + set_target_properties(ZynAddSubFX_lv2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "lv2") + set_target_properties(ZynAddSubFX_lv2 PROPERTIES OUTPUT_NAME "ZynAddSubFX") + set_target_properties(ZynAddSubFX_lv2 PROPERTIES PREFIX "") +endif() set_target_properties(ZynAddSubFX_vst PROPERTIES COMPILE_DEFINITIONS "DISTRHO_PLUGIN_TARGET_VST") set_target_properties(ZynAddSubFX_vst PROPERTIES LIBRARY_OUTPUT_DIRECTORY "vst") @@ -104,29 +108,40 @@ set_target_properties(ZynAddSubFX_vst PROPERTIES PREFIX "") if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") set(PLATFORM_LIBRARIES ws2_32 winmm + glu32 + gdi32 + opengl32 wsock32 "-static" iphlpapi "-static" winpthread) elseif(ZestGui) set(PLATFORM_LIBRARIES X11 GL rt) -else() +elseif(NtkGui OR FltkGui) set(PLATFORM_LIBRARIES X11 rt) +else() + set(PLATFORM_LIBRARIES rt) endif() -target_link_libraries(ZynAddSubFX_lv2 zynaddsubfx_core ${OS_LIBRARIES} ${LIBLO_LIBRARIES} - ${PLATFORM_LIBRARIES}) +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + target_link_libraries(ZynAddSubFX_lv2 zynaddsubfx_core ${OS_LIBRARIES} ${LIBLO_LIBRARIES} + ${PLATFORM_LIBRARIES}) +endif() target_link_libraries(ZynAddSubFX_vst zynaddsubfx_core ${OS_LIBRARIES} ${LIBLO_LIBRARIES} ${PLATFORM_LIBRARIES}) -if(ZestGui) +if(ZestGui AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") target_link_libraries(ZynAddSubFX_lv2_ui X11 GL) endif() -install(TARGETS ZynAddSubFX_lv2 LIBRARY DESTINATION ${PluginLibDir}/lv2/ZynAddSubFX.lv2/) -install(TARGETS ZynAddSubFX_vst LIBRARY DESTINATION ${PluginLibDir}/vst/) +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + install(TARGETS ZynAddSubFX_lv2 LIBRARY DESTINATION ${PluginLibDir}/lv2/ZynAddSubFX.lv2/) + install(TARGETS ZynAddSubFX_vst LIBRARY DESTINATION ${PluginLibDir}/vst/) -add_custom_command(TARGET ZynAddSubFX_lv2 POST_BUILD - COMMAND ../../lv2-ttl-generator $<TARGET_FILE:ZynAddSubFX_lv2> - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lv2) + add_custom_command(TARGET ZynAddSubFX_lv2 POST_BUILD + COMMAND ../../lv2-ttl-generator $<TARGET_FILE:ZynAddSubFX_lv2> + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lv2) + + add_dependencies(ZynAddSubFX_lv2 lv2-ttl-generator) +endif() install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lv2/manifest.ttl diff --git a/src/Plugin/ZynAddSubFX/ZynAddSubFX-UI-Zest.cpp b/src/Plugin/ZynAddSubFX/ZynAddSubFX-UI-Zest.cpp @@ -13,7 +13,14 @@ // DPF includes #include "DistrhoUI.hpp" +#ifdef WIN32 +#include <windows.h> +#else +#ifndef __USE_GNU +#define __USE_GNU +#endif #include <dlfcn.h> +#endif typedef void *zest_t; struct zest_handles { @@ -41,13 +48,37 @@ public: : UI(1181, 659) { printf("[INFO] Opened the zynaddsubfx UI...\n"); - handle = dlopen("/opt/zyn-fusion/libzest.so", RTLD_LAZY); +#ifdef WIN32 + char path[1024]; + GetModuleFileName(GetModuleHandle("ZynAddSubFX.dll"), path, sizeof(path)); + if(strstr(path, "ZynAddSubFX.dll")) + strstr(path, "ZynAddSubFX.dll")[0] = 0; + strcat(path, "libzest.dll"); + printf("[DEBUG] Loading zest library from <%s>\n", path); + handle = LoadLibrary(path); + if(!handle) + handle = LoadLibrary("./libzest.dll"); + if(!handle) + handle = LoadLibrary("libzest.dll"); +#else + handle = dlopen("./libzest.so", RTLD_LAZY); + if(!handle) + handle = dlopen("/opt/zyn-fusion/libzest.so", RTLD_LAZY); + if(!handle) + handle = dlopen("libzest.so", RTLD_LAZY); +#endif if(!handle) { printf("[ERROR] Cannot Open libzest.so\n"); +#ifndef WIN32 printf("[ERROR] '%s'\n", dlerror()); +#endif } memset(&z, 0, sizeof(z)); +#ifdef WIN32 +#define get_sym(x) z.zest_##x = (decltype(z.zest_##x))GetProcAddress(handle, "zest_"#x) +#else #define get_sym(x) z.zest_##x = (decltype(z.zest_##x))dlsym(handle, "zest_"#x) +#endif if(handle) { get_sym(open); get_sym(setup); @@ -70,8 +101,11 @@ public: printf("[INFO:Zyn] zest_close()\n"); if(z.zest) z.zest_close(z.zest); +#ifdef WIN32 +#else if(handle) - dlclose(handle); + dlclose(handle); +#endif } protected: @@ -156,13 +190,15 @@ protected: if(!z.zest) { if(!z.zest_open) return; - //printf("[INFO:Zyn] zest_open()\n"); +if(!oscPort) + return; + printf("[INFO:Zyn] zest_open()\n"); char address[1024]; snprintf(address, sizeof(address), "osc.udp://127.0.0.1:%d",oscPort); printf("[INFO:Zyn] zest_open(%s)\n", address); z.zest = z.zest_open(address); - printf("[INFO:Zyn] zest_setup()\n"); + printf("[INFO:Zyn] zest_setup(%s)\n", address); z.zest_setup(z.zest); } @@ -195,7 +231,11 @@ protected: private: int oscPort; zest_handles z; +#ifdef WIN32 + HMODULE handle; +#else void *handle; +#endif DISTRHO_DECLARE_NON_COPY_CLASS(ZynAddSubFXUI) diff --git a/src/Plugin/ZynAddSubFX/ZynAddSubFX.cpp b/src/Plugin/ZynAddSubFX/ZynAddSubFX.cpp @@ -115,8 +115,6 @@ public: oscPort(0), middlewareThread(new MiddleWareThread()) { - config.init(); - synth.buffersize = static_cast<int>(getBufferSize()); synth.samplerate = static_cast<uint>(getSampleRate()); @@ -226,7 +224,7 @@ protected: parameter.ranges.def = 0.0f; break; } - if(kParamSlot1 <= index && index <= kParamSlot16) { + if(index <= kParamSlot16) { parameter.hints = kParameterIsAutomable; parameter.name = ("Slot " + zyn::to_s(index-kParamSlot1 + 1)).c_str(); parameter.symbol = ("slot" + zyn::to_s(index-kParamSlot1 + 1)).c_str(); @@ -248,7 +246,7 @@ protected: case kParamOscPort: return oscPort; } - if(kParamSlot1 <= index && index <= kParamSlot16) { + if(index <= kParamSlot16) { return master->automate.getSlot(index - kParamSlot1); } return 0.0f; @@ -262,10 +260,8 @@ protected: */ void setParameterValue(uint32_t index, float value) noexcept override { - // only an output port for now - if(kParamSlot1 <= index && index <= kParamSlot16) { + if(index <= kParamSlot16) master->automate.setSlot(index - kParamSlot1, value); - } } /* -------------------------------------------------------------------------------------------------------- diff --git a/src/Synth/OscilGen.cpp b/src/Synth/OscilGen.cpp @@ -38,27 +38,30 @@ const rtosc::Ports OscilGen::non_realtime_ports = { rOptions(linear,dB scale (-40), dB scale (-60), dB scale (-80), dB scale (-100)), + rDefault(linear), "Type of magnitude for harmonics"), rOption(Pcurrentbasefunc, rShort("base"), rOptions(sine, triangle, pulse, saw, power, gauss, diode, abssine, pulsesine, stretchsine, chirp, absstretchsine, chebyshev, sqr, spike, circle), rOpt(127,use-as-base waveform), + rDefault(sine), "Base Waveform for harmonics"), - rParamZyn(Pbasefuncpar, rShort("shape"), + rParamZyn(Pbasefuncpar, rShort("shape"), rDefault(64), "Morph between possible base function shapes " "(e.g. rising sawtooth vs a falling sawtooth)"), rOption(Pbasefuncmodulation, rShort("mod"), - rOptions(None, Rev, Sine, Power, Chop), + rOptions(None, Rev, Sine, Power, Chop), rDefault(None), "Modulation applied to Base function spectra"), - rParamZyn(Pbasefuncmodulationpar1, rShort("p1"), + rParamZyn(Pbasefuncmodulationpar1, rShort("p1"), rDefault(64), "Base function modulation parameter"), - rParamZyn(Pbasefuncmodulationpar2, rShort("p2"), + rParamZyn(Pbasefuncmodulationpar2, rShort("p2"), rDefault(64), "Base function modulation parameter"), - rParamZyn(Pbasefuncmodulationpar3, rShort("p3"), + rParamZyn(Pbasefuncmodulationpar3, rShort("p3"), rDefault(32), "Base function modulation parameter"), - rParamZyn(Pwaveshaping, rShort("amount"), "Degree Of Waveshaping"), - rOption(Pwaveshapingfunction, rShort("distort"), + rParamZyn(Pwaveshaping, rShort("amount"), rDefault(64), + "Degree Of Waveshaping"), + rOption(Pwaveshapingfunction, rShort("distort"), rDefault(Undistorted), rOptions(Undistorted, Arctangent, Asymmetric, Pow, Sine, Quantisize, Zigzag, Limiter, Upper Limiter, Lower Limiter, @@ -66,22 +69,29 @@ const rtosc::Ports OscilGen::non_realtime_ports = { "Shape of distortion to be applied"), rOption(Pfiltertype, rShort("filter"), rOptions(No Filter, lp, hp1, hp1b, bp1, bs1, lp2, hp2, bp2, bs2, - cos, sin, low_shelf, s), "Harmonic Filter"), - rParamZyn(Pfilterpar1, rShort("p1"), "Filter parameter"), - rParamZyn(Pfilterpar2, rShort("p2"), "Filter parameter"), - rToggle(Pfilterbeforews, rShort("pre/post"), "Filter before waveshaping spectra;" + cos, sin, low_shelf, s), rDefaultId(No Filter), "Harmonic Filter"), + rParamZyn(Pfilterpar1, rShort("p1"), rDefault(64), "Filter parameter"), + rParamZyn(Pfilterpar2, rShort("p2"), rDefault(64), "Filter parameter"), + rToggle(Pfilterbeforews, rShort("pre/post"), rDefault(false), + "Filter before waveshaping spectra;" "When enabled oscilfilter(freqs); then waveshape(freqs);, " "otherwise waveshape(freqs); then oscilfilter(freqs);"), rOption(Psatype, rShort("spec. adj."), rOptions(None, Pow, ThrsD, ThrsU), - "Spectral Adjustment Type"), - rParamZyn(Psapar, rShort("p1"), "Spectral Adjustment Parameter"), - rParamI(Pharmonicshift, rLinear(-64,64), rShort("shift"), "Amount of shift on harmonics"), - rToggle(Pharmonicshiftfirst, rShort("pre/post"), "If harmonics are shifted before waveshaping/filtering"), + rDefault(None), "Spectral Adjustment Type"), + rParamZyn(Psapar, rShort("p1"), rDefault(64), + "Spectral Adjustment Parameter"), + rParamI(Pharmonicshift, rLinear(-64,64), rShort("shift"), rDefault(0), + "Amount of shift on harmonics"), + rToggle(Pharmonicshiftfirst, rShort("pre/post"), rDefault(false), + "If harmonics are shifted before waveshaping/filtering"), rOption(Pmodulation, rShort("FM"), rOptions(None, Rev, Sine, Power), - "Frequency Modulation To Combined Spectra"), - rParamZyn(Pmodulationpar1, rShort("p1"), "modulation parameter"), - rParamZyn(Pmodulationpar2, rShort("p2"), "modulation parameter"), - rParamZyn(Pmodulationpar3, rShort("p3"), "modulation parameter"), + rDefault(None), "Frequency Modulation To Combined Spectra"), + rParamZyn(Pmodulationpar1, rShort("p1"), rDefault(64), + "modulation parameter"), + rParamZyn(Pmodulationpar2, rShort("p2"), rDefault(64), + "modulation parameter"), + rParamZyn(Pmodulationpar3, rShort("p3"), rDefault(32), + "modulation parameter"), //TODO update to rArray and test @@ -190,19 +200,21 @@ const rtosc::Ports OscilGen::realtime_ports{ rPresetType, rParamZyn(Prand, rLinear(-64, 63), rShort("phase rnd"), "Oscillator Phase Randomness: smaller than 0 is \"" "group\", larger than 0 is for each harmonic"), - rParamZyn(Pamprandpower, rShort("variance"), + rParamZyn(Pamprandpower, rShort("variance"), rDefault(64), "Variance of harmonic randomness"), rOption(Pamprandtype, rShort("distribution"), rOptions(None, Pow, Sin), + rDefault(None), "Harmonic random distribution to select from"), rOption(Padaptiveharmonics, rShort("adapt") rOptions(OFF, ON, Square, 2xSub, 2xAdd, 3xSub, 3xAdd, 4xSub, 4xAdd), + rDefault(OFF), "Adaptive Harmonics Mode"), rParamI(Padaptiveharmonicsbasefreq, rShort("c. freq"), rLinear(0,255), - "Base frequency of adaptive harmonic (30..3000Hz)"), + rDefault(128), "Base frequency of adaptive harmonic (30..3000Hz)"), rParamI(Padaptiveharmonicspower, rShort("amount"), rLinear(0,200), - "Adaptive Harmonic Strength"), + rDefault(100), "Adaptive Harmonic Strength"), rParamI(Padaptiveharmonicspar, rShort("power"), rLinear(0,100), - "Adaptive Harmonics Postprocessing Power"), + rDefault(50), "Adaptive Harmonics Postprocessing Power"), {"waveform:", rDoc("Returns waveform points"), NULL, [](const char *, rtosc::RtData &d) { OscilGen &o = *((OscilGen*)d.obj); diff --git a/src/Synth/Resonance.cpp b/src/Synth/Resonance.cpp @@ -29,12 +29,17 @@ namespace zyn { const rtosc::Ports Resonance::ports = { rSelf(Resonance), rPaste, - rToggle(Penabled, rShort("enable"), "resonance enable"), - rToggle(Pprotectthefundamental, rShort("p.fund."), "Disable resonance filter on first harmonic"), - rParams(Prespoints, N_RES_POINTS, "Resonance data points"), - rParamZyn(PmaxdB, rShort("max"), "how many dB the signal may be amplified"), - rParamZyn(Pcenterfreq, rShort("c.freq"), "Center frequency"), - rParamZyn(Poctavesfreq, rShort("oct"), "The number of octaves..."), + rToggle(Penabled, rShort("enable"), rDefault(false), + "resonance enable"), + rToggle(Pprotectthefundamental, rShort("p.fund."), rDefault(false), + "Disable resonance filter on first harmonic"), + rParams(Prespoints, N_RES_POINTS, rDefaultMissing, + "Resonance data points"), + rParamZyn(PmaxdB, rShort("max"), rDefault(20), + "how many dB the signal may be amplified"), + rParamZyn(Pcenterfreq, rShort("c.freq"), rDefault(64), "Center frequency"), + rParamZyn(Poctavesfreq, rShort("oct"), rDefault(64), + "The number of octaves..."), rActioni(randomize, rMap(min,0), rMap(max, 2), "Randomize frequency response"), rActioni(interpolatepeaks, rMap(min,0), rMap(max, 2), "Generate response from peak values"), rAction(smooth, "Smooth out frequency response"), diff --git a/src/Tests/CMakeLists.txt b/src/Tests/CMakeLists.txt @@ -29,7 +29,8 @@ CXXTEST_ADD_TEST(MemoryStressTest MemoryStressTest.cpp #Extra libraries added to make test and full compilation use the same library #links for quirky compilers -set(test_lib zynaddsubfx_core ${GUI_LIBRARIES} ${ZLIB_LIBRARY} ${FFTW_LIBRARIES} ${MXML_LIBRARIES} pthread) +set(test_lib zynaddsubfx_core ${GUI_LIBRARIES} ${ZLIB_LIBRARY} ${FFTW_LIBRARIES} + ${MXML_LIBRARIES} pthread "-Wl,--no-as-needed -lpthread") message(STATUS "Linking tests with: ${test_lib}") target_link_libraries(ADnoteTest ${test_lib}) @@ -42,7 +43,7 @@ target_link_libraries(XMLwrapperTest ${test_lib}) target_link_libraries(RandTest ${test_lib}) target_link_libraries(PADnoteTest ${test_lib}) target_link_libraries(MqTest ${test_lib}) -target_link_libraries(WatchTest ${test_lib}) +target_link_libraries(WatchTest ${test_lib}) target_link_libraries(PluginTest zynaddsubfx_core zynaddsubfx_nio zynaddsubfx_gui_bridge ${GUI_LIBRARIES} ${NIO_LIBRARIES} ${AUDIO_LIBRARIES}) @@ -63,5 +64,12 @@ target_link_libraries(EffectTest ${test_lib}) add_executable(ins-test InstrumentStats.cpp) target_link_libraries(ins-test ${test_lib} rt) +add_executable(save-osc SaveOSC.cpp) +target_link_libraries(save-osc + zynaddsubfx_core zynaddsubfx_nio + zynaddsubfx_gui_bridge + ${GUI_LIBRARIES} ${NIO_LIBRARIES} ${AUDIO_LIBRARIES}) + + #message(STATUS "Plugin Test ${GUI_LIBRARIES} ${NIO_LIBRARIES} ${AUDIO_LIBRARIES}") diff --git a/src/Tests/InstrumentStats.cpp b/src/Tests/InstrumentStats.cpp @@ -22,6 +22,13 @@ #include "../Misc/Microtonal.h" #include "../DSP/FFTwrapper.h" #include "../globals.h" + +#include "../Effects/EffectMgr.h" +#include "../Params/LFOParams.h" +#include "../Params/EnvelopeParams.h" +#include "../Params/ADnoteParameters.h" +#include "../Params/PADnoteParameters.h" +#include "../Params/SUBnoteParameters.h" using namespace std; using namespace zyn; @@ -143,22 +150,250 @@ void memUsage() printf("%lld", alloc.totalAlloced()); } +/* + * Kit fields used + * + * Kit type + * + * Add synth engines used + * Add synth voices used + * Sub synth engines used + * Pad synth engines used + * + * + * Total Envelopes + * Optional Envelopes + * + * Total Free mode Envelopes + * + * Total LFO + * Optional LFO + * + * Total Filters + * + * Total 'Analog' Filters + * Total SVF Filters + * Total Formant Filters + * + * Total Effects + */ +void usage_stats(void) +{ + int kit_type = 0; + int kits_used = 0; + int add_engines = 0; + int add_voices = 0; + int sub_engines = 0; + int pad_engines = 0; + + int env_total = 0; + int env_optional = 0; + int env_free = 0; + + int lfo_total = 0; + int lfo_optional = 0; + + int filter_total = 0; + int filter_analog = 0; + int filter_svf = 0; + int filter_formant = 0; + + int effects_total = 0; + + kit_type = p->Pkitmode; + for(int i=0; i<NUM_KIT_ITEMS; ++i) { + auto &k = p->kit[i]; + if(!(k.Penabled || (i==0 && p->Pkitmode == 0l))) + continue; + + if(k.Padenabled) { + auto &e = *k.adpars; + add_engines += 1; + for(int j=0; j<NUM_VOICES; ++j) { + auto &v = k.adpars->VoicePar[j]; + if(!v.Enabled) + continue; + add_voices += 1; + if(v.PFilterEnabled) { + auto &f = *v.VoiceFilter; + filter_total += 1; + if(f.Pcategory == 0) + filter_analog += 1; + else if(f.Pcategory == 1) + filter_formant += 1; + else + filter_svf += 1; + } + if(v.PFreqLfoEnabled && v.FreqLfo->Pintensity) { + lfo_optional += 1; + lfo_total += 1; + } + if(v.PFilterLfoEnabled && v.FilterLfo->Pintensity) { + lfo_optional += 1; + lfo_total += 1; + } + if(v.PAmpLfoEnabled && v.AmpLfo->Pintensity) { + lfo_optional += 1; + lfo_total += 1; + } + if(v.PFreqEnvelopeEnabled) { + env_optional += 1; + env_total += 1; + env_free += !!v.FreqEnvelope->Pfreemode; + } + if(v.PFilterEnvelopeEnabled) { + env_optional += 1; + env_total += 1; + env_free += !!v.FilterEnvelope->Pfreemode; + } + if(v.PAmpEnvelopeEnabled) { + env_optional += 1; + env_total += 1; + env_free += !!v.AmpEnvelope->Pfreemode; + } + } + + + + if(e.GlobalPar.GlobalFilter) { + auto &f = *e.GlobalPar.GlobalFilter; + filter_total += 1; + if(f.Pcategory == 0) + filter_analog += 1; + else if(f.Pcategory == 1) + filter_formant += 1; + else + filter_svf += 1; + } + if(e.GlobalPar.FreqLfo->Pintensity) + lfo_total += 1; + if(e.GlobalPar.FilterLfo->Pintensity) + lfo_total += 1; + if(e.GlobalPar.AmpLfo->Pintensity) + lfo_total += 1; + env_total += 3; + env_free += !!e.GlobalPar.FreqEnvelope->Pfreemode; + env_free += !!e.GlobalPar.FilterEnvelope->Pfreemode; + env_free += !!e.GlobalPar.AmpEnvelope->Pfreemode; + } + + if(k.Ppadenabled) { + pad_engines += 1; + auto &e = *k.padpars; + if(e.GlobalFilter) { + auto &f = *e.GlobalFilter; + filter_total += 1; + if(f.Pcategory == 0) + filter_analog += 1; + else if(f.Pcategory == 1) + filter_formant += 1; + else + filter_svf += 1; + } + if(e.FreqLfo->Pintensity) + lfo_total += 1; + if(e.FilterLfo->Pintensity) + lfo_total += 1; + if(e.AmpLfo->Pintensity) + lfo_total += 1; + env_total += 3; + env_free += !!e.FreqEnvelope->Pfreemode; + env_free += !!e.FilterEnvelope->Pfreemode; + env_free += !!e.AmpEnvelope->Pfreemode; + } + + if(k.Psubenabled) { + sub_engines += 1; + auto &e = *k.subpars; + + if(e.PGlobalFilterEnabled) { + auto &f = *e.GlobalFilter; + filter_total += 1; + if(f.Pcategory == 0) + filter_analog += 1; + else if(f.Pcategory == 1) + filter_formant += 1; + else + filter_svf += 1; + } + if(e.PFreqEnvelopeEnabled) { + env_total += 1; + env_optional += 1; + env_free += !!e.FreqEnvelope->Pfreemode; + } + if(e.PGlobalFilterEnabled) { + env_total += 1; + env_optional += 1; + env_free += !!e.GlobalFilterEnvelope->Pfreemode; + } + if(e.PBandWidthEnvelopeEnabled) { + env_total += 1; + env_optional += 1; + env_free += !!e.BandWidthEnvelope->Pfreemode; + } + } + + kits_used += 1; + } + + for(int i=0; i<NUM_PART_EFX; ++i) { + if(p->partefx[i]->efx) + effects_total += 1; + } + + printf("Kit type: %d\n", kit_type); + printf("Kits used: %d\n", kits_used); + printf("Add engines: %d\n", add_engines); + printf(" Add voices: %d\n", add_voices); + printf("Sub engines: %d\n", sub_engines); + printf("Pad engines: %d\n", pad_engines); + + printf("\n"); + + printf("Env total: %d\n", env_total); + printf("Env optional: %d\n", env_optional); + printf("Env free: %d\n", env_free); + + printf("\n"); + + printf("LFO total: %d\n", lfo_total); + printf("LFO optional: %d\n", lfo_optional); + + printf("\n"); + + printf("Filter total: %d\n", filter_total); + printf("Filter analog: %d\n", filter_analog); + printf("Filter svf: %d\n", filter_svf); + printf("Filter formant: %d\n", filter_formant); + + printf("\n"); + + printf("Effects Total: %d\n", effects_total); +} + int main(int argc, char **argv) { - if(argc != 2) { + if(argc < 2) { fprintf(stderr, "Please supply a xiz file\n"); return 1; } - - mode = MODE_PROFILE; - setup(); - xml(argv[1]); - load(); - memUsage(); - printf(", "); - noteOn(); - speed(); - noteOff(); - memUsage(); - printf("\n"); + if(argc == 2) { + mode = MODE_PROFILE; + setup(); + xml(argv[1]); + load(); + memUsage(); + printf(", "); + noteOn(); + speed(); + noteOff(); + memUsage(); + printf("\n"); + } else if(argc == 3) { + mode = MODE_TEST; + setup(); + xml(argv[2]); + load(); + usage_stats(); + } } diff --git a/src/Tests/MessageTest.h b/src/Tests/MessageTest.h @@ -184,14 +184,48 @@ class MessageTest:public CxxTest::TestSuite mw->transmitMsg("/learn", "s", "/Pvolume"); mw->transmitMsg("/virtual_midi_cc", "iii", 0, 23, 108); + //param is at default until rt-thread is run + TS_ASSERT_EQUALS(ms->Pvolume, 80); + + //Perform a learning operation + run_realtime(); - run_realtime(); //1. runs learning and identifies a CC to bind - mw->tick(); //2. produces new binding table - run_realtime(); //3. applies new binding table + //Verify binding affects control + TS_ASSERT_EQUALS(ms->Pvolume, 108); + + printf("# Trying to save automations\n"); + start_realtime(); mw->transmitMsg("/save_xlz", "s", "test-midi-learn.xlz"); + stop_realtime(); + + //Verify that some file exists + printf("# Verifying file exists\n"); + FILE *f = fopen("test-midi-learn.xlz", "r"); + TS_ASSERT(f); + + if(f) + fclose(f); + + printf("# Clearing automation\n"); + //Clear out state + mw->transmitMsg("/clear_xlz", ""); + //Send dummy message + mw->transmitMsg("/virtual_midi_cc", "iii", 0, 23, 27); + run_realtime(); + + //Verify automation table is clear + TS_ASSERT_EQUALS(ms->Pvolume, 108); + + printf("# Loading automation\n"); mw->transmitMsg("/load_xlz", "s", "test-midi-learn.xlz"); + //Send message + mw->transmitMsg("/virtual_midi_cc", "iii", 0, 23, 28); + run_realtime(); + + //Verify automation table is restored + TS_ASSERT_EQUALS(ms->Pvolume, 28); } void testLfoPaste(void) @@ -246,7 +280,7 @@ class MessageTest:public CxxTest::TestSuite void testFilterDepricated(void) { vector<string> v = {"Pfreq", "Pfreqtrack", "Pgain", "Pq"}; - for(int i=0; i<v.size(); ++i) { + for(int i=0; i<(int)v.size(); ++i) { string path = "/part0/kit0/adpars/GlobalPar/GlobalFilter/"+v[i]; for(int j=0; j<128; ++j) { mw->transmitMsg(path.c_str(), "i", j); //Set @@ -284,6 +318,7 @@ class MessageTest:public CxxTest::TestSuite state = 0; } + (void) id; //printf("Message #%d %s:%s\n", id++, msg, rtosc_argument_string(msg)); //if(rtosc_narguments(msg)) // printf(" %d\n", rtosc_argument(msg, 0).i); diff --git a/src/Tests/PluginTest.h b/src/Tests/PluginTest.h @@ -56,7 +56,7 @@ void print_string_differences(string orig, string next) //Insertion is 2 cost and moves +2 State (+2 if symbols are different) //Deletion is 1 cost and moves +0 State (+2 if symbols are different) char *transition = new char[N*M]; - int *cost = new int[N*M]; + float *cost = new float[N*M]; const int match = 1; const int insert = 2; @@ -69,8 +69,8 @@ void print_string_differences(string orig, string next) } //Just assume the -1 line is the same - cost[0*M + 0] = (a[0] == b[0])*3; - cost[0*M + 1] = (a[1] == b[0])*2 + 2; + cost[0*M + 0] = (a[0] != b[0])*3; + cost[0*M + 1] = (a[1] != b[0])*2 + 2; for(int i=1; i<N; ++i) { for(int j=0; j<M; ++j) { int cost_match = 0xffffff; @@ -80,7 +80,7 @@ void print_string_differences(string orig, string next) if(j > 1) cost_ins = cost[(i-1)*M + (j-2)] + 1 + 2*(a[i] != b[j]); if(j > 0) - cost_match = cost[(i-1)*M + (j-1)] + 2*(a[i] != b[j]); + cost_match = cost[(i-1)*M + (j-1)] + 3*(a[i] != b[j]); if(cost_match >= 0xffff && cost_ins >= 0xffff && cost_del >= 0xffff) { ; @@ -97,28 +97,68 @@ void print_string_differences(string orig, string next) } } + //int off = 0; + //int len = N; + //for(int i=off; i<off+len; ++i) { + // for(int j=off; j<off+len; ++j) { + // printf("%4d ", (int)(cost[i*M+j] > 4000 ? -1 : cost[i*M+j])); + // } + // printf("\n"); + //} + //for(int i=off; i<off+len; ++i) { + // for(int j=off; j<off+len; ++j) { + // printf("%d ", transition[i*M+j]); + // } + // printf("\n"); + //} + + //for(int i=off; i<off+len; ++i) + // printf("%d: %s\n", i, a[i].c_str()); + //for(int i=off; i<off+len; ++i) + // printf("%d: %s\n", i, b[i].c_str()); + //exit(1); + int total_cost = cost[(N-1)*M + (M-1)]; if(total_cost < 500) { - printf("total cost = %d\n", cost[(N-1)*M + (M-1)]); + printf("total cost = %f\n", cost[(N-1)*M + (M-1)]); - //int b_pos = b.size()-1; + int b_pos = b.size()-1; int a_pos = a.size()-1; - for(int i=(M-1); i >= 0; --i) { - if(a[a_pos] != b[i]) { - printf("- %s\n", a[a_pos].c_str()); - printf("+ %s\n", b[i].c_str()); - } - if(transition[i*M+a_pos] == match) { + while(a_pos > 0 && b_pos > 0) { + //printf("state = (%d, %d) => %f\n", a_pos, b_pos, cost[a_pos*M+b_pos]); + if(transition[a_pos*M+b_pos] == match) { + if(a[a_pos] != b[b_pos]) { + printf("REF - %s\n", a[a_pos].c_str()); + printf("NEW + %s\n", b[b_pos].c_str()); + } //printf("R"); a_pos -= 1; - } else if(transition[i*M+a_pos] == del) { + b_pos -= 1; + } else if(transition[a_pos*M+b_pos] == del) { //printf("D"); - } else if(transition[i*M+a_pos] == insert) { + //if(a[a_pos] != b[b_pos]) { + //printf("- %s\n", a[a_pos].c_str()); + printf("NEW - %s\n", b[b_pos].c_str()); + //} + b_pos -= 1; + } else if(transition[a_pos*M+b_pos] == insert) { + //if(a[a_pos] != b[b_pos]) { + printf("REF - %s\n", a[a_pos].c_str()); + printf("NEW + %s\n", b[b_pos].c_str()); + printf("NEW + %s\n", b[b_pos-1].c_str()); + //} //printf("I"); - a_pos -= 2; + a_pos -= 1; + b_pos -= 2; + } else { + printf("ERROR STATE @(%d, %d)\n", a_pos, b_pos); + exit(1); } + } //printf("%d vs %d\n", N, M); + } else { + printf("[WARNING] XML File appears to be radically different\n"); } } diff --git a/src/Tests/SaveOSC.cpp b/src/Tests/SaveOSC.cpp @@ -0,0 +1,103 @@ +#include <cassert> +#include <thread> +#include <iostream> +#include <unistd.h> + +#include <cxxtest/TestSuite.h> + +#include "../Misc/Master.h" +#include "../Misc/MiddleWare.h" +#include "../UI/NSM.H" + +// for linking purposes only: +NSM_Client *nsm = 0; +zyn::MiddleWare *middleware = 0; + +char *instance_name=(char*)""; + +// Middleware is not required, since all ports requiring MiddleWare use the +// rNoWalk macro. If you still want to enable it, uncomment this: +// #define RUN_MIDDLEWARE + +class SaveOSCTest +{ + void setUp() { + synth = new zyn::SYNTH_T; + synth->buffersize = 256; + synth->samplerate = 48000; + synth->alias(); + + mw = new zyn::MiddleWare(std::move(*synth), &config); + master = mw->spawnMaster(); + realtime = nullptr; + } + + void tearDown() { + delete mw; + delete synth; + } + + public: + SaveOSCTest() { setUp(); } + ~SaveOSCTest() { tearDown(); } + + int run(int argc, char** argv) + { + assert(argc == 2); + const char *filename = argv[1]; + + int tmp = master->loadXML(filename); + if(tmp < 0) { + std::cerr << "ERROR: Could not load master file " << filename + << "." << std::endl; + exit(1); + } + + assert(master); + return (master->saveOSC(NULL) == 0) ? 0 : 1; + } + + + void start_realtime(void) + { + do_exit = false; +#ifdef RUN_MIDDLEWARE + realtime = new std::thread([this](){ + while(!do_exit) + { + /*while(bToU->hasNext()) { + const char *rtmsg = bToU->read(); + bToUhandle(rtmsg); + }*/ + mw->tick(); + usleep(500); + }}); +#endif + } + void stop_realtime(void) + { + do_exit = true; +#ifdef RUN_MIDDLEWARE + realtime->join(); + delete realtime; + realtime = NULL; +#endif + } + + private: + zyn::Config config; + zyn::SYNTH_T* synth; + zyn::MiddleWare* mw; + zyn::Master* master; + std::thread* realtime; + bool do_exit; +}; + +int main(int argc, char** argv) +{ + SaveOSCTest test; + test.start_realtime(); + int res = test.run(argc, argv); + test.stop_realtime(); + return res; +} diff --git a/src/Tests/guitar-adnote.xmz b/src/Tests/guitar-adnote.xmz @@ -2,7 +2,7 @@ <?xml version="1.0f" encoding="UTF-8"?> <!DOCTYPE ZynAddSubFX-data> <ZynAddSubFX-data version-major="3" version-minor="0" -version-revision="1" ZynAddSubFX-author="Nasca Octavian Paul"> +version-revision="2" ZynAddSubFX-author="Nasca Octavian Paul"> <INFORMATION> <par_bool name="PADsynth_used" value="yes" /> </INFORMATION> @@ -28,6 +28,9 @@ version-revision="1" ZynAddSubFX-author="Nasca Octavian Paul"> <par name="a_note" value="69" /> <par_real name="a_freq" value="440" exact_value="0x43DC0000" /> </MICROTONAL> +<automation> +<mgr-info nslots="16" nautomations="4" ncontrol="8" /> +</automation> <PART id="0"> <par_bool name="enabled" value="yes" /> <par name="volume" value="96" /> diff --git a/src/UI/BankUI.fl b/src/UI/BankUI.fl @@ -23,6 +23,9 @@ decl {\#include <FL/Fl_Button.H>} {public local decl {\#include <FL/Fl_File_Chooser.H>} {public local } +decl {\#include <FL/Fl_Input.H>} {public local +} + decl {\#include "Fl_Osc_Interface.h"} {public local } @@ -95,6 +98,15 @@ refreshmainwindow();} banklist->value(0);} tooltip {Refresh the bank list (rescan)} xywh {230 8 105 20} box THIN_UP_BOX color 50 labelsize 11 } + Fl_Input {} { + label {Search by name: } + code0 {o->when(FL_WHEN_CHANGED);} + callback { + std::string str = o->value(); + update_search(str)} + tooltip {Enter text to search for} + xywh {460 8 105 20} box THIN_UP_BOX color 50 labelsize 11 + } } } Function {BankUI(int *npart_, Fl_Osc_Interface *osc_)} {open @@ -139,6 +151,15 @@ bankview->refresh();} {} if (banklist->size() == 0) banklist->add(" ");} {} } + Function {update_search(std::string search_string)} {open + } { + code {if (search_string.empty()) { + refreshmainwindow(); +} else { + osc->write("/bank/search", "s", search_string.c_str()); +} + } {} + } decl {Fl_Osc_Interface *osc;} {private local } decl {Fl_Valuator *cbwig;} {public local diff --git a/src/UI/BankView.cpp b/src/UI/BankView.cpp @@ -225,15 +225,17 @@ void BankViewControls::mode(int m) BankView::BankView(int x,int y, int w, int h, const char *label) - :Fl_Group(x,y,w,h,label), bvc(NULL), slots{0}, osc(0), - loc(""), nselected(-1), npart(0), cbwig_(0) + :Fl_Group(x,y,w,h,label), Fl_Osc_Widget(), + bvc(NULL), slots{0}, nselected(-1), npart(0), cbwig_(0) {} BankView::~BankView(void) { - if(osc) - osc->removeLink("/bankview", this); + if(osc) { + osc->removeLink("/bankview", this); + osc->removeLink("/bank/search_results", this); + } } void BankView::init(Fl_Osc_Interface *osc_, BankViewControls *bvc_, int *npart_) @@ -245,6 +247,7 @@ void BankView::init(Fl_Osc_Interface *osc_, BankViewControls *bvc_, int *npart_) npart = npart_; osc->createLink("/bankview", this); + osc->createLink("/bank/search_results", this); //Element Size const float width = w()/5.0; @@ -346,14 +349,30 @@ void BankView::react(int event, int nslot) void BankView::OSC_raw(const char *msg) { - if(!strcmp(rtosc_argument_string(msg), "iss")) { + if(!strcmp(msg, "/bank/search_results")) { + const char *ptr = rtosc_argument_string(msg); + int slot = 0; + + while (ptr[0] == 's' && ptr[1] == 's') { + const char *bank = rtosc_argument(msg, 2*slot).s; + const char *fname = rtosc_argument(msg, 2*slot + 1).s; + + /* store search results directly into slot */ + slots[slot]->update(bank, fname); + if (++slot == 160) + break; + ptr += 2; + } + while (slot < 160) + slots[slot++]->update("", ""); + } else if(!strcmp(rtosc_argument_string(msg), "iss")) { int nslot = rtosc_argument(msg,0).i; const char *name = rtosc_argument(msg,1).s; const char *fname = rtosc_argument(msg,2).s; if(0 <= nslot && nslot < 160) slots[nslot]->update(name, fname); - } if(!strcmp(rtosc_argument_string(msg), "ss")) { + } else if(!strcmp(rtosc_argument_string(msg), "ss")) { while(*msg && !isdigit(*msg)) msg++; int nslot = atoi(msg); const char *name = rtosc_argument(msg,0).s; diff --git a/src/UI/BankView.h b/src/UI/BankView.h @@ -96,9 +96,6 @@ class BankView: public Fl_Group, public Fl_Osc_Widget BankViewControls *bvc; BankSlot *slots[160]; - Fl_Osc_Interface *osc; - std::string loc; - //XXX TODO locked banks... int nselected; int *npart; diff --git a/src/UI/Fl_Osc_Tree.H b/src/UI/Fl_Osc_Tree.H @@ -107,7 +107,7 @@ class Fl_Osc_Tree: public Fl_Tree const char *name = port.name; if(!index(name, '/'))//only accept objects that will have subports continue; - if(rtosc_match(name, s.c_str())) { + if(rtosc_match(name, s.c_str(), NULL)) { return subtree_lookup(port.ports, s.substr(index(s.c_str(), '/')-s.c_str()+1)); } diff --git a/src/main.cpp b/src/main.cpp @@ -3,7 +3,7 @@ main.cpp - Main file of the synthesizer Copyright (C) 2002-2005 Nasca Octavian Paul - Copyright (C) 2012-2016 Mark McCurry + Copyright (C) 2012-2017 Mark McCurry This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -21,7 +21,9 @@ #include <algorithm> #include <signal.h> +#ifndef WIN32 #include <err.h> +#endif #include <unistd.h> #include <pthread.h> @@ -48,8 +50,10 @@ GUI::ui_handle_t gui; #ifdef ZEST_GUI +#ifndef WIN32 #include <sys/wait.h> #endif +#endif //Glue Layer #include "Misc/MiddleWare.h" @@ -135,7 +139,9 @@ void exitprogram(const Config& config) #ifdef WIN32 #include <windows.h> #include <mmsystem.h> +namespace zyn{ extern InMgr *in; +} HMIDIIN winmidiinhandle = 0; void CALLBACK WinMidiInProc(HMIDIIN hMidiIn,UINT wMsg,DWORD dwInstance, @@ -215,7 +221,6 @@ int main(int argc, char *argv[]) { SYNTH_T synth; Config config; - config.init(); int noui = 0; cerr << "\nZynAddSubFX - Copyright (c) 2002-2013 Nasca Octavian Paul and others" @@ -313,7 +318,7 @@ int main(int argc, char *argv[]) opterr = 0; int option_index = 0, opt, exitwithhelp = 0, exitwithversion = 0; int prefered_port = -1; - int auto_save_interval = 60; + int auto_save_interval = 0; int wmidi = -1; string loadfile, loadinstrument, execAfterInit, loadmidilearn; @@ -324,7 +329,7 @@ int wmidi = -1; /**\todo check this process for a small memory leak*/ opt = getopt_long(argc, argv, - "l:L:M:r:b:o:I:O:N:e:P:A:D:hvapSDUYZ", + "l:L:M:r:b:o:I:O:N:e:P:A:d:D:hvapSDUYZ", opts, &option_index); char *optarguments = optarg; @@ -644,10 +649,13 @@ int wmidi = -1; } #ifdef ZEST_GUI +#ifndef WIN32 pid_t gui_pid = 0; +#endif if(!noui) { printf("[INFO] Launching Zyn-Fusion...\n"); const char *addr = middleware->getServerAddress(); +#ifndef WIN32 gui_pid = fork(); if(gui_pid == 0) { execlp("zyn-fusion", "zyn-fusion", addr, "--builtin", "--no-hotload", 0); @@ -655,6 +663,28 @@ int wmidi = -1; err(1,"Failed to launch Zyn-Fusion"); } +#else + STARTUPINFO si; +PROCESS_INFORMATION pi; +memset(&si, 0, sizeof(si)); +memset(&pi, 0, sizeof(pi)); +char *why_windows = strrchr(addr, ':'); +char *seriously_why = why_windows + 1; +char start_line[256] = {0}; +if(why_windows) + snprintf(start_line, sizeof(start_line), "zyn-fusion.exe osc.udp://127.0.0.1:%s", seriously_why); +else { + printf("COULD NOT PARSE <%s>\n", addr); + exit(1); +} +printf("[INFO] starting subprocess via <%s>\n", start_line); +if(!CreateProcess(NULL, start_line, +NULL, NULL, 0, 0, NULL, NULL, &si, &pi)) { + printf("Failed to launch Zyn-Fusion...\n"); + exit(1); +} + +#endif } #endif @@ -698,6 +728,7 @@ done: #endif #ifdef ZEST_GUI +#ifndef WIN32 if(!noui) { int status = 0; int ret = waitpid(gui_pid, &status, WNOHANG); @@ -705,6 +736,7 @@ done: Pexitprogram = 1; } #endif +#endif } exitprogram(config); diff --git a/zynaddsubfx-oss.desktop b/zynaddsubfx-oss.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=ZynAddSubFX - OSS +Comment=A powerful realtime software synthesizer +Comment[fr]=Un synthétiseur logiciel temps-réel puissant +Keywords=audio;sound;alsa;midi;synth;synthesizer; +Exec=zynaddsubfx -I OSS -O OSS +Icon=zynaddsubfx +Terminal=false +Type=Application +Categories=AudioVideo;Audio;