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}