[voicerss] Null annotations added on the main service class (#12190)

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
lolodomo 2022-02-03 19:48:14 +01:00 committed by GitHub
parent 3c7d4d8a38
commit 02776d8585
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 84 additions and 44 deletions

View File

@ -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";
}
}

View File

@ -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='"
+ trimmedMsg + "' to file=" + cachedFile);
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 + "'");
}
}
}
}