Skip to content

Commit

Permalink
[flutter_tts] Add getVoices and setVoice APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
seungsoo47 committed Nov 18, 2024
1 parent ad1081e commit b349f7a
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 33 deletions.
2 changes: 1 addition & 1 deletion packages/flutter_tts/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Update minimum Flutter and Dart version to 3.13 and 3.1.
* Update flutter_tts 3.6.3 to 4.2.0.
* Update the example app.
* Add isLanguageAvailable API which was not implemented.
* Add isLanguageAvailable, getVoices and setVoice APIs which were not implemented.


## 1.4.0
Expand Down
3 changes: 3 additions & 0 deletions packages/flutter_tts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ The features supported by Tizen are as follows. Other features are not supported
- [x] pause
- [x] get languages
- [x] set language
- [x] is language available
- [x] get voices
- [x] set voice
- [x] set speech rate
- [x] set speech volume (requires privilege `http://tizen.org/privilege/volume.set` in `tizen_manifest.xml`)
- [x] get default voice
Expand Down
11 changes: 10 additions & 1 deletion packages/flutter_tts/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class _MyAppState extends State<MyApp> {

_getDefaultVoice();

_getSupportedVoices();

flutterTts.setStartHandler(() {
setState(() {
print("Playing");
Expand Down Expand Up @@ -90,7 +92,14 @@ class _MyAppState extends State<MyApp> {
Future<void> _getDefaultVoice() async {
var voice = await flutterTts.getDefaultVoice;
if (voice != null) {
print(voice);
print('_getDefaultVoice(): $voice');
}
}

Future<void> _getSupportedVoices() async {
var voices = await flutterTts.getVoices;
if (voices != null) {
print('_getSupportedVoices(): $voices');
}
}

Expand Down
57 changes: 54 additions & 3 deletions packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <flutter/standard_method_codec.h>

#include <algorithm>
#include <map>
#include <memory>
#include <optional>
#include <string>
Expand All @@ -22,6 +23,19 @@ namespace {
typedef flutter::MethodChannel<flutter::EncodableValue> FlMethodChannel;
typedef flutter::MethodResult<flutter::EncodableValue> FlMethodResult;

template <typename T>
bool GetValueFromEncodableMap(flutter::EncodableMap &map, std::string key,
T &out) {
auto iter = map.find(flutter::EncodableValue(key));
if (iter != map.end() && !iter->second.IsNull()) {
if (auto pval = std::get_if<T>(&iter->second)) {
out = *pval;
return true;
}
}
return false;
}

class FlutterTtsTizenPlugin : public flutter::Plugin {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrar *registrar) {
Expand Down Expand Up @@ -103,9 +117,13 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
} else if (method_name == "setLanguage") {
OnSetLanguage(arguments);
} else if (method_name == "getLanguages") {
OnGetLanguage();
OnGetLanguages();
} else if (method_name == "getDefaultVoice") {
OnGetDefaultVoice();
} else if (method_name == "setVoice") {
OnSetVoice(arguments);
} else if (method_name == "getVoices") {
OnGetVoices();
} else if (method_name == "getMaxSpeechInputLength") {
OnGetMaxSpeechInputLength();
} else if (method_name == "setVolume") {
Expand Down Expand Up @@ -197,9 +215,11 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
SendResult(flutter::EncodableValue(0));
}

void OnGetLanguage() {
void OnGetLanguages() {
auto languages = tts_->GetSupportedLanaguages();

flutter::EncodableList list;
for (auto language : tts_->GetSupportedLanaguages()) {
for (auto language : languages) {
list.push_back(flutter::EncodableValue(language));
}
SendResult(flutter::EncodableValue(list));
Expand All @@ -221,6 +241,37 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
SendResult(flutter::EncodableValue(default_voice_map));
}

void OnSetVoice(const flutter::EncodableValue &arguments) {
if (std::holds_alternative<flutter::EncodableMap>(arguments)) {
auto voice = std::get<flutter::EncodableMap>(arguments);

std::string voice_name;
if (GetValueFromEncodableMap(voice, "name", voice_name)) {
tts_->SetDefaultVoiceType(voice_name);
SendResult(flutter::EncodableValue(1));
return;
}
}
SendResult(flutter::EncodableValue(0));
}

void OnGetVoices() {
auto voices = tts_->GetSupportedVoiceTypes();

flutter::EncodableList list;
for (auto voice : voices) {
flutter::EncodableMap map;
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
flutter::EncodableValue("name"),
flutter::EncodableValue(voice["name"])));
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
flutter::EncodableValue("locale"),
flutter::EncodableValue(voice["locale"])));
list.push_back(flutter::EncodableValue(map));
}
SendResult(flutter::EncodableValue(list));
}

void OnGetMaxSpeechInputLength() {
std::optional<int32_t> length = tts_->GetMaxSpeechInputLength();
SendResult(length.has_value() ? flutter::EncodableValue(*length)
Expand Down
78 changes: 51 additions & 27 deletions packages/flutter_tts/tizen/src/text_to_speech.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,35 @@

namespace {

std::string GetVoiceName(int32_t voice_type) {
switch (voice_type) {
case TTS_VOICE_TYPE_AUTO:
return "auto";
case TTS_VOICE_TYPE_MALE:
return "male";
case TTS_VOICE_TYPE_FEMALE:
return "female";
case TTS_VOICE_TYPE_CHILD:
return "child";
default:
return "unknown";
}
}

int32_t GetVoiceType(std::string voice_name) {
if (voice_name == "auto") {
return TTS_VOICE_TYPE_AUTO;
} else if (voice_name == "male") {
return TTS_VOICE_TYPE_MALE;
} else if (voice_name == "female") {
return TTS_VOICE_TYPE_FEMALE;
} else if (voice_name == "child") {
return TTS_VOICE_TYPE_CHILD;
} else {
return -1;
}
}

TtsState ConvertTtsState(tts_state_e state) {
switch (state) {
case TTS_STATE_CREATED:
Expand Down Expand Up @@ -44,6 +73,7 @@ bool TextToSpeech::Initialize() {

RegisterCallbacks();
Prepare();
InitializeSupportedLanaguagesAndVoiceType();

return true;
}
Expand Down Expand Up @@ -107,8 +137,8 @@ void TextToSpeech::UnregisterCallbacks() {
tts_unset_error_cb(tts_);
}

std::vector<std::string> &TextToSpeech::GetSupportedLanaguages() {
if (supported_lanaguages_.size() == 0) {
void TextToSpeech::InitializeSupportedLanaguagesAndVoiceType() {
if (supported_lanaguages_.size() == 0 || supported_voice_types_.size() == 0) {
tts_foreach_supported_voices(
tts_,
[](tts_h tts, const char *language, int32_t voice_type,
Expand All @@ -117,18 +147,26 @@ std::vector<std::string> &TextToSpeech::GetSupportedLanaguages() {
return false;
}
TextToSpeech *self = static_cast<TextToSpeech *>(user_data);
self->supported_lanaguages_.push_back(std::string(language));
LOG_INFO("Supported voice: language(%s), type(%d)", language,
voice_type);
self->supported_lanaguages_.push_back(language);

std::map<std::string, std::string> item;
item["name"] = GetVoiceName(voice_type);
item["locale"] = language;
self->supported_voice_types_.push_back(item);

LOG_INFO("Supported language: %s, voice_type: %s",
item["locale"].c_str(), item["name"].c_str());
return true;
},
this);

supported_lanaguages_.erase(
unique(supported_lanaguages_.begin(), supported_lanaguages_.end()),
supported_lanaguages_.end());
supported_voice_types_.erase(
unique(supported_voice_types_.begin(), supported_voice_types_.end()),
supported_voice_types_.end());
}
return supported_lanaguages_;
}

std::optional<std::pair<std::string, std::string>>
Expand All @@ -147,25 +185,15 @@ TextToSpeech::GetDefaultVoice() {
default_voice.first = language;
free(language);
}
default_voice.second = GetVoiceName(voice_type);
return default_voice;
}

switch (voice_type) {
case TTS_VOICE_TYPE_AUTO:
default_voice.second = "auto";
break;
case TTS_VOICE_TYPE_MALE:
default_voice.second = "male";
break;
case TTS_VOICE_TYPE_FEMALE:
default_voice.second = "female";
break;
case TTS_VOICE_TYPE_CHILD:
default_voice.second = "child";
break;
default:
default_voice.second = "unknown";
break;
void TextToSpeech::SetDefaultVoiceType(const std::string &voice) {
int32_t voice_type = GetVoiceType(voice);
if (voice_type != -1) {
default_voice_type_ = voice_type;
}
return default_voice;
}

std::optional<int32_t> TextToSpeech::GetMaxSpeechInputLength() {
Expand Down Expand Up @@ -246,10 +274,6 @@ bool TextToSpeech::GetSpeedRange(int32_t *min, int32_t *normal, int32_t *max) {
}

bool TextToSpeech::IsLanguageAvailable(const std::string &language) {
if (supported_lanaguages_.size() == 0) {
GetSupportedLanaguages();
}

if (std::find(supported_lanaguages_.begin(), supported_lanaguages_.end(),
language) != supported_lanaguages_.end()) {
return true;
Expand Down
15 changes: 14 additions & 1 deletion packages/flutter_tts/tizen/src/text_to_speech.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <tts.h>

#include <functional>
#include <map>
#include <optional>
#include <string>
#include <vector>
Expand Down Expand Up @@ -40,10 +41,21 @@ class TextToSpeech {
default_language_ = language;
}

std::vector<std::string> &GetSupportedLanaguages();
void InitializeSupportedLanaguagesAndVoiceType();

const std::vector<std::string> &GetSupportedLanaguages() {
return supported_lanaguages_;
}

const std::vector<std::map<std::string, std::string>> &
GetSupportedVoiceTypes() {
return supported_voice_types_;
}

std::optional<std::pair<std::string, std::string>> GetDefaultVoice();

void SetDefaultVoiceType(const std::string &voice);

std::optional<int32_t> GetMaxSpeechInputLength();

std::optional<TtsState> GetState();
Expand Down Expand Up @@ -86,6 +98,7 @@ class TextToSpeech {
int32_t system_volume_ = 0;
int32_t system_max_volume_ = 0;
std::vector<std::string> supported_lanaguages_;
std::vector<std::map<std::string, std::string>> supported_voice_types_;

StateChangedCallback state_changed_callback_;
UtteranceCompletedCallback utterance_completed_callback_;
Expand Down

0 comments on commit b349f7a

Please sign in to comment.