Closed rivetingpeppermint closed 3 years ago
I've found that if I changed partialResults: partialResults || null != pauseFor,
(speech_to_text.dart line 330, inside the bool started = await SpeechToTextPlatform.instance.listen()
call) to just partialResults: partialResults,
, the duplicated calls doesn't occur.
Certainly sounds like an error in the plugin, thanks for reporting. I'll have a look at the behaviour.
I think I found the issue. I'll have to think about how to resolve it but in the meantime I think you can avoid it pretty easily. Currently you have pauseFor and listenFor set to the same value. That's making the two timers expire at the same instant and causing your duplicate callbacks. Can you try the code again with pauseFor
set to 2 seconds with listenFor
still at 5 and see if you're still getting duplicates?
I believe I'm experiencing the same issue in my iOS simulator (14.4
) using the latest version of this library 3.1.0
. I did not set listenFor
and my listen
call looks like: .listen(pauseFor: Duration(seconds: 2));
I'm using SpeechToTextProvider
and watching .lastResult.finalResult
. Most of the time I get 2 final results, sometimes 3. I can't quite detect the pattern but after I start to listen sometimes it cuts off after one word, not waiting the full 2 seconds. In those cases I get a single finalResult
. Setting listenFor
to a value greater than pauseFor
did not address the issue for me.
UPDATE:
I have two separate widget files (using riverpod
to consume the SpeechToTextProvider
):
a) button with an onPressed
speech.listen(pauseFor: Duration(seconds: 2));
speech.stream.listen((event) {
if (event.eventType == SpeechRecognitionEventType.finalRecognitionEvent) {
print('stream: ${event.recognitionResult.recognizedWords}');
}
});
b) a plain widget watching .lastResult
for debugging
String output = result?.recognizedWords ?? '';
if (result?.finalResult ?? false) {
print('words: $output, confidence: ${result.confidence}');
}
iOS Simulator v12.4 using iOS v14.4: Every time (about 10 attempts) I hit listen and speak the b) widget prints 2-3 times and the stream listener prints in increments of 2. So the 1st pass, stream prints twice. 2nd pass stream prints 4 times, 3rd pass 6 times, etc.
Physical iPhone 6s using iOS v14.4: The b) widget prints once most of the time. I can't figure out the pattern. I can try 10 times and all attempts print once. Try another 10 attempts and 7 print once and 3 times prints twice. The stream listener prints in increments of 1, so the first attempt it prints once, 2nd pass prints 2 times, etc.
I don't know if any of this is useful or just vague rambling.
(I'm just getting back to using this excellent package after a long time dragged off onto other things)
I can confirm the problem with pauseFor on the iOS Simulator.
I am using the null-safe version of the package (4.0.0-nullsafety) and I am running the example from the pub.dev page (https://pub.dev/packages/speech_to_text/versions/4.0.0-nullsafety/example). The only modifications I have made to the example code are as follows:
@@ -235,5 +235,12 @@
void resultListener(SpeechRecognitionResult result) {
++resultListened;
- print('Result listener $resultListened');
+ final now = DateTime.now().toIso8601String();
+ print('$now resultListener: final ${result.finalResult}');
+ if (result.finalResult) {
+ print(' no. alternates ${result.alternates.length}');
+ for (final a in result.alternates) {
+ print(' - ${a.recognizedWords} (${a.confidence})');
+ }
+ }
setState(() {
lastWords = '${result.recognizedWords} - ${result.finalResult}';
@@ -258,6 +265,6 @@
void statusListener(String status) {
- // print(
- // 'Received listener status: $status, listening: ${speech.isListening}');
+ final now = DateTime.now().toIso8601String();
+ print('$now statusListener: Received listener status: $status, listening: ${speech.isListening}');
setState(() {
lastStatus = '$status';
In other words I print a little more information in each call to resultListener and also print each call to statusListener.
When I run in the iOS Simulator specifying iPhone 8 with iOS 14.4 I get the following output:
flutter: 2021-03-20T18:03:07.739014 statusListener: Received listener status: listening, listening: true flutter: 2021-03-20T18:03:09.583452 resultListener: final false flutter: 2021-03-20T18:03:09.750945 resultListener: final false flutter: 2021-03-20T18:03:10.140940 resultListener: final false flutter: 2021-03-20T18:03:10.371313 resultListener: final false flutter: 2021-03-20T18:03:10.469003 resultListener: final false flutter: 2021-03-20T18:03:17.746529 statusListener: Received listener status: notListening, listening: false flutter: 2021-03-20T18:03:17.761650 statusListener: Received listener status: notListening, listening: false flutter: 2021-03-20T18:03:17.847628 resultListener: final true flutter: no. alternates 1 flutter: - Can you hear me (0.582) flutter: 2021-03-20T18:03:17.910716 resultListener: final false flutter: 2021-03-20T18:03:17.946371 resultListener: final true flutter: no. alternates 6 flutter: - Can you hear me (0.616) flutter: - Can you hear (0.683) flutter: - Can you him (0.61) flutter: - Can you heal me (0.592) flutter: - Can you help (0.595) flutter: - Can you help me (0.592) flutter: 2021-03-20T18:03:17.947894 statusListener: Received listener status: notListening, listening: false
This shows 5 partial results while I am speaking (final == false). It then shows two (?) calls to statusListener as pauseFor times out (pauseFor is set to 5 seconds in the example but there seems to be a gap of more like 7 seconds after the last partial result). Then I get a run of 3 calls to resultListener, the first has finalResult set to true, then the second has finalResult set to false (??) and the third has finalResult set to true again. Notice that for a final result I have printed the alternates (as I'm really interested in them). The first "final" result does not include alternates (i.e. it looks just like a partial result but with finalResult set to true). The second final result (the third call to resultListener after the pauseFor timeout happened) includes alternates and looks more like a genuine final result.
I'm guessing this isn't the expected behaviour? If it is, is there a way to distinguish between the pseudo (first) final result and the real one? I don't want to rely on knowing I'll get a sequence of final/not final/final results because that feels very fragile.
I am just getting back to this package, so I am still in the process of setting up to test on a real iOS device as well as both an Android emulator and a real Android device. I will check those as I progress...
I can reproduce the behaviour on a real iOS device, a 7th gen iPad running iOS 14.4.1. Here is the same output from this device (there appears to be more debug generated by the plugin itself):
[plugin] HypothesizeTranscription
[plugin] Encoded JSON result: {"alternates":[{"recognizedWords":"I'm listening you say","confidence":0.881}],"finalResult":false}
[plugin] invokeFlutter textRecognition
flutter: 2021-03-20T20:54:59.588787 resultListener: final false
69 [plugin] invokeFlutter soundLevelChange
[plugin] invokeFlutter notifyStatus
[plugin] Finished reading audio
[plugin] invokeFlutter notifyStatus
flutter: 2021-03-20T20:55:06.481517 statusListener: Received listener status: notListening, listening: false
flutter: 2021-03-20T20:55:06.482147 statusListener: Received listener status: notListening, listening: false
flutter: 2021-03-20T20:55:06.589004 resultListener: final true
flutter: no. alternates 1
flutter: - I'm listening you say (0.881)
[plugin] HypothesizeTranscription
[plugin] Encoded JSON result: {"alternates":[{"recognizedWords":"I'm listening you say","confidence":0.881}],"finalResult":false}
[plugin] invokeFlutter textRecognition
[plugin] FinishRecognition true
[plugin] Encoded JSON result: {"alternates":[{"recognizedWords":"I'm listening you say","confidence":0.881},{"recognizedWords":"I'm listening to you say","confidence":0.704}],"finalResult":true}
[plugin] invokeFlutter textRecognition
[plugin] FinishSuccessfully
[plugin] invokeFlutter notifyStatus
flutter: 2021-03-20T20:55:06.610140 resultListener: final false
flutter: 2021-03-20T20:55:06.611101 resultListener: final true
flutter: no. alternates 2
flutter: - I'm listening you say (0.881)
flutter: - I'm listening to you say (0.704)
flutter: 2021-03-20T20:55:06.611475 statusListener: Received listener status: notListening, listening: false
Thanks for posting. No, this isn't the expected result. I think there might be a problem with the way I was synthetically creating final results on iOS. I've just committed a change that provides control over that behaviour using a new finalTimeout
value on the initialize
method.
I'll do further tests to try to reproduce your results and see if this change resolves them. If you're curious you could test against the repo version to see if it changes the behaviour.
Good news, I was able to reproduce that behaviour and this change does seem to resolve it. There is still an issue if you set the finalTimeout
too low, like 100 ms, I'll try to fix that as well and then release this version.
My latest commit should ensure only a single final result even if you set a short finalTimeout
. That resolves all the causes of this behaviour I can think of. Let me know if you have a chance to give it a try @ab36245.
Thanks very much @sowens-csd. Impressive turnaround!
Looks good on the simulator so far:
flutter: 2021-03-21T12:38:03.862388 statusListener: Received listener status: listening, listening: true
flutter: 2021-03-21T12:38:05.542383 resultListener: final false
flutter: 2021-03-21T12:38:05.863257 resultListener: final false
flutter: 2021-03-21T12:38:06.061371 resultListener: final false
flutter: 2021-03-21T12:38:06.496103 resultListener: final false
flutter: 2021-03-21T12:38:06.729233 resultListener: final false
flutter: 2021-03-21T12:38:06.829229 resultListener: final false
flutter: 2021-03-21T12:38:13.886688 statusListener: Received listener status: notListening, listening: false
flutter: 2021-03-21T12:38:13.901414 statusListener: Received listener status: notListening, listening: false
flutter: 2021-03-21T12:38:14.044987 resultListener: final false
flutter: 2021-03-21T12:38:14.051626 resultListener: final true
flutter: no. alternates 6
flutter: - You say I'm listening (0.607)
flutter: - Can you say I'm listening (0.524)
flutter: - And you say I'm listening (0.524)
flutter: - Are you say I'm listening (0.524)
flutter: - Do you say I'm listening (0.524)
flutter: - You say I am listening (0.454)
flutter: 2021-03-21T12:38:14.055538 statusListener: Received listener status: notListening, listening: false
Only one final result, which is just what I'm after. I'll be able to check on the iPad a little later.
Two minor questions (and happy to open separate issues if necessary):
I'll update again when I've checked the true iOS device.
Checks out on true iOS device too. Thank you
Excellent, thanks for checking it out. Let's create a new issue for those other two items. We can close this one as soon as I push out the next release.
Happy to open another issue for the other questions. The first is minor and the second I'll wait to have some more concrete data before I start bothering you with it.
I've published 4.1.0-nullsafety to pub.dev with this fix. Thanks for the help troubleshooting @ab36245!
For anyone following this thread in the future you shouldn't need to take any action. This issue is resolved in the latest version by default. The finalTimeout
property provides even more control but you shouldn't need to use it unless you want to completely disable guaranteed final results or want a timeout of longer than two seconds.
For the second issue @ab36245, when you are testing if you could use the latest version in the repo instead of the released version that would be helpful. I opened a separate issue, #191 and I've committed a potential fix. Some more testing on that resolution would be very helpful. Thanks!
Will do. I should get a chance to check tomorrow
I modified the example code to take commands and reply to them. I used the
pauseFor
parameter since on iOS, the listening activity won't stop until the fulllistenFor
duration is completed. This works perfectly fine on my Android device (Galaxy Tab A, Android 9). When I tried it on iOS (iPhone 11 iOS 13 simulator and an iPad Pro running iOS 14.2), the onResult callback is fired multiple times. See screenshot.If I remove the pauseFor parameter, it works fine on iOS, the multiple callbacks doesn't happen. I can't figure out what's wrong.
Here's my full code:
I used flutter
1.17.5
and Dart2.8.4
. Here's my pubspec.yaml file:There aren't any errors in the logs, just the onResult callback being fired multiple times in a row.