Add wrapper class for TTS functions 56/202456/8 submit/tizen/20190402.051338
authorLukasz Wlazly <l.wlazly@partner.samsung.com>
Thu, 28 Mar 2019 14:37:08 +0000 (15:37 +0100)
committerLukasz Wlazly <l.wlazly@partner.samsung.com>
Mon, 1 Apr 2019 09:48:32 +0000 (11:48 +0200)
Change-Id: I30502a127bc02267b56a55c58917f841026c5a0e

src/AppContext.cpp
src/AppContext.hpp
src/ScreenReaderPage.cpp
src/ScreenReaderSettingsPage.cpp
src/ScreenReaderSettingsPage.hpp
src/TextToSpeech.cpp [new file with mode: 0644]
src/TextToSpeech.hpp [new file with mode: 0644]

index e782613..07f4dbe 100644 (file)
@@ -6,30 +6,6 @@
 
 #include <app.h>
 
-static const char *get_tts_error(int r)
-{
-       switch (r) {
-       case TTS_ERROR_NONE: {
-               return "no error";
-       }
-       case TTS_ERROR_INVALID_PARAMETER: {
-               return "inv param";
-       }
-       case TTS_ERROR_OUT_OF_MEMORY: {
-               return "out of memory";
-       }
-       case TTS_ERROR_OPERATION_FAILED: {
-               return "oper failed";
-       }
-       case TTS_ERROR_INVALID_STATE: {
-               return "inv state";
-       }
-       default: {
-               return "uknown error";
-       }
-       }
-}
-
 AppContext::AppContext()
 {
        elm_app_base_scale_set(2.4);
@@ -55,17 +31,12 @@ AppContext::AppContext()
 
        conformant->show();
 
-       auto ret = createTTSHandle();
-       if (ret == -1)
-               destroyTTSHandle();
-
        window_->show();
 }
 
 AppContext::~AppContext()
 {
        delete mainPage_;
-       destroyTTSHandle();
 }
 
 Window *AppContext::getWindow() const
@@ -90,41 +61,3 @@ void AppContext::addRotateSupport()
                elm_win_wm_rotation_available_rotations_set(window_->getObject(), rots, 4);
        }
 }
-
-int AppContext::createTTSHandle()
-{
-       int ret;
-       ret = tts_create(&ttsHandle_);
-       if (ret != 0) {
-               ERROR("tts_create %s", get_tts_error(ret));
-               return -1;
-       }
-       ret = tts_set_mode(ttsHandle_, TTS_MODE_DEFAULT);
-       if (ret != 0) {
-               ERROR("tts_set_mode %s", get_tts_error(ret));
-               return -1;
-       }
-       ret = tts_prepare(ttsHandle_);
-       if (ret != 0) {
-               ERROR("tts_prepare %s", get_tts_error(ret));
-               return -1;
-       }
-       ret = tts_set_state_changed_cb(ttsHandle_, state_changed_cb, nullptr);
-       if (ret != 0) {
-               ERROR("tts_set_state_changed_cb %s", get_tts_error(ret));
-               return -1;
-       }
-       return 0;
-}
-
-void AppContext::destroyTTSHandle()
-{
-       auto ret = tts_destroy(ttsHandle_);
-       if (ret != 0)
-               WARNING("Fail to tts destroy");
-}
-
-void AppContext::state_changed_cb(tts_h tts, tts_state_e previous, tts_state_e current, void *user_data)
-{
-       DEBUG(" previous = %d current = %d", previous, current);
-}
index 0a7dcb1..d698fe9 100644 (file)
@@ -8,7 +8,6 @@
 
 #include <memory>
 #include <string>
-#include <tts.h>
 
 class MainPage;
 class AppContext
@@ -23,13 +22,9 @@ class AppContext
        // TODO: should be private
        UniversalSwitchDbusConfig config;
        UniversalSwitchConfiguration us_configuration;
-       tts_h ttsHandle_ = nullptr;
 
        private:
        void addRotateSupport();
-       int createTTSHandle();
-       void destroyTTSHandle();
-       static void state_changed_cb(tts_h tts, tts_state_e previous, tts_state_e current, void *user_data);
 
        std::unique_ptr<Window> window_;
        Naviframe *naviframe_ = nullptr;
