Skip to content

Commit

Permalink
improved error handling, avoiding System.err output in lib
Browse files Browse the repository at this point in the history
  • Loading branch information
jonelo committed Jan 4, 2023
1 parent ef43bd9 commit f882f7c
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 109 deletions.
48 changes: 23 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,31 @@ set the voice of your choice either directly or by providing voice preferences a
String text = "The answer to the ultimate question of life, the universe, and everything is 42";
try {
SpeechEngine speechEngine = SpeechEngineNative.getInstance();

List<Voice> voices = speechEngine.getAvailableVoices();
if (voices.size() > 0) {
System.out.println("For now the following voices are supported:\n");
for (Voice voice : voices) {
System.out.printf("%s\n", voice);
}
// We want to find a voice according our preferences
VoicePreferences voicePreferences = new VoicePreferences();
voicePreferences.setLanguage("en"); // ISO-639-1
voicePreferences.setCountry("GB"); // ISO 3166-1 Alpha-2 code
voicePreferences.setGender(VoicePreferences.Gender.FEMALE);

Voice voice = speechEngine.findVoiceByPreferences(voicePreferences);
// simple fallback just in case our preferences didn't match any voice
if (voice == null) {
System.out.printf("Warning: Voice has not been found by the voice preferences %s\n", voicePreferences);
voice = voices.get(0);
System.out.printf("Using \"%s\" instead.\n", voice);
}
speechEngine.setVoice(voice.getName());
speechEngine.say(text);
} else {
System.out.printf("Error: not even one voice have been found.\n");

System.out.println("For now the following voices are supported:\n");
for (Voice voice : voices) {
System.out.printf("%s%n", voice);
}

} catch (NotSupportedOperatingSystemException | IOException e) {
// We want to find a voice according our preferences
VoicePreferences voicePreferences = new VoicePreferences();
voicePreferences.setLanguage("en"); // ISO-639-1
voicePreferences.setCountry("GB"); // ISO 3166-1 Alpha-2 code
voicePreferences.setGender(VoicePreferences.Gender.FEMALE);
Voice voice = speechEngine.findVoiceByPreferences(voicePreferences);

// simple fallback just in case our preferences didn't match any voice
if (voice == null) {
System.out.printf("Warning: Voice has not been found by the voice preferences %s%n", voicePreferences);
voice = voices.get(0); // it is guaranteed that the speechEngine supports at least one voice
System.out.printf("Using \"%s\" instead.%n", voice);
}

speechEngine.setVoice(voice.getName());
speechEngine.say(text);

} catch (SpeechEngineCreationException | IOException e) {
System.err.println(e.getMessage());
}
```
Expand All @@ -59,7 +57,7 @@ Please stay tuned for an artifact on Maven Central.
For demonstration purposes, you can also call the Text User Interface (see also Main.java):

```
> java -jar jadapter-for-native-tts-0.11.0.jar "The answer to the ultimate question of life, the universe, and everything is 42"
> java -jar jadapter-for-native-tts-0.12.0.jar "The answer to the ultimate question of life, the universe, and everything is 42"
0: name='Microsoft Hedda Desktop', culture='de-DE', gender='Female', age='Adult', description='Microsoft Hedda Desktop (de_DE, Female)'
1: name='Microsoft Hedda', culture='de-DE', gender='Female', age='Adult', description='Microsoft Hedda (de_DE, Female)'
2: name='Microsoft Katja', culture='de-DE', gender='Female', age='Adult', description='Microsoft Katja (de_DE, Female)'
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group = "io.github.jonelo"
version = "0.11.0"
version = "0.12.0"

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.github.jonelo</groupId>
<artifactId>jadapter-for-native-tts</artifactId>
<version>0.11.0</version>
<version>0.12.0</version>
<packaging>jar</packaging>

<properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

package io.github.jonelo.jAdapterForNativeTTS.engines;

import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.NotEvenOneVoiceAvailableException;
import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.ParseException;

import java.io.IOException;
Expand All @@ -41,10 +40,12 @@ public interface SpeechEngine {

Voice parse(String line) throws ParseException;

void findAvailableVoices() throws IOException, InterruptedException, ParseException, NotEvenOneVoiceAvailableException;
void findAvailableVoices() throws IOException, InterruptedException;

List<Voice> getAvailableVoices();

List<ParseException> getParseExceptions();

Voice findVoiceByPreferences(VoicePreferences voicePreferences);

void setRate(int rate) throws IllegalArgumentException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

package io.github.jonelo.jAdapterForNativeTTS.engines;

import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.NotEvenOneVoiceAvailableException;
import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.SpeechEngineCreationException;
import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.ParseException;
import io.github.jonelo.jAdapterForNativeTTS.util.os.ProcessHelper;

Expand All @@ -38,61 +38,72 @@ public abstract class SpeechEngineAbstract implements SpeechEngine {
protected Process process;
protected List<Voice> availableVoices;
protected int rate;
protected List<ParseException> parseExceptions;

public void setVoice(String voice) {
this.voice = voice;
}

public SpeechEngineAbstract() {
/**
* Creates a SpeechEngine for your platform.
* After construction, it is guaranteed that at least one voice can be provided.
* @throws SpeechEngineCreationException if the SpeechEngine cannot be created or if not even one voice can be found.
*/
public SpeechEngineAbstract() throws SpeechEngineCreationException {
parseExceptions = new ArrayList<>();
try {
findAvailableVoices();
} catch (Exception e) {
System.err.println(e.getMessage());
} catch (IOException | InterruptedException e) {
throw new SpeechEngineCreationException(e.getMessage());
}
if (availableVoices.size() == 0) {
throw new SpeechEngineCreationException(String.format("Not even one voice has been found. There were %s parse error(s).%n", getParseExceptions().size()));
}
this.voice = null;
rate = 0; // normal voice speed
}


public SpeechEngineAbstract(String voice) {
public SpeechEngineAbstract(String voice) throws SpeechEngineCreationException {
this();
this.voice = voice;
}

/**
* Returns all available voices that are supported by this SpeechEngine instance.
* @return all available voices that are supported by this SpeechEngine instance.
*/
public List<Voice> getAvailableVoices() {
return availableVoices;
}

public void findAvailableVoices() throws IOException, InterruptedException, NotEvenOneVoiceAvailableException, ParseException {
/**
* Returns all ParseExceptions during creation of this SpeechEngine instance.
* @return all ParseExceptions during creation of this SpeechEngine instance.
*/
public List<ParseException> getParseExceptions() {
return parseExceptions;
}

public void findAvailableVoices() throws IOException, InterruptedException {
ArrayList<String> list = ProcessHelper.startApplicationAndGetOutput(getSayExecutable(), getSayOptionsToGetSupportedVoices());
ArrayList<Voice> voices = new ArrayList<>();
int parsedLines = 0;
int parseErrors = 0;
for (String line : list) {
try {
parsedLines++;
Voice v = parse(line);
if (v != null) {
voices.add(v);
}
} catch (ParseException e) {
parseErrors++;
e.printStackTrace();
parseExceptions.add(e);
}
}
availableVoices = voices;
if (availableVoices.size() == 0) {
throw new NotEvenOneVoiceAvailableException(String.format("Not even one voice has been found. There were %s parse errors.%n", parseErrors));
}
if (parseErrors > 0) {
throw new ParseException(String.format("%s voices have been found, but there were %s parse errors.%n", availableVoices.size(), parseErrors));
}
}

/**
* Find a Voice instance by providing voice preferences.
* @param voicePreferences the preferences for a voice
* @return an instance of the voice that matches the voicePreferences
* @return an instance of the voice that matches the voicePreferences, null if voice is not found by the voicePreferences
*/
public Voice findVoiceByPreferences(VoicePreferences voicePreferences) {
for (Voice voice : availableVoices) {
Expand Down Expand Up @@ -130,5 +141,4 @@ public void setRate(int rate) throws IllegalArgumentException {
this.rate = rate;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@

package io.github.jonelo.jAdapterForNativeTTS.engines;

import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.NotEvenOneVoiceAvailableException;
import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.SpeechEngineCreationException;
import io.github.jonelo.jAdapterForNativeTTS.engines.linux.SpeechEngineLinux;
import io.github.jonelo.jAdapterForNativeTTS.engines.macos.SpeechEngineMacOS;
import io.github.jonelo.jAdapterForNativeTTS.engines.windows.SpeechEngineWindows;
import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.NotSupportedOperatingSystemException;

import java.util.Locale;

Expand All @@ -39,7 +38,7 @@ public class SpeechEngineNative {

private static SpeechEngine engine = null;

public static SpeechEngine getInstance() throws NotSupportedOperatingSystemException, NotEvenOneVoiceAvailableException {
public static SpeechEngine getInstance() throws SpeechEngineCreationException {
if (engine == null) {
String osName = System.getProperty("os.name");
String osNameForComparison = osName.replaceAll("\\s", "").toLowerCase(Locale.US);
Expand All @@ -57,13 +56,9 @@ public static SpeechEngine getInstance() throws NotSupportedOperatingSystemExcep
if (osNameForComparison.startsWith("linux")) {
engine = new SpeechEngineLinux();
} else {
throw new NotSupportedOperatingSystemException(String.format("Operating System '%s' is not supported.", osName));
}
if (engine.getAvailableVoices().size() == 0) {
throw new NotEvenOneVoiceAvailableException("Not even one voice has been found.");
throw new SpeechEngineCreationException(String.format("Operating System '%s' is not supported.", osName));
}
}

return engine;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

package io.github.jonelo.jAdapterForNativeTTS.engines.exceptions;

public class NotEvenOneVoiceAvailableException extends Exception {
public NotEvenOneVoiceAvailableException(String string) {
super(string);
public class SpeechEngineCreationException extends Exception {
public SpeechEngineCreationException(String string) {
super(string);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@
import io.github.jonelo.jAdapterForNativeTTS.engines.SpeechEngineAbstract;
import io.github.jonelo.jAdapterForNativeTTS.engines.Voice;
import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.ParseException;
import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.SpeechEngineCreationException;

public class SpeechEngineLinux extends SpeechEngineAbstract {

public SpeechEngineLinux() throws SpeechEngineCreationException {
super();
}

public String getSayExecutable() {
return "spd-say";
Expand All @@ -48,7 +52,7 @@ public Voice parse(String line) throws ParseException {
String[] tokens = line.trim().split(" +");

if (tokens.length != 3) {
throw new ParseException(String.format("This is an unexpected line from %s: %s%n", getSayExecutable(), line));
throw new ParseException(String.format("Unexpected line from %s: %s", getSayExecutable(), line));
}

// we don't want the header of the output
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import io.github.jonelo.jAdapterForNativeTTS.engines.SpeechEngineAbstract;
import io.github.jonelo.jAdapterForNativeTTS.engines.Voice;
import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.ParseException;
import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.SpeechEngineCreationException;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -46,6 +47,10 @@ public class SpeechEngineMacOS extends SpeechEngineAbstract {
// female names from say in macOS 10.13, 10.14, 10.15, 11, and 12
"Alva,Agnes,Alice,Allison,Andrea,Angelica,Anna,Amelie,Aurelie,Ava,Catarina,Carmit,Chantal,Claire,Damayanti,Ellen,Ewa,Fiona,Frederica,Ioana,Iveta,Joana,Kanya,Karen,Kate,Kathy,Katja,Klara,Kyoko,Laila,Laura,Lekha,Luciana,Mariska,Milena,Mei-Jia,Melina,Moira,Monica,Nora,Paola,Paulina,Petra,Princess,Samantha,Sara,Satu,Serena,Sin-ji,Soledad,Susan,Tessa,Ting-Ting,Veena,Vicki,Victoria,Yelda,Yuna,Zosia,Zuzana,";

public SpeechEngineMacOS() throws SpeechEngineCreationException {
super();
}

public String getSayExecutable() {
return "say";
}
Expand Down Expand Up @@ -97,7 +102,7 @@ public Voice parse(String line) throws ParseException {
voice.setDescription(String.format("%s (%s, %s)", name, culture, gender));
return voice;
} else {
throw new ParseException(String.format("This is an unexpected line from %s: %s%n", getSayExecutable(), line));
throw new ParseException(String.format("Unexpected line from %s: %s", getSayExecutable(), line));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import io.github.jonelo.jAdapterForNativeTTS.engines.SpeechEngineAbstract;
import io.github.jonelo.jAdapterForNativeTTS.engines.Voice;
import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.ParseException;
import io.github.jonelo.jAdapterForNativeTTS.engines.exceptions.SpeechEngineCreationException;

public class SpeechEngineWindows extends SpeechEngineAbstract {

Expand All @@ -53,6 +54,9 @@ public class SpeechEngineWindows extends SpeechEngineAbstract {
"ConvertTo-Csv -NoTypeInformation | ",
"Select-Object -Skip 1;");

public SpeechEngineWindows() throws SpeechEngineCreationException {
super();
}

public String getSayExecutable() {
return "PowerShell";
Expand All @@ -73,7 +77,6 @@ public String[] getSayOptionsToSayText(String text) {
return new String[]{"-Command", String.join("", QUOTE, code, QUOTE)};
}


public String[] getSayOptionsToGetSupportedVoices() {
return new String[]{"-Command", String.join("", QUOTE, POWER_SHELL_CODE_SUPPORTED_VOICES, QUOTE)};
}
Expand All @@ -87,7 +90,7 @@ public Voice parse(String csvLine) throws ParseException {

//List<String> tokens = CSVParser.parseLine(csvLine);
if (tokens.length != 5) {
throw new ParseException("This is an invalid csv line: " + csvLine);
throw new ParseException(String.format("Invalid csv line: %s", csvLine));
}

Voice voice = new Voice();
Expand Down
Loading

0 comments on commit f882f7c

Please sign in to comment.