]> git.basschouten.com Git - openhab-addons.git/commitdiff
[voicerss] Fix and enhance the external tool to prefill the cache (#12155)
authorlolodomo <lg.hc@free.fr>
Wed, 2 Feb 2022 17:39:35 +0000 (18:39 +0100)
committerGitHub <noreply@github.com>
Wed, 2 Feb 2022 17:39:35 +0000 (18:39 +0100)
* [voicerss] Fix and enhance the external tool to prefill the cache
* Make the tool independent of core openHAB
* Fix the parsing of the command arguments.
* Allow to provide optional audio codec and format.
* Null annotations added on most of the classes
* Simplified doc about classpath
* Suppress obsolete mention of open issue in doc

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
bundles/org.openhab.voice.voicerss/README.md
bundles/org.openhab.voice.voicerss/src/main/java/org/openhab/voice/voicerss/internal/VoiceRSSAudioStream.java
bundles/org.openhab.voice.voicerss/src/main/java/org/openhab/voice/voicerss/internal/VoiceRSSTTSService.java
bundles/org.openhab.voice.voicerss/src/main/java/org/openhab/voice/voicerss/internal/VoiceRSSVoice.java
bundles/org.openhab.voice.voicerss/src/main/java/org/openhab/voice/voicerss/internal/cloudapi/CachedVoiceRSSCloudImpl.java
bundles/org.openhab.voice.voicerss/src/main/java/org/openhab/voice/voicerss/internal/cloudapi/VoiceRSSCloudAPI.java
bundles/org.openhab.voice.voicerss/src/main/java/org/openhab/voice/voicerss/internal/cloudapi/VoiceRSSCloudImpl.java
bundles/org.openhab.voice.voicerss/src/main/java/org/openhab/voice/voicerss/tool/CreateTTSCache.java

index 7cca6bc40983f7027e1150a5c83922d996f9f544..5d8d908455d81c3a6dd9ee9fdb80da580b98a20a 100644 (file)
@@ -177,17 +177,17 @@ Synopsis of this tool:
 
 ```
 Usage: java org.openhab.voice.voicerss.tool.CreateTTSCache <args>
-Arguments: --api-key <key> <cache-dir> <locale> { <text> | @inputfile }
+Arguments: --api-key <key> <cache-dir> <locale> <voice> { <text> | @inputfile } [ <codec> <format> ]
   key       the VoiceRSS API Key, e.g. "123456789"
   cache-dir is directory where the files will be stored, e.g. "voicerss-cache"
   locale    the language locale, has to be valid, e.g. "en-us", "de-de"
+  voice     the voice, "default" for the default voice
   text      the text to create audio file for, e.g. "Hello World"
   inputfile a name of a file, where all lines will be translatet to text, e.g. "@message.txt"
+  codec     the audio codec, "MP3", "WAV", "OGG" or "AAC", "MP3" by default
+  format    the audio format, "44khz_16bit_mono" by default
 
-Sample: java org.openhab.voice.voicerss.tool.CreateTTSCache --api-key 1234567890 cache en-US @messages.txt
+Sample: java org.openhab.voice.voicerss.tool.CreateTTSCache --api-key 1234567890 cache en-US default @messages.txt
 ```
 
-
-## Open Issues
-
-* do not log API-Key in plain text
+You will need to specify the classpath for your addon (jar) in the command line (java -cp <path> ...).
index 8657c5805ffaf192166111eb61556498f0700677..787a0312f2dcdb48a0710db71583112059483bd1 100644 (file)
@@ -14,6 +14,7 @@ package org.openhab.voice.voicerss.internal;
 
 import java.io.File;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.core.audio.AudioException;
 import org.openhab.core.audio.AudioFormat;
 import org.openhab.core.audio.AudioStream;
@@ -27,6 +28,7 @@ import org.openhab.core.audio.FileAudioStream;
  *
  * @author Jochen Hiller - Initial contribution and API
  */
+@NonNullByDefault
 class VoiceRSSAudioStream extends FileAudioStream {
 
     public VoiceRSSAudioStream(File audioFile, AudioFormat format) throws AudioException {
index 2fdae5808475e3cb9b54cab1a4f3dd8be84dd428..73eecbec82c40af563ade56903c931c64cb29705 100644 (file)
@@ -170,7 +170,82 @@ public class VoiceRSSTTSService implements TTSService {
      * @return The audio formats of this instance
      */
     private Set<AudioFormat> initAudioFormats() {
-        return voiceRssImpl.getAvailableAudioFormats();
+        Set<AudioFormat> audioFormats = new HashSet<>();
+        for (String codec : voiceRssImpl.getAvailableAudioCodecs()) {
+            switch (codec) {
+                case "MP3":
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_NONE, AudioFormat.CODEC_MP3, null, 16, 64000,
+                            44_100L));
+                    break;
+                case "OGG":
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_OGG, AudioFormat.CODEC_VORBIS, null, 16,
+                            null, 44_100L));
+                    break;
+                case "AAC":
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_NONE, AudioFormat.CODEC_AAC, null, 16, null,
+                            44_100L));
+                    break;
+                case "WAV":
+                    // Consider only mono formats
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false,
+                            8, 64_000, 8_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false,
+                            16, 128_000, 8_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false,
+                            8, 88_200, 11_025L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false,
+                            16, 176_400, 11_025L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false,
+                            8, 96_000, 12_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false,
+                            16, 192_000, 12_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false,
+                            8, 128_000, 16_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false,
+                            16, 256_000, 16_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false,
+                            8, 176_400, 22_050L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false,
+                            16, 352_800, 22_050L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false,
+                            8, 192_000, 24_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false,
+                            16, 384_000, 24_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false,
+                            8, 256_000, 32_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false,
+                            16, 512_000, 32_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false,
+                            8, 352_800, 44_100L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false,
+                            16, 705_600, 44_100L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false,
+                            8, 384_000, 48_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false,
+                            16, 768_000, 48_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ALAW, null, 8,
+                            64_000, 8_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ALAW, null, 8,
+                            88_200, 11_025L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ALAW, null, 8,
+                            176_400, 22_050L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ALAW, null, 8,
+                            352_800, 44_100L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ULAW, null, 8,
+                            64_000, 8_000L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ULAW, null, 8,
+                            88_200, 11_025L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ULAW, null, 8,
+                            176_400, 22_050L));
+                    audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ULAW, null, 8,
+                            352_800, 44_100L));
+                    break;
+                default:
+                    logger.debug("Audio codec {} not yet supported", codec);
+                    break;
+            }
+        }
+        return audioFormats;
     }
 
     /**
@@ -221,7 +296,7 @@ public class VoiceRSSTTSService implements TTSService {
     }
 
     private CachedVoiceRSSCloudImpl initVoiceImplementation() throws IllegalStateException {
-        return new CachedVoiceRSSCloudImpl(getCacheFolderName());
+        return new CachedVoiceRSSCloudImpl(getCacheFolderName(), true);
     }
 
     private String getCacheFolderName() {
index 4b2c4b0af2859133b1bb717dba17e602b530f648..6a752f70db4e415e8b712696c619a5e58d431179 100644 (file)
@@ -14,6 +14,7 @@ package org.openhab.voice.voicerss.internal;
 
 import java.util.Locale;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.core.voice.Voice;
 import org.openhab.voice.voicerss.internal.cloudapi.VoiceRSSCloudImpl;
 
@@ -23,6 +24,7 @@ import org.openhab.voice.voicerss.internal.cloudapi.VoiceRSSCloudImpl;
  *
  * @author Jochen Hiller - Initial contribution and API
  */
