csdcorp / speech_to_text

A Flutter plugin that exposes device specific text to speech recognition capability.
BSD 3-Clause "New" or "Revised" License
351 stars 218 forks source link

Timeout period is too short for continuous listening #429

Closed ParthSolankii closed 6 months ago

ParthSolankii commented 8 months ago

I tried this, but haven't found any way to listen to speech continuously even user does not speak anything,

Currently, the issue is when the user clicks on the mic button it starts listening for 2 or 3 seconds and stops listening automatically. When the user says anything and completes speaking, the mic also stops the very next second. and when the user doesn't say anything it stops listening in 2 seconds.

I want to keep the mic on and listen to the speech for an hour or some 10 or 15 minutes, in both cases if a user is speaking or not until I kill the app,

chaitanya-mandala commented 8 months ago

@ParthSolanki0 did you find any solution for yhis?

ParthSolankii commented 8 months ago

@chaitanya-mandala No, Haven't found anything yet, also stuck with the default sound playing when start and stop listening, which I want to disable.

onursahindur commented 8 months ago

Hello,

I try to solve a similar problem. Finally, I put somethings work by this way:

Initialize the library with "onStatus" callback For example:

await _speechToText.initialize(
  onStatus: (status) async {
    debugPrint("status: $status");
    if (_isRecording && status == "notListening") {
      debugPrint("status: should continue");
      await _speechToText.stop();
      await _startListeningFromMic();
    }
  },
);

As you may follow from the inside function, I check a _isRecording flag when a user pressed the microphone button. I keep that boolean as true whenever user stops the microphone.

In the callback, I check the status that the library calls back, if it equals to notListening, but I am actually listening, I stop the speech to text and start listening again.

I also made some arrangements to listen function of the library, too. First I used ListenMode.dictation, Second which is important for me; used partialResults: false . By that way I get the results after complete and put them into a class variable.

Please check these other codes too;

void _startRecording() async {
  setState(() {
    _isRecording = true;
    _speechToTextResults = [];
  });
  await _startListeningFromMic();
}

Future<void> _startListeningFromMic() async {
  await _speechToText.listen(
    pauseFor: const Duration(seconds: 10),
    localeId: "en-US",
    listenMode: ListenMode.dictation,
    partialResults: false,
    onResult: (SpeechRecognitionResult result) {
      print("status: result: ${result.recognizedWords}");
      _speechToTextResults.add(result.recognizedWords);
    }
  );
}

void _stopRecording() async {
  debugPrint('isRecording: false');
  setState(() {
    _isRecording = false;
  });
  debugPrint("all recognized words: ${_speechToTextResults.join(" ")}");
}

Hope this helps you all or even give you some idea to solve your problems too.

PS: I am still testing this approach, I am not 100% sure this will solve the problem.

ParthSolankii commented 8 months ago

Hi @onursahindur I tried this way but unfortunately, it's not working for continuous listening to the user.

However, I have found a solution for this to keep listening continuously. @chaitanya-mandala , @onursahindur

SaudSultan2001 commented 8 months ago

Hi @ParthSolanki0 could you kindly share how you achieved continuous listening, as I am also struggling with this issue.

It would be great help, thank you in advance.

ParthSolankii commented 8 months ago

Hi @SaudSultan2001 ,

You just need to start the listening function repeatedly when speech-to-text is finished or not listening.

Shivam-dev925 commented 8 months ago

Hello,

I try to solve a similar problem. Finally, I put somethings work by this way:

Initialize the library with "onStatus" callback For example:

await _speechToText.initialize(
  onStatus: (status) async {
    debugPrint("status: $status");
    if (_isRecording && status == "notListening") {
      debugPrint("status: should continue");
      await _speechToText.stop();
      await _startListeningFromMic();
    }
  },
);

As you may follow from the inside function, I check a _isRecording flag when a user pressed the microphone button. I keep that boolean as true whenever user stops the microphone.

In the callback, I check the status that the library calls back, if it equals to notListening, but I am actually listening, I stop the speech to text and start listening again.

I also made some arrangements to listen function of the library, too. First I used ListenMode.dictation, Second which is important for me; used partialResults: false . By that way I get the results after complete and put them into a class variable.

Please check these other codes too;

void _startRecording() async {
  setState(() {
    _isRecording = true;
    _speechToTextResults = [];
  });
  await _startListeningFromMic();
}

