//This code is copyright Andrew Story (Kaslai/Aslai) All rights reserved.
//This and derivative works can be distributed as seen fit,
//so long as this notice remains unchanged and present

#include "engine/chiptune.h"

void Chiptunes::parse_samplerate( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_SAMPLERATE, getInt(0) );}
void Chiptunes::parse_chan( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_CHAN, getInt(0), getString(1) );}
void Chiptunes::parse_pcm( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_PCM, getInt(0), getInt(1), getInt(2), getInt(3), getString(4) );}
void Chiptunes::parse_setsample( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_SETSAMPLE, getInt(0), getInt(1) );}
void Chiptunes::parse_lbl( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_LBL, getString(0) );}
void Chiptunes::parse_goto( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_GO2, getString(0) );}
void Chiptunes::parse_gotocount( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_GOTOCOUNT, getString(0), getInt(1) );}
void Chiptunes::parse_gotocountid( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_GOTOCOUNTID, getString(0), getInt(1), getString(2) );}
void Chiptunes::parse_gotorand( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_GOTORAND, getInt(0), getInt(1), getString(2) );}
void Chiptunes::parse_gotorandcount( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_GOTORANDCOUNT, getInt(0), getInt(1), getString(2) );}
void Chiptunes::parse_gotorandcountid( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_GOTORANDCOUNTID, getInt(0), getString(1), getInt(2), getString(3) );}
void Chiptunes::parse_resetcount( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_RESETCOUNT );}
void Chiptunes::parse_resetcountid( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_RESETCOUNTID, getString(0) );}
void Chiptunes::parse_decay( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_DECAY, getInt(0), getInt(1), getInt(2), getString(3), getString(4) );}
void Chiptunes::parse_decaytime( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_DECAYTIME, getInt(0) );}
void Chiptunes::parse_start( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_START, getInt(0), getInt(1), getInt(2), getInt(3) );}
void Chiptunes::parse_stop( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_STOP, getInt(0) );}
void Chiptunes::parse_looppcm( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_LOOPPCM, getInt(0), getInt(1) );}
void Chiptunes::parse_freq( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_FREQ, getInt(0), getInt(1) );}
void Chiptunes::parse_wait( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_WAIT, getInt(0) );}
void Chiptunes::parse_durmult( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_DURMULT, getInt(0) );}
void Chiptunes::parse_dutycycle( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_DUTYCYCLE, getInt(0), getInt(1) );}
void Chiptunes::parse_volume( void* achip, int* args ){((Chiptunes*)achip)->push_queue( ((Chiptunes*)achip)->Parse_Chan, Chiptunes::QUETYP_VOLUME, getInt(0), getInt(1) );}

//Add Commands Here


long long Chiptunes::queue_length( int num, int unit )
{
    unsigned long long ret = 0;
    for( int i = 0; i < Queues[ num ].EventQueue.size(); i ++ ){
        if( Queues[ num ].EventQueue[i].type == QUETYP_WAIT )
            ret += Queues[ num ].EventQueue[i].args.wait.duration;
    }
    switch( unit ){
        case 0: //Seconds
            return (ret * Queues[ num ].durmult)/sample_rate;
        case 1: //Samples
            return (ret * Queues[ num ].durmult);
        case 2: //Samples/durmult
            return (ret);
        case 3: //milliseconds
            return (ret * Queues[ num ].durmult * 1000)/sample_rate;

    }
    return 0;
}

void Chiptunes::queue_seek( int num, long long time, int unit )
{
    long long sample;
    switch( unit ){
        case 0: //Seconds
            sample = time * sample_rate; break;
        case 1: //Samples
            sample = time; break;
        case 2: //Samples/durmult
            sample = time * Queues[ num ].durmult; break;
        case 3: //milliseconds
            sample = time * sample_rate / 1000; break;
    }

    Queues[ num ].samplestowait = 0;
    Queues[ num ].queuepos = 0;
    while( sample > 0 ) {
        Queues[ num ].execute_next_cmd();
        sample -= Queues[ num ].samplestowait;
        Queues[ num ].samplestowait = 0;
    }
    Queues[ num ].samplestowait = -sample;
}

int Chiptunes::create_queue()
{
    QueueObj a;
    a.parent = this;
    a.decaytime = 1;
    a.durmult = 1;
    a.playing = false;
    a.queuepos = 0;
    a.samplestowait = 0;
    a.volume = 1000;
    Queues.push_back( a );
    return Queues.size()-1;
}

void Chiptunes::play_queue( int num )
{
    Queues[num].playing = true;
    for( int i = 0; i < Queues[num].queuechantrack.size(); i ++ ){
        channels[Queues[num].queuechantrack[i]].playing = true;
    }
}
void Chiptunes::volume_queue( int num, int vol )
{
    Queues[num].volume = vol;
}

void Chiptunes::stop_queue( int num )
{
    Queues[num].playing = false;
    Queues[num].queuepos = 0;
    Queues[num].samplestowait = 0;
    for( int i = 0; i < Queues[num].queuechantrack.size(); i ++ ){
        stop(Queues[num].queuechantrack[i]);
        //channels[Queues[num].queuechantrack[i]].playing = false;
        //channels[Queues[num].queuechantrack[i]].freq = 0;
    }
}

void Chiptunes::pause_queue( int num )
{
    Queues[num].playing = false;
    for( int i = 0; i < Queues[num].queuechantrack.size(); i ++ ){
        //stop(Queues[num].queuechantrack[i]);
        channels[Queues[num].queuechantrack[i]].playing = false;
    }
}



char* tune_to_string( int tun )
{
    switch( tun ){
        case TUNE_PCM: return      "PCM     ";
        case TUNE_TRIANGLE: return "TRIANGLE";
        case TUNE_SQUARE: return   "SQUARE  ";
        case TUNE_NOISE: return    "NOISE   ";

    }
    return "UNKNOWN ";
}

