pure-data/extra/fiddle~/fiddle~.c

/* Copyright (c) 1997-1999 Miller Puckette and Ted Apel.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

/*
 * Fiddle is a pitch tracker hardwired to have hop size ("H") equal to
 * half its window size ("N").
 *
 * This version should compile for Max "0.26," JMAX, Pd, or Max/MSP.
 *
 * The "lastanalysis" field holds the shifted FT of the previous H
 * samples.  The buffer contains in effect points 1/2,  3/2, ..., (N-1)/2
 * of the DTFT of a real vector of length N, half of whose points are zero,
 * i.e.,  only the first H points are used.  Put another way, we get the
 * the odd-numbered points of the FFT of the H points, zero padded to 4*H in
 * length. The integer points 0, 1, ..., H-1
 * are found by interpolating these others,  using the fact that the
 * half-integer points are band-limited (they only have positive frequencies.)
 * To facilitate the interpolation the "lastanalysis" buffer contains
 * FILTSIZE extra points (1/2-FILTSIZE, ...,  -1/2) at the beginning and
 * FILTSIZE again at the end ((N+1)/2, ..., FILTSIZE+(N-1)/2).  The buffer
 * therefore has N+4*FILTSIZE floating-point numbers in it.
 *
 * after doing this I found out that you can just do a real FFT
 * of the H new points, zero-padded to contain N points, and using a similar
 * but simpler interpolation scheme you can still get 2N points of the DTFT
 * of the N points.  Jean Laroche is a big fat hen.
 *
 */


/* These pragmas are only used for MSVC, not MinGW or Cygwin <[email protected]> */
#ifdef _MSC_VER
#pragma warning (disable: 4305 4244)
#endif

/* this #ifdef does nothing, but its there... */
#ifdef _WIN32
#define flog
#define fexp
#define fsqrt
#else
#define flog
#define fexp
#define fsqrt
#endif

char fiddle_version[] =;

#ifdef JMAX
#include "fts.h"
#include <stdio.h>
#include <stdlib.h>
typedef float t_float;
typedef float t_floatarg;
typedef fts_symbol_t t_symbol;

static void *getbytes(size_t nbytes)
{
    void *ret;
    if (nbytes < 1) nbytes = 1;
    ret = (void *)malloc(nbytes);
    return (ret);
}

static void *resizebytes(void *old, size_t oldsize, size_t newsize)
{
    void *ret;
    if (newsize < 1) newsize = 1;
    ret = (void *)realloc((char *)old, newsize);
    return (ret);
}

static void freebytes(void *fatso, size_t nbytes)
{
    free(fatso);
}

#define CLASSNAME

#define OUTLETpower
#define OUTLETmicropitch1
#define OUTLETmicropitch2
#define OUTLETmicropitch3
#define OUTLETattack
#define OUTLETpitch

static fts_symbol_t *dsp_symbol = 0;
#define error

#endif /* FTS */

#ifdef MAX26
#define t_floatarg
#include "m_extern.h"
#include "d_graph.h"
#include "d_ugen.h"
#endif /* MAX26 */

#ifdef PD
#include "m_pd.h"
#endif /* PD */

#ifdef MSP
#define flog
#define fexp
#define fsqrt
#endif /* MSP */

#ifdef MSP
#include "ext.h"
#include "z_dsp.h"
#include "fft_mayer.proto.h"
typedef float t_float;
typedef double t_floatarg;
#endif /* MSP */

#include <math.h>


#define MINBIN
#define DEFAMPLO
#define DEFAMPHI
#define DEFATTACKTIME
#define DEFATTACKTHRESH
#define DEFVIBTIME
#define DEFVIBDEPTH
#define GLISS
#define DBFUDGE
#define MINFREQINBINS

#define MAXNPITCH
#define MAXHIST

#define MAXPOINTS
#define MINPOINTS
#define DEFAULTPOINTS

#define HISTORY
#define MAXPEAK
#define DEFNPEAK

#define MAXNPEAK
#define MINBW

#define BINPEROCT
#define BPERO_OVER_LOG2
#define FACTORTOBINS
#define BINGUARD
#define PARTIALDEVIANCE
#define LOGTODB

#define KNOCKTHRESH


static t_float sigfiddle_partialonset[] =;

