Define class for new TTS play thread 80/271880/2
authorSuyeon Hwang <stom.hwang@samsung.com>
Fri, 30 Apr 2021 08:20:09 +0000 (17:20 +0900)
committerSuyeon Hwang <stom.hwang@samsung.com>
Tue, 8 Mar 2022 05:01:13 +0000 (14:01 +0900)
ttsd_player.cpp has too complexity responsibility to play PCM data. This complexity makes bad code
to maintain.

This patch makes new class PlayerThread to manage thread for playing pcm data. By this new class,
we can simplify the responsibility of ttsd_player.cpp, and this enhance the maintainability.

Change-Id: Icc7c6f294a8879243a310383c59e46358780cd75
Signed-off-by: Suyeon Hwang <stom.hwang@samsung.com>
server/CMakeLists.txt
server/PlayerThread.cpp [new file with mode: 0644]
server/PlayerThread.h [new file with mode: 0644]

index e3cd232..bce8425 100644 (file)
@@ -11,6 +11,7 @@ SET(SRCS
        ttsd_player.cpp
        BackgroundVolume.cpp
        AudioStream.cpp
+       PlayerThread.cpp
        ttsd_server.c
        ../common/tts_config_mgr.c
        ../common/tts_config_parser.c
diff --git a/server/PlayerThread.cpp b/server/PlayerThread.cpp
new file mode 100644 (file)
index 0000000..ed58411
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+*  Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*  http://www.apache.org/licenses/LICENSE-2.0
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*/
+
+#include "ttsd_main.h"
+#include "ttsd_data.h"
+#include "PlayerThread.h"
+
+
+using namespace std;
+
+void PlayerThread::runThread(PlayerThread* player)
+{
+       SLOG(LOG_INFO, tts_tag(), "[Player] Run player thread");
+       player->runPlayer();
+       SLOG(LOG_INFO, tts_tag(), "[Player] Exit player thread");
+}
+
+PlayerThread::PlayerThread(PlayUtteranceCallback threadFucntion)
+{
+       SLOG(LOG_INFO, tts_tag(), "[PlayerThread] Constructor");
+       __currentUid = TTS_INVALID_UID;
+       __playerAvailable = true;
+       __playUtterance = threadFucntion;
+
+       SLOG(LOG_INFO, tts_tag(), "[PlayerThread] Start thread");
+       __playerThread = thread(runThread, this);
+}
+
+PlayerThread::~PlayerThread()
+{
+       SLOG(LOG_INFO, tts_tag(), "[PlayerThread] Destructor");
+       unique_lock<mutex> controlLock(__controlMutex);
+       __playerAvailable = false;
+       __playUtterance = nullptr;
+
+       tryToStopPlayer();
+       SLOG(LOG_INFO, tts_tag(), "[PlayerThread] Thread is stopped or waiting");
+
+       __threadCond.notify_all();
+       __playerThread.join();
+       SLOG(LOG_INFO, tts_tag(), "[PlayerThread] Finish thread");
+}
+
+void PlayerThread::tryToStopPlayer()
+{
+       __currentUid = TTS_INVALID_UID;
+
+       unique_lock<mutex> stopCheckLock(__stopCheckMutex);
+       cv_status ret = __stopCheckCond.wait_for(stopCheckLock, chrono::milliseconds(500));
+       if (ret == cv_status::timeout) {
+               SLOG(LOG_WARN, tts_tag(), "[PlayerThread] Timeout stop request");
+       }
+}
+
+bool PlayerThread::isPlayerAvailable()
+{
+       return __playerAvailable.load();
+}
+
+unsigned int PlayerThread::getCurrentUid()
+{
+       return __currentUid.load();
+}
+
+bool PlayerThread::isCurrentUid(unsigned int uid)
+{
+       if (0 > ttsd_data_is_client(uid)) {
+               return false;
+       }
+
+       if (uid != __currentUid.load()) {
+               return false;
+       }
+
+       return true;
+}
+
+void PlayerThread::requestPlay(unsigned int uid)
+{
+       lock_guard<mutex> lock(__controlMutex);
+       if (false == isPlayerAvailable()) {
+               SLOG(LOG_ERROR, tts_tag(), "[PlayerThread] Player is already finished.");
+               return;
+       }
+
+       SLOG(LOG_INFO, tts_tag(), "[PlayerThread] Play request. uid(%u)", uid);
+       __currentUid = uid;
+       __threadCond.notify_all();
+}
+
+void PlayerThread::requestStop()
+{
+       lock_guard<mutex> lock(__controlMutex);
+       if (false == isPlayerAvailable()) {
+               SLOG(LOG_ERROR, tts_tag(), "[PlayerThread] Player is already finished.");
+               return;
+       }
+
+       unsigned int uid = __currentUid.load();
+       if (uid == TTS_INVALID_UID) {
+               SLOG(LOG_INFO, tts_tag(), "[PlayerThread] Thread is already stopped");
+               return;
+       }
+
+       SLOG(LOG_INFO, tts_tag(), "[PlayerThread] Stop request. current played uid(%u)", uid);
+       tryToStopPlayer();
+       SLOG(LOG_INFO, tts_tag(), "[PlayerThread] Stop playing");
+}
+
+void PlayerThread::runPlayer()
+{
+       unique_lock<mutex> lock(__threadMutex);
+       if (nullptr == __playUtterance) {
+               SLOG(LOG_ERROR, tts_tag(), "[PlayerThread] Play utterance callback is not set");
+               return;
+       }
+
+       while (isPlayerAvailable()) {
+               SLOG(LOG_INFO, tts_tag(), "[PlayerThread] Wait playing");
+               if (isThreadStopped()) {
+                       __threadCond.wait(lock);
+               }
+
+               while (false == isThreadStopped()) {
+                       unsigned int uid = getCurrentUid();
+                       SLOG(LOG_INFO, tts_tag(), "[Player] Current player uid(%u)", uid);
+                       __playUtterance(this, uid);
+               }
+
+               __stopCheckCond.notify_all();
+       }
+}
+
+bool PlayerThread::isThreadStopped()
+{
+       bool isStopped = (TTS_INVALID_UID == __currentUid.load()) || (false == isPlayerAvailable());
+       return isStopped;
+}
diff --git a/server/PlayerThread.h b/server/PlayerThread.h
new file mode 100644 (file)
index 0000000..8fee3bb
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+*  Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*  http://www.apache.org/licenses/LICENSE-2.0
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*/
+
+
+#ifndef __TTSD_PLAYER_MANAGER_H_
+#define __TTSD_PLAYER_MANAGER_H_
+
+
+#include <mutex>
+#include <condition_variable>
+#include <thread>
+#include <atomic>
+#include <queue>
+
+class PlayerThread {
+public:
+       typedef void (*PlayUtteranceCallback)(PlayerThread* player_instance, unsigned int uid);
+
+       PlayerThread(PlayUtteranceCallback threadFucntion);
+       ~PlayerThread();
+
+       unsigned int getCurrentUid();
+       bool isCurrentUid(unsigned int uid);
+
+       void requestPlay(unsigned int uid);
+       void requestStop();
+private:
+       static void runThread(PlayerThread* player);
+
+       void runPlayer();
+       void tryToStopPlayer();
+       bool isPlayerAvailable();
+       bool isThreadStopped();
+
+private:
+       std::atomic<unsigned int> __currentUid;
+       std::atomic<bool> __playerAvailable;
+
+       std::thread __playerThread;
+       std::mutex __threadMutex;
+       std::mutex __controlMutex;
+       std::mutex __stopCheckMutex;
+       std::condition_variable __threadCond;
+       std::condition_variable __stopCheckCond;
+
+       PlayUtteranceCallback __playUtterance;
+};
+
+
+#endif /* __TTSD_PLAYER_MANAGER_H_ */