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.
22 #include "CAudioIODef.h"
25 using namespace tizen_media_audio;
31 CAudioIO::CAudioIO() :
32 mpAudioSessionHandler(NULL),
33 mpPulseAudioClient(NULL),
34 __mMutex(PTHREAD_MUTEX_INITIALIZER),
35 __mSessionMutex(PTHREAD_MUTEX_INITIALIZER),
36 __mCondMutex(PTHREAD_MUTEX_INITIALIZER),
37 __mCond(PTHREAD_COND_INITIALIZER),
39 __mForceIgnore(false) {
40 mInterruptCode = IAudioSessionEventListener::EInterruptCode::INTERRUPT_MAX;
41 mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
42 mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
43 mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
45 mIsInterrupted = false;
48 CAudioIO::CAudioIO(CAudioInfo& audioInfo) :
49 mpAudioSessionHandler(NULL),
50 mpPulseAudioClient(NULL),
51 __mMutex(PTHREAD_MUTEX_INITIALIZER),
52 __mSessionMutex(PTHREAD_MUTEX_INITIALIZER),
53 __mCondMutex(PTHREAD_MUTEX_INITIALIZER),
54 __mCond(PTHREAD_COND_INITIALIZER),
56 __mForceIgnore(false) {
57 mAudioInfo = audioInfo;
58 mInterruptCode = IAudioSessionEventListener::EInterruptCode::INTERRUPT_MAX;
59 mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
60 mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
61 mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
63 mIsInterrupted = false;
66 CAudioIO::~CAudioIO() {
69 void CAudioIO::setInit(bool flag) {
73 bool CAudioIO::isInit() {
77 bool CAudioIO::IsReady() {
78 return ((mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING ||
79 mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)? true : false);
82 void CAudioIO::internalLock() {
83 if (__mIsInit == false)
84 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
86 if (pthread_mutex_lock(&__mMutex) != 0)
87 THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
89 #ifdef _AUDIO_IO_DEBUG_TIMING_
90 AUDIO_IO_LOGD(COLOR_RED "%p LOCKED" COLOR_END, &__mMutex);
94 void CAudioIO::internalUnlock() {
95 if (__mIsInit == false)
96 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
98 if (pthread_mutex_unlock(&__mMutex) != 0)
99 THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
101 #ifdef _AUDIO_IO_DEBUG_TIMING_
102 AUDIO_IO_LOGD(COLOR_GREEN "%p UNLOCKED" COLOR_END, &__mMutex);
106 void CAudioIO::internalSessionLock() {
107 if (__mIsInit == false)
108 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
110 if (pthread_mutex_lock(&__mSessionMutex) != 0)
111 THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed session pthread_mutex_lock()");
113 #ifdef _AUDIO_IO_DEBUG_TIMING_
114 AUDIO_IO_LOGD(COLOR_RED "%p LOCKED" COLOR_END, &__mSessionMutex);
118 void CAudioIO::internalSessionUnlock() {
119 if (__mIsInit == false)
120 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
122 if (pthread_mutex_unlock(&__mSessionMutex) != 0)
123 THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed session pthread_mutex_unlock()");
125 #ifdef _AUDIO_IO_DEBUG_TIMING_
126 AUDIO_IO_LOGD(COLOR_GREEN "%p UNLOCKED" COLOR_END, &__mSessionMutex);
131 void CAudioIO::internalWait() {
132 if (__mIsInit == false)
133 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
135 #ifdef _AUDIO_IO_DEBUG_TIMING_
136 AUDIO_IO_LOGD(COLOR_RED "WAIT" COLOR_END);
139 pthread_mutex_lock(&__mCondMutex);
140 pthread_cond_wait(&__mCond, &__mCondMutex);
141 pthread_mutex_unlock(&__mCondMutex);
144 void CAudioIO::internalSignal() {
145 if (__mIsInit == false)
146 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
148 #ifdef _AUDIO_IO_DEBUG_TIMING_
149 AUDIO_IO_LOGD(COLOR_GREEN "SIGNAL" COLOR_END);
152 pthread_mutex_lock(&__mCondMutex);
153 pthread_cond_signal(&__mCond);
154 pthread_mutex_unlock(&__mCondMutex);
157 bool CAudioIO::isForceIgnore() {
158 return __mForceIgnore;
161 bool CAudioIO::isSessionEnabled() {
162 if (mpAudioSessionHandler &&
163 mpAudioSessionHandler->isSkipSession() == false &&
164 mpAudioSessionHandler->getId() >= 0)
170 void CAudioIO::initialize() {
171 if (__mIsInit == true)
174 AUDIO_IO_LOGD("initialize");
176 int ret = pthread_mutex_init(&__mMutex, NULL);
178 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_init()");
180 ret = pthread_mutex_init(&__mSessionMutex, NULL);
182 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed session pthread_mutex_init()");
184 ret = pthread_cond_init(&__mCond, NULL);
186 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_init()");
191 void CAudioIO::finalize() {
192 if (__mIsInit == false)
195 AUDIO_IO_LOGD("finalize");
197 bool error_occured = false;
198 int ret = pthread_mutex_destroy(&__mMutex);
200 AUDIO_IO_LOGE("Failed pthread_mutex_destroy(%p) errno:%d", &__mMutex, ret);
201 error_occured = true;
204 ret = pthread_mutex_destroy(&__mSessionMutex);
206 AUDIO_IO_LOGE("Failed session pthread_mutex_destroy(%p) errno:%d", &__mSessionMutex, ret);
207 error_occured = true;
210 ret = pthread_mutex_destroy(&__mCondMutex);
212 AUDIO_IO_LOGE("Failed cond pthread_mutex_destroy(%p) errno:%d", &__mCondMutex, ret);
213 error_occured = true;
216 ret = pthread_cond_destroy(&__mCond);
218 AUDIO_IO_LOGE("Failed pthread_cond_destroy(%p) errno:%d", &__mCond, ret);
219 error_occured = true;
223 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Finalize Failed");
228 void CAudioIO::onStream(CPulseAudioClient* pClient, size_t length) {
229 assert(__mIsInit == true);
230 assert(pClient != NULL);
233 #ifdef _AUDIO_IO_DEBUG_TIMING_
234 AUDIO_IO_LOGD("mStreamCallback.onStream(%p), pClient(%p), length(%zu)", mStreamCallback.onStream, pClient, length);
237 if (mStreamCallback.onStream != NULL)
238 mStreamCallback.onStream(length, mStreamCallback.mUserData);
241 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) {
242 assert(__mIsInit == true);
243 assert(state >= CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE && state < CAudioInfo::EAudioIOState::AUDIO_IO_STATE_MAX);
247 mByPolicy = byPolicy;
249 if (mState == mStatePrev)
252 const char* state_string[] = { "NONE", "IDLE", "RUNNING", "PAUSED" };
254 AUDIO_IO_LOGD("previous(%s,%d) ===> current(%s,%d), by_policy(%d)",
255 state_string[static_cast<int>(mStatePrev)],
256 static_cast<int>(mStatePrev),
257 state_string[static_cast<int>(mState)],
258 static_cast<int>(mState),
261 if (mStateChangedCallback.onStateChanged != NULL)
262 mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData);
265 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state) {
266 onStateChanged(state, false);
269 CAudioInfo::EAudioIOState CAudioIO::getState() {
273 int CAudioIO::sendInterrupt(void* user_data) {
274 CAudioIO *pCaudioIo = (CAudioIO *)user_data;
276 if (pCaudioIo && pCaudioIo->mInterruptCallback.onInterrupt) {
277 AUDIO_IO_LOGD("sending interrupt [%d]", static_cast<int>(pCaudioIo->mInterruptCode));
278 pCaudioIo->mInterruptCallback.onInterrupt(pCaudioIo->mInterruptCode, pCaudioIo->mInterruptCallback.mUserData);
283 int caudio_gsource_callback(void *user_data) {
284 CAudioIO::sendInterrupt(user_data);
288 void CAudioIO::onInterrupt(CAudioSessionHandler* pHandler, int id, mm_sound_focus_type_e focus_type,
289 mm_sound_focus_state_e state, const char *reason_for_change, const char *additional_info) {
292 int session_option = pHandler->getOptions();
295 ///////////////////////////////////////
296 // Triggered by 'focus watch callback'
297 ///////////////////////////////////////
299 if (session_option & (MM_SESSION_OPTION_PAUSE_OTHERS | MM_SESSION_OPTION_UNINTERRUPTIBLE)) {
300 AUDIO_IO_LOGD("Session option is pausing others or uninterruptible, skip...");
304 if (state == FOCUS_IS_RELEASED) {
305 // Focus handle(id) of the other application was released, notify resume
306 // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
307 state = FOCUS_IS_ACQUIRED;
308 } else if (state == FOCUS_IS_ACQUIRED) {
309 // Focus handle(id) of the other application was acquired, do pause if possible
311 if (mpPulseAudioClient)
312 mpPulseAudioClient->cork(true);
313 mIsInterrupted = true;
316 // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
317 state = FOCUS_IS_RELEASED;
320 ///////////////////////////////////////
321 // Triggered by 'focus callback'
322 ///////////////////////////////////////
324 if (pHandler->getId() != id)
325 AUDIO_IO_LOGW("Id is different, why? [mId : %d]", pHandler->getId());
327 if (session_option & MM_SESSION_OPTION_UNINTERRUPTIBLE) {
328 AUDIO_IO_LOGD("Session option is uninterruptible, skip...");
332 if (state == FOCUS_IS_RELEASED) {
333 // Focus handle(id) was released, do pause here
335 if (mpPulseAudioClient)
336 mpPulseAudioClient->cork(true);
338 mIsInterrupted = true;
340 } else if (state == FOCUS_IS_ACQUIRED) {
341 // Focus handle(id) was acquired again,
342 // check reason_for_change ("call-voice","call-video","voip","alarm","notification", ...)
343 // do resume here and call interrupt completed callback to application.
347 if (mInterruptCallback.onInterrupt != NULL) {
348 IAudioSessionEventListener::EInterruptCode e = IAudioSessionEventListener::EInterruptCode::INTERRUPT_COMPLETED;
349 e = IAudioSessionEventListener::convertInterruptedCode(state, reason_for_change);
351 if (EInterruptCode::INTERRUPT_COMPLETED == e) {
353 g_idle_add(caudio_gsource_callback, this);
355 mInterruptCallback.onInterrupt(e, mInterruptCallback.mUserData);
360 void CAudioIO::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
363 if (signal == MM_SOUND_SIGNAL_RELEASE_INTERNAL_FOCUS) {
364 if (value == 1 && pHandler->getSubscribeId() > 0) {
365 // Unregister focus watch callback & disable session handler
366 pHandler->disableSessionHandler();
367 AUDIO_IO_LOGD("Session handler disabled by signal");
368 } else if (value == 0) {
369 // Currently do nothing...
374 void CAudioIO::prepare() {
375 if (__mIsInit == false)
376 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
379 if (mIsInterrupted) {
380 internalSessionLock();
381 AUDIO_IO_LOGE("This is preparing during interrupted!!!");
382 if (isSessionEnabled() && __mForceIgnore == false) {
383 AUDIO_IO_LOGE("Session updatePlaying!!!");
384 mpAudioSessionHandler->updatePlaying();
386 internalSessionUnlock();
388 } catch (CAudioError& e) {
389 internalSessionUnlock();
393 if (mIsInterrupted) {
394 if (mpPulseAudioClient && mpPulseAudioClient->isCorked()) {
395 AUDIO_IO_LOGE("Uncork!");
396 mpPulseAudioClient->cork(false);
398 mIsInterrupted = false;
402 void CAudioIO::unprepare() {
403 if (__mIsInit == false)
404 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
407 void CAudioIO::pause() {
408 if (__mIsInit == false || IsReady() == false)
409 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
413 AUDIO_IO_LOGD("pause");
414 mpPulseAudioClient->cork(true);
416 } catch (CAudioError& e) {
422 void CAudioIO::resume() {
423 if (__mIsInit == false || IsReady() == false)
424 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
428 AUDIO_IO_LOGD("resume");
429 mpPulseAudioClient->cork(false);
431 } catch (CAudioError& e) {
437 void CAudioIO::drain() {
438 if (__mIsInit == false || IsReady() == false)
439 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
442 if (mpPulseAudioClient->isInThread()) {
443 mpPulseAudioClient->drain();
446 mpPulseAudioClient->drain();
449 } catch (CAudioError& e) {
450 if (!mpPulseAudioClient->isInThread())
456 void CAudioIO::flush() {
457 if (__mIsInit == false || IsReady() == false)
458 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
461 if (mpPulseAudioClient->isInThread()) {
462 mpPulseAudioClient->flush();
465 mpPulseAudioClient->flush();
468 } catch (CAudioError& e) {
469 if (!mpPulseAudioClient->isInThread())
475 CAudioInfo& CAudioIO::getAudioInfo() {
476 if (__mIsInit == false)
477 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
482 void CAudioIO::setStreamCallback(SStreamCallback callback) {
483 if (__mIsInit == false)
484 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
486 mStreamCallback = callback;
489 CAudioIO::SStreamCallback CAudioIO::getStreamCallback() {
490 if (__mIsInit == false)
491 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
493 return mStreamCallback;
496 void CAudioIO::setStateChangedCallback(SStateChangedCallback callback) {
497 if (__mIsInit == false)
498 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
500 mStateChangedCallback = callback;
503 CAudioIO::SStateChangedCallback CAudioIO::getStateChangedCallback() {
504 if (__mIsInit == false)
505 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
507 return mStateChangedCallback;
510 void CAudioIO::setInterruptCallback(SInterruptCallback callback) {
511 if (__mIsInit == false)
512 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
514 mInterruptCallback = callback;
517 CAudioIO::SInterruptCallback CAudioIO::getInterruptCallback() {
518 if (__mIsInit == false)
519 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
521 return mInterruptCallback;
524 void CAudioIO::ignoreSession() {
525 if (__mIsInit == false)
526 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
530 if (mpPulseAudioClient != NULL && mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
531 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "An Operation is not permitted while started");
533 abandonInternalFocus();
535 } catch (CAudioError& e) {
541 void CAudioIO::setStreamInfo(sound_stream_info_h stream_info) {
542 if (stream_info == NULL)
543 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "stream_info is NULL");
545 if (__mIsInit == false)
546 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
549 if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
550 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
552 abandonInternalFocus();
554 int errorCode = SOUND_MANAGER_ERROR_NONE;
555 CAudioInfo::EAudioType audioType = CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA;
560 if ((errorCode = sound_manager_is_available_stream_information(stream_info, NATIVE_API_AUDIO_IO, &avail)) != SOUND_MANAGER_ERROR_NONE)
561 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info is invalid [ret:%d]", errorCode);
563 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED_TYPE, "Input stream is not supported");
565 if ((errorCode = sound_manager_get_type_from_stream_information(stream_info, &type)) != SOUND_MANAGER_ERROR_NONE)
566 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->stream_type is invalid [ret:%d]", errorCode);
567 if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN)
568 getAudioInfo().convertInputStreamType2AudioType(type, &audioType);
570 getAudioInfo().convertOutputStreamType2AudioType(type, &audioType);
571 getAudioInfo().setAudioType(audioType);
573 if ((errorCode = sound_manager_get_index_from_stream_information(stream_info, &index)) != SOUND_MANAGER_ERROR_NONE)
574 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->index is invalid [ret:%d]", errorCode);
575 getAudioInfo().setAudioIndex(index);
577 } catch (CAudioError& e) {
582 void CAudioIO::setInternalStreamInfo() {
583 if (__mIsInit == false)
584 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
586 if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
587 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
589 sound_stream_info_h stream_info = NULL;
592 internalSessionLock();
593 if (mpAudioSessionHandler &&
594 mpAudioSessionHandler->getMultimediaSession() == MM_SESSION_TYPE_VOIP) {
595 mpAudioSessionHandler->getInternalVoipStreamInfo(&stream_info);
596 AUDIO_IO_LOGD("get internal VOIP stream info(%p)", stream_info);
598 internalSessionUnlock();
599 } catch (CAudioError& e) {
600 internalSessionUnlock();
604 /* NOTE: to avoid double-lock, this should be outside of try-catch. */
606 setStreamInfo(stream_info);
609 void CAudioIO::abandonInternalFocus() {
611 internalSessionLock();
612 if (isSessionEnabled()) {
613 mpAudioSessionHandler->unregisterSound();
614 mpAudioSessionHandler->finalize(); /* FIXME : SAFE_FINALIZE or SAFE_DELETE? */
616 __mForceIgnore = true;
617 internalSessionUnlock();
618 } catch (CAudioError& e) {
619 internalSessionUnlock();