From 7852372b15f850a46a02d1aa991215619096ad50 Mon Sep 17 00:00:00 2001 From: Alex Evans <715855+mmalex@users.noreply.github.com> Date: Wed, 6 Nov 2024 17:47:11 +0000 Subject: add oled feedback for flashing process --- bootloader/Core/Src/main.c | 3 +- bootloader/plinkybl debug.launch | 4 +- sw/Core/Src/main.c | 67 +- sw/Core/Src/plinky.c | 6480 +++++++++++++++++++------------------- 4 files changed, 3293 insertions(+), 3261 deletions(-) diff --git a/bootloader/Core/Src/main.c b/bootloader/Core/Src/main.c index 2f45dc4..a16aade 100755 --- a/bootloader/Core/Src/main.c +++ b/bootloader/Core/Src/main.c @@ -341,8 +341,9 @@ int main(void) /* USER CODE BEGIN 3 */ if (encstate==1) startreset=true; - if (encstate==0x80 && startreset) { + if (encstate==0x0 && startreset) { // released the encoder + HAL_Delay(100); scb_reset_system(); } if (encheld>10000) diff --git a/bootloader/plinkybl debug.launch b/bootloader/plinkybl debug.launch index 6908cb2..1c6fef8 100755 --- a/bootloader/plinkybl debug.launch +++ b/bootloader/plinkybl debug.launch @@ -5,7 +5,7 @@ - + @@ -73,7 +73,7 @@ - + diff --git a/sw/Core/Src/main.c b/sw/Core/Src/main.c index d070d81..173bf96 100755 --- a/sw/Core/Src/main.c +++ b/sw/Core/Src/main.c @@ -188,71 +188,6 @@ bool update_accelerometer_raw(void) { return true; } -#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); - if (count!=64/4 || magic!=0xa738ea75) - return; - rb32[64]++; // clear the magic - const uint32_t *app_base = (const uint32_t *)DELAY_BUF; - 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) { - return; - } - - // first word is stack base - needs to be in RAM region and word-aligned - if ((app_base[0] & 0xff000003) != 0x20000000) { - 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) { - return; - } - DebugLog("FLASHING BOOTLOADER! DO NOT RESET\r\n"); - 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); - 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"); -} int miditest(void); void midiinit(void); @@ -325,7 +260,7 @@ int main(void) } - check_bootloader_flash(); + //check_bootloader_flash(); // used to be here, but now we do it in plinky_init // miditest(); 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 -#else -#define EMSCRIPTEN_KEEPALIVE -#endif - -#ifndef EMU -#include - -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 -#include -#include -#include -#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<> 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<theosc[2].pitch - 43000) * (1.f / 65536.f)); - } - - int bit=1<=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 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<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>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=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<=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<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<= 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 (to16, 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 +#else +#define EMSCRIPTEN_KEEPALIVE +#endif + +#ifndef EMU +#include + +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 +#include +#include +#include +#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<> 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<theosc[2].pitch - 43000) * (1.f / 65536.f)); + } + + int bit=1<=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 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<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>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=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<=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<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<= 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 (to16, 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" + -- cgit v1.2.3