/*
 * HalfNES by Andrew Hoffman
 * Licensed under the GNU GPL Version 3. See LICENSE file
 */
package com.grapeshot.halfnes.audio;

//import com.grapeshot.halfnes.ui.DebugUI;
import static com.grapeshot.halfnes.utils.*;
//import java.awt.image.BufferedImage;
import java.util.Arrays;

/**
 *
 * @author Andrew
 */
public class VRC7SoundChip implements ExpansionSoundChip {

    //Emulates the YM2413 sound chip, pretty much only found in Lagrange Point
    //sound test in lagrange point: hold A and B on controller 2 and reset.
    //this is the cut-down version from the vrc7. Only 6 channels, no percussion. 
    private static enum EnvState {

        CUTOFF, ATTACK, DECAY, RELEASE;
    }
    private final EnvState[] modenv_state = new EnvState[6], carenv_state = new EnvState[6];
    private final int[] vol = new int[6], freq = new int[6],
            octave = new int[6], instrument = new int[6],
            mod = new int[6],
            oldmodout = new int[6], out = new int[6];
    private final boolean[] key = new boolean[6], chSust = new boolean[6];
    private int fmctr = 0, amctr = 0; //free running counter for indices
    private final double[] phase = new double[6];
    private final int[] usertone = new int[8], modenv_vol = new int[6], carenv_vol = new int[6];
    private final int[][] instdata = { //instrument parameters
        usertone, //modifiable user tone register is instrument 0
        //i'm surprised no one's bothered to decap it and take a look
        //here's the latest one from rainwarrior aug.2012
        {0x03, 0x21, 0x05, 0x06, 0xB8, 0x82, 0x42, 0x27},
        {0x13, 0x41, 0x13, 0x0D, 0xD8, 0xD6, 0x23, 0x12},
        {0x31, 0x11, 0x08, 0x08, 0xFA, 0x9A, 0x22, 0x02},
        {0x31, 0x61, 0x18, 0x07, 0x78, 0x64, 0x30, 0x27},
        {0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28},
        {0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0xF5},
        {0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x16, 0x07},
        {0x23, 0x21, 0x1A, 0x17, 0xCF, 0x72, 0x25, 0x17},
        {0x15, 0x11, 0x25, 0x00, 0x4F, 0x71, 0x00, 0x11},
        {0x85, 0x01, 0x12, 0x0F, 0x99, 0xA2, 0x40, 0x02},
        {0x07, 0xC1, 0x69, 0x07, 0xF3, 0xF5, 0xA7, 0x12},
        {0x71, 0x23, 0x0D, 0x06, 0x66, 0x75, 0x23, 0x16},
        {0x01, 0x02, 0xD3, 0x05, 0xA3, 0x92, 0xF7, 0x52},
        {0x61, 0x63, 0x0C, 0x00, 0x94, 0xAF, 0x34, 0x06},
        {0x21, 0x62, 0x0D, 0x00, 0xB1, 0xA0, 0x54, 0x17}
    };
    private final static int[] LOGSIN = genlogsintbl(), EXP = genexptbl(),
            AM = genamtbl();
    private final static double[] MULTIPLIER = {0.5, 1, 2, 3, 4, 5,
        6, 7, 8, 9, 10, 10, 12, 12, 15, 15}, VIBRATO = genvibtbl();
    private final static int[] KEYSCALE = {0, 1536, 2048, 2368, 2560,
        2752, 2880, 3008, 3072, 3200, 3264, 3328, 3392, 3456, 3520, 3584
    };

    public VRC7SoundChip() {
        Arrays.fill(modenv_state, EnvState.CUTOFF);
        Arrays.fill(carenv_state, EnvState.CUTOFF);
        Arrays.fill(modenv_vol, 511);
        Arrays.fill(carenv_vol, 511);
    }

    public static int clamp(final int a) {
        return (a != (a & 0xff)) ? ((a < 0) ? 0 : 255) : a;
    }

    private static double[] genvibtbl() {
        //vibrato wavetable. Yes this is a waste of memory. sue me.
        //from looking at genplus gx code, the vibrato depth is supposed
        //to vary per octave, but exactly how is complex.

        double l = 1789773 / 6.;
        double f = 6.4;
        int depth = 10; //blatant guess
        double[] tbl = new double[(int) Math.ceil(l / f)];
        for (int x = 0; x < tbl.length; ++x) {
            tbl[x] = (depth * tri(2 * Math.PI * f * x / l));
        }
        return tbl;
    }