#define NPARTIALONSET

static int sigfiddle_intpartialonset[] =;

/* these coefficients, which come from the "upsamp" subdirectory,
are a filter kernel for upsampling by a factor of two, assuming
the sound to be upsampled has no energy above half the Nyquist, i.e.,
that it's already 2x oversampled compared to the theoretically possible
sample rate.  I got these by trial and error. */

#define FILT1
#define FILT2
#define FILT3
#define FILT4
#define FILT5
#define FILTSIZE

t_peakout;

t_peak;

t_histopeak;

t_pitchhist;

t_sigfiddle;

#if CHECKER
t_float fiddle_checker[1024];
#endif

#ifdef MSP
/* Mac compiler requires prototypes for everything */

int sigfiddle_ilog2(int n);
t_float fiddle_mtof(t_float f);
t_float fiddle_ftom(t_float f);
void sigfiddle_doit(t_sigfiddle *x);
void sigfiddle_debug(t_sigfiddle *x);
void sigfiddle_print(t_sigfiddle *x);
void sigfiddle_assist(t_sigfiddle *x, void *b, long m, long a, char *s);
void sigfiddle_amprange(t_sigfiddle *x, double amplo,  double amphi);
void sigfiddle_reattack(t_sigfiddle *x, t_floatarg attacktime, t_floatarg
attackthresh);
void sigfiddle_vibrato(t_sigfiddle *x, t_floatarg vibtime, t_floatarg
vibdepth);
void sigfiddle_npartial(t_sigfiddle *x, double npartial);
void sigfiddle_auto(t_sigfiddle *x, t_floatarg f);
void sigfiddle_setnpoints(t_sigfiddle *x, t_floatarg f);
int sigfiddle_doinit(t_sigfiddle *x, long npoints, long npitch, long
npeakanal, long npeakout);
static t_int *fiddle_perform(t_int *w);
void sigfiddle_dsp(t_sigfiddle *x, t_signal **sp);
void sigfiddle_tick(t_sigfiddle *x);
void sigfiddle_bang(t_sigfiddle *x);
void sigfiddle_ff(t_sigfiddle *x);
void *sigfiddle_new(long npoints, long npitch,
    long npeakanal, long npeakout);
void msp_fft(t_float *buf, long np, long inv);
t_float msp_ffttemp[MAXPOINTS*2];
int errno;
#endif

int sigfiddle_ilog2(int n)
{}

t_float fiddle_mtof(t_float f)
{}

t_float fiddle_ftom(t_float f)
{}
#define ftom
#define mtof

void sigfiddle_doit(t_sigfiddle *x)
{}

void sigfiddle_debug(t_sigfiddle *x)
{}

void sigfiddle_print(t_sigfiddle *x)
{}

void sigfiddle_amprange(t_sigfiddle *x, t_floatarg amplo, t_floatarg amphi)
{}

void sigfiddle_reattack(t_sigfiddle *x,
    t_floatarg attacktime, t_floatarg attackthresh)
{}

void sigfiddle_vibrato(t_sigfiddle *x, t_floatarg vibtime, t_floatarg vibdepth)
{}

void sigfiddle_npartial(t_sigfiddle *x, t_floatarg npartial)
{}

void sigfiddle_auto(t_sigfiddle *x, t_floatarg f)
{}

static void sigfiddle_freebird(t_sigfiddle *x)
{}

int sigfiddle_setnpoints(t_sigfiddle *x, t_floatarg fnpoints)
{}

int sigfiddle_doinit(t_sigfiddle *x, long npoints, long npitch,
    long npeakanal, long npeakout)
{}

    /* formalities for JMAX */

#ifdef JMAX

void sigfiddle_debug13(fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at)
{
  t_sigfiddle *x = (t_sigfiddle *)o;
  sigfiddle_debug(x);
}

void sigfiddle_print13(fts_object_t *o, int winlet, fts_symbol_t s,
    int ac, const fts_atom_t *at)
{
  t_sigfiddle *x = (t_sigfiddle *)o;
  sigfiddle_print(x);
}

