├── wrInterpolate.h ├── .gitignore ├── wrShaper.h ├── wrHistory.h ├── wrInterpolate.c ├── wrResamp2.h ├── wrClip.h ├── wrBlocks.h ├── wrResamp.h ├── wrWaveGuide.h ├── wrOscSine.h ├── wrDelay.h ├── wrVtl.h ├── wrLpGate.h ├── wrMix.h ├── wrBlocks.c ├── wrTransport.h ├── wrHead.h ├── wrWaveGuide.c ├── wrResamp2.c ├── wrClip.c ├── wrHistory.c ├── wrMix.c ├── wrFuncGen.h ├── wrFilter.h ├── wrDelay.c ├── wrResamp.c ├── wrVtl.c ├── wrLpGate.c ├── wrTransport.c ├── wrHead.c ├── wrFilter.c ├── wrFuncGen.c ├── wrShaper.c └── wrOscSine.c /wrInterpolate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | float interp_linear_2pt( float coeff, float* zeroth ); 4 | float interp_hermite_4pt( float coeff, float* zeroth ); 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Object files 4 | *.o 5 | *.d 6 | *.ko 7 | *.obj 8 | *.elf 9 | *.dmp 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Libraries 16 | *.lib 17 | *.a 18 | *.la 19 | *.lo 20 | 21 | # Shared objects (inc. Windows DLLs) 22 | *.dll 23 | *.so 24 | *.so.* 25 | *.dylib 26 | 27 | # Executables 28 | *.exe 29 | *.out 30 | *.app 31 | *.i*86 32 | *.x86_64 33 | *.hex 34 | 35 | # Debug files 36 | *.dSYM/ 37 | *.su 38 | 39 | *.swp 40 | -------------------------------------------------------------------------------- /wrShaper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern const float log_lut[]; // make LUT public to project 6 | 7 | float shaper_apply( float input 8 | , uint8_t zone // [0:3] 9 | , float coeff // (0,1) 10 | ); 11 | float* shaper_apply_v( float shape 12 | , float* shape_audio 13 | , float* io 14 | , int b_size 15 | ); 16 | -------------------------------------------------------------------------------- /wrHistory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../wrLib/wrQueue.h" 4 | 5 | typedef struct{ 6 | int max_stages; 7 | int max_bsize; 8 | float* history; 9 | float** h; 10 | queue_t* q; 11 | } history_t; 12 | 13 | history_t* history_init( int max_stages, int max_block_size ); 14 | void history_deinit( history_t* self ); 15 | 16 | float* history_write_v( history_t* self, float* in, int size ); 17 | float* history_read_v( history_t* self, int stages, float* out, int size ); 18 | -------------------------------------------------------------------------------- /wrInterpolate.c: -------------------------------------------------------------------------------- 1 | #include "wrInterpolate.h" 2 | 3 | float interp_linear_2pt( float coeff, float* zeroth ){ 4 | return zeroth[0] + coeff*(zeroth[1] - zeroth[0]); 5 | } 6 | 7 | float interp_hermite_4pt( float coeff, float* zeroth ){ 8 | float* y = zeroth; 9 | float c0 = y[0]; 10 | float c1 = 0.5*(y[1] - y[-1]); 11 | float c2 = y[-1] - 2.5*y[0] + 2*y[1] - 0.5*y[2]; 12 | float c3 = 0.5*(y[2] - y[-1]) + 1.5*(y[0] - y[1]); 13 | return ((c3 * coeff + c2) * coeff + c1) * coeff + c0; 14 | } 15 | -------------------------------------------------------------------------------- /wrResamp2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // wrap buffer zero to buf_len 4 | float buffer_peek( float* buf, int buf_len, float phase ); 5 | 6 | // provided buf must have leading & trailing samples 7 | float buffer_peek_unsafe( float* buf, float phase ); 8 | 9 | // TODO: benchmark against basic for loop around buffer_peek 10 | float* buffer_peek_v( float* io 11 | , float* buf 12 | , int buf_len 13 | , float phase // initial sample 14 | , float* rate // step through buf with this array 15 | , int size 16 | ); 17 | -------------------------------------------------------------------------------- /wrClip.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct clip_soft{ 4 | float t; // threshold 5 | float g; // gain 6 | float c; // parabolic coefficient 7 | float o; // parabolic offset (aka max gain) 8 | } clip_soft_t; 9 | 10 | clip_soft_t* clip_soft_init_default( void ); 11 | clip_soft_t* clip_soft_init( float threshold, float gain ); 12 | void clip_soft_deinit( clip_soft_t* self ); 13 | 14 | void clip_soft_set_threshold( clip_soft_t* self, float threshold ); 15 | void clip_soft_set_gain( clip_soft_t* self, float gain ); 16 | 17 | float clip_soft_step( clip_soft_t* self, float in ); 18 | float* clip_soft_step_v( clip_soft_t* self, float* in, int size ); 19 | -------------------------------------------------------------------------------- /wrBlocks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | float* b_cp( float* dest, float src, int size ); // memset 7 | float* b_cp_v( float* dest, float* src, int size ); 8 | float* b_add( float* io, float add, int size ); 9 | float* b_sub( float* io, float sub, int size ); 10 | float* b_mul( float* io, float mul, int size ); 11 | float* b_accum_v( float* dest, float* src, int size ); // += *src 12 | float* b_accum_vv( float** signals, int count, int size ); // reduce(+=) 13 | 14 | typedef float (b_fn_t)( float in ); 15 | float* b_map( b_fn_t fn, float* io, int size ); 16 | 17 | typedef float* (b_reducer_t)( float* dest, float* src, int size ); 18 | float* b_reduce_vv( b_reducer_t reduce, float** signals, int count, int size ); 19 | -------------------------------------------------------------------------------- /wrResamp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct IO_block { 4 | int size; 5 | float* audio; 6 | } IO_block_t; // dsp_block.h, tape.h, rhead.h 7 | 8 | IO_block_t* resamp_to( float* speed 9 | , IO_block_t* codec 10 | , IO_block_t* tapeio 11 | , int s_origin 12 | , float s_interp 13 | ); 14 | IO_block_t* resamp_from( float* speed 15 | , IO_block_t* tapeio 16 | , int s_origin 17 | , float s_interp 18 | , IO_block_t* codec 19 | // , int b_size 20 | ); 21 | -------------------------------------------------------------------------------- /wrWaveGuide.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "wrDelay.h" 5 | 6 | typedef struct{ 7 | float max_time; 8 | float time; 9 | float feedback; 10 | // private 11 | float _mem; // TODO init 12 | int max_samps; 13 | int read; 14 | int write; 15 | delay_t* del[2]; 16 | } waveguide_t; 17 | 18 | int waveguide_init( waveguide_t* self, float max_time 19 | , float time 20 | ); 21 | void waveguide_set_ms( waveguide_t* self, float time ); 22 | float waveguide_get_ms( waveguide_t* self ); 23 | void waveguide_set_feedback( waveguide_t* self, float feedback ); 24 | float waveguide_get_feedback( waveguide_t* self ); 25 | float waveguide_step( waveguide_t* self, float in ); 26 | float* waveguide_step_v( waveguide_t* self, float* in 27 | , uint16_t size 28 | ); 29 | -------------------------------------------------------------------------------- /wrOscSine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define LUT_SIN_SIZE ((uint32_t)1024) 6 | #define LUT_SIN_HALF (LUT_SIN_SIZE >> 1) 7 | extern const float sine_lut[]; 8 | 9 | typedef struct osc_sine{ 10 | float rate; 11 | float id; 12 | int8_t zero_x; 13 | } osc_sine_t; 14 | 15 | // initialization 16 | osc_sine_t* sine_init( void ); 17 | 18 | // input fns 19 | void osc_sine_time( osc_sine_t* self, float time ); 20 | void osc_sine_reset( osc_sine_t* self ); 21 | 22 | // status 23 | int8_t osc_sine_get_zc( osc_sine_t* self ); 24 | 25 | // process 26 | float osc_sine_step( osc_sine_t* self, float fm ); 27 | void osc_sine_process_v( osc_sine_t* self 28 | , uint16_t b_size 29 | , float* exp_fm 30 | , float* lin_fm 31 | , float* out ); 32 | float* sine_process_base_v( osc_sine_t* self 33 | , float* out 34 | , uint16_t b_size ); 35 | -------------------------------------------------------------------------------- /wrDelay.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct{ 4 | float* buffer; 5 | float max_time; 6 | float max_samps; 7 | float rate; 8 | float feedback; 9 | float tap_fb; 10 | float tap_read; 11 | float tap_write; 12 | } delay_t; 13 | 14 | delay_t* delay_init( float max_time 15 | , float time 16 | ); 17 | void delay_deinit( delay_t* self ); 18 | void delay_set_ms( delay_t* self, float time ); 19 | void delay_set_time_percent( delay_t* self, float percent ); 20 | void delay_set_rate( delay_t* self, float rate ); 21 | void delay_set_read_phase( delay_t* self, float percent ); 22 | float delay_get_ms( delay_t* self ); 23 | void delay_set_feedback( delay_t* self, float feedback ); 24 | float delay_get_feedback( delay_t* self ); 25 | float delay_step( delay_t* self, float in ); 26 | float* delay_step_v( delay_t* self, float* buffer 27 | , int b_size 28 | ); 29 | -------------------------------------------------------------------------------- /wrVtl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef enum{ vtl_mode_transient 6 | , vtl_mode_sustain 7 | , vtl_mode_cycle 8 | } vtl_mode_t; 9 | 10 | typedef struct{ 11 | float dest; 12 | float level; 13 | float vel; 14 | float rtime; 15 | float ftime; 16 | vtl_mode_t mode; 17 | uint8_t state; // RISE ? FALL etc 18 | } vtl_t; 19 | 20 | // variable speed & ∆rise/fall relationship 21 | vtl_t* vtl_init( void ); 22 | void vtl_deinit( vtl_t* self ); 23 | void vtl_mode( vtl_t* self 24 | , vtl_mode_t mode 25 | ); 26 | void vtl_params( vtl_t* self 27 | , float speed 28 | , float ARratio 29 | ); 30 | void vtl_dest( vtl_t* self 31 | , float dest 32 | ); 33 | float vtl_get_level( vtl_t* self ); 34 | float vtl_step( vtl_t* self ); 35 | 36 | float* vtl_step_v( vtl_t* self 37 | , float* out 38 | , int b_size 39 | ); 40 | -------------------------------------------------------------------------------- /wrLpGate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define LOG_VOL_CONST ((float)(0.1/1.1)) 6 | #define HPF_COEFF ((float)(0.997)) 7 | 8 | #define LPGATE_HPF_OFF 0 9 | #define LPGATE_HPF_ON 1 10 | #define LPGATE_VCA 0 11 | #define LPGATE_FILTER 1 12 | 13 | typedef struct lpgate{ 14 | float level; 15 | float* (*lpgate_fnptr)( struct lpgate* self 16 | , float* level 17 | , float* buffer 18 | , int b_size 19 | ); 20 | float prev_lpf; 21 | float prev_out; 22 | uint8_t hpf; 23 | uint8_t filter; 24 | } lpgate_t; 25 | 26 | lpgate_t* lpgate_init( uint8_t hpf, uint8_t filter ); 27 | void lpgate_deinit( lpgate_t* self ); 28 | 29 | void lpgate_hpf_mode( lpgate_t* self, uint8_t hpf ); 30 | void lpgate_filter_mode( lpgate_t* self, uint8_t filter ); 31 | // level input expects (0-1) 32 | float lpgate_step( lpgate_t* self, float level, float in ); 33 | float* lpgate_v( lpgate_t* self 34 | , float* level 35 | , float* buffer 36 | , int b_size 37 | ); 38 | -------------------------------------------------------------------------------- /wrMix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef struct mix_tanh { 7 | float bus; // mix bus 8 | filter_dc_t* hpf; 9 | uint16_t b_size;// block size 10 | float* bus_v; // mix bus vector 11 | } mix_tanh_t; 12 | 13 | typedef struct mix_peaks { 14 | float bus; // mix bus 15 | uint16_t b_size;// block size 16 | float* scale; // inverse scaler table 17 | float* bus_v; // mix bus vector 18 | } mix_peaks_t; 19 | 20 | // TANH 21 | int8_t mix_tanh_init(mix_tanh_t* mix, uint16_t size); 22 | // per-sample 23 | void mix_tanh_add(mix_tanh_t* mix, float input); 24 | float mix_tanh_step(mix_tanh_t* mix); 25 | // vectorized 26 | void mix_tanh_add_v(mix_tanh_t* mix, float* input); 27 | void mix_tanh_v(mix_tanh_t* mix, float* output); 28 | 29 | // PEAKS 30 | int8_t mix_peaks_init(mix_peaks_t* mix, uint16_t size, uint16_t chans); 31 | // per-sample 32 | void mix_peaks_add(mix_peaks_t* mix, float input, uint16_t ix); 33 | float mix_peaks_step(mix_peaks_t* mix); 34 | // vectorized 35 | void mix_peaks_add_v(mix_peaks_t* mix, float* input, uint16_t ix); 36 | void mix_peaks_v(mix_peaks_t* mix, float* output); 37 | 38 | /*void mix_compress_clear(void); 39 | void mix_compress_add(float* input, uint16_t size); 40 | void mix_compress_calc(float* output, uint16_t size); 41 | 42 | void mix_peaks_clear(void); 43 | void mix_peaks_add(float* input, uint16_t scale, uint16_t size); 44 | */ 45 | -------------------------------------------------------------------------------- /wrBlocks.c: -------------------------------------------------------------------------------- 1 | #include "wrBlocks.h" 2 | 3 | float* b_cp( float* dest, float src, int size ){ 4 | float* d = dest; 5 | for( int i=0; i 4 | #include "wrFilter.h" // filter_lp1_t 5 | 6 | typedef enum{ transport_motor_standard 7 | , transport_motor_quick 8 | , transport_motor_instant 9 | } transport_motor_speed_t; 10 | 11 | typedef struct{ 12 | float max_speed; 13 | 14 | float accel_standard; 15 | float accel_quick; 16 | float accel_seek; 17 | float accel_nudge; 18 | 19 | float nudge_release; 20 | } std_speeds_t; 21 | 22 | typedef struct{ 23 | uint8_t active; 24 | int8_t tape_islocked; 25 | 26 | filter_lp1_t* speed_slew; // smoothing for speed changes 27 | float speed_active; 28 | float speed_inactive; 29 | 30 | filter_lp1_t* speed_manual; // smoothing for manual changes 31 | float nudge; // how much are we currently nudging? 32 | float nudge_accum; 33 | 34 | std_speeds_t speeds; 35 | } transport_t; 36 | 37 | transport_t* transport_init( void ); 38 | void transport_deinit( transport_t* self ); 39 | 40 | void transport_active( transport_t* self 41 | , uint8_t active 42 | , transport_motor_speed_t slew 43 | ); 44 | void transport_change_std_speeds( transport_t* self 45 | , std_speeds_t speeds 46 | ); 47 | void transport_speed_active( transport_t* self, float speed ); 48 | void transport_speed_inactive( transport_t* self, float speed ); 49 | void transport_nudge( transport_t* self, float delta ); 50 | 51 | uint8_t transport_is_active( transport_t* self ); 52 | float transport_get_speed( transport_t* self ); 53 | 54 | float* transport_speed_block( transport_t* self 55 | , float* buffer 56 | , int b_size 57 | ); 58 | 59 | uint8_t transport_is_tape_moving( transport_t* self ); 60 | -------------------------------------------------------------------------------- /wrHead.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wrFilter.h" // filter_lp 4 | #include "wrResamp.h" 5 | 6 | #define HEAD_SLEW_RECORD ((float)(0.05)) 7 | #define HEAD_SLEW_FEEDBACK ((float)(0.05)) 8 | #define HEAD_SLEW_MONITOR ((float)(0.05)) 9 | 10 | #define INVALID_SAMP 0x7FFFFFFF 11 | #define BIT_HEADROOM 0 12 | #define F_TO_TAPE_SCALE ((float)(MAX24b >> BIT_HEADROOM)) 13 | 14 | typedef struct{ 15 | filter_lp1_t* record; 16 | filter_lp1_t* feedback; 17 | filter_lp1_t* monitor; 18 | } rhead_t; 19 | 20 | // tapehead mode, refactored from tapedeck.h 21 | typedef enum{ READONLY 22 | , OVERDUB 23 | , OVERWRITE 24 | , LOOPER // overwrite, but returns old tape content (external fb) 25 | , tape_mode_count 26 | } tape_mode_t; 27 | 28 | typedef enum{ HEAD_Inactive = -1 29 | , HEAD_Active 30 | , HEAD_Fadein 31 | , HEAD_Fadeout 32 | } HEAD_Action_t; // motion_block_t 33 | 34 | rhead_t* RH_Init( void ); 35 | void RH_DeInit( rhead_t* self ); 36 | 37 | void RH_set_rw( rhead_t* self, tape_mode_t rmode ); 38 | 39 | void RH_set_record_level( rhead_t* self, float level ); 40 | void RH_set_record_params( rhead_t* self 41 | , float feedback 42 | , float monitor 43 | ); 44 | 45 | IO_block_t* RH_rw_process( rhead_t* self 46 | , IO_block_t* headbuf 47 | , HEAD_Action_t action 48 | , int32_t** access 49 | , int count 50 | , int* dirty 51 | , int write_offset 52 | ); 53 | float RH_rw_process_cv( rhead_t* self 54 | , float input 55 | , uint8_t action // HEAD_Action_t 56 | , int16_t* cv_read 57 | , int* dirty 58 | , int write_offset 59 | ); 60 | -------------------------------------------------------------------------------- /wrWaveGuide.c: -------------------------------------------------------------------------------- 1 | #include "wrWaveGuide.h" 2 | #include 3 | #include "wrMath.h" 4 | 5 | #define SAMPLE_RATE 48000 // FIXME how to define this globally 6 | #define MS_TO_SAMPS (SAMPLE_RATE / 1000.0) 7 | #define SAMP_AS_MS (1.0 / MS_TO_SAMPS) 8 | 9 | int waveguide_init( waveguide_t* self, float max_time 10 | , float time 11 | ) 12 | { 13 | self->del[i] = malloc( sizeof(delay_t) * 2 ); 14 | for( int i=0; i<2; i++ ){ 15 | delay_init( &(self->del[i]), max_time, time ); 16 | } 17 | self->max_time = max_time; 18 | waveguide_set_ms( self, time ); 19 | waveguide_set_feedback( self, 0.0 ); 20 | // private 21 | self->read = 0; 22 | return 0; 23 | } 24 | 25 | void waveguide_set_ms( waveguide_t* self, float time ) 26 | { 27 | for( int i=0; i<2; i++ ){ 28 | delay_set_ms( &(self->del[i]), time ); 29 | } 30 | } 31 | 32 | float waveguide_get_ms( waveguide_t* self ) 33 | { 34 | return delay_get_ms( &(self->del[0] ); 35 | } 36 | 37 | void waveguide_set_feedback( waveguide_t* self, float feedback ) 38 | { 39 | for( int i=0; i<2; i++ ){ 40 | delay_set_feedback( &(self->del[i]), feedback ); 41 | } 42 | } 43 | 44 | float waveguide_get_feedback( waveguide_t* self ) 45 | { 46 | return delay_get_feedback( &(self->del[0] ); 47 | } 48 | 49 | float waveguide_step( waveguide_t* self, float in ) 50 | { 51 | float out = delay_step( &(self->del[0]), in + self->_mem); 52 | self->_mem = -delay_step( &(self->del[0]), -out ); 53 | 54 | 55 | 56 | 57 | float out = self->buffer[self->read++]; 58 | self->read %= self->max_samps; 59 | self->buffer[self->write++] = in + out * self->feedback; 60 | self->write %= self->max_samps; 61 | return out; 62 | } 63 | float* waveguide_step_v( waveguide_t* self, float* in 64 | , uint16_t size 65 | ) 66 | { 67 | float* in2 = in; 68 | for( int i=0; i= buf_len ){ p1 -= buf_len; } 17 | if( p2 >= buf_len ){ p2 -= buf_len; } 18 | 19 | // calc coefficient 20 | float coeff = phase - (float)p0; 21 | 22 | // interpolate array to find value 23 | float samps[4] = { buf[pn1] 24 | , buf[p0] 25 | , buf[p1] 26 | , buf[p2] 27 | }; 28 | return interp_hermite_4pt( coeff, &samps[1] ); 29 | } 30 | 31 | float buffer_peek_unsafe( float* buf, float phase ){ 32 | return interp_hermite_4pt( phase - (float)(int)phase, buf ); 33 | } 34 | 35 | float* buffer_peek_v( float* io 36 | , float* buf 37 | , int buf_len 38 | , float phase // initial sample 39 | , float* rate // step through buf with this array 40 | , int size 41 | ) 42 | { 43 | // working buffers 44 | float maxrate = (rate[0] > rate[size-1]) ? rate[0] : rate[size-1]; 45 | int sampsize = maxrate*size + 4; // maybe +3? 46 | float s[sampsize]; 47 | int p0 = (int)phase; 48 | 49 | { // build contiguous source array 50 | int ix[sampsize]; 51 | int pn1 = p0 - 1; 52 | for( int i=0; i= buf_len ){ ix[i--] -= buf_len; } 60 | } 61 | for( int i=0; i= 1.0){ 74 | coeff -= 1.0; 75 | ix++; 76 | } 77 | } 78 | return io; 79 | } 80 | -------------------------------------------------------------------------------- /wrClip.c: -------------------------------------------------------------------------------- 1 | #include "wrClip.h" 2 | 3 | #include // printf 4 | #include // NULL 5 | 6 | 7 | // from emb's SoftClip (from softcut on norns) 8 | // "two-stage quadratic soft clipper with variable gain 9 | // nice odd harmonics, kinda carbon-mic sound" 10 | 11 | 12 | /////////////////////////////// 13 | // private declarations 14 | 15 | void clip_soft_calc_coeffs( clip_soft_t* self ); 16 | 17 | 18 | /////////////////////////////// 19 | // public fns 20 | 21 | clip_soft_t* clip_soft_init_default( void ) 22 | { 23 | return clip_soft_init( 0.68, 1.2 ); 24 | } 25 | clip_soft_t* clip_soft_init( float threshold, float gain ) 26 | { 27 | clip_soft_t* self = malloc( sizeof(clip_soft_t) ); 28 | if( !self ){ printf("clip_soft: couldn't malloc\n"); return NULL; } 29 | 30 | self->t = threshold; 31 | self->g = gain; 32 | clip_soft_calc_coeffs( self ); 33 | 34 | return self; 35 | } 36 | 37 | void clip_soft_deinit( clip_soft_t* self ) 38 | { 39 | free(self); self = NULL; 40 | } 41 | 42 | void clip_soft_set_threshold( clip_soft_t* self, float threshold ) 43 | { 44 | self->t = threshold; 45 | clip_soft_calc_coeffs( self ); 46 | } 47 | void clip_soft_set_gain( clip_soft_t* self, float gain ) 48 | { 49 | self->g = gain; 50 | clip_soft_calc_coeffs( self ); 51 | } 52 | 53 | #include // fabs 54 | float clip_soft_step( clip_soft_t* self, float in ) 55 | { 56 | float ax = fabs(in); 57 | 58 | if( ax > 1.0 ){ ax = 1.0; } 59 | 60 | if (ax < self->t) { 61 | return in * self->g; 62 | } else { 63 | float sx = in > 0.0 ? 1.0 : -1.0; 64 | float q = ax - 1.0; 65 | float y = (self->c * q * q) + self->o; 66 | return sx * y; 67 | } 68 | } 69 | 70 | float* clip_soft_step_v( clip_soft_t* self, float* in, int size ) 71 | { 72 | float* s = in; 73 | // FIXME: unroll the single-sample version 74 | for( int i=0; it - 1.0; 91 | if (t_1 < 0.0) { 92 | self->c = self->g / (2.0 * t_1); 93 | self->o = self->g * self->t - (self->c * t_1 * t_1); 94 | } else { 95 | self->c = 0.0; 96 | self->o = 1.0; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /wrHistory.c: -------------------------------------------------------------------------------- 1 | #include "wrHistory.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "wrBlocks.h" 7 | 8 | //typedef struct{ 9 | // int max_stages; 10 | // int max_bsize; 11 | // float* history; 12 | // float** h; 13 | //} history_t; 14 | 15 | history_t* history_init( int max_stages, int max_block_size ) 16 | { 17 | history_t* self = malloc( sizeof( history_t ) ); 18 | if( !self ){ printf("history couldn't malloc.\n"); return NULL; } 19 | 20 | self->max_stages = max_stages; 21 | self->max_bsize = max_block_size; 22 | 23 | // sample buffer 24 | self->history = malloc( sizeof(float) * self->max_stages * self->max_bsize ); 25 | if( !self->history ){ 26 | printf("history->history couldn't malloc.\n"); 27 | free(self); self = NULL; 28 | return NULL; 29 | } 30 | 31 | // pointers to each stage 32 | self->h = malloc( sizeof(float) * self->max_stages ); 33 | if( !self->h ){ 34 | printf("history->h couldn't malloc.\n"); 35 | free(self->history); self->history = NULL; 36 | free(self); self = NULL; 37 | return NULL; 38 | } 39 | 40 | // link pointers to buffer 41 | for( int i=0; ih[i] = &(self->history[i * self->max_bsize]); 43 | } 44 | 45 | self->q = queue_init( self->max_stages ); 46 | if( !self->q ){ 47 | printf("history->q couldn't init.\n"); 48 | free(self->h); self->h = NULL; 49 | free(self->history); self->history = NULL; 50 | free(self); self = NULL; 51 | return NULL; 52 | } 53 | 54 | for( int i=0; ih[i], 0.0, self->max_bsize ); // zero history 56 | queue_enqueue( self->q ); // tell the queue it's full 57 | } 58 | 59 | return self; 60 | } 61 | 62 | void history_deinit( history_t* self ) 63 | { 64 | queue_deinit( self->q ); 65 | free(self->h); self->h = NULL; 66 | free(self->history); self->history = NULL; 67 | free(self); self = NULL; 68 | } 69 | 70 | float* history_write_v( history_t* self, float* in, int size ) 71 | { 72 | queue_dequeue( self->q ); // make room in the queue 73 | int ix = queue_enqueue( self->q ); 74 | b_cp_v( self->h[ix], in, size ); 75 | return in; 76 | } 77 | 78 | float* history_read_v( history_t* self, int stage, float* out, int size ) 79 | { 80 | int oldest = queue_front( self->q ); // front of queue is longest delay 81 | int s = oldest - 1 - stage; // -1 goes to oldest, then -stage to step through 82 | while( s < 0 ){ s += self->max_stages; } 83 | return self->h[s]; 84 | } 85 | -------------------------------------------------------------------------------- /wrMix.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // INIT FUNCTIONS 8 | int8_t mix_tanh_init(mix_tanh_t* mix, uint16_t size) 9 | { 10 | int8_t err = 0; 11 | mix->bus = 0; 12 | mix->hpf = dc_init(); 13 | mix->b_size = size; 14 | mix->bus_v = NULL; 15 | mix->bus_v = malloc(sizeof(float)*size); 16 | if( mix->bus_v == NULL ){ err = 1; } 17 | for( uint16_t i=0; ibus_v[i] = 0.0; 19 | } 20 | 21 | return err; 22 | } 23 | 24 | int8_t mix_peaks_init(mix_peaks_t* mix, uint16_t size, uint16_t chans) 25 | { 26 | int8_t err = 0; 27 | mix->bus = 0; 28 | mix->b_size = size; 29 | mix->scale = NULL; 30 | mix->scale = malloc(sizeof(float)*chans); 31 | if( mix->scale == NULL ){ err = 1; } 32 | for(uint16_t i=0; iscale[i] = 1 / (float)(i+1); 34 | } 35 | mix->bus_v = NULL; 36 | mix->bus_v = malloc(sizeof(float)*size); 37 | if(mix->bus_v == NULL) { err = 2; } 38 | for( uint16_t i=0; ibus_v[i] = 0.0; 40 | } 41 | 42 | return err; 43 | } 44 | 45 | ////////// 46 | // TANH // 47 | ////////// 48 | 49 | void mix_tanh_add(mix_tanh_t* mix, float input) 50 | { 51 | mix->bus += input; 52 | } 53 | 54 | float mix_tanh_step(mix_tanh_t* mix) 55 | { 56 | float tmp = dc_step( mix->hpf, mix->bus ); 57 | mix->bus = 0; 58 | return tanh_fast(tmp); 59 | } 60 | 61 | 62 | /////////// 63 | // PEAKS // 64 | /////////// 65 | 66 | void mix_peaks_add(mix_peaks_t* mix, float input, uint16_t ix) 67 | { 68 | float tmp = input * mix->scale[ix]; 69 | // float tmp = input; 70 | if(tmp > mix->bus){ 71 | mix->bus = tmp; 72 | } 73 | } 74 | 75 | float mix_peaks_step(mix_peaks_t* mix) 76 | { 77 | float tmp = mix->bus; 78 | mix->bus = -1.0; // reset bus 79 | return tmp; 80 | } 81 | 82 | 83 | //////////// 84 | // TANH_V // 85 | //////////// 86 | 87 | void mix_tanh_add_v(mix_tanh_t* mix, float* input) 88 | { 89 | float* bus2 = mix->bus_v; 90 | 91 | for(uint16_t i=0; i<(mix->b_size); i++){ 92 | *bus2++ += *input++; 93 | } 94 | } 95 | 96 | void mix_tanh_v(mix_tanh_t* mix, float* output) 97 | { 98 | float* bus2 = mix->bus_v; 99 | 100 | for(uint16_t i=0; i<(mix->b_size); i++){ 101 | output[i] = mix->bus_v[i]; 102 | } 103 | tanh_fast_v( dc_step_v( mix->hpf 104 | , output 105 | , mix->b_size) 106 | , mix->b_size 107 | ); 108 | 109 | for(uint16_t i=0; i<(mix->b_size); i++){ 110 | *bus2++ = 0; // zero mix bus 111 | } 112 | } 113 | 114 | ///////////// 115 | // PEAKS_V // 116 | ///////////// 117 | 118 | void mix_peaks_add_v(mix_peaks_t* mix, float* input, uint16_t ix) 119 | { 120 | float tmp; 121 | float* bus2 = mix->bus_v; 122 | 123 | for(uint16_t i=0; i<(mix->b_size); i++){ 124 | tmp = (*input++) * mix->scale[ix]; 125 | if(tmp > *bus2){ 126 | *bus2 = tmp; 127 | } 128 | bus2++; 129 | } 130 | } 131 | 132 | void mix_peaks_v(mix_peaks_t* mix, float* output) 133 | { 134 | float* bus2 = mix->bus_v; 135 | float* out2 = output; 136 | 137 | for(uint16_t i=0; i<(mix->b_size); i++){ 138 | *out2++ = *bus2; 139 | *bus2++ = -1.0; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /wrFuncGen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // use 0x00800000 as float (smallest +ve value) 6 | #define MIN_POS_FLOAT (0.00000001f) 7 | 8 | // should be an enum, so more like a type for public access 9 | #define FNGEN_TRANSIENT 0 10 | #define FNGEN_SUSTAIN 1 11 | #define FNGEN_CYCLE 2 12 | 13 | typedef enum 14 | { fg_transient 15 | , fg_sustain 16 | , fg_cycle 17 | } fg_mode_t; 18 | 19 | typedef struct func_gen{ 20 | int8_t go; 21 | float id; 22 | float rate; 23 | uint8_t tr_state; 24 | float fm_ix; 25 | int8_t loop; // nb: -1 is infinite loop. +ve is number of retrigs left. 26 | uint8_t s_mode; 27 | uint8_t sustain_state; 28 | uint8_t sustaining; 29 | uint8_t zc; 30 | float r_up; 31 | float r_down; 32 | } func_gen_t; 33 | 34 | // Initialization 35 | func_gen_t* function_init( int loop ); 36 | 37 | // Param functions 38 | 39 | // Triggers 40 | void function_trig_reset ( func_gen_t* self 41 | , uint8_t state 42 | ); 43 | void function_trig_wait ( func_gen_t* self 44 | , uint8_t state 45 | ); 46 | void function_trig_sustain( func_gen_t* self 47 | , uint8_t state 48 | ); 49 | void function_trig_vari ( func_gen_t* self 50 | , uint8_t state 51 | , float cutoff 52 | ); 53 | void function_trig_burst ( func_gen_t* self 54 | , uint8_t state 55 | , float count 56 | ); 57 | void function_trig_spill( func_gen_t* self 58 | , uint8_t state 59 | , float cutoff 60 | ); 61 | 62 | void function_mode( func_gen_t* self, uint8_t mode ); 63 | void function_loop( func_gen_t* self, int8_t loop ); 64 | void function_sustain( func_gen_t* self, uint8_t sust ); 65 | void function_rate( func_gen_t* self, float rate ); 66 | void function_fm_ix( func_gen_t* self, float ix ); 67 | 68 | float function_get_rate( func_gen_t* self ); 69 | 70 | // Audio rate process 71 | void function_ramp( func_gen_t* self, float skew ); 72 | float* function_ramp_v( func_gen_t* self 73 | , float ctrl_rate 74 | , float* audio_rate 75 | , float* two_ramps 76 | , int b_size 77 | ); 78 | void function_ramp_v_global( uint16_t b_size 79 | , float ctrl_rate 80 | , float* audio_rate 81 | , float* ramp_up 82 | , float* ramp_down 83 | ); 84 | 85 | float function_step( func_gen_t* self, float fm_in ); 86 | float* function_v( func_gen_t* self 87 | , float* ramps 88 | , float* fm_return 89 | , int b_size 90 | ); 91 | void function_fmix_v( func_gen_t* self 92 | , uint16_t b_size 93 | , float* r_up 94 | , float* r_dn 95 | , float* fm_in 96 | , float* fm_ix 97 | , float* out 98 | ); 99 | 100 | float function_lookup( float id ); 101 | -------------------------------------------------------------------------------- /wrFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct filter_lp1 { 6 | float x; // destination 7 | float y; 8 | float c; 9 | } filter_lp1_t; 10 | 11 | typedef struct filter_lp1_a { 12 | float y; 13 | float c_rise; 14 | float c_fall; 15 | } filter_lp1_a_t; 16 | 17 | typedef struct filter_awin { 18 | float* history; 19 | float out; 20 | int win_size; 21 | int win_ix; 22 | float win_scale; 23 | float slope_sense; 24 | } filter_awin_t; 25 | 26 | typedef struct filter_sr { 27 | float ramp; 28 | float rate; 29 | } filter_sr_t; 30 | 31 | typedef struct _filter_dc { 32 | float coeff; 33 | float prev_in; 34 | float prev_out; 35 | } filter_dc_t; 36 | 37 | typedef struct filter_svf { 38 | float x[3]; 39 | float q; 40 | float c; 41 | uint8_t mode; 42 | int sample_rate; 43 | } filter_svf_t; 44 | 45 | // Lowpass: 1-pole 46 | filter_lp1_t* lp1_init(void); 47 | void lp1_deinit( filter_lp1_t* f ); 48 | void lp1_set_dest( filter_lp1_t* f, float in ); 49 | float lp1_get_dest( filter_lp1_t* f ); 50 | void lp1_set_out( filter_lp1_t* f, float level ); 51 | float lp1_get_out( filter_lp1_t* f ); 52 | float lp1_step( filter_lp1_t* f, float in ); 53 | float lp1_step_internal(filter_lp1_t* f); 54 | void lp1_set_coeff( filter_lp1_t* f, float c ); 55 | float lp1_get_coeff( filter_lp1_t* f ); 56 | void lp1_set_freq( filter_lp1_t* f, float freq ); 57 | void lp1_step_v( filter_lp1_t* f, float* in 58 | , float* out 59 | , uint16_t size 60 | ); 61 | float* lp1_step_c_v(filter_lp1_t* f, float* buffer 62 | , uint16_t size 63 | ); 64 | 65 | // Lowpass: 1-pole assymetrical 66 | filter_lp1_a_t* lp1_a_init( void ); 67 | void lp1_a_deinit( filter_lp1_a_t* self ); 68 | float lp1_a_step( filter_lp1_a_t* f, float in ); 69 | void lp1_a_set_coeff( filter_lp1_a_t* f, float c_rise 70 | , float c_fall ); 71 | void lp1_a_step_v( filter_lp1_a_t* f, float* in 72 | , float* out 73 | , uint16_t size ); 74 | 75 | // Switch & Ramp: smooth discontinuities 76 | filter_sr_t* switch_ramp_init( void ); 77 | void switch_ramp_deinit( filter_sr_t* f ); 78 | void switch_ramp_set_rate( filter_sr_t* f, float rate ); 79 | void switch_ramp_jump( filter_sr_t* f, float step_size ); 80 | float* switch_ramp_step_v( filter_sr_t* f, float* io 81 | , int size ); 82 | 83 | // Windowed average smoother 84 | filter_awin_t* awin_init( int win_size ); 85 | void awin_deinit( filter_awin_t* self ); 86 | void awin_slope( filter_awin_t* f, float slope_sensitivity ); 87 | float awin_step( filter_awin_t* f, float input ); 88 | float awin_get_out( filter_awin_t* f ); 89 | float awin_get_in( filter_awin_t* f ); 90 | 91 | // DC-Blocker: Leaky Integrator -> Differentiator 92 | filter_dc_t* dc_init( void ); 93 | void dc_deinit( filter_dc_t* self ); 94 | void dc_time( filter_dc_t* self, float hpc ); 95 | float dc_step( filter_dc_t* self, float in ); 96 | float* dc_step_v( filter_dc_t* self, float* buffer 97 | , int b_size 98 | ); 99 | 100 | // State-variable: 2-pole 101 | filter_svf_t* svf_init( uint8_t mode, int sample_rate ); 102 | void svf_deinit( filter_svf_t* self ); 103 | float svf_process_frame( filter_svf_t* f, float input ); 104 | float svf_step( filter_svf_t* f, float input ); 105 | void svf_set_mode( filter_svf_t* f, uint8_t mode ); 106 | void svf_set_coeff( filter_svf_t* f, float coeff ); 107 | void svf_set_freq( filter_svf_t* f, float freq ); 108 | void svf_set_q( filter_svf_t* f, float quality ); 109 | -------------------------------------------------------------------------------- /wrDelay.c: -------------------------------------------------------------------------------- 1 | #include "wrDelay.h" 2 | #include 3 | #include // printf 4 | #include "wrMath.h" 5 | 6 | #define SAMPLE_RATE 48000 // FIXME how to define this globally 7 | #define MS_TO_SAMPS (SAMPLE_RATE / 1000.0) 8 | #define SAMP_AS_MS (1.0 / MS_TO_SAMPS) 9 | 10 | // private declarations 11 | float wrap( float input, float modulo ); 12 | float peek( delay_t* self, float tap ); 13 | void poke( delay_t* self, float input ); 14 | 15 | // public defns 16 | delay_t* delay_init( float max_time 17 | , float time 18 | ){ 19 | delay_t* self = malloc( sizeof(delay_t) ); 20 | if( !self ){ printf("delay: couldn't malloc\n"); return NULL; } 21 | 22 | self->max_time = max_time; 23 | self->max_samps = max_time * MS_TO_SAMPS; 24 | self->buffer = malloc( sizeof(float) * (int)(self->max_samps + 1) ); 25 | if( !self->buffer ){ 26 | printf("delay: couldn't malloc buf\n"); 27 | free(self); 28 | return NULL; 29 | } 30 | 31 | for( int i=0; i<(int)(self->max_samps + 1); i++ ){ 32 | self->buffer[i] = 0.0; 33 | } 34 | self->rate = 1.0; 35 | self->tap_write = 0.0; 36 | self->tap_read = 0.0; 37 | self->tap_fb = 0.0; 38 | delay_set_ms( self, time ); 39 | delay_set_read_phase( self, time ); 40 | delay_set_feedback( self, 0.0 ); 41 | 42 | return self; 43 | } 44 | 45 | void delay_deinit( delay_t* self ){ 46 | free(self->buffer); self->buffer = NULL; 47 | free(self); self = NULL; 48 | } 49 | 50 | void delay_set_ms( delay_t* self, float time ){ 51 | self->tap_fb = wrap( self->tap_write - (time * MS_TO_SAMPS) 52 | , self->max_samps 53 | ); 54 | } 55 | 56 | void delay_set_time_percent( delay_t* self, float percent ){ 57 | delay_set_ms( self, self->max_time * lim_f_0_1(percent) ); 58 | } 59 | 60 | void delay_set_rate( delay_t* self, float rate ){ 61 | self->rate = lim_f( rate, 1.0/16.0, 16.0 ); 62 | } 63 | 64 | void delay_set_read_phase( delay_t* self, float percent ){ 65 | self->tap_read = wrap( self->tap_write 66 | - (lim_f_0_1(percent) * self->max_time * MS_TO_SAMPS) 67 | , self->max_samps 68 | ); 69 | } 70 | 71 | float delay_get_ms( delay_t* self ){ 72 | return SAMP_AS_MS * wrap( self->tap_write - self->tap_fb 73 | , self->max_samps 74 | ); 75 | } 76 | 77 | void delay_set_feedback( delay_t* self, float feedback ){ 78 | self->feedback = lim_f( feedback, -0.999, 0.999 ); 79 | } 80 | 81 | float delay_get_feedback( delay_t* self ){ 82 | return self->feedback; 83 | } 84 | 85 | float delay_step( delay_t* self, float in ){ 86 | self->tap_fb = wrap( self->tap_fb + self->rate 87 | , self->max_samps 88 | ); 89 | self->tap_read = wrap( self->tap_read + self->rate 90 | , self->max_samps 91 | ); 92 | self->tap_write = wrap( self->tap_write + self->rate 93 | , self->max_samps 94 | ); 95 | float out = peek( self, self->tap_read ); 96 | poke( self, in * ((self->rate >= 1.0) ? 1.0 : self->rate) 97 | + self->feedback * peek( self, self->tap_fb ) 98 | ); 99 | return out; 100 | } 101 | 102 | float* delay_step_v( delay_t* self, float* buffer 103 | , int b_size 104 | ){ 105 | float* in = buffer; 106 | float* out = buffer; 107 | for( int i=0; i= modulo ){ input -= modulo; } 116 | while( input < 0.0 ){ input += modulo; } 117 | return input; 118 | } 119 | 120 | float peek( delay_t* self, float tap ){ 121 | int ixA = (int)tap; 122 | int ixB = (int)wrap( tap + 1, self->max_samps ); 123 | float c = tap - (float)ixA; 124 | return self->buffer[ixA] + c*(self->buffer[ixB] - self->buffer[ixA]); 125 | } 126 | 127 | void poke( delay_t* self, float input ){ 128 | int ixA = (int)self->tap_write; 129 | int ixB = (int)wrap( self->tap_write + 1, self->max_samps ); 130 | float c = self->tap_write - (float)ixA; 131 | // these coeffs seem backward to me, but reversed is terrible aliasing?! 132 | self->buffer[ixA] = input * c; 133 | self->buffer[ixB] = input * (1.0 - c); 134 | } 135 | -------------------------------------------------------------------------------- /wrResamp.c: -------------------------------------------------------------------------------- 1 | #include "wrResamp.h" 2 | #include "wrMath.h" // _Abs 3 | 4 | static void interp_fast( float* codec 5 | , float* tape 6 | , float coeff 7 | , float* speed 8 | , int b_size 9 | ){ 10 | for( int i=0; i= 1.0 ){ 30 | coeff -= 1.0; 31 | tape++; 32 | } 33 | } 34 | } 35 | 36 | 37 | static void interp_normal( float* codec 38 | , float* tape 39 | , float coeff 40 | , float* speed 41 | , int b_size 42 | ){ 43 | for( int i=0; i= 1.0 ){ 53 | coeff -= 1.0; 54 | tape++; 55 | } 56 | } 57 | } 58 | 59 | 60 | static void interp_slow( float* codec 61 | , float* tape 62 | , float coeff 63 | , float* speed 64 | , int b_size 65 | ){ 66 | for( int i=0; i= 1.0 ){ 78 | coeff -= 1.0; 79 | tape++; 80 | } 81 | } 82 | } 83 | 84 | IO_block_t* resamp_to( float* speed 85 | , IO_block_t* codec 86 | , IO_block_t* tapeio 87 | , int s_origin 88 | , float s_interp 89 | // , int b_size 90 | ){ 91 | float aspd = _Abs(speed[0]); 92 | if( aspd > 1.0 ){ 93 | interp_fast( codec->audio 94 | , &(tapeio->audio[s_origin]) 95 | , s_interp 96 | , speed 97 | , codec->size 98 | ); 99 | } else if( aspd == 1.0 ){ 100 | interp_normal( codec->audio 101 | , &(tapeio->audio[s_origin]) 102 | , s_interp 103 | , speed 104 | , codec->size 105 | ); 106 | } else { 107 | interp_slow( codec->audio 108 | , &(tapeio->audio[s_origin]) 109 | , s_interp 110 | , speed 111 | , codec->size 112 | ); 113 | } 114 | return tapeio; 115 | } 116 | 117 | IO_block_t* resamp_from( float* speed 118 | , IO_block_t* tapeio 119 | , int s_origin 120 | , float s_interp 121 | , IO_block_t* codec 122 | ){ 123 | float* buf = codec->audio; 124 | float* hb = &(tapeio->audio[s_origin -1]); // wide for lagrange 125 | float co = s_interp; 126 | for( int i=0; i<(codec->size); i++ ){ 127 | float coeff[4]; 128 | // shifted from the textbook by +1 to co (shift range to 0-1) 129 | // Julius Smith III on 3rd order lagrange interpolation 130 | coeff[0] = -((co )*(co-1.0)*(co-2.0))/6.0; 131 | coeff[1] = ((co+1.0)*(co-1.0)*(co-2.0))/2.0; 132 | coeff[2] = -((co+1.0)*(co )*(co-2.0))/2.0; 133 | coeff[3] = ((co+1.0)*(co )*(co-1.0))/6.0; 134 | 135 | *buf = 0.0; 136 | for( int s=0; s<4; s++ ){ 137 | *buf += hb[s] * coeff[s]; 138 | } 139 | buf++; 140 | co += *speed++; 141 | while( co < 0.0 ){ 142 | co += 1.0; 143 | hb--; 144 | } 145 | while( co >= 1.0 ){ 146 | co -= 1.0; 147 | hb++; 148 | } 149 | } 150 | return codec; 151 | } 152 | -------------------------------------------------------------------------------- /wrVtl.c: -------------------------------------------------------------------------------- 1 | #include "wrVtl.h" 2 | 3 | #include 4 | #include // printf 5 | 6 | #include "wrGlobals.h" 7 | #include "wrMath.h" // math_get_ramps() 8 | 9 | vtl_t* vtl_init( void ){ 10 | vtl_t* self = malloc( sizeof( vtl_t ) ); 11 | if(self == NULL){ printf("VTL: can't malloc!\n"); } 12 | 13 | self->dest = 0.0; 14 | self->level = 0.0; 15 | self->vel = 1.0; 16 | vtl_mode( self, vtl_mode_sustain ); 17 | vtl_params( self, 100.0, 0.1 ); 18 | return self; 19 | } 20 | 21 | void vtl_deinit( vtl_t* self ){ 22 | free(self); self = NULL; 23 | } 24 | 25 | void vtl_mode( vtl_t* self 26 | , vtl_mode_t mode 27 | ){ 28 | self->mode = mode; 29 | if(mode != vtl_mode_sustain){ 30 | self->dest = 0.0; 31 | } 32 | } 33 | 34 | void vtl_params( vtl_t* self 35 | , float speed 36 | , float ARratio 37 | ){ 38 | self->rtime = 0.5 / (0.998 * lim_f_0_1(ARratio) + 0.001); 39 | self->ftime = 1.0 / (2.0 - (1.0 / self->rtime)); 40 | float ttime = lim_f( lim_f_0_1(speed) * 0.007 41 | , 0.000001 42 | , 1.0 43 | ); 44 | self->rtime *= ttime; 45 | self->ftime *= ttime; 46 | } 47 | 48 | // unroll this in your loop if calling it per sample (otherwise it's minor improvement) 49 | void vtl_dest( vtl_t* self 50 | , float dest 51 | ){ 52 | self->dest = dest; // set destination value 53 | 54 | if(dest > nFloor) { 55 | self->vel = dest; // treat as 'velocity' input 56 | } 57 | } 58 | 59 | float vtl_get_level( vtl_t* self ){ 60 | return self->level; 61 | } 62 | 63 | float vtl_step( vtl_t* self ){ 64 | float slew_mod; 65 | float location = self->level; 66 | 67 | // difference between current & dest 68 | float sub_diff = self->dest - self->level; 69 | 70 | if(sub_diff > 0.0) { // rising 71 | slew_mod = self->rtime; 72 | slew_mod = min_f( slew_mod + slew_mod * location * location * 2.0 73 | , 0.2); // some kind of hysteresis: out += 2 * in * previous^2 74 | self->level += slew_mod * sub_diff; 75 | 76 | 77 | if(sub_diff < nFloor) { // we're close enough! (~58dB) 78 | if(self->mode != vtl_mode_sustain) { 79 | self->dest = 0; // go toward zero if not sustaining 80 | } 81 | } 82 | } else { // falling 83 | slew_mod = self->ftime; 84 | slew_mod = min_f( slew_mod + slew_mod * location * location * 2.0 85 | , 0.2); // some kind of hysteresis: out += 2 * in * previous^2 86 | self->level += slew_mod * sub_diff; 87 | 88 | if(sub_diff > -nFloor) { // if we've gone past the dest 89 | if(self->mode == vtl_mode_cycle) { 90 | self->dest = self->vel; // rise if cycling 91 | } 92 | } 93 | } 94 | return self->level; // needs to be limited to 0-1f 95 | } 96 | 97 | float* vtl_step_v( vtl_t* self 98 | , float* out 99 | , int b_size 100 | ){ 101 | float slew_mod, slew_fix; 102 | float* out2=out; 103 | float* out3=out; 104 | uint16_t i; 105 | 106 | // difference between current & dest 107 | float sub_diff = self->dest - self->level; 108 | // 1.0 // 0.007 109 | 110 | if( sub_diff > 0.0 ){ // rising 111 | if( sub_diff < nFloor ) { // call it even 112 | if( self->mode != vtl_mode_sustain ){ 113 | self->dest = 0.0; // go toward zero 114 | slew_fix = self->ftime; 115 | } else { // sustain mode, so hold val 116 | // escape w/ fixed output value 117 | for( i=0; idest; 119 | } 120 | self->level = self->dest; // save last val 121 | return out; // EARLY EXIT 122 | } 123 | } else { // normal rise 124 | slew_fix = self->rtime; 125 | } 126 | } else { // falling 127 | if( sub_diff > -nFloor ){ // call it even 128 | if( self->mode == vtl_mode_cycle ){ 129 | if (self->dest == self->vel){ // AT MAX! 130 | self->dest = 0.0; // go to fall 131 | slew_fix = self->ftime; 132 | } else { // go to rise 133 | self->dest = self->vel; 134 | slew_fix = self->rtime; 135 | } 136 | } else { // hit bottom 137 | for( i=0; idest; 139 | } 140 | self->level = self->dest; 141 | return out; // EARLY EXIT 142 | } 143 | } else { // normal falling 144 | slew_fix = self->ftime; 145 | } 146 | } 147 | 148 | // some kind of hysteresis: out += 2 * in * previous^2 149 | slew_mod = slew_fix + slew_fix * self->level * self->level * 2.0; 150 | if(slew_mod > 0.2) { slew_mod = 0.2; } // limit rate to 1/5 per samp 151 | *out2++ = self->level + (slew_mod * sub_diff); 152 | 153 | for( i=1; idest - *out3; 156 | 157 | if( sub_diff > 0.0 ){ // rising 158 | if( sub_diff < nFloor ){ // call it even 159 | if( self->mode != vtl_mode_sustain ){ 160 | self->dest = 0.0; // go toward zero 161 | slew_fix = self->ftime; 162 | } else { // sustain mode, so hold val 163 | while( i++ < b_size ){ 164 | *out2++ = self->dest; 165 | } 166 | self->level = self->dest; // save last val 167 | return out; // EARLY EXIT 168 | } 169 | } else { // normal rise 170 | slew_fix = self->rtime; 171 | } 172 | } else { // falling 173 | if(sub_diff > -nFloor) { // call it even 174 | if( self->mode == vtl_mode_cycle ){ 175 | if (self->dest == self->vel){ // AT MAX! 176 | self->dest = 0.0; // go to fall 177 | slew_fix = self->ftime; 178 | } else { // go to rise 179 | self->dest = self->vel; 180 | slew_fix = self->rtime; 181 | } 182 | } else { // hit bottom 183 | while( i++ < b_size ){ 184 | *out2++ = self->dest; 185 | } 186 | self->level = self->dest; 187 | return out; // EARLY EXIT 188 | } 189 | } else { // normal falling 190 | slew_fix = self->ftime; 191 | } 192 | } 193 | 194 | slew_mod = slew_fix + slew_fix * *out3 * *out3 * 2.0; 195 | if(slew_mod > 0.2) { slew_mod = 0.2; } // limit rate to 1/5 per samp 196 | *out2++ = (*out3++) + (slew_mod * sub_diff); 197 | } 198 | // save 199 | self->level = *out3; 200 | 201 | return out; 202 | } 203 | -------------------------------------------------------------------------------- /wrLpGate.c: -------------------------------------------------------------------------------- 1 | #include "wrLpGate.h" 2 | 3 | #include 4 | 5 | // private declarations 6 | void _lpgate_mode_select( lpgate_t* self ); 7 | float* lpgate_v_filt( lpgate_t* self, float* level, float* buffer, int b_size ); 8 | float* lpgate_v_gate( lpgate_t* self, float* level, float* buffer, int b_size ); 9 | float* lpgate_v_filt_hpf( lpgate_t* self, float* level, float* buffer, int b_size ); 10 | float* lpgate_v_gate_hpf( lpgate_t* self, float* level, float* buffer, int b_size ); 11 | 12 | lpgate_t* lpgate_init( uint8_t hpf 13 | , uint8_t filter 14 | ){ 15 | lpgate_t* self = malloc( sizeof(lpgate_t) ); 16 | 17 | self->hpf = hpf; 18 | self->filter = filter; 19 | _lpgate_mode_select(self); 20 | 21 | self->prev_lpf = 0; 22 | self->prev_out = 0; 23 | return self; 24 | } 25 | 26 | void lpgate_deinit( lpgate_t* self 27 | ){ 28 | free(self); self = NULL; 29 | } 30 | 31 | void lpgate_hpf_mode( lpgate_t* self 32 | , uint8_t hpf 33 | ){ 34 | self->hpf = !!hpf; 35 | _lpgate_mode_select(self); 36 | } 37 | 38 | void lpgate_filter_mode( lpgate_t* self 39 | , uint8_t filter 40 | ){ 41 | self->filter = !!filter; 42 | _lpgate_mode_select(self); 43 | } 44 | void _lpgate_mode_select( lpgate_t* self 45 | ){ 46 | // [filter][hpf] 47 | static float* (*fnptr[2][2])() = 48 | { { lpgate_v_gate 49 | , lpgate_v_gate_hpf } 50 | , { lpgate_v_filt 51 | , lpgate_v_filt_hpf } 52 | }; 53 | self->lpgate_fnptr = fnptr[ self->filter ][ self->hpf ]; 54 | } 55 | 56 | float lpgate_step( lpgate_t* self 57 | , float level 58 | , float in 59 | ){ 60 | float out_lo, out_hi; 61 | 62 | if(self->filter){ // BOTH MODE (LPF -> VOL) 63 | out_lo = self->prev_lpf + 64 | (level * 65 | (in - self->prev_lpf)); 66 | out_lo *= level/(0.1f + level) + LOG_VOL_CONST; 67 | } else { 68 | out_lo = self->prev_lpf + 69 | ((0.5f + level*0.5f) * 70 | (in - self->prev_lpf)); 71 | out_lo *= level; 72 | } 73 | 74 | if(self->hpf){ // HPF ACTIVE 75 | out_hi = out_lo - self->prev_lpf + (HPF_COEFF * self->prev_out); 76 | } else{ 77 | out_hi = out_lo; 78 | } 79 | self->prev_lpf = out_lo; 80 | self->prev_out = out_hi; 81 | return out_hi; 82 | } 83 | 84 | float* lpgate_v( lpgate_t* self 85 | , float* level 86 | , float* buffer 87 | , int b_size 88 | ){ 89 | return (*self->lpgate_fnptr)( self 90 | , level 91 | , buffer 92 | , b_size 93 | ); 94 | } 95 | 96 | // private function definitions 97 | float* lpgate_v_filt_hpf( lpgate_t* self 98 | , float* level 99 | , float* buffer 100 | , int b_size 101 | ){ 102 | float lowpass[b_size]; 103 | float* lp = lowpass; 104 | float* lp2 = lowpass; 105 | float* lvl = level; 106 | float* in = buffer; 107 | 108 | // filter 109 | // TODO can set first elem of lowpass to prev_lpf & no need for separate case! 110 | *lp++ = (*lvl / (0.1 + *lvl) + LOG_VOL_CONST) 111 | * (self->prev_lpf 112 | + *lvl * (*in++ - self->prev_lpf)); 113 | lvl++; 114 | 115 | for( int i=1; iprev_lpf + (HPF_COEFF * self->prev_out); 128 | for( int i=1; iprev_lpf = *in2; 133 | self->prev_out = *out2; 134 | 135 | return buffer; 136 | } 137 | 138 | float* lpgate_v_gate_hpf( lpgate_t* self 139 | , float* level 140 | , float* buffer 141 | , int b_size 142 | ){ 143 | float lowpass[b_size]; 144 | float* lp = lowpass; 145 | float* lp2 = lowpass; 146 | float* lvl = level; 147 | float* in = buffer; 148 | 149 | // gate 150 | // TODO can set first elem of lowpass to prev_lpf & no need for separate case! 151 | *lp++ = *lvl 152 | * (self->prev_lpf 153 | + (0.5 + *lvl * 0.5) * (*in++ - self->prev_lpf)); 154 | lvl++; 155 | 156 | for( int i=1; iprev_lpf + (HPF_COEFF * self->prev_out); 168 | for( int i=1; iprev_lpf = *in2; 173 | self->prev_out = *out2; 174 | 175 | return buffer; 176 | } 177 | 178 | float* lpgate_v_filt( lpgate_t* self 179 | , float* level 180 | , float* buffer 181 | , int b_size 182 | ){ 183 | float lp; 184 | float* lvl = level; 185 | float* in = buffer; 186 | float* out = buffer; 187 | 188 | lp = (*lvl / (0.1 + *lvl) + LOG_VOL_CONST) 189 | * (self->prev_lpf 190 | + *lvl * (*in++ - self->prev_lpf)); 191 | lvl++; 192 | *out++ = lp; 193 | 194 | for( int i=1; iprev_lpf = lp; 202 | self->prev_out = lp; 203 | 204 | return buffer; 205 | } 206 | 207 | float* lpgate_v_gate( lpgate_t* self 208 | , float* level 209 | , float* buffer 210 | , int b_size 211 | ){ 212 | float lp; 213 | float* lvl = level; 214 | float* in = buffer; 215 | float* out = buffer; 216 | 217 | lp = *lvl 218 | * (self->prev_lpf 219 | + (0.5 + *lvl * 0.5) * (*in++ - self->prev_lpf)); 220 | lvl++; 221 | *out++ = lp; 222 | 223 | for( int i=1; iprev_lpf = lp; 230 | self->prev_out = lp; 231 | 232 | return buffer; 233 | } 234 | -------------------------------------------------------------------------------- /wrTransport.c: -------------------------------------------------------------------------------- 1 | #include "wrTransport.h" 2 | 3 | #include 4 | #include // printf 5 | #include "wrMath.h" 6 | 7 | 8 | transport_t* transport_init( void ) 9 | { 10 | transport_t* self = malloc( sizeof( transport_t ) ); 11 | if( !self ){ printf("wrTransport malloc failed\n"); return NULL; } 12 | 13 | self->active = 0; 14 | self->tape_islocked = 0; 15 | 16 | // default speed values 17 | transport_change_std_speeds( self 18 | , (std_speeds_t) 19 | { .max_speed = 2.0 20 | , .accel_standard = 0.001 21 | , .accel_quick = 0.05 22 | , .accel_seek = 0.001 23 | , .accel_nudge = 0.005 24 | , .nudge_release = 0.002 25 | } 26 | ); 27 | 28 | self->speed_slew = lp1_init(); 29 | lp1_set_coeff( self->speed_slew, (self->speeds).accel_standard ); 30 | 31 | self->speed_active = 1.0; 32 | self->speed_inactive = 0.0; 33 | 34 | self->speed_manual = lp1_init(); 35 | lp1_set_coeff( self->speed_manual, (self->speeds).accel_seek ); 36 | self->nudge = 0.0; 37 | self->nudge_accum = 0.0; 38 | 39 | return self; 40 | } 41 | 42 | 43 | void transport_deinit( transport_t* self ) 44 | { 45 | lp1_deinit( self->speed_manual ); 46 | lp1_deinit( self->speed_slew ); 47 | free(self); self = NULL; 48 | } 49 | 50 | 51 | void transport_active( transport_t* self 52 | , uint8_t active 53 | , transport_motor_speed_t slew 54 | ) 55 | { 56 | self->active = !!active; 57 | lp1_set_coeff( self->speed_manual 58 | , (self->active) 59 | ? (self->speeds).accel_nudge 60 | : (self->speeds).accel_seek 61 | ); 62 | lp1_set_coeff( self->speed_slew 63 | , (slew == transport_motor_standard) 64 | ? (self->speeds).accel_standard 65 | : (self->speeds).accel_quick 66 | ); 67 | if( slew == transport_motor_instant ){ 68 | lp1_set_out( self->speed_slew 69 | , transport_get_speed( self ) 70 | ); 71 | } 72 | if( self->active ){ 73 | //switch statement unlocks tape 74 | switch( self->tape_islocked ){ 75 | case -1: if( transport_get_speed( self ) > 0.0 ){ self->tape_islocked = 0; } break; 76 | case 1: if( transport_get_speed( self ) < 0.0 ){ self->tape_islocked = 0; } break; 77 | default: break; 78 | } 79 | } 80 | } 81 | 82 | 83 | void transport_speed_inactive( transport_t* self, float speed ) 84 | { 85 | float tmin = -(self->speeds).max_speed; 86 | float tmax = (self->speeds).max_speed; 87 | switch( self->tape_islocked ){ 88 | case -1: 89 | if( speed > 0.0 ){ self->tape_islocked = 0; } 90 | else{ tmin = 0.0; } 91 | break; 92 | case 1: 93 | if( speed < 0.0 ){ self->tape_islocked = 0; } 94 | else{ tmax = 0.0; } 95 | break; 96 | default: break; 97 | } 98 | self->speed_inactive = lim_f( speed 99 | , tmin 100 | , tmax 101 | ); 102 | } 103 | 104 | 105 | void transport_speed_active( transport_t* self, float speed ) 106 | { 107 | self->speed_active = lim_f( speed 108 | , -self->speeds.max_speed 109 | , self->speeds.max_speed 110 | ); 111 | } 112 | 113 | 114 | void transport_nudge( transport_t* self, float delta ) 115 | { 116 | //switch statement unlocks tape 117 | self->nudge = delta; 118 | switch( self->tape_islocked ){ 119 | case -1: if( delta > 0.0 ){ self->tape_islocked = 0; } break; 120 | case 1: if( delta < 0.0 ){ self->tape_islocked = 0; } break; 121 | default: break; 122 | } 123 | } 124 | 125 | 126 | uint8_t transport_is_active( transport_t* self ) 127 | { 128 | return (self->active); 129 | } 130 | 131 | 132 | void transport_change_std_speeds( transport_t* self, std_speeds_t speeds ) 133 | { 134 | self->speeds = speeds; 135 | } 136 | 137 | 138 | float transport_get_speed( transport_t* self ) 139 | { 140 | return ((self->active) 141 | ? self->speed_active 142 | : self->speed_inactive); 143 | } 144 | 145 | 146 | float* transport_speed_block( transport_t* self 147 | , float* buffer 148 | , int b_size 149 | ) 150 | { 151 | lp1_set_dest( self->speed_slew 152 | , transport_get_speed( self ) 153 | ); 154 | 155 | // apply nudge / seek 156 | if( self->nudge ){ 157 | if( self->active ){ // only nudge! 158 | self->nudge_accum = lim_f( self->nudge_accum + self->nudge 159 | , -0.01 160 | , 0.01 161 | ); 162 | } else { 163 | self->nudge_accum = lim_f( self->nudge_accum + self->nudge 164 | , -self->speeds.max_speed 165 | , self->speeds.max_speed 166 | ); 167 | } 168 | } else { 169 | if( self->nudge_accum >= 0.0 ){ 170 | self->nudge_accum = lim_f( self->nudge_accum 171 | - self->speeds.nudge_release 172 | , 0.0 173 | , self->speeds.max_speed 174 | ); 175 | } else { 176 | self->nudge_accum = lim_f( self->nudge_accum 177 | + self->speeds.nudge_release 178 | , -self->speeds.max_speed 179 | , 0.0 180 | ); 181 | } 182 | } 183 | 184 | // limit nudged speed to +/-2.0 185 | float tmax = self->speeds.max_speed; 186 | float tmin = -self->speeds.max_speed; 187 | switch( self->tape_islocked ){ 188 | case -1: tmin = 0.0; break; 189 | case 1: tmax = 0.0; break; 190 | default: break; 191 | } 192 | lp1_set_out( self->speed_slew 193 | , lim_f( lp1_get_out( self->speed_slew ) + self->nudge_accum 194 | , tmin 195 | , tmax 196 | ) ); 197 | 198 | // slew toward new speed 199 | return lp1_step_c_v( self->speed_slew 200 | , buffer 201 | , b_size 202 | ); 203 | } 204 | 205 | 206 | uint8_t transport_is_tape_moving( transport_t* self ) 207 | { 208 | float speed = lp1_get_out( self->speed_slew ); 209 | return( speed < -nFloor 210 | || speed > nFloor ); 211 | } 212 | -------------------------------------------------------------------------------- /wrHead.c: -------------------------------------------------------------------------------- 1 | #include "wrHead.h" 2 | #include "wrMath.h" // iMAX24f, MAX24b, lim_i24_audio 3 | #include 4 | #include 5 | 6 | #include "wrConvert.h" // _s12_to_sf() _sf_to_s12() 7 | 8 | rhead_t* RH_Init( void ) 9 | { 10 | rhead_t* self = malloc( sizeof( rhead_t ) ); 11 | if( !self ){ printf("RH: malloc failed\n"); return NULL; } 12 | 13 | self->record = lp1_init(); 14 | self->feedback = lp1_init(); 15 | self->monitor = lp1_init(); 16 | // monitor needs to default to on 17 | lp1_set_dest( self->feedback, 1.0 ); 18 | lp1_set_out( self->feedback, 1.0 ); 19 | lp1_set_dest( self->monitor, 1.0 ); 20 | // slew times. can tune each individually? 21 | lp1_set_coeff( self->record, HEAD_SLEW_RECORD ); 22 | lp1_set_coeff( self->feedback, HEAD_SLEW_FEEDBACK ); 23 | lp1_set_coeff( self->monitor, HEAD_SLEW_MONITOR ); 24 | 25 | return self; 26 | } 27 | 28 | 29 | 30 | void RH_DeInit( rhead_t* self ) 31 | { 32 | lp1_deinit( self->monitor ); 33 | lp1_deinit( self->feedback ); 34 | lp1_deinit( self->record ); 35 | free(self); 36 | } 37 | 38 | 39 | 40 | 41 | void RH_set_rw( rhead_t* self, tape_mode_t rmode ) 42 | { 43 | lp1_set_dest( self->record, (float)(rmode != READONLY) ); 44 | lp1_set_dest( self->feedback, (float)(rmode <= OVERDUB) ); 45 | lp1_set_dest( self->monitor, (float)(rmode != OVERWRITE) ); 46 | } 47 | 48 | 49 | 50 | 51 | void RH_set_record_level( rhead_t* self, float level ) 52 | { 53 | lp1_set_dest( self->record, level ); 54 | } 55 | 56 | 57 | 58 | 59 | void RH_set_record_params( rhead_t* self 60 | , float feedback 61 | , float monitor 62 | ) 63 | { 64 | lp1_set_dest( self->feedback, feedback ); 65 | lp1_set_dest( self->monitor, monitor ); 66 | } 67 | 68 | 69 | 70 | 71 | IO_block_t* RH_rw_process( rhead_t* self 72 | , IO_block_t* headbuf 73 | , HEAD_Action_t action 74 | , int32_t** access 75 | , int count 76 | , int* dirty 77 | , int write_offset 78 | ) 79 | { 80 | uint16_t i; 81 | float* input_v = headbuf->audio; 82 | float* return_v = headbuf->audio; 83 | int32_t** tape_r = access; 84 | 85 | if( action == HEAD_Fadeout // if xfading, act on 1st 86 | || action == HEAD_Active ){ // if !xfade, act on only head 87 | lp1_step_internal( self->record ); 88 | lp1_step_internal( self->feedback ); 89 | lp1_step_internal( self->monitor ); 90 | } 91 | 92 | // playback mode if record is staying at 0.0 93 | if( lp1_get_dest( self->record ) == 0.0 94 | && lp1_get_out( self->record ) == 0.0 ){ 95 | if( action == HEAD_Fadeout ){ 96 | float fade_step = 1.0 / (float)(count - 1); 97 | float fade_out = 1.0; 98 | for( i=0; i<(count); i++ ){ 99 | fade_out -= fade_step; 100 | *(write_offset + *tape_r) 101 | = **tape_r; // 'write' action is playback 102 | // 'read' action 103 | *return_v++ = iMAX24f * (float)(**tape_r++ << BIT_HEADROOM) 104 | * lp1_get_out( self->monitor ) 105 | * fade_out; 106 | } 107 | } else if( action == HEAD_Fadein ){ 108 | float fade_step = 1.0 / (float)(count - 1); 109 | float fade_in = 0.0; 110 | for( i=0; i<(count); i++ ){ 111 | fade_in += fade_step; 112 | *(write_offset + *tape_r) 113 | = **tape_r; // 'write' action is playback 114 | // 'read' action 115 | *return_v++ = iMAX24f * (float)(**tape_r++ << BIT_HEADROOM) 116 | * lp1_get_out( self->monitor ) 117 | * fade_in; 118 | } 119 | } else { 120 | for( i=0; i<(count); i++ ){ 121 | *(write_offset + *tape_r) 122 | = **tape_r; // 'write' action is playback 123 | // 'read' action 124 | *return_v++ = iMAX24f * (float)(**tape_r++ << BIT_HEADROOM) 125 | * lp1_get_out( self->monitor ); 126 | } 127 | } 128 | } else { 129 | if( action == HEAD_Fadeout ){ 130 | float fade_step = 1.0 / (float)(count - 1); 131 | float fade_out = 1.0; 132 | 133 | for( i=0; i<(count); i++ ){ 134 | fade_out -= fade_step; // fade out 135 | // 'write' action 136 | if( ( *(write_offset + *tape_r) == INVALID_SAMP ) ){ 137 | *(write_offset + *tape_r) 138 | = lim_i24_audio( (int32_t) 139 | // INPUT 140 | ( lp1_get_out( self->record ) 141 | * fade_out 142 | * (*input_v++) * F_TO_TAPE_SCALE 143 | // SCALE FEEDBACK TOWARD PLAYBACK 144 | + ( ( lp1_get_out( self->feedback )-1.0) 145 | * lp1_get_out( self->record ) 146 | + 1.0 147 | // FEEDBACK 148 | ) * (float)(**tape_r) 149 | ) 150 | ); 151 | } else { 152 | *(write_offset + *tape_r) 153 | = lim_i24_audio( (int32_t) 154 | // INPUT 155 | ( lp1_get_out( self->record ) 156 | * fade_out 157 | * (*input_v++) * F_TO_TAPE_SCALE 158 | ) 159 | // FEEDBACK (already copied) 160 | + *(write_offset + *tape_r) 161 | ); 162 | } 163 | // 'read' action 164 | *return_v++ = iMAX24f * (float)(**tape_r++ << BIT_HEADROOM) 165 | * lp1_get_out( self->monitor ) 166 | // fade_in starts to affect as self->record fades out 167 | * (1.0 + (fade_out-1.0)*(1.0-lp1_get_out( self->record ))); 168 | } 169 | } 170 | 171 | else if( action == HEAD_Fadein ){ 172 | float fade_step = 1.0 / (float)(count - 1); 173 | float fade_in = 0.0; 174 | 175 | for( i=0; i<(count); i++ ){ 176 | fade_in += fade_step; // linear ramp, start step above zero 177 | // 'write' action 178 | if( ( *(write_offset + *tape_r) == INVALID_SAMP ) ){ 179 | *(write_offset + *tape_r) 180 | = lim_i24_audio( (int32_t) 181 | // INPUT * FADEIN 182 | ( lp1_get_out( self->record ) 183 | * fade_in 184 | * (*input_v++) * F_TO_TAPE_SCALE 185 | // SCALED FEEDBACK TOWARD PLAYBACK 186 | + ( ( lp1_get_out( self->feedback )-1.0) 187 | * lp1_get_out( self->record ) 188 | + 1.0 189 | ) 190 | // FEEDBACK (already copied) 191 | * (float)(**tape_r) 192 | ) 193 | ); 194 | } else { 195 | *(write_offset + *tape_r) 196 | = lim_i24_audio( (int32_t) 197 | ( lp1_get_out( self->record ) 198 | * fade_in 199 | * (*input_v++) * F_TO_TAPE_SCALE 200 | ) 201 | + *(write_offset + *tape_r) 202 | ); 203 | } 204 | // 'read' action 205 | *return_v++ = iMAX24f * (float)(**tape_r++ << BIT_HEADROOM) 206 | * lp1_get_out( self->monitor ) 207 | // fade_in starts to affect as self->record fades out 208 | * (1.0 + (fade_in-1.0)*(1.0-lp1_get_out( self->record ))); 209 | } 210 | } 211 | 212 | else if( action == HEAD_Active ){ 213 | for( i=0; i<(count); i++ ){ 214 | // 'write' action 215 | if( ( *(write_offset + *tape_r) == INVALID_SAMP ) ){ 216 | *(write_offset + *tape_r) 217 | = lim_i24_audio( (int32_t) 218 | // INPUT 219 | ( lp1_get_out( self->record ) 220 | * (*input_v++) * F_TO_TAPE_SCALE 221 | // SCALE FEEDBACK TOWARD PLAYBACK 222 | + ( ( lp1_get_out( self->feedback )-1.0) 223 | * lp1_get_out( self->record ) 224 | + 1.0 225 | ) 226 | // FEEDBACK 227 | * (float)(**tape_r) 228 | ) 229 | ); 230 | } else { 231 | *(write_offset + *tape_r) 232 | = lim_i24_audio( (int32_t) 233 | // INPUT 234 | ( lp1_get_out( self->record ) 235 | * (*input_v++) * F_TO_TAPE_SCALE 236 | ) 237 | // FEEDBACK (already copied) 238 | + *(write_offset + *tape_r) 239 | ); 240 | } 241 | // 'read' action 242 | *return_v++ = iMAX24f * (float)(**tape_r++ << BIT_HEADROOM) 243 | * lp1_get_out( self->monitor ); 244 | } 245 | } // else {} inactive. won't happen 246 | 247 | // MARK DIRTY FLAG 248 | *dirty = 1; 249 | } 250 | return headbuf; 251 | } 252 | 253 | 254 | float RH_rw_process_cv( rhead_t* self 255 | , float input 256 | , uint8_t action // HEAD_Action_t 257 | , int16_t* cv_read 258 | , int* dirty 259 | , int write_offset 260 | ) 261 | { 262 | float rec_level = lp1_get_out( self->record ); 263 | float fb_level = lp1_get_out( self->feedback ); 264 | float mon_level = lp1_get_out( self->monitor ); 265 | 266 | float ontape = _s12_to_sf( *cv_read ); 267 | if( action ){ 268 | *(cv_read + write_offset) = _sf_to_s12( ontape * fb_level 269 | + input * rec_level 270 | ); 271 | *dirty = 1; 272 | }else{ 273 | mon_level = 1.0; 274 | } 275 | 276 | return ontape * mon_level; 277 | } 278 | -------------------------------------------------------------------------------- /wrFilter.c: -------------------------------------------------------------------------------- 1 | #include "wrFilter.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "wrMath.h" 8 | 9 | /////////////// 10 | // 1Pole LPF // 11 | /////////////// 12 | 13 | filter_lp1_t* lp1_init(void){ 14 | filter_lp1_t* self = malloc( sizeof( filter_lp1_t ) ); 15 | if( !self ){ printf("Lp1: malloc failed\n"); return NULL; } 16 | self->x = 0; 17 | self->y = 0; 18 | self->c = 0.97; 19 | return self; 20 | } 21 | void lp1_deinit( filter_lp1_t* f ){ 22 | free(f); f = NULL; 23 | } 24 | 25 | void lp1_set_dest(filter_lp1_t* f, float in) 26 | { 27 | f->x = in; 28 | } 29 | float lp1_get_dest( filter_lp1_t* f ) 30 | { 31 | return f->x; 32 | } 33 | void lp1_set_out(filter_lp1_t* f, float level) 34 | { 35 | f->y = level; 36 | } 37 | float lp1_get_out( filter_lp1_t* f ) 38 | { 39 | return f->y; 40 | } 41 | float lp1_step(filter_lp1_t* f, float in) 42 | { 43 | f->y = f->y + f->c * (in - f->y); 44 | return f->y; 45 | } 46 | uint8_t lp1_converged( filter_lp1_t* f ) 47 | { 48 | float diff = f->x - f->y; 49 | if((diff > -nFloor) && (diff < nFloor)) { return 1; } 50 | return 0; 51 | } 52 | float lp1_step_internal(filter_lp1_t* f) 53 | { 54 | if(lp1_converged( f )){ 55 | f->y = f->x; 56 | } else { 57 | f->y = f->y + f->c * (f->x - f->y); 58 | } 59 | return f->y; 60 | } 61 | void lp1_set_coeff(filter_lp1_t* f, float c) 62 | { 63 | f->c = c; 64 | } 65 | float lp1_get_coeff(filter_lp1_t* f) 66 | { 67 | return f->c; 68 | } 69 | void lp1_set_freq(filter_lp1_t* f, float freq) 70 | { 71 | f->c = freq/48000; // expo! 72 | } 73 | void lp1_step_v(filter_lp1_t* f, float* in, float* out, uint16_t size) 74 | { 75 | float* in2=in; 76 | float* out2=out; 77 | float* out3=out; // point to start of arrays 78 | // out3 = y = previous OUT 79 | 80 | // first samp 81 | *out2++ = f->y + f->c * ((*in2++) - f->y); 82 | 83 | // remainder of samps -> add nFloor early exit to avoid denormals 84 | for(uint16_t i=0; i<(size-1); i++) { 85 | *out2++ = (*out3) + f->c * ((*in2++) - (*out3)); 86 | out3++; 87 | } 88 | 89 | f->y = *out3; // last output 90 | } 91 | float* lp1_step_c_v(filter_lp1_t* f, float* buffer, uint16_t size) 92 | { 93 | float* out = buffer; 94 | float* out2 = buffer; 95 | 96 | // first samp 97 | // check if we've already converged 98 | if( lp1_converged(f) ){ 99 | for(uint16_t i=0; ix; 101 | } 102 | f->y = f->x; 103 | } else { 104 | 105 | *out++ = f->y + f->c * (f->x - f->y); 106 | 107 | // remainder of samps -> add nFloor early exit to avoid denormals 108 | for(uint16_t i=0; i<(size-1); i++) { 109 | *out++ = (*out2) + f->c * (f->x - (*out2)); 110 | out2++; 111 | } 112 | 113 | f->y = *out2; // last output 114 | } 115 | return buffer; 116 | } 117 | 118 | 119 | 120 | ////////////////////////////// 121 | // 1Pole LPF (Assymetrical) // 122 | ////////////////////////////// 123 | 124 | filter_lp1_a_t* lp1_a_init( void ) 125 | { 126 | filter_lp1_a_t* self = malloc( sizeof( lp1_a_init ) ); 127 | if( !self ){ printf("Lp1_A malloc failed\n"); return NULL; } 128 | self->y = 0; 129 | self->c_rise = 0.98; 130 | self->c_fall = 0.8; 131 | return self; 132 | } 133 | 134 | void lp1_a_deinit( filter_lp1_a_t* self ) 135 | { 136 | free(self); self = NULL; 137 | } 138 | 139 | float lp1_a_step( filter_lp1_a_t* f, float in ) 140 | { 141 | float c = (in > f->y) ? f->c_rise : f->c_fall; 142 | f->y = f->y + c * (in - f->y); 143 | return f->y; 144 | } 145 | void lp1_a_set_coeff( filter_lp1_a_t* f, float c_rise 146 | , float c_fall 147 | ) 148 | { 149 | f->c_rise = c_rise; 150 | f->c_fall = c_fall; 151 | } 152 | void lp1_a_step_v( filter_lp1_a_t* f, float* in 153 | , float* out 154 | , uint16_t size 155 | ) 156 | { 157 | float* in2=in; 158 | float* out2=out; 159 | float* out3=out; 160 | // out3 = y = previous OUT 161 | 162 | // if we match this case, we can't assume same direction this block 163 | if( (in[0] >= 0.0) 164 | ^ (in[size-1] >= 0.0) ){ // frame changes direction: case on each sample 165 | float c = (*in2 > f->y) ? f->c_rise : f->c_fall; 166 | *out2++ = f->y + c * ((*in2++) - f->y); 167 | for(uint16_t i=0; i<(size-1); i++) { 168 | c = (*in2 > *out3) ? f->c_rise : f->c_fall; 169 | *out2++ = (*out3) + c * ((*in2++) - (*out3)); 170 | out3++; 171 | } 172 | } else { // assume the whole in frame is same direction relative to output 173 | float c = (in[0] > f->y) ? f->c_rise : f->c_fall; 174 | 175 | *out2++ = f->y + c * ((*in2++) - f->y); 176 | for(uint16_t i=0; i<(size-1); i++) { 177 | *out2++ = (*out3) + c * ((*in2++) - (*out3)); 178 | out3++; 179 | } 180 | } 181 | f->y = *out3; // last output 182 | } 183 | 184 | /////////////////// 185 | // SWITCH & RAMP // 186 | /////////////////// 187 | 188 | filter_sr_t* switch_ramp_init( void ) 189 | { 190 | filter_sr_t* self = malloc( sizeof( filter_sr_t ) ); 191 | if( !self ){ printf("SR: malloc failed\n"); return NULL; } 192 | self->ramp = 0.0; 193 | self->rate = 0.001; // per-sample step-size, or 1pole coefficient? 194 | return self; 195 | } 196 | void switch_ramp_deinit( filter_sr_t* f ){ 197 | free(f); f = NULL; 198 | } 199 | 200 | void switch_ramp_set_rate( filter_sr_t* f, float rate ) 201 | { 202 | f->rate = rate; 203 | } 204 | void switch_ramp_jump( filter_sr_t* f, float step_size ) 205 | { 206 | f->ramp += step_size; // accumulate in case of overlapping ramps 207 | } 208 | float* switch_ramp_step_v( filter_sr_t* f, float* io 209 | , int b_size 210 | ){ 211 | float* samp = io; 212 | if( f->ramp != 0.0 ){ // passthrough if no ramp 213 | if( f->ramp >= (b_size * f->rate) ){ // positive ramp 214 | for( int i=0; iramp; 216 | f->ramp -= f->rate; 217 | } 218 | } else if( f->ramp <= -(b_size * f->rate) ){ // negative ramp 219 | for( int i=0; iramp; 221 | f->ramp += f->rate; 222 | } 223 | } else { // almost zero. check each step 224 | if( f->ramp > 0 ){ // pos 225 | for( int i=0; iramp; 227 | f->ramp -= f->rate; 228 | if( f->ramp <= 0.0 ){ 229 | f->ramp = 0.0; 230 | break; 231 | } 232 | } 233 | } else { // neg 234 | for( int i=0; iramp; 236 | f->ramp += f->rate; 237 | if( f->ramp >= 0.0 ){ 238 | f->ramp = 0.0; 239 | break; 240 | } 241 | } 242 | } 243 | } 244 | } 245 | return io; 246 | } 247 | 248 | 249 | //////////////////////////// 250 | // AVERAGED WINDOW SMOOTH // 251 | //////////////////////////// 252 | 253 | filter_awin_t* awin_init( int win_size ) 254 | { 255 | filter_awin_t* self = malloc( sizeof( filter_awin_t ) ); 256 | if( !self ){ printf("awin malloc failed\n"); return NULL; } 257 | self->out = 0.5; 258 | self->win_size = win_size; 259 | self->win_ix = 0; 260 | self->win_scale = 1.0 / (float)(win_size + 1); 261 | 262 | self->history = malloc(sizeof(float)*win_size); 263 | if( !self->history ){ printf("history!\n"); return NULL; } 264 | for( int i=0; ihistory[i] = 0.5; } 265 | 266 | self->slope_sense = 6000.0 * self->win_scale; 267 | return self; 268 | } 269 | 270 | void awin_deinit( filter_awin_t* self ) 271 | { 272 | free(self->history); self->history = NULL; 273 | free(self); self = NULL; 274 | } 275 | 276 | void awin_slope( filter_awin_t* f, float slope_sensitivity) 277 | { 278 | f->slope_sense = slope_sensitivity; 279 | } 280 | float awin_step( filter_awin_t* f, float input ) 281 | { 282 | float windowed_avg = input; 283 | for( int i=0; i<(f->win_size); i++ ){ 284 | windowed_avg += f->history[i]; 285 | } 286 | windowed_avg *= f->win_scale; 287 | f->history[f->win_ix++] = input; 288 | if( f->win_ix >= f->win_size ){ f->win_ix=0; } 289 | 290 | // rate of change 291 | float roc = f->slope_sense 292 | * ( windowed_avg - f->out ); 293 | 294 | roc = lim_f_0_1( max_f( roc * roc 295 | , 0.008 296 | ) 297 | ); 298 | 299 | // slope-sensitive-smoother 300 | f->out = lim_f_0_1( input 301 | + ( 1.0 - roc ) 302 | * ( f->out - input ) 303 | ); 304 | return f->out; 305 | } 306 | float awin_get_out( filter_awin_t* f ) 307 | { 308 | return f->out; 309 | } 310 | float awin_get_in( filter_awin_t* f ) 311 | { 312 | int16_t ix = (f->win_ix)-1; 313 | if( ix<0 ){ ix += f->win_size; } 314 | return f->history[ix]; 315 | } 316 | 317 | 318 | //////////////// 319 | // DC-Blocker // 320 | //////////////// 321 | 322 | // Differentiator followed by leaky integrator 323 | filter_dc_t* dc_init( void ){ 324 | filter_dc_t* self = malloc( sizeof( filter_dc_t ) ); 325 | if( self == NULL ){ printf("DC: malloc failed\n"); } 326 | 327 | self->coeff = 0.997; 328 | self->prev_in = 0.0; 329 | self->prev_out = 0.0; 330 | 331 | return self; 332 | } 333 | void dc_deinit( filter_dc_t* self ){ 334 | free(self); self = NULL; 335 | } 336 | 337 | void dc_time( filter_dc_t* self, float hpc ){ 338 | // f->c = 1.0 means DC-coupling 339 | // f->c = 0.0 means differentiator (only changes are passed) 340 | self->coeff = lim_f_0_1( hpc ); 341 | } 342 | 343 | float dc_step(filter_dc_t* self, float in){ 344 | self->prev_out = in - self->prev_in + (self->coeff * self->prev_out); 345 | self->prev_in = in; // save previous input 346 | return self->prev_out; 347 | } 348 | 349 | float* dc_step_v( filter_dc_t* self, float* buffer 350 | , int b_size 351 | ){ 352 | float* in = buffer; 353 | float* out = buffer; 354 | for( int i=0; iprev_out = *in - self->prev_in + (self->coeff * self->prev_out); 356 | self->prev_in = *in++; 357 | *out++ = self->prev_out; 358 | } 359 | return buffer; 360 | } 361 | 362 | 363 | //////////////////////////// 364 | // State-variable: 2-pole // 365 | //////////////////////////// 366 | 367 | filter_svf_t* svf_init( uint8_t mode, int sample_rate ){ 368 | filter_svf_t* self = malloc( sizeof( filter_svf_t ) ); 369 | if( !self ){ printf("SVF malloc failed\n"); return NULL; } 370 | self->x[0] = 0; 371 | self->x[1] = 0; 372 | self->x[2] = 0; 373 | self->q = 0.5; 374 | self->c = 0.02; 375 | self->mode = mode; 376 | self->sample_rate = sample_rate; 377 | return self; 378 | } 379 | 380 | void svf_deinit( filter_svf_t* self ){ 381 | free(self); self = NULL; 382 | } 383 | 384 | void svf_set_mode(filter_svf_t* f, uint8_t mode) { 385 | f->mode = mode; 386 | } 387 | float svf_process_frame(filter_svf_t* f, float input) { 388 | f->x[0] = f->x[0] + (f->c * f->x[1]); 389 | f->x[2] = (input - (f->q * f->x[1])) - f->x[0]; 390 | f->x[1] = f->x[1] + (f->c * f->x[2]); 391 | return f->x[f->mode]; 392 | } 393 | float svf_step(filter_svf_t* f, float input) { 394 | // 2x oversample & average 395 | float out = svf_process_frame(f, input); 396 | out += svf_process_frame(f, input); 397 | out *= 0.5; 398 | return out; 399 | } 400 | void svf_set_coeff(filter_svf_t* f, float coeff) { 401 | f->c = lim_f(coeff * 0.5, 0.001, 0.499); 402 | } 403 | void svf_set_freq(filter_svf_t* f, float freq) { 404 | // f->c = lim_f(freq/(f->sample_rate), 0.001, 0.499); // need expo lookup here 405 | // when freq = sample_rate/4, f->c = 0.5 406 | 407 | f->c = exp(-2.0 * WR_PI * freq * 1/f->sample_rate ); 408 | 409 | // Set mixfreq to whatever rate your system is using (eg 48Khz) 410 | // Calculate filter cutoff frequencies 411 | // es->lf = 2 * sin(M_PI * ((double)lowfreq / (double)mixfreq)); 412 | } 413 | void svf_set_q(filter_svf_t* f, float quality) { 414 | f->q = lim_f(0.5 - (quality * 0.5), 0.001, 0.499); // q needs stronger limits to avoid overload 415 | } 416 | -------------------------------------------------------------------------------- /wrFuncGen.c: -------------------------------------------------------------------------------- 1 | #include "wrFuncGen.h" 2 | 3 | #include 4 | #include // malloc 5 | #include // printf 6 | 7 | #include "../wrLib/wrMath.h" 8 | 9 | func_gen_t* function_init( int loop ) 10 | { 11 | func_gen_t* self = malloc( sizeof(func_gen_t) ); 12 | if( !self ){ printf("!func_gen\n"); return NULL; } 13 | self->go = 0; // stopped 14 | self->id = 0; 15 | self->rate = 0.05; 16 | self->tr_state = 0; 17 | self->fm_ix = 0; 18 | self->loop = loop; 19 | if(loop != 0){ 20 | self->go = 1; // start if looping 21 | } 22 | self->s_mode = 0; 23 | self->sustain_state = 0; 24 | self->sustaining = 0; 25 | self->zc = 0; 26 | self->r_up = 1; 27 | self->r_down = 1; 28 | return self; 29 | } 30 | 31 | // Param Functions 32 | 33 | // Called on trigger edges only 34 | 35 | /* function_reset is same as cutoff = -1; 36 | function_trig is same as cutoff = 0; */ 37 | 38 | void function_trig_reset( func_gen_t* self 39 | , uint8_t state ) 40 | { 41 | if(state){ // release stage/stopped 42 | self->id = MIN_POS_FLOAT; // reset 43 | self->go = 1; 44 | } 45 | self->sustain_state = state; 46 | } 47 | void function_trig_wait( func_gen_t* self 48 | , uint8_t state ) 49 | { 50 | if(state && (!self->go)){ // release stage/stopped 51 | self->id = MIN_POS_FLOAT; // reset 52 | self->go = 1; 53 | } 54 | self->sustain_state = state; 55 | } 56 | void function_trig_sustain( func_gen_t* self 57 | , uint8_t state ) 58 | { 59 | /*if(state && !self->go){ // stopped & high trigger 60 | self->id = MIN_POS_FLOAT; 61 | self->go = 1; 62 | } else if( state 63 | && self->id <=0.0 ){ // release phase & high trigger 64 | float eye = shaper_rev_lookup( &friends.shaper 65 | , self->id 66 | , BLOCK_SIZE-1 67 | ); 68 | self->id = eye 69 | } else if( !state 70 | && self->id > 0.0 ){ // attack phase & release 71 | self->id = -(self->id); // naive flip to release 72 | } 73 | self->sustain_state = state;*/ 74 | } 75 | void function_trig_vari( func_gen_t* self 76 | , uint8_t state 77 | , float cutoff ) 78 | { 79 | // -1 is always, 0 is only in release, +1 is never 80 | uint8_t tr; 81 | (cutoff >= 0.0f) 82 | // EOR to EOC 83 | ? ( tr = (self->id <= 0.0f) // is falling AND 84 | && (self->id > (cutoff - 1.0f)) ) // 85 | // Always to EOR 86 | : ( tr = (self->id <= 0.0f) // is falling OR 87 | || (self->id > (1.0f + cutoff) ) ); // is rising 88 | if( state && tr // is sensitive! 89 | || self->go == 0 ){ // or channel is already stopped 90 | self->id = MIN_POS_FLOAT; // reset 91 | self->go = 1; 92 | } 93 | self->sustain_state = state; 94 | } 95 | void function_trig_burst( func_gen_t* self 96 | , uint8_t state 97 | , float count ) 98 | { 99 | // -1 is zero, 0 is 6, +1 is 36 100 | if(state){ // release stage/stopped 101 | self->id = MIN_POS_FLOAT; // reset 102 | if(count <= -0.7f){ // choke channel if below -3v5 103 | self->go = 0; 104 | self->loop = 0; 105 | } else { 106 | self->go = 1; 107 | self->loop = (int8_t)powf( 6.0f 108 | , count + 1.0f) 109 | - 1.0f; 110 | } 111 | } 112 | self->sustain_state = state; 113 | } 114 | void function_trig_spill( func_gen_t* self 115 | , uint8_t state 116 | , float cutoff ) 117 | { 118 | // -1 is always, 0 is only in release, +1 is never 119 | uint8_t tr; 120 | (cutoff >= 0.0f) 121 | ? ( tr = (self->id <= 0.0f) 122 | && (self->id > -(cutoff)) ) 123 | : ( tr = (self->id <= 0.0f) 124 | || (self->id > (1.0f + cutoff) ) ); 125 | if(state && tr){ // release stage/stopped 126 | self->id = -(self->id); // flip (soft-sync) 127 | if(!self->go){ // explicit start required 128 | self->id = MIN_POS_FLOAT; 129 | } 130 | self->go = 1; 131 | } 132 | self->sustain_state = state; 133 | } 134 | 135 | void function_mode( func_gen_t* self, uint8_t mode ) 136 | { 137 | function_loop(self, 0-(FNGEN_CYCLE == mode)); // 0 or -1 138 | function_sustain(self, (FNGEN_SUSTAIN == mode)); 139 | } 140 | 141 | void function_loop( func_gen_t* self, int8_t loop ) 142 | { 143 | self->loop = loop; 144 | if(loop != 0){ 145 | self->go = 1; 146 | } 147 | } 148 | 149 | void function_sustain( func_gen_t* self, uint8_t sust ) 150 | { 151 | self->s_mode = sust; 152 | } 153 | 154 | void function_rate( func_gen_t* self, float rate ) 155 | { 156 | self->rate = lim_f_n1_1( rate ); 157 | } 158 | 159 | float function_get_rate( func_gen_t* self ) 160 | { 161 | return self->rate; 162 | } 163 | 164 | void function_fm_ix( func_gen_t* self, float ix ) 165 | { 166 | self->fm_ix = lim_f_0_1(ix) * 0.1f; 167 | } 168 | 169 | // Audio Rate Process (helper function) 170 | void function_ramp( func_gen_t* self, float skew ) 171 | { 172 | math_get_ramps( skew, 173 | &(self->r_up), 174 | &(self->r_down)); 175 | // self->r_up = 0.5 / (0.998 * level + 0.001); 176 | // self->r_down = 1/ (2- (1/ self->r_up)); 177 | } 178 | 179 | void function_ramp_v_global( uint16_t b_size 180 | , float ctrl_rate 181 | , float* audio_rate 182 | , float* ramp_up 183 | , float* ramp_down ) 184 | { 185 | float* audio_rate2 = audio_rate; 186 | float* ramp_up2 = ramp_up; 187 | float* ramp_down2 = ramp_down; 188 | 189 | // bandlimiting based on base pitch of function 190 | for(uint16_t i=0;irate); 215 | float max = 1.0 - f; // (-1,1 range) 216 | float min = 0.0 + f; // (-1,1 range) 217 | // bandlimiting based on base pitch of function 218 | for(uint16_t i=0;igo ){ 236 | float move; 237 | 238 | // determine rate based on direction 239 | if( self->id >= 0 ){ 240 | move = self->rate * self->r_up + (fm_in * self->fm_ix); // (+ phase mod) 241 | } else { 242 | move = self->rate * self->r_down + (fm_in * self->fm_ix); 243 | } 244 | // increment w/ overflow protection 245 | while( move != 0.0f ){ 246 | if( self->id >= 0.0f ){ // are we above zero BEFORE moving 247 | self->id += move; 248 | move = 0.0f; 249 | if( self->id >= 1.0f ){ 250 | move = (self->id - 1.0f) * self->r_down / self->r_up; 251 | self->id = -1.0f; 252 | } else if( self->id < 0.0f ){ 253 | if( self->loop != 0 ){ 254 | move = self->id * self->r_down / self->r_up; 255 | if( self->loop > 0 ) { self->loop += 1; } 256 | } 257 | self->id = 0.0f; 258 | } 259 | } else { 260 | self->id += move; 261 | move = 0.0f; 262 | if( self->id >= 0.0f ){ 263 | if( self->loop != 0 ){ 264 | move = self->id * self->r_up / self->r_down; 265 | if( self->loop > 0 ) { self->loop -= 1; } 266 | self->id = MIN_POS_FLOAT; 267 | } else { 268 | self->go = 0; 269 | self->id = 0.0f; 270 | } 271 | } else if( self->id < -1.0f ){ 272 | move = (self->id + 1.0f) * self->r_up / self->r_down; 273 | self->id = 1.0f; 274 | } 275 | } 276 | } 277 | } 278 | return self->id; 279 | // NB: this is +/-1 sawtooth, with 0 as 'stopped' 280 | // use function_lookup(self->id) to convert 281 | } 282 | 283 | // compiler inlines this anyway 284 | float sign( float n ) 285 | { 286 | return ( (n > 0.0f) - (n < 0.0f) ); 287 | } 288 | 289 | float function_lookup( float id ) 290 | { 291 | return ( sign(id)*2.0f - 1.0f ); 292 | } 293 | 294 | float* function_v( func_gen_t* self 295 | , float* ramps // array of 2xb_size for UP/DOWN 296 | , float* fm_return // MODIFIED IN PLACE 297 | , int b_size 298 | ){ 299 | float* fm_in2 = fm_return; 300 | float* out2 = fm_return; 301 | float* r_up2 = &ramps[0]; 302 | float* r_down2 = &ramps[b_size]; 303 | 304 | self->sustaining = 0; 305 | self->zc = 0; 306 | 307 | if( self->go ){ 308 | for(uint16_t i=0; ifm_ix) // linear FM 310 | + self->rate // base freq 311 | * ( (self->id >= 0.0f) // is rising? 312 | ? (*r_up2) 313 | : (*r_down2) 314 | ); 315 | while( move != 0.0f ){ 316 | if( self->id > 0.0f ){ // attack 317 | self->id += move; 318 | move = 0.0f; 319 | if( self->id >= 1.0f ){ 320 | if( self->s_mode && self->sustain_state ){ 321 | // fill rest of block with 1s 322 | self->sustaining = 1; 323 | self->id = 1.0f; 324 | while( i++ < b_size ){ 325 | *out2++ = self->id; 326 | } 327 | return fm_return; 328 | } 329 | move = (self->id - 1.0f) * (*r_down2) / (*r_up2); 330 | self->id = -1.0f; 331 | } else if( self->id < 0.0f ){ // rev TZ 332 | self->zc = 1; 333 | if( self->loop ){ 334 | move = self->id * (*r_down2) / (*r_up2); 335 | if( self->loop > 0.0f ) { self->loop++; } // TZ adds to burst! 336 | } 337 | self->id = 0.0f; 338 | } 339 | } else { // release 340 | self->id += move; 341 | move = 0.0f; 342 | if( self->id >= 0.0f ){ // rel -> ?atk 343 | self->zc = 1; 344 | if( self->loop ){ 345 | move = self->id * (*r_up2) / (*r_down2); 346 | if( self->loop > 0 ) { self->loop--; } 347 | self->id = MIN_POS_FLOAT; // get into attack case 348 | } else { 349 | // fill rest of block with 0s 350 | self->id = 0.0f; // only for STOP 351 | self->go = 0; 352 | while( i++ < b_size ){ 353 | *out2++ = self->id; 354 | } 355 | return fm_return; 356 | } 357 | } else if( self->id < -1.0f ){ // TZ back to attack 358 | move = (self->id + 1.0f) * (*r_up2) / (*r_down2); 359 | self->id = 1.0f; 360 | } 361 | } 362 | } 363 | *out2++ = self->id; 364 | r_up2++; r_down2++; 365 | } 366 | } else { 367 | self->id = 0.0f; 368 | for( uint16_t i=0; iid; 370 | } 371 | } 372 | return fm_return; 373 | } 374 | void function_fmix_v( func_gen_t* self 375 | , uint16_t b_size 376 | , float* r_up 377 | , float* r_dn 378 | , float* fm_in 379 | , float* fm_ix 380 | , float* out 381 | ) 382 | { 383 | float* out2 = out; 384 | float* r_up2 = r_up; 385 | float* r_down2 = r_dn; 386 | float* fm_in2 = fm_in; 387 | 388 | self->sustaining = 0; 389 | self->zc = 0; 390 | 391 | if( self->go ){ 392 | for(uint16_t i=0; ifm_ix // FM pot 395 | + 0.1 * *fm_ix++ ) // FM index adds to pot 396 | ) // linear FM 397 | + self->rate // base freq 398 | * ( (self->id >= 0.0f) // is rising? 399 | ? (*r_up2) 400 | : (*r_down2) 401 | ); 402 | while( move != 0.0f ){ 403 | if( self->id > 0.0f ){ // attack 404 | self->id += move; 405 | move = 0.0f; 406 | if( self->id >= 1.0f ){ 407 | if( self->s_mode && self->sustain_state ){ 408 | // fill rest of block with 1s 409 | self->sustaining = 1; 410 | self->id = 1.0f; 411 | while( i++ < b_size ){ 412 | *out2++ = self->id; 413 | } 414 | return; 415 | } 416 | move = (self->id - 1.0f) * (*r_down2) / (*r_up2); 417 | self->id = -1.0f; 418 | } else if( self->id < 0.0f ){ // rev TZ 419 | self->zc = 1; 420 | if( self->loop ){ 421 | move = self->id * (*r_down2) / (*r_up2); 422 | if( self->loop > 0.0f ) { self->loop++; } // TZ adds to burst! 423 | } 424 | self->id = 0.0f; 425 | } 426 | } else { // release 427 | self->id += move; 428 | move = 0.0f; 429 | if( self->id >= 0.0f ){ // rel -> ?atk 430 | self->zc = 1; 431 | if( self->loop ){ 432 | move = self->id * (*r_up2) / (*r_down2); 433 | if( self->loop > 0 ) { self->loop--; } 434 | self->id = MIN_POS_FLOAT; // get into attack case 435 | } else { 436 | // fill rest of block with 0s 437 | self->id = 0.0f; // only for STOP 438 | self->go = 0; 439 | while( i++ < b_size ){ 440 | *out2++ = self->id; 441 | } 442 | return; 443 | } 444 | } else if( self->id < -1.0f ){ // TZ back to attack 445 | move = (self->id + 1.0f) * (*r_up2) / (*r_down2); 446 | self->id = 1.0f; 447 | } 448 | } 449 | } 450 | *out2++ = self->id; 451 | r_up2++; r_down2++; 452 | } 453 | } else { 454 | self->id = 0.0f; 455 | for( uint16_t i=0; iid; 457 | } 458 | } 459 | return; 460 | } 461 | -------------------------------------------------------------------------------- /wrShaper.c: -------------------------------------------------------------------------------- 1 | #include "wrShaper.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "../wrLib/wrMath.h" 7 | #include "../wrLib/wrGlobals.h" 8 | #include "wrOscSine.h" 9 | 10 | // verbose names for switch statement 11 | #define SQ_LOG 0 12 | #define LOG_TRI 1 13 | #define TRI_EXP 2 14 | #define EXP_SINE 3 15 | 16 | 17 | const float log_lut[LUT_SIN_HALF + 1]={ 18 | -0.9999902200489,-0.978281604225695,-0.957221104523062,-0.936771140858207,-0.91689730980816,-0.897568036367262,-0.878754272154726,-0.860429232830196,-0.842568168759236,-0.825148164000937,-0.808147959521389,-0.791547797211688,-0.775329281839953,-0.759475258518497,-0.743969703639602,-0.728797627541498,-0.713944987422418,-0.699398609234617,-0.685146117469572,-0.671175871896579,-0.657476910444454,-0.644038897524108,-0.630852077181678,-0.617907230550289,-0.605195637135646,-0.592709039528244,-0.580439611184561,-0.568379926962425,-0.556522936132754,-0.54486193762206,-0.533390557268032,-0.522102726894906,-0.510992665036675,-0.50005485915479,-0.48928404921347,-0.478675212490087,-0.468223549510837,-0.45792447101312,-0.447773585845982,-0.437766689728762,-0.427899754795935,-0.418168919863072,-0.408570481355037,-0.39910088484309,-0.389756717142501,-0.380534698926699,-0.371431677817971,-0.362444621918287,-0.353570613747011,-0.344806844555196,-0.336150608988712,-0.327599300074853,-0.319150404509164,-0.310801498221173,-0.302550242199463,-0.294394378558079,-0.286331726827727,-0.278360180456529,-0.270477703506271,-0.2626823275312,-0.254972148627377,-0.247345324641549,-0.239800072529273,-0.232334665852837,-0.224947432410193,-0.217636751986734,-0.210401054222392,-0.203238816586988,-0.196148562457347,-0.189128859290076,-0.182178316884354,-0.175295585729478,-0.168479355432229,-0.1617283532195,-0.155041342511883,-0.148417121564231,-0.141854522169453,-0.135352408422041,-0.128909675538064,-0.122525248728554,-0.116198082123421,-0.109927157743185,-0.103711484516019,-0.0975500973377138,-0.0914420561723365,-0.0853864451914893,-0.0793823719501999,-0.0734289665975826,-0.0675253811205289,-0.0616707886187803,-0.0558643826098356,-0.0501053763622298,-0.0443930022558067,-0.0387265111676836,-0.0331051718826815,-0.0275282705270582,-0.0219951100244499,-0.016505009572981,-0.011057304142565,-0.00565134399146683,-0.000286494201245269,0.00503786577075294,0.0103223425221382,0.0155675291202108,0.0207740055013884,0.0259423388560034,0.0310730839991048,0.0361667837278765,0.0412239691662428,0.0462451600972096,0.0512308652834631,0.0561815827767185,0.0610978002162896,0.0659799951173273,0.0708286351491527,0.0756441784040902,0.0804270736571882,0.0851777606171939,0.0898966701691349,0.0945842246088406,0.0992408378697239,0.103866915742128,0.108462856085526,0.113029049033857,0.117565877194253,0.122073715839426,0.126552933093938,0.131003890114599,0.135426941265214,0.139822434285876,0.144190710457029,0.148532104758473,0.152846946023513,0.157135557088419,0.161398254937372,0.165635350843057,0.169847150503061,0.174033954172217,0.178196056791052,0.182333748110464,0.186447312812762,0.190537030629205,0.194603176454146,0.198646020455907,0.202665828184497,0.206662860676282,0.210637374555696,0.214589622134111,0.218519851505953,0.222428306642154,0.226315227481025,0.230180850016647,0.234025406384847,0.237849124946852,0.241652230370675,0.245434943710336,0.249197482482952,0.252940060743801,0.256662889159388,0.260366175078606,0.264050122602032,0.267714932649428,0.27136080302549,0.274987928483917,0.278596500789833,0.282186708780624,0.285758738425237,0.28931277288198,0.292848992554878,0.29636757514862,0.299868695722149,0.303352526740917,0.306819238127871,0.310268997313177,0.313701969282747,0.317118316625587,0.320518199579996,0.323901776078671,0.327269201792725,0.330620630174663,0.333956212500342,0.337276097909945,0.340580433447994,0.343869364102426,0.347143032842772,0.350401580657447,0.353645146590184,0.356873867775635,0.360087879474161,0.363287315105835,0.36647230628367,0.369642982846115,0.372799472888804,0.375941902795617,0.37907039726904,0.382185079359861,0.385286070496206,0.388373490511953,0.391447457674511,0.394508088712009,0.397555498839889,0.400589801786934,0.403611109820736,0.406619533772616,0.409615183062025,0.41259816572042,0.415568588414642,0.418526556469804,0.421472173891696,0.424405543388734,0.427326766393445,0.430235943083511,0.433133172402389,0.436018552079497,0.438892178649995,0.44175414747417,0.444604552756418,0.447443487563854,0.450271043844541,0.453087312445365,0.455892383129544,0.458686344593795,0.461469284485167,0.464241289417534,0.467002444987775,0.469752835791627,0.472492545439244,0.475221656570442,0.47794025086966,0.480648409080628,0.483346211020758,0.486033735595258,0.48871106081098,0.491378263790006,0.494035420782978,0.496682607182178,0.499319897534363,0.501947365553361,0.504565084132436,0.507173125356416,0.509771560513614,0.512360460107507,0.514939893868228,0.517509930763825,0.52007063901133,0.522622086087623,0.525164338740102,0.527697462997158,0.530221524178468,0.532736586905098,0.535242715109435,0.53773997204493,0.540228420295688,0.54270812178587,0.545179137788943,0.547641528936767,0.550095355228515,0.552540676039452,0.554977550129552,0.557406035651973,0.559826190161378,0.562238070622126,0.564641733416315,0.567037234351689,0.569424628669413,0.571803971051719,0.574175315629419,0.576538715989293,0.57889422518136,0.581241895726016,0.583581779621066,0.585913928348631,0.588238392881949,0.590555223692053,0.592864470754353,0.5951661835551,0.597460411097751,0.59974720190923,0.602026604046082,0.604298665100539,0.606563432206478,0.60882095204529,0.611071270851651,0.613314434419207,0.615550488106163,0.617779476840786,0.620001445126824,0.622216437048836,0.624424496277439,0.626625666074476,0.628819989298101,0.631007508407787,0.633188265469254,0.635362302159324,0.637529659770698,0.639690379216666,0.641844501035738,0.643992065396206,0.646133112100643,0.648267680590327,0.650395809949597,0.652517538910153,0.654632905855282,0.656741948824027,0.658844705515286,0.660941213291862,0.663031509184442,0.665115629895523,0.667193611803277,0.669265490965366,0.671331303122688,0.673391083703087,0.675444867824987,0.677492690300993,0.67953458564143,0.681570588057829,0.683600731466371,0.685625049491273,0.687643575468131,0.689656342447218,0.691663383196723,0.693664730205956,0.695660415688507,0.697650471585352,0.699634929567921,0.701613821041125,0.703587177146336,0.705555028764328,0.707517406518178,0.709474340776123,0.711425861654379,0.713371999019927,0.71531278249325,0.717248241451041,0.719178405028872,0.72110330212382,0.723022961397072,0.724937411276477,0.726846679959077,0.728750795413596,0.7306497853829,0.732543677386424,0.734432498722561,0.736316276471024,0.738195037495177,0.740068808444335,0.741937615756026,0.743801485658236,0.745660444171613,0.747514517111649,0.749363730090831,0.751208108520761,0.753047677614257,0.754882462387415,0.756712487661653,0.758537778065726,0.760358358037715,0.762174251826989,0.763985483496142,0.765792076922911,0.767594055802058,0.76939144364724,0.771184263792849,0.772972539395827,0.774756293437462,0.776535548725163,0.778310327894205,0.78008065340946,0.781846547567103,0.783608032496294,0.785365130160845,0.787117862360864,0.788866250734374,0.790610316758918,0.792350081753142,0.79408556687836,0.795816793140096,0.797543781389608,0.799266552325402,0.800985126494711,0.802699524294972,0.804409765975279,0.806115871637812,0.80781786123926,0.809515754592221,0.811209571366584,0.812899331090896,0.814585053153717,0.81626675680495,0.817944461157162,0.819618185186888,0.821287947735917,0.822953767512567,0.824615663092941,0.82627365292217,0.827927755315643,0.829577988460219,0.831224370415426,0.832866919114651,0.834505652366307,0.836140587854997,0.837771743142653,0.839399135669672,0.841022782756034,0.842642701602409,0.844258909291246,0.84587142278786,0.847480258941495,0.849085434486386,0.850686966042797,0.852284870118059,0.853879163107589,0.855469861295895,0.857056980857585,0.85864053785834,0.860220548255901,0.861797027901029,0.863369992538458,0.864939457807842,0.866505439244685,0.868067952281265,0.869627012247548,0.871182634372086,0.872734833782914,0.874283625508431,0.875829024478274,0.877371045524178,0.878909703380835,0.880445012686736,0.881976987985006,0.883505643724233,0.885030994259284,0.886553053852114,0.888071836672564,0.889587356799159,0.891099628219884,0.892608664832961,0.894114480447617,0.895617088784841,0.897116503478134,0.89861273807425,0.900105806033934,0.901595720732645,0.903082495461277,0.904566143426867,0.906046677753306,0.907524111482029,0.908998457572704,0.910469728903921,0.911937938273859,0.913403098400959,0.914865221924583,0.91632432140567,0.917780409327379,0.919233498095736,0.920683600040265,0.922130727414614,0.92357489239718,0.925016107091722,0.926454383527967,0.92788973366222,0.929322169377952,0.930751702486395,0.932178344727126,0.933602107768643,0.935023003208942,0.936441042576079,0.937856237328734,0.939268598856766,0.940678138481765,0.94208486745759,0.943488796970918,0.944889938141768,0.946288302024038,0.94768389960602,0.949076741810925,0.950466839497392,0.951854203459998,0.953238844429759,0.954620773074632,0.956,0.957376535749171,0.958750390803852,0.960121575584636,0.961490100451469,0.962855975704125,0.964219211582667,0.965579818267912,0.966937805881885,0.968293184488268,0.969645964092853,0.970996154643981,0.972343766032983,0.973688808094614,0.975031290607483,0.976371223294482,0.977708615823205,0.979043477806369,0.980375818802231,0.98170564831499,0.983032975795203,0.984357810640184,0.985680162194403,0.987000039749882,0.988317452546588,0.989632409772818,0.990944920565586,0.992254994011005,0.993562639144661,0.994867864951988,0.996170680368641,0.997471094280861,0.998769115525838 19 | // ,1.0 20 | }; 21 | 22 | static void shaper_prep_v( uint8_t* zone 23 | , float* coeff 24 | , float shape 25 | , float* shape_audio 26 | , int b_size 27 | ) 28 | { 29 | for( int i=0; i 1016) { fmp = 1016; } // clamp after increasing level 176 | trunk = (int32_t)fmp; // truncate down to int 177 | if(trunk < 0) { trunk = 0; } 178 | trunk2 = trunk+1; 179 | if(trunk2>1023) { trunk2 = 1023; } // limited above to 1016 anyway 180 | funk = fmp - (float)trunk; // find fract diff 181 | } 182 | switch(curve_mode[block]){ 183 | case 3: { // EXP-SQ 184 | return (exp_[trunk] + funk*(exp_[trunk2] - exp_[trunk])); 185 | // break; 186 | } 187 | } 188 | } else if(dir != STOP) { 189 | if(curve_mode[block] == 3) { // SQ slide/fade 190 | fmp = sq_[(int32_t)(1023.0f * curve_mix[block])] * (1024-(samp * i8192f)); 191 | if(fmp >= 1016) fmp = 1016; // clamp after increasing level 192 | trunk = (int32_t)fmp; // truncate down to int 193 | if(trunk < 0) trunk = 0; 194 | trunk2 = trunk+1; // higher interp value 195 | if(trunk2>1023) { trunk2 = 1023; } 196 | funk = fmp - (float)trunk; // find fract diff 197 | } 198 | switch(curve_mode[block]){ 199 | case 3: { // EXP-SQ 200 | return (MAX24f-exp_[trunk] + funk*((MAX24f-exp_[trunk2]) - (MAX24f-exp_[trunk]))); 201 | // break; 202 | } 203 | 204 | }*/ 205 | -------------------------------------------------------------------------------- /wrOscSine.c: -------------------------------------------------------------------------------- 1 | #include "wrOscSine.h" 2 | 3 | #include // malloc() 4 | #include // printf() 5 | 6 | #include 7 | #include 8 | 9 | // build with: 10 | // for(i=0; irate = 0.02f; 29 | self->id = 0.0f; 30 | self->zero_x = 1; 31 | return self; 32 | } 33 | 34 | // input fns 35 | // expect 0-1, but can accept through -1 for TZ effects 36 | void osc_sine_time( osc_sine_t* self, float time_ ) 37 | { 38 | // 1.0 = sample_rate 39 | // 0.0 = stopped 40 | // -1. = inverse SR 41 | self->rate = lim_f_n1_1( time_ ); 42 | } 43 | 44 | void osc_sine_reset( osc_sine_t* self ) 45 | { 46 | self->id = 0.0f; 47 | self->zero_x = 1; 48 | } 49 | 50 | // status 51 | int8_t osc_sine_get_zc( osc_sine_t* self ) 52 | { 53 | return (self->zero_x); 54 | } 55 | 56 | // nb: incrementers run 0-1 w/ zero cross at 0.5 57 | 58 | // single-sample 59 | float osc_sine_step( osc_sine_t* self, float fm ) 60 | { 61 | float odd = self->id; 62 | self->id += self->rate + fm; 63 | 64 | // edge & zero-cross detection 65 | if( self->id >= 2.0f ){ 66 | self->id -= 2.0f; 67 | self->zero_x = 1; // ZERO 68 | } else if( (self->id >= 1.0f) && (odd < 1.0f) ){ 69 | self->zero_x = -1; // PEAK/TROUGH 70 | } else if( self->id < 0.0f ){ 71 | self->id += 2.0f; 72 | self->zero_x = 1; // ZERO 73 | } else { 74 | self->zero_x = 0; 75 | } 76 | 77 | // lookup table w/ linear interpolation 78 | float fbase = (float)LUT_SIN_HALF * self->id; 79 | uint16_t base = (uint16_t)fbase; 80 | float mix = fbase - (float)base; 81 | float lut = sine_lut[base]; 82 | return (lut + mix * (sine_lut[base + 1] - lut)); 83 | } 84 | 85 | void osc_sine_process_v( osc_sine_t* self 86 | , uint16_t b_size 87 | , float* exp_fm 88 | , float* lin_fm 89 | , float* out 90 | ) 91 | { 92 | float* expfm = exp_fm; 93 | float* linfm = lin_fm; 94 | float* out2 = out; 95 | 96 | float odd; 97 | float fbase; 98 | uint32_t base; 99 | float mix; 100 | float* lut; 101 | 102 | for( uint16_t i=0; iid; 104 | self->id += self->rate * (*expfm++) + (*linfm++); 105 | 106 | // edge & zero-cross detection 107 | if( self->id >= 2.0f ){ 108 | self->id -= 2.0f; 109 | self->zero_x = i+1; 110 | } else if( (self->id >= 1.0f) && (odd < 1.0f) ){ 111 | self->zero_x = -(i+1); 112 | } else if( self->id < 0.0f ){ 113 | self->id += 2.0f; 114 | self->zero_x = 1; 115 | } else { 116 | self->zero_x = 0; 117 | } 118 | 119 | // lookup table w/ linear interpolation 120 | fbase = (float)LUT_SIN_HALF * self->id; 121 | base = (uint32_t)fbase; 122 | mix = fbase - (float)base; 123 | lut = (float*) &sine_lut[base]; 124 | *out2++ = *lut + mix * (lut[1] - *lut); 125 | } 126 | } 127 | 128 | float* sine_process_base_v( osc_sine_t* self 129 | , float* out 130 | , uint16_t b_size 131 | ) 132 | { 133 | float* out2 = out; 134 | 135 | float odd; 136 | float fbase; 137 | uint32_t base; 138 | float mix; 139 | float* lut; 140 | 141 | for( uint16_t i=0; iid; 143 | self->id += self->rate; 144 | 145 | // edge & zero-cross detection 146 | if( self->id >= 2.0f ){ 147 | self->id -= 2.0f; 148 | self->zero_x = i+1; 149 | } else if( (self->id >= 1.0f) && (odd < 1.0f) ){ 150 | self->zero_x = -(i+1); 151 | } else if( self->id < 0.0f ){ 152 | self->id += 2.0f; 153 | self->zero_x = 1; 154 | } else { 155 | self->zero_x = 0; 156 | } 157 | 158 | // lookup table w/ linear interpolation 159 | fbase = (float)LUT_SIN_HALF * self->id; 160 | base = (uint32_t)fbase; 161 | mix = fbase - (float)base; 162 | lut = (float*) &sine_lut[base]; 163 | *out2++ = *lut + mix * (lut[1] - *lut); 164 | } 165 | return out; 166 | } 167 | --------------------------------------------------------------------------------