/* FFTINST - runs fft on input

All it does is copy samples from one file (or audio device) and
runs fft.

p0 = output start time
p1 = input start time
p2 = input duration
p3 = amplitude multiplier
p4 = input channel [optional, default is 0]
p5 = percent of signal to left output channel [optional, default is .5]

JGG <johngbson@virginia.edu>, 12 April 2000
Judy Franklin 8/7/01
*/

#include <iostream.h> /* needed only for cout, etc. if you want it */
#include <stdio.h>
#include <stdlib.h>
#include <ugens.h>
#include <math.h>
#include <mixerr.h>
#include <Instrument.h> /* the base class for this instrument */
#include "FFTINST.h" /* declarations for this instrument class */
#include <rt.h>
#include <rtdefs.h>

/* now problem is that for >1 chan scan, hist will show || of all channels */
/* ok-->that's not a bug, it's a feature! */

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include "../H/complexf.h"
#include "../H/byte_routines.h"
#include "../H/sfheader.h"
#include <sndlibsupport.h>

SFHEADER sfh;
int bytestoread;

extern int swap; /* defined in sys/check_byte_order.c */

struct FFTINSTFACT
{

float freq;
float mag;

};

extern "C"{

int fft(long, long, complex[]);

}

/* Construct an instance of this instrument and initialize some variables. */
FFTINST :: FFTINST() : Instrument()
{

in = NULL;
branch = 0;

}


/* Destruct an instance of this instrument, freeing memory for the
input buffer.
*/

FFTINST :: ~FFTINST()
{

delete [] in;

}

int FFTINST :: init(float p[], int n_args)
{

float outskip, inskip, dur;

outskip = p[0];
inskip = p[1];
dur = p[2];
amp = p[3];

/* Here's how to handle optional pfields: */
inchan = n_args > 4 ? (int) p[4] : 0; /* default is chan 0 */
pctleft = n_args > 5 ? p[5] : 0.5; /* default is .5 */

nsamps = rtsetoutput(outskip, dur, this);

/* Set file pointer on audio input. */
rtsetinput(inskip, this);

if (inchan >= inputchans)
die("FFTINST", "You asked for channel %d of a %d-channel file.",
inchan, inputchans);

amparray = floc(1);
if (amparray) {

int lenamp = fsize(1);
tableset(dur, lenamp, amptabs);

}
else

advise("FFTINST", "Setting phrase curve to all 1's.");

/* Set control rate counter. */
skip = (int) (SR / (float) resetval);
samps = SR*.5/2;

return nsamps;

}


/* Called by the scheduler for every time slice in which this instrument
should run. This is where the real work of the instrument is done.
*/

