/* FFTBASS */

#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 "FFTBASS.h" /* declarations for this instrument class */
#include <rt.h>
#include <rtdefs.h>
#include <globals.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 FFTBASSFACT
{

float freq;
float mag;

};

//the STRUM queue
strumq *curstrumq[6];

extern "C"{

void sset(float, float, float, strumq*);
void randfill(float, int, strumq*);
void filefill(float, int, strumq*);
float strum(float, strumq*);
int fft(long, long, complex[]);

}

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

in = NULL;
branch = 0;
first = 1;

}

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

FFTBASS :: ~FFTBASS()
{

if (deleteflag == 1) {

delete strumq1;

}

delete [] in;

}

// FFTBASS - requires randomvalues.dat to be in the same folder
//
// p0 = start; p1 = dur; p2 = amp; p3(optional) = inchan; p4(optional) = spread
//--------------------------------------------------------------

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

float outskip, inskip, dur;

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

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

//STRUM values here
fdecay = .50; // strum fundamental decay
nydecay = 0.1; // strum Nyquist decay
amp = 1; // amplitude
squish = 1; // squish
spread = 0.5; // stereo spread
deleteflag = 1; // delete flag - see strum

nsamps = rtsetoutput(outskip, dur, this);

chordtime = 2.0;

//set up strum queue
strumq1 = new strumq;
curstrumq[0] = strumq1;

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

if (inchan >= inputchans)
die("FFTBASS", "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("FFTBASS", "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 FFTBASS :: 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 FFTBASSFACT maxes[4]={{0,0},{0,0},{0,0},{0,0}};

if (in == NULL)

in = new float [RTBUFSAMPS * inputchans];

Instrument::run();

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

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);

//check the values here. The first freq reported greater than 100

//appears to be the fundamental
if( (maxes[j].mag == 40)&&(maxes[j].freq > 100)){

//if it is the first time that this value is found, save it as the fund freq

if(first){

freq = maxes[j].freq;
first = 0;

}

}

}
printf("\n");


}//end if(cursamp.....

//strum stuff........
interval = (int)(SR/chordtime)*(dur);
if( (cursamp % interval == 0)&&(freq > 100.0)){

sset(freq, fdecay, nydecay, strumq1);
filefill(amp, squish, strumq1);
printf("Counter: %d\n", counter++);

}


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] = strum(0, strumq1) * aamp;

if (outputchans == 2) {

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

}

rtaddout(out);

cursamp++;

}//end big for loop..........

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 *makeFFTBASS()
{

FFTBASS *inst;

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

return inst;

}