summaryrefslogtreecommitdiff
path: root/sw/Core/Src/arp.h
diff options
context:
space:
mode:
Diffstat (limited to 'sw/Core/Src/arp.h')
-rwxr-xr-xsw/Core/Src/arp.h553
1 files changed, 553 insertions, 0 deletions
diff --git a/sw/Core/Src/arp.h b/sw/Core/Src/arp.h
new file mode 100755
index 0000000..96151f8
--- /dev/null
+++ b/sw/Core/Src/arp.h
@@ -0,0 +1,553 @@
+#pragma once
+
+u8 arpbits; // the output of the arpeggiator - which fingers are down
+bool arpretrig; // causes the notes to re-attack
+
+s8 curarpfinger;
+s8 arpoctave;
+u8 arpdir;
+s8 arp_nonpedalfinger;
+u8 arpused,arpused2;
+s8 arpmode;
+s32 freearpclock;
+#ifdef WIN32
+int __builtin_popcount(int x) {
+ int c = 0;
+ while (x) { c++; x &= x - 1; }
+ return c;
+}
+int __builtin_popcountll(unsigned long long x) {
+ int c = 0;
+ while (x) { c++; x &= x - 1; }
+ return c;
+}
+int __builtin_ctzll(unsigned long long x) {
+ if (!x) return 64;
+ int c = 0;
+ while (!(x&(1ull<<c))) { c++; }
+ return c;
+}
+#endif
+
+u8 pickrandombit(u8 mask) {
+ if (!mask) return 0;
+ int num = __builtin_popcount(mask);
+ num = rand() % num;
+ for (; num--;) mask &= mask - 1;
+ return mask ^ (mask & (mask-1));
+}
+u8 nextup(u8 allowedfingers) {
+ s8 prevarpfinger = curarpfinger;
+ while (1) {
+ curarpfinger = (curarpfinger + 1) & 7;
+ if (allowedfingers & (1 << curarpfinger))
+ break;
+ }
+ return (prevarpfinger >= curarpfinger);
+}
+u8 nextdown(u8 allowedfingers) {
+ s8 prevarpfinger = curarpfinger;
+ while (1) {
+ curarpfinger = (curarpfinger - 1) & 7;
+ if (allowedfingers & (1 << curarpfinger))
+ break;
+ }
+ return (prevarpfinger <= curarpfinger);
+}
+
+void arp_reset_impl(bool partial) { // a 'partial' reset is used when it panics that none of the fingers that are down match the currently set arp bits.
+#ifdef EMU
+ if (curarpfinger >= 0)
+ EmuDebugLog("!!ARP RESET\r\n");
+#endif
+ arpretrig = false;
+ arp_rhythm.trigcount = 0;
+ arpbits = 0;
+ arpused = 0;
+ arpused2 = 0;
+ arp_nonpedalfinger = -2;
+ freearpclock = 0;
+ arp_divide_counter = 0;
+ ticks_since_arp = 0;
+ if (!partial) {
+ curarpfinger = -1;
+ arpoctave = 0;
+ arpdir = 0;
+ }
+}
+
+ void arp_reset(void) {
+ arp_reset_impl(false);
+}
+
+static inline void arp_reset_partial(void) {
+ arp_reset_impl(true);
+}
+
+bool euclidstuff(euclid_state *s, int patlen, int prob, int arpmode) {
+ int aprob = clampi((abs(prob) + 256) >> 9, 0, 128);
+ int apatlen = abs(patlen);
+ bool click;
+ if (apatlen <= 1) {
+ if (arpmode == ARP_ALL)
+ click = true;
+ else
+ click = (rand() & 127) < aprob;
+ }
+ else {
+ float k = aprob * (1.f / 128.f); //arpeuclid / (float) (abspatlen);
+ click = (floor(s->trigcount * k) != floor(s->trigcount * k - k)); // euclidian rhythms!
+ }
+ // printf("%d: %d\n", arptrigcount, arpretrig);
+ s->trigcount++;
+ //arpretrig = true;
+ bool step; // do we go to the next step?
+ if (apatlen > 0) {
+ s->trigcount %= apatlen;
+ }
+ if ((patlen < 0) ^ (prob < 0)) {
+ // if patlen or prob are negative, then we clock at a regular rate but silence some steps
+ step = true;
+ s->supress = !click;
+ }
+ else {
+ // if they're both pos (or neg), then we simply clock at the erratic rate.
+ step = click;
+ int gatelen = param_eval_int(P_GATE_LENGTH, any_rnd, env16, pressure16) >> 8;
+ bool do_you_want_silence_in_long_bits = gatelen < 256;
+ s->supress = do_you_want_silence_in_long_bits ? !click : false;
+ }
+ s->did_a_retrig = click;
+// if (!click) {
+// int i = 1;
+// }
+ return step;
+}
+
+bool arpupwards(u8 allowedfingers, int minoctave, int maxoctave) {
+ bool wrap = nextup(allowedfingers);
+ if (wrap) {
+ if (++arpoctave > maxoctave) {
+ if (arpmode == ARP_UPDOWN || arpmode == ARP_PEDALUPDOWN) {
+ arpdir = 1;
+ arpoctave = maxoctave;
+ nextdown(allowedfingers);
+ nextdown(allowedfingers);
+ }
+ else if (arpmode == ARP_UPDOWNREP || arpmode == ARP_UPDOWN8) {
+ arpdir = 1;
+ arpoctave = maxoctave;
+ nextdown(allowedfingers);
+ }
+ else
+ arpoctave = minoctave;
+ }
+ }
+ return wrap;
+}
+
+bool arpdownwards(u8 allowedfingers, int minoctave, int maxoctave) {
+ bool wrap = nextdown(allowedfingers);
+ if (wrap) {
+ if (--arpoctave < minoctave) {
+ if (arpmode == ARP_UPDOWN || arpmode == ARP_PEDALUPDOWN) {
+ arpdir = 0;
+ arpoctave = minoctave;
+ nextup(allowedfingers);
+ nextup(allowedfingers);
+ }
+ else if (arpmode == ARP_UPDOWNREP || arpmode == ARP_UPDOWN8) {
+ arpdir = 0;
+ arpoctave = minoctave;
+ nextup(allowedfingers);
+ }
+ else
+ arpoctave = maxoctave;
+ }
+ }
+ return wrap;
+}
+
+void arprandom(u8 allowedfingers, int minoctave, int maxoctave) {
+ arpoctave = minoctave + (rand() % (maxoctave + 1 - minoctave));
+ u8 left = allowedfingers & ~arpused;
+ if (left == 0) {
+ arpused = 0;
+ left = allowedfingers;
+ }
+ curarpfinger = -1;
+ arpbits = pickrandombit(left);
+ arpused |= arpbits;
+ if (arpmode == ARP_RANDOM2 || arpmode == ARP_RANDOM28) {
+ // pick a second random!
+ u8 left = allowedfingers & ~arpused2;
+ left &= ~(1 << curarpfinger);
+ if (left == 0) {
+ arpused2 = 0;
+ left = allowedfingers & ~(1 << curarpfinger);
+ }
+ if (left) {
+ u8 bit = pickrandombit(left);
+ arpused2 |= bit;
+ arpbits |= bit;
+ }
+ }
+}
+
+extern int audiotime;
+
+void arptrig(u8 fingerdown_music) {
+ // try to find a higher finger
+ if (fingerdown_music == 0) {
+ arp_reset();
+ return;
+ }
+ int arpoctaves = param_eval_int(P_ARPOCT, any_rnd, env16, pressure16);
+ int arppatlen = param_eval_int(P_ARPLEN, any_rnd, env16, pressure16);
+ int prob = param_eval_int(P_ARPPROB, any_rnd, env16, pressure16);
+ bool arpstep = euclidstuff(&arp_rhythm, arppatlen, prob, arpmode);
+ arpretrig = arp_rhythm.did_a_retrig;
+
+#ifdef EMU
+ static int pt = 0;
+ int delta = audiotime - pt;
+ if (delta > 2048 && pt) {
+ int i = 1;
+ }
+ pt = audiotime;
+ EmuDebugLog("arp %d %d %d\r\n", arpstep, arpretrig,delta);
+#endif
+
+ if (!arpstep)
+ return;
+ arpbits = 0;
+ u8 allowedfingers = fingerdown_music;
+ if (arpmode >= ARP_UP8)
+ allowedfingers = 0xff;
+ int maxoctave = (arpoctaves+1) / 2;
+ int minoctave = maxoctave - arpoctaves;
+ switch (arpmode) {
+ default: return;
+ case ARP_ALL:
+ arpbits = allowedfingers;
+ // in chord mode, with no euclid rhythm, we randomly drop chord notes
+ if (abs(arppatlen) <= 1) {
+ int aprob = abs(prob);
+ for (int i = 0; i < 8; ++i) if (arpbits & (1 << i)) {
+ bool click = (rand() & 32767) < (aprob>>1);
+ if (!click) arpbits ^= (1 << i);
+ }
+ }
+ break;
+ case ARP_PEDALDOWN: case ARP_PEDALUP: case ARP_PEDALUPDOWN:
+ {
+ if (arp_nonpedalfinger >= 0 && allowedfingers & (1 << arp_nonpedalfinger)) {
+ //for pedal, if we have a remembered arpfinger, we just play that
+ curarpfinger = arp_nonpedalfinger;
+ arp_nonpedalfinger = -2;
+ }
+ else {
+ //otherwise we are due to play the pedal, we instead remove the pedal note from the allowed fingers
+ //then we do the logic as usual
+ u8 allowed_no_pedal = allowedfingers & (allowedfingers - 1);
+ if (allowed_no_pedal == 0) allowed_no_pedal = allowedfingers;
+ if (arpmode == ARP_PEDALDOWN || (arpmode == ARP_PEDALUPDOWN && arpdir))
+ arpdownwards(allowed_no_pedal, minoctave, maxoctave);
+ else
+ arpupwards(allowed_no_pedal, minoctave, maxoctave);
+ // actually, we're gonna play the pedal! wooahahah
+ arp_nonpedalfinger = curarpfinger;
+ arpbits = allowedfingers ^ allowed_no_pedal;
+ curarpfinger = -1;
+ }
+ break;
+ }
+ case ARP_UPDOWN: case ARP_UPDOWN8: case ARP_UPDOWNREP:
+ if (arpdir)
+ arpdownwards(allowedfingers, minoctave, maxoctave);
+ else
+ arpupwards(allowedfingers, minoctave, maxoctave);
+ break;
+ case ARP_UP: case ARP_UP8:
+ arpupwards(allowedfingers,minoctave,maxoctave);
+ break;
+ case ARP_DOWN: case ARP_DOWN8:
+ arpdownwards(allowedfingers, minoctave, maxoctave);
+ break;
+ case ARP_RANDOM: case ARP_RANDOM8: case ARP_RANDOM2: case ARP_RANDOM28:
+ arprandom(allowedfingers,minoctave,maxoctave);
+ break;
+ }
+ if (arp_rhythm.supress)
+ arpbits = 0;
+ else if (curarpfinger >= 0 && curarpfinger < 8) {
+ arpbits |= 1 << curarpfinger;
+ }
+ synthfingertrigger |= arpbits;
+}
+
+void seq_reset(void ) {
+ ticks_since_step = 0;
+ seq_divide_counter = 0;
+ seq_rhythm.trigcount = 0;
+ seq_used_bits = 0;
+ seq_dir = 0;
+}
+
+void seq_step(int initial) { // initial means - this is the initial clock pulse when switching into play mode
+ int seqpatlen = param_eval_int(P_SEQLEN, any_rnd, env16, pressure16);
+ int prob = param_eval_int(P_SEQPROB, any_rnd, env16, pressure16);
+ bool controlled_by_gatecv = seqdiv < 0;
+ if (initial>0) {
+ arp_reset();
+ seq_divide_counter = 0;
+ ticks_since_step = 0;
+ seq_used_bits |= ((uint64_t)1) << (cur_step & 63);
+ if (!controlled_by_gatecv) {
+ bool seqretrig = euclidstuff(&seq_rhythm, seqpatlen, prob, -1);
+ (void)seqretrig;
+ }
+ return;
+ }
+ seq_divide_counter++;
+
+ if (initial>=0) if (seq_divide_counter <= seqdiv || controlled_by_gatecv) // if initial is negative, we FORCE a step
+ return;
+ seq_divide_counter = 0;
+
+// EmuDebugLog("last_step_period %d\n", last_step_period);
+ last_step_period = ticks_since_step;
+ ticks_since_step = 0;
+ seq_divide_counter = 0;
+ int prevstep = cur_step;
+ if (!isplaying()) {
+ seq_rhythm.did_a_retrig = false;
+ } else { // playing!
+ // actually advance!
+ bool seqretrig = euclidstuff(&seq_rhythm, seqpatlen, prob, -1);
+ if (!seqretrig)
+ return;
+ u8 loopstart_step = (rampreset.loopstart_step_no_offset + step_offset) & 63;
+ int seqmode = param_eval_int(P_SEQMODE, any_rnd, env16, pressure16);
+ switch (seqmode) {
+ default:
+ case SEQ_FWD:
+ set_cur_step(cur_step + 1, true);
+ if (cur_step <= prevstep)
+ OnLoop();
+ break;
+ case SEQ_BACK:
+ set_cur_step(cur_step - 1, true);
+ if (cur_step >= prevstep)
+ OnLoop();
+ break;
+ case SEQ_PAUSE:
+ break;
+ case SEQ_PINGPONG: {
+ bool looped = false;
+ int end = rampreset.looplen_step + loopstart_step - 1;
+ if (seq_dir == 0 && cur_step >= end) {
+ seq_dir = 1;
+ looped = true;
+ }
+ else if (seq_dir == 1 && cur_step <= loopstart_step) {
+ seq_dir = 0;
+ looped = true;
+ }
+ set_cur_step(cur_step + (seq_dir ? -1 : 1), true);
+ if (looped)
+ OnLoop();
+ break; }
+ case SEQ_PINGPONGREP: {
+ int end = rampreset.looplen_step + loopstart_step - 1;
+ if (seq_dir == 0 && cur_step >= end) {
+ seq_dir = 1;
+ set_cur_step(end, true);
+ OnLoop();
+ }
+ else if (seq_dir == 1 && cur_step <= loopstart_step) {
+ seq_dir = 0;
+ set_cur_step(loopstart_step, true);
+ OnLoop();
+ }
+ else
+ set_cur_step(cur_step + (seq_dir ? -1 : 1), true);
+ break;
+ }
+ case SEQ_RANDOM: {
+ int len = rampreset.looplen_step & 63;
+ uint64_t mask = len ? (((uint64_t)1) << len) - 1 : ~0ull;
+ uint64_t bits = mask & ~(seq_used_bits >> loopstart_step); // bitmask of which steps we are allowed to choose from
+ bool looped = false;
+ if (!bits) {
+ seq_used_bits = 0;
+ bits = mask; // reset!
+ looped = true;
+ }
+ int n = bits ? rand() % __builtin_popcountll(bits) : 1; // pick a random bit number
+ while (n-- > 0) bits &= bits - 1; // peel off the bits
+ int step = bits ? __builtin_ctzll(bits) : 0;
+ set_cur_step(loopstart_step + step, true);
+ if (looped)
+ OnLoop();
+ break;
+ }
+ }
+ seq_used_bits |= ((uint64_t)1) << (cur_step & 63);
+ if (playmode == PLAY_WAITING_FOR_CLOCK_STOP) {
+ playmode = PLAY_STOPPED;
+ seq_rhythm.did_a_retrig = false;
+ }
+ }
+}
+
+
+void update_arp(bool clock) {
+ arpretrig = false;
+// bool called_trig = false;
+ arpmode = ((rampreset.flags & FLAGS_ARP)) ? param_eval_int(P_ARPMODE, any_rnd, env16, pressure16) : -1;
+ if (arpmode>=0 && !isgrainpreview()) {
+ int div = param_eval_int(P_ARPDIV, any_rnd, env16, pressure16);
+ if (div < 0) {
+ u32 dclock = (u32)(table_interp(pitches, 32768 + (-div >> 2)) * (1 << 24));
+ freearpclock -= dclock;
+ clock = freearpclock < 0;
+ if (clock) {
+ freearpclock += 1<<31;
+ arp_divide_counter = 0;
+ }
+ div = 0;
+ }
+ else {
+ div = divisions[(clampi(div, 0,65535) * DIVISIONS_MAX) >>16]-1;
+ }
+ if (arp_divide_counter > div)
+ arp_divide_counter = 0;
+ if (clock) {
+ if (arp_divide_counter <= 0) {
+// called_trig = true;
+ last_arp_period = ticks_since_arp;
+ /*
+ if (last_arp_period < 31 || last_arp_period>32) {
+ EmuDebugLog("arp %d\r\n", last_arp_period);
+ int i = 1;
+ }
+ */
+ ticks_since_arp = 0;
+ arptrig(synthfingerdown_nogatelen);
+ arp_divide_counter = 0;
+ }
+ arp_divide_counter++;
+
+ }
+
+ }
+
+}
+
+void OnGotReset(void) {
+ u8 loopstart_step = (rampreset.loopstart_step_no_offset + step_offset) & 63;
+ set_cur_step(loopstart_step, false);
+ bpm_clock_phase = 0;
+ seq_reset();
+ arp_reset();
+ seq_step(1);
+ OnLoop();
+}
+
+extern volatile u8 gotclkin;
+volatile u8 gotclkin=0;
+
+
+
+int update_clock(void) { // returns 1 for clock, 2 for half clock, 0 for neither
+ tick++;
+ bool gotclock = false;
+ //////////////////////////////////////////// eurorack clock input
+// int gate_input_max = 65536;
+ static u8 prevgotclk=0;
+ u8 newgotclk=gotclkin;
+ if(newgotclk!=prevgotclk) {
+ prevgotclk=newgotclk;
+ gotclock=true;
+ external_clock_enable=true;
+ }
+
+ /*
+ for (int i = 0; i < ADC_SAMPLES * ADC_CHANS; i += ADC_CHANS) {
+ if (adcbuf[i + ADC_CLK] <= 0)
+ clk_in_high = true;
+ if (adcbuf[i + ADC_RESET] <= 0)
+ reset_in_high = true;
+ gate_input_max = mini(gate_input_max, adcbuf[i + ADC_GATE]);
+ }
+ */
+
+
+ if (/*(reset_in_high && !reset_in_high_prev) || */got_ui_reset) { // TODO - if audio in level is turned down, look for pulses?
+ OnGotReset();
+ } else if (playmode == PLAYING && seqdiv < 0 && getgatesense()) {
+ // gate cv controls step
+ static bool curgate_digital = true;
+ float curgate = GetADCSmoothed(ADC_GATE);
+ float thresh = curgate_digital ? 0.01f : 0.02f;
+ bool newgate_digital = curgate > thresh;
+ if (newgate_digital && !curgate_digital) {
+ seq_step(-1); // force a step
+ }
+ curgate_digital = newgate_digital;
+ }
+ got_ui_reset = false;
+// clk_in_high_prev = clk_in_high;
+// reset_in_high_prev = reset_in_high;
+#define ACCURATE_FS 31250
+ //////////////////////////////////////////// intenral clock
+ if (!external_clock_enable) {
+ bpm10x = ((param_eval_int(P_TEMPO, any_rnd, env16, pressure16) * 1200) >> 16) + 1200;
+
+ u32 dclock = ((1 << 18) * bpm10x) / ((ACCURATE_FS * 600 / 32) / BLOCK_SAMPLES);
+ bpm_clock_phase += dclock;
+ if (bpm_clock_phase > 1 << 21) {
+ bpm_clock_phase &= (1 << 21) - 1;
+ gotclock = true;
+ }
+ //if (playmode == PLAY_WAITING_FOR_CLOCK) {
+ // bpm_clock_phase = 0;
+ // gotclock = true;
+ //}
+ }
+ ////////////////////////////////////////////////////// clock advance
+ ticks_since_clock++;
+ ticks_since_step++;
+ ticks_since_arp++;
+ // if (playmode == PLAY_PREVIEW && shift_down==SB_PLAY) -- maybe eat a single clock if the finger is still down?
+
+ if (!gotclock) {
+ if (ticks_since_clock == last_clock_period / 2) {
+ SetOutputCVClk(0);
+ seq_step(0);
+ return 2;
+ }
+ return 0;
+ }
+ SetOutputCVClk(65535);
+ if (external_clock_enable) {
+ // figure out bpm from the external clock's last 2 periods
+ float avgclockperiod_per_sec = (ticks_since_clock + last_clock_period) * (0.5f * BLOCK_SAMPLES / ACCURATE_FS);
+ float guessed_bpm = (1200.f / 8.f) / maxf(1.f / 16.f, avgclockperiod_per_sec);
+ guessed_bpm+=(bpm10x-guessed_bpm)*(1.f-0.2f); // smooth it a bit
+ bpm10x = (int)(guessed_bpm+0.5f);
+ }
+
+ last_clock_period = ticks_since_clock;
+ ticks_since_clock = 0;
+ int initial = 0;
+ if (playmode == PLAY_WAITING_FOR_CLOCK_START) {
+ playmode = PLAYING;
+ initial = 1;
+ // also sync the arp!
+ arp_reset();
+ }
+ seq_step(initial);
+ return true;
+}