@@ -67,7 +67,10 @@ extern "C" { | |||
#include "zynaddsubfx/rtosc/cpp/undo-history.cpp" | |||
// zynaddsubfx includes | |||
#include "zynaddsubfx/version.cpp" | |||
#include "zynaddsubfx/Containers/MultiPseudoStack.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -75,6 +78,15 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Containers/NotePool.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
#undef rChangeCb | |||
#define rChangeCb | |||
#include "zynaddsubfx/Containers/ScratchString.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -82,6 +94,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/DSP/AnalogFilter.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -89,6 +102,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/DSP/FFTwrapper.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -96,6 +110,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/DSP/Filter.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -103,6 +118,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/DSP/FormantFilter.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -110,6 +126,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/DSP/SVFilter.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -117,6 +134,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/DSP/Unison.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -124,6 +142,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Effects/Alienwah.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -131,6 +150,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Effects/Chorus.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -138,6 +158,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Effects/Distorsion.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -145,6 +166,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Effects/DynamicFilter.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -153,6 +175,7 @@ extern "C" { | |||
#include "zynaddsubfx/Effects/Echo.cpp" | |||
#undef MAX_DELAY | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -160,6 +183,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Effects/Effect.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -167,6 +191,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Effects/EffectLFO.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -174,6 +199,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Effects/EffectMgr.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -181,6 +207,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Effects/EQ.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -188,16 +215,18 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Effects/Phaser.cpp" | |||
#undef PHASER_LFO_SHAPE | |||
#undef ONE_ | |||
#undef ZERO_ | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
#undef rChangeCb | |||
#define rChangeCb | |||
#undef PHASER_LFO_SHAPE | |||
#undef ONE_ | |||
#undef ZERO_ | |||
#include "zynaddsubfx/Effects/Reverb.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -205,6 +234,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Misc/Allocator.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -214,6 +244,15 @@ extern "C" { | |||
#include "zynaddsubfx/Misc/Bank.cpp" | |||
#undef INSTRUMENT_EXTENSION | |||
#undef FORCE_BANK_DIR_FILE | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
#undef rChangeCb | |||
#define rChangeCb | |||
#include "zynaddsubfx/Misc/BankDb.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -221,6 +260,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Misc/CallbackRepeater.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -228,6 +268,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Misc/Config.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -243,6 +284,7 @@ extern "C" { | |||
#include "zynaddsubfx/Misc/Microtonal.cpp" | |||
#undef MAX_LINE_SIZE | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -250,6 +292,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Misc/MiddleWare.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -258,6 +301,7 @@ extern "C" { | |||
#include "zynaddsubfx/Misc/Part.cpp" | |||
#undef CLONE | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -265,6 +309,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Misc/PresetExtractor.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -272,6 +317,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Misc/Recorder.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -279,6 +325,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Misc/Util.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -286,6 +333,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Misc/WavFile.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -293,6 +341,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Misc/WaveShapeSmps.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -300,6 +349,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Misc/XMLwrapper.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -308,6 +358,7 @@ extern "C" { | |||
#include "zynaddsubfx/Params/ADnoteParameters.cpp" | |||
#undef EXPAND | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -315,6 +366,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Params/Controller.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -322,6 +374,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Params/EnvelopeParams.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -329,6 +382,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Params/FilterParams.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -336,6 +390,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Params/LFOParams.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -345,6 +400,7 @@ extern "C" { | |||
#include "zynaddsubfx/Params/PADnoteParameters.cpp" | |||
#undef PC | |||
#undef P_C | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -352,6 +408,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Params/Presets.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -359,6 +416,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Params/PresetsArray.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -366,6 +424,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Params/PresetsStore.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -375,6 +434,7 @@ extern "C" { | |||
#include "zynaddsubfx/Params/SUBnoteParameters.cpp" | |||
#undef doPaste | |||
#undef doPPaste | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -382,6 +442,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Synth/ADnote.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -389,6 +450,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Synth/Envelope.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -396,6 +458,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Synth/LFO.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -403,6 +466,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Synth/ModFilter.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -416,6 +480,7 @@ extern "C" { | |||
#undef RESTORE | |||
#undef FUNC | |||
#undef FILTER | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -423,6 +488,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Synth/PADnote.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -430,6 +496,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Synth/Resonance.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -437,6 +504,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Synth/SUBnote.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -444,6 +512,15 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/Synth/SynthNote.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
#undef rChangeCb | |||
#define rChangeCb | |||
#include "zynaddsubfx/Synth/WatchPoint.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -451,6 +528,7 @@ extern "C" { | |||
#define rChangeCb | |||
#include "zynaddsubfx/UI/ConnectionDummy.cpp" | |||
#undef rBegin | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
@@ -28,6 +28,11 @@ LockFreeQueue::LockFreeQueue(qli_t *data_, int n) | |||
tag[i] = INVALID; | |||
} | |||
LockFreeQueue::~LockFreeQueue(void) | |||
{ | |||
delete [] tag; | |||
} | |||
qli_t *LockFreeQueue::read(void) { | |||
retry: | |||
@@ -34,6 +34,7 @@ class LockFreeQueue | |||
std::atomic<int32_t> avail; | |||
public: | |||
LockFreeQueue(qli_t *data_, int n); | |||
~LockFreeQueue(void); | |||
qli_t *read(void); | |||
void write(qli_t *Q); | |||
}; | |||
@@ -0,0 +1,39 @@ | |||
#include "ScratchString.h" | |||
#include <cstring> | |||
#include <cstdio> | |||
ScratchString::ScratchString(void) | |||
{ | |||
memset(c_str, 0, sizeof(c_str)); | |||
} | |||
ScratchString::ScratchString(int num) | |||
{ | |||
snprintf(c_str, SCRATCH_SIZE, "%d", num); | |||
} | |||
ScratchString::ScratchString(unsigned char num) | |||
{ | |||
snprintf(c_str, SCRATCH_SIZE, "%d", num); | |||
} | |||
ScratchString::ScratchString(const char *str) | |||
{ | |||
if(str) | |||
strncpy(c_str, str, SCRATCH_SIZE); | |||
else | |||
memset(c_str, 0, sizeof(c_str)); | |||
} | |||
ScratchString ScratchString::operator+(const ScratchString s) | |||
{ | |||
ScratchString ss; | |||
strncpy(ss.c_str, c_str, SCRATCH_SIZE); | |||
strncat(ss.c_str, s.c_str, SCRATCH_SIZE-strlen(c_str)); | |||
return ss; | |||
} | |||
//ScratchString::operator const char*() const | |||
//{ | |||
// return c_str; | |||
//} |
@@ -0,0 +1,17 @@ | |||
#pragma once | |||
#define SCRATCH_SIZE 128 | |||
//Fixed Size String Substitute | |||
struct ScratchString | |||
{ | |||
ScratchString(void); | |||
ScratchString(int num); | |||
ScratchString(unsigned char num); | |||
ScratchString(const char *str); | |||
ScratchString operator+(const ScratchString s); | |||
//operator const char*() const; | |||
char c_str[SCRATCH_SIZE]; | |||
}; |
@@ -61,28 +61,35 @@ void AnalogFilter::cleanup() | |||
needsinterpolation = false; | |||
} | |||
void AnalogFilter::computefiltercoefs(void) | |||
AnalogFilter::Coeff AnalogFilter::computeCoeff(int type, float cutoff, float q, | |||
int stages, float gain, float fs, int &order) | |||
{ | |||
float tmp; | |||
AnalogFilter::Coeff coeff; | |||
bool zerocoefs = false; //this is used if the freq is too high | |||
const float samplerate_f = fs; | |||
const float halfsamplerate_f = fs/2; | |||
//do not allow frequencies bigger than samplerate/2 | |||
float freq = this->freq; | |||
float freq = cutoff; | |||
if(freq > (halfsamplerate_f - 500.0f)) { | |||
freq = halfsamplerate_f - 500.0f; | |||
zerocoefs = true; | |||
} | |||
if(freq < 0.1f) | |||
freq = 0.1f; | |||
//do not allow bogus Q | |||
if(q < 0.0f) | |||
q = 0.0f; | |||
float tmpq, tmpgain; | |||
if(stages == 0) { | |||
tmpq = q; | |||
tmpgain = gain; | |||
} | |||
else { | |||
} else { | |||
tmpq = (q > 1.0f) ? powf(q, 1.0f / (stages + 1)) : q; | |||
tmpgain = powf(gain, 1.0f / (stages + 1)); | |||
} | |||
@@ -100,6 +107,9 @@ void AnalogFilter::computefiltercoefs(void) | |||
//the "Cookbook formulae for audio EQ" by Robert Bristow-Johnson | |||
//The original location of the Cookbook is: | |||
//http://www.harmony-central.com/Computer/Programming/Audio-EQ-Cookbook.txt | |||
float tmp; | |||
float tgp1; | |||
float tgm1; | |||
switch(type) { | |||
case 0: //LPF 1 pole | |||
if(!zerocoefs) | |||
@@ -205,20 +215,15 @@ void AnalogFilter::computefiltercoefs(void) | |||
if(!zerocoefs) { | |||
tmpq = sqrtf(tmpq); | |||
beta = sqrtf(tmpgain) / tmpq; | |||
tmp = (tmpgain + 1.0f) + (tmpgain - 1.0f) * cs + beta * sn; | |||
c[0] = tmpgain | |||
* ((tmpgain | |||
+ 1.0f) - (tmpgain - 1.0f) * cs + beta * sn) / tmp; | |||
c[1] = 2.0f * tmpgain | |||
* ((tmpgain - 1.0f) - (tmpgain + 1.0f) * cs) / tmp; | |||
c[2] = tmpgain | |||
* ((tmpgain | |||
+ 1.0f) - (tmpgain - 1.0f) * cs - beta * sn) / tmp; | |||
d[1] = -2.0f * ((tmpgain - 1.0f) + (tmpgain + 1.0f) * cs) | |||
/ tmp * -1.0f; | |||
d[2] = ((tmpgain + 1.0f) + (tmpgain - 1.0f) * cs - beta * sn) | |||
/ tmp * -1.0f; | |||
tgp1 = tmpgain + 1.0f; | |||
tgm1 = tmpgain - 1.0f; | |||
tmp = tgp1 + tgm1 * cs + beta * sn; | |||
c[0] = tmpgain * (tgp1 - tgm1 * cs + beta * sn) / tmp; | |||
c[1] = 2.0f * tmpgain * (tgm1 - tgp1 * cs) / tmp; | |||
c[2] = tmpgain * (tgp1 - tgm1 * cs - beta * sn) / tmp; | |||
d[1] = -2.0f * (tgm1 + tgp1 * cs) / tmp * -1.0f; | |||
d[2] = (tgp1 + tgm1 * cs - beta * sn) / tmp * -1.0f; | |||
} | |||
else { | |||
c[0] = tmpgain; | |||
@@ -230,20 +235,15 @@ void AnalogFilter::computefiltercoefs(void) | |||
if(!zerocoefs) { | |||
tmpq = sqrtf(tmpq); | |||
beta = sqrtf(tmpgain) / tmpq; | |||
tmp = (tmpgain + 1.0f) - (tmpgain - 1.0f) * cs + beta * sn; | |||
c[0] = tmpgain | |||
* ((tmpgain | |||
+ 1.0f) + (tmpgain - 1.0f) * cs + beta * sn) / tmp; | |||
c[1] = -2.0f * tmpgain | |||
* ((tmpgain - 1.0f) + (tmpgain + 1.0f) * cs) / tmp; | |||
c[2] = tmpgain | |||
* ((tmpgain | |||
+ 1.0f) + (tmpgain - 1.0f) * cs - beta * sn) / tmp; | |||
d[1] = 2.0f * ((tmpgain - 1.0f) - (tmpgain + 1.0f) * cs) | |||
/ tmp * -1.0f; | |||
d[2] = ((tmpgain + 1.0f) - (tmpgain - 1.0f) * cs - beta * sn) | |||
/ tmp * -1.0f; | |||
tgp1 = tmpgain + 1.0f; | |||
tgm1 = tmpgain - 1.0f; | |||
tmp = tgp1 - tgm1 * cs + beta * sn; | |||
c[0] = tmpgain * (tgp1 + tgm1 * cs + beta * sn) / tmp; | |||
c[1] = -2.0f * tmpgain * (tgm1 + tgp1 * cs) / tmp; | |||
c[2] = tmpgain * (tgp1 + tgm1 * cs - beta * sn) / tmp; | |||
d[1] = 2.0f * (tgm1 - tgp1 * cs) / tmp * -1.0f; | |||
d[2] = (tgp1 - tgm1 * cs - beta * sn) / tmp * -1.0f; | |||
} | |||
else { | |||
c[0] = 1.0f; | |||
@@ -252,10 +252,16 @@ void AnalogFilter::computefiltercoefs(void) | |||
order = 2; | |||
break; | |||
default: //wrong type | |||
type = 0; | |||
computefiltercoefs(); | |||
assert(false && "wrong type for a filter"); | |||
break; | |||
} | |||
return coeff; | |||
} | |||
void AnalogFilter::computefiltercoefs(void) | |||
{ | |||
coeff = AnalogFilter::computeCoeff(type, freq, q, stages, gain, | |||
samplerate_f, order); | |||
} | |||
@@ -46,6 +46,9 @@ class AnalogFilter:public Filter | |||
d[3]; //Feed Back | |||
} coeff, oldCoeff; | |||
static Coeff computeCoeff(int type, float cutoff, float q, int stages, | |||
float gain, float fs, int &order); | |||
private: | |||
struct fstage { | |||
float x1, x2; //Input History | |||
@@ -12,11 +12,37 @@ | |||
*/ | |||
#include <cmath> | |||
#include <rtosc/port-sugar.h> | |||
#include <rtosc/ports.h> | |||
#include "../Misc/Allocator.h" | |||
#include "Alienwah.h" | |||
using std::complex; | |||
#define rObject Alienwah | |||
#define rBegin [](const char *, rtosc::RtData &) { | |||
#define rEnd } | |||
rtosc::Ports Alienwah::ports = { | |||
{"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) | |||
rDoc("Instrument Presets"), 0, | |||
rBegin; | |||
rEnd}, | |||
//Pvolume/Ppanning are common | |||
rEffPar(Pfreq, 2, rShort("freq"), "Effect Frequency"), | |||
rEffPar(Pfreqrnd, 3, rShort("rand"), "Frequency Randomness"), | |||
rEffPar(PLFOtype, 4, rShort("shape"), "LFO Shape"), | |||
rEffParTF(PStereo, 5, rShort("stereo"), "Stereo/Mono Mode"), | |||
rEffPar(Pdepth, 6, rShort("depth"), "LFO Depth"), | |||
rEffPar(Pfeedback, 7, rShort("fb"), "Feedback"), | |||
rEffPar(Pdelay, 8, rShort("delay"), "Delay"), | |||
rEffPar(Plrcross, 9, rShort("l/r"), "Left/Right Crossover"), | |||
rEffPar(Pphase, 10, rShort("phase"), "Phase"), | |||
}; | |||
#undef rBegin | |||
#undef rEnd | |||
#undef rObject | |||
Alienwah::Alienwah(EffectParams pars) | |||
:Effect(pars), | |||
lfo(pars.srate, pars.bufsize), | |||
@@ -33,6 +33,7 @@ class Alienwah:public Effect | |||
unsigned char getpar(int npar) const; | |||
void cleanup(void); | |||
static rtosc::Ports ports; | |||
private: | |||
//Alienwah Parameters | |||
EffectLFO lfo; //lfo-ul Alienwah | |||
@@ -12,12 +12,39 @@ | |||
*/ | |||
#include <cmath> | |||
#include <rtosc/ports.h> | |||
#include <rtosc/port-sugar.h> | |||
#include "../Misc/Allocator.h" | |||
#include "Chorus.h" | |||
#include <iostream> | |||
using namespace std; | |||
#define rObject Chorus | |||
#define rBegin [](const char *, rtosc::RtData &) { | |||
#define rEnd } | |||
rtosc::Ports Chorus::ports = { | |||
{"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) | |||
rDoc("Instrument Presets"), 0, | |||
rBegin; | |||
rEnd}, | |||
//Pvolume/Ppanning are common | |||
rEffPar(Pfreq, 2, rShort("freq"), "Effect Frequency"), | |||
rEffPar(Pfreqrnd, 3, rShort("rand"), "Frequency Randomness"), | |||
rEffPar(PLFOtype, 4, rShort("shape"), "LFO Shape"), | |||
rEffParTF(PStereo,5, rShort("stereo"), "Stereo/Mono 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"), | |||
}; | |||
#undef rBegin | |||
#undef rEnd | |||
#undef rObject | |||
Chorus::Chorus(EffectParams pars) | |||
:Effect(pars), | |||
lfo(pars.srate, pars.bufsize), | |||
@@ -68,6 +68,7 @@ class Chorus:public Effect | |||
unsigned char getpar(int npar) const; | |||
void cleanup(void); | |||
static rtosc::Ports ports; | |||
private: | |||
//Chorus Parameters | |||
unsigned char Pvolume; | |||
@@ -16,6 +16,33 @@ | |||
#include "../Misc/WaveShapeSmps.h" | |||
#include "../Misc/Allocator.h" | |||
#include <cmath> | |||
#include <rtosc/ports.h> | |||
#include <rtosc/port-sugar.h> | |||
#define rObject Distorsion | |||
#define rBegin [](const char *, rtosc::RtData &) { | |||
#define rEnd } | |||
rtosc::Ports Distorsion::ports = { | |||
{"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) | |||
rDoc("Instrument Presets"), 0, | |||
rBegin; | |||
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"), | |||
rEffPar(Ptype, 5, rShort("type"), "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"), | |||
}; | |||
#undef rBegin | |||
#undef rEnd | |||
#undef rObject | |||
Distorsion::Distorsion(EffectParams pars) | |||
:Effect(pars), | |||
@@ -29,6 +29,7 @@ class Distorsion:public Effect | |||
void cleanup(void); | |||
void applyfilters(float *efxoutl, float *efxoutr); | |||
static rtosc::Ports ports; | |||
private: | |||
//Parameters | |||
unsigned char Pvolume; //Volume or E/R | |||
@@ -16,6 +16,55 @@ | |||
#include "DynamicFilter.h" | |||
#include "../DSP/Filter.h" | |||
#include "../Misc/Allocator.h" | |||
#include <rtosc/ports.h> | |||
#include <rtosc/port-sugar.h> | |||
#define rObject DynamicFilter | |||
#define rBegin [](const char *, rtosc::RtData &) { | |||
#define rEnd } | |||
rtosc::Ports DynamicFilter::ports = { | |||
{"preset::i", rOptions(WahWah, AutoWah, Sweep, VocalMorph1, VocalMorph1) | |||
rDoc("Instrument Presets"), 0, | |||
rBegin; | |||
rEnd}, | |||
//Pvolume/Ppanning are common | |||
{"Pfreq::i", rShort("freq") | |||
rDoc("Effect Frequency"), 0, | |||
rBegin; | |||
rEnd}, | |||
{"Pfreqrnd::i", rShort("rand") | |||
rDoc("Frequency Randomness"), 0, | |||
rBegin; | |||
rEnd}, | |||
{"PLFOtype::i", rShort("shape") | |||
rDoc("LFO Shape"), 0, | |||
rBegin; | |||
rEnd}, | |||
{"PStereo::T:F", rShort("stereo") | |||
rDoc("Stereo/Mono Mode"), 0, | |||
rBegin; | |||
rEnd}, | |||
{"Pdepth::i", rShort("depth") | |||
rDoc("LFO Depth"), 0, | |||
rBegin; | |||
rEnd}, | |||
{"Pampsns::i", rShort("sense") | |||
rDoc("how the filter varies according to the input amplitude"), 0, | |||
rBegin; | |||
rEnd}, | |||
{"Pampsnsinv::T:F", rShort("sns.inv") | |||
rDoc("Sense Inversion"), 0, | |||
rBegin; | |||
rEnd}, | |||
{"Pampsmooth::i", rShort("smooth") | |||
rDoc("how smooth the input amplitude changes the filter"), 0, | |||
rBegin; | |||
rEnd}, | |||
}; | |||
#undef rBegin | |||
#undef rEnd | |||
#undef rObject | |||
DynamicFilter::DynamicFilter(EffectParams pars, const AbsTime *time) | |||
:Effect(pars), | |||
@@ -30,6 +30,7 @@ class DynamicFilter:public Effect | |||
unsigned char getpar(int npar) const; | |||
void cleanup(void); | |||
static rtosc::Ports ports; | |||
private: | |||
//Parametrii DynamicFilter | |||
EffectLFO lfo; //lfo-ul DynamicFilter | |||
@@ -14,11 +14,34 @@ | |||
*/ | |||
#include <cmath> | |||
#include <rtosc/ports.h> | |||
#include <rtosc/port-sugar.h> | |||
#include "../Misc/Allocator.h" | |||
#include "Echo.h" | |||
#define MAX_DELAY 2 | |||
#define rObject Echo | |||
#define rBegin [](const char *, rtosc::RtData &) { | |||
#define rEnd } | |||
rtosc::Ports Echo::ports = { | |||
{"preset::i", rOptions(Echo 1, Echo 2, Echo 3, Simple Echo, Canyon, Panning Echo 1, Panning Echo 2, Panning Echo 3, Feedback Echo) | |||
rProp(parameter) | |||
rDoc("Instrument Presets"), 0, | |||
rBegin; | |||
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"), | |||
}; | |||
#undef rBegin | |||
#undef rEnd | |||
#undef rObject | |||
Echo::Echo(EffectParams pars) | |||
:Effect(pars), | |||
Pvolume(50), | |||
@@ -59,6 +59,8 @@ class Echo:public Effect | |||
unsigned char getpar(int npar) const; | |||
int getnumparams(void); | |||
void cleanup(void); | |||
static rtosc::Ports ports; | |||
private: | |||
//Parameters | |||
unsigned char Pvolume; /**<#1 Volume or Dry/Wetness*/ | |||
@@ -22,6 +22,27 @@ | |||
class FilterParams; | |||
class Allocator; | |||
#ifndef rEffPar | |||
#define rEffPar(name, idx, ...) \ | |||
{STRINGIFY(name) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rEffParCb(idx)} | |||
#define rEffParTF(name, idx, ...) \ | |||
{STRINGIFY(name) "::T:F", rProp(parameter) DOC(__VA_ARGS__), NULL, rEffParTFCb(idx)} | |||
#define rEffParCb(idx) \ | |||
[](const char *msg, rtosc::RtData &d) {\ | |||
rObject &obj = *(rObject*)d.obj; \ | |||
if(rtosc_narguments(msg)) \ | |||
obj.changepar(idx, rtosc_argument(msg, 0).i); \ | |||
else \ | |||
d.reply(d.loc, "i", obj.getpar(idx));} | |||
#define rEffParTFCb(idx) \ | |||
[](const char *msg, rtosc::RtData &d) {\ | |||
rObject &obj = *(rObject*)d.obj; \ | |||
if(rtosc_narguments(msg)) \ | |||
obj.changepar(idx, rtosc_argument(msg, 0).T*127); \ | |||
else \ | |||
d.reply(d.loc, obj.getpar(idx)?"T":"F");} | |||
#endif | |||
struct EffectParams | |||
{ | |||
/** | |||
@@ -33,6 +33,14 @@ | |||
#define rObject EffectMgr | |||
#define rSubtype(name) \ | |||
{STRINGIFY(name)"/", NULL, &name::ports,\ | |||
[](const char *msg, rtosc::RtData &data){\ | |||
rObject &o = *(rObject*)data.obj; \ | |||
data.obj = o.efx; \ | |||
SNIP \ | |||
name::ports.dispatch(msg, data); \ | |||
}} | |||
static const rtosc::Ports local_ports = { | |||
rSelf(EffectMgr), | |||
rPaste, | |||
@@ -71,7 +79,7 @@ static const rtosc::Ports local_ports = { | |||
//update parameters as well | |||
strncpy(loc, d.loc, 1024); | |||
char *tail = rindex(loc, '/'); | |||
char *tail = strrchr(loc, '/'); | |||
if(!tail) | |||
return; | |||
for(int i=0;i<128;++i) { | |||
@@ -94,7 +102,9 @@ static const rtosc::Ports local_ports = { | |||
eq->getFilter(a,b); | |||
d.reply(d.loc, "bb", sizeof(a), a, sizeof(b), b); | |||
}}, | |||
{"efftype::i", rProp(parameter) rDoc("Get Effect Type"), NULL, | |||
{"efftype::i", rOptions(Disabled, Reverb, Echo, Chorus, | |||
Phaser, Alienwah, Distorsion, EQ, DynamicFilter) | |||
rProp(parameter) rDoc("Get Effect Type"), NULL, | |||
[](const char *m, rtosc::RtData &d) | |||
{ | |||
EffectMgr *eff = (EffectMgr*)d.obj; | |||
@@ -121,7 +131,9 @@ static const rtosc::Ports local_ports = { | |||
//Return the old data for distruction | |||
d.reply("/free", "sb", "EffectMgr", sizeof(EffectMgr*), &eff_); | |||
}}, | |||
rSubtype(Echo), | |||
rSubtype(Alienwah), | |||
rSubtype(Distorsion), | |||
}; | |||
const rtosc::Ports &EffectMgr::ports = local_ports; | |||
@@ -17,11 +17,43 @@ | |||
#include <cmath> | |||
#include <algorithm> | |||
#include <rtosc/ports.h> | |||
#include <rtosc/port-sugar.h> | |||
#include "../Misc/Allocator.h" | |||
#include "Phaser.h" | |||
using namespace std; | |||
#define rObject Phaser | |||
#define rBegin [](const char *, rtosc::RtData &) { | |||
#define rEnd } | |||
rtosc::Ports Phaser::ports = { | |||
{"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) | |||
rDoc("Instrument Presets"), 0, | |||
rBegin; | |||
rEnd}, | |||
//Pvolume/Ppanning are common | |||
rEffPar(lfo.Pfreq, 2, ""), | |||
rEffPar(lfo.Prandomness, 3, ""), | |||
rEffPar(lfo.PLFOtype, 4, ""), | |||
rEffPar(lfo.Pstereo, 5, ""), | |||
rEffPar(Pdepth, 6, ""), | |||
rEffPar(Pfb, 7, ""), | |||
rEffPar(Pstages, 8, ""), | |||
rEffPar(Plrcross, 9, ""), | |||
rEffPar(Poffset, 9, ""), | |||
rEffParTF(Poutsub, 10, ""), | |||
rEffPar(Pphase, 11, ""), | |||
rEffPar(Pwidth, 11, ""), | |||
rEffPar(Phyper, 12, ""), | |||
rEffPar(Pdistortion, 13, ""), | |||
rEffPar(Panalog, 14, ""), | |||
}; | |||
#undef rBegin | |||
#undef rEnd | |||
#undef rObject | |||
#define PHASER_LFO_SHAPE 2 | |||
#define ONE_ 0.99999f // To prevent LFO ever reaching 1.0f for filter stability purposes | |||
#define ZERO_ 0.00001f // Same idea as above. | |||
@@ -34,6 +34,7 @@ class Phaser:public Effect | |||
unsigned char getpar(int npar) const; | |||
void cleanup(); | |||
static rtosc::Ports ports; | |||
private: | |||
//Phaser parameters | |||
EffectLFO lfo; //Phaser modulator | |||
@@ -29,6 +29,7 @@ | |||
#include "Config.h" | |||
#include "Util.h" | |||
#include "Part.h" | |||
#include "BankDb.h" | |||
#define INSTRUMENT_EXTENSION ".xiz" | |||
@@ -39,7 +40,7 @@ using namespace std; | |||
Bank::Bank(Config *config) | |||
:bankpos(0), defaultinsname(" "), config(config), | |||
bank_msb(0), bank_lsb(0) | |||
db(new BankDb), bank_msb(0), bank_lsb(0) | |||
{ | |||
clearbank(); | |||
bankfiletitle = dirname; | |||
@@ -57,6 +58,7 @@ Bank::Bank(Config *config) | |||
Bank::~Bank() | |||
{ | |||
clearbank(); | |||
delete db; | |||
} | |||
/* | |||
@@ -179,7 +181,8 @@ int Bank::savetoslot(unsigned int ninstrument, Part *part) | |||
err = part->saveXML(filename.c_str()); | |||
if(err) | |||
return err; | |||
addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz", (char *) part->Pname); | |||
addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz", | |||
(char *) part->Pname); | |||
return 0; | |||
} | |||
@@ -350,6 +353,7 @@ bool Bank::bankstruct::operator<(const bankstruct &b) const | |||
void Bank::rescanforbanks() | |||
{ | |||
db->clear(); | |||
//remove old banks | |||
banks.clear(); | |||
@@ -362,6 +366,7 @@ void Bank::rescanforbanks() | |||
//remove duplicate bank names | |||
for(int j = 0; j < (int) banks.size() - 1; ++j) { | |||
db->addBankDir(banks[j].dir); | |||
int dupl = 0; | |||
for(int i = j + 1; i < (int) banks.size(); ++i) { | |||
if(banks[i].name == banks[j].name) { | |||
@@ -376,6 +381,7 @@ void Bank::rescanforbanks() | |||
if(dupl) | |||
j += dupl; | |||
} | |||
db->scanBanks(); | |||
} | |||
void Bank::setMsb(uint8_t msb) | |||
@@ -453,6 +459,17 @@ void Bank::clearbank() | |||
dirname.clear(); | |||
} | |||
std::vector<std::string> Bank::search(std::string s) const | |||
{ | |||
std::vector<std::string> out; | |||
auto vec = db->search(s); | |||
for(auto e:vec) { | |||
out.push_back(e.name); | |||
out.push_back(e.bank+e.file); | |||
} | |||
return out; | |||
} | |||
int Bank::addtobank(int pos, string filename, string name) | |||
{ | |||
if((pos >= 0) && (pos < BANK_SIZE)) { | |||
@@ -68,7 +68,7 @@ class Bank | |||
std::vector<bankstruct> banks; | |||
int bankpos; | |||
struct ins_t { | |||
ins_t(void); | |||
std::string name; | |||
@@ -76,6 +76,8 @@ class Bank | |||
std::string filename; | |||
} ins[BANK_SIZE]; | |||
std::vector<std::string> search(std::string) const; | |||
private: | |||
//it adds a filename to the bank | |||
@@ -100,6 +102,7 @@ class Bank | |||
void normalizedirsuffix(std::string &dirname) const; | |||
Config* const config; | |||
class BankDb *db; | |||
public: | |||
uint8_t bank_msb; | |||
@@ -0,0 +1,219 @@ | |||
#include "BankDb.h" | |||
#include "XMLwrapper.h" | |||
#include "../globals.h" | |||
#include <cstring> | |||
#include <dirent.h> | |||
#define INSTRUMENT_EXTENSION ".xiz" | |||
using std::string; | |||
typedef BankDb::svec svec; | |||
typedef BankDb::bvec bvec; | |||
BankEntry::BankEntry(void) | |||
:id(0), add(false), pad(false), sub(false) | |||
{} | |||
bool sfind(std::string hay, std::string needle) | |||
{ | |||
return strcasestr(hay.c_str(), needle.c_str()); | |||
} | |||
bool BankEntry::match(string s) const | |||
{ | |||
if(s == "#pad") | |||
return pad; | |||
else if(s == "#sub") | |||
return sub; | |||
else if(s == "#add") | |||
return add; | |||
return sfind(file,s) || sfind(name,s) || sfind(bank, s) || | |||
sfind(type, s) || sfind(comments,s) || sfind(author,s); | |||
} | |||
static svec split(string s) | |||
{ | |||
svec vec; | |||
string ss; | |||
for(char c:s) { | |||
if(isspace(c) && !ss.empty()) { | |||
vec.push_back(ss); | |||
ss.clear(); | |||
} else if(!isspace(c)) { | |||
ss.push_back(c); | |||
} | |||
} | |||
if(!ss.empty()) | |||
vec.push_back(ss); | |||
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; | |||
} | |||
bvec BankDb::search(std::string ss) const | |||
{ | |||
bvec vec; | |||
const svec sterm = split(ss); | |||
for(auto field:fields) { | |||
bool match = true; | |||
for(auto s:sterm) | |||
match &= field.match(s); | |||
if(match) | |||
vec.push_back(field); | |||
} | |||
return vec; | |||
} | |||
void BankDb::addBankDir(std::string bnk) | |||
{ | |||
bool repeat = false; | |||
for(auto b:banks) | |||
repeat |= b == bnk; | |||
if(!repeat) | |||
banks.push_back(bnk); | |||
} | |||
void BankDb::clear(void) | |||
{ | |||
banks.clear(); | |||
fields.clear(); | |||
} | |||
void BankDb::scanBanks(void) | |||
{ | |||
fields.clear(); | |||
for(auto bank:banks) | |||
{ | |||
DIR *dir = opendir(bank.c_str()); | |||
if(!dir) | |||
continue; | |||
struct dirent *fn; | |||
while((fn = readdir(dir))) { | |||
const char *filename = fn->d_name; | |||
//check for extension | |||
if(!strstr(filename, INSTRUMENT_EXTENSION)) | |||
continue; | |||
auto xiz = processXiz(filename, bank); | |||
fields.push_back(xiz); | |||
} | |||
closedir(dir); | |||
} | |||
} | |||
BankEntry BankDb::processXiz(std::string filename, std::string bank) const | |||
{ | |||
//verify if the name is like this NNNN-name (where N is a digit) | |||
int no = 0; | |||
unsigned int startname = 0; | |||
for(unsigned int i = 0; i < 4; ++i) { | |||
if(filename.length() <= i) | |||
break; | |||
if(isdigit(filename[i])) { | |||
no = no * 10 + (filename[i] - '0'); | |||
startname++; | |||
} | |||
} | |||
if(startname + 1 < filename.length()) | |||
startname++; //to take out the "-" | |||
std::string name = filename; | |||
//remove the file extension | |||
for(int i = name.size() - 1; i >= 2; i--) { | |||
if(name[i] == '.') { | |||
name = name.substr(0, i); | |||
break; | |||
} | |||
} | |||
BankEntry entry; | |||
entry.file = filename; | |||
entry.bank = bank; | |||
entry.id = no; | |||
if(no != 0) //the instrument position in the bank is found | |||
entry.name = name.substr(startname); | |||
else | |||
entry.name = name; | |||
const char *types[] = { | |||
"None", | |||
"Piano", | |||
"Chromatic Percussion", | |||
"Organ", | |||
"Guitar", | |||
"Bass", | |||
"Solo Strings", | |||
"Ensemble", | |||
"Brass", | |||
"Reed", | |||
"Pipe", | |||
"Synth Lead", | |||
"Synth Pad", | |||
"Synth Effects", | |||
"Ethnic", | |||
"Percussive", | |||
"Sound Effects", | |||
}; | |||
//Try to obtain other metadata (expensive) | |||
XMLwrapper xml; | |||
string fname = bank+filename; | |||
int ret = xml.loadXMLfile(fname); | |||
if(xml.enterbranch("INSTRUMENT")) { | |||
if(xml.enterbranch("INFO")) { | |||
char author[1024]; | |||
char comments[1024]; | |||
int type = 0; | |||
xml.getparstr("author", author, 1024); | |||
xml.getparstr("comments", comments, 1024); | |||
type = xml.getpar("type", 0, 0, 16); | |||
entry.author = author; | |||
entry.comments = comments; | |||
entry.type = types[type]; | |||
xml.exitbranch(); | |||
} | |||
if(xml.enterbranch("INSTRUMENT_KIT")) { | |||
for(int i = 0; i < NUM_KIT_ITEMS; ++i) { | |||
if(xml.enterbranch("INSTRUMENT_KIT_ITEM", i) == 0) { | |||
entry.add |= xml.getparbool("add_enabled", false); | |||
entry.sub |= xml.getparbool("sub_enabled", false); | |||
entry.pad |= xml.getparbool("pad_enabled", false); | |||
xml.exitbranch(); | |||
} | |||
} | |||
xml.exitbranch(); | |||
} | |||
xml.exitbranch(); | |||
} | |||
//printf("Bank Entry:\n"); | |||
//printf("\tname - %s\n", entry.name.c_str()); | |||
//printf("\tauthor - %s\n", line(entry.author).c_str()); | |||
//printf("\tadd/pad/sub - %d/%d/%d\n", entry.add, entry.pad, entry.sub); | |||
return entry; | |||
} |
@@ -0,0 +1,50 @@ | |||
#pragma once | |||
#include <string> | |||
#include <vector> | |||
struct BankEntry | |||
{ | |||
BankEntry(void); | |||
std::string file; | |||
std::string bank; | |||
std::string name; | |||
std::string comments; | |||
std::string author; | |||
std::string type; | |||
int id; | |||
bool add; | |||
bool pad; | |||
bool sub; | |||
typedef std::vector<std::string> svec; | |||
svec tags(void) const; | |||
bool match(std::string) const; | |||
}; | |||
class BankDb | |||
{ | |||
public: | |||
typedef std::vector<std::string> svec; | |||
typedef std::vector<BankEntry> bvec; | |||
//search for banks | |||
//uses a space separated list of keywords and | |||
//finds something that matches ALL keywords | |||
bvec search(std::string) const; | |||
//fully qualified paths only | |||
void addBankDir(std::string); | |||
//clear all known entries and banks | |||
void clear(void); | |||
//List of all tags | |||
svec tags(void) const; | |||
//scan banks | |||
void scanBanks(void); | |||
private: | |||
BankEntry processXiz(std::string, std::string) const; | |||
bvec fields; | |||
svec banks; | |||
}; |
@@ -22,6 +22,7 @@ | |||
#include "../Effects/EffectMgr.h" | |||
#include "../DSP/FFTwrapper.h" | |||
#include "../Misc/Allocator.h" | |||
#include "../Containers/ScratchString.h" | |||
#include "../Nio/Nio.h" | |||
#include "PresetExtractor.h" | |||
@@ -74,7 +75,7 @@ static const Ports sysefxPort = | |||
static const Ports sysefsendto = | |||
{ | |||
{"to#" STRINGIFY(NUM_SYS_EFX) "::i", | |||
{"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 | |||
@@ -97,6 +98,17 @@ static const Ports sysefsendto = | |||
}} | |||
}; | |||
#define rBegin [](const char *msg, RtData &d) { Master *m = (Master*)d.obj | |||
#define rEnd } | |||
static const Ports watchPorts = { | |||
{"add:s", rDoc("Add synthesis state to watch"), 0, | |||
rBegin; | |||
m->watcher.add_watch(rtosc_argument(msg,0).s); | |||
rEnd}, | |||
}; | |||
extern const Ports bankPorts; | |||
static const Ports master_ports = { | |||
rString(last_xmz, XMZ_PATH_MAX, "File name for last name loaded if any."), | |||
rRecursp(part, 16, "Part"),//NUM_MIDI_PARTS | |||
@@ -104,8 +116,12 @@ static const Ports master_ports = { | |||
rRecursp(insefx, 8, "Insertion Effect"),//NUM_INS_EFX | |||
rRecur(microtonal, "Micrtonal Mapping Functionality"), | |||
rRecur(ctl, "Controller"), | |||
rArrayI(Pinsparts, NUM_INS_EFX, "Part to insert part onto"), | |||
{"Pkeyshift::i", rProp(parameter) rLinear(0,127) rDoc("Global Key Shift"), 0, [](const char *m, RtData&d) { | |||
rArrayI(Pinsparts, NUM_INS_EFX, rOpt(-1, Master), | |||
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) { | |||
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') { | |||
@@ -116,6 +132,22 @@ static const Ports master_ports = { | |||
{"get-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) { | |||
Master *m = (Master*)d.obj; | |||
d.reply("/vu-meter", "bb", sizeof(m->vu), &m->vu, sizeof(float)*NUM_MIDI_PARTS, m->vuoutpeakpart);}}, | |||
{"vu-meter:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) { | |||
Master *m = (Master*)d.obj; | |||
char types[6+NUM_MIDI_PARTS+1] = {0}; | |||
rtosc_arg_t args[6+NUM_MIDI_PARTS+1]; | |||
for(int i=0; i<6+NUM_MIDI_PARTS; ++i) | |||
types[i] = 'f'; | |||
args[0].f = m->vu.outpeakl; | |||
args[1].f = m->vu.outpeakr; | |||
args[2].f = m->vu.maxoutpeakl; | |||
args[3].f = m->vu.maxoutpeakr; | |||
args[4].f = m->vu.rmspeakl; | |||
args[5].f = m->vu.rmspeakr; | |||
for(int i=0; i<NUM_MIDI_PARTS; ++i) | |||
args[6+i].f = m->vuoutpeakpart[i]; | |||
d.replyArray("/vu-meter", types, args);}}, | |||
{"reset-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) { | |||
Master *m = (Master*)d.obj; | |||
m->vuresetpeaks();}}, | |||
@@ -129,14 +161,21 @@ static const Ports master_ports = { | |||
m->part[i] = p; | |||
p->initialize_rt(); | |||
}}, | |||
{"Pvolume::i", rProp(parameter) rLinear(0,127) rDoc("Master Volume"), 0, | |||
{"active_keys:", rProp("Obtain a list of active notes"), 0, | |||
rBegin; | |||
char keys[129] = {0}; | |||
for(int i=0; i<128; ++i) | |||
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, | |||
[](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", rProp(parameter) rLinear(0,127) rDoc("Master Volume"), 0, | |||
{"volume::i", rShort("volume") rProp(parameter) rLinear(0,127) rDoc("Master Volume"), 0, | |||
[](const char *m, rtosc::RtData &d) { | |||
if(rtosc_narguments(m)==0) { | |||
d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); | |||
@@ -240,8 +279,18 @@ static const Ports master_ports = { | |||
{"HDDRecorder/pause:", rDoc("Pause recording"), 0, [](const char *, RtData &d) { | |||
Master *m = (Master*)d.obj; | |||
m->HDDRecorder.pause();}}, | |||
{"watch/", rDoc("Interface to grab out live synthesis state"), &watchPorts, | |||
rBOIL_BEGIN; | |||
SNIP; | |||
watchPorts.dispatch(msg, data); | |||
rBOIL_END}, | |||
{"bank/", rDoc("Controls for instrument banks"), &bankPorts, | |||
[](const char*,RtData&) {}}, | |||
}; | |||
#undef rBegin | |||
#undef rEnd | |||
const Ports &Master::ports = master_ports; | |||
class DataObj:public rtosc::RtData | |||
@@ -257,6 +306,12 @@ class DataObj:public rtosc::RtData | |||
forwarded = false; | |||
} | |||
virtual void replyArray(const char *path, const char *args, rtosc_arg_t *vals) override | |||
{ | |||
char *buffer = bToU->buffer(); | |||
rtosc_amessage(buffer,bToU->buffer_size(),path,args,vals); | |||
reply(buffer); | |||
} | |||
virtual void reply(const char *path, const char *args, ...) override | |||
{ | |||
va_list va; | |||
@@ -333,9 +388,11 @@ Master::Master(const SYNTH_T &synth_, Config* config) | |||
fakepeakpart[npart] = 0; | |||
} | |||
ScratchString ss; | |||
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) | |||
part[npart] = new Part(*memory, synth, time, config->cfg.GzipCompression, | |||
config->cfg.Interpolation, µtonal, fft); | |||
config->cfg.Interpolation, µtonal, fft, &watcher, | |||
(ss+"/part"+npart+"/").c_str); | |||
//Insertion Effects init | |||
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) | |||
@@ -345,6 +402,9 @@ Master::Master(const SYNTH_T &synth_, Config* config) | |||
for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) | |||
sysefx[nefx] = new EffectMgr(*memory, synth, 0, &time); | |||
//Note Visualization | |||
for(int i=0; i<128; ++i) | |||
activeNotes[i] = 0; | |||
defaults(); | |||
@@ -358,7 +418,7 @@ void Master::applyOscEvent(const char *msg) | |||
DataObj d{loc_buf, 1024, this, bToU}; | |||
memset(loc_buf, 0, sizeof(loc_buf)); | |||
d.matches = 0; | |||
if(strcmp(msg, "/get-vu") && false) { | |||
fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 5 + 30, 0 + 40); | |||
fprintf(stdout, "backend[*]: '%s'<%s>\n", msg, | |||
@@ -411,12 +471,14 @@ void Master::defaults() | |||
void Master::noteOn(char chan, char note, char velocity) | |||
{ | |||
if(velocity) { | |||
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) | |||
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { | |||
if(chan == part[npart]->Prcvchn) { | |||
fakepeakpart[npart] = velocity * 2; | |||
if(part[npart]->Penabled) | |||
part[npart]->NoteOn(note, velocity, keyshift); | |||
} | |||
} | |||
activeNotes[(int)note] = 1; | |||
} | |||
else | |||
this->noteOff(chan, note); | |||
@@ -431,6 +493,7 @@ void Master::noteOff(char chan, char note) | |||
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) | |||
if((chan == part[npart]->Prcvchn) && part[npart]->Penabled) | |||
part[npart]->NoteOff(note); | |||
activeNotes[(int)note] = 0; | |||
} | |||
/* | |||
@@ -633,6 +696,13 @@ bool Master::AudioOut(float *outr, float *outl) | |||
bToU->write("/request-memory", ""); | |||
pendingMemory = true; | |||
} | |||
//Handle watch points | |||
if(bToU) | |||
watcher.write_back = bToU; | |||
watcher.tick(); | |||
//Handle user events TODO move me to a proper location | |||
char loc_buf[1024]; | |||
DataObj d{loc_buf, 1024, this, bToU}; | |||
@@ -677,7 +747,7 @@ bool Master::AudioOut(float *outr, float *outl) | |||
} | |||
if(events>1 && false) | |||
fprintf(stderr, "backend: %d events per cycle\n",events); | |||
//Swaps the Left channel with Right Channel | |||
if(swaplr) | |||
@@ -24,6 +24,7 @@ | |||
#include "Recorder.h" | |||
#include "../Params/Controller.h" | |||
#include "../Synth/WatchPoint.h" | |||
class Allocator; | |||
@@ -158,6 +159,13 @@ class Master | |||
//Statistics on output levels | |||
vuData vu; | |||
//Display info on midi notes | |||
bool activeNotes[128]; | |||
//Other watchers | |||
WatchManager watcher; | |||
//Midi Learn | |||
rtosc::MidiMapperRT midi; | |||
bool frozenState;//read-only parameters for threadsafe actions | |||
@@ -73,8 +73,8 @@ void path_search(const char *m, const char *url) | |||
using rtosc::Port; | |||
//assumed upper bound of 32 ports (may need to be resized) | |||
char types[129]; | |||
rtosc_arg_t args[128]; | |||
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; | |||
@@ -95,7 +95,7 @@ void path_search(const char *m, const char *url) | |||
if(ports) { | |||
//RTness not confirmed here | |||
for(const Port &p:*ports) { | |||
if(strstr(p.name, needle)!=p.name) | |||
if(strstr(p.name, needle) != p.name || !p.name) | |||
continue; | |||
types[pos] = 's'; | |||
args[pos++].s = p.name; | |||
@@ -120,6 +120,8 @@ void path_search(const char *m, const char *url) | |||
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); | |||
} | |||
} | |||
@@ -137,7 +139,7 @@ static int handler_function(const char *path, const char *types, lo_arg **argv, | |||
mw->transmitMsg("/echo", "ss", "OSC_URL", tmp); | |||
mw->activeUrl(tmp); | |||
} | |||
free((void*)tmp); | |||
} | |||
char buffer[2048]; | |||
@@ -146,7 +148,7 @@ static int handler_function(const char *path, const char *types, lo_arg **argv, | |||
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()); | |||
} else if(buffer[0]=='/' && rindex(buffer, '/')[1]) { | |||
} else if(buffer[0]=='/' && strrchr(buffer, '/')[1]) { | |||
mw->transmitMsg(rtosc::Ports::collapsePath(buffer)); | |||
} | |||
@@ -155,6 +157,16 @@ static int handler_function(const char *path, const char *types, lo_arg **argv, | |||
typedef void(*cb_t)(void*,const char*); | |||
//utility method (should be moved to a better location) | |||
template <class T, class V> | |||
std::vector<T> keys(const std::map<T,V> &m) | |||
{ | |||
std::vector<T> vec; | |||
for(auto &kv: m) | |||
vec.push_back(kv.first); | |||
return vec; | |||
} | |||
/***************************************************************************** | |||
* Memory Deallocation * | |||
@@ -484,7 +496,8 @@ public: | |||
master->time, | |||
config->cfg.GzipCompression, | |||
config->cfg.Interpolation, | |||
&master->microtonal, master->fft); | |||
&master->microtonal, master->fft, &master->watcher, | |||
("/part"+to_s(npart)+"/").c_str()); | |||
if(p->loadXMLinstrument(filename)) | |||
fprintf(stderr, "Warning: failed to load part<%s>!\n", filename); | |||
@@ -662,6 +675,11 @@ public: | |||
void write(const char *path, const char *args, ...); | |||
void write(const char *path, const char *args, va_list va); | |||
void currentUrl(string addr) | |||
{ | |||
curr_url = addr; | |||
known_remotes.insert(addr); | |||
} | |||
// Send a message to a remote client | |||
void sendToRemote(const char *msg, std::string dest); | |||
@@ -720,6 +738,7 @@ public: | |||
//LIBLO | |||
lo_server server; | |||
string last_url, curr_url; | |||
std::set<string> known_remotes; | |||
//Synthesis Rate Parameters | |||
const SYNTH_T synth; | |||
@@ -750,6 +769,7 @@ class MwDataObj:public rtosc::RtData | |||
~MwDataObj(void) | |||
{ | |||
delete[] loc; | |||
delete[] buffer; | |||
} | |||
@@ -773,6 +793,17 @@ class MwDataObj:public rtosc::RtData | |||
} | |||
va_end(va); | |||
} | |||
virtual void replyArray(const char *path, const char *args, rtosc_arg_t *argd) override | |||
{ | |||
//printf("reply building '%s'\n", path); | |||
if(!strcmp(path, "/forward")) { //forward the information to the backend | |||
args++; | |||
rtosc_amessage(buffer,4*4096,path,args,argd); | |||
} else { | |||
rtosc_amessage(buffer,4*4096,path,args,argd); | |||
reply(buffer); | |||
} | |||
} | |||
virtual void reply(const char *msg){ | |||
mwi->sendToCurrentRemote(msg); | |||
}; | |||
@@ -841,7 +872,8 @@ using rtosc::RtData; | |||
* - Load Bank * | |||
* - Refresh List of Banks * | |||
*****************************************************************************/ | |||
rtosc::Ports bankPorts = { | |||
extern const rtosc::Ports bankPorts; | |||
const rtosc::Ports bankPorts = { | |||
{"rescan:", 0, 0, | |||
rBegin; | |||
impl.rescanforbanks(); | |||
@@ -851,6 +883,48 @@ rtosc::Ports bankPorts = { | |||
d.reply("/bank/bank_select", "iss", i++, elm.name.c_str(), elm.dir.c_str()); | |||
d.reply("/bank/bank_select", "i", impl.bankpos); | |||
rEnd}, | |||
{"bank_list:", 0, 0, | |||
rBegin; | |||
#define MAX_BANKS 256 | |||
char types[MAX_BANKS*2+1]={0}; | |||
rtosc_arg_t args[MAX_BANKS*2]; | |||
int i = 0; | |||
for(auto &elm : impl.banks) { | |||
types[i] = types [i + 1] = 's'; | |||
args[i++].s = elm.name.c_str(); | |||
args[i++].s = elm.dir.c_str(); | |||
} | |||
d.replyArray("/bank/bank_list", types, args); | |||
#undef MAX_BANKS | |||
rEnd}, | |||
{"types:", 0, 0, | |||
rBegin; | |||
const char *types[17]; | |||
types[ 0] = "None"; | |||
types[ 1] = "Piano"; | |||
types[ 2] = "Chromatic Percussion"; | |||
types[ 3] = "Organ"; | |||
types[ 4] = "Guitar"; | |||
types[ 5] = "Bass"; | |||
types[ 6] = "Solo Strings"; | |||
types[ 7] = "Ensemble"; | |||
types[ 8] = "Brass"; | |||
types[ 9] = "Reed"; | |||
types[10] = "Pipe"; | |||
types[11] = "Synth Lead"; | |||
types[12] = "Synth Pad"; | |||
types[13] = "Synth Effects"; | |||
types[14] = "Ethnic"; | |||
types[15] = "Percussive"; | |||
types[16] = "Sound Effects"; | |||
char t[17+1]={0}; | |||
rtosc_arg_t args[17]; | |||
for(int i=0; i<17; ++i) { | |||
t[i] = 's'; | |||
args[i].s = types[i]; | |||
} | |||
d.replyArray("/bank/types", t, args); | |||
rEnd}, | |||
{"slot#1024:", 0, 0, | |||
rBegin; | |||
@@ -913,13 +987,19 @@ rtosc::Ports bankPorts = { | |||
d.reply("/alert", "s", | |||
"Failed To Clear Bank Slot, please check file permissions"); | |||
rEnd}, | |||
{"msb:i", 0, 0, | |||
{"msb::i", 0, 0, | |||
rBegin; | |||
impl.setMsb(rtosc_argument(msg, 0).i); | |||
if(rtosc_narguments(msg)) | |||
impl.setMsb(rtosc_argument(msg, 0).i); | |||
else | |||
d.reply(d.loc, "i", impl.bank_msb); | |||
rEnd}, | |||
{"lsb:i", 0, 0, | |||
{"lsb::i", 0, 0, | |||
rBegin; | |||
impl.setLsb(rtosc_argument(msg, 0).i); | |||
if(rtosc_narguments(msg)) | |||
impl.setLsb(rtosc_argument(msg, 0).i); | |||
else | |||
d.reply(d.loc, "i", impl.bank_lsb); | |||
rEnd}, | |||
{"newbank:s", 0, 0, | |||
rBegin; | |||
@@ -927,6 +1007,19 @@ rtosc::Ports bankPorts = { | |||
if(err) | |||
d.reply("/alert", "s", "Error: Could not make a new bank (directory).."); | |||
rEnd}, | |||
{"search:s", 0, 0, | |||
rBegin; | |||
auto res = impl.search(rtosc_argument(msg, 0).s); | |||
#define MAX_SEARCH 128 | |||
char res_type[MAX_SEARCH+1] = {0}; | |||
rtosc_arg_t res_dat[MAX_SEARCH] = {0}; | |||
for(unsigned i=0; i<res.size() && i<MAX_SEARCH; ++i) { | |||
res_type[i] = 's'; | |||
res_dat[i].s = res[i].c_str(); | |||
} | |||
d.replyArray("/bank/search_results", res_type, res_dat); | |||
#undef MAX_SEARCH | |||
rEnd}, | |||
}; | |||
/****************************************************************************** | |||
@@ -1025,6 +1118,10 @@ static rtosc::Ports middwareSnoopPorts = { | |||
xml.loadXMLfile(file); | |||
loadMidiLearn(xml, impl.midi_mapper); | |||
rEnd}, | |||
{"clear_xlz:", 0, 0, | |||
rBegin; | |||
impl.midi_mapper.clear(); | |||
rEnd}, | |||
//scale file stuff | |||
{"load_xsz:s", 0, 0, | |||
rBegin; | |||
@@ -1134,6 +1231,30 @@ static rtosc::Ports middwareSnoopPorts = { | |||
rBegin; | |||
impl.undo.seekHistory(+1); | |||
rEnd}, | |||
//port to observe the midi mappings | |||
{"midi-learn-values:", 0, 0, | |||
rBegin; | |||
auto &midi = impl.midi_mapper; | |||
auto key = keys(midi.inv_map); | |||
//cc-id, path, min, max | |||
#define MAX_MIDI 32 | |||
rtosc_arg_t args[MAX_MIDI*4]; | |||
char argt[MAX_MIDI*4+1] = {0}; | |||
for(unsigned i=0; i<key.size() && i<MAX_MIDI; ++i) { | |||
auto val = midi.inv_map[key[i]]; | |||
argt[4*i+0] = 'i'; | |||
args[4*i+0].i = std::get<1>(val); | |||
argt[4*i+1] = 's'; | |||
args[4*i+1].s = key[i].c_str(); | |||
argt[4*i+2] = 'i'; | |||
args[4*i+2].i = 0; | |||
argt[4*i+3] = 'i'; | |||
args[4*i+3].i = 127; | |||
} | |||
d.replyArray(d.loc, argt, args); | |||
#undef MAX_MIDI | |||
rEnd}, | |||
{"learn:s", 0, 0, | |||
rBegin; | |||
string addr = rtosc_argument(msg, 0).s; | |||
@@ -1162,7 +1283,7 @@ static rtosc::Ports middlewareReplyPorts = { | |||
const char *type = rtosc_argument(msg, 0).s; | |||
const char *url = rtosc_argument(msg, 1).s; | |||
if(!strcmp(type, "OSC_URL")) | |||
impl.curr_url = url; | |||
impl.currentUrl(url); | |||
rEnd}, | |||
{"free:sb", 0, 0, | |||
rBegin; | |||
@@ -1349,8 +1470,9 @@ void MiddleWareImpl::broadcastToRemote(const char *rtmsg) | |||
sendToRemote(rtmsg, "GUI"); | |||
//Send to remote UI if there's one listening | |||
if(curr_url != "GUI") | |||
sendToRemote(rtmsg, curr_url); | |||
for(auto rem:known_remotes) | |||
if(rem != "GUI") | |||
sendToRemote(rtmsg, rem); | |||
broadcast = false; | |||
} | |||
@@ -1369,6 +1491,8 @@ void MiddleWareImpl::sendToRemote(const char *rtmsg, std::string dest) | |||
lo_address addr = lo_address_new_from_url(dest.c_str()); | |||
if(addr) | |||
lo_send_message(addr, rtmsg, msg); | |||
lo_address_free(addr); | |||
lo_message_free(msg); | |||
} | |||
} | |||
@@ -1480,7 +1604,7 @@ void MiddleWareImpl::kitEnable(int part, int kit, int type) | |||
void MiddleWareImpl::handleMsg(const char *msg) | |||
{ | |||
//Check for known bugs | |||
assert(msg && *msg && rindex(msg, '/')[1]); | |||
assert(msg && *msg && strrchr(msg, '/')[1]); | |||
assert(strstr(msg,"free") == NULL || strstr(rtosc_argument_string(msg), "b") == NULL); | |||
assert(strcmp(msg, "/part0/Psysefxvol")); | |||
assert(strcmp(msg, "/Penabled")); | |||
@@ -1495,7 +1619,7 @@ void MiddleWareImpl::handleMsg(const char *msg) | |||
fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40); | |||
} | |||
const char *last_path = rindex(msg, '/'); | |||
const char *last_path = strrchr(msg, '/'); | |||
if(!last_path) { | |||
printf("Bad message in handleMsg() <%s>\n", msg); | |||
assert(false); | |||
@@ -25,6 +25,7 @@ | |||
#include "../Synth/ADnote.h" | |||
#include "../Synth/SUBnote.h" | |||
#include "../Synth/PADnote.h" | |||
#include "../Containers/ScratchString.h" | |||
#include "../DSP/FFTwrapper.h" | |||
#include "../Misc/Util.h" | |||
#include <cstdlib> | |||
@@ -59,7 +60,8 @@ static const Ports partPorts = { | |||
rParamZyn(Pminkey, rShort("min"), "Min Used Key"), | |||
rParamZyn(Pmaxkey, rShort("max"), "Max Used Key"), | |||
rParamZyn(Pkeyshift, rShort("shift"), "Part keyshift"), | |||
rParamZyn(Prcvchn, "Active MIDI channel"), | |||
rParamZyn(Prcvchn, rOptions(ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8, ch9, ch10, ch11, ch12, ch13, ch14, ch15, ch16), | |||
"Active MIDI channel"), | |||
rParamZyn(Pvelsns, "Velocity sensing"), | |||
rParamZyn(Pveloffs, "Velocity offset"), | |||
rToggle(Pnoteon, "If the channel accepts note on events"), | |||
@@ -163,7 +165,9 @@ static const Ports kitPorts = { | |||
rToggle(Padenabled, "ADsynth enable"), | |||
rToggle(Psubenabled, "SUBsynth enable"), | |||
rToggle(Ppadenabled, "PADsynth enable"), | |||
rParamZyn(Psendtoparteffect, "Effect Levels"), | |||
rParamZyn(Psendtoparteffect, | |||
rOptions(FX1, FX2, FX3, Off), | |||
"Effect Levels"), | |||
rString(Pname, PART_MAX_NAME_LEN, "Kit User Specified Label"), | |||
{"captureMin:", rDoc("Capture minimum valid note"), NULL, | |||
[](const char *, RtData &r) | |||
@@ -195,7 +199,7 @@ const Ports &Part::ports = partPorts; | |||
Part::Part(Allocator &alloc, const SYNTH_T &synth_, const AbsTime &time_, | |||
const int &gzip_compression, const int &interpolation, | |||
Microtonal *microtonal_, FFTwrapper *fft_) | |||
Microtonal *microtonal_, FFTwrapper *fft_, WatchManager *wm_, const char *prefix_) | |||
:Pdrummode(false), | |||
Ppolymode(true), | |||
Plegatomode(false), | |||
@@ -204,12 +208,18 @@ Part::Part(Allocator &alloc, const SYNTH_T &synth_, const AbsTime &time_, | |||
ctl(synth_, &time_), | |||
microtonal(microtonal_), | |||
fft(fft_), | |||
wm(wm_), | |||
memory(alloc), | |||
synth(synth_), | |||
time(time_), | |||
gzip_compression(gzip_compression), | |||
interpolation(interpolation) | |||
{ | |||
if(prefix_) | |||
strncpy(prefix, prefix_, sizeof(prefix)); | |||
else | |||
memset(prefix, 0, sizeof(prefix)); | |||
monomemClear(); | |||
for(int n = 0; n < NUM_KIT_ITEMS; ++n) { | |||
@@ -482,6 +492,7 @@ bool Part::NoteOn(unsigned char note, | |||
//Create New Notes | |||
for(uint8_t i = 0; i < NUM_KIT_ITEMS; ++i) { | |||
ScratchString pre = prefix; | |||
auto &item = kit[i]; | |||
if(Pkitmode != 0 && !item.validNote(note)) | |||
continue; | |||
@@ -493,13 +504,15 @@ bool Part::NoteOn(unsigned char note, | |||
try { | |||
if(item.Padenabled) | |||
notePool.insertNote(note, sendto, | |||
{memory.alloc<ADnote>(kit[i].adpars, pars), 0, i}); | |||
{memory.alloc<ADnote>(kit[i].adpars, pars, | |||
wm, (pre+"kit"+i+"/adpars/").c_str), 0, i}); | |||
if(item.Psubenabled) | |||
notePool.insertNote(note, sendto, | |||
{memory.alloc<SUBnote>(kit[i].subpars, pars), 1, i}); | |||
if(item.Ppadenabled) | |||
notePool.insertNote(note, sendto, | |||
{memory.alloc<PADnote>(kit[i].padpars, pars, interpolation), 2, i}); | |||
{memory.alloc<PADnote>(kit[i].padpars, pars, interpolation, wm, | |||
(pre+"kit"+i+"/padpars/").c_str), 2, i}); | |||
} catch (std::bad_alloc & ba) { | |||
std::cerr << "dropped new note: " << ba.what() << std::endl; | |||
} | |||
@@ -31,7 +31,7 @@ class Part | |||
* @param fft_ Pointer to the FFTwrapper*/ | |||
Part(Allocator &alloc, const SYNTH_T &synth, const AbsTime &time, | |||
const int& gzip_compression, const int& interpolation, | |||
Microtonal *microtonal_, FFTwrapper *fft_); | |||
Microtonal *microtonal_, FFTwrapper *fft_, WatchManager *wm=0, const char *prefix=0); | |||
/**Destructor*/ | |||
~Part(); | |||
@@ -192,6 +192,8 @@ class Part | |||
float oldfreq; //this is used for portamento | |||
Microtonal *microtonal; | |||
FFTwrapper *fft; | |||
WatchManager *wm; | |||
char prefix[64]; | |||
Allocator &memory; | |||
const SYNTH_T &synth; | |||
const AbsTime &time; | |||
@@ -12,6 +12,7 @@ using namespace rtosc; | |||
* - 'tooltip' : string [OPTIONAL] | |||
* - 'type' : type | |||
* - 'domain' : range [OPTIONAL] | |||
* - 'options' : [option...] [OPTIONAL] | |||
* type : {'int', 'float', 'boolean'} | |||
* action : | |||
* - 'path' : path-id | |||
@@ -19,6 +20,9 @@ using namespace rtosc; | |||
* arg : | |||
* - 'type' : type | |||
* - 'domain' : range [OPTIONAL] | |||
* option : | |||
* - 'id' : id-number | |||
* - 'value' : string-rep | |||
*/ | |||
void walk_ports2(const rtosc::Ports *base, | |||
@@ -98,15 +102,24 @@ 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 *name, void *v) | |||
void dump_param_cb(const rtosc::Port *p, const char *full_name, void *v) | |||
{ | |||
typedef std::vector<std::pair<int,string>> opts; | |||
std::ostream &o = *(std::ostream*)v; | |||
auto meta = p->meta(); | |||
const char *args = strchr(p->name, ':'); | |||
auto mparameter = meta.find("parameter"); | |||
auto mdoc = meta.find("documentation"); | |||
auto msname = meta.find("shortname"); | |||
opts options; | |||
string doc; | |||
string name = p->name;; | |||
{ | |||
size_t pos = 0; | |||
if((pos = name.find_first_of(":")) != string::npos) | |||
name = name.substr(0, pos); | |||
} | |||
//Escape Characters | |||
if(mdoc != p->meta().end()) { | |||
@@ -139,7 +152,7 @@ void dump_param_cb(const rtosc::Port *p, const char *name, void *v) | |||
} | |||
if(!type) { | |||
fprintf(stderr, "rtosc port dumper: Cannot handle '%s'\n", name); | |||
fprintf(stderr, "rtosc port dumper: Cannot handle '%s'\n", full_name); | |||
fprintf(stderr, " args = <%s>\n", args); | |||
return; | |||
} | |||
@@ -154,23 +167,43 @@ void dump_param_cb(const rtosc::Port *p, const char *name, void *v) | |||
const char *min = meta["min"]; | |||
const char *max = meta["max"]; | |||
for(auto m:meta) { | |||
if(strlen(m.title) >= 5 && !bcmp(m.title, "map ", 4)) { | |||
int id = atoi(m.title+4); | |||
std::string val = m.value; | |||
options.push_back(std::make_pair(id, val)); | |||
} | |||
} | |||
if(!first) | |||
o << ",\n"; | |||
else | |||
first = false; | |||
o << " {\n"; | |||
o << " \"path\" : \"" << name << "\",\n"; | |||
o << " \"path\" : \"" << full_name << "\",\n"; | |||
if(msname != meta.end()) | |||
o << " \"shortname\": \"" << msname.value << "\",\n"; | |||
o << " \"name\" : \"" << p->name << "\",\n"; | |||
o << " \"name\" : \"" << name << "\",\n"; | |||
o << " \"tooltip\" : \"" << doc << "\",\n"; | |||
o << " \"type\" : \"" << type << "\""; | |||
if(min && max) | |||
o << ",\n \"range\" : [" << min << "," << max << "]\n"; | |||
else | |||
o << "\n"; | |||
o << " }"; | |||
o << ",\n \"range\" : [" << min << "," << max << "]"; | |||
if(!options.empty()) { | |||
o << ",\n \"options\" : [\n"; | |||
int N = options.size(); | |||
for(int i=0; i<N; ++i) { | |||
o << " {\n"; | |||
o << " \"id\" : " << options[i].first << ",\n"; | |||
o << " \"value\" : \"" << options[i].second << "\"\n"; | |||
o << " }"; | |||
if(i != N-1) | |||
o << ","; | |||
o << "\n"; | |||
} | |||
o << " ]"; | |||
} | |||
o << "\n }"; | |||
} | |||
void dump_json(std::ostream &o, const rtosc::Ports &p) | |||
@@ -87,10 +87,6 @@ const char *mxmlElementGetAttr(const mxml_node_t *node, const char *name) | |||
XMLwrapper::XMLwrapper() | |||
{ | |||
version.Major = 2; | |||
version.Minor = 5; | |||
version.Revision = 3; | |||
minimal = true; | |||
node = tree = mxmlNewElement(MXML_NO_PARENT, | |||
@@ -106,11 +102,11 @@ XMLwrapper::XMLwrapper() | |||
node = root = addparams("ZynAddSubFX-data", 4, | |||
"version-major", stringFrom<int>( | |||
version.Major).c_str(), | |||
version.major()).c_str(), | |||
"version-minor", stringFrom<int>( | |||
version.Minor).c_str(), | |||
version.minor()).c_str(), | |||
"version-revision", | |||
stringFrom<int>(version.Revision).c_str(), | |||
stringFrom<int>(version.revision()).c_str(), | |||
"ZynAddSubFX-author", "Nasca Octavian Paul"); | |||
//make the empty branch that will contain the information parameters | |||
@@ -327,14 +323,13 @@ int XMLwrapper::loadXMLfile(const string &filename) | |||
return -3; //the XML doesnt embbed zynaddsubfx data | |||
//fetch version information | |||
version.Major = stringTo<int>(mxmlElementGetAttr(root, "version-major")); | |||
version.Minor = stringTo<int>(mxmlElementGetAttr(root, "version-minor")); | |||
version.Revision = | |||
stringTo<int>(mxmlElementGetAttr(root, "version-revision")); | |||
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"))); | |||
if(verbose) | |||
cout << "loadXMLfile() version: " << version.Major << '.' | |||
<< version.Minor << '.' << version.Revision << endl; | |||
cout << "loadXMLfile() version: " << fileversion << endl; | |||
return 0; | |||
} | |||
@@ -16,6 +16,7 @@ | |||
#include <mxml.h> | |||
#include <string> | |||
#include <vector> | |||
#include "../version.h" | |||
#ifndef XML_WRAPPER_H | |||
#define XML_WRAPPER_H | |||
@@ -272,12 +273,7 @@ class XMLwrapper | |||
mxml_node_t *addparams(const char *name, unsigned int params, | |||
...) const; | |||
/**@todo keep these numbers up to date*/ | |||
struct { | |||
int Major; /**<major version number.*/ | |||
int Minor; /**<minor version number.*/ | |||
int Revision; /**<version revision number.*/ | |||
} version; | |||
version_type fileversion; | |||
}; | |||
#endif |
@@ -11,6 +11,7 @@ | |||
of the License, or (at your option) any later version. | |||
*/ | |||
#include <stdlib.h> | |||
#include <iostream> | |||
#include <cmath> | |||
@@ -20,6 +21,7 @@ using namespace std; | |||
#include "../Misc/Config.h" | |||
#include "InMgr.h" | |||
#include "AlsaEngine.h" | |||
#include "Compressor.h" | |||
#include "Nio.h" | |||
AlsaEngine::AlsaEngine(const SYNTH_T &synth) | |||
@@ -28,6 +30,7 @@ AlsaEngine::AlsaEngine(const SYNTH_T &synth) | |||
audio.buffer = new short[synth.buffersize * 2]; | |||
name = "ALSA"; | |||
audio.handle = NULL; | |||
audio.peaks[0] = 0; | |||
midi.handle = NULL; | |||
midi.alsaId = -1; | |||
@@ -251,9 +254,13 @@ short *AlsaEngine::interleave(const Stereo<float *> &smps) | |||
int idx = 0; //possible off by one error here | |||
double scaled; | |||
for(int frame = 0; frame < bufferSize; ++frame) { // with a nod to libsamplerate ... | |||
scaled = smps.l[frame] * (8.0f * 0x10000000); | |||
float l = smps.l[frame]; | |||
float r = smps.r[frame]; | |||
stereoCompressor(synth.samplerate, audio.peaks[0], l, r); | |||
scaled = l * (8.0f * 0x10000000); | |||
shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16); | |||
scaled = smps.r[frame] * (8.0f * 0x10000000); | |||
scaled = r * (8.0f * 0x10000000); | |||
shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16); | |||
} | |||
return shortInterleaved; | |||
@@ -267,7 +274,12 @@ bool AlsaEngine::openAudio() | |||
int rc = 0; | |||
/* Open PCM device for playback. */ | |||
audio.handle = NULL; | |||
rc = snd_pcm_open(&audio.handle, "hw:0", | |||
const char *device = getenv("ALSA_DEVICE"); | |||
if(device == 0) | |||
device = "hw:0"; | |||
rc = snd_pcm_open(&audio.handle, device, | |||
SND_PCM_STREAM_PLAYBACK, 0); | |||
if(rc < 0) { | |||
fprintf(stderr, | |||
@@ -67,6 +67,7 @@ class AlsaEngine:public AudioOut, MidiIn | |||
unsigned int periods; | |||
short *buffer; | |||
pthread_t pThread; | |||
float peaks[1]; | |||
} audio; | |||
void *processAudio(); | |||
@@ -0,0 +1,59 @@ | |||
/* | |||
ZynAddSubFX - a software synthesizer | |||
Compressor.h - simple audio compressor macros | |||
Copyright (C) 2016 Hans Petter Selasky | |||
This program is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU General Public License | |||
as published by the Free Software Foundation; either version 2 | |||
of the License, or (at your option) any later version. | |||
*/ | |||
#ifndef _COMPRESSOR_H_ | |||
#define _COMPRESSOR_H_ | |||
#define floatIsValid(x) ({ \ | |||
float __r = (x) * 0.0; \ | |||
__r == 0.0 || __r == -0.0; \ | |||
}) | |||
#define stereoCompressor(div,pv,l,r) do { \ | |||
/* \ | |||
* Don't max the output range to avoid \ | |||
* overflowing sample rate conversion and \ | |||
* equalizer filters in the DSP's output \ | |||
* path. Keep one 10th, 1dB, reserved. \ | |||
*/ \ | |||
const float __limit = 1.0 - (1.0 / 10.0); \ | |||
float __peak; \ | |||
\ | |||
/* sanity checks */ \ | |||
__peak = (pv); \ | |||
if (!floatIsValid(__peak)) \ | |||
__peak = 0.0; \ | |||
if (!floatIsValid(l)) \ | |||
(l) = 0.0; \ | |||
if (!floatIsValid(r)) \ | |||
(r) = 0.0; \ | |||
/* compute maximum */ \ | |||
if ((l) < -__peak) \ | |||
__peak = -(l); \ | |||
else if ((l) > __peak) \ | |||
__peak = (l); \ | |||
if ((r) < -__peak) \ | |||
__peak = -(r); \ | |||
else if ((r) > __peak) \ | |||
__peak = (r); \ | |||
/* compressor */ \ | |||
if (__peak > __limit) { \ | |||
(l) /= __peak; \ | |||
(r) /= __peak; \ | |||
(l) *= __limit; \ | |||
(r) *= __limit; \ | |||
__peak -= __peak / (div); \ | |||
} \ | |||
(pv) = __peak; \ | |||
} while (0) | |||
#endif /* _COMPRESSOR_H_ */ |
@@ -131,6 +131,10 @@ int JackMultiEngine::processAudio(jack_nframes_t nframes) | |||
float *buffers[NUM_MIDI_PARTS * 2 + 2]; | |||
for(int i = 0; i < NUM_MIDI_PARTS * 2 + 2; ++i) { | |||
//Abort if ports are only partially initialized | |||
if(!impl->ports[i]) | |||
return false; | |||
buffers[i] = | |||
(float *)jack_port_get_buffer(impl->ports[i], nframes); | |||
assert(buffers[i]); | |||
@@ -12,6 +12,8 @@ | |||
*/ | |||
#include "OssEngine.h" | |||
#include "Compressor.h" | |||
#include "../Misc/Util.h" | |||
#include "../Misc/Config.h" | |||
#include "../globals.h" | |||
@@ -186,6 +188,8 @@ OssEngine::OssEngine(const SYNTH_T &synth, | |||
audio.smps.ps32 = new int[synth.buffersize * 2]; | |||
memset(audio.smps.ps32, 0, sizeof(int) * synth.buffersize * 2); | |||
memset(&midi.state, 0, sizeof(midi.state)); | |||
audio.peaks[0] = 0; | |||
} | |||
OssEngine::~OssEngine() | |||
@@ -401,21 +405,10 @@ void *OssEngine::audioThreadCb() | |||
while(getAudioEn()) { | |||
const Stereo<float *> smps = getNext(); | |||
float l, r; | |||
for(int i = 0; i < synth.buffersize; ++i) { | |||
l = smps.l[i]; | |||
r = smps.r[i]; | |||
if(l < -1.0f) | |||
l = -1.0f; | |||
else | |||
if(l > 1.0f) | |||
l = 1.0f; | |||
if(r < -1.0f) | |||
r = -1.0f; | |||
else | |||
if(r > 1.0f) | |||
r = 1.0f; | |||
float l = smps.l[i]; | |||
float r = smps.r[i]; | |||
stereoCompressor(synth.samplerate, audio.peaks[0], l, r); | |||
if (audio.is32bit) { | |||
audio.smps.ps32[i * 2] = (int) (l * 2147483647.0f); | |||
@@ -71,6 +71,10 @@ class OssEngine:public AudioOut, MidiIn | |||
short int *ps16; | |||
int *ps32; | |||
} smps; | |||
/* peak values used for compressor */ | |||
float peaks[1]; | |||
bool en; | |||
bool is32bit; | |||
} audio; | |||
@@ -29,6 +29,7 @@ | |||
#include "../Misc/Util.h" | |||
#include "OssMultiEngine.h" | |||
#include "Compressor.h" | |||
extern MiddleWare *middleware; | |||
@@ -53,12 +54,18 @@ OssMultiEngine :: OssMultiEngine(const SYNTH_T &synth, | |||
/* allocate buffer */ | |||
smps.ps32 = new int[maxbuffersize / sizeof(int)]; | |||
memset(smps.ps32, 0, maxbuffersize); | |||
/* setup compressor */ | |||
unsigned peaksize = NUM_MIDI_PARTS * sizeof(float); | |||
peaks = new float[peaksize / sizeof(float)]; | |||
memset(peaks, 0, peaksize); | |||
} | |||
OssMultiEngine :: ~OssMultiEngine() | |||
{ | |||
Stop(); | |||
delete [] smps.ps32; | |||
delete [] peaks; | |||
} | |||
bool | |||
@@ -213,8 +220,6 @@ OssMultiEngine :: audioThreadCb() | |||
while(getAudioEn()) { | |||
int error; | |||
float l; | |||
float r; | |||
int x; | |||
int y; | |||
@@ -227,32 +232,18 @@ OssMultiEngine :: audioThreadCb() | |||
if (is32bit) { | |||
for (y = 0; y != synth.buffersize; y++) { | |||
l = part->partoutl[y]; | |||
if (l < -1.0f) | |||
l = -1.0f; | |||
else if (l > 1.0f) | |||
l = 1.0f; | |||
float l = part->partoutl[y]; | |||
float r = part->partoutr[y]; | |||
stereoCompressor(synth.samplerate, peaks[x/2], l, r); | |||
smps.ps32[y * channels + x] = (int)(l * 2147483647.0f); | |||
r = part->partoutr[y]; | |||
if (r < -1.0f) | |||
r = -1.0f; | |||
else if (r > 1.0f) | |||
r = 1.0f; | |||
smps.ps32[y * channels + x + 1] = (int)(r * 2147483647.0f); | |||
} | |||
} else { | |||
for (y = 0; y != synth.buffersize; y++) { | |||
l = part->partoutl[y]; | |||
if (l < -1.0f) | |||
l = -1.0f; | |||
else if (l > 1.0f) | |||
l = 1.0f; | |||
float l = part->partoutl[y]; | |||
float r = part->partoutr[y]; | |||
stereoCompressor(synth.samplerate, peaks[x/2], l, r); | |||
smps.ps16[y * channels + x] = (short int)(l * 32767.0f); | |||
r = part->partoutr[y]; | |||
if (r < -1.0f) | |||
r = -1.0f; | |||
else if (r > 1.0f) | |||
r = 1.0f; | |||
smps.ps16[y * channels + x + 1] = (short int)(r * 32767.0f); | |||
} | |||
} | |||
@@ -52,6 +52,9 @@ class OssMultiEngine : public AudioOut | |||
int *ps32; | |||
} smps; | |||
/* peak values used for compressor */ | |||
float *peaks; | |||
bool en; | |||
bool is32bit; | |||
@@ -18,6 +18,7 @@ | |||
#include "ADnoteParameters.h" | |||
#include "EnvelopeParams.h" | |||
#include "LFOParams.h" | |||
#include "../Misc/Time.h" | |||
#include "../Misc/XMLwrapper.h" | |||
#include "../DSP/FFTwrapper.h" | |||
#include "../Synth/OscilGen.h" | |||
@@ -66,63 +67,68 @@ static const Ports voicePorts = { | |||
rRecurp(FMAmpEnvelope, "Modulator Amplitude Envelope"), | |||
rRecurp(VoiceFilter, "Optional Voice Filter"), | |||
rToggle(Enabled, "Voice Enable"), | |||
rParamZyn(Unison_size, "Number of subvoices"), | |||
rParamZyn(Unison_phase_randomness, "Phase Randomness"), | |||
rParamZyn(Unison_frequency_spread, "Subvoice detune"), | |||
rParamZyn(Unison_stereo_spread, "Subvoice L/R Separation"), | |||
rParamZyn(Unison_vibratto, "Subvoice vibratto"), | |||
rParamZyn(Unison_vibratto_speed, "Subvoice vibratto speed"), | |||
rOption(Unison_invert_phase, rOptions(none, random, 50%, 33%, 25%), "Subvoice Phases"), | |||
rOption(Type, rOptions(Sound,White,Pink), "Type of Sound"), | |||
rParamZyn(PDelay, "Voice Startup Delay"), | |||
rToggle(Presonance, "Resonance Enable"), | |||
rParamI(Pextoscil, rMap(min, -1), rMap(max, 16), "External Oscilator Selection"), | |||
rParamI(PextFMoscil, rMap(min, -1), rMap(max, 16), "External FM Oscilator Selection"), | |||
rParamZyn(Poscilphase, "Oscillator Phase"), | |||
rParamZyn(PFMoscilphase, "FM Oscillator Phase"), | |||
rToggle(Pfilterbypass, "Filter Bypass"), | |||
rToggle(Enabled, rShort("enable"), "Voice Enable"), | |||
rParamZyn(Unison_size, rShort("size"), "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"), | |||
rOption(Unison_invert_phase, rShort("inv."), | |||
rOptions(none, random, 50%, 33%, 25%), "Subvoice Phases"), | |||
rOption(Type, rShort("type"), rOptions(Sound,White,Pink), "Type of Sound"), | |||
rParamZyn(PDelay, rShort("delay"), "Voice Startup Delay"), | |||
rToggle(Presonance, rShort("enable"), "Resonance Enable"), | |||
rParamI(Pextoscil, rShort("ext."), | |||
rMap(min, -1), rMap(max, 16), "External Oscilator Selection"), | |||
rParamI(PextFMoscil, rShort("ext."), | |||
rMap(min, -1), rMap(max, 16), "External FM Oscilator Selection"), | |||
rParamZyn(Poscilphase, rShort("phase"), "Oscillator Phase"), | |||
rParamZyn(PFMoscilphase, rShort("phase"), "FM Oscillator Phase"), | |||
rToggle(Pfilterbypass, rShort("bypass"), "Filter Bypass"), | |||
//Freq Stuff | |||
rToggle(Pfixedfreq, "If frequency is fixed"), | |||
rParamZyn(PfixedfreqET, "Equal Tempermant Parameter"), | |||
rParamZyn(PBendAdjust, "Pitch bend adjustment"), | |||
rParamZyn(POffsetHz, "Voice constant offset"), | |||
rParamI(PDetune, "Fine Detune"), | |||
rParamI(PCoarseDetune, "Coarse Detune"), | |||
rParamZyn(PDetuneType, "Magnitude of Detune"), | |||
rToggle(PFreqEnvelopeEnabled, "Frequency Envelope Enable"), | |||
rToggle(PFreqLfoEnabled, "Frequency LFO Enable"), | |||
rToggle(Pfixedfreq, rShort("fixed"), "If frequency is fixed"), | |||
rParamZyn(PfixedfreqET, rShort("e.t."), "Equal Tempermant Parameter"), | |||
rParamZyn(PBendAdjust, rShort("bend"), "Pitch bend adjustment"), | |||
rParamZyn(POffsetHz, rShort("offset"), "Voice constant offset"), | |||
rParamI(PDetune, rShort("fine"), "Fine Detune"), | |||
rParamI(PCoarseDetune, rShort("coarse"), "Coarse Detune"), | |||
rParamZyn(PDetuneType, rShort("type"), "Magnitude of Detune"), | |||
rToggle(PFreqEnvelopeEnabled, rShort("enable"), "Frequency Envelope Enable"), | |||
rToggle(PFreqLfoEnabled, rShort("enable"), "Frequency LFO Enable"), | |||
//Amplitude Stuff | |||
rParamZyn(PPanning, "Panning"), | |||
rParamZyn(PVolume, "Volume"), | |||
rToggle(PVolumeminus, "Signal Inverter"), //do we really need this?? | |||
rParamZyn(PAmpVelocityScaleFunction, "Velocity Sensing"), | |||
rToggle(PAmpEnvelopeEnabled, "Amplitude Envelope Enable"), | |||
rToggle(PAmpLfoEnabled, "Amplitude LFO Enable"), | |||
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"), | |||
//Filter Stuff | |||
rToggle(PFilterEnabled, "Filter Enable"), | |||
rToggle(PFilterEnvelopeEnabled, "Filter Envelope Enable"), | |||
rToggle(PFilterLfoEnabled, "Filter LFO Enable"), | |||
rParamZyn(PFilterVelocityScale, "Filter Velocity Magnitude"), | |||
rParamZyn(PFilterVelocityScaleFunction, "Filter Velocity Function Shape"), | |||
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"), | |||
//Modulator Stuff | |||
rOption(PFMEnabled, rOptions(none, morph, ring modulation, phase modulation, | |||
frequency modulation, pitch modulation), "Modulator mode"), | |||
rParamI(PFMVoice, "Modulator Oscillator Selection"), | |||
rParamZyn(PFMVolume, "Modulator Magnitude"), | |||
rParamZyn(PFMVolumeDamp, "Modulator HF dampening"), | |||
rParamZyn(PFMVelocityScaleFunction, "Modulator Velocity Function"), | |||
rParamI(PFMDetune, "Modulator Fine Detune"), | |||
rParamI(PFMCoarseDetune, "Modulator Coarse Detune"), | |||
rParamZyn(PFMDetuneType, "Modulator Detune Magnitude"), | |||
rToggle(PFMFixedFreq, "Modulator Frequency Fixed"), | |||
rToggle(PFMFreqEnvelopeEnabled, "Modulator Frequency Envelope"), | |||
rToggle(PFMAmpEnvelopeEnabled, "Modulator Amplitude Envelope"), | |||
rOption(PFMEnabled, rShort("mode"), rOptions(none, morph, ring, phase, | |||
frequency, pitch), "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"), | |||
rParamI(PFMDetune, rShort("fine"), "Modulator Fine Detune"), | |||
rParamI(PFMCoarseDetune, rShort("coarse"), "Modulator Coarse Detune"), | |||
rParamZyn(PFMDetuneType, rShort("type"), "Modulator Detune Magnitude"), | |||
rToggle(PFMFixedFreq, rShort("fixed"), "Modulator Frequency Fixed"), | |||
rToggle(PFMFreqEnvelopeEnabled, rShort("enable"), "Modulator Frequency Envelope"), | |||
rToggle(PFMAmpEnvelopeEnabled, rShort("enable"), "Modulator Amplitude Envelope"), | |||
//weird stuff for PCoarseDetune | |||
@@ -229,28 +235,31 @@ static const Ports globalPorts = { | |||
rRecurp(AmpEnvelope, "Frequency Envelope"), | |||
rRecurp(FilterEnvelope, "Frequency Envelope"), | |||
rRecurp(GlobalFilter, "Filter"), | |||
rToggle(PStereo, "Mono/Stereo Enable"), | |||
rToggle(PStereo, rShort("stereo"), "Mono/Stereo Enable"), | |||
//Frequency | |||
rParamI(PDetune, "Fine Detune"), | |||
rParamI(PCoarseDetune, "Coarse Detune"), | |||
rParamZyn(PDetuneType, "Detune Scaling Type"), | |||
rParamZyn(PBandwidth, "Relative Fine Detune Gain"), | |||
rParamI(PDetune, rShort("fine"), "Fine Detune"), | |||
rParamI(PCoarseDetune, rShort("coarse"), "Coarse Detune"), | |||
rParamZyn(PDetuneType, rShort("type"), | |||
rOptions(L35cents, L10cents, E100cents, E1200cents), | |||
"Detune Scaling Type"), | |||
rParamZyn(PBandwidth, rShort("bw."), "Relative Fine Detune Gain"), | |||
//Amplitude | |||
rParamZyn(PPanning, "Panning of ADsynth (0 random, 1 left, 127 right)"), | |||
rParamZyn(PVolume, "volume control"), | |||
rParamZyn(PAmpVelocityScaleFunction, "Volume Velocity Control"), | |||
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, "Punch Strength"), | |||
rParamZyn(PPunchTime, "UNKNOWN"), | |||
rParamZyn(PPunchStretch, "How Punch changes with note frequency"), | |||
rParamZyn(PPunchVelocitySensing, "Punch Velocity control"), | |||
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"), | |||
//Filter | |||
rParamZyn(PFilterVelocityScale, "Filter Velocity Magnitude"), | |||
rParamZyn(PFilterVelocityScaleFunction, "Filter Velocity Function Shape"), | |||
rParamZyn(PFilterVelocityScale, rShort("scale"), "Filter Velocity Magnitude"), | |||
rParamZyn(PFilterVelocityScaleFunction, rShort("sense"), "Filter Velocity Function Shape"), | |||
//Resonance | |||
@@ -27,31 +27,31 @@ using namespace rtosc; | |||
#undef rChangeCb | |||
#define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } | |||
const rtosc::Ports Controller::ports = { | |||
rParamZyn(panning.depth, "Depth of Panning MIDI Control"), | |||
rParamZyn(filtercutoff.depth, "Depth of Filter Cutoff MIDI Control"), | |||
rParamZyn(filterq.depth, "Depth of Filter Q MIDI Control"), | |||
rParamZyn(bandwidth.depth, "Depth of Bandwidth MIDI Control"), | |||
rToggle(bandwidth.exponential, "Bandwidth Exponential Mode"), | |||
rParamZyn(modwheel.depth, "Depth of Modwheel MIDI Control"), | |||
rToggle(modwheel.exponential, "Modwheel Exponential Mode"), | |||
rToggle(pitchwheel.is_split, "If PitchWheel Has unified bendrange or not"), | |||
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, "Range of MIDI Pitch Wheel"), | |||
rParamI(pitchwheel.bendrange_down, "Lower Range of MIDI Pitch Wheel"), | |||
rToggle(expression.receive, "Expression MIDI Receive"), | |||
rToggle(fmamp.receive, "FM amplitude MIDI Receive"), | |||
rToggle(volume.receive, "Volume MIDI Receive"), | |||
rToggle(sustain.receive, "Sustain MIDI Receive"), | |||
rToggle(portamento.receive, "Portamento MIDI Receive"), | |||
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, "UNDOCUMENTED"), | |||
rParamZyn(portamento.time, "Portamento Length"), | |||
rToggle(portamento.proportional, "If all portamentos are proportional to the distance they span"), | |||
rParamZyn(portamento.propRate, "Portamento proportional rate"), | |||
rParamZyn(portamento.propDepth, "Portamento proportional depth"), | |||
rParamZyn(portamento.pitchthresh, "Threshold for portamento"), | |||
rToggle(portamento.pitchthreshtype, "Type of threshold"), | |||
rParamZyn(portamento.time, rShort("time"), "Portamento Length"), | |||
rToggle(portamento.proportional, rShort("propt."), "If all portamentos are proportional to the distance they span"), | |||
rParamZyn(portamento.propRate, rShort("rate"), "Portamento proportional rate"), | |||
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, "UNDOCUMENTED"), | |||
rParamZyn(resonancecenter.depth, "Resonance Center MIDI Depth"), | |||
rParamZyn(resonancebandwidth.depth, "Resonance Bandwidth MIDI Depth"), | |||
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"), | |||
rAction(defaults), | |||
}; | |||
@@ -23,6 +23,9 @@ | |||
#define rObject EnvelopeParams | |||
using namespace rtosc; | |||
#define rBegin [](const char *msg, RtData &d) { \ | |||
EnvelopeParams *env = (rObject*) d.obj | |||
#define rEnd } | |||
static const rtosc::Ports localPorts = { | |||
rSelf(EnvelopeParams), | |||
@@ -32,60 +35,85 @@ static const rtosc::Ports localPorts = { | |||
obj->last_update_timestamp = obj->time->time(); } | |||
rToggle(Pfreemode, "Complex Envelope Definitions"), | |||
#undef rChangeCb | |||
#define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } | |||
#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, rProp(internal), "Location of the sustain point"), | |||
rParams(Penvdt, MAX_ENVELOPE_POINTS, "Envelope Delay Times"), | |||
rParams(Penvval, MAX_ENVELOPE_POINTS, "Envelope Values"), | |||
rParamZyn(Penvstretch, "Stretch with respect to frequency"), | |||
rToggle(Pforcedrelease, "Force Envelope to fully evaluate"), | |||
rToggle(Plinearenvelope, "Linear or Logarithmic Envelopes"), | |||
rParamZyn(PA_dt, "Attack Time"), | |||
rParamZyn(PA_val, "Attack Value"), | |||
rParamZyn(PD_dt, "Decay Time"), | |||
rParamZyn(PD_val, "Decay Value"), | |||
rParamZyn(PS_val, "Sustain Value"), | |||
rParamZyn(PR_dt, "Release Time"), | |||
rParamZyn(PR_val, "Release Value"), | |||
{"addPoint:i", rProp(internal) rDoc("Add point to envelope"), NULL, [](const char *msg, RtData &d) | |||
{ | |||
EnvelopeParams *env = (rObject*) d.obj; | |||
const int curpoint = rtosc_argument(msg, 0).i; | |||
//int curpoint=freeedit->lastpoint; | |||
if (curpoint<0 || curpoint>env->Penvpoints || env->Penvpoints>=MAX_ENVELOPE_POINTS) | |||
return; | |||
for (int i=env->Penvpoints; i>=curpoint+1; i--) { | |||
env->Penvdt[i]=env->Penvdt[i-1]; | |||
env->Penvval[i]=env->Penvval[i-1]; | |||
} | |||
if (curpoint==0) { | |||
env->Penvdt[1]=64; | |||
} | |||
env->Penvpoints++; | |||
if (curpoint<=env->Penvsustain) env->Penvsustain++; | |||
}}, | |||
{"delPoint:i", rProp(internal) rDoc("Delete Envelope Point"), NULL, [](const char *msg, RtData &d) | |||
{ | |||
EnvelopeParams *env = (rObject*) d.obj; | |||
const int curpoint=rtosc_argument(msg, 0).i; | |||
if(curpoint<1 || curpoint>=env->Penvpoints-1 || env->Penvpoints<=3) | |||
return; | |||
for (int i=curpoint+1;i<env->Penvpoints;i++){ | |||
env->Penvdt[i-1]=env->Penvdt[i]; | |||
env->Penvval[i-1]=env->Penvval[i]; | |||
}; | |||
env->Penvpoints--; | |||
if (curpoint<=env->Penvsustain) | |||
env->Penvsustain--; | |||
}}, | |||
rParamZyn(Penvstretch, rShort("stretch"), | |||
"Stretch with respect to frequency"), | |||
rToggle(Pforcedrelease, rShort("frcr"), | |||
"Force Envelope to fully evaluate"), | |||
rToggle(Plinearenvelope, rShort("lin/log"), | |||
"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"), | |||
{"envdt:", rDoc("Envelope Delay Times"), NULL, | |||
rBegin; | |||
const int N = MAX_ENVELOPE_POINTS; | |||
rtosc_arg_t args[N]; | |||
char arg_types[N+1] = {0}; | |||
for(int i=0; i<N; ++i) { | |||
args[i].f = env->getdt(i); | |||
arg_types[i] = 'f'; | |||
} | |||
d.replyArray(d.loc, arg_types, args); | |||
rEnd}, | |||
{"envval:", rDoc("Envelope Delay Times"), NULL, | |||
rBegin; | |||
const int N = MAX_ENVELOPE_POINTS; | |||
rtosc_arg_t args[N]; | |||
char arg_types[N+1] = {0}; | |||
for(int i=0; i<N; ++i) { | |||
args[i].f = env->Penvval[i]/127.0f; | |||
arg_types[i] = 'f'; | |||
} | |||
d.replyArray(d.loc, arg_types, args); | |||
rEnd}, | |||
{"addPoint:i", rProp(internal) rDoc("Add point to envelope"), NULL, | |||
rBegin; | |||
const int curpoint = rtosc_argument(msg, 0).i; | |||
//int curpoint=freeedit->lastpoint; | |||
if (curpoint<0 || curpoint>env->Penvpoints || env->Penvpoints>=MAX_ENVELOPE_POINTS) | |||
return; | |||
for (int i=env->Penvpoints; i>=curpoint+1; i--) { | |||
env->Penvdt[i]=env->Penvdt[i-1]; | |||
env->Penvval[i]=env->Penvval[i-1]; | |||
} | |||
if (curpoint==0) | |||
env->Penvdt[1]=64; | |||
env->Penvpoints++; | |||
if (curpoint<=env->Penvsustain) | |||
env->Penvsustain++; | |||
rEnd}, | |||
{"delPoint:i", rProp(internal) rDoc("Delete Envelope Point"), NULL, | |||
rBegin; | |||
const int curpoint=rtosc_argument(msg, 0).i; | |||
if(curpoint<1 || curpoint>=env->Penvpoints-1 || env->Penvpoints<=3) | |||
return; | |||
for (int i=curpoint+1;i<env->Penvpoints;i++){ | |||
env->Penvdt[i-1]=env->Penvdt[i]; | |||
env->Penvval[i-1]=env->Penvval[i]; | |||
}; | |||
env->Penvpoints--; | |||
if (curpoint<=env->Penvsustain) | |||
env->Penvsustain--; | |||
rEnd}, | |||
}; | |||
#undef rChangeCb | |||
@@ -14,6 +14,7 @@ | |||
#include "FilterParams.h" | |||
#include "../Misc/Util.h" | |||
#include "../Misc/Time.h" | |||
#include "../DSP/AnalogFilter.h" | |||
#include <cmath> | |||
#include <cstdio> | |||
#include <cstdlib> | |||
@@ -58,18 +59,29 @@ const rtosc::Ports FilterParams::ports = { | |||
rSelf(FilterParams), | |||
rPaste, | |||
rArrayPaste, | |||
rParamZyn(Pcategory, "Class of filter"), | |||
rParamZyn(Ptype, "Filter Type"), | |||
rParamZyn(Pfreq, "Center Freq"), | |||
rParamZyn(Pq, "Quality Factor (resonance/bandwidth)"), | |||
rParamZyn(Pstages, "Filter Stages + 1"), | |||
rParamZyn(Pfreqtrack, "Frequency Tracking amount"), | |||
rParamZyn(Pgain, "Output Gain"), | |||
rParamZyn(Pnumformants, "Number of formants to be used"), | |||
rParamZyn(Pformantslowness, "Rate that formants change"), | |||
rParamZyn(Pvowelclearness, "Cost for mixing vowels"), | |||
rParamZyn(Pcenterfreq, "Center Freq (formant)"), | |||
rParamZyn(Poctavesfreq, "Number of octaves for formant"), | |||
rOption(Pcategory, rShort("class"), | |||
rOptions(analog, formant, st.var.), "Class of filter"), | |||
rOption(Ptype, rShort("type"), | |||
rOptions(LP1, HP1, LP2, HP2, BP, notch, peak, | |||
l.shelf, h.shelf), "Filter Type"), | |||
rParamZyn(Pfreq, rShort("cutoff"), "Center Freq"), | |||
rParamZyn(Pq, rShort("q"), | |||
"Quality Factor (resonance/bandwidth)"), | |||
rOption(Pstages, rShort("stages"), | |||
rOptions(1, 2, 3, 4, 5), "Filter Stages"), | |||
rParamZyn(Pfreqtrack, rShort("f.track"), | |||
"Frequency Tracking amount"), | |||
rParamZyn(Pgain, rShort("gain"), "Output Gain"), | |||
rParamZyn(Pnumformants, rShort("formants"), | |||
"Number of formants to be used"), | |||
rParamZyn(Pformantslowness, rShort("slew"), | |||
"Rate that formants change"), | |||
rParamZyn(Pvowelclearness, rShort("clarity"), | |||
"Cost for mixing vowels"), | |||
rParamZyn(Pcenterfreq, rShort("cutoff"), | |||
"Center Freq (formant)"), | |||
rParamZyn(Poctavesfreq, rShort("octaves"), | |||
"Number of octaves for formant"), | |||
//TODO check if FF_MAX_SEQUENCE is acutally expanded or not | |||
rParamZyn(Psequencesize, rMap(max, FF_MAX_SEQUENCE), "Length of vowel sequence"), | |||
@@ -110,11 +122,79 @@ const rtosc::Ports FilterParams::ports = { | |||
FilterParams *obj = (FilterParams *) d.obj; | |||
d.reply(d.loc, "f", obj->getoctavesfreq()); | |||
}}, | |||
{"q_value:", | |||
rDoc("Q value for UI Response Graphs"), | |||
NULL, [](const char *, RtData &d) { | |||
FilterParams *obj = (FilterParams *) d.obj; | |||
d.reply(d.loc, "f", obj->getq()); | |||
}}, | |||
{"response:", | |||
rDoc("Get a frequency response"), | |||
NULL, [](const char *, RtData &d) { | |||
FilterParams *obj = (FilterParams *) d.obj; | |||
int order = 0; | |||
float gain = dB2rap(obj->getgain()); | |||
if(obj->Ptype != 6 && obj->Ptype != 7 && obj->Ptype != 8) | |||
gain = 1.0; | |||
auto cf = AnalogFilter::computeCoeff(obj->Ptype, | |||
Filter::getrealfreq(obj->getfreq()), | |||
obj->getq(), obj->Pstages, | |||
gain, 48000, order); | |||
if(order == 2) { | |||
d.reply(d.loc, "fffffff", | |||
(float)obj->Pstages, | |||
cf.c[0], cf.c[1], cf.c[2], | |||
0.0, cf.d[1], cf.d[2]); | |||
} else if(order == 1) { | |||
d.reply(d.loc, "fffff", | |||
(float)obj->Pstages, | |||
cf.c[0], cf.c[1], | |||
0.0, cf.d[1]); | |||
} | |||
}}, | |||
// "", NULL, [](){}},"/freq" | |||
//{"Pvowels#" FF_MAX_VOWELS "/formants#" FF_MAX_FORMANTS "/amp", | |||
// "", NULL, [](){}}, | |||
//{"Pvowels#" FF_MAX_VOWELS "/formants#" FF_MAX_FORMANTS "/q", | |||
// "", NULL, [](){}}, | |||
// | |||
//struct Pvowels_t { | |||
// struct formants_t { | |||
// unsigned char freq, amp, q; //frequency,amplitude,Q | |||
// } formants[FF_MAX_FORMANTS]; | |||
//} Pvowels[FF_MAX_VOWELS]; | |||
{"vowels:", | |||
rDoc("Get info for formant graph"), | |||
NULL, [](const char *, RtData &d) { | |||
FilterParams *obj = (FilterParams *) d.obj; | |||
rtosc_arg_t args[2+3*FF_MAX_FORMANTS*FF_MAX_VOWELS]; | |||
char type[2+3*FF_MAX_FORMANTS*FF_MAX_VOWELS + 1] = {0}; | |||
type[0] = 'i'; | |||
type[1] = 'i'; | |||
args[0].i = FF_MAX_VOWELS; | |||
args[1].i = FF_MAX_FORMANTS; | |||
for(int i=0; i<FF_MAX_VOWELS; ++i) { | |||
auto &val = obj->Pvowels[i]; | |||
for(int j=0; j<FF_MAX_FORMANTS; ++j) { | |||
auto &f = val.formants[j]; | |||
//each formant is 3 arguments | |||
//each vowel is FF_MAX_FORMANTS * length of formants long | |||
auto *a = args + i*FF_MAX_FORMANTS*3 + j*3 + 2; | |||
auto *t = type + i*FF_MAX_FORMANTS*3 + j*3 + 2; | |||
a[0].f = obj->getformantfreq(f.freq); | |||
a[1].f = obj->getformantamp(f.amp); | |||
a[2].f = obj->getformantq(f.q); | |||
//printf("<%d,%d,%d,%d,%d,%f,%f,%f>\n", i, j, f.freq, f.amp, f.q, a[0].f, a[1].f, a[2].f); | |||
t[0] = t[1] = t[2] = 'f'; | |||
} | |||
} | |||
d.replyArray(d.loc, type, args); | |||
}}, | |||
}; | |||
#undef rChangeCb | |||
#define rChangeCb | |||
@@ -287,21 +367,18 @@ float FilterParams::getfreqpos(float freq) const | |||
*/ | |||
float FilterParams::getformantfreq(unsigned char freq) const | |||
{ | |||
float result = getfreqx(freq / 127.0f); | |||
return result; | |||
return getfreqx(freq / 127.0f); | |||
} | |||
float FilterParams::getformantamp(unsigned char amp) const | |||
{ | |||
float result = powf(0.1f, (1.0f - amp / 127.0f) * 4.0f); | |||
return result; | |||
return powf(0.1f, (1.0f - amp / 127.0f) * 4.0f); | |||
} | |||
float FilterParams::getformantq(unsigned char q) const | |||
{ | |||
//temp | |||
float result = powf(25.0f, (q - 32.0f) / 64.0f); | |||
return result; | |||
return powf(25.0f, (q - 32.0f) / 64.0f); | |||
} | |||
@@ -27,6 +27,8 @@ using namespace rtosc; | |||
#define rObject LFOParams | |||
#undef rChangeCb | |||
#define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } | |||
#define rBegin [](const char *msg, rtosc::RtData &d) { | |||
#define rEnd } | |||
static const rtosc::Ports _ports = { | |||
rSelf(LFOParams), | |||
rPaste, | |||
@@ -35,8 +37,8 @@ static const rtosc::Ports _ports = { | |||
"true frequency is [0,85.33] Hz"), | |||
rParamZyn(Pintensity, rShort("depth"), "Intensity of LFO"), | |||
rParamZyn(Pstartphase, rShort("start"), rSpecial(random), "Starting Phase"), | |||
rOption(PLFOtype, rShort("type"), rOptions(sine, triangle, square, ramp-up, ramp-down, | |||
exponential-down1, exponential-down2), "Shape of LFO"), | |||
rOption(PLFOtype, rShort("type"), rOptions(sine, triangle, square, up, down, | |||
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), | |||
@@ -45,7 +47,20 @@ static const rtosc::Ports _ports = { | |||
"0..4 second delay"), | |||
rToggle(Pcontinous, rShort("c"), "Enable for global operation"), | |||
rParamZyn(Pstretch, rShort("str"), rCentered, "Note frequency stretch"), | |||
//Float valued aliases | |||
{"delay::f", rProp(parameter) rMap(units, ms) rLog(0,4000), 0, | |||
rBegin; | |||
rEnd}, | |||
#define rPseudoLog(a,b) rLog(a,b) | |||
{"period::f", rProp(parameter) rMap(units, ms) rPseudoLog(0.10, 1500.0), 0, | |||
rBegin; | |||
rEnd}, | |||
}; | |||
#undef rPseudoLog | |||
#undef rBegin | |||
#undef rEnd | |||
#undef rChangeCb | |||
const rtosc::Ports &LFOParams::ports = _ports; | |||
@@ -18,6 +18,7 @@ | |||
#include "../Synth/Resonance.h" | |||
#include "../Synth/OscilGen.h" | |||
#include "../Misc/WavFile.h" | |||
#include "../Misc/Time.h" | |||
#include <cstdio> | |||
#include <rtosc/ports.h> | |||
@@ -136,30 +137,30 @@ static const rtosc::Ports non_realtime_ports = | |||
rRecurp(resonance, "Resonance"), | |||
//Harmonic Shape | |||
rOption(Pmode, rMap(min, 0), rMap(max, 2), rOptions(bandwidth,discrete,continious), | |||
rOption(Pmode, rMap(min, 0), rMap(max, 2), rShort("distribution"), rOptions(bandwidth,discrete,continious), | |||
"Harmonic Distribution Model"), | |||
rOption(Php.base.type, rOptions(Gaussian, Rectanglar, Double Exponential), | |||
rOption(Php.base.type, rOptions(Gaussian, Rectanglar, Double Exponential), rShort("shape"), | |||
"Harmonic profile shape"), | |||
rParamZyn(Php.base.par1, "Harmonic shape distribution parameter"), | |||
rParamZyn(Php.freqmult, "Frequency multiplier on distribution"), | |||
rParamZyn(Php.modulator.par1, "Distribution modulator parameter"), | |||
rParamZyn(Php.modulator.freq, "Frequency of modulator parameter"), | |||
rParamZyn(Php.width, "Width of base harmonic"), | |||
rOption(Php.amp.mode, rOptions(Sum, Mult, Div1, Div2), | |||
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), | |||
"Amplitude harmonic multiplier type"), | |||
//Harmonic Modulation | |||
rOption(Php.amp.type, rOptions(Off, Gauss, Sine, Flat), | |||
rOption(Php.amp.type, rShort("mult"), rOptions(Off, Gauss, Sine, Flat), | |||
"Type of amplitude multipler"), | |||
rParamZyn(Php.amp.par1, "Amplitude multiplier parameter"), | |||
rParamZyn(Php.amp.par2, "Amplitude multiplier parameter"), | |||
rToggle(Php.autoscale, "Autoscaling Harmonics"), | |||
rOption(Php.onehalf, | |||
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"), | |||
rOption(Php.onehalf, rShort("side"), | |||
rOptions(Full, Upper Half, Lower Half), | |||
"Harmonic cutoff model"), | |||
//Harmonic Bandwidth | |||
rOption(Pbwscale, | |||
rOption(Pbwscale, rShort("bw scale"), | |||
rOptions(Normal, | |||
EqualHz, Quater, | |||
Half, 75%, 150%, | |||
@@ -171,26 +172,26 @@ static const rtosc::Ports non_realtime_ports = | |||
rOptions(Harmonic, ShiftU, ShiftL, PowerU, PowerL, Sine, | |||
Power, Shift), | |||
"Harmonic Overtone shifting mode"), | |||
rParamI(Phrpos.par1, rLinear(0,255), "Harmonic position parameter"), | |||
rParamI(Phrpos.par2, rLinear(0,255), "Harmonic position parameter"), | |||
rParamI(Phrpos.par3, rLinear(0,255), "Harmonic position parameter"), | |||
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"), | |||
//Quality | |||
rOption(Pquality.samplesize, | |||
rOption(Pquality.samplesize, rShort("quality"), | |||
rOptions(16k (Tiny), 32k, 64k (Small), 128k, | |||
256k (Normal), 512k, 1M (Big)), | |||
"Size of each wavetable element"), | |||
rOption(Pquality.basenote, | |||
rOptions( C-2, G-2, C-3, G-3, C-4, | |||
rOption(Pquality.basenote, rShort("basenote"), | |||
rOptions(C-2, G-2, C-3, G-3, C-4, | |||
G-4, C-5, G-5, G-6,), | |||
"Base note for wavetable"), | |||
rOption(Pquality.smpoct, | |||
rOption(Pquality.smpoct, rShort("smp/oct"), | |||
rOptions(0.5, 1, 2, 3, 4, 6, 12), | |||
"Samples per octave"), | |||
rParamI(Pquality.oct, rLinear(0,7), | |||
rParamI(Pquality.oct, rShort("octaves"), rLinear(0,7), | |||
"Number of octaves to sample (above the first sample"), | |||
{"Pbandwidth::i", rProp(parameter) rLinear(0,1000) rDoc("Bandwith Of Harmonics"), NULL, | |||
{"Pbandwidth::i", rShort("bandwidth") rProp(parameter) rLinear(0,1000) rDoc("Bandwith Of Harmonics"), NULL, | |||
[](const char *msg, rtosc::RtData &d) { | |||
PADnoteParameters *p = ((PADnoteParameters*)d.obj); | |||
if(rtosc_narguments(msg)) { | |||
@@ -225,8 +226,24 @@ static const rtosc::Ports non_realtime_ports = | |||
float *tmp = new float[n]; | |||
float realbw = p->getprofile(tmp, n); | |||
d.reply(d.loc, "b", n*sizeof(float), tmp); | |||
d.reply(d.loc, "i", realbw); | |||
d.reply(d.loc, "i", (int)realbw); | |||
delete[] tmp;}}, | |||
{"harmonic_profile:", rProp(non-realtime) rDoc("UI display of the harmonic profile"), | |||
NULL, [](const char *m, rtosc::RtData &d) { | |||
PADnoteParameters *p = ((PADnoteParameters*)d.obj); | |||
#define RES 512 | |||
char types[RES+2] = {0}; | |||
rtosc_arg_t args[RES+1]; | |||
float tmp[RES]; | |||
types[0] = 'f'; | |||
args[0].f = p->getprofile(tmp, RES); | |||
for(int i=0; i<RES; ++i) { | |||
types[i+1] = 'f'; | |||
args[i+1].f = tmp[i]; | |||
} | |||
d.replyArray(d.loc, types, args); | |||
#undef RES | |||
}}, | |||
{"needPrepare:", rDoc("Unimplemented Stub"), | |||
NULL, [](const char *, rtosc::RtData&) {}}, | |||
}; | |||
@@ -26,102 +26,147 @@ | |||
#define rObject SUBnoteParameters | |||
using namespace rtosc; | |||
#define rBegin [](const char *msg, RtData &d) { \ | |||
SUBnoteParameters *obj = (SUBnoteParameters*) d.obj | |||
#define rEnd } | |||
#undef rChangeCb | |||
#define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } | |||
static const rtosc::Ports SUBnotePorts = { | |||
rSelf(SUBnoteParameters), | |||
rPaste, | |||
rToggle(Pstereo, "Stereo Enable"), | |||
rParamZyn(PVolume, "Volume"), | |||
rParamZyn(PPanning, "Left Right Panning"), | |||
rParamZyn(PAmpVelocityScaleFunction, "Amplitude Velocity Sensing function"), | |||
rParamI(PDetune, "Detune in detune type units"), | |||
rParamI(PCoarseDetune, "Coarse Detune"), | |||
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"), "Detune in detune type units"), | |||
rParamI(PCoarseDetune, rShort("cdetune"), "Coarse Detune"), | |||
//Real values needed | |||
rOption(PDetuneType, rOptions("100 cents", "200 cents", "500 cents"), "Detune Scale"), | |||
rToggle(PFreqEnvelopeEnabled, "Enable for Frequency Envelope"), | |||
rToggle(PBandWidthEnvelopeEnabled, "Enable for Bandwidth Envelope"), | |||
rToggle(PGlobalFilterEnabled, "Enable for Global Filter"), | |||
rParamZyn(PGlobalFilterVelocityScale, "Filter Velocity Magnitude"), | |||
rParamZyn(PGlobalFilterVelocityScaleFunction, "Filter Velocity Function Shape"), | |||
rOption(PDetuneType, rShort("det. scl."), rOptions(100 cents, 200 cents, 500 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"), | |||
//rRecur(FreqEnvelope, EnvelopeParams), | |||
//rToggle(),//continue | |||
rToggle(Pfixedfreq, "Base frequency fixed frequency enable"), | |||
rParamZyn(PfixedfreqET, "Equal temeperate control for fixed frequency operation"), | |||
rParamZyn(PBendAdjust, "Pitch bend adjustment"), | |||
rParamZyn(POffsetHz, "Voice constant offset"), | |||
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"), | |||
#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), | |||
rParamI(POvertoneSpread.type, rMap(min, 0), rMap(max, 7), rShort("spread type") | |||
rOptions(Harmonic, ShiftU, ShiftL, PowerU, PowerL, Sine, Power, Shift), | |||
"Spread of harmonic frequencies"), | |||
rParamI(POvertoneSpread.par1, rMap(min, 0), rMap(max, 255), | |||
rParamI(POvertoneSpread.par1, rMap(min, 0), rMap(max, 255), rShort("p1"), | |||
"Overtone Parameter"), | |||
rParamI(POvertoneSpread.par2, rMap(min, 0), rMap(max, 255), | |||
rParamI(POvertoneSpread.par2, rMap(min, 0), rMap(max, 255), rShort("p2"), | |||
"Overtone Parameter"), | |||
rParamI(POvertoneSpread.par3, rMap(min, 0), rMap(max, 255), | |||
rParamI(POvertoneSpread.par3, rMap(min, 0), rMap(max, 255), rShort("p3"), | |||
"Overtone Parameter"), | |||
#undef rChangeCb | |||
#define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } | |||
rParamZyn(Pnumstages, rMap(min, 1), rMap(max, 5), "Number of filter stages"), | |||
rParamZyn(Pbandwidth, "Bandwidth of filters"), | |||
rParamZyn(Phmagtype, "How the magnitudes are computed (0=linear,1=-60dB,2=-60dB)"), | |||
rParamZyn(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"),"How the magnitudes are computed (0=linear,1=-60dB,2=-60dB)"), | |||
rArray(Phmag, MAX_SUB_HARMONICS, "Harmonic magnitudes"), | |||
rArray(Phrelbw, MAX_SUB_HARMONICS, "Relative bandwidth"), | |||
rParamZyn(Pbwscale, "Bandwidth scaling with frequency"), | |||
rParamZyn(Pbwscale, rShort("stretch"), "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, rOptions("zero", "random", "ones"), "How harmonics are initialized"), | |||
{"clear:", rDoc("Reset all harmonics to equal bandwidth/zero amplitude"), NULL, [](const char *, RtData &d) | |||
{ | |||
SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; | |||
for(int i=0; i<MAX_SUB_HARMONICS; ++i) { | |||
obj->Phmag[i] = 0; | |||
obj->Phrelbw[i] = 64; | |||
} | |||
obj->Phmag[0] = 127; | |||
}}, | |||
{"detunevalue:", rDoc("Get note detune value"), NULL, [](const char *, RtData &d) | |||
{ | |||
SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; | |||
d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); | |||
}}, | |||
rOption(Pstart, rShort("initial"), rOptions(zero, random, ones), "How harmonics are initialized"), | |||
{"clear:", rDoc("Reset all harmonics to equal bandwidth/zero amplitude"), NULL, | |||
rBegin; | |||
for(int i=0; i<MAX_SUB_HARMONICS; ++i) { | |||
obj->Phmag[i] = 0; | |||
obj->Phrelbw[i] = 64; | |||
} | |||
obj->Phmag[0] = 127; | |||
rEnd}, | |||
{"detunevalue:", rDoc("Get note detune value"), NULL, | |||
rBegin; | |||
d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); | |||
rEnd}, | |||
//weird stuff for PCoarseDetune | |||
{"octave::c:i", rProp(parameter) rDoc("Note octave shift"), NULL, | |||
[](const char *msg, RtData &d) | |||
{ | |||
SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; | |||
if(!rtosc_narguments(msg)) { | |||
int k=obj->PCoarseDetune/1024; | |||
if (k>=8) k-=16; | |||
d.reply(d.loc, "i", k); | |||
} else { | |||
int k=(int) rtosc_argument(msg, 0).i; | |||
if (k<0) k+=16; | |||
obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; | |||
} | |||
}}, | |||
rBegin; | |||
if(!rtosc_narguments(msg)) { | |||
int k=obj->PCoarseDetune/1024; | |||
if (k>=8) k-=16; | |||
d.reply(d.loc, "i", k); | |||
} else { | |||
int k=(int) rtosc_argument(msg, 0).i; | |||
if (k<0) k+=16; | |||
obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; | |||
} | |||
rEnd}, | |||
{"coarsedetune::c:i", rProp(parameter) rDoc("Note coarse detune"), NULL, | |||
[](const char *msg, RtData &d) | |||
{ | |||
SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; | |||
if(!rtosc_narguments(msg)) { | |||
int k=obj->PCoarseDetune%1024; | |||
if (k>=512) k-=1024; | |||
d.reply(d.loc, "i", k); | |||
} else { | |||
int k=(int) rtosc_argument(msg, 0).i; | |||
if (k<0) k+=1024; | |||
obj->PCoarseDetune = k + (obj->PCoarseDetune/1024)*1024; | |||
} | |||
}}, | |||
rBegin; | |||
if(!rtosc_narguments(msg)) { | |||
int k=obj->PCoarseDetune%1024; | |||
if (k>=512) k-=1024; | |||
d.reply(d.loc, "i", k); | |||
} else { | |||
int k=(int) rtosc_argument(msg, 0).i; | |||
if (k<0) k+=1024; | |||
obj->PCoarseDetune = k + (obj->PCoarseDetune/1024)*1024; | |||
} | |||
rEnd}, | |||
{"response:", rDoc("Filter response at 440Hz. with 48kHz sample rate\n\n" | |||
"Format: stages, filter*active_filters\n" | |||
" filter = [frequency, bandwidth, amplitude]"), | |||
NULL, | |||
rBegin; | |||
//Identify the active harmonics | |||
int pos[MAX_SUB_HARMONICS]; | |||
int harmonics; | |||
obj->activeHarmonics(pos, harmonics); | |||
float base_freq = 440.0f; | |||
char types[3*MAX_SUB_HARMONICS+2]; | |||
rtosc_arg_t args[3*MAX_SUB_HARMONICS+1]; | |||
args[0].i = obj->Pnumstages; | |||
types[0] = 'i'; | |||
for(int n=0; n<harmonics; ++n) { | |||
const float freq = base_freq * obj->POvertoneFreqMult[pos[n]]; | |||
//the bandwidth is not absolute(Hz); it is relative to frequency | |||
const float bw = obj->convertBandwidth(obj->Pbandwidth, | |||
obj->Pnumstages, freq, obj->Pbwscale, obj->Phrelbw[pos[n]]); | |||
//try to keep same amplitude on all freqs and bw. (empirically) | |||
const float hgain = obj->convertHarmonicMag(obj->Phmag[pos[n]], | |||
obj->Phmagtype); | |||
const float gain = hgain * sqrt(1500.0f / (bw * freq)); | |||
int base = 1+3*n; | |||
args[base + 0].f = freq; | |||
args[base + 1].f = bw; | |||
args[base + 2].f = gain; | |||
types[base + 0] = 'f'; | |||
types[base + 1] = 'f'; | |||
types[base + 2] = 'f'; | |||
} | |||
types[3*harmonics+1] = 0; | |||
d.replyArray(d.loc, types, args); | |||
rEnd}, | |||
}; | |||
#undef rChangeCb | |||
#undef rBegin | |||
#undef rEnd | |||
const rtosc::Ports &SUBnoteParameters::ports = SUBnotePorts; | |||
@@ -143,6 +188,56 @@ SUBnoteParameters::SUBnoteParameters(const AbsTime *time_) | |||
defaults(); | |||
} | |||
void SUBnoteParameters::activeHarmonics(int *pos, int &harmonics) const | |||
{ | |||
harmonics = 0; | |||
for(int n = 0; n < MAX_SUB_HARMONICS; ++n) { | |||
if(Phmag[n] == 0) | |||
continue; | |||
pos[harmonics++] = n; | |||
} | |||
} | |||
float SUBnoteParameters::convertBandwidth(int bw_, int stages, float freq, | |||
int scale, int relbw) | |||
{ | |||
//the bandwidth is not absolute(Hz); it is relative to frequency | |||
float bw = powf(10, (bw_ - 127.0f) / 127.0f * 4) * stages; | |||
//Bandwidth Scale | |||
bw *= powf(1000 / freq, (scale - 64.0f) / 64.0f * 3.0f); | |||
//Relative BandWidth | |||
bw *= powf(100, (relbw - 64.0f) / 64.0f); | |||
if(bw > 25.0f) | |||
bw = 25.0f; | |||
return bw; | |||
} | |||
float SUBnoteParameters::convertHarmonicMag(int mag, int type) | |||
{ | |||
const float hmagnew = 1.0f - mag / 127.0f; | |||
switch(type) { | |||
case 1: | |||
return expf(hmagnew * logf(0.01f)); | |||
break; | |||
case 2: | |||
return expf(hmagnew * logf(0.001f)); | |||
break; | |||
case 3: | |||
return expf(hmagnew * logf(0.0001f)); | |||
break; | |||
case 4: | |||
return expf(hmagnew * logf(0.00001f)); | |||
break; | |||
default: | |||
return 1.0f - hmagnew; | |||
} | |||
} | |||
void SUBnoteParameters::defaults() | |||
{ | |||
@@ -24,6 +24,15 @@ class SUBnoteParameters:public Presets | |||
SUBnoteParameters(const AbsTime *time_ = nullptr); | |||
~SUBnoteParameters(); | |||
//Identify active harmonic positions | |||
// - pos : int[MAX_SUB_HARMONICS] offsets of active harmonics | |||
// - harmonics : number of active harmonics | |||
void activeHarmonics(int *pos, int &harmonics) const; | |||
static float convertBandwidth(int bw, int stages, float freq, | |||
int scale, int relbw); | |||
static float convertHarmonicMag(int mag, int type); | |||
void add2XML(XMLwrapper& xml); | |||
void defaults(); | |||
void getfromXML(XMLwrapper& xml); | |||
@@ -31,7 +40,7 @@ class SUBnoteParameters:public Presets | |||
void paste(SUBnoteParameters &sub); | |||
//Parameters | |||
//AMPLITUDE PARAMETRERS | |||
//AMPLITUDE PARAMETERS | |||
unsigned char Pstereo; //0 for mono,1 for stereo | |||
unsigned char PVolume; | |||
unsigned char PPanning; | |||
@@ -22,11 +22,13 @@ | |||
#include "../Misc/Util.h" | |||
#include "../Misc/Allocator.h" | |||
#include "../Params/ADnoteParameters.h" | |||
#include "../Containers/ScratchString.h" | |||
#include "ModFilter.h" | |||
#include "OscilGen.h" | |||
#include "ADnote.h" | |||
ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||
ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars, | |||
WatchManager *wm, const char *prefix) | |||
:SynthNote(spars), pars(*pars_) | |||
{ | |||
memory.beginTransaction(); | |||
@@ -450,7 +452,7 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||
memset(tmpwave_unison[k], 0, synth.bufferbytes); | |||
} | |||
initparameters(); | |||
initparameters(wm, prefix); | |||
memory.endTransaction(); | |||
} | |||
@@ -772,16 +774,17 @@ ADnote::~ADnote() | |||
/* | |||
* Init the parameters | |||
*/ | |||
void ADnote::initparameters() | |||
void ADnote::initparameters(WatchManager *wm, const char *prefix) | |||
{ | |||
int tmp[NUM_VOICES]; | |||
ScratchString pre = prefix; | |||
//ADnoteParameters &pars = *partparams; | |||
// Global Parameters | |||
NoteGlobalPar.initparameters(pars.GlobalPar, synth, | |||
time, | |||
memory, basefreq, velocity, | |||
stereo); | |||
stereo, wm, prefix); | |||
NoteGlobalPar.AmpEnvelope->envout_dB(); //discard the first envelope output | |||
globalnewamplitude = NoteGlobalPar.Volume | |||
@@ -816,22 +819,28 @@ void ADnote::initparameters() | |||
newamplitude[nvoice] = 1.0f; | |||
if(param.PAmpEnvelopeEnabled) { | |||
vce.AmpEnvelope = memory.alloc<Envelope>(*param.AmpEnvelope, basefreq, synth.dt()); | |||
vce.AmpEnvelope = memory.alloc<Envelope>(*param.AmpEnvelope, | |||
basefreq, synth.dt(), wm, | |||
(pre+"VoicePar"+nvoice+"/AmpEnvelope/").c_str); | |||
vce.AmpEnvelope->envout_dB(); //discard the first envelope sample | |||
newamplitude[nvoice] *= vce.AmpEnvelope->envout_dB(); | |||
} | |||
if(param.PAmpLfoEnabled) { | |||
vce.AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, time); | |||
vce.AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, time, wm, | |||
(pre+"VoicePar"+nvoice+"/AmpLfo/").c_str); | |||
newamplitude[nvoice] *= vce.AmpLfo->amplfoout(); | |||
} | |||
/* Voice Frequency Parameters Init */ | |||
if(param.PFreqEnvelopeEnabled) | |||
vce.FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, basefreq, synth.dt()); | |||
vce.FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, | |||
basefreq, synth.dt(), wm, | |||
(pre+"VoicePar"+nvoice+"/FreqEnvelope/").c_str); | |||
if(param.PFreqLfoEnabled) | |||
vce.FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time); | |||
vce.FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time, wm, | |||
(pre+"VoicePar"+nvoice+"/FreqLfo/").c_str); | |||
/* Voice Filter Parameters Init */ | |||
if(param.PFilterEnabled) { | |||
@@ -843,12 +852,15 @@ void ADnote::initparameters() | |||
if(param.PFilterEnvelopeEnabled) { | |||
vce.FilterEnvelope = | |||
memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | |||
memory.alloc<Envelope>(*param.FilterEnvelope, | |||
basefreq, synth.dt(), wm, | |||
(pre+"VoicePar"+nvoice+"/FilterEnvelope/").c_str); | |||
vce.Filter->addMod(*vce.FilterEnvelope); | |||
} | |||
if(param.PFilterLfoEnabled) { | |||
vce.FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time); | |||
vce.FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time, wm, | |||
(pre+"VoicePar"+nvoice+"/FilterLfo/").c_str); | |||
vce.Filter->addMod(*vce.FilterLfo); | |||
} | |||
} | |||
@@ -892,13 +904,17 @@ void ADnote::initparameters() | |||
} | |||
if(param.PFMFreqEnvelopeEnabled) | |||
vce.FMFreqEnvelope = memory.alloc<Envelope>(*param.FMFreqEnvelope, basefreq, synth.dt()); | |||
vce.FMFreqEnvelope = memory.alloc<Envelope>(*param.FMFreqEnvelope, | |||
basefreq, synth.dt(), wm, | |||
(pre+"VoicePar"+nvoice+"/FMFreqEnvelope/").c_str); | |||
FMnewamplitude[nvoice] = vce.FMVolume * ctl.fmamp.relamp; | |||
if(param.PFMAmpEnvelopeEnabled ) { | |||
vce.FMAmpEnvelope = | |||
memory.alloc<Envelope>(*param.FMAmpEnvelope, basefreq, synth.dt()); | |||
memory.alloc<Envelope>(*param.FMAmpEnvelope, | |||
basefreq, synth.dt(), wm, | |||
(pre+"VoicePar"+nvoice+"/FMAmpEnvelope/").c_str); | |||
FMnewamplitude[nvoice] *= vce.FMAmpEnvelope->envout_dB(); | |||
} | |||
} | |||
@@ -1873,13 +1889,20 @@ void ADnote::Global::initparameters(const ADnoteGlobalParam ¶m, | |||
const AbsTime &time, | |||
class Allocator &memory, | |||
float basefreq, float velocity, | |||
bool stereo) | |||
bool stereo, | |||
WatchManager *wm, | |||
const char *prefix) | |||
{ | |||
FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, basefreq, synth.dt()); | |||
FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time); | |||
ScratchString pre = prefix; | |||
FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, basefreq, | |||
synth.dt(), wm, (pre+"GlobalPar/FreqEnvelope/").c_str); | |||
FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time, wm, | |||
(pre+"GlobalPar/FreqLfo/").c_str); | |||
AmpEnvelope = memory.alloc<Envelope>(*param.AmpEnvelope, basefreq, synth.dt()); | |||
AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, time); | |||
AmpEnvelope = memory.alloc<Envelope>(*param.AmpEnvelope, basefreq, | |||
synth.dt(), wm, (pre+"GlobalPar/AmpEnvelope/").c_str); | |||
AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, time, wm, | |||
(pre+"GlobalPar/AmpLfo/").c_str); | |||
Volume = 4.0f * powf(0.1f, 3.0f * (1.0f - param.PVolume / 96.0f)) //-60 dB .. 0 dB | |||
* VelF(velocity, param.PAmpVelocityScaleFunction); //sensing | |||
@@ -1887,8 +1910,10 @@ void ADnote::Global::initparameters(const ADnoteGlobalParam ¶m, | |||
Filter = memory.alloc<ModFilter>(*param.GlobalFilter, synth, time, memory, | |||
stereo, basefreq); | |||
FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | |||
FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time); | |||
FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, | |||
synth.dt(), wm, (pre+"GlobalPar/FilterEnvelope/").c_str); | |||
FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time, wm, | |||
(pre+"GlobalPar/FilterLfo/").c_str); | |||
Filter->addMod(*FilterEnvelope); | |||
Filter->addMod(*FilterLfo); | |||
@@ -34,7 +34,8 @@ class ADnote:public SynthNote | |||
/**Constructor. | |||
* @param pars Note Parameters | |||
* @param spars Synth Engine Agnostic Parameters*/ | |||
ADnote(ADnoteParameters *pars, SynthParams &spars); | |||
ADnote(ADnoteParameters *pars, SynthParams &spars, | |||
WatchManager *wm=0, const char *prefix=0); | |||
/**Destructor*/ | |||
~ADnote(); | |||
@@ -62,7 +63,7 @@ class ADnote:public SynthNote | |||
/**Compute parameters for next tick*/ | |||
void computecurrentparameters(); | |||
/**Initializes All Parameters*/ | |||
void initparameters(); | |||
void initparameters(WatchManager *wm, const char *prefix); | |||
/**Deallocate/Cleanup given voice*/ | |||
void KillVoice(int nvoice); | |||
/**Deallocate Note resources and voice resources*/ | |||
@@ -117,7 +118,9 @@ class ADnote:public SynthNote | |||
const AbsTime &time, | |||
class Allocator &memory, | |||
float basefreq, float velocity, | |||
bool stereo); | |||
bool stereo, | |||
WatchManager *wm, | |||
const char *prefix); | |||
/****************************************** | |||
* FREQUENCY GLOBAL PARAMETERS * | |||
******************************************/ | |||
@@ -15,7 +15,9 @@ | |||
#include "Envelope.h" | |||
#include "../Params/EnvelopeParams.h" | |||
Envelope::Envelope(EnvelopeParams &pars, float basefreq, float bufferdt) | |||
Envelope::Envelope(EnvelopeParams &pars, float basefreq, float bufferdt, | |||
WatchManager *m, const char *watch_prefix) | |||
:watchOut(m, watch_prefix, "out") | |||
{ | |||
envpoints = pars.Penvpoints; | |||
if(envpoints > MAX_ENVELOPE_POINTS) | |||
@@ -88,7 +90,7 @@ void Envelope::releasekey() | |||
if(keyreleased) | |||
return; | |||
keyreleased = true; | |||
if(forcedrelease != 0) | |||
if(forcedrelease) | |||
t = 0.0f; | |||
} | |||
@@ -100,20 +102,28 @@ void Envelope::forceFinish(void) | |||
/* | |||
* Envelope Output | |||
*/ | |||
float Envelope::envout() | |||
float Envelope::envout(bool doWatch) | |||
{ | |||
float out; | |||
if(envfinish) { //if the envelope is finished | |||
envoutval = envval[envpoints - 1]; | |||
if(doWatch) { | |||
float pos[2] = {(float)envpoints - 1, envoutval}; | |||
watchOut(pos, 2); | |||
} | |||
return envoutval; | |||
} | |||
if((currentpoint == envsustain + 1) && !keyreleased) { //if it is sustaining now | |||
envoutval = envval[envsustain]; | |||
if(doWatch) { | |||
float pos[2] = {(float)envsustain, envoutval}; | |||
watchOut(pos, 2); | |||
} | |||
return envoutval; | |||
} | |||
if(keyreleased && (forcedrelease != 0)) { //do the forced release | |||
if(keyreleased && forcedrelease) { //do the forced release | |||
int tmp = (envsustain < 0) ? (envpoints - 1) : (envsustain + 1); //if there is no sustain point, use the last point for release | |||
if(envdt[tmp] < 0.00000001f) | |||
@@ -130,6 +140,12 @@ float Envelope::envout() | |||
if((currentpoint >= envpoints) || (envsustain < 0)) | |||
envfinish = true; | |||
} | |||
if(doWatch) { | |||
float pos[2] = {(float)tmp + t, envoutval}; | |||
watchOut(pos, 2); | |||
} | |||
return out; | |||
} | |||
if(inct >= 1.0f) | |||
@@ -149,6 +165,11 @@ float Envelope::envout() | |||
} | |||
envoutval = out; | |||
if(doWatch) { | |||
float pos[2] = {(float)currentpoint + t, envoutval}; | |||
watchOut(pos, 2); | |||
} | |||
return out; | |||
} | |||
@@ -166,10 +187,10 @@ inline float Envelope::env_rap2dB(float rap) { | |||
float Envelope::envout_dB() | |||
{ | |||
float out; | |||
if(linearenvelope != 0) | |||
return envout(); | |||
if(linearenvelope) | |||
return envout(true); | |||
if((currentpoint == 1) && (!keyreleased || (forcedrelease == 0))) { //first point is always lineary interpolated | |||
if((currentpoint == 1) && (!keyreleased || !forcedrelease)) { //first point is always lineary interpolated | |||
float v1 = env_dB2rap(envval[0]); | |||
float v2 = env_dB2rap(envval[1]); | |||
out = v1 + (v2 - v1) * t; | |||
@@ -186,9 +207,11 @@ float Envelope::envout_dB() | |||
envoutval = env_rap2dB(out); | |||
else | |||
envoutval = MIN_ENVELOPE_DB; | |||
} | |||
else | |||
out = env_dB2rap(envout()); | |||
} else | |||
out = env_dB2rap(envout(false)); | |||
float pos[2] = {(float)currentpoint + t, out}; | |||
watchOut(pos, 2); | |||
return out; | |||
} | |||
@@ -15,6 +15,7 @@ | |||
#define ENVELOPE_H | |||
#include "../globals.h" | |||
#include "WatchPoint.h" | |||
/**Implementation of a general Envelope*/ | |||
class Envelope | |||
@@ -22,17 +23,18 @@ class Envelope | |||
public: | |||
/**Constructor*/ | |||
Envelope(class EnvelopeParams &pars, float basefreq, float dt); | |||
Envelope(class EnvelopeParams &pars, float basefreq, float dt, WatchManager *m=0, | |||
const char *watch_prefix=0); | |||
/**Destructor*/ | |||
~Envelope(); | |||
void releasekey(); | |||
~Envelope(void); | |||
void releasekey(void); | |||
/**Push Envelope to finishing state*/ | |||
void forceFinish(void); | |||
float envout(); | |||
float envout_dB(); | |||
float envout(bool doWatch=true); | |||
float envout_dB(void); | |||
/**Determines the status of the Envelope | |||
* @return returns 1 if the envelope is finished*/ | |||
bool finished() const; | |||
bool finished(void) const; | |||
private: | |||
float env_rap2dB(float rap); | |||
float env_dB2rap(float db); | |||
@@ -44,12 +46,14 @@ class Envelope | |||
int linearenvelope; | |||
int currentpoint; //current envelope point (starts from 1) | |||
int forcedrelease; | |||
bool forcedrelease; | |||
bool keyreleased; //if the key was released | |||
bool envfinish; | |||
float t; // the time from the last point | |||
float inct; // the time increment | |||
float envoutval; //used to do the forced release | |||
VecWatchPoint watchOut; | |||
}; | |||
@@ -19,13 +19,15 @@ | |||
#include <cstdio> | |||
#include <cmath> | |||
LFO::LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t) | |||
LFO::LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t, WatchManager *m, | |||
const char *watch_prefix) | |||
:first_half(-1), | |||
delayTime(t, lfopars.Pdelay / 127.0f * 4.0f), //0..4 sec | |||
waveShape(lfopars.PLFOtype), | |||
deterministic(!lfopars.Pfreqrand), | |||
dt_(t.dt()), | |||
lfopars_(lfopars), basefreq_(basefreq) | |||
lfopars_(lfopars), basefreq_(basefreq), | |||
watchOut(m, watch_prefix, "out") | |||
{ | |||
int stretch = lfopars.Pstretch; | |||
if(stretch == 0) | |||
@@ -166,6 +168,10 @@ float LFO::lfoout() | |||
computeNextFreqRnd(); | |||
} | |||
float watch_data[2] = {phase, out}; | |||
watchOut(watch_data, 2); | |||
return out; | |||
} | |||
@@ -16,6 +16,7 @@ | |||
#include "../globals.h" | |||
#include "../Misc/Time.h" | |||
#include "WatchPoint.h" | |||
/**Class for creating Low Frequency Oscillators*/ | |||
class LFO | |||
@@ -26,7 +27,8 @@ class LFO | |||
* @param lfopars pointer to a LFOParams object | |||
* @param basefreq base frequency of LFO | |||
*/ | |||
LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t); | |||
LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t, WatchManager *m=0, | |||
const char *watch_prefix=0); | |||
~LFO(); | |||
float lfoout(); | |||
@@ -63,6 +65,8 @@ class LFO | |||
const LFOParams &lfopars_; | |||
const float basefreq_; | |||
VecWatchPoint watchOut; | |||
void computeNextFreqRnd(void); | |||
}; | |||
@@ -34,54 +34,54 @@ const rtosc::Ports OscilGen::non_realtime_ports = { | |||
rSelf(OscilGen), | |||
rPaste, | |||
//TODO ensure min/max | |||
rOption(Phmagtype, | |||
rOption(Phmagtype, rShort("scale"), | |||
rOptions(linear,dB scale (-40), | |||
dB scale (-60), dB scale (-80), | |||
dB scale (-100)), | |||
"Type of magnitude for harmonics"), | |||
rOption(Pcurrentbasefunc, | |||
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), | |||
"Base Waveform for harmonics"), | |||
rParamZyn(Pbasefuncpar, | |||
rParamZyn(Pbasefuncpar, rShort("shape"), | |||
"Morph between possible base function shapes " | |||
"(e.g. rising sawtooth vs a falling sawtooth)"), | |||
rOption(Pbasefuncmodulation, | |||
rOption(Pbasefuncmodulation, rShort("mod"), | |||
rOptions(None, Rev, Sine, Power, Chop), | |||
"Modulation applied to Base function spectra"), | |||
rParamZyn(Pbasefuncmodulationpar1, | |||
rParamZyn(Pbasefuncmodulationpar1, rShort("p1"), | |||
"Base function modulation parameter"), | |||
rParamZyn(Pbasefuncmodulationpar2, | |||
rParamZyn(Pbasefuncmodulationpar2, rShort("p2"), | |||
"Base function modulation parameter"), | |||
rParamZyn(Pbasefuncmodulationpar3, | |||
rParamZyn(Pbasefuncmodulationpar3, rShort("p3"), | |||
"Base function modulation parameter"), | |||
rParamZyn(Pwaveshaping, "Degree Of Waveshaping"), | |||
rOption(Pwaveshapingfunction, | |||
rOption(Pwaveshapingfunction, rShort("distort"), | |||
rOptions(Undistorted, | |||
Arctangent, Asymmetric, Pow, Sine, Quantisize, | |||
Zigzag, Limiter, Upper Limiter, Lower Limiter, | |||
Inverse Limiter, Clip, Asym2, Pow2, sigmoid), | |||
"Shape of distortion to be applied"), | |||
rOption(Pfiltertype, rOptions(No Filter, | |||
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, "Filter parameter"), | |||
rParamZyn(Pfilterpar2, "Filter parameter"), | |||
rToggle(Pfilterbeforews, "Filter before waveshaping spectra;" | |||
rParamZyn(Pfilterpar1, rShort("p1"), "Filter parameter"), | |||
rParamZyn(Pfilterpar2, rShort("p2"), "Filter parameter"), | |||
rToggle(Pfilterbeforews, rShort("pre/post"), "Filter before waveshaping spectra;" | |||
"When enabled oscilfilter(freqs); then waveshape(freqs);, " | |||
"otherwise waveshape(freqs); then oscilfilter(freqs);"), | |||
rOption(Psatype, rOptions(None, Pow, ThrsD, ThrsU), | |||
rOption(Psatype, rShort("spec. adj."), rOptions(None, Pow, ThrsD, ThrsU), | |||
"Spectral Adjustment Type"), | |||
rParamZyn(Psapar, "Spectral Adjustment Parameter"), | |||
rParamI(Pharmonicshift, "Amount of shift on harmonics"), | |||
rToggle(Pharmonicshiftfirst, "If harmonics are shifted before waveshaping/filtering"), | |||
rOption(Pmodulation, rOptions(None, Rev, Sine, Power), | |||
rParamZyn(Psapar, rShort("p1"), "Spectral Adjustment Parameter"), | |||
rParamI(Pharmonicshift, rShort("shift"), "Amount of shift on harmonics"), | |||
rToggle(Pharmonicshiftfirst, rShort("pre/post"), "If harmonics are shifted before waveshaping/filtering"), | |||
rOption(Pmodulation, rShort("FM"), rOptions(None, Rev, Sine, Power), | |||
"Frequency Modulation To Combined Spectra"), | |||
rParamZyn(Pmodulationpar1, "modulation parameter"), | |||
rParamZyn(Pmodulationpar2, "modulation parameter"), | |||
rParamZyn(Pmodulationpar3, "modulation parameter"), | |||
rParamZyn(Pmodulationpar1, rShort("p1"), "modulation parameter"), | |||
rParamZyn(Pmodulationpar2, rShort("p2"), "modulation parameter"), | |||
rParamZyn(Pmodulationpar3, rShort("p3"), "modulation parameter"), | |||
//TODO update to rArray and test | |||
@@ -91,9 +91,20 @@ const rtosc::Ports OscilGen::non_realtime_ports = { | |||
while(*mm && !isdigit(*mm)) ++mm; | |||
unsigned char &phase = ((OscilGen*)d.obj)->Phphase[atoi(mm)]; | |||
if(!rtosc_narguments(m)) | |||
d.reply(d.loc, "c", phase); | |||
else | |||
d.reply(d.loc, "i", phase); | |||
else { | |||
phase = rtosc_argument(m,0).i; | |||
//XXX hack hack | |||
char *repath = strdup(d.loc); | |||
char *edit = strrchr(repath, '/')+1; | |||
strcpy(edit, "prepare"); | |||
OscilGen &o = *((OscilGen*)d.obj); | |||
fft_t *data = new fft_t[o.synth.oscilsize / 2]; | |||
o.prepare(data); | |||
// fprintf(stderr, "sending '%p' of fft data\n", data); | |||
d.chain(repath, "b", sizeof(fft_t*), &data); | |||
o.pendingfreqs = data; | |||
} | |||
}}, | |||
//TODO update to rArray and test | |||
{"magnitude#128::c:i", rProp(parameter) rLinear(0,127) rDoc("Sets harmonic magnitude"), | |||
@@ -103,13 +114,13 @@ const rtosc::Ports OscilGen::non_realtime_ports = { | |||
while(*mm && !isdigit(*mm)) ++mm; | |||
unsigned char &mag = ((OscilGen*)d.obj)->Phmag[atoi(mm)]; | |||
if(!rtosc_narguments(m)) | |||
d.reply(d.loc, "c", mag); | |||
d.reply(d.loc, "i", mag); | |||
else { | |||
mag = rtosc_argument(m,0).i; | |||
//printf("setting magnitude\n\n"); | |||
//XXX hack hack | |||
char *repath = strdup(d.loc); | |||
char *edit = rindex(repath, '/')+1; | |||
char *edit = strrchr(repath, '/')+1; | |||
strcpy(edit, "prepare"); | |||
OscilGen &o = *((OscilGen*)d.obj); | |||
fft_t *data = new fft_t[o.synth.oscilsize / 2]; | |||
@@ -163,20 +174,20 @@ const rtosc::Ports OscilGen::non_realtime_ports = { | |||
const rtosc::Ports OscilGen::realtime_ports{ | |||
rSelf(OscilGen), | |||
rPresetType, | |||
rParamZyn(Prand, "Oscilator Phase Randomness: smaller than 0 is \"" | |||
rParamZyn(Prand, rShort("phase rnd"), "Oscilator Phase Randomness: smaller than 0 is \"" | |||
"group\", larger than 0 is for each harmonic"), | |||
rParamZyn(Pamprandpower, | |||
rParamZyn(Pamprandpower, rShort("variance"), | |||
"Variance of harmonic randomness"), | |||
rOption(Pamprandtype, rOptions(None, Pow, Sin), | |||
rOption(Pamprandtype, rShort("distribution"), rOptions(None, Pow, Sin), | |||
"Harmonic random distribution to select from"), | |||
rOption(Padaptiveharmonics, | |||
rOption(Padaptiveharmonics, rShort("adapt") | |||
rOptions(OFF, ON, Square, 2xSub, 2xAdd, 3xSub, 3xAdd, 4xSub, 4xAdd), | |||
"Adaptive Harmonics Mode"), | |||
rParamI(Padaptiveharmonicsbasefreq, rLinear(0,255), | |||
rParamI(Padaptiveharmonicsbasefreq, rShort("c. freq"), rLinear(0,255), | |||
"Base frequency of adaptive harmonic (30..3000Hz)"), | |||
rParamI(Padaptiveharmonicspower,rLinear(0,200), | |||
rParamI(Padaptiveharmonicspower, rShort("amount"), rLinear(0,200), | |||
"Adaptive Harmonic Strength"), | |||
rParamZyn(Padaptiveharmonicspar, | |||
rParamZyn(Padaptiveharmonicspar, rShort("par"), | |||
"Adaptive Harmonics Postprocessing Power"), | |||
{"waveform:", rDoc("Returns waveform points"), | |||
NULL, [](const char *, rtosc::RtData &d) { | |||
@@ -19,10 +19,12 @@ | |||
#include "../Params/PADnoteParameters.h" | |||
#include "../Params/Controller.h" | |||
#include "../Params/FilterParams.h" | |||
#include "../Containers/ScratchString.h" | |||
#include "../Misc/Util.h" | |||
PADnote::PADnote(const PADnoteParameters *parameters, | |||
SynthParams pars, const int& interpolation) | |||
SynthParams pars, const int& interpolation, WatchManager *wm, | |||
const char *prefix) | |||
:SynthNote(pars), pars(*parameters), interpolation(interpolation) | |||
{ | |||
NoteGlobalPar.GlobalFilter = nullptr; | |||
@@ -30,14 +32,16 @@ PADnote::PADnote(const PADnoteParameters *parameters, | |||
NoteGlobalPar.FilterLfo = nullptr; | |||
firsttime = true; | |||
setup(pars.frequency, pars.velocity, pars.portamento, pars.note); | |||
setup(pars.frequency, pars.velocity, pars.portamento, pars.note, false, wm, prefix); | |||
} | |||
void PADnote::setup(float freq, | |||
float velocity_, | |||
int portamento_, | |||
int midinote, | |||
bool legato) | |||
bool legato, | |||
WatchManager *wm, | |||
const char *prefix) | |||
{ | |||
portamento = portamento_; | |||
velocity = velocity_; | |||
@@ -129,11 +133,21 @@ void PADnote::setup(float freq, | |||
else | |||
NoteGlobalPar.Punch.Enabled = 0; | |||
NoteGlobalPar.FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, basefreq, synth.dt()); | |||
NoteGlobalPar.FreqLfo = memory.alloc<LFO>(*pars.FreqLfo, basefreq, time); | |||
NoteGlobalPar.AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, basefreq, synth.dt()); | |||
NoteGlobalPar.AmpLfo = memory.alloc<LFO>(*pars.AmpLfo, basefreq, time); | |||
ScratchString pre = prefix; | |||
NoteGlobalPar.FreqEnvelope = | |||
memory.alloc<Envelope>(*pars.FreqEnvelope, basefreq, synth.dt(), | |||
wm, (pre+"FreqEnvelope/").c_str); | |||
NoteGlobalPar.FreqLfo = | |||
memory.alloc<LFO>(*pars.FreqLfo, basefreq, time, | |||
wm, (pre+"FreqLfo/").c_str); | |||
NoteGlobalPar.AmpEnvelope = | |||
memory.alloc<Envelope>(*pars.AmpEnvelope, basefreq, synth.dt(), | |||
wm, (pre+"AmpEnvelope/").c_str); | |||
NoteGlobalPar.AmpLfo = | |||
memory.alloc<LFO>(*pars.AmpLfo, basefreq, time, | |||
wm, (pre+"AmpLfo/").c_str); | |||
} | |||
NoteGlobalPar.Volume = 4.0f | |||
@@ -147,6 +161,7 @@ void PADnote::setup(float freq, | |||
* NoteGlobalPar.AmpLfo->amplfoout(); | |||
if(!legato) { | |||
ScratchString pre = prefix; | |||
auto &flt = NoteGlobalPar.GlobalFilter; | |||
auto &env = NoteGlobalPar.FilterEnvelope; | |||
auto &lfo = NoteGlobalPar.FilterLfo; | |||
@@ -154,8 +169,10 @@ void PADnote::setup(float freq, | |||
flt = memory.alloc<ModFilter>(*pars.GlobalFilter, synth, time, memory, true, basefreq); | |||
//setup mod | |||
env = memory.alloc<Envelope>(*pars.FilterEnvelope, basefreq, synth.dt()); | |||
lfo = memory.alloc<LFO>(*pars.FilterLfo, basefreq, time); | |||
env = memory.alloc<Envelope>(*pars.FilterEnvelope, basefreq, | |||
synth.dt(), wm, (pre+"FilterEnvelope/").c_str); | |||
lfo = memory.alloc<LFO>(*pars.FilterLfo, basefreq, time, | |||
wm, (pre+"FilterLfo/").c_str); | |||
flt->addMod(*env); | |||
flt->addMod(*lfo); | |||
} | |||
@@ -23,7 +23,7 @@ class PADnote:public SynthNote | |||
{ | |||
public: | |||
PADnote(const PADnoteParameters *parameters, SynthParams pars, | |||
const int &interpolation); | |||
const int &interpolation, WatchManager *wm=0, const char *prefix=0); | |||
~PADnote(); | |||
SynthNote *cloneLegato(void); | |||
@@ -36,7 +36,7 @@ class PADnote:public SynthNote | |||
void releasekey(); | |||
private: | |||
void setup(float freq, float velocity, int portamento_, | |||
int midinote, bool legato = false); | |||
int midinote, bool legato = false, WatchManager *wm=0, const char *prefix=0); | |||
void fadein(float *smps); | |||
void computecurrentparameters(); | |||
bool finished_; | |||
@@ -20,17 +20,19 @@ | |||
#include <rtosc/port-sugar.h> | |||
#define rObject Resonance | |||
#define rBegin [](const char *msg, RtData &d) { rObject &o = *(rObject*)d.obj | |||
#define rEnd } | |||
using namespace rtosc; | |||
const rtosc::Ports Resonance::ports = { | |||
rSelf(Resonance), | |||
rPaste, | |||
rToggle(Penabled, "resonance enable"), | |||
rToggle(Pprotectthefundamental, "Disable resonance filter on first harmonic"), | |||
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, "how many dB the signal may be amplified"), | |||
rParamZyn(Pcenterfreq, "Center frequency"), | |||
rParamZyn(Poctavesfreq, "The number of octaves..."), | |||
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..."), | |||
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"), | |||
@@ -42,7 +44,29 @@ const rtosc::Ports Resonance::ports = { | |||
{"octavesfreq:", rDoc("Get center freq of graph"), NULL, | |||
[](const char *, RtData &d) | |||
{d.reply(d.loc, "f", ((rObject*)d.obj)->getoctavesfreq());}}, | |||
{"respoints", 0, 0, | |||
rBegin; | |||
if(rtosc_narguments(msg)) { | |||
int i=0; | |||
auto itr = rtosc_itr_begin(msg); | |||
while(!rtosc_itr_end(itr) && i < N_RES_POINTS) { | |||
auto ival = rtosc_itr_next(&itr); | |||
if(ival.type == 'f') | |||
o.Prespoints[i++] = ival.val.f*127; | |||
} | |||
} else { | |||
rtosc_arg_t args[N_RES_POINTS]; | |||
char types[N_RES_POINTS+1] = {0}; | |||
for(int i=0; i<N_RES_POINTS; ++i) { | |||
args[i].f = o.Prespoints[i]/127.0; | |||
types[i] = 'f'; | |||
} | |||
d.replyArray(d.loc, types, args); | |||
} | |||
rEnd}, | |||
}; | |||
#undef rBegin | |||
#undef rEnd | |||
Resonance::Resonance():Presets() | |||
{ | |||
@@ -20,6 +20,7 @@ | |||
#include "SUBnote.h" | |||
#include "Envelope.h" | |||
#include "ModFilter.h" | |||
#include "../Containers/ScratchString.h" | |||
#include "../Params/Controller.h" | |||
#include "../Params/SUBnoteParameters.h" | |||
#include "../Params/FilterParams.h" | |||
@@ -35,7 +36,8 @@ SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars) | |||
GlobalFilter(nullptr), | |||
GlobalFilterEnvelope(nullptr), | |||
NoteEnabled(true), | |||
lfilter(nullptr), rfilter(nullptr) | |||
lfilter(nullptr), rfilter(nullptr), | |||
wm(nullptr) | |||
{ | |||
setup(spars.frequency, spars.velocity, spars.portamento, spars.note); | |||
} | |||
@@ -46,46 +48,19 @@ float SUBnote::setupFilters(int *pos, bool automation) | |||
float reduceamp = 0.0f; | |||
for(int n = 0; n < numharmonics; ++n) { | |||
float freq = basefreq * pars.POvertoneFreqMult[pos[n]]; | |||
const float freq = basefreq * pars.POvertoneFreqMult[pos[n]]; | |||
overtone_freq[n] = freq; | |||
overtone_rolloff[n] = computerolloff(freq); | |||
//the bandwidth is not absolute(Hz); it is relative to frequency | |||
float bw = | |||
powf(10, (pars.Pbandwidth - 127.0f) / 127.0f * 4) * numstages; | |||
//Bandwidth Scale | |||
bw *= powf(1000 / freq, (pars.Pbwscale - 64.0f) / 64.0f * 3.0f); | |||
//Relative BandWidth | |||
bw *= powf(100, (pars.Phrelbw[pos[n]] - 64.0f) / 64.0f); | |||
if(bw > 25.0f) | |||
bw = 25.0f; | |||
const float bw = SUBnoteParameters::convertBandwidth(pars.Pbandwidth, | |||
numstages, freq, pars.Pbwscale, pars.Phrelbw[pos[n]]); | |||
//try to keep same amplitude on all freqs and bw. (empirically) | |||
float gain = sqrt(1500.0f / (bw * freq)); | |||
float hmagnew = 1.0f - pars.Phmag[pos[n]] / 127.0f; | |||
float hgain; | |||
switch(pars.Phmagtype) { | |||
case 1: | |||
hgain = expf(hmagnew * logf(0.01f)); | |||
break; | |||
case 2: | |||
hgain = expf(hmagnew * logf(0.001f)); | |||
break; | |||
case 3: | |||
hgain = expf(hmagnew * logf(0.0001f)); | |||
break; | |||
case 4: | |||
hgain = expf(hmagnew * logf(0.00001f)); | |||
break; | |||
default: | |||
hgain = 1.0f - hmagnew; | |||
} | |||
gain *= hgain; | |||
const float hgain = SUBnoteParameters::convertHarmonicMag(pars.Phmag[pos[n]], | |||
pars.Phmagtype); | |||
const float gain = hgain * sqrt(1500.0f / (bw * freq)); | |||
reduceamp += hgain; | |||
for(int nph = 0; nph < numstages; ++nph) { | |||
@@ -158,14 +133,10 @@ void SUBnote::setup(float freq, | |||
// basefreq*=ctl.pitchwheel.relfreq;//pitch wheel | |||
int pos[MAX_SUB_HARMONICS]; | |||
int harmonics = 0; | |||
int harmonics; | |||
pars.activeHarmonics(pos, harmonics); | |||
//select only harmonics that desire to compute | |||
for(int n = 0; n < MAX_SUB_HARMONICS; ++n) { | |||
if(pars.Phmag[n] == 0) | |||
continue; | |||
pos[harmonics++] = n; | |||
} | |||
if(!legato) //normal note | |||
firstnumharmonics = numharmonics = harmonics; | |||
else { | |||
@@ -198,9 +169,9 @@ void SUBnote::setup(float freq, | |||
oldbandwidth = 64; | |||
if(!legato) { //normal note | |||
if(pars.Pfixedfreq == 0) | |||
initparameters(basefreq); | |||
initparameters(basefreq, wm); | |||
else | |||
initparameters(basefreq / 440.0f * freq); | |||
initparameters(basefreq / 440.0f * freq, wm); | |||
} | |||
else { | |||
if(pars.Pfixedfreq == 0) | |||
@@ -376,18 +347,25 @@ void SUBnote::filter(bpfilter &filter, float *smps) | |||
/* | |||
* Init Parameters | |||
*/ | |||
void SUBnote::initparameters(float freq) | |||
void SUBnote::initparameters(float freq, WatchManager *wm) | |||
{ | |||
AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, freq, synth.dt()); | |||
//TODO populate this base string | |||
ScratchString pre; | |||
AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, freq, | |||
synth.dt(), wm, (pre+"AmpEnvelope/").c_str); | |||
if(pars.PFreqEnvelopeEnabled) | |||
FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, freq, synth.dt()); | |||
FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, freq, | |||
synth.dt(), wm, (pre+"FreqEnvelope/").c_str); | |||
if(pars.PBandWidthEnvelopeEnabled) | |||
BandWidthEnvelope = memory.alloc<Envelope>(*pars.BandWidthEnvelope, freq, synth.dt()); | |||
BandWidthEnvelope = memory.alloc<Envelope>(*pars.BandWidthEnvelope, | |||
freq, synth.dt(), wm, (pre+"BandWidthEnvelope/").c_str); | |||
if(pars.PGlobalFilterEnabled) { | |||
GlobalFilterEnvelope = memory.alloc<Envelope>(*pars.GlobalFilterEnvelope, freq, synth.dt()); | |||
GlobalFilterEnvelope = | |||
memory.alloc<Envelope>(*pars.GlobalFilterEnvelope, freq, | |||
synth.dt(), wm, (pre+"GlobalFilterEnvelope/").c_str); | |||
GlobalFilter = memory.alloc<ModFilter>(*pars.GlobalFilter, synth, time, memory, stereo, freq); | |||
@@ -429,14 +407,9 @@ void SUBnote::computecurrentparameters() | |||
//A little bit of copy/paste for now | |||
int pos[MAX_SUB_HARMONICS]; | |||
int harmonics = 0; | |||
int harmonics; | |||
//select only harmonics that desire to compute | |||
for(int n = 0; n < MAX_SUB_HARMONICS; ++n) { | |||
if(pars.Phmag[n] == 0) | |||
continue; | |||
pos[harmonics++] = n; | |||
} | |||
pars.activeHarmonics(pos, harmonics); | |||
bool delta_harmonics = (harmonics != numharmonics); | |||
if(delta_harmonics) { | |||
@@ -43,7 +43,7 @@ class SUBnote:public SynthNote | |||
* Initialize envelopes and global filter | |||
* calls computercurrentparameters() | |||
*/ | |||
void initparameters(float freq); | |||
void initparameters(float freq, WatchManager *wm); | |||
void KillNote(); | |||
const SUBnoteParameters &pars; | |||
@@ -101,6 +101,7 @@ class SUBnote:public SynthNote | |||
int oldpitchwheel, oldbandwidth; | |||
float globalfiltercenterq; | |||
float velocity; | |||
WatchManager *wm; | |||
}; | |||
#endif |
@@ -108,6 +108,7 @@ class SynthNote | |||
const Controller &ctl; | |||
const SYNTH_T &synth; | |||
const AbsTime &time; | |||
WatchManager *wm; | |||
}; | |||
#endif |
@@ -0,0 +1,163 @@ | |||
/* | |||
ZynAddSubFX - a software synthesizer | |||
WatchPoint.cpp - Synthesis State Watcher | |||
Copyright (C) 2015-2015 Mark McCurry | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of version 2 of the GNU General Public License | |||
as published by the Free Software Foundation. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License (version 2 or later) for more details. | |||
You should have received a copy of the GNU General Public License (version 2) | |||
along with this program; if not, write to the Free Software Foundation, | |||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
*/ | |||
#include "WatchPoint.h" | |||
#include <cstring> | |||
#include <rtosc/thread-link.h> | |||
WatchPoint::WatchPoint(WatchManager *ref, const char *prefix, const char *id) | |||
:active(false), samples_left(0), reference(ref) | |||
{ | |||
identity[0] = 0; | |||
if(prefix) | |||
strncpy(identity, prefix, 128); | |||
if(id) | |||
strncat(identity, id, 128); | |||
} | |||
bool WatchPoint::is_active(void) | |||
{ | |||
//Either the watchpoint is already active or the watchpoint manager has | |||
//received another activation this frame | |||
if(active) | |||
return true; | |||
if(reference && reference->active(identity)) { | |||
active = true; | |||
samples_left = 1; | |||
return true; | |||
} | |||
return false; | |||
} | |||
FloatWatchPoint::FloatWatchPoint(WatchManager *ref, const char *prefix, const char *id) | |||
:WatchPoint(ref, prefix, id) | |||
{} | |||
VecWatchPoint::VecWatchPoint(WatchManager *ref, const char *prefix, const char *id) | |||
:WatchPoint(ref, prefix, id) | |||
{} | |||
WatchManager::WatchManager(thrlnk *link) | |||
:write_back(link), new_active(false) | |||
{ | |||
memset(active_list, 0, sizeof(active_list)); | |||
memset(sample_list, 0, sizeof(sample_list)); | |||
memset(data_list, 0, sizeof(data_list)); | |||
memset(deactivate, 0, sizeof(deactivate)); | |||
} | |||
void WatchManager::add_watch(const char *id) | |||
{ | |||
//Apply to a free slot | |||
for(int i=0; i<MAX_WATCH; ++i) { | |||
if(!active_list[i][0]) { | |||
strncpy(active_list[i], id, 128); | |||
new_active = true; | |||
sample_list[i] = 0; | |||
break; | |||
} | |||
} | |||
} | |||
void WatchManager::del_watch(const char *id) | |||
{ | |||
//Queue up the delete | |||
for(int i=0; i<MAX_WATCH; ++i) | |||
if(!strcmp(active_list[i], id)) | |||
return (void) (deactivate[i] = true); | |||
} | |||
void WatchManager::tick(void) | |||
{ | |||
//Try to send out any vector stuff | |||
for(int i=0; i<MAX_WATCH; ++i) { | |||
if(sample_list[i]) { | |||
char arg_types[MAX_SAMPLE+1] = {0}; | |||
rtosc_arg_t arg_val[MAX_SAMPLE]; | |||
for(int j=0; j<sample_list[i]; ++j) { | |||
arg_types[j] = 'f'; | |||
arg_val[j].f = data_list[i][j]; | |||
} | |||
write_back->writeArray(active_list[i], arg_types, arg_val); | |||
deactivate[i] = true; | |||
} | |||
} | |||
//Cleanup internal data | |||
new_active = false; | |||
//Clear deleted slots | |||
for(int i=0; i<MAX_WATCH; ++i) { | |||
if(deactivate[i]) { | |||
memset(active_list[i], 0, 128); | |||
sample_list[i] = 0; | |||
deactivate[i] = false; | |||
} | |||
} | |||
} | |||
bool WatchManager::active(const char *id) const | |||
{ | |||
assert(this); | |||
assert(id); | |||
if(new_active || true) | |||
for(int i=0; i<MAX_WATCH; ++i) | |||
if(!strcmp(active_list[i], id)) | |||
return true; | |||
return false; | |||
} | |||
int WatchManager::samples(const char *id) const | |||
{ | |||
for(int i=0; i<MAX_WATCH; ++i) | |||
if(!strcmp(active_list[i], id)) | |||
return sample_list[i]; | |||
return 0; | |||
} | |||
void WatchManager::satisfy(const char *id, float f) | |||
{ | |||
//printf("trying to satisfy '%s'\n", id); | |||
if(write_back) | |||
write_back->write(id, "f", f); | |||
del_watch(id); | |||
} | |||
void WatchManager::satisfy(const char *id, float *f, int n) | |||
{ | |||
int selected = -1; | |||
for(int i=0; i<MAX_WATCH; ++i) | |||
if(!strcmp(active_list[i], id)) | |||
selected = i; | |||
if(selected == -1) | |||
return; | |||
//FIXME buffer overflow | |||
for(int i=0; i<n; ++i) | |||
data_list[selected][sample_list[selected]++] = f[i]; | |||
} |
@@ -0,0 +1,81 @@ | |||
/* | |||
ZynAddSubFX - a software synthesizer | |||
WatchPoint.h - Synthesis State Watcher | |||
Copyright (C) 2015-2015 Mark McCurry | |||
Author: Mark McCurry | |||
This program is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU General Public License | |||
as published by the Free Software Foundation; either version 2 | |||
of the License, or (at your option) any later version. | |||
*/ | |||
#pragma once | |||
struct WatchManager; | |||
namespace rtosc {class ThreadLink;} | |||
struct WatchPoint | |||
{ | |||
bool active; | |||
int samples_left; | |||
WatchManager *reference; | |||
char identity[128]; | |||
WatchPoint(WatchManager *ref, const char *prefix, const char *id); | |||
bool is_active(void); | |||
}; | |||
#define MAX_WATCH 16 | |||
#define MAX_WATCH_PATH 128 | |||
#define MAX_SAMPLE 128 | |||
struct WatchManager | |||
{ | |||
typedef rtosc::ThreadLink thrlnk; | |||
thrlnk *write_back; | |||
bool new_active; | |||
char active_list[MAX_WATCH][MAX_WATCH_PATH]; | |||
float data_list[MAX_SAMPLE][MAX_WATCH]; | |||
int sample_list[MAX_WATCH]; | |||
bool deactivate[MAX_WATCH]; | |||
//External API | |||
WatchManager(thrlnk *link=0); | |||
void add_watch(const char *); | |||
void del_watch(const char *); | |||
void tick(void); | |||
//Watch Point Query API | |||
bool active(const char *) const; | |||
int samples(const char *) const; | |||
//Watch Point Response API | |||
void satisfy(const char *, float); | |||
void satisfy(const char *, float*, int); | |||
}; | |||
struct FloatWatchPoint:public WatchPoint | |||
{ | |||
FloatWatchPoint(WatchManager *ref, const char *prefix, const char *id); | |||
inline void operator()(float f) | |||
{ | |||
if(is_active() && reference) { | |||
reference->satisfy(identity, f); | |||
active = false; | |||
} | |||
} | |||
}; | |||
//basically the same as the float watch point, only it consumes tuples | |||
struct VecWatchPoint : public WatchPoint | |||
{ | |||
VecWatchPoint(WatchManager *ref, const char *prefix, const char *id); | |||
inline void operator()(float *f, int n) | |||
{ | |||
if(is_active() && reference) { | |||
reference->satisfy(identity, f, n); | |||
active = false; | |||
} | |||
} | |||
}; |
@@ -407,7 +407,7 @@ class UI_Interface:public Fl_Osc_Interface | |||
virtual void damage(const char *path) override | |||
{ | |||
#ifndef NO_UI | |||
printf("\n\nDamage(\"%s\")\n", path); | |||
//printf("\n\nDamage(\"%s\")\n", path); | |||
std::set<Fl_Osc_Widget*> to_update; | |||
for(auto pair:map) { | |||
if(strstr(pair.first.c_str(), path)) { | |||
@@ -428,7 +428,7 @@ class UI_Interface:public Fl_Osc_Interface | |||
//DEBUG | |||
//if(strcmp(msg, "/vu-meter"))//Ignore repeated message | |||
// printf("trying the link for a '%s'<%s>\n", msg, rtosc_argument_string(msg)); | |||
const char *handle = rindex(msg,'/'); | |||
const char *handle = strrchr(msg,'/'); | |||
if(handle) | |||
++handle; | |||
@@ -312,6 +312,11 @@ if (filename==NULL) return; | |||
osc->write("/load_xlz", "s", filename);} | |||
xywh {40 40 100 20} | |||
} | |||
MenuItem {} { | |||
label {Clear Midi Learn...} | |||
callback {osc->write("/clear_xlz", "");} | |||
xywh {40 40 100 20} | |||
} | |||
MenuItem {} { | |||
label {Save Midi Learn...} | |||
callback {char *filename; | |||
@@ -1485,7 +1490,7 @@ selectuiwindow->hide();} | |||
xywh {10 165 100 35} color 229 labelfont 1 labelsize 16 | |||
} | |||
Fl_Box {} { | |||
label {.. if you have used ZynAddSubFX before, or you like to have full controll to all parameters.} | |||
label {.. if you have used ZynAddSubFX before, or you like to have full control to all parameters.} | |||
xywh {110 165 310 35} labelfont 1 labelsize 11 align 144 | |||
} | |||
Fl_Button {} { | |||
@@ -470,7 +470,7 @@ class UI_Interface:public Fl_Osc_Interface | |||
//DEBUG | |||
//if(strcmp(msg, "/vu-meter"))//Ignore repeated message | |||
// printf("trying the link for a '%s'<%s>\n", msg, rtosc_argument_string(msg)); | |||
const char *handle = rindex(msg,'/'); | |||
const char *handle = strrchr(msg,'/'); | |||
if(handle) | |||
++handle; | |||
@@ -572,9 +572,11 @@ const char *help_message = | |||
"zynaddsubfx-ext-gui [options] uri - Connect to remote ZynAddSubFX\n" | |||
" --help print this help message\n" | |||
" --no-uri run without a remote ZynAddSubFX\n" | |||
" --embed window ID [Internal Flag For Embedding Windows]\n" | |||
"\n" | |||
" example: zynaddsubfx-ext-gui osc.udp://localhost:1234/\n" | |||
" use the -P option for zynaddsubfx to specify the port of the backend\n"; | |||
" This will connect to a running zynaddsubfx instance on the same\n" | |||
" machine on port 1234.\n"; | |||
#ifndef CARLA_VERSION_STRING | |||
int main(int argc, char *argv[]) | |||
@@ -45,6 +45,7 @@ class EnvelopeParams; | |||
class LFOParams; | |||
class FilterParams; | |||
struct WatchManager; | |||
class LFO; | |||
class Envelope; | |||
class OscilGen; | |||
@@ -3,7 +3,7 @@ | |||
main.cpp - Main file of the synthesizer | |||
Copyright (C) 2002-2005 Nasca Octavian Paul | |||
Copyright (C) 2012-2014 Mark McCurry | |||
Copyright (C) 2012-2016 Mark McCurry | |||
This program is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU General Public License | |||
@@ -35,6 +35,7 @@ | |||
#include "Misc/Master.h" | |||
#include "Misc/Part.h" | |||
#include "Misc/Util.h" | |||
#include "version.h" | |||
//Nio System | |||
#include "Nio/Nio.h" | |||
@@ -364,7 +365,7 @@ int main(int argc, char *argv[]) | |||
synth.alias(); | |||
if(exitwithversion) { | |||
cout << "Version: " << VERSION << endl; | |||
cout << "Version: " << version << endl; | |||
return 0; | |||
} | |||
if(exitwithhelp != 0) { | |||
@@ -382,7 +383,7 @@ int main(int argc, char *argv[]) | |||
" -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n" | |||
<< " -N , --named\t\t\t\t Postfix IO Name when possible\n" | |||
<< " -a , --auto-connect\t\t\t AutoConnect when using JACK\n" | |||
<< " -A , --auto-save=INTERVAL\t\t Automatically save at interval (disabled for negative intervals)\n" | |||
<< " -A , --auto-save=INTERVAL\t\t Automatically save at interval (disabled with 0 interval)\n" | |||
<< " -p , --pid-in-client-name\t\t Append PID to (JACK) " | |||
"client name\n" | |||
<< " -P , --preferred-port\t\t\t Preferred OSC Port\n" | |||
@@ -489,7 +490,7 @@ int main(int argc, char *argv[]) | |||
"Default IO did not initialize.\nDefaulting to NULL backend."); | |||
} | |||
if(auto_save_interval >= 0) { | |||
if(auto_save_interval > 0) { | |||
int old_save = middleware->checkAutoSave(); | |||
if(old_save > 0) | |||
GUI::raiseUi(gui, "/alert-reload", "i", old_save); | |||
@@ -342,6 +342,16 @@ void MidiMappernRT::delMapping(int ID, bool coarse, const char *addr){ | |||
}; | |||
void MidiMappernRT::replaceMapping(int, bool, const char *){}; | |||
void MidiMappernRT::clear(void) | |||
{ | |||
storage = new MidiMapperStorage(); | |||
learnQueue.clear(); | |||
inv_map.clear(); | |||
char buf[1024]; | |||
rtosc_message(buf, 1024, "/midi-learn/midi-bind", "b", sizeof(storage), &storage); | |||
rt_cb(buf); | |||
} | |||
std::map<std::string, std::string> MidiMappernRT::getMidiMappingStrings(void) | |||
@@ -16,6 +16,10 @@ class UndoHistoryImpl | |||
UndoHistoryImpl(void) | |||
:max_history_size(20) | |||
{} | |||
~UndoHistoryImpl(void) | |||
{ | |||
clear(); | |||
} | |||
std::deque<pair<time_t, const char *>> history; | |||
long history_pos; | |||
unsigned max_history_size;//XXX Expose this via a public API | |||
@@ -24,6 +28,7 @@ class UndoHistoryImpl | |||
void rewind(const char *msg); | |||
void replay(const char *msg); | |||
bool mergeEvent(time_t t, const char *msg, char *buf, size_t N); | |||
void clear(void); | |||
}; | |||
UndoHistory::UndoHistory(void) | |||
@@ -32,6 +37,11 @@ UndoHistory::UndoHistory(void) | |||
impl->history_pos = 0; | |||
} | |||
UndoHistory::~UndoHistory(void) | |||
{ | |||
delete impl; | |||
} | |||
void UndoHistory::recordEvent(const char *msg) | |||
{ | |||
//TODO Properly account for when you have traveled back in time. | |||
@@ -121,6 +131,14 @@ bool UndoHistoryImpl::mergeEvent(time_t now, const char *msg, char *buf, size_t | |||
return false; | |||
} | |||
void UndoHistoryImpl::clear(void) | |||
{ | |||
for(auto elm : history) | |||
delete [] elm.second; | |||
history.clear(); | |||
history_pos = 0; | |||
} | |||
void UndoHistory::seekHistory(int distance) | |||
@@ -200,8 +200,8 @@ static bool is_charwise(uint8_t c) | |||
int rtosc_subpath_pat_type(const char *pattern) | |||
{ | |||
int charwise_only = 1; | |||
const char *last_star = rindex(pattern, '*'); | |||
const char *pound = index(pattern, '#'); | |||
const char *last_star = strrchr(pattern, '*'); | |||
const char *pound = strchr(pattern, '#'); | |||
if(!strcmp("*", pattern)) | |||
return RTOSC_MATCH_ALL; | |||
@@ -115,6 +115,8 @@ class MidiMappernRT | |||
void delMapping(int ID, bool coarse, const char *addr); | |||
void replaceMapping(int, bool, const char *); | |||
void clear(void); | |||
std::map<std::string, std::string> getMidiMappingStrings(void); | |||
//unclear if this should be be here as a helper or not | |||
@@ -144,7 +144,7 @@ struct rtosc_hack_decltype_t | |||
#define rArrayF(name, length, ...) \ | |||
{STRINGIFY(name) "#" STRINGIFY(length) "::f", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayFCb(name)} | |||
#define rArray(name, length, ...) \ | |||
{STRINGIFY(name) "#" STRINGIFY(length) "::c", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayCb(name)} | |||
{STRINGIFY(name) "#" STRINGIFY(length) "::c:i", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayCb(name)} | |||
#define rArrayT(name, length, ...) \ | |||
{STRINGIFY(name) "#" STRINGIFY(length) "::T:F", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayTCb(name)} | |||
#define rArrayI(name, length, ...) \ | |||
@@ -54,12 +54,18 @@ struct RtData | |||
const Port *port; | |||
const char *message; | |||
virtual void replyArray(const char *path, const char *args, | |||
rtosc_arg_t *vals){}; | |||
virtual void reply(const char *path, const char *args, ...); | |||
virtual void reply(const char *msg); | |||
virtual void chain(const char *path, const char *args, ...){}; | |||
virtual void chain(const char *msg){}; | |||
virtual void chainArray(const char *path, const char *args, | |||
rtosc_arg_t *vals){}; | |||
virtual void broadcast(const char *path, const char *args, ...); | |||
virtual void broadcast(const char *msg); | |||
virtual void broadcastArray(const char *path, const char *args, | |||
rtosc_arg_t *vals){}; | |||
virtual void forward(const char *rational=NULL); | |||
}; | |||
@@ -13,6 +13,7 @@ class UndoHistory | |||
//TODO think about the consequences of largish loads | |||
public: | |||
UndoHistory(void); | |||
~UndoHistory(void); | |||
//Records any undoable event | |||
void recordEvent(const char *msg); | |||
@@ -0,0 +1,46 @@ | |||
/* | |||
ZynAddSubFX - a software synthesizer | |||
version.cpp - implementation of version_type class | |||
Copyright (C) 2016 Johannes Lorenz | |||
Author: Johannes Lorenz | |||
This program is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU General Public License | |||
as published by the Free Software Foundation; either version 2 | |||
of the License, or (at your option) any later version. | |||
*/ | |||
#include <iostream> | |||
#include "version.h" | |||
constexpr int version_type::v_strcmp(const version_type& v2, int i) const | |||
{ | |||
return (i == sizeof(version)) | |||
? 0 | |||
: ((version[i] == v2.version[i]) | |||
? v_strcmp(v2, i+1) | |||
: (version[i] - v2.version[i])); | |||
} | |||
constexpr bool version_type::operator<(const version_type& other) const | |||
{ | |||
return v_strcmp(other, 0) < 0; | |||
} | |||
std::ostream& operator<< (std::ostream& os, | |||
const version_type& v) | |||
{ | |||
return os << v.major() << '.' | |||
<< v.minor() << '.' | |||
<< v.revision(); | |||
} | |||
static_assert(!(version_type(3,1,1) < version_type(1,3,3)), | |||
"version operator failed"); | |||
static_assert(version_type(2,9,9) < version_type(3,4,3), | |||
"version operator failed"); | |||
static_assert(!(version_type(2,4,3) < version_type(2,4,3)), | |||
"version operator failed"); | |||
@@ -0,0 +1,59 @@ | |||
/* | |||
ZynAddSubFX - a software synthesizer | |||
version.h - declaration of version_type class | |||
contains the current zynaddsubfx version | |||
Copyright (C) 2016 Johannes Lorenz | |||
Author: Johannes Lorenz | |||
This program is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU General Public License | |||
as published by the Free Software Foundation; either version 2 | |||
of the License, or (at your option) any later version. | |||
*/ | |||
#ifndef VERSION_H | |||
#define VERSION_H | |||
#include <iosfwd> | |||
//! class containing a zynaddsubfx version | |||
class version_type | |||
{ | |||
char version[3]; | |||
// strcmp-like comparison against another version_type | |||
constexpr int v_strcmp(const version_type& v2, int i) const; | |||
public: | |||
constexpr version_type(char maj, char min, char rev) : | |||
version{maj, min, rev} | |||
{ | |||
} | |||
//! constructs the current zynaddsubfx version | |||
constexpr version_type() : | |||
version_type(2, 5, 4) | |||
{ | |||
} | |||
void set_major(int maj) { version[0] = maj; } | |||
void set_minor(int min) { version[1] = min; } | |||
void set_revision(int rev) { version[2] = rev; } | |||
int major() const { return version[0]; } | |||
int minor() const { return version[1]; } | |||
int revision() const { return version[2]; } | |||
constexpr bool operator<(const version_type& other) const; | |||
//! prints version as <major>.<minor>.<revision> | |||
friend std::ostream& operator<< (std::ostream& os, | |||
const version_type& v); | |||
}; | |||
//! the current zynaddsubfx version | |||
constexpr version_type version; | |||
#endif | |||