diff options
Diffstat (limited to 'sw/Core/Src/params.h')
| -rwxr-xr-x | sw/Core/Src/params.h | 1132 |
1 files changed, 1132 insertions, 0 deletions
diff --git a/sw/Core/Src/params.h b/sw/Core/Src/params.h new file mode 100755 index 0000000..c3cb507 --- /dev/null +++ b/sw/Core/Src/params.h @@ -0,0 +1,1132 @@ + +static inline u8 lfohashi(u16 step) { + return rndtab[step]; +} +static inline float lfohashf(u16 step) { + return (float) (lfohashi(step) * (2.f / 256.f) - 1.f); +} + +float EvalTri(float t, u32 step) { + return 1.f - (t + t); +} +float EvalEnv(float t, u32 step) { + // unipolar pseudo exponential up/down + if (step & 1) { + t *= t; + t *= t; + return t; + } + else { + t = 1.f - t; + t *= t; + t *= t; + return 1.f-t; + } +} +float EvalSin(float t, u32 step) { + t = t * t * (3.f - t - t); + return 1.f - (t + t); +} +float EvalSaw(float t, u32 step) { + return (step &1) ? t-1.f : 1.f-t; +} +float EvalSquare(float t, u32 step) { + return (step & 1) ? 0.f : 1.f; +} +float EvalBiSquare(float t, u32 step) { + return (step & 1) ? -1.f : 1.f; +} +float EvalSandcastle(float t, u32 step) { + return (step & 1) ? ((t<0.5f) ? 0.f : -1.f) : ((t<0.5f) ? 1.f : 0.f); +} +static inline float triggy(float t) { + t=1.f-(t+t); + t=t*t; + return t*t; +} +float EvalTrigs(float t, u32 step) { + return (step & 1) ? ((t<0.5f) ? 0.f : triggy(1.f-t)) : ((t<0.5f) ? triggy(t) : 0.f); +} +float EvalBiTrigs(float t, u32 step) { + return (step & 1) ? ((t<0.5f) ? 0.f : -triggy(1.f-t)) : ((t<0.5f) ? triggy(t) : 0.f); +} +float EvalStepNoise(float t, u32 step) { + return lfohashf(step); +} +float EvalSmoothNoise(float t, u32 step) { + float n0 = lfohashf(step + (step&1)), n1 = lfohashf(step | 1); + return n0 + (n1 - n0) * t; +} + +float (*lfofuncs[LFO_LAST])(float t, u32 step) = { + [LFO_TRI]=EvalTri, [LFO_SIN]=EvalSin, [LFO_SMOOTHNOISE]=EvalSmoothNoise, [LFO_STEPNOISE]=EvalStepNoise, + [LFO_BISQUARE]=EvalBiSquare, [LFO_SQUARE]=EvalSquare, [LFO_SANDCASTLE]=EvalSandcastle, [LFO_BITRIGS]=EvalBiTrigs, [LFO_TRIGS]=EvalTrigs, [LFO_ENV]=EvalEnv, + [LFO_SAW]=EvalSaw, +}; + +float lfo_eval(u32 ti, float warp, unsigned int shape) { + int step = (ti >> 16)<<1; + float t = (ti & 65535) * (1.f / 65536.f); + if (t < warp) + t /= warp; + else { + step++; + t = (1.f - t) / (1.f-warp); + } + if (shape>=LFO_LAST) shape=0; + return (*lfofuncs[shape])(t, step); +} + + + + +const char * const paramnames[P_LAST]={ +#ifdef NEW_LAYOUT + [P_A2] = I_ADSR_A "Attack2", + [P_D2] = I_ADSR_D "Decay2", + [P_S2] = I_ADSR_S "Sustain2", + [P_R2] = I_ADSR_R "Release2", + [P_SWING] = I_TEMPO "Swing", +#else + [P_ENV_RATE] = I_PERIOD "Env Rate", + [P_ENV_WARP] = I_WARP "Env Warp", + [P_ENV_REPEAT] = I_FEEDBACK "Env Repeat", +#endif + + [P_SENS]=I_TOUCH "Sensitivity", + [P_DRIVE]=I_DISTORT "Distort", + [P_A]=I_ADSR_A "Attack", + [P_D]=I_ADSR_D "Decay", + [P_S]=I_ADSR_S "Sustain", + [P_R]=I_ADSR_R "Release", + + + [P_MIXSYNTH] = I_WAVE "Synth Lvl", + [P_MIXINPUT] = I_JACK "Input Lvl", + [P_MIXINWETDRY] = I_JACK "In Wet/Dry", + [P_MIXWETDRY] = I_REVERB "Main Wet/Dry", + [P_MIXHPF] = I_HPF "High Pass", + [P_MIXRESO] = I_DISTORT "Resonance", + + [P_OCT]=I_OCTAVE "Octave", + [P_PITCH]=I_PIANO "Pitch", + [P_GLIDE]=I_GLIDE "Glide", + [P_INTERVAL]=I_INTERVAL "Interval", + [P_GATE_LENGTH] = I_INTERVAL "Gate Len", + [P_ENV_LEVEL] = I_AMPLITUDE "Env Level", + + + + [P_PWM] = "Shape", + [P_RVUNUSED] = "<unused>", + + [P_SCALE]=I_PIANO "Scale", + [P_ROTATE]=I_FEEDBACK "Degree", + [P_MICROTUNE]=I_MICRO "Microtone", + [P_STRIDE]=I_INTERVAL "Stride", + + [P_ARPONOFF] = I_NOTES "Arp On/Off", + [P_LATCHONOFF] = "Latch On/Off", + + [P_ARPMODE]=I_ORDER "Arp", + [P_ARPDIV]=I_DIVIDE "Divide", + [P_ARPPROB]=I_PERCENT "Prob %", + [P_ARPLEN]=I_LENGTH "Euclid Len", + [P_ARPOCT]=I_OCTAVE "Octaves", + [P_TEMPO]= "BPM", + + [P_SEQMODE]=I_ORDER "Seq", + [P_SEQDIV]=I_DIVIDE "Divide", + [P_SEQPROB]=I_PERCENT "Prob %", + [P_SEQLEN]=I_LENGTH "Euclid Len", + [P_SEQSTEP]=I_SEQ "Step Ofs", + [P_SEQPAT]=I_PRESET "Pattern", + + [P_DLSEND]=I_SEND "Send", + [P_DLTIME]=I_TIME "Time", + [P_DLFB]=I_FEEDBACK "Feedback", + //[P_DLCOLOR]=I_COLOR "Colour", + [P_DLWOB]=I_AMPLITUDE "Wobble", + [P_DLRATIO]=I_DIVIDE "2nd Tap", + + [P_RVSEND]=I_SEND "Send", + [P_RVTIME]=I_TIME "Time", + [P_RVSHIM]=I_FEEDBACK "Shimmer", + //[P_RVCOLOR]=I_COLOR "Colour", + [P_RVWOB]=I_AMPLITUDE "Wobble", + //[P_RVUNUSED]=" ", + + [P_SAMPLE] = I_WAVE "Sample", + [P_SMP_POS] = "Scrub", + [P_SMP_RATE] = I_NOTES "Rate", + [P_SMP_GRAINSIZE] = I_PERIOD "Grain Sz", + [P_SMP_TIME] = I_TIME "Timestretch", + [P_CV_QUANT] = I_JACK "CV Quantise", + + [P_ACCEL_SENS] = I_AMPLITUDE "Accel Sens", + [P_MIX_WIDTH] = I_AMPLITUDE "Stereo Width", + [P_NOISE] = I_WAVE "Noise", + [P_JIT_POS] = "Scrub", + [P_JIT_RATE] = I_NOTES "Rate", + [P_JIT_GRAINSIZE] = I_PERIOD "Grain Sz", + [P_JIT_PULSE] = I_ENV "<unused>", + [P_HEADPHONE] = "'Phones Vol", + + [P_AOFFSET]=I_OFFSET "CV Offset", + [P_ASCALE]= I_TIMES "CV Scale", + [P_ADEPTH]=I_AMPLITUDE "LFO Depth", + [P_AFREQ]= I_PERIOD "LFO Rate", + [P_ASHAPE]=I_SHAPE "LFO Shape", + [P_AWARP]= I_WARP "LFO Warp", + + [P_BOFFSET]=I_OFFSET "CV Offset", + [P_BSCALE]=I_TIMES "CV Scale", + [P_BDEPTH]=I_AMPLITUDE "LFO Depth", + [P_BFREQ]=I_PERIOD "LFO Rate", + [P_BSHAPE]=I_SHAPE "LFO Shape", + [P_BWARP]=I_WARP "LFO Warp", + + [P_XOFFSET]=I_OFFSET "CV Offset", + [P_XSCALE]=I_TIMES "CV Scale", + [P_XDEPTH]=I_AMPLITUDE "LFO Depth", + [P_XFREQ]=I_PERIOD "LFO Rate", + [P_XSHAPE]=I_SHAPE "LFO Shape", + [P_XWARP]=I_WARP "LFO Warp", + + [P_YOFFSET]=I_OFFSET "CV Offset", + [P_YSCALE]=I_TIMES "CV Scale", + [P_YDEPTH]=I_AMPLITUDE "LFO Depth", + [P_YFREQ]=I_PERIOD "LFO Rate", + [P_YSHAPE]=I_SHAPE "LFO Shape", + [P_YWARP]=I_WARP "LFO Warp", + +}; + +#define FLAG_SIGNED 128 +#define FLAG_MASK 127 + + +const static u8 param_flags[P_LAST] = { + [P_PWM]=FLAG_SIGNED, + [P_ARPMODE] = ARP_LAST, + [P_ARPDIV] = FLAG_SIGNED, + [P_ARPPROB] =0, // FLAG_SIGNED, - used to have this signed, but its nice to slam it into 0 + [P_ARPLEN] = 17 + FLAG_SIGNED, + [P_ARPOCT] = 4, + [P_TEMPO] = FLAG_SIGNED, + +#ifdef NEW_LAYOUT + [P_A2] = 0, + [P_D2] = 0, + [P_S2] = 0, + [P_R2] = 0, + [P_SWING] = FLAG_SIGNED, + + [P_ACCEL_SENS] = FLAG_SIGNED, + [P_MIX_WIDTH] = FLAG_SIGNED, +#else + [P_ENV_WARP] = FLAG_SIGNED, + [P_ENV_RATE] = FLAG_SIGNED, + [P_ENV_REPEAT] = FLAG_SIGNED, +#endif + [P_MIXWETDRY] = FLAG_SIGNED, + [P_MIXINWETDRY] = FLAG_SIGNED, + + [P_SEQMODE] = SEQ_LAST, + [P_SEQDIV] = DIVISIONS_MAX+1, + [P_SEQPROB] =FLAG_SIGNED, + [P_SEQLEN] = 17 + FLAG_SIGNED, + [P_SEQPAT]=24, + [P_SEQSTEP] = FLAG_SIGNED + 64, + + + + [P_DLSEND]=0, + [P_DLTIME]=FLAG_SIGNED, + [P_DLFB]=0, + //[P_DLCOLOR]=0, + [P_DLWOB]=0, + [P_DLRATIO]=0, + + [P_RVSEND]=0, + [P_RVTIME]=0, + [P_RVSHIM]=0, + //[P_RVCOLOR]=0, + [P_RVWOB]=0, + //[P_RVUNUSED]=0, + + [P_SENS]=0, + [P_DRIVE]=FLAG_SIGNED, + [P_A]=0, + [P_D]=0, + [P_S]=0, + [P_R]=0, + + + [P_OCT]=4+FLAG_SIGNED, + [P_PITCH]=FLAG_SIGNED, + [P_GLIDE]=0, + [P_INTERVAL]=FLAG_SIGNED, + + [P_SCALE]=S_LAST, + [P_ROTATE]=FLAG_SIGNED+24, + [P_MICROTUNE]=0, + [P_STRIDE]=13, + + [P_ASCALE ]=FLAG_SIGNED, + [P_AOFFSET]=FLAG_SIGNED, + [P_ADEPTH ]=FLAG_SIGNED, + [P_AFREQ ]=FLAG_SIGNED, + [P_ASHAPE ]=LFO_LAST, + [P_AWARP ]=FLAG_SIGNED, + + [P_BSCALE ]=FLAG_SIGNED, + [P_BOFFSET]=FLAG_SIGNED, + [P_BDEPTH ]=FLAG_SIGNED, + [P_BFREQ ]=FLAG_SIGNED, + [P_BSHAPE ]=LFO_LAST, + [P_BWARP ]=FLAG_SIGNED, + + [P_XSCALE ]=FLAG_SIGNED, + [P_XOFFSET]=FLAG_SIGNED, + [P_XDEPTH ]=FLAG_SIGNED, + [P_XFREQ ]=FLAG_SIGNED, + [P_XSHAPE ]=LFO_LAST, + [P_XWARP ]=FLAG_SIGNED, + + [P_YSCALE ]=FLAG_SIGNED, + [P_YOFFSET]=FLAG_SIGNED, + [P_YDEPTH ]=FLAG_SIGNED, + [P_YFREQ ]=FLAG_SIGNED, + [P_YSHAPE ]=LFO_LAST, + [P_YWARP ]=FLAG_SIGNED, + + [P_SAMPLE] = 9, + [P_SMP_RATE] = FLAG_SIGNED, + [P_SMP_TIME] = FLAG_SIGNED, + [P_CV_QUANT] = CVQ_LAST, + [P_JIT_PULSE] = FLAG_SIGNED, +}; + +#define C ( 0*512) +#define Cs ( 1*512) +#define D ( 2*512) +#define Ds ( 3*512) +#define E ( 4*512) +#define F ( 5*512) +#define Fs ( 6*512) +#define G ( 7*512) +#define Gs ( 8*512) +#define A ( 9*512) +#define As (10*512) +#define B (11*512) + +#define Es F +#define Bs C + +#define Ab Gs +#define Bb As +#define Cb B +#define Db Cs +#define Eb Ds +#define Fb E +#define Gb Fs + +#define CENTS(c) (((c)*512)/100) + +const static u16 scaletab[S_LAST][16] = { +[S_CHROMATIC] = {12, C,Cs,D,Ds,E,F,Fs,G,Gs,A,As,B}, +[S_MAJOR] = {7, C,D,E,F,G,A,B}, +[S_MINOR] = {7, C,D,Eb,F,G,Ab,Bb}, +[S_HARMMINOR] = {7,C,D,Ds,F,G,Gs,B}, +[S_PENTA] = {5, C,D,E,G,A}, +[S_PENTAMINOR] = {5, C,Ds,F,G,As}, +[S_HIRAJOSHI] = {5, C,D,Ds,G,Gs}, +[S_INSEN] = {5, C,Cs,F,G,As}, +[S_IWATO] = {5, C,Cs,F,Fs,As}, +[S_MINYO] = {5, C,D,F,G,A}, + +[S_FIFTHS] = {2, C,G}, +[S_TRIADMAJOR] = {3, C,E,G}, +[S_TRIADMINOR] = {3, C,Eb,G}, + +// these are dups of major/minor/rotations thereof, but lets throw them in anyway +[S_DORIAN] = {7, C,D,Ds,F,G,A,As}, +[S_PHYRGIAN] = {7, C,Db,Eb,F,G,Ab,Bb}, +[S_LYDIAN] = {7, C,D,E,Fs,G,A,B}, +[S_MIXOLYDIAN] = {7, C,D,E,F,G,A,Bb}, +[S_AEOLIAN] = {7, C,D,Eb,F,G,Ab,Bb}, +[S_LOCRIAN] = {7, C,Db,Eb,F,Gb,Ab,Bb}, + +[S_BLUESMAJOR] = {6,C,D,Ds,E,G,A}, +[S_BLUESMINOR] = {6,C,Ds,F,Fs,G,As}, + +[S_ROMANIAN] = {7,C,D,Ds,Fs,G,A,As}, +[S_WHOLETONE] = {6,C,D,E,Fs,Gs,As}, + +// microtonal stuff +[S_HARMONICS] = {4, C, E - CENTS(14), G + CENTS(2), Bb - CENTS(31)}, +[S_HEXANY] = { 5, CENTS(0), CENTS(386), CENTS(498), CENTS(702), CENTS(814)}, // kinda C,E,F,G,G# but the E is quite flat + +[S_JUST] = {7, CENTS(0), CENTS(204), CENTS(386), CENTS(498), CENTS(702), CENTS(884), CENTS(1088)}, +[S_DIMINISHED] = {8,C,D,Ds,F,Fs,Gs,A,B}, +}; + +#undef C +#undef D +#undef E +#undef F +#undef G +#undef A +#undef B +#undef Cs +#undef Ds +#undef Es +#undef Fs +#undef Gs +#undef As +#undef Bs +#undef Cb +#undef Db +#undef Eb +#undef Fb +#undef Gb +#undef Ab +#undef Bb + + + + +static inline u8 scalelen(int scale) { + return scaletab[scale][0]; +} +static inline int lookupscale(int scale, int step) { + u8 len=scaletab[scale][0]; + int oct=step/len; + step-=oct*len; + if (step<0) { step+=len; oct--; } + return oct*(12*512) + scaletab[scale][step+1]; +} + + +int params_premod[P_LAST]; // parameters with the lfos/inputs pre-mixed in +#define FULLBITS 10 +#define FULL 1024 +#define HALF (FULL/2) +#define QUARTER (FULL/4) +#define EIGHTH (FULL/8) +#define QUANT(v,maxi) ( ((v)*FULL+FULL/2)/(maxi) ) + +#define FIRST_PRESET_IDX 0 +#define LAST_PRESET_IDX 32 +#define FIRST_PATTERN_IDX LAST_PRESET_IDX +#define LAST_PATTERN_IDX 128 // 24 patterns x 4 quarters = 96 pages starting from page 32 +#define FIRST_SAMPLE_IDX LAST_PATTERN_IDX +#define LAST_SAMPLE_IDX 136 +#define LAST_IDX LAST_SAMPLE_IDX +#define OPS_IDX 0xfe +typedef struct PageFooter { + u8 idx; // preset 0-31, pattern (quarters!) 32-127, sample 128-136, blank=0xff + u8 version; + u16 crc; + u32 seq; +} PageFooter; +typedef struct SysParams { + u8 curpreset; + u8 paddy; + u8 systemflags; + s8 headphonevol; + u8 pad[16 - 4]; +} SysParams; +enum { + SYS_DEPRACATED_ARPON=1, + SYS_DEPRACATED_LATCHON=2, +}; +enum { + FLAGS_ARP=1, + FLAGS_LATCH=2, +}; +// preset version 1: ?? +// preset version 2: add SAW lfo shape +#define CUR_PRESET_VERSION 2 +typedef struct Preset { + s16 params[96][8]; + u8 flags; + s8 loopstart_step_no_offset; + s8 looplen_step; + u8 paddy[3]; + u8 version; + u8 category; + u8 name[8]; +} Preset; +static_assert((sizeof(Preset)&15)==0,"?"); +static_assert(sizeof(Preset)+sizeof(SysParams)+sizeof(PageFooter)<=2048, "?"); +typedef struct SampleInfo { + u8 waveform4_b[1024]; // 4 bits x 2048 points, every 1024 samples + int splitpoints[8]; + int samplelen; // must be after splitpoints, so that splitpoints[8] is always the length. + s8 notes[8]; + u8 pitched; + u8 loop; // bottom bit: loop; next bit: slice vs all + u8 paddy[2]; +} SampleInfo; +static_assert(sizeof(SampleInfo) + sizeof(SysParams) + sizeof(PageFooter) <= 2048, "?"); +static_assert((sizeof(SampleInfo)&15)==0,"?"); +typedef struct FingerRecord { + u8 pos[4]; + u8 pressure[8]; +} FingerRecord; +typedef struct PatternQuarter { + FingerRecord steps[16][8]; + s8 autoknob[16*8][2]; +} PatternQuarter; +static_assert(sizeof(PatternQuarter) + sizeof(SysParams) + sizeof(PageFooter) <= 2048, "?"); +static_assert((sizeof(PatternQuarter)&15)==0,"?"); +SampleInfo ramsample; +Preset rampreset; +PatternQuarter rampattern[4]; +SysParams sysparams; +u8 ramsample1_idx=255; +u8 rampreset_idx=255; +u8 rampattern_idx=255; +u8 updating_bank2 = 0; +u8 edit_preset_pending = 255; +u8 edit_pattern_pending = 255; +u8 edit_sample1_pending = 255; + +u8 prev_preset_pending = 255; +u8 prev_pattern_pending = 255; +u8 prev_sample1_pending = 255; + +u8 cur_sample1; // this is the one we are playing, derived from param, can modulate. 0 means off, 1-8 is sample +u8 cur_pattern; // this is the current pattern, derived from param, can modulate. +s8 cur_step = 0; // current step +s8 step_offset = 0; // derived from param +u8 edit_sample0 = 0; // this is the one we are editing. no modulation. sample 0-7. not 1 based! +u8 copyrequest = 255; +u8 copyfrompreset = 0; +u8 copyfrompattern = 0; +u8 copyfromsample = 0; +u8 recordingknobs = 0; +s8 last_preset_selection_rotstep = 0; // the thing that gets cleared when you hold down X + +float knobbase[2]; + +enum { + GEN_PRESET, + GEN_PAT0, + GEN_PAT1, + GEN_PAT2, + GEN_PAT3, + GEN_SYS, + GEN_SAMPLE, + GEN_LAST +}; +u32 flashtime[GEN_LAST]; // for each thing we care about, what have we written to? +u32 ramtime[GEN_LAST]; //...and what has the UI set up? + +static u8 const zero[2048]={0}; +typedef struct FlashPage { + union { + u8 raw[2048 - sizeof(SysParams) - sizeof(PageFooter)]; + Preset preset; + PatternQuarter patternquarter; + SampleInfo sampleinfo; + }; + SysParams sysparams; + PageFooter footer; +} FlashPage; +static_assert(sizeof(FlashPage) == 2048, "?"); +u8 latestpagesidx[LAST_IDX]; +u8 backuppagesidx[LAST_PRESET_IDX]; +SysParams sysparams; +static inline FlashPage* GetFlashPagePtr(u8 page) { return (FlashPage*)(FLASH_ADDR_256 + page * 2048); } + +static Preset const init_params; +int mod_cur[8]; // 16 bit fp + + +static inline int GetHeadphoneAsParam(void) { + return (sysparams.headphonevol + 45) * (FULL / 64); +} + +static inline int param_eval_premod(u8 paramidx) { + if (paramidx == P_HEADPHONE) + return params_premod[paramidx] = GetHeadphoneAsParam() * 65536 ; + s16* p = rampreset.params[paramidx]; + int tv = p[M_BASE] << 16; + tv += (mod_cur[M_A] * p[M_A]); + tv += (mod_cur[M_B] * p[M_B]); + tv += (mod_cur[M_X] * p[M_X]); + tv += (mod_cur[M_Y] * p[M_Y]); + params_premod[paramidx] = tv; + return tv; +} + + + +static inline Preset* GetSavedPreset(u8 presetidx) { +#ifdef HALF_FLASH + return (Preset*)&init_params; +#endif + if (presetidx >= 32) + return (Preset*)&init_params; + FlashPage*fp=GetFlashPagePtr(latestpagesidx[presetidx]); + if (fp->footer.idx!=presetidx || fp->footer.version!=2) + return (Preset*)&init_params; + return (Preset * )fp; +} +static inline PatternQuarter* GetSavedPatternQuarter(u8 patternq) { +#ifdef HALF_FLASH + return (PatternQuarter*)zero; +#endif + if (patternq >= 24*4) + return (PatternQuarter*)zero; + FlashPage* fp = GetFlashPagePtr(latestpagesidx[patternq + FIRST_PATTERN_IDX]); + if (fp->footer.idx != patternq+ FIRST_PATTERN_IDX || fp->footer.version != 2) + return (PatternQuarter*)zero; + return (PatternQuarter*)fp; +} +static inline SampleInfo* GetSavedSampleInfo(u8 sample0) { +#ifdef HALF_FLASH + return (SampleInfo*)zero; +#endif + if (sample0 >= 8) + return (SampleInfo*)zero; + FlashPage* fp = GetFlashPagePtr(latestpagesidx[sample0 + FIRST_SAMPLE_IDX]); + if (fp->footer.idx != sample0 + FIRST_SAMPLE_IDX || fp->footer.version != 2) + return (SampleInfo*)zero; + return (SampleInfo*)fp; +} + +u16 computehash(const void* data, int nbytes) { + u16 hash = 123; + const u8* src = (const u8 * )data; + for (int i=0;i<nbytes;++i) + hash = hash* 23 + *src++; + return hash; +} + +const static bool IsGenDirty(int gen) { + return ramtime[gen] != flashtime[gen]; +} +void SwapParams(int a, int b) { + for (int k = 0; k < 8; ++k) { + int t = rampreset.params[a][k]; + rampreset.params[a][k] = rampreset.params[b][k]; + rampreset.params[b][k] = t; + } +} +bool CopyPresetToRam(bool force) { + if (rampreset_idx == sysparams.curpreset && !force) + return true; // nothing to do + if (updating_bank2 || IsGenDirty(GEN_PRESET)) return false; // not copied yet + memcpy(&rampreset, GetSavedPreset(sysparams.curpreset), sizeof(rampreset)); + for (int m = 1; m < M_LAST; ++m) + rampreset.params[P_HEADPHONE][m] = 0; + // upgrade rampreset.version to CUR_PRESET_VERSION + if (rampreset.version == 0) { + rampreset.version = 1; + // swappin these around ;) + SwapParams(P_MIX_WIDTH,P_ACCEL_SENS); + rampreset.params[P_MIX_WIDTH][0] = HALF; // set default + } + if (rampreset.version == 1) { + rampreset.version = 2; + // insert a new lfo shape at LFO_SAW + for (int p = 0; p < 4; ++p) { + s16* data = rampreset.params[P_ASHAPE + p * 6]; + *data = (*data * (LFO_LAST - 1)) / (LFO_LAST); // rescale to add extra enum entry + if (*data >= (LFO_SAW * FULL) / LFO_LAST) // and shift high numbers up + *data += (1 * FULL) / LFO_LAST; + } + } + rampreset_idx = sysparams.curpreset; + return true; +} +bool CopySampleToRam(bool force) { + if (ramsample1_idx == cur_sample1 && !force) + return true; // nothing to do + if (updating_bank2 || IsGenDirty(GEN_SAMPLE)) return false; // not copied yet + if (cur_sample1 == 0) + memset(&ramsample, 0, sizeof(ramsample)); + else + memcpy(&ramsample, GetSavedSampleInfo(cur_sample1 - 1), sizeof(ramsample)); + ramsample1_idx = cur_sample1; + return true; +} +bool CopyPatternToRam(bool force) { + if (rampattern_idx == cur_pattern && !force) + return true; // nothing to do + if (updating_bank2 || IsGenDirty(GEN_PAT0) || IsGenDirty(GEN_PAT1) || IsGenDirty(GEN_PAT2) || IsGenDirty(GEN_PAT3)) + return false; // not copied yet + for (int i = 0; i < 4; ++i) + memcpy(&rampattern[i], GetSavedPatternQuarter((cur_pattern) * 4 + i), sizeof(rampattern[0])); + rampattern_idx = cur_pattern; + return true; +} + + +u8 next_free_page=0; +u32 next_seq = 0; +void InitParamsOnBoot(void) { + u8 dummypage = 0; + memset(latestpagesidx, dummypage, sizeof(latestpagesidx)); + memset(backuppagesidx, dummypage, sizeof(backuppagesidx)); + u32 highest_seq = 0; + next_free_page = 0; + memset(&sysparams, 0, sizeof(sysparams)); +#ifndef HALF_FLASH + // scan for the latest page for each object + for (int page = 0 ; page < 255; ++page) { + FlashPage* p = GetFlashPagePtr(page); + int i = p->footer.idx; + if (i >= LAST_IDX) + continue;// skip blank + if (p->footer.version < 2) + continue; // skip old + u16 check = computehash(p, 2040); + if (check != p->footer.crc) { + DebugLog("flash page %d has a bad crc!\r\n", page); + if (page == dummypage) { + // shit, the dummy page is dead! move to a different dummy + for (int i = 0; i < sizeof(latestpagesidx); ++i) if (latestpagesidx[i] == dummypage) + latestpagesidx[i]++; + for (int i = 0; i < sizeof(backuppagesidx); ++i) if (backuppagesidx[i] == dummypage) + backuppagesidx[i]++; + dummypage++; + } + continue; + } + if (p->footer.seq > highest_seq) { + highest_seq = p->footer.seq; + next_free_page = page + 1; + sysparams = p->sysparams; + } + FlashPage* existing = GetFlashPagePtr(latestpagesidx[i]); + if (existing->footer.idx!=i || p->footer.seq > existing->footer.seq || existing->footer.version<2) + latestpagesidx[i] = page; + } +#endif + next_seq = highest_seq + 1; + memcpy(backuppagesidx, latestpagesidx, sizeof(backuppagesidx)); + + // clear remaining state + edit_preset_pending = -1; + edit_pattern_pending = -1; + edit_sample1_pending = -1; + rampattern_idx = -1; + ramsample1_idx = -1; + rampreset_idx = -1; + edit_sample0 = 0; + // relocate the first preset and pattern into ram + copyrequest = 255; + for (int i = 0; i < GEN_LAST; ++i) { + ramtime[i] = 0; + flashtime[i] = 0; + } + codec_setheadphonevol(sysparams.headphonevol + 45); + last_preset_selection_rotstep = sysparams.curpreset; +} + +int getheadphonevol(void) { // for emu really + return sysparams.headphonevol + 45; +} + +u8 AllocAndEraseFlashPage(void) { +#ifdef HALF_FLASH + return 255; +#endif + while (1) { + FlashPage* p = GetFlashPagePtr(next_free_page); + bool inuse = next_free_page == 255; + inuse |= (p->footer.idx < LAST_IDX&& latestpagesidx[p->footer.idx] == next_free_page); + inuse |= (p->footer.idx < LAST_PRESET_IDX&& backuppagesidx[p->footer.idx] == next_free_page); + if (inuse) { + ++next_free_page; + continue; + } + DebugLog("erasing flash page %d\r\n", next_free_page); + flash_erase_page(next_free_page); + return next_free_page++; + } +} + + +void ProgramPage(void* datasrc, u32 datasize, u8 index) { +#ifndef HALF_FLASH + + updating_bank2 = 1; + HAL_FLASH_Unlock(); + u8 page = AllocAndEraseFlashPage(); + u8* dst = (u8*)(FLASH_ADDR_256 + page * 2048); + flash_program_block(dst, datasrc, datasize); + flash_program_block(dst + 2048 - sizeof(SysParams) - sizeof(PageFooter), &sysparams, sizeof(SysParams)); + PageFooter f; + f.idx = index; + f.seq = next_seq++; + f.version = 2; + f.crc = computehash(dst, 2040); + flash_program_block(dst + 2040, &f, 8); + HAL_FLASH_Lock(); + latestpagesidx[index] = page; + updating_bank2 = 0; +#endif +} + +void SetPreset(u8 preset, bool force) { + if (preset >= 32) + return; + if (preset == sysparams.curpreset && !force) + return; + sysparams.curpreset = preset; + CopyPresetToRam(force); + ramtime[GEN_SYS]=millis(); +} +/* +void SetPattern(u8 pattern, bool force) { + if (pattern >= 24) + return; + if (pattern == cur_pattern && !force) + return; + cur_pattern = pattern; + CopyPatternToRam(force); + ramtime[GEN_SYS]=millis(); +}*/ + + +int GetParam(u8 paramidx, u8 mod) { + if (paramidx == P_HEADPHONE) + return mod ? 0 : GetHeadphoneAsParam(); + return rampreset.params[paramidx][mod]; +} + +void EditParamNoQuant(u8 paramidx, u8 mod, s16 data) { + if (paramidx >= P_LAST || mod >= M_LAST) + return; + if (paramidx == P_HEADPHONE) { + if (mod == M_BASE) { + data = clampi(-45, (data / (FULL / 64)) - 45, 18); + if (data == sysparams.headphonevol) + return; + sysparams.headphonevol = data; + ramtime[GEN_SYS] = millis(); + } + return; + } + if (!CopyPresetToRam(false)) + return; // oh dear we haven't backed up the previous one yet! + int olddata = GetParam(paramidx, mod); + if (olddata == data) + return; + rampreset.params[paramidx][mod] = data; + param_eval_premod(paramidx); + //if (paramidx == P_SEQSTEP && mod == M_BASE) + // return; // dont set dirty when you're just moving the current playhead pos. + ramtime[GEN_PRESET]=millis(); +} + +void EditParamQuant(u8 paramidx, u8 mod, s16 data) { + int max = param_flags[paramidx] & FLAG_MASK; + if (max>0) { + data %= max; + if (data < 0 && !(param_flags[paramidx] & FLAG_SIGNED)) + data += max; + data = ((data * 2 + 1) * FULL) / (max * 2); + } + EditParamNoQuant(paramidx, mod, data); +} + +bool NeedWrite(int gen, u32 now) { + if (ramtime[gen] == flashtime[gen]) + return false; + if (gen == GEN_PRESET && sysparams.curpreset != rampreset_idx) { + // the current preset is not equal to the ram preset, but the ram preset is dirty! WE GOTTA WRITE IT NOW! + return true; + } + if (gen == GEN_SAMPLE && cur_sample1 != ramsample1_idx) { + // the current sample is not equal to the ram preset, but the ram sample is dirty! WE GOTTA WRITE IT NOW! + return true; + } + if (gen >= GEN_PAT0 && gen <= GEN_PAT3 && cur_pattern != rampattern_idx) { + // the current pattern is not equal to the ram pattern, but the ram pattern is dirty! WE GOTTA WRITE IT NOW! + return true; + } + + u32 age = ramtime[gen] - flashtime[gen]; + if (age > 60000) + return true; + u32 time_since_edit = now - ramtime[gen]; + return time_since_edit > 5000; +} + +void WritePattern(u32 now) { + for (int i = 0; i < 4; ++i) if (NeedWrite(GEN_PAT0 + i, now)) { + flashtime[GEN_SYS] = ramtime[GEN_SYS]; + flashtime[GEN_PAT0 + i] = ramtime[GEN_PAT0 + i]; + ProgramPage(&rampattern[i], sizeof(PatternQuarter), FIRST_PATTERN_IDX + (rampattern_idx) * 4 + i); + } +} + +void WriteSample(u32 now) { + if (NeedWrite(GEN_SAMPLE, now)) { + flashtime[GEN_SYS] = ramtime[GEN_SYS]; + flashtime[GEN_SAMPLE] = ramtime[GEN_SAMPLE]; + if (ramsample1_idx > 0) + ProgramPage(&ramsample, sizeof(SampleInfo), FIRST_SAMPLE_IDX + ramsample1_idx - 1); + } +} + +void WritePreset(u32 now) { + if (NeedWrite(GEN_PRESET, now) || NeedWrite(GEN_SYS, now)) { + flashtime[GEN_SYS] = ramtime[GEN_SYS]; + flashtime[GEN_PRESET] = ramtime[GEN_PRESET]; + ProgramPage(&rampreset, sizeof(Preset), rampreset_idx); + } +} + + + +void PumpFlashWrites(void) { + if (enable_audio != EA_PLAY) + return; + u32 now = millis(); + + if (copyrequest != 255) { + // we want to copy TO copyrequest, FROM copyfrompreset + if (copyrequest & 128) { + // wipe! + copyrequest &= 63; + if (copyrequest < 32) { + memcpy(&rampreset, &init_params, sizeof(rampreset)); + ramtime[GEN_PRESET] = now; + } + else if (copyrequest < 64 - 8) { + memset(&rampattern, 0, sizeof(rampattern)); + ramtime[GEN_PAT0] = now; + ramtime[GEN_PAT1] = now; + ramtime[GEN_PAT2] = now; + ramtime[GEN_PAT3] = now; + } + else { + memset(&ramsample, 0, sizeof(ramsample)); + ramtime[GEN_SAMPLE] = now; + } + } + else if (copyrequest<64) { + // copy! + if (copyrequest < 32) { + if (copyrequest == copyfrompreset) { + // toggle + WritePreset(now + 100000); // flush any writes + int t = backuppagesidx[copyfrompreset]; + backuppagesidx[copyfrompreset] = latestpagesidx[copyfrompreset]; + latestpagesidx[copyfrompreset] = t; + memcpy(&rampreset, GetSavedPreset(sysparams.curpreset), sizeof(rampreset)); + + } + else { + // copy preset + ProgramPage(GetSavedPreset(copyfrompreset), sizeof(Preset), copyrequest); + } + SetPreset(copyrequest, true); + } + else if (copyrequest < 64 - 8) { + int srcpat = copyfrompattern; + int dstpat = copyrequest - 32; + /*if (srcpat == dstpat) { toggle not available for patterns + // toggle + WritePattern(now + 100000); // flush any writes + for (int i = 0; i < 4; ++i) { + int j = srcpat * 4 + i + 32; + int t = backuppagesidx[j]; + backuppagesidx[j] = latestpagesidx[j]; + latestpagesidx[j] = t; + memcpy(&rampattern[i], GetSavedPatternQuarter(srcpat*4+i), sizeof(PatternQuarter)); + } + } + else */ + { + // copy pattern + for (int i = 0; i < 4; ++i) + ProgramPage(GetSavedPatternQuarter(srcpat * 4 + i), sizeof(PatternQuarter), 32 + dstpat * 4 + i); + } + EditParamQuant(P_SEQPAT, M_BASE, dstpat); + } + } + copyrequest = 255; + editmode = EM_PLAY; + } + + + WritePattern(now); + WriteSample(now); + WritePreset(now); + +} + +static Preset const init_params = { + .looplen_step=8, + .version=CUR_PRESET_VERSION, + .params= + { + [P_SENS] = {HALF}, + [P_DRIVE] = {0}, + [P_A] = {EIGHTH}, + [P_D] = {QUARTER}, + [P_S] = {FULL}, + [P_R] = {EIGHTH}, + + [P_OCT] = {0,0,0}, + [P_PITCH] = {0,0,0}, + [P_SCALE] = {QUANT(S_MAJOR,S_LAST)}, + [P_MICROTUNE] = {EIGHTH}, + [P_STRIDE] = {QUANT(7,13)}, + [P_INTERVAL] = {(0 * FULL) / 12}, + [P_ROTATE] = {0,0,0}, + + [P_NOISE] = {0,0,0}, + + [P_SMP_RATE] = {HALF}, + [P_SMP_GRAINSIZE] = {HALF}, + [P_SMP_TIME] = {HALF}, + + + // [P_ARPMODE]={QUANT(ARP_UP,ARP_LAST)}, + [P_ARPDIV] = {QUANT(2,DIVISIONS_MAX) }, + [P_ARPPROB] = {FULL}, + [P_ARPLEN] = {QUANT(8,17)}, + [P_ARPOCT] = {QUANT(0,4)}, + [P_GLIDE] = {0}, + + [P_SEQMODE] = {QUANT(SEQ_FWD,SEQ_LAST)}, + [P_SEQDIV] = {QUANT(6,DIVISIONS_MAX+1)}, + [P_SEQPROB] = {FULL}, + [P_SEQLEN] = {QUANT(8,17)}, + [P_SEQPAT] = {QUANT(0,24)}, + [P_SEQSTEP] = {0}, + [P_TEMPO] = {0}, + + [P_GATE_LENGTH] = {FULL}, + + //[P_DLSEND]={HALF}, + [P_DLTIME] = {QUANT(3,8)}, + [P_DLFB] = {HALF}, + //[P_DLCOLOR]={FULL}, + [P_DLWOB] = {QUARTER}, + [P_DLRATIO] = {FULL}, + + [P_RVSEND]={QUARTER}, + [P_RVTIME] = {HALF}, + [P_RVSHIM] = {QUARTER}, + //[P_RVCOLOR]={FULL-QUARTER}, + [P_RVWOB] = {QUARTER}, + //[P_RVUNUSED]={0}, + + + [P_MIXSYNTH] = {HALF}, + [P_MIX_WIDTH] = {(HALF * 7)/8}, + [P_MIXINWETDRY] = {0}, +#ifdef EMU + [P_MIXINPUT] = {0}, +#else + [P_MIXINPUT] = {HALF}, +#endif + [P_MIXWETDRY] = {0}, + +#ifdef NEW_LAYOUT + [P_A2] = {EIGHTH}, + [P_D2] = {QUARTER}, + [P_S2] = {FULL}, + [P_R2] = {EIGHTH}, + [P_SWING] = {0}, +#else + [P_ENV_RATE] = {QUARTER}, + [P_ENV_REPEAT] = {0}, + [P_ENV_WARP] = {-FULL}, +#endif + [P_ENV_LEVEL] = {HALF}, + [P_CV_QUANT] = {QUANT(CVQ_OFF,CVQ_LAST)}, + + [P_AOFFSET] = {0}, + [P_ASCALE] = {HALF}, + [P_ADEPTH] = {0}, + [P_AFREQ] = {0}, + //[P_ASHAPE] = {QUANT(LFO_ENV,LFO_LAST)}, + [P_AWARP] = {0}, + + [P_BOFFSET] = {0}, + [P_BSCALE] = {HALF}, + [P_BDEPTH] = {0}, + [P_BFREQ] = {100}, + [P_BSHAPE] = {0}, + [P_BWARP] = {0}, + + [P_XOFFSET] = {0}, + [P_XSCALE] = {HALF}, + [P_XDEPTH] = {0}, + [P_XFREQ] = {-123}, + [P_XSHAPE] = {0}, + [P_XWARP] = {0}, + + [P_YOFFSET] = {0}, + [P_YSCALE] = {HALF}, + [P_YDEPTH] = {0}, + [P_YFREQ] = {-315}, + [P_YSHAPE] = {0}, + [P_YWARP] = {0}, + + [P_ACCEL_SENS] = {HALF}, + } +}; // init params + + +u8 lfo_history[16][4]; +u8 lfo_history_pos; +uint64_t lfo_pos[4]; +u16 finger_rnd[8] = { 0, 1 << 12, 2 << 12, 3 << 12, 4 << 12, 5 << 12, 6 << 12, 7 << 12 }; // incremented by a big prime each time the finger is triggered +u16 any_rnd = { 8 << 12 }; // incremented every time any finger goes down +int tilt16 = 0; // average tilt, +int env16 = 0; // global attack/decay env - TODO +int pressure16 = 0; // max pressure +static inline int index_to_tilt16(int fingeridx) { + return fingeridx * 16384 - (28672*2); +} +static inline int param_eval_int_noscale(u8 paramidx, int rnd, int env16, int pressure16) { // 16 bit fp + s16* p = rampreset.params[paramidx]; + int tv = params_premod[paramidx]; // p[M_BASE] * 65538; + if (p[M_RND]) { + u16 ri = (u16)(rnd + paramidx); + if (p[M_RND] > 0) + // unsigned uniform distribution + tv += (rndtab[ri] * p[M_RND]) << 8; + else { + // signed! triangular distribution + ri += ri; + tv += (((int)rndtab[ri] - (int)rndtab[ri - 1]) * p[M_RND]) << 8; + } + } + //tv += (tilt16 * p[M_TILT]); + tv += (env16 * p[M_ENV]); + tv += (maxi(0,pressure16) * p[M_PRESSURE]); + /* + tv += (mod_cur[M_A] * p[M_A]); + tv += (mod_cur[M_B] * p[M_B]); + tv += (mod_cur[M_X] * p[M_X]); + tv += (mod_cur[M_Y] * p[M_Y]); + */ + u8 flags = param_flags[paramidx]; + u8 maxi = flags & FLAG_MASK; + return clampi(tv >> FULLBITS, (flags & FLAG_SIGNED) ? -65536 : 0, maxi ? 65535 : 65536); +} + +static inline int param_eval_int(u8 paramidx, int rnd, int env16, int pressure16) { // 16 bit fp + u8 flags = param_flags[paramidx]; + u8 maxi = flags & FLAG_MASK; + int tv = param_eval_int_noscale(paramidx, rnd, env16, pressure16); + if (maxi) { + tv = (tv * maxi) >> 16; + } + return tv; +} + |