index b2ee774..47a943c 100644 (file)
@@ -19,6 +19,7 @@
 #include "AccessibilitySettingLog.hpp"
 #include "Genlist.hpp"
 #include "Singleton.hpp"
+#include "TextToSpeech.hpp"
 #include "setting-accessibility.h"
 
 #include <app.h>
@@ -38,12 +39,9 @@ ScreenReaderPage::ScreenReaderPage()
                        auto state = item->getState();
                        Singleton<VConfInterface>::instance().set(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, state);
                        if (!state) {
-                               auto buf = TranslatedString{"IDS_ST_MBODY_SCREEN_READER_HTTS"}.str() + " " + TranslatedString{"IDS_ST_BODY_OFF"}.str();
-                               int utterance_id;
-                               int ret = tts_add_text(context_.ttsHandle_, buf.c_str(), NULL, TTS_VOICE_TYPE_AUTO, TTS_SPEED_AUTO, &utterance_id);
-                               DEBUG("tts_add_text %d", ret);
-                               ret = tts_play(context_.ttsHandle_);
-                               DEBUG("tts_play %d", ret);
+                               auto buf = TranslatedString{"IDS_ST_MBODY_SCREEN_READER_HTTS"}.str() + " " +
+                                                  TranslatedString{"IDS_ST_BODY_OFF"}.str();
+                               Singleton<TextToSpeech>::instance().play(buf);
                        }
                },
                GenlistItem::WidgetType::toggle});
@@ -57,9 +55,12 @@ ScreenReaderPage::ScreenReaderPage()
 
        genlist->appendItem({"multiline", {}, "IDS_ACCS_BODY_WHILE_SCREEN_READER_IS_ENABLED_YOUR_PHONE_WILL_PROVIDE_VOICE_FEEDBACK_FOR_EXAMPLE_SCREEN_READER_WILL_MSG"});
 