int string_to_tune( char* str )
{
    if( strcmp( str, "PCM" ) == 0 )
        return TUNE_PCM;
    if( strcmp( str, "TRIANGLE" ) == 0 )
        return TUNE_TRIANGLE;
    if( strcmp( str, "TRI" ) == 0 )
        return TUNE_TRIANGLE;
    if( strcmp( str, "SQUARE" ) == 0 )
        return TUNE_SQUARE;
    if( strcmp( str, "SQU" ) == 0 )
        return TUNE_SQUARE;
    if( strcmp( str, "NOISE" ) == 0 )
        return TUNE_NOISE;
    if( strcmp( str, "NSE" ) == 0 )
        return TUNE_NOISE;
    return -1;
}
int string_to_decaytype( char* str )
{
    if( strcmp( str, "LIN" ) == 0 )
        return TUNE_DECAY_LINEAR;
    if( strcmp( str, "LINEAR" ) == 0 )
        return TUNE_DECAY_LINEAR;
    if( strcmp( str, "QUAD" ) == 0 )
        return TUNE_DECAY_QUAD;
    if( strcmp( str, "QUADRATIC" ) == 0 )
        return TUNE_DECAY_QUAD;
    if( strcmp( str, "EXP" ) == 0 )
        return TUNE_DECAY_EXP;
    if( strcmp( str, "EXPONENTIAL" ) == 0 )
        return TUNE_DECAY_EXP;
    return TUNE_DECAY_NONE;
}
int string_to_decayto( char* str )
{
    if( strcmp( str, "VOL" ) == 0 )
        return TUNE_DECAY_VOL;
    if( strcmp( str, "VOLUME" ) == 0 )
        return TUNE_DECAY_VOL;
    if( strcmp( str, "FREQ" ) == 0 )
        return TUNE_DECAY_FREQ;
    if( strcmp( str, "FREQUENCY" ) == 0 )
        return TUNE_DECAY_FREQ;
    if( strcmp( str, "PITCH" ) == 0 )
        return TUNE_DECAY_FREQ;
    return TUNE_DECAY_VOL;
}


int Chiptunes::QueueObj::execute_next_cmd()
{
    if( playing == false )
        return 1;
    if(samplestowait > 0 ){
        samplestowait --;
        return 1;
    }
    if( queuepos >= EventQueue.size() )
        return 1;
    Queue a = EventQueue[queuepos++];
    int type = a.type;
    switch (type){
        case QUETYP_CHAN:{


        }break;
        case QUETYP_DECAY:{
            parent->decay( queuechan[a.args.decay.channel], string_to_decaytype(a.args.decay.type), a.args.decay.toward, a.args.decay.amount, string_to_decayto( a.args.decay.towhat ) );
            /*topush.args.decay.channel = va_arg( l,int  );
            topush.args.decay.amount = va_arg( l, int  );
            topush.args.decay.toward = va_arg( l, int );
            topush.args.decay.type = va_arg( l, char* );
            topush.args.decay.towhat = va_arg( l, char* );*/
        }break;
        case QUETYP_DECAYTIME:{
            decaytime = a.args.decaytime.samples;
            //topush.args.decaytime.samples = va_arg( l, int );
        }break;
        case QUETYP_DURMULT:{
            durmult = a.args.durmult.samples;
            //topush.args.durmult.samples = va_arg( l, int );
        }break;
        case QUETYP_FREQ:{
            parent->channels[queuechan[a.args.freq.channel]].freq = a.args.freq.freq;
            //topush.args.freq.channel = va_arg( l, int );
            //topush.args.freq.freq = va_arg( l, int );
        }break;
        case QUETYP_GO2:{
            queuepos = queue_label_map[ a.args.go2.id ];
            //printf( "Going to %s (pos %i)\n", a.args.go2.id, queue_label_map[ a.args.go2.id ] );
            //topush.args.go2.id = va_arg( l, char* );
        }break;
        case QUETYP_GOTOCOUNT:{
            //topush.args.gotocount.id = va_arg( l, char* );
            //topush.args.gotocount.count = va_arg( l, int );
        }break;
        case QUETYP_GOTOCOUNTID:{
            if( queue_count_map[a.args.gotocountid.countid] > 0 ){
                queuepos = queue_label_map[ a.args.gotocountid.id ];
                queue_count_map[a.args.gotocountid.countid]--;
            }

            /*topush.args.gotocountid.id = va_arg( l, char* );
            topush.args.gotocountid.count = va_arg( l, int );
            topush.args.gotocountid.countid = va_arg( l, char* );
            queue_count_map[topush.args.gotocountid.countid] = topush.args.gotocountid.count;*/
        }break;
        case QUETYP_GOTORAND:{
            //topush.args.gotorand.num = va_arg( l, int );
            //topush.args.gotorand.labels = va_arg( l, char* );
        }break;
        case QUETYP_GOTORANDCOUNT:{
            //topush.args.gotorandcount.count = va_arg( l, int );
            //topush.args.gotorandcount.num = va_arg( l, int );
            //topush.args.gotorandcount.labels = va_arg( l, char* );
        }break;
        case QUETYP_GOTORANDCOUNTID:{
            /*topush.args.gotorandcountid.count = va_arg( l, int );
            topush.args.gotorandcountid.id = va_arg( l, char* );
            topush.args.gotorandcountid.num = va_arg( l, int );
            topush.args.gotorandcountid.labels = va_arg( l, char* );
            queue_count_map[topush.args.gotorandcountid.id] = topush.args.gotorandcountid.count;*/
        }break;
        case QUETYP_LBL:{

            /*topush.args.lbl.id = va_arg( l, char* );
            queue_label_map[topush.args.lbl.id] = EventQueue.size();*/
        }break;
        case QUETYP_LOOPPCM:{
            parent->loop( queuechan[a.args.looppcm.channel], a.args.looppcm.whichpcm );
            //topush.args.looppcm.channel = va_arg( l, int );
            //topush.args.looppcm.whichpcm = va_arg( l, int );
        }break;
        case QUETYP_PCM:{
            /*topush.args.pcm.whichpcm = va_arg( l, int );
            topush.args.pcm.samplenum = va_arg( l, int );
            topush.args.pcm.rate = va_arg( l, int );
            topush.args.pcm.freq = va_arg( l, int );
            topush.args.pcm.samples = va_arg( l, char* );*/
        }break;
        case QUETYP_RESETCOUNT:{
        }break;
        case QUETYP_RESETCOUNTID:{
            queue_count_map[a.args.resetcountid.countid] = a.args.resetcountid.towhat;
            //topush.args.resetcountid.countid = va_arg( l, char* );
        }break;
        case QUETYP_SAMPLERATE:{
            //topush.args.samplerate.rate = va_arg( l, int );
        }break;
        case QUETYP_SETSAMPLE:{
            /*topush.args.setsample.channel = va_arg( l, int );
            topush.args.setsample.which = va_arg( l, int );*/
        }break;
        case QUETYP_START:{
            parent->start( queuechan[a.args.start.channel], a.args.start.freq, a.args.start.vol * volume / 1000 );
            parent->set_duration( queuechan[a.args.start.channel], a.args.start.duration );
            //printf( "START: chan:%i freq: %i vol: %i dur: %i\n", queuechan[a.args.start.channel], a.args.start.freq, a.args.start.vol, a.args.start.duration );
            /*topush.args.start.channel = va_arg( l, int );
            topush.args.start.freq = va_arg( l, int );
            topush.args.start.vol = va_arg( l, int );
            topush.args.start.duration = va_arg( l, int );*/
        }break;
        case QUETYP_STOP:{
            parent->stop(  queuechan[a.args.stop.channel] );
            parent->channels[queuechan[a.args.stop.channel]].playing = false;
            //printf("Stopping %i\n", queuechan[a.args.stop.channel] );
            //topush.args.stop.channel = va_arg( l, int );
        }break;
        case QUETYP_WAIT:{
            samplestowait = durmult * a.args.wait.duration;
            //printf("waiting %i\n", samplestowait );
            //topush.args.wait.duration = va_arg( l, int );
        }break;
        case QUETYP_DUTYCYCLE:{
            parent->channels[a.args.dutycycle.channel].dutycycle = a.args.dutycycle.amount;
            //samplestowait = durmult * a.args.wait.duration;
            //printf("waiting %i\n", samplestowait );
            //topush.args.wait.duration = va_arg( l, int );
        }break;
        case QUETYP_VOLUME:{
            parent->channels[queuechan[a.args.volume.channel]].volumemod = a.args.volume.amount;
            //parent->volume_queue( a.args.volume.channel, a.args.volume.amount );
            //samplestowait = durmult * a.args.wait.duration;
            //printf("waiting %i\n", samplestowait );
            //topush.args.wait.duration = va_arg( l, int );
        }break;
        //Add Commands Here

    }
    return 0;
}

