Skip to content

Commit 3171ec0

Browse files
committed
Bit of work toward making this usable for twitch
1 parent 21a20f9 commit 3171ec0

8 files changed

Lines changed: 293 additions & 101 deletions

File tree

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package simplexity.clitts;
2+
3+
import com.amazonaws.ClientConfiguration;
4+
import com.amazonaws.auth.BasicAWSCredentials;
5+
import com.amazonaws.regions.Region;
6+
import com.amazonaws.services.polly.AmazonPolly;
7+
import com.amazonaws.services.polly.AmazonPollyClient;
8+
import com.amazonaws.services.polly.model.OutputFormat;
9+
import com.amazonaws.services.polly.model.SynthesizeSpeechRequest;
10+
import com.amazonaws.services.polly.model.SynthesizeSpeechResult;
11+
import com.amazonaws.services.polly.model.VoiceId;
12+
import javazoom.jl.decoder.JavaLayerException;
13+
import javazoom.jl.player.advanced.AdvancedPlayer;
14+
import simplexity.clitts.util.Colors;
15+
16+
import java.io.InputStream;
17+
18+
public class SpeechHandling {
19+
20+
static Region AWS_REGION;
21+
static AmazonPolly polly;
22+
static VoiceId VOICE_ID;
23+
24+
private SpeechHandling() {
25+
polly = new AmazonPollyClient(new BasicAWSCredentials(TTSConfig.getInstance().getAccessID(), TTSConfig.getInstance().getAccessSecret()), new ClientConfiguration());
26+
polly.setRegion(AWS_REGION);
27+
}
28+
29+
private static SpeechHandling instance;
30+
31+
public static SpeechHandling getInstance() {
32+
if (instance == null) instance = new SpeechHandling();
33+
return instance;
34+
}
35+
36+
public void processSpeech(String text) {
37+
TextToSpeech tts = TextToSpeech.getInstance();
38+
String newText = replaceText(text);
39+
boolean useSSML = !text.equals(newText);
40+
try {
41+
InputStream speechStream;
42+
if (!useSSML) {
43+
speechStream = synthesizeSpeech(polly, newText, VOICE_ID);
44+
} else {
45+
speechStream = synthesizeSSMLSpeech(polly, newText, VOICE_ID);
46+
}
47+
if (speechStream == null) {
48+
System.out.println(Colors.boldRed + "Error: Speech stream is null." + Colors.formatReset);
49+
return;
50+
}
51+
AdvancedPlayer player = new AdvancedPlayer(speechStream,
52+
javazoom.jl.player.FactoryRegistry.systemRegistry().createAudioDevice());
53+
player.play();
54+
} catch (JavaLayerException e) {
55+
System.out.println(Colors.boldRed + "Error playing speech. " + e + Colors.formatReset);
56+
}
57+
}
58+
59+
public String replaceText(String text) {
60+
for (String key : TTSConfig.getInstance().getReplaceText().keySet()) {
61+
text = text.replace(key, TTSConfig.getInstance().getReplaceText().get(key));
62+
}
63+
for (String key : TTSConfig.getInstance().getVoicePrefixes().keySet()) {
64+
if (text.startsWith(key)) {
65+
text = text.replace(key, "");
66+
SpeechHandling.VOICE_ID = TTSConfig.getInstance().getVoicePrefixes().get(key);
67+
}
68+
}
69+
return text;
70+
}
71+
72+
public InputStream synthesizeSSMLSpeech(AmazonPolly polly, String text, VoiceId voice) {
73+
String ssml = "<speak>" + text + "</speak>";
74+
SynthesizeSpeechRequest synthesizeSpeechRequest;
75+
try {
76+
synthesizeSpeechRequest = new SynthesizeSpeechRequest()
77+
.withText(ssml)
78+
.withTextType(com.amazonaws.services.polly.model.TextType.Ssml)
79+
.withVoiceId(voice)
80+
.withOutputFormat(OutputFormat.Mp3);
81+
SynthesizeSpeechResult synthesizeSpeechResult = polly.synthesizeSpeech(synthesizeSpeechRequest);
82+
return synthesizeSpeechResult.getAudioStream();
83+
} catch (RuntimeException e) {
84+
System.out.println(Colors.boldRed + "Error: " + e.getMessage() + Colors.formatReset);
85+
return null;
86+
}
87+
}
88+
89+
public InputStream synthesizeSpeech(AmazonPolly polly, String text, VoiceId voice) {
90+
SynthesizeSpeechRequest synthesizeSpeechRequest = new SynthesizeSpeechRequest()
91+
.withText(text)
92+
.withVoiceId(voice)
93+
.withOutputFormat(OutputFormat.Mp3);
94+
SynthesizeSpeechResult synthesizeSpeechResult = polly.synthesizeSpeech(synthesizeSpeechRequest);
95+
return synthesizeSpeechResult.getAudioStream();
96+
}
97+
}

