import java.util.Random;

/**
 * Created by zulupero on 01/12/15.
 */
public class GuitarString {
    final Random RAND = new Random();
    //final double F_ECH = StdAudio.SAMPLE_RATE ;
    double a = 0.9 ;
    double b = 1.0;
    double c = 0.15;
    final double ATTN = 0.996/(a+b+c); // 0.98 ou 0.996 ;
    int toc ;
    RingBuffer buf ;

    /**
     * Constructeur. Alloue un RingBuffer de capacité Fe/freq
     * @param freq
     */
    public GuitarString(double freq){
        toc =0 ;
        buf = new RingBuffer((int)Math.round(StdAudio.SAMPLE_RATE/ freq + 0.5));
		// on remplit avec des 0 pour pouvoir jouer, dans le main(), toutes les cordes à chaque itération, 
		// y compris celles qui n'ont pas été pluckées.
        for(int i=0; i< buf.capacity; i++ )
            buf.enqueue(0);
    }

    /**
     * Initialise le buffer avec du bruit blanc
     */
    public void pluck(){
        while(!buf.isEmpty()){
            buf.dequeue(); // le buffer est plein de 0, il faut le vider
        }
        while(!buf.isFull()){
            buf.enqueue(-0.5 + RAND.nextDouble()); // pour mettre le bruit blanc
        }
        /*
         * bricolage sur l'excitation.
         * Ce qui est dessous n'était pas demandé
         */
        for(int i=0; i< buf.capacity; i++ )
            buf.buffer[i] = -0.5 + RAND.nextDouble() ;
        /* filtrage PB du bruit blanc */
        for(int i=0; i< buf.capacity; i++ )
            buf.buffer[i] = 0.5*(buf.buffer[i]+buf.buffer[(i+1)%(buf.capacity)]) ;
        /* forçage moche des positions des attributs de la file  */
        buf.first = 0 ;
        buf.last = 0;
        buf.size = buf.capacity ;

    }

    /**
     * execute un pas de simulation
     */
    public void tic(){
        if(buf.size() > 1) {
            /*
             * version demandée dans l'énoncé :
             *  buf.enqueue((buf.dequeue()+buf.pick())*0.5*ATTN);
             */
            // version bricolée avec un filtre d'ordre 2
            buf.enqueue((buf.dequeue()*a+b*buf.pick()+c*buf.pickz1())*ATTN);
            toc++ ;
        }
    }

    /**
     * Renvoie l'échantillon à jouer (en tête de file)
     * @return
     */
    public double sample(){
        return buf.pick() ;
    }

    /**
     * Renvoie le nombre de pas de simulation effectués
     * @return
     */
    public int time(){
        return toc ;
    }

    public static void main(String[] args) {
        String keyboard = "aéze'r(tyèu_içop)^$";
        double f0 = 523.25; // do5;
        // déclaration / allocation d'un tableau de cordes,
        // à raison d'une par touche du keyboard (clavier)
        GuitarString[] guitar = new GuitarString[keyboard.length()];
        // initialisation de chaque corde avec sa propre fréquence fondamentale
        for(int s=0; s< keyboard.length(); s++ ){
            guitar[s] = new GuitarString(f0*Math.pow(2, (s-24)/12.0));
        }

        // à chaque touche frappée, on met du bruit blanc
        // dans le buffer de la corde associée.
        while (true) {
            if (StdDraw.hasNextKeyTyped()) {
                char key = StdDraw.nextKeyTyped();
                for (int s=0; s<keyboard.length(); s++){
                    if (key == keyboard.charAt(s))
                        guitar[s].pluck();
                }
            }
            // à chaque instant d'échantillonnage, on calcule
            // la valeur à envoyer sur la sortie audio
            // en faisant la somme des contributions de toutes
            // les cordes.
            // Celles qui n'ont pas été jouées ont des 0 dans leur buffer
            // et ne contribuent donc pas réellement.
            double sample = 0.0;
            for (int s=0; s<keyboard.length(); s++){
                sample += guitar[s].sample();
            }
            // on joue l'échantillon
            StdAudio.play(sample);

            // on avance d'un pas les simulations de toutes les cordes.
            for (int s=0; s<keyboard.length(); s++){
                guitar[s].tic();
            }
        }
    }
}