void Chiptunes::push_queue( int num, int type, ... )
{
    Queue topush;
    topush.type = type;
    va_list l;
    va_start( l, type );
    switch (type){
        case QUETYP_CHAN:{
            topush.args.chan.channel = va_arg( l, int );
            topush.args.chan.voice = va_arg( l, char* );
            int t = add_channel( string_to_tune( topush.args.chan.voice ) );
            Queues[num].queuechan[ topush.args.chan.channel] = t;
            Queues[num].queuechantrack.push_back( t );
            Queues[num].achantrack[t] = topush.args.chan.channel;
        }break;
        case QUETYP_DECAY:{
            topush.args.decay.channel = va_arg( l,int  );
            topush.args.decay.amount = va_arg( l, int  );
            topush.args.decay.toward = va_arg( l, int );
            topush.args.decay.type = va_arg( l, char* );
            topush.args.decay.towhat = va_arg( l, char* );
        }break;
        case QUETYP_DECAYTIME:{
            topush.args.decaytime.samples = va_arg( l, int );
        }break;
        case QUETYP_DURMULT:{
            topush.args.durmult.samples = va_arg( l, int );
            Queues[num].durmult = topush.args.durmult.samples;
        }break;
        case QUETYP_FREQ:{
            topush.args.freq.channel = va_arg( l, int );
            topush.args.freq.freq = va_arg( l, int );
        }break;
        case QUETYP_GO2:{
            topush.args.go2.id = va_arg( l, char* );
        }break;
        case QUETYP_GOTOCOUNT:{
            topush.args.gotocount.id = va_arg( l, char* );
            topush.args.gotocount.count = va_arg( l, int );
        }break;
        case QUETYP_GOTOCOUNTID:{
            topush.args.gotocountid.id = va_arg( l, char* );
            topush.args.gotocountid.count = va_arg( l, int );
            topush.args.gotocountid.countid = va_arg( l, char* );
            Queues[num].queue_count_map[topush.args.gotocountid.countid] = topush.args.gotocountid.count;
        }break;
        case QUETYP_GOTORAND:{
            topush.args.gotorand.num = va_arg( l, int );
            topush.args.gotorand.labels = va_arg( l, char* );
        }break;
        case QUETYP_GOTORANDCOUNT:{
            topush.args.gotorandcount.count = va_arg( l, int );
            topush.args.gotorandcount.num = va_arg( l, int );
            topush.args.gotorandcount.labels = va_arg( l, char* );
        }break;
        case QUETYP_GOTORANDCOUNTID:{
            topush.args.gotorandcountid.count = va_arg( l, int );
            topush.args.gotorandcountid.id = va_arg( l, char* );
            topush.args.gotorandcountid.num = va_arg( l, int );
            topush.args.gotorandcountid.labels = va_arg( l, char* );
            Queues[num].queue_count_map[topush.args.gotorandcountid.id] = topush.args.gotorandcountid.count;
        }break;
        case QUETYP_LBL:{
            topush.args.lbl.id = va_arg( l, char* );
            Queues[num].queue_label_map[topush.args.lbl.id] = Queues[num].EventQueue.size();
        }break;
        case QUETYP_LOOPPCM:{
            topush.args.looppcm.channel = va_arg( l, int );
            topush.args.looppcm.whichpcm = va_arg( l, int );
        }break;
        case QUETYP_PCM:{
            topush.args.pcm.whichpcm = va_arg( l, int );
            topush.args.pcm.samplenum = va_arg( l, int );
            topush.args.pcm.rate = va_arg( l, int );
            topush.args.pcm.freq = va_arg( l, int );
            topush.args.pcm.samples = va_arg( l, char* );
        }break;
        case QUETYP_RESETCOUNT:{
        }break;
        case QUETYP_RESETCOUNTID:{
            topush.args.resetcountid.countid = va_arg( l, char* );
            topush.args.resetcountid.towhat = va_arg( l, int );

        }break;
        case QUETYP_SAMPLERATE:{
            topush.args.samplerate.rate = va_arg( l, int );
        }break;
        case QUETYP_SETSAMPLE:{
            topush.args.setsample.channel = va_arg( l, int );
            topush.args.setsample.which = va_arg( l, int );
        }break;
        case QUETYP_START:{
            topush.args.start.channel = va_arg( l, int );
            topush.args.start.freq = va_arg( l, int );
            topush.args.start.vol = va_arg( l, int );
            topush.args.start.duration = va_arg( l, int );
        }break;
        case QUETYP_STOP:{
            topush.args.stop.channel = va_arg( l, int );
        }break;
        case QUETYP_WAIT:{
            topush.args.wait.duration = va_arg( l, int );
        }break;
        case QUETYP_DUTYCYCLE:{
            topush.args.dutycycle.channel = va_arg( l, int );
            topush.args.dutycycle.amount = va_arg( l, int );

        }break;
        case QUETYP_VOLUME:{
            topush.args.volume.channel = va_arg( l, int );
            topush.args.volume.amount = va_arg( l, int );

        }break;

    }
    //Add Commands Here
    va_end( l );
    Queues[num].EventQueue.push_back(topush);
}