TTSJava/src/main/java/simplexity/clitts/TTSConfig.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import com.amazonaws.services.polly.model.VoiceId;
66
import com.typesafe.config.Config;
77
import com.typesafe.config.ConfigFactory;
8+
import com.typesafe.config.ConfigValue;
9+
import com.typesafe.config.ConfigValueFactory;
810

911
import java.io.File;
1012
import java.io.FileWriter;
@@ -17,7 +19,8 @@ public class TTSConfig {
1719
private final HashMap<String, VoiceId> voicePrefixes = new HashMap<>();
1820
private Region AWS_REGION;
1921
private VoiceId defaultVoice;
20-
private String accessID, accessSecret;
22+
private boolean useTwitch = false;
23+
private String accessID, accessSecret, twitchChannel, twitchAuthCode;
2124

2225
private TTSConfig() {
2326
}
@@ -36,6 +39,7 @@ public void reloadConfig() {
3639
reloadRegion(config);
3740
reloadDefaultVoice(config);
3841
reloadAccessStrings(config);
42+
reloadTwitchChannel(config);
3943
}
4044

4145
private Config loadConfig() {
@@ -53,6 +57,9 @@ private void createDefaultConfigFile(File file) {
5357
writer.write("default-voice = \"Kimberly\"\n");
5458
writer.write("aws-access-id = \"\"\n");
5559
writer.write("aws-access-secret = \"\"\n");
60+
writer.write("twitch-channel = \"\"\n");
61+
writer.write("twitch-auth-code = \"\"\n");
62+
writer.write("connect-to-twitch = false\n");
5663
writer.write("""
5764
replace-text {
5865
"**" = "<prosody volume=\\"x-loud\\" pitch=\\"low\\" rate=\\"slow\\">"
@@ -143,6 +150,21 @@ private void reloadAccessStrings(Config config) {
143150
accessSecret = config.getString("aws-access-secret");
144151
}
145152

153+
private void reloadTwitchChannel(Config config) {
154+
twitchChannel = config.getString("twitch-channel");
155+
if (!twitchChannel.isEmpty()) {
156+
useTwitch = config.getBoolean("connect-to-twitch");
157+
}
158+
}
159+
160+
public String getTwitchChannel() {
161+
return twitchChannel;
162+
}
163+
164+
public boolean connectToTwitch() {
165+
return useTwitch;
166+
}
167+
146168
public HashMap<String, String> getReplaceText() {
147169
return replaceText;
148170
}
@@ -167,5 +189,16 @@ public String getAccessID() {
167189
public String getAccessSecret() {
168190
return accessSecret;
169191
}
192+
public String getTwitchAuthCode() {
193+
return twitchAuthCode;
194+
}
195+
196+
public void setTwitchAuthCode(String authCode) {
197+
twitchAuthCode = authCode;
198+
Config config = loadConfig();
199+
ConfigValue twitchCode = ConfigValueFactory.fromAnyRef(authCode);
200+
config.withValue("twitch-auth-code", twitchCode);
201+
}
202+
170203

171204
}
Lines changed: 55 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,17 @@
11
package simplexity.clitts;
22

3-
import com.amazonaws.ClientConfiguration;
4-
import com.amazonaws.auth.BasicAWSCredentials;
5-
import com.amazonaws.regions.Region;
6-
import com.amazonaws.services.polly.AmazonPolly;
7-
import com.amazonaws.services.polly.AmazonPollyClient;
8-
import com.amazonaws.services.polly.model.OutputFormat;
9-
import com.amazonaws.services.polly.model.SynthesizeSpeechRequest;
10-
import com.amazonaws.services.polly.model.SynthesizeSpeechResult;
11-
import com.amazonaws.services.polly.model.VoiceId;
12-
import javazoom.jl.decoder.JavaLayerException;
13-
import javazoom.jl.player.advanced.AdvancedPlayer;
3+
import simplexity.clitts.twitch.TwitchAuthServer;
4+
import simplexity.clitts.util.Messages;
145

15-
import java.io.InputStream;
6+
import java.io.IOException;
167
import java.util.Scanner;
178

189
public class TextToSpeech {
1910

20-
private static Region AWS_REGION;
11+
2112
private static final Scanner scanner = new Scanner(System.in);
22-
private static AmazonPolly polly;
23-
private static VoiceId VOICE_ID;
2413
private static boolean runProgram = true;
2514
private static TextToSpeech instance;
26-
private static final String boldRed = "\u001b[31;1m";
27-
private static final String formatReset = "\u001b[0m";
28-
private static final String boldGreen = "\u001b[32;1m";
29-
private static final String cyan = "\u001b[36m";
30-
private static final String yellow = "\u001b[33m";
31-
32-
public TextToSpeech() {
33-
polly = new AmazonPollyClient(new BasicAWSCredentials(TTSConfig.getInstance().getAccessID(), TTSConfig.getInstance().getAccessSecret()), new ClientConfiguration());
34-
polly.setRegion(AWS_REGION);
35-
}
3615

3716
public static TextToSpeech getInstance() {
3817
if (instance == null) {
@@ -41,102 +20,78 @@ public static TextToSpeech getInstance() {
4120
return instance;
4221
}
4322

44-
public InputStream synthesizeSpeech(AmazonPolly polly, String text, VoiceId voice) {
45-
SynthesizeSpeechRequest synthesizeSpeechRequest = new SynthesizeSpeechRequest()
46-
.withText(text)
47-
.withVoiceId(voice)
48-
.withOutputFormat(OutputFormat.Mp3);
49-
SynthesizeSpeechResult synthesizeSpeechResult = polly.synthesizeSpeech(synthesizeSpeechRequest);
50-
return synthesizeSpeechResult.getAudioStream();
51-
}
52-
53-
public InputStream synthesizeSSMLSpeech(AmazonPolly polly, String text, VoiceId voice) {
54-
String ssml = "<speak>" + text + "</speak>";
55-
SynthesizeSpeechRequest synthesizeSpeechRequest;
56-
try {
57-
synthesizeSpeechRequest = new SynthesizeSpeechRequest()
58-
.withText(ssml)
59-
.withTextType(com.amazonaws.services.polly.model.TextType.Ssml)
60-
.withVoiceId(voice)
61-
.withOutputFormat(OutputFormat.Mp3);
62-
SynthesizeSpeechResult synthesizeSpeechResult = polly.synthesizeSpeech(synthesizeSpeechRequest);
63-
return synthesizeSpeechResult.getAudioStream();
64-
} catch (RuntimeException e) {
65-
System.out.println(boldRed + "Error: " + e.getMessage() + formatReset);
66-
return null;
67-
}
68-
}
69-
70-
public String replaceText(String text) {
71-
for (String key : TTSConfig.getInstance().getReplaceText().keySet()) {
72-
text = text.replace(key, TTSConfig.getInstance().getReplaceText().get(key));
73-
}
74-
for (String key : TTSConfig.getInstance().getVoicePrefixes().keySet()) {
75-
if (text.startsWith(key)) {
76-
text = text.replace(key, "");
77-
VOICE_ID = TTSConfig.getInstance().getVoicePrefixes().get(key);
78-
}
79-
}
80-
return text;
81-
}
82-
83-
public static void processSpeech(String text) {
84-
TextToSpeech tts = getInstance();
85-
String newText = tts.replaceText(text);
86-
boolean useSSML = !text.equals(newText);
87-
try {
88-
InputStream speechStream;
89-
if (!useSSML) {
90-
speechStream = tts.synthesizeSpeech(polly, newText, VOICE_ID);
91-
} else {
92-
speechStream = tts.synthesizeSSMLSpeech(polly, newText, VOICE_ID);
93-
}
94-
if (speechStream == null) {
95-
System.out.println(boldRed + "Error: Speech stream is null." + formatReset);
96-
return;
97-
}
98-
AdvancedPlayer player = new AdvancedPlayer(speechStream,
99-
javazoom.jl.player.FactoryRegistry.systemRegistry().createAudioDevice());
100-
player.play();
101-
} catch (JavaLayerException e) {
102-
System.out.println(boldRed + "Error playing speech. " + e + formatReset);
103-
}
104-
}
105-
10623
public static void main(String[] args) {
10724
TTSConfig.getInstance().reloadConfig();
108-
System.out.println("Type your text then press" + cyan + " Enter" + formatReset + " to convert to speech."
109-
+ "\nType" + cyan + " --reload" + formatReset + " to reload the configuration."
110-
+ "\nType " + cyan + "--exit" + formatReset + " to end the program.");
111-
VOICE_ID = TTSConfig.getInstance().getDefaultVoice();
112-
AWS_REGION = TTSConfig.getInstance().getRegion();
25+
System.out.println(Messages.startupMessage);
26+
SpeechHandling.VOICE_ID = TTSConfig.getInstance().getDefaultVoice();
27+
SpeechHandling.AWS_REGION = TTSConfig.getInstance().getRegion();
28+
checkTwitch();
11329
while (runProgram) {
114-
System.out.println(yellow + "Enter text:" + formatReset);
30+
System.out.println(Messages.enterText);
11531
String text = scanner.nextLine();
11632
switch (text) {
11733
case ("--exit") -> {
11834
runProgram = false;
119-
System.out.println("Program ended.");
35+
System.out.println(Messages.exitMessage);
12036
}
121-
case ("--help") -> System.out.println("Type your text, press Enter to convert to speech. " +
122-
"Type '--exit' to end the program.");
37+
case ("--help") -> System.out.println(Messages.helpMessage);
12338
case ("--reload") -> {
12439
TTSConfig.getInstance().reloadConfig();
125-
System.out.println(boldGreen + "Config Reloaded" + formatReset);
40+
System.out.println(Messages.reloadMessage);
12641
}
12742
default -> {
12843
if (TTSConfig.getInstance().getAccessID().isBlank() || TTSConfig.getInstance().getAccessID().isEmpty()) {
129-
System.out.println(boldRed + "AWS Access ID is not set. Please set your AWS Access ID and Access secret then use '--reload'" + formatReset);
44+
System.out.println(Messages.secretOrIDNotSet);
13045
continue;
13146
}
13247
if (TTSConfig.getInstance().getAccessSecret().isBlank() || TTSConfig.getInstance().getAccessSecret().isEmpty()) {
133-
System.out.println(boldRed + "AWS Access Secret is not set. Please set your AWS Access ID and Access secret then use '--reload'" + formatReset);
48+
System.out.println(Messages.secretOrIDNotSet);
13449
continue;
13550
}
136-
processSpeech(text);
51+
SpeechHandling.getInstance().processSpeech(text);
52+
}
53+
}
54+
}
55+
}
56+
57+
private static void checkTwitch() {
58+
if (TTSConfig.getInstance().connectToTwitch()) {
59+
String channel = TTSConfig.getInstance().getTwitchChannel();
60+
System.out.println(Messages.currentlyConnectedTo + channel + Messages.stayConnectedPrompt);
61+
boolean validInput = false;
62+
while (!validInput) {
63+
String input = scanner.nextLine();
64+
input = input.toLowerCase();
65+
switch (input) {
66+
case ("y") -> {
67+
validInput = true;
68+
checkTwitchAuth();
69+
70+
}
71+
case ("n") -> {
72+
validInput = true;
73+
}
74+
default -> System.out.println(Messages.stayConnectedError);
13775
}
13876

13977
}
14078
}
14179
}
80+
81+
private static void checkTwitchAuth() {
82+
if (TTSConfig.getInstance().connectToTwitch()) {
83+
String authCode = TTSConfig.getInstance().getTwitchAuthCode();
84+
if (authCode == null || authCode.isBlank()) {
85+
System.out.println(Messages.needTwitchAuth);
86+
System.out.println(Messages.visitLink + Messages.twitchAuthLink);
87+
try {
88+
TwitchAuthServer.run();
89+
} catch (IOException e) {
90+
System.out.println(Messages.twitchAuthError);
91+
e.printStackTrace();
92+
}
93+
}
94+
}
95+
}
96+
14297
}

0 commit comments

Comments
 (0)