--- /dev/null
+/*
+* 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();
+ }
+}
--- /dev/null
+/*
+* 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_ */