Chiptunes::Chiptunes(){
    modding = false;
    sample_rate = 22050;
    myparser.addFunc( "SAMPLERATE", "i", parse_samplerate, (void*)this );
    myparser.addFunc( "CHAN", "is", parse_chan, (void*)this );
    myparser.addFunc( "PCM", "iiiis", parse_pcm, (void*)this );
    myparser.addFunc( "SETSAMPLE", "ii", parse_setsample, (void*)this );
    myparser.addFunc( "LBL", "s", parse_lbl, (void*)this );
    myparser.addFunc( "GOTO", "s", parse_goto, (void*)this );
    myparser.addFunc( "GOTOCOUNT", "si", parse_gotocount, (void*)this );
    myparser.addFunc( "GOTOCOUNTID", "sis", parse_gotocountid, (void*)this );
    myparser.addFunc( "GOTORAND", "iis", parse_gotorand, (void*)this );
    myparser.addFunc( "GOTORANDCOUNT", "iis", parse_gotorandcount, (void*)this );
    myparser.addFunc( "GOTORANDCOUNTID", "isis", parse_gotorandcountid, (void*)this );
    myparser.addFunc( "RESETCOUNT", "", parse_resetcount, (void*)this );
    myparser.addFunc( "RESETCOUNTID", "s", parse_resetcountid, (void*)this );
    myparser.addFunc( "DECAY", "iiiss", parse_decay, (void*)this );
    myparser.addFunc( "DECAYTIME", "i", parse_decaytime, (void*)this );
    myparser.addFunc( "START", "iiii", parse_start, (void*)this );
    myparser.addFunc( "STOP", "i", parse_stop, (void*)this );
    myparser.addFunc( "LOOPPCM", "ii", parse_looppcm, (void*)this );
    myparser.addFunc( "FREQ", "ii", parse_freq, (void*)this );
    myparser.addFunc( "WAIT", "i", parse_wait, (void*)this );
    myparser.addFunc( "DURMULT", "i", parse_durmult, (void*)this );
    myparser.addFunc( "DUTYCYCLE", "ii", parse_dutycycle, (void*)this );
    myparser.addFunc( "VOLUME", "ii", parse_volume, (void*)this );

    //Add Commands Here

    //echolast = 0;
    //echodelaypos = 0;
    //memset( echodelays, 0, 22050/4*sizeof(int) );
}
void Chiptunes::set_duration( int channel, int amount ){
    channels[channel].duration = amount;
}
Chiptunes::PCM* Chiptunes::set_samples( int channel, void* samples, int sample_cnt, int rate, int basefreq )
{
    PCM* pcm = new PCM;
    pcm->basefreq = basefreq;
    pcm->samplerate = rate;
    pcm->finished = false;
    pcm->overflowamt = 0;
    pcm->samplepos = 0;
    pcm->samples = samples;
    pcm->sample_count = sample_cnt;
    pcm->started = true;
    channels[channel].samples = pcm;
    return pcm;
}
void Chiptunes::loop( int channel, bool should ){
    channels[channel].looping = should;
}
void Chiptunes::decay( int channel, int type, int goal, int delta, bool effect )
{
    channels[channel].decaytype = type;
    channels[channel].decayeffect = effect;
    channels[channel].decaymax = goal;
    channels[channel].decaydelta = delta;
    if( type == TUNE_DECAY_QUAD )
        channels[channel].seek = 0;
    channels[channel].decayoverflow = 0;


}
void Chiptunes::decay_end( int channel )
{
    channels[channel].decaytype = TUNE_DECAY_NONE;
}

int Chiptunes::add_channel( int type )
{
   // while(modding);modding = true;

    channels.push_back( { type, 0, 0, 0, 1000, 0, 0, 0, TUNE_DECAY_NONE, -1, 0, 0, 500, 0, true, false, false, false } );
    //modding = false;
    return channels.size()-1;
}
void Chiptunes::start( int channel, int frequency, int volume )
{
   // while(modding);modding = true;
    if( frequency > 0 )
        channels[channel].freq = frequency;
    if( volume < 1000 )
        channels[channel].volume = volume;
    channels[channel].playing = true;
   // modding = false;
}
void Chiptunes::stop( int channel )
{
    //while(modding);modding = true;
    channels[channel].playing = false;
    channels[channel].volume = 0;
    //modding = false;
}