Future<void> _startListeningFromMic() async {
  await _speechToText.listen(
    pauseFor: const Duration(seconds: 10),
    localeId: "en-US",
    listenMode: ListenMode.dictation,
    partialResults: false,
    onResult: (SpeechRecognitionResult result) {
      print("status: result: ${result.recognizedWords}");
      _speechToTextResults.add(result.recognizedWords);
    }
  );
}

void _stopRecording() async {
  debugPrint('isRecording: false');
  setState(() {
    _isRecording = false;
  });
  debugPrint("all recognized words: ${_speechToTextResults.join(" ")}");
}

Hope this helps you all or even give you some idea to solve your problems too.

PS: I am still testing this approach, I am not 100% sure this will solve the problem.

Hello,

I try to solve a similar problem. Finally, I put somethings work by this way:

Initialize the library with "onStatus" callback For example:

await _speechToText.initialize(
  onStatus: (status) async {
    debugPrint("status: $status");
    if (_isRecording && status == "notListening") {
      debugPrint("status: should continue");
      await _speechToText.stop();
      await _startListeningFromMic();
    }
  },
);

As you may follow from the inside function, I check a _isRecording flag when a user pressed the microphone button. I keep that boolean as true whenever user stops the microphone.

In the callback, I check the status that the library calls back, if it equals to notListening, but I am actually listening, I stop the speech to text and start listening again.

I also made some arrangements to listen function of the library, too. First I used ListenMode.dictation, Second which is important for me; used partialResults: false . By that way I get the results after complete and put them into a class variable.

Please check these other codes too;

void _startRecording() async {
  setState(() {
    _isRecording = true;
    _speechToTextResults = [];
  });
  await _startListeningFromMic();
}

Future<void> _startListeningFromMic() async {
  await _speechToText.listen(
    pauseFor: const Duration(seconds: 10),
    localeId: "en-US",
    listenMode: ListenMode.dictation,
    partialResults: false,
    onResult: (SpeechRecognitionResult result) {
      print("status: result: ${result.recognizedWords}");
      _speechToTextResults.add(result.recognizedWords);
    }
  );
}

void _stopRecording() async {
  debugPrint('isRecording: false');
  setState(() {
    _isRecording = false;
  });
  debugPrint("all recognized words: ${_speechToTextResults.join(" ")}");
}

Hope this helps you all or even give you some idea to solve your problems too.

PS: I am still testing this approach, I am not 100% sure this will solve the problem.