-       genlist->appendItem({"type1", "IDS_ST_OPT_SETTINGS", {}, [this](auto item) {
-                                                        this->screenReaderSettingsPage_ = std::make_unique<ScreenReaderSettingsPage>();
-                                                }});
+       genlist->appendItem({"type1",
+               "IDS_ST_OPT_SETTINGS",
+               {},
+               [this](auto item) {
+                       this->screenReaderSettingsPage_ = std::make_unique<ScreenReaderSettingsPage>();
+               }});
 
        context_.getNaviframe()->pushBack("IDS_ST_MBODY_SCREEN_READER_HTTS", genlist, [this]() {
                screenReaderStateHandle_ = {};
index 6a8b52e..e0b8e20 100644 (file)
 #include "Layout.hpp"
 #include "Singleton.hpp"
 #include "Slider.hpp"
+#include "TextToSpeech.hpp"
 #include "VConf.hpp"
 #include "setting-accessibility.h"
 #include "utils.hpp"
 
 #include <app.h>
 #include <efl_extension.h>
-#include <tts_setting.h>
 #include <vconf-internal-keys.h>
 #include <vconf.h>
 
@@ -54,11 +54,6 @@ namespace
 ScreenReaderSettingsPage::ScreenReaderSettingsPage()
        : context_(Singleton<AppContext>::instance())
 {
-       int s_speed = -1;
-       int s_speed_normal = -1;
-       int s_speed_max = -1;
-       int s_speed_min = -1;
-
        auto genlist = Widget::make<Genlist>(context_.getNaviframe());
        genlist->setMode(ELM_LIST_COMPRESS);
        genlist->setStyle("dialogue");
@@ -66,45 +61,29 @@ ScreenReaderSettingsPage::ScreenReaderSettingsPage()
 
        DEBUG("Creating items");
 
-       if (0 != tts_setting_initialize())
-               ERROR("Fail to setting initialize");
-
-       DEBUG("tts initialized");
-       if (0 != tts_setting_get_speed(&s_speed))
-               ERROR("Fail to get speed");
-
-       if (0 != tts_setting_get_speed_range(&s_speed_min, &s_speed_normal, &s_speed_max))
-               ERROR("Fail to get speed range");
-       auto denominator = s_speed_max - s_speed_min;
-       double s_speed_percent = 0.0;
-       if (denominator != 0)
-               s_speed_percent = (((double)s_speed - (double)s_speed_min) / ((double)denominator)) * 100;
-       std::string buf = std::to_string(std::lround(s_speed_percent));
-       DEBUG("TTS rate is %.0f", s_speed_percent);
-
-       /* Item 1 : Speech Rate */
-       speechRateItem_ = genlist->appendItem(
-               {"type1", "IDS_ST_BODY_SPEECH_RATE", buf, [this](auto item) { this->createSpeechRatePopup(); }});
+       auto percentage = calculateSpeedPercentage();
+       speechRateItem_ = genlist->appendItem({"type1", "IDS_ST_BODY_SPEECH_RATE", percentage, [this](auto item) { this->createSpeechRatePopup(); }});
 
-       tts_setting_set_speed_changed_cb(ttsSpeedChangedCb, speechRateItem_);
+       Singleton<TextToSpeech>::instance().addSpeedChangedCb([item = speechRateItem_](auto val) {
+               DEBUG("Speed changed to (%d)", val);
+               item->setDescription(std::to_string(val));
+               item->update();
+       });
 
        auto item = genlist->appendItem({"multiline",
                "IDS_ACCS_OPT_KEYBOARD_FEEDBACK_TTS",
                "IDS_ACCS_OPT_ALWAYS_READ_OUT_KEYBOARD_INPUT_TTS",
                [](auto item) {
-                       Singleton<VConfInterface>::instance().set(
-                               VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_KEYBOARD_FEEDBACK, item->getState());
+                       Singleton<VConfInterface>::instance().set(VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_KEYBOARD_FEEDBACK, item->getState());
                },
                GenlistItem::WidgetType::toggle});
-       item->setState(
-               Singleton<VConfInterface>::instance().get(VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_KEYBOARD_FEEDBACK, false));
+       item->setState(Singleton<VConfInterface>::instance().get(VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_KEYBOARD_FEEDBACK, false));
 
        item = genlist->appendItem({"type1",
                "IDS_ACCS_MBODY_READ_OUT_USAGE_HINTS_ABB",
                {},
                [](auto item) {
-                       Singleton<VConfInterface>::instance().set(
-                               VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_DESCRIPTION, item->getState());
+                       Singleton<VConfInterface>::instance().set(VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_DESCRIPTION, item->getState());
                },
                GenlistItem::WidgetType::toggle});
        item->setState(Singleton<VConfInterface>::instance().get(VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_DESCRIPTION, false));
@@ -113,8 +92,7 @@ ScreenReaderSettingsPage::ScreenReaderSettingsPage()
                "IDS_ACCS_MBODY_VIBRATION_FEEDBACK",
                {},
                [](auto item) {
-                       Singleton<VConfInterface>::instance().set(
-                               VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_HAPTIC, item->getState());
+                       Singleton<VConfInterface>::instance().set(VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_HAPTIC, item->getState());
                },
                GenlistItem::WidgetType::toggle});
        item->setState(Singleton<VConfInterface>::instance().get(VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_HAPTIC, false));
@@ -123,15 +101,12 @@ ScreenReaderSettingsPage::ScreenReaderSettingsPage()
                "IDS_ACCS_MBODY_SOUND_FEEDBACK",
                {},
                [](auto item) {
-                       Singleton<VConfInterface>::instance().set(
-                               VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_SOUND_FEEDBACK, item->getState());
+                       Singleton<VConfInterface>::instance().set(VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_SOUND_FEEDBACK, item->getState());
                },
                GenlistItem::WidgetType::toggle});
-       item->setState(
-               Singleton<VConfInterface>::instance().get(VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_SOUND_FEEDBACK, false));
+       item->setState(Singleton<VConfInterface>::instance().get(VCONFKEY_SETAPPL_ACCESSIBILITY_SCREEN_READER_SOUND_FEEDBACK, false));
 