void sigfiddle_amprange13(fts_object_t *o, int winlet, fts_symbol_t s,
    int ac, const fts_atom_t *at)
{
    t_sigfiddle *x = (t_sigfiddle *)o;
    t_float lo =  (t_float) fts_get_float_arg(ac, at, 0, 0);
    t_float hi =  (t_float) fts_get_float_arg(ac, at, 1, 0);
    sigfiddle_amprange(x, lo, hi);
}

void sigfiddle_reattack13(fts_object_t *o, int winlet, fts_symbol_t s,
    int ac, const fts_atom_t *at)
{
    t_sigfiddle *x = (t_sigfiddle *)o;
    long msec =  fts_get_float_arg(ac, at, 0, 0);
    t_float db =  (t_float) fts_get_float_arg(ac, at, 1, 0);
    sigfiddle_reattack(x, msec, db);
}

void sigfiddle_vibrato13(fts_object_t *o, int winlet, fts_symbol_t s,
    int ac, const fts_atom_t *at)
{
    t_sigfiddle *x = (t_sigfiddle *)o;
    long msec =  fts_get_float_arg(ac, at, 0, 0);
    t_float halftones =  (t_float) fts_get_float_arg(ac, at, 1, 0);
    sigfiddle_vibrato(x, msec, halftones);
}

void sigfiddle_npartial13(fts_object_t *o, int winlet, fts_symbol_t s,
    int ac, const fts_atom_t *at)
{
    t_sigfiddle *x = (t_sigfiddle *)o;
    t_float npartial =  (t_float) fts_get_float_arg(ac, at, 0, 0);
    sigfiddle_npartial(x, npartial);
}


void ftl_sigfiddle(fts_word_t *a)
{
    t_sigfiddle *x = (t_sigfiddle *)fts_word_get_long(a);
    t_float *in = (t_float *)fts_word_get_long(a + 1);
    long n_tick = fts_word_get_long(a + 2);

    int count;
    t_float *fp,  *fp2;
    for (count = 0, fp = x->x_inbuf + x->x_phase;
            count < n_tick; count++) *fp++ = *in++;
    if (fp == x->x_inbuf + x->x_hop)
    {
        sigfiddle_doit(x);
        x->x_phase = 0;
        fts_alarm_set_delay(&x->x_clock, 0L);        /* output bang */
        fts_alarm_arm(&x->x_clock);

        if (x->x_nprint) x->x_nprint--;
    }
    else x->x_phase += n_tick;
}

void sigfiddle_put(fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at)
{
    t_sigfiddle *x = (t_sigfiddle *)o;
    fts_dsp_descr_t *dsp = (fts_dsp_descr_t *)fts_get_long_arg(ac, at, 0, 0);
    fts_atom_t a[3];

    x->x_sr = fts_dsp_get_input_srate(dsp, 0);
    sigfiddle_reattack(x, x->x_attacktime, x->x_attackthresh);
    sigfiddle_vibrato(x, x->x_vibtime, x->x_vibdepth);

    fts_set_long(a, (long)x);
    fts_set_symbol(a+1, fts_dsp_get_input_name(dsp, 0));
    fts_set_long(a+2, fts_dsp_get_input_size(dsp, 0));
    dsp_add_funcall(dsp_symbol, 3, a);
}

void sigfiddle_tick(fts_alarm_t *alarm, void *p)
{
    fts_object_t *o = (fts_object_t *)p;
    t_sigfiddle *x = (t_sigfiddle *)p;

    int i;
    t_pitchhist *ph;
    fts_outlet_float(o, OUTLETpower, x->x_dbs[x->x_histphase]);
    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)
    {
        fts_atom_t at[2];
        fts_set_float(at, ph->h_pitches[x->x_histphase]);
        fts_set_float(at+1, ph->h_amps[x->x_histphase]);
        fts_outlet_list(o, OUTLETmicropitch3 - i, 2, at);
    }
    if (x->x_attackvalue) fts_outlet_bang(o, OUTLETattack);
    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)
        if (ph->h_pitch) fts_outlet_float(o, OUTLETpitch, ph->h_pitch);
}

static void sigfiddle_delete(fts_object_t *o, int winlet, fts_symbol_t *s, int ac,
 const fts_atom_t *at)
{
  t_sigfiddle *x = (t_sigfiddle *)o;

  fts_free(x->x_inbuf);
  fts_free(x->x_lastanalysis);
  fts_free(x->x_spiral);
  dsp_list_remove(o);
}

