001package info.kramann.vr;
002
003import processing.core.*;
004import processing.opengl.*;
005import processing.vr.*;
006
007
008import android.media.AudioTrack;
009import android.media.AudioFormat;
010import android.media.AudioManager;
011
012import java.util.concurrent.Executors;
013import java.util.concurrent.ScheduledExecutorService;
014import java.util.concurrent.TimeUnit;
015
016import java.util.ArrayList;
017
018import java.util.Random;
019
020/**
021*  <h4>Spiel.java</h4>
022*
023*  Copyright (C) 2017 Guido Kramann<br/>
024*  kramann@fh-brandenburg.de<br/>
025*  http://www.kramann.info<br/>
026*  <br/>
027*
028*  Laden und Abspielen von Mono-Wav-Dateien mit 48kHz Samplingrate und 16Bit.<br/>
029*  Als Default wird ein geloopter Orgelklang auf 64 Quellen geladen<br/>
030*  Diese Einstellung kann überschrieben werden (siehe Beispiel)<br/>
031*  sound.draw() entfernen, wenn andere Visualisierung realisiert werden soll.
032*
033*/
034public class Klang implements Runnable
035{
036     public PApplet pap;
037    
038     public Random zufall = new Random(System.currentTimeMillis());
039    
040     public PShape sternhaufen;
041  
042     //Bereits geladene Samples und deren Namen merken:
043     public ArrayList<String> sample_name = new ArrayList<String>();
044     public ArrayList<float[]> sample_werte = new ArrayList<float[]>();
045  
046     //343,2 m/s
047     public float VSCHALL = 343.0f; // m/s
048     public float MAXDIST = 400.0f; //wie im Labor!
049//     public float WAVSAMPRATE = 44100.0f; // gilt für alle zu ladenden wav-Files !!!!
050     public float WAVSAMPRATE = 48000.0f; // gilt für alle zu ladenden wav-Files !!!!
051     
052//     public PShape sternhaufen;
053     public int ANZSTERNE;
054//     int ANZSTERNE = 96;
055//     float BASISVERSTAERKUNG = 512000.0f/(float)ANZSTERNE;
056
057      float BASISVERSTAERKUNG = 4000.0f;
058
059//    float BASISVERSTAERKUNG = 32000.0f/(float)ANZSTERNE;
060
061    //float BASISVERSTAERKUNG = 1024000.0f/(float)ANZSTERNE;
062    //float BASISVERSTAERKUNG = 256000.0f/(float)ANZSTERNE;
063
064    float[][] regenbogen = {
065      {4,0,208},//0  //17 Stufen == 16 Einheiten
066      {0,68,220},
067      {1,114,226},
068      {1,159,232},
069      {11,175,162},
070      {23,179,77},
071      {0,212,28},
072      {0,255,0},
073      {128,255,0},
074      {200,255,0},
075      {255,255,0},
076      {255,219,0},
077      {255,182,0},
078      {255,146,0},
079      {255,109,0},
080      {255,73,0},
081      {255,0,0} //16
082                           };
083
084  
085     float[] hoerrichtung_links  = {-1.0f, 0.0f,0.0f};
086     float[] hoerrichtung_rechts = { 1.0f, 0.0f,0.0f};
087     float[] st_x; //Sternposition
088     float[] st_y;
089     float[] st_z;
090     float[] st_dist; //Abstand vom Hörpunkt
091     float[] st_r;    //Farbe ROT GRÜN BLAU
092     float[] st_g;
093     float[] st_b;
094     float[] st_d;    //Sterndurchmesser (umgekehrt proportional zum Abstand)
095     float[] st_ueli; //Übereinstimmung links (zwischen Blickrichtung und Sternposition)
096     float[] st_uere; //          ...   rechts
097     float[] st_xn;   //Normalenvektor der Richtung, in der der Stern liegt
098     float[] st_yn;
099     float[] st_zn;
100     float[] st_uelismooth; //Abminderung der Veränderung bei schnellen Kopfbewegungen
101     float[] st_ueresmooth;
102
103     //NEU:
104     public float[][] st_samples; 
105     public float[]   st_quellfreq;  //Frequenz wie im Sample 
106     public float[]   st_zielfreq;   //Frequenz wie sie abzuspielen ist 
107     public float[]   st_quellmidi;  //wenn temperiert, Miditon, sonst mit Nachkommastelle 
108     public float[]   st_zielmidi;   // 
109     public float[]   st_volume;    //Basislautstärke des einzelnen Sterns
110
111     //NEUNEU: Jede Stimme braucht ihre eigene Zeit!!!! und zwar für den linken und den rechten kanal!
112     public float[]   st_tli;   // 
113     public float[]   st_tre;    //Basislautstärke des einzelnen Sterns
114
115     //NEUNEUNEU: Es kann ein Zielpunkt und eine Vortriebsgeschwindigkeit angegeben werden.
116     //Dies führt zu einem Dopplerweffekt und einer Phasenverschiebung,
117     //Wenn alles exakt läuft, so ergibt sich die Phasenverschiebung in korrekter Weise von alleine!!!!
118     public float[]   st_xziel;   // 
119     public float[]   st_yziel;   
120     public float[]   st_zziel;   
121     public float[]   st_vziel;   
122
123     //NEUNEUNEU
124     public boolean[] st_invers; //Möglichkeit eine Wav-Datei mit Faktor -1 abzuspielen, damit sie sich mit ihrem nicht inversen Pendent auslöschen kann.
125     
126  
127     PMatrix3D eyeMat = new PMatrix3D();
128     float ez_x = 0.0f; 
129     float ez_y = 0.0f;
130     float ez_z = 0.0f;
131  
132     AudioTrack audioTrack;
133     //int sr = 11025;
134     //Versuchen die Qualität zu verbessern:
135     int sr = 24000;
136     //int buffsize = 512;
137     int buffsize = 1024;
138     int buffsize2 = buffsize*2;
139     public float t=0.0f;
140  
141     float dt = 1.0f/(float)sr;
142     float dth = 0.5f*dt;
143     protected ScheduledExecutorService schedExecService;  
144     short[] puffer = new short[buffsize*2]; //4 Byte pro Sample == 16Bit Stereo  //2 * bei short
145  
146     public float random(float von , float bis)
147     {
148           float wert = zufall.nextFloat();
149           wert*=(bis-von);
150           wert+=von;
151           return wert;
152     }
153     
154     
155     //PGraphicsVR pvr;
156     
157     public Klang(int ANZSTERNE, PApplet pap)     
158     {
159        this.pap = pap; 
160        //pvr = (PGraphicsVR)pap.g; 
161        this.ANZSTERNE = ANZSTERNE;
162        
163        eyeMat = new PMatrix3D();       
164       
165        this.eyeMat.m00 = 1.0f;
166        this.eyeMat.m10 = 0.0f;
167        this.eyeMat.m20 = 0.0f;
168             
169        this.eyeMat.m01 = 0.0f;
170        this.eyeMat.m11 = 1.0f;
171        this.eyeMat.m21 = 0.0f;
172             
173        this.eyeMat.m02 = 0.0f;
174        this.eyeMat.m12 = 0.0f;
175        this.eyeMat.m22 = 1.0f;
176             
177        this.eyeMat.m03 = 0.0f;
178        this.eyeMat.m13 = 0.0f;
179        this.eyeMat.m23 = 0.0f;
180        
181        st_x = new float[ANZSTERNE]; //Sternposition
182        st_y = new float[ANZSTERNE];
183        st_z = new float[ANZSTERNE];
184        st_dist = new float[ANZSTERNE]; //Abstand vom Hörpunkt
185        st_r = new float[ANZSTERNE];    //Farbe ROT GRÜN BLAU
186        st_g = new float[ANZSTERNE];
187        st_b = new float[ANZSTERNE];
188        st_d = new float[ANZSTERNE];    //Sterndurchmesser (umgekehrt proportional zum Abstand)
189        st_ueli = new float[ANZSTERNE]; //Übereinstimmung links (zwischen Blickrichtung und Sternposition)
190        st_uere = new float[ANZSTERNE]; //          ...   rechts
191        st_xn = new float[ANZSTERNE];   //Normalenvektor der Richtung, in der der Stern liegt
192        st_yn = new float[ANZSTERNE];
193        st_zn = new float[ANZSTERNE];
194        st_uelismooth = new float[ANZSTERNE]; //Abminderung der Veränderung bei schnellen Kopfbewegungen
195        st_ueresmooth = new float[ANZSTERNE];
196
197     //NEU:
198        st_samples = new float[ANZSTERNE][]; 
199        st_quellfreq = new float[ANZSTERNE];  //Frequenz wie im Sample 
200        st_zielfreq = new float[ANZSTERNE];   //Frequenz wie sie abzuspielen ist 
201        st_quellmidi = new float[ANZSTERNE];  //wenn temperiert, Miditon, sonst mit Nachkommastelle 
202        st_zielmidi = new float[ANZSTERNE];   // 
203        st_volume  = new float[ANZSTERNE];    //Basislautstärke des einzelnen Sterns
204
205     //NEUNEU: Jede Stimme braucht ihre eigene Zeit!!!! und zwar für den linken und den rechten kanal!
206        st_tli = new float[ANZSTERNE];   // 
207        st_tre  = new float[ANZSTERNE];    //Basislautstärke des einzelnen Sterns
208
209     //NEUNEUNEU: Es kann ein Zielpunkt und eine Vortriebsgeschwindigkeit angegeben werden.
210     //Dies führt zu einem Dopplerweffekt und einer Phasenverschiebung,
211     //Wenn alles exakt läuft, so ergibt sich die Phasenverschiebung in korrekter Weise von alleine!!!!
212        st_xziel  = new float[ANZSTERNE];   // 
213        st_yziel  = new float[ANZSTERNE];   
214        st_zziel  = new float[ANZSTERNE];   
215        st_vziel  = new float[ANZSTERNE];   
216     
217        st_invers  = new boolean[ANZSTERNE];   
218     
219       
220        erzeugeSterne();
221        erzeugeSterneVisualisierung();
222        aktualisiereHoerrichtung();
223        
224        try
225        {
226            audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sr, 
227                                    AudioFormat.CHANNEL_OUT_STEREO, 
228                                    AudioFormat.ENCODING_PCM_16BIT, 
229                                    buffsize*4, 
230                                    AudioTrack.MODE_STREAM);
231            audioTrack.setStereoVolume(1.0f, 1.0f);
232            audioTrack.play();
233            
234            schedExecService = Executors.newSingleThreadScheduledExecutor();
235            long period = (buffsize*1000)/sr; //Seconds per Beat==60/BPM, die Hälfte weil 8tel, mal 1000 weil Millisekunden.
236            schedExecService.scheduleAtFixedRate(this, 0, period, TimeUnit.MILLISECONDS);                
237        }
238        catch(Exception ee)
239        {
240            System.out.println("FEHLER: "+ee);
241        }              
242     }
243
244     public void run()
245     {
246         //Blickrichtung holen
247         //Ohrrichtungen berechnen
248         //Für jeden Ton Positionsrichtung, Abstand und Fluchtgeschwindigkeit holen
249         aktualisiereHoerrichtung();
250         
251         
252                for(int i=0;i<buffsize2;i++)
253                      puffer[i] = 0;  
254       
255                for(int k=0;k<ANZSTERNE;k++)
256                {
257                    float[] welle = st_samples[k];
258                  
259                    float VVV=st_ueli[k]*BASISVERSTAERKUNG*st_volume[k];
260//                    float FFF=st_dist[k]*0.0003f;
261//                    float FFF=st_dist[k]*0.0006f;  //Frequenz um so höher, je weiter weg
262  
263                    float FFF = st_zielfreq[k];  //Frequenz ist im Array die Zielfrequenz!
264//Folgendes:
265//wenn die Samplingrate des geladenen Samples == 44100Hz ist,
266//dann ist die Dauer des Samples == welle.length/44100 Sekunden
267//Abtastung:
268//Stimmen Quell- und Zielfrequenz überein, muß einfach mit dt abgetastet werden
269//Ansonsten:
270// dt_neu = dt*Fziel/Fquell
271                    float Tsamp = (float)welle.length/WAVSAMPRATE;  //Dauer des Samples
272
273                    float freq_li = st_zielfreq[k];
274                    float freq_re = st_zielfreq[k];
275                    
276                    //Anpassung für Geschwindigkeiten != 0:
277                    if(st_vziel[k]!=0.0f)
278                    {
279                          //Wenn Distanz erreicht ist, die kleiner ist als v*DT, mit DT==Zeitintervall eines Puffer Updates, dann ziel==pos setzen
280                          //und v=0 setzen:
281                          float DT = (float)buffsize/(float)sr; // 512/11025 == 0,046439909 Sekunden
282                          float r_x = st_xziel[k]-st_x[k];
283                          float r_y = st_yziel[k]-st_y[k];
284                          float r_z = st_zziel[k]-st_z[k];
285                          float r_d = PApplet.sqrt(r_x*r_x + r_y*r_y + r_z*r_z);
286                          float strecke = DT*st_vziel[k];
287                          
288                          if(strecke>=r_d)  //Aktuelle Position auf die Zielposition setzen und Geschwindigkeit Null setzen
289                          {
290                              st_vziel[k] = 0.0f;
291                              float x = st_xziel[k];
292                              float y = st_yziel[k];
293                              float z = st_zziel[k];
294                              neueSternPosition(k, x, y, z);
295                          }
296                          else
297                          {
298                              //andernfalls die aktuelle Tonhöhe gemäß Doppler effekt anpassen.
299                              //Dazu muß die relative Flucht-Geschwindigkeit zum linken und rechten Ohr getrennt bestimmt werden
300                              //Berechnung über Skalarprodukt:
301                              
302                              float vx = (st_vziel[k]*r_x)/r_d;
303                              float vy = (st_vziel[k]*r_y)/r_d;
304                              float vz = (st_vziel[k]*r_z)/r_d;
305                              
306                              //Fluchtgeschw. relativ zu Hörrichtungen bestimmen:
307                              float v_flucht_li = hoerrichtung_links[0]*vx + hoerrichtung_links[1]*vy + hoerrichtung_links[2]*vz;
308                              float v_flucht_re = hoerrichtung_rechts[0]*vx + hoerrichtung_rechts[1]*vy + hoerrichtung_rechts[2]*vz;
309                              //Sich daraus ergebende Frequenzänderung der Zielfrequenz:
310                              //Dazu:
311                              //Bei einer herankommenden Schallquelle wird die Wellenlänge scheinbar kürzer:
312                              //     wellenlänge_neu = wellenlänge_alt * 
313                              //Formel:  f_wahrgenommen = f_tatsächlich / ( 1 - (vquelle/vschall) )
314                              
315//                              freq_li = st_zielfreq[k]/(1.0f - (v_flucht_li/VSCHALL) );
316//                              freq_re = st_zielfreq[k]/(1.0f - (v_flucht_re/VSCHALL) );
317                              freq_li = st_zielfreq[k]/(1.0f + (v_flucht_li/VSCHALL) );
318                              freq_re = st_zielfreq[k]/(1.0f + (v_flucht_re/VSCHALL) );
319                              
320                              //Neue Position berechnen und setzen:
321                              float x = st_x[k] + vx*DT;
322                              float y = st_y[k] + vy*DT;
323                              float z = st_z[k] + vz*DT;
324                              neueSternPosition(k, x, y, z);
325                              
326                          }
327                    }
328                    
329                    //float dt_neu_h = dth*st_zielfreq[k]/st_quellfreq[k]; 
330                    float dt_neu_h = dth*freq_li/st_quellfreq[k]; 
331                  
332                    float VVV2=st_uere[k]*BASISVERSTAERKUNG*st_volume[k];
333
334                    float t = st_tli[k];
335                    float ttt = 0.0f;                    
336                    if(VVV>0.0f)
337                    {
338                      for(int i=0;i<buffsize2;i+=2)
339                      {
340                        ttt = t+(float)i*dt_neu_h; //quasi in dt schritten, da i immer +=2, also einfach die Zeit!
341                        float tt = ttt - Tsamp*PApplet.floor(ttt/Tsamp);  //Über die Dauer des Samples beschränkte Zeit
342                        //tt*=(float)(welle.length-1);
343                        int inx = (int)(   (tt/Tsamp)*(float)(welle.length)   ); //Index im Sample
344                        float anteil = tt/Tsamp - PApplet.floor(tt/Tsamp);
345                        float wert = (1.0f-anteil)*welle[inx] + anteil*welle[(inx+1)%welle.length];
346                        
347                        if(st_invers[k])
348                            puffer[i] -= VVV*wert;  //LINKS
349                        else    
350                            puffer[i] += VVV*wert;  //LINKS
351                      }
352                    }  
353                    else
354                    {
355                      for(int i=0;i<buffsize2;i+=2)
356                        ttt = t+(float)i*dt_neu_h; //quasi in dt schritten, da i immer +=2, also einfach die Zeit! mitnehmen, sonst inkonsistent!!!
357                    }
358                    st_tli[k] = ttt + dt_neu_h*2.0f; //für den nächsten Durchgang
359                    
360                    t = st_tre[k];
361                    //float dt_neu_h = dth*st_zielfreq[k]/st_quellfreq[k]; 
362                    dt_neu_h = dth*freq_re/st_quellfreq[k]; 
363                    
364                    if(VVV2>0.0f)
365                    {
366                      for(int i=1;i<buffsize2;i+=2)
367                      {
368                        ttt = t+(float)(i-1)*dt_neu_h;
369                        float tt = ttt - Tsamp*PApplet.floor(ttt/Tsamp);  //weitere Verbesserung: Lookup-Table im Bereich 0..1 sehr sehr fein (10000 Werte z.B.)
370                        //tt*=(float)(welle.length-1);
371                        int inx = (int)(   (tt/Tsamp)*(float)(welle.length)   ); //Index im Sample
372                        float anteil = tt/Tsamp - PApplet.floor(tt/Tsamp);
373                        float wert = (1.0f-anteil)*welle[inx] + anteil*welle[(inx+1)%welle.length];
374
375                        if(st_invers[k])
376                            puffer[i] -= VVV2*wert;  //LINKS
377                        else
378                            puffer[i] += VVV2*wert;  //LINKS
379                        
380                      }
381                    }  
382                    else
383                    {
384                      for(int i=1;i<buffsize2;i+=2)
385                        ttt = t+(float)(i-1)*dt_neu_h;
386                    }    
387                    st_tre[k] = ttt + dt_neu_h*2.0f; //für den nächsten Durchgang
388                }
389                                
390                t+=dt*(float)buffsize;
391         
392                audioTrack.write(puffer, 0,buffsize2);
393          
394     }     
395     
396     
397    /**
398    Achtung, muß zyklisch in draw() aufgerufen werden, wenn eyeMat dort sowieso aktualisiert wird.
399    */
400    public void setzeEyeMat(PMatrix3D eyeMat)
401    {
402         if(eyeMat!=null)
403         {
404             this.eyeMat.m00 = eyeMat.m00;
405             this.eyeMat.m10 = eyeMat.m10;
406             this.eyeMat.m20 = eyeMat.m20;
407             
408             this.eyeMat.m01 = eyeMat.m01;
409             this.eyeMat.m11 = eyeMat.m11;
410             this.eyeMat.m21 = eyeMat.m21;
411             
412             this.eyeMat.m02 = eyeMat.m02;
413             this.eyeMat.m12 = eyeMat.m12;
414             this.eyeMat.m22 = eyeMat.m22;
415             
416             this.eyeMat.m03 = eyeMat.m03;
417             this.eyeMat.m13 = eyeMat.m13;
418             this.eyeMat.m23 = eyeMat.m23;
419             
420         }    
421    }
422     
423    public void aktualisiereHoerrichtung()
424    {
425    //Technik: man dreht den eye ez Vektor innerhalb der eye ez,ex-Ebene für
426    //das linke Ohr um 10 Grad gegen, für's rechte 10Grad im Uhrzeigersinn:
427    //Oder vereinfacht:
428    //LINKS  = ez - 0.1*ex  / betrag
429    //RECHTS = ez + 0.1*ex  / betrag
430    //PApplet.getEyeMatrix(eyeMat); //Kameramatrix auslesen
431    
432    ez_x =  eyeMat.m02;
433    ez_y = -eyeMat.m12;
434    ez_z =  eyeMat.m22;
435    
436    hoerrichtung_links[0] =  eyeMat.m02 - 0.2f*eyeMat.m00; 
437    hoerrichtung_links[1] =  eyeMat.m12 - 0.2f*eyeMat.m10; //Orientierung umkehren  ?????
438    hoerrichtung_links[2] =  eyeMat.m22 - 0.2f*eyeMat.m20;
439    
440    hoerrichtung_rechts[0] =  eyeMat.m02 + 0.2f*eyeMat.m00; 
441    hoerrichtung_rechts[1] =  eyeMat.m12 + 0.2f*eyeMat.m10; //Orientierung umkehren ?????
442    hoerrichtung_rechts[2] =  eyeMat.m22 + 0.2f*eyeMat.m20;
443    
444    //Durch Beträge teilen:
445    float betrag_links = PApplet.sqrt( hoerrichtung_links[0]*hoerrichtung_links[0] + hoerrichtung_links[1]*hoerrichtung_links[1] + hoerrichtung_links[2]*hoerrichtung_links[2] );
446    float betrag_rechts = PApplet.sqrt( hoerrichtung_rechts[0]*hoerrichtung_rechts[0] + hoerrichtung_rechts[1]*hoerrichtung_rechts[1] + hoerrichtung_rechts[2]*hoerrichtung_rechts[2] );
447    
448    hoerrichtung_links[0] /= betrag_links;  
449    hoerrichtung_links[1] /= betrag_links;  
450    hoerrichtung_links[2] /= betrag_links;
451    
452    hoerrichtung_rechts[0] /= betrag_rechts;  
453    hoerrichtung_rechts[1] /= betrag_rechts;  
454    hoerrichtung_rechts[2] /= betrag_rechts;
455    
456
457        //Übereinstimmung für Sterne bestimmen:
458        for(int i=0;i<ANZSTERNE;i++)
459        {      
460            st_ueli[i] = hoerrichtung_links[0]*st_xn[i] + hoerrichtung_links[1]*st_yn[i] + hoerrichtung_links[2]*st_zn[i];
461            st_uere[i] = hoerrichtung_rechts[0]*st_xn[i] + hoerrichtung_rechts[1]*st_yn[i] + hoerrichtung_rechts[2]*st_zn[i];
462            st_ueli[i]-=0.7f;
463            st_uere[i]-=0.7f;  //niedrige abschneiden!!
464                    
465            if(st_ueli[i]<0.0f)
466              st_ueli[i]=0.0f;
467            if(st_uere[i]<0.0f)
468              st_uere[i]=0.0f;
469                        
470            st_ueli[i]*=1.0f/0.3f;    
471            st_uere[i]*=1.0f/0.3f;    
472                        
473            st_ueli[i]*=st_ueli[i];    
474            st_ueli[i]*=st_ueli[i];  //NOCH mehr konzentrieren              
475        }
476    }
477
478    public void neueSternPosition(int i, float x, float y, float z)
479    {
480        float x_alt = st_x[i]; 
481        float y_alt = st_y[i]; 
482        float z_alt = st_z[i]; 
483        float scale_alt = st_d[i];
484      
485        st_x[i] = x;  //man könnte auch 0 0 0 hier eintragen und x y z als Ziel und v!=0!!!! ---- sinnvoll für WFS-Anlage!!!
486        st_y[i] = y;
487        st_z[i] = z;
488        float dist = PApplet.sqrt(x*x+y*y+z*z);
489        st_dist[i] = dist;
490        //normierter Vektor:
491        st_xn[i] = x/dist;
492        st_yn[i] = y/dist;
493        st_zn[i] = z/dist;
494        float maxdist = PApplet.sqrt( 1000.0f*1000.0f + 1000.0f*1000.0f + 1000.0f*1000.0f);
495        //Rot grün blau wie Regenbogen: schwarz lila blau grün gelb orange rot
496        //Eckfarben: Bereich 100 bis 1000 mappen auf 0..16
497        float farbstrahl = 15.999f*(dist-100.0f)/(maxdist-100.0f);
498        int index_untere_farbe = (int)farbstrahl;
499        int index_obere_farbe  = index_untere_farbe+1;
500        
501        if(index_untere_farbe<0)
502        {
503            index_untere_farbe=0;
504            index_obere_farbe=1;
505        }    
506        if(index_obere_farbe>regenbogen.length-1)
507        {
508              index_obere_farbe = regenbogen.length-1;
509              index_untere_farbe = regenbogen.length-2;              
510        }
511        
512        float anteil = farbstrahl - (float)((int)farbstrahl);
513
514        
515        st_r[i] = (1.0f-anteil)*regenbogen[index_untere_farbe][0] + anteil*regenbogen[index_obere_farbe][0];
516        st_g[i] = (1.0f-anteil)*regenbogen[index_untere_farbe][1] + anteil*regenbogen[index_obere_farbe][1];
517        st_b[i] = (1.0f-anteil)*regenbogen[index_untere_farbe][2] + anteil*regenbogen[index_obere_farbe][2];
518
519        //Durchmesser umgekehrt proportional zum Abstand:
520        st_d[i] = 16.0f - 12.0f*(dist-100.0f)/(maxdist-100.0f); 
521
522        
523        PShape sphere = sternhaufen.getChild(i);
524        sphere.setStroke(false);
525        sphere.setFill(pap.color(st_r[i],st_g[i],st_b[i]));
526        //sphere.fill(st_r[i],st_g[i],st_b[i]);
527        sphere.translate(st_x[i]-x_alt,st_y[i]-y_alt,st_z[i]-z_alt);        
528    }
529     
530    public void erzeugeSterne()
531    {
532    //    http://wiki.baw.de/de/index.php/Farbverlauf:_Regenbogen,_29_Farben
533      float[] welle;
534      welle = ladeWavMono("orgel64_2.wav");
535
536      sample_name.add("orgel64_2.wav");
537      sample_werte.add(welle);
538      
539      //Alle Sterne bekommen Zugriff auf das gleiche wav-File, jedoch wird eine unterschiedliche Zielfrequenz eingestellt:
540      for(int i=0;i<ANZSTERNE;i++)
541      {
542          st_samples[i]   = welle;
543          st_quellfreq[i] = midi2Frequency(64); 
544          st_quellmidi[i] = 64;
545          //st_zielfreq[i]  =  abh. von Distanz, s.u.!!!
546          st_volume[i]    = 0.0f;
547      }  
548  
549                                     //inx   0 1 2   3  4 5 6     7              8              9            10   11    12    13             14
550//   float[][] sterne = new float[6][15]; // x y z dist r g b durchmesser   Übereinst_li   Übereinst_re   xnorm ynorm znorm  ue_li_smooth   ue_re_smooth
551    
552    for(int i=0;i<ANZSTERNE;i++)
553    {
554        float x = random(-1000, +1000);
555        float y = random(-1000, +1000);
556        float z = random(-1000, +1000);
557        float dist = PApplet.sqrt(x*x+y*y+z*z);
558        
559        float dist_boden =  PApplet.sqrt(x*x+(y-1200.0f)*(y-1200.0f)+z*z);
560        
561        while(dist<100.0f || dist_boden<1300.0f)    //auch Boden frei lassen!
562        {
563            x = random(-1000, +1000); 
564            y = random(-1000, +1000);
565            z = random(-1000, +1000);    
566            dist = PApplet.sqrt(x*x+y*y+z*z);
567            dist_boden =  PApplet.sqrt(x*x+(y-1200.0f)*(y-1200.0f)+z*z);
568        }
569
570
571        float maxdist = PApplet.sqrt( 1000.0f*1000.0f + 1000.0f*1000.0f + 1000.0f*1000.0f);
572
573        //WFS-Labor: Alles so stauchen, dass e sim Bereich 400 paßt: 4/10 == 2/5
574        //&&&&&&&&&&&&&&&&&&&&&&&
575        float Q=2.0f/5.0f;
576        x*=Q;
577        y*=Q;
578        z*=Q;
579        dist = PApplet.sqrt(x*x+y*y+z*z);
580        dist_boden =  PApplet.sqrt(x*x+(y-1200.0f)*(y-1200.0f)+z*z);
581        maxdist = PApplet.sqrt( 400.0f*400.0f + 400.0f*400.0f + 400.0f*400.0f);
582        //&&&&&&&&&&&&&&&&&&&&&&&
583        
584        st_x[i] = x;  //man könnte auch 0 0 0 hier eintragen und x y z als Ziel und v!=0!!!! ---- sinnvoll für WFS-Anlage!!!
585        st_y[i] = y;
586        st_z[i] = z;
587        st_xziel[i] = x;  //identisch zu x,y,z, damit sich keine Bewegung ergibt
588        st_yziel[i] = y;
589        st_zziel[i] = z;
590        st_vziel[i] = 0.0f;  //keine Vortriebsgeschwindigkeit
591        st_dist[i] = dist;
592
593        //Hier Frequenz festlegen, mit der abgespielt wird:
594        st_zielfreq[i]  =  dist;  //Distanz liegt zwischen 100 und 1000, somit die Frequenz auch zwischen 100Hz und 1000Hz 
595        st_zielmidi[i]  =  frequency2Midi(st_zielfreq[i]);
596        
597
598        //normierter Vektor:
599        st_xn[i] = x/dist;
600        st_yn[i] = y/dist;
601        st_zn[i] = z/dist;
602
603        
604        //Rot grün blau wie Regenbogen: schwarz lila blau grün gelb orange rot
605        //Eckfarben: Bereich 100 bis 1000 mappen auf 0..16
606        float farbstrahl = 15.999f*(dist-100.0f)/(maxdist-100.0f);
607        int index_untere_farbe = (int)farbstrahl;
608        int index_obere_farbe  = index_untere_farbe+1;
609        
610        if(index_untere_farbe<0)
611        {
612            index_untere_farbe=0;
613            index_obere_farbe=1;
614        }    
615        if(index_obere_farbe>regenbogen.length-1)
616        {
617              index_obere_farbe = regenbogen.length-1;
618              index_untere_farbe = regenbogen.length-2;              
619        }
620        
621        float anteil = farbstrahl - (float)((int)farbstrahl);
622
623        
624        st_r[i] = (1.0f-anteil)*regenbogen[index_untere_farbe][0] + anteil*regenbogen[index_obere_farbe][0];
625        st_g[i] = (1.0f-anteil)*regenbogen[index_untere_farbe][1] + anteil*regenbogen[index_obere_farbe][1];
626        st_b[i] = (1.0f-anteil)*regenbogen[index_untere_farbe][2] + anteil*regenbogen[index_obere_farbe][2];
627
628        //Durchmesser umgekehrt proportional zum Abstand:
629        st_d[i] = 16.0f - 12.0f*(dist-100.0f)/(maxdist-100.0f); 
630      }
631      
632   }
633        
634   public void erzeugeSterneVisualisierung()
635   {
636      sternhaufen = pap.createShape(PApplet.GROUP);  
637      for(int i=0;i<ANZSTERNE;i++)
638      {
639        //Alle mit gleichem Durchmesser, sonst Probleme bei der Aktualisierung!:
640        PShape sphere = pap.createShape(PApplet.SPHERE, 6.0f);
641        sphere.setStroke(false);
642        sphere.setFill(pap.color(st_r[i],st_g[i],st_b[i]));
643        //sphere.fill(st_r[i],st_g[i],st_b[i]);
644        sphere.translate(st_x[i],st_y[i],st_z[i]);
645        sternhaufen.addChild(sphere);
646      }
647   }
648
649   public void draw()
650   {
651       pap.shape(sternhaufen);
652   }
653   
654   public float midi2Frequency(float midi)
655   {
656        float freq69 = PApplet.pow(2.0f,69.0f/12.0f);
657        return PApplet.pow(2.0f,midi/12.0f)*440.0f/freq69;
658   }
659   
660   public float frequency2Midi(float freq)
661   {
662       float freq69 = PApplet.pow(2.0f,69.0f/12.0f);
663       return 12.0f*PApplet.log(freq*freq69/440.0f)/PApplet.log(2.0f);
664   }  
665   
666    public float[][] ladeWav(String name, int sampleanzahl)
667    {
668            int zl,zh,lo,gesamt;
669            float gross = 0.5f*(255.0f*256.0f + 255.0f);
670          
671            byte[] dat = pap.loadBytes(name);
672
673            int anz = dat.length;
674          
675            //double[][] y = new double[2][(anz-44)/4];                        
676            float[][] y = new float[2][sampleanzahl];                        
677
678            int inx=44;
679            
680            for(int i=0;i<y[0].length;i++)
681            {
682                 zl = dat[inx++];
683                 zh = dat[inx++];
684                 if(zl>127)
685                     zl-=256;
686            
687                 if(zh>127)
688                     zh-=256;
689
690                 lo     =  zl;
691                 if(lo<0)
692                     lo+=256;
693                 gesamt = (zh+128)*256;
694                
695                 gesamt+=lo;
696                
697                 y[0][i] = ((float)gesamt - gross)/gross;        
698
699                 zl = dat[inx++];
700                 zh = dat[inx++];
701                 if(zl>127)
702                     zl-=256;
703            
704                 if(zh>127)
705                     zh-=256;
706
707                 lo     =  zl;
708                 if(lo<0)
709                     lo+=256;
710                 gesamt = (zh+128)*256;
711                
712                 gesamt+=lo;
713                
714                 y[1][i] = ((float)gesamt - gross)/gross;        
715                 
716            }    
717            
718            return y;
719    }
720    
721    public float[] ladeWavMix(String name) //Gleich nur Mix aus linkem und rechtem Kanal laden (aus 44100 2x16Bit extrahieren)
722    {
723            int zl,zh,lo,gesamt;
724            float gross = 0.5f*(255.0f*256.0f + 255.0f);
725          
726            byte[] dat = pap.loadBytes(name);
727
728            int anz = dat.length;
729          
730            //double[][] y = new double[2][(anz-44)/4];                        
731            //float[] y = new float[sampleanzahl];                        
732            float[] y = new float[(anz-44)/4];                        
733
734            int inx=44;
735            
736            for(int i=0;i<y.length;i++)
737            {
738                 zl = dat[inx++];
739                 zh = dat[inx++];
740                 if(zl>127)
741                     zl-=256;
742            
743                 if(zh>127)
744                     zh-=256;
745
746                 lo     =  zl;
747                 if(lo<0)
748                     lo+=256;
749                 gesamt = (zh+128)*256;
750                
751                 gesamt+=lo;
752                
753                 //y[0][i] = ((float)gesamt - gross)/gross;        
754                 y[i] = 0.5f*((float)gesamt - gross)/gross;        
755
756                 zl = dat[inx++];
757                 zh = dat[inx++];
758                 if(zl>127)
759                     zl-=256;
760            
761                 if(zh>127)
762                     zh-=256;
763
764                 lo     =  zl;
765                 if(lo<0)
766                     lo+=256;
767                 gesamt = (zh+128)*256;
768                
769                 gesamt+=lo;
770                
771                 //y[1][i] = ((float)gesamt - gross)/gross;        
772                 y[i] += 0.5f*((float)gesamt - gross)/gross;        
773                 
774            }    
775            
776            return y;
777    }
778
779    /**
780    *  Lädt wav-Files, die als 16-Bit 48kHz-Mono-Files vorliegen
781    */
782    public float[] ladeWavMono(String name) //Gleich nur Mix aus linkem und rechtem Kanal laden (aus 44100 2x16Bit extrahieren)
783    {
784            int zl,zh,lo,gesamt;
785            float gross = 0.5f*(255.0f*256.0f + 255.0f);
786          
787            byte[] dat = pap.loadBytes(name);
788
789            int anz = dat.length;
790          
791            //double[][] y = new double[2][(anz-44)/4];                        
792            //float[] y = new float[sampleanzahl];                        
793            //float[] y = new float[(anz-44)/4];                        
794            float[] y = new float[(anz-44)/2];                        
795
796            int inx=44;
797            
798            for(int i=0;i<y.length;i++)
799            {
800                 zl = dat[inx++];
801                 zh = dat[inx++];
802                 if(zl>127)
803                     zl-=256;
804            
805                 if(zh>127)
806                     zh-=256;
807
808                 lo     =  zl;
809                 if(lo<0)
810                     lo+=256;
811                 gesamt = (zh+128)*256;
812                
813                 gesamt+=lo;
814                
815                 //y[0][i] = ((float)gesamt - gross)/gross;        
816                 y[i] = ((float)gesamt - gross)/gross;                         
817            }    
818            
819            return y;
820    }
821
822    //Kundenmethoden
823    //*************************************
824/**
825Lautstärke eines einzelnen Klanges setzen
826*/
827 public void volume(int index, float wert)
828{
829    if(index>=0 && index< ANZSTERNE && wert>=0.0f && wert<=1.0f)
830         st_volume[index] = wert;     
831}
832
833/**
834Lautstärke aller Klänge setzen
835*/
836 public void volume(float wert)
837{
838    for(int i=0;i< ANZSTERNE;i++)
839            st_volume[i] = wert;     
840}
841
842/**
843Zeit synchron zurückspulen
844*/
845 public void rewind(int index)
846{
847      st_tli[index] = 0.0f; 
848      st_tre[index] = 0.0f;
849}
850
851/**
852Zeit synchron zurückspulen
853*/
854 public void rewind()
855{
856     for(int i=0;i< ANZSTERNE;i++)
857     {
858          st_tli[i] = 0.0f; 
859          st_tre[i] = 0.0f;
860     }
861      t = 0.0f;
862}
863
864
865/**
866Wav-File für Klangquelle laden und Klang positionieren
867*/
868 public void load(int index, String wavfilename, float x, float y, float z, float volume, float zielfreq, float quellfreq, boolean invers)
869{
870    if(index>=0 && index< ANZSTERNE && volume>=0.0f && volume<=1.0f && (x!=0.0f || y!=0.0f || z!=0.0f) )
871    {
872          float[] welle = null;
873          for(int i=0;i< sample_name.size();i++)
874          {
875                if( sample_name.get(i).equals(wavfilename))
876                {
877                      welle =  sample_werte.get(i);
878                      break;
879                }
880          }
881          if(welle==null)
882          {
883                welle =  ladeWavMono(wavfilename);
884          }
885          if(welle!=null)
886          {
887               st_samples[index] = welle;
888               st_zielfreq[index] = zielfreq;
889               st_quellfreq[index] = quellfreq;
890               st_zielmidi[index] =  frequency2Midi(zielfreq);
891               st_quellmidi[index] =  frequency2Midi(quellfreq);
892               st_volume[index] = volume;
893               st_invers[index] = invers;
894               neueSternPosition(index,x,y,z);                
895          }
896    }
897}
898
899/**
900Laden in die x-z-Ebene unter Angabe von phi (in Grad) und R.
901*/
902 public void load(int index, String wavfilename, float R, float PHI, float volume, float zielfreq, float quellfreq, boolean invers)
903{
904     PHI*=PApplet.PI/180.0f;
905     float y = 0.0f;
906     float x =  R*PApplet.sin(PHI); //positiv im Uhrzeigersinn 
907     float z = -R*PApplet.cos(PHI);
908     load(index,wavfilename,x,y,z,volume, zielfreq,quellfreq,invers);
909}
910
911 public void load(int index, String wavfilename, float R, float PHI, float volume, int zielmidi, int quellmidi, boolean invers)
912{
913     PHI*=PApplet.PI/180.0f;
914     float y = 0.0f;
915     float x =  R*PApplet.sin(PHI); //positiv im Uhrzeigersinn 
916     float z = -R*PApplet.cos(PHI);
917     float zielfreq =  midi2Frequency(zielmidi);
918     float quellfreq =  midi2Frequency(quellmidi);
919     load(index,wavfilename,x,y,z,volume, zielfreq,quellfreq,invers);
920}
921
922 public void load(int index, String wavfilename, float x, float y, float z, float volume, int zielmidi, int quellmidi, boolean invers)
923{
924     float zielfreq =  midi2Frequency(zielmidi);
925     float quellfreq =  midi2Frequency(quellmidi);
926     load(index,wavfilename,x,y,z,volume, zielfreq,quellfreq,invers);
927}
928
929
930/**
931Klangquelle unmittelbar an bestimmte Position setzen
932*/
933 public void pos(int index, float x, float y, float z)
934{
935      if(index>=0 && index< ANZSTERNE)
936      {
937               neueSternPosition(index,x,y,z);
938      }        
939}
940
941/**
942Verschiebung mit bestimmter Geschwindigkeit V
943*/
944 public void shiftV(int index, float x, float y, float z, float v)
945{
946      if(index>=0 && index< ANZSTERNE && v< VSCHALL)
947      {
948               st_xziel[index] = x;
949               st_yziel[index] = y;
950               st_zziel[index] = z;
951               st_vziel[index] = v;
952      }        
953}
954 public void shiftV(int index, float R, float PHI, float v)
955{
956      PHI*=PApplet.PI/180.0f;
957      float y = 0.0f;
958      float x =  R*PApplet.sin(PHI); //positiv im Uhrzeigersinn 
959      float z = -R*PApplet.cos(PHI);
960      shiftV(index,x,y,z,v);
961}
962
963/**
964Verschiebung innerhalb bestimmter Zeit T 
965*/
966 public void shiftT(int index, float x, float y, float z, float T)
967{
968      if(index>=0 && index< ANZSTERNE && T>0.0f)
969      {
970              float dist = PApplet.sqrt(  ( st_x[index]-x)*( st_x[index]-x)+( st_y[index]-y)*( st_y[index]-y)+( st_z[index]-z)*( st_z[index]-z)  );
971              float v = dist/T;
972              if(v< VSCHALL)
973              {
974                   st_xziel[index] = x;
975                   st_yziel[index] = y;
976                   st_zziel[index] = z;
977                   st_vziel[index] = v;
978      }       }
979}
980 public void shiftT(int index, float R, float PHI, float T)
981{
982      PHI*=PApplet.PI/180.0f;
983      float y = 0.0f;
984      float x =  R*PApplet.sin(PHI); //positiv im Uhrzeigersinn 
985      float z = -R*PApplet.cos(PHI);
986      shiftV(index,x,y,z,T);
987}
988
989    
990}