-
Notifications
You must be signed in to change notification settings - Fork 238
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: Correct way to restart listener for continuous listening #253
Comments
The restart should probably happen in response to the new |
|
Do you have the 5.0.0 version of the plugin? It's a new state. If you do, and you're not seeing that event, then there's an issue with it. |
Yes, as it turns out, I was using outdated version 4.2.2.
But it works quite odd:
There is silence for some ~60 seconds, during which listening is not active.
P.S. This seems to happen only after Update: seems that the length of this silence during which speech is not processed is related with
What's more, I'm still getting: |
Thanks for the detailed findings. This is helpful, and your result is very, very weird. Just to confirm all of this is from Android, correct? What do you have |
Yes, all these findings were found by using real Android device (Xiaomi Mi9 with latest MIUI 12.5.1). Unfortunately I don't have access to any iOS device, so I can't check anything on it :/
Tried also to reduce it to 0, but it just went in "fail loop": beeped all the time |
I've got what I think is a reproduce on this behaviour. There's definitely something odd going on when a timeout happens but I'm not sure what it is yet. Here's the full code of the loop I'm testing: import 'package:flutter/material.dart';
import 'package:speech_to_text/speech_recognition_error.dart';
import 'package:speech_to_text/speech_recognition_result.dart';
import 'package:speech_to_text/speech_to_text.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Speech To Text Continuous'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
SpeechToText? speech;
bool _listenLoop = false;
String lastHeard = '';
StringBuffer totalHeard = StringBuffer();
@override
void initState() {
super.initState();
}
void startListening({bool forced = false}) async {
if (forced) {
setState(() {
_listenLoop = !_listenLoop;
});
}
if (!_listenLoop) return;
print('startListening()');
speech = SpeechToText();
bool _available = await speech!.initialize(
onStatus: _onStatus,
//onError: (val) => print('onError: $val'),
onError: (val) => onError(val),
);
if (_available) {
print('startListening() -> _available = true');
await listen();
} else {
print('startListening() -> _available = false');
}
}
Future listen() async {
print('speech!.listen()');
speech!.listen(
onResult: (val) => onResult(val),
); // Doesn't do anything
}
void onError(SpeechRecognitionError val) async {
print('onError(): ${val.errorMsg}');
startListening();
}
void onResult(SpeechRecognitionResult val) async {
print('onResult()');
print('val.alternates ${val.alternates}');
if (val.finalResult) {
setState(() {
lastHeard = val.recognizedWords;
totalHeard.write(lastHeard);
totalHeard.write(" ");
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(_listenLoop ? Icons.mic : Icons.mic_external_off),
Text(
'You said:',
),
Text(lastHeard),
Text(
'Heard so far:',
),
Text(totalHeard.toString()),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => startListening(forced: true),
tooltip: 'Start listening',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
void _onStatus(String status) {
if ('done' == status) {
startListening();
}
}
} |
Android was calling both onSpeechEnd and onError in some cases but not all. These changes allow the 'done' callback to only happen once regardless of the order or type of these events.
I've committed an experimental change to improve this behaviour. If you have a chance and could try it on your device that would be helpful. Here's the code I was testing it with: import 'package:flutter/material.dart';
import 'package:speech_to_text/speech_recognition_error.dart';
import 'package:speech_to_text/speech_recognition_result.dart';
import 'package:speech_to_text/speech_to_text.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Speech To Text Continuous'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
SpeechToText? speech;
bool _listenLoop = false;
String lastHeard = '';
StringBuffer totalHeard = StringBuffer();
void _onStatus(String status) {
if ('done' == status) {
print('onStatus(): $status ');
startListening();
}
}
void startListening({bool forced = false}) async {
if (forced) {
setState(() {
_listenLoop = !_listenLoop;
});
}
if (!_listenLoop) return;
print('startListening()');
speech = SpeechToText();
bool _available = await speech!.initialize(
onStatus: _onStatus,
//onError: (val) => print('onError: $val'),
onError: (val) => onError(val),
debugLogging: true,
);
if (_available) {
print('startListening() -> _available = true');
await listen();
} else {
print('startListening() -> _available = false');
}
}
Future listen() async {
print('speech!.listen()');
speech!.listen(
onResult: (val) => onResult(val),
); // Doesn't do anything
}
void onError(SpeechRecognitionError val) async {
print('onError(): ${val.errorMsg}');
}
void onResult(SpeechRecognitionResult val) async {
print('onResult()');
print('val.alternates ${val.alternates}');
if (val.finalResult) {
setState(() {
lastHeard = val.recognizedWords;
totalHeard.write(lastHeard);
totalHeard.write(" ");
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(_listenLoop ? Icons.mic : Icons.mic_external_off),
Text(
'You said:',
),
Text(lastHeard),
Text(
'Heard so far:',
),
Text(totalHeard.toString()),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => startListening(forced: true),
tooltip: 'Start listening',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
} |
Thanks. I only added this condition "'notListening' == status"
|
@sowens-csd, my sincere apologies for such late reply - I somehow missed your comment. With the version I have your code works quite OK if something is recognized: it restarts the listener straight away and next words are listened. That's great. Though, if nothing is recognized, e.g. I say nothing, then it just stops working:
|
Thanks for the response. To try the experimental version you can change the Pubspec reference to refer to GitHub instead of using the version. The syntax looks like: speech_to_text:
git: https://github.com/csdcorp/speech_to_text.git
ref: main
path: speech_to_text/ |
These changes are now live as 5.1.0. Let me know if you have a chance to try it. |
With
Any chance that sound signal could be turned off for such restarts when nothing is recognized? |
Three thumbs up! I'll take it, best review I've had yet. I will have a look as to why By |
Odd, on my device I am getting |
Sad to hear that. Would make a lot of sense for such use-case where listener is repeatedly restarted as long as some supported command is recognized. About that
|
Could you try that same test on that device with the updated example and with the 'Log events' checkbox checked and then post that log? The log you provided above looks exactly like the log I get from an older Android device but I do get the |
Sorry, but I didn't understand this part. Searched in Visual Studio Code, searched in Google, but didn't find anything related with flutter. |
Sorry! The example app that ships with the plugin |
Thanks for the clarification! |
Took code from example app as a basis and still occasionally it fails - ends with status 'notListening' Code:
} void startListening() { void errorListener(SpeechRecognitionError error) { void statusListener(String status) { void resultListener(SpeechRecognitionResult result) { Log:
|
I posted this solution in StackOverflow which worked perfectly on my Google Pixel 6 Pro device https://stackoverflow.com/a/73597058/13079132 |
Hello, Is the continuous listening issue exclusive to flutter? Will continous listening not be an issue if simply using native android? |
No, it's not Flutter specific. The plugin is designed around the way the native Android speech recognition library works. You would have more control over how you interact with the speech subsystem if you were developing a native app but the fundamental restrictions would be the same. Android speech is not designed to be used for continuous recognition. |
How to destroy speech if MyHomePage is placed on the second page?
|
Hi,
Could anyone help me with a workaround solution for "continuous" listening? I know that continuous listening as such is not supported with this library, but it seems that it should be possible just to restart the listener after each result/error, e.g. error_no_match, which is raised when no match is found within x seconds.
Here's my code:
Seems that restarting of listener works just fine in onResult(), but it doesn't - in onError(), which I'm using to restart listener when "error_no_match" is raised (x seconds of silence).
Console:
I/flutter (22431): startListening()
I/flutter (22431): startListening() -> _available = true
I/flutter (22431): listen()
I/flutter (22431): listen() -> isListening = true
I/flutter (22431): onStatus: notListening
I/flutter (22431): speech!.stopPED
I/flutter (22431): speech!.listen()
I/flutter (22431): onStatus: listening
I/flutter (22431): onError: error_busy
I/flutter (22431): startListening()
I/flutter (22431): startListening() -> _available = true
I/flutter (22431): listen()
I/flutter (22431): listen() -> isListening = true
I/flutter (22431): onStatus: notListening
I/flutter (22431): speech!.stopPED
I/flutter (22431): speech!.listen()
I/flutter (22431): onStatus: listening
I/flutter (22431): onError: error_busy**...and so on**
The text was updated successfully, but these errors were encountered: