Skip to content
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

Open
itanium21 opened this issue Aug 15, 2021 · 25 comments
Open

Question: Correct way to restart listener for continuous listening #253

itanium21 opened this issue Aug 15, 2021 · 25 comments

Comments

@itanium21
Copy link

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:

void startListening() async {
	print('startListening()');
	speech = stt.SpeechToText();

	bool _available = await speech!.initialize(
	  onStatus: (val) => print('onStatus: $val'),
	  //onError: (val) => print('onError: $val'),
	  onError: (val) => onError(val),
	);
	if (_available) {
	  print('startListening() -> _available = true');
	  listen();
	} else {
	  print('startListening() -> _available = false');
	}
}

void listen() async {
	print('listen()');
	if (speech!.isListening) {
	  print('listen() -> isListening = true');
	  //await speech!.stop();
	  await speech!.cancel(); // Tried with .stop() - no difference
	  speech.
	  print('speech!.stopPED');
	} else {
	  print('listen() -> isListening = false');
	}
	print('speech!.listen()');
	speech!.listen(
		onResult: (val) => onResult(val),
		listenFor: Duration(seconds: 60),
		pauseFor: Duration(seconds: 60)); // 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}');
	startListening();
}

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**

@sowens-csd
Copy link
Contributor

The restart should probably happen in response to the new done event in the onStatus listener. After that event all of the OS level resources in use should have been released.

@itanium21
Copy link
Author

done event?
From the debugging results attached above it seems that event done is never raised, just:

I/flutter (22431): onStatus: notListening
I/flutter (22431): onStatus: listening

@sowens-csd
Copy link
Contributor

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.

@itanium21
Copy link
Author

itanium21 commented Aug 17, 2021

Yes, as it turns out, I was using outdated version 4.2.2.
Updated speech_to_text to latest 5.0.0 version and moved restarting to status done.

  void onStatus(String val) {
    print('onStatus: $val');
    if (val == 'done') {
      startListening();
    }
  }

But it works quite odd:
After error_no_match speech!.listen() is called, but no actual listening happens.

/flutter (23739): startListening()
I/flutter (23739): startListening() -> _available = true
I/flutter (23739): listen()
I/flutter (23739): listen() -> isListening = false
I/flutter (23739): speech!.listen()
I/flutter (23739): onStatus: listening
I/flutter (23739): onError: error_no_match
I/flutter (23739): onStatus: done
I/flutter (23739): startListening()
I/flutter (23739): startListening() -> _available = true
I/flutter (23739): listen()
I/flutter (23739): listen() -> isListening = false
I/flutter (23739): speech!.listen()

There is silence for some ~60 seconds, during which listening is not active.
After this silence listening actually starts (results are processed).

I/flutter (23739): listen() -> isListening = false
I/flutter (23739): speech!.listen()
I/flutter (23739): onResult

P.S. This seems to happen only after error_speech_timeout, because if there is some result, then it works quite flawlessly.

Update: seems that the length of this silence during which speech is not processed is related with pauseFor value, which makes no sense?

[pauseFor] sets the maximum duration of a pause in speech with no words detected, after that it automatically stops the listen for you.

What's more, I'm still getting:
I/flutter (27988): onError: error_busy
and also something new:
I/flutter (27988): onError: error_client
From which description I can't tell that much.

@sowens-csd
Copy link
Contributor

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 pauseFor set to? Have you tried the same test on iOS?

@itanium21
Copy link
Author

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 :/
I tried with multiple pauseFor values:

  • pauseFor = 60, there was 60 second silence since listen() was initiated after listen() was called
  • pauseFor = 5, there was 5 second silence since listen() was initiated after listen() was called

Tried also to reduce it to 0, but it just went in "fail loop": beeped all the time

@sowens-csd
Copy link
Contributor

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();
    }
  }
}