static void sigfiddle_init(fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at)
{
    t_sigfiddle *x = (t_sigfiddle *)o;
    t_float *buf1, *buf2,  *buf3;
    int i, hop;
    long npoints    = fts_get_long_arg(ac, at, 1, 0);
    long npitch    = fts_get_long_arg(ac, at, 2, 0);
    long npeakanal    = fts_get_long_arg(ac, at, 3, 0);
    long npeakout    = fts_get_long_arg(ac, at, 4, 0);

    if (!sigfiddle_doinit(x, npoints, npitch, npeakanal, npeakout))
    {
        post("fiddle~: initialization failed");
        return;
    }
    hop = npoints>>1;
    if (fts_fft_declaresize(hop) != fts_Success)
        post("fiddle~: bad FFT size");

    fts_alarm_init(&(x->x_clock), 0, sigfiddle_tick, x);
    dsp_list_insert(o);
}

static fts_status_t sigfiddle_instantiate(fts_class_t *cl, int ac,
    const fts_atom_t *at)
{
  int i;
  fts_type_t a[5];

  fts_class_init(cl, sizeof(t_sigfiddle), 1, 6, 0);  /* 1 inlet + 6 outlets */

  /* the system methods */

  a[0] = fts_Symbol;
  a[1] = fts_Long | fts_OptArg;
  a[2] = fts_Long | fts_OptArg;
  fts_method_define(cl, fts_SystemInlet, fts_s_init, sigfiddle_init, 3, a);

  fts_method_define(cl, fts_SystemInlet, fts_s_delete, sigfiddle_delete, 0, a);
  a[0] = fts_Object;
  fts_method_define(cl, fts_SystemInlet, fts_s_put, sigfiddle_put, 1, a);

  /* class' own methods */
  fts_method_define(cl, 0, fts_new_symbol("print"), sigfiddle_print13, 0, a);
  fts_method_define(cl, 0, fts_new_symbol("debug"), sigfiddle_debug13, 0, a);
  fts_method_define(cl, 0, fts_new_symbol("amp-range"), sigfiddle_amprange13,
        0, a);
  fts_method_define(cl, 0, fts_new_symbol("reattack"), sigfiddle_reattack13,
        0, a);
  fts_method_define(cl, 0, fts_new_symbol("vibrato"), sigfiddle_vibrato13,
        0, a);
  fts_method_define(cl, 0, fts_new_symbol("npartial"), sigfiddle_npartial13,
        0, a);

  /* classes signal inlets */
  dsp_sig_inlet(cl, 0);                    /* declare signal input #0 */

  /* classes outlets */
  a[0] = fts_Float;
  fts_outlet_type_define(cl, OUTLETpitch, fts_s_float, 1, a); /* declare outlet #0 */
  fts_outlet_type_define(cl, OUTLETattack, fts_s_bang, 0, a); /* declare outlet #1 */
  a[0] = fts_VarArgs;
  fts_outlet_type_define(cl, OUTLETmicropitch1, fts_s_list, 1, a); /* declare outlet #2 */
  fts_outlet_type_define(cl, OUTLETmicropitch2, fts_s_list, 1, a); /* declare outlet #3 */
  fts_outlet_type_define(cl, OUTLETmicropitch3, fts_s_list, 1, a); /* declare outlet #4 */
  a[0] = fts_Float;
  fts_outlet_type_define(cl, OUTLETpower, fts_s_float, 1, a); /* declare outlet #5 */

  dsp_symbol = fts_new_symbol("fiddle");
  dsp_declare_function(dsp_symbol, ftl_sigfiddle);

  /* DSP properties  */

  fts_class_put_prop(cl, fts_s_dsp_is_sink, fts_true);

  return(fts_Success);
}

void fiddle_config(void)
{
  sys_log(fiddle_version);
  fts_metaclass_create(fts_new_symbol(CLASSNAME), sigfiddle_instantiate, fts_always_equiv);
}

fts_module_t fiddle_module =
  {"fiddle", "sonic meat fiddle", fiddle_config, 0};

#endif  /* JMAX */

#ifdef PD

static t_int *fiddle_perform(t_int *w)
{}

