2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include "CAudioIODef.h"
24 using namespace tizen_media_audio;
30 CAudioIO::CAudioIO() :
31 mpAudioSessionHandler(NULL),
32 mpPulseAudioClient(NULL),
33 __mMutex(PTHREAD_MUTEX_INITIALIZER),
34 __mCond(PTHREAD_COND_INITIALIZER),
36 __mForceIgnore(false) {
37 mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
38 mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
39 mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
43 CAudioIO::CAudioIO(CAudioInfo& audioInfo) :
44 mpAudioSessionHandler(NULL),
45 mpPulseAudioClient(NULL),
46 __mMutex(PTHREAD_MUTEX_INITIALIZER),
47 __mCond(PTHREAD_COND_INITIALIZER),
49 __mForceIgnore(false) {
50 mAudioInfo = audioInfo;
51 mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
52 mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
53 mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
57 CAudioIO::~CAudioIO() {
60 void CAudioIO::setInit(bool flag) {
64 bool CAudioIO::isInit() {
68 bool CAudioIO::IsReady() {
69 return ((mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING || mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)? true : false);
72 void CAudioIO::internalLock() throw(CAudioError) {
73 if (__mIsInit == false) {
74 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
77 if (pthread_mutex_lock(&__mMutex) != 0) {
78 THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
80 #ifdef _AUDIO_IO_DEBUG_TIMING_
81 AUDIO_IO_LOGD(COLOR_RED "LOCK" COLOR_END);
85 void CAudioIO::internalUnlock() throw(CAudioError) {
86 if (__mIsInit == false) {
87 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
90 if (pthread_mutex_unlock(&__mMutex) != 0) {
91 THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
93 #ifdef _AUDIO_IO_DEBUG_TIMING_
94 AUDIO_IO_LOGD(COLOR_GREEN "UNLOCK" COLOR_END);
98 void CAudioIO::internalWait() throw(CAudioError) {
99 if (__mIsInit == false) {
100 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
103 #ifdef _AUDIO_IO_DEBUG_TIMING_
104 AUDIO_IO_LOGD(COLOR_RED "WAIT" COLOR_END);
107 pthread_cond_wait(&__mCond, &__mMutex);
110 void CAudioIO::internalSignal() throw(CAudioError) {
111 if (__mIsInit == false) {
112 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
115 #ifdef _AUDIO_IO_DEBUG_TIMING_
116 AUDIO_IO_LOGD(COLOR_GREEN "SIGNAL" COLOR_END);
119 pthread_cond_signal(&__mCond);
122 bool CAudioIO::isForceIgnore() {
123 return __mForceIgnore;
126 void CAudioIO::initialize() throw(CAudioError) {
127 if (__mIsInit == true) {
131 AUDIO_IO_LOGD("initialize");
133 int ret = pthread_mutex_init(&__mMutex, NULL);
135 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_init()");
138 ret = pthread_cond_init(&__mCond, NULL);
140 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_init()");
146 void CAudioIO::finalize() {
147 if (__mIsInit == false) {
151 AUDIO_IO_LOGD("finalize");
153 int ret = pthread_mutex_destroy(&__mMutex);
155 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_destroy() ret:%d", ret);
158 ret = pthread_cond_destroy(&__mCond);
160 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_destroy() ret:%d", ret);
166 void CAudioIO::onStream(CPulseAudioClient* pClient, size_t length) {
167 assert(__mIsInit == true);
168 assert(pClient != NULL);
171 #ifdef _AUDIO_IO_DEBUG_TIMING_
172 AUDIO_IO_LOGD("mStreamCallback.onStream(%p), pClient(%p), length(%zu)", mStreamCallback.onStream, pClient, length);
175 if (mStreamCallback.onStream != NULL) {
176 mStreamCallback.onStream(length, mStreamCallback.mUserData);
180 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) {
181 assert(__mIsInit == true);
182 assert(state >= CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE && state < CAudioInfo::EAudioIOState::AUDIO_IO_STATE_MAX);
186 mByPolicy = byPolicy;
188 if (mState == mStatePrev)
191 const char* state_string[] = { "NONE", "IDLE", "RUNNING", "PAUSED" };
193 AUDIO_IO_LOGD("previous(%s,%d) ===> current(%s,%d), by_policy(%d)",
194 state_string[static_cast<int>(mStatePrev)],
195 static_cast<int>(mStatePrev),
196 state_string[static_cast<int>(mState)],
197 static_cast<int>(mState),
200 if (mStateChangedCallback.onStateChanged != NULL) {
201 mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData);
205 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state) {
206 onStateChanged(state, false);
209 CAudioInfo::EAudioIOState CAudioIO::getState() {
213 void CAudioIO::onInterrupt(CAudioSessionHandler* pHandler, int id, mm_sound_focus_type_e focus_type, mm_sound_focus_state_e state, const char *reason_for_change, const char *additional_info) {
216 int session_option = pHandler->getOptions();
219 ///////////////////////////////////////
220 // Triggered by 'focus watch callback'
221 ///////////////////////////////////////
223 if (session_option & (MM_SESSION_OPTION_PAUSE_OTHERS | MM_SESSION_OPTION_UNINTERRUPTIBLE)) {
224 AUDIO_IO_LOGD("Session option is pausing others or uninterruptible, skip...");
228 if (state == FOCUS_IS_RELEASED) {
229 // Focus handle(id) of the other application was released, do resume if possible
231 if (mpPulseAudioClient) {
232 mpPulseAudioClient->cork(false);
233 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
237 // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
238 state = FOCUS_IS_ACQUIRED;
239 } else if (state == FOCUS_IS_ACQUIRED) {
240 // Focus handle(id) of the other application was acquired, do pause if possible
242 if (mpPulseAudioClient) {
243 /* FIXME: Skip this codes due to the blocking of drain() function
244 if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
245 if (mpPulseAudioClient->drain() == false) {
246 AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
250 mpPulseAudioClient->cork(true);
251 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
255 // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
256 state = FOCUS_IS_RELEASED;
259 ///////////////////////////////////////
260 // Triggered by 'focus callback'
261 ///////////////////////////////////////
263 if (pHandler->getId() != id) {
264 AUDIO_IO_LOGW("Id is different, why? [mId : %d]", pHandler->getId());
267 if (session_option & MM_SESSION_OPTION_UNINTERRUPTIBLE) {
268 AUDIO_IO_LOGD("Session option is uninterruptible, skip...");
272 if (state == FOCUS_IS_RELEASED) {
273 // Focus handle(id) was released, do pause here
275 if (mpPulseAudioClient) {
276 /* FIXME: Skip this codes due to the blocking of drain() function
277 if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
278 if (mpPulseAudioClient->drain() == false) {
279 AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
283 mpPulseAudioClient->cork(true);
284 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
287 } else if (state == FOCUS_IS_ACQUIRED) {
288 // Focus handle(id) was acquired again,
289 // check reason_for_change ("call-voice","call-video","voip","alarm","notification", ...)
290 // do resume here and call interrupt completed callback to application.
292 if (mpPulseAudioClient) {
293 mpPulseAudioClient->cork(false);
294 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
300 if (mInterruptCallback.onInterrupt != NULL) {
301 IAudioSessionEventListener::EInterruptCode e = IAudioSessionEventListener::EInterruptCode::INTERRUPT_COMPLETED;
302 e = IAudioSessionEventListener::convertInterruptedCode(state, reason_for_change);
303 mInterruptCallback.onInterrupt(e, mInterruptCallback.mUserData);
307 void CAudioIO::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
310 if (signal == MM_SOUND_SIGNAL_RELEASE_INTERNAL_FOCUS) {
311 if (value == 1 && pHandler->getSubscribeId() > 0) {
312 // Unregister focus watch callback & disable session handler
313 pHandler->disableSessionHandler();
314 AUDIO_IO_LOGD("Session handler disabled by signal");
315 } else if (value == 0) {
316 // Currently do nothing...
321 void CAudioIO::prepare() throw(CAudioError) {
322 if (__mIsInit == false) {
323 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
327 AUDIO_IO_LOGD("------> prepare done");
329 } catch (CAudioError e) {
334 void CAudioIO::unprepare() throw(CAudioError) {
335 if (__mIsInit == false) {
336 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
340 AUDIO_IO_LOGD("unprepare ----->");
342 } catch (CAudioError e) {
347 void CAudioIO::pause() throw(CAudioError) {
348 if (__mIsInit == false || IsReady() == false) {
349 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
354 AUDIO_IO_LOGD("pause");
355 mpPulseAudioClient->cork(true);
357 } catch (CAudioError e) {
363 void CAudioIO::resume() throw(CAudioError) {
364 if (__mIsInit == false || IsReady() == false) {
365 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
370 AUDIO_IO_LOGD("resume");
371 mpPulseAudioClient->cork(false);
373 } catch (CAudioError e) {
379 void CAudioIO::drain() throw(CAudioError) {
380 if (__mIsInit == false || IsReady() == false) {
381 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
385 if (mpPulseAudioClient->isInThread()) {
386 mpPulseAudioClient->drain();
389 mpPulseAudioClient->drain();
392 } catch (CAudioError e) {
393 if (!mpPulseAudioClient->isInThread()) {
400 void CAudioIO::flush() throw(CAudioError) {
401 if (__mIsInit == false || IsReady() == false) {
402 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
406 if (mpPulseAudioClient->isInThread()) {
407 mpPulseAudioClient->flush();
410 mpPulseAudioClient->flush();
413 } catch (CAudioError e) {
414 if (!mpPulseAudioClient->isInThread()) {
421 CAudioInfo& CAudioIO::getAudioInfo() throw(CAudioError) {
422 if (__mIsInit == false) {
423 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
429 void CAudioIO::setStreamCallback(SStreamCallback callback) throw(CAudioError) {
430 if (__mIsInit == false) {
431 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
434 mStreamCallback = callback;
437 CAudioIO::SStreamCallback CAudioIO::getStreamCallback() throw(CAudioError) {
438 if (__mIsInit == false) {
439 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
442 return mStreamCallback;
445 void CAudioIO::setStateChangedCallback(SStateChangedCallback callback) throw(CAudioError) {
446 if (__mIsInit == false) {
447 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
450 mStateChangedCallback = callback;
453 CAudioIO::SStateChangedCallback CAudioIO::getStateChangedCallback() throw(CAudioError) {
454 if (__mIsInit == false) {
455 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
458 return mStateChangedCallback;
461 void CAudioIO::setInterruptCallback(SInterruptCallback callback) throw(CAudioError) {
462 if (__mIsInit == false) {
463 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
466 mInterruptCallback = callback;
469 CAudioIO::SInterruptCallback CAudioIO::getInterruptCallback() throw(CAudioError) {
470 if (__mIsInit == false) {
471 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
474 return mInterruptCallback;
477 void CAudioIO::ignoreSession() throw(CAudioError) {
478 if (__mIsInit == false)
479 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
484 if (mpPulseAudioClient != NULL && mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
485 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "An Operation is not permitted while started");
487 abandonInternalFocus();
490 } catch (CAudioError e) {
496 void CAudioIO::setStreamInfo(sound_stream_info_h stream_info) throw(CAudioError) {
497 if (stream_info == NULL)
498 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "stream_info is NULL");
500 if (__mIsInit == false)
501 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
504 if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
505 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
507 abandonInternalFocus();
509 int errorCode = SOUND_MANAGER_ERROR_NONE;
510 CAudioInfo::EAudioType audioType = CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA;
515 if ((errorCode = sound_manager_is_available_stream_information(stream_info, NATIVE_API_AUDIO_IO, &avail)) != SOUND_MANAGER_ERROR_NONE)
516 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info is invalid [ret:%d]", errorCode);
519 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED_TYPE, "Input stream is not supported");
521 if ((errorCode = sound_manager_get_type_from_stream_information(stream_info, &type)) != SOUND_MANAGER_ERROR_NONE)
522 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->stream_type is invalid [ret:%d]", errorCode);
524 if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN)
525 getAudioInfo().convertInputStreamType2AudioType(type, &audioType);
527 getAudioInfo().convertOutputStreamType2AudioType(type, &audioType);
528 getAudioInfo().setAudioType(audioType);
530 if ((errorCode = sound_manager_get_index_from_stream_information(stream_info, &index)) != SOUND_MANAGER_ERROR_NONE)
531 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->index is invalid [ret:%d]", errorCode);
533 getAudioInfo().setAudioIndex(index);
535 AUDIO_IO_LOGD("stream info(%p) is set", stream_info);
536 } catch (CAudioError e) {
541 void CAudioIO::setInternalStreamInfo() throw(CAudioError) {
542 if (__mIsInit == false)
543 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
546 if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
547 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
549 if (mpAudioSessionHandler &&
550 mpAudioSessionHandler->getMultimediaSession() == MM_SESSION_TYPE_VOIP) {
551 sound_stream_info_h stream_info = NULL;
552 mpAudioSessionHandler->getInternalVoipStreamInfo(&stream_info);
553 AUDIO_IO_LOGD("get internal VOIP stream info(%p)", stream_info);
554 setStreamInfo(stream_info);
556 } catch (CAudioError e) {
561 void CAudioIO::abandonInternalFocus() throw(CAudioError) {
562 bool isSkip = mpAudioSessionHandler->isSkipSession();
563 int id = mpAudioSessionHandler->getId();
566 if (isSkip == false && id >= 0)
567 mpAudioSessionHandler->unregisterSound();
569 mpAudioSessionHandler->finalize();
570 __mForceIgnore = true;
571 } catch (CAudioError e) {