

/**
 * visualize stream
 *
 * @author matecsaba
 */
public class visDoer {

    private final static int maxX = 52;

    private final static int maxY = 14;

    private final double[] vl = new double[2048];

    private final double[] vr = new double[2048];

    /**
     * create instance
     */
    public visDoer() {
        System.out.print("\033[0;0H");
        System.out.print("\033[2J");
    }

    /**
     * do one round
     * @param buf buffer
     * @param len length
     */
    public void doer(byte[] buf, int len) {
        System.out.print("\033[0;0H");
        int num = len / (devicer.smpb * 2);
        int pos = vl.length - num;
        System.arraycopy(vl, num, vl, 0, pos);
        System.arraycopy(vr, num, vr, 0, pos);
        for (int i = 0; i < len; i += devicer.smpb * 2) {
            vl[pos] = sample(buf, i + 0);
            vr[pos] = sample(buf, i + devicer.smpb);
            pos++;
        }
        char[][] sl;
        char[][] sr;
        sl = wav(vl);
        sr = wav(vr);
        print(sl, sr);
        sl = rms(vl, 1, -1);
        sr = rms(vr, 0, 1);
        print(sl, sr);
        sl = fft(vl);
        sr = fft(vr);
        print(sl, sr);
    }

    private static void print(char[][] sl, char[][] sr) {
        for (int i = 0; i < sl.length; i++) {
            System.out.println(new String(sl[i]) + "    " + new String(sr[i]));
        }

    }

    private static double sample(byte[] buf, int ofs) {
        int i = (buf[ofs + 0] << 8) + (buf[ofs + 1] & 0xff);
        return (double) i / 32768.0;
    }

    private static int bitrev(int j, int nu) {
        int j1 = j;
        int k = 0;
        for (int i = 1; i <= nu; i++) {
            int j2 = j1 / 2;
            k = 2 * k + j1 - 2 * j2;
            j1 = j2;
        }
        return k;
    }

    private static int freqs(int x, int l, int m) {
        return (int) (Math.exp((x - l) / (double) l) * m);
    }

    private static char[][] fft(double sam[]) {
        double xre[] = new double[sam.length];
        System.arraycopy(sam, 0, xre, 0, sam.length);
        int num = xre.length;
        int nu = (int) (Math.log(num) / Math.log(2));
        int n2 = num / 2;
        int nu1 = nu - 1;
        double[] xim = new double[num];
        double[] mag = new double[n2];
        double tr, ti, p, arg, c, s;
        for (int l = 1; l <= nu; l++) {
            for (int k = 0; k < num; k += n2) {
                for (int i = 1; i <= n2; i++) {
                    p = bitrev(k >> nu1, nu);
                    arg = 2 * Math.PI * p / num;
                    c = Math.cos(arg);
                    s = Math.sin(arg);
                    tr = xre[k + n2] * c + xim[k + n2] * s;
                    ti = xim[k + n2] * c - xre[k + n2] * s;
                    xre[k + n2] = xre[k] - tr;
                    xim[k + n2] = xim[k] - ti;
                    xre[k] += tr;
                    xim[k] += ti;
                    k++;
                }
            }
            nu1--;
            n2 = n2 / 2;
        }
        for (int k = 0; k < num; k++) {
            int r = bitrev(k, nu);
            if (r > k) {
                tr = xre[k];
                ti = xim[k];
                xre[k] = xre[r];
                xim[k] = xim[r];
                xre[r] = tr;
                xim[r] = ti;
            }
        }
        mag[0] = Math.sqrt(xre[0] * xre[0] + xim[0] * xim[0]) / num;
        for (int i = 1; i < num / 2; i++) {
            mag[i] = 2 * Math.sqrt(xre[i] * xre[i] + xim[i] * xim[i]) / num;
        }
        double bin[] = new double[maxX];
        double max = Double.MIN_VALUE;
        for (int i = 0; i < bin.length; i++) {
            int pos = freqs(i, bin.length, mag.length);
            num = freqs(i + 1, bin.length, mag.length) - pos;
            double cur = 0;
            for (int o = 0; o < num; o++) {
                cur += mag[pos];
                pos++;
            }
            max = Math.max(max, cur);
            bin[i] = cur;
        }
        char[][] scr = new char[maxY][bin.length];
        for (int i = 0; i < bin.length; i++) {
            bin[i] = scr.length * bin[i] / max;
        }
        for (int i = 0; i < bin.length; i++) {
            int pos = scr.length - (int) bin[i];
            for (int o = 0; o < pos; o++) {
                scr[o][i] = ' ';
            }
            for (int o = pos; o < scr.length; o++) {
                scr[o][i] = '*';
            }
        }
        return scr;
    }

    private static char[][] rms(double sam[], int beg, int dir) {
        double rms = 0;
        for (int i = 0; i < sam.length; i++) {
            double o = sam[i];
            rms += o * o;
        }
        rms = Math.sqrt(rms / (double) sam.length);
        char[] scr = new char[maxX];
        int pos = beg * (scr.length + dir);
        rms = (scr.length * Math.log10(rms)) + scr.length;
        rms = Math.max(0, rms);
        rms = Math.min(scr.length, rms);
        int cur = (int) rms;
        for (int i = 0; i < cur; i++) {
            scr[pos] = '#';
            pos += dir;
        }
        for (int i = cur; i < scr.length; i++) {
            scr[pos] = ' ';
            pos += dir;
        }
        return new char[][]{scr};
    }

    private static char[][] wav(double sam[]) {
        char[][] scr = new char[maxY][maxX];
        for (int i = 0; i < scr.length; i++) {
            for (int o = 0; o < scr[0].length; o++) {
                scr[i][o] = ' ';
            }
        }
        int hlf = scr.length / 2;
        double[] bin = new double[scr[0].length];
        int num = sam.length / bin.length;
        for (int i = 0; i < bin.length; i++) {
            int pos = i * num;
            double cur = 0;
            for (int o = 0; o < num; o++) {
                cur += sam[pos];
                pos++;
            }
            bin[i] = cur / (double) num * (double) hlf;
        }
        for (int i = 0; i < bin.length; i++) {
            int o = hlf + (int) bin[i];
            scr[o][i] = '-';
        }
        return scr;
    }

}
