summaryrefslogtreecommitdiff
path: root/sw/Core/Src/plinky.c
diff options
context:
space:
mode:
authorAlex Evans <715855+mmalex@users.noreply.github.com>2024-11-06 17:47:11 +0000
committerAlex Evans <715855+mmalex@users.noreply.github.com>2024-11-06 17:47:11 +0000
commit7852372b15f850a46a02d1aa991215619096ad50 (patch)
treef82a2b81b92e20f332bffb584075338c220d157b /sw/Core/Src/plinky.c
parent2ecdaa538fc7a9b9613b01357fbd2cdb293735a8 (diff)
downloadplinky-7852372b15f850a46a02d1aa991215619096ad50.tar.gz
add oled feedback for flashing process
Diffstat (limited to 'sw/Core/Src/plinky.c')
-rwxr-xr-xsw/Core/Src/plinky.c6480
1 files changed, 3288 insertions, 3192 deletions
diff --git a/sw/Core/Src/plinky.c b/sw/Core/Src/plinky.c
index fdf9a9b..cdcec60 100755
--- a/sw/Core/Src/plinky.c
+++ b/sw/Core/Src/plinky.c
@@ -1,3192 +1,3288 @@
-#if defined(_WIN32) || defined(__APPLE__)
-#define EMU
-#pragma warning(disable:4244)
-#endif
-
-#ifdef WASM
-#include <emscripten.h>
-#else
-#define EMSCRIPTEN_KEEPALIVE
-#endif
-
-#ifndef EMU
-#include <main.h>
-
-extern ADC_HandleTypeDef hadc1;
-extern DMA_HandleTypeDef hdma_adc1;
-
-extern DAC_HandleTypeDef hdac1;
-extern DMA_HandleTypeDef hdma_dac_ch1;
-extern DMA_HandleTypeDef hdma_dac_ch2;
-
-extern I2C_HandleTypeDef hi2c2;
-
-extern SAI_HandleTypeDef hsai_BlockA1;
-extern SAI_HandleTypeDef hsai_BlockB1;
-extern DMA_HandleTypeDef hdma_sai1_a;
-extern DMA_HandleTypeDef hdma_sai1_b;
-
-extern SPI_HandleTypeDef hspi2;
-extern DMA_HandleTypeDef hdma_spi2_tx;
-extern DMA_HandleTypeDef hdma_spi2_rx;
-
-extern TIM_HandleTypeDef htim1;
-extern TIM_HandleTypeDef htim2;
-extern TIM_HandleTypeDef htim3;
-extern TIM_HandleTypeDef htim4;
-extern TIM_HandleTypeDef htim5;
-extern TIM_HandleTypeDef htim6;
-
-extern TSC_HandleTypeDef htsc;
-
-extern UART_HandleTypeDef huart3;
-
-
-#endif
-
-#include <assert.h>
-#include <string.h>
-#include <math.h>
-#include <stdlib.h>
-#define IMPL
-#define BLOCK_SAMPLES 64
-#ifdef WASM
-#define ASSERT(...)
-#else
-#define ASSERT assert
-#endif
-#include "core.h"
-#include "oled.h"
-#include "codec.h"
-#include "leds.h"
-#include "adc.h"
-#include "dac.h"
-#include "gfx.h"
-#include "spi.h"
-#include "tables.h"
-#include "audiointrin.h"
-#include "lfo.h"
-#include "enums.h"
-
-
-const static float table_interp(const float *table, int x) { // 16 bit unsigned input, looked up in a 1024 entry table and linearly interpolated
- x=SATURATEU16(x);
- table+=x>>6;
- x&=63;
- return table[0]+(table[1]-table[0])*(x*(1.f/64.f));
-}
-
-#define TWENTY_OVER_LOG2_10 6.02059991328f // (20.f/log2(10.f));
-
-static inline float lin2db(float lin) { return log2f(lin) * TWENTY_OVER_LOG2_10; }
-static inline float db2lin(float db) { return exp2f(db * (1.f / TWENTY_OVER_LOG2_10)); }
-
-typedef struct knobsmoother {
- float y1, y2;
-} knobsmoother;
-
-
-void knobsmooth_reset(knobsmoother* s, float ival) { s->y1 = s->y2 = ival; }
-
-float knobsmooth_update_knob(knobsmoother* s, float newval, float max_scale) {
- // inspired by https ://cytomic.com/files/dsp/DynamicSmoothing.pdf
- float band = fabsf(s->y2 - s->y1);
- float sens = 8.f / max_scale;
- float g = minf(1.f, 0.05f + band * sens);
- s->y1 += (newval - s->y1) * g;
- s->y2 += (s->y1 - s->y2) * g;
- return s->y2;
-}
-float knobsmooth_update_cv(knobsmoother* s, float newval) { // same as update but with faster constants
- // inspired by https ://cytomic.com/files/dsp/DynamicSmoothing.pdf
- float band = fabsf(s->y2 - s->y1);
- const static float sens = 10.f;
- float g = minf(1.f, 0.1f + band * sens);
- s->y1 += (newval - s->y1) * g;
- s->y2 += (s->y1 - s->y2) * g;
- return s->y2;
-}
-
-typedef struct Osc {
- u32 phase, prevsample;
- s32 dphase;
- s32 targetdphase;
- int pitch;
-} Osc;
-
-typedef struct GrainPair {
- int fpos24;
- int pos[2];
- int vol24;
- int dvol24;
- int dpos24;
- float grate_ratio;
- float multisample_grate;
- int bufadjust; // for reverse grains, we adjust the dma buffer address by this many samples
- int outflags;
-} GrainPair;
-
-typedef struct Voice {
- float vol;
- float y[4];
- Osc theosc[4];
- GrainPair thegrains[2];
- // grain synth state
- int playhead8;
- u8 sliceidx;
- int initialfingerpos;
- knobsmoother fingerpos;
-
- u8 decaying;
- int env_cur16;
- float noise;
- float env_level;
-#ifdef NEW_LAYOUT
- int env_decaying;
-#else
- uint64_t env_phase;
-#endif
-} Voice;
-
-TickCounter _tc_budget;
-TickCounter _tc_all;
-TickCounter _tc_fx;
-TickCounter _tc_audio;
-TickCounter _tc_touch;
-TickCounter _tc_led;
-TickCounter _tc_osc;
-TickCounter _tc_filter;
-
-knobsmoother adc_smooth[8];
-volatile int encval = 0;
-volatile u8 encbtn = 0;
-float encaccel;
-u8 prevsynthfingerdown = 0;
-u8 prevsynthfingerdown_nogatelen = 0; // same as above, but without gatelen applied
-u8 prevprevsynthfingerdown_nogatelen = 0; // same as above, but without gatelen applied
-u8 synthfingerdown = 0; // bit set when finger is down
-u8 synthfingerdown_nogatelen = 0;
-u8 synthfingertrigger = 0; // bit set on frame finger goes down
-s8 shift_down = -1;//-1 means up; -2 means ghosted (supressed) touch; 0-7 means down
-int shift_down_time = 0;
-s8 editmode = EM_PLAY;
-int last_time_shift_was_untouched = 0;
-u32 tick = 0; // increments every 64 samples
-
-s32 bpm10x = 120 * 10;
-
-static inline bool isgrainpreview(void) {
- return editmode == EM_SAMPLE;
-}
-
-
-enum {
- PLAY_STOPPED,
- PLAY_PREVIEW,
- PLAY_WAITING_FOR_CLOCK_START,
- PLAY_WAITING_FOR_CLOCK_STOP,
- PLAYING,
-};
-
-u8 playmode = PLAY_STOPPED;
-bool recording = false;
-u8 pending_loopstart_step = 255; // set when we want to jump on next loop
-
-static inline bool isplaying(void) {
- return playmode == PLAYING || playmode == PLAY_WAITING_FOR_CLOCK_STOP;
-}
-
-
-u32 bpm_clock_phase = 0;
-int ticks_since_clock = 0;
-int ticks_since_arp = 0;
-int last_clock_period = 0;
-int last_step_period = 0;
-int last_arp_period = 0;
-int ticks_since_step = 0; // this counts up, along with the seq_divide_counter, even when not playing, in order to give a sense of time eg for recording
-bool external_clock_enable = false;
-
-int seq_divide_counter = 0;
-int arp_divide_counter = 0;
-int seqdiv = 0; // what we count up to , to get seq division
-uint64_t seq_used_bits=0;
-u8 seq_dir=0;
-
-static inline int calcseqsubstep(int tick_offset, int maxsubsteps) { // where are we within a recorded step?
- if (last_step_period <= 0)
- return 0;
- if (ticks_since_step + tick_offset >= last_step_period)
- return maxsubsteps-1;
- if (ticks_since_step + tick_offset <= 0)
- return 0;
- int s = ((ticks_since_step+tick_offset) * maxsubsteps) / last_step_period;
- if (s < 0) s = 0;
- s=mini(maxsubsteps - 1, s);
- return s;
-}
-static inline int calcarpsubstep(int tick_offset, int maxsubsteps) { // where are we within a recorded step?
- if (last_arp_period <= 0)
- return 0;
- if (ticks_since_arp + tick_offset >= last_arp_period)
- return maxsubsteps-1;
- if (ticks_since_arp + tick_offset <= 0)
- return 0;
- return mini(maxsubsteps - 1, ((ticks_since_arp + tick_offset) * maxsubsteps) / last_arp_period);
-}
-
-u8 edit_mod=0,edit_param=0xff;
-//u8 ui_edit_param_prev[2][4] = { {P_LAST,P_LAST,P_LAST,P_LAST},{P_LAST,P_LAST,P_LAST,P_LAST} }; // push to front history
-static float surf[2][8][8];
-
-Voice voices[8];
-#ifdef EMU
-short delaybuf[DLMASK + 1];
-short reverbbuf[RVMASK + 1];
-int emupitchsense;
-int emugatesense;
-#else
-//__attribute__((section(".dlram"))) short delaybuf[DLMASK + 1];
-//__attribute__((section(".rvram"))) short reverbbuf[RVMASK + 1];
-short *reverbbuf=(short*)0x10000000; // use ram2 :)
-short *delaybuf= (short*)0x20008000; // use end of ram1 :)
-
-#endif
-static int reverbpos = 0;
-
-static int k_reverb_fade = 240;
-static int k_reverb_shim = 240;
-static float k_reverb_wob = 0.5f;
-static int k_reverbsend=0;
-static int shimmerpos1 = 2000;
-static int shimmerpos2 = 1000;
-static int shimmerfade = 0;
-static int dshimmerfade = 32768/4096;
-
-static lfo aplfo = LFOINIT(1.f / 32777.f * 9.4f);
-static lfo aplfo2= LFOINIT(1.3f / 32777.f * 3.15971f);
-
-
-/*
-
-update
-kick fetch for this pos
-*/
-
-u32 scope[128];
-
-
-static inline u32 ticks(void) { return tick; }
-
-enum {
- EA_OFF = 0,
- EA_PASSTHRU = -1,
- EA_PLAY = 1,
- EA_PREERASE = 2,
- EA_MONITOR_LEVEL = 3,
- EA_ARMED = 4,
- EA_RECORDING = 5,
- EA_STOPPING1 = 6, // we stop for 4 cycles to write 0s at the end
- EA_STOPPING2 = 7,
- EA_STOPPING3 = 8,
- EA_STOPPING4 = 9,
-};
-s8 enable_audio = EA_OFF;
-
-
-#include "flash.h"
-#include "params.h"
-#include "touch.h"
-#include "calib.h"
-#include "arp.h"
-#include "edit.h"
-
-#include "webusb.h"
-
-
-bool gatecv_trig = false;
-
-// lpf_k = 1-exp(-0.707/t) where t is in samples to get to half
-static inline float lpf_k(int x) {
- return table_interp(lpf_ks, x);
-}
-
-
-float param_eval_float(u8 paramidx, int rnd, int env16, int pressure16) {
- return param_eval_int(paramidx, rnd, env16, pressure16) * (1.f / 65536.f);
-}
-
-int param_eval_finger(u8 paramidx, int fingeridx, Finger* f) {
- return param_eval_int(paramidx, finger_rnd[fingeridx], voices[fingeridx].env_cur16, f->pressure * 32);
-}
-
-extern int16_t accel_raw[3];
-extern float accel_lpf[2];
-extern float accel_smooth[2];
-s16 accel_sens=0;
-
-//extern int debuga[4];
-//int debuga[4];
-
-
-void update_params(int fingertrig, int fingerdown) {
-
- // DebugLog("%d,%d,%d,%d,%d,%d,%d,%d,%d\r\n",adcbuf[0],adcbuf[1],adcbuf[2],adcbuf[3],adcbuf[4],adcbuf[5],adcbuf[6],adcbuf[7],adcbuf[8]);
-
- // update envelopes
-#ifdef NEW_LAYOUT
- param_eval_premod(P_ENV_LEVEL);
- param_eval_premod(P_A2);
- param_eval_premod(P_D2);
- param_eval_premod(P_S2);
- param_eval_premod(P_R2);
-#else
- param_eval_premod(P_ENV_RATE);
- param_eval_premod(P_ENV_WARP);
- param_eval_premod(P_ENV_LEVEL);
- param_eval_premod(P_ENV_REPEAT);
-#endif
- for (int vi = 0; vi < 8; ++vi) {
- int bit = 1 << vi;
- Voice* v = &voices[vi];
- Finger* f = touch_synth_getlatest(vi);
-#ifdef NEW_LAYOUT
- if (fingertrig & bit) {
- v->env_level = 0.f;
- v->env_decaying = false;
- }
- int down = (fingerdown & bit);
- float target = down ? (v->env_decaying) ? 2.f*(param_eval_finger(P_S2, vi, f)*(1.f/65536.f)) : 2.2f : 0.f;
- float dlevel = target - v->env_level;
- float k = lpf_k(param_eval_finger((dlevel > 0.f) ? P_A2 : (v->env_decaying && down) ? P_D2 : P_R2, vi, f));
- // update v->env_level
- v->env_level += (target - v->env_level) * k;
- if (v->env_level >= 2.f && down)
- v->env_decaying = true;
- v->env_cur16 = SATURATE17(v->env_level * param_eval_finger(P_ENV_LEVEL, vi, f));
-#else
- if (fingertrig & bit) {
- v->env_phase = (uint64_t)(65536.f * 65536.f * 2.f * (0.5f - 0.4999f));
- v->env_level = 2.f; // so that it clips!
- }
- int lfofreq = param_eval_finger(P_ENV_RATE, vi, f);
- u32 dlfo = (u32)(table_interp(pitches, 32768 + (lfofreq >> 1)) * (1 << 24));
- //u32 dlfo=(u32)exp2f(24.f+lfofreq*10.f);
- float lfowarp = param_eval_finger(P_ENV_WARP, vi, f) * (0.4999f / 65536.f) + 0.5f;
- u32 prev_cycle = v->env_phase >> 32;
- float lfoval = lfo_eval((u32)((v->env_phase) >> 16), lfowarp, LFO_ENV);
- v->env_phase += dlfo;
- u32 cur_cycle = v->env_phase >> 32;
- if (cur_cycle != prev_cycle)
- v->env_level *= param_eval_finger(P_ENV_REPEAT, vi, f) * (1.f / 65536.f);
- lfoval *= v->env_level;
- lfoval *= param_eval_finger(P_ENV_LEVEL, vi, f);
- v->env_cur16 = SATURATE17((int)lfoval);
-#endif
- }
- // update average tilt + pressure
- int totw = 256;
- int tottilt = tilt16 * 256;
- int maxp = 0;
- int maxenv = 0;
- for (int fi = 0; fi < 8; ++fi) {
- Finger* f = touch_synth_getlatest(fi);
- int p = f->pressure;
- if (p < 0) p = 0;
- totw += p;
- maxp = maxi(maxp, p);
- maxenv = maxf(maxenv, voices[fi].env_cur16);
- tottilt += index_to_tilt16(fi) * p;
- if (fingertrig & (1 << fi)) {
- finger_rnd[fi] += 4813;
- }
- }
- if (fingertrig)
- any_rnd += 4813;
- tilt16 = tottilt / totw;
- env16 = maxenv;
- pressure16 = maxp * (65536 / 2048);
- // update lfos on modulation sources
-
- float aknob = clampf(GetADCSmoothed(ADC_AKNOB), -1.f, 1.f);
- float bknob = clampf(GetADCSmoothed(ADC_BKNOB), -1.f, 1.f);
- float acv = clampf(GetADCSmoothed(ADC_ACV) * IN_CV_SCALE, -1.f, 1.f);
- float bcv = clampf(GetADCSmoothed(ADC_BCV) * IN_CV_SCALE, -1.f, 1.f);
- float xcv = clampf(GetADCSmoothed(ADC_XCV) * IN_CV_SCALE, -1.f, 1.f);
- float ycv = clampf(GetADCSmoothed(ADC_YCV) * IN_CV_SCALE, -1.f, 1.f);
-
- // accelerometer
- static int accel_counter;
- float accel_sens_f = (2.f/16384.f/32768.f) * abs(accel_sens);
- accel_counter++;
- int axisswap = accel_raw[2] > 4000; // run 2 plinkys have the accelerometer rotated 90 degrees and upside down from the addon... detect it via z direction
- for (int j=0;j<2;++j) {
- float f=accel_raw[j^axisswap]*accel_sens_f;
- if (!j) {
- if (!axisswap)
- f = -f; // reverse x
- } else if (accel_sens < 0)
- f = -f; // reverse y if accel sens negative
- accel_lpf[j]+=(f-accel_lpf[j])*0.0001f;
- accel_smooth[j]+=(f-accel_smooth[j])*0.1f;
- if(accel_counter<1000)
- accel_lpf[j]=accel_smooth[j]=f;
- }
-// int t1=1000*accel_lpf[0];
-// int t2=1000*accel_lpf[1];
-// int t3=1000*accel_smooth[0];
-// int t4=1000*accel_smooth[1];
-
- int gatesense = getgatesense();
- int pitchsense = getpitchsense();
- float pitchcv = pitchsense ? GetADCSmoothed(ADC_PITCH) : 0.f;
- float gatecv = gatesense ? clampf(GetADCSmoothed(ADC_GATE)*1.15f-0.05f, 0.f, 1.f) : 1.f;
- knobsmooth_update_cv(adc_smooth + 0, acv);
- knobsmooth_update_cv(adc_smooth + 1, bcv);
- knobsmooth_update_cv(adc_smooth + 2, xcv);
- knobsmooth_update_cv(adc_smooth + 3, ycv);
- knobsmooth_update_knob(adc_smooth + 4, aknob, 1.f);
- knobsmooth_update_knob(adc_smooth + 5, bknob, 1.f);
- knobsmooth_update_cv(adc_smooth + 6, pitchcv);
- knobsmooth_update_cv(adc_smooth + 7, gatecv);
- u8 prevlfohp = (lfo_history_pos >> 4) & 15;
- lfo_history_pos++;
- u8 lfohp = (lfo_history_pos >> 4) & 15;
- if (lfohp != prevlfohp)
- lfo_history[lfohp][0] =
- lfo_history[lfohp][1] =
- lfo_history[lfohp][2] =
- lfo_history[lfohp][3] = 0;
- //compute new mod_cur for each mod source
- int phase0 = calcseqsubstep(0, 65536);
- int phase1 = phase0 + (65536 / 8);
- int nextstep = cur_step;
- if (phase1 >= 65536) {
- phase1 &= 65535;
- nextstep++;
- u8 loopstart_step = (rampreset.loopstart_step_no_offset + step_offset) & 63;
- if (nextstep >= loopstart_step + rampreset.looplen_step)
- nextstep -= rampreset.looplen_step;
- nextstep &= 63;
- }
- int q1 = (cur_step >> 4) & 3;
- int q2 = (nextstep >> 4) & 3;
- s8* autoknob1 = rampattern[q1].autoknob[(cur_step & 15) * 8 + (phase0>>13)];
- s8* autoknob2 = rampattern[q2].autoknob[(nextstep & 15) * 8 + (phase1>>13)];
- float autoknobinterp = (phase0 & (65536 / 8 - 1)) * (1.f/(65536/8));
- for (int i = 0; i < 4; ++i) {
- float adc = adc_smooth[i].y2;
- float adcknob = 0.f;
- if (i < 2) {
- adcknob = adc_smooth[i + 4].y2;
- if (!(recordingknobs&(1<<i)))
- adcknob += (autoknob1[i]+(autoknob2[i]-autoknob1[i])* autoknobinterp )*(1.f/127.f);
- } else
- adcknob=accel_smooth[i-2]-accel_lpf[i-2];
-
- int i6 = i * 6;
- mod_cur[M_A + i] = (int)((adc) * 65536.f); // modulate yourself with the raw input!
- param_eval_premod(P_AFREQ + i6);
- param_eval_premod(P_AWARP + i6);
- param_eval_premod(P_ASHAPE + i6);
- param_eval_premod(P_ADEPTH + i6);
- param_eval_premod(P_AOFFSET + i6);
- param_eval_premod(P_ASCALE + i6);
-
- int lfofreq = param_eval_int(P_AFREQ + i6, any_rnd, env16, pressure16);
- u32 dlfo = (u32)(table_interp(pitches, 32768 + (lfofreq >> 1)) * (1 << 24));
- //u32 dlfo=(u32)exp2f(24.f+lfofreq*10.f);
- float lfowarp = param_eval_float(P_AWARP + i6, any_rnd, env16, pressure16) * 0.49f + 0.5f;
- int lfoshape = param_eval_int(P_ASHAPE + i6, any_rnd, env16, pressure16);
- float lfoval = lfo_eval((u32)((lfo_pos[i] += dlfo) >> 16), lfowarp, lfoshape);
-
- lfoval *= param_eval_float(P_ADEPTH + i6, any_rnd, env16, pressure16);
- //expander_out[i] = clampi(EXPANDER_ZERO - (int)(lfoval * (float)(EXPANDER_RANGE)), 0, EXPANDER_MAX);
-
- int cvval = param_eval_int(P_AOFFSET + i6, any_rnd, env16, pressure16);
- //if (i == 0) debuga[0] = cvval>>8;
- cvval += (int)(adc * (param_eval_int(P_ASCALE + i6, any_rnd, env16, pressure16)<<1));
- cvval += (int)(adcknob * 65536.f); // knob is not scaled by the cv bias/scale parameters. I think thats useful.
- mod_cur[M_A + i] = ((int)(lfoval * 65536.f)) + cvval;
- //if (i == 0) {
- // debuga[1] = adc * 256.f;
- // debuga[2] = adcknob * 256.f;
- // debuga[3] = lfoval * 256.f;
- //}
-
- float expander_val = mod_cur[M_A + i] * (EXPANDER_GAIN * EXPANDER_RANGE / 65536.f);
- expander_out[i] = clampi(EXPANDER_ZERO - (int)(expander_val), 0, EXPANDER_MAX);
-
-
- int scopey = (-(mod_cur[M_A + i] * 7 + (1<<16)) >> 17) + 4;
- if (scopey >= 0 && scopey < 8)
- lfo_history[lfohp][i] |= 1 << scopey;
-
-
- }
-
-
-
- for (int i = 0; i < P_LAST; ++i) {
- int pg = i / 6;
- if (pg == PG_A || pg == PG_B || pg == PG_X || pg == PG_Y) {
- i += 5;
- continue;
- }
- param_eval_premod(i);
- }
-
- accel_sens = clampi(param_eval_int(P_ACCEL_SENS, any_rnd, env16, pressure16)/2, -32767, 32767);
-
- // DebugLog("%d,%d,%d,%d\r\n",mod_cur[0]/256,mod_cur[1]/256,mod_cur[2]/256,mod_cur[3]/256);
- // HAL_UART_Transmit(&huart3, (u8*) mod_cur, 4*4, 1000);
-
-}
-
-
-
-
-
-static inline void putscopepixel(unsigned int x, unsigned int y) {
- if (y>=32) return;
- scope[x]|=(1<<y);
-}
-
-
-
-bool trigout = false;
-
-
-float UpdateEnvelope(Voice *v, int fingeridx, float targetvol) {
- Finger *f=touch_synth_getlatest(fingeridx);
- float sens = param_eval_finger(P_SENS, fingeridx, f);
- sens *= (2.f / 65536.f);
- targetvol *= sens*sens;
- bool gp = isgrainpreview();
- const float sustain = gp ? 1.f : squaref(param_eval_finger(P_S, fingeridx, f) * (1.f/65536.f)); // lerp(0.f,1.f,pot1);
- const float attack = gp ? 0.5f : lpf_k((param_eval_finger(P_A, fingeridx, f) ));
- const float decay = gp ? 1.f : lpf_k((param_eval_finger(P_D, fingeridx, f) ));
- const float release =gp ? 0.5f : lpf_k((param_eval_finger(P_R, fingeridx, f) ));
-
- if (targetvol < 0.f)
- targetvol = 0.f;
- targetvol *= targetvol;
- // pitch compensation
- if (!ramsample.samplelen) {
- targetvol *= 1.f + ((v->theosc[2].pitch - 43000) * (1.f / 65536.f));
- }
-
- int bit=1<<fingeridx;
- if (arpmode>=0) {
- if (!(arpbits & bit))
- targetvol = 0.f;
- /*else if (targetvol > 0.f) {
- // gate len for arp here!
- int gatelen = param_eval_finger(P_GATE_LENGTH, fingeridx, f) >> 8;
- if (gatelen < 256) {
- int phase = calcarpsubstep(0, 256);
- if (phase > gatelen || !arp_rhythm.did_a_retrig)
- targetvol = 0.f;
- }
- }*/
- }
-
-
- float vol = v->vol;
- if (arpretrig || (synthfingertrigger & bit)) {
- //if (vol>sustain*0.5f)
- // vol = sustain*0.5f; // make sure you hear the retrig :) not sure... legato would be nice. maybe use sustain as a hint?
- vol *= sustain;
- v->decaying = false;
- trigout = true;
- }
-
- float decay_or_release = decay;
- if (targetvol <= 0.f) {
- v->decaying = 0; // release phase
- decay_or_release = release;
- } else if (v->decaying) {
- targetvol *= sustain;
- }
-
- float attack_threshvol = targetvol;
- float dvol = (targetvol - vol);
- dvol *= (dvol > 0.f) ? attack : decay_or_release; // scale delta back by release time
- targetvol = vol + dvol; // new target
- if (targetvol > attack_threshvol * 0.95f)
- v->decaying = 1; // we hit the peak! time to decay.
- if (targetvol > 1.f) {
- targetvol=1.f;
- v->decaying = 1;
- }
- return targetvol;
-}
-
-inline s32 trifold(u32 x) {
- if (x > 0x80000000)
- x = 0xffffffff - x;
- return (s32)(x >> 4);
-}
-
-static inline int sample_slice_pos8(int pos16) {
- pos16 = clampi(pos16,0,65535);
- int i = pos16 >> 13;
- int p0 = ramsample.splitpoints[i];
- int p1 = ramsample.splitpoints[i+1];
- return (p0<<8) + (((p1 - p0) * (pos16 & 0x1fff)) >> (13-8));
-}
-
-static inline int calcloopstart(u8 sliceidx) {
- int all = ramsample.loop & 2;
- return (all) ? 0 : ramsample.splitpoints[sliceidx];
-}
-static inline int calcloopend(u8 sliceidx) {
- int all = ramsample.loop & 2;
- return (all || sliceidx>=7) ? ramsample.samplelen - 192 : ramsample.splitpoints[sliceidx+1];
-}
-
-static inline int doloop(int playhead, u8 sliceidx) {
- if (!(ramsample.loop & 1)) return playhead;
- int loopstart = calcloopstart(sliceidx);
- int loopend = calcloopend(sliceidx);
- int looplen = loopend - loopstart;
- if (looplen > 0 && (playhead < loopstart || playhead >= loopstart + looplen)) {
- playhead = (playhead - loopstart) % looplen;
- if (playhead < 0) playhead += looplen;
- playhead += loopstart;
- }
- return playhead;
-}
-static inline int doloop8(int playhead, u8 sliceidx) {
- if (!(ramsample.loop & 1)) return playhead;
- int loopstart = calcloopstart(sliceidx)<<8;
- int loopend = calcloopend(sliceidx)<<8;
- int looplen = loopend - loopstart;
- if (looplen > 0 && (playhead < loopstart || playhead >= loopstart + looplen)) {
- playhead = (playhead - loopstart) % looplen;
- if (playhead < 0) playhead += looplen;
- playhead += loopstart;
- }
- return playhead;
-}
-
-#ifdef EMU
-float arpdebug[1024];
-int arpdebugi;
-#endif
-
-#ifdef EMU
-#define SMUAD(o, a, b) o=(int)(((s16)(a))*((s16)(b))+((s16)(a>>16))*((s16)(b>>16)))
-#else
-#define SMUAD(o, a, b) asm("smuad %0, %1, %2" : "=r" (o) : "r" (a), "r" (b))
-#endif
-
-
-
-//s16 wavetable[WAVETABLE_SIZE*WT_LAST];
-#ifndef EMU
-__attribute__((section(".wavetableSection")))
-#endif
-#include "wavetable.h"
-#ifdef _WIN32
-//#define clz __lzcnt
-u32 clz(u32 val) {
- u8 res = 0;
- if (!val) return 32;
- while (!(val & 0x80000000))
- {
- res++;
- val <<= 1;
- }
- return res;
-}
-#else
-#define clz __builtin_clz
-#endif
-void RunVoice(Voice *v, int fingeridx, float targetvol, u32 *outbuf) {
-
-// if (fingeridx == 0) targetvol = 1.f;
- //if (fingeridx > 0) return;
- //float otargetvol = targetvol;
- targetvol = UpdateEnvelope(v, fingeridx, targetvol);
- tc_start(&_tc_osc);
- float noise;
-#ifdef EMU
- if (fingeridx == 4) {
- //bool click = otargetvol > 0.5f && (arpbits & 16) && arpretrig ;
- arpdebug[arpdebugi] = targetvol;// +(click ? 0.5 : 0);
- //if (click && targetvol < 0.001f) {
- // arpdebug[arpdebugi] = 1.f;
- //}
- }
-#endif
- Finger *f=touch_synth_getlatest(fingeridx);
-
- /* the touch.h version should handle this damn it
- if (targetvol > 0.01f && v->vol < 0.01f) {
- // trigger! reset pitches?
- for (int c1 = 0; c1 < 8; ++c1) {
- fingers_synth_sorted[fingeridx][c1] = *f;
- }
- }*/
-
- float glide =lpf_k(param_eval_finger(P_GLIDE, fingeridx, f)>>2) * (0.5f/BLOCK_SAMPLES);
- int drivelvl=param_eval_finger(P_DRIVE, fingeridx, f);
- float fdrive = table_interp(pitches, ((32768-2048)+drivelvl/2));
- if (drivelvl<-65536+2048)
- fdrive*=(drivelvl+65536)*(1.f/2048.f); // ensure drive goes right to 0 when full minimum
- float drive = fdrive * (0.75f/65536.f);
-
- float targetnoise = param_eval_finger(P_NOISE, fingeridx, f) * (1.f / 65536.f);
- targetnoise *= targetnoise;
- if (drivelvl > 0)
- targetnoise *= fdrive;
- float dnoise = (targetnoise - v->noise) * (1.f / BLOCK_SAMPLES);
-
- int resonancei = 65536 - param_eval_finger(P_MIXRESO, fingeridx, f);
- float resonance = 2.1f - (table_interp(pitches, resonancei) * (2.1f / pitches[1024]));
-
- drive *= 2.f / (resonance + 2.f);
-
- //glide=0.25f/BLOCK_SAMPLES;
-
- Finger* synthf = touch_synth_getlatest(fingeridx);
- float timestretch = 1.f;
- float posjit = 0.f;
- float sizejit = 1.f;
- float gsize = 0.125f;
- float grate = 1.f;
- float gratejit = 0.f;
- int smppos = 0;
- if (ramsample.samplelen) {
- if (!isgrainpreview()) {
- timestretch = param_eval_finger(P_SMP_TIME, fingeridx, synthf) * (2.f / 65536.f);
- gsize = param_eval_finger(P_SMP_GRAINSIZE, fingeridx, synthf)* (1.414f / 65536.f);
- grate = param_eval_finger(P_SMP_RATE, fingeridx, synthf) * (2.f / 65536.f);
- smppos = (param_eval_finger(P_SMP_POS, fingeridx, synthf) * ramsample.samplelen) >> 16;
- posjit = param_eval_finger(P_JIT_POS, fingeridx, synthf) * (1.f / 65536.f);
- sizejit = param_eval_finger(P_JIT_GRAINSIZE, fingeridx, synthf) * (1.f / 65536.f);
- gratejit = param_eval_finger(P_JIT_RATE, fingeridx, synthf) * (1.f / 65536.f);
- }
- }
- int trig = synthfingertrigger & (1 << fingeridx);
-
- int prevsliceidx = v->sliceidx;
- if (ramsample.samplelen)
- {
- bool gp = isgrainpreview();
-
- // decide on the sample for the NEXT frame
- if (trig) {// on trigger frames, we FADE out the old grains! then the next dma fetch will be the new sample and we can fade in again
- EmuDebugLog("!!!!!!!!!!!!TRIG! %d\n", arpretrig);
- targetvol = 0.f;
- // DebugLog("\r\n%d", fingeridx);
- int ypos = 0;
- if (ramsample.pitched && !gp) {
- /// / / / ////////////////////// multisample choice
- int best = fingeridx;
- int bestdist = 0x7fffffff;
- int mypitch = (v->theosc[1].pitch + v->theosc[2].pitch) / 2;
- int mysemi = (mypitch) >> 9;
- static u8 multisampletime[8];
- static u8 trigcount = 0;
- trigcount++;
- for (int i = 0; i < 8; ++i) {
- int dist = abs(mysemi - ramsample.notes[i]) * 256 - (u8)(trigcount - multisampletime[i]);
- if (dist < bestdist) {
- bestdist = dist;
- best = i;
- }
- }
- multisampletime[best] = trigcount; // for round robin
- v->sliceidx = best;
- if (grate < 0.f)
- ypos = 8;
- }
- else {
- v->sliceidx = fingeridx;
- ypos = (f->pos / 256);
- if (gp)
- ypos = 0;
- if (grate < 0.f)
- ypos++;
- }
- v->initialfingerpos = gp ? 128 : f->pos;
- v->playhead8 = sample_slice_pos8(((v->sliceidx * 8) + ypos) << (16 - 6)) ;
- if (grate < 0.f) {
- v->playhead8 -= 192 << 8;
- if (v->playhead8 < 0)
- v->playhead8 = 0;
- }
- knobsmooth_reset(&v->fingerpos, 0);
- }
- else { // not trigger - just advance playhead
- float ms2 = (v->thegrains[0].multisample_grate + v->thegrains[1].multisample_grate); // double multisample rate
- int delta_playhead8 = (int)(grate * ms2 * timestretch * (BLOCK_SAMPLES * 0.5f * 256.f) + 0.5f);
- v->playhead8 = doloop8(v->playhead8 + delta_playhead8, v->sliceidx);
-
- float gdeadzone = clampf(minf(1.f - posjit, timestretch * 2.f), 0.f, 1.f); // if playing back normally and not jittering, add a deadzone
- float fpos = deadzone(f->pos - v->initialfingerpos, gdeadzone * 32.f);
- if (gp)
- fpos = 0.f;
-// EmuDebugLog("scrub pos %0.2f\n", fpos);
- knobsmooth_update_knob(&v->fingerpos, fpos, 2048.f);
- }
- } // sampler prep
-
-#define OSC_COUNT 2 // DO NOT CHECK IN 1 // XXX 2
- s32 pwm_base = rampreset.params[P_PWM][0];
- s32 pwm = param_eval_finger(P_PWM, fingeridx, synthf);
- if (pwm_base >=-8 && pwm_base<8)
- pwm = 0;
- else if (pwm_base > 0)
- pwm = clampi(pwm, 1, 65535);
- else
- pwm = clampi(pwm, -65535, -1);
-
-
-
- for (int osci = 0; osci < OSC_COUNT; osci++) {
- s16 *dst = ((s16*) outbuf) + (osci & 1);
- noise = v->noise;
- float y1 = v->y[0 + osci] , y2 = v->y[2 + osci];
- int randtabpos = rand() & 16383;
- if (ramsample.samplelen) {
- // mix grains
- GrainPair* g = &v->thegrains[osci];
- int grainidx = fingeridx * 4 + osci * 2;
- int g0start = 0;
- if (grainidx) g0start = grainbufend[grainidx - 1];
- int g1start = grainbufend[grainidx];
- int g2start = grainbufend[grainidx + 1];
-
- int64_t posa = g->pos[0];
- int64_t posb = g->pos[1];
- int loopstart = calcloopstart(prevsliceidx);
- int loopend = calcloopend(prevsliceidx);
- bool outofrange0 = posa < loopstart || posa >= loopend;
- bool outofrange1 = posb < loopstart || posb >= loopend;
- int gvol24 = g->vol24;
- int dgvol24 = g->dvol24;
- int dpos24 = g->dpos24;
- int fpos24 = g->fpos24;
- float vol = v->vol;
- float dvol = (targetvol - vol) * (1.f / BLOCK_SAMPLES);
- outofrange0 |= g1start - g0start <= 2;
- outofrange1 |= g2start - g1start <= 2;
- g->outflags = (outofrange0 ? 1 : 0) + (outofrange1 ? 2 : 0);
- if ((g1start - g0start <= 2 && g2start - g1start <= 2)) {
- // fast mode :) emulate side effects without doing any work
- vol += dvol * BLOCK_SAMPLES;
- noise += dnoise * BLOCK_SAMPLES;
- gvol24 -= dgvol24 * BLOCK_SAMPLES;
- fpos24 += dpos24 * BLOCK_SAMPLES;
- int id = fpos24 >> 24;
- g->pos[0] += id;
- g->pos[1] += id;
- fpos24 &= 0xffffff;
- }
- else {
- const s16* src0 = (outofrange0 ? (const s16*)zero : &grainbuf[g0start + 2]) + g->bufadjust;
- const s16* src0_backup = src0;
- const s16* src1 = (outofrange1 ? (const s16*)zero : &grainbuf[g1start + 2]) + g->bufadjust;
- if (spistate && spistate <= grainidx + 2) {
- //DebugLog("!"); // spidebug
- while (spistate && spistate <= grainidx + 2);
- }
- for (int i = 0; i < BLOCK_SAMPLES; ++i) {
- int o0, o1;
-#ifdef EMU
- ASSERT(outofrange0 || (src0 >= &grainbuf[g0start + 2] && src0 + 1 < &grainbuf[g1start]));
- ASSERT(outofrange1 || (src1 >= &grainbuf[g1start + 2] && src1 + 1 < &grainbuf[g2start]));
-#endif
- u32 ab0 = *(u32*)(src0); // fetch a pair of 16 bit samples to interpolate between
- u32 mix = (fpos24 << (16 - 9)) & 0x7fff0000;
- mix |= 32767 - (mix >> 16); // mix is now the weights for the linear interpolation
- SMUAD(o0, ab0, mix); // do the interpolation, result is *32768
- o0 >>= 16;
-
- u32 ab1 = *(u32*)(src1); // fetch a pair for the other grain in the pair
- SMUAD(o1, ab1, mix); // linear interp by same weights
- o1 >>= 16;
-
- fpos24 += dpos24; // advance fractional sample pos
- int bigdpos = (fpos24 >> 24);
- fpos24 &= 0xffffff;
- src0 += bigdpos; // advance source pointers by any whole sample increment
- src1 += bigdpos;
-
- mix = (gvol24 >> 9) & 0x7fff; // blend between the two grain results
- mix |= (32767 - mix) << 16;
- u32 o01 = STEREOPACK(o0, o1);
- int ofinal;
- SMUAD(ofinal, o01, mix);
- gvol24 -= dgvol24;
- if (gvol24 < 0)
- gvol24 = 0;
-
- s16 n = ((s16*)rndtab)[randtabpos++]; // mix in a white noise source
- noise += dnoise; // volume ramp for noise
-
- vol += dvol; // volume ramp for grain signal
- float input = (ofinal * drive + n * noise) ; // input to filter
- float cutoff = 1.f - squaref(maxf(0.f, 1.f - vol * 1.1f)); // filter cutoff for low pass gate
- y1 += (input - y1) * cutoff; // do the lowpass
-
- int yy = FLOAT2FIXED(y1 * vol, 0); // for granular, we include an element of straight VCA
- *dst = SATURATE16(*dst + yy); // write to output
- dst += 2;
-
- }
- int bigposdelta = src0 - src0_backup;
- g->pos[0] += bigposdelta;
- g->pos[1] += bigposdelta;
- } // grain mix
- g->fpos24 = fpos24;
- g->vol24 = gvol24;
-
- if (gvol24 <= dgvol24 || trig) { // new grain trigger! this is for the *next* frame
-// if (targetvol > 0.01f)
-// DebugLog("%c", 'a' + (int)(targetvol * 25));
- if (targetvol > 0.01f) {
- int i = 1;
- }
- if (!trig) {
- int i = 1;
- }
- int ph = v->playhead8 >> 8;
- int slicelen = ramsample.splitpoints[v->sliceidx + 1] - ramsample.splitpoints[v->sliceidx];
- if (editmode != EM_SAMPLE) {
- ph += ((int)(v->fingerpos.y2 * slicelen)) >> (10);
- ph += smppos; // scrub input
- }
- g->vol24 = ((1 << 24) - 1);
- int grainsize = ((rand() & 127) * sizejit + 128.f) * (gsize * gsize) + 0.5f;
- grainsize *= BLOCK_SAMPLES;
- int jitpos = (rand() & 255) * posjit;
- ph += ((grainsize+8192) * jitpos) >> 8;
- g->dvol24 = g->vol24 / grainsize;
-
- float grate2 = 1.f + ((rand() & 255) * (gratejit * gratejit)) * (1.f / 256.f);
- //float revprob = (0.125f - timestretch) * (4.f * 256.f);
- //if ((rand() & 255) < (int)revprob)
- if (timestretch<0.f)
- grate2 = -grate2;
- g->grate_ratio = grate2;
-#ifdef EMU
-// if (osci==0 && (targetvol > 0.01f || trig))
-// EmuDebugLog("%s grain at ph %d, other at %d, volume went from %f to %f, next to %f \n",
-// trig ? "trigger" : "new",
-// ph, (int)(g->pos[1]), v->vol, targetvol);
-#endif
- g->pos[0] = trig ? ph : g->pos[1];
- g->pos[1] = ph;
- }
- }
- else
- {
- // synth
- float vol = v->vol;
- float dvol = (targetvol - vol) * (1.f / BLOCK_SAMPLES);
-
- Osc* o = &v->theosc[osci];
-
- u32 flippity = 0;
- if (pwm!=0) {
- flippity = ~0;
- //if (abs(pwm)
- {
- //u32 pp = pwm >> 16;
- //if (pp > 1024) pp = 1024;
- u32 avgdp = (o[0].dphase + o[2].dphase) / 2;
- o[0].dphase = avgdp;//(s32)((avgdp - o[0].dphase) * pp) >> 10;
- o[2].dphase = avgdp;// (s32)((avgdp - o[2].dphase)* pp) >> 10;
- avgdp = (o[0].targetdphase + o[2].targetdphase) / 2;
- o[0].targetdphase = avgdp;// (s32)((avgdp - o[0].targetdphase)* pp) >> 10;
- o[2].targetdphase = avgdp;// (s32)((avgdp - o[2].targetdphase)* pp) >> 10;
-
- if (pwm < 0) {
- s32 phase0fix = (s32)(o[2].phase - o[0].phase - (pwm<<16) + (1 << 31)) / (BLOCK_SAMPLES);
- o[0].dphase += phase0fix;
- o[0].targetdphase += phase0fix;
- }
- }
- }
- // o->dphase=o->targetdphase;// XXXX REMOVE GLIDE
- int ddphase1 = (int)((o->targetdphase - o->dphase) * glide);
- u32 phase1 = o->phase;
- s32 dphase1 = o->dphase;
- u32 prevsample1 = o->prevsample;
- o += 2;
- // o->dphase=o->targetdphase;// XXXX REMOVE GLIDE
- int ddphase2 = (int)((o->targetdphase - o->dphase) * glide);
- u32 phase2 = o->phase;
- s32 dphase2 = o->dphase;
- u32 prevsample2 = o->prevsample;
- o -= 2;
-
- if (pwm>0) {
- //pwm -= 4096;
- // we need to choose the shift so that, after shifting right, there are 16 bits of fractional part in each cycle
- // if the increment was say, 1<<(32-9) = ((1<<23)-1), we would take just over 512 steps to cycle, so we want to shift by 7
- //dphase1 = (1 << 22) - 1;
- int shift1 = 16-clz(maxi(dphase1,1<<22));
- const static u16 wavetable_octave_offset[17] = { 0,0,0,0,0,0,0,
- 0,// 7 - 9 bits
- 513, // 8 - 8 bits
- 513 + 257, // 9 - 7 bits
- 513 + 257 + 129, // 10 - 6 bits
- 513 + 257 + 129 + 65,
- 513 + 257 + 129 + 65 + 33,
- 513 + 257 + 129 + 65 + 33 + 17,
- 513 + 257 + 129 + 65 + 33 + 17 + 9,
- 513 + 257 + 129 + 65 + 33 + 17 + 9 + 5,
- 513 + 257 + 129 + 65 + 33 + 17 + 9 + 5 + 3, // 1031
- };
- // 16 wave shapes
-// pwm = 2<<12;
- u32 subwave = (pwm & 4095) << (1);
- //subwave = 0; DISABLE BLENDING
- subwave = subwave | ((8191 - subwave) << 16);
- int wtbase = (pwm) >> (12);
- const s16* table1 = wavetable[wtbase] + wavetable_octave_offset[shift1];
- for (int i = 0; i < BLOCK_SAMPLES; ++i) {
- unsigned int i,i2;
- int s0, s1;
- dphase1 += ddphase1;
- i = (phase1 += dphase1) >> shift1;
- i2 = i >> 16;
- s0 = table1[i2], s1 = table1[i2 + 1];
- s32 out0 = (s0<<16) + ((s1 - s0) * (u16)(i));
- i2 += WAVETABLE_SIZE;
- s0 = table1[i2], s1 = table1[i2 + 1];
- s32 out1 = (s0<<16) + ((s1 - s0) * (u16)(i));
- u32 packed=STEREOPACK(out1>>16, out0>>16);
- s32 out;
- SMUAD(out,packed,subwave);
- //////////////////////////////////////////////////
- // rest is same as polyblep
- s16 n = ((s16*)rndtab)[randtabpos++];
- noise += dnoise;
-
- vol += dvol;
- y1 += (out * drive + n * noise - (y2 - y1) * resonance - y1) * vol; // drive
- // y1 *= 16383.f / (16384.f + fabsf(y2));
- y1 *= 0.999f;
- y2 += (y1 - y2) * vol;
-
- y2 *= 0.999f;
-
- int yy = FLOAT2FIXED(y2, 0);
- *dst = SATURATE16(*dst + yy);
- dst += 2;
- }
- }
- else {
- for (int i = 0; i < BLOCK_SAMPLES; ++i) {
- dphase1 += ddphase1;
- phase1 += dphase1;
- u32 newsample1 = phase1;
-
- if (unlikely(phase1 < (u32)dphase1)) {
- // edge! polyblep it.
- u32 fractime = mini(65535, phase1 / (dphase1 >> 16));
- prevsample1 -= (fractime * fractime) >> 1;
- fractime = 65535 - fractime;
- newsample1 += (fractime * fractime) >> 1;
- }
- s32 out = (s32)(prevsample1 >> 4); // (s32)prevsample1 >> 4
- prevsample1 = newsample1;
-
- dphase2 += ddphase2;
- phase2 += dphase2;
- u32 newsample2 = phase2;
- if (unlikely(phase2 < (u32)dphase2)) {
- // edge! polyblep it.
- u32 fractime = mini(65535, phase2 / (dphase2 >> 16));
- prevsample2 -= (fractime * fractime) >> 1;
- fractime = 65535 - fractime;
- newsample2 += (fractime * fractime) >> 1;
-
- }
-
-#ifdef EMU
- /*
- if (fingeridx == 3 && targetvol>0.f && osci==1) {
- static FILE* testy = 0;
- if (!testy)
- testy = fopen("testy.raw", "wb");
- s16 aa = (out >> 16) - 32678 / 16;
- s16 bb = (((prevsample2 ^ flippity) >> 4) >> 16) - 32768 / 16;
- fwrite(&aa, 1, 2, testy);
- fwrite(&bb, 1, 2, testy);
- }*/
-#endif
-
- out += (s32)((prevsample2 ^ flippity) >> 4) - (2 << (31 - 4));
- prevsample2 = newsample2;
-
- s16 n = ((s16*)rndtab)[randtabpos++];
- noise += dnoise;
-
- vol += dvol;
- y1 += (out * drive + n * noise - (y2 - y1) * resonance - y1) * vol; // drive
- // y1 *= 16383.f / (16384.f + fabsf(y2));
- y1 *= 0.999f;
- y2 += (y1 - y2) * vol;
-
- y2 *= 0.999f;
-
- int yy = FLOAT2FIXED(y2, 0);
- *dst = SATURATE16(*dst + yy);
- dst += 2;
- } // samples
- }
- o[0].phase = phase1;
- o[0].dphase = dphase1;
- o[0].prevsample = prevsample1;
-
- o[2].phase = phase2;
- o[2].dphase = dphase2;
- o[2].prevsample = prevsample2;
- } // synth
- v->y[osci] = y1, v->y[osci + 2] = y2;
- }// osc loop
-
- v->vol = targetvol;
- v->noise = noise;
-
- if (ramsample.samplelen) {
- // update pitch (aka dpos24) for next time!
- for (int gi = 0; gi < 2; ++gi) {
- float multisample_grate;
- if (ramsample.pitched && !isgrainpreview()) {
- int relpitch = v->theosc[1 + gi].pitch - ramsample.notes[v->sliceidx] * 512;
- if (relpitch < -512 * 12 * 5) {
- multisample_grate = 0.f;
- }
- else {
- multisample_grate = // exp2f(relpitch / (512.f * 12.f));
- table_interp(pitches, relpitch+32768);
-
- }
- }
- else {
- multisample_grate = 1.f;
- }
-// multisample_grate = 1.f; // debug - force multisample to original pitch
- v->thegrains[gi].multisample_grate = multisample_grate;
- int dpos24 = (1 << 24) * (grate * v->thegrains[gi].grate_ratio * multisample_grate);
- while (dpos24 > (2 << 24))
- dpos24 >>= 1;
- v->thegrains[gi].dpos24 = dpos24;
- }
- }
-
- tc_stop(&_tc_osc);
-
-}
-
-
-s32 Reverb2(s32 input, s16 *buf) {
- int i = reverbpos;
-// int fb = buf[i];
- int outl = 0, outr = 0;
- float wob = lfo_next(&aplfo) * k_reverb_wob;
- int apwobpos = FLOAT2FIXED((wob + 1.f), 12 + 6);
- wob = lfo_next(&aplfo2) * k_reverb_wob;
- int delaywobpos = FLOAT2FIXED((wob + 1.f), 12 + 6);
-#define RVDIV /2
-#define CHECKACC // assert(acc>=-32768 && acc<32767);
-#define AP(len) { \
- int j = (i + len RVDIV) & RVMASK; \
- s16 d = buf[j]; \
- acc -= d >> 1; \
- buf[i] = SATURATE16(acc); \
- acc = (acc >> 1) + d; \
- i = j; \
- CHECKACC \
- }
-#define AP_WOBBLE(len, wobpos) { \
- int j = (i + len RVDIV) & RVMASK;\
- s16 d = LINEARINTERPRV(buf, j, wobpos); \
- acc -= d >> 1; \
- buf[i] = SATURATE16(acc); \
- acc = (acc >> 1) + d; \
- i = j; \
- CHECKACC \
- }
-#define DELAY(len) { \
- int j = (i + len RVDIV) & RVMASK; \
- buf[i] = SATURATE16(acc); \
- acc = buf[j]; \
- i = j; \
- CHECKACC \
- }
-#define DELAY_WOBBLE(len, wobpos) { \
- int j = (i + len RVDIV) & RVMASK; \
- buf[i] = SATURATE16(acc); \
- acc = LINEARINTERPRV(buf, j, wobpos); \
- i=j; \
- CHECKACC \
- }
-
- // Griesinger according to datorro does 142, 379, 107, 277 on the way in - totoal 905 (20ms)
- // then the loop does 672+excursion, delay 4453, (damp), 1800, delay 3720 - total 10,645 (241ms)
- // then decay, and feed in
- // and on the other side 908+excursion, delay 4217, (damp), 2656, delay 3163 - total 10,944 (248 ms)
-
- // keith barr says:
- // I really like 2AP, delay, 2AP, delay, in a loop.
- // I try to set the delay to somewhere a bit less than the sum of the 2 preceding AP delays,
- // which are of course much longer than the initial APs(before the loop)
- // Yeah, the big loop is great; you inject input everywhere, but take it out in only two places
- // It just keeps comin� newand fresh as the thing decays away.�If you�ve got the memoryand processing!
-
- // lets try the 4 greisinger initial Aps, inject stereo after the first AP,
-
- int acc = ((s16)(input)) * k_reverbsend >> 17;
- AP(142);
- AP(379);
- acc += (input >> 16) * k_reverbsend >> 17;
- AP(107);
-// int reinject2 = acc;
- AP(277);
- int reinject = acc;
- static int fb1 = 0;
- acc += fb1;
- AP_WOBBLE(672, apwobpos);
- AP(1800);
- DELAY(4453);
-
- if (1) {
- // shimmer - we can read from up to about 2000 samples ago
-
- // Brief shimmer walkthrough:
- // - We walk backwards through the reverb buffer with 2 indices: shimmerpos1 and shimmerpos2.
- // - shimmerpos1 is the *previous* shimmer position.
- // - shimmerpos2 is the *current* shimmer position.
- // - Note that we add these to i (based on reverbpos), which is also walking backwards
- // through the buffer.
- // - shimmerfade controls the crossfade between the shimmer from shimmerpos1 and shimmerpos2.
- // - When shimmerfade == 0, shimmerpos1 (the old shimmer) is chosen.
- // - When shimmerfade == SHIMMER_FADE_LEN - 1, shimmerpos2 (the new shimmer) is chosen.
- // - For everything in-between, we linearly interpolate (crossfade).
- // - When we hit the end of the fade, we reset shimmerpos2 to a random new position and set
- // shimmerpos1 to the old shimmerpos2.
- // - dshimmerfade controls the speed at which we fade.
-
- #define SHIMMER_FADE_LEN 32768
- shimmerfade += dshimmerfade;
-
- if (shimmerfade >= SHIMMER_FADE_LEN) {
- shimmerfade -= SHIMMER_FADE_LEN;
-
- shimmerpos1 = shimmerpos2;
- shimmerpos2 = (rand() & 4095) + 8192;
- dshimmerfade = (rand() & 7) + 8; // somewhere between SHIMMER_FADE_LEN/2048 and SHIMMER_FADE_LEN/4096 ie 8 and 16
- }
-
- // L = shimmer from shimmerpos1, R = shimmer from shimmerpos2
- u32 shim1 = STEREOPACK(buf[(i + shimmerpos1) & RVMASK], buf[(i + shimmerpos2) & RVMASK]);
- u32 shim2 = STEREOPACK(buf[(i + shimmerpos1 + 1) & RVMASK], buf[(i + shimmerpos2 + 1) & RVMASK]);
- u32 shim = STEREOADDAVERAGE(shim1, shim2);
-
- // Fixed point crossfade:
-#ifdef CORTEX
- u32 a = STEREOPACK((SHIMMER_FADE_LEN - 1) - shimmerfade, shimmerfade);
- s32 shimo;
- asm("smuad %0, %1, %2" : "=r" (shimo) : "r" (a), "r" (shim));
-#else
- STEREOUNPACK(shim);
- s32 shimo = shiml * ((SHIMMER_FADE_LEN - 1) - shimmerfade) +
- shimr * shimmerfade;
-#endif
- shimo >>= 15; // Divide by SHIMMER_FADE_LEN
-
- // Apply user-selected shimmer amount.
- shimo *= k_reverb_shim;
- shimo >>= 8;
-
- // Tone down shimmer amount.
- shimo >>= 1;
-
- acc += shimo;
- outl = shimo;
- outr = shimo;
-
- shimmerpos1--;
- shimmerpos2--;
- }
-
-
- const static float k_reverb_color = 0.95f;
- static float lpf = 0.f, dc = 0.f;
- lpf += (((acc * k_reverb_fade) >> 8) - lpf) * k_reverb_color;
- dc += (lpf - dc) * 0.005f;
- acc = (int)(lpf - dc);
- outl += acc;
-
- acc += reinject;
- AP_WOBBLE(908, delaywobpos);
- AP(2656);
- DELAY(3163);
- static float lpf2 = 0.f;
- lpf2+= (((acc * k_reverb_fade) >> 8) - lpf2) * k_reverb_color;
- acc = (int)(lpf2);
-
- outr += acc;
-
-// acc += reinject2;
-// AP(4931);
-// AP(3713);
-// DELAY(6137);
- /*
- int acc=((s16) (input))*k_reverbsend >> 17;
- AP(142);
- acc += (input >> 16) * k_reverbsend >> 17;
- AP(107);
- AP_WOBBLE(379, delaywobpos);
- AP(277);
- static int fb1=0;
- // first leg
- acc+=fb1;
- AP_WOBBLE(672, apwobpos);
- AP(4453);
-
- if (1) {
- // shimmer - we can read from up to about 2000 samples ago
- shimmerfade += dshimmerfade;
- if (shimmerfade >= 32768) {
- shimmerpos1 = shimmerpos2;
- shimmerpos2 = (rand() & 1023) + 1024;
- shimmerfade -= 32768;
- dshimmerfade = (rand() & 31) + 32; // somewhere between 65536/1024 and 65536/512 ie 64 and 128
- }
- u32 shim1 = STEREOPACK(buf[(i + shimmerpos1) & RVMASK], buf[(i + shimmerpos2) & RVMASK]);
- u32 shim2 = STEREOPACK(buf[(i + shimmerpos1 + 1) & RVMASK], buf[(i + shimmerpos2 + 1) & RVMASK]);
- u32 shim = STEREOADDAVERAGE(shim1, shim2);
-#ifdef CORTEX
- u32 a = STEREOPACK(32767 - shimmerfade, shimmerfade);
- s32 shimo;
- asm ("smuad %0, %1, %2" : "=r" (shimo) : "r" (a), "r" (shim));
-#else
- STEREOUNPACK(shim);
- s32 shimo=(shimr*shimmerfade + shiml*(32767-shimmerfade));
-#endif
- shimo >>= 16;
- shimo*=k_reverb_shim;
- shimo>>=8;
- acc += shimo>>1;
- outl = shimo;
- outr = shimo;
- shimmerpos1--;
- shimmerpos2--;
- // outl=outr=shimo;
- }
-
-
- outr+=acc;
-
- static float lpf = 0.f, dc=0.f;
- const static float k_reverb_color = 0.95f;
- lpf += (((acc*k_reverb_fade)>>8) - lpf) * k_reverb_color;
- dc+=(lpf-dc)*0.005f;
- acc = (int) (lpf - dc);
- AP(1800);
- DELAY(3270);
- outl+=acc;
- */
- reverbpos = (reverbpos - 1) & RVMASK;
- fb1=(acc*k_reverb_fade)>>8;
- return STEREOPACK(SATURATE16(outl), SATURATE16(outr));
-
-
-}
-
-
-
-
-
-u32 STEREOSIGMOID(u32 in) {
- u16 l = sigmoid[(u16) in];
- u16 r = sigmoid[in >> 16];
- return STEREOPACK(l, r);
-}
-
-s16 MONOSIGMOID(int in) {
- in=SATURATE16(in);
- return sigmoid[(u16) in];
-}
-
-void update_params(int fingertrig, int fingerdown);
-
-
-
-#ifdef EMU
-float powerout; // squared power
-float gainhistoryrms[512];
-int ghi;
-#endif
-
-int stride(u32 scale, int stride_semitones, int fingeridx) { // returns the number of scale steps up from 0 for finger index fi
- static u8 stridetable[8]; // memoise the result indexed by fi
- static u8 stridehash[8];
- //XXX TEST stride_semitones =2;
- u8 newhash = stride_semitones + (scale * 16);
- if (newhash != stridehash[fingeridx]) {
- // memoise this man
- stridehash[fingeridx] = newhash;
- int pos = 0;
- u8 usedsteps[16] = { 1, 0 };
- const u16* scaleptr = scaletab[scale];
- u8 numsteps = *scaleptr++;
- for (int fi = 0; fi < fingeridx; ++fi) {
- pos += stride_semitones;
- int step = pos % 12;
- int best = 0, bestdist = 0;
- int bestscore = 9999;
- for (int i = 0; i < numsteps; ++i) {
- int qstep = scaleptr[i]/512; // candidate step in the scale
- int dist = qstep - step;
- if (dist < -6) {
- dist += 12; qstep += 12;
- }
- else if (dist > 6) {
- dist -= 12; qstep -= 12;
- }
- int score = abs(dist) * 16 + usedsteps[i]; // penalise steps we have used many times
- if (score < bestscore) {
- bestscore = score;
- best = i;
- bestdist = dist;
- }
- }
- usedsteps[best]++;
- pos += bestdist;
- stridetable[fingeridx] = best + (pos / 12) * numsteps;
- }
- }
- return stridetable[fingeridx];
-}
-
-#ifdef EMU
-float m_compressor,m_dry, m_audioin, m_dry2wet, m_delaysend, m_delayreturn, m_reverbin, m_reverbout, m_fxout, m_output;
-void MONITORPEAK(float *mon, u32 stereoin) {
- STEREOUNPACK(stereoin);
- float peak = (1.f/32768.f)*maxi(abs(stereoinl), abs(stereoinr));
- if (peak > *mon) *mon = peak;
- else *mon += (peak - *mon) * 0.0001f;
-}
-#else
-#define MONITORPEAK(mon,stereoin)
-#endif
-u8 midi24ppqncounter;
-const s8 midicctable[128] = {
- // 0 1 2 3 4 5 6 7
- /* 0 */ -1, -1, P_NOISE, P_SENS, P_DRIVE, P_GLIDE, -1, P_MIXSYNTH,
- /* 8 */ P_MIXWETDRY,P_PITCH, -1, P_GATE_LENGTH,P_DLTIME, P_PWM, P_INTERVAL, P_SMP_POS,
- /* 16 */ P_SMP_GRAINSIZE,P_SMP_RATE,P_SMP_TIME,P_ENV_LEVEL,P_A2, P_D2, P_S2, P_R2,
- /* 24 */ P_AFREQ, P_ADEPTH, P_AOFFSET, P_BFREQ, P_BDEPTH, P_BOFFSET, -1, P_MIXHPF,
- /* 32 */ -1, -1, -1, -1, -1, -1, -1, -1,
- /* 40 */ -1, -1, -1, -1, -1, -1, -1, -1,
- /* 48 */ -1, -1, -1, -1, -1, -1, -1, -1,
- /* 56 */ -1, -1, -1, -1, -1, -1, -1, -1,
- /* 64 */ -1, -1, -1, -1, -1, -1, -1, P_MIXRESO,
- /* 72 */ P_R, P_A, P_S, P_D, P_XFREQ, P_XDEPTH, P_XOFFSET, P_YFREQ,
- /* 80 */ P_YDEPTH, P_YOFFSET, P_SAMPLE, P_SEQPAT, -1, P_SEQSTEP, -1, -1,
- /* 88 */ -1, P_MIXINPUT, P_MIXINWETDRY,P_RVSEND, P_RVTIME, P_RVSHIM, P_DLSEND, P_DLFB,
- /* 96 */ -1, -1, -1, -1, -1, P_LATCHONOFF, P_ARPONOFF, P_ARPMODE,
- /* 104 */ P_ARPDIV, P_ARPPROB, P_ARPLEN, P_ARPOCT, P_SEQMODE, P_SEQDIV, P_SEQPROB, P_SEQLEN,
- /* 112 */ P_DLRATIO, P_DLWOB, P_RVWOB, -1, P_JIT_POS, P_JIT_GRAINSIZE, P_JIT_RATE, P_JIT_PULSE,
- /* 120 */ -1, -1, -1, -1, -1, -1, -1, -1,
-};
-
-
-
-
-
-bool midi_receive(unsigned char packet[4]); // usb midi poll
-void processmidimsg(u8 msg, u8 d1, u8 d2);
-bool processusbmidi(void) {
- u8 midipacket[4];
- if (!midi_receive(midipacket))
- return false;
- // 09 90 30 64
- // 08 80 30 40
- //DebugLog("got midi %02x %02x %02x %02x\r\n", midipacket[0], midipacket[1], midipacket[2], midipacket[3]);
- u8 msg = midipacket[1];
- u8 d1 = midipacket[2];
- u8 d2 = midipacket[3];
- processmidimsg(msg,d1,d2);
- return true;
-}
-
-
-void midi_panic(void) {
- midi_pressure_override=0, midi_pitch_override=0;
- memset(midi_notes,0,sizeof(midi_notes));
- memset(midi_velocities, 0, sizeof(midi_velocities));
- memset(midi_aftertouch, 0, sizeof(midi_aftertouch));
- memset(midi_channels, 255, sizeof(midi_channels));
- memset(midi_chan_aftertouch, 0, sizeof(midi_chan_aftertouch));
- memset(midi_chan_pitchbend, 0, sizeof(midi_chan_pitchbend));
- midi_next_finger = 0;
-}
-
-bool send_midimsg(u8 status, u8 data1, u8 data2);
-void processmidimsg(u8 msg, u8 d1, u8 d2) {
- u8 chan = msg & 15;
- u8 type = msg >> 4;
-
- //int midi_ch_in = ((GetParam(P_MIDI_CH_IN, 0) * 16)/FULL) & 15;
- //int midi_ch_in = clampi(((GetParam(P_MIDI_CH_IN, 0) * 15)/FULL),0,15);
-
- int midi_ch_in = clampi((mini(GetParam(P_MIDI_CH_IN, 0), FULL - 1) * 16) / FULL,0,15);
-
- //if ((chan != 0)&&(type != 0xF)) // chan != 0 = allow all channels; LPZW KAY Fix for MIDI Sync type == F continue
- if ((chan != midi_ch_in)&&(type != 0xF)) // allow only selected channel and MIDI sync
- return;
- if (type < 8)
- return;
-
- // send_midimsg(msg, d1, d2); // midi echo!
-
- if (type == 9 && d2 == 0)
- type = 8;
-
- switch (type) {
- case 0xc: // program change
- if (d1<32)
- SetPreset(d1, false);
- break;
- case 8: { // note up
- // find the voice for this note up
- u8 fi = find_midi_note(chan, d1);
- if (fi < 8) {
- midi_pressure_override &= ~(1 << fi);
- midi_channels[fi] = 255;
- }
- }
- break;
- case 9: { // note down
- u8 fi = find_midi_note(chan, d1);
- if (fi == 255)
- fi = find_midi_free_channel();
- if (fi < 8) {
- midi_notes[fi] = d1;
- midi_channels[fi] = chan;
- midi_velocities[fi] = d2;
- midi_aftertouch[fi] = 0;
- midi_pressure_override |= 1 << fi;
- midi_pitch_override |= 1 << fi;
- }
- }
- break;
- case 0xe: // pitchbend
- midi_chan_pitchbend[chan] = (d1 + (d2 << 7)) - 0x2000;
- break;
- case 0xa: { // polyphonic aftertouch
- u8 fi = find_midi_note(chan, d1);
- if (fi < 8) {
- midi_aftertouch[fi] = d2;
- }
- }
- break;
- case 0xd: { // channel aftertouch
- midi_chan_aftertouch[chan] = d2;
- }
- break;
- case 0xb: // cc param
- {
- if (d1 >= 32 && d1 < 64)
- midi_lsb[d1 - 32] = d2;
- s8 param = (d1 < 128) ? midicctable[d1] : -1;
- if (param >= 0 && param < P_LAST) {
- int val;
- if (d1 < 32)
- val = (d2 << 7) + midi_lsb[d1];
- else
- val = (d2 << 7) + d2;
- val = (val * FULL) / (127 * 128 + 127);
- if (param_flags[param] & FLAG_SIGNED)
- val = val * 2 - FULL;
- EditParamNoQuant(param, M_BASE, val);
-
- if (param == P_ARPONOFF){
- if (val > 64){
- rampreset.flags = rampreset.flags | FLAGS_ARP;
- }else{
- rampreset.flags = rampreset.flags & ~FLAGS_ARP;
- }
- ShowMessage(F_32_BOLD, ((rampreset.flags & FLAGS_ARP)) ? "arp on" : "arp off", 0);
- }
- if (param == P_LATCHONOFF){
- if (val > 64){
- rampreset.flags = rampreset.flags | FLAGS_LATCH;
- }else{
- rampreset.flags = rampreset.flags & ~FLAGS_LATCH;
- }
- ShowMessage(F_32_BOLD, ((rampreset.flags & FLAGS_LATCH)) ? "latch on" : "latch off", 0);
- }
-
- }
- break;
- }
- case 0xf: // system
- if (msg == 0xfa) { // start
- got_ui_reset = true;
- midi24ppqncounter = 5; // 2020-02-26: Used to be 0, changed to 5: https://discord.com/channels/784856175937585152/784884878994702387/814951459581067264
- playmode = PLAYING;
- // seq_step(1);
- }
- else if (msg == 0xfb) { // continue
- playmode = PLAYING;
- }
- else if (msg == 0xfc) { // stop
- midi24ppqncounter = 0;
- playmode = PLAY_STOPPED;
- OnLoop();
- }
- else if (msg == 0xf8) {
- // midi clock! 24ppqn, we want 4, so divide by 6.
- midi24ppqncounter++;
- if (midi24ppqncounter == 6) {
- gotclkin++;
- midi24ppqncounter = 0;
- }
-
- }
- break;
- }
-
-}
-
-void DoRecordModeAudio(u32* dst, u32* audioin) {
- // recording or level testing
- int newaudiorec_gain = 65535 - GetADCSmoothedNoCalib(ADC_AKNOB);
- if (abs(newaudiorec_gain - audiorec_gain_target) > 256) // hysteresis
- audiorec_gain_target = newaudiorec_gain;
- knobsmooth_update_knob(&recgain_smooth, audiorec_gain_target, 65536.f);
- int audiorec_gain = (int)(recgain_smooth.y2)/2;
-
-
- cur_sample1 = edit_sample0 + 1;
- CopySampleToRam(false);
- if (enable_audio == EA_ARMED) {
- if (audioin_peak > 1024) {
- recording_trigger();
- }
- }
- if (enable_audio > EA_PREERASE && enable_audio < EA_STOPPING4) {
- s16* dldst = delaybuf + (recpos & DLMASK);
- const s16* asrc = (const s16*)audioin;
- s16* adst = (s16*)dst;
- if (enable_audio >= EA_STOPPING1) {
- memset(dldst, 0, BLOCK_SAMPLES * 2);
- enable_audio++;
- }
- else {
- for (int i = 0; i < BLOCK_SAMPLES; ++i) {
- s16 smp = *dldst++ = SATURATE16((((int)(asrc[0] + asrc[1])) * audiorec_gain) >> 14);
-#ifdef EMU
- smp = 0; // disable loopback
-#endif
- adst[0] = adst[1] = smp;
- adst += 2;
- asrc += 2;
- }
- }
- recpos += BLOCK_SAMPLES;
- }
-}
-
-s16 audioin_is_stereo = 0;
-s16 noisegate=0;
-#ifdef DEBUG
-//#define NOISETEST
-#endif
-#ifdef NOISETEST
-float noisetestl=0, noisetestr=0,noisetest=0;
-#endif
-void PreProcessAudioIn(u32* audioin) {
- int newpeak = 0, newpeakr=0;
-#ifdef NOISETEST
- int newpeakl=0;
-#endif
- static float dcl, dcr;
- int ng=mini(256, noisegate);
- // dc remover from audio in, and peak detector while we're there.
- for (int i = 0; i < BLOCK_SAMPLES; ++i) {
- u32 inp = audioin[i];
- STEREOUNPACK(inp);
- dcl += (inpl - dcl) * 0.0001f;
- dcr += (inpr - dcr) * 0.0001f;
- inpl -= dcl;
- inpr -= dcr;
- newpeakr = maxi(newpeakr, abs(inpr));
-#ifdef NOISETEST
- newpeakl = maxi(newpeakl, abs(inpl));
-#endif
- if (!audioin_is_stereo)
- inpr = inpl;
- newpeak = maxi(newpeak, abs(inpl + inpr));
- inpl=(inpl*ng) >> 8;
- inpr=(inpr*ng) >> 8;
-
- audioin[i] = STEREOPACK(inpl, inpr);
- }
- if (newpeak > 400)
- noisegate=1000;
- else
- if (noisegate > 0)
- noisegate--;
-
- if (newpeakr > 300)
- audioin_is_stereo = 1000;
- else
- if (audioin_is_stereo > 0)
- audioin_is_stereo--;
-
-#ifdef NOISETEST
- if(newpeakl>noisetestl) noisetestl=newpeakl; else noisetestl+=(newpeakl-noisetestl)*0.01f;
- if(newpeakr>noisetestr) noisetestr=newpeakr; else noisetestr+=(newpeakr-noisetestr)*0.01f;
- if(newpeak>noisetest) noisetest=newpeak; else noisetest+=(newpeak-noisetest)*0.01f;
-#endif
- int audiorec_gain = (int)(recgain_smooth.y2)/2;
-
-
- newpeak = SATURATE16((newpeak * audiorec_gain) >> 14);
- audioin_peak = maxi((audioin_peak * 220) >> 8, newpeak);
- if (audioin_peak > audioin_hold || audioin_holdtime++ > 500) {
- audioin_hold = audioin_peak;
- audioin_holdtime = 0;
- }
-}
-static s16 scopex = 0;
-
-void usb_midi_update(void);
-void serial_midi_update(void);
-
-u8 midi_last_pitch[8];
-u8 midi_first_vol[8];
-u8 midi_last_at[8];
-u8 midi_last_pos[8];
-u8 midi_last_pressure[8];
-u8 midi_send_chan;
-u8 midi_desired_note[8];
-void kick_midi_send(void);
-bool serial_midi_ready(void);
-
-void midi_send_update(void) {
- if (!serial_midi_ready())
- return;
- for (int i = 0; i < 8; ++i) {
- Finger* synthf = touch_synth_getlatest(midi_send_chan);
- Finger* prevsynthf = touch_synth_getprev(midi_send_chan);
-
- bool pressurestable = abs(prevsynthf->pressure - synthf->pressure) < 100;
- bool posstable = abs(prevsynthf->pos - synthf->pos) < 32;
- bool pressure_significant = synthf->pressure > 200;
-
- int desired_pitch = midi_desired_note[midi_send_chan];
- int desired_vol = clampi((synthf->pressure-100)/48, 0, 127);
- //int prev_vol = clampi((prevsynthf->pressure-100) / 48, 0, 127);
- u8 desired_pos = clampi(127 - (synthf->pos/13-16), 0, 127);
- if (desired_pitch == 0)
- desired_vol = 0;
- if (arpmode >= 0 && !(arpbits & (1 << midi_send_chan))) {
- desired_vol = 0;
- }
- if (desired_vol <= 0)
- desired_pitch = 0;
- bool sent = false;
- u8 cur_pitch = midi_last_pitch[midi_send_chan];
-
- if (desired_pitch != cur_pitch && (desired_pitch==0 || (posstable && pressurestable))) {
- // send note up
- if (cur_pitch != 0) {
- //if (midi_send_chan == 0) printf("note up because %d vs %d\n", desired_pitch, cur_pitch);
- if (!send_midimsg(0x80, cur_pitch, 0)) break;
- midi_last_pitch[midi_send_chan] = 0;
- midi_last_at[midi_send_chan] = 0;
- sent=true;
- }
- // send new note down
- if (desired_pitch != 0) {
- //if (midi_send_chan == 0) printf("note down because %d vs %d %d\n", desired_pitch, cur_pitch, desired_vol);
- if (!send_midimsg(0x90, desired_pitch, desired_vol)) break;
- midi_last_pitch[midi_send_chan] = desired_pitch;
- midi_first_vol[midi_send_chan] = desired_vol;
- midi_last_at[midi_send_chan] = 0;
- sent=true;
- }
- }
-
- u8 cur_at = midi_last_at[midi_send_chan];
- int desired_at = desired_vol - midi_first_vol[midi_send_chan];
- if (desired_at < 0) desired_at = 0;
- if (abs(desired_at -cur_at) > 4) {
- if (!send_midimsg(0xa0, cur_pitch, desired_at)) break;
- midi_last_at[midi_send_chan] = desired_at;
- sent=true;
- }
-
- u8 cur_pos = midi_last_pos[midi_send_chan];
- if (abs(desired_pos - cur_pos) > 1 && pressure_significant && pressurestable) {
- if (!send_midimsg(0xb0, 32 + midi_send_chan, desired_pos)) break;
- midi_last_pos[midi_send_chan] = desired_pos;
- sent=true;
- }
- u8 cur_pressure = midi_last_pressure[midi_send_chan];
- if (abs(desired_vol - cur_pressure) > 1) {
- if (!send_midimsg(0xb0, 40 + midi_send_chan, desired_vol)) break;
- midi_last_pressure[midi_send_chan] = desired_vol;
- sent=true;
- }
-
- midi_send_chan = (midi_send_chan + 1) & 7;
- if (sent) {
- kick_midi_send();
- break;
- }
- }
-}
-
-int audiotime = 0;
-void DoAudio(u32 *dst, u32 *audioin) {
- audiotime += BLOCK_SAMPLES;
- tc_start(&_tc_audio);
- memset(dst, 0, 4 * BLOCK_SAMPLES);
- PreProcessAudioIn(audioin); // dc remover; peak detector
-// enable_audio = EA_PASSTHRU;
- if (enable_audio <= 0) {
- if (enable_audio == EA_PASSTHRU) {
- // memcpy(dst, audioin, 4 * BLOCK_SAMPLES);
- for (int i=0;i<BLOCK_SAMPLES;++i) {
- float t=(i-BLOCK_SAMPLES/2)*(2.f/BLOCK_SAMPLES);
- if (t<0) t=-t;
- t=EvalSin(t,0);
- s16 ss=t*32767.f;
- t=(((i*2)&(BLOCK_SAMPLES-1))-BLOCK_SAMPLES/2)*(2.f/BLOCK_SAMPLES);
- if (t<0) t=-t;
- t=EvalSin(t,0);
- s16 tt=t*32767.f;
- dst[i]=STEREOPACK(tt,ss);
- }
- }
- return;
- }
-
- if (enable_audio > EA_PLAY) {
- DoRecordModeAudio(dst, audioin);
- return;
- }
- int ainlvl = param_eval_int(P_MIXINPUT, any_rnd, env16, pressure16);
- int audiorec_gain_target = ainlvl; // we want to update recgain_smooth as it is used to maintain the pretty audio gain history
- knobsmooth_update_knob(&recgain_smooth, audiorec_gain_target, 65536.f);
-
- //////////////////////////////////////////////////////////
- // PLAYMODE
-
- CopyPresetToRam(false);
- // a few midi messages per tick. WCGW
- if (1) {
- midi_send_update();
-#ifndef EMU
- usb_midi_update();
- serial_midi_update();
-#endif
- for (int i = 0; i < 2; ++i) if (!processusbmidi())
- break;
- }
- // do the clock first so we can update the sequencer step etc
- bool gotclock = update_clock();
- static u8 whichhalf = 0;
- for (int fi = whichhalf; fi < whichhalf + 4; ++fi) {
- finger_synth_update(fi);
- if (fi == 7) {
-
- if (total_ui_pressure<=0 && prev_total_ui_pressure <= 0 && prev_prev_total_ui_pressure > 0 && recording && playmode != PLAYING) {
- // you've released your fingers, you're recording in step mode - let's advance!
- set_cur_step(cur_step + 1, false);
- }
- prev_prev_total_ui_pressure = prev_total_ui_pressure;
- prev_total_ui_pressure = total_ui_pressure;
- // rather than incrementing, we let finger_frame_synth shadow the ui. that way we get the full variability of the
- // ui input (due to it tikcing slowly), but we dont accidentally read ahead
- finger_frame_synth = finger_frame_ui;
- //(finger_frame_synth + 1) & 7;
- }
- }
- whichhalf ^= 4;
-
- prevsynthfingerdown = synthfingerdown;
- synthfingerdown = 0;
- prevprevsynthfingerdown_nogatelen = prevsynthfingerdown_nogatelen;
- prevsynthfingerdown_nogatelen = synthfingerdown_nogatelen;
- synthfingerdown_nogatelen = synthfingerdown_nogatelen_internal;
- for (int fi = 0; fi < 8; ++fi) {
- Finger* synthf = touch_synth_getlatest(fi);
- int thresh = (prevsynthfingerdown & (1 << fi)) ? -50 : 1;
- if (synthf->pressure > thresh) {
- synthfingerdown |= 1 << fi;
- }
- }
- synthfingertrigger = (synthfingerdown & ~prevsynthfingerdown);
- // needs finger_down to be set
-
- bool latchon = ((rampreset.flags & FLAGS_LATCH));
-
- if (!isplaying() && !read_from_seq && !latchon && synthfingerdown_nogatelen != 0 && prevsynthfingerdown_nogatelen == 0) {
- // they have put their finger down after no arp playing, trigger it immediately!
- arp_reset_partial();
- /* -- this caused missed clock steps! what!
- if (!external_clock_enable) {
- bpm_clock_phase = 0;
- ticks_since_clock = 0;
- gotclock = true;
- }
- */
- seq_reset(); // so that gate length works
- }
- /* this causes random restarts when jamming, I dont like it
- else if (rampreset.arpon && arp_rhythm.did_a_retrig && !arp_rhythm.supress && synthfingerdown_nogatelen && !(arpbits & synthfingerdown_nogatelen)) {
- // oh no! suddenly the arp bits dont overlap with the fingers. maybe the sequencer moved on. do a partial reset of the arp
- if (!isarpmode8step(arpmode)) {
- arp_reset_partial();
- gotclock = true;
- }
- }
- */
-
- update_arp(gotclock);
- update_params(synthfingertrigger, synthfingerdown);
- int seqdivi = param_eval_int(P_SEQDIV, any_rnd, env16, pressure16);
- seqdiv = (seqdivi==DIVISIONS_MAX) ? -1 : divisions[ clampi(seqdivi, 0, DIVISIONS_MAX-1) ]-1;
-
- cur_sample1 = param_eval_int(P_SAMPLE, any_rnd, env16, pressure16);
- cur_pattern = param_eval_int(P_SEQPAT, any_rnd, env16, pressure16);
- step_offset = param_eval_int(P_SEQSTEP, any_rnd, env16, pressure16);
- check_curstep();
- CopySampleToRam(false);
- CopyPatternToRam(false);
-
- //static int fr = 0;
- //DebugLog("%02x - %d - frame synth %d\n", synthfingerdown, touch_synth_getlatest(7)->pressure, finger_frame_synth);
- //if (synthfingertrigger)
- // DebugLog("%d %2x\r\n", fr,synthfingertrigger);
- //fr++;
-
-
-
- // decide on pitches & run the dry synth for the 8 fingers!
- int maxpressure = 0;
- int pitchhi = 0;
- int maxvol = 0;
- bool gotlow = false, gothi = false;
- trigout = false;
-
-
- int cvpitch = (int)(adc_smooth[6].y2 * (512.f * 12.f)); // pitch cv input
- int cvquant = param_eval_int(P_CV_QUANT, any_rnd, env16, pressure16);
- if (cvquant) {
- cvpitch = (cvpitch + 256) & (~511);
- }
- for (int fi = 0; fi < 8; ++fi) {
- Finger* synthf = touch_synth_getlatest(fi);
- float vol = (synthf->pressure) * 1.f / 2048.f ; // sensitivity
- {
- // pitch table is (64*8) steps per semitone, ie 512 per semitone
- int octave = arpoctave + param_eval_finger(P_OCT, fi, synthf);
- // so heres my maths, this comes out at 435
- // 8887421 comes from the value of pitch when playing a C
- // the pitch of middle c in plinky as written is (4.0/(65536.0*65536.0/8887421.0/31250.0f))
- // which is 1.0114729530400526 too low
- // which is 0.19749290999 semitones flat
- // *512 = 101. so I need to add 101 to pitch_base
-
-#define PITCH_BASE ((32768-(12<<9)) + (1*512) + 101)
- int pitchbase =12*((octave<<9) + (param_eval_finger(P_PITCH, fi, synthf)>>7)); // +- one octave
- int root = param_eval_finger(P_ROTATE, fi, synthf);
- int interval = (param_eval_finger(P_INTERVAL, fi, synthf) * 12) >> 7;
- int totpitch = 0;
- if (midi_pitch_override & (1 << fi)) {
- Finger* f = fingers_synth_sorted[fi] + 2;
- int midinote = ((midi_notes[fi]-12*2) << 9) + midi_chan_pitchbend[midi_channels[fi]]/8;
- for (int i = 0; i < 4; ++i) {
- int pitch = pitchbase + ((i & 1) ? interval : 0) + (i-2)*64 + midinote; // (lookupscale(scale, ystep + root)) + +((fine * microtune) >> 14);
- totpitch += pitch;
- voices[fi].theosc[i].pitch = pitch;
- voices[fi].theosc[i].targetdphase = maxi(65536, (int)(table_interp(pitches, pitch + PITCH_BASE) * (65536.f * 128.f)));
- ++f;
- }
- }
- else {
- u32 scale = param_eval_finger(P_SCALE, fi, synthf);
- if (scale >= S_LAST) scale = 0;
- if (cvquant != CVQ_SCALE)
- pitchbase += cvpitch;
- else {
- // remap the 12 semitones input to the scale steps, evenly, with a slight offset so white keys map to major scale etc
- int steps = ((cvpitch / 512) * scaletab[scale][0] + 1) / 12;
- root += steps;
- }
- int stride_semitones = maxi(0,param_eval_finger(P_STRIDE, fi, synthf));
- root += stride(scale, stride_semitones, fi);
- int microtune = 64 + param_eval_finger(P_MICROTUNE, fi, synthf); // really, micro-tune amount
-
- Finger* f = fingers_synth_sorted[fi] + 2;
- for (int i = 0; i < 4; ++i) {
- // if (ramsample.samplelen)
- // f = synthf; // XXX FORCE LATEST
- int ystep = 7 - (f->pos >> 8);
- int fine = 128 - (f->pos & 255);
- int pitch = pitchbase + (lookupscale(scale, ystep + root)) + ((i & 1) ? interval : 0) + ((fine * microtune) >> 14);
- totpitch += pitch;
- voices[fi].theosc[i].pitch = pitch;
- voices[fi].theosc[i].targetdphase = maxi(65536, (int)(table_interp(pitches, pitch + PITCH_BASE) * (65536.f * 128.f)));
- ++f;
- }
- }
-#ifdef DEBUG
- if (fi == 0)
-#endif
- //if (fi<6)
- RunVoice(&voices[fi], fi, vol, dst);
- if (vol > 0.001f) {
- if (!gotlow) {
- SetOutputCVPitchLo(totpitch, true);
- gotlow = true;
- }
- if (arpmode < 0 || (arpbits & (1<<fi))) {
- pitchhi = totpitch;
- gothi = true;
- }
- midi_desired_note[fi] = clampi((totpitch + 1024) / 2048 + 24, 0, 127);
- }
- maxvol = maxi(maxvol, (int)(vol * 65536.f));
- }
- if (synthf->pressure > maxpressure)
- maxpressure = synthf->pressure;
- }
- SetOutputCVPressure(maxpressure * 8);
- SetOutputCVTrigger(trigout ? 65535 : 0);
- if (gothi)
- SetOutputCVPitchHi(pitchhi, true);
- SetOutputCVGate(maxvol);
- AdvanceCVOut();
- tc_stop(&_tc_audio);
-
- if (ramsample.samplelen > 0) {
- // decide on a priority for 8 voices
- int gprio[8];
- u32 sampleaddr = ((cur_sample1 - 1) & 7) * MAX_SAMPLE_LEN;
-
- for (int i = 0; i < 8; ++i) {
- GrainPair* g = voices[i].thegrains;
- int glen0 = ((abs(g[0].dpos24) * (BLOCK_SAMPLES/2) + g[0].fpos24/2 + 1) >> 23) + 2; // +2 for interpolation
- int glen1 = ((abs(g[1].dpos24) * (BLOCK_SAMPLES/2) + g[1].fpos24/2 + 1) >> 23) + 2; // +2 for interpolation
-
- // TODO - if pos at end of next fetch will be out of bounds, negate dpos24 and grate_ratio so we ping pong back for the rest of the grain!
- int glen = maxi(glen0, glen1);
- glen = clampi(glen, 0, AVG_GRAINBUF_SAMPLE_SIZE*2);
- g[0].bufadjust = (g[0].dpos24< 0) ? maxi(glen-2,0): 0;
- g[1].bufadjust = (g[1].dpos24< 0) ? maxi(glen-2, 0) : 0;
- grainpos[i * 4 + 0] = (int)(g[0].pos[0]) - g[0].bufadjust + sampleaddr ;
- grainpos[i * 4 + 1] = (int)(g[0].pos[1]) - g[0].bufadjust + sampleaddr;
- grainpos[i * 4 + 2] = (int)(g[1].pos[0]) - g[1].bufadjust + sampleaddr;
- grainpos[i * 4 + 3] = (int)(g[1].pos[1]) - g[1].bufadjust + sampleaddr;
- glen += 2; // 2 extra 'samples' for the SPI header
- // if (i==0) EmuDebugLog("%d %d %d %d\n", grainpos[0], grainpos[1], grainpos[2], grainpos[3]);
- gprio[i]=((int)(voices[i].vol * 65535.f) << 12) + i + (glen << 3);
- }
- sort8(gprio, gprio);
- u8 lengths[8];
- int pos = 0,i;
-#if defined DEBUG
-#define MAX_SAMPLE_VOICES 1
-#else
-#define MAX_SAMPLE_VOICES 6
-#endif
- for (i = 7; i >= 0; --i) {
- int prio = gprio[i];
- int fi = prio & 7;
- int len = (prio >> 3) & 255;
- if (i < 8 - MAX_SAMPLE_VOICES)
- len = 0; // we only budget for MAX_SPI_STATE transfers. so after that, len goes to 0. also helps CPU load.
- else if (voices[fi].vol <= 0.01f && !(synthfingerdown & (1 << fi)))
- len = 0; // if your finger is up and the volume is 0, we can just skip this one.
- lengths[fi] = (pos + len * 4 > GRAINBUF_BUDGET) ? 0 : len;
- pos += len*4;
- }
- // cumulative sum
- pos = 0;
- for (int i = 0; i < 32; ++i) {
- pos += lengths[i / 4];
- grainbufend[i] = pos;
- }
- if (spistate == 0)
- spi_readgrain_dma(0); // kick off the dma for next time
- else {
- //DebugLog("?"); // spidebug
- }
- } else {
- // just update dac when not in sampler mode
- if (spistate == 0)
- spi_update_dac(0);
- }
- tc_start(&_tc_fx);
- // /// ////////////////////////////////////////////////////////////////////////
- // half rate fx
- static u16 delaypos = 0;
- static u32 wetlr;
- const float k_target_fb = param_eval_float(P_DLFB, any_rnd, env16,pressure16) * (0.35f); // 3/4
- static float k_fb=0.f;
- //const int k_delay2out = param_eval_int(P_MIXDL, any_rnd, env16,pressure16) >> 8; // 1
- int k_target_delaytime = param_eval_int(P_DLTIME, any_rnd, env16, pressure16);
- if (k_target_delaytime > 0) {
- // free timing
- k_target_delaytime = (((k_target_delaytime+255) >> 8) * k_target_delaytime) >> 8;
- k_target_delaytime = (k_target_delaytime * (DLMASK-64))>>16;
- }
- else {
- k_target_delaytime = divisions[clampi((-k_target_delaytime*13)>>16,0,12)]; // results in a number 1-32
- // figure out how samples we can have, max, in a beat synced scenario
- int max_delay = (32000 * 600 * 4) / (maxi(150, bpm10x));
- while (max_delay > DLMASK - 64)
- max_delay >>= 1;
- k_target_delaytime = (max_delay * k_target_delaytime) >> 5;
-
- }
- k_target_delaytime = clampi(k_target_delaytime, BLOCK_SAMPLES, DLMASK - 64) << 12;
- int k_delaysend=(param_eval_int(P_DLSEND, any_rnd, env16, pressure16)>>9);
-
- static int wobpos=0;
- static int dwobpos=0;
- static int wobcount=0;
- if (wobcount<=0) {
- const int wobamount =param_eval_int(P_DLWOB, any_rnd, env16,pressure16); // 1/2
- int newwobtarget=((rand()&8191)*wobamount)>>8;
- if (newwobtarget>k_target_delaytime/2)
- newwobtarget=k_target_delaytime/2;
- wobcount=((rand()&8191)+8192)&(~(BLOCK_SAMPLES-1));
- dwobpos=(newwobtarget-wobpos+wobcount/2)/wobcount;
- }
- wobcount-=BLOCK_SAMPLES;
-
-
-
- /*
- for (int i=0;i<64;++i) {
- s16 input = ((s16*)spibigrx[1])[i+2];
- input>>=3;
- dst[i]=STEREOADDSAT(STEREOPACK(input,input), dst[i]);
- }
- if (spistate==0)
- spi_read256_dma(0,0);
- */
- ///////////////// ok lets do hpf earlier!
- static float peak = 0.f;
- peak *= 0.99f;
- static float power = 0.f;
- // at sample rate, lpf k 0.002 takes 10ms to go to half; .0006 takes 40ms; k=.0002 takes 100ms;
-// at buffer rate, k=0.13 goes to half in 10ms; 0.013 goes to half in 100ms; 0.005 is 280ms
-
-float g = param_eval_float(P_MIXHPF, any_rnd, env16, pressure16); // tanf(3.141592f * 8000.f / 32000.f); // highpass constant // TODO PARAM 0 -1
-g *= g;
-g *= g;
-g += (10.f / 32000.f);
-//const float k = 2.f -2.f * param_eval_float(P_HPF_RESO, any_rnd, env16, pressure16); // 2.f - 2.f * res;
-const static float k = 2.f;
-float a1 = 1.f / (1.f + g * (g + k));
-float a2 = g * a1;
-
-
-#ifdef DEBUG
-#define ENABLE_HPF 0
-#else
-#define ENABLE_HPF 1
-#endif
-
-if (ENABLE_HPF) for (int i = 0; i < BLOCK_SAMPLES; ++i) {
- u32 input = STEREOSIGMOID(dst[i]);
- STEREOUNPACK(input);
- static float ic1l, ic2l, ic1r, ic2r;
- float l = inputl, r = inputr;
- float v1l = a1 * ic1l + a2 * (l - ic2l);
- float v2l = ic2l + g * v1l;
- ic1l = v1l + v1l - ic1l;
- ic2l = v2l + v2l - ic2l;
- l -= k * v1l + v2l;
-
- float v1r = a1 * ic1r + a2 * (r - ic2r);
- float v2r = ic2r + g * v1r;
- ic1r = v1r + v1r - ic1r;
- ic2r = v2r + v2r - ic2r;
- r -= k * v1r + v2r;
-
- power *= 0.999f;
- power += l * l + r * r;
- peak = maxf(peak, l + r);
-
- s16 li = (s16)SATURATE16(l);
- s16 ri = (s16)SATURATE16(r);
- dst[i] = STEREOPACK(li, ri);
- }
-
- u32 *src = (u32*) dst;
-
- float f=1.f-clampf(param_eval_float(P_RVTIME, any_rnd, env16, pressure16),0.f,1.f);
- f*=f; f*=f;
- k_reverb_fade=(int)(250*(1.f-f));
- k_reverb_shim=(param_eval_int(P_RVSHIM, any_rnd, env16, pressure16)>>9);
- k_reverb_wob=(param_eval_float(P_RVWOB, any_rnd, env16, pressure16));
- //k_reverb_color=(param_eval_float(P_RVCOLOR, any_rnd, env16, pressure16));
- k_reverbsend=(param_eval_int(P_RVSEND, any_rnd, env16, pressure16));
-
- int synthlvl_ = param_eval_int(P_MIXSYNTH, any_rnd, env16, pressure16);
- int synthwidth = param_eval_int(P_MIX_WIDTH, any_rnd, env16, pressure16);
- int asynthwidth = abs(synthwidth);
- int synthlvl_mid;
- int synthlvl_side;
- if (asynthwidth <= 32768) { // make more narrow
- synthlvl_mid = synthlvl_;
- synthlvl_side = (synthwidth * synthlvl_) >> 15;
- }
- else {
- synthlvl_side = (synthwidth < 0) ? -synthlvl_ : synthlvl_;
- asynthwidth = 65536 - asynthwidth;
- synthlvl_mid = (asynthwidth * synthlvl_) >> 15;
- }
-
- int ainwetdry= param_eval_int(P_MIXINWETDRY, any_rnd, env16, pressure16);
- int wetdry = param_eval_int(P_MIXWETDRY, any_rnd, env16, pressure16);
- int wetlvl = 65536 - maxi(-wetdry, 0);
- int drylvl = 65536 - maxi(wetdry, 0);
-
- int ainwetlvl = 65536 - maxi(-ainwetdry, 0);
- int aindrylvl = 65536 - maxi(ainwetdry, 0);
-
-
- //int ainlvl = param_eval_int(P_MIXINPUT, any_rnd, env16, pressure16);
- ainwetlvl = ((ainwetlvl >>4) * (ainlvl>>4)) >> 8;
-
- ainlvl = ((ainlvl >> 4) * (drylvl >> 4)) >> 8; // prescale by dry level
- ainlvl = ((ainlvl >> 4) * (aindrylvl >>4)) >> 8; // prescale by fx dry level
-
- int delayratio=param_eval_int(P_DLRATIO,any_rnd, env16, pressure16)>>8;
- static int delaytime = BLOCK_SAMPLES<<12;
- int scopescale = (65536 * 24) / maxi(16384, (int)peak);
- //int scopetrig = (65536 / 2) / (1 + scopex);
-
-#ifdef DEBUG
-#define ENABLE_FX 0
-#else
-#define ENABLE_FX 1
-#endif
- if (ENABLE_FX) for (int i = 0; i < BLOCK_SAMPLES / 2; ++i) {
- //float lfopos1 = lfo_next(&delaylfo1) * wob;
- //int wobpos1 = FLOAT2FIXED(lfopos1, 12 + 6);
- int targetdt=k_target_delaytime+2048-(int)wobpos;
- wobpos+=dwobpos;
- delaytime+=(targetdt-delaytime)>>10;
- s16 delayreturnl = LINEARINTERPDL(delaybuf, delaypos, delaytime);
- s16 delayreturnr = LINEARINTERPDL(delaybuf, delaypos, ((delaytime>>4)*delayratio)>>4);
- // soft clipper due to drive; reduces range to half also giving headroom on tape & output
- u32 drylr0 = STEREOSIGMOID(src[0]);
- u32 drylr1 = STEREOSIGMOID(src[1]);
-
- /////////////////////////////////// COMPRESSOR
- u32 drylr01 = STEREOADDAVERAGE(drylr0, drylr1); // this is gonna have absolute max +-32768
- STEREOUNPACK(drylr01);
- static float peaktrack=1.f;
- float peaky = (float)((1.f/4096.f/65536.f)*(maxi(maxi(drylr01l, -drylr01l), maxi(drylr01r, -drylr01r)) * synthlvl_));
- if (peaky > peaktrack) peaktrack += (peaky - peaktrack) *0.01f;
- else {
- peaktrack += (peaky - peaktrack) *0.0002f;
- peaktrack = maxf(peaktrack, 1.f);
- }
- float recip = (2.5f / peaktrack);
- int lvl_mid = synthlvl_mid * recip;
- int lvl_side = synthlvl_side * recip;
-#ifdef EMU
- m_compressor = synthlvl_ * recip /65536.f;
-#endif
- ///////////////////////////////////////////////////////////////////////////////////////////
- drylr0 = MIDSIDESCALE(drylr0, lvl_mid, lvl_side);
- drylr1 = MIDSIDESCALE(drylr1, lvl_mid, lvl_side);
-
- MONITORPEAK(&m_dry, drylr0);
- MONITORPEAK(&m_dry, drylr1);
-
- u32 ain0 = audioin[i * 2 + 0];
- u32 ain1 = audioin[i * 2 + 1];
-
- u32 audioinwet = STEREOSCALE(STEREOADDAVERAGE(ain0, ain1), ainwetlvl);
- u32 dry2wetlr = STEREOADDAVERAGE(drylr0, drylr1);
- dry2wetlr = STEREOADDSAT(dry2wetlr,audioinwet);
-
- MONITORPEAK(&m_dry2wet, dry2wetlr);
-
-
- int delaysend = (int)((delayreturnl+(delayreturnr>>1)) * k_fb);
- delaysend += (((s16) (dry2wetlr) + (s16) (dry2wetlr >> 16)) * k_delaysend ) >> 8;
- static float lpf=0.f,dc=0.f;
- lpf+=(delaysend-lpf)*0.75f;
- dc+=(lpf-dc)*0.05f;
- delaysend=(int)(lpf-dc);
- //- compressor in feedback of delay
- delaysend=MONOSIGMOID(delaysend);
-
- MONITORPEAK(&m_delaysend, delaysend);
-
-
- /*int peak=abs(delaysend);
- if (unlikely(peak>32000)) {
- // adjust feedback down as by 32000/peak
- k_fb*=16000.f/peak;
- } else */
- {
- // adjust feedback up again
- k_fb+=(k_target_fb-k_fb)*0.001f;
- }
- delaypos &= DLMASK;
- delaybuf[delaypos] = delaysend;
- delaypos++;
- //s16 l=(delayreturnl*k_delay2out)>>9;
- //s16 r=(delayreturnr*k_delay2out)>>9;
- //if (i & 1)
- {
- s16 li = dry2wetlr;
- s16 ri = dry2wetlr>>16;
- static s16 prevli = 0;
- static s16 prevprevli = 0;
- static u16 bestedge = 0;
- static s16 antiturningpointli = 0;
- bool turningpoint = (prevli>prevprevli && prevli>li);
- bool antiturningpoint = (prevli < prevprevli && prevli < li);
- if (antiturningpoint)
- antiturningpointli = prevli; // remember the last turning point at the bottom
- if (turningpoint) { // we are at a peak!
- int edgesize = prevli- antiturningpointli;
- if (scopex >= 256 || (scopex<0 && edgesize>bestedge)) {
- scopex = -256;
- bestedge = edgesize;
- }
- }
- prevprevli = prevli;
- prevli = li;
- if (scopex < 256 && scopex>=0) {
- int x = scopex / 2;
- if (!(scopex&1))
- scope[x]= 0;
- putscopepixel(x, (li * scopescale >> 16) + 16);
- putscopepixel(x, (ri * scopescale >> 16) + 16);
- }
- scopex++;
- if (scopex > 1024)
- scopex = -256;
- //scopex &= 4095;
- }
-
- u32 newwetlr = STEREOPACK(delayreturnl, delayreturnr);
- MONITORPEAK(&m_delayreturn, newwetlr);
-
- // reverb
-#ifndef DEBUG
- if (1)
- {
- u32 reverbin=STEREOADDAVERAGE(newwetlr,dry2wetlr);
- MONITORPEAK(&m_reverbin, reverbin);
- u32 reverbout=Reverb2(reverbin, reverbbuf);
- MONITORPEAK(&m_reverbout, reverbout);
- newwetlr=STEREOADDSAT(newwetlr, reverbout);
- MONITORPEAK(&m_fxout, newwetlr);
-
- }
-#endif
- // output upsample
- newwetlr = STEREOSCALE(newwetlr, wetlvl);
- u32 midwetlr = STEREOADDAVERAGE(newwetlr, wetlr);
- wetlr = newwetlr;
-
- u32 audioin0 = STEREOSIGMOID(STEREOSCALE(ain0, ainlvl)); // ainlvl already scaled by drylvl
- u32 audioin1 = STEREOSIGMOID(STEREOSCALE(ain1, ainlvl));
- MONITORPEAK(&m_audioin, audioin0);
- MONITORPEAK(&m_audioin, audioin1);
-
- src[0] = STEREOADDSAT(STEREOADDSAT(STEREOSCALE(drylr0,drylvl), audioin0), midwetlr);
- src[1] = STEREOADDSAT(STEREOADDSAT(STEREOSCALE(drylr1,drylvl), audioin1), newwetlr);
-
- MONITORPEAK(&m_output, src[0]);
- MONITORPEAK(&m_output, src[1]);
-
-
- src += 2;
- }
-
-#ifdef EMU
- powerout = power / (BLOCK_SAMPLES * 2.f * 32768.f * 32768.f);
- gainhistoryrms[ghi] = lin2db(powerout+1.f/65536.f)*0.5f;
- ghi = (ghi + 1) & 511;
-#endif
- tc_stop(&_tc_fx);
-}
-
-/////////////////////////////////////////////////////////
-
-
-#ifdef EMU
-uint32_t emupixels[128*32];
-void OledFlipEmu(const u8 * vram) {
- if (!vram)
- return;
- const u8* src = vram + 1;
- for (int y = 0; y < 32; y += 8) {
- for (int x = 0; x < 128; x++) {
- u8 b = *src++;
- for (int yy = 0; yy < 8; ++yy) {
- u32 c = (b & 1) ? 0xffffffff : 0xff000000;
- int y2 = y + yy;
-#ifdef ROTATE_OLED
- //pixels[(y2 + (127-x) * 32)] = c; // rotated, pins at bottom
- emupixels[((31 - y2) + x * 32)] = c; // rotated, pins at top
-#else
- emupixels[(y2 * 128 + x)] = c;
-#endif
- b >>= 1;
- }
- }
- }
-}
-
- int * EMSCRIPTEN_KEEPALIVE getemubitmap(void) {
- return (int*)emupixels;
-}
- uint8_t *EMSCRIPTEN_KEEPALIVE getemuleds() {
- return (uint8_t*)led_ram;
-}
-
-#endif
-
-void EMSCRIPTEN_KEEPALIVE uitick(u32 *dst, const u32 *src, int half) {
- tc_stop(&_tc_budget);
- tc_start(&_tc_budget);
-
- tc_start(&_tc_all);
-// if (half)
- {
- tc_start(&_tc_touch);
- touch_update();
- tc_stop(&_tc_touch);
- }
-// else
- {
- tc_start(&_tc_led);
- led_update();
- tc_stop(&_tc_led);
- }
-
- // clear some scope pixels
-
-
- // pass thru: memcpy(dst,src,64*4);
-
- DoAudio((u32*)dst, (u32*)src);
- /* triangle wave test
- static u16 foo;
- for (int i=0;i<BLOCK_SAMPLES;++i) {
- s16 s=(foo<32768) ? foo*2-32768 : 65536+32767-foo*2;
- foo+=256;
-
- dst[i] = STEREOPACK(s,s);
- }
- */
- /*
- static int th=0;
- th+=16;
-
- HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, adcbuf[ADC_IN5]>>4);
- HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2, DAC_ALIGN_12B_R, adcbuf[ADC_IN6]>>4);
-
- __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (th>>0)&255);
- __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, (th >> 1) & 255);
-*/
-
-
- tc_stop(&_tc_all);
-
-
-
-}
-
-void bootswish(void);
-void cv_calib(void);
-
-
-void reflash(void) {
- clear();
- drawstr(0, 0, F_16_BOLD, "Re-flash");
- drawstr(0, 16, F_16, "over USB DFU");
- oled_flip(vrambuf);
- HAL_Delay(100);
- jumptobootloader();
-}
-
-void set_test_mux(int c) {
-#ifndef EMU
- GPIOD->ODR &= ~(GPIO_PIN_1 | GPIO_PIN_3 | GPIO_PIN_4); // rgb led off
- if (c & 1)
- GPIOD->ODR |= GPIO_PIN_3;
- if (c & 2)
- GPIOD->ODR |= GPIO_PIN_1;
- if (c & 4)
- GPIOD->ODR |= GPIO_PIN_4;
- if (c & 8)
- GPIOA->ODR |= GPIO_PIN_8;
- else
- GPIOA->ODR &= ~GPIO_PIN_8;
-#endif
-}
-void set_test_rgb(int c) {
- set_test_mux(c^7);
-}
-
-short *getrxbuf(void);
-
-#undef ERROR
-#define ERROR(msg, ...) do { errorcount++; DebugLog("\r\n" msg "\r\n", __VA_ARGS__); } while (0)
-
-void test_jig(void) {
- // pogo pin layout:
- //GND DEBUG = GND / PA8 - 67
- //GND GND
- //MISO SPICLK = PD3 - 84 / PD1 - 82
- //MOSI GND = PD4 - 85 / GND
- // rgb led is hooked to PD1,PD3,PD4. configure it for output
- // mux address hooked to LSB=PD3, PD1, PD4, PA8=MSB
-#ifndef EMU
- GPIO_InitTypeDef GPIO_InitStruct = { 0 };
- GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_3 | GPIO_PIN_4;
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
- HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
-
- // we also use debug as an output now!
- GPIO_InitStruct.Pin = GPIO_PIN_8;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- GPIOA->ODR &= ~GPIO_PIN_8;
-#endif
- clear();
- drawstr(0, 0, F_32_BOLD, "TEST JIG");
- oled_flip(vrambuf);
- HAL_Delay(100);
- enable_audio=EA_PASSTHRU;
- SetOutputCVTrigger(0);
- SetOutputCVClk(0);
- SetOutputCVGate(0);
- SetOutputCVPressure(0);
- int gndcalib[ADC_CHANS]={0};
- int refcalib[ADC_CHANS]={0};
- float pdac[2][2]={0};
-#define PITCH_1V_OUT (43000 - 8500)// about 8500 per volt; 43000 is zero ish.
-#define PITCH_4V_OUT (43000 - 8500 * 4)
- static int const expected_mvolts[11][2]={
- {0,0},// gnd
- {2500,2500}, // 2.5 ref
- {3274,3274}, // 3.3 supply
- {4779,4779}, // 5v supply
- {950,950}, // 1v from 12v supply
- {1039,4230}, // pitch lo 1v/4v
- {1039,4230}, // pitch hi 1v/4v
- {0,4700}, // clock
- {0,4700}, // trig,
- {0,4700}, // gate,
- {0,4700}, // pressure
- };
- static int const tol_mvolts[11]={
- 100, // gnd
- 10, // ref
- 300, // 3.3
- 500, // 5
- 100, // 1v
- 100,100,// pitch
- 150,150,150,150, // outputs
- };
- const char * const names[11][2]={
- {"gnd",0},
- {"2.5v",0},
- {"3.3v",0},
- {"5v",0},
- {"1v from 12v",0},
- {"plo (1v)","plo (4v)"},
- {"phi (1v)","phi (4v)"},
- {"clk (0v)","clk (4.6v)"},
- {"trig (0v)","trig (4.6v)"},
- {"gate (0v)","gate (4.6v)"},
- {"pressure (0v)","pressure (4.6v)"}
- };
- while (1) {
- DebugLog("mux in: pitch gate x y a b | mux:\r\n");
- int errorcount=0;
- int rangeok=0,zerook=0;
- gotclkin=0;
-#ifndef EMU
- if (!update_accelerometer_raw()) {
- drawstr(0, 0, F_32_BOLD, "BAD ACCEL");
- oled_flip(vrambuf);
- HAL_Delay(1000);
- errorcount++;
- }
-#endif
- for (int iter=0;iter<4;++iter) {
- SetOutputCVClk(0);
- HAL_Delay(3);
- SetOutputCVClk(65535);
- HAL_Delay(3);
- }
- if (gotclkin!=4)
- ERROR("expected clkin of 4, got %d", gotclkin);
- for (int mux=0;mux<11;++mux ){
- set_test_mux(mux);
- int numlohi = (mux<5) ? 1 : 2;
- for (int lohi=0;lohi<numlohi;++lohi) {
- int data=lohi?49152:0;
- int pitch=lohi? PITCH_4V_OUT : PITCH_1V_OUT;
- SetOutputCVTrigger(data);
- SetOutputCVClk(data);
- SetOutputCVGate(data);
- SetOutputCVPressure(data);
- SetOutputCVPitchLo(pitch,0);
- SetOutputCVPitchHi(pitch,0);
-
- HAL_Delay(3);
- int tot[ADC_CHANS] = {0};
- clear();
-
-#define NUMITER 32
- for (int iter = 0; iter < NUMITER; ++iter) {
- HAL_Delay(2);
- short *rx=getrxbuf();
- for (int x=0;x<128;++x) {
- putpixel(x,16+rx[x*2]/1024,1);
- putpixel(x,16+rx[x*2+1]/1024,1);
- }
- for (int j = 0; j < ADC_SAMPLES; ++j)
- for (int ch=0;ch<ADC_CHANS;++ch)
- tot[ch] += adcbuf[j * ADC_CHANS + ch];
- }
- if (lohi)
- invertrectangle(0,0,128,32);
- oled_flip(vrambuf);
- for (int ch=0;ch<ADC_CHANS;++ch)
- tot[ch] /= ADC_SAMPLES * NUMITER;
-
- DebugLog("-----\nmux = %d lohi = %d\n", mux, lohi);
- for (int ch=0;ch<ADC_CHANS;++ch) {
- DebugLog("adc ch reads %d\n",tot[ch]);
- }
- DebugLog("-----\n");
- switch(mux) {
- case 0:
- for (int ch=0;ch<ADC_CHANS;++ch) {
- gndcalib[ch]=tot[ch];
- int expected = (ch==0) ? 43262 : (ch>=6) ? 31500 : 31035 ;
- int error=abs(expected-tot[ch]);
- if (error>2000)
- ERROR("ADC Channel %d zero point is %d, expected %d", ch, tot[ch], expected);
- else
- zerook|=(1<<ch);
- }
- break;
- case 1:
- for (int ch=0;ch<ADC_CHANS;++ch) {
- refcalib[ch]=tot[ch];
- int range=gndcalib[ch]-refcalib[ch];
- int expected=(ch==0) ? 21600 : (ch>=6) ? 0 : 14386;
- int error=abs(expected-range);
- if (error>2000)
- ERROR("ADC Channel %d range is %d, expected %d", ch, range, expected);
- else
- rangeok|=(1<<ch);
- }
- break;
- case 5:
- case 6: {
- int range0 = (refcalib[0] - gndcalib[0]);
- if (range0 == 0) range0 = 1;
- pdac[mux - 5][lohi] = (tot[0] - gndcalib[0]) * (2.5f / range0);
- break;
- }
- }
- DebugLog("%4d: ",mux);
- for (int ch=0;ch<6;++ch) {
- int range=(refcalib[ch]-gndcalib[ch]);
- if (range==0) range=1;
- float gain = 2500.f / range;
- int mvolts=(tot[ch]-gndcalib[ch])*gain;
- int exp_mvolts=expected_mvolts[mux][lohi];
- int error=abs(exp_mvolts-mvolts);
- int tol=tol_mvolts[mux];
- bool ok=true;
- if (error>tol) {
- ok=false;
- ERROR("ADC channel %d was %dmv, expected %dmv, outside tolerence of %dmv", ch, mvolts, exp_mvolts, tol);
- }
- DebugLog("%6dmv%c ", mvolts, ok ? ' ' : '*');
- }
- DebugLog("| %s. clocks=%d\r\n",names[mux][lohi],gotclkin);
- }
- }
- DebugLog("zero: ");
- for (int ch=0;ch<8;++ch)
- DebugLog("%6d%c ",gndcalib[ch], (zerook&(1<<ch)) ? ' ':'*');
- DebugLog("\r\nrange ");
- for (int ch=0;ch<6;++ch)
- DebugLog("%6d%c ",gndcalib[ch]-refcalib[ch], (rangeok&(1<<ch)) ? ' ':'*');
- DebugLog("\r\n%d errors\r\n\r\n", errorcount);
- set_test_rgb(errorcount ? 4 : 2);
- clear();
- if (errorcount==0)
- drawstr(0,0,F_32_BOLD,"GOOD!");
- else
- fdrawstr(0,0,F_32_BOLD,"%d ERRORS", errorcount);
- oled_flip(vrambuf);
- /* fill in cv calib - bias and scale x 10
- {52100.f, 1.f / -9334.833333f},
- {31716.f, 0.2f / -6548.1f},
- {31665.f, 0.2f / -6548.1f},
- {31666.f, 0.2f / -6548.1f},
- {31041.f, 0.2f / -6548.1f},
- {31712.f, 0.2f / -6548.1f},
- // 2 pots
- {32768.f, 1.05f / -32768.f},
- {32768.f, 1.05f / -32768.f},
- // 2 output
- // 2048 per semitone, so...
- {42490.f, (26620-42490) * (1.f / (2048.f * 12.f * 2.f))},
- {42511.f, (26634-42511) * (1.f / (2048.f * 12.f * 2.f))},
- */
- for (int ch = 0; ch < 8; ++ch) {
- int zero = gndcalib[ch];
- int range = gndcalib[ch] - refcalib[ch];
- if (range == 0) range = 1;
- cvcalib[ch].bias = zero;
- if (ch >= 6)
- cvcalib[ch].scale = -1.01f / (zero+1);
- else if (ch==0)
- cvcalib[ch].scale = -2.5f / range; // range is measured off 2.5; so this scales it so that we get true volts out
- else
- cvcalib[ch].scale = (-2.5f/5.f) / range; // range is measured off 2.5; so this scales it so that we get 1 out for 5v in
- }
- // ok pdac[k][0] tells us what we got from the ADC when we set the DAC to PITCH_1V_OUT, and pdac[k][1] tells us what we got when we output PITCH_4V_OUT
- //so we have dacb + dacs * plo0 = PITCH_1V_OUT
- // and dacb + dacs * plo1 = PITCH_4V_OUT
- // dacs = (PITCH_1V_OUT-PITCH_4V_OUT) / (plo0-plo1)
- // dacb = PITCH_1V_OUT - dacs*plo0
- for (int dacch = 0; dacch < 2; ++dacch) {
- float range = (pdac[dacch][0] - pdac[dacch][1]);
- if (range == 0) range = 1.f;
- float scale_per_volt = (PITCH_1V_OUT - PITCH_4V_OUT) / range;
- float zero = PITCH_1V_OUT - scale_per_volt * pdac[dacch][0];
- DebugLog("dac channel %d has zero at %d and %d steps per volt, should be around 42500 and -8000 ish\r\n", dacch, (int)zero, (int)scale_per_volt);
- cvcalib[dacch + 8].bias = zero;
- cvcalib[dacch + 8].scale = scale_per_volt * (1.f / (2048.f * 12.f)); // 2048 per semitone
- }
-
- flash_writecalib(2);
-
-
- HAL_Delay(errorcount ? 2000 : 4000);
- }
-}
-
-void plinky_frame(void);
-
-
-
-
-void EMSCRIPTEN_KEEPALIVE emu_setadc(float araw, float braw, float pitchcv, float gatecv, float xcv, float ycv, float acv, float bcv, int gateforce, int pitchsense, int gatesense) {
-#ifdef EMU
- emupitchsense = pitchsense;
- emugatesense = gatesense;
-#endif
- u16* a = adcbuf;
- for (int i = 0; i < ADC_SAMPLES; ++i) {
- a[0] = clampi((int)(52100 - 9334.83f * pitchcv * 1.f / 12.f), 0, 65535);
- a[1] = gateforce ? 0 : clampi((int)(31716 - 6548.11f * gatecv), 0, 65535);
- a[2] = clampi((int)(31665 - 6548.11f * xcv), 0, 65535);
- a[3] = clampi((int)(31666 - 6548.11f * ycv), 0, 65535);
- a[4] = clampi((int)(31041 - 6548.11f * acv), 0, 65535);
- a[5] = clampi((int)(31712 - 6548.11f * bcv), 0, 65535);
- a[ADC_AKNOB] = (u16)((1.f - araw) * 65535);
- a[ADC_BKNOB] = (u16)((1.f - braw) * 65535);
- a += ADC_CHANS;
- }
-}
-
-//#define BITBANG
-void SetExpanderDAC(int chan, int data) {
-#ifndef EMU
- GPIOA->BRR = 1<<8; // cs low
- u16 daccmd = (2<<14) + ((chan&3)<<12) + (data & 0xfff);
-#ifdef BITBANG
- for (int i=0;i<16;++i) {
- if (daccmd&0x8000) GPIOD->BSRR=1<<4; else GPIOD->BRR=1<<4;
- daccmd<<=1;
- HAL_Delay(1);
- GPIOD->BRR = 1<<1; // clock low
- HAL_Delay(1);
- GPIOD->BSRR = 1<<1; // clock high
- }
- HAL_Delay(1);
-#else
- spidelay();
- daccmd=(daccmd>>8) | (daccmd<<8);
- HAL_SPI_Transmit(&hspi2, (uint8_t*) &daccmd, 2, -1);
- spidelay();
-#endif
- GPIOA->BSRR = 1<<8; // cs high
-#endif
-}
-
-
-#ifdef WASM
-
-bool send_midimsg(u8 status, u8 data1, u8 data2) {
- return true;
-}
-void spi_update_dac(int chan) {
- resetspistate();
-}
-
-void EmuStartSound(void) {
-}
-
-bool midi_receive(unsigned char packet[4]) {
- return false;// fill in packet and return true if midi found
-}
-int emutouch[9][2];
-void EMSCRIPTEN_KEEPALIVE wasm_settouch(int idx, int pos, int pressure) {
- if (idx >= 0 && idx < 9)
- emutouch[idx][1] = pos, emutouch[idx][0] = pressure;
-}
-
-void EMSCRIPTEN_KEEPALIVE plinky_frame_wasm(void) {
- plinky_frame();
-}
-u32 wasmbuf[BLOCK_SAMPLES];
-uint8_t* EMSCRIPTEN_KEEPALIVE get_wasm_audio_buf(void) {
- return (uint8_t*)wasmbuf;
-}
-uint8_t* EMSCRIPTEN_KEEPALIVE get_wasm_preset_buf(void) {
- return (uint8_t*)&rampreset;
-}
-void EMSCRIPTEN_KEEPALIVE wasm_audio(void) {
- static u8 half=0;
- u32 audioin[BLOCK_SAMPLES] = {0};
- uitick(wasmbuf, audioin, half);
- half = 1 - half;
-}
-void EMSCRIPTEN_KEEPALIVE wasm_pokepreset(int offset, int byte) {
- if (offset >= 0 && offset < sizeof(rampreset)) ((u8*)&rampreset)[offset] = byte;
-}
-int EMSCRIPTEN_KEEPALIVE wasm_peekpreset(int offset) {
- if (offset >= 0 && offset < sizeof(rampreset)) return ((u8*)&rampreset)[offset];
- return 0;
-}
-int EMSCRIPTEN_KEEPALIVE wasm_getcurpreset(void) {
- return sysparams.curpreset;
-}
-void EMSCRIPTEN_KEEPALIVE wasm_setcurpreset(int i) {
- SetPreset(i, false);
-}
-#endif
-
-static u8 uartbuf[16];
-void serial_midi_init(void) {
-#ifndef EMU
- HAL_UART_Receive_DMA(&huart3, uartbuf, sizeof(uartbuf));
-#endif
-}
-void serial_midi(const u8*buf, u8 len) {
- static u8 state=0;
- static u8 msg[3];
- for (;len--;){
- u8 data=*buf++;
- if (data & 0x80) state = 0;
- else if (state == 3) state = 1; // running status
- if (state < 3) {
- msg[state++] = data;
- if (state == 1 && (msg[0] >= 0xF8 && msg[0] <= 0xFC)) { //BUG FIX NO MIDI CLOCK FROM HW MIDI KAY LPZW //real time start stop clock msg[1]=0;
- msg[2]=0;
- state = 3;
- }
- if (state == 2 && (msg[0] >= 0xc0 && msg[0] <= 0xdf)) {
- msg[state++] = 0; // two byte messages. wtf midi.//WE NEED TO DEBUG THIS COS IT SEEMS NOT TO WORK
- }
- if (state==3) {
- processmidimsg(msg[0], msg[1], msg[2]);
- }
- }
- }
-}
-
-#ifndef EMU
-// from https://community.st.com/s/question/0D50X00009XkflR/haluartirqhandler-bug
-// what a trash fire
-// USART Error Handler
-void HAL_UART_ErrorCallback(UART_HandleTypeDef* huart) {
- __HAL_UART_CLEAR_OREFLAG(huart);
- __HAL_UART_CLEAR_NEFLAG(huart);
- __HAL_UART_CLEAR_FEFLAG(huart);
- /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
- __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
- //The most important thing when UART framing error occur/any error is restart the RX process
- midi_panic();
- HAL_UART_Receive_DMA(&huart3, uartbuf, sizeof(uartbuf));
-}
-
-typedef unsigned int uint;
-u8 midisendbuf[16+16];
-uint midisendhead,midisendtail;
-bool usb_midi_write(const uint8_t packet[4]);
-bool serial_midi_ready(void) {
- return huart3.TxXferCount == 0;
-}
-void kick_midi_send(void) {
- if (huart3.TxXferCount==0 && midisendhead!=midisendtail) {
- uint from=midisendtail&15;
- uint to = midisendhead&15;
- if (to>from) {
- midisendtail+=(to-from);
- HAL_UART_Transmit_DMA(&huart3, midisendbuf + from, to-from );
-
- } else if (to<from) {
- // wrapped! send from->16, and 0->to
- u8 sendlen = (16-from) + to;
- memcpy(midisendbuf+16,midisendbuf,to); // copy looped part to end so we can send it all in one go! good on us.
- midisendtail+=sendlen;
- HAL_UART_Transmit_DMA(&huart3, midisendbuf + from, sendlen);
- }
- }
-}
-
-
-bool send_midi_serial(const u8 *data, int len) {
- if (len<=0) return true;
- if (midisendhead-midisendtail+len > 16)
- return false; // full
- while (len--)
- midisendbuf[(midisendhead++)&15]=*data++;
- return true;
-}
-
-bool send_midimsg(u8 status, u8 data1, u8 data2) { // returns false if too full
-
- u8 len=3;
-
- if (status>=0xc0 && status<0xe0) // Cn Program Change, Dn Mono / Channel Aftertouch
- len=2;
- if (!(status&0x80))
- return true;
- if (status == 0x80 && data1 == 0)
- return true; // er, no
-
- //int midi_ch_out = ((GetParam(P_MIDI_CH_OUT, 0) * 16)/FULL) & 15;
- //int midi_ch_out = clampi(((GetParam(P_MIDI_CH_OUT, 0) * 16)/FULL),0,15);
- int midi_ch_out = clampi((mini(GetParam(P_MIDI_CH_OUT, 0), FULL - 1) * 16) / FULL,0,15);
-
- if (status<0xf0) status += midi_ch_out; // sets output channel
-
- u8 buf[4]={status>>4, status,data1,data2};
- usb_midi_write(buf);
-#ifdef DEBUG
-// DebugLog("%02x %02x %02x\r\n", status, data1, data2);
-#endif
- send_midi_serial(buf + 1, len);
- return true;
-}
-#else
-void kick_midi_send(void) {}
-bool serial_midi_ready(void) {
- return true;
-}
-#endif
-
-void serial_midi_update(void) {
- kick_midi_send();
- static u8 old_pos=0;
-#ifdef EMU
- u8 pos = 0;
- #else
- u8 pos = 16 - __HAL_DMA_GET_COUNTER(huart3.hdmarx);
-#endif
- if (pos != old_pos) {
- if (pos > old_pos) {
- serial_midi(&uartbuf[old_pos], pos - old_pos);
- } else {
- serial_midi(&uartbuf[old_pos], 16 - old_pos);
- serial_midi(&uartbuf[0], pos);
- }
- }
- old_pos = pos;
-}
-
-
-void EMSCRIPTEN_KEEPALIVE plinky_init(void) {
- denormals_init();
- touch_reset_calib();
- tc_init();
-
- adc_init();
-#ifdef EMU
- emu_setadc(0.5f, 0.5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, false, false, false);
-#endif
- dac_init();
- reverb_clear(); // ram2 is not cleared by startup.s as written.
- delay_clear();
- HAL_Delay(100); // stablise power before bringing oled up
- oled_init();
- codec_init();
- adc_start(); // also dac_start effectively
-
-#ifdef EMU
- void EmuStartSound(void);
- EmuStartSound();
-#endif
-
- // see if were in the testjig - it pulls PA8 (pin 67) down 'DEBUG'
-#ifndef EMU
- if (!(GPIOA->IDR & (1<<8))) {
- test_jig();
- }
-
- // turn debug pin to an output
- GPIO_InitTypeDef GPIO_InitStruct = { 0 };
- GPIO_InitStruct.Pin = GPIO_PIN_8;
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- GPIOA->BSRR = 1<<8; // cs high
- HAL_Delay(1);
-
- spi_setchip(0xffffffff);
- int spiid = spi_readid();
- DebugLog("SPI flash chip 1 id %04x\r\n", spiid);
- spi_setchip(0);
- spiid = spi_readid();
- DebugLog("SPI flash chip 0 id %04x\r\n", spiid);
- // kick off serial midi in!
- serial_midi_init();
-
- /*
- // BIT BANG TEST
-#ifdef BITBANG
- // PD1 is spiclk, pd4 is mosi
- GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_4;
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
- GPIOD->BSRR = (1<<1) + (1<<4); // clock, data high
-#endif
- int count=0;
- while (1) {
- SetExpanderDAC(0,(count&1)?0xfff:0);
- SetExpanderDAC(1,(count&2)?0xfff:0);
- SetExpanderDAC(2,(count&4)?0xfff:0);
- SetExpanderDAC(3,(count&8)?0xfff:0);
- count++;
- HAL_Delay(250);
- }
-*/
-#endif
- led_init();
-
- //enable_audio=EA_PASSTHRU; // DO NOT CHECK IN
- //while(1);
-
-
- int flashvalid = flash_readcalib();
- if (!(flashvalid & 1)) { // no calib at all
- touch_reset_calib();
- calib();
- flashvalid |= 1;
- flash_writecalib(flashvalid );
- }
- if (!(flashvalid &2)) {
- //cv_reset_calib();
- cv_calib();
- flashvalid |= 2;
- flash_writecalib(3);
- }
- HAL_Delay(80);
- int knoba= GetADCSmoothedNoCalib(ADC_AKNOB);
- int knobb= GetADCSmoothedNoCalib(ADC_BKNOB);
- bootswish();
- knoba=abs(knoba-(int)GetADCSmoothedNoCalib(ADC_AKNOB));
- knobb=abs(knobb-(int)GetADCSmoothedNoCalib(ADC_BKNOB));
- DebugLog("knob turned by %d,%d during boot\r\n", knoba,knobb);
- //knoba = 10000; // DO NOT CHECK IN - FORCE CALIBRATION
- //knobb = 10000; // DO NOT CHECK IN - FORCE CV CALIB
- // turn knobs during boot to force calibration
-#ifndef WASM
- if (knoba>4096 || knobb>4096) {
- if (knoba > 4096 && knobb > 4096) {
- // both knobs twist on boot - jump to stm flash bootloader
- reflash();
- }
- if (knoba > 4096) {
- // left knob twist on boot - full calib
- touch_reset_calib();
- calib();
- }
- else {
- // right knob twist on boot - cv calib only
- //cv_reset_calib();
- cv_calib();
- }
- flash_writecalib(3);
- }
-#endif
- InitParamsOnBoot();
-
-
-
-
- /*
- DebugLog("erase test ...\r\n");
- spi_erase64k(0, 0);
- spi_read256(0);
- for (int i = 0; i < 256; ++i) if (spibigrx[i + 4] != 255) {
- DebugLog("erase fail at %d\r\n", i);
- }
- memset(spibigrx, 0, sizeof(spibigrx));
- for (int i = 0; i < 256; ++i) spibigtx[i + 4] = i * 23 + 72;
- spi_write256(0);
- memset(spibigrx, 0, sizeof(spibigrx));
- spi_read256(0);
- for (int i = 0; i < 256; ++i) if (spibigrx[i + 4] != (u8)(i*23+72)) {
- DebugLog("write fail at %d\r\n", i);
- }
- spi_erase64k(0, 0);
- spi_read256(0);
- for (int i = 0; i < 256; ++i) if (spibigrx[i + 4] != 255) {
- DebugLog("erase 2 fail at %d\r\n", i);
- }
-
-
- DebugSPIPage(65536+32768);
- */
-
- enable_audio = EA_PLAY;
-
-}
-
-
-
-#include "ui.h"
-
+#if defined(_WIN32) || defined(__APPLE__)
+#define EMU
+#pragma warning(disable:4244)
+#endif
+
+#ifdef WASM
+#include <emscripten.h>
+#else
+#define EMSCRIPTEN_KEEPALIVE
+#endif
+
+#ifndef EMU
+#include <main.h>
+
+extern ADC_HandleTypeDef hadc1;
+extern DMA_HandleTypeDef hdma_adc1;
+
+extern DAC_HandleTypeDef hdac1;
+extern DMA_HandleTypeDef hdma_dac_ch1;
+extern DMA_HandleTypeDef hdma_dac_ch2;
+
+extern I2C_HandleTypeDef hi2c2;
+
+extern SAI_HandleTypeDef hsai_BlockA1;
+extern SAI_HandleTypeDef hsai_BlockB1;
+extern DMA_HandleTypeDef hdma_sai1_a;
+extern DMA_HandleTypeDef hdma_sai1_b;
+
+extern SPI_HandleTypeDef hspi2;
+extern DMA_HandleTypeDef hdma_spi2_tx;
+extern DMA_HandleTypeDef hdma_spi2_rx;
+
+extern TIM_HandleTypeDef htim1;
+extern TIM_HandleTypeDef htim2;
+extern TIM_HandleTypeDef htim3;
+extern TIM_HandleTypeDef htim4;
+extern TIM_HandleTypeDef htim5;
+extern TIM_HandleTypeDef htim6;
+
+extern TSC_HandleTypeDef htsc;
+
+extern UART_HandleTypeDef huart3;
+
+
+#endif
+
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#define IMPL
+#define BLOCK_SAMPLES 64
+#ifdef WASM
+#define ASSERT(...)
+#else
+#define ASSERT assert
+#endif
+#include "core.h"
+#include "oled.h"
+#include "codec.h"
+#include "leds.h"
+#include "adc.h"
+#include "dac.h"
+#include "gfx.h"
+#include "spi.h"
+#include "tables.h"
+#include "audiointrin.h"
+#include "lfo.h"
+#include "enums.h"
+
+
+const static float table_interp(const float *table, int x) { // 16 bit unsigned input, looked up in a 1024 entry table and linearly interpolated
+ x=SATURATEU16(x);
+ table+=x>>6;
+ x&=63;
+ return table[0]+(table[1]-table[0])*(x*(1.f/64.f));
+}
+
+#define TWENTY_OVER_LOG2_10 6.02059991328f // (20.f/log2(10.f));
+
+static inline float lin2db(float lin) { return log2f(lin) * TWENTY_OVER_LOG2_10; }
+static inline float db2lin(float db) { return exp2f(db * (1.f / TWENTY_OVER_LOG2_10)); }
+
+typedef struct knobsmoother {
+ float y1, y2;
+} knobsmoother;
+
+
+void knobsmooth_reset(knobsmoother* s, float ival) { s->y1 = s->y2 = ival; }
+
+float knobsmooth_update_knob(knobsmoother* s, float newval, float max_scale) {
+ // inspired by https ://cytomic.com/files/dsp/DynamicSmoothing.pdf
+ float band = fabsf(s->y2 - s->y1);
+ float sens = 8.f / max_scale;
+ float g = minf(1.f, 0.05f + band * sens);
+ s->y1 += (newval - s->y1) * g;
+ s->y2 += (s->y1 - s->y2) * g;
+ return s->y2;
+}
+float knobsmooth_update_cv(knobsmoother* s, float newval) { // same as update but with faster constants
+ // inspired by https ://cytomic.com/files/dsp/DynamicSmoothing.pdf
+ float band = fabsf(s->y2 - s->y1);
+ const static float sens = 10.f;
+ float g = minf(1.f, 0.1f + band * sens);
+ s->y1 += (newval - s->y1) * g;
+ s->y2 += (s->y1 - s->y2) * g;
+ return s->y2;
+}
+
+typedef struct Osc {
+ u32 phase, prevsample;
+ s32 dphase;
+ s32 targetdphase;
+ int pitch;
+} Osc;
+
+typedef struct GrainPair {
+ int fpos24;
+ int pos[2];
+ int vol24;
+ int dvol24;
+ int dpos24;
+ float grate_ratio;
+ float multisample_grate;
+ int bufadjust; // for reverse grains, we adjust the dma buffer address by this many samples
+ int outflags;
+} GrainPair;
+
+typedef struct Voice {
+ float vol;
+ float y[4];
+ Osc theosc[4];
+ GrainPair thegrains[2];
+ // grain synth state
+ int playhead8;
+ u8 sliceidx;
+ int initialfingerpos;
+ knobsmoother fingerpos;
+
+ u8 decaying;
+ int env_cur16;
+ float noise;
+ float env_level;
+#ifdef NEW_LAYOUT
+ int env_decaying;
+#else
+ uint64_t env_phase;
+#endif
+} Voice;
+
+TickCounter _tc_budget;
+TickCounter _tc_all;
+TickCounter _tc_fx;
+TickCounter _tc_audio;
+TickCounter _tc_touch;
+TickCounter _tc_led;
+TickCounter _tc_osc;
+TickCounter _tc_filter;
+
+knobsmoother adc_smooth[8];
+volatile int encval = 0;
+volatile u8 encbtn = 0;
+float encaccel;
+u8 prevsynthfingerdown = 0;
+u8 prevsynthfingerdown_nogatelen = 0; // same as above, but without gatelen applied
+u8 prevprevsynthfingerdown_nogatelen = 0; // same as above, but without gatelen applied
+u8 synthfingerdown = 0; // bit set when finger is down
+u8 synthfingerdown_nogatelen = 0;
+u8 synthfingertrigger = 0; // bit set on frame finger goes down
+s8 shift_down = -1;//-1 means up; -2 means ghosted (supressed) touch; 0-7 means down
+int shift_down_time = 0;
+s8 editmode = EM_PLAY;
+int last_time_shift_was_untouched = 0;
+u32 tick = 0; // increments every 64 samples
+
+s32 bpm10x = 120 * 10;
+
+static inline bool isgrainpreview(void) {
+ return editmode == EM_SAMPLE;
+}
+
+
+enum {
+ PLAY_STOPPED,
+ PLAY_PREVIEW,
+ PLAY_WAITING_FOR_CLOCK_START,
+ PLAY_WAITING_FOR_CLOCK_STOP,
+ PLAYING,
+};
+
+u8 playmode = PLAY_STOPPED;
+bool recording = false;
+u8 pending_loopstart_step = 255; // set when we want to jump on next loop
+
+static inline bool isplaying(void) {
+ return playmode == PLAYING || playmode == PLAY_WAITING_FOR_CLOCK_STOP;
+}
+
+
+u32 bpm_clock_phase = 0;
+int ticks_since_clock = 0;
+int ticks_since_arp = 0;
+int last_clock_period = 0;
+int last_step_period = 0;
+int last_arp_period = 0;
+int ticks_since_step = 0; // this counts up, along with the seq_divide_counter, even when not playing, in order to give a sense of time eg for recording
+bool external_clock_enable = false;
+
+int seq_divide_counter = 0;
+int arp_divide_counter = 0;
+int seqdiv = 0; // what we count up to , to get seq division
+uint64_t seq_used_bits=0;
+u8 seq_dir=0;
+
+static inline int calcseqsubstep(int tick_offset, int maxsubsteps) { // where are we within a recorded step?
+ if (last_step_period <= 0)
+ return 0;
+ if (ticks_since_step + tick_offset >= last_step_period)
+ return maxsubsteps-1;
+ if (ticks_since_step + tick_offset <= 0)
+ return 0;
+ int s = ((ticks_since_step+tick_offset) * maxsubsteps) / last_step_period;
+ if (s < 0) s = 0;
+ s=mini(maxsubsteps - 1, s);
+ return s;
+}
+static inline int calcarpsubstep(int tick_offset, int maxsubsteps) { // where are we within a recorded step?
+ if (last_arp_period <= 0)
+ return 0;
+ if (ticks_since_arp + tick_offset >= last_arp_period)
+ return maxsubsteps-1;
+ if (ticks_since_arp + tick_offset <= 0)
+ return 0;
+ return mini(maxsubsteps - 1, ((ticks_since_arp + tick_offset) * maxsubsteps) / last_arp_period);
+}
+
+u8 edit_mod=0,edit_param=0xff;
+//u8 ui_edit_param_prev[2][4] = { {P_LAST,P_LAST,P_LAST,P_LAST},{P_LAST,P_LAST,P_LAST,P_LAST} }; // push to front history
+static float surf[2][8][8];
+
+Voice voices[8];
+#ifdef EMU
+short delaybuf[DLMASK + 1];
+short reverbbuf[RVMASK + 1];
+int emupitchsense;
+int emugatesense;
+#else
+//__attribute__((section(".dlram"))) short delaybuf[DLMASK + 1];
+//__attribute__((section(".rvram"))) short reverbbuf[RVMASK + 1];
+short *reverbbuf=(short*)0x10000000; // use ram2 :)
+short *delaybuf= (short*)0x20008000; // use end of ram1 :)
+
+#endif
+static int reverbpos = 0;
+
+static int k_reverb_fade = 240;
+static int k_reverb_shim = 240;
+static float k_reverb_wob = 0.5f;
+static int k_reverbsend=0;
+static int shimmerpos1 = 2000;
+static int shimmerpos2 = 1000;
+static int shimmerfade = 0;
+static int dshimmerfade = 32768/4096;
+
+static lfo aplfo = LFOINIT(1.f / 32777.f * 9.4f);
+static lfo aplfo2= LFOINIT(1.3f / 32777.f * 3.15971f);
+
+
+/*
+
+update
+kick fetch for this pos
+*/
+
+u32 scope[128];
+
+
+static inline u32 ticks(void) { return tick; }
+
+enum {
+ EA_OFF = 0,
+ EA_PASSTHRU = -1,
+ EA_PLAY = 1,
+ EA_PREERASE = 2,
+ EA_MONITOR_LEVEL = 3,
+ EA_ARMED = 4,
+ EA_RECORDING = 5,
+ EA_STOPPING1 = 6, // we stop for 4 cycles to write 0s at the end
+ EA_STOPPING2 = 7,
+ EA_STOPPING3 = 8,
+ EA_STOPPING4 = 9,
+};
+s8 enable_audio = EA_OFF;
+
+
+#include "flash.h"
+#include "params.h"
+#include "touch.h"
+#include "calib.h"
+#include "arp.h"
+#include "edit.h"
+
+#include "webusb.h"
+
+
+bool gatecv_trig = false;
+
+// lpf_k = 1-exp(-0.707/t) where t is in samples to get to half
+static inline float lpf_k(int x) {
+ return table_interp(lpf_ks, x);
+}
+
+
+float param_eval_float(u8 paramidx, int rnd, int env16, int pressure16) {
+ return param_eval_int(paramidx, rnd, env16, pressure16) * (1.f / 65536.f);
+}
+
+int param_eval_finger(u8 paramidx, int fingeridx, Finger* f) {
+ return param_eval_int(paramidx, finger_rnd[fingeridx], voices[fingeridx].env_cur16, f->pressure * 32);
+}
+
+extern int16_t accel_raw[3];
+extern float accel_lpf[2];
+extern float accel_smooth[2];
+s16 accel_sens=0;
+
+//extern int debuga[4];
+//int debuga[4];
+
+
+void update_params(int fingertrig, int fingerdown) {
+
+ // DebugLog("%d,%d,%d,%d,%d,%d,%d,%d,%d\r\n",adcbuf[0],adcbuf[1],adcbuf[2],adcbuf[3],adcbuf[4],adcbuf[5],adcbuf[6],adcbuf[7],adcbuf[8]);
+
+ // update envelopes
+#ifdef NEW_LAYOUT
+ param_eval_premod(P_ENV_LEVEL);
+ param_eval_premod(P_A2);
+ param_eval_premod(P_D2);
+ param_eval_premod(P_S2);
+ param_eval_premod(P_R2);
+#else
+ param_eval_premod(P_ENV_RATE);
+ param_eval_premod(P_ENV_WARP);
+ param_eval_premod(P_ENV_LEVEL);
+ param_eval_premod(P_ENV_REPEAT);
+#endif
+ for (int vi = 0; vi < 8; ++vi) {
+ int bit = 1 << vi;
+ Voice* v = &voices[vi];
+ Finger* f = touch_synth_getlatest(vi);
+#ifdef NEW_LAYOUT
+ if (fingertrig & bit) {
+ v->env_level = 0.f;
+ v->env_decaying = false;
+ }
+ int down = (fingerdown & bit);
+ float target = down ? (v->env_decaying) ? 2.f*(param_eval_finger(P_S2, vi, f)*(1.f/65536.f)) : 2.2f : 0.f;
+ float dlevel = target - v->env_level;
+ float k = lpf_k(param_eval_finger((dlevel > 0.f) ? P_A2 : (v->env_decaying && down) ? P_D2 : P_R2, vi, f));
+ // update v->env_level
+ v->env_level += (target - v->env_level) * k;
+ if (v->env_level >= 2.f && down)
+ v->env_decaying = true;
+ v->env_cur16 = SATURATE17(v->env_level * param_eval_finger(P_ENV_LEVEL, vi, f));
+#else
+ if (fingertrig & bit) {
+ v->env_phase = (uint64_t)(65536.f * 65536.f * 2.f * (0.5f - 0.4999f));
+ v->env_level = 2.f; // so that it clips!
+ }
+ int lfofreq = param_eval_finger(P_ENV_RATE, vi, f);
+ u32 dlfo = (u32)(table_interp(pitches, 32768 + (lfofreq >> 1)) * (1 << 24));
+ //u32 dlfo=(u32)exp2f(24.f+lfofreq*10.f);
+ float lfowarp = param_eval_finger(P_ENV_WARP, vi, f) * (0.4999f / 65536.f) + 0.5f;
+ u32 prev_cycle = v->env_phase >> 32;
+ float lfoval = lfo_eval((u32)((v->env_phase) >> 16), lfowarp, LFO_ENV);
+ v->env_phase += dlfo;
+ u32 cur_cycle = v->env_phase >> 32;
+ if (cur_cycle != prev_cycle)
+ v->env_level *= param_eval_finger(P_ENV_REPEAT, vi, f) * (1.f / 65536.f);
+ lfoval *= v->env_level;
+ lfoval *= param_eval_finger(P_ENV_LEVEL, vi, f);
+ v->env_cur16 = SATURATE17((int)lfoval);
+#endif
+ }
+ // update average tilt + pressure
+ int totw = 256;
+ int tottilt = tilt16 * 256;
+ int maxp = 0;
+ int maxenv = 0;
+ for (int fi = 0; fi < 8; ++fi) {
+ Finger* f = touch_synth_getlatest(fi);
+ int p = f->pressure;
+ if (p < 0) p = 0;
+ totw += p;
+ maxp = maxi(maxp, p);
+ maxenv = maxf(maxenv, voices[fi].env_cur16);
+ tottilt += index_to_tilt16(fi) * p;
+ if (fingertrig & (1 << fi)) {
+ finger_rnd[fi] += 4813;
+ }
+ }
+ if (fingertrig)
+ any_rnd += 4813;
+ tilt16 = tottilt / totw;
+ env16 = maxenv;
+ pressure16 = maxp * (65536 / 2048);
+ // update lfos on modulation sources
+
+ float aknob = clampf(GetADCSmoothed(ADC_AKNOB), -1.f, 1.f);
+ float bknob = clampf(GetADCSmoothed(ADC_BKNOB), -1.f, 1.f);
+ float acv = clampf(GetADCSmoothed(ADC_ACV) * IN_CV_SCALE, -1.f, 1.f);
+ float bcv = clampf(GetADCSmoothed(ADC_BCV) * IN_CV_SCALE, -1.f, 1.f);
+ float xcv = clampf(GetADCSmoothed(ADC_XCV) * IN_CV_SCALE, -1.f, 1.f);
+ float ycv = clampf(GetADCSmoothed(ADC_YCV) * IN_CV_SCALE, -1.f, 1.f);
+
+ // accelerometer
+ static int accel_counter;
+ float accel_sens_f = (2.f/16384.f/32768.f) * abs(accel_sens);
+ accel_counter++;
+ int axisswap = accel_raw[2] > 4000; // run 2 plinkys have the accelerometer rotated 90 degrees and upside down from the addon... detect it via z direction
+ for (int j=0;j<2;++j) {
+ float f=accel_raw[j^axisswap]*accel_sens_f;
+ if (!j) {
+ if (!axisswap)
+ f = -f; // reverse x
+ } else if (accel_sens < 0)
+ f = -f; // reverse y if accel sens negative
+ accel_lpf[j]+=(f-accel_lpf[j])*0.0001f;
+ accel_smooth[j]+=(f-accel_smooth[j])*0.1f;
+ if(accel_counter<1000)
+ accel_lpf[j]=accel_smooth[j]=f;
+ }
+// int t1=1000*accel_lpf[0];
+// int t2=1000*accel_lpf[1];
+// int t3=1000*accel_smooth[0];
+// int t4=1000*accel_smooth[1];
+
+ int gatesense = getgatesense();
+ int pitchsense = getpitchsense();
+ float pitchcv = pitchsense ? GetADCSmoothed(ADC_PITCH) : 0.f;
+ float gatecv = gatesense ? clampf(GetADCSmoothed(ADC_GATE)*1.15f-0.05f, 0.f, 1.f) : 1.f;
+ knobsmooth_update_cv(adc_smooth + 0, acv);
+ knobsmooth_update_cv(adc_smooth + 1, bcv);
+ knobsmooth_update_cv(adc_smooth + 2, xcv);
+ knobsmooth_update_cv(adc_smooth + 3, ycv);
+ knobsmooth_update_knob(adc_smooth + 4, aknob, 1.f);
+ knobsmooth_update_knob(adc_smooth + 5, bknob, 1.f);
+ knobsmooth_update_cv(adc_smooth + 6, pitchcv);
+ knobsmooth_update_cv(adc_smooth + 7, gatecv);
+ u8 prevlfohp = (lfo_history_pos >> 4) & 15;
+ lfo_history_pos++;
+ u8 lfohp = (lfo_history_pos >> 4) & 15;
+ if (lfohp != prevlfohp)
+ lfo_history[lfohp][0] =
+ lfo_history[lfohp][1] =
+ lfo_history[lfohp][2] =
+ lfo_history[lfohp][3] = 0;
+ //compute new mod_cur for each mod source
+ int phase0 = calcseqsubstep(0, 65536);
+ int phase1 = phase0 + (65536 / 8);
+ int nextstep = cur_step;
+ if (phase1 >= 65536) {
+ phase1 &= 65535;
+ nextstep++;
+ u8 loopstart_step = (rampreset.loopstart_step_no_offset + step_offset) & 63;
+ if (nextstep >= loopstart_step + rampreset.looplen_step)
+ nextstep -= rampreset.looplen_step;
+ nextstep &= 63;
+ }
+ int q1 = (cur_step >> 4) & 3;
+ int q2 = (nextstep >> 4) & 3;
+ s8* autoknob1 = rampattern[q1].autoknob[(cur_step & 15) * 8 + (phase0>>13)];
+ s8* autoknob2 = rampattern[q2].autoknob[(nextstep & 15) * 8 + (phase1>>13)];
+ float autoknobinterp = (phase0 & (65536 / 8 - 1)) * (1.f/(65536/8));
+ for (int i = 0; i < 4; ++i) {
+ float adc = adc_smooth[i].y2;
+ float adcknob = 0.f;
+ if (i < 2) {
+ adcknob = adc_smooth[i + 4].y2;
+ if (!(recordingknobs&(1<<i)))
+ adcknob += (autoknob1[i]+(autoknob2[i]-autoknob1[i])* autoknobinterp )*(1.f/127.f);
+ } else
+ adcknob=accel_smooth[i-2]-accel_lpf[i-2];
+
+ int i6 = i * 6;
+ mod_cur[M_A + i] = (int)((adc) * 65536.f); // modulate yourself with the raw input!
+ param_eval_premod(P_AFREQ + i6);
+ param_eval_premod(P_AWARP + i6);
+ param_eval_premod(P_ASHAPE + i6);
+ param_eval_premod(P_ADEPTH + i6);
+ param_eval_premod(P_AOFFSET + i6);
+ param_eval_premod(P_ASCALE + i6);
+
+ int lfofreq = param_eval_int(P_AFREQ + i6, any_rnd, env16, pressure16);
+ u32 dlfo = (u32)(table_interp(pitches, 32768 + (lfofreq >> 1)) * (1 << 24));
+ //u32 dlfo=(u32)exp2f(24.f+lfofreq*10.f);
+ float lfowarp = param_eval_float(P_AWARP + i6, any_rnd, env16, pressure16) * 0.49f + 0.5f;
+ int lfoshape = param_eval_int(P_ASHAPE + i6, any_rnd, env16, pressure16);
+ float lfoval = lfo_eval((u32)((lfo_pos[i] += dlfo) >> 16), lfowarp, lfoshape);
+
+ lfoval *= param_eval_float(P_ADEPTH + i6, any_rnd, env16, pressure16);
+ //expander_out[i] = clampi(EXPANDER_ZERO - (int)(lfoval * (float)(EXPANDER_RANGE)), 0, EXPANDER_MAX);
+
+ int cvval = param_eval_int(P_AOFFSET + i6, any_rnd, env16, pressure16);
+ //if (i == 0) debuga[0] = cvval>>8;
+ cvval += (int)(adc * (param_eval_int(P_ASCALE + i6, any_rnd, env16, pressure16)<<1));
+ cvval += (int)(adcknob * 65536.f); // knob is not scaled by the cv bias/scale parameters. I think thats useful.
+ mod_cur[M_A + i] = ((int)(lfoval * 65536.f)) + cvval;
+ //if (i == 0) {
+ // debuga[1] = adc * 256.f;
+ // debuga[2] = adcknob * 256.f;
+ // debuga[3] = lfoval * 256.f;
+ //}
+
+ float expander_val = mod_cur[M_A + i] * (EXPANDER_GAIN * EXPANDER_RANGE / 65536.f);
+ expander_out[i] = clampi(EXPANDER_ZERO - (int)(expander_val), 0, EXPANDER_MAX);
+
+
+ int scopey = (-(mod_cur[M_A + i] * 7 + (1<<16)) >> 17) + 4;
+ if (scopey >= 0 && scopey < 8)
+ lfo_history[lfohp][i] |= 1 << scopey;
+
+
+ }
+
+
+
+ for (int i = 0; i < P_LAST; ++i) {
+ int pg = i / 6;
+ if (pg == PG_A || pg == PG_B || pg == PG_X || pg == PG_Y) {
+ i += 5;
+ continue;
+ }
+ param_eval_premod(i);
+ }
+
+ accel_sens = clampi(param_eval_int(P_ACCEL_SENS, any_rnd, env16, pressure16)/2, -32767, 32767);
+
+ // DebugLog("%d,%d,%d,%d\r\n",mod_cur[0]/256,mod_cur[1]/256,mod_cur[2]/256,mod_cur[3]/256);
+ // HAL_UART_Transmit(&huart3, (u8*) mod_cur, 4*4, 1000);
+
+}
+
+
+
+
+
+static inline void putscopepixel(unsigned int x, unsigned int y) {
+ if (y>=32) return;
+ scope[x]|=(1<<y);
+}
+
+
+
+bool trigout = false;
+
+
+float UpdateEnvelope(Voice *v, int fingeridx, float targetvol) {
+ Finger *f=touch_synth_getlatest(fingeridx);
+ float sens = param_eval_finger(P_SENS, fingeridx, f);
+ sens *= (2.f / 65536.f);
+ targetvol *= sens*sens;
+ bool gp = isgrainpreview();
+ const float sustain = gp ? 1.f : squaref(param_eval_finger(P_S, fingeridx, f) * (1.f/65536.f)); // lerp(0.f,1.f,pot1);
+ const float attack = gp ? 0.5f : lpf_k((param_eval_finger(P_A, fingeridx, f) ));
+ const float decay = gp ? 1.f : lpf_k((param_eval_finger(P_D, fingeridx, f) ));
+ const float release =gp ? 0.5f : lpf_k((param_eval_finger(P_R, fingeridx, f) ));
+
+ if (targetvol < 0.f)
+ targetvol = 0.f;
+ targetvol *= targetvol;
+ // pitch compensation
+ if (!ramsample.samplelen) {
+ targetvol *= 1.f + ((v->theosc[2].pitch - 43000) * (1.f / 65536.f));
+ }
+
+ int bit=1<<fingeridx;
+ if (arpmode>=0) {
+ if (!(arpbits & bit))
+ targetvol = 0.f;
+ /*else if (targetvol > 0.f) {
+ // gate len for arp here!
+ int gatelen = param_eval_finger(P_GATE_LENGTH, fingeridx, f) >> 8;
+ if (gatelen < 256) {
+ int phase = calcarpsubstep(0, 256);
+ if (phase > gatelen || !arp_rhythm.did_a_retrig)
+ targetvol = 0.f;
+ }
+ }*/
+ }
+
+
+ float vol = v->vol;
+ if (arpretrig || (synthfingertrigger & bit)) {
+ //if (vol>sustain*0.5f)
+ // vol = sustain*0.5f; // make sure you hear the retrig :) not sure... legato would be nice. maybe use sustain as a hint?
+ vol *= sustain;
+ v->decaying = false;
+ trigout = true;
+ }
+
+ float decay_or_release = decay;
+ if (targetvol <= 0.f) {
+ v->decaying = 0; // release phase
+ decay_or_release = release;
+ } else if (v->decaying) {
+ targetvol *= sustain;
+ }
+
+ float attack_threshvol = targetvol;
+ float dvol = (targetvol - vol);
+ dvol *= (dvol > 0.f) ? attack : decay_or_release; // scale delta back by release time
+ targetvol = vol + dvol; // new target
+ if (targetvol > attack_threshvol * 0.95f)
+ v->decaying = 1; // we hit the peak! time to decay.
+ if (targetvol > 1.f) {
+ targetvol=1.f;
+ v->decaying = 1;
+ }
+ return targetvol;
+}
+
+inline s32 trifold(u32 x) {
+ if (x > 0x80000000)
+ x = 0xffffffff - x;
+ return (s32)(x >> 4);
+}
+
+static inline int sample_slice_pos8(int pos16) {
+ pos16 = clampi(pos16,0,65535);
+ int i = pos16 >> 13;
+ int p0 = ramsample.splitpoints[i];
+ int p1 = ramsample.splitpoints[i+1];
+ return (p0<<8) + (((p1 - p0) * (pos16 & 0x1fff)) >> (13-8));
+}
+
+static inline int calcloopstart(u8 sliceidx) {
+ int all = ramsample.loop & 2;
+ return (all) ? 0 : ramsample.splitpoints[sliceidx];
+}
+static inline int calcloopend(u8 sliceidx) {
+ int all = ramsample.loop & 2;
+ return (all || sliceidx>=7) ? ramsample.samplelen - 192 : ramsample.splitpoints[sliceidx+1];
+}
+
+static inline int doloop(int playhead, u8 sliceidx) {
+ if (!(ramsample.loop & 1)) return playhead;
+ int loopstart = calcloopstart(sliceidx);
+ int loopend = calcloopend(sliceidx);
+ int looplen = loopend - loopstart;
+ if (looplen > 0 && (playhead < loopstart || playhead >= loopstart + looplen)) {
+ playhead = (playhead - loopstart) % looplen;
+ if (playhead < 0) playhead += looplen;
+ playhead += loopstart;
+ }
+ return playhead;
+}
+static inline int doloop8(int playhead, u8 sliceidx) {
+ if (!(ramsample.loop & 1)) return playhead;
+ int loopstart = calcloopstart(sliceidx)<<8;
+ int loopend = calcloopend(sliceidx)<<8;
+ int looplen = loopend - loopstart;
+ if (looplen > 0 && (playhead < loopstart || playhead >= loopstart + looplen)) {
+ playhead = (playhead - loopstart) % looplen;
+ if (playhead < 0) playhead += looplen;
+ playhead += loopstart;
+ }
+ return playhead;
+}
+
+#ifdef EMU
+float arpdebug[1024];
+int arpdebugi;
+#endif
+
+#ifdef EMU
+#define SMUAD(o, a, b) o=(int)(((s16)(a))*((s16)(b))+((s16)(a>>16))*((s16)(b>>16)))
+#else
+#define SMUAD(o, a, b) asm("smuad %0, %1, %2" : "=r" (o) : "r" (a), "r" (b))
+#endif
+
+
+
+//s16 wavetable[WAVETABLE_SIZE*WT_LAST];
+#ifndef EMU
+__attribute__((section(".wavetableSection")))
+#endif
+#include "wavetable.h"
+#ifdef _WIN32
+//#define clz __lzcnt
+u32 clz(u32 val) {
+ u8 res = 0;
+ if (!val) return 32;
+ while (!(val & 0x80000000))
+ {
+ res++;
+ val <<= 1;
+ }
+ return res;
+}
+#else
+#define clz __builtin_clz
+#endif
+void RunVoice(Voice *v, int fingeridx, float targetvol, u32 *outbuf) {
+
+// if (fingeridx == 0) targetvol = 1.f;
+ //if (fingeridx > 0) return;
+ //float otargetvol = targetvol;
+ targetvol = UpdateEnvelope(v, fingeridx, targetvol);
+ tc_start(&_tc_osc);
+ float noise;
+#ifdef EMU
+ if (fingeridx == 4) {
+ //bool click = otargetvol > 0.5f && (arpbits & 16) && arpretrig ;
+ arpdebug[arpdebugi] = targetvol;// +(click ? 0.5 : 0);
+ //if (click && targetvol < 0.001f) {
+ // arpdebug[arpdebugi] = 1.f;
+ //}
+ }
+#endif
+ Finger *f=touch_synth_getlatest(fingeridx);
+
+ /* the touch.h version should handle this damn it
+ if (targetvol > 0.01f && v->vol < 0.01f) {
+ // trigger! reset pitches?
+ for (int c1 = 0; c1 < 8; ++c1) {
+ fingers_synth_sorted[fingeridx][c1] = *f;
+ }
+ }*/
+
+ float glide =lpf_k(param_eval_finger(P_GLIDE, fingeridx, f)>>2) * (0.5f/BLOCK_SAMPLES);
+ int drivelvl=param_eval_finger(P_DRIVE, fingeridx, f);
+ float fdrive = table_interp(pitches, ((32768-2048)+drivelvl/2));
+ if (drivelvl<-65536+2048)
+ fdrive*=(drivelvl+65536)*(1.f/2048.f); // ensure drive goes right to 0 when full minimum
+ float drive = fdrive * (0.75f/65536.f);
+
+ float targetnoise = param_eval_finger(P_NOISE, fingeridx, f) * (1.f / 65536.f);
+ targetnoise *= targetnoise;
+ if (drivelvl > 0)
+ targetnoise *= fdrive;
+ float dnoise = (targetnoise - v->noise) * (1.f / BLOCK_SAMPLES);
+
+ int resonancei = 65536 - param_eval_finger(P_MIXRESO, fingeridx, f);
+ float resonance = 2.1f - (table_interp(pitches, resonancei) * (2.1f / pitches[1024]));
+
+ drive *= 2.f / (resonance + 2.f);
+
+ //glide=0.25f/BLOCK_SAMPLES;
+
+ Finger* synthf = touch_synth_getlatest(fingeridx);
+ float timestretch = 1.f;
+ float posjit = 0.f;
+ float sizejit = 1.f;
+ float gsize = 0.125f;
+ float grate = 1.f;
+ float gratejit = 0.f;
+ int smppos = 0;
+ if (ramsample.samplelen) {
+ if (!isgrainpreview()) {
+ timestretch = param_eval_finger(P_SMP_TIME, fingeridx, synthf) * (2.f / 65536.f);
+ gsize = param_eval_finger(P_SMP_GRAINSIZE, fingeridx, synthf)* (1.414f / 65536.f);
+ grate = param_eval_finger(P_SMP_RATE, fingeridx, synthf) * (2.f / 65536.f);
+ smppos = (param_eval_finger(P_SMP_POS, fingeridx, synthf) * ramsample.samplelen) >> 16;
+ posjit = param_eval_finger(P_JIT_POS, fingeridx, synthf) * (1.f / 65536.f);
+ sizejit = param_eval_finger(P_JIT_GRAINSIZE, fingeridx, synthf) * (1.f / 65536.f);
+ gratejit = param_eval_finger(P_JIT_RATE, fingeridx, synthf) * (1.f / 65536.f);
+ }
+ }
+ int trig = synthfingertrigger & (1 << fingeridx);
+
+ int prevsliceidx = v->sliceidx;
+ if (ramsample.samplelen)
+ {
+ bool gp = isgrainpreview();
+
+ // decide on the sample for the NEXT frame
+ if (trig) {// on trigger frames, we FADE out the old grains! then the next dma fetch will be the new sample and we can fade in again
+ EmuDebugLog("!!!!!!!!!!!!TRIG! %d\n", arpretrig);
+ targetvol = 0.f;
+ // DebugLog("\r\n%d", fingeridx);
+ int ypos = 0;
+ if (ramsample.pitched && !gp) {
+ /// / / / ////////////////////// multisample choice
+ int best = fingeridx;
+ int bestdist = 0x7fffffff;
+ int mypitch = (v->theosc[1].pitch + v->theosc[2].pitch) / 2;
+ int mysemi = (mypitch) >> 9;
+ static u8 multisampletime[8];
+ static u8 trigcount = 0;
+ trigcount++;
+ for (int i = 0; i < 8; ++i) {
+ int dist = abs(mysemi - ramsample.notes[i]) * 256 - (u8)(trigcount - multisampletime[i]);
+ if (dist < bestdist) {
+ bestdist = dist;
+ best = i;
+ }
+ }
+ multisampletime[best] = trigcount; // for round robin
+ v->sliceidx = best;
+ if (grate < 0.f)
+ ypos = 8;
+ }
+ else {
+ v->sliceidx = fingeridx;
+ ypos = (f->pos / 256);
+ if (gp)
+ ypos = 0;
+ if (grate < 0.f)
+ ypos++;
+ }
+ v->initialfingerpos = gp ? 128 : f->pos;
+ v->playhead8 = sample_slice_pos8(((v->sliceidx * 8) + ypos) << (16 - 6)) ;
+ if (grate < 0.f) {
+ v->playhead8 -= 192 << 8;
+ if (v->playhead8 < 0)
+ v->playhead8 = 0;
+ }
+ knobsmooth_reset(&v->fingerpos, 0);
+ }
+ else { // not trigger - just advance playhead
+ float ms2 = (v->thegrains[0].multisample_grate + v->thegrains[1].multisample_grate); // double multisample rate
+ int delta_playhead8 = (int)(grate * ms2 * timestretch * (BLOCK_SAMPLES * 0.5f * 256.f) + 0.5f);
+ v->playhead8 = doloop8(v->playhead8 + delta_playhead8, v->sliceidx);
+
+ float gdeadzone = clampf(minf(1.f - posjit, timestretch * 2.f), 0.f, 1.f); // if playing back normally and not jittering, add a deadzone
+ float fpos = deadzone(f->pos - v->initialfingerpos, gdeadzone * 32.f);
+ if (gp)
+ fpos = 0.f;
+// EmuDebugLog("scrub pos %0.2f\n", fpos);
+ knobsmooth_update_knob(&v->fingerpos, fpos, 2048.f);
+ }
+ } // sampler prep
+
+#define OSC_COUNT 2 // DO NOT CHECK IN 1 // XXX 2
+ s32 pwm_base = rampreset.params[P_PWM][0];
+ s32 pwm = param_eval_finger(P_PWM, fingeridx, synthf);
+ if (pwm_base >=-8 && pwm_base<8)
+ pwm = 0;
+ else if (pwm_base > 0)
+ pwm = clampi(pwm, 1, 65535);
+ else
+ pwm = clampi(pwm, -65535, -1);
+
+
+
+ for (int osci = 0; osci < OSC_COUNT; osci++) {
+ s16 *dst = ((s16*) outbuf) + (osci & 1);
+ noise = v->noise;
+ float y1 = v->y[0 + osci] , y2 = v->y[2 + osci];
+ int randtabpos = rand() & 16383;
+ if (ramsample.samplelen) {
+ // mix grains
+ GrainPair* g = &v->thegrains[osci];
+ int grainidx = fingeridx * 4 + osci * 2;
+ int g0start = 0;
+ if (grainidx) g0start = grainbufend[grainidx - 1];
+ int g1start = grainbufend[grainidx];
+ int g2start = grainbufend[grainidx + 1];
+
+ int64_t posa = g->pos[0];
+ int64_t posb = g->pos[1];
+ int loopstart = calcloopstart(prevsliceidx);
+ int loopend = calcloopend(prevsliceidx);
+ bool outofrange0 = posa < loopstart || posa >= loopend;
+ bool outofrange1 = posb < loopstart || posb >= loopend;
+ int gvol24 = g->vol24;
+ int dgvol24 = g->dvol24;
+ int dpos24 = g->dpos24;
+ int fpos24 = g->fpos24;
+ float vol = v->vol;
+ float dvol = (targetvol - vol) * (1.f / BLOCK_SAMPLES);
+ outofrange0 |= g1start - g0start <= 2;
+ outofrange1 |= g2start - g1start <= 2;
+ g->outflags = (outofrange0 ? 1 : 0) + (outofrange1 ? 2 : 0);
+ if ((g1start - g0start <= 2 && g2start - g1start <= 2)) {
+ // fast mode :) emulate side effects without doing any work
+ vol += dvol * BLOCK_SAMPLES;
+ noise += dnoise * BLOCK_SAMPLES;
+ gvol24 -= dgvol24 * BLOCK_SAMPLES;
+ fpos24 += dpos24 * BLOCK_SAMPLES;
+ int id = fpos24 >> 24;
+ g->pos[0] += id;
+ g->pos[1] += id;
+ fpos24 &= 0xffffff;
+ }
+ else {
+ const s16* src0 = (outofrange0 ? (const s16*)zero : &grainbuf[g0start + 2]) + g->bufadjust;
+ const s16* src0_backup = src0;
+ const s16* src1 = (outofrange1 ? (const s16*)zero : &grainbuf[g1start + 2]) + g->bufadjust;
+ if (spistate && spistate <= grainidx + 2) {
+ //DebugLog("!"); // spidebug
+ while (spistate && spistate <= grainidx + 2);
+ }
+ for (int i = 0; i < BLOCK_SAMPLES; ++i) {
+ int o0, o1;
+#ifdef EMU
+ ASSERT(outofrange0 || (src0 >= &grainbuf[g0start + 2] && src0 + 1 < &grainbuf[g1start]));
+ ASSERT(outofrange1 || (src1 >= &grainbuf[g1start + 2] && src1 + 1 < &grainbuf[g2start]));
+#endif
+ u32 ab0 = *(u32*)(src0); // fetch a pair of 16 bit samples to interpolate between
+ u32 mix = (fpos24 << (16 - 9)) & 0x7fff0000;
+ mix |= 32767 - (mix >> 16); // mix is now the weights for the linear interpolation
+ SMUAD(o0, ab0, mix); // do the interpolation, result is *32768
+ o0 >>= 16;
+
+ u32 ab1 = *(u32*)(src1); // fetch a pair for the other grain in the pair
+ SMUAD(o1, ab1, mix); // linear interp by same weights
+ o1 >>= 16;
+
+ fpos24 += dpos24; // advance fractional sample pos
+ int bigdpos = (fpos24 >> 24);
+ fpos24 &= 0xffffff;
+ src0 += bigdpos; // advance source pointers by any whole sample increment
+ src1 += bigdpos;
+
+ mix = (gvol24 >> 9) & 0x7fff; // blend between the two grain results
+ mix |= (32767 - mix) << 16;
+ u32 o01 = STEREOPACK(o0, o1);
+ int ofinal;
+ SMUAD(ofinal, o01, mix);
+ gvol24 -= dgvol24;
+ if (gvol24 < 0)
+ gvol24 = 0;
+
+ s16 n = ((s16*)rndtab)[randtabpos++]; // mix in a white noise source
+ noise += dnoise; // volume ramp for noise
+
+ vol += dvol; // volume ramp for grain signal
+ float input = (ofinal * drive + n * noise) ; // input to filter
+ float cutoff = 1.f - squaref(maxf(0.f, 1.f - vol * 1.1f)); // filter cutoff for low pass gate
+ y1 += (input - y1) * cutoff; // do the lowpass
+
+ int yy = FLOAT2FIXED(y1 * vol, 0); // for granular, we include an element of straight VCA
+ *dst = SATURATE16(*dst + yy); // write to output
+ dst += 2;
+
+ }
+ int bigposdelta = src0 - src0_backup;
+ g->pos[0] += bigposdelta;
+ g->pos[1] += bigposdelta;
+ } // grain mix
+ g->fpos24 = fpos24;
+ g->vol24 = gvol24;
+
+ if (gvol24 <= dgvol24 || trig) { // new grain trigger! this is for the *next* frame
+// if (targetvol > 0.01f)
+// DebugLog("%c", 'a' + (int)(targetvol * 25));
+ if (targetvol > 0.01f) {
+ int i = 1;
+ }
+ if (!trig) {
+ int i = 1;
+ }
+ int ph = v->playhead8 >> 8;
+ int slicelen = ramsample.splitpoints[v->sliceidx + 1] - ramsample.splitpoints[v->sliceidx];
+ if (editmode != EM_SAMPLE) {
+ ph += ((int)(v->fingerpos.y2 * slicelen)) >> (10);
+ ph += smppos; // scrub input
+ }
+ g->vol24 = ((1 << 24) - 1);
+ int grainsize = ((rand() & 127) * sizejit + 128.f) * (gsize * gsize) + 0.5f;
+ grainsize *= BLOCK_SAMPLES;
+ int jitpos = (rand() & 255) * posjit;
+ ph += ((grainsize+8192) * jitpos) >> 8;
+ g->dvol24 = g->vol24 / grainsize;
+
+ float grate2 = 1.f + ((rand() & 255) * (gratejit * gratejit)) * (1.f / 256.f);
+ //float revprob = (0.125f - timestretch) * (4.f * 256.f);
+ //if ((rand() & 255) < (int)revprob)
+ if (timestretch<0.f)
+ grate2 = -grate2;
+ g->grate_ratio = grate2;
+#ifdef EMU
+// if (osci==0 && (targetvol > 0.01f || trig))
+// EmuDebugLog("%s grain at ph %d, other at %d, volume went from %f to %f, next to %f \n",
+// trig ? "trigger" : "new",
+// ph, (int)(g->pos[1]), v->vol, targetvol);
+#endif
+ g->pos[0] = trig ? ph : g->pos[1];
+ g->pos[1] = ph;
+ }
+ }
+ else
+ {
+ // synth
+ float vol = v->vol;
+ float dvol = (targetvol - vol) * (1.f / BLOCK_SAMPLES);
+
+ Osc* o = &v->theosc[osci];
+
+ u32 flippity = 0;
+ if (pwm!=0) {
+ flippity = ~0;
+ //if (abs(pwm)
+ {
+ //u32 pp = pwm >> 16;
+ //if (pp > 1024) pp = 1024;
+ u32 avgdp = (o[0].dphase + o[2].dphase) / 2;
+ o[0].dphase = avgdp;//(s32)((avgdp - o[0].dphase) * pp) >> 10;
+ o[2].dphase = avgdp;// (s32)((avgdp - o[2].dphase)* pp) >> 10;
+ avgdp = (o[0].targetdphase + o[2].targetdphase) / 2;
+ o[0].targetdphase = avgdp;// (s32)((avgdp - o[0].targetdphase)* pp) >> 10;
+ o[2].targetdphase = avgdp;// (s32)((avgdp - o[2].targetdphase)* pp) >> 10;
+
+ if (pwm < 0) {
+ s32 phase0fix = (s32)(o[2].phase - o[0].phase - (pwm<<16) + (1 << 31)) / (BLOCK_SAMPLES);
+ o[0].dphase += phase0fix;
+ o[0].targetdphase += phase0fix;
+ }
+ }
+ }
+ // o->dphase=o->targetdphase;// XXXX REMOVE GLIDE
+ int ddphase1 = (int)((o->targetdphase - o->dphase) * glide);
+ u32 phase1 = o->phase;
+ s32 dphase1 = o->dphase;
+ u32 prevsample1 = o->prevsample;
+ o += 2;
+ // o->dphase=o->targetdphase;// XXXX REMOVE GLIDE
+ int ddphase2 = (int)((o->targetdphase - o->dphase) * glide);
+ u32 phase2 = o->phase;
+ s32 dphase2 = o->dphase;
+ u32 prevsample2 = o->prevsample;
+ o -= 2;
+
+ if (pwm>0) {
+ //pwm -= 4096;
+ // we need to choose the shift so that, after shifting right, there are 16 bits of fractional part in each cycle
+ // if the increment was say, 1<<(32-9) = ((1<<23)-1), we would take just over 512 steps to cycle, so we want to shift by 7
+ //dphase1 = (1 << 22) - 1;
+ int shift1 = 16-clz(maxi(dphase1,1<<22));
+ const static u16 wavetable_octave_offset[17] = { 0,0,0,0,0,0,0,
+ 0,// 7 - 9 bits
+ 513, // 8 - 8 bits
+ 513 + 257, // 9 - 7 bits
+ 513 + 257 + 129, // 10 - 6 bits
+ 513 + 257 + 129 + 65,
+ 513 + 257 + 129 + 65 + 33,
+ 513 + 257 + 129 + 65 + 33 + 17,
+ 513 + 257 + 129 + 65 + 33 + 17 + 9,
+ 513 + 257 + 129 + 65 + 33 + 17 + 9 + 5,
+ 513 + 257 + 129 + 65 + 33 + 17 + 9 + 5 + 3, // 1031
+ };
+ // 16 wave shapes
+// pwm = 2<<12;
+ u32 subwave = (pwm & 4095) << (1);
+ //subwave = 0; DISABLE BLENDING
+ subwave = subwave | ((8191 - subwave) << 16);
+ int wtbase = (pwm) >> (12);
+ const s16* table1 = wavetable[wtbase] + wavetable_octave_offset[shift1];
+ for (int i = 0; i < BLOCK_SAMPLES; ++i) {
+ unsigned int i,i2;
+ int s0, s1;
+ dphase1 += ddphase1;
+ i = (phase1 += dphase1) >> shift1;
+ i2 = i >> 16;
+ s0 = table1[i2], s1 = table1[i2 + 1];
+ s32 out0 = (s0<<16) + ((s1 - s0) * (u16)(i));
+ i2 += WAVETABLE_SIZE;
+ s0 = table1[i2], s1 = table1[i2 + 1];
+ s32 out1 = (s0<<16) + ((s1 - s0) * (u16)(i));
+ u32 packed=STEREOPACK(out1>>16, out0>>16);
+ s32 out;
+ SMUAD(out,packed,subwave);
+ //////////////////////////////////////////////////
+ // rest is same as polyblep
+ s16 n = ((s16*)rndtab)[randtabpos++];
+ noise += dnoise;
+
+ vol += dvol;
+ y1 += (out * drive + n * noise - (y2 - y1) * resonance - y1) * vol; // drive
+ // y1 *= 16383.f / (16384.f + fabsf(y2));
+ y1 *= 0.999f;
+ y2 += (y1 - y2) * vol;
+
+ y2 *= 0.999f;
+
+ int yy = FLOAT2FIXED(y2, 0);
+ *dst = SATURATE16(*dst + yy);
+ dst += 2;
+ }
+ }
+ else {
+ for (int i = 0; i < BLOCK_SAMPLES; ++i) {
+ dphase1 += ddphase1;
+ phase1 += dphase1;
+ u32 newsample1 = phase1;
+
+ if (unlikely(phase1 < (u32)dphase1)) {
+ // edge! polyblep it.
+ u32 fractime = mini(65535, phase1 / (dphase1 >> 16));
+ prevsample1 -= (fractime * fractime) >> 1;
+ fractime = 65535 - fractime;
+ newsample1 += (fractime * fractime) >> 1;
+ }
+ s32 out = (s32)(prevsample1 >> 4); // (s32)prevsample1 >> 4
+ prevsample1 = newsample1;
+
+ dphase2 += ddphase2;
+ phase2 += dphase2;
+ u32 newsample2 = phase2;
+ if (unlikely(phase2 < (u32)dphase2)) {
+ // edge! polyblep it.
+ u32 fractime = mini(65535, phase2 / (dphase2 >> 16));
+ prevsample2 -= (fractime * fractime) >> 1;
+ fractime = 65535 - fractime;
+ newsample2 += (fractime * fractime) >> 1;
+
+ }
+
+#ifdef EMU
+ /*
+ if (fingeridx == 3 && targetvol>0.f && osci==1) {
+ static FILE* testy = 0;
+ if (!testy)
+ testy = fopen("testy.raw", "wb");
+ s16 aa = (out >> 16) - 32678 / 16;
+ s16 bb = (((prevsample2 ^ flippity) >> 4) >> 16) - 32768 / 16;
+ fwrite(&aa, 1, 2, testy);
+ fwrite(&bb, 1, 2, testy);
+ }*/
+#endif
+
+ out += (s32)((prevsample2 ^ flippity) >> 4) - (2 << (31 - 4));
+ prevsample2 = newsample2;
+
+ s16 n = ((s16*)rndtab)[randtabpos++];
+ noise += dnoise;
+
+ vol += dvol;
+ y1 += (out * drive + n * noise - (y2 - y1) * resonance - y1) * vol; // drive
+ // y1 *= 16383.f / (16384.f + fabsf(y2));
+ y1 *= 0.999f;
+ y2 += (y1 - y2) * vol;
+
+ y2 *= 0.999f;
+
+ int yy = FLOAT2FIXED(y2, 0);
+ *dst = SATURATE16(*dst + yy);
+ dst += 2;
+ } // samples
+ }
+ o[0].phase = phase1;
+ o[0].dphase = dphase1;
+ o[0].prevsample = prevsample1;
+
+ o[2].phase = phase2;
+ o[2].dphase = dphase2;
+ o[2].prevsample = prevsample2;
+ } // synth
+ v->y[osci] = y1, v->y[osci + 2] = y2;
+ }// osc loop
+
+ v->vol = targetvol;
+ v->noise = noise;
+
+ if (ramsample.samplelen) {
+ // update pitch (aka dpos24) for next time!
+ for (int gi = 0; gi < 2; ++gi) {
+ float multisample_grate;
+ if (ramsample.pitched && !isgrainpreview()) {
+ int relpitch = v->theosc[1 + gi].pitch - ramsample.notes[v->sliceidx] * 512;
+ if (relpitch < -512 * 12 * 5) {
+ multisample_grate = 0.f;
+ }
+ else {
+ multisample_grate = // exp2f(relpitch / (512.f * 12.f));
+ table_interp(pitches, relpitch+32768);
+
+ }
+ }
+ else {
+ multisample_grate = 1.f;
+ }
+// multisample_grate = 1.f; // debug - force multisample to original pitch
+ v->thegrains[gi].multisample_grate = multisample_grate;
+ int dpos24 = (1 << 24) * (grate * v->thegrains[gi].grate_ratio * multisample_grate);
+ while (dpos24 > (2 << 24))
+ dpos24 >>= 1;
+ v->thegrains[gi].dpos24 = dpos24;
+ }
+ }
+
+ tc_stop(&_tc_osc);
+
+}
+
+
+s32 Reverb2(s32 input, s16 *buf) {
+ int i = reverbpos;
+// int fb = buf[i];
+ int outl = 0, outr = 0;
+ float wob = lfo_next(&aplfo) * k_reverb_wob;
+ int apwobpos = FLOAT2FIXED((wob + 1.f), 12 + 6);
+ wob = lfo_next(&aplfo2) * k_reverb_wob;
+ int delaywobpos = FLOAT2FIXED((wob + 1.f), 12 + 6);
+#define RVDIV /2
+#define CHECKACC // assert(acc>=-32768 && acc<32767);
+#define AP(len) { \
+ int j = (i + len RVDIV) & RVMASK; \
+ s16 d = buf[j]; \
+ acc -= d >> 1; \
+ buf[i] = SATURATE16(acc); \
+ acc = (acc >> 1) + d; \
+ i = j; \
+ CHECKACC \
+ }
+#define AP_WOBBLE(len, wobpos) { \
+ int j = (i + len RVDIV) & RVMASK;\
+ s16 d = LINEARINTERPRV(buf, j, wobpos); \
+ acc -= d >> 1; \
+ buf[i] = SATURATE16(acc); \
+ acc = (acc >> 1) + d; \
+ i = j; \
+ CHECKACC \
+ }
+#define DELAY(len) { \
+ int j = (i + len RVDIV) & RVMASK; \
+ buf[i] = SATURATE16(acc); \
+ acc = buf[j]; \
+ i = j; \
+ CHECKACC \
+ }
+#define DELAY_WOBBLE(len, wobpos) { \
+ int j = (i + len RVDIV) & RVMASK; \
+ buf[i] = SATURATE16(acc); \
+ acc = LINEARINTERPRV(buf, j, wobpos); \
+ i=j; \
+ CHECKACC \
+ }
+
+ // Griesinger according to datorro does 142, 379, 107, 277 on the way in - totoal 905 (20ms)
+ // then the loop does 672+excursion, delay 4453, (damp), 1800, delay 3720 - total 10,645 (241ms)
+ // then decay, and feed in
+ // and on the other side 908+excursion, delay 4217, (damp), 2656, delay 3163 - total 10,944 (248 ms)
+
+ // keith barr says:
+ // I really like 2AP, delay, 2AP, delay, in a loop.
+ // I try to set the delay to somewhere a bit less than the sum of the 2 preceding AP delays,
+ // which are of course much longer than the initial APs(before the loop)
+ // Yeah, the big loop is great; you inject input everywhere, but take it out in only two places
+ // It just keeps comin� newand fresh as the thing decays away.�If you�ve got the memoryand processing!
+
+ // lets try the 4 greisinger initial Aps, inject stereo after the first AP,
+
+ int acc = ((s16)(input)) * k_reverbsend >> 17;
+ AP(142);
+ AP(379);
+ acc += (input >> 16) * k_reverbsend >> 17;
+ AP(107);
+// int reinject2 = acc;
+ AP(277);
+ int reinject = acc;
+ static int fb1 = 0;
+ acc += fb1;
+ AP_WOBBLE(672, apwobpos);
+ AP(1800);
+ DELAY(4453);
+
+ if (1) {
+ // shimmer - we can read from up to about 2000 samples ago
+
+ // Brief shimmer walkthrough:
+ // - We walk backwards through the reverb buffer with 2 indices: shimmerpos1 and shimmerpos2.
+ // - shimmerpos1 is the *previous* shimmer position.
+ // - shimmerpos2 is the *current* shimmer position.
+ // - Note that we add these to i (based on reverbpos), which is also walking backwards
+ // through the buffer.
+ // - shimmerfade controls the crossfade between the shimmer from shimmerpos1 and shimmerpos2.
+ // - When shimmerfade == 0, shimmerpos1 (the old shimmer) is chosen.
+ // - When shimmerfade == SHIMMER_FADE_LEN - 1, shimmerpos2 (the new shimmer) is chosen.
+ // - For everything in-between, we linearly interpolate (crossfade).
+ // - When we hit the end of the fade, we reset shimmerpos2 to a random new position and set
+ // shimmerpos1 to the old shimmerpos2.
+ // - dshimmerfade controls the speed at which we fade.
+
+ #define SHIMMER_FADE_LEN 32768
+ shimmerfade += dshimmerfade;
+
+ if (shimmerfade >= SHIMMER_FADE_LEN) {
+ shimmerfade -= SHIMMER_FADE_LEN;
+
+ shimmerpos1 = shimmerpos2;
+ shimmerpos2 = (rand() & 4095) + 8192;
+ dshimmerfade = (rand() & 7) + 8; // somewhere between SHIMMER_FADE_LEN/2048 and SHIMMER_FADE_LEN/4096 ie 8 and 16
+ }
+
+ // L = shimmer from shimmerpos1, R = shimmer from shimmerpos2
+ u32 shim1 = STEREOPACK(buf[(i + shimmerpos1) & RVMASK], buf[(i + shimmerpos2) & RVMASK]);
+ u32 shim2 = STEREOPACK(buf[(i + shimmerpos1 + 1) & RVMASK], buf[(i + shimmerpos2 + 1) & RVMASK]);
+ u32 shim = STEREOADDAVERAGE(shim1, shim2);
+
+ // Fixed point crossfade:
+#ifdef CORTEX
+ u32 a = STEREOPACK((SHIMMER_FADE_LEN - 1) - shimmerfade, shimmerfade);
+ s32 shimo;
+ asm("smuad %0, %1, %2" : "=r" (shimo) : "r" (a), "r" (shim));
+#else
+ STEREOUNPACK(shim);
+ s32 shimo = shiml * ((SHIMMER_FADE_LEN - 1) - shimmerfade) +
+ shimr * shimmerfade;
+#endif
+ shimo >>= 15; // Divide by SHIMMER_FADE_LEN
+
+ // Apply user-selected shimmer amount.
+ shimo *= k_reverb_shim;
+ shimo >>= 8;
+
+ // Tone down shimmer amount.
+ shimo >>= 1;
+
+ acc += shimo;
+ outl = shimo;
+ outr = shimo;
+
+ shimmerpos1--;
+ shimmerpos2--;
+ }
+
+
+ const static float k_reverb_color = 0.95f;
+ static float lpf = 0.f, dc = 0.f;
+ lpf += (((acc * k_reverb_fade) >> 8) - lpf) * k_reverb_color;
+ dc += (lpf - dc) * 0.005f;
+ acc = (int)(lpf - dc);
+ outl += acc;
+
+ acc += reinject;
+ AP_WOBBLE(908, delaywobpos);
+ AP(2656);
+ DELAY(3163);
+ static float lpf2 = 0.f;
+ lpf2+= (((acc * k_reverb_fade) >> 8) - lpf2) * k_reverb_color;
+ acc = (int)(lpf2);
+
+ outr += acc;
+
+// acc += reinject2;
+// AP(4931);
+// AP(3713);
+// DELAY(6137);
+ /*
+ int acc=((s16) (input))*k_reverbsend >> 17;
+ AP(142);
+ acc += (input >> 16) * k_reverbsend >> 17;
+ AP(107);
+ AP_WOBBLE(379, delaywobpos);
+ AP(277);
+ static int fb1=0;
+ // first leg
+ acc+=fb1;
+ AP_WOBBLE(672, apwobpos);
+ AP(4453);
+
+ if (1) {
+ // shimmer - we can read from up to about 2000 samples ago
+ shimmerfade += dshimmerfade;
+ if (shimmerfade >= 32768) {
+ shimmerpos1 = shimmerpos2;
+ shimmerpos2 = (rand() & 1023) + 1024;
+ shimmerfade -= 32768;
+ dshimmerfade = (rand() & 31) + 32; // somewhere between 65536/1024 and 65536/512 ie 64 and 128
+ }
+ u32 shim1 = STEREOPACK(buf[(i + shimmerpos1) & RVMASK], buf[(i + shimmerpos2) & RVMASK]);
+ u32 shim2 = STEREOPACK(buf[(i + shimmerpos1 + 1) & RVMASK], buf[(i + shimmerpos2 + 1) & RVMASK]);
+ u32 shim = STEREOADDAVERAGE(shim1, shim2);
+#ifdef CORTEX
+ u32 a = STEREOPACK(32767 - shimmerfade, shimmerfade);
+ s32 shimo;
+ asm ("smuad %0, %1, %2" : "=r" (shimo) : "r" (a), "r" (shim));
+#else
+ STEREOUNPACK(shim);
+ s32 shimo=(shimr*shimmerfade + shiml*(32767-shimmerfade));
+#endif
+ shimo >>= 16;
+ shimo*=k_reverb_shim;
+ shimo>>=8;
+ acc += shimo>>1;
+ outl = shimo;
+ outr = shimo;
+ shimmerpos1--;
+ shimmerpos2--;
+ // outl=outr=shimo;
+ }
+
+
+ outr+=acc;
+
+ static float lpf = 0.f, dc=0.f;
+ const static float k_reverb_color = 0.95f;
+ lpf += (((acc*k_reverb_fade)>>8) - lpf) * k_reverb_color;
+ dc+=(lpf-dc)*0.005f;
+ acc = (int) (lpf - dc);
+ AP(1800);
+ DELAY(3270);
+ outl+=acc;
+ */
+ reverbpos = (reverbpos - 1) & RVMASK;
+ fb1=(acc*k_reverb_fade)>>8;
+ return STEREOPACK(SATURATE16(outl), SATURATE16(outr));
+
+
+}
+
+
+
+
+
+u32 STEREOSIGMOID(u32 in) {
+ u16 l = sigmoid[(u16) in];
+ u16 r = sigmoid[in >> 16];
+ return STEREOPACK(l, r);
+}
+
+s16 MONOSIGMOID(int in) {
+ in=SATURATE16(in);
+ return sigmoid[(u16) in];
+}
+
+void update_params(int fingertrig, int fingerdown);
+
+
+
+#ifdef EMU
+float powerout; // squared power
+float gainhistoryrms[512];
+int ghi;
+#endif
+
+int stride(u32 scale, int stride_semitones, int fingeridx) { // returns the number of scale steps up from 0 for finger index fi
+ static u8 stridetable[8]; // memoise the result indexed by fi
+ static u8 stridehash[8];
+ //XXX TEST stride_semitones =2;
+ u8 newhash = stride_semitones + (scale * 16);
+ if (newhash != stridehash[fingeridx]) {
+ // memoise this man
+ stridehash[fingeridx] = newhash;
+ int pos = 0;
+ u8 usedsteps[16] = { 1, 0 };
+ const u16* scaleptr = scaletab[scale];
+ u8 numsteps = *scaleptr++;
+ for (int fi = 0; fi < fingeridx; ++fi) {
+ pos += stride_semitones;
+ int step = pos % 12;
+ int best = 0, bestdist = 0;
+ int bestscore = 9999;
+ for (int i = 0; i < numsteps; ++i) {
+ int qstep = scaleptr[i]/512; // candidate step in the scale
+ int dist = qstep - step;
+ if (dist < -6) {
+ dist += 12; qstep += 12;
+ }
+ else if (dist > 6) {
+ dist -= 12; qstep -= 12;
+ }
+ int score = abs(dist) * 16 + usedsteps[i]; // penalise steps we have used many times
+ if (score < bestscore) {
+ bestscore = score;
+ best = i;
+ bestdist = dist;
+ }
+ }
+ usedsteps[best]++;
+ pos += bestdist;
+ stridetable[fingeridx] = best + (pos / 12) * numsteps;
+ }
+ }
+ return stridetable[fingeridx];
+}
+
+#ifdef EMU
+float m_compressor,m_dry, m_audioin, m_dry2wet, m_delaysend, m_delayreturn, m_reverbin, m_reverbout, m_fxout, m_output;
+void MONITORPEAK(float *mon, u32 stereoin) {
+ STEREOUNPACK(stereoin);
+ float peak = (1.f/32768.f)*maxi(abs(stereoinl), abs(stereoinr));
+ if (peak > *mon) *mon = peak;
+ else *mon += (peak - *mon) * 0.0001f;
+}
+#else
+#define MONITORPEAK(mon,stereoin)
+#endif
+u8 midi24ppqncounter;
+const s8 midicctable[128] = {
+ // 0 1 2 3 4 5 6 7
+ /* 0 */ -1, -1, P_NOISE, P_SENS, P_DRIVE, P_GLIDE, -1, P_MIXSYNTH,
+ /* 8 */ P_MIXWETDRY,P_PITCH, -1, P_GATE_LENGTH,P_DLTIME, P_PWM, P_INTERVAL, P_SMP_POS,
+ /* 16 */ P_SMP_GRAINSIZE,P_SMP_RATE,P_SMP_TIME,P_ENV_LEVEL,P_A2, P_D2, P_S2, P_R2,
+ /* 24 */ P_AFREQ, P_ADEPTH, P_AOFFSET, P_BFREQ, P_BDEPTH, P_BOFFSET, -1, P_MIXHPF,
+ /* 32 */ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* 40 */ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* 48 */ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* 56 */ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* 64 */ -1, -1, -1, -1, -1, -1, -1, P_MIXRESO,
+ /* 72 */ P_R, P_A, P_S, P_D, P_XFREQ, P_XDEPTH, P_XOFFSET, P_YFREQ,
+ /* 80 */ P_YDEPTH, P_YOFFSET, P_SAMPLE, P_SEQPAT, -1, P_SEQSTEP, -1, -1,
+ /* 88 */ -1, P_MIXINPUT, P_MIXINWETDRY,P_RVSEND, P_RVTIME, P_RVSHIM, P_DLSEND, P_DLFB,
+ /* 96 */ -1, -1, -1, -1, -1, P_LATCHONOFF, P_ARPONOFF, P_ARPMODE,
+ /* 104 */ P_ARPDIV, P_ARPPROB, P_ARPLEN, P_ARPOCT, P_SEQMODE, P_SEQDIV, P_SEQPROB, P_SEQLEN,
+ /* 112 */ P_DLRATIO, P_DLWOB, P_RVWOB, -1, P_JIT_POS, P_JIT_GRAINSIZE, P_JIT_RATE, P_JIT_PULSE,
+ /* 120 */ -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+
+
+
+
+bool midi_receive(unsigned char packet[4]); // usb midi poll
+void processmidimsg(u8 msg, u8 d1, u8 d2);
+bool processusbmidi(void) {
+ u8 midipacket[4];
+ if (!midi_receive(midipacket))
+ return false;
+ // 09 90 30 64
+ // 08 80 30 40
+ //DebugLog("got midi %02x %02x %02x %02x\r\n", midipacket[0], midipacket[1], midipacket[2], midipacket[3]);
+ u8 msg = midipacket[1];
+ u8 d1 = midipacket[2];
+ u8 d2 = midipacket[3];
+ processmidimsg(msg,d1,d2);
+ return true;
+}
+
+
+void midi_panic(void) {
+ midi_pressure_override=0, midi_pitch_override=0;
+ memset(midi_notes,0,sizeof(midi_notes));
+ memset(midi_velocities, 0, sizeof(midi_velocities));
+ memset(midi_aftertouch, 0, sizeof(midi_aftertouch));
+ memset(midi_channels, 255, sizeof(midi_channels));
+ memset(midi_chan_aftertouch, 0, sizeof(midi_chan_aftertouch));
+ memset(midi_chan_pitchbend, 0, sizeof(midi_chan_pitchbend));
+ midi_next_finger = 0;
+}
+
+bool send_midimsg(u8 status, u8 data1, u8 data2);
+void processmidimsg(u8 msg, u8 d1, u8 d2) {
+ u8 chan = msg & 15;
+ u8 type = msg >> 4;
+
+ //int midi_ch_in = ((GetParam(P_MIDI_CH_IN, 0) * 16)/FULL) & 15;
+ //int midi_ch_in = clampi(((GetParam(P_MIDI_CH_IN, 0) * 15)/FULL),0,15);
+
+ int midi_ch_in = clampi((mini(GetParam(P_MIDI_CH_IN, 0), FULL - 1) * 16) / FULL,0,15);
+
+ //if ((chan != 0)&&(type != 0xF)) // chan != 0 = allow all channels; LPZW KAY Fix for MIDI Sync type == F continue
+ if ((chan != midi_ch_in)&&(type != 0xF)) // allow only selected channel and MIDI sync
+ return;
+ if (type < 8)
+ return;
+
+ // send_midimsg(msg, d1, d2); // midi echo!
+
+ if (type == 9 && d2 == 0)
+ type = 8;
+
+ switch (type) {
+ case 0xc: // program change
+ if (d1<32)
+ SetPreset(d1, false);
+ break;
+ case 8: { // note up
+ // find the voice for this note up
+ u8 fi = find_midi_note(chan, d1);
+ if (fi < 8) {
+ midi_pressure_override &= ~(1 << fi);
+ midi_channels[fi] = 255;
+ }
+ }
+ break;
+ case 9: { // note down
+ u8 fi = find_midi_note(chan, d1);
+ if (fi == 255)
+ fi = find_midi_free_channel();
+ if (fi < 8) {
+ midi_notes[fi] = d1;
+ midi_channels[fi] = chan;
+ midi_velocities[fi] = d2;
+ midi_aftertouch[fi] = 0;
+ midi_pressure_override |= 1 << fi;
+ midi_pitch_override |= 1 << fi;
+ }
+ }
+ break;
+ case 0xe: // pitchbend
+ midi_chan_pitchbend[chan] = (d1 + (d2 << 7)) - 0x2000;
+ break;
+ case 0xa: { // polyphonic aftertouch
+ u8 fi = find_midi_note(chan, d1);
+ if (fi < 8) {
+ midi_aftertouch[fi] = d2;
+ }
+ }
+ break;
+ case 0xd: { // channel aftertouch
+ midi_chan_aftertouch[chan] = d2;
+ }
+ break;
+ case 0xb: // cc param
+ {
+ if (d1 >= 32 && d1 < 64)
+ midi_lsb[d1 - 32] = d2;
+ s8 param = (d1 < 128) ? midicctable[d1] : -1;
+ if (param >= 0 && param < P_LAST) {
+ int val;
+ if (d1 < 32)
+ val = (d2 << 7) + midi_lsb[d1];
+ else
+ val = (d2 << 7) + d2;
+ val = (val * FULL) / (127 * 128 + 127);
+ if (param_flags[param] & FLAG_SIGNED)
+ val = val * 2 - FULL;
+ EditParamNoQuant(param, M_BASE, val);
+
+ if (param == P_ARPONOFF){
+ if (val > 64){
+ rampreset.flags = rampreset.flags | FLAGS_ARP;
+ }else{
+ rampreset.flags = rampreset.flags & ~FLAGS_ARP;
+ }
+ ShowMessage(F_32_BOLD, ((rampreset.flags & FLAGS_ARP)) ? "arp on" : "arp off", 0);
+ }
+ if (param == P_LATCHONOFF){
+ if (val > 64){
+ rampreset.flags = rampreset.flags | FLAGS_LATCH;
+ }else{
+ rampreset.flags = rampreset.flags & ~FLAGS_LATCH;
+ }
+ ShowMessage(F_32_BOLD, ((rampreset.flags & FLAGS_LATCH)) ? "latch on" : "latch off", 0);
+ }
+
+ }
+ break;
+ }
+ case 0xf: // system
+ if (msg == 0xfa) { // start
+ got_ui_reset = true;
+ midi24ppqncounter = 5; // 2020-02-26: Used to be 0, changed to 5: https://discord.com/channels/784856175937585152/784884878994702387/814951459581067264
+ playmode = PLAYING;
+ // seq_step(1);
+ }
+ else if (msg == 0xfb) { // continue
+ playmode = PLAYING;
+ }
+ else if (msg == 0xfc) { // stop
+ midi24ppqncounter = 0;
+ playmode = PLAY_STOPPED;
+ OnLoop();
+ }
+ else if (msg == 0xf8) {
+ // midi clock! 24ppqn, we want 4, so divide by 6.
+ midi24ppqncounter++;
+ if (midi24ppqncounter == 6) {
+ gotclkin++;
+ midi24ppqncounter = 0;
+ }
+
+ }
+ break;
+ }
+
+}
+
+void DoRecordModeAudio(u32* dst, u32* audioin) {
+ // recording or level testing
+ int newaudiorec_gain = 65535 - GetADCSmoothedNoCalib(ADC_AKNOB);
+ if (abs(newaudiorec_gain - audiorec_gain_target) > 256) // hysteresis
+ audiorec_gain_target = newaudiorec_gain;
+ knobsmooth_update_knob(&recgain_smooth, audiorec_gain_target, 65536.f);
+ int audiorec_gain = (int)(recgain_smooth.y2)/2;
+
+
+ cur_sample1 = edit_sample0 + 1;
+ CopySampleToRam(false);
+ if (enable_audio == EA_ARMED) {
+ if (audioin_peak > 1024) {
+ recording_trigger();
+ }
+ }
+ if (enable_audio > EA_PREERASE && enable_audio < EA_STOPPING4) {
+ s16* dldst = delaybuf + (recpos & DLMASK);
+ const s16* asrc = (const s16*)audioin;
+ s16* adst = (s16*)dst;
+ if (enable_audio >= EA_STOPPING1) {
+ memset(dldst, 0, BLOCK_SAMPLES * 2);
+ enable_audio++;
+ }
+ else {
+ for (int i = 0; i < BLOCK_SAMPLES; ++i) {
+ s16 smp = *dldst++ = SATURATE16((((int)(asrc[0] + asrc[1])) * audiorec_gain) >> 14);
+#ifdef EMU
+ smp = 0; // disable loopback
+#endif
+ adst[0] = adst[1] = smp;
+ adst += 2;
+ asrc += 2;
+ }
+ }
+ recpos += BLOCK_SAMPLES;
+ }
+}
+
+s16 audioin_is_stereo = 0;
+s16 noisegate=0;
+#ifdef DEBUG
+//#define NOISETEST
+#endif
+#ifdef NOISETEST
+float noisetestl=0, noisetestr=0,noisetest=0;
+#endif
+void PreProcessAudioIn(u32* audioin) {
+ int newpeak = 0, newpeakr=0;
+#ifdef NOISETEST
+ int newpeakl=0;
+#endif
+ static float dcl, dcr;
+ int ng=mini(256, noisegate);
+ // dc remover from audio in, and peak detector while we're there.
+ for (int i = 0; i < BLOCK_SAMPLES; ++i) {
+ u32 inp = audioin[i];
+ STEREOUNPACK(inp);
+ dcl += (inpl - dcl) * 0.0001f;
+ dcr += (inpr - dcr) * 0.0001f;
+ inpl -= dcl;
+ inpr -= dcr;
+ newpeakr = maxi(newpeakr, abs(inpr));
+#ifdef NOISETEST
+ newpeakl = maxi(newpeakl, abs(inpl));
+#endif
+ if (!audioin_is_stereo)
+ inpr = inpl;
+ newpeak = maxi(newpeak, abs(inpl + inpr));
+ inpl=(inpl*ng) >> 8;
+ inpr=(inpr*ng) >> 8;
+
+ audioin[i] = STEREOPACK(inpl, inpr);
+ }
+ if (newpeak > 400)
+ noisegate=1000;
+ else
+ if (noisegate > 0)
+ noisegate--;
+
+ if (newpeakr > 300)
+ audioin_is_stereo = 1000;
+ else
+ if (audioin_is_stereo > 0)
+ audioin_is_stereo--;
+
+#ifdef NOISETEST
+ if(newpeakl>noisetestl) noisetestl=newpeakl; else noisetestl+=(newpeakl-noisetestl)*0.01f;
+ if(newpeakr>noisetestr) noisetestr=newpeakr; else noisetestr+=(newpeakr-noisetestr)*0.01f;
+ if(newpeak>noisetest) noisetest=newpeak; else noisetest+=(newpeak-noisetest)*0.01f;
+#endif
+ int audiorec_gain = (int)(recgain_smooth.y2)/2;
+
+
+ newpeak = SATURATE16((newpeak * audiorec_gain) >> 14);
+ audioin_peak = maxi((audioin_peak * 220) >> 8, newpeak);
+ if (audioin_peak > audioin_hold || audioin_holdtime++ > 500) {
+ audioin_hold = audioin_peak;
+ audioin_holdtime = 0;
+ }
+}
+static s16 scopex = 0;
+
+void usb_midi_update(void);
+void serial_midi_update(void);
+
+u8 midi_last_pitch[8];
+u8 midi_first_vol[8];
+u8 midi_last_at[8];
+u8 midi_last_pos[8];
+u8 midi_last_pressure[8];
+u8 midi_send_chan;
+u8 midi_desired_note[8];
+void kick_midi_send(void);
+bool serial_midi_ready(void);
+
+void midi_send_update(void) {
+ if (!serial_midi_ready())
+ return;
+ for (int i = 0; i < 8; ++i) {
+ Finger* synthf = touch_synth_getlatest(midi_send_chan);
+ Finger* prevsynthf = touch_synth_getprev(midi_send_chan);
+
+ bool pressurestable = abs(prevsynthf->pressure - synthf->pressure) < 100;
+ bool posstable = abs(prevsynthf->pos - synthf->pos) < 32;
+ bool pressure_significant = synthf->pressure > 200;
+
+ int desired_pitch = midi_desired_note[midi_send_chan];
+ int desired_vol = clampi((synthf->pressure-100)/48, 0, 127);
+ //int prev_vol = clampi((prevsynthf->pressure-100) / 48, 0, 127);
+ u8 desired_pos = clampi(127 - (synthf->pos/13-16), 0, 127);
+ if (desired_pitch == 0)
+ desired_vol = 0;
+ if (arpmode >= 0 && !(arpbits & (1 << midi_send_chan))) {
+ desired_vol = 0;
+ }
+ if (desired_vol <= 0)
+ desired_pitch = 0;
+ bool sent = false;
+ u8 cur_pitch = midi_last_pitch[midi_send_chan];
+
+ if (desired_pitch != cur_pitch && (desired_pitch==0 || (posstable && pressurestable))) {
+ // send note up
+ if (cur_pitch != 0) {
+ //if (midi_send_chan == 0) printf("note up because %d vs %d\n", desired_pitch, cur_pitch);
+ if (!send_midimsg(0x80, cur_pitch, 0)) break;
+ midi_last_pitch[midi_send_chan] = 0;
+ midi_last_at[midi_send_chan] = 0;
+ sent=true;
+ }
+ // send new note down
+ if (desired_pitch != 0) {
+ //if (midi_send_chan == 0) printf("note down because %d vs %d %d\n", desired_pitch, cur_pitch, desired_vol);
+ if (!send_midimsg(0x90, desired_pitch, desired_vol)) break;
+ midi_last_pitch[midi_send_chan] = desired_pitch;
+ midi_first_vol[midi_send_chan] = desired_vol;
+ midi_last_at[midi_send_chan] = 0;
+ sent=true;
+ }
+ }
+
+ u8 cur_at = midi_last_at[midi_send_chan];
+ int desired_at = desired_vol - midi_first_vol[midi_send_chan];
+ if (desired_at < 0) desired_at = 0;
+ if (abs(desired_at -cur_at) > 4) {
+ if (!send_midimsg(0xa0, cur_pitch, desired_at)) break;
+ midi_last_at[midi_send_chan] = desired_at;
+ sent=true;
+ }
+
+ u8 cur_pos = midi_last_pos[midi_send_chan];
+ if (abs(desired_pos - cur_pos) > 1 && pressure_significant && pressurestable) {
+ if (!send_midimsg(0xb0, 32 + midi_send_chan, desired_pos)) break;
+ midi_last_pos[midi_send_chan] = desired_pos;
+ sent=true;
+ }
+ u8 cur_pressure = midi_last_pressure[midi_send_chan];
+ if (abs(desired_vol - cur_pressure) > 1) {
+ if (!send_midimsg(0xb0, 40 + midi_send_chan, desired_vol)) break;
+ midi_last_pressure[midi_send_chan] = desired_vol;
+ sent=true;
+ }
+
+ midi_send_chan = (midi_send_chan + 1) & 7;
+ if (sent) {
+ kick_midi_send();
+ break;
+ }
+ }
+}
+
+int audiotime = 0;
+void DoAudio(u32 *dst, u32 *audioin) {
+ audiotime += BLOCK_SAMPLES;
+ tc_start(&_tc_audio);
+ memset(dst, 0, 4 * BLOCK_SAMPLES);
+ PreProcessAudioIn(audioin); // dc remover; peak detector
+// enable_audio = EA_PASSTHRU;
+ if (enable_audio <= 0) {
+ if (enable_audio == EA_PASSTHRU) {
+ // memcpy(dst, audioin, 4 * BLOCK_SAMPLES);
+ for (int i=0;i<BLOCK_SAMPLES;++i) {
+ float t=(i-BLOCK_SAMPLES/2)*(2.f/BLOCK_SAMPLES);
+ if (t<0) t=-t;
+ t=EvalSin(t,0);
+ s16 ss=t*32767.f;
+ t=(((i*2)&(BLOCK_SAMPLES-1))-BLOCK_SAMPLES/2)*(2.f/BLOCK_SAMPLES);
+ if (t<0) t=-t;
+ t=EvalSin(t,0);
+ s16 tt=t*32767.f;
+ dst[i]=STEREOPACK(tt,ss);
+ }
+ }
+ return;
+ }
+
+ if (enable_audio > EA_PLAY) {
+ DoRecordModeAudio(dst, audioin);
+ return;
+ }
+ int ainlvl = param_eval_int(P_MIXINPUT, any_rnd, env16, pressure16);
+ int audiorec_gain_target = ainlvl; // we want to update recgain_smooth as it is used to maintain the pretty audio gain history
+ knobsmooth_update_knob(&recgain_smooth, audiorec_gain_target, 65536.f);
+
+ //////////////////////////////////////////////////////////
+ // PLAYMODE
+
+ CopyPresetToRam(false);
+ // a few midi messages per tick. WCGW
+ if (1) {
+ midi_send_update();
+#ifndef EMU
+ usb_midi_update();
+ serial_midi_update();
+#endif
+ for (int i = 0; i < 2; ++i) if (!processusbmidi())
+ break;
+ }
+ // do the clock first so we can update the sequencer step etc
+ bool gotclock = update_clock();
+ static u8 whichhalf = 0;
+ for (int fi = whichhalf; fi < whichhalf + 4; ++fi) {
+ finger_synth_update(fi);
+ if (fi == 7) {
+
+ if (total_ui_pressure<=0 && prev_total_ui_pressure <= 0 && prev_prev_total_ui_pressure > 0 && recording && playmode != PLAYING) {
+ // you've released your fingers, you're recording in step mode - let's advance!
+ set_cur_step(cur_step + 1, false);
+ }
+ prev_prev_total_ui_pressure = prev_total_ui_pressure;
+ prev_total_ui_pressure = total_ui_pressure;
+ // rather than incrementing, we let finger_frame_synth shadow the ui. that way we get the full variability of the
+ // ui input (due to it tikcing slowly), but we dont accidentally read ahead
+ finger_frame_synth = finger_frame_ui;
+ //(finger_frame_synth + 1) & 7;
+ }
+ }
+ whichhalf ^= 4;
+
+ prevsynthfingerdown = synthfingerdown;
+ synthfingerdown = 0;
+ prevprevsynthfingerdown_nogatelen = prevsynthfingerdown_nogatelen;
+ prevsynthfingerdown_nogatelen = synthfingerdown_nogatelen;
+ synthfingerdown_nogatelen = synthfingerdown_nogatelen_internal;
+ for (int fi = 0; fi < 8; ++fi) {
+ Finger* synthf = touch_synth_getlatest(fi);
+ int thresh = (prevsynthfingerdown & (1 << fi)) ? -50 : 1;
+ if (synthf->pressure > thresh) {
+ synthfingerdown |= 1 << fi;
+ }
+ }
+ synthfingertrigger = (synthfingerdown & ~prevsynthfingerdown);
+ // needs finger_down to be set
+
+ bool latchon = ((rampreset.flags & FLAGS_LATCH));
+
+ if (!isplaying() && !read_from_seq && !latchon && synthfingerdown_nogatelen != 0 && prevsynthfingerdown_nogatelen == 0) {
+ // they have put their finger down after no arp playing, trigger it immediately!
+ arp_reset_partial();
+ /* -- this caused missed clock steps! what!
+ if (!external_clock_enable) {
+ bpm_clock_phase = 0;
+ ticks_since_clock = 0;
+ gotclock = true;
+ }
+ */
+ seq_reset(); // so that gate length works
+ }
+ /* this causes random restarts when jamming, I dont like it
+ else if (rampreset.arpon && arp_rhythm.did_a_retrig && !arp_rhythm.supress && synthfingerdown_nogatelen && !(arpbits & synthfingerdown_nogatelen)) {
+ // oh no! suddenly the arp bits dont overlap with the fingers. maybe the sequencer moved on. do a partial reset of the arp
+ if (!isarpmode8step(arpmode)) {
+ arp_reset_partial();
+ gotclock = true;
+ }
+ }
+ */
+
+ update_arp(gotclock);
+ update_params(synthfingertrigger, synthfingerdown);
+ int seqdivi = param_eval_int(P_SEQDIV, any_rnd, env16, pressure16);
+ seqdiv = (seqdivi==DIVISIONS_MAX) ? -1 : divisions[ clampi(seqdivi, 0, DIVISIONS_MAX-1) ]-1;
+
+ cur_sample1 = param_eval_int(P_SAMPLE, any_rnd, env16, pressure16);
+ cur_pattern = param_eval_int(P_SEQPAT, any_rnd, env16, pressure16);
+ step_offset = param_eval_int(P_SEQSTEP, any_rnd, env16, pressure16);
+ check_curstep();
+ CopySampleToRam(false);
+ CopyPatternToRam(false);
+
+ //static int fr = 0;
+ //DebugLog("%02x - %d - frame synth %d\n", synthfingerdown, touch_synth_getlatest(7)->pressure, finger_frame_synth);
+ //if (synthfingertrigger)
+ // DebugLog("%d %2x\r\n", fr,synthfingertrigger);
+ //fr++;
+
+
+
+ // decide on pitches & run the dry synth for the 8 fingers!
+ int maxpressure = 0;
+ int pitchhi = 0;
+ int maxvol = 0;
+ bool gotlow = false, gothi = false;
+ trigout = false;
+
+
+ int cvpitch = (int)(adc_smooth[6].y2 * (512.f * 12.f)); // pitch cv input
+ int cvquant = param_eval_int(P_CV_QUANT, any_rnd, env16, pressure16);
+ if (cvquant) {
+ cvpitch = (cvpitch + 256) & (~511);
+ }
+ for (int fi = 0; fi < 8; ++fi) {
+ Finger* synthf = touch_synth_getlatest(fi);
+ float vol = (synthf->pressure) * 1.f / 2048.f ; // sensitivity
+ {
+ // pitch table is (64*8) steps per semitone, ie 512 per semitone
+ int octave = arpoctave + param_eval_finger(P_OCT, fi, synthf);
+ // so heres my maths, this comes out at 435
+ // 8887421 comes from the value of pitch when playing a C
+ // the pitch of middle c in plinky as written is (4.0/(65536.0*65536.0/8887421.0/31250.0f))
+ // which is 1.0114729530400526 too low
+ // which is 0.19749290999 semitones flat
+ // *512 = 101. so I need to add 101 to pitch_base
+
+#define PITCH_BASE ((32768-(12<<9)) + (1*512) + 101)
+ int pitchbase =12*((octave<<9) + (param_eval_finger(P_PITCH, fi, synthf)>>7)); // +- one octave
+ int root = param_eval_finger(P_ROTATE, fi, synthf);
+ int interval = (param_eval_finger(P_INTERVAL, fi, synthf) * 12) >> 7;
+ int totpitch = 0;
+ if (midi_pitch_override & (1 << fi)) {
+ Finger* f = fingers_synth_sorted[fi] + 2;
+ int midinote = ((midi_notes[fi]-12*2) << 9) + midi_chan_pitchbend[midi_channels[fi]]/8;
+ for (int i = 0; i < 4; ++i) {
+ int pitch = pitchbase + ((i & 1) ? interval : 0) + (i-2)*64 + midinote; // (lookupscale(scale, ystep + root)) + +((fine * microtune) >> 14);
+ totpitch += pitch;
+ voices[fi].theosc[i].pitch = pitch;
+ voices[fi].theosc[i].targetdphase = maxi(65536, (int)(table_interp(pitches, pitch + PITCH_BASE) * (65536.f * 128.f)));
+ ++f;
+ }
+ }
+ else {
+ u32 scale = param_eval_finger(P_SCALE, fi, synthf);
+ if (scale >= S_LAST) scale = 0;
+ if (cvquant != CVQ_SCALE)
+ pitchbase += cvpitch;
+ else {
+ // remap the 12 semitones input to the scale steps, evenly, with a slight offset so white keys map to major scale etc
+ int steps = ((cvpitch / 512) * scaletab[scale][0] + 1) / 12;
+ root += steps;
+ }
+ int stride_semitones = maxi(0,param_eval_finger(P_STRIDE, fi, synthf));
+ root += stride(scale, stride_semitones, fi);
+ int microtune = 64 + param_eval_finger(P_MICROTUNE, fi, synthf); // really, micro-tune amount
+
+ Finger* f = fingers_synth_sorted[fi] + 2;
+ for (int i = 0; i < 4; ++i) {
+ // if (ramsample.samplelen)
+ // f = synthf; // XXX FORCE LATEST
+ int ystep = 7 - (f->pos >> 8);
+ int fine = 128 - (f->pos & 255);
+ int pitch = pitchbase + (lookupscale(scale, ystep + root)) + ((i & 1) ? interval : 0) + ((fine * microtune) >> 14);
+ totpitch += pitch;
+ voices[fi].theosc[i].pitch = pitch;
+ voices[fi].theosc[i].targetdphase = maxi(65536, (int)(table_interp(pitches, pitch + PITCH_BASE) * (65536.f * 128.f)));
+ ++f;
+ }
+ }
+#ifdef DEBUG
+ if (fi == 0)
+#endif
+ //if (fi<6)
+ RunVoice(&voices[fi], fi, vol, dst);
+ if (vol > 0.001f) {
+ if (!gotlow) {
+ SetOutputCVPitchLo(totpitch, true);
+ gotlow = true;
+ }
+ if (arpmode < 0 || (arpbits & (1<<fi))) {
+ pitchhi = totpitch;
+ gothi = true;
+ }
+ midi_desired_note[fi] = clampi((totpitch + 1024) / 2048 + 24, 0, 127);
+ }
+ maxvol = maxi(maxvol, (int)(vol * 65536.f));
+ }
+ if (synthf->pressure > maxpressure)
+ maxpressure = synthf->pressure;
+ }
+ SetOutputCVPressure(maxpressure * 8);
+ SetOutputCVTrigger(trigout ? 65535 : 0);
+ if (gothi)
+ SetOutputCVPitchHi(pitchhi, true);
+ SetOutputCVGate(maxvol);
+ AdvanceCVOut();
+ tc_stop(&_tc_audio);
+
+ if (ramsample.samplelen > 0) {
+ // decide on a priority for 8 voices
+ int gprio[8];
+ u32 sampleaddr = ((cur_sample1 - 1) & 7) * MAX_SAMPLE_LEN;
+
+ for (int i = 0; i < 8; ++i) {
+ GrainPair* g = voices[i].thegrains;
+ int glen0 = ((abs(g[0].dpos24) * (BLOCK_SAMPLES/2) + g[0].fpos24/2 + 1) >> 23) + 2; // +2 for interpolation
+ int glen1 = ((abs(g[1].dpos24) * (BLOCK_SAMPLES/2) + g[1].fpos24/2 + 1) >> 23) + 2; // +2 for interpolation
+
+ // TODO - if pos at end of next fetch will be out of bounds, negate dpos24 and grate_ratio so we ping pong back for the rest of the grain!
+ int glen = maxi(glen0, glen1);
+ glen = clampi(glen, 0, AVG_GRAINBUF_SAMPLE_SIZE*2);
+ g[0].bufadjust = (g[0].dpos24< 0) ? maxi(glen-2,0): 0;
+ g[1].bufadjust = (g[1].dpos24< 0) ? maxi(glen-2, 0) : 0;
+ grainpos[i * 4 + 0] = (int)(g[0].pos[0]) - g[0].bufadjust + sampleaddr ;
+ grainpos[i * 4 + 1] = (int)(g[0].pos[1]) - g[0].bufadjust + sampleaddr;
+ grainpos[i * 4 + 2] = (int)(g[1].pos[0]) - g[1].bufadjust + sampleaddr;
+ grainpos[i * 4 + 3] = (int)(g[1].pos[1]) - g[1].bufadjust + sampleaddr;
+ glen += 2; // 2 extra 'samples' for the SPI header
+ // if (i==0) EmuDebugLog("%d %d %d %d\n", grainpos[0], grainpos[1], grainpos[2], grainpos[3]);
+ gprio[i]=((int)(voices[i].vol * 65535.f) << 12) + i + (glen << 3);
+ }
+ sort8(gprio, gprio);
+ u8 lengths[8];
+ int pos = 0,i;
+#if defined DEBUG
+#define MAX_SAMPLE_VOICES 1
+#else
+#define MAX_SAMPLE_VOICES 6
+#endif
+ for (i = 7; i >= 0; --i) {
+ int prio = gprio[i];
+ int fi = prio & 7;
+ int len = (prio >> 3) & 255;
+ if (i < 8 - MAX_SAMPLE_VOICES)
+ len = 0; // we only budget for MAX_SPI_STATE transfers. so after that, len goes to 0. also helps CPU load.
+ else if (voices[fi].vol <= 0.01f && !(synthfingerdown & (1 << fi)))
+ len = 0; // if your finger is up and the volume is 0, we can just skip this one.
+ lengths[fi] = (pos + len * 4 > GRAINBUF_BUDGET) ? 0 : len;
+ pos += len*4;
+ }
+ // cumulative sum
+ pos = 0;
+ for (int i = 0; i < 32; ++i) {
+ pos += lengths[i / 4];
+ grainbufend[i] = pos;
+ }
+ if (spistate == 0)
+ spi_readgrain_dma(0); // kick off the dma for next time
+ else {
+ //DebugLog("?"); // spidebug
+ }
+ } else {
+ // just update dac when not in sampler mode
+ if (spistate == 0)
+ spi_update_dac(0);
+ }
+ tc_start(&_tc_fx);
+ // /// ////////////////////////////////////////////////////////////////////////
+ // half rate fx
+ static u16 delaypos = 0;
+ static u32 wetlr;
+ const float k_target_fb = param_eval_float(P_DLFB, any_rnd, env16,pressure16) * (0.35f); // 3/4
+ static float k_fb=0.f;
+ //const int k_delay2out = param_eval_int(P_MIXDL, any_rnd, env16,pressure16) >> 8; // 1
+ int k_target_delaytime = param_eval_int(P_DLTIME, any_rnd, env16, pressure16);
+ if (k_target_delaytime > 0) {
+ // free timing
+ k_target_delaytime = (((k_target_delaytime+255) >> 8) * k_target_delaytime) >> 8;
+ k_target_delaytime = (k_target_delaytime * (DLMASK-64))>>16;
+ }
+ else {
+ k_target_delaytime = divisions[clampi((-k_target_delaytime*13)>>16,0,12)]; // results in a number 1-32
+ // figure out how samples we can have, max, in a beat synced scenario
+ int max_delay = (32000 * 600 * 4) / (maxi(150, bpm10x));
+ while (max_delay > DLMASK - 64)
+ max_delay >>= 1;
+ k_target_delaytime = (max_delay * k_target_delaytime) >> 5;
+
+ }
+ k_target_delaytime = clampi(k_target_delaytime, BLOCK_SAMPLES, DLMASK - 64) << 12;
+ int k_delaysend=(param_eval_int(P_DLSEND, any_rnd, env16, pressure16)>>9);
+
+ static int wobpos=0;
+ static int dwobpos=0;
+ static int wobcount=0;
+ if (wobcount<=0) {
+ const int wobamount =param_eval_int(P_DLWOB, any_rnd, env16,pressure16); // 1/2
+ int newwobtarget=((rand()&8191)*wobamount)>>8;
+ if (newwobtarget>k_target_delaytime/2)
+ newwobtarget=k_target_delaytime/2;
+ wobcount=((rand()&8191)+8192)&(~(BLOCK_SAMPLES-1));
+ dwobpos=(newwobtarget-wobpos+wobcount/2)/wobcount;
+ }
+ wobcount-=BLOCK_SAMPLES;
+
+
+
+ /*
+ for (int i=0;i<64;++i) {
+ s16 input = ((s16*)spibigrx[1])[i+2];
+ input>>=3;
+ dst[i]=STEREOADDSAT(STEREOPACK(input,input), dst[i]);
+ }
+ if (spistate==0)
+ spi_read256_dma(0,0);
+ */
+ ///////////////// ok lets do hpf earlier!
+ static float peak = 0.f;
+ peak *= 0.99f;
+ static float power = 0.f;
+ // at sample rate, lpf k 0.002 takes 10ms to go to half; .0006 takes 40ms; k=.0002 takes 100ms;
+// at buffer rate, k=0.13 goes to half in 10ms; 0.013 goes to half in 100ms; 0.005 is 280ms
+
+float g = param_eval_float(P_MIXHPF, any_rnd, env16, pressure16); // tanf(3.141592f * 8000.f / 32000.f); // highpass constant // TODO PARAM 0 -1
+g *= g;
+g *= g;
+g += (10.f / 32000.f);
+//const float k = 2.f -2.f * param_eval_float(P_HPF_RESO, any_rnd, env16, pressure16); // 2.f - 2.f * res;
+const static float k = 2.f;
+float a1 = 1.f / (1.f + g * (g + k));
+float a2 = g * a1;
+
+
+#ifdef DEBUG
+#define ENABLE_HPF 0
+#else
+#define ENABLE_HPF 1
+#endif
+
+if (ENABLE_HPF) for (int i = 0; i < BLOCK_SAMPLES; ++i) {
+ u32 input = STEREOSIGMOID(dst[i]);
+ STEREOUNPACK(input);
+ static float ic1l, ic2l, ic1r, ic2r;
+ float l = inputl, r = inputr;
+ float v1l = a1 * ic1l + a2 * (l - ic2l);
+ float v2l = ic2l + g * v1l;
+ ic1l = v1l + v1l - ic1l;
+ ic2l = v2l + v2l - ic2l;
+ l -= k * v1l + v2l;
+
+ float v1r = a1 * ic1r + a2 * (r - ic2r);
+ float v2r = ic2r + g * v1r;
+ ic1r = v1r + v1r - ic1r;
+ ic2r = v2r + v2r - ic2r;
+ r -= k * v1r + v2r;
+
+ power *= 0.999f;
+ power += l * l + r * r;
+ peak = maxf(peak, l + r);
+
+ s16 li = (s16)SATURATE16(l);
+ s16 ri = (s16)SATURATE16(r);
+ dst[i] = STEREOPACK(li, ri);
+ }
+
+ u32 *src = (u32*) dst;
+
+ float f=1.f-clampf(param_eval_float(P_RVTIME, any_rnd, env16, pressure16),0.f,1.f);
+ f*=f; f*=f;
+ k_reverb_fade=(int)(250*(1.f-f));
+ k_reverb_shim=(param_eval_int(P_RVSHIM, any_rnd, env16, pressure16)>>9);
+ k_reverb_wob=(param_eval_float(P_RVWOB, any_rnd, env16, pressure16));
+ //k_reverb_color=(param_eval_float(P_RVCOLOR, any_rnd, env16, pressure16));
+ k_reverbsend=(param_eval_int(P_RVSEND, any_rnd, env16, pressure16));
+
+ int synthlvl_ = param_eval_int(P_MIXSYNTH, any_rnd, env16, pressure16);
+ int synthwidth = param_eval_int(P_MIX_WIDTH, any_rnd, env16, pressure16);
+ int asynthwidth = abs(synthwidth);
+ int synthlvl_mid;
+ int synthlvl_side;
+ if (asynthwidth <= 32768) { // make more narrow
+ synthlvl_mid = synthlvl_;
+ synthlvl_side = (synthwidth * synthlvl_) >> 15;
+ }
+ else {
+ synthlvl_side = (synthwidth < 0) ? -synthlvl_ : synthlvl_;
+ asynthwidth = 65536 - asynthwidth;
+ synthlvl_mid = (asynthwidth * synthlvl_) >> 15;
+ }
+
+ int ainwetdry= param_eval_int(P_MIXINWETDRY, any_rnd, env16, pressure16);
+ int wetdry = param_eval_int(P_MIXWETDRY, any_rnd, env16, pressure16);
+ int wetlvl = 65536 - maxi(-wetdry, 0);
+ int drylvl = 65536 - maxi(wetdry, 0);
+
+ int ainwetlvl = 65536 - maxi(-ainwetdry, 0);
+ int aindrylvl = 65536 - maxi(ainwetdry, 0);
+
+
+ //int ainlvl = param_eval_int(P_MIXINPUT, any_rnd, env16, pressure16);
+ ainwetlvl = ((ainwetlvl >>4) * (ainlvl>>4)) >> 8;
+
+ ainlvl = ((ainlvl >> 4) * (drylvl >> 4)) >> 8; // prescale by dry level
+ ainlvl = ((ainlvl >> 4) * (aindrylvl >>4)) >> 8; // prescale by fx dry level
+
+ int delayratio=param_eval_int(P_DLRATIO,any_rnd, env16, pressure16)>>8;
+ static int delaytime = BLOCK_SAMPLES<<12;
+ int scopescale = (65536 * 24) / maxi(16384, (int)peak);
+ //int scopetrig = (65536 / 2) / (1 + scopex);
+
+#ifdef DEBUG
+#define ENABLE_FX 0
+#else
+#define ENABLE_FX 1
+#endif
+ if (ENABLE_FX) for (int i = 0; i < BLOCK_SAMPLES / 2; ++i) {
+ //float lfopos1 = lfo_next(&delaylfo1) * wob;
+ //int wobpos1 = FLOAT2FIXED(lfopos1, 12 + 6);
+ int targetdt=k_target_delaytime+2048-(int)wobpos;
+ wobpos+=dwobpos;
+ delaytime+=(targetdt-delaytime)>>10;
+ s16 delayreturnl = LINEARINTERPDL(delaybuf, delaypos, delaytime);
+ s16 delayreturnr = LINEARINTERPDL(delaybuf, delaypos, ((delaytime>>4)*delayratio)>>4);
+ // soft clipper due to drive; reduces range to half also giving headroom on tape & output
+ u32 drylr0 = STEREOSIGMOID(src[0]);
+ u32 drylr1 = STEREOSIGMOID(src[1]);
+
+ /////////////////////////////////// COMPRESSOR
+ u32 drylr01 = STEREOADDAVERAGE(drylr0, drylr1); // this is gonna have absolute max +-32768
+ STEREOUNPACK(drylr01);
+ static float peaktrack=1.f;
+ float peaky = (float)((1.f/4096.f/65536.f)*(maxi(maxi(drylr01l, -drylr01l), maxi(drylr01r, -drylr01r)) * synthlvl_));
+ if (peaky > peaktrack) peaktrack += (peaky - peaktrack) *0.01f;
+ else {
+ peaktrack += (peaky - peaktrack) *0.0002f;
+ peaktrack = maxf(peaktrack, 1.f);
+ }
+ float recip = (2.5f / peaktrack);
+ int lvl_mid = synthlvl_mid * recip;
+ int lvl_side = synthlvl_side * recip;
+#ifdef EMU
+ m_compressor = synthlvl_ * recip /65536.f;
+#endif
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ drylr0 = MIDSIDESCALE(drylr0, lvl_mid, lvl_side);
+ drylr1 = MIDSIDESCALE(drylr1, lvl_mid, lvl_side);
+
+ MONITORPEAK(&m_dry, drylr0);
+ MONITORPEAK(&m_dry, drylr1);
+
+ u32 ain0 = audioin[i * 2 + 0];
+ u32 ain1 = audioin[i * 2 + 1];
+
+ u32 audioinwet = STEREOSCALE(STEREOADDAVERAGE(ain0, ain1), ainwetlvl);
+ u32 dry2wetlr = STEREOADDAVERAGE(drylr0, drylr1);
+ dry2wetlr = STEREOADDSAT(dry2wetlr,audioinwet);
+
+ MONITORPEAK(&m_dry2wet, dry2wetlr);
+
+
+ int delaysend = (int)((delayreturnl+(delayreturnr>>1)) * k_fb);
+ delaysend += (((s16) (dry2wetlr) + (s16) (dry2wetlr >> 16)) * k_delaysend ) >> 8;
+ static float lpf=0.f,dc=0.f;
+ lpf+=(delaysend-lpf)*0.75f;
+ dc+=(lpf-dc)*0.05f;
+ delaysend=(int)(lpf-dc);
+ //- compressor in feedback of delay
+ delaysend=MONOSIGMOID(delaysend);
+
+ MONITORPEAK(&m_delaysend, delaysend);
+
+
+ /*int peak=abs(delaysend);
+ if (unlikely(peak>32000)) {
+ // adjust feedback down as by 32000/peak
+ k_fb*=16000.f/peak;
+ } else */
+ {
+ // adjust feedback up again
+ k_fb+=(k_target_fb-k_fb)*0.001f;
+ }
+ delaypos &= DLMASK;
+ delaybuf[delaypos] = delaysend;
+ delaypos++;
+ //s16 l=(delayreturnl*k_delay2out)>>9;
+ //s16 r=(delayreturnr*k_delay2out)>>9;
+ //if (i & 1)
+ {
+ s16 li = dry2wetlr;
+ s16 ri = dry2wetlr>>16;
+ static s16 prevli = 0;
+ static s16 prevprevli = 0;
+ static u16 bestedge = 0;
+ static s16 antiturningpointli = 0;
+ bool turningpoint = (prevli>prevprevli && prevli>li);
+ bool antiturningpoint = (prevli < prevprevli && prevli < li);
+ if (antiturningpoint)
+ antiturningpointli = prevli; // remember the last turning point at the bottom
+ if (turningpoint) { // we are at a peak!
+ int edgesize = prevli- antiturningpointli;
+ if (scopex >= 256 || (scopex<0 && edgesize>bestedge)) {
+ scopex = -256;
+ bestedge = edgesize;
+ }
+ }
+ prevprevli = prevli;
+ prevli = li;
+ if (scopex < 256 && scopex>=0) {
+ int x = scopex / 2;
+ if (!(scopex&1))
+ scope[x]= 0;
+ putscopepixel(x, (li * scopescale >> 16) + 16);
+ putscopepixel(x, (ri * scopescale >> 16) + 16);
+ }
+ scopex++;
+ if (scopex > 1024)
+ scopex = -256;
+ //scopex &= 4095;
+ }
+
+ u32 newwetlr = STEREOPACK(delayreturnl, delayreturnr);
+ MONITORPEAK(&m_delayreturn, newwetlr);
+
+ // reverb
+#ifndef DEBUG
+ if (1)
+ {
+ u32 reverbin=STEREOADDAVERAGE(newwetlr,dry2wetlr);
+ MONITORPEAK(&m_reverbin, reverbin);
+ u32 reverbout=Reverb2(reverbin, reverbbuf);
+ MONITORPEAK(&m_reverbout, reverbout);
+ newwetlr=STEREOADDSAT(newwetlr, reverbout);
+ MONITORPEAK(&m_fxout, newwetlr);
+
+ }
+#endif
+ // output upsample
+ newwetlr = STEREOSCALE(newwetlr, wetlvl);
+ u32 midwetlr = STEREOADDAVERAGE(newwetlr, wetlr);
+ wetlr = newwetlr;
+
+ u32 audioin0 = STEREOSIGMOID(STEREOSCALE(ain0, ainlvl)); // ainlvl already scaled by drylvl
+ u32 audioin1 = STEREOSIGMOID(STEREOSCALE(ain1, ainlvl));
+ MONITORPEAK(&m_audioin, audioin0);
+ MONITORPEAK(&m_audioin, audioin1);
+
+ src[0] = STEREOADDSAT(STEREOADDSAT(STEREOSCALE(drylr0,drylvl), audioin0), midwetlr);
+ src[1] = STEREOADDSAT(STEREOADDSAT(STEREOSCALE(drylr1,drylvl), audioin1), newwetlr);
+
+ MONITORPEAK(&m_output, src[0]);
+ MONITORPEAK(&m_output, src[1]);
+
+
+ src += 2;
+ }
+
+#ifdef EMU
+ powerout = power / (BLOCK_SAMPLES * 2.f * 32768.f * 32768.f);
+ gainhistoryrms[ghi] = lin2db(powerout+1.f/65536.f)*0.5f;
+ ghi = (ghi + 1) & 511;
+#endif
+ tc_stop(&_tc_fx);
+}
+
+/////////////////////////////////////////////////////////
+
+
+#ifdef EMU
+uint32_t emupixels[128*32];
+void OledFlipEmu(const u8 * vram) {
+ if (!vram)
+ return;
+ const u8* src = vram + 1;
+ for (int y = 0; y < 32; y += 8) {
+ for (int x = 0; x < 128; x++) {
+ u8 b = *src++;
+ for (int yy = 0; yy < 8; ++yy) {
+ u32 c = (b & 1) ? 0xffffffff : 0xff000000;
+ int y2 = y + yy;
+#ifdef ROTATE_OLED
+ //pixels[(y2 + (127-x) * 32)] = c; // rotated, pins at bottom
+ emupixels[((31 - y2) + x * 32)] = c; // rotated, pins at top
+#else
+ emupixels[(y2 * 128 + x)] = c;
+#endif
+ b >>= 1;
+ }
+ }
+ }
+}
+
+ int * EMSCRIPTEN_KEEPALIVE getemubitmap(void) {
+ return (int*)emupixels;
+}
+ uint8_t *EMSCRIPTEN_KEEPALIVE getemuleds() {
+ return (uint8_t*)led_ram;
+}
+
+#endif
+
+void EMSCRIPTEN_KEEPALIVE uitick(u32 *dst, const u32 *src, int half) {
+ tc_stop(&_tc_budget);
+ tc_start(&_tc_budget);
+
+ tc_start(&_tc_all);
+// if (half)
+ {
+ tc_start(&_tc_touch);
+ touch_update();
+ tc_stop(&_tc_touch);
+ }
+// else
+ {
+ tc_start(&_tc_led);
+ led_update();
+ tc_stop(&_tc_led);
+ }
+
+ // clear some scope pixels
+
+
+ // pass thru: memcpy(dst,src,64*4);
+
+ DoAudio((u32*)dst, (u32*)src);
+ /* triangle wave test
+ static u16 foo;
+ for (int i=0;i<BLOCK_SAMPLES;++i) {
+ s16 s=(foo<32768) ? foo*2-32768 : 65536+32767-foo*2;
+ foo+=256;
+
+ dst[i] = STEREOPACK(s,s);
+ }
+ */
+ /*
+ static int th=0;
+ th+=16;
+
+ HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, adcbuf[ADC_IN5]>>4);
+ HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2, DAC_ALIGN_12B_R, adcbuf[ADC_IN6]>>4);
+
+ __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (th>>0)&255);
+ __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, (th >> 1) & 255);
+*/
+
+
+ tc_stop(&_tc_all);
+
+
+
+}
+
+void bootswish(void);
+void cv_calib(void);
+
+
+void reflash(void) {
+ clear();
+ drawstr(0, 0, F_16_BOLD, "Re-flash");
+ drawstr(0, 16, F_16, "over USB DFU");
+ oled_flip(vrambuf);
+ HAL_Delay(100);
+ jumptobootloader();
+}
+
+void set_test_mux(int c) {
+#ifndef EMU
+ GPIOD->ODR &= ~(GPIO_PIN_1 | GPIO_PIN_3 | GPIO_PIN_4); // rgb led off
+ if (c & 1)
+ GPIOD->ODR |= GPIO_PIN_3;
+ if (c & 2)
+ GPIOD->ODR |= GPIO_PIN_1;
+ if (c & 4)
+ GPIOD->ODR |= GPIO_PIN_4;
+ if (c & 8)
+ GPIOA->ODR |= GPIO_PIN_8;
+ else
+ GPIOA->ODR &= ~GPIO_PIN_8;
+#endif
+}
+void set_test_rgb(int c) {
+ set_test_mux(c^7);
+}
+
+short *getrxbuf(void);
+
+#define REVERB_BUF 0x10000000
+#define DELAY_BUF 0x20008000
+
+void check_bootloader_flash(void) {
+ int count=0;
+ uint32_t *rb32=(uint32_t*)REVERB_BUF;
+ uint32_t magic=rb32[64];
+ char *rb=(char*)REVERB_BUF;
+ for (;count<64;++count) if (rb[count]!=1) break;
+ DebugLog("bootloader left %d ones for us magic is %08x\r\n", count, magic);
+ const uint32_t *app_base = (const uint32_t *)DELAY_BUF;
+
+ if (count!=64/4 || magic!=0xa738ea75) {
+ return;
+ }
+ clear();
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%08x %d", magic, count);
+ drawstr(0, 0, F_16, buf);
+ snprintf(buf, sizeof(buf), "%08x %08x", app_base[0], app_base[1]);
+ drawstr(0, 16, F_12, buf);
+ oled_flip(vrambuf);
+
+ rb32[64]++; // clear the magic
+
+ DebugLog("bootloader app base is %08x %08x\r\n", app_base[0], app_base[1]);
+
+ /*
+ * We refuse to program the first word of the app until the upload is marked
+ * complete by the host. So if it's not 0xffffffff, we should try booting it.
+ */
+ if (app_base[0] == 0xffffffff || app_base[0]== 0) {
+ HAL_Delay(10000);
+ return;
+ }
+
+ // first word is stack base - needs to be in RAM region and word-aligned
+ if ((app_base[0] & 0xff000003) != 0x20000000) {
+ HAL_Delay(10000);
+ return;
+ }
+
+ /*
+ * The second word of the app is the entrypoint; it must point within the
+ * flash area (or we have a bad flash).
+ */
+ if (app_base[1] < 0x08000000 || app_base[1]>=0x08010000) {
+ HAL_Delay(10000);
+ return;
+ }
+ DebugLog("FLASHING BOOTLOADER! DO NOT RESET\r\n");
+ clear();
+ drawstr(0,0,F_16_BOLD,"FLASHING\nBOOTLOADER");
+ oled_flip(vrambuf);
+ HAL_FLASH_Unlock();
+ FLASH_EraseInitTypeDef EraseInitStruct;
+ EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
+ EraseInitStruct.Banks = FLASH_BANK_1;
+ EraseInitStruct.Page = 0;
+ EraseInitStruct.NbPages = 65536/2048;
+ uint32_t SECTORError = 0;
+ if (HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError) != HAL_OK) {
+ DebugLog("BOOTLOADER flash erase error %d\r\n", SECTORError);
+ clear();
+ drawstr(0,0,F_16_BOLD,"BOOTLOADER\nERASE ERROR");
+ oled_flip(vrambuf);
+ HAL_Delay(10000);
+ return ;
+ }
+ DebugLog("BOOTLOADER flash erased ok!\r\n");
+
+ __HAL_FLASH_DATA_CACHE_DISABLE();
+ __HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
+ __HAL_FLASH_DATA_CACHE_RESET();
+ __HAL_FLASH_INSTRUCTION_CACHE_RESET();
+ __HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
+ __HAL_FLASH_DATA_CACHE_ENABLE();
+ uint64_t* s = (uint64_t*)DELAY_BUF;
+ volatile uint64_t* d = (volatile uint64_t*)0x08000000;
+ u32 size_bytes=65536;
+ for (;size_bytes>0;size_bytes-=8) {
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (uint32_t)(size_t)(d++), *s++);
+ }
+ HAL_FLASH_Lock();
+ DebugLog("BOOTLOADER has been flashed!\r\n");
+ clear();
+ drawstr(0,0,F_16_BOLD,"BOOTLOADER\nFLASHED OK!");
+ oled_flip(vrambuf);
+ HAL_Delay(3000);
+
+}
+
+
+#undef ERROR
+#define ERROR(msg, ...) do { errorcount++; DebugLog("\r\n" msg "\r\n", __VA_ARGS__); } while (0)
+
+void test_jig(void) {
+ // pogo pin layout:
+ //GND DEBUG = GND / PA8 - 67
+ //GND GND
+ //MISO SPICLK = PD3 - 84 / PD1 - 82
+ //MOSI GND = PD4 - 85 / GND
+ // rgb led is hooked to PD1,PD3,PD4. configure it for output
+ // mux address hooked to LSB=PD3, PD1, PD4, PA8=MSB
+#ifndef EMU
+ GPIO_InitTypeDef GPIO_InitStruct = { 0 };
+ GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_3 | GPIO_PIN_4;
+ GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+ HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
+
+ // we also use debug as an output now!
+ GPIO_InitStruct.Pin = GPIO_PIN_8;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+ GPIOA->ODR &= ~GPIO_PIN_8;
+#endif
+ clear();
+ drawstr(0, 0, F_32_BOLD, "TEST JIG");
+ oled_flip(vrambuf);
+ HAL_Delay(100);
+ enable_audio=EA_PASSTHRU;
+ SetOutputCVTrigger(0);
+ SetOutputCVClk(0);
+ SetOutputCVGate(0);
+ SetOutputCVPressure(0);
+ int gndcalib[ADC_CHANS]={0};
+ int refcalib[ADC_CHANS]={0};
+ float pdac[2][2]={0};
+#define PITCH_1V_OUT (43000 - 8500)// about 8500 per volt; 43000 is zero ish.
+#define PITCH_4V_OUT (43000 - 8500 * 4)
+ static int const expected_mvolts[11][2]={
+ {0,0},// gnd
+ {2500,2500}, // 2.5 ref
+ {3274,3274}, // 3.3 supply
+ {4779,4779}, // 5v supply
+ {950,950}, // 1v from 12v supply
+ {1039,4230}, // pitch lo 1v/4v
+ {1039,4230}, // pitch hi 1v/4v
+ {0,4700}, // clock
+ {0,4700}, // trig,
+ {0,4700}, // gate,
+ {0,4700}, // pressure
+ };
+ static int const tol_mvolts[11]={
+ 100, // gnd
+ 10, // ref
+ 300, // 3.3
+ 500, // 5
+ 100, // 1v
+ 100,100,// pitch
+ 150,150,150,150, // outputs
+ };
+ const char * const names[11][2]={
+ {"gnd",0},
+ {"2.5v",0},
+ {"3.3v",0},
+ {"5v",0},
+ {"1v from 12v",0},
+ {"plo (1v)","plo (4v)"},
+ {"phi (1v)","phi (4v)"},
+ {"clk (0v)","clk (4.6v)"},
+ {"trig (0v)","trig (4.6v)"},
+ {"gate (0v)","gate (4.6v)"},
+ {"pressure (0v)","pressure (4.6v)"}
+ };
+ while (1) {
+ DebugLog("mux in: pitch gate x y a b | mux:\r\n");
+ int errorcount=0;
+ int rangeok=0,zerook=0;
+ gotclkin=0;
+#ifndef EMU
+ if (!update_accelerometer_raw()) {
+ drawstr(0, 0, F_32_BOLD, "BAD ACCEL");
+ oled_flip(vrambuf);
+ HAL_Delay(1000);
+ errorcount++;
+ }
+#endif
+ for (int iter=0;iter<4;++iter) {
+ SetOutputCVClk(0);
+ HAL_Delay(3);
+ SetOutputCVClk(65535);
+ HAL_Delay(3);
+ }
+ if (gotclkin!=4)
+ ERROR("expected clkin of 4, got %d", gotclkin);
+ for (int mux=0;mux<11;++mux ){
+ set_test_mux(mux);
+ int numlohi = (mux<5) ? 1 : 2;
+ for (int lohi=0;lohi<numlohi;++lohi) {
+ int data=lohi?49152:0;
+ int pitch=lohi? PITCH_4V_OUT : PITCH_1V_OUT;
+ SetOutputCVTrigger(data);
+ SetOutputCVClk(data);
+ SetOutputCVGate(data);
+ SetOutputCVPressure(data);
+ SetOutputCVPitchLo(pitch,0);
+ SetOutputCVPitchHi(pitch,0);
+
+ HAL_Delay(3);
+ int tot[ADC_CHANS] = {0};
+ clear();
+
+#define NUMITER 32
+ for (int iter = 0; iter < NUMITER; ++iter) {
+ HAL_Delay(2);
+ short *rx=getrxbuf();
+ for (int x=0;x<128;++x) {
+ putpixel(x,16+rx[x*2]/1024,1);
+ putpixel(x,16+rx[x*2+1]/1024,1);
+ }
+ for (int j = 0; j < ADC_SAMPLES; ++j)
+ for (int ch=0;ch<ADC_CHANS;++ch)
+ tot[ch] += adcbuf[j * ADC_CHANS + ch];
+ }
+ if (lohi)
+ invertrectangle(0,0,128,32);
+ oled_flip(vrambuf);
+ for (int ch=0;ch<ADC_CHANS;++ch)
+ tot[ch] /= ADC_SAMPLES * NUMITER;
+
+ DebugLog("-----\nmux = %d lohi = %d\n", mux, lohi);
+ for (int ch=0;ch<ADC_CHANS;++ch) {
+ DebugLog("adc ch reads %d\n",tot[ch]);
+ }
+ DebugLog("-----\n");
+ switch(mux) {
+ case 0:
+ for (int ch=0;ch<ADC_CHANS;++ch) {
+ gndcalib[ch]=tot[ch];
+ int expected = (ch==0) ? 43262 : (ch>=6) ? 31500 : 31035 ;
+ int error=abs(expected-tot[ch]);
+ if (error>2000)
+ ERROR("ADC Channel %d zero point is %d, expected %d", ch, tot[ch], expected);
+ else
+ zerook|=(1<<ch);
+ }
+ break;
+ case 1:
+ for (int ch=0;ch<ADC_CHANS;++ch) {
+ refcalib[ch]=tot[ch];
+ int range=gndcalib[ch]-refcalib[ch];
+ int expected=(ch==0) ? 21600 : (ch>=6) ? 0 : 14386;
+ int error=abs(expected-range);
+ if (error>2000)
+ ERROR("ADC Channel %d range is %d, expected %d", ch, range, expected);
+ else
+ rangeok|=(1<<ch);
+ }
+ break;
+ case 5:
+ case 6: {
+ int range0 = (refcalib[0] - gndcalib[0]);
+ if (range0 == 0) range0 = 1;
+ pdac[mux - 5][lohi] = (tot[0] - gndcalib[0]) * (2.5f / range0);
+ break;
+ }
+ }
+ DebugLog("%4d: ",mux);
+ for (int ch=0;ch<6;++ch) {
+ int range=(refcalib[ch]-gndcalib[ch]);
+ if (range==0) range=1;
+ float gain = 2500.f / range;
+ int mvolts=(tot[ch]-gndcalib[ch])*gain;
+ int exp_mvolts=expected_mvolts[mux][lohi];
+ int error=abs(exp_mvolts-mvolts);
+ int tol=tol_mvolts[mux];
+ bool ok=true;
+ if (error>tol) {
+ ok=false;
+ ERROR("ADC channel %d was %dmv, expected %dmv, outside tolerence of %dmv", ch, mvolts, exp_mvolts, tol);
+ }
+ DebugLog("%6dmv%c ", mvolts, ok ? ' ' : '*');
+ }
+ DebugLog("| %s. clocks=%d\r\n",names[mux][lohi],gotclkin);
+ }
+ }
+ DebugLog("zero: ");
+ for (int ch=0;ch<8;++ch)
+ DebugLog("%6d%c ",gndcalib[ch], (zerook&(1<<ch)) ? ' ':'*');
+ DebugLog("\r\nrange ");
+ for (int ch=0;ch<6;++ch)
+ DebugLog("%6d%c ",gndcalib[ch]-refcalib[ch], (rangeok&(1<<ch)) ? ' ':'*');
+ DebugLog("\r\n%d errors\r\n\r\n", errorcount);
+ set_test_rgb(errorcount ? 4 : 2);
+ clear();
+ if (errorcount==0)
+ drawstr(0,0,F_32_BOLD,"GOOD!");
+ else
+ fdrawstr(0,0,F_32_BOLD,"%d ERRORS", errorcount);
+ oled_flip(vrambuf);
+ /* fill in cv calib - bias and scale x 10
+ {52100.f, 1.f / -9334.833333f},
+ {31716.f, 0.2f / -6548.1f},
+ {31665.f, 0.2f / -6548.1f},
+ {31666.f, 0.2f / -6548.1f},
+ {31041.f, 0.2f / -6548.1f},
+ {31712.f, 0.2f / -6548.1f},
+ // 2 pots
+ {32768.f, 1.05f / -32768.f},
+ {32768.f, 1.05f / -32768.f},
+ // 2 output
+ // 2048 per semitone, so...
+ {42490.f, (26620-42490) * (1.f / (2048.f * 12.f * 2.f))},
+ {42511.f, (26634-42511) * (1.f / (2048.f * 12.f * 2.f))},
+ */
+ for (int ch = 0; ch < 8; ++ch) {
+ int zero = gndcalib[ch];
+ int range = gndcalib[ch] - refcalib[ch];
+ if (range == 0) range = 1;
+ cvcalib[ch].bias = zero;
+ if (ch >= 6)
+ cvcalib[ch].scale = -1.01f / (zero+1);
+ else if (ch==0)
+ cvcalib[ch].scale = -2.5f / range; // range is measured off 2.5; so this scales it so that we get true volts out
+ else
+ cvcalib[ch].scale = (-2.5f/5.f) / range; // range is measured off 2.5; so this scales it so that we get 1 out for 5v in
+ }
+ // ok pdac[k][0] tells us what we got from the ADC when we set the DAC to PITCH_1V_OUT, and pdac[k][1] tells us what we got when we output PITCH_4V_OUT
+ //so we have dacb + dacs * plo0 = PITCH_1V_OUT
+ // and dacb + dacs * plo1 = PITCH_4V_OUT
+ // dacs = (PITCH_1V_OUT-PITCH_4V_OUT) / (plo0-plo1)
+ // dacb = PITCH_1V_OUT - dacs*plo0
+ for (int dacch = 0; dacch < 2; ++dacch) {
+ float range = (pdac[dacch][0] - pdac[dacch][1]);
+ if (range == 0) range = 1.f;
+ float scale_per_volt = (PITCH_1V_OUT - PITCH_4V_OUT) / range;
+ float zero = PITCH_1V_OUT - scale_per_volt * pdac[dacch][0];
+ DebugLog("dac channel %d has zero at %d and %d steps per volt, should be around 42500 and -8000 ish\r\n", dacch, (int)zero, (int)scale_per_volt);
+ cvcalib[dacch + 8].bias = zero;
+ cvcalib[dacch + 8].scale = scale_per_volt * (1.f / (2048.f * 12.f)); // 2048 per semitone
+ }
+
+ flash_writecalib(2);
+
+
+ HAL_Delay(errorcount ? 2000 : 4000);
+ }
+}
+
+void plinky_frame(void);
+
+
+
+
+void EMSCRIPTEN_KEEPALIVE emu_setadc(float araw, float braw, float pitchcv, float gatecv, float xcv, float ycv, float acv, float bcv, int gateforce, int pitchsense, int gatesense) {
+#ifdef EMU
+ emupitchsense = pitchsense;
+ emugatesense = gatesense;
+#endif
+ u16* a = adcbuf;
+ for (int i = 0; i < ADC_SAMPLES; ++i) {
+ a[0] = clampi((int)(52100 - 9334.83f * pitchcv * 1.f / 12.f), 0, 65535);
+ a[1] = gateforce ? 0 : clampi((int)(31716 - 6548.11f * gatecv), 0, 65535);
+ a[2] = clampi((int)(31665 - 6548.11f * xcv), 0, 65535);
+ a[3] = clampi((int)(31666 - 6548.11f * ycv), 0, 65535);
+ a[4] = clampi((int)(31041 - 6548.11f * acv), 0, 65535);
+ a[5] = clampi((int)(31712 - 6548.11f * bcv), 0, 65535);
+ a[ADC_AKNOB] = (u16)((1.f - araw) * 65535);
+ a[ADC_BKNOB] = (u16)((1.f - braw) * 65535);
+ a += ADC_CHANS;
+ }
+}
+
+//#define BITBANG
+void SetExpanderDAC(int chan, int data) {
+#ifndef EMU
+ GPIOA->BRR = 1<<8; // cs low
+ u16 daccmd = (2<<14) + ((chan&3)<<12) + (data & 0xfff);
+#ifdef BITBANG
+ for (int i=0;i<16;++i) {
+ if (daccmd&0x8000) GPIOD->BSRR=1<<4; else GPIOD->BRR=1<<4;
+ daccmd<<=1;
+ HAL_Delay(1);
+ GPIOD->BRR = 1<<1; // clock low
+ HAL_Delay(1);
+ GPIOD->BSRR = 1<<1; // clock high
+ }
+ HAL_Delay(1);
+#else
+ spidelay();
+ daccmd=(daccmd>>8) | (daccmd<<8);
+ HAL_SPI_Transmit(&hspi2, (uint8_t*) &daccmd, 2, -1);
+ spidelay();
+#endif
+ GPIOA->BSRR = 1<<8; // cs high
+#endif
+}
+
+
+#ifdef WASM
+
+bool send_midimsg(u8 status, u8 data1, u8 data2) {
+ return true;
+}
+void spi_update_dac(int chan) {
+ resetspistate();
+}
+
+void EmuStartSound(void) {
+}
+
+bool midi_receive(unsigned char packet[4]) {
+ return false;// fill in packet and return true if midi found
+}
+int emutouch[9][2];
+void EMSCRIPTEN_KEEPALIVE wasm_settouch(int idx, int pos, int pressure) {
+ if (idx >= 0 && idx < 9)
+ emutouch[idx][1] = pos, emutouch[idx][0] = pressure;
+}
+
+void EMSCRIPTEN_KEEPALIVE plinky_frame_wasm(void) {
+ plinky_frame();
+}
+u32 wasmbuf[BLOCK_SAMPLES];
+uint8_t* EMSCRIPTEN_KEEPALIVE get_wasm_audio_buf(void) {
+ return (uint8_t*)wasmbuf;
+}
+uint8_t* EMSCRIPTEN_KEEPALIVE get_wasm_preset_buf(void) {
+ return (uint8_t*)&rampreset;
+}
+void EMSCRIPTEN_KEEPALIVE wasm_audio(void) {
+ static u8 half=0;
+ u32 audioin[BLOCK_SAMPLES] = {0};
+ uitick(wasmbuf, audioin, half);
+ half = 1 - half;
+}
+void EMSCRIPTEN_KEEPALIVE wasm_pokepreset(int offset, int byte) {
+ if (offset >= 0 && offset < sizeof(rampreset)) ((u8*)&rampreset)[offset] = byte;
+}
+int EMSCRIPTEN_KEEPALIVE wasm_peekpreset(int offset) {
+ if (offset >= 0 && offset < sizeof(rampreset)) return ((u8*)&rampreset)[offset];
+ return 0;
+}
+int EMSCRIPTEN_KEEPALIVE wasm_getcurpreset(void) {
+ return sysparams.curpreset;
+}
+void EMSCRIPTEN_KEEPALIVE wasm_setcurpreset(int i) {
+ SetPreset(i, false);
+}
+#endif
+
+static u8 uartbuf[16];
+void serial_midi_init(void) {
+#ifndef EMU
+ HAL_UART_Receive_DMA(&huart3, uartbuf, sizeof(uartbuf));
+#endif
+}
+void serial_midi(const u8*buf, u8 len) {
+ static u8 state=0;
+ static u8 msg[3];
+ for (;len--;){
+ u8 data=*buf++;
+ if (data & 0x80) state = 0;
+ else if (state == 3) state = 1; // running status
+ if (state < 3) {
+ msg[state++] = data;
+ if (state == 1 && (msg[0] >= 0xF8 && msg[0] <= 0xFC)) { //BUG FIX NO MIDI CLOCK FROM HW MIDI KAY LPZW
+ //real time start stop clock
+ msg[1]=0;
+ msg[2]=0;
+ state = 3;
+ }
+ if (state == 2 && (msg[0] >= 0xc0 && msg[0] <= 0xdf)) {
+ msg[state++] = 0; // two byte messages. wtf midi.//WE NEED TO DEBUG THIS COS IT SEEMS NOT TO WORK
+ }
+ if (state==3) {
+ processmidimsg(msg[0], msg[1], msg[2]);
+ }
+ }
+ }
+}
+
+#ifndef EMU
+// from https://community.st.com/s/question/0D50X00009XkflR/haluartirqhandler-bug
+// what a trash fire
+// USART Error Handler
+void HAL_UART_ErrorCallback(UART_HandleTypeDef* huart) {
+ __HAL_UART_CLEAR_OREFLAG(huart);
+ __HAL_UART_CLEAR_NEFLAG(huart);
+ __HAL_UART_CLEAR_FEFLAG(huart);
+ /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
+ __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
+ //The most important thing when UART framing error occur/any error is restart the RX process
+ midi_panic();
+ HAL_UART_Receive_DMA(&huart3, uartbuf, sizeof(uartbuf));
+}
+
+typedef unsigned int uint;
+u8 midisendbuf[16+16];
+uint midisendhead,midisendtail;
+bool usb_midi_write(const uint8_t packet[4]);
+bool serial_midi_ready(void) {
+ return huart3.TxXferCount == 0;
+}
+void kick_midi_send(void) {
+ if (huart3.TxXferCount==0 && midisendhead!=midisendtail) {
+ uint from=midisendtail&15;
+ uint to = midisendhead&15;
+ if (to>from) {
+ midisendtail+=(to-from);
+ HAL_UART_Transmit_DMA(&huart3, midisendbuf + from, to-from );
+
+ } else if (to<from) {
+ // wrapped! send from->16, and 0->to
+ u8 sendlen = (16-from) + to;
+ memcpy(midisendbuf+16,midisendbuf,to); // copy looped part to end so we can send it all in one go! good on us.
+ midisendtail+=sendlen;
+ HAL_UART_Transmit_DMA(&huart3, midisendbuf + from, sendlen);
+ }
+ }
+}
+
+
+bool send_midi_serial(const u8 *data, int len) {
+ if (len<=0) return true;
+ if (midisendhead-midisendtail+len > 16)
+ return false; // full
+ while (len--)
+ midisendbuf[(midisendhead++)&15]=*data++;
+ return true;
+}
+
+bool send_midimsg(u8 status, u8 data1, u8 data2) { // returns false if too full
+
+ u8 len=3;
+
+ if (status>=0xc0 && status<0xe0) // Cn Program Change, Dn Mono / Channel Aftertouch
+ len=2;
+ if (!(status&0x80))
+ return true;
+ if (status == 0x80 && data1 == 0)
+ return true; // er, no
+
+ //int midi_ch_out = ((GetParam(P_MIDI_CH_OUT, 0) * 16)/FULL) & 15;
+ //int midi_ch_out = clampi(((GetParam(P_MIDI_CH_OUT, 0) * 16)/FULL),0,15);
+ int midi_ch_out = clampi((mini(GetParam(P_MIDI_CH_OUT, 0), FULL - 1) * 16) / FULL,0,15);
+
+ if (status<0xf0) status += midi_ch_out; // sets output channel
+
+ u8 buf[4]={status>>4, status,data1,data2};
+ usb_midi_write(buf);
+#ifdef DEBUG
+// DebugLog("%02x %02x %02x\r\n", status, data1, data2);
+#endif
+ send_midi_serial(buf + 1, len);
+ return true;
+}
+#else
+void kick_midi_send(void) {}
+bool serial_midi_ready(void) {
+ return true;
+}
+#endif
+
+void serial_midi_update(void) {
+ kick_midi_send();
+ static u8 old_pos=0;
+#ifdef EMU
+ u8 pos = 0;
+ #else
+ u8 pos = 16 - __HAL_DMA_GET_COUNTER(huart3.hdmarx);
+#endif
+ if (pos != old_pos) {
+ if (pos > old_pos) {
+ serial_midi(&uartbuf[old_pos], pos - old_pos);
+ } else {
+ serial_midi(&uartbuf[old_pos], 16 - old_pos);
+ serial_midi(&uartbuf[0], pos);
+ }
+ }
+ old_pos = pos;
+}
+
+
+void EMSCRIPTEN_KEEPALIVE plinky_init(void) {
+ denormals_init();
+ touch_reset_calib();
+ tc_init();
+
+ adc_init();
+#ifdef EMU
+ emu_setadc(0.5f, 0.5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, false, false, false);
+#endif
+ dac_init();
+ HAL_Delay(100); // stablise power before bringing oled up
+ oled_init();
+ check_bootloader_flash();
+ reverb_clear(); // ram2 is not cleared by startup.s as written.
+ delay_clear();
+ codec_init();
+ adc_start(); // also dac_start effectively
+
+#ifdef EMU
+ void EmuStartSound(void);
+ EmuStartSound();
+#endif
+
+ // see if were in the testjig - it pulls PA8 (pin 67) down 'DEBUG'
+#ifndef EMU
+ if (!(GPIOA->IDR & (1<<8))) {
+ test_jig();
+ }
+
+ // turn debug pin to an output
+ GPIO_InitTypeDef GPIO_InitStruct = { 0 };
+ GPIO_InitStruct.Pin = GPIO_PIN_8;
+ GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+ GPIOA->BSRR = 1<<8; // cs high
+ HAL_Delay(1);
+
+ spi_setchip(0xffffffff);
+ int spiid = spi_readid();
+ DebugLog("SPI flash chip 1 id %04x\r\n", spiid);
+ spi_setchip(0);
+ spiid = spi_readid();
+ DebugLog("SPI flash chip 0 id %04x\r\n", spiid);
+ // kick off serial midi in!
+ serial_midi_init();
+
+ /*
+ // BIT BANG TEST
+#ifdef BITBANG
+ // PD1 is spiclk, pd4 is mosi
+ GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_4;
+ GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+ HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
+ GPIOD->BSRR = (1<<1) + (1<<4); // clock, data high
+#endif
+ int count=0;
+ while (1) {
+ SetExpanderDAC(0,(count&1)?0xfff:0);
+ SetExpanderDAC(1,(count&2)?0xfff:0);
+ SetExpanderDAC(2,(count&4)?0xfff:0);
+ SetExpanderDAC(3,(count&8)?0xfff:0);
+ count++;
+ HAL_Delay(250);
+ }
+*/
+#endif
+ led_init();
+
+ //enable_audio=EA_PASSTHRU; // DO NOT CHECK IN
+ //while(1);
+
+
+ int flashvalid = flash_readcalib();
+ if (!(flashvalid & 1)) { // no calib at all
+ touch_reset_calib();
+ calib();
+ flashvalid |= 1;
+ flash_writecalib(flashvalid );
+ }
+ if (!(flashvalid &2)) {
+ //cv_reset_calib();
+ cv_calib();
+ flashvalid |= 2;
+ flash_writecalib(3);
+ }
+ HAL_Delay(80);
+ int knoba= GetADCSmoothedNoCalib(ADC_AKNOB);
+ int knobb= GetADCSmoothedNoCalib(ADC_BKNOB);
+ bootswish();
+ knoba=abs(knoba-(int)GetADCSmoothedNoCalib(ADC_AKNOB));
+ knobb=abs(knobb-(int)GetADCSmoothedNoCalib(ADC_BKNOB));
+ DebugLog("knob turned by %d,%d during boot\r\n", knoba,knobb);
+ //knoba = 10000; // DO NOT CHECK IN - FORCE CALIBRATION
+ //knobb = 10000; // DO NOT CHECK IN - FORCE CV CALIB
+ // turn knobs during boot to force calibration
+#ifndef WASM
+ if (knoba>4096 || knobb>4096) {
+ if (knoba > 4096 && knobb > 4096) {
+ // both knobs twist on boot - jump to stm flash bootloader
+ reflash();
+ }
+ if (knoba > 4096) {
+ // left knob twist on boot - full calib
+ touch_reset_calib();
+ calib();
+ }
+ else {
+ // right knob twist on boot - cv calib only
+ //cv_reset_calib();
+ cv_calib();
+ }
+ flash_writecalib(3);
+ }
+#endif
+ InitParamsOnBoot();
+
+
+
+
+ /*
+ DebugLog("erase test ...\r\n");
+ spi_erase64k(0, 0);
+ spi_read256(0);
+ for (int i = 0; i < 256; ++i) if (spibigrx[i + 4] != 255) {
+ DebugLog("erase fail at %d\r\n", i);
+ }
+ memset(spibigrx, 0, sizeof(spibigrx));
+ for (int i = 0; i < 256; ++i) spibigtx[i + 4] = i * 23 + 72;
+ spi_write256(0);
+ memset(spibigrx, 0, sizeof(spibigrx));
+ spi_read256(0);
+ for (int i = 0; i < 256; ++i) if (spibigrx[i + 4] != (u8)(i*23+72)) {
+ DebugLog("write fail at %d\r\n", i);
+ }
+ spi_erase64k(0, 0);
+ spi_read256(0);
+ for (int i = 0; i < 256; ++i) if (spibigrx[i + 4] != 255) {
+ DebugLog("erase 2 fail at %d\r\n", i);
+ }
+
+
+ DebugSPIPage(65536+32768);
+ */
+
+ enable_audio = EA_PLAY;
+
+}
+
+
+
+#include "ui.h"
+