/* * tabreadmix.c - an overlap add tabread~ clone * * Copyright (c) 2000-2003 by Tom Schouten * Additional code (audiorate, wrap messages) copyright (c) 2008 by * Damian Stewart, damian [at] frey [dot] co [dot] nz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "extlib_util.h" /******************** tabreadmix~ ***********************/ static t_class *tabreadmix_tilde_class; typedef struct _tabreadmix_tilde { t_object x_obj; int x_npoints; t_float *x_vec; t_symbol *x_arrayname; t_float x_f; /* file position vars */ t_float x_currpos; t_float x_prevpos; t_float x_posinc; /* cross fader state vars */ int x_xfade_size; int x_xfade_phase; t_float x_xfade_cos; t_float x_xfade_sin; t_float x_xfade_state_c; t_float x_xfade_state_s; /* wrap start and end */ int x_wrap_start; int x_wrap_end; int x_wrap_length; } t_tabreadmix_tilde; inline void tabreadmix_tilde_wrapindices(t_tabreadmix_tilde *x) { /* modulo */ while ( x->x_currpos > x->x_wrap_end ) x->x_currpos -= x->x_wrap_length; while ( x->x_prevpos > x->x_wrap_end ) x->x_prevpos -= x->x_wrap_length; /* make sure 0<=..x_npoints */ //if (x->x_currpos < 0) x->x_currpos += x->x_npoints; //if (x->x_prevpos < 0) x->x_prevpos += x->x_npoints; x->x_currpos += (x->x_currpos < 0) * x->x_npoints; x->x_prevpos += (x->x_prevpos < 0) * x->x_npoints; } #define min(x,y) ((x)<(y)?(x):(y)) static t_int *tabreadmix_tilde_perform(t_int *w) { t_tabreadmix_tilde *x = (t_tabreadmix_tilde *)(w[1]); t_float *pos = (t_float *)(w[2]); t_float *out = (t_float *)(w[3]); int n = (int)(w[4]); int maxxindex; t_float *buf = x->x_vec; int i; t_float currgain, prevgain; t_float c,s; int chunk; int leftover; int newpos = (int)*pos; // maxxindex = x->x_npoints; maxxindex = x->x_wrap_end; if (!buf) goto zero; if (maxxindex <= 0) goto zero; while (n){ /* process as much data as possible */ leftover = x->x_xfade_size - x->x_xfade_phase; chunk = min(n, leftover); for (i = 0; i < chunk; i++){ /* compute crossfade gains from oscillator state */ currgain = 0.5 - x->x_xfade_state_c; prevgain = 0.5 + x->x_xfade_state_c; /* check indices & wrap */ tabreadmix_tilde_wrapindices(x); /* mix and write */ newpos = (int)(*pos++); *out++ = currgain * buf[(int)x->x_currpos] + prevgain * buf[(int)x->x_prevpos]; x->x_currpos += x->x_posinc; x->x_prevpos += x->x_posinc; /* advance oscillator */ c = x->x_xfade_state_c * x->x_xfade_cos - x->x_xfade_state_s * x->x_xfade_sin; s = x->x_xfade_state_c * x->x_xfade_sin + x->x_xfade_state_s * x->x_xfade_cos; x->x_xfade_state_c = c; x->x_xfade_state_s = s; } /* update indices */ x->x_xfade_phase += chunk; n -= chunk; //pos += chunk; /* check if prev chunk is finished */ if (x->x_xfade_size == x->x_xfade_phase){ x->x_prevpos = x->x_currpos; x->x_currpos = newpos; x->x_xfade_state_c = 0.5; x->x_xfade_state_s = 0.0; x->x_xfade_phase = 0; } } /* return if we ran out of data */ return (w+5); zero: while (n--) *out++ = 0; return (w+5); } static void tabreadmix_tilde_blocksize(t_tabreadmix_tilde *x, t_float size) { double prev_phase; int max; t_float fmax = (t_float)x->x_wrap_length * 0.5; if (size < 1.0) size = 1.0; // post( "got input size %f\n", size ); prev_phase = (double)x->x_xfade_phase; prev_phase *= size; prev_phase /= (double)x->x_xfade_size; /* preserve the crossfader state */ x->x_xfade_phase = (int)prev_phase; x->x_xfade_size = (int)size; x->x_xfade_cos = cos(M_PI / (t_float)x->x_xfade_size); x->x_xfade_sin = sin(M_PI / (t_float)x->x_xfade_size); /* make sure indices are inside array */ if (x->x_npoints == 0){ x->x_currpos = 0; x->x_prevpos = 0; } //else tabreadmix_tilde_wrapindices(x); } void tabreadmix_tilde_wrap(t_tabreadmix_tilde *x, t_float start, t_float end ) { if ( start > end ) error( "tabreadmix~: wrap: start %i must be lower than end %i", (int)start, (int)end ); else if ( 0 > start || start > x->x_npoints ) error( "tabreadmix~: wrap: start %i must be between 0 and bufsize (%d)", (int)start, (int)x->x_npoints ); else if ( 0 > end || end > x->x_npoints ) error( "tabreadmix~: wrap: end %i must be between 0 and bufsize (%d)", (int)end, (int)x->x_npoints ); else { x->x_wrap_start = start; x->x_wrap_end = end; x->x_wrap_length = end - start; } } void tabreadmix_tilde_pitch(t_tabreadmix_tilde *x, t_float f) { if (f < 1) f = 1; tabreadmix_tilde_blocksize(x, sys_getsr() / f); } void tabreadmix_tilde_audiorate(t_tabreadmix_tilde *x, t_float f) { if ( f<0.001 ) f = 0.001; x->x_posinc = f; } void tabreadmix_tilde_chunks(t_tabreadmix_tilde *x, t_float f) { if (f < 1.0) f = 1.0; tabreadmix_tilde_blocksize(x, (t_float)x->x_npoints / f); } void tabreadmix_tilde_bang(t_tabreadmix_tilde *x, t_float f) { //trigger a chunk reset on next dsp call x->x_xfade_phase = x->x_xfade_size; } void tabreadmix_tilde_set(t_tabreadmix_tilde *x, t_symbol *s) { t_garray *a; int old_npoints = x->x_npoints; x->x_arrayname = s; if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) { if (*s->s_name) error("tabreadmix~: %s: no such array", x->x_arrayname->s_name); x->x_vec = 0; } else if (!garray_getfloatarray(a, &x->x_npoints, &x->x_vec)) { error("%s: bad template for tabreadmix~", x->x_arrayname->s_name); x->x_vec = 0; } else garray_usedindsp(a); // reset wrappage if ( old_npoints != x->x_npoints ) { x->x_wrap_start = 0; x->x_wrap_end = x->x_npoints; x->x_wrap_length = x->x_npoints; } /* make sure indices are inside array */ if (x->x_npoints == 0){ x->x_currpos = 0; x->x_prevpos = 0; } //else tabreadmix_tilde_wrapindices(x); } static void tabreadmix_tilde_dsp(t_tabreadmix_tilde *x, t_signal **sp) { tabreadmix_tilde_set(x, x->x_arrayname); dsp_add(tabreadmix_tilde_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); } static void tabreadmix_tilde_free(t_tabreadmix_tilde *x) { } static void *tabreadmix_tilde_new(t_symbol *s) { t_tabreadmix_tilde *x = (t_tabreadmix_tilde *)pd_new(tabreadmix_tilde_class); x->x_arrayname = s; x->x_vec = 0; inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("blocksize")); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; x->x_xfade_phase = 0; x->x_xfade_size = 1024; x->x_currpos = 0; x->x_prevpos = 0; x->x_posinc = 1.0; x->x_xfade_state_c = 0.5; x->x_xfade_state_s = 0.0; tabreadmix_tilde_blocksize(x, 1024); return (x); } void tabreadmix_tilde_setup(void) { tabreadmix_tilde_class = class_new(gensym("tabreadmix~"), (t_newmethod)tabreadmix_tilde_new, (t_method)tabreadmix_tilde_free, sizeof(t_tabreadmix_tilde), 0, A_DEFSYM, 0); CLASS_MAINSIGNALIN(tabreadmix_tilde_class, t_tabreadmix_tilde, x_f); class_addmethod(tabreadmix_tilde_class, (t_method)tabreadmix_tilde_dsp, gensym("dsp"), 0); class_addmethod(tabreadmix_tilde_class, (t_method)tabreadmix_tilde_set, gensym("set"), A_SYMBOL, 0); class_addmethod(tabreadmix_tilde_class, (t_method)tabreadmix_tilde_blocksize, gensym("blocksize"), A_FLOAT, 0); class_addmethod(tabreadmix_tilde_class, (t_method)tabreadmix_tilde_pitch, gensym("pitch"), A_FLOAT, 0); class_addmethod(tabreadmix_tilde_class, (t_method)tabreadmix_tilde_chunks, gensym("chunks"), A_FLOAT, 0); class_addmethod(tabreadmix_tilde_class, (t_method)tabreadmix_tilde_audiorate, gensym("audiorate"), A_FLOAT, 0 ); class_addmethod(tabreadmix_tilde_class, (t_method)tabreadmix_tilde_wrap, gensym("wrap"), A_FLOAT, A_FLOAT, 0 ); class_addmethod(tabreadmix_tilde_class, (t_method)tabreadmix_tilde_bang, gensym("bang"), 0); }