kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




Erweiterung der Android-Processing-VR-Funktionalität um den Befehl "sprich(String)"

(EN google-translate)

(PL google-translate)

Der Sprachsynthesizer wurde bereits mehrfach in vorangehend besprochenen Sketches benutzt. Nun geht es mehr darum, ihn sinnvoll zu modularisieren. Insbesondere gilt es dabei, die Komplexität der zugehörigen Programmstruktur für den "Benutzer-Programmierer" zu verbergen, die insbesondere darin besteht...

  • ... überhaupt den Sprachsynthesizser anzusprechen, bzw. initialisieren,
  • ... bei schnellem mehrmaligem Aufruf von "sprich(String)" dafür zu sorgen, dass ein neuer Text erst synthetisiert wird, wenn eine vorangehende Synthetisierung beendet wurde.

Lazy Binding

Nicht in jedem Programm wird die Sprachsynthese benötigt werden. Andererseits möchte man für die einfache Benutzung nicht gerne noch einen Konstruktor bei Bedarf aufrufen müssen.

Eine Lösung für dieses Problem stellt die Verwendung des s.g. Lazy-Binding dar: Programmstrukturen, die nicht immer benötigt werden, werden erst initialisiert und geladen, wenn sie das erste mal benutzt werden sollen. Im konkreten Fall kann mit Aufrufen der Funktion "sprich(String)" jedesmal geprüft werden, ob das Sprach-Synthesizer-Objekt "null" ist und ggf. der Konstruktor aufgerufen werden.

Umsetzung

In der folgenden Realisierung kann nach Übernahme des Tabs "Zusatz" in einen beliebigen Sketch (für VR-Android) im Hauptprogramm der Sprachsynthesizer einfach durch Verwendung der Funktion sprich("Mein Text"); verwendet werden:

Sprich.zip - Processing-Sketch.
import processing.vr.*;


//ACHTUNG:
//Android->VR anhaken

PMatrix3D eyeMat = new PMatrix3D();

String zeichen = "ABCDEFGHIJKLMNOPQRSTUVWXZ";

int RESTZEIT = 0;
String[] AUSWAHL = {"1","2","3"};
int inx=0;

void setup() 
{
    fullScreen(MONO);
}


void draw() 
{
  //Standardeinstellungen für unser Arbeiten:
  background(0);
  getEyeMatrix(eyeMat); //Kameramatrix auslesen  
  translate(eyeMat.m03, eyeMat.m13, eyeMat.m23); //Welt in Koordinatenursprung von Kamera setzen.
  lights();
  float ez_x =  eyeMat.m02;  //Einheitsvektor der aktuellen Blickrichtung
//  float ez_y =  -eyeMat.m12;
  float ez_y =  eyeMat.m12;
  float ez_z =  eyeMat.m22;  
  
  //ENDE Standardeinstellungen für unser Arbeiten:
 
  //Buchstaben auf "Platten" zeichnen:
  textSize(64);
  for(int i=0;i<zeichen.length();i++)
  {
      pushMatrix();
      
      float z = -500.0f;
      float y = - 200 + (i/5)*100;
      float x = 100*(i%5) - 200;

      //Nähe zum Zielkreis:
      
      //1) Einheitsvektor bilden:
      float laenge = sqrt(x*x+y*y+z*z);
      float xx=x/laenge;
      float yy=y/laenge;
      float zz=z/laenge;
      
      //2) Skalarprodukt mit Blickrichtung (ez)      
      float sk = xx*ez_x + yy*ez_y + zz*ez_z;
      
      if(sk>0.99f && RESTZEIT==0)
      {
           AUSWAHL[inx]=zeichen.substring(i,i+1);
           inx++;
           inx%=AUSWAHL.length;
           
           if(AUSWAHL[0].equals(AUSWAHL[1]) && AUSWAHL[1].equals(AUSWAHL[2]))
           {
                sprich(AUSWAHL[0]);
                AUSWAHL[0] ="1";
                AUSWAHL[1] ="2";
                AUSWAHL[2] ="3";
           }
           
           RESTZEIT = 30;
      }
      
      translate(x,y,z); 
      noStroke();
      fill(0,255,0);
      text(zeichen.substring(i,i+1),-20,20);
      popMatrix();
      
  }
  
  //Zielkreis zeichnen
  eye(); //Transformation realtiv zur Kamera
  translate(0, 0, 95); //Zielkreis relativ zur Kamera zeichnen
  noFill();
  stroke(255,0,0);
  ellipse(0, 0, 10, 10);
  
  if(RESTZEIT>0)
      RESTZEIT--;
}

Code 0-1: Sprich.pde

//TTS:
import android.speech.tts.TextToSpeech;
import java.util.Locale;
import java.util.Set;
import android.media.AudioManager;
import android.content.Context;

TTS tts = null;

public void sprich(String text)
{
    if(tts==null)
    {
        tts = new TTS();
        tts.setzeSpechgeschwindigkeit(1.0f);
        tts.setzeStimmhoehe(1.0f);
        tts.setzeLautstaerke(0.8f);      
    }
    
    if(tts!=null)
    {
        tts.sprich(text);
    }
}

public class TTS implements Runnable
{
     ArrayList<String> warteschlange = new ArrayList<String>();
  
  
     TextToSpeech tts;
     AudioManager audio;
     int currentVolume;
     int maxVolume;

     public void setzeSpechgeschwindigkeit(float s)
     {
         tts.setSpeechRate(s);    
     }

     public void setzeStimmhoehe(float h)
     {
         tts.setSpeechRate(h);    
     }

     public void setzeLautstaerke(float ls)
     {
         if(ls<0.0f)
             ls=0.0f;
         if(ls>1.0f)
             ls=1.0f;
         int LAUT  = (int) (maxVolume*ls);
             
         audio.setStreamVolume(AudioManager.STREAM_MUSIC, LAUT, 0);             
     }

     public void run()
     {
         while(true)
         {
              if(tts!=null && warteschlange.size()>0 && !tts.isSpeaking())
              {
                  String text = warteschlange.get(0);
                  warteschlange.remove(0);
                
                  tts.speak(text, TextToSpeech.QUEUE_FLUSH, null);
              }  
                         
              try
              {
                  Thread.sleep(100);
              }
              catch(Exception e)
              {
              }
         }
     }

     public void sprich(String text)
     {
        warteschlange.add(text);       
     }

     public TTS()
     {
         //NEU:  getActivity(). ab Proc. 3!
         tts=new TextToSpeech(getActivity().getApplicationContext(), new TextToSpeech.OnInitListener() {
         //@Override
            public void onInit(int status) {
               if(status != TextToSpeech.ERROR) {
                  tts.setLanguage(Locale.GERMAN);
               }
            }
         });

         audio = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);  //NEU:  getActivity(). ab Proc. 3!
         currentVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
         maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         
         //eigenen Thread starten:
         (new Thread(this)).start();
     }
}

Code 0-2: Zusatz.pde