Skip to content

Commit 74d8268

Browse files
baike960boltomli
authored andcommitted
Add CustomVoiceApi Java sample (#114)
* Add Java Samples * Fix obvious format and grammar issues * Manage all dependencies using Maven. Rewrote README.md
1 parent 902fed8 commit 74d8268

File tree

118 files changed

+44725
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+44725
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## Custom Voice API
2+
3+
This project contains samples of HTTP-based Microsoft Custom Voice Synthesis APIs.
4+
5+
## Build the samples
6+
7+
1. First, you must obtain a standard (not free) Speech API subscription key by following instructions in Microsoft Cognitive Services subscription.
8+
2. Make sure that you have Maven installed. Under the directory with the pom file, run the command 'mvn clean install' to install all project dependencies.
9+
3. Run the VoiceSynthsisMain.java file. You need to pass in arguments of endpoint, ibizaStsUrl, subscriptionKey, localInputTextFile, locale, voiceName and concatenateResult based on the information of the region, your subscription key, your designated voice and your desirable format of output wav file.
10+
11+
## Note:
12+
13+
1.The input text file should be Unicode format with 'UTF-8-BOM' (you can check the text format with your preferred advanced text editor), like the one zh-CN.txt, and should be more than 50 lines.
14+
2.The voiceId could be acquired from the function getVoiceId() in the java file.
15+
3.'concatenateResult' is an optional parameter, if not given, the output will be multiple wave files per line.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
This is the waistline , and it's falling .
2+
Alexis , meet Bill and Hillary , and the rest of America .
3+
This is Jordan , Scottie Pippen and the ring dynasty .
4+
The more I looked , the gloomier I got .
5+
He saw Macbeth , the three witches , and the boiling cauldron .
6+
But it is good to get together , cook together , eat together and enjoy .
7+
The priorities , they say , are sofa , blanket , and clothing .
8+
The metallic walls curve , twist , and turn .
9+
The seagrass fiber is tough , durable and smooth .
10+
The dissenters were Stevens , Souter , Ginsburg and Breyer .
11+
They're also seeing Casey Martin , the golfer .
12+
Lawrence Weschler , staff writer , The New Yorker .
13+
And Ronaldo has his dedications to fulfill .
14+
It's very troubling to me and it should be very troubling to all Americans .
15+
He has been superb , and nothing changed early in the game .
16+
The smell of gunsmoke and excrement drifted on the air .
17+
The dissenters were Breyer , Stevens , Ginsburg and Souter .
18+
These include fur hats , steel helmets , caps and other badges , and watches .
19+
The background music and the special decor complete the Latin theme .
20+
The resident female retorts in kind , piping and pointing .
21+
I take one pillowcase , and stuff the other pillowcase and both sheets into it .
22+
The same two crops , and blueberries , are threatened in Georgia .
23+
The coastline boasts cliffs , caves , bays , and beaches .
24+
The Bash began in baseball , and it went everywhere .
25+
Less fit letters are not forwarded , and so die .
26+
The hijackers had gained entry to the cockpit , and she did not know how .
27+
They engage in all activities .
28+
It's miserable , the cigarette business .
29+
I think it is ridiculous , and I think it's unfair , and I think it's offensive .
30+
People are born , live , and die on the sidewalks .
31+
I'm enchanted by the tenants of Buddhism , like reincarnation and the development of the mind .
32+
Like other visitors , he is a believer .
33+
The biggest winners are the airlines .
34+
He controls references to time , place , and location .
35+
The casino is in a jurisdiction that allows it , and the money is in a jurisdiction that allows it .
36+
But his weight lifting , bicycling and horseback riding have kept him taut and ready for all comers .
37+
It's gross during the summer .
38+
Sometimes the bodies are never identified .
39+
It was Enron, WorldCom and Global Crossing .
40+
The rotation is solid , defense is solid , the lineup's solid , the bullpen's solid .
41+
That helped push crude oil prices higher .
42+
The farm is in the Catskills , near Worcester , New York .
43+
No , none whatsoever , and that shopkeeper was right .
44+
She knows everything and everyone in the town , literally .
45+
In response , a rock grew taller , pushing the children higher , and higher .
46+
The industry has been cannibalizing itself .
47+
The chiffon stretch , the chiffon silk , wool .
48+
We thank you for your consideration , and look forward to hearing from you .
49+
Like Philip Barry , Mike , the reporter , is an outsider .
50+
The language in the settlement is unacceptable .
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<groupId>voiceSynthesis</groupId>
6+
<artifactId>voiceSynthesis-java</artifactId>
7+
<version>0.0.1-SNAPSHOT</version>
8+
<packaging>jar</packaging>
9+
10+
<name>voiceSynthesis-java</name>
11+
<url>http://maven.apache.org</url>
12+
13+
<properties>
14+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15+
<maven.compiler.source>1.7</maven.compiler.source>
16+
<maven.compiler.target>1.7</maven.compiler.target>
17+
</properties>
18+
19+
20+
<dependencies>
21+
<dependency>
22+
<groupId>junit</groupId>
23+
<artifactId>junit</artifactId>
24+
<version>4.12</version>
25+
<scope>test</scope>
26+
</dependency>
27+
<dependency>
28+
<groupId>com.squareup.okhttp</groupId>
29+
<artifactId>logging-interceptor</artifactId>
30+
<version>2.7.5</version>
31+
</dependency>
32+
<dependency>
33+
<groupId>com.squareup.okhttp</groupId>
34+
<artifactId>okhttp</artifactId>
35+
<version>2.7.5</version>
36+
</dependency>
37+
<dependency>
38+
<groupId>com.squareup.okio</groupId>
39+
<artifactId>okio</artifactId>
40+
<version>1.6.0</version>
41+
</dependency>
42+
<dependency>
43+
<groupId>org.json</groupId>
44+
<artifactId>json</artifactId>
45+
<version>20180813</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>io.swagger</groupId>
49+
<artifactId>swagger-annotations</artifactId>
50+
<version>1.5.18</version>
51+
</dependency>
52+
<dependency>
53+
<groupId>org.threeten</groupId>
54+
<artifactId>threetenbp</artifactId>
55+
<version>1.3.5</version>
56+
</dependency>
57+
<dependency>
58+
<groupId>commons-io</groupId>
59+
<artifactId>commons-io</artifactId>
60+
<version>2.6</version>
61+
</dependency>
62+
<dependency>
63+
<groupId>com.google.code.gson</groupId>
64+
<artifactId>gson</artifactId>
65+
<version>2.8.1</version>
66+
</dependency>
67+
<dependency>
68+
<groupId>io.gsonfire</groupId>
69+
<artifactId>gson-fire</artifactId>
70+
<version>1.8.0</version>
71+
</dependency>
72+
<dependency>
73+
<groupId>org.hamcrest</groupId>
74+
<artifactId>hamcrest-core</artifactId>
75+
<version>1.3</version>
76+
<scope>test</scope>
77+
</dependency>
78+
79+
</dependencies>
80+
81+
</project>
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import java.io.BufferedInputStream;
2+
import java.io.File;
3+
import java.io.FileOutputStream;
4+
import java.io.IOException;
5+
import java.net.MalformedURLException;
6+
import java.net.URI;
7+
import java.net.URL;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.Scanner;
11+
import java.util.UUID;
12+
13+
import org.json.JSONException;
14+
import org.json.JSONObject;
15+
16+
import io.swagger.client.ApiException;
17+
import io.swagger.client.ApiResponse;
18+
import io.swagger.client.api.VoiceSynthesisApi;
19+
import io.swagger.client.model.Voice;
20+
import io.swagger.client.model.VoiceSynthesis;
21+
import io.swagger.client.model.VoiceSynthesis.StatusEnum;
22+
import com.squareup.okhttp.Response;
23+
24+
public class VoiceSynthesisMain {
25+
26+
//Cognitive service link
27+
//https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/rest-apis#authentication
28+
29+
public static void main(String[] args) throws ApiException, IOException, InterruptedException, JSONException {
30+
if(args.length<7) {
31+
System.out.println("Not enough arguments. Expected number of arguments is 7.\n"
32+
+ "endpoint: i.e. https://centralindia.cris.ai \n"
33+
+ "ibizaStsUrl: i.e. https://centralindia.api.cognitive.microsoft.com/sts/v1.0/issueToken \n"
34+
+ "subscriptionKey: a standard one acquired from Azure \n"
35+
+ "localInputTextFile: i.e. \\Java project\\VoiceSynthsisAPIToJAVA\\en-US.txt \n"
36+
+ "locale: i.e. it-IT \n"
37+
+ "voiceName: i.e. ElsaNeural \n"
38+
+ "concatenateResult: true or false. Whether you want the output file to be one part or multipart. \n"
39+
+ "Program exited.");
40+
return;
41+
}
42+
String endpoint = args[0];
43+
String ibizaStsUrl = args[1];
44+
// the Subscription key should be a standard one, not the free one.
45+
String subscriptionKey = args[2];
46+
// The input text file could contains only plain text or only SSML or mixed together(as shown in blow script)
47+
// The input text file encoding format should be UTF-8-BOM
48+
// The input text file should contains at least 50 lines of text
49+
String localInputTextFile = args[3];
50+
51+
String locale = args[4];
52+
String voiceName = args[5];
53+
if(!new File(localInputTextFile).exists()) {
54+
System.out.println("Input input text file does not exist. Program exited.");
55+
return;
56+
}
57+
58+
// indicate if want concatenate the output waves with a single file or not. True or false
59+
String concatenateResult = args[6];
60+
VoiceSynthsisAPIs(endpoint, ibizaStsUrl, subscriptionKey, localInputTextFile, locale, voiceName, concatenateResult);
61+
62+
}
63+
64+
65+
private static void VoiceSynthsisAPIs(String endpoint, String ibizaStsUrl, String subscriptionKey, String
66+
localInputTextFile, String locale, String voiceName, String concatenateResult)
67+
throws ApiException, IOException, JSONException, InterruptedException
68+
{
69+
70+
71+
72+
final String name = "Simple neural TTS batch synthesis";
73+
final String description = "Simple neural TTS batch synthesis description";
74+
75+
// public voice means the voice could be used by all Subscriptions, if the voice is private(for your Subscription only), this should be set to false
76+
boolean isPublicVoice = true;
77+
78+
// you can directly set the voiceId or query the voice information by name/locale/ispublic properties from server.
79+
//var voiceId = new Guid("Your voice model Guid");
80+
81+
VoiceSynthesisApi voiceApi=new VoiceSynthesisApi(subscriptionKey, endpoint);
82+
83+
UUID voiceId = GetVoiceId( voiceApi, locale, voiceName, isPublicVoice);
84+
85+
if (voiceId.getLeastSignificantBits()==0L && voiceId.getMostSignificantBits()==0L)
86+
{
87+
System.out.println("Does not have a available voice for locale :"+locale+ ", name : "+voiceName+", public : "+isPublicVoice);
88+
return;
89+
}
90+
91+
File file=new File(localInputTextFile);
92+
// Submit a voice synthesis request and get a ID
93+
//URI synthesisLocation = customVoiceAPI.CreateVoiceSynthesis(name, description, locale, localInputTextFile, voiceId, concatenateResult);
94+
95+
JSONObject properties = new JSONObject();
96+
97+
properties.put("ConcatenateResult", concatenateResult);
98+
99+
Response res=voiceApi.NewcreateVoiceSynthesisWithHttpInfo(name, description, locale, voiceId.toString(), properties.toString(),file);
100+
List<String> synthesisLocation=res.headers("Location");
101+
if(synthesisLocation==null ||synthesisLocation.size()==0) {
102+
System.out.println("No synthesis location returned from server. Program exited.");
103+
}
104+
String[] seq=synthesisLocation.get(0).toString().split("/");
105+
UUID synthesisId = UUID.fromString(seq.length>0?seq[seq.length-1]:"");
106+
107+
System.out.println("Checking status.");
108+
// check for the status of the submitted synthesis every 10 sec. (can also be 1, 2, 5 min depending on usage)
109+
boolean completed = false;
110+
while (!completed)
111+
{
112+
//var synthesis = customVoiceAPI.s(synthesisId);
113+
VoiceSynthesis synthesis=voiceApi.getVoiceSynthesis(synthesisId);
114+
115+
switch (synthesis.getStatus().toString())
116+
{
117+
case "Failed":
118+
case "Succeeded":
119+
completed = true;
120+
// if the synthesis was successfull, download the results to local
121+
if (synthesis.getStatus().toString() == "Succeeded")
122+
{
123+
String resultsUri = synthesis.getResultsUrl();
124+
System.out.println(resultsUri);
125+
URL url=new URL(resultsUri);
126+
//WebClient webClient = new WebClient();
127+
//String filename = Path.GetTempFileName()+"_"+synthesis.Id+"_.zip";
128+
//webClient.DownloadFile(resultsUri, filename);
129+
130+
File filename=File.createTempFile("_"+synthesis.getId()+"_", ".zip");
131+
try (BufferedInputStream inputStream = new BufferedInputStream(url.openStream());
132+
FileOutputStream fileOS = new FileOutputStream(filename)) {
133+
byte data[] = new byte[1024];
134+
int byteContent;
135+
while ((byteContent = inputStream.read(data, 0, 1024)) != -1) {
136+
fileOS.write(data, 0, byteContent);
137+
}
138+
} catch (IOException e) {
139+
// handles IO exceptions
140+
}
141+
System.out.println("Synthesis succeeded. Results: "+filename);
142+
}
143+
break;
144+
145+
case "Running":
146+
break;
147+
148+
case "NotStarted":
149+
break;
150+
}
151+
152+
System.out.println("Syntheses status: "+ synthesis.getStatus());
153+
//await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
154+
Thread.sleep(10*1000L);
155+
}
156+
157+
System.out.println("Press any key...");
158+
Scanner input=new Scanner(System.in);
159+
input.nextLine();
160+
}
161+
162+
private static UUID GetVoiceId( VoiceSynthesisApi voiceApi, String locale, String voiceName, boolean publicVoice) throws ApiException
163+
{
164+
// Get available voices list
165+
//VoiceSynthesisApi voiceApi=new VoiceSynthesisApi();
166+
Iterable<Voice> voices = voiceApi.getSupportedVoicesForVoiceSynthesis();
167+
Voice voice = null;
168+
List<Voice> list=new ArrayList<>();
169+
170+
171+
if (publicVoice)
172+
{
173+
for(Voice v:voices)
174+
{
175+
if( v.getName().contains(voiceName) &&v.getLocale().equals(locale) && v.isIsPublicVoice())
176+
list.add(v);
177+
}
178+
voice=(list.size()>0)?list.get(0):null;
179+
180+
}
181+
else
182+
{
183+
for(Voice v:voices)
184+
{
185+
if(v.getLocale().equals(locale) && v.getName().contains(voiceName))
186+
list.add(v);
187+
}
188+
voice=(list.size()>0)?list.get(0):null;
189+
}
190+
if (voice == null)
191+
{
192+
System.out.println("Does not have a available voice for locale : "+locale+", name : "+ voiceName+", public : "+publicVoice);
193+
return new UUID( 0L , 0L );
194+
}
195+
return voice.getId();
196+
}
197+
198+
199+
200+
201+
}

0 commit comments

Comments
 (0)