option(TESTS "enable/disable universal switch tests" ON)
pkg_check_modules(pkgs REQUIRED
+ atspi-2
capi-appfw-service-application
capi-appfw-application
capi-media-player
capi-telephony
- elementary
- ecore
- sqlite3
- atspi-2
capi-ui-efl-util
+ ecore
efl-extension
- gobject-2.0
eldbus
+ elementary
+ gobject-2.0
+ sqlite3
+ tts
)
SET(COMMON_FLAGS "-fdiagnostics-color=always -fPIC")
BuildRequires: at-spi2-core-devel
BuildRequires: cmake
BuildRequires: gettext-tools
+BuildRequires: tts
+BuildRequires: tts-devel
BuildRequires: pkgconfig(capi-appfw-service-application)
BuildRequires: pkgconfig(capi-appfw-application)
BuildRequires: pkgconfig(capi-media-player)
auto turnOnOffVoice = std::make_shared<MenuItem>(
std::vector<std::string> {"IDS_TURN_ON_VOICE", "IDS_TURN_OFF_VOICE"},
defaultImg,
- std::string {}/*TODO add activity*/,
+ std::string {"TOGGLE_VOICE_FEEDBACK_ENABLED_ACTIVITY"},
VconfKeyType::BOOL,
std::vector<std::string> {VCONF_KEY_FEEDBACK_VOICE_ENABLED});
auto turnOnOffSound = std::make_shared<MenuItem>(
#include "RowScanner.hpp"
#include "ScanningProperties.hpp"
#include "Sound.hpp"
+#include "TextToSpeech.hpp"
#include "UniversalSwitch.hpp"
#include "UniversalSwitchLog.hpp"
#include "Window.hpp"
#include <Elementary.h>
#include <functional>
-#include <vector>
#include <utility>
+#include <vector>
static const int DASHED_LINE_LENGTH = 20;
}
}
+namespace feedback
+{
+ void playSoundFeedback()
+ {
+ auto tts = Singleton<UniversalSwitch>::instance().getTextToSpeech();
+ if (tts->isEnabled()) {
+ auto elem = Singleton<NavigationInterface>::instance().getCurrentElement();
+ Singleton<UniversalSwitch>::instance().getTextToSpeech()->speak(elem);
+ }
+ Sound::playSoundFeedback(Sound::ID::NAVIGATION_ITERATED);
+ }
+}
+
class RowScannerImpl : public ScreenScanner
{
public:
if (state == State::ITEMS)
continueScanning = true;
stopScanning();
- Sound::playSoundFeedback(Sound::ID::NAVIGATION_ITERATED);
+ feedback::playSoundFeedback();
state = State::START;
}
return {};
case State::ROWS:
stopScanning();
- Sound::playSoundFeedback(Sound::ID::NAVIGATION_ITERATED);
startItemInRowScanning();
+ feedback::playSoundFeedback();
state = State::ITEMS;
return {};
case State::ITEMS:
stopScanning();
- Sound::playSoundFeedback(Sound::ID::NAVIGATION_ITERATED);
+ feedback::playSoundFeedback();
state = State::END;
DEBUG("Scanning complete");
return Singleton<NavigationInterface>::instance().getCurrentElement();
ASSERT(0, "State::END case should not be reached");
return false;
}
- Sound::playSoundFeedback(Sound::ID::NAVIGATION_ITERATED);
+ feedback::playSoundFeedback();
return true;
}
#include "UniversalSwitchLog.hpp"
#include "Window.hpp"
#include "Sound.hpp"
+#include "TextToSpeech.hpp"
#include "UniversalSwitch.hpp"
#include <functional>
}
stopAutoscanning();
+ Singleton<UniversalSwitch>::instance().getTextToSpeech()->speak(*element);
notify(*element);
}
--- /dev/null
+#include "Atspi.hpp"
+#include "TextToSpeech.hpp"
+#include "UniversalSwitch.hpp"
+#include "UniversalSwitchLog.hpp"
+#include "utils.hpp"
+#include "VConfKeys.hpp"
+
+
+#define EXIT_IF_ERROR(error) \
+ do { \
+ if (error != TTS_ERROR_NONE) { \
+ ERROR("error = %s", get_tts_error(error)); \
+ return; \
+ } \
+ } while (0)
+
+namespace
+{
+ const char *get_tts_error(int r)
+ {
+ switch (r) {
+ 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";
+ default:
+ return "unknown error";
+ }
+ }
+}
+
+TextToSpeech::TextToSpeech()
+{
+ auto error = tts_create(&ttsHandler);
+ EXIT_IF_ERROR(error);
+ error = tts_set_mode(ttsHandler, TTS_MODE_SCREEN_READER);
+ EXIT_IF_ERROR(error);
+ error = tts_prepare(ttsHandler);
+ EXIT_IF_ERROR(error);
+
+ callbackHandles.push_back(Singleton<VConfInterface>::instance().registerAndGet<int>(VCONF_KEY_FEEDBACK_VOICE_SPEECH_RATE, TTS_SPEED_AUTO,
+ [this](auto speed) {
+ int min, normal, max;
+ auto error = tts_get_speed_range(ttsHandler, &min, &normal, &max);
+ if (error != TTS_ERROR_NONE) {
+ DEBUG("error = %s", get_tts_error(error));
+ return;
+ }
+ speechRate = utils::clamp(speed, min, max);
+ }));
+
+ callbackHandles.push_back(Singleton<VConfInterface>::instance().registerAndGet<bool>(VCONF_KEY_FEEDBACK_VOICE_ENABLED, false,
+ [this](auto state) {
+ DEBUG("Feedback voice %s", state ? "enabled" : "disabled");
+ enabled = state;
+ }));
+}
+
+TextToSpeech::~TextToSpeech()
+{
+ if (!ttsHandler)
+ return;
+
+ auto error = tts_stop(ttsHandler);
+ EXIT_IF_ERROR(error);
+ error = tts_destroy(ttsHandler);
+ EXIT_IF_ERROR(error);
+ ttsHandler = nullptr;
+}
+
+//TODO: support for long texts
+void TextToSpeech::speak(std::string text) const
+{
+ if (!enabled) {
+ DEBUG("Voice feedback is disabled");
+ return;
+ }
+
+ if (text.empty()) {
+ ERROR("Empty string passed to speak method");
+ return;
+ }
+
+ tts_state_e state;
+ auto error = tts_get_state(ttsHandler, &state);
+ EXIT_IF_ERROR(error);
+ if (state == TTS_STATE_PLAYING || state == TTS_STATE_PAUSED) {
+ error = tts_stop(ttsHandler);
+ EXIT_IF_ERROR(error);
+ }
+
+ auto maxSize = 0u;
+ error = tts_get_max_text_size(ttsHandler, &maxSize);
+ EXIT_IF_ERROR(error);
+ if (text.size() > maxSize) {
+ ERROR("Text is too long. passed = %d max = %d", text.size(), maxSize);
+ //TODO: support for long texts
+ text.resize(maxSize);
+ }
+
+ int utteranceID;
+ error = tts_add_text(ttsHandler, text.c_str(), language, TTS_VOICE_TYPE_AUTO, speechRate, &utteranceID);
+ EXIT_IF_ERROR(error);
+ DEBUG("speaking: %s", text.c_str());
+ error = tts_play(ttsHandler);
+ EXIT_IF_ERROR(error);
+}
+
+void TextToSpeech::speak(const std::shared_ptr<UIElement> &element) const
+{
+ if (!enabled) {
+ DEBUG("Voice feedback is disabled");
+ return;
+ }
+
+ if (!element) {
+ DEBUG("Element is nullptr");
+ return;
+ }
+
+ Singleton<UniversalSwitch>::instance().getAtspi()->getName(element->getObject(),
+ [ptr = shared_from_this()](Optional<std::string> name) {
+ if (name)
+ ptr->speak(*name);
+ });
+}
+
+bool TextToSpeech::isEnabled() const
+{
+ return enabled;
+}
--- /dev/null
+#ifndef TEXT_TO_SPEECH_HPP
+#define TEXT_TO_SPEECH_HPP
+
+#include "UIElement.hpp"
+#include "VConf.hpp"
+
+#include <tts.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+
+class TextToSpeech : public std::enable_shared_from_this<TextToSpeech>
+{
+public:
+ TextToSpeech();
+ ~TextToSpeech();
+
+ bool isEnabled() const;
+ void speak(std::string text) const;
+ void speak(const std::shared_ptr<UIElement> &element) const;
+
+private:
+ tts_h ttsHandler = nullptr;
+ bool enabled = false;
+ int speechRate = TTS_SPEED_AUTO;
+ const char *language = "en_US"; //TODO synchronize with system parameters
+
+ std::vector<VConfInterface::CallbackHandle> callbackHandles;
+};
+
+#endif
--- /dev/null
+#include "Activity.hpp"
+#include "ActivityFactory.hpp"
+#include "UniversalSwitchLog.hpp"
+#include "VConf.hpp"
+#include "VConfKeys.hpp"
+
+
+class ToggleVoiceFeedbackEnabledActivity : public Activity, private RegisterActivity<ToggleVoiceFeedbackEnabledActivity>
+{
+public:
+ constexpr static const char *activityType = "TOGGLE_VOICE_FEEDBACK_ENABLED_ACTIVITY";
+ ToggleVoiceFeedbackEnabledActivity()
+ : Activity(activityType)
+ {}
+
+ bool process() override
+ {
+ auto isEnabled = Singleton<VConfInterface>::instance().get(VCONF_KEY_FEEDBACK_VOICE_ENABLED, true);
+ Singleton<VConfInterface>::instance().set(VCONF_KEY_FEEDBACK_VOICE_ENABLED, !isEnabled);
+ return true;
+ }
+};
#include "AccessoriesSwitchProvider.hpp"
#include "ActivityFactory.hpp"
+#include "Atspi.hpp"
#include "CameraSwitchProvider.hpp"
#include "CompositeSwitchProvider.hpp"
#include "DBusInterface.hpp"
#include "SQLiteConfiguration.hpp"
#include "SwitchConfigurationItem.hpp"
#include "SwitchManager.hpp"
+#include "TextToSpeech.hpp"
#include "UniversalSwitch.hpp"
#include "UniversalSwitchLog.hpp"
-#include "Window.hpp"
#include "VConfKeys.hpp"
-#include "Atspi.hpp"
-
+#include "Window.hpp"
void UniversalSwitch::initialize()
{
auto activityFactory = ActivityFactory::getInstance();
switchManager = SwitchManager::create<SwitchManager>(compositeSwitchProvider, configuration, activityFactory);
+ textToSpeech = std::make_shared<TextToSpeech>();
+
screenScannerManager = std::make_shared<ScreenScannerManager>();
screenScannerManager->startAutoscanning();
-
}
void UniversalSwitch::stopScanning()
switchManager->terminate();
switchManager.reset();
}
+ textToSpeech.reset();
atspi.reset();
}
return dbusInterface;
}
+std::shared_ptr<TextToSpeech> UniversalSwitch::getTextToSpeech() const
+{
+ return textToSpeech;
+}
+
void UniversalSwitch::setSwitchManager(const std::shared_ptr<SwitchManager> &sm)
{
switchManager = sm;
#include <memory>
-class SwitchManager;
+
+class Atspi;
class CompositeSwitchProvider;
class Configuration;
class DBusInterface;
class ScreenScannerManager;
+class SwitchManager;
+class TextToSpeech;
class Window;
-class Atspi;
+
class UniversalSwitch
{
std::shared_ptr<ScreenScannerManager> getScreenScannerManager() const;
std::shared_ptr<Atspi> getAtspi() const;
std::shared_ptr<Window> getMainWindow();
+ std::shared_ptr<TextToSpeech> getTextToSpeech() const;
void setSwitchManager(const std::shared_ptr<SwitchManager> &sm);
void setCompositeSwitchProvider(const std::shared_ptr<CompositeSwitchProvider> &csp);
std::shared_ptr<Configuration> configuration;
std::shared_ptr<DBusInterface> dbusInterface;
std::shared_ptr<Atspi> atspi;
+ std::shared_ptr<TextToSpeech> textToSpeech;
std::shared_ptr<ScreenScannerManager> screenScannerManager;
std::weak_ptr<Window> mainWindow;
#define VCONF_KEY_HID_DETECTED "memory/isf/hw_keyboard_input_detected"
+#define VCONF_KEY_LANGUAGE "db/menu_widget/language"
#endif
--- /dev/null
+#ifndef UTILS_HPP
+#define UTILS_HPP
+
+
+namespace utils
+{
+ // TODO it would be removed when c++17 came
+ template<class T>
+ T clamp(T v, const T &lo, const T &hi)
+ {
+ if (v < lo)
+ v = lo;
+ else if (v > hi)
+ v = hi;
+ return v;
+ }
+}
+
+#endif
$VCONFTOOL bool "${VCONF_PROJECT_PREFIX}FEEDBACK_SOUND_ENABLED" 1
$VCONFTOOL double "${VCONF_PROJECT_PREFIX}FEEDBACK_SOUND_VOLUME" 1.0
-$VCONFTOOL bool "${VCONF_PROJECT_PREFIX}FEEDBACK_VOICE_ENABLED" 0
-$VCONFTOOL double "${VCONF_PROJECT_PREFIX}FEEDBACK_VOICE_SPEECH_RATE" 1.0
+$VCONFTOOL bool "${VCONF_PROJECT_PREFIX}FEEDBACK_VOICE_ENABLED" 0
+$VCONFTOOL int "${VCONF_PROJECT_PREFIX}FEEDBACK_VOICE_SPEECH_RATE" 8 # value beetwen 1 and 15, 0 = TTS_SPEED_AUTO
$VCONFTOOL double "${VCONF_PROJECT_PREFIX}FEEDBACK_VOICE_SPEECH_VOLUME" 1.0
$VCONFTOOL int "${VCONF_PROJECT_PREFIX}GRANULARITY_UNIT" 0 # 0 = CHARACTER, 1 = WORD, 2 = LINE, 3 = PARAGRAPH