int Chiptunes::rem_channel( int num )
{
    //while(modding);modding = true;
    channels[num].valid = false;
    //modding = false;
    return channels[num].valid;
}

int Chiptunes::render_sample( int channel ){

    Channel a = channels[channel];
    /*if( a.seek > sample_rate * 5 ){
        channels[channel].freq = 0;
        channels[channel].seek = 0;
        return 0;
    }*/
    a.volume = a.volume * a.volumemod / 1000;
    if( a.duration == 0 || a.volume == 0 || a.freq == 0 || a.playing == false ) return 0;

    if( a.duration > 0 )
        channels[channel].duration -= 1;
    int *eff = ( a.decayeffect == TUNE_DECAY_VOL )?&(channels[channel].volume):&(channels[channel].freq);
    int *ovf = &(channels[channel].decayoverflow);
    switch( a.decaytype ){
        case TUNE_DECAY_LINEAR:
            if( a.decaymax < *eff ){ *eff -= a.decaydelta/10000; *ovf -= a.decaydelta%10000; if( *ovf <= -10000 ){*eff += *ovf/10000; *ovf%=10000;}
                if( a.decaymax >= *eff ) {channels[channel].decaytype = TUNE_DECAY_NONE;*eff=a.decaymax;}
            } else { *eff += a.decaydelta/10000; *ovf += a.decaydelta%10000; if( *ovf >= 10000 ){*eff += *ovf/10000; *ovf%=10000;}
                if( a.decaymax <= *eff ) {channels[channel].decaytype = TUNE_DECAY_NONE;*eff=a.decaymax;}}
            break;
        case TUNE_DECAY_QUAD:
            if( a.decaymax < *eff ){ *eff -= a.decaydelta*2*a.seek/1000/10000; *ovf -= (a.decaydelta*2*a.seek/1000)%10000; if( *ovf <= -10000 ){*eff += *ovf/10000; *ovf%=10000;}
                if( a.decaymax >= *eff ) {channels[channel].decaytype = TUNE_DECAY_NONE;*eff=a.decaymax;}
            } else { *eff += a.decaydelta*2*a.seek/1000/10000; *ovf += (a.decaydelta*2*a.seek/1000)%10000; if( *ovf >= 10000 ){*eff += *ovf/10000; *ovf%=10000;}
                if( a.decaymax <= *eff ) {channels[channel].decaytype = TUNE_DECAY_NONE;*eff=a.decaymax;}}
            break;
        case TUNE_DECAY_EXP:
            if( a.decaymax < *eff ){ *eff = ((*eff-a.decaymax) * a.decaydelta) / 10000 + a.decaymax; *ovf += ((*eff-a.decaymax) * a.decaydelta) % 10000; if( abs(*ovf) >= 10000 ){*eff += *ovf/10000; *ovf%=10000;}
                if( a.decaymax >= *eff ) {channels[channel].decaytype = TUNE_DECAY_NONE;*eff=a.decaymax;}
            } else { *eff = ((*eff-a.decaymax) * a.decaydelta) / 10000 + a.decaymax; *ovf += ((*eff-a.decaymax) * a.decaydelta) % 10000; if( abs(*ovf) >= 10000 ){*eff += *ovf/10000; *ovf%=10000;}
                if( a.decaymax <= *eff ) {channels[channel].decaytype = TUNE_DECAY_NONE;*eff=a.decaymax;}}
            break;
    }
    channels[channel].seek ++;
    if( a.valid == false || a.playing == false )
        return 0;
    switch( a.type ){
        case TUNE_SQUARE:{
            return 1000 * a.freq * a.seek / sample_rate %(1000) < a.dutycycle ? a.volume : -a.volume;
            break;
        }
        case TUNE_TRIANGLE:{
            return abs( 512 * a.seek * a.freq / sample_rate % 512 - 256 ) * a.volume / 128 - a.volume;
            break;
        }
        case TUNE_NOISE:{
            return rand() % a.volume - a.volume / 2;
            break;
        }
        case TUNE_PCM:{
            if( a.samples == 0 ) return 0;
            if( a.samples->finished == true ) return 0;
            a.samples->started = true;
            int t = ((unsigned char*)a.samples->samples)[a.samples->samplepos];
            t -= 127;
            t = t * a.volume / 64;
            int adv = (a.freq * a.samples->samplerate) / (a.samples->basefreq * sample_rate);
            a.samples->overflowamt += (a.freq * a.samples->samplerate) % (a.samples->basefreq * sample_rate);
            if( a.samples->overflowamt >= abs(a.samples->basefreq * sample_rate) ){
                adv += a.samples->overflowamt / (a.samples->basefreq * sample_rate);
                 a.samples->overflowamt %= (a.samples->basefreq * sample_rate);
            }
            a.samples->samplepos += adv;

            if( a.samples->samplepos >= a.samples->sample_count ){
                a.samples->finished = true;
                if( a.looping == true ){
                    a.samples->finished = false;
                    a.samples->overflowamt = 0;
                    a.samples->samplepos = 0;
                    t = ((unsigned char*)a.samples->samples)[a.samples->sample_count-1];
                    t -= 127;
                    t = t * a.volume / 127;
                }
            }
            return t;
            break;
        }

    }
    return 0;
}
void* Chiptunes::get_samples( int sample_count, void* buffer )
{
    //while(modding);modding = true;
    unsigned char* renderbuf = (unsigned char*) buffer;
    int len = channels.size();
    int cur_samp = 0;

    for( int k = 0; k < sample_count; k ++ ){

        for( int i = 0; i < Queues.size(); i ++ )
            while( (Queues[i].execute_next_cmd()) == 0 /*&& tracker++ < 128*/ ){}
        for( int i = 0; i < len; i ++ ){
            if( channels[i].valid == false || channels[i].playing == false )
                continue;
            cur_samp += render_sample( i );
        }
        cur_samp /= 2;

        //cur_samp += echodelays[echodelaypos]*7/10;
        //echodelays[echodelaypos] = cur_samp;
        //echodelaypos = (echodelaypos+1)%(22050/4);
        if( cur_samp < -128 ) cur_samp = -128;
        if( cur_samp > 127 ) cur_samp = 127;
        *(renderbuf++) = cur_samp;
        cur_samp = 0;
    }

    //modding = false;
    return buffer;
}

