Skip to content

Commit 03c793e

Browse files
committed
[WIP][flutter_tts] Refactor the C++
- Extract method. - Hiding tizen api in plugin.cc - Enhance error message handling (working) - Minimize use of output parameters - Change method name(Init-> TtsCreate, DeInit -> TtsDestroy, ...)
1 parent 6ec410e commit 03c793e

File tree

6 files changed

+266
-164
lines changed

6 files changed

+266
-164
lines changed

packages/flutter_tts/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.2.1
2+
3+
* Refactor the C++ code.
4+
15
## 1.2.0
26

37
* Fix a bug where `setLanguage()` wasn't invoked due to typo in `setLanguage`.

packages/flutter_tts/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: flutter_tts_tizen
22
description: The tizen implementation of flutter_tts plugin.
33
homepage: https://github.com/flutter-tizen/plugins
44
repository: https://github.com/flutter-tizen/plugins/tree/master/packages/flutter_tts
5-
version: 1.2.0
5+
version: 1.2.1
66

77
dependencies:
88
flutter:

packages/flutter_tts/tizen/inc/flutter_tts_tizen_plugin.h

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Copyright 2021 Samsung Electronics Co., Ltd. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
15
#ifndef FLUTTER_PLUGIN_FLUTTER_TTS_TIZEN_PLUGIN_H_
26
#define FLUTTER_PLUGIN_FLUTTER_TTS_TIZEN_PLUGIN_H_
37

packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc

+182-98
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@
88
#include <flutter/plugin_registrar.h>
99
#include <flutter/standard_method_codec.h>
1010

11-
#include <map>
1211
#include <memory>
13-
#include <sstream>
1412
#include <string>
1513
#include <vector>
1614

1715
#include "log.h"
1816
#include "text_to_speech.h"
1917

18+
namespace {
19+
20+
typedef flutter::MethodChannel<flutter::EncodableValue> FlMethodChannel;
21+
typedef flutter::MethodResult<flutter::EncodableValue> FlMethodResult;
22+
2023
class FlutterTtsTizenPlugin : public flutter::Plugin {
2124
public:
2225
static void RegisterWithRegistrar(flutter::PluginRegistrar *registrar) {
@@ -25,15 +28,25 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
2528
}
2629

2730
FlutterTtsTizenPlugin(flutter::PluginRegistrar *registrar) {
28-
channel_ =
29-
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
30-
registrar->messenger(), "flutter_tts",
31-
&flutter::StandardMethodCodec::GetInstance());
31+
channel_ = std::make_unique<FlMethodChannel>(
32+
registrar->messenger(), "flutter_tts",
33+
&flutter::StandardMethodCodec::GetInstance());
3234
channel_->SetMethodCallHandler([this](const auto &call, auto result) {
3335
this->HandleMethodCall(call, std::move(result));
3436
});
3537

3638
tts_ = std::make_unique<TextToSpeech>();
39+
try {
40+
tts_->Initialize();
41+
} catch (const TextToSpeechError &error) {
42+
// TODO : Handle initialization failure cases
43+
// Rarely, initializing TextToSpeech can fail. we should consider catching
44+
// the exception and propagating it to the flutter side. however, I think
45+
// this is optional because flutter side is not expecting any errors.
46+
LOG_ERROR("Operation failed : %s", error.GetErrorString().c_str());
47+
tts_ = nullptr;
48+
return;
49+
}
3750
tts_->SetOnStateChanagedCallback(
3851
[this](tts_state_e previous, tts_state_e current) -> void {
3952
std::unique_ptr<flutter::EncodableValue> value =
@@ -76,108 +89,177 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
7689
void HandleMethodCall(
7790
const flutter::MethodCall<flutter::EncodableValue> &method_call,
7891
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
79-
// Keep in sync with the return values implemented in:
80-
// https://github.com/dlutton/flutter_tts/blob/master/android/src/main/java/com/tundralabs/fluttertts/FlutterTtsPlugin.java.
81-
// In principle, MethodResult was designed to call MethodResult.Error() to
82-
// notify the dart code of any method call failures from the host platform.
83-
// However, in the case of flutter_tts, it expects a return value 0 on
84-
// failure(and value 1 on success). Therefore in the scope of this plugin,
85-
// we call result->Success(flutter::EncodableValue(0)) to notify errors.
86-
8792
const auto method_name = method_call.method_name();
8893
const auto &arguments = *method_call.arguments();
8994

90-
if (method_name.compare("awaitSpeakCompletion") == 0) {
91-
if (std::holds_alternative<bool>(arguments)) {
92-
await_speak_completion_ = std::get<bool>(arguments);
93-
result->Success(flutter::EncodableValue(1));
94-
return;
95-
}
96-
result->Success(flutter::EncodableValue(0));
97-
} else if (method_name.compare("speak") == 0) {
98-
if (tts_->GetState() == TTS_STATE_PLAYING) {
99-
LOG_ERROR("[TTS] : You cannot speak again while speaking.");
100-
result->Success(flutter::EncodableValue(0));
95+
result_ = std::move(result);
96+
97+
if (!tts_) {
98+
result_->Error("Operation failed", "TTS is invalid.");
99+
return;
100+
}
101+
102+
if (method_name == "awaitSpeakCompletion") {
103+
OnAwaitSpeakCompletion(arguments);
104+
} else if (method_name == "speak") {
105+
OnSpeak(arguments);
106+
} else if (method_name == "stop") {
107+
OnStop();
108+
} else if (method_name == "pause") {
109+
OnPause();
110+
} else if (method_name == "getSpeechRateValidRange") {
111+
OnGetSpeechRateValidRange();
112+
} else if (method_name == "setSpeechRate") {
113+
OnSetSpeechRate(arguments);
114+
} else if (method_name == "setLanguage") {
115+
OnSetLanguage(arguments);
116+
} else if (method_name == "getLanguages") {
117+
OnGetLanguage();
118+
} else if (method_name == "setVolume") {
119+
OnSetVolume(arguments);
120+
} else {
121+
result_->NotImplemented();
122+
}
123+
}
124+
125+
void OnAwaitSpeakCompletion(const flutter::EncodableValue &arguments) {
126+
if (std::holds_alternative<bool>(arguments)) {
127+
await_speak_completion_ = std::get<bool>(arguments);
128+
SendResult(flutter::EncodableValue(1));
129+
return;
130+
}
131+
SendErrorResult("Invalid argument", "Argument is invaild.");
132+
}
133+
134+
void OnSpeak(const flutter::EncodableValue &arguments) {
135+
try {
136+
if (tts_->GetState() == TtsState::kPlaying) {
137+
SendErrorResult("Operation cancelled",
138+
"You cannot speak again while speaking.");
101139
return;
102140
}
103141

104142
if (std::holds_alternative<std::string>(arguments)) {
105143
std::string text = std::get<std::string>(arguments);
106-
if (!tts_->AddText(text)) {
107-
result->Success(flutter::EncodableValue(0));
108-
return;
109-
}
144+
tts_->AddText(text);
110145
}
111146

112-
if (tts_->Speak()) {
113-
if (await_speak_completion_ && !result_for_await_speak_completion_) {
114-
LOG_DEBUG("Store result ptr for await speak completion");
115-
result_for_await_speak_completion_ = std::move(result);
116-
} else {
117-
result->Success(flutter::EncodableValue(1));
118-
}
119-
} else {
120-
result->Success(flutter::EncodableValue(0));
121-
}
122-
} else if (method_name.compare("stop") == 0) {
123-
if (tts_->Stop()) {
124-
result->Success(flutter::EncodableValue(1));
125-
} else {
126-
result->Success(flutter::EncodableValue(0));
127-
}
128-
} else if (method_name.compare("pause") == 0) {
129-
if (tts_->Pause()) {
130-
result->Success(flutter::EncodableValue(1));
131-
} else {
132-
result->Success(flutter::EncodableValue(0));
133-
}
134-
} else if (method_name.compare("getSpeechRateValidRange") == 0) {
135-
int min = 0, normal = 0, max = 0;
147+
tts_->Speak();
148+
149+
} catch (const TextToSpeechError &error) {
150+
SendErrorResult("Operation failed", error.GetErrorString());
151+
}
152+
153+
if (await_speak_completion_ && !result_for_await_speak_completion_) {
154+
LOG_DEBUG("Store result ptr for await speak completion");
155+
result_for_await_speak_completion_ = std::move(result_);
156+
} else {
157+
SendResult(flutter::EncodableValue(1));
158+
}
159+
}
160+
161+
void OnStop() {
162+
try {
163+
tts_->Stop();
164+
} catch (const TextToSpeechError &error) {
165+
SendErrorResult("Operation failed", error.GetErrorString());
166+
}
167+
SendResult(flutter::EncodableValue(1));
168+
}
169+
170+
void OnPause() {
171+
try {
172+
tts_->Pause();
173+
} catch (const TextToSpeechError &error) {
174+
SendErrorResult("Operation failed", error.GetErrorString());
175+
}
176+
SendResult(flutter::EncodableValue(1));
177+
}
178+
179+
void OnGetSpeechRateValidRange() {
180+
int min = 0, normal = 0, max = 0;
181+
try {
136182
tts_->GetSpeedRange(&min, &normal, &max);
137-
flutter::EncodableMap map;
138-
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
139-
"min", min));
140-
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
141-
"normal", normal));
142-
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
143-
"max", max));
144-
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
145-
"platform", "tizen"));
146-
result->Success(flutter::EncodableValue(std::move(map)));
147-
} else if (method_name.compare("setSpeechRate") == 0) {
148-
if (std::holds_alternative<double>(arguments)) {
149-
int speed = (int)std::get<double>(arguments);
150-
tts_->SetTtsSpeed(speed);
151-
result->Success(flutter::EncodableValue(1));
152-
return;
153-
}
154-
result->Success(flutter::EncodableValue(0));
155-
} else if (method_name.compare("setLanguage") == 0) {
156-
if (std::holds_alternative<std::string>(arguments)) {
157-
std::string language = std::move(std::get<std::string>(arguments));
158-
tts_->SetDefaultLanguage(language);
159-
result->Success(flutter::EncodableValue(1));
183+
} catch (const TextToSpeechError &error) {
184+
SendErrorResult("Operation failed", error.GetErrorString());
185+
return;
186+
}
187+
flutter::EncodableMap map;
188+
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
189+
"min", min));
190+
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
191+
"normal", normal));
192+
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
193+
"max", max));
194+
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
195+
"platform", "tizen"));
196+
SendResult(flutter::EncodableValue(std::move(map)));
197+
}
198+
199+
void OnSetSpeechRate(const flutter::EncodableValue &arguments) {
200+
if (std::holds_alternative<double>(arguments)) {
201+
int speed = (int)std::get<double>(arguments);
202+
tts_->SetTtsSpeed(speed);
203+
SendResult(flutter::EncodableValue(1));
204+
return;
205+
}
206+
SendErrorResult("Invalid argument", "SpeechRate is invaild.");
207+
}
208+
209+
void OnSetLanguage(const flutter::EncodableValue &arguments) {
210+
if (std::holds_alternative<std::string>(arguments)) {
211+
std::string language = std::move(std::get<std::string>(arguments));
212+
tts_->SetDefaultLanguage(language);
213+
SendResult(flutter::EncodableValue(1));
214+
return;
215+
}
216+
SendErrorResult("Invalid argument", "Language is invaild.");
217+
}
218+
219+
void OnGetLanguage() {
220+
flutter::EncodableList list;
221+
for (auto language : tts_->GetSupportedLanaguages()) {
222+
list.push_back(flutter::EncodableValue(language));
223+
}
224+
SendResult(flutter::EncodableValue(list));
225+
}
226+
227+
void OnSetVolume(const flutter::EncodableValue &arguments) {
228+
if (std::holds_alternative<double>(arguments)) {
229+
double volume = std::get<double>(arguments);
230+
try {
231+
tts_->SetVolume(volume);
232+
} catch (const TextToSpeechError &error) {
233+
SendErrorResult("Operation failed", error.GetErrorString());
160234
return;
161235
}
162-
result->Success(flutter::EncodableValue(0));
163-
} else if (method_name.compare("getLanguages") == 0) {
164-
flutter::EncodableList list;
165-
for (auto language : tts_->GetSupportedLanaguages()) {
166-
list.push_back(flutter::EncodableValue(language));
167-
}
168-
result->Success(flutter::EncodableValue(list));
169-
} else if (method_name.compare("setVolume") == 0) {
170-
if (std::holds_alternative<double>(arguments)) {
171-
double rate = std::get<double>(arguments);
172-
if (tts_->SetVolume(rate)) {
173-
result->Success(flutter::EncodableValue(1));
174-
return;
175-
}
176-
}
177-
result->Success(flutter::EncodableValue(0));
178-
} else {
179-
result->Error("-1", "Not supported method");
180236
}
237+
SendErrorResult("Invalid argument", "Volume is invaild.");
238+
}
239+
240+
void SendResult(const flutter::EncodableValue &result) {
241+
if (!result_) {
242+
return;
243+
}
244+
result_->Success(result);
245+
result_ = nullptr;
246+
}
247+
248+
void SendErrorResult(const std::string &error_code,
249+
const std::string &error_message) {
250+
// Keep in sync with the return values implemented in:
251+
// https://github.com/dlutton/flutter_tts/blob/master/android/src/main/java/com/tundralabs/fluttertts/FlutterTtsPlugin.java.
252+
// In principle, MethodResult was designed to call MethodResult.Error() to
253+
// notify the dart code of any method call failures from the host platform.
254+
// However, in the case of flutter_tts, it expects a return value 0 on
255+
// failure(and value 1 on success). Therefore in the scope of this plugin,
256+
// we call result_->Success(flutter::EncodableValue(0)) to notify errors.
257+
if (!result_) {
258+
return;
259+
}
260+
LOG_ERROR("%s", std::string(error_code + " : " + error_message).c_str());
261+
result_->Success(flutter::EncodableValue(0));
262+
result_ = nullptr;
181263
}
182264

183265
void HandleAwaitSpeakCompletion(int value) {
@@ -190,13 +272,15 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
190272
}
191273

192274
std::unique_ptr<TextToSpeech> tts_;
193-
std::unique_ptr<flutter::MethodChannel<flutter::EncodableValue>> channel_;
194-
195275
bool await_speak_completion_ = false;
196-
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>
197-
result_for_await_speak_completion_;
276+
277+
std::unique_ptr<FlMethodResult> result_for_await_speak_completion_;
278+
std::unique_ptr<FlMethodResult> result_;
279+
std::unique_ptr<FlMethodChannel> channel_;
198280
};
199281

282+
} // namespace
283+
200284
void FlutterTtsTizenPluginRegisterWithRegistrar(
201285
FlutterDesktopPluginRegistrarRef registrar) {
202286
FlutterTtsTizenPlugin::RegisterWithRegistrar(

0 commit comments

Comments
 (0)