-       genlist->appendItem(
-               {"type1", "IDS_ACCS_TMBODY_STATUS_BAR_INFORMATION", {}, [this](auto item) { this->mouseUpGendialListCb(); }});
+       genlist->appendItem({"type1", "IDS_ACCS_TMBODY_STATUS_BAR_INFORMATION", {}, [this](auto item) { this->mouseUpGendialListCb(); }});
 
        genlist->appendItem({"multiline",
                {},
@@ -139,16 +114,8 @@ ScreenReaderSettingsPage::ScreenReaderSettingsPage()
                "TAP_THE_SCREEN_WITH_TWO_FINGERS_MSG",
                [this](auto item) {}});
 
-       context_.getNaviframe()->pushBack("IDS_ST_OPT_SETTINGS", genlist, []() {
-               if (0 != tts_setting_unset_speed_changed_cb())
-                       ERROR("Fail to unset speed changed cb");
-
-               if (0 != tts_setting_unset_voice_changed_cb())
-                       ERROR("Fail to unset voice changed cb");
-
-               if (0 != tts_setting_finalize()) {
-                       DEBUG("Fail to setting finalize");
-               }
+       context_.getNaviframe()->pushBack("IDS_ST_OPT_SETTINGS", genlist, [this]() {
+               Singleton<TextToSpeech>::instance().clearSpeedChangedCallbacks();
        });
 }
 
@@ -180,8 +147,7 @@ void ScreenReaderSettingsPage::mouseUpGendialListCb()
                screenreaderCheckbox->setState(vConfCheckData[i].state);
        }
 
-       genlist->blockScrolling(
-               Elm_Scroller_Movement_Block(ELM_SCROLLER_MOVEMENT_BLOCK_VERTICAL | ELM_SCROLLER_MOVEMENT_BLOCK_HORIZONTAL));
+       genlist->blockScrolling(Elm_Scroller_Movement_Block(ELM_SCROLLER_MOVEMENT_BLOCK_VERTICAL | ELM_SCROLLER_MOVEMENT_BLOCK_HORIZONTAL));
        genlist->setScrollbarsVisibility(ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF);
 
        box->packEnd(genlist);
@@ -204,14 +170,6 @@ void ScreenReaderSettingsPage::mouseUpGendialListCb()
        this->popup_->setPartContent("button2", btnDone);
 }
 
