Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/flutter_tts/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 1.3.0

* Limit the range of `setSpeechRate` to be between 0.0 and 1.0.
* Unsupport `getSpeechRateValidRange`.
* Update flutter_tts to 3.5.0.
* Update the example app.

## 1.2.1

* Refactor the C++ code.
Expand Down
6 changes: 3 additions & 3 deletions packages/flutter_tts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

[![pub package](https://img.shields.io/pub/v/flutter_tts_tizen.svg)](https://pub.dev/packages/flutter_tts_tizen)

The tizen implementation of [`flutter_tts`](https://github.com/dlutton/flutter_tts).
The Tizen implementation of [`flutter_tts`](https://github.com/dlutton/flutter_tts).

## Getting Started

This package is not an _endorsed_ implementation of `flutter_tts`. Therefore, you have to include `flutter_tts_tizen` alongside `flutter_tts` as dependencies in your `pubspec.yaml` file.

```yaml
dependencies:
flutter_tts: ^3.2.2
flutter_tts_tizen: ^1.2.0
flutter_tts: ^3.5.0
flutter_tts_tizen: ^1.3.0
```

Then you can import `flutter_tts` in your Dart code:
Expand Down
11 changes: 8 additions & 3 deletions packages/flutter_tts/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class _MyAppState extends State<MyApp> {
initTts() {
flutterTts = FlutterTts();

_setAwaitOptions();

flutterTts.setStartHandler(() {
setState(() {
print("Playing");
Expand Down Expand Up @@ -88,12 +90,15 @@ class _MyAppState extends State<MyApp> {

if (_newVoiceText != null) {
if (_newVoiceText!.isNotEmpty) {
await flutterTts.awaitSpeakCompletion(true);
await flutterTts.speak(_newVoiceText!);
}
}
}

Future _setAwaitOptions() async {
await flutterTts.awaitSpeakCompletion(true);
}

Future _stop() async {
var result = await flutterTts.stop();
if (result == 1) setState(() => ttsState = TtsState.stopped);
Expand Down Expand Up @@ -241,8 +246,8 @@ class _MyAppState extends State<MyApp> {
setState(() => rate = newRate);
},
min: 0.0,
max: 15.0,
divisions: 15,
max: 1.0,
divisions: 10,
label: "Rate: $rate",
activeColor: Colors.green,
);
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter_tts/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ publish_to: "none"
dependencies:
flutter:
sdk: flutter
flutter_tts: ^3.2.2
flutter_tts: ^3.5.0
flutter_tts_tizen:
path: ../

Expand Down
4 changes: 2 additions & 2 deletions packages/flutter_tts/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: flutter_tts_tizen
description: The tizen implementation of flutter_tts plugin.
description: The Tizen implementation of flutter_tts plugin.
homepage: https://github.com/flutter-tizen/plugins
repository: https://github.com/flutter-tizen/plugins/tree/master/packages/flutter_tts
version: 1.2.1
version: 1.3.0

dependencies:
flutter:
Expand Down
107 changes: 40 additions & 67 deletions packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <flutter/plugin_registrar.h>
#include <flutter/standard_method_codec.h>

#include <algorithm>
#include <memory>
#include <optional>
#include <string>
Expand Down Expand Up @@ -38,45 +39,35 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {

tts_ = std::make_unique<TextToSpeech>();
if (!tts_->Initialize()) {
// TODO : Handle initialization failure cases
// Rarely, initializing TextToSpeech can fail. we should consider catching
// the exception and propagating it to the flutter side. however, I think
// this is optional because flutter side is not expecting any errors.
tts_ = nullptr;
return;
}
tts_->SetStateChanagedCallback(
[this](tts_state_e previous, tts_state_e current) -> void {
std::unique_ptr<flutter::EncodableValue> value =
std::make_unique<flutter::EncodableValue>(true);
if (current == TTS_STATE_PLAYING) {
if (previous == TTS_STATE_READY) {
channel_->InvokeMethod("speak.onStart", std::move(value));
} else if (previous == TTS_STATE_PAUSED) {
channel_->InvokeMethod("speak.onContinue", std::move(value));
}
} else if (current == TTS_STATE_PAUSED) {
channel_->InvokeMethod("speak.onPause", std::move(value));
} else if (current == TTS_STATE_READY) {
if (previous == TTS_STATE_PLAYING || previous == TTS_STATE_PAUSED) {
// utterance ID is not zero during speaking and pausing.
if (tts_->GetUttId()) {
channel_->InvokeMethod("speak.onCancel", std::move(value));
HandleAwaitSpeakCompletion(0);
}
}
tts_->SetStateChanagedCallback([this](TtsState previous, TtsState current) {
std::unique_ptr<flutter::EncodableValue> value =
std::make_unique<flutter::EncodableValue>(true);
if (current == TtsState::kPlaying) {
if (previous == TtsState::kReady) {
channel_->InvokeMethod("speak.onStart", std::move(value));
} else if (previous == TtsState::kPaused) {
channel_->InvokeMethod("speak.onContinue", std::move(value));
}
} else if (current == TtsState::kPaused) {
channel_->InvokeMethod("speak.onPause", std::move(value));
} else if (current == TtsState::kReady) {
if (previous == TtsState::kPlaying || previous == TtsState::kPaused) {
// The utterance ID is not zero while speaking and pausing.
if (tts_->GetUttId()) {
channel_->InvokeMethod("speak.onCancel", std::move(value));
HandleAwaitSpeakCompletion(flutter::EncodableValue(0));
}
});

tts_->SetUtteranceCompletedCallback([this](int32_t utt_id) -> void {
}
}
});
tts_->SetUtteranceCompletedCallback([this](int32_t utt_id) {
std::unique_ptr<flutter::EncodableValue> args =
std::make_unique<flutter::EncodableValue>(true);
channel_->InvokeMethod("speak.onComplete", std::move(args));
HandleAwaitSpeakCompletion(1);
});

tts_->SetErrorCallback([](int32_t utt_id, tts_error_e reason) -> void {
// It seems unnecessary for now.
HandleAwaitSpeakCompletion(flutter::EncodableValue(1));
});
}

Expand All @@ -85,21 +76,17 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
private:
void HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue> &method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
// Keep in sync with the return values implemented in:
// https://github.com/dlutton/flutter_tts/blob/master/android/src/main/java/com/tundralabs/fluttertts/FlutterTtsPlugin.java.
// In principle, MethodResult was designed to call MethodResult.Error() to
// notify the dart code of any method call failures from the host platform.
// However, in the case of flutter_tts, it expects a return value 0 on
// failure(and value 1 on success). Therefore in the scope of this plugin,
// we call SendResult(flutter::EncodableValue(0)); to notify errors.
std::unique_ptr<FlMethodResult> result) {
// Keep the return values consistent with the Android implementation.
// The Dart side of this plugin expects a return value of 1 on success and
// 0 on failure, but not result->Error() as other plugins normally expect.
const auto method_name = method_call.method_name();
const auto &arguments = *method_call.arguments();

result_ = std::move(result);

if (!tts_) {
result_->Error("Operation failed", "TTS is invalid.");
result_->Error("Operation failed", "TTS initialization failed.");
return;
}

Expand All @@ -111,8 +98,6 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
OnStop();
} else if (method_name == "pause") {
OnPause();
} else if (method_name == "getSpeechRateValidRange") {
OnGetSpeechRateValidRange();
} else if (method_name == "setSpeechRate") {
OnSetSpeechRate(arguments);
} else if (method_name == "setLanguage") {
Expand Down Expand Up @@ -180,25 +165,15 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
}
}

void OnGetSpeechRateValidRange() {
int32_t min = 0, normal = 0, max = 0;
tts_->GetSpeedRange(&min, &normal, &max);
// FIXME: The value of "platform" is temporarily set to "android" instead of
// "tizen". Because it is not declared in TextToSpeechPlatform of
// TextToSpeech example.
flutter::EncodableMap map = {
{flutter::EncodableValue("min"), flutter::EncodableValue(min)},
{flutter::EncodableValue("normal"), flutter::EncodableValue(normal)},
{flutter::EncodableValue("max"), flutter::EncodableValue(max)},
{flutter::EncodableValue("platform"),
flutter::EncodableValue("android")},
};
SendResult(flutter::EncodableValue(map));
}

void OnSetSpeechRate(const flutter::EncodableValue &arguments) {
if (std::holds_alternative<double>(arguments)) {
int32_t speed = std::get<double>(arguments);
double rate = std::get<double>(arguments);
rate = std::max(rate, 0.0);
rate = std::min(rate, 1.0);
// Scale the value to be between the supported range.
int32_t min = 0, normal = 0, max = 0;
tts_->GetSpeedRange(&min, &normal, &max);
int32_t speed = min + (max - min) * rate;
tts_->SetTtsSpeed(speed);
SendResult(flutter::EncodableValue(1));
return;
Expand Down Expand Up @@ -236,17 +211,15 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
}

void SendResult(const flutter::EncodableValue &result) {
if (!result_) {
return;
if (result_) {
result_->Success(result);
result_ = nullptr;
}
result_->Success(result);
result_ = nullptr;
}

void HandleAwaitSpeakCompletion(int32_t value) {
void HandleAwaitSpeakCompletion(const flutter::EncodableValue &result) {
if (await_speak_completion_) {
result_for_await_speak_completion_->Success(
flutter::EncodableValue(value));
result_for_await_speak_completion_->Success(result);
result_for_await_speak_completion_ = nullptr;
}
}
Expand Down
47 changes: 27 additions & 20 deletions packages/flutter_tts/tizen/src/text_to_speech.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@

#include "log.h"

namespace {

TtsState ConvertTtsState(tts_state_e state) {
switch (state) {
case TTS_STATE_CREATED:
return TtsState::kCreated;
case TTS_STATE_READY:
return TtsState::kReady;
case TTS_STATE_PLAYING:
return TtsState::kPlaying;
case TTS_STATE_PAUSED:
return TtsState::kPaused;
}
}

} // namespace

TextToSpeech::~TextToSpeech() {
UnregisterCallbacks();

Expand Down Expand Up @@ -61,24 +78,25 @@ void TextToSpeech::RegisterCallbacks() {
[](tts_h tts, tts_state_e previous, tts_state_e current,
void *user_data) {
TextToSpeech *self = static_cast<TextToSpeech *>(user_data);
self->OnStateChanged(previous, current);
self->SwitchVolumeOnStateChange(previous, current);
self->state_changed_callback_(ConvertTtsState(previous),
ConvertTtsState(current));
},
this);

tts_set_utterance_completed_cb(
tts_,
[](tts_h tts, int32_t utt_id, void *user_data) {
TextToSpeech *self = static_cast<TextToSpeech *>(user_data);
self->OnUtteranceCompleted(utt_id);
// Explicitly call stop method to change the tts state to ready.
self->utterance_completed_callback_(utt_id);
self->ClearUttId();
// Explicitly call Stop() to change the TTS state to ready.
self->Stop();
},
this);
tts_set_error_cb(
tts_,
[](tts_h tts, int32_t utt_id, tts_error_e reason, void *user_data) {
TextToSpeech *self = static_cast<TextToSpeech *>(user_data);
self->OnError(utt_id, reason);
LOG_ERROR("TTS error: utt_id(%d), reason(%d)", utt_id, reason);
},
this);
}
Expand All @@ -100,7 +118,7 @@ std::vector<std::string> &TextToSpeech::GetSupportedLanaguages() {
}
TextToSpeech *self = static_cast<TextToSpeech *>(user_data);
self->supported_lanaguages_.push_back(std::string(language));
LOG_INFO("Supported Voices - Language(%s), Type(%d)", language,
LOG_INFO("Supported voice: language(%s), type(%d)", language,
voice_type);
return true;
},
Expand All @@ -120,21 +138,10 @@ std::optional<TtsState> TextToSpeech::GetState() {
LOG_ERROR("tts_get_state failed: %s", get_error_message(ret));
return std::nullopt;
}
switch (state) {
case TTS_STATE_CREATED:
return TtsState::kCreated;
case TTS_STATE_READY:
return TtsState::kReady;
case TTS_STATE_PLAYING:
return TtsState::kPlaying;
case TTS_STATE_PAUSED:
return TtsState::kPaused;
default:
return std::nullopt;
}
return ConvertTtsState(state);
}

bool TextToSpeech::AddText(std::string text) {
bool TextToSpeech::AddText(const std::string &text) {
int ret = tts_add_text(tts_, text.c_str(), default_language_.c_str(),
default_voice_type_, tts_speed_, &utt_id_);
if (ret != TTS_ERROR_NONE) {
Expand Down
Loading