Move drain method to subclass CAudioOutput
[platform/core/api/audio-io.git] / src / cpp / CAudioIO.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include <pthread.h>
19 #include <assert.h>
20 #include "CAudioIODef.h"
21 #include <sound_manager_internal.h>
22 #include <sys/time.h>
23 #include <string.h>
24
25 using namespace std;
26 using namespace tizen_media_audio;
27
28
29 /**
30  * class CAudioIO
31  */
32 //LCOV_EXCL_START
33 CAudioIO::CAudioIO() :
34     mpPulseAudioClient(nullptr),
35     __mMutex(PTHREAD_MUTEX_INITIALIZER),
36     __mCondMutex(PTHREAD_MUTEX_INITIALIZER),
37     __mCond(PTHREAD_COND_INITIALIZER),
38     __mIsInit(false) {
39     mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
40     mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
41     mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
42     mByPolicy = false;
43 }
44 //LCOV_EXCL_STOP
45
46 CAudioIO::CAudioIO(CAudioInfo& audioInfo) :
47     mpPulseAudioClient(nullptr),
48     __mMutex(PTHREAD_MUTEX_INITIALIZER),
49     __mCondMutex(PTHREAD_MUTEX_INITIALIZER),
50     __mCond(PTHREAD_COND_INITIALIZER),
51     __mIsInit(false) {
52     mAudioInfo = audioInfo;
53     mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
54     mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
55     mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
56     mByPolicy = false;
57 }
58
59 void CAudioIO::setInit(bool flag) {
60     __mIsInit = flag;
61 }
62
63 bool CAudioIO::isInit() {
64     return __mIsInit;
65 }
66
67 bool CAudioIO::IsReady() {
68     return ((mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING ||
69              mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED));
70 }
71
72 void CAudioIO::internalLock() {
73     if (!__mIsInit)
74         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
75
76     if (pthread_mutex_lock(&__mMutex) != 0)
77         THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()"); //LCOV_EXCL_LINE
78
79 #ifdef _AUDIO_IO_DEBUG_TIMING_
80     AUDIO_IO_LOGD(COLOR_RED "%p LOCKED" COLOR_END, &__mMutex);
81 #endif
82 }
83
84 void CAudioIO::internalUnlock() {
85     if (!__mIsInit)
86         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
87
88     if (pthread_mutex_unlock(&__mMutex) != 0)
89         THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()"); //LCOV_EXCL_LINE
90
91 #ifdef _AUDIO_IO_DEBUG_TIMING_
92     AUDIO_IO_LOGD(COLOR_GREEN "%p UNLOCKED" COLOR_END, &__mMutex);
93 #endif
94 }
95
96 void CAudioIO::internalWait() {
97     if (!__mIsInit)
98         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
99
100 #ifdef _AUDIO_IO_DEBUG_TIMING_
101     AUDIO_IO_LOGD(COLOR_RED "WAIT" COLOR_END);
102 #endif
103     pthread_mutex_lock(&__mCondMutex);
104
105     struct timeval now = { 0, };
106     struct timeval to_wait = { 0, };
107     struct timeval until = { 0, };
108     struct timespec until_ts = { 0, };
109
110     constexpr int COND_TIMEOUT_MS = 200;
111
112     gettimeofday(&now, nullptr);
113     to_wait.tv_sec = COND_TIMEOUT_MS / 1000UL;
114     to_wait.tv_usec = (COND_TIMEOUT_MS % 1000UL) * 1000UL;
115     timeradd(&now, &to_wait, &until);
116     until_ts.tv_sec = until.tv_sec;
117     until_ts.tv_nsec = until.tv_usec * 1000UL;
118
119     int ret = pthread_cond_timedwait(&__mCond, &__mCondMutex, &until_ts);
120     if (ret != 0) {
121         char str_error[256];
122         AUDIO_IO_LOGE("pthread_cond_timedwait error=[%d][%s]", ret, strerror_r(ret, str_error, sizeof(str_error)));
123     }
124
125     pthread_mutex_unlock(&__mCondMutex);
126 }
127
128 void CAudioIO::internalSignal() {
129     if (!__mIsInit)
130         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
131
132 #ifdef _AUDIO_IO_DEBUG_TIMING_
133     AUDIO_IO_LOGD(COLOR_GREEN "SIGNAL" COLOR_END);
134 #endif
135
136     pthread_mutex_lock(&__mCondMutex);
137     pthread_cond_signal(&__mCond);
138     pthread_mutex_unlock(&__mCondMutex);
139 }
140
141 void CAudioIO::initialize() {
142     if (__mIsInit)
143         return;
144
145     AUDIO_IO_LOGD("initialize");
146
147     int ret = pthread_mutex_init(&__mMutex, NULL);
148     if (ret != 0)
149         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_init()"); //LCOV_EXCL_LINE
150
151     ret = pthread_cond_init(&__mCond, NULL);
152     if (ret != 0)
153         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_init()"); //LCOV_EXCL_LINE
154
155     __mIsInit = true;
156 }
157
158 void CAudioIO::finalize() {
159     if (!__mIsInit)
160         return;
161
162     AUDIO_IO_LOGD("finalize");
163
164     bool error_occured = false;
165     int ret = pthread_mutex_destroy(&__mMutex);
166     if (ret != 0) {
167 //LCOV_EXCL_START
168         AUDIO_IO_LOGE("Failed pthread_mutex_destroy(%p) errno:%d", &__mMutex, ret);
169         error_occured = true;
170 //LCOV_EXCL_STOP
171     }
172
173     ret = pthread_mutex_destroy(&__mCondMutex);
174     if (ret != 0) {
175 //LCOV_EXCL_START
176         AUDIO_IO_LOGE("Failed cond pthread_mutex_destroy(%p) errno:%d", &__mCondMutex, ret);
177         error_occured = true;
178 //LCOV_EXCL_STOP
179     }
180
181     ret = pthread_cond_destroy(&__mCond);
182     if (ret != 0) {
183 //LCOV_EXCL_START
184         AUDIO_IO_LOGE("Failed pthread_cond_destroy(%p) errno:%d", &__mCond, ret);
185         error_occured = true;
186 //LCOV_EXCL_STOP
187     }
188
189     if (error_occured)
190         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Finalize Failed"); //LCOV_EXCL_LINE
191
192     __mIsInit = false;
193 }
194
195 void CAudioIO::onStream(CPulseAudioClient* pClient, size_t length) {
196     assert(__mIsInit);
197     assert(pClient);
198     assert(length > 0);
199
200 #ifdef _AUDIO_IO_DEBUG_TIMING_
201     AUDIO_IO_LOGD("mStreamCallback.onStream(%p), pClient(%p), length(%zu)", mStreamCallback.onStream, pClient, length);
202 #endif
203
204     if (mStreamCallback.onStream)
205         mStreamCallback.onStream(length, mStreamCallback.mUserData);
206 }
207
208 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) {
209     assert(__mIsInit);
210     assert(state >= CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE && state < CAudioInfo::EAudioIOState::AUDIO_IO_STATE_MAX);
211
212     mStatePrev = mState;
213     mState = state;
214     mByPolicy = byPolicy;
215
216     if (mState == mStatePrev)
217         return;
218
219     static const char* state_string[] = { "NONE", "IDLE", "RUNNING", "PAUSED" };
220
221     AUDIO_IO_LOGD("previous(%s,%d) ===> current(%s,%d), by_policy(%d)",
222                   state_string[static_cast<int>(mStatePrev)],
223                   static_cast<int>(mStatePrev),
224                   state_string[static_cast<int>(mState)],
225                   static_cast<int>(mState),
226                   mByPolicy);
227
228     if (mStateChangedCallback.onStateChanged)
229         mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData);
230 }
231
232 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state) {
233     onStateChanged(state, false);
234 }
235
236 CAudioInfo::EAudioIOState CAudioIO::getState() noexcept {
237     return mState;
238 }
239
240 void CAudioIO::prepare() {
241     if (!__mIsInit)
242         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
243 }
244
245 void CAudioIO::unprepare() {
246     if (!__mIsInit)
247         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
248 }
249
250 void CAudioIO::pause() {
251     if (!__mIsInit || !IsReady())
252         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO"); //LCOV_EXCL_LINE
253
254     try {
255         internalLock();
256         AUDIO_IO_LOGD("pause");
257         mpPulseAudioClient->cork(true);
258         internalUnlock();
259     } catch (const CAudioError& e) {
260         internalUnlock();
261         throw;
262     }
263 }
264
265 void CAudioIO::resume() {
266     if (!__mIsInit || !IsReady())
267         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO"); //LCOV_EXCL_LINE
268
269     try {
270         internalLock();
271         AUDIO_IO_LOGD("resume");
272         mpPulseAudioClient->cork(false);
273         internalUnlock();
274     } catch (const CAudioError& e) {
275         internalUnlock();
276         throw;
277     }
278 }
279 void CAudioIO::flush() {
280     if (!__mIsInit || !IsReady())
281         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO"); //LCOV_EXCL_LINE
282
283     try {
284         if (mpPulseAudioClient->isInThread()) {
285             mpPulseAudioClient->flush();
286         } else {
287             internalLock();
288             mpPulseAudioClient->flush();
289             internalUnlock();
290         }
291     } catch (const CAudioError& e) {
292         if (!mpPulseAudioClient->isInThread())
293             internalUnlock();
294         throw;
295     }
296 }
297
298 CAudioInfo& CAudioIO::getAudioInfo() {
299     if (!__mIsInit)
300         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
301
302     return mAudioInfo;
303 }
304
305 void CAudioIO::setStreamCallback(SStreamCallback callback) {
306     if (!__mIsInit)
307         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
308
309     mStreamCallback = callback;
310 }
311
312 CAudioIO::SStreamCallback CAudioIO::getStreamCallback() {
313     if (!__mIsInit)
314         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
315
316     return mStreamCallback;
317 }
318
319 void CAudioIO::setStateChangedCallback(SStateChangedCallback callback) {
320     if (!__mIsInit)
321         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
322
323     mStateChangedCallback = callback;
324 }
325
326 CAudioIO::SStateChangedCallback CAudioIO::getStateChangedCallback() {
327     if (!__mIsInit)
328         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
329
330     return mStateChangedCallback;
331 }
332
333 void CAudioIO::setStreamInfo(sound_stream_info_h stream_info) {
334     if (!stream_info)
335         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "stream_info is NULL"); //LCOV_EXCL_LINE
336
337     if (!__mIsInit)
338         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
339
340     if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
341         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started"); //LCOV_EXCL_LINE
342
343     int errorCode = SOUND_MANAGER_ERROR_NONE;
344     char *type = nullptr;
345     int index = -1;
346     bool avail = false;
347
348     if ((errorCode = sound_manager_is_available_stream_information(stream_info, NATIVE_API_AUDIO_IO, &avail)) != SOUND_MANAGER_ERROR_NONE)
349         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info is invalid [ret:%d]", errorCode); //LCOV_EXCL_LINE
350     if (!avail)
351         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED_TYPE, "Input stream is not supported"); //LCOV_EXCL_LINE
352
353     if ((errorCode = sound_manager_get_type_from_stream_information(stream_info, &type)) != SOUND_MANAGER_ERROR_NONE)
354         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->stream_type is invalid [ret:%d]", errorCode); //LCOV_EXCL_LINE
355     if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN)
356         getAudioInfo().setAudioTypeByInputStreamType(type);
357     else
358         getAudioInfo().setAudioTypeByOutputStreamType(type);
359
360     if ((errorCode = sound_manager_get_index_from_stream_information(stream_info, &index)) != SOUND_MANAGER_ERROR_NONE)
361         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->index is invalid [ret:%d]", errorCode); //LCOV_EXCL_LINE
362     getAudioInfo().setAudioIndex(index);
363 }