content: /**
   * Copyright 2011 DFKI GmbH.
   * All Rights Reserved.  Use is subject to license terms.
   * <p/>
   * This file is part of MARY TTS.
   * <p/>
   * MARY TTS is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, version 3 of the License.
   * <p/>
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Lesser General Public License for more details.
   * <p/>
   * You should have received a copy of the GNU Lesser General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  package marytts;
  
  import java.util.HashSet;
  import java.util.Locale;
  import java.util.Set;
  
  import lib.sound.sampled.AudioFileFormat;
  import lib.sound.sampled.AudioFormat;
  import lib.sound.sampled.AudioInputStream;
  import lib.sound.sampled.AudioSystem;
  import marytts.config.LanguageConfig;
  import marytts.config.MaryConfig;
  import marytts.datatypes.MaryData;
  import marytts.datatypes.MaryDataType;
  import marytts.exceptions.MaryConfigurationException;
  import marytts.exceptions.SynthesisException;
  import marytts.modules.synthesis.Voice;
  import marytts.server.Request;
  import marytts.util.MaryRuntimeUtils;
  import mf.org.w3c.dom.Document;
  
  /**
   * This class and its subclasses are intended to grow into a simple-to-use, unified interface for both the local MARY server and a MARY client.
   *
   * @author marc
   */
  public class LocalMaryInterface implements MaryInterface {
  
      private MaryDataType inputType;
      private MaryDataType outputType;
      private Locale locale;
      private Voice voice;
      private AudioFileFormat audioFileFormat;
      private String effects;
      private String style;
      private String outputTypeParams;
      private boolean isStreaming;
  
  
      public LocalMaryInterface() throws MaryConfigurationException {
          try {
              MaryRuntimeUtils.ensureMaryStarted();
          } catch (Exception e) {
              throw new MaryConfigurationException("Cannot start MARY server", e);
          }
  
          init();
      }
  
      protected void init() {
          setReasonableDefaults();
      }
  
      protected void setReasonableDefaults() {
          inputType = MaryDataType.TEXT;
          outputType = MaryDataType.AUDIO;
          locale = Locale.US;
          voice = Voice.getDefaultVoice(locale);
          setAudioFileFormatForVoice();
          effects = null;
          style = null;
          outputTypeParams = null;
          isStreaming = false;
  
      }
  
      private void setAudioFileFormatForVoice() {
          if (voice != null) {
              AudioFormat af = voice.dbAudioFormat();
              audioFileFormat = new AudioFileFormat(AudioFileFormat.Type.WAVE, af, AudioSystem.NOT_SPECIFIED);
          } else {
              audioFileFormat = null;
          }
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#getInputType()
       */
      @Override
      public String getInputType() {
          return inputType.name();
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#setInputType(java.lang.String)
       */
      @Override
      public void setInputType(String newInputType) throws IllegalArgumentException {
          inputType = MaryDataType.get(newInputType);
          if (inputType == null) {
              throw new IllegalArgumentException("No such type: " + newInputType);
          } else if (!inputType.isInputType()) {
              throw new IllegalArgumentException("Not an input type: " + newInputType);
          }
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#getOutputType()
       */
      @Override
      public String getOutputType() {
          return outputType.name();
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#setOutputType(java.lang.String)
       */
      @Override
      public void setOutputType(String newOutputType) throws IllegalArgumentException {
          outputType = MaryDataType.get(newOutputType);
          if (outputType == null) {
              throw new IllegalArgumentException("No such type: " + newOutputType);
          } else if (!outputType.isOutputType()) {
              throw new IllegalArgumentException("Not an output type: " + newOutputType);
          }
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#getLocale()
       */
      @Override
      public Locale getLocale() {
          return locale;
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#setLocale(java.util.Locale)
       */
      @Override
      public void setLocale(Locale newLocale) throws IllegalArgumentException {
          if (MaryConfig.getLanguageConfig(newLocale) == null) {
              throw new IllegalArgumentException("Unsupported locale: " + newLocale);
          }
          locale = newLocale;
          voice = Voice.getDefaultVoice(locale);
          setAudioFileFormatForVoice();
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#getVoice()
       */
      @Override
      public String getVoice() {
          if (voice == null) {
              return null;
          }
          return voice.getName();
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#setVoice(java.lang.String)
       */
      @Override
      public void setVoice(String voiceName) throws IllegalArgumentException {
          voice = Voice.getVoice(voiceName);
          if (voice == null) {
              throw new IllegalArgumentException("No such voice: " + voiceName);
          }
          locale = voice.getLocale();
          setAudioFileFormatForVoice();
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#getAudioEffects()
       */
      @Override
      public String getAudioEffects() {
          return effects;
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#setAudioEffects(java.lang.String)
       */
      @Override
      public void setAudioEffects(String audioEffects) {
          effects = audioEffects;
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#getStyle()
       */
      @Override
      public String getStyle() {
          return style;
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#setStyle(java.lang.String)
       */
      @Override
      public void setStyle(String newStyle) {
          style = newStyle;
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#getOutputTypeParams()
       */
      @Override
      public String getOutputTypeParams() {
          return outputTypeParams;
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#setOutputTypeParams(java.lang.String)
       */
      @Override
      public void setOutputTypeParams(String params) {
          this.outputTypeParams = params;
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#isStreamingAudio()
       */
      @Override
      public boolean isStreamingAudio() {
          return isStreaming;
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#setStreamingAudio(boolean)
       */
      @Override
      public void setStreamingAudio(boolean newIsStreaming) {
          isStreaming = newIsStreaming;
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#generateText(java.lang.String)
       */
      @Override
      public String generateText(String text) throws SynthesisException {
          verifyInputTypeIsText();
          verifyOutputTypeIsText();
          MaryData in = getMaryDataFromText(text);
          MaryData out = process(in);
          return out.getPlainText();
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#generateText(org.w3c.dom.Document)
       */
      @Override
      public String generateText(Document doc) throws SynthesisException {
          verifyInputTypeIsXML();
          verifyOutputTypeIsText();
          MaryData in = getMaryDataFromXML(doc);
          MaryData out = process(in);
          return out.getPlainText();
      }
  
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#generateXML(java.lang.String)
       */
      @Override
      public Document generateXML(String text) throws SynthesisException {
          verifyInputTypeIsText();
          verifyOutputTypeIsXML();
          MaryData in = getMaryDataFromText(text);
          MaryData out = process(in);
          return out.getDocument();
      }
  
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#generateXML(org.w3c.dom.Document)
       */
      @Override
      public Document generateXML(Document doc) throws SynthesisException {
          verifyInputTypeIsXML();
          verifyOutputTypeIsXML();
          MaryData in = getMaryDataFromXML(doc);
          MaryData out = process(in);
          return out.getDocument();
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#generateAudio(java.lang.String)
       */
      @Override
      public AudioInputStream generateAudio(String text) throws SynthesisException {
          verifyInputTypeIsText();
          verifyOutputTypeIsAudio();
          verifyVoiceIsAvailableForLocale();
          MaryData in = getMaryDataFromText(text);
          MaryData out = process(in);
          return out.getAudio();
      }
  
      /* (non-Javadoc)
       * @see marytts.MaryInterface#generateAudio(org.w3c.dom.Document)
       */
      @Override
      public AudioInputStream generateAudio(Document doc) throws SynthesisException {
          verifyInputTypeIsXML();
          verifyOutputTypeIsAudio();
          verifyVoiceIsAvailableForLocale();
          MaryData in = getMaryDataFromXML(doc);
          MaryData out = process(in);
          return out.getAudio();
      }
  
      private void verifyOutputTypeIsXML() {
          if (!outputType.isXMLType()) {
              throw new IllegalArgumentException("Cannot provide XML output for non-XML-based output type " + outputType);
          }
      }
  
      private void verifyInputTypeIsXML() {
          if (!inputType.isXMLType()) {
              throw new IllegalArgumentException("Cannot provide XML input for non-XML-based input type " + inputType);
          }
      }
  
      private void verifyInputTypeIsText() {
          if (inputType.isXMLType()) {
              throw new IllegalArgumentException("Cannot provide plain-text input for XML-based input type " + inputType);
          }
      }
  
      private void verifyOutputTypeIsAudio() {
          if (!outputType.equals(MaryDataType.AUDIO)) {
              throw new IllegalArgumentException("Cannot provide audio output for non-audio output type " + outputType);
          }
      }
  
      private void verifyOutputTypeIsText() {
          if (outputType.isXMLType() || !outputType.isTextType()) {
              throw new IllegalArgumentException("Cannot provide text output for non-text output type " + outputType);
          }
      }
  
      /**
       * Synthesis will fail if {@link MaryDataType#AUDIO AUDIO} is requested but no voice is available for the requested Locale.
       * Moreover, the {@linkplain #audioFileFormat} will be null because {@linkplain #setAudioFileFormatForVoice()} silently
       * ignores if {@linkplain #voice} is null.
       *
       * @throws IllegalArgumentException which should actually be a {@link MaryConfigurationException}.
       */
      private void verifyVoiceIsAvailableForLocale() {
          if (outputType.equals(MaryDataType.AUDIO)) {
              if (getAvailableVoices(locale).isEmpty()) {
                  throw new IllegalArgumentException("No voice is available for Locale: " + locale);
              }
          }
      }
  
      private MaryData getMaryDataFromText(String text) throws SynthesisException {
          MaryData in = new MaryData(inputType, locale);
          try {
              in.setData(text);
          } catch (Exception ioe) {
              throw new SynthesisException(ioe);
          }
          return in;
      }
  
      private MaryData getMaryDataFromXML(Document doc) throws SynthesisException {
          MaryData in = new MaryData(inputType, locale);
          try {
              in.setDocument(doc);
          } catch (Exception ioe) {
              throw new SynthesisException(ioe);
          }
          return in;
      }
  
      private MaryData process(MaryData in) throws SynthesisException {
          Request r = new Request(inputType, outputType, locale, voice, effects, style, 1, audioFileFormat, isStreaming, outputTypeParams);
          r.setInputData(in);
          try {
              r.process();
          } catch (Exception e) {
              throw new SynthesisException("cannot process", e);
          }
          return r.getOutputData();
      }
  
      @Override
      public Set<String> getAvailableVoices() {
          Set<String> voices = new HashSet<String>();
          for (Voice v : Voice.getAvailableVoices()) {
              voices.add(v.getName());
          }
          return voices;
      }
  
      @Override
      public Set<String> getAvailableVoices(Locale aLocale) {
          Set<String> voices = new HashSet<String>();
          for (Voice v : Voice.getAvailableVoices(aLocale)) {
              voices.add(v.getName());
          }
          return voices;
      }
  
      @Override
      public Set<Locale> getAvailableLocales() {
          Set<Locale> locales = new HashSet<Locale>();
          for (LanguageConfig lc : MaryConfig.getLanguageConfigs()) {
              locales.addAll(lc.getLocales());
          }
          return locales;
      }
  
      @Override
      public Set<String> getAvailableInputTypes() {
          return new HashSet<String>(MaryDataType.getInputTypeStrings());
      }
  
      @Override
      public Set<String> getAvailableOutputTypes() {
          return new HashSet<String>(MaryDataType.getOutputTypeStrings());
      }
  
      @Override
      public boolean isTextType(String dataType) {
          MaryDataType d = MaryDataType.get(dataType);
          if (d != null) {
              return d.isTextType() && !d.isXMLType();
          }
          return false;
      }
  
      @Override
      public boolean isXMLType(String dataType) {
          MaryDataType d = MaryDataType.get(dataType);
          if (d != null) {
              return d.isXMLType();
          }
          return false;
      }
  
      @Override
      public boolean isAudioType(String dataType) {
          return "AUDIO".equals(dataType);
      }
  }
  