-void ScreenReaderSettingsPage::ttsSpeedChangedCb(int speed, void *user_data)
-{
-       DEBUG("=== Speed changed to (%d)", speed);
-       auto item = static_cast<GenlistItem *>(user_data);
-       item->setDescription(std::to_string(speed));
-       item->update();
-}
-
 void ScreenReaderSettingsPage::createSpeechRatePopup()
 {
        auto window = context_.getWindow();
@@ -235,22 +193,19 @@ void ScreenReaderSettingsPage::createSpeechRatePopup()
        slider->setIndicatorFormat("%1.0f");
        slider->setRange(1, 15);
 
-       int val = -1;
-       if (0 != tts_setting_get_speed(&val))
-               ERROR("Fail to get speed");
+       auto speed = Singleton<TextToSpeech>::instance().getSpeed();
 
        slider->setStep(2);
-       slider->setValue(val);
+       slider->setValue(speed);
        layout->setPartContent("slider", slider);
 
        auto cancelBtn = Widget::make<Button>(popup_, removeCb, "IDS_ST_BUTTON_CANCEL_ABB", "bottom");
        popup_->setPartContent("button1", cancelBtn);
 
        auto doneBtn = Widget::make<Button>(popup_,
-               [p = popup_, window, slider]() {
-                       auto value = static_cast<int>(slider->getValue());
-                       if (0 != tts_setting_set_speed(value))
-                               WARNING("Setting speed to tts engine failed");
+               [p = popup_, window, slider, this]() {
+                       auto val = static_cast<size_t>(slider->getValue());
+                       Singleton<TextToSpeech>::instance().setSpeed(val);
                        window->removeChild(p);
                },
                "IDS_ST_SK3_DONE",
@@ -261,27 +216,24 @@ void ScreenReaderSettingsPage::createSpeechRatePopup()
        evas_object_event_callback_add(popup_->getObject(), EVAS_CALLBACK_DEL, onPopupDel, this);
 }
 
-void ScreenReaderSettingsPage::onPopupDel(void *data, Evas *e, Evas_Object *obj, void *event_info)
+std::string ScreenReaderSettingsPage::calculateSpeedPercentage()
 {
-       int speed_val = -1;
-       int s_speed_normal = -1;
-       int s_speed_max = -1;
-       int s_speed_min = -1;
-
-       auto self = static_cast<ScreenReaderSettingsPage *>(data);
-
-       if (0 != tts_setting_get_speed(&speed_val))
-               ERROR("Failed to get speed");
+       auto speed = Singleton<TextToSpeech>::instance().getSpeed();
+       auto speedRange = Singleton<TextToSpeech>::instance().getSpeedRange();
 
-       if (0 != tts_setting_get_speed_range(&s_speed_min, &s_speed_normal, &s_speed_max))
-               ERROR("Fail to get speed range");
-       auto denominator = s_speed_max - s_speed_min;
-       double s_speed_percent = 0.0;
+       auto denominator = speedRange.max - speedRange.min;
+       auto speedPercent = 0.0;
        if (denominator != 0)
-               s_speed_percent = (((double)speed_val - (double)s_speed_min) / ((double)denominator)) * 100;
+               speedPercent = (static_cast<double>(speed - speedRange.min) / denominator) * 100;
 
-       std::string buf = std::to_string(std::lround(s_speed_percent));
-       DEBUG("Updating speed to %s", buf.c_str());
-       self->speechRateItem_->setDescription(buf);
+       DEBUG("TTS rate is %.0f", speedPercent);
+       return std::to_string(std::lround(speedPercent));
+}
+
+void ScreenReaderSettingsPage::onPopupDel(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+       auto self = static_cast<ScreenReaderSettingsPage *>(data);
+       auto percentage = self->calculateSpeedPercentage();
+       self->speechRateItem_->setDescription(percentage);
        self->speechRateItem_->update();
 }
index 97ff7b7..47a5986 100644 (file)
@@ -40,8 +40,8 @@ class ScreenReaderSettingsPage
 
        private:
        void mouseUpGendialListCb();
-       static void ttsSpeedChangedCb(int speed, void *user_data);
        void createSpeechRatePopup();
+       std::string calculateSpeedPercentage();
        static void onPopupDel(void *data, Evas *e, Evas_Object *obj, void *event_info);
 
        AppContext &context_;
diff --git a/src/TextToSpeech.cpp b/src/TextToSpeech.cpp
new file mode 100644 (file)
index 0000000..d4519f5
--- /dev/null
@@ -0,0 +1,139 @@
+#include "TextToSpeech.hpp"
+
+TextToSpeech::TextToSpeech()
+{
+       if (auto ret = tts_create(&handle_)) {
+               ERROR("tts_create: %s", ttsErrorToString(ret).c_str());
+               return;
+       }
+
+       if (auto ret = tts_set_mode(handle_, TTS_MODE_DEFAULT)) {
+               ERROR("tts_set_mode: %s", ttsErrorToString(ret).c_str());
+               return;
+       }
+
+       if (auto ret = tts_prepare(handle_)) {
+               ERROR("tts_prepare: %s", ttsErrorToString(ret).c_str());
+               return;
+       }
+
+       if (auto ret = tts_setting_initialize())
+               ERROR("tts_setting_initialize: %s", ttsSettingErrorToString(ret).c_str());
+}
+
+TextToSpeech::~TextToSpeech()
+{
+       if (tts_setting_finalize())
+               WARNING("Fail to setting finalize");
+
+       if (tts_destroy(handle_))
+               WARNING("Fail to tts destroy");
+}
+
+void TextToSpeech::play(const std::string &text)
+{
+       int utterance = 0;
+       if (auto ret = tts_add_text(handle_, text.c_str(), nullptr, TTS_VOICE_TYPE_AUTO, TTS_SPEED_AUTO, &utterance))
+               ERROR("tts_add_text: %s", ttsErrorToString(ret).c_str());
+
+       if (auto ret = tts_play(handle_))
+               ERROR("tts_play: %s", ttsErrorToString(ret).c_str());
+}
+
+size_t TextToSpeech::getSpeed()
+{
+       int speed{};
+       auto ret = tts_setting_get_speed(&speed);
+       if (ret)
+               ERROR("tts_setting_get_speed: %s", ttsSettingErrorToString(ret).c_str());
+       else
+               return static_cast<size_t>(speed);
+
+       return {};
+}
+
+void TextToSpeech::setSpeed(size_t speed)
+{
+       auto ret = tts_setting_set_speed(speed);
+       if (ret)
+               ERROR("tts_setting_set_speed: %s", ttsSettingErrorToString(ret).c_str());
+}
+
+auto TextToSpeech::getSpeedRange() -> SpeedRange
+{
+       int min{};
+       int normal{};
+       int max{};
+
+       auto ret = tts_setting_get_speed_range(&min, &normal, &max);
+       if (ret)
+               ERROR("tts_setting_get_speed_range: %s", ttsSettingErrorToString(ret).c_str());
+       else
+               return {static_cast<size_t>(min), static_cast<size_t>(normal), static_cast<size_t>(max)};
+
+       return {};
+}
+
+void TextToSpeech::addSpeedChangedCb(std::function<void(size_t)> onChange)
+{
+       auto ret = tts_setting_set_speed_changed_cb(onSpeedChange, this);
+       if (ret)
+               ERROR("tts_setting_set_speed_changed_cb: %s", ttsSettingErrorToString(ret).c_str());
+       speedChangedCallbacks_.push_back(std::move(onChange));
+}
+
+void TextToSpeech::clearSpeedChangedCallbacks()
+{
+       speedChangedCallbacks_.clear();
+}
+
+std::string TextToSpeech::ttsErrorToString(int error)
+{
+       switch (error) {
+       case TTS_ERROR_NONE: {
+               return "no error";
+       }
+       case TTS_ERROR_INVALID_PARAMETER: {
+               return "invalid parameter";
+       }
+       case TTS_ERROR_OUT_OF_MEMORY: {
+               return "out of memory";
+       }
+       case TTS_ERROR_OPERATION_FAILED: {
+               return "operation failed";
+       }
+       case TTS_ERROR_INVALID_STATE: {
+               return "invalid state";
+       }
+       }
+
+       return "unknown error";
+}
+
+std::string TextToSpeech::ttsSettingErrorToString(int error)
+{
+       switch (error) {
+       case TTS_SETTING_ERROR_NONE: {
+               return "no error";
+       }
+       case TTS_SETTING_ERROR_INVALID_STATE: {
+               return "alread initialized";
+       }
+       case TTS_SETTING_ERROR_OPERATION_FAILED: {
+               return "operation failed";
+       }
+       case TTS_SETTING_ERROR_NOT_SUPPORTED: {
+               return "tts is not supported";
+       }
+       }
+
+       return "unknown error";
+}
+
+void TextToSpeech::onSpeedChange(int speed, void *user_data)
+{
+       auto self = static_cast<TextToSpeech *>(user_data);
+       for (auto it : self->speedChangedCallbacks_)
+               if (it)
+                       it(speed);
+}
\ No newline at end of file
diff --git a/src/TextToSpeech.hpp b/src/TextToSpeech.hpp
new file mode 100644 (file)
index 0000000..473986b
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef TEXT_TO_SPEECH_HPP
+#define TEXT_TO_SPEECH_HPP
+
+#include "AccessibilitySettingLog.hpp"
+
+#include <functional>
+#include <tts.h>
+#include <tts_setting.h>
+#include <vector>
+
+class TextToSpeech
+{
+       public:
+       struct SpeedRange
+       {
+               size_t min{};
+               size_t normal{};
+               size_t max{};
+       };
+
+       TextToSpeech();
+       ~TextToSpeech();
+
+       void play(const std::string &text);
+       size_t getSpeed();
+       void setSpeed(size_t speed);
+       SpeedRange getSpeedRange();
+       void addSpeedChangedCb(std::function<void(size_t)> onChange);
+       void clearSpeedChangedCallbacks();
+
+       private:
+       std::string ttsErrorToString(int error);
+       std::string ttsSettingErrorToString(int error);
+       static void onSpeedChange(int speed, void *user_data);
+
+       tts_h handle_ = nullptr;
+       std::vector<std::function<void(size_t)>> speedChangedCallbacks_;
+};
+
+#endif