void sigfiddle_dsp(t_sigfiddle *x, t_signal **sp)
{}

    /* This is the callback function for the clock, but also acts as
    the "bang" method; you can leave "auto" on to get this called
    automatically (the default) or turn auto off and bang it yourself. */

void sigfiddle_bang(t_sigfiddle *x)
{}

void sigfiddle_ff(t_sigfiddle *x)               /* cleanup on free */
{}

static t_class *sigfiddle_class;

void *sigfiddle_new(t_floatarg npoints, t_floatarg npitch,
    t_floatarg fnpeakanal, t_floatarg fnpeakout)
{}

void fiddle_tilde_setup(void)
{}

void fiddle_setup(void)
{}
#endif /* PD */

#ifdef MAX26

void cu_fiddle(t_float *in1, t_sigfiddle *x, int n)
{
    int count;
    t_float *fp,  *fp2;
    for (count = 0, fp = x->x_inbuf + x->x_phase;
            count < n; count++) *fp++ = *in1++;
    if (fp == x->x_inbuf + x->x_hop)
    {
        sigfiddle_doit(x);
        x->x_phase = 0;
        if (x->x_auto) clock_delay(x->x_clock, 0L);
        if (x->x_nprint) x->x_nprint--;
    }
    else x->x_phase += n;
}

void sigfiddle_put(t_sigfiddle *x, long whether)
{
    if (whether)
    {
        u_stdout(x);
        x->x_sr = x->x_io[0]->s_sr;
        sigfiddle_reattack(x, x->x_attacktime, x->x_attackthresh);
        sigfiddle_vibrato(x, x->x_vibtime, x->x_vibdepth);
        dspchain_addc(cu_fiddle, 3,
            x->x_io[0]->s_shit, x, x->x_io[0]->s_n);
    }
}

void sigfiddle_tick(t_sigfiddle *x)     /* callback function for the clock */
{
    int i;
    t_pitchhist *ph;
    outlet_float(x->x_envout, x->x_dbs[x->x_histphase]);
    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)
    {
        t_atom at[2];
        SETFLOAT(at, ph->h_pitches[x->x_histphase]);
        SETFLOAT(at+1, ph->h_amps[x->x_histphase]);
        outlet_list(ph->h_outlet, NIL, 2, at);
    }
    if (x->x_attackvalue) outlet_bang(x->x_attackout);
    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)
        if (ph->h_pitch) outlet_float(x->x_noteout, ph->h_pitch);
}

void sigfiddle_ff(t_sigfiddle *x)               /* cleanup on free */
{
    if (x->x_inbuf)
    {
        freebytes(x->x_inbuf, sizeof(t_float) * x->x_hop);
        freebytes(x->x_lastanalysis, sizeof(t_float) * (2*x->x_hop + 4 * FILTSIZE));
        freebytes(x->x_spiral, sizeof(t_float) * 2*x->x_hop);
        clock_free(x->x_clock);
        u_clean(x);
    }
}

t_externclass *sigfiddle_class;

void *sigfiddle_new(long npoints, long npitch,
    long npeakanal, long npeakout)
{
    t_sigfiddle *x = (t_sigfiddle *)obj_new(&sigfiddle_class, 0);
    int i;

    if (!sigfiddle_doinit(x, npoints, npitch, npeakanal, npeakout))
    {
        x->x_inbuf = 0;     /* prevent the free routine from cleaning up */
        obj_free(x);
        return (0);
    }
    u_setup(x, IN1, OUT0);
    x->x_envout = outlet_new(x, gensym("float"));
    for (i = 0; i < x->x_npitch; i++)
        x->x_hist[i].h_outlet = outlet_new(x, gensym("list"));
    x->x_attackout = outlet_new(x, gensym("bang"));
    x->x_noteout = outlet_new(x, gensym("float"));
    x->x_clock = clock_new(x, sigfiddle_tick);
    return (x);
}