i have done the same it is working in mobiles and web also but when i stops talking the onResult callback is not giving me finalWords equal to true because to show the text in a streaming way i have to show the words which are correct (it behaves mixing up the texts if you append the recognized words to the local variable every time

here is the code snippet void _onSpeechResult(SpeechRecognitionResult result) { try { if (result.finalResult) { assistantController.conversation.value += result.recognizedWords; } } catch (e) { print("exception in recognizition ===> $e"); } }

Shivam-dev925 commented 8 months ago

Hello,

I try to solve a similar problem. Finally, I put somethings work by this way:

Initialize the library with "onStatus" callback For example:

await _speechToText.initialize(
  onStatus: (status) async {
    debugPrint("status: $status");
    if (_isRecording && status == "notListening") {
      debugPrint("status: should continue");
      await _speechToText.stop();
      await _startListeningFromMic();
    }
  },
);

As you may follow from the inside function, I check a _isRecording flag when a user pressed the microphone button. I keep that boolean as true whenever user stops the microphone.

In the callback, I check the status that the library calls back, if it equals to notListening, but I am actually listening, I stop the speech to text and start listening again.

I also made some arrangements to listen function of the library, too. First I used ListenMode.dictation, Second which is important for me; used partialResults: false . By that way I get the results after complete and put them into a class variable.

Please check these other codes too;

void _startRecording() async {
  setState(() {
    _isRecording = true;
    _speechToTextResults = [];
  });
  await _startListeningFromMic();
}

Future<void> _startListeningFromMic() async {
  await _speechToText.listen(
    pauseFor: const Duration(seconds: 10),
    localeId: "en-US",
    listenMode: ListenMode.dictation,
    partialResults: false,
    onResult: (SpeechRecognitionResult result) {
      print("status: result: ${result.recognizedWords}");
      _speechToTextResults.add(result.recognizedWords);
    }
  );
}

void _stopRecording() async {
  debugPrint('isRecording: false');
  setState(() {
    _isRecording = false;
  });
  debugPrint("all recognized words: ${_speechToTextResults.join(" ")}");
}

Hope this helps you all or even give you some idea to solve your problems too.

PS: I am still testing this approach, I am not 100% sure this will solve the problem.

NOTE: this problem coming in web only

ichitaka commented 8 months ago

I created a quick PR where I solve the timeout issue while preserving the partialResults functionality. #436

@Shivam-dev925

Shivam-dev925 commented 7 months ago

I created a quick PR where I solve the timeout issue while preserving the partialResults functionality. #436

@Shivam-dev925

hii ,Thankyou for your changes and update for my message @ichitaka i have tested your changes locally on webapp it working fine but there is another problem which is coming on again ( webapp only )
problem is when i stops talking then in recognition results callback finalResults are not comming unless i manually stops the listening

ichitaka commented 7 months ago

Could you provide an example of the callback you mean? I get the true flag for ...finalResult after the pauseFor time is up in the preview of the example app.

Shivam-dev925 commented 7 months ago

Okay I will share the entire code related to recognition Thanks

Shivam-dev925 commented 7 months ago

@ichitaka This is how implemented void _initSpeech() async { print("speechToText initialized "); try { await _speechToText.initialize( onStatus: (status) { print("status ===> $status"); print("micEnabled==>$micEnabled"); if (status == "done" && micEnabled) { print(status);

          _startListening();
        }
      },
      debugLogging: true);
  speechToTextErrorListener();
} catch (e) {
  print("exception  ===> $e");
}

}

/// Starts listening to the user's speech input using the device's default listen mode. /// /// Sets [micEnabled] to true and calls [_onSpeechResult] on each speech recognition result. /// /// Throws an exception if an error occurs during the speech recognition process.

void _startListening() async { print("started listening"); try {

  micEnabled = true;
  await _speechToText.listen(
      onResult: _onSpeechResult,
      listenMode: ListenMode.deviceDefault,
      listenFor: Duration(minutes: 10));
} catch (e) {
  print("exception in start listening ===> $e");
}

}

/// Sets an error listener for the [_speechToText] instance and prints the error message to the console. /// If [micEnabled] is true, it starts listening again.

void speechToTextErrorListener() { _speechToText.errorListener = (value) { print("error in recognizition ===> $value"); if (micEnabled) { _startListening(); } }; }

/// Stops the speech-to-text recognition and updates the state of the widget.

void _stopListening({bool? usinginTimer = false}) async { try { if (!usinginTimer!) { micEnabled = false; } print("stoped recording==>$micEnabled"); await _speechToText.stop(); } catch (e) { print("exception in stop listening ===> $e"); } }

/// This is the callback that the SpeechToText plugin calls when /// the platform returns recognized words. void _onSpeechResult(SpeechRecognitionResult result) { try { if (result.finalResult) { assistantController.conversation.value += result.recognizedWords; _scrollController.animateTo( _scrollController.position.maxScrollExtent, duration: Duration( milliseconds: 300), // You can adjust the duration as needed curve: Curves.easeOut, );

  }
  if (kIsWeb) {
    Future.delayed(Duration(seconds: 4), () async {
      await _speechToText.stop();
    });
  }
} catch (e) {
  print("exception in recognizition ===> $e");
}
ichitaka commented 7 months ago

And do you get any prints from the Exception that you are catching? The example app should have result.finalResult == true, so your code should execute, so the most probably result there should be the try clause. It's good practice to not generally catch all exceptions but only the ones that you expect to catch.

This is sadly not a minimal example for me to reproduce.

Shivam-dev925 commented 7 months ago

Nothing is coming in coming in console But the catch is whenever I am stopping the microphone manually the onStatus callback is coming and the final result is also coming to true ..

ichitaka commented 7 months ago

After waiting 10 minutes, correct? Because you are not setting pauseFor, the event should be triggered after 10 minutes of listening.

Shivam-dev925 commented 7 months ago

Nop, Scenario is given below... I opened the microphone, started taking without a break , stopped taking after 1 min or so , then final result should come which coming in mobile , if I manually stop the recognition then final result ==true is coming and my value is updating on the screen

ichitaka commented 7 months ago

But you are not setting pauseFor to 1 minute but leave it at null, so why should it stop?

Shivam-dev925 commented 7 months ago

Wait, I am creating a sample web app for you. It will help you understand the issue.