/* Copyright (c) 2002-2003 krzYszcz and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include #include "m_pd.h" #include "shared.h" #include "sickle/sic.h" #include "sickle/arsic.h" #define RECORD_REDRAWPAUSE 1000. /* refractory period */ typedef struct _record { t_arsic x_arsic; float x_startpoint; /* the inputs */ float x_endpoint; int x_appendmode; int x_loopmode; int x_startindex; int x_endindex; /* (one past last record position) */ int x_pauseindex; int x_phase; /* writing head */ float x_sync; float x_syncincr; int x_isrunning; /* to know if sync should be 0.0 or 1.0 */ t_clock *x_clock; double x_clocklasttick; } t_record; static t_class *record_class; static void record_tick(t_record *x) { double timesince = clock_gettimesince(x->x_clocklasttick); if (timesince >= RECORD_REDRAWPAUSE) { arsic_redraw((t_arsic *)x); x->x_clocklasttick = clock_getlogicaltime(); } else clock_delay(x->x_clock, RECORD_REDRAWPAUSE - timesince); } static void record_setsync(t_record *x) { /* CHECKED: clipped to array size -- using indices, not points */ float range = (float)(x->x_endindex - x->x_startindex); int phase = x->x_phase; if (phase == SHARED_INT_MAX || range < 1.) { x->x_sync = (x->x_isrunning ? 1. : 0.); /* CHECKED */ x->x_syncincr = 0.; } else { x->x_sync = (float)(phase - x->x_startindex) / range; x->x_syncincr = 1. / range; } } static void record_mstoindex(t_record *x) { t_arsic *sic = (t_arsic *)x; x->x_startindex = (int)(x->x_startpoint * sic->s_ksr); if (x->x_startindex < 0) x->x_startindex = 0; /* CHECKED */ x->x_endindex = (int)(x->x_endpoint * sic->s_ksr); if (x->x_endindex > sic->s_vecsize || x->x_endindex <= 0) x->x_endindex = sic->s_vecsize; /* CHECKED (both ways) */ record_setsync(x); } static void record_set(t_record *x, t_symbol *s) { arsic_setarray((t_arsic *)x, s, 1); record_mstoindex(x); } static void record_reset(t_record *x) { x->x_startpoint = x->x_endpoint = 0.; x->x_pauseindex = SHARED_INT_MAX; x->x_phase = SHARED_INT_MAX; x->x_isrunning = 0; record_mstoindex(x); } static void record_startpoint(t_record *x, t_floatarg f) { x->x_startpoint = f; record_mstoindex(x); } static void record_endpoint(t_record *x, t_floatarg f) { x->x_endpoint = f; record_mstoindex(x); } static void record_float(t_record *x, t_float f) { if (x->x_isrunning = (f != 0)) { /* CHECKED: no (re)start in append mode */ /* LATER consider restart if x->x_pauseindex == SHARED_INT_MAX */ x->x_phase = x->x_appendmode ? x->x_pauseindex : x->x_startindex; if (x->x_phase >= x->x_endindex) x->x_phase = SHARED_INT_MAX; } else if (x->x_phase != SHARED_INT_MAX) /* CHECKED: no rewind */ { clock_delay(x->x_clock, 10.); x->x_pauseindex = x->x_phase; x->x_phase = SHARED_INT_MAX; } record_setsync(x); } static void record_append(t_record *x, t_floatarg f) { if (f != 0) { x->x_appendmode = 1; /* CHECKED: always allow appending */ } else x->x_appendmode = 0; } static void record_loop(t_record *x, t_floatarg f) { x->x_loopmode = (f != 0); } static t_int *record_perform(t_int *w) { t_arsic *sic = (t_arsic *)(w[1]); int nblock = (int)(w[2]); int nch = sic->s_nchannels; t_float *out = (t_float *)(w[3 + nch]); t_record *x = (t_record *)sic; int phase = x->x_phase; int endphase = x->x_endindex; float sync = x->x_sync; if (sic->s_playable && endphase > phase) { int vecsize = sic->s_vecsize; float syncincr = x->x_syncincr; int ch, over, i, nxfer, ndone = 0; loopover: if ((nxfer = endphase - phase) > nblock) { nxfer = nblock; over = 0; } else over = 1; ch = nch; while (ch--) { t_word *vp = sic->s_vectors[ch]; if (vp) { t_float *ip = (t_float *)(w[3 + ch]) + ndone; vp += phase; i = nxfer; /* LATER consider handling under and overflows */ // while (i--) *vp++ = *ip++; int j = 0; while (i--) { vp[j].w_float = ip[j]; j++; } } } i = nxfer; sync = phase; syncincr = 1.; while (i--) { *out++ = sync; sync += syncincr; } if (over) { clock_delay(x->x_clock, 0); nblock -= nxfer; if (x->x_loopmode && (phase = x->x_startindex) < endphase) { x->x_phase = phase; x->x_sync = sync = 0; if (nblock > 0) { ndone += nxfer; goto loopover; } goto alldone; } /* CHECKED: no restart in append mode */ x->x_pauseindex = SHARED_INT_MAX; x->x_phase = SHARED_INT_MAX; x->x_sync = 1.; x->x_syncincr = 0.; } else { x->x_phase += nxfer; x->x_sync = sync; goto alldone; } } while (nblock--) *out++ = -1; //sync; alldone: return (w + sic->s_nperfargs + 1); } static void record_dsp(t_record *x, t_signal **sp) { arsic_dsp((t_arsic *)x, sp, record_perform, 1); record_mstoindex(x); } static void record_free(t_record *x) { arsic_free((t_arsic *)x); if (x->x_clock) clock_free(x->x_clock); } static void *record_new(t_symbol *s, t_floatarg f) { /* one auxiliary signal: sync output */ t_record *x = (t_record *)arsic_new(record_class, s, (int)f, 0, 1); if (x) { int nch = arsic_getnchannels((t_arsic *)x); arsic_setminsize((t_arsic *)x, 2); x->x_appendmode = 0; x->x_loopmode = 0; record_reset(x); x->x_clock = clock_new(x, (t_method)record_tick); x->x_clocklasttick = clock_getlogicaltime(); while (--nch) inlet_new((t_object *)x, (t_pd *)x, &s_signal, &s_signal); inlet_new((t_object *)x, (t_pd *)x, &s_float, gensym("ft-2")); inlet_new((t_object *)x, (t_pd *)x, &s_float, gensym("ft-1")); outlet_new((t_object *)x, &s_signal); } return (x); } void record_tilde_setup(void) { record_class = class_new(gensym("record~"), (t_newmethod)record_new, (t_method)record_free, sizeof(t_record), 0, A_DEFSYM, A_DEFFLOAT, 0); arsic_setup(record_class, record_dsp, record_float); class_addmethod(record_class, (t_method)record_startpoint, gensym("ft-2"), A_FLOAT, 0); class_addmethod(record_class, (t_method)record_endpoint, gensym("ft-1"), A_FLOAT, 0); class_addmethod(record_class, (t_method)record_append, gensym("append"), A_FLOAT, 0); class_addmethod(record_class, (t_method)record_loop, gensym("loop"), A_FLOAT, 0); class_addmethod(record_class, (t_method)record_set, gensym("set"), A_SYMBOL, 0); class_addmethod(record_class, (t_method)record_reset, gensym("reset"), 0); // logpost(NULL, 4, "this is cyclone/record~ %s, %dth %s build", // CYCLONE_VERSION, CYCLONE_BUILD, CYCLONE_RELEASE); }