    private static int[] genamtbl() {
        double l = 1789773 / 6.;
        double f = 3.7;
        int depth = 128;
        int[] tbl = new int[(int) Math.ceil(l / f)];//one full cycle of wave
        for (int x = 0; x < tbl.length; ++x) {
            tbl[x] = (int) (depth * tri(2 * Math.PI * f * x / l) + depth);
            //should be a triangle wave?
        }
        return tbl;
    }

    private static double tri(double x) {
        //triangle wave function.
        x %= 2 * Math.PI;
        if (x < (Math.PI / 2)) {
            return x / (Math.PI);
        } else if (x < (3 * Math.PI) / 2) {
            return 1 - (x / (Math.PI));
        } else {
            return x / (Math.PI) - 2;
        }
    }

    private static int[] genlogsintbl() {
        int[] tbl = new int[256];
        for (int i = 0; i < tbl.length; ++i) {
            //y = round(-log(sin((x+0.5)*pi/256/2))/log(2)*256)
            //see https://docs.google.com/Doc?id=dd8kqn9f_13cqjkf4gp for info
            tbl[i] = (int) Math.round(-Math.log(Math.sin((i + 0.5) * Math.PI / 256 / 2)) / Math.log(2) * 256);
        }
        return tbl;
    }

    private static int[] genexptbl() {
        int[] tbl = new int[256];
        for (int i = 0; i < tbl.length; ++i) {
            //y = round((power(2, x/256)-1)*1024)
            tbl[i] = (int) Math.round((Math.pow(2, i / 256.) - 1) * 1024.);
        }
        return tbl;
    }