+@NonNullByDefault
 public class VoiceRSSVoice implements Voice {
 
     /**
index fd4977f2f4cfaafe1269e1526815a5a40a02fd8e..ba6f8fdb81b9d296cbd80cd2e7a140f0a96e6f77 100644 (file)
@@ -23,7 +23,8 @@ import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Objects;
 
-import org.slf4j.Logger;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
 import org.slf4j.LoggerFactory;
 
 /**
@@ -33,6 +34,7 @@ import org.slf4j.LoggerFactory;
  *
  * @author Jochen Hiller - Initial contribution
  */
+@NonNullByDefault
 public class CachedVoiceRSSCloudImpl extends VoiceRSSCloudImpl {
 
     /**
@@ -40,12 +42,11 @@ public class CachedVoiceRSSCloudImpl extends VoiceRSSCloudImpl {
      */
     private static final int READ_BUFFER_SIZE = 4096;
 
-    private final Logger logger = LoggerFactory.getLogger(CachedVoiceRSSCloudImpl.class);
-
     private final File cacheFolder;
 
-    public CachedVoiceRSSCloudImpl(String cacheFolderName) throws IllegalStateException {
-        if (cacheFolderName == null) {
+    public CachedVoiceRSSCloudImpl(String cacheFolderName, boolean logging) throws IllegalStateException {
+        super(logging);
+        if (cacheFolderName.isBlank()) {
             throw new IllegalStateException("Folder for cache must be defined");
         }
         // Lazy create the cache folder
@@ -89,7 +90,7 @@ public class CachedVoiceRSSCloudImpl extends VoiceRSSCloudImpl {
      *
      * Sample: "en-US_00a2653ac5f77063bc4ea2fee87318d3"
      */
-    private String getUniqueFilenameForText(String text, String locale, String voice, String format) {
+    private @Nullable String getUniqueFilenameForText(String text, String locale, String voice, String format) {
         try {
             byte[] bytesOfMessage = text.getBytes(StandardCharsets.UTF_8);
             MessageDigest md = MessageDigest.getInstance("MD5");
@@ -112,7 +113,10 @@ public class CachedVoiceRSSCloudImpl extends VoiceRSSCloudImpl {
             return filename;
         } catch (NoSuchAlgorithmException ex) {
             // should not happen
-            logger.error("Could not create MD5 hash for '{}'", text, ex);
+            if (logging) {
+                LoggerFactory.getLogger(CachedVoiceRSSCloudImpl.class).error("Could not create MD5 hash for '{}'", text,
+                        ex);
+            }
             return null;
         }
     }
index 27fb29290a6273d66436f5a3e2ec19c9f9ef7004..84ec44ffcbcd223c184dfd8ce157f293d535cbbf 100644 (file)
@@ -17,7 +17,7 @@ import java.io.InputStream;
 import java.util.Locale;
 import java.util.Set;
 
-import org.openhab.core.audio.AudioFormat;
+import org.eclipse.jdt.annotation.NonNullByDefault;
 
 /**
  * Interface which represents the functionality needed from the VoiceRSS TTS
@@ -25,6 +25,7 @@ import org.openhab.core.audio.AudioFormat;
  *
  * @author Jochen Hiller - Initial contribution
  */
+@NonNullByDefault
 public interface VoiceRSSCloudAPI {
 
     /**
@@ -35,13 +36,12 @@ public interface VoiceRSSCloudAPI {
     Set<Locale> getAvailableLocales();
 
     /**
-     * Get all supported audio formats by the TTS service. This includes MP3,
-     * WAV and more audio formats as used in APIs. About supported audio
-     * formats, see {@link AudioFormat}
+     * Get all supported audio codecs by the TTS service. This includes MP3,
+     * WAV and more audio formats as used in APIs.
      *
-     * @return A set of all audio formats supported
+     * @return A set of all audio codecs supported
      */
-    Set<AudioFormat> getAvailableAudioFormats();
+    Set<String> getAvailableAudioCodecs();
 
     /**
      * Get all supported voices.
index 0c6f8c372fdaff5be354bf598673a11ee9c49fcb..cb6bd5f2d5656e302e4ca016a92951ff74090822 100644 (file)
@@ -27,7 +27,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 
-import org.openhab.core.audio.AudioFormat;
+import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory;
  * @author Laurent Garnier - add support for OGG and AAC audio formats
  * @author Andreas Brenk - add support for WAV audio format
  */
+@NonNullByDefault
 public class VoiceRSSCloudImpl implements VoiceRSSCloudAPI {
 
     public static final String DEFAULT_VOICE = "default";
@@ -55,36 +56,7 @@ public class VoiceRSSCloudImpl implements VoiceRSSCloudAPI {
     public static final String API_URL = "https://api.voicerss.org/?key=%s&hl=%s&c=%s&f=%s&src=%s";
     public static final String API_URL_WITH_VOICE = API_URL + "&v=%s";
 
-    private static final Set<AudioFormat> SUPPORTED_AUDIO_FORMATS = Set.of(
-            new AudioFormat(AudioFormat.CONTAINER_NONE, AudioFormat.CODEC_MP3, null, 16, null, 44_100L),
-            new AudioFormat(AudioFormat.CONTAINER_OGG, AudioFormat.CODEC_VORBIS, null, 16, null, 44_100L),
-            new AudioFormat(AudioFormat.CONTAINER_NONE, AudioFormat.CODEC_AAC, null, 16, null, 44_100L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, null, 8, 64_000, 8_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, null, 16, 128_000, 8_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false, 8, 88_200, 11_025L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16, 176_400, 11_025L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false, 8, 96_000, 12_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16, 192_000, 12_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false, 8, 128_000, 16_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16, 256_000, 16_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false, 8, 176_400, 22_050L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16, 352_800, 22_050L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false, 8, 192_000, 24_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16, 384_000, 24_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false, 8, 256_000, 32_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16, 512_000, 32_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false, 8, 352_800, 44_100L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16, 705_600, 44_100L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_UNSIGNED, false, 8, 384_000, 48_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16, 768_000, 48_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ALAW, null, 8, 64_000, 8_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ALAW, null, 8, 88_200, 11_025L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ALAW, null, 8, 176_400, 22_050L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ALAW, null, 8, 352_800, 44_100L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ULAW, null, 8, 64_000, 8_000L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ULAW, null, 8, 88_200, 11_025L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ULAW, null, 8, 176_400, 22_050L),
-            new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_ULAW, null, 8, 352_800, 44_100L));
+    private static final Set<String> SUPPORTED_AUDIO_CODECS = Set.of("MP3", "OGG", "AAC", "WAV", "CAF");
 
     private static final Set<Locale> SUPPORTED_LOCALES = new HashSet<>();
     static {
@@ -192,11 +164,15 @@ public class VoiceRSSCloudImpl implements VoiceRSSCloudAPI {
         SUPPORTED_VOICES.put("zh-tw", Set.of("Akemi", "Lin", "Lee"));
     }
 
-    private final Logger logger = LoggerFactory.getLogger(VoiceRSSCloudImpl.class);
+    protected boolean logging;
+
+    public VoiceRSSCloudImpl(boolean logging) {
+        this.logging = logging;
+    }
 
     @Override
-    public Set<AudioFormat> getAvailableAudioFormats() {
-        return SUPPORTED_AUDIO_FORMATS;
+    public Set<String> getAvailableAudioCodecs() {
+        return SUPPORTED_AUDIO_CODECS;
     }
 
     @Override
@@ -242,7 +218,9 @@ public class VoiceRSSCloudImpl implements VoiceRSSCloudAPI {
     public InputStream getTextToSpeech(String apiKey, String text, String locale, String voice, String audioCodec,
             String audioFormat) throws IOException {
         String url = createURL(apiKey, text, locale, voice, audioCodec, audioFormat);
-        logger.debug("Call {}", url.replace(apiKey, "***"));
+        if (logging) {
+            LoggerFactory.getLogger(VoiceRSSCloudImpl.class).debug("Call {}", url.replace(apiKey, "***"));
+        }
         URLConnection connection = new URL(url).openConnection();
 
         // we will check return codes. The service will ALWAYS return a HTTP
@@ -250,12 +228,18 @@ public class VoiceRSSCloudImpl implements VoiceRSSCloudAPI {
         // the error message in body
         int status = ((HttpURLConnection) connection).getResponseCode();
         if (HttpURLConnection.HTTP_OK != status) {
-            logger.warn("Call {} returned HTTP {}", url.replace(apiKey, "***"), status);
+            if (logging) {
+                LoggerFactory.getLogger(VoiceRSSCloudImpl.class).warn("Call {} returned HTTP {}",
+                        url.replace(apiKey, "***"), status);
+            }
             throw new IOException("Could not read from service: HTTP code " + status);
         }
-        if (logger.isTraceEnabled()) {
-            for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
-                logger.trace("Response.header: {}={}", header.getKey(), header.getValue());
+        if (logging) {
+            Logger logger = LoggerFactory.getLogger(VoiceRSSCloudImpl.class);
+            if (logger.isTraceEnabled()) {
+                for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
+                    logger.trace("Response.header: {}={}", header.getKey(), header.getValue());
+                }
             }
         }
         String contentType = connection.getHeaderField("Content-Type");
@@ -268,7 +252,9 @@ public class VoiceRSSCloudImpl implements VoiceRSSCloudAPI {
             try {
                 is.close();
             } catch (IOException ex) {
-                logger.debug("Failed to close inputstream", ex);
+                if (logging) {
+                    LoggerFactory.getLogger(VoiceRSSCloudImpl.class).debug("Failed to close inputstream", ex);
+                }
             }
             throw new IOException(
                     "Could not read audio content, service returned an error: " + new String(bytes, "UTF-8"));
index f5d1c1ae6162390a7f1c28c3014e8078e4bc7e00..6e9fed935f98ae759f3931a50098b7f758bb8f16 100644 (file)
@@ -17,6 +17,7 @@ import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.voice.voicerss.internal.cloudapi.CachedVoiceRSSCloudImpl;
 
 /**
@@ -24,12 +25,14 @@ import org.openhab.voice.voicerss.internal.cloudapi.CachedVoiceRSSCloudImpl;
  *
  * @author Jochen Hiller - Initial contribution
  */
+@NonNullByDefault
 public class CreateTTSCache {
 
     public static final int RC_OK = 0;
     public static final int RC_USAGE = 1;
     public static final int RC_INPUT_FILE_NOT_FOUND = 2;
     public static final int RC_API_KEY_MISSING = 3;
+    public static final int RC_INVALID_CODEC = 4;
 
     public static void main(String[] args) throws IOException {
         CreateTTSCache tool = new CreateTTSCache();
@@ -38,7 +41,7 @@ public class CreateTTSCache {
     }
 
     public int doMain(String[] args) throws IOException {
-        if ((args == null) || (args.length != 5)) {
+        if (args.length < 6) {
             usage();
             return RC_USAGE;
         }
@@ -50,6 +53,21 @@ public class CreateTTSCache {
         String cacheDir = args[2];
         String locale = args[3];
         String voice = args[4];
+        String codec = "MP3";
+        if (args.length >= 7) {
+            switch (args[6]) {
+                case "MP3":
+                case "WAV":
+                case "OGG":
+                case "AAC":
+                    codec = args[6];
+                    break;
+                default:
+                    usage();
+                    return RC_INVALID_CODEC;
+            }
+        }
+        String format = args.length >= 8 ? args[7] : "44khz_16bit_mono";
         if (args[5].startsWith("@")) {
             String inputFileName = args[5].substring(1);
             File inputFile = new File(inputFileName);
@@ -58,17 +76,18 @@ public class CreateTTSCache {
                 System.err.println("File " + inputFileName + " not found");
                 return RC_INPUT_FILE_NOT_FOUND;
             }
-            generateCacheForFile(apiKey, cacheDir, locale, voice, inputFileName);
+            generateCacheForFile(apiKey, cacheDir, locale, voice, codec, format, inputFileName);
         } else {
             String text = args[5];
-            generateCacheForMessage(apiKey, cacheDir, locale, voice, text);
+            generateCacheForMessage(apiKey, cacheDir, locale, voice, codec, format, text);
         }
         return RC_OK;
     }
 
     private void usage() {
         System.out.println("Usage: java org.openhab.voice.voicerss.tool.CreateTTSCache <args>");
-        System.out.println("Arguments: --api-key <key> <cache-dir> <locale> { <text> | @inputfile }");
+        System.out.println(
+                "Arguments: --api-key <key> <cache-dir> <locale> <voice> { <text> | @inputfile } [ <codec> <format> ]");
         System.out.println("  key       the VoiceRSS API Key, e.g. \"123456789\"");
         System.out.println("  cache-dir is directory where the files will be stored, e.g. \"voicerss-cache\"");
         System.out.println("  locale    the language locale, has to be valid, e.g. \"en-us\", \"de-de\"");
@@ -76,38 +95,41 @@ public class CreateTTSCache {
         System.out.println("  text      the text to create audio file for, e.g. \"Hello World\"");
         System.out.println(
                 "  inputfile a name of a file, where all lines will be translatet to text, e.g. \"@message.txt\"");
+        System.out.println("  codec     the audio codec, \"MP3\", \"WAV\", \"OGG\" or \"AAC\", \"MP3\" by default");
+        System.out.println("  format    the audio format, \"44khz_16bit_mono\" by default");
         System.out.println();
         System.out.println(
-                "Sample: java org.openhab.voice.voicerss.tool.CreateTTSCache --api-key 1234567890 cache en-US @messages.txt");
+                "Sample: java org.openhab.voice.voicerss.tool.CreateTTSCache --api-key 1234567890 cache en-US default @messages.txt");
         System.out.println();
     }
 
-    private void generateCacheForFile(String apiKey, String cacheDir, String locale, String voice, String inputFileName)
-            throws IOException {
+    private void generateCacheForFile(String apiKey, String cacheDir, String locale, String voice, String codec,
+            String format, String inputFileName) throws IOException {
         File inputFile = new File(inputFileName);
         try (BufferedReader br = new BufferedReader(new FileReader(inputFile))) {
             String line;
             while ((line = br.readLine()) != null) {
                 // process the line.
-                generateCacheForMessage(apiKey, cacheDir, locale, voice, line);
+                generateCacheForMessage(apiKey, cacheDir, locale, voice, codec, format, line);
             }
         }
     }
 
-    private void generateCacheForMessage(String apiKey, String cacheDir, String locale, String voice, String msg)
-            throws IOException {
-        if (msg == null) {
-            System.err.println("Ignore msg=null");
-            return;
-        }
+    private void generateCacheForMessage(String apiKey, String cacheDir, String locale, String voice, String codec,
+            String format, String msg) throws IOException {
         String trimmedMsg = msg.trim();
         if (trimmedMsg.length() == 0) {
             System.err.println("Ignore msg=''");
             return;
         }
-        CachedVoiceRSSCloudImpl impl = new CachedVoiceRSSCloudImpl(cacheDir);
-        File cachedFile = impl.getTextToSpeechAsFile(apiKey, trimmedMsg, locale, voice, "MP3", null);
-        System.out.println(
-                "Created cached audio for locale='" + locale + "', msg='" + trimmedMsg + "' to file=" + cachedFile);
+        try {
+            CachedVoiceRSSCloudImpl impl = new CachedVoiceRSSCloudImpl(cacheDir, false);
+            File cachedFile = impl.getTextToSpeechAsFile(apiKey, trimmedMsg, locale, voice, codec, format);
+            System.out.println("Created cached audio for locale='" + locale + "', voice='" + voice + "', msg='"
+                    + trimmedMsg + "' to file=" + cachedFile);
+        } catch (IllegalStateException | IOException ex) {
+            System.err.println("Failed to create cached audio for locale='" + locale + "', voice='" + voice + "',msg='"
+                    + trimmedMsg + "'");
+        }
     }
 }