int Chiptunes::load_kau_pt( char* fname )
{
    Parse_Chan = create_queue();
    if( !(myparser.parseScipt( fname )) )
        return -1;
    return Parse_Chan;
}
int Chiptunes::load_kau_pt_mem( void* mem, int len )
{
    Parse_Chan = create_queue();
    myparser.parseScipt( mem, len );
    return Parse_Chan;
}

struct __tmp__load_kau_midi{
    int channel;
    int note;
};

#ifdef MIDI
#include "midireader.h"
int Chiptunes::load_kau_midi( char* fname )
{

    MIDI_file midir;
    struct __midifile * midi =  midi_load( fname, &midir );

    if( midir.valid == false )
        return -1;
    //struct __midifile * midi =  &midir;

    #define fprintf(a,...) {  int l = snprintf(0,0,__VA_ARGS__);\
                                while( buflen < (l + bufpos)*2 ){ buflen *= 2;\
                                a = (char*) realloc( a, buflen + 10 );}\
                                bufpos += snprintf( a + bufpos, buflen, __VA_ARGS__ ); }


    int bufpos = 0;
    int buflen = 10000;
    char* out = (char*) malloc( buflen + 10 );
    if( out <= 0 )
        return -1;
    int ticksps;
    int td = midi->head.timedivision;
    if( (td & 0x8000) != 0 ){
        ticksps = (((td & 0x7F00) - 1 ) >> 8 ) * ( td & 0xFF );
    } else {
        ticksps = (td & 0x7FFF) * 120 /*BPM*/ / 60 /*To BPS*/;
    }
    int oticksps = ticksps;
    int durmult = 100;
    int samplerate = 22050;
    fprintf( out, "[]\nSAMPLERATE %i\nDURMULT %i\n", samplerate, durmult );
    for( int i = 0; i < 16; i ++ ){
        //fprintf( out, "CHAN %i \"%s\"\n", i, "SQU" );
    }
    //bool chans[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    int chans[16] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
    int chanvols[16] = {1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000};
    int z = 2;
    std::vector<std::vector<__tmp__load_kau_midi> > playin(32);
    fprintf( out, "\n" );
    Chunk_Track* ctrack = midi->tracks;
    int curchan = 0;
    while( ctrack != 0 ){
        Chunk_Event* cev = ctrack->last;
        int lastnote[256];
        for( int i = 0; i < 256; i ++ )
            lastnote[i] = 255;
        while( cev != 0 ){
            if( cev->meta == 0 ){

                /*~~~~~~~~~~~~~~~~~~~~~~~~~~Events~~~~~~~~~~~~~~~~~~~~~~~~~~~~*\
                Note Off 	        0x8 	note number         velocity
                Note On 	        0x9 	note number         velocity
                Note Aftertouch     0xA 	note number         aftertouch value
                Controller 	        0xB 	controller number 	controller value
                Program Change 	    0xC 	program number 	    not used
                Channel Aftertouch 	0xD 	aftertouch value    not used
                Pitch Bend 	        0xE 	pitch value (LSB)   pitch value (MSB)*/

                if( cev->deltatime != 0 ){
                    fprintf( out, "WAIT %i\n", cev->deltatime * samplerate / (ticksps * durmult) );
                }

                switch( cev->event.bits.eventtype ){
                    case 0x8 :{
                        //if( lastnote[cev->event.bits.channel] == cev->param.normal.param1 ){
                            //lastnote[cev->event.bits.channel] = 255;
                            for( int i = 0; i < playin[cev->event.bits.channel].size(); i ++ ){
                                if( playin[cev->event.bits.channel][i].note == cev->param.normal.param1 ){
                                    fprintf( out, "STOP %i\n", playin[cev->event.bits.channel][i].channel );
                                    playin[cev->event.bits.channel][i].note = -1;
                                    break;
                                }
                            }

                        //}
                    }break;
                    case 0x9 :{
                        if(chans[cev->event.bits.channel] == -1)
                        {
                            chans[cev->event.bits.channel] = 0;
                            //__tmp__load_kau_midi a;
                            //a.channel = curchan++;
                            //a.note = -1;
                            //playin[cev->event.bits.channel].push_back(a);
                            //fprintf( out, "CHAN %i \"%s\"\n", a.channel, tune_to_string( chans[cev->event.bits.channel] ) );
                            //fprintf( out, "CHAN %i \"SQUARE\"\n", cev->event.bits.channel );
                        }
                        //lastnote[cev->event.bits.channel] = cev->param.normal.param1;
                        int tt = 0;
                        for( int i = 0; i < playin[cev->event.bits.channel].size(); i ++ ){
                            if( playin[cev->event.bits.channel][i].note < 0 ){
                                playin[cev->event.bits.channel][i].note = cev->param.normal.param1;
                                fprintf( out, "START %i %i %i %i\n", playin[cev->event.bits.channel][i].channel, midi_note_to_freq( cev->param.normal.param1 ), cev->param.normal.param2/2, -1 );
                                tt = 1;
                                break;
                            }
                        }
                        if( tt ) break;
                        __tmp__load_kau_midi a;
                        a.channel = curchan++;
                        a.note = cev->param.normal.param1;
                        playin[cev->event.bits.channel].push_back(a);
                        fprintf( out, "CHAN %i %s\n", a.channel, tune_to_string( chans[cev->event.bits.channel] ) );
                        fprintf( out, "START %i %i %i %i\n", a.channel, midi_note_to_freq( cev->param.normal.param1 ), cev->param.normal.param2/2, -1 );
                        fprintf( out, "VOLUME %i %i\n", a.channel, chanvols[cev->event.bits.channel]  );
                        //fprintf( out, "START %i %i %i %i\n", cev->event.bits.channel, midi_note_to_freq( cev->param.normal.param1 ), cev->param.normal.param2/4, -1 );
                    }break;
                    case 0xA :{
                        fprintf( out, "[0x%X 0x%X 0x%X 0x%X]->\n", cev->event.bits.eventtype, cev->event.bits.channel, cev->param.normal.param1, cev->param.normal.param2 );
                    }break;
                    case 0xB :{
                        //fprintf( out, "[Controller event, not implemented: 0x%X 0x%X 0x%X 0x%X]\n", cev->event.bits.eventtype, cev->event.bits.channel, cev->param.normal.param1, cev->param.normal.param2 );
                        switch(cev->param.normal.param1){
                            case 0x7:
                                for( int i = 0; i < playin[cev->event.bits.channel].size(); i ++ ){
                                    fprintf( out, "VOLUME %i %i\n", playin[cev->event.bits.channel][i].channel,  cev->param.normal.param2 * 1000 / 127 );
                                }
                                chanvols[cev->event.bits.channel] = cev->param.normal.param2 * 1000 / 127;
                            break;
                        }
                    }break;
                    case 0xC :{
                        //fprintf( out, "CHAN %i \"", cev->event.bits.channel );
                        chans[cev->event.bits.channel] = cev->param.normal.param1 % 4;
                        /*switch( cev->param.normal.param1 % 4 ){
                            case 0: fprintf( out, "SQU" ); break;
                            case 1: fprintf( out, "TRI" ); break;
                            case 2: fprintf( out, "NOISE" ); break;
                            case 3: fprintf( out, "PCM" ); break;
                        }
                        fprintf( out, "\"\n" );*/
                    }break;
                    case 0xD :{
                        fprintf( out, "[Channel aftertouch event, not implemented: 0x%X 0x%X 0x%X 0x%X]\n", cev->event.bits.eventtype, cev->event.bits.channel, cev->param.normal.param1, cev->param.normal.param2 );
                    }break;
                    case 0xE :{
                        //DECAY ChannelNum amount toward "type" "towhat"
                        for( int i = 0; i < playin[cev->event.bits.channel].size(); i ++ ){
                            unsigned short a;
                            a = (cev->param.normal.param2 & 0x7F) << 7;
                            a += (cev->param.normal.param1 & 0x7F);
                            short b;
                            b = a - 8192;
                            int lastnote = playin[cev->event.bits.channel][i].note;


                            int freq = midi_note_to_freq( lastnote );
                            int freqgoal;
                            if( b < 0 ) freqgoal = midi_note_to_freq( lastnote - 2 );
                            else        freqgoal = midi_note_to_freq( lastnote + 2 );
                            freqgoal = freq + 1 * ( freqgoal - freq ) * b / 8192;


                            fprintf( out, "DECAY %i %i %i \"EXP\" \"FREQ\"\n", playin[cev->event.bits.channel][i].channel, 9989, freqgoal );
                        }

                        //fprintf( out, "DECAY %i %i %i \"EXP\" \"FREQ\"\n", cev->event.bits.channel, 9989, freqgoal );
                    }break;
                }

            } else {
                if( cev->deltatime != 0 ){
                    fprintf( out, "WAIT %i\n", cev->deltatime * samplerate / (ticksps * durmult) );
                }
                switch( cev->param.meta.type ){
                    case 0x00 :{ //Sequence Number  Length: 2 Param: Number MSB, Number LSB

                    }break;
                    case 0x01 :{ //Text Event Length: String length     Param: ASCII text
                        //if( ((char*)cev->param.meta.param2)[0] != 0 && !(((char*)cev->param.meta.param2)[0] == '\n' && cev->param.meta.param1 < 3 )  )
                        //fprintf( out, "[Note: %*s]\n", cev->param.meta.param1, cev->param.meta.param2 );
                    }break;
                    case 0x02 :{ //Copyright notice     Length: String Length  Param: ASCII text
                        //fprintf( out, "[Copyright: %*s]\n", cev->param.meta.param1, cev->param.meta.param2 );
                    }break;
                    case 0x03 :{
                    }break;
                    case 0x04 :{
                    }break;
                    case 0x05 :{
                        fprintf( out, "%*s\n", cev->param.meta.param1, cev->param.meta.param2 );
                    }break;
                    case 0x06 :{
                    }break;
                    case 0x07 :{
                    }break;
                    case 0x20 :{
                    }break;
                    case 0x2F :{
                        for( int i = 0; i < playin.size(); i ++ ){
                            for( int j = 0; j < playin[i].size(); j ++ ){
                                //if( playin[i][j].note < 0 ){
                                    fprintf( out, "STOP %i\n", playin[i][j].channel );
                                    playin[i][j].note = -1;
                                //}
                            }
                        }

                    }break;
                    case 0x51 :{
                        unsigned char* t = (unsigned char*)cev->param.meta.param2;
                        int g = 0;
                        unsigned char* tg = (unsigned char*)&g;
                        tg[0] = t[2]; tg[1] = t[1]; tg[2] = t[0];
                        ticksps = (oticksps * 500000) / g;

                        //printf( "TPS: %i; G: %i\n", ticksps, g );
                    }break;
                    case 0x54 :{
                    }break;
                    case 0x58 :{
                    }break;
                    case 0x59 :{
                    }break;
                    case 0x7f :{
                    }break;

                }

            }
            cev = cev->prev;
        }
        ctrack = ctrack->next;
    }
    printf("\n\n%i\n\n", curchan );
    #undef fprintf
    //printf( "%s", out );
    int ret = load_kau_pt_mem( out, bufpos );
    volume_queue( ret, 500 );
    free( out );
    midi_free_file( midi );
    return ret;
}
#endif


