I have a problem when using SpeechRecognizer.stopListening() on Android, after having invoked startListening(). It simply seems to have no effect. The audio continues to be processed, and the recognition results are returned, just as if stopListening() had not been invoked.
Has anyone else had similar problems? May I be doing something wrong?
A possible clue: Immediately after invoking stopListening(), onError() is called with SpeechRecognizer.ERROR_CLIENT. Perhaps this means that the stop invocation failed?
The problem appears both when stopListening() is invoked before the start of speech is detected, or while speech is being processed.
startListening() and stopListening() are both invoked from the main thread.
Tested with both Android 5 and six on at least two different devices.
stopListening() in your explanation is behaving as expected. This call does not prevent the Recognizer from processing the speech input up until this point.
It is also expected that it will call onError() with SpeechRecognizer.ERROR_CLIENT. If you don't wish to handle this as an 'error', then at the point in your code where you call stopListening() add a simple boolean value:
deliberatelyCalledStop = true;
And then inside the error handling, you'll know if this has been thrown as an expected outcome and you can disregard it.
In order to shut down the recognition service and ignore all speech input detected thus far, you need to call:
recognizer.cancel();
recognizer.destroy(); // if you want to destroy it
These methods will achieve what you want.
Please be aware, at the time of writing, there are some pretty major bugs with Google's Recognition Service.
I recommend you read my answer to this question and then check out the gist of the bugs that I've already reported.
Related
In terms of performance and usability, what is the best approach? What are the main differences between these two methods?
I currently have an implementation on "OnResults" that is constantly listening and compares with a couple strings, taking distinct actions for each word detected. However, it fails on recognizing the words some times and sometimes doesn't even listen to anything. If I moved the logic to "OnPartialResults" would improve the usability?
onResults is called when SpeechRecognizer finishes listening.
onPartialResults is called when SpeechRecognizer detect new word you have spoken, even before end of listening.
Both of them should have got the same results for single spoken words, but if your speech is longer, onResults can modify your output to make it little more grammatically correct (but just little bit).
Usage of them depends of your purposes. But more accuratrate results are given with onResults.
If you want to match spoken words to action, create own matcher, which would choose the best matching (but not always equal, because it does not work always).
More about onResults and onPartialResults at developer.android.com
Important: to get partial results, you have to add extra to recognizer intent:
intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
I am developing a simple app which will speak the contact name or an unknown number when call is received. I am implementing the app using broadcastReceiver and Services. If i run the app on emulator and start the call using DDMS, with 2 or 3 contacts saved, the app works fine since onInit() is called before tts.speak() runs.
Now when i try to run the same app on my android phone, onInit is called after the tts.speak(). From what i have understood while searching for an answer to this question, this happens due to tts.speak() not waiting for onInit to called.
One solution i found on this question was on How to wait for TextToSpeech initialization on Android but that didn't work either.
This question has been asked a lot of times but i couldn't find a working solution. This link suggested to use handler http://davidcheney.wordpress.com/2010/11/16/multitasking-in-android/ but being a newbie i have no idea as to how to implement that.
From what i understood i have to wait till onInit is called before i can use tts.speak() but i don't know how to do it.
Update
I was trying to call speak function outside the onInit since the data which was to be spoken was coming from elsewhere and i didn't want to do all the coding in onInit,this was not working. So i changed my code and finally somehow managed to run that speak() inside onInit().
Although the code is now running but there must be a way to call speak() outside onInit. So i will wait for a better answer else post my code for others facing same problem.
You either set a class member flag boolean mTtsInitialized and check this flag everytime you call speak or put the code to get the data to be spoken in onInit
This is not the most elegant way of handling this, I'm sure, but could you extend the class containing the onInit() method?
In this class, you could have a boolean variable that effectively "locks" your thread. Override the onInit() method, call super(), and then after super() set this value to true. Then, enter a loop that blocks the thread which calls tts.speak() until this value is true.
You'll want to keep in mind that you can't do this in the UI thread, because if you block that for too long it will crash your app.
I hope I understood your question correctly. :)
I've written some code for Android 2.2 that plays an audio file using the Android MediaPlayer. Without getting into the details of the code, I noticed that there exists a function called
isPlaying()
that allows you to check if an audio file is currently being played by the MediaPlayer. So, for example, when the following snippet of code runs
Toast.makeText(getApplicationContext(), "Sound playing is: " +
mediaPlayer.isPlaying(), Toast.LENGTH_SHORT).show();
it displays the following message
Sound playing is: true / false
depending on whether there's sound playing or not.
When I wrote some code to record sound from the microphone using the Android MediaRecorder, however, I noticed that there did not look like there exists a function called
isRecording()
that checks to see whether a recording is in progress.
So, I was wondering if the onus is on the programmer then to figure out if a recording is in progress by embedding some logic into their code - or if perhaps there indeed exists a way to do this (check if a recording is in progress) by using another in-built function offered by the Android API.
Doesn't look like there is such a function after all. I think it makes sense to try to embed some logic in the code to do this cleanly.
Important
I found a nice workaround to handle this issue, and you can read the workaround right away, but I strongly suggest that you read the entire explanation first.
The scenario
I was trying to find a way to figure out if the mediarecorder had started recording or not, I assumed that calling the start() method on the recorder after the prepare() method was enough, but it turns out, it isn't.
Before you get offended by what I just said, Let me explain the scenario...
I was building a simple audio recording app from scratch, no libraries, copy paste, all hardwork. So I knew exactly what each part of my code is doing. Or at least I thought I did.
Until I decided to try and break my application by clicking on the buttons to start and stop recording like I was playing a piano. And yes, my stop button wasn't even appearing until the mediarecorder's start() method was called.
So I was greeted with a crash and logcat welcomed me with
java.lang.RuntimeException: stop failed.
at android.media.MediaRecorder.stop(Native Method)
along with
E/MediaRecorder(15709): stop failed: -1007
So I read online and found out that calling stop() right after start() on MediaRecorder causes this problem.
So the biggest question was, how do I detect if it is now SAFE for me to enable the STOP button on my recorder?
The Workaround (Not at all perfect, but it works)
MediaRecorder.getMaxAmplitude() // The maximum absolute amplitude measured since the last call, or 0 when called for the first time
As you can see, the MediaRecorder.getMaxAmplitude() method or the MediaRecorder.maxAmplitude property return 0 when called for the first time and the amplitude after that.
So, instead of allowing the user to Stop the recording right after calling MediaRecorder.start() I am now waiting until the MediaRecorder.maxAmplitude value is greater than zero, at which point I can be sure that the MediaRecorder is initialized, started, and recording, and is in a state where calling stop() is allowed. You can accomplish this by using a runnable that keeps checking until the amplitude is greater than 0. I am already using a runnable for the timer, so I perform the check in that.
Please Note
When working on an emulator, the value returned by MediaRecorder.maxAmplitude is always 0. So, you should use an Android Device to check if everything works as expected.
Now my buttons stay disabled for less than a second when I first start recording. But if I stop and start too quickly, they remain disabled for a bit longer, and I show a "Please wait while the recording starts" message for the user.
I hope this answer helps someone.
Regards!
For everyone using Android's voice recognition API, there used to be a handy RecognitionListener you could register that would push various events to your callbacks. In particular, there was the following onBufferReceived(byte[]) method:
public abstract void onBufferReceived (byte[] buffer)
Since: API Level 8 More sound has been received. The purpose of this
function is to allow giving feedback to the user regarding the
captured audio. There is no guarantee that this method will be called.
Parameters buffer a buffer containing a sequence of big-endian 16-bit
integers representing a single channel audio stream. The sample rate
is implementation dependent.
Although the method explicitly states that there is no guarantee it will be called, in ICS and prior it would effectively be called 100% of the time: regularly enough, at least, that by concatenating all the bytes received this way, you could reconstruct the entire audio stream and play it back.
For some reason, however, in the Jellybean SDK, this magically stopped working. There's no notice of deprecation and the code still compiles, but the onBufferReceived is now never called. Technically this isn't breaking their API (since it says there's "no guarantee" the method will be called), but clearly this is a breaking change for a lot of things that depended on this behaviour.
Does anybody know why this functionality was disabled, and if there's a way to replicate its behaviour on Jellybean?
Clarification: I realize that the whole RecognizerIntent thing is an interface with multiple implementations (including some available on the Play Store), and that they can each choose what to do with RecognitionListener. I am specifically referring to the default Google implementation that the vast majority of Jellybean phones use.
Google does not call this method their Jelly Bean speech app (QuickSearchBox). Its simply not in the code. Unless there is an official comment from a Google Engineer I cannot give a definite answer "why" they did this. I did search the developer forums but did not see any commentary about this decision.
The ics default for speech recognition comes from Google's VoiceSearch.apk. You can decompile this apk and see and find see there is an Activity to handle an intent of action *android.speech.action.RECOGNIZE_SPEECH*. In this apk I searched for "onBufferReceived" and found a reference to it in com.google.android.voicesearch.GoogleRecognitionService$RecognitionCallback.
With Jelly Bean, Google renamed VoiceSearch.apk to QuickSearch.apk and made a lot of new additions to the app (ex. offline dictation). You would expect to still find an onBufferReceived call, but for some reason it is completely gone.
I too was using the onBufferReceived method and was disappointed that the (non-guaranteed) call to the method was dropped in Jelly Bean. Well, if we can't grab the audio with onBufferReceived(), maybe there is a possibility of running an AudioRecord simultaneously with voice recognition. Anyone tried this? If not, I'll give it a whirl and report back.
I ran in to the same problem. The reason why I didn't just accept that "this does not work" was because Google Nows "note-to-self" record the audio and sends it to you. What I found out in logcat while running the "note-to-self"-operation was:
02-20 14:04:59.664: I/AudioService(525): AudioFocus requestAudioFocus() from android.media.AudioManager#42439ca8com.google.android.voicesearch.audio.ByteArrayPlayer$1#424cca50
02-20 14:04:59.754: I/AbstractCardController.SelfNoteController(8675): #attach
02-20 14:05:01.006: I/AudioService(525): AudioFocus abandonAudioFocus() from android.media.AudioManager#42439ca8com.google.android.voicesearch.audio.ByteArrayPlayer$1#424cca50
02-20 14:05:05.791: I/ActivityManager(525): START u0 {act=com.google.android.gm.action.AUTO_SEND typ=text/plain cmp=com.google.android.gm/.AutoSendActivity (has extras)} from pid 8675
02-20 14:05:05.821: I/AbstractCardView.SelfNoteCard(8675): #onViewDetachedFromWindow
This makes me belive that google disposes the audioFocus from google now (the regonizerIntent), and that they use an audio recorder or something similar when the Note-to-self-tag appears in onPartialResults. I can not confirm this, has anyone else made tries to make this work?
I have a service that is implementing RecognitionListener and I also override onBufferReceived(byte[]) method. I was investigating why the speech recognition is much slower to call onResults() on <=ICS . The only difference I could find was that onBufferReceived is called on phones <= ICS. On JellyBean the onBufferReceived() is never called and onResults() is called significantly faster and I'm thinking its because of the overhead to call onBufferReceived every second or millisecond. Maybe thats why they did away with onBufferReceived()?
For various reasons, I need to use the raw SpeechRecognizer API instead of the easier RecognizerIntent (RECOGNIZE_SPEECH) activity.
That means, among other things, that I need to handle RecognitionListener.onError() myself.
In response to some of the errors, I simply want to re-start listening. This looks straightforward but when I just call SpeechRecognizer.startListening() upon error, this sometimes seems to trigger two different errors:
ERROR/ServerConnectorImpl(619): Previous session not destroyed
and
"concurrent startListening received - ignoring this call"
Which hints that I should have done some cleanup before attempting to call SpeechRecognizer.startListening() again.
If this is true, it means that upon a RecognitionListener error, listening is not automatically stopped and/or canceled.
It is also possible that some errors do stop/cancel listening, while others don't. There are really only 9 SpeechRecognizer errors:
ERROR_NETWORK_TIMEOUT
ERROR_NETWORK
ERROR_AUDIO
ERROR_SERVER
ERROR_CLIENT
ERROR_SPEECH_TIMEOUT
ERROR_NO_MATCH
ERROR_RECOGNIZER_BUSY
ERROR_INSUFFICIENT_PERMISSIONS
Since the documentation isn't very detailed about which error cancels listening and which doesn't, do you happen to know, based on your experience, which errors require doing cleanup (and to which extent) before attempting SpeechRecognizer.startListening() again?
No, cancel is not called when onError is invoked. You can look at the source here.
you can destroy current session by destroy(). And you can restart it again
Actually Femi, some of the error conditions DO stop the transcription service from listening (SpeechRecognizer.ERROR_SPEECH_TIMEOUT for example). It is not necessary to call destroy, just startlistening again.