int FFTINST :: run()
{

int i, k;
float aamp, insig;
float out[2]; /* Space for only 2 output chans! */

float end = 0; /* assume channel 0 */
int incr = RTBUFSAMPS; /* must be a power of 2 */
float *fpoint, aver, *xpoint;
int channel = (int)end + .01;
int size;
int j,jj = 0;
float output[131072];
complex s[131072];
int tempo = 120;

float peak = 0;
float nstars = 40.;
float currout=0;
struct FFTINSTFACT maxes[4]={{0,0},{0,0},{0,0},{0,0}};



/* If this is first call to run, <in> will still be NULL, so we
allocate the input buffer. We do this here, instead of in the
ctor or init method, to reduce the memory demands of the inst.
*/

if (in == NULL)

in = new float [RTBUFSAMPS * inputchans];

/* You MUST call the base class's run method here. */
Instrument::run();

/* <chunksamps> is the number of sample frames -- 1 sample for each
channel -- that we have to write during this scheduler time slice.
*/

// note: given tempo, tempo/60 is beats per second.
// 1 beat lasts 60/tempo seconds.
// SR*(60/tempo) is the number of samples per beat.

/* 1 beat takes SR*(60/tempo) samples. */
/* We could take input every 12th of a beat (in case of triplets) */
/* Therefore we'd take in SR*(60/tempo)/12 samples at a time */
/* This is the value of samps, set in init() */

// if tempo is 120, 60/tempo is .5
// and if SR is 44100, there are 22050 samples in a beat.
// 16384 is the power of two closest but less than 22050

// half a beat (an eighth note) is 11025 samples
//
// 8192 is closest power of two. So take the 11025 samples,
// but run fft only on the first 8192. See what happens.


/* Each loop iteration processes 1 sample frame. */

for (k = 0; k < chunksamps; k += inputchans) {

/* Read <samps> samples from the input file (or audio input device). */

if(cursamp%samps == 0)
{

rtgetin(in, this, samps);

aamp = amp; /* in case amparray == NULL */

// synchronization will be a problem. Should sample often
// and store freqs then decide on bassline based on stored freqs.
// so samps will be a lot smaller and may have to output some zeros
// in between.


fpoint = in;
size = 8192; // hard code for now

/* next is excerpt from sortout(buffer, size, end); */
for (i = 0, j = channel, aver = 0; i < size; i++, j += inputchans)

aver += fpoint[i] = fpoint[j];

aver /= (float)size;
printf("size = %i, aver = %f\n", size, aver);
for (i = 0; i < size; i++)

fpoint[i] -= aver; /* takes out DC bias */

xpoint = (float *)fpoint;
for (jj = 0; jj < size; jj++)
{

s[jj].re = xpoint[jj]; /* sets up array of complex numbers */
s[jj].im = 0;

}

fft(1, size, s);
for (jj = 0; jj < size; jj++)

output[jj] = s[jj].re;

for (i = 0; i < size / 2; i++)

if (output[i] > peak)

peak = output[i];

for (i = 0; i < size / 2; i++) {

currout = (nstars * output[i] / peak);

if(currout>maxes[3].mag)
{

maxes[0]=maxes[1];
maxes[1]=maxes[2];
maxes[2]=maxes[3];
maxes[3].mag=currout;
maxes[3].freq= SR * (float)i / (float)size;

}
else if(currout>maxes[2].mag)
{

maxes[0]=maxes[1];
maxes[1]=maxes[2];
maxes[2].mag=currout;
maxes[2].freq= SR * (float)i / (float)size;

}

else if(currout>maxes[1].mag)
{

maxes[0]=maxes[1];
maxes[1].mag=currout;
maxes[1].freq=SR * (float)i / (float)size;

}

else if(currout>maxes[0].mag)
{

maxes[0].mag=currout;
maxes[0].freq=SR * (float)i / (float)size;

}

}

for(j=0;j<4;++j)

printf("\n%6.0f %6.0f ", maxes[j].freq, maxes[j].mag);

printf("\n");

}


/* Every <skip> frames, update the amplitude envelope, if there
is one. This is also the place to update other values from
makegen curves, such as filter sweep, glissando, etc, and
to do any rtupdate pfield updating. (See insts.base/WAVETABLE
for an example of this.)
*/

if (--branch < 0) {

if (amparray)

aamp = tablei(cursamp, amparray, amptabs) * amp;

branch = skip;

}

/* Grab the current input sample, scaled by the amplitude multiplier. */
insig = in[k + inchan] * aamp;

/* Just copy it to the output array with no processing. */
out[0] = insig;

/* If we have stereo output, use the pctleft pfield to pan.
(Note: insts.jg/PAN/PAN.C shows a better method of panning,
using constant power panning controlled by a makegen.)
*/

if (outputchans == 2) {

out[1] = out[0] * (1.0 - pctleft);
out[0] *= pctleft;

}

/* Write this sample frame to the output buffer. */
rtaddout(out);

/* Keep track of how many sample frames this instrument has written. */
cursamp++;

}

return chunksamps;

}


/* The scheduler calls this to create an instance of this instrument,
and to set up the bus-routing fields in the base Instrument class.
This happens for every "note" in a score.
*/

Instrument *makeFFTINST()
{

FFTINST *inst;

inst = new FFTINST();
inst->set_bus_config("FFTINST");

return inst;

}


/* The rtprofile introduces this instrument to the RTcmix core, and
associates a Minc name (in quotes below) with the instrument. This
is the name the instrument goes by in a Minc script.
*/

void rtprofile()
{

RT_INTRO("FFTINST", makeFFTINST);

}