int Chiptunes::cmd_to_str( Chiptunes::Queue que, char* buf, int len )
{

    switch (que.type){
        case QUETYP_CHAN:{
            return snprintf( buf, len, "CHAN %i %s\n", que.args.chan.channel, que.args.chan.voice );
        }break;
        case QUETYP_DECAY:{
            return snprintf( buf, len, "DECAY %i %i %i %s %s\n", que.args.decay.channel, que.args.decay.amount,
                            que.args.decay.toward, que.args.decay.type, que.args.decay.towhat );
        }break;
        case QUETYP_DECAYTIME:{
            return snprintf( buf, len, "DECAYTIME %i\n", que.args.decaytime.samples );
        }break;
        case QUETYP_DURMULT:{
            return snprintf( buf, len, "DURMULT %i\n", que.args.durmult.samples );
        }break;
        case QUETYP_FREQ:{
            return snprintf( buf, len, "FREQ %i %i\n", que.args.freq.channel, que.args.freq.freq );
        }break;
        case QUETYP_GO2:{
            return snprintf( buf, len, "GOTO %s\n",
            que.args.go2.id );
        }break;
        case QUETYP_GOTOCOUNT:{
            return snprintf( buf, len, "GOTOCOUNT %s %i\n",
            que.args.gotocount.id,
            que.args.gotocount.count );
        }break;
        case QUETYP_GOTOCOUNTID:{
            return snprintf( buf, len, "GOTOCOUNTID %s %i %s\n",
            que.args.gotocountid.id,
            que.args.gotocountid.count,
            que.args.gotocountid.countid );
        }break;
        case QUETYP_GOTORAND:{
            return snprintf( buf, len, "GOTORAND %i %s\n",
            que.args.gotorand.num,
            que.args.gotorand.labels );
        }break;
        case QUETYP_GOTORANDCOUNT:{
            return snprintf( buf, len, "GOTORANDCOUNT %i %i %s\n",
            que.args.gotorandcount.count,
            que.args.gotorandcount.num,
            que.args.gotorandcount.labels );
        }break;
        case QUETYP_GOTORANDCOUNTID:{
            return snprintf( buf, len, "GOTORANDCOUNT %i %s %i %s\n",
            que.args.gotorandcountid.count,
            que.args.gotorandcountid.id,
            que.args.gotorandcountid.num,
            que.args.gotorandcountid.labels );
        }break;
        case QUETYP_LBL:{
            return snprintf( buf, len, "LBL %s\n",
            que.args.lbl.id );
        }break;
        case QUETYP_LOOPPCM:{
            return snprintf( buf, len, "LOOPPCM %i %i\n",
            que.args.looppcm.channel,
            que.args.looppcm.whichpcm );
        }break;
        case QUETYP_PCM:{
            return snprintf( buf, len, "PCM %i %i %i %i %s\n",
            que.args.pcm.whichpcm,
            que.args.pcm.samplenum,
            que.args.pcm.rate,
            que.args.pcm.freq,
            que.args.pcm.samples );
        }break;
        case QUETYP_RESETCOUNT:{
            return snprintf( buf, len, "RESETCOUNT\n" );
        }break;
        case QUETYP_RESETCOUNTID:{
            return snprintf( buf, len, "RESETCOUNTID %s %i\n",
            que.args.resetcountid.countid,
            que.args.resetcountid.towhat );

        }break;
        case QUETYP_SAMPLERATE:{
            return snprintf( buf, len, "SAMPLERATE %i\n",
            que.args.samplerate.rate );
        }break;
        case QUETYP_SETSAMPLE:{
            return snprintf( buf, len, "SETSAMPLE %i %i\n",
            que.args.setsample.channel,
            que.args.setsample.which );
        }break;
        case QUETYP_START:{
            return snprintf( buf, len, "START %i %i %i %i\n",
            que.args.start.channel,
            que.args.start.freq,
            que.args.start.vol,
            que.args.start.duration );
        }break;
        case QUETYP_STOP:{
            return snprintf( buf, len, "STOP %i\n",
            que.args.stop.channel );
        }break;
        case QUETYP_WAIT:{
            return snprintf( buf, len, "WAIT %i\n",
            que.args.wait.duration );
        }break;
        case QUETYP_DUTYCYCLE:{
            return snprintf( buf, len, "DUTYCYCLE %i %i\n",
            que.args.dutycycle.channel,
            que.args.dutycycle.amount );
        }break;
        case QUETYP_VOLUME:{
            return snprintf( buf, len, "VOLUME %i %i\n",
            que.args.volume.channel,
            que.args.volume.amount );
        }break;

        //Add Commands Here

    }
}