    @Override
    public final void write(int register, int data) {
        switch (register) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
            case 6:
            case 7:
                //parameters for instrument 0 (user settable instrument)
                usertone[register & 7] = data;
                break;
            case 0x10:
            case 0x11:
            case 0x12:
            case 0x13:
            case 0x14:
            case 0x15: //frequency registers for ch. 0-5
                int n = register - 0x10;
                freq[n] = (freq[n] & 0xf00) | data;
                break;
            case 0x20:
            case 0x21:
            case 0x22:
            case 0x23:
            case 0x24:
            case 0x25: // ??stooof
//f: Upper bit of frequency
//o: Octave Select 
//t: Channel keying on/off (key on = note starts, key off: note decays).
//s: bit 5 is _channel_ sustain, 
//?: bit 6 and 7 are unused?
                int m = register - 0x20;
                octave[m] = (data >> 1) & 7;
                freq[m] = (freq[m] & 0xff) | ((data & 1) << 8);
                if (((data & (BIT4)) != 0) && !key[m]) {
                    //when note is keyed on
                    carenv_state[m] = EnvState.CUTOFF;
                    modenv_state[m] = EnvState.CUTOFF;
                    // printarray(key);
                }
                //TODO: when key is released,
                //modulator release shouldn't do anything if sustain is on
                //http://famitracker.com/forum/posts.php?id=6804
                key[m] = ((data & (BIT4)) != 0);
                chSust[m] = ((data & (BIT5)) != 0);
                break;
            case 0x30:
            case 0x31:
            case 0x32:
            case 0x33:
            case 0x34:
            case 0x35: //top 4 bits instrument number, bottom 4 volume
                int j = register - 0x30;
                vol[j] = data & 0xf;
                //System.err.println(j + " " + hex(data));
                instrument[j] = (data >> 4) & 0xf;
                break;
            default:
            //System.err.println(hex(register) + " doesn't exist " + hex(data));
        }
    }
    int ch = 0;

    @Override
    public final void clock(final int cycle) {
        /*
         real chip runs at ~3.6 mhz from a separate oscillator
         but this emulation operates at NTSC NES clock freq of 1.789
         this would be a problem if anyone wanted to use it on a PAL system
         real chip takes 72 cycles to advance through all ch (49716 hz)
         I update all ch every 36 NES clocks (about the same speed) but I do the
         carrier and the modulator in the same cycle instead of separately
         */

        for (int i = 0; i < cycle; ++i) {
            ch = (ch + 1) % (36);
            if (ch < 6) {
                operate();
            }
        }
    }

    private void operate() {
        fmctr = (fmctr + 1) % VIBRATO.length;
        amctr = (amctr + 1) % AM.length;
        phase[ch] += (1 / 512.) * (freq[ch] << (octave[ch]));
        //Tuned this with audacity so it's definitely ok this time.
        phase[ch] %= 1024;
        int[] inst = instdata[instrument[ch]];
        //envelopes
        int modEnvelope = setenvelope(inst, modenv_state, modenv_vol, ch, false)
                << 2;
        int carEnvelope = setenvelope(inst, carenv_state, carenv_vol, ch, true)
                << 2;
        //key scaling
        int keyscale = KEYSCALE[freq[ch] >> 5] - 512 * (7 - octave[ch]);
        if (keyscale < 0) {
            keyscale = 0;
        }
        int modks = inst[2] >> 6;
        modks = (modks == 0) ? 0 : (keyscale >> (3 - modks));
        int carks = (inst[3] >> 6);
        carks = (carks == 0) ? 0 : (keyscale >> (3 - carks));
        int fb = (~inst[3] & 7);
        //now the operator cells
        //invaluable info: http://gendev.spritesmind.net/forum/viewtopic.php?t=386
        //http://www.smspower.org/maxim/Documents/YM2413ApplicationManual
        //http://forums.nesdev.com/viewtopic.php?f=3&t=9102
        final double modVibrato = ((inst[0] & (BIT6)) != 0) ? VIBRATO[fmctr] * (1 << octave[ch]) : 0;
        final double modFreqMultiplier = MULTIPLIER[inst[0] & 0xf];
        final int modFeedback = (fb == 7) ? 0 : (mod[ch] + oldmodout[ch]) >> (2 + fb);
        //no i don't know why it adds the last 2 old outputs but MAME
        //does it that way and the feedback doesn't sound right w/o it
        final int mod_f = modFeedback + (int) (modVibrato + modFreqMultiplier * phase[ch]);
        //each of these values is an attenuation value
        final int modVol = (inst[2] & 0x3f) * 32;//modulator vol
        final int modAM = ((inst[0] & (BIT7)) != 0) ? AM[amctr] : 0;
        final boolean modRectify = ((inst[3] & (BIT3)) != 0);
        //calculate modulator operator value
        mod[ch] = operator(mod_f, (int) (modVol + modEnvelope + modks + modAM), modRectify) << 2;
        oldmodout[ch] = mod[ch];
        //now repeat most of that for the carrier
        final double carVibrato = ((inst[1] & (BIT6)) != 0) ? VIBRATO[fmctr] * (freq[ch] << octave[ch]) / 512. : 0;
        final double carFreqMultiplier = MULTIPLIER[inst[1] & 0xf];
        final int carFeedback = (mod[ch] + oldmodout[ch]) >> 1; //inaccurately named
        final int car_f = carFeedback + (int) (carVibrato + carFreqMultiplier * phase[ch]);
        final int carVol = vol[ch] * 128; //4 bits for carrier vol not 6
        final int carAM = ((inst[1] & (BIT7)) != 0) ? AM[amctr] : 0;
        final boolean carRectify = ((inst[3] & (BIT4)) != 0);
        out[ch] = operator(car_f, (int) (carVol + carEnvelope + carks + carAM), carRectify) << 2;
        outputSample(ch);
    }

    private int operator(final int phase, final int gain, final boolean rectify) {
        return exp((logsin(phase, rectify) + gain));
    }

    private int exp(int val) {
        //perform e^x function on 13 bit fp output value using the hardware table on the chip
        //value should never be negative; if it is, find out why.
//        if (val < 0) {
//            val = 0;
//            System.err.println("why");
//           
//        }
        //values saturate instead of rolling over in the actual hardware
        if (val > (BIT13) - 1) {
            val = (BIT13) - 1;
        }
        //val &= (BIT12); //uncomment for sega smash pack vol. 1 for dreamcast
        int mantissa = EXP[(-val & 0xff)];
        int exponent = (-val) >> 8;
//        int a = (int) Math.scalb(mantissa + 1024, exponent) * s; //correct but slow
        return ((((mantissa + 1024) >> (-exponent)))) * s; //not correct for negative #s
    }
    private int s; // sign flag: positive (1), negative (-1) or muted (0)

    private int logsin(final int x, final boolean rectify) {
        //s stores sign of the output, in actual hw the sign bit bypasses 
        //everything else and goes directly to the dac.
        switch ((x >> 8) & 3) {
            case 0:
                s = 1;
                return LOGSIN[(x & 0xff)];
            case 1:
                s = 1;
                return LOGSIN[255 - (x & 0xff)];
            case 2:
                s = rectify ? 0 : -1;
                return LOGSIN[(x & 0xff)];
            case 3:
            default:
                s = rectify ? 0 : -1;
                return LOGSIN[255 - (x & 0xff)];
        }
    }
    int lpaccum = 0;
    int lpaccum2 = 0;

    private void outputSample(int ch) {
        int sample = out[ch] * 24;
        //two stage low pass filter (looked @ schematic of hybrid on PCB)
        sample += lpaccum;
        lpaccum -= sample >> 2;
        int j = lpaccum;
        j += lpaccum2;
        lpaccum2 -= j >> 2;
    }

    @Override
    public final int getval() {
        return lpaccum2;
    }
    final private static int ZEROVOL = 8388608; //2^23
    final private static int MAXVOL = 0;

