Define class for managing audio stream 67/271267/2
authorSuyeon Hwang <stom.hwang@samsung.com>
Wed, 28 Apr 2021 02:48:38 +0000 (11:48 +0900)
committerSuyeon Hwang <stom.hwang@samsung.com>
Fri, 18 Feb 2022 05:11:20 +0000 (14:11 +0900)
ttsd_player.cpp has too complexity responsibility to play PCM data. This complexity makes bad code
to maintain.

This patch makes new class AudioStream to manage audio stream. By this new class, we can simplify
the responsibility of ttsd_player.cpp, and this enhance the maintainability.

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

diff --git a/server/AudioStream.cpp b/server/AudioStream.cpp
new file mode 100644 (file)
index 0000000..bd2f896
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+*  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 <dlog.h>
+
+#include "ttsd_main.h"
+
+#include "AudioStream.h"
+
+using namespace std;
+
+
+static const char* FOCUS_SERVER_READY = "/tmp/.sound_server_ready";
+
+
+AudioStream::AudioStream(focusReleaseCallback focusReleaseCb)
+{
+       __prepared = false;
+       __focusReleaseCallback = focusReleaseCb;
+       __focusAquired = false;
+
+       createSoundStreamInfo();
+       createAudioHandle(TTSE_AUDIO_TYPE_RAW_S16, 16000);
+}
+
+void AudioStream::createSoundStreamInfo()
+{
+       int cnt = 0;
+       while (1) {
+               if (0 == access(FOCUS_SERVER_READY, F_OK)) {
+                       SLOG(LOG_ERROR, tts_tag(), "[AudioStream] focus server is available");
+                       break;
+               } else {
+                       if (0 == cnt++ % 10)
+                               SLOG(LOG_ERROR, tts_tag(), "[AudioStream] focus server is not available");
+                       usleep(50000);
+               }
+       }
+
+       int ret = sound_manager_create_stream_information(SOUND_STREAM_TYPE_VOICE_INFORMATION, __focusStateChangedCallback, this, &__streamInfo);
+       if (SOUND_MANAGER_ERROR_NONE != ret) {
+               SLOG(LOG_ERROR, tts_tag(), "[AudioStream] Fail to create stream info");
+               return;
+       }
+
+       __focusAquired = false;
+       SLOG(LOG_DEBUG, tts_tag(), "[AudioStream] Create stream info");
+}
+
+AudioStream::~AudioStream()
+{
+       __focusReleaseCallback = nullptr;
+       __focusAquired = false;
+
+       unprepareAudioOut();
+       destroyAudioHandle();
+       destroySoundStreamInfo();
+}
+
+void AudioStream::destroySoundStreamInfo()
+{
+       int ret = sound_manager_destroy_stream_information(__streamInfo);
+       if (SOUND_MANAGER_ERROR_NONE != ret) {
+               SLOG(LOG_WARN, tts_tag(), "[AudioStream] Fail to destroy stream info");
+       }
+       __streamInfo = nullptr;
+       __focusAquired = false;
+}
+
+int AudioStream::setAudioFormat(ttse_audio_type_e type, int rate)
+{
+       if (__audioType == type && __audioRate == rate) {
+               return TTSD_ERROR_NONE;
+       }
+
+       SLOG(LOG_INFO, tts_tag(), "[AudioStream] Audio info is different. type:(%d)/(%d), rate:(%d)/(%d)",
+                       __audioType, type, __audioRate, rate);
+
+       destroyAudioHandle();
+       if (TTSD_ERROR_NONE != createAudioHandle(type, rate)) {
+               return TTSD_ERROR_OPERATION_FAILED;
+       }
+
+       if (__focusAquired) {
+               const char* extra_info = __currentExtraInfo.empty() ? nullptr : __currentExtraInfo.c_str();
+               acquireSoundFocus(extra_info);
+       }
+
+       __state = AUDIO_STATE_READY;
+       return TTSD_ERROR_NONE;
+}
+
+int AudioStream::acquireSoundFocus(const char* extra_info)
+{
+       if (nullptr == extra_info) {
+               __currentExtraInfo.clear();
+       } else {
+               __currentExtraInfo.assign(extra_info);
+       }
+
+       int ret = sound_manager_acquire_focus(__streamInfo, SOUND_STREAM_FOCUS_FOR_PLAYBACK, SOUND_BEHAVIOR_NONE, extra_info);
+       if (SOUND_MANAGER_ERROR_NONE != ret) {
+               SLOG(LOG_WARN, tts_tag(), "[AudioStream] Fail to acquire focus");
+       } else {
+               SLOG(LOG_DEBUG, tts_tag(), "[AudioStream] Success to acquire focus");
+       }
+
+       ret = audio_out_set_sound_stream_info(__audioHandle, __streamInfo);
+       if (AUDIO_IO_ERROR_NONE != ret) {
+               SLOG(LOG_WARN, tts_tag(), "[AudioStream] Fail to set stream info");
+               return TTSD_ERROR_OPERATION_FAILED;
+       }
+
+       __focusAquired = true;
+       return TTSD_ERROR_NONE;
+}
+
+int AudioStream::releaseSoundFocus()
+{
+       sound_stream_focus_state_e focusState = SOUND_STREAM_FOCUS_STATE_ACQUIRED;
+       int ret = sound_manager_get_focus_state(__streamInfo, &focusState, nullptr);
+       if (SOUND_MANAGER_ERROR_NONE != ret) {
+               SLOG(LOG_WARN, tts_tag(), "[AudioStream] Fail to get focus state: %d", ret);
+       }
+
+       if (SOUND_STREAM_FOCUS_STATE_ACQUIRED != focusState) {
+               SLOG(LOG_INFO, tts_tag(), "[AudioStream] This handle has no focus");
+               return TTSD_ERROR_NONE;
+       }
+
+       ret = sound_manager_release_focus(__streamInfo, SOUND_STREAM_FOCUS_FOR_PLAYBACK, SOUND_BEHAVIOR_NONE, nullptr);
+       if (SOUND_MANAGER_ERROR_NONE != ret) {
+               SLOG(LOG_WARN, tts_tag(), "[AudioStream] Fail to release focus");
+               return TTSD_ERROR_OPERATION_FAILED;
+       }
+
+       __focusAquired = false;
+       return TTSD_ERROR_NONE;
+}
+
+int AudioStream::prepareAudioOut()
+{
+       if (__prepared) {
+               SLOG(LOG_DEBUG, tts_tag(), "[AudioStream] Audio is already prepared");
+               return TTSD_ERROR_NONE;
+       }
+
+       int ret = audio_out_prepare(__audioHandle);
+       if (AUDIO_IO_ERROR_NONE != ret) {
+               SLOG(LOG_ERROR, tts_tag(), "[AudioStream] Fail to prepare audio : %d", ret);
+               return TTSD_ERROR_OPERATION_FAILED;
+       }
+
+       __prepared = true;
+       __state = AUDIO_STATE_PLAY;
+       return TTSD_ERROR_NONE;
+}
+
+int AudioStream::playAudioData(char* buffer, unsigned int length)
+{
+       if (false == __prepared) {
+               SLOG(LOG_ERROR, tts_tag(), "[AudioStream] Audio is not prepared");
+               return TTSD_ERROR_OPERATION_FAILED;
+       }
+
+       int ret = audio_out_write(__audioHandle, static_cast<void*>(buffer), length);
+       if (0 > ret) {
+               SLOG(LOG_ERROR, tts_tag(), "[AudioStream] Fail to audio write - %d", ret);
+               return TTSD_ERROR_OPERATION_FAILED;
+       }
+
+       return TTSD_ERROR_NONE;
+}
+
+int AudioStream::unprepareAudioOut()
+{
+       if (false == __prepared) {
+               SLOG(LOG_DEBUG, tts_tag(), "[AudioStream] Audio is not prepared");
+               return TTSD_ERROR_NONE;
+       }
+
+       int ret = audio_out_unprepare(__audioHandle);
+       if (AUDIO_IO_ERROR_NONE != ret) {
+               SLOG(LOG_ERROR, tts_tag(), "[AudioStream] Fail to prepare audio : %d", ret);
+               return TTSD_ERROR_OPERATION_FAILED;
+       }
+
+       __prepared = false;
+       __state = AUDIO_STATE_READY;
+       return TTSD_ERROR_NONE;
+}
+
+void AudioStream::waitForPlay()
+{
+       __state = AUDIO_STATE_WAIT_FOR_PLAYING;
+}
+
+AudioStream::AudioState AudioStream::getState()
+{
+       return __state;
+}
+
+int AudioStream::createAudioHandle(ttse_audio_type_e type, int rate)
+{
+       audio_sample_type_e sample_type;
+       if (TTSE_AUDIO_TYPE_RAW_S16 == type) {
+               sample_type = AUDIO_SAMPLE_TYPE_S16_LE;
+       } else {
+               sample_type = AUDIO_SAMPLE_TYPE_U8;
+       }
+
+       int ret = audio_out_create_new(rate, AUDIO_CHANNEL_MONO, sample_type, &__audioHandle);
+       if (AUDIO_IO_ERROR_NONE != ret) {
+               SLOG(LOG_ERROR, tts_tag(), "[AudioStream] Fail to create audio out handle. ret(%s)", get_error_message(ret));
+               return TTSD_ERROR_OPERATION_FAILED;
+       }
+
+       __audioType = type;
+       __audioRate = rate;
+       __state = AUDIO_STATE_READY;
+
+       SLOG(LOG_INFO, tts_tag(), "[AudioStream] Create audio");
+       return TTSD_ERROR_NONE;
+}
+
+void AudioStream::destroyAudioHandle()
+{
+       if (nullptr == __audioHandle) {
+               SLOG(LOG_INFO, tts_tag(), "[AudioStream] Audio handle is not exist");
+               return;
+       }
+
+       int ret = audio_out_destroy(__audioHandle);
+       if (AUDIO_IO_ERROR_NONE != ret) {
+               SLOG(LOG_ERROR, tts_tag(), "[AudioStream] Fail to destroy audio out handle. ret(%s)", get_error_message(ret));
+       } else {
+               SLOG(LOG_INFO, tts_tag(), "[AudioStream] Destroy audio");
+       }
+
+       __state = AUDIO_STATE_NONE;
+}
+
+void AudioStream::__focusStateChangedCallback(sound_stream_info_h stream_info, sound_stream_focus_mask_e focus_mask,
+               sound_stream_focus_state_e focus_state, sound_stream_focus_change_reason_e reason_for_change,
+               int sound_behavior, const char *extra_info, void *user_data)
+{
+       SLOG(LOG_INFO, tts_tag(), "[AudioStream] focus state changed to (%d) with reason(%d) and extra info(%s)",
+                       (int)focus_state, (int)reason_for_change, extra_info);
+
+       AudioStream* audioOut = static_cast<AudioStream*>(user_data);
+       if (stream_info != audioOut->__streamInfo) {
+               SLOG(LOG_ERROR, tts_tag(), "[AudioStream] Invalid stream info handle");
+               return;
+       }
+
+       if (false == audioOut->__prepared || AUDIO_STATE_NONE == audioOut->__state || AUDIO_STATE_READY == audioOut->__state) {
+               return;
+       }
+
+       if (SOUND_STREAM_FOCUS_FOR_PLAYBACK != focus_mask || SOUND_STREAM_FOCUS_STATE_RELEASED != focus_state) {
+               return;
+       }
+
+       SLOG(LOG_INFO, tts_tag(), "[AudioStream] Focus is released on playing. Invoke callback");
+       if (audioOut->__focusReleaseCallback) {
+               audioOut->__focusReleaseCallback();
+       }
+}
diff --git a/server/AudioStream.h b/server/AudioStream.h
new file mode 100644 (file)
index 0000000..a726aa4
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+*  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_AUDIO_OUT_H_
+#define __TTSD_AUDIO_OUT_H_
+
+
+#include <string>
+#include <audio_io.h>
+#include <sound_manager.h>
+
+#include "ttse.h"
+
+class AudioStream {
+public:
+       enum AudioState{
+               AUDIO_STATE_NONE = 0,
+               AUDIO_STATE_READY,
+               AUDIO_STATE_WAIT_FOR_PLAYING,
+               AUDIO_STATE_PLAY
+       };
+
+       typedef void (*focusReleaseCallback)();
+
+       AudioStream(focusReleaseCallback focusReleaseCb);
+       ~AudioStream();
+
+       int setAudioFormat(ttse_audio_type_e type, int rate);
+       int acquireSoundFocus(const char* extra_info);
+       int releaseSoundFocus();
+       int prepareAudioOut();
+       int unprepareAudioOut();
+       void waitForPlay();
+       AudioState getState();
+       int playAudioData(char* buffer, unsigned int length);
+
+private:
+       static void __focusStateChangedCallback(sound_stream_info_h stream_info, sound_stream_focus_mask_e focus_mask,
+                       sound_stream_focus_state_e focus_state, sound_stream_focus_change_reason_e reason_for_change, int sound_behavior, const char *extra_info,
+                       void *user_data);
+
+       int createAudioHandle(ttse_audio_type_e type, int rate);
+       void destroyAudioHandle();
+
+       void createSoundStreamInfo();
+       void destroySoundStreamInfo();
+
+private:
+       audio_out_h __audioHandle;
+       ttse_audio_type_e __audioType;
+       int __audioRate;
+       bool __prepared;
+
+       sound_stream_info_h __streamInfo;
+       focusReleaseCallback __focusReleaseCallback;
+       std::string __currentExtraInfo;
+       bool __focusAquired;
+
+       AudioState __state;
+};
+
+
+#endif /* __TTSD_AUDIO_OUT_H_ */
index 25f710aab16e27f65a7ee18d865a91be3fa33153..550a6b117282877bdb6fcdcfc228975533df72c5 100644 (file)
@@ -11,6 +11,7 @@ SET(SRCS
        ttsd_network.c
        ttsd_player.cpp
        BackgroundVolume.cpp
+       AudioStream.cpp
        ttsd_server.c
        ../common/tts_config_mgr.c
        ../common/tts_config_parser.c