void fiddle_setup()
{
    c_extern(&sigfiddle_class, sigfiddle_new, sigfiddle_ff,
        gensym("fiddle"), sizeof(t_sigfiddle), 0, A_DEFLONG, A_DEFLONG,
            A_DEFLONG, A_DEFLONG, 0);
    c_addmess(sigfiddle_put, gensym("put"), A_CANT, 0);
    c_addmess(sigfiddle_debug, gensym("debug"), 0);
    c_addmess(sigfiddle_amprange, gensym("amp-range"), A_FLOAT, A_FLOAT, 0);
    c_addmess(sigfiddle_reattack, gensym("reattack"), A_FLOAT, A_FLOAT, 0);
    c_addmess(sigfiddle_vibrato, gensym("vibrato"), A_LONG, A_FLOAT, 0);
    c_addmess(sigfiddle_npartial, gensym("npartial"), A_FLOAT, 0);
    c_addmess(sigfiddle_print, gensym("print"), 0);
    u_inletmethod(0);   /* one signal input */
#ifdef MAX
    post(fiddle_version);
#endif
}

#endif /* MAX26 */

/************* Beginning of MSP Code ******************************/

#ifdef MSP

static t_int *fiddle_perform(t_int *w)
{
    t_float *in = (t_float *)(w[1]);
    t_sigfiddle *x = (t_sigfiddle *)(w[2]);
    int n = (int)(w[3]);
    int count,inc = x->x_downsample;
    t_float *fp;

    if (x->x_obj.z_disabled)
        goto skip;
    for (count = 0, fp = x->x_inbuf + x->x_phase; count < n; count+=inc) {
        *fp++ = *in;
        in += inc;
    }
    if (fp == x->x_inbuf + x->x_hop)
    {
                sigfiddle_doit(x);
                x->x_phase = 0;
                if (x->x_auto) clock_delay(x->x_clock, 0L);
                if (x->x_nprint) x->x_nprint--;
    }
    else x->x_phase += n;
skip:
    return (w+4);
}

void sigfiddle_dsp(t_sigfiddle *x, t_signal **sp)
{
     if (sp[0]->s_n > x->x_hop) {
        x->x_downsample = sp[0]->s_n / x->x_hop;
        post("* warning: fiddle~: will downsample input by %ld",x->x_downsample);
        x->x_sr = sp[0]->s_sr / x->x_downsample;
    } else {
        x->x_downsample = 1;
                x->x_sr = sp[0]->s_sr;
        }
        sigfiddle_reattack(x, x->x_attacktime, x->x_attackthresh);
    sigfiddle_vibrato(x, x->x_vibtime, x->x_vibdepth);
    dsp_add(fiddle_perform, 3, sp[0]->s_vec, x, (t_int)sp[0]->s_n);
}

void sigfiddle_tick(t_sigfiddle *x)     /* callback function for the clock MSP*/
{
    int i;
    t_pitchhist *ph;
    if (x->x_npeakout)
    {
        int npeakout = x->x_npeakout;
        t_peakout *po;
        for (i = 0, po = x->x_peakbuf; i < npeakout; i++, po++)
        {
                t_atom at[3];
                SETINT(at, i+1);
                SETFLOAT(at+1, po->po_freq);
                SETFLOAT(at+2, po->po_amp);
                outlet_list(x->x_peakout, 0, 3, at);
                }
    }
    outlet_float(x->x_envout, x->x_dbs[x->x_histphase]);
    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)
    {
        t_atom at[2];
        SETFLOAT(at, ph->h_pitches[x->x_histphase]);
        SETFLOAT(at+1, ph->h_amps[x->x_histphase]);
        outlet_list(ph->h_outlet, 0, 2, at);
    }
    if (x->x_attackvalue) outlet_bang(x->x_attackout);
    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)
        if (ph->h_pitch) outlet_float(x->x_noteout, ph->h_pitch);
}

void sigfiddle_bang(t_sigfiddle *x)             
{
    int i;
    t_pitchhist *ph;
    if (x->x_npeakout)
    {
        int npeakout = x->x_npeakout;
        t_peakout *po;
        for (i = 0, po = x->x_peakbuf; i < npeakout; i++, po++)
        {
            t_atom at[3];
            SETLONG(at, i+1);
            SETFLOAT(at+1, po->po_freq);
            SETFLOAT(at+2, po->po_amp);
            outlet_list(x->x_peakout, 0, 3, at);
        }
    }
    outlet_float(x->x_envout, x->x_dbs[x->x_histphase]);
    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)
    {
        t_atom at[2];
        SETFLOAT(at, ph->h_pitches[x->x_histphase]);
        SETFLOAT(at+1, ph->h_amps[x->x_histphase]);
        outlet_list(ph->h_outlet, 0, 2, at);
    }
    if (x->x_attackvalue) outlet_bang(x->x_attackout);
    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)
        if (ph->h_pitch) outlet_float(x->x_noteout, ph->h_pitch);
}


