[voicerss] Null annotations added on the main service class (#12190)
Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
parent
3c7d4d8a38
commit
02776d8585
|
@ -20,6 +20,8 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.OpenHAB;
|
||||
import org.openhab.core.audio.AudioException;
|
||||
import org.openhab.core.audio.AudioFormat;
|
||||
|
@ -30,6 +32,7 @@ import org.openhab.core.voice.TTSService;
|
|||
import org.openhab.core.voice.Voice;
|
||||
import org.openhab.voice.voicerss.internal.cloudapi.CachedVoiceRSSCloudImpl;
|
||||
import org.osgi.framework.Constants;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -41,6 +44,7 @@ import org.slf4j.LoggerFactory;
|
|||
* @author Jochen Hiller - Initial contribution and API
|
||||
* @author Laurent Garnier - add support for OGG and AAC audio formats
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "org.openhab.voicerss", property = Constants.SERVICE_PID + "=org.openhab.voicerss")
|
||||
@ConfigurableService(category = "voice", label = "VoiceRSS Text-to-Speech", description_uri = "voice:voicerss")
|
||||
public class VoiceRSSTTSService implements TTSService {
|
||||
|
@ -66,27 +70,28 @@ public class VoiceRSSTTSService implements TTSService {
|
|||
|
||||
private final Logger logger = LoggerFactory.getLogger(VoiceRSSTTSService.class);
|
||||
|
||||
private String apiKey;
|
||||
private @Nullable String apiKey;
|
||||
|
||||
/**
|
||||
* We need the cached implementation to allow for FixedLengthAudioStream.
|
||||
*/
|
||||
private CachedVoiceRSSCloudImpl voiceRssImpl;
|
||||
private @Nullable CachedVoiceRSSCloudImpl voiceRssImpl;
|
||||
|
||||
/**
|
||||
* Set of supported voices
|
||||
*/
|
||||
private Set<Voice> voices;
|
||||
private @Nullable Set<Voice> voices;
|
||||
|
||||
/**
|
||||
* Set of supported audio formats
|
||||
*/
|
||||
private Set<AudioFormat> audioFormats;
|
||||
private @Nullable Set<AudioFormat> audioFormats;
|
||||
|
||||
/**
|
||||
* DS activate, with access to ConfigAdmin
|
||||
*/
|
||||
protected void activate(Map<String, Object> config) {
|
||||
@Activate
|
||||
protected void activate(@Nullable Map<String, Object> config) {
|
||||
try {
|
||||
modified(config);
|
||||
voiceRssImpl = initVoiceImplementation();
|
||||
|
@ -100,7 +105,7 @@ public class VoiceRSSTTSService implements TTSService {
|
|||
}
|
||||
|
||||
@Modified
|
||||
protected void modified(Map<String, Object> config) {
|
||||
protected void modified(@Nullable Map<String, Object> config) {
|
||||
if (config != null) {
|
||||
apiKey = config.containsKey(CONFIG_API_KEY) ? config.get(CONFIG_API_KEY).toString() : null;
|
||||
}
|
||||
|
@ -108,37 +113,41 @@ public class VoiceRSSTTSService implements TTSService {
|
|||
|
||||
@Override
|
||||
public Set<Voice> getAvailableVoices() {
|
||||
return Collections.unmodifiableSet(voices);
|
||||
Set<Voice> localVoices = voices;
|
||||
return localVoices == null ? Set.of() : Collections.unmodifiableSet(localVoices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AudioFormat> getSupportedFormats() {
|
||||
return Collections.unmodifiableSet(audioFormats);
|
||||
Set<AudioFormat> localFormats = audioFormats;
|
||||
return localFormats == null ? Set.of() : Collections.unmodifiableSet(localFormats);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AudioStream synthesize(String text, Voice voice, AudioFormat requestedFormat) throws TTSException {
|
||||
logger.debug("Synthesize '{}' for voice '{}' in format {}", text, voice.getUID(), requestedFormat);
|
||||
// Validate known api key
|
||||
if (apiKey == null) {
|
||||
throw new TTSException("Missing API key, configure it first before using");
|
||||
CachedVoiceRSSCloudImpl voiceRssCloud = voiceRssImpl;
|
||||
if (voiceRssCloud == null) {
|
||||
throw new TTSException("The service is not correctly initialized");
|
||||
}
|
||||
// Validate arguments
|
||||
if (text == null) {
|
||||
throw new TTSException("The passed text is null");
|
||||
// Validate known api key
|
||||
String key = apiKey;
|
||||
if (key == null) {
|
||||
throw new TTSException("Missing API key, configure it first before using");
|
||||
}
|
||||
// trim text
|
||||
String trimmedText = text.trim();
|
||||
if (trimmedText.isEmpty()) {
|
||||
throw new TTSException("The passed text is empty");
|
||||
}
|
||||
if (!voices.contains(voice)) {
|
||||
Set<Voice> localVoices = voices;
|
||||
if (localVoices == null || !localVoices.contains(voice)) {
|
||||
throw new TTSException("The passed voice is unsupported");
|
||||
}
|
||||
|
||||
// now create the input stream for given text, locale, voice, codec and format.
|
||||
try {
|
||||
File cacheAudioFile = voiceRssImpl.getTextToSpeechAsFile(apiKey, trimmedText,
|
||||
File cacheAudioFile = voiceRssCloud.getTextToSpeechAsFile(key, trimmedText,
|
||||
voice.getLocale().toLanguageTag(), voice.getLabel(), getApiAudioCodec(requestedFormat),
|
||||
getApiAudioFormat(requestedFormat));
|
||||
return new VoiceRSSAudioStream(cacheAudioFile, requestedFormat);
|
||||
|
@ -153,11 +162,16 @@ public class VoiceRSSTTSService implements TTSService {
|
|||
* Initializes voices.
|
||||
*
|
||||
* @return The voices of this instance
|
||||
* @throws IllegalStateException if voiceRssImpl is null
|
||||
*/
|
||||
private Set<Voice> initVoices() {
|
||||
private Set<Voice> initVoices() throws IllegalStateException {
|
||||
CachedVoiceRSSCloudImpl voiceRssCloud = voiceRssImpl;
|
||||
if (voiceRssCloud == null) {
|
||||
throw new IllegalStateException("The service is not correctly initialized");
|
||||
}
|
||||
Set<Voice> voices = new HashSet<>();
|
||||
for (Locale locale : voiceRssImpl.getAvailableLocales()) {
|
||||
for (String voiceLabel : voiceRssImpl.getAvailableVoices(locale)) {
|
||||
for (Locale locale : voiceRssCloud.getAvailableLocales()) {
|
||||
for (String voiceLabel : voiceRssCloud.getAvailableVoices(locale)) {
|
||||
voices.add(new VoiceRSSVoice(locale, voiceLabel));
|
||||
}
|
||||
}
|
||||
|
@ -168,10 +182,15 @@ public class VoiceRSSTTSService implements TTSService {
|
|||
* Initializes audioFormats
|
||||
*
|
||||
* @return The audio formats of this instance
|
||||
* @throws IllegalStateException if voiceRssImpl is null
|
||||
*/
|
||||
private Set<AudioFormat> initAudioFormats() {
|
||||
private Set<AudioFormat> initAudioFormats() throws IllegalStateException {
|
||||
CachedVoiceRSSCloudImpl voiceRssCloud = voiceRssImpl;
|
||||
if (voiceRssCloud == null) {
|
||||
throw new IllegalStateException("The service is not correctly initialized");
|
||||
}
|
||||
Set<AudioFormat> audioFormats = new HashSet<>();
|
||||
for (String codec : voiceRssImpl.getAvailableAudioCodecs()) {
|
||||
for (String codec : voiceRssCloud.getAvailableAudioCodecs()) {
|
||||
switch (codec) {
|
||||
case "MP3":
|
||||
audioFormats.add(new AudioFormat(AudioFormat.CONTAINER_NONE, AudioFormat.CODEC_MP3, null, 16, 64000,
|
||||
|
@ -271,15 +290,18 @@ public class VoiceRSSTTSService implements TTSService {
|
|||
* @throws TTSException if {@code format} is not supported
|
||||
*/
|
||||
private String getApiAudioFormat(AudioFormat format) throws TTSException {
|
||||
final int bitDepth = format.getBitDepth() != null ? format.getBitDepth() : 16;
|
||||
final Long frequency = format.getFrequency() != null ? format.getFrequency() : 44_100L;
|
||||
final Integer formatBitDepth = format.getBitDepth();
|
||||
final int bitDepth = formatBitDepth != null ? formatBitDepth.intValue() : 16;
|
||||
final Long formatFrequency = format.getFrequency();
|
||||
final Long frequency = formatFrequency != null ? formatFrequency.longValue() : 44_100L;
|
||||
final String apiFrequency = FREQUENCY_MAP.get(frequency);
|
||||
|
||||
if (apiFrequency == null || (bitDepth != 8 && bitDepth != 16)) {
|
||||
throw new TTSException("Unsupported audio format: " + format);
|
||||
}
|
||||
|
||||
switch (format.getCodec() != null ? format.getCodec() : AudioFormat.CODEC_PCM_SIGNED) {
|
||||
String codec = format.getCodec();
|
||||
switch (codec != null ? codec : AudioFormat.CODEC_PCM_SIGNED) {
|
||||
case AudioFormat.CODEC_PCM_ALAW:
|
||||
return "alaw_" + apiFrequency + "_mono";
|
||||
case AudioFormat.CODEC_PCM_ULAW:
|
||||
|
@ -310,7 +332,7 @@ public class VoiceRSSTTSService implements TTSService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(Locale locale) {
|
||||
public String getLabel(@Nullable Locale locale) {
|
||||
return "VoiceRSS";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.io.BufferedReader;
|
|||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.voice.voicerss.internal.cloudapi.CachedVoiceRSSCloudImpl;
|
||||
|
@ -73,7 +74,10 @@ public class CreateTTSCache {
|
|||
File inputFile = new File(inputFileName);
|
||||
if (!inputFile.exists()) {
|
||||
usage();
|
||||
System.err.println("File " + inputFileName + " not found");
|
||||
PrintStream printStream = System.err;
|
||||
if (printStream != null) {
|
||||
printStream.println("File " + inputFileName + " not found");
|
||||
}
|
||||
return RC_INPUT_FILE_NOT_FOUND;
|
||||
}
|
||||
generateCacheForFile(apiKey, cacheDir, locale, voice, codec, format, inputFileName);
|
||||
|
@ -85,22 +89,26 @@ public class CreateTTSCache {
|
|||
}
|
||||
|
||||
private void usage() {
|
||||
System.out.println("Usage: java org.openhab.voice.voicerss.tool.CreateTTSCache <args>");
|
||||
System.out.println(
|
||||
PrintStream printStream = System.out;
|
||||
if (printStream == null) {
|
||||
return;
|
||||
}
|
||||
printStream.println("Usage: java org.openhab.voice.voicerss.tool.CreateTTSCache <args>");
|
||||
printStream.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\"");
|
||||
System.out.println(" voice the voice, \"default\" for the default voice");
|
||||
System.out.println(" text the text to create audio file for, e.g. \"Hello World\"");
|
||||
System.out.println(
|
||||
printStream.println(" key the VoiceRSS API Key, e.g. \"123456789\"");
|
||||
printStream.println(" cache-dir is directory where the files will be stored, e.g. \"voicerss-cache\"");
|
||||
printStream.println(" locale the language locale, has to be valid, e.g. \"en-us\", \"de-de\"");
|
||||
printStream.println(" voice the voice, \"default\" for the default voice");
|
||||
printStream.println(" text the text to create audio file for, e.g. \"Hello World\"");
|
||||
printStream.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(
|
||||
printStream.println(" codec the audio codec, \"MP3\", \"WAV\", \"OGG\" or \"AAC\", \"MP3\" by default");
|
||||
printStream.println(" format the audio format, \"44khz_16bit_mono\" by default");
|
||||
printStream.println();
|
||||
printStream.println(
|
||||
"Sample: java org.openhab.voice.voicerss.tool.CreateTTSCache --api-key 1234567890 cache en-US default @messages.txt");
|
||||
System.out.println();
|
||||
printStream.println();
|
||||
}
|
||||
|
||||
private void generateCacheForFile(String apiKey, String cacheDir, String locale, String voice, String codec,
|
||||
|
@ -117,19 +125,29 @@ public class CreateTTSCache {
|
|||
|
||||
private void generateCacheForMessage(String apiKey, String cacheDir, String locale, String voice, String codec,
|
||||
String format, String msg) throws IOException {
|
||||
PrintStream printStream;
|
||||
String trimmedMsg = msg.trim();
|
||||
if (trimmedMsg.length() == 0) {
|
||||
System.err.println("Ignore msg=''");
|
||||
printStream = System.err;
|
||||
if (printStream != null) {
|
||||
printStream.println("Ignore msg=''");
|
||||
}
|
||||
return;
|
||||
}
|
||||
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='"
|
||||
printStream = System.out;
|
||||
if (printStream != null) {
|
||||
printStream.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 + "'");
|
||||
printStream = System.err;
|
||||
if (printStream != null) {
|
||||
printStream.println("Failed to create cached audio for locale='" + locale + "', voice='" + voice
|
||||
+ "',msg='" + trimmedMsg + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue