//*********************************************************************************************** //Impromptu Modular: Modules for VCV Rack by Marc Boulé //*********************************************************************************************** #ifndef FOUNDRY_SEQUENCER_KERNEL_HPP #define FOUNDRY_SEQUENCER_KERNEL_HPP #include "ImpromptuModular.hpp" namespace rack_plugin_ImpromptuModular { class StepAttributes { unsigned long attributes; public: static const unsigned long ATT_MSK_GATE = 0x01000000, gateShift = 24; static const unsigned long ATT_MSK_GATEP = 0x02000000; static const unsigned long ATT_MSK_SLIDE = 0x04000000; static const unsigned long ATT_MSK_TIED = 0x08000000; static const unsigned long ATT_MSK_GATETYPE = 0xF0000000, gateTypeShift = 28; static const unsigned long ATT_MSK_VELOCITY = 0x000000FF, velocityShift = 0; static const unsigned long ATT_MSK_GATEP_VAL = 0x0000FF00, gatePValShift = 8; static const unsigned long ATT_MSK_SLIDE_VAL = 0x00FF0000, slideValShift = 16; static const int INIT_VELOCITY = 100; static const int MAX_VELOCITY = 200; static const int INIT_PROB = 50;// range is 0 to 100 static const int INIT_SLIDE = 10;// range is 0 to 100 static const unsigned long ATT_MSK_INITSTATE = ((ATT_MSK_GATE) | (INIT_VELOCITY << velocityShift) | (INIT_PROB << gatePValShift) | (INIT_SLIDE << slideValShift)); inline void clear() {attributes = 0ul;} inline void init() {attributes = ATT_MSK_INITSTATE;} inline void randomize() {attributes = ( (randomu32() & (ATT_MSK_GATE | ATT_MSK_GATEP | ATT_MSK_SLIDE /*| ATT_MSK_TIED*/)) | ((randomu32() % 101) << gatePValShift) | ((randomu32() % 101) << slideValShift) | (randomu32() % (MAX_VELOCITY + 1)) ) ;} inline bool getGate() {return (attributes & ATT_MSK_GATE) != 0;} inline int getGateType() {return (int)((attributes & ATT_MSK_GATETYPE) >> gateTypeShift);} inline bool getTied() {return (attributes & ATT_MSK_TIED) != 0;} inline bool getGateP() {return (attributes & ATT_MSK_GATEP) != 0;} inline int getGatePVal() {return (int)((attributes & ATT_MSK_GATEP_VAL) >> gatePValShift);} inline bool getSlide() {return (attributes & ATT_MSK_SLIDE) != 0;} inline int getSlideVal() {return (int)((attributes & ATT_MSK_SLIDE_VAL) >> slideValShift);} inline int getVelocityVal() {return (int)((attributes & ATT_MSK_VELOCITY) >> velocityShift);} inline unsigned long getAttribute() {return attributes;} inline void setGate(bool gate1State) {attributes &= ~ATT_MSK_GATE; if (gate1State) attributes |= ATT_MSK_GATE;} inline void setGateType(int gateType) {attributes &= ~ATT_MSK_GATETYPE; attributes |= (((unsigned long)gateType) << gateTypeShift);} inline void setTied(bool tiedState) { attributes &= ~ATT_MSK_TIED; if (tiedState) { attributes |= ATT_MSK_TIED; attributes &= ~(ATT_MSK_GATE | ATT_MSK_GATEP | ATT_MSK_SLIDE);// clear other attributes if tied } } inline void setGateP(bool GatePState) {attributes &= ~ATT_MSK_GATEP; if (GatePState) attributes |= ATT_MSK_GATEP;} inline void setGatePVal(int gatePval) {attributes &= ~ATT_MSK_GATEP_VAL; attributes |= (((unsigned long)gatePval) << gatePValShift);} inline void setSlide(bool slideState) {attributes &= ~ATT_MSK_SLIDE; if (slideState) attributes |= ATT_MSK_SLIDE;} inline void setSlideVal(int slideVal) {attributes &= ~ATT_MSK_SLIDE_VAL; attributes |= (((unsigned long)slideVal) << slideValShift);} inline void setVelocityVal(int _velocity) {attributes &= ~ATT_MSK_VELOCITY; attributes |= (((unsigned long)_velocity) << velocityShift);} inline void setAttribute(unsigned long _attributes) {attributes = _attributes;} };// class StepAttributes //***************************************************************************** class Phrase { // a phrase is a sequence number and a number of repetitions; it is used to make a song unsigned long phrase; public: static const unsigned long PHR_MSK_SEQNUM = 0x00FF; static const unsigned long PHR_MSK_REPS = 0xFF00, repShift = 8;// a rep is 0 to 99 inline void init() {phrase = (1 << repShift);} inline void randomize(int maxSeqs) {phrase = ((randomu32() % maxSeqs) | ((randomu32() % 4 + 1) << repShift));} inline int getSeqNum() {return (int)(phrase & PHR_MSK_SEQNUM);} inline int getReps() {return (int)((phrase & PHR_MSK_REPS) >> repShift);} inline unsigned long getPhraseJson() {return phrase - (1 << repShift);}// compression trick (store 0 instead of 1) inline void setSeqNum(int seqn) {phrase &= ~PHR_MSK_SEQNUM; phrase |= ((unsigned long)seqn);} inline void setReps(int _reps) {phrase &= ~PHR_MSK_REPS; phrase |= (((unsigned long)_reps) << repShift);} inline void setPhraseJson(unsigned long _phrase) {phrase = (_phrase + (1 << repShift));}// compression trick (store 0 instead of 1) };// class Phrase //***************************************************************************** class SeqAttributes { unsigned long attributes; public: static const unsigned long SEQ_MSK_LENGTH = 0x000000FF;// number of steps in each sequence, min value is 1 static const unsigned long SEQ_MSK_RUNMODE = 0x0000FF00, runModeShift = 8; static const unsigned long SEQ_MSK_TRANSPOSE = 0x007F0000, transposeShift = 16; static const unsigned long SEQ_MSK_TRANSIGN = 0x00800000;// manually implement sign bit static const unsigned long SEQ_MSK_ROTATE = 0x7F000000, rotateShift = 24; static const unsigned long SEQ_MSK_ROTSIGN = 0x80000000;// manually implement sign bit (+ is right, - is left) inline void init(int length, int runMode) {attributes = ((length) | (((unsigned long)runMode) << runModeShift));} inline void randomize(int maxSteps, int numModes) {attributes = ( (1 + (randomu32() % maxSteps)) | (((unsigned long)(randomu32() % numModes) << runModeShift)) );} inline int getLength() {return (int)(attributes & SEQ_MSK_LENGTH);} inline int getRunMode() {return (int)((attributes & SEQ_MSK_RUNMODE) >> runModeShift);} inline int getTranspose() { int ret = (int)((attributes & SEQ_MSK_TRANSPOSE) >> transposeShift); if ( (attributes & SEQ_MSK_TRANSIGN) != 0)// if negative ret *= -1; return ret; } inline int getRotate() { int ret = (int)((attributes & SEQ_MSK_ROTATE) >> rotateShift); if ( (attributes & SEQ_MSK_ROTSIGN) != 0)// if negative ret *= -1; return ret; } inline unsigned long getSeqAttrib() {return attributes;} inline void setLength(int length) {attributes &= ~SEQ_MSK_LENGTH; attributes |= ((unsigned long)length);} inline void setRunMode(int runMode) {attributes &= ~SEQ_MSK_RUNMODE; attributes |= (((unsigned long)runMode) << runModeShift);} inline void setTranspose(int transp) { attributes &= ~ (SEQ_MSK_TRANSPOSE | SEQ_MSK_TRANSIGN); attributes |= (((unsigned long)abs(transp)) << transposeShift); if (transp < 0) attributes |= SEQ_MSK_TRANSIGN; } inline void setRotate(int rotn) { attributes &= ~ (SEQ_MSK_ROTATE | SEQ_MSK_ROTSIGN); attributes |= (((unsigned long)abs(rotn)) << rotateShift); if (rotn < 0) attributes |= SEQ_MSK_ROTSIGN; } inline void setSeqAttrib(unsigned long _attributes) {attributes = _attributes;} };// class SeqAttributes //***************************************************************************** // SequencerKernel //***************************************************************************** struct SeqCPbuffer; struct SongCPbuffer; class SequencerKernel { public: // Sequencer kernel dimensions static const int MAX_STEPS = 32;// must be a power of two (some multi select loops have bitwise "& (MAX_STEPS - 1)") static const int MAX_SEQS = 64; static const int MAX_PHRASES = 99;// maximum value is 99 (index value is 0 to 98; disp will be 1 to 99) // Run modes enum RunModeIds {MODE_FWD, MODE_REV, MODE_PPG, MODE_PEN, MODE_BRN, MODE_RND, MODE_TKA, NUM_MODES}; static const std::string modeLabels[NUM_MODES]; private: // Gate types static const int NUM_GATES = 12; static const uint64_t advGateHitMaskLow[NUM_GATES]; static const uint64_t advGateHitMaskHigh[NUM_GATES]; static constexpr float INIT_CV = 0.0f; int id; std::string ids; // Need to save int pulsesPerStep;// stored range is [1:49] so must ALWAYS read thgouth getPulsesPerStep(). Must do this because of knob int delay; int runModeSong; int songBeginIndex; int songEndIndex; Phrase phrases[MAX_PHRASES];// This is the song (series of phases; a phrase is a sequence number and a repetition value) SeqAttributes sequences[MAX_SEQS]; float cv[MAX_SEQS][MAX_STEPS];// [-3.0 : 3.917]. StepAttributes attributes[MAX_SEQS][MAX_STEPS]; char dirty[MAX_SEQS]; // No need to save int stepIndexRun; unsigned long stepIndexRunHistory; int phraseIndexRun; unsigned long phraseIndexRunHistory; int ppqnCount; int ppqnLeftToSkip;// used in clock delay int gateCode;// -1 = Killed for all pulses of step, 0 = Low for current pulse of step, 1 = High for current pulse of step, 2 = Clk high pulse, 3 = 1ms trig unsigned long slideStepsRemain;// 0 when no slide under way, downward step counter when sliding float slideCVdelta;// no need to initialize, this is only used when slideStepsRemain is not 0 SequencerKernel *masterKernel;// nullprt for track 0, used for grouped run modes (tracks B,C,D follow A when random, for example) bool* holdTiedNotesPtr; unsigned long clockPeriod;// counts number of step() calls upward from last clock (reset after clock processed) bool moveStepIndexRunIgnore; public: void construct(int _id, SequencerKernel *_masterKernel, bool* _holdTiedNotesPtr); // don't want regaular constructor mechanism inline int getRunModeSong() {return runModeSong;} inline int getRunModeSeq(int seqn) {return sequences[seqn].getRunMode();} inline int getBegin() {return songBeginIndex;} inline int getEnd() {return songEndIndex;} inline int getLength(int seqn) {return sequences[seqn].getLength();} inline int getPhraseSeq(int phrn) {return phrases[phrn].getSeqNum();} inline int getPhraseReps(int phrn) {return phrases[phrn].getReps();} inline int getPulsesPerStep() {return (pulsesPerStep > 2 ? ((pulsesPerStep - 1) << 1) : pulsesPerStep);} inline int getDelay() {return delay;} inline int getTransposeOffset(int seqn) {return sequences[seqn].getTranspose();} inline int getRotateOffset(int seqn) {return sequences[seqn].getRotate();} inline int getStepIndexRun() {return stepIndexRun;} inline int getPhraseIndexRun() {return phraseIndexRun;} inline float getCV(int seqn, int stepn) {return cv[seqn][stepn];} inline float getCVRun() {return cv[phrases[phraseIndexRun].getSeqNum()][stepIndexRun];} inline StepAttributes getAttribute(int seqn, int stepn) {return attributes[seqn][stepn];} inline StepAttributes getAttributeRun() {return attributes[phrases[phraseIndexRun].getSeqNum()][stepIndexRun];} inline bool getGate(int seqn, int stepn) {return attributes[seqn][stepn].getGate();} inline bool getGateP(int seqn, int stepn) {return attributes[seqn][stepn].getGateP();} inline bool getSlide(int seqn, int stepn) {return attributes[seqn][stepn].getSlide();} inline bool getTied(int seqn, int stepn) {return attributes[seqn][stepn].getTied();} inline int getGatePVal(int seqn, int stepn) {return attributes[seqn][stepn].getGatePVal();} inline int getSlideVal(int seqn, int stepn) {return attributes[seqn][stepn].getSlideVal();} inline int getVelocityVal(int seqn, int stepn) {return attributes[seqn][stepn].getVelocityVal();} inline int getVelocityValRun() {return getAttributeRun().getVelocityVal();} inline int getGateType(int seqn, int stepn) {return attributes[seqn][stepn].getGateType();} inline void setPhraseIndexRun(int _phraseIndexRun) {phraseIndexRun = _phraseIndexRun;} inline void setPulsesPerStep(int _pps) {pulsesPerStep = _pps;} inline void setDelay(int _delay) {delay = _delay;} inline void setLength(int seqn, int _length) {sequences[seqn].setLength(_length);} inline void setPhraseReps(int phrn, int _reps) {phrases[phrn].setReps(_reps);} inline void setPhraseSeqNum(int phrn, int _seqn) {phrases[phrn].setSeqNum(_seqn);} inline void setBegin(int phrn) {songBeginIndex = phrn; songEndIndex = max(phrn, songEndIndex);} inline void setEnd(int phrn) {songEndIndex = phrn; songBeginIndex = min(phrn, songBeginIndex);} inline void setRunModeSong(int _runMode) {runModeSong = _runMode;} inline void setRunModeSeq(int seqn, int _runMode) {sequences[seqn].setRunMode(_runMode);} void setGate(int seqn, int stepn, bool newGate, int count); void setGateP(int seqn, int stepn, bool newGateP, int count); void setSlide(int seqn, int stepn, bool newSlide, int count); void setTied(int seqn, int stepn, bool newTied, int count); void setGatePVal(int seqn, int stepn, int gatePval, int count); void setSlideVal(int seqn, int stepn, int slideVal, int count); void setVelocityVal(int seqn, int stepn, int velocity, int count); void setGateType(int seqn, int stepn, int gateType, int count); void setMoveStepIndexRunIgnore() {moveStepIndexRunIgnore = true;} inline int modRunModeSong(int delta) { runModeSong = clamp(runModeSong += delta, 0, NUM_MODES - 1); return runModeSong; } inline int modRunModeSeq(int seqn, int delta) { int rVal = sequences[seqn].getRunMode(); rVal = clamp(rVal + delta, 0, NUM_MODES - 1); sequences[seqn].setRunMode(rVal); return rVal; } inline int modLength(int seqn, int delta) { int lVal = sequences[seqn].getLength(); lVal = clamp(lVal + delta, 1, MAX_STEPS); sequences[seqn].setLength(lVal); return lVal; } inline int modPhraseSeqNum(int phrn, int delta) { int seqn = phrases[phrn].getSeqNum(); seqn = moveIndex(seqn, seqn + delta, MAX_SEQS); phrases[phrn].setSeqNum(seqn); return seqn; } inline int modPhraseReps(int phrn, int delta) { int rVal = phrases[phrn].getReps(); rVal = clamp(rVal + delta, 0, 99); phrases[phrn].setReps(rVal); return rVal; } inline int modPulsesPerStep(int delta) { pulsesPerStep += delta; if (pulsesPerStep < 1) pulsesPerStep = 1; if (pulsesPerStep > 49) pulsesPerStep = 49; return pulsesPerStep; } inline int modDelay(int delta) { delay = clamp(delay + delta, 0, 99); return delay; } inline int modGatePVal(int seqn, int stepn, int delta, int count) { int pVal = getGatePVal(seqn, stepn); pVal = clamp(pVal + delta, 0, 100); setGatePVal(seqn, stepn, pVal, count); return pVal; } inline int modSlideVal(int seqn, int stepn, int delta, int count) { int sVal = getSlideVal(seqn, stepn); sVal = clamp(sVal + delta, 0, 100); setSlideVal(seqn, stepn, sVal, count); return sVal; } inline int modVelocityVal(int seqn, int stepn, int delta, int upperLimit, int count) { int vVal = getVelocityVal(seqn, stepn); vVal = clamp(vVal + delta, 0, upperLimit); setVelocityVal(seqn, stepn, vVal, count); return vVal; } inline void decSlideStepsRemain() {if (slideStepsRemain > 0ul) slideStepsRemain--;} inline bool toggleGate(int seqn, int stepn, int count) { bool newGate = !attributes[seqn][stepn].getGate(); setGate(seqn, stepn, newGate, count); return newGate; } inline bool toggleGateP(int seqn, int stepn, int count) { bool newGateP = !attributes[seqn][stepn].getGateP(); setGateP(seqn, stepn, newGateP, count); return newGateP; } inline bool toggleSlide(int seqn, int stepn, int count) { bool newSlide = !attributes[seqn][stepn].getSlide(); setSlide(seqn, stepn, newSlide, count); return newSlide; } inline bool toggleTied(int seqn, int stepn, int count) { bool newTied = !attributes[seqn][stepn].getTied(); setTied(seqn, stepn, newTied, count); return newTied; } float applyNewOctave(int seqn, int stepn, int newOct, int count); float applyNewKey(int seqn, int stepn, int newKeyIndex, int count); void writeCV(int seqn, int stepn, float newCV, int count); inline float calcSlideOffset() {return (slideStepsRemain > 0ul ? (slideCVdelta * (float)slideStepsRemain) : 0.0f);} inline bool calcGate(Trigger clockTrigger, float sampleRate) { if (ppqnLeftToSkip != 0) return false; if (gateCode < 2) return gateCode == 1; if (gateCode == 2) return clockTrigger.isHigh(); return clockPeriod < (unsigned long) (sampleRate * 0.01f); } inline void initPulsesPerStep() {pulsesPerStep = 1;} inline void initDelay() {delay = 0;} void initSequence(int seqn); void initSong(); void randomizeSequence(int seqn); void randomizeSong(); void copySequence(SeqCPbuffer* seqCPbuf, int seqn, int startCP, int countCP); void pasteSequence(SeqCPbuffer* seqCPbuf, int seqn, int startCP); void copySong(SongCPbuffer* songCPbuf, int startCP, int countCP); void pasteSong(SongCPbuffer* songCPbuf, int startCP); void reset(); void randomize(int seqn); void toJson(json_t *rootJ); void fromJson(json_t *rootJ); void initRun(); bool clockStep(); inline void step() { clockPeriod++; } int keyIndexToGateTypeEx(int keyIndex); void transposeSeq(int seqn, int delta); void unTransposeSeq(int seqn) { transposeSeq(seqn, getTransposeOffset(seqn) * -1); } void rotateSeq(int seqn, int delta); void unRotateSeq(int seqn) { rotateSeq(seqn, getRotateOffset(seqn) * -1); } private: void rotateSeqByOne(int seqn, bool directionRight); inline void propagateCVtoTied(int seqn, int stepn) { for (int i = stepn + 1; i < MAX_STEPS && attributes[seqn][i].getTied(); i++) cv[seqn][i] = cv[seqn][i - 1]; } void activateTiedStep(int seqn, int stepn); void deactivateTiedStep(int seqn, int stepn); void calcGateCodeEx(); bool moveStepIndexRun(bool init); void moveSongIndexBackward(bool init, bool rollover); void moveSongIndexForeward(bool init, bool rollover); int tempPhraseIndexes[MAX_PHRASES];// used only in next method void moveSongIndexRandom(bool init, uint32_t randomValue); void moveSongIndexBrownian(bool init, uint32_t randomValue); void movePhraseIndexRun(bool init); };// class SequencerKernel struct SeqCPbuffer { float cvCPbuffer[SequencerKernel::MAX_STEPS];// copy paste buffer for CVs StepAttributes attribCPbuffer[SequencerKernel::MAX_STEPS]; SeqAttributes seqAttribCPbuffer; int storedLength;// number of steps that contain actual cp data SeqCPbuffer() {reset();} void reset(); };// struct SeqCPbuffer struct SongCPbuffer { Phrase phraseCPbuffer[SequencerKernel::MAX_PHRASES]; int beginIndex; int endIndex; int runModeSong; int storedLength;// number of steps that contain actual cp data SongCPbuffer() {reset();} void reset(); };// song SeqCPbuffer } // namespace rack_plugin_ImpromptuModular #endif