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 __mCondMutex(PTHREAD_MUTEX_INITIALIZER),
36 __mCond(PTHREAD_COND_INITIALIZER),
38 __mForceIgnore(false) {
39 mInterruptCode = IAudioSessionEventListener::EInterruptCode::INTERRUPT_MAX;
40 mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
41 mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
42 mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
44 mIsInterrupted = false;
47 CAudioIO::CAudioIO(CAudioInfo& audioInfo) :
48 mpAudioSessionHandler(NULL),
49 mpPulseAudioClient(NULL),
50 __mMutex(PTHREAD_MUTEX_INITIALIZER),
51 __mCondMutex(PTHREAD_MUTEX_INITIALIZER),
52 __mCond(PTHREAD_COND_INITIALIZER),
54 __mForceIgnore(false) {
55 mAudioInfo = audioInfo;
56 mInterruptCode = IAudioSessionEventListener::EInterruptCode::INTERRUPT_MAX;
57 mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
58 mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
59 mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
61 mIsInterrupted = false;
64 CAudioIO::~CAudioIO() {
67 void CAudioIO::setInit(bool flag) {
71 bool CAudioIO::isInit() {
75 bool CAudioIO::IsReady() {
76 return ((mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING || mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)? true : false);
79 void CAudioIO::internalLock() {
80 if (__mIsInit == false) {
81 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
84 if (pthread_mutex_lock(&__mMutex) != 0) {
85 THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
88 #ifdef _AUDIO_IO_DEBUG_TIMING_
89 AUDIO_IO_LOGD(COLOR_RED "%p LOCKED" COLOR_END, &__mMutex);
93 void CAudioIO::internalUnlock() {
94 if (__mIsInit == false) {
95 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::internalWait() {
107 if (__mIsInit == false) {
108 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
111 #ifdef _AUDIO_IO_DEBUG_TIMING_
112 AUDIO_IO_LOGD(COLOR_RED "WAIT" COLOR_END);
115 pthread_mutex_lock(&__mCondMutex);
116 pthread_cond_wait(&__mCond, &__mCondMutex);
117 pthread_mutex_unlock(&__mCondMutex);
120 void CAudioIO::internalSignal() {
121 if (__mIsInit == false) {
122 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
125 #ifdef _AUDIO_IO_DEBUG_TIMING_
126 AUDIO_IO_LOGD(COLOR_GREEN "SIGNAL" COLOR_END);
129 pthread_mutex_lock(&__mCondMutex);
130 pthread_cond_signal(&__mCond);
131 pthread_mutex_unlock(&__mCondMutex);
134 bool CAudioIO::isForceIgnore() {
135 return __mForceIgnore;
138 void CAudioIO::initialize() {
139 if (__mIsInit == true) {
143 AUDIO_IO_LOGD("initialize");
145 int ret = pthread_mutex_init(&__mMutex, NULL);
147 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_init()");
150 ret = pthread_cond_init(&__mCond, NULL);
152 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_init()");
158 void CAudioIO::finalize() {
159 if (__mIsInit == false) {
163 AUDIO_IO_LOGD("finalize");
165 bool error_occured = false;
166 int ret = pthread_mutex_destroy(&__mMutex);
168 AUDIO_IO_LOGE("Failed pthread_mutex_destroy(%p) errno:%d", &__mMutex, ret);
169 error_occured = true;
171 ret = pthread_mutex_destroy(&__mCondMutex);
173 AUDIO_IO_LOGE("Failed pthread_mutex_destroy(%p) errno:%d", &__mCondMutex, ret);
174 error_occured = true;
178 ret = pthread_cond_destroy(&__mCond);
180 AUDIO_IO_LOGE("Failed pthread_cond_destroy(%p) errno:%d", &__mCond, ret);
181 error_occured = true;
185 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Finalize Failed");
190 void CAudioIO::onStream(CPulseAudioClient* pClient, size_t length) {
191 assert(__mIsInit == true);
192 assert(pClient != NULL);
195 #ifdef _AUDIO_IO_DEBUG_TIMING_
196 AUDIO_IO_LOGD("mStreamCallback.onStream(%p), pClient(%p), length(%zu)", mStreamCallback.onStream, pClient, length);
199 if (mStreamCallback.onStream != NULL) {
200 mStreamCallback.onStream(length, mStreamCallback.mUserData);
204 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) {
205 assert(__mIsInit == true);
206 assert(state >= CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE && state < CAudioInfo::EAudioIOState::AUDIO_IO_STATE_MAX);
210 mByPolicy = byPolicy;
212 if (mState == mStatePrev)
215 const char* state_string[] = { "NONE", "IDLE", "RUNNING", "PAUSED" };
217 AUDIO_IO_LOGD("previous(%s,%d) ===> current(%s,%d), by_policy(%d)",
218 state_string[static_cast<int>(mStatePrev)],
219 static_cast<int>(mStatePrev),
220 state_string[static_cast<int>(mState)],
221 static_cast<int>(mState),
224 if (mStateChangedCallback.onStateChanged != NULL) {
225 mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData);
229 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state) {
230 onStateChanged(state, false);
233 CAudioInfo::EAudioIOState CAudioIO::getState() {
237 int CAudioIO::sendInterrupt(void* user_data) {
238 CAudioIO *pCaudioIo = (CAudioIO *)user_data;
240 if (pCaudioIo && pCaudioIo->mInterruptCallback.onInterrupt) {
241 AUDIO_IO_LOGD("sending interrupt [%d]", static_cast<int>(pCaudioIo->mInterruptCode));
242 pCaudioIo->mInterruptCallback.onInterrupt(pCaudioIo->mInterruptCode, pCaudioIo->mInterruptCallback.mUserData);
247 int caudio_gsource_callback(void *user_data) {
248 CAudioIO::sendInterrupt(user_data);
252 void CAudioIO::onInterrupt(CAudioSessionHandler* pHandler, int id, mm_sound_focus_type_e focus_type,
253 mm_sound_focus_state_e state, const char *reason_for_change, const char *additional_info) {
256 int session_option = pHandler->getOptions();
259 ///////////////////////////////////////
260 // Triggered by 'focus watch callback'
261 ///////////////////////////////////////
263 if (session_option & (MM_SESSION_OPTION_PAUSE_OTHERS | MM_SESSION_OPTION_UNINTERRUPTIBLE)) {
264 AUDIO_IO_LOGD("Session option is pausing others or uninterruptible, skip...");
268 if (state == FOCUS_IS_RELEASED) {
269 // Focus handle(id) of the other application was released, notify resume
270 // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
271 state = FOCUS_IS_ACQUIRED;
272 } else if (state == FOCUS_IS_ACQUIRED) {
273 // Focus handle(id) of the other application was acquired, do pause if possible
275 if (mpPulseAudioClient) {
276 mpPulseAudioClient->cork(true);
278 mIsInterrupted = true;
281 // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
282 state = FOCUS_IS_RELEASED;
285 ///////////////////////////////////////
286 // Triggered by 'focus callback'
287 ///////////////////////////////////////
289 if (pHandler->getId() != id) {
290 AUDIO_IO_LOGW("Id is different, why? [mId : %d]", pHandler->getId());
293 if (session_option & MM_SESSION_OPTION_UNINTERRUPTIBLE) {
294 AUDIO_IO_LOGD("Session option is uninterruptible, skip...");
298 if (state == FOCUS_IS_RELEASED) {
299 // Focus handle(id) was released, do pause here
301 if (mpPulseAudioClient) {
302 mpPulseAudioClient->cork(true);
305 mIsInterrupted = true;
307 } else if (state == FOCUS_IS_ACQUIRED) {
308 // Focus handle(id) was acquired again,
309 // check reason_for_change ("call-voice","call-video","voip","alarm","notification", ...)
310 // do resume here and call interrupt completed callback to application.
314 if (mInterruptCallback.onInterrupt != NULL) {
315 IAudioSessionEventListener::EInterruptCode e = IAudioSessionEventListener::EInterruptCode::INTERRUPT_COMPLETED;
316 e = IAudioSessionEventListener::convertInterruptedCode(state, reason_for_change);
318 if (EInterruptCode::INTERRUPT_COMPLETED == e) {
320 g_idle_add(caudio_gsource_callback, this);
322 mInterruptCallback.onInterrupt(e, mInterruptCallback.mUserData);
327 void CAudioIO::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
330 if (signal == MM_SOUND_SIGNAL_RELEASE_INTERNAL_FOCUS) {
331 if (value == 1 && pHandler->getSubscribeId() > 0) {
332 // Unregister focus watch callback & disable session handler
333 pHandler->disableSessionHandler();
334 AUDIO_IO_LOGD("Session handler disabled by signal");
335 } else if (value == 0) {
336 // Currently do nothing...
341 void CAudioIO::prepare() {
342 if (__mIsInit == false) {
343 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
345 if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN) {
346 AUDIO_IO_LOGD("Prepare for Audio in");
347 } else if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_OUT) {
348 AUDIO_IO_LOGD("Prepare for Audio Out");
351 if (mIsInterrupted) {
352 AUDIO_IO_LOGE("This is preparing during interrupted!!!");
353 bool isSkip = mpAudioSessionHandler->isSkipSession();
354 if (__mForceIgnore == false && isSkip == false && mpAudioSessionHandler->getId() >= 0) {
355 AUDIO_IO_LOGE("Session updatePlaying!!!");
356 mpAudioSessionHandler->updatePlaying();
359 if (mpPulseAudioClient && mpPulseAudioClient->isCorked()) {
360 AUDIO_IO_LOGE("Uncork!");
361 mpPulseAudioClient->cork(false);
363 mIsInterrupted = false;
366 AUDIO_IO_LOGD("------> prepare done");
368 } catch (CAudioError& e) {
373 void CAudioIO::unprepare() {
374 if (__mIsInit == false) {
375 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
379 AUDIO_IO_LOGD("unprepare ----->");
381 } catch (CAudioError& e) {
386 void CAudioIO::pause() {
387 if (__mIsInit == false || IsReady() == false) {
388 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
393 AUDIO_IO_LOGD("pause");
394 mpPulseAudioClient->cork(true);
396 } catch (CAudioError& e) {
402 void CAudioIO::resume() {
403 if (__mIsInit == false || IsReady() == false) {
404 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
409 AUDIO_IO_LOGD("resume");
410 mpPulseAudioClient->cork(false);
412 } catch (CAudioError& e) {
418 void CAudioIO::drain() {
419 if (__mIsInit == false || IsReady() == false) {
420 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
424 if (mpPulseAudioClient->isInThread()) {
425 mpPulseAudioClient->drain();
428 mpPulseAudioClient->drain();
431 } catch (CAudioError& e) {
432 if (!mpPulseAudioClient->isInThread()) {
439 void CAudioIO::flush() {
440 if (__mIsInit == false || IsReady() == false) {
441 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
445 if (mpPulseAudioClient->isInThread()) {
446 mpPulseAudioClient->flush();
449 mpPulseAudioClient->flush();
452 } catch (CAudioError& e) {
453 if (!mpPulseAudioClient->isInThread()) {
460 CAudioInfo& CAudioIO::getAudioInfo() {
461 if (__mIsInit == false) {
462 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
468 void CAudioIO::setStreamCallback(SStreamCallback callback) {
469 if (__mIsInit == false) {
470 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
473 mStreamCallback = callback;
476 CAudioIO::SStreamCallback CAudioIO::getStreamCallback() {
477 if (__mIsInit == false) {
478 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
481 return mStreamCallback;
484 void CAudioIO::setStateChangedCallback(SStateChangedCallback callback) {
485 if (__mIsInit == false) {
486 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
489 mStateChangedCallback = callback;
492 CAudioIO::SStateChangedCallback CAudioIO::getStateChangedCallback() {
493 if (__mIsInit == false) {
494 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
497 return mStateChangedCallback;
500 void CAudioIO::setInterruptCallback(SInterruptCallback callback) {
501 if (__mIsInit == false) {
502 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
505 mInterruptCallback = callback;
508 CAudioIO::SInterruptCallback CAudioIO::getInterruptCallback() {
509 if (__mIsInit == false) {
510 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
513 return mInterruptCallback;
516 void CAudioIO::ignoreSession() {
517 if (__mIsInit == false)
518 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
523 if (mpPulseAudioClient != NULL && mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
524 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "An Operation is not permitted while started");
526 abandonInternalFocus();
529 } catch (CAudioError& e) {
535 void CAudioIO::setStreamInfo(sound_stream_info_h stream_info) {
536 if (stream_info == NULL)
537 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "stream_info is NULL");
539 if (__mIsInit == false)
540 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
543 if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
544 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
546 abandonInternalFocus();
548 int errorCode = SOUND_MANAGER_ERROR_NONE;
549 CAudioInfo::EAudioType audioType = CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA;
554 if ((errorCode = sound_manager_is_available_stream_information(stream_info, NATIVE_API_AUDIO_IO, &avail)) != SOUND_MANAGER_ERROR_NONE)
555 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info is invalid [ret:%d]", errorCode);
558 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED_TYPE, "Input stream is not supported");
560 if ((errorCode = sound_manager_get_type_from_stream_information(stream_info, &type)) != SOUND_MANAGER_ERROR_NONE)
561 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->stream_type is invalid [ret:%d]", errorCode);
563 if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN)
564 getAudioInfo().convertInputStreamType2AudioType(type, &audioType);
566 getAudioInfo().convertOutputStreamType2AudioType(type, &audioType);
567 getAudioInfo().setAudioType(audioType);
569 if ((errorCode = sound_manager_get_index_from_stream_information(stream_info, &index)) != SOUND_MANAGER_ERROR_NONE)
570 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->index is invalid [ret:%d]", errorCode);
572 getAudioInfo().setAudioIndex(index);
574 AUDIO_IO_LOGD("stream info(%p) is set", stream_info);
575 } catch (CAudioError& e) {
580 void CAudioIO::setInternalStreamInfo() {
581 if (__mIsInit == false)
582 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
585 if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
586 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
588 if (mpAudioSessionHandler &&
589 mpAudioSessionHandler->getMultimediaSession() == MM_SESSION_TYPE_VOIP) {
590 sound_stream_info_h stream_info = NULL;
591 mpAudioSessionHandler->getInternalVoipStreamInfo(&stream_info);
592 AUDIO_IO_LOGD("get internal VOIP stream info(%p)", stream_info);
593 setStreamInfo(stream_info);
595 } catch (CAudioError& e) {
600 void CAudioIO::abandonInternalFocus() {
601 bool isSkip = mpAudioSessionHandler->isSkipSession();
602 int id = mpAudioSessionHandler->getId();
605 if (isSkip == false && id >= 0)
606 mpAudioSessionHandler->unregisterSound();
608 mpAudioSessionHandler->finalize();
609 __mForceIgnore = true;
610 } catch (CAudioError& e) {