//    Twiddler t = new Twiddler(0.4);
    private int setenvelope(final int[] instrument, final EnvState[] state,
            final int[] vol, final int ch, final boolean isCarrier) {
        final boolean keyscaleRate = ((instrument[(isCarrier ? 1 : 0)] & (BIT4)) != 0);
        final int ksrShift = keyscaleRate ?
                (octave[ch] << 1) + (freq[ch] >> 8)
                : octave[ch] >> 1;
        //^ the key scaling bit (java should really have unions, this is such a mess)
        /*
         Most of these constants were computed backwards from a
         table in a badly translated YM2413 technical manual 
         (that was given in ms to decay 40db) and are thus only approximate.       
         The key scaling stuff is similarly just a best guess.
         I suspect it's a 23 bit int value and each envelope update changes
         at least 1 LSB
         */

        //from docs on the OPL3: envelope starts at 511 (max attenuation)
        //and counts down to zero (no attenuation)
        //on real HW the envelope out is probably the upper 9 bits of a 23 bit
        //attenuation register (this would add 1 LSB per clock at slowest rate)
        //System.err.println(state[ch]);
        switch (state[ch]) {
            default:
            case CUTOFF:
                if (vol[ch] < ZEROVOL) {
                    vol[ch] += 16384; //decay to off in 10ms
                    /*
                     programmer's manual seems to say it takes a few ms to decay
                     before the new note starts its attack run.
                     need to listen really hard to the HW recordings and tune
                     this by ear
                     */
                } else {
                    vol[ch] = ZEROVOL;
                    if (key[ch]) {
                        /*the programmer's manual suggests that sound has to
                         decay back to zero volume when keyed on before the attack
                         happens, but other references don't say this
                         */
                        state[ch] = EnvState.ATTACK;
                        phase[ch] = 0;
                        //reset phase to avoid popping? can't tell if the chip does this.
                    }
                }
                break;
            case ATTACK:
                if (vol[ch] > MAXVOL + 8) {
                    vol[ch]-= ATTACKVAL[
                            (instrument[(isCarrier ? 5 : 4)] >> 4) * 4
                            + ksrShift
                            ];
                } else {
                    state[ch] = EnvState.DECAY;
                }
                if (!key[ch]) {
                    state[ch] = EnvState.RELEASE;
                }
                break;
            case DECAY:
                if (vol[ch] < ((instrument[(isCarrier ? 7 : 6)] >> 4)) * 524288) { // <-- check this 524288
                    //the higher the sustain value is, the lower the volume when
                    //it switches to sustain.
                    vol[ch] += DECAYVAL[(instrument[(isCarrier ? 5 : 4)] & 0xf) * 4
                            + ksrShift];
                } else {
                    state[ch] = EnvState.RELEASE;
                }
                if (!key[ch]) {
                    state[ch] = EnvState.RELEASE;
                }
                break;
            case RELEASE:

                //there are 3 things we need to know:
                //1. Is the key on?
                //2. Is the channel sustain bit set?
                //3. Is bit 5 in patch register 0 or 1 set?
                //What makes this especially confusing is that the sustain bit in the patch
                //is bit 5 and the sustain bit in the channel is *also* a bit 5,
                //in its respective register (ugh)
                //for consistency with it though let's call the channel sustain SUS
                //and the patch register D5
                boolean d5 = ((instrument[isCarrier ? 1 : 0] & (BIT5)) != 0);
                boolean SUS = chSust[ch];
                if (key[ch]) {
                    //if we're keyed on
                    if (d5) {
                        //sustained tone
                        //don't decay at all
                    } else {
                        //percussive tone
                        //decay at release rate
                        vol[ch] += DECAYVAL[(instrument[(isCarrier ? 7 : 6)] & 0xf) * 4
                                + ksrShift];
                    }
                } else {
                    //key is off
                    if (d5) {
                        //sustained tone
                        if (SUS) {
                            //decay at rate of 1.2 seconds to cut off
                            vol[ch] += DECAYVAL[5 * 4
                                    + ksrShift];
                        } else {
                            //decay at release rate
                            vol[ch] += DECAYVAL[(instrument[(isCarrier ? 7 : 6)] & 0xf) * 4
                                    + ksrShift];

                        }
                    } else {
                        //percussive tone
                        if (SUS) {
                            //decay at rate of 1.2 seconds to cut off
                            vol[ch] += DECAYVAL[5 * 4
                                    + ksrShift];
                        } else {
                            //decay at release rate prime, 310ms to cutoff
                            //according to the docs,
                            //or a rate of 7 according
                            //to tests on nesdev forums.
                            vol[ch] += DECAYVAL[7 * 4
                                    + ksrShift];
                            //maybe we do apply key scaling to these still
                        }
                    }
                }
                break;
                //there was also something about one of the decay values not working
                //if a modulator or something
                //on the famitracker forums or somewhere
        }
        if (vol[ch] < MAXVOL) {
            vol[ch] = MAXVOL;
        }
        if (vol[ch] > ZEROVOL) {
            vol[ch] = ZEROVOL;
        }
        if(state[ch] == EnvState.ATTACK){
            //logarithmic envelope attack
            //48 dB - (48 dB * ln(EGC) / ln(1<<23)) from Disch's doc
            //also accounting for env ctr is running down not up
            //this is slow and probably wrong vs real chip
            //but stays for now bc it works and sounds ok
            int output = 8388608 - (int)(8388608 * Math.log(8388608 - vol[ch]) / Math.log(8388608));
            return output >> 14;
        }
        return vol[ch] >> 14;
    }

    private final static int[] ATTACKVAL = {0, 0, 0, 0,
        98, 120, 146, 171,
        195, 216, 293, 341,
        390, 471, 602, 683,
        780, 964, 1168, 1366,
        1560, 1927, 2315, 2731,
        3075, 3855, 4682, 5461,
        6242, 8035, 9364, 10921,
        12480, 15423, 18727, 21856,
        24960, 30847, 37413, 43713,
        51130, 61580, 74991, 87425,
        99841, 123161, 149319, 173949,
        200870, 241044, 281218, 312464,
        337461, 401739, 496266, 562435,
        602609, 766957, 937392, 1205218,
        8388607, 8388607, 8388607, 8388607,
        8388607, 8388607, 8388607, 8388607,
        8388607, 8388607, 8388607, 8388607,
        8388607, 8388607, 8388607, 8388607};
    private final static int[] DECAYVAL = {0, 0, 0, 0,
        8, 10, 12, 14, //+2
        16, 20, 24, 28, //+4
        32, 40, 48, 56, //+8
        65, 77, 96, 112, //+16
        129, 161, 193, 224, //+32
        258, 321, 386, 449, //+64
        516, 643, 771, 898, //+128
        1032, 1285, 1542, 1796, //+256
        2064, 2570, 3084, 3591, //+512
        4211, 5268, 6167, 7183, //+1024
        8255, 10282, 12407, 14360, //+2048
        16510, 20552, 24668, 28745, //+4096
        33020, 41154, 49336, 57391, //+8192
        66169, 82308, 98673, 114783, //+16384
        132859, 132859, 132859, 132859,
        132859, 132859, 132859, 132859,
        132859, 132859, 132859, 132859,
        132859, 132859, 132859, 132859,};
}
//these 2 tables calculated with excel based on the envelope length table
//in the YM2413 programming guide.

//decay table is SO CLOSE to neat powers of 2
//and it really has to be created with simple logic
//on the real chip where there is no table of envelope values. 
