text: #include "MSPd.h"
  
  static t_class *bvplay_class;
  
  
  #define OBJECT_NAME "bvplay~"
  typedef struct {
    t_word *b_samples;
    long b_valid;
    long b_nchans;
    long b_frames;
  } t_guffer; // stuff we care about from garrays and buffers
  
  
  typedef struct _bvplay
  {
  
    t_object x_obj;
    float x_f;
    t_symbol *sfname; // name of soundfile
    t_guffer *wavebuf; // store needed buffer or garray data
  
    long object_chans; // number of channels for a given instantiation
    float taper_dur;
    int R;
    int framesize;
    float *notedata;
    int active;
    float buffer_duration;
    int taper_frames;
    float amp;
    int start_frame;
    int note_frames;
    int end_frame;
    float increment;
    float findex;
    int index ;
    short verbose;
    short mute;
  } t_bvplay;
  
  static t_int *bvplay_perform_mono(t_int *w);
  static t_int *bvplay_perform_stereo(t_int *w);
  static void bvplay_dsp(t_bvplay *x, t_signal **sp);
  static void bvplay_set(t_bvplay *x, t_symbol *s);
  static void *bvplay_new(t_symbol *s, t_floatarg taperdur);
  static void bvplay_notelist(t_bvplay *x, t_symbol *msg, int argc, t_atom *argv );
  static void bvplay_verbose(t_bvplay *x, t_floatarg t);
  static void bvplay_mute(t_bvplay *x, t_floatarg t);
  static void bvplay_taper(t_bvplay *x, t_floatarg t);
  static void bvplay_dsp_free(t_bvplay *x);
  
  void bvplay_tilde_setup(void)
  {
    bvplay_class = class_new(gensym("bvplay~"),(t_newmethod)bvplay_new,
                             (t_method)bvplay_dsp_free, sizeof(t_bvplay), 0, A_SYMBOL, A_FLOAT,0);
    CLASS_MAINSIGNALIN(bvplay_class,t_bvplay, x_f);
    class_addmethod(bvplay_class,(t_method)bvplay_dsp,gensym("dsp"),A_CANT,0);
    class_addmethod(bvplay_class,(t_method)bvplay_notelist,gensym("list"),A_GIMME,0);
    class_addmethod(bvplay_class,(t_method)bvplay_verbose,gensym("verbose"),A_FLOAT,0);
    class_addmethod(bvplay_class,(t_method)bvplay_mute,gensym("mute"),A_FLOAT,0);
    class_addmethod(bvplay_class,(t_method)bvplay_taper,gensym("taper"),A_FLOAT,0);
  
    potpourri_announce(OBJECT_NAME);
  }
  
  void bvplay_taper(t_bvplay *x, t_floatarg t)
  {
    if(t>0) {
      x->taper_dur = (float)t/1000.0;
      x->taper_frames = x->R * x->taper_dur;
    }
  }
  
  
  void bvplay_mute(t_bvplay *x, t_floatarg f)
  {
    x->mute = (short)f;
  }
  
  void bvplay_verbose(t_bvplay *x, t_floatarg f)
  {
    x->verbose = (short)f;
  }
  
  
  void bvplay_notelist(t_bvplay *x, t_symbol *msg, int argc, t_atom *argv)
  {
  
    if( x->active ) {
      if( x->verbose )
        error("object still playing - cannot add note!");
      return;
    }
    bvplay_set(x, x->sfname);
    if(! x->wavebuf->b_valid) {
      post("%s: no valid buffer yet",OBJECT_NAME);
      return;
    }
  
    // read note data
    if( argc != 4 ) {
      if( x->verbose ) {
        post("improper note data");
        post("notelist parameters: skiptime, duration, increment, amplitude");
      }
    }
  
    x->notedata[0] = atom_getfloatarg(0,argc,argv) / 1000.0;
    x->notedata[1] = atom_getfloatarg(1,argc,argv) / 1000.0;
    x->notedata[2] = atom_getfloatarg(2,argc,argv);
    x->notedata[3] = atom_getfloatarg(3,argc,argv);
  
    x->start_frame = x->notedata[0] * x->R;
    x->increment = x->notedata[2];
    x->index = x->findex = x->start_frame;
  
    if( x->increment == 0.0 ) {
      if( x->verbose )
        post("zero increment!");
      return;
    }
    x->note_frames =  x->notedata[1] * x->increment  * x->R;
    x->end_frame = x->start_frame + x->note_frames;
  
    x->amp = x->notedata[3];
    if( x->start_frame < 0 || x->start_frame >= x->wavebuf->b_frames) {
      if( x->verbose )
        post("%s: bad start time",OBJECT_NAME);
      return;
    }
    if( x->end_frame < 0 || x->end_frame >= x->wavebuf->b_frames) {
      if( x->verbose )
        post("%s: bad end time",OBJECT_NAME);
      return;
    }
  
    x->active = 1;
  }
  
  t_int *bvplay_perform_mono(t_int *w)
  {
    t_bvplay *x = (t_bvplay *)(w[1]);
    t_float *out = (t_float *)(w[2]);
    int n = (int) w[3];
    t_word *tab;
    long iindex = x->index;
    float findex = x->findex;
    int end_frame = x->end_frame;
    float increment = x->increment;
    int start_frame = x->start_frame;
    int taper_frames = x->taper_frames;
    float noteamp = x->amp;
    float frac, amp;
    /**********************/
    bvplay_set(x,x->sfname);
  
    if(!x->wavebuf->b_valid) {
      post("invalid buffer");
      memset(out, 0, sizeof(float) * n);
      return (w+4);
    }
    tab = x->wavebuf->b_samples;
  
    if(x->active) {
      while(n--) {
        // post("index: %d endframe %d", iindex, end_frame);
        if((increment > 0 && iindex < end_frame) || (increment < 0 && iindex > end_frame)) {
          // envelope
          if( increment > 0 ) {
            if( findex < start_frame + taper_frames ) {
              amp = noteamp * ((findex - (float) start_frame) / (float) taper_frames );
            } else if ( findex > end_frame - taper_frames) {
              amp = noteamp * (((float)end_frame - findex) / (float) taper_frames);
            } else {
              amp = noteamp;
            }
          } else { // negative increment case
            if( findex > start_frame - taper_frames ) {
              amp =  noteamp * ( (start_frame - findex) / taper_frames );
            } else if ( findex < end_frame + taper_frames) {
              amp = noteamp * (( findex - end_frame ) / taper_frames) ;
            } else {
              amp = noteamp;
            }
  
          }
          frac = findex - iindex ;
          *out++ = amp * (tab[iindex].w_float + frac * (tab[iindex + 1].w_float - tab[iindex].w_float));
          findex += increment;
          iindex = findex ;
        } else {
          *out++ = 0;
          x->active = 0;
        }
      }
  
    }
    else{
      while(n--) {
        *out++ = 0;
      }
    }
  
    x->index = iindex;
    x->findex = findex;
  
    return (w+4);
  }
  
  void bvplay_set(t_bvplay *x, t_symbol *wavename)
  {
  
    t_garray *a;
    int b_frames;
    t_word *b_samples;
    if (!(a = (t_garray *)pd_findbyclass(wavename, garray_class))) {
      if (*wavename->s_name) pd_error(x, "%s: %s: no such array",OBJECT_NAME,wavename->s_name);
      x->wavebuf->b_valid = 0;
    }
    else if (!garray_getfloatwords(a, &b_frames, &b_samples)) {
      pd_error(x, "%s: bad array for %s", wavename->s_name,OBJECT_NAME);
      x->wavebuf->b_valid = 0;
    }
    else  {
      x->wavebuf->b_valid = 1;
      x->wavebuf->b_frames = b_frames;
      x->wavebuf->b_nchans = 1;
      x->wavebuf->b_samples = b_samples;
      garray_usedindsp(a);
    }
  
  }
  
  
  void *bvplay_new(t_symbol *s, t_floatarg taperdur)
  {
    int ichan = 1;
  
    t_bvplay *x = (t_bvplay *)pd_new(bvplay_class);
    outlet_new(&x->x_obj, gensym("signal"));
    x->object_chans = ichan;
    taperdur /= 1000.0; // convert to seconds
    if(taperdur <= 0)
      taperdur = .005;
    x->sfname = s;
    x->R = sys_getsr();
    if(! x->R) {
      error("zero sampling rate - set to 44100");
      x->R = 44100;
    }
    x->notedata = (float *) calloc(4, sizeof(float));
    x->wavebuf = (t_guffer *) calloc(1, sizeof(t_guffer));
    x->taper_dur = taperdur;
    x->taper_frames = x->R * x->taper_dur;
    x->buffer_duration = 0.0 ;
    x->framesize = 0;
    x->active = 0;
    x->verbose = 0;
    x->mute = 0;
    // post("channels %f, taper duration %.4f, taperframes %d", chan, taperdur, x->taper_frames );
  
    // post("arguments: channels, taper_duration(secs.)");
    return x;
  }
  
  void bvplay_dsp_free(t_bvplay *x)
  {
    free(x->notedata);
    free(x->wavebuf);
  }
  
  void bvplay_dsp(t_bvplay *x, t_signal **sp)
  {
    bvplay_set(x,x->sfname);
  
    if(x->R != sp[0]->s_sr) {
      x->R = sp[0]->s_sr;
      x->taper_frames = x->R * x->taper_dur;
    }
    dsp_add(bvplay_perform_mono, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n);
  }
  