diff options
Diffstat (limited to 'sw/Core/Src/arp.h')
| -rwxr-xr-x | sw/Core/Src/arp.h | 553 |
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; +} |