void sigfiddle_ff(t_sigfiddle *x)               /* cleanup on free  MSP  */
{

    if (x->x_inbuf)
    {
        t_freebytes(x->x_inbuf, sizeof(t_float) * x->x_hop);
        t_freebytes(x->x_lastanalysis, sizeof(t_float) * (2*x->x_hop + 4 *
FILTSIZE));
        t_freebytes(x->x_spiral, sizeof(t_float) * 2*x->x_hop);
        t_freebytes(x->x_peakbuf, sizeof(*x->x_peakbuf) * x->x_npeakout);
    }
     dsp_free((t_pxobject *)x);
}

void *sigfiddle_class;

void *sigfiddle_new(long npoints, long npitch,
    long npeakanal, long npeakout)
{
    t_sigfiddle *x = (t_sigfiddle *)newobject(sigfiddle_class);
    int i;

    if (!sigfiddle_doinit(x, npoints, npitch, npeakanal, npeakout))
    {
        x->x_inbuf = 0;     /* prevent the free routine from cleaning up */
        return (0);
    }
    dsp_setup((t_pxobject *)x,1);

    x->x_clock = clock_new(x, (method)sigfiddle_tick);
     if (x->x_npeakout)
        x->x_peakout = listout((t_object *)x);
    else x->x_peakout = 0;
    x->x_envout = floatout((t_object *)x);
    for (i = 0; i < x->x_npitch; i++)
                x->x_hist[i].h_outlet = listout((t_object *)x);
        x->x_attackout = bangout((t_object *)x);
        x->x_noteout = floatout((t_object *)x);
          return (x);


}

void main()
{
        setup(&sigfiddle_class, sigfiddle_new, (method)sigfiddle_ff,
                (short)sizeof(t_sigfiddle), 0L, A_DEFLONG, A_DEFLONG,
A_DEFLONG, A_DEFLONG, 0);
        addmess((method)sigfiddle_dsp,          "dsp",
        A_CANT, 0);
    addmess((method)sigfiddle_debug,    "debug",                0);
    addmess((method)sigfiddle_setnpoints, "npoints",    A_FLOAT, 0);
    addmess((method)sigfiddle_amprange, "amp-range",    A_FLOAT, A_FLOAT, 0);
    addmess((method)sigfiddle_reattack, "reattack",     A_FLOAT, A_FLOAT, 0);
    addmess((method)sigfiddle_vibrato,  "vibrato",              A_FLOAT,
A_FLOAT, 0);
    addmess((method)sigfiddle_npartial, "npartial",     A_FLOAT, 0);
    addmess((method)sigfiddle_auto,             "auto",
        A_FLOAT, 0);
    addmess((method)sigfiddle_print,    "print",                0);
        addmess((method)sigfiddle_assist,       "assist",
        A_CANT, 0);
        addbang((method)sigfiddle_bang);
    dsp_initclass();
    rescopy('STR#',3748);
    post(fiddle_version);
}

void sigfiddle_assist(t_sigfiddle *x, void *b, long m, long a, char *s)
{
        assist_string(3748,m,a,1,2,s);
}

void msp_fft(t_float *buf, long np, long inv)
{
        t_float *src,*real,*rp,*imag,*ip;
        long i;

        /*
        // because this fft algorithm uses separate real and imaginary
        // buffers
        // we must split the real and imaginary parts into two buffers,
        // then do the opposite on output
        // a more ambitious person would either do an in-place conversion
        // or rewrite the fft algorithm
        */
    
        real = rp = msp_ffttemp;
        imag = ip = real + MAXPOINTS;
        src = buf;
        for (i = 0; i < np; i++) {
                *rp++ = *src++;
                *ip++ = *src++;
        }
        if (inv)
                ifft(np,real,imag);
        else
                fft(np,real,imag);
        rp = real;
        ip = imag;
        src = buf;
        for (i = 0; i < np; i++) {
                *src++ = *rp++;
                *src++ = *ip++;
        }
}

#endif /* MSP */