I am using the Google API Text-To-Speech and I would like to simply hear "Hello World".
This is what I have so far:
/** Demonstrates using the Text-to-Speech API. */
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void hello() throws Exception {
InputStream stream = getResources().openRawResource(R.raw.credential); // R.raw.credential is credential.json
GoogleCredentials credentials = GoogleCredentials.fromStream(stream);
TextToSpeechSettings textToSpeechSettings =
TextToSpeechSettings.newBuilder()
.setCredentialsProvider(
FixedCredentialsProvider.create(credentials)
).build()
;
// Instantiates a client
try (TextToSpeechClient textToSpeechClient = TextToSpeechClient.create(textToSpeechSettings)) {
// Set the text input to be synthesized
SynthesisInput input = SynthesisInput.newBuilder().setText("Hello, World!").build();
// Build the voice request, select the language code ("en-US") and the ssml voice gender
// ("neutral")
VoiceSelectionParams voice =
VoiceSelectionParams.newBuilder()
.setLanguageCode("en-US")
.setSsmlGender(SsmlVoiceGender.NEUTRAL)
.build();
// Select the type of audio file you want returned
AudioConfig audioConfig =
AudioConfig.newBuilder().setAudioEncoding(AudioEncoding.MP3).build();
// Perform the text-to-speech request on the text input with the selected voice parameters and
// audio file type
SynthesizeSpeechResponse response =
textToSpeechClient.synthesizeSpeech(input, voice, audioConfig);
// Get the audio contents from the response
ByteString audioContents = response.getAudioContent();
// Write the response to the output file.
try (OutputStream out = new FileOutputStream("output.mp3")) {
out.write(audioContents.toByteArray());
System.out.println("Audio content written to file \"output.mp3\"");
}
}
}
I get the error:
java.io.FileNotFoundException: output.mp3 (Read-only file system)
Most of the codes I have copied from Google's documentation but I don't even want to save that audio in a file. The text "Hello, World!" should simply be played without being saved first. Is this possible?
Related
I am using SSML, so my app can speak. The app itself works perfectly fine on my phone BUT when I connect my phone with a device over Bluetooth, there is mostly a gap or a delay. Either at the beginning or in the middle of the speech.
So for instance, when the audio is Hello John, I am your assistant. How can I help you?, the output could be sistant. How can I help you?. Sometimes the sentences are fluent but sometimes there are these gaps.
This is how I play the audio file:
String myFile = context.getFilesDir() + "/output.mp3";
mMediaPlayer.reset();
mMediaPlayer.setDataSource(myFile);
mMediaPlayer.prepare();
mMediaPlayer.start();
And this is the entire class of it:
public class Tts {
public Context context;
private final MediaPlayer mMediaPlayer;
public Tts(Context context, MediaPlayer mMediaPlayer) {
this.context = context;
this.mMediaPlayer = mMediaPlayer;
}
#SuppressLint({"NewApi", "ResourceType", "UseCompatLoadingForColorStateLists"})
public void say(String text) throws Exception {
InputStream stream = context.getResources().openRawResource(R.raw.credential); // R.raw.credential is credential.json
GoogleCredentials credentials = GoogleCredentials.fromStream(stream);
TextToSpeechSettings textToSpeechSettings =
TextToSpeechSettings.newBuilder()
.setCredentialsProvider(
FixedCredentialsProvider.create(credentials)
).build();
// Instantiates a client
try (TextToSpeechClient textToSpeechClient = TextToSpeechClient.create(textToSpeechSettings)) {
// Replace {name} with target
SharedPreferences sharedPreferences = context.getSharedPreferences("target", Context.MODE_PRIVATE);
String target = sharedPreferences.getString("target", null);
text = (target != null) ? text.replace("{name}", target) : text.replace("null", "");
// Set the text input to be synthesized
String myString = "<speak><prosody pitch=\"low\">" + text + "</prosody></speak>";
SynthesisInput input = SynthesisInput.newBuilder().setSsml(myString).build();
// Build the voice request, select the language code ("en-US") and the ssml voice gender
// ("neutral")
VoiceSelectionParams voice =
VoiceSelectionParams.newBuilder()
.setName("de-DE-Wavenet-E")
.setLanguageCode("de-DE")
.setSsmlGender(SsmlVoiceGender.MALE)
.build();
// Select the type of audio file you want returned
AudioConfig audioConfig =
AudioConfig.newBuilder().setAudioEncoding(AudioEncoding.MP3).build();
// Perform the text-to-speech request on the text input with the selected voice parameters and
// audio file type
SynthesizeSpeechResponse response = textToSpeechClient.synthesizeSpeech(input, voice, audioConfig);
// Get the audio contents from the response
ByteString audioContents = response.getAudioContent();
// Write the response to the output file.
try (FileOutputStream out = new FileOutputStream(context.getFilesDir() + "/output.mp3")) {
out.write(audioContents.toByteArray());
}
String myFile = context.getFilesDir() + "/output.mp3";
mMediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build());
mMediaPlayer.reset();
mMediaPlayer.setDataSource(myFile);
mMediaPlayer.prepare();
mMediaPlayer.setOnPreparedListener(mediaPlayer -> mMediaPlayer.start());
}
}
}
The distance cannot be the reason, since my phone is right next to the device.
Google's SSML needs an internet connection. So I am not quite sure if the gap is because of Bluetooth or internet connection.
So I am trying to close the gap, no matter what the reason is. The audio should be played, when it is prepared and ready to be played.
What I tried
This is what I have tried but I don't hear a difference:
mMediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SPEECH).build());
Instead of mMediaPlayer.prepare(), I also tried it with mMediaPlayer.prepareAsync() but then the audio will not be played (or at least I can't hear it).
Invoking start() in a listener:
mMediaPlayer.setOnPreparedListener(mediaPlayer -> {
mMediaPlayer.start();
});
Unfortunately, the gap is sometimes still there.
Here is my proposed solution. Check out the // *** comments in the code to see what I changed in respect to your code from the question.
Also take it with a grain of salt, because I have no way of testing that right now.
Nevertheless - as far as I can tell - that is all you can do using the MediaPlayer API. If that still doesn't work right for your BlueTooth device, you should try a different BlueTooth device and if that doesn't help either, maybe you can switch the whole thing to use the AudioTrack API instead of MediaPlayer, which gives you a low latency setting and you could use the audio data directly from the response instead of writing it to a file and reading it from there again.
public class Tts {
public Context context;
private final MediaPlayer mMediaPlayer;
public Tts(Context context, MediaPlayer mMediaPlayer) {
this.context = context;
this.mMediaPlayer = mMediaPlayer;
}
#SuppressLint({"NewApi", "ResourceType", "UseCompatLoadingForColorStateLists"})
public void say(String text) throws Exception {
InputStream stream = context.getResources().openRawResource(R.raw.credential); // R.raw.credential is credential.json
GoogleCredentials credentials = GoogleCredentials.fromStream(stream);
TextToSpeechSettings textToSpeechSettings =
TextToSpeechSettings.newBuilder()
.setCredentialsProvider(
FixedCredentialsProvider.create(credentials)
).build();
// Instantiates a client
try (TextToSpeechClient textToSpeechClient = TextToSpeechClient.create(textToSpeechSettings)) {
// Replace {name} with target
SharedPreferences sharedPreferences = context.getSharedPreferences("target", Context.MODE_PRIVATE);
String target = sharedPreferences.getString("target", null);
text = text.replace("{name}", (target != null) ? target : ""); // *** bug fixed
// Set the text input to be synthesized
String myString = "<speak><prosody pitch=\"low\">" + text + "</prosody></speak>";
SynthesisInput input = SynthesisInput.newBuilder().setSsml(myString).build();
// Build the voice request, select the language code ("en-US") and the ssml voice gender
// ("neutral")
VoiceSelectionParams voice =
VoiceSelectionParams.newBuilder()
.setName("de-DE-Wavenet-E")
.setLanguageCode("de-DE")
.setSsmlGender(SsmlVoiceGender.MALE)
.build();
// Select the type of audio file you want returned
AudioConfig audioConfig =
AudioConfig.newBuilder().setAudioEncoding(AudioEncoding.MP3).build();
// Perform the text-to-speech request on the text input with the selected voice parameters and
// audio file type
SynthesizeSpeechResponse response = textToSpeechClient.synthesizeSpeech(input, voice, audioConfig);
// Get the audio contents from the response
ByteString audioContents = response.getAudioContent();
// Write the response to the output file.
try (FileOutputStream out = new FileOutputStream(context.getFilesDir() + "/output.mp3")) {
out.write(audioContents.toByteArray());
}
String myFile = context.getFilesDir() + "/output.mp3";
mMediaPlayer.reset();
mMediaPlayer.setDataSource(myFile);
mMediaPlayer.setAudioAttributes(new AudioAttributes.Builder() // *** moved here (should be done before prepare and very likely AFTER reset)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // *** changed to speech
.setUsage(AudioAttributes.USAGE_ASSISTANT) // *** added
.setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED) // *** added
.build());
mMediaPlayer.prepare();
// *** following line changed since handler was defined AFTER prepare and
// *** the prepare call isn't asynchronous, thus the handler would never be called.
mMediaPlayer.start();
}
}
}
Hope that get's you going!
I have found this code on a Google documentation page (Android Studio changed it a bit automatically):
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static void ssmlToAudio(String ssmlText, String outFile) throws Exception {
// Instantiates a client
try (TextToSpeechClient textToSpeechClient = TextToSpeechClient.create()) {
// Set the ssml text input to synthesize
SynthesisInput input = SynthesisInput.newBuilder().setSsml(ssmlText).build();
// Build the voice request, select the language code ("en-US") and
// the ssml voice gender ("male")
VoiceSelectionParams voice =
VoiceSelectionParams.newBuilder()
.setLanguageCode("en-US")
.setSsmlGender(SsmlVoiceGender.MALE)
.build();
// Select the audio file type
AudioConfig audioConfig =
AudioConfig.newBuilder().setAudioEncoding(AudioEncoding.MP3).build();
// Perform the text-to-speech request on the text input with the selected voice parameters and
// audio file type
SynthesizeSpeechResponse response =
textToSpeechClient.synthesizeSpeech(input, voice, audioConfig);
// Get the audio contents from the response
ByteString audioContents = response.getAudioContent();
// Write the response to the output file
try (OutputStream out = new FileOutputStream(outFile)) {
out.write(audioContents.toByteArray());
System.out.println("Audio content written to file " + outFile);
}
}
}
I would like to run this method on a click event. So this is what I have tried so far:
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void onClick(View view) throws Exception {
ssmlToAudio("Hello", "test");
}
But if I run my app and click on a button, I'll get this error:
java.lang.IllegalStateException: Could not execute method for
android:onClick
What am I doing wrong?
You have to implement the onClickListener in your activity and then override the onClick method.
I have a Spring Boot application.
Users can login to my application and upload files.
All the files of users are stored in a Google Cloud Storage.
Now, I want the users to be able to download their files.
So, I have to download the files form the Cloud Storage.
I don't know how my controller should look.
With my current code I'm getting an empty file. The upload is already made and the connection is fine as well.
public static Blob downloadFile(Storage storage, String fileName){
Blob blob = storage.get(BUCKET_NAME, fileName);
return blob;
}
#RequestMapping(value = "/downloadFileTest")
#ResponseBody
public void downloadFile(HttpSession session,
HttpServletResponse response) {
Storage storage = de.msm.msmcenter.service.cloudstorage.Authentication.getStorage();
Blob blob = de.msm.msmcenter.service.cloudstorage.Authentication.downloadFile(storage,"test.txt");
ReadChannel readChannel = blob.reader();
InputStream inputStream = Channels.newInputStream(readChannel);
try {
response.setContentType("application/force-download");
response.setHeader("Content-Disposition", "attachment; filename=test.txt");
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
inputStream.close();
} catch (Exception e){
e.printStackTrace();
}
}
I actually want to be able to download any file, not only txt.
When the user opens the link, the file with the name test.txt gets downloaded but it's empty..
It seems like you just want to give access to the user to be able to download the file.
A solution for that would be to use a Signed URL, which can let you provide the user with an URL to access/download the object for a limited time. If you redirect the user directly to that URL the download would start immediately.
Thank you #Mayeru
I changed my code to :
public static String downloadFile(Storage storage, String fileName){
Blob blob = storage.get(BUCKET_NAME, fileName);
String PATH_TO_JSON_KEY = "/your/path";
URL signedUrl = null;
try {
signedUrl = storage.signUrl(BlobInfo.newBuilder(BUCKET_NAME, fileName).build(),
1, TimeUnit.DAYS, SignUrlOption.signWith(ServiceAccountCredentials.fromStream(
new FileInputStream(PATH_TO_JSON_KEY))));
} catch (IOException e) {
e.printStackTrace();
}
return signedUrl.toString();
}
#add this line to you spring-boot application.properties file
spring.cloud.gcp.credentials.location=classpath:key.json
// read/download objects
public static ResponseEntity<byte[]> getObjectFromGCP(String yourfileName) throws IOException {
String objectNameWithLocation ="your file location with file name in GCP bucket";
//create your storage object with your credentials
Credentials credentials = GoogleCredentials.fromStream(new
ClassPathResource("key.json").getInputStream());
Storage storage = StorageOptions.newBuilder().setCredentials(credentials).build().getService();
BlobId blobId = BlobId.of(bucketName, objectNameWithLocation);
Blob blob = storage.get(blobId);
return ResponseEntity.ok().contentType(MediaType.valueOf(FileTypeMap.getDefaultFileTypeMap().getContentType(yourfileName)))
.body(blob.getContent(BlobSourceOption.generationMatch()));
}
With the help of using the Google document Writing files to a blob store
I can store the file in Google Cloud in below way,
//My servlet Class
private StorageService storage = new StorageService();
FileService fileService = FileServiceFactory.getFileService();
AppEngineFile file = fileService.createNewBlobFile(mime, fileName);
boolean lock = true;
FileWriteChannel writeChannel = fileService.openWriteChannel(file, lock);
byte[] b1 = new byte[BUFFER_SIZE];
int readBytes1;
storage.init(fileName, mime); // Calling my Java Class Here
while ((readBytes1 = is1.read(b1)) != -1) {
writeChannel.write(ByteBuffer.wrap(b1, 0, readBytes1));
storage.storeFile(b1, readBytes1);}
writeChannel.closeFinally();
I have a storage java class to store the data in Google cloud that is below here,
//My Storage Class
public class StorageService {
public static final String BUCKET_NAME = "MyBucket";
public void init(String fileName, String mime) throws Exception {
GSFileOptionsBuilder builder = new GSFileOptionsBuilder().setAcl("public_read").setBucket(BUCKET_NAME).setKey(fileName).setMimeType(mime);
AppEngineFile writableFile = fileService.createNewGSFile(builder.build());
boolean lock = true;
writeChannel = fileService.openWriteChannel(writableFile, lock);
bos = new BufferedOutputStream(Channels.newOutputStream(writeChannel));
public void storeFile(byte[] b, int readSize) throws Exception {
bos.write(b,0,readSize);
bos.flush();
}}
From the above reference i can store the file directly in Google cloud. But i need to store those files as blob values using the File service.
Can anyone suggest me an idea.
your help will be appreciated.
Have you add appengine service account into Google API Console Project ?
If you have not done, you should prepare Google API Console Project.
Confirm your appengine service account on "Application Settings" tab on GAE Admin Console
Create new Project on Google API Console. or Open API Console Project for your exists bucket.
Make enable "Google Cloud Storage" in "Services" tab.
Add appengine service account as team on API Console "Team" tab.
i am trying to have a REST service return a zip file from the local harddrive .
Following is what i am doing ,
#Path("/interface3/{Ent_id}/{esf_app_id}/{esf_app_ver}")
public class Interface3Mock {
// This method is called if TEXT_PLAIN is request
#GET
#Produces("application/zip")
public Response callInterface3_text(
#PathParam("Ent_id") Integer entitlement_id,
#PathParam("eapp_id") String eapp_id,
#PathParam("eapp_ver") String eapp_ver) {
File f = new File("D:\\Documentation\\Documentation.zip");
String mt = new MimetypesFileTypeMap().getContentType(f);
return Response.ok(f, mt).build();
}
}
Now when i use the browser ie. Internet Explorer and key in the url http://localhost:9788/mockRESTServer/rest/interface3/123456/k123/l345
i see a file download dialog that says "Do you want to save the file l345`.
i want it to ask me for the zip download ie. D:\\Documentation\\Documentation.zip.
But somehow it takes up the last parameter in the request URL.
return Response.ok(f, mt)
.header("Content-Disposition", "attachment; filename=Documentation.zip")
.build();
See How to set response header in JAX-RS so that user sees download popup for Excel?