Reputation: 201
I have a project using RecognitionListener written in Kotlin. The speech-to-text function was always a success and never presented any problems.
Since last week, it's onResult function started to be called twice. No changes were made on the project. I tested old versions of the project (from months ago) and those had the same problem.
There are three different cases:
Here is the VoiceRecognition speech-to-text class code:
class VoiceRecognition(private val activity: Activity, language: String = "pt_BR") : RecognitionListener {
private val AudioLogTag = "AudioInput"
var voiceRecognitionIntentHandler: VoiceRecognitionIntentHandler? = null
var voiceRecognitionOnResultListener: VoiceRecognitionOnResultListener? = null //Must have this
var voiceRecognitionLayoutChanger: VoiceRecognitionLayoutChanger? = null
var isListening = false
private val intent: Intent
private var speech: SpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(activity)
init {
speech.setRecognitionListener(this)
intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
intent.putExtra(
RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
)
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language)
}
//It is important to put this function inside a clickListener
fun listen(): Boolean {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.RECORD_AUDIO), 1)
return false
}
speech.startListening(intent)
Log.i(AudioLogTag, "startListening")
return true
}
//Use this if you want to stop listening but still get recognition results
fun endListening(){
Log.i(AudioLogTag, "stopListening")
speech.stopListening()
isListening = false
}
fun cancelListening(){
Log.i(AudioLogTag, "cancelListening")
speech.cancel()
voiceRecognitionLayoutChanger?.endListeningChangeLayout()
isListening = false
}
override fun onReadyForSpeech(p0: Bundle?) {
Log.i(AudioLogTag, "onReadyForSpeech")
voiceRecognitionLayoutChanger?.startListeningChangeLayout()
isListening = true
}
override fun onRmsChanged(p0: Float) {
// Log.i(AudioLogTag, "onRmsChanged: $p0")
// progressBar.setProgress((Int) p0)
}
override fun onBufferReceived(p0: ByteArray?) {
Log.i(AudioLogTag, "onBufferReceived: $p0")
}
override fun onPartialResults(p0: Bundle?) {
Log.i(AudioLogTag, "onPartialResults")
}
override fun onEvent(p0: Int, p1: Bundle?) {
Log.i(AudioLogTag, "onEvent")
}
override fun onBeginningOfSpeech() {
Log.i(AudioLogTag, "onBeginningOfSpeech")
}
override fun onEndOfSpeech() {
Log.i(AudioLogTag, "onEndOfSpeech")
voiceRecognitionLayoutChanger?.endListeningChangeLayout()
isListening = false
}
override fun onError(p0: Int) {
speech.cancel()
val errorMessage = getErrorText(p0)
Log.d(AudioLogTag, "FAILED: $errorMessage")
voiceRecognitionLayoutChanger?.endListeningChangeLayout()
isListening = false
}
override fun onResults(p0: Bundle?) {
val results: ArrayList<String> = p0?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION) as ArrayList<String>
Log.i(AudioLogTag, "onResults -> ${results.size}")
val voiceIntent: Int? = voiceRecognitionIntentHandler?.getIntent(results[0])
if (voiceIntent != null && voiceIntent != 0) {
voiceRecognitionIntentHandler?.handle(voiceIntent)
return
}
voiceRecognitionOnResultListener!!.onResult(results[0])
}
private fun getErrorText(errorCode: Int): String {
val message: String
when (errorCode) {
SpeechRecognizer.ERROR_AUDIO -> message = "Audio recording error"
SpeechRecognizer.ERROR_CLIENT -> message = "Client side error"
SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS -> message = "Insufficient permissions"
SpeechRecognizer.ERROR_NETWORK -> message = "Network error"
SpeechRecognizer.ERROR_NETWORK_TIMEOUT -> message = "Network timeout"
SpeechRecognizer.ERROR_NO_MATCH -> message = "No match"
SpeechRecognizer.ERROR_RECOGNIZER_BUSY -> message = "RecognitionService busy"
SpeechRecognizer.ERROR_SERVER -> message = "Error from server"
SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> message = "No speech input"
else -> message = "Didn't understand, please try again."
}
return message
}
//Use it in your overriden onPause function.
fun onPause() {
voiceRecognitionLayoutChanger?.endListeningChangeLayout()
isListening = false
speech.cancel()
Log.i(AudioLogTag, "pause")
}
//Use it in your overriden onDestroy function.
fun onDestroy() {
speech.destroy()
}
listen(), endListening() and cancelListening() are all called from a button.
Upvotes: 15
Views: 3483
Reputation: 174
ya even I faced the same issue with my app but I fixed it with a custom logic that is using a flag means a variable let it be a temp variable and by default set it as a false.
what you need to do set the temp as true wherever you are starting listening voice. Then in your handler what you need to do is just add a if condition on the basis of temp variable like
if (temp) { do something temp = false }
so what will happen you handler will be called twice as usual but you will be able to handle this data only.
Upvotes: 0
Reputation: 11
I use the the following code based on time differences, which should continue to work if Google ever gets around to fixing this bug.
long mStartTime = System.currentTimeMillis(); // Global Var
@Override
public void onResults(Bundle results)
{
long difference = System.currentTimeMillis() - mStartTime;
if (difference < 100)
{
return;
}
mStartTime = System.currentTimeMillis();
ArrayList<String> textMatchList =
results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
Event_Handler(VOICE_DATA, textMatchList.get(0));
// process event
}
Upvotes: 0
Reputation: 661
this is my temporary workaround
singleResult=true;
@Override
public void onResults(Bundle results) {
Log.d(TAG, "onResults"); //$NON-NLS-1$
if (singleResult) {
ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
if (matches != null && matches.size() > 0) {
Log.d("single Result", "" + matches.get(0));
}
singleResult=false;
}
getHandler().postDelayed(new Runnable() {
@Override
public void run() {
singleResult=true;
}
},100);
}
Upvotes: 3
Reputation: 154
I had the same problem and I've just added a boolean flag in my code, but ofcourse it's a temporary solution and I don't know the source of this problem.
val recognizer = SpeechRecognizer.createSpeechRecognizer(context)
recognizer.setRecognitionListener(
object : RecognitionListener {
var singleResult = true
override fun onResults(results: Bundle?) {
if (singleResult) {
results?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION).let {
// do something with result
}
// next result will be ignored
singleResult = false
}
}
}
Upvotes: 2
Reputation: 3659
I found this open issue: https://issuetracker.google.com/issues/152628934
As I commented, I assume it is an issue with the "speech recognition service" and not with the Android RecognitionListener class.
Upvotes: 4
Reputation: 3212
This just started happening in one of my apps yesterday. I added a boolean to allow the code to execute only once, but I'd love an explanation as to why it suddenly started doing this. Any updates?
Upvotes: 1