void Chiptunes::save_kau_pt( QUEUE queuenum, char* fname )
{
    FILE* out = fopen( fname, "wb" );
    if( out <= 0 )
        return;
    int buflen = 1000;
    char* tmpbuf = (char*) malloc( buflen );
    for( int i = 0; i < Queues[queuenum].EventQueue.size(); i ++ ){
        while( cmd_to_str( Queues[queuenum].EventQueue[i], tmpbuf, buflen ) > buflen ){
            buflen *= 2;
            tmpbuf = (char*) realloc( tmpbuf, buflen );
        }
        fputs( tmpbuf, out );
    }
    fclose( out );
}


void Chiptunes::save_wav( QUEUE num, char* fname )
{
    int len = queue_length( num, 1 );
    FILE* a = fopen( fname, "wb" );
    if( a <= 0 ) return;
    wavhead w;
    w.ckid[0]='R';w.ckid[1]='I';w.ckid[2]='F';w.ckid[3]='F';
    w.cksize = 30 + len;
    w.waveid[0]='W';w.waveid[1]='A';w.waveid[2]='V';w.waveid[3]='E';

    w.head[0]='f';w.head[1]='m';w.head[2]='t';w.head[3]=' ';
    w.cksize2 = 16;
    w.wformattag = 1;
    w.channels = 1;
    w.samplerate = sample_rate;
    w.datarate = sample_rate;
    w.blocksize = 1;
    w.bps = 8;
    w.datah[0]='d';w.datah[1]='a';w.datah[2]='t';w.datah[3]='a';
    w.cksize3 = len;
    fwrite( &w, 44, 1, a );
    void* tmp = malloc( sample_rate );
    stop_queue( num );
    play_queue( num );
    while( len > 0 ){
        len -= sample_rate;
        get_samples( sample_rate, tmp );
        for( int i = 0; i < sample_rate; i ++ ){
            ((unsigned char*)tmp)[i] += 128;
        }
        fwrite( tmp, sample_rate, 1, a );
    }
    free( tmp );
    fclose( a );
}