sowens-csd pushed a commit that referenced this issue Aug 24, 2021
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.
@sowens-csd
Copy link
Contributor

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.
    );
  }
}

@juliopuebla
Copy link

juliopuebla commented Aug 27, 2021

Thanks.
With your experimental change works great in android and Web (using flutter 2.0.6 for web).

I only added this condition "'notListening' == status"

void _onStatus(String status) { if ('done' == status || 'notListening' == status) { print('onStatus(): $status '); startListening(); } }

@itanium21
Copy link
Author

itanium21 commented Aug 31, 2021

@sowens-csd, my sincere apologies for such late reply - I somehow missed your comment.
I tried the code you added above with the speech_to_text version 5.0.0 (didn't find a way how to use experimental version that contains your latest changes made in SpeechToTextPlugin.kt and speech_to_text.dart).
Though, flutter pub upgrade says that there is nothing newer than what I'm using.

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:

D/SpeechToTextPlugin(25853): Error 7 after start at 5270 -2.0 / 10.0
I/flutter (25853): onError(): error_no_match
I/flutter (25853): onStatus(): done
I/flutter (25853): startListening()
I/flutter (25853): startListening() -> _available = true
I/flutter (25853): speech!.listen()

@sowens-csd
Copy link
Contributor

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/

@sowens-csd
Copy link
Contributor

These changes are now live as 5.1.0. Let me know if you have a chance to try it.

@itanium21
Copy link
Author

itanium21 commented Sep 18, 2021

With
speech_to_text: ^5.1.1 and 'if ('done' == status || 'notListening' == status)' ('done' status was not called for some random noise, 'notListening' was, so I added it)

  • Some phrase: 👍
  • Silence: 👍
  • Some noise: 👍

Any chance that sound signal could be turned off for such restarts when nothing is recognized?

@sowens-csd
Copy link
Contributor

Three thumbs up! I'll take it, best review I've had yet. I will have a look as to why done isn't being sent in that case, my goal is that it should always be sent if speech recognition ends.

By sound signal I think you mean the system sounds that play when recognition starts/ends? If so, then no. I haven't found any system supported way to turn those off.

@sowens-csd
Copy link
Contributor

Odd, on my device I am getting done for random noise. What device / OS are you using? Are you getting any error notifications?

@itanium21
Copy link
Author

itanium21 commented Sep 19, 2021

By sound signal I think you mean the system sounds that play when recognition starts/ends? If so, then no. I haven't found any system supported way to turn those off.

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 done status: Xiaomi Mi 9, Android 11 (MIUI 12.5.1.0)
From Debug Console (I just did long "shhhh" :D ):

I/flutter (16326): startListening()
D/SpeechToTextPlugin(16326): Start initialize
D/SpeechToTextPlugin(16326): Checked permission
D/SpeechToTextPlugin(16326): has permission, completing
D/SpeechToTextPlugin(16326): completeInitialize
D/SpeechToTextPlugin(16326): Testing recognition availability
D/SpeechToTextPlugin(16326): sending result
D/SpeechToTextPlugin(16326): leaving complete
D/SpeechToTextPlugin(16326): leaving initializeIfPermitted
I/flutter (16326): startListening() -> _available = true
I/flutter (16326): speech!.listen()
D/SpeechToTextPlugin(16326): Start listening
D/SpeechToTextPlugin(16326): setupRecognizerIntent
D/SpeechToTextPlugin(16326): Notify status:listening
D/SpeechToTextPlugin(16326): Start listening done
I/flutter (16326): onStatus(): listening
D/SpeechToTextPlugin(16326): rmsDB -2.0 / -2.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / -0.68000007
I/chatty (16326): uid=10009(com.example.quiz_game_new) identical 1 line
D/SpeechToTextPlugin(16326): rmsDB -2.0 / -0.68000007
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 2.6799998
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
I/chatty (16326): uid=10009(com.example.quiz_game_new) identical 72 lines
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
I/chatty (16326): uid=10009(com.example.quiz_game_new) identical 2 lines
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
I/chatty (16326): uid=10009(com.example.quiz_game_new) identical 54 lines
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(16326): Error 7 after start at 5115 -2.0 / 10.0
D/SpeechToTextPlugin(16326): Notify status:notListening
D/SpeechToTextPlugin(16326): Notify status:doneNoResult
I/flutter (16326): onStatus(): notListening
I/flutter (16326): onStatus(): notListening -> startListening()

@sowens-csd
Copy link
Contributor

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 done status update.

@itanium21
Copy link
Author

itanium21 commented Sep 26, 2021

'Log events' checkbox checked

Sorry, but I didn't understand this part. Searched in Visual Studio Code, searched in Google, but didn't find anything related with flutter.

@sowens-csd
Copy link
Contributor

Sorry! The example app that ships with the plugin speech_to_text/speech_to_text/example/lib/main.dart has a 'Log events' checkbox on it. When checked the app logs every state change, error and result that is receives.

@itanium21
Copy link
Author

Thanks for the clarification!
Weird, with example app status 'done' appears all the time, but with code provided by you earlier time to time status 'done' doesn't appear. Will try to understand what's the difference.

@itanium21
Copy link
Author

itanium21 commented Sep 29, 2021

Took code from example app as a basis and still occasionally it fails - ends with status 'notListening'

Code:
` Future initSpeech({bool forced = false}) async {
if (forced) {
setState(() {
_listenLoop = !_listenLoop;
});
}
if (!_listenLoop) return;
printWithTimestamp('Initialize');
var hasSpeech = await speech.initialize(
onError: errorListener,
onStatus: statusListener,
debugLogging: true,
finalTimeout: Duration(milliseconds: 0));
if (hasSpeech) {
// Get the list of languages installed on the supporting platform so they
// can be displayed in the UI for selection by the user.
localeNames = await speech.locales();

  var systemLocale = await speech.systemLocale();
  currentLocaleId = systemLocale?.localeId ?? '';

  startListening();
}

}

void startListening() {
printWithTimestamp('start listening');
speech.listen(
onResult: resultListener,
listenFor: Duration(seconds: 30),
pauseFor: Duration(seconds: 5),
partialResults: true,
localeId: 'en_US', //currentLocaleId,
cancelOnError: true,
listenMode: ListenMode.confirmation);
setState(() {});
}

void errorListener(SpeechRecognitionError error) {
printWithTimestamp(
'Received error status: $error, listening: ${speech.isListening}');
}

void statusListener(String status) {
printWithTimestamp(
'Received listener status: $status, listening: ${speech.isListening}');
if ('done' == status) {
printWithTimestamp('onStatus(): $status -> startListening()');
Timer(Duration(milliseconds: 100), () {
initSpeech();
});
}
}

void resultListener(SpeechRecognitionResult result) {
print(
'Result listener final: ${result.finalResult}, words: ${result.recognizedWords}');
if (result.finalResult) {
setState(() {
lastHeard = result.recognizedWords;
totalHeard.write(lastHeard);
totalHeard.write(" ");
});
}
}`

Log:

I/flutter (11881): 20:44:45.121 - Initialize
D/SpeechToTextPlugin(11881): Received extra language broadcast
D/SpeechToTextPlugin(11881): Extra supported languages
I/flutter (11881): 20:44:45.135 - start listening
D/SpeechToTextPlugin(11881): Start listening
D/SpeechToTextPlugin(11881): setupRecognizerIntent
D/SpeechToTextPlugin(11881): Notify status:listening
D/SpeechToTextPlugin(11881): Start listening done
I/flutter (11881): 20:44:45.137 - Received listener status: listening, listening: true
D/SpeechToTextPlugin(11881): rmsDB -2.0 / -2.0
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/chatty (11881): uid=10009(com.example.quiz_game_new) identical 1 line
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/chatty (11881): uid=10009(com.example.quiz_game_new) identical 13 lines
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/chatty (11881): uid=10009(com.example.quiz_game_new) identical 2 lines
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/chatty (11881): uid=10009(com.example.quiz_game_new) identical 13 lines
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(11881): Error 8 after start at 1362 -2.0 / 10.0
D/SpeechToTextPlugin(11881): Notify status:notListening
D/SpeechToTextPlugin(11881): Notify status:doneNoResult
I/flutter (11881): 20:44:46.501 - Received listener status: notListening, listening: false
I/flutter (11881): 20:44:46.503 - Received listener status: done, listening: false
I/flutter (11881): 20:44:46.504 - onStatus(): done -> startListening()
I/flutter (11881): 20:44:46.508 - Received error status: SpeechRecognitionError msg: error_busy, permanent: true, listening: false
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/chatty (11881): uid=10009(com.example.quiz_game_new) identical 3 lines
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/flutter (11881): 20:44:46.608 - Initialize
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(11881): Received extra language broadcast
D/SpeechToTextPlugin(11881): Extra supported languages
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(11881): Calling results callback
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/flutter (11881): 20:44:46.654 - start listening
D/SpeechToTextPlugin(11881): Start listening
D/SpeechToTextPlugin(11881): setupRecognizerIntent
D/SpeechToTextPlugin(11881): Notify status:listening
D/SpeechToTextPlugin(11881): Start listening done
I/flutter (11881): 20:44:46.658 - Result listener final: false, words:
I/flutter (11881): 20:44:46.662 - Received listener status: listening, listening: true
D/SpeechToTextPlugin(11881): rmsDB 2.3200002 / 2.3200002
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 2.3200002
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 7.12
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 7.6000004
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 7.84
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/chatty (11881): uid=10009(com.example.quiz_game_new) identical 2 lines
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/chatty (11881): uid=10009(com.example.quiz_game_new) identical 1 line
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/chatty (11881): uid=10009(com.example.quiz_game_new) identical 15 lines
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/chatty (11881): uid=10009(com.example.quiz_game_new) identical 15 lines
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(11881): Calling results callback
I/flutter (11881): 20:44:48.304 - Result listener final: false, words:
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
I/chatty (11881): uid=10009(com.example.quiz_game_new) identical 27 lines
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(11881): Stop listening
D/SpeechToTextPlugin(11881): Notify status:notListening
D/SpeechToTextPlugin(11881): Notify status:done
D/SpeechToTextPlugin(11881): Stop listening done
I/flutter (11881): 20:44:48.865 - Received listener status: notListening, listening: false
D/SpeechToTextPlugin(11881): rmsDB -2.0 / 10.0
D/SpeechToTextPlugin(11881): Calling results callback
I/flutter (11881): 20:44:48.901 - Result listener final: false, words: check
D/SpeechToTextPlugin(11881): Error 7 after start at 2845 -2.0 / 10.0
I/flutter (11881): 20:44:49.506 - Received error status: SpeechRecognitionError msg: error_no_match, permanent: true, listening: false
D/SpeechToTextPlugin(11881): Error 7 after start at 5845 -2.0 / 10.0
I/flutter (11881): 20:44:52.507 - Received error status: SpeechRecognitionError msg: error_no_match, permanent: true, listening: false

@searleser97
Copy link

I posted this solution in StackOverflow which worked perfectly on my Google Pixel 6 Pro device https://stackoverflow.com/a/73597058/13079132

@AndreVicencio
Copy link

Hello, Is the continuous listening issue exclusive to flutter? Will continous listening not be an issue if simply using native android?

@sowens-csd
Copy link
Contributor

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.

@yanmingLiu
Copy link

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.
    );
  }
}

How to destroy speech if MyHomePage is placed on the second page?

 @override
  void dispose() {
    speech?.stop();
    speech?.cancel();
    super.dispose();
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants