zynaddsubfx

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

commit 608620d75b967b27553f770e7c7b2ca372eb7e9c
parent c165dab6ba8a661183c72aacc1d77c690332c191
Author: Johannes Lorenz <[email protected]>
Date:   Mon,  4 Jun 2018 21:30:46 +0200

Merge branch 'master' of github.com:zynaddsubfx/zynaddsubfx

Diffstat:
M.gitmodules | 2+-
MCMakeLists.txt | 2+-
DTODO-default-values.txt | 16----------------
Msrc/DSP/SVFilter.cpp | 65+++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/DSP/SVFilter.h | 5++++-
Msrc/Misc/MiddleWare.cpp | 80++++++++++++++++++++-----------------------------------------------------------
Msrc/Params/ADnoteParameters.cpp | 2+-
Msrc/Params/ADnoteParameters.h | 4++--
Msrc/Params/PADnoteParameters.cpp | 3++-
Msrc/Synth/ADnote.cpp | 18+++++++++---------
Msrc/Synth/ADnote.h | 8++++----
Msrc/Tests/CMakeLists.txt | 14++++++++++++--
Asrc/Tests/check-ports.rb | 16++++++++++++++++
Msrc/main.cpp | 12+++++++++++-
Azynaddsubfx-jack-multi.desktop | 11+++++++++++
15 files changed, 145 insertions(+), 113 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -7,4 +7,4 @@ branch = default_values [submodule "DPF"] path = DPF - url = git://github.com/DISTRHO/DPF + url = https://github.com/DISTRHO/DPF diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -38,7 +38,7 @@ 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 zynaddsubfx-oss.desktop +install(FILES zynaddsubfx-jack-multi.desktop zynaddsubfx-jack.desktop zynaddsubfx-alsa.desktop zynaddsubfx-oss.desktop DESTINATION share/applications) install(FILES zynaddsubfx.svg DESTINATION share/pixmaps) diff --git a/TODO-default-values.txt b/TODO-default-values.txt @@ -1,16 +0,0 @@ -TODOs for default values: -* use # for bundles, not a -* 1 ... 7 => 1 2 ... 7 -* also after scanning? - -* rEnabled vs rExists: - * clarify/rename - * make a clean function? - -test: -* zyn fx (all presets) -* rtosc arg val maths - -finally: -* remove rDefaultMissing if possible -* fix all TODOs from default_values diff --git a/src/DSP/SVFilter.cpp b/src/DSP/SVFilter.cpp @@ -26,6 +26,12 @@ namespace zyn { +enum FilterInterpolationType { + INTERPOLATE_EXTREME = 0x01, + INTERPOLATE_NON_ZERO, + INTERPOLATE_NONE +}; + SVFilter::SVFilter(unsigned char Ftype, float Ffreq, float Fq, unsigned char Fstages, unsigned int srate, int bufsize) :Filter(srate, bufsize), @@ -34,7 +40,7 @@ SVFilter::SVFilter(unsigned char Ftype, float Ffreq, float Fq, freq(Ffreq), q(Fq), gain(1.0f), - needsinterpolation(false), + needsinterpolation(INTERPOLATE_NONE), firsttime(true) { if(stages >= MAX_FILTER_STAGES) @@ -100,6 +106,8 @@ SVFilter::response SVFilter::computeResponse(int type, } } + + void SVFilter::computefiltercoefs(void) { par.f = freq / samplerate_f * 4.0f; @@ -127,8 +135,14 @@ void SVFilter::setfreq(float frequency) //if the frequency is changed fast, it needs interpolation if((rap > 3.0f) || nyquistthresh) { //(now, filter and coefficients backup) if(!firsttime) - needsinterpolation = true; + needsinterpolation = INTERPOLATE_EXTREME; + ipar = par; + } else if(rap != 1.0) { + if (!firsttime) + needsinterpolation = INTERPOLATE_NON_ZERO; ipar = par; + } else { + needsinterpolation = INTERPOLATE_NONE; } freq = frequency; computefiltercoefs(); @@ -168,8 +182,7 @@ void SVFilter::setstages(int stages_) computefiltercoefs(); } -void SVFilter::singlefilterout(float *smp, fstage &x, parameters &par) -{ +float *SVFilter::getfilteroutfortype(SVFilter::fstage &x) { float *out = NULL; switch(type) { case 0: @@ -188,11 +201,20 @@ void SVFilter::singlefilterout(float *smp, fstage &x, parameters &par) out = &x.low; warnx("Impossible SVFilter type encountered [%d]", type); } + return out; +} +void SVFilter::singlefilterout_with_par_interpolation(float *smp, fstage &x, parameters &par1, parameters &par2) +{ + float *out = getfilteroutfortype(x); for(int i = 0; i < buffersize; ++i) { - x.low = x.low + par.f * x.band; - x.high = par.q_sqrt * smp[i] - x.low - par.q * x.band; - x.band = par.f * x.high + x.band; + float p = i / buffersize_f; + float f = par1.f + (par2.f - par1.f) * p; + float q = par1.q + (par2.q - par1.q) * p; + float q_sqrt = sqrtf(q); + x.low = x.low + f * x.band; + x.high = q_sqrt * smp[i] - x.low - q * x.band; + x.band = f * x.high + x.band; x.notch = x.high + x.low; smp[i] = *out; } @@ -209,23 +231,38 @@ void SVFilter::singlefilterout(float *smp, fstage &x, parameters &par) // xl = pf*pfxh*z(-1)/(1-z(-1))^2 -void SVFilter::filterout(float *smp) + +void SVFilter::singlefilterout(float *smp, SVFilter::fstage &x, SVFilter::parameters &par) { - for(int i = 0; i < stages + 1; ++i) - singlefilterout(smp, st[i], par); + float *out = getfilteroutfortype(x); + for(int i = 0; i < buffersize; ++i) { + x.low = x.low + par.f * x.band; + x.high = par.q_sqrt * smp[i] - x.low - par.q * x.band; + x.band = par.f * x.high + x.band; + x.notch = x.high + x.low; + smp[i] = *out; + } +} - if(needsinterpolation) { +void SVFilter::filterout(float *smp) +{ + if (needsinterpolation == INTERPOLATE_EXTREME) { float ismp[buffersize]; + for(int i = 0; i < stages + 1; ++i) + singlefilterout(smp, st[i], par); memcpy(ismp, smp, bufferbytes); - for(int i = 0; i < stages + 1; ++i) singlefilterout(ismp, st[i], ipar); - for(int i = 0; i < buffersize; ++i) { float x = i / buffersize_f; smp[i] = ismp[i] * (1.0f - x) + smp[i] * x; } - needsinterpolation = false; + } else if (needsinterpolation == INTERPOLATE_NON_ZERO) { + for(int i = 0; i < stages + 1; ++i) + singlefilterout_with_par_interpolation(smp, st[i], ipar, par); + } else { + for(int i = 0; i < stages + 1; ++i) + singlefilterout(smp, st[i], par); } for(int i = 0; i < buffersize; ++i) diff --git a/src/DSP/SVFilter.h b/src/DSP/SVFilter.h @@ -56,7 +56,9 @@ class SVFilter:public Filter float f, q, q_sqrt; } par, ipar; + float *getfilteroutfortype(SVFilter::fstage &x); void singlefilterout(float *smp, fstage &x, parameters &par); + void singlefilterout_with_par_interpolation(float *smp, fstage &x, parameters &par1, parameters &par2); void computefiltercoefs(void); int type; // The type of the filter (LPF1,HPF1,LPF2,HPF2...) int stages; // how many times the filter is applied (0->1,1->2,etc.) @@ -66,7 +68,8 @@ class SVFilter:public Filter bool abovenq, //if the frequency is above the nyquist oldabovenq; - bool needsinterpolation, firsttime; + int needsinterpolation; + bool firsttime; }; } diff --git a/src/Misc/MiddleWare.cpp b/src/Misc/MiddleWare.cpp @@ -94,64 +94,6 @@ static void liblo_error_cb(int i, const char *m, const char *loc) fprintf(stderr, "liblo :-( %d-%s@%s\n",i,m,loc); } -void path_search(const char *m, const char *url) -{ - using rtosc::Ports; - using rtosc::Port; - - //assumed upper bound of 32 ports (may need to be resized) - char types[256+1]; - rtosc_arg_t args[256]; - size_t pos = 0; - const Ports *ports = NULL; - const char *str = rtosc_argument(m,0).s; - const char *needle = rtosc_argument(m,1).s; - - //zero out data - memset(types, 0, sizeof(types)); - memset(args, 0, sizeof(args)); - - if(!*str) { - ports = &Master::ports; - } else { - const Port *port = Master::ports.apropos(rtosc_argument(m,0).s); - if(port) - ports = port->ports; - } - - if(ports) { - //RTness not confirmed here - for(const Port &p:*ports) { - if(strstr(p.name, needle) != p.name || !p.name) - continue; - types[pos] = 's'; - args[pos++].s = p.name; - types[pos] = 'b'; - if(p.metadata && *p.metadata) { - args[pos].b.data = (unsigned char*) p.metadata; - auto tmp = rtosc::Port::MetaContainer(p.metadata); - args[pos++].b.len = tmp.length(); - } else { - args[pos].b.data = (unsigned char*) NULL; - args[pos++].b.len = 0; - } - } - } - - - //Reply to requester [wow, these messages are getting huge...] - char buffer[1024*20]; - size_t length = rtosc_amessage(buffer, sizeof(buffer), "/paths", types, args); - if(length) { - lo_message msg = lo_message_deserialise((void*)buffer, length, NULL); - lo_address addr = lo_address_new_from_url(url); - if(addr) - lo_send_message(addr, buffer, msg); - lo_address_free(addr); - lo_message_free(msg); - } -} - static int handler_function(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data) { @@ -173,8 +115,26 @@ static int handler_function(const char *path, const char *types, lo_arg **argv, memset(buffer, 0, sizeof(buffer)); size_t size = 2048; lo_message_serialise(msg, path, buffer, &size); - if(!strcmp(buffer, "/path-search") && !strcmp("ss", rtosc_argument_string(buffer))) { - path_search(buffer, mw->activeUrl().c_str()); + + if(!strcmp(buffer, "/path-search") && + !strcmp("ss", rtosc_argument_string(buffer))) { + auto reply_cb = [](const char* url, const char* types, const rtosc_arg_t* args) + { + char buffer[1024*20]; + size_t length = rtosc_amessage(buffer, sizeof(buffer), + "/paths", types, args); + if(length) { + lo_message msg = lo_message_deserialise((void*)buffer, + length, NULL); + lo_address addr = lo_address_new_from_url(url); + if(addr) + lo_send_message(addr, buffer, msg); + lo_address_free(addr); + lo_message_free(msg); + } + }; + rtosc::path_search(Master::ports, buffer, mw->activeUrl().c_str(), + reply_cb); } else if(buffer[0]=='/' && strrchr(buffer, '/')[1]) { mw->transmitMsg(rtosc::Ports::collapsePath(buffer)); } diff --git a/src/Params/ADnoteParameters.cpp b/src/Params/ADnoteParameters.cpp @@ -153,7 +153,7 @@ static const Ports voicePorts = { //Modulator Stuff - rOption(PFMEnabled, rShort("mode"), rOptions(none, morph, ring, phase, + rOption(PFMEnabled, rShort("mode"), rOptions(none, mix, ring, phase, frequency, pulse), rDefault(none), "Modulator mode"), rParamI(PFMVoice, rShort("voice"), rDefault(-1), "Modulator Oscillator Selection"), diff --git a/src/Params/ADnoteParameters.h b/src/Params/ADnoteParameters.h @@ -20,7 +20,7 @@ namespace zyn { enum FMTYPE { - NONE, MORPH, RING_MOD, PHASE_MOD, FREQ_MOD, PW_MOD + NONE, MIX, RING_MOD, PHASE_MOD, FREQ_MOD, PW_MOD }; /*****************************************************************/ @@ -258,7 +258,7 @@ struct ADnoteVoiceParam { * MODULLATOR PARAMETERS * ****************************/ - /* Modullator Parameters (0=off,1=Morph,2=RM,3=PM,4=FM.. */ + /* Modullator Parameters (0=off,1=Mix,2=RM,3=PM,4=FM.. */ unsigned char PFMEnabled; /* Voice that I use as modullator instead of FMSmp. diff --git a/src/Params/PADnoteParameters.cpp b/src/Params/PADnoteParameters.cpp @@ -231,7 +231,8 @@ static const rtosc::Ports non_realtime_ports = 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, + {"Pbandwidth::i", rShort("bandwidth") rProp(parameter) rLinear(0,1000) + rDefault(500) rDoc("Bandwith Of Harmonics"), NULL, [](const char *msg, rtosc::RtData &d) { PADnoteParameters *p = ((PADnoteParameters*)d.obj); if(rtosc_narguments(msg)) { diff --git a/src/Synth/ADnote.cpp b/src/Synth/ADnote.cpp @@ -422,7 +422,7 @@ void ADnote::setupVoiceMod(int nvoice, bool first_run) else switch(param.PFMEnabled) { case 1: - voice.FMEnabled = MORPH; + voice.FMEnabled = MIX; break; case 2: voice.FMEnabled = RING_MOD; @@ -453,7 +453,7 @@ void ADnote::setupVoiceMod(int nvoice, bool first_run) float tmp = 1.0f; if((pars.VoicePar[vc].FMSmp->Padaptiveharmonics != 0) - || (voice.FMEnabled == MORPH) + || (voice.FMEnabled == MIX) || (voice.FMEnabled == RING_MOD)) tmp = getFMvoicebasefreq(nvoice); @@ -730,7 +730,7 @@ void ADnote::legatonote(LegatoParams lpars) && (NoteVoicePar[nvoice].FMVoice < 0)) { pars.VoicePar[nvoice].FMSmp->newrandseed(prng()); - //Perform Anti-aliasing only on MORPH or RING MODULATION + //Perform Anti-aliasing only on MIX or RING MODULATION int vc = nvoice; if(pars.VoicePar[nvoice].PextFMoscil != -1) @@ -920,7 +920,7 @@ void ADnote::initparameters(WatchManager *wm, const char *prefix) param.FMSmp->newrandseed(prng()); vce.FMSmp = memory.valloc<float>(synth.oscilsize + OSCIL_SMP_EXTRA_SAMPLES); - //Perform Anti-aliasing only on MORPH or RING MODULATION + //Perform Anti-aliasing only on MIX or RING MODULATION int vc = nvoice; if(param.PextFMoscil != -1) @@ -928,7 +928,7 @@ void ADnote::initparameters(WatchManager *wm, const char *prefix) float tmp = 1.0f; if((pars.VoicePar[vc].FMSmp->Padaptiveharmonics != 0) - || (vce.FMEnabled == MORPH) + || (vce.FMEnabled == MIX) || (vce.FMEnabled == RING_MOD)) tmp = getFMvoicebasefreq(nvoice); @@ -1301,9 +1301,9 @@ inline void ADnote::ComputeVoiceOscillator_CubicInterpolation(int nvoice){ }; */ /* - * Computes the Oscillator (Morphing) + * Computes the Oscillator (Mixing) */ -inline void ADnote::ComputeVoiceOscillatorMorph(int nvoice) +inline void ADnote::ComputeVoiceOscillatorMix(int nvoice) { ComputeVoiceOscillator_LinearInterpolation(nvoice); if(FMnewamplitude[nvoice] > 1.0f) @@ -1617,8 +1617,8 @@ int ADnote::noteout(float *outl, float *outr) switch (NoteVoicePar[nvoice].noisetype) { case 0: //voice mode=sound switch(NoteVoicePar[nvoice].FMEnabled) { - case MORPH: - ComputeVoiceOscillatorMorph(nvoice); + case MIX: + ComputeVoiceOscillatorMix(nvoice); break; case RING_MOD: ComputeVoiceOscillatorRingModulation(nvoice); diff --git a/src/Synth/ADnote.h b/src/Synth/ADnote.h @@ -86,9 +86,9 @@ class ADnote:public SynthNote * Affects tmpwave_unison and updates oscposhi/oscposlo * @todo remove this declaration if it is commented out*/ inline void ComputeVoiceOscillator_CubicInterpolation(int nvoice); - /**Computes the Oscillator samples with morphing. + /**Computes the Oscillator samples with mixing. * updates tmpwave_unison*/ - inline void ComputeVoiceOscillatorMorph(int nvoice); + inline void ComputeVoiceOscillatorMix(int nvoice); /**Computes the Ring Modulated Oscillator.*/ inline void ComputeVoiceOscillatorRingModulation(int nvoice); /**Computes the Frequency Modulated Oscillator. @@ -255,8 +255,8 @@ class ADnote:public SynthNote /* INTERNAL VALUES OF THE NOTE AND OF THE VOICES */ /********************************************************/ - //pinking filter (Paul Kellet) - float pinking[NUM_VOICES][14]; + //pinking filter (Paul Kellet) + float pinking[NUM_VOICES][14]; //the size of unison for a single voice int unison_size[NUM_VOICES]; diff --git a/src/Tests/CMakeLists.txt b/src/Tests/CMakeLists.txt @@ -1,3 +1,8 @@ +function(cp_script script_name) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${script_name} + ${CMAKE_CURRENT_BINARY_DIR}/${script_name} COPYONLY) +endfunction() + #for tests looking for files stored in the source dir add_definitions(-DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") @@ -64,13 +69,18 @@ target_link_libraries(EffectTest ${test_lib}) add_executable(ins-test InstrumentStats.cpp) target_link_libraries(ins-test ${test_lib} rt) +if(LIBLO_FOUND) + cp_script(check-ports.rb) + add_test(PortChecker check-ports.rb) +endif() + add_executable(save-osc SaveOSC.cpp) target_link_libraries(save-osc zynaddsubfx_core zynaddsubfx_nio zynaddsubfx_gui_bridge ${GUI_LIBRARIES} ${NIO_LIBRARIES} ${AUDIO_LIBRARIES}) -#this will be replace with a for loop when the code will get more stable: -add_test(SaveOsc save-osc ../../../instruments/examples/Arpeggio\ 1.xmz) +#this will be replaced with a for loop when the code will get more stable: +add_test(SaveOsc save-osc ${CMAKE_CURRENT_SOURCE_DIR}/../../instruments/examples/Arpeggio\ 1.xmz) #message(STATUS "Plugin Test ${GUI_LIBRARIES} ${NIO_LIBRARIES} ${AUDIO_LIBRARIES}") diff --git a/src/Tests/check-ports.rb b/src/Tests/check-ports.rb @@ -0,0 +1,16 @@ +#!/usr/bin/ruby + +require 'open3' + +# start zyn, grep the lo server port, and connect the port checker to it +Open3.popen3(Dir.pwd + "/../zynaddsubfx -O null --no-gui") do |stdin, stdout, stderr, wait_thr| + pid = wait_thr[:pid] + while line=stderr.gets do + # print "line: " + line; + if /^lo server running on (\d+)$/.match(line) then + rval = system(Dir.pwd + "/../../rtosc/port-checker 'osc.udp://localhost:" + $1 + "/'") + Process.kill("KILL", pid) + exit rval + end + end +end diff --git a/src/main.cpp b/src/main.cpp @@ -28,6 +28,9 @@ #include <getopt.h> +#include <sys/types.h> +#include <signal.h> + #include <rtosc/rtosc.h> #include <rtosc/ports.h> #include "Params/PADnoteParameters.h" @@ -735,7 +738,14 @@ done: #endif #endif } - +#ifdef ZEST_GUI +#ifndef WIN32 + int ret = kill(gui_pid, SIGHUP); + if (ret == -1) { + err(1, "Failed to terminate Zyn-Fusion...\n"); + } +#endif +#endif exitprogram(config); return 0; } diff --git a/zynaddsubfx-jack-multi.desktop b/zynaddsubfx-jack-multi.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=ZynAddSubFX - Jack (multi channel) +Comment=A powerful realtime software synthesizer +Comment[fr]=Un synthétiseur logiciel temps-réel puissant +Comment[pl]=Funkcjonalny syntezator wirtualny czasu rzeczywistego +Keywords=audio;sound;jack;midi;synth;synthesizer; +Exec=zynaddsubfx -I jack -O jack-multi +Icon=zynaddsubfx +Terminal=false +Type=Application +Categories=AudioVideo;Audio;