import android.media.AudioTrack;
import android.media.AudioFormat;
import android.media.AudioManager;

/**
* Die Puffergröße wird so groß wie eine 8-Tel gewählt.
* Somit läßt sich das Schreiben in line gut mit der Zeitsteuerung synchronisieren.
*
* Jedesmal, wenn ein INDEX-Inkrement auftritt, wird ein stereo-double-puffer, der in 8-tel-Paketen aufgeteilt ist
* an der aktuellen Idex-Stelle und den nachfolgenden gefüllt.
* Bei allen außer dem Letzten wird auf das Vorhandene aufaddiert (Nachklang).
* Beim Letzten wird dessen Inhalt zunächst gelöscht.
*
* In dem Parameter NACHKLANG ist festgelegt, wieviele Blöcke (8-tel) lang ein Ton klingt.
* 
*
*
*/

public class Rasterplayer
{
    private AudioTrack out;
    private byte[] puffer;
    private byte[] zwei = new byte[2];
    double gross = 0.5*(255.0*256.0 + 255.0);
    
    //Pentatonische Frequenzen:
    //degah
    //a  h  d  e  g  a  h  d
    // 2  3  2  3   2  2  3
    double h=Math.pow(2.0,1.0/12.0);
    double h2=h*h;
    double h3=h*h*h;
    double[] fpenta = {440.0,440.0*h2,440.0*h2*h3,440.0*h2*h3*h2,
                       440.0*h2*h3*h2*h3,440.0*h2*h3*h2*h3*h2,440.0*h2*h3*h2*h3*h2*h2,440.0*h2*h3*h2*h3*h2*h2*h3};
  
    //Paraneter für Rasterplayer
    int[][] raster;  //Spielfeld mit Einträgen der Tonereignisse.
    int NACHKLANG=3;
    int SAMPLERATE=44100;
    double dt = 1.0/(double)SAMPLERATE;
    double t=0.0;
    int PUFFERGROESSE; //Anzahl der Samples, die in einer 8-tel Platz haben.
    double[][] stereopuffer_links;
    double[][] stereopuffer_rechts;
  
    int SPALTEN,ZEILEN,BPM;

    public int benoetigteSampleanzahl()
    {
          return NACHKLANG*PUFFERGROESSE;
    }
  
    public Rasterplayer(int[][] raster, int BPM)
    {
        this.raster = raster;
        this.ZEILEN = raster.length;
        this.SPALTEN = raster[0].length;
        this.BPM = BPM;
      
        try
        {
            out = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLERATE, 
                                    AudioFormat.CHANNEL_OUT_STEREO, 
                                    AudioFormat.ENCODING_PCM_16BIT, 
                                    16384*4, 
                                    AudioTrack.MODE_STREAM);
            out.play();
        }
        catch(Exception ee)
        {
            System.out.println("FEHLER: "+ee);
        }
        
        //Berechnung der Pufferlänge eines Blocks gemäß der Samplerate und dem Tempo:
        //Millisekunden pro Beat:
        int msb = 30000/(int)BPM;
        PUFFERGROESSE = (msb*SAMPLERATE)/1000;
        System.out.println("PUFFERGROESSE="+PUFFERGROESSE);
        stereopuffer_links = new double[SPALTEN][PUFFERGROESSE];
        stereopuffer_rechts = new double[SPALTEN][PUFFERGROESSE];
        
        //puffer = new byte[PUFFERGROESSE*4]; //4 Byte pro Sample == 16Bit Stereo 
        //11050...
        puffer = new byte[16384*4]; //4 Byte pro Sample == 16Bit Stereo
    }
    
    public void loescheHinterenPuffer(int INDEX)
    {
         int ih = (INDEX+NACHKLANG-1)%SPALTEN;
         for(int i=0;i<PUFFERGROESSE;i++)
             stereopuffer_links[ih][i]=0.0;
         for(int i=0;i<PUFFERGROESSE;i++)
             stereopuffer_rechts[ih][i]=0.0;
    }
    
    public void trageToeneEin(int zaehlzeit)
    {
        for(int i=0;i<raster.length;i++)//Zeilen durchgehen
        {
            if(raster[i][zaehlzeit]>0) //Wenn Tonereignis im Raster
            {
                 int klang = raster[i][zaehlzeit]-1;
                 int hoehe = raster.length-1-i;
                 double f = fpenta[hoehe];
                 //Oktavieren:
                 if(klang>0)
                     f*=0.5;
                 if(klang>1)
                     f*=0.5;
                 
                 double PAN = (double)klang/(double)(kreisfarben.length-1);     
                 t=0.0;
                 double tmax = (double)(PUFFERGROESSE*NACHKLANG)*dt-dt;
                 double takt = 0.0;

                 int wavindex=0;
                 for(int p=0;p<NACHKLANG;p++) //Blöcke durchgehen und Samples darauf vertreilen
                 {
                     int z = (zaehlzeit + p)%SPALTEN;
                     for(int k=0;k<PUFFERGROESSE;k++)
                     {
                         //double huell = (tmax-takt)/tmax;
                         double huell = 1.0;
                         
                         if(takt>tmax*0.9)
                         {
                             huell = (tmax-takt)/(tmax*0.1);
                         }
                         
                         huell*=huell;
                         
                       
//                         stereopuffer_links[z][k]  += huell*(PAN*0.5+0.5)*0.1*Math.sin(2.0*Math.PI*f*t);
//                         stereopuffer_rechts[z][k] += huell*((1.0-PAN)*0.5+0.5)*0.1*Math.sin(2.0*Math.PI*f*t);
                         stereopuffer_links[z][k]  += huell*(PAN*0.5+0.5)*0.5*ton[hoehe+klang*ZEILEN][0][wavindex];
                         stereopuffer_rechts[z][k] += huell*((1.0-PAN)*0.5+0.5)*0.5*ton[hoehe+klang*ZEILEN][1][wavindex];
                         t+=dt;
                         takt+=dt;
                         wavindex++;
                     }
                 }     
            }
        }
    }

    private void double2Byte(byte[] zwei,double wert)
    {
                int gesamt = (int)(wert*gross + gross);    
               
                int hi = gesamt/256;
                int lo = gesamt - hi*256;
            
                int zh = hi-128;
                int zl = 0;
                if(lo<=127)
                    zl = lo;
                else        
                    zl = lo-256;
                    
                zwei[0]=(byte)zl;    
                zwei[1]=(byte)zh;    
      
    }
    
    //In den Line-Puffer aktuellen Stereopuffer schreiben.
    public void schreibePuffer(int zaehlzeit)
    {
        for(int i=0;i<PUFFERGROESSE;i++)
        {
            double2Byte(zwei,stereopuffer_links[zaehlzeit][i]);
            puffer[i*4+0]=zwei[0];            
            puffer[i*4+1]=zwei[1];            
            double2Byte(zwei,stereopuffer_rechts[zaehlzeit][i]);
            puffer[i*4+2]=zwei[0];            
            puffer[i*4+3]=zwei[1];            
        }
        
//        int geschrieben = out.write(puffer, PUFFERGROESSE*4,AudioManager.WRITE_BLOCKING);                                
        int geschrieben = out.write(puffer, 0,PUFFERGROESSE*4);                                
                
        if(geschrieben!=PUFFERGROESSE*4)
        {
              System.out.println("Fehler beim Lesen oder Schreiben: "+geschrieben);
        }
        
        
        
        
    }
}
