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 mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
38 mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
42 CAudioIO::CAudioIO(CAudioInfo& audioInfo) :
43 mpAudioSessionHandler(NULL),
44 mpPulseAudioClient(NULL),
45 __mMutex(PTHREAD_MUTEX_INITIALIZER),
46 __mCond(PTHREAD_COND_INITIALIZER),
48 __mForceIgnore(false) {
49 mAudioInfo = audioInfo;
50 mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
51 mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
55 CAudioIO::~CAudioIO() {
58 void CAudioIO::setInit(bool flag) {
62 bool CAudioIO::isInit() {
66 bool CAudioIO::IsReady() {
67 return ((mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING || mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)? true : false);
70 void CAudioIO::internalLock() throw(CAudioError) {
71 if (__mIsInit == false) {
72 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
75 if (pthread_mutex_lock(&__mMutex) != 0) {
76 THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
78 #ifdef _AUDIO_IO_DEBUG_TIMING_
79 AUDIO_IO_LOGD(COLOR_RED "LOCK" COLOR_END);
83 void CAudioIO::internalUnlock() throw(CAudioError) {
84 if (__mIsInit == false) {
85 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
88 if (pthread_mutex_unlock(&__mMutex) != 0) {
89 THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
91 #ifdef _AUDIO_IO_DEBUG_TIMING_
92 AUDIO_IO_LOGD(COLOR_GREEN "UNLOCK" COLOR_END);
96 void CAudioIO::internalWait() throw(CAudioError) {
97 if (__mIsInit == false) {
98 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
101 #ifdef _AUDIO_IO_DEBUG_TIMING_
102 AUDIO_IO_LOGD(COLOR_RED "WAIT" COLOR_END);
105 pthread_cond_wait(&__mCond, &__mMutex);
108 void CAudioIO::internalSignal() throw(CAudioError) {
109 if (__mIsInit == false) {
110 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
113 #ifdef _AUDIO_IO_DEBUG_TIMING_
114 AUDIO_IO_LOGD(COLOR_GREEN "SIGNAL" COLOR_END);
117 pthread_cond_signal(&__mCond);
120 bool CAudioIO::isForceIgnore() {
121 return __mForceIgnore;
124 void CAudioIO::initialize() throw(CAudioError) {
125 if (__mIsInit == true) {
129 AUDIO_IO_LOGD("initialize");
131 int ret = pthread_mutex_init(&__mMutex, NULL);
133 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_init()");
136 ret = pthread_cond_init(&__mCond, NULL);
138 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_init()");
144 void CAudioIO::finalize() {
145 if (__mIsInit == false) {
149 AUDIO_IO_LOGD("finalize");
151 int ret = pthread_mutex_destroy(&__mMutex);
153 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_destroy() ret:%d", ret);
156 ret = pthread_cond_destroy(&__mCond);
158 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_destroy() ret:%d", ret);
164 void CAudioIO::onStream(CPulseAudioClient* pClient, size_t length) {
165 assert(__mIsInit == true);
166 assert(pClient != NULL);
169 #ifdef _AUDIO_IO_DEBUG_TIMING_
170 AUDIO_IO_LOGD("mStreamCallback.onStream(%p), pClient(%p), length(%zu)", mStreamCallback.onStream, pClient, length);
173 if (mStreamCallback.onStream != NULL) {
174 mStreamCallback.onStream(length, mStreamCallback.mUserData);
178 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) {
179 assert(__mIsInit == true);
180 assert(state >= CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE && state < CAudioInfo::EAudioIOState::AUDIO_IO_STATE_MAX);
184 mByPolicy = byPolicy;
186 if (mState == mStatePrev)
189 const char* state_string[] = { "NONE", "IDLE", "RUNNING", "PAUSED" };
191 AUDIO_IO_LOGD("previous(%s,%d) ===> current(%s,%d), by_policy(%d)",
192 state_string[(int)mStatePrev], mStatePrev, state_string[(int)mState], mState, mByPolicy);
194 if (mStateChangedCallback.onStateChanged != NULL) {
195 mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData);
199 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state) {
200 onStateChanged(state, false);
203 CAudioInfo::EAudioIOState CAudioIO::getState() {
207 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) {
210 int session_option = pHandler->getOptions();
213 ///////////////////////////////////////
214 // Triggered by 'focus watch callback'
215 ///////////////////////////////////////
217 if (session_option & (MM_SESSION_OPTION_PAUSE_OTHERS | MM_SESSION_OPTION_UNINTERRUPTIBLE)) {
218 AUDIO_IO_LOGD("Session option is pausing others or uninterruptible, skip...");
222 if (state == FOCUS_IS_RELEASED) {
223 // Focus handle(id) of the other application was released, do resume if possible
225 if (mpPulseAudioClient) {
226 mpPulseAudioClient->cork(false);
227 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
231 // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
232 state = FOCUS_IS_ACQUIRED;
233 } else if (state == FOCUS_IS_ACQUIRED) {
234 // Focus handle(id) of the other application was acquired, do pause if possible
236 if (mpPulseAudioClient) {
237 if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
238 if (mpPulseAudioClient->drain() == false) {
239 AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
242 mpPulseAudioClient->cork(true);
243 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
247 // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
248 state = FOCUS_IS_RELEASED;
251 ///////////////////////////////////////
252 // Triggered by 'focus callback'
253 ///////////////////////////////////////
255 if (pHandler->getId() != id) {
256 AUDIO_IO_LOGW("Id is different, why? [mId : %d]", pHandler->getId());
259 if (session_option & MM_SESSION_OPTION_UNINTERRUPTIBLE) {
260 AUDIO_IO_LOGD("Session option is uninterruptible, skip...");
264 if (state == FOCUS_IS_RELEASED) {
265 // Focus handle(id) was released, do pause here
267 if (mpPulseAudioClient) {
268 if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
269 if (mpPulseAudioClient->drain() == false) {
270 AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
273 mpPulseAudioClient->cork(true);
274 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
277 } else if (state == FOCUS_IS_ACQUIRED) {
278 // Focus handle(id) was acquired again,
279 // check reason_for_change ("call-voice","call-video","voip","alarm","notification", ...)
280 // do resume here and call interrupt completed callback to application.
282 if (mpPulseAudioClient) {
283 mpPulseAudioClient->cork(false);
284 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
290 if (mInterruptCallback.onInterrupt != NULL) {
291 IAudioSessionEventListener::EInterruptCode e = IAudioSessionEventListener::EInterruptCode::INTERRUPT_COMPLETED;
292 e = IAudioSessionEventListener::convertInterruptedCode(state, reason_for_change);
293 mInterruptCallback.onInterrupt(e, mInterruptCallback.mUserData);
297 void CAudioIO::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
300 if (signal == MM_SOUND_SIGNAL_RELEASE_INTERNAL_FOCUS) {
301 if (value == 1 && pHandler->getSubscribeId() > 0) {
302 // Unregister focus watch callback & disable session handler
303 pHandler->disableSessionHandler();
304 AUDIO_IO_LOGD("Session handler disabled by signal");
305 } else if (value == 0) {
306 // Currently do nothing...
311 void CAudioIO::prepare() throw(CAudioError) {
312 if (__mIsInit == false) {
313 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
317 AUDIO_IO_LOGD("------> prepare done");
319 } catch (CAudioError e) {
324 void CAudioIO::unprepare() throw(CAudioError) {
325 if (__mIsInit == false) {
326 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
330 AUDIO_IO_LOGD("unprepare ----->");
332 } catch (CAudioError e) {
337 void CAudioIO::pause() throw(CAudioError) {
338 if (__mIsInit == false || IsReady() == false) {
339 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
344 AUDIO_IO_LOGD("pause");
345 mpPulseAudioClient->cork(true);
347 } catch (CAudioError e) {
353 void CAudioIO::resume() throw(CAudioError) {
354 if (__mIsInit == false || IsReady() == false) {
355 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
360 AUDIO_IO_LOGD("resume");
361 mpPulseAudioClient->cork(false);
363 } catch (CAudioError e) {
369 void CAudioIO::drain() throw(CAudioError) {
370 if (__mIsInit == false || IsReady() == false) {
371 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
375 if (mpPulseAudioClient->isInThread()) {
376 mpPulseAudioClient->drain();
379 mpPulseAudioClient->drain();
382 } catch (CAudioError e) {
383 if (!mpPulseAudioClient->isInThread()) {
390 void CAudioIO::flush() throw(CAudioError) {
391 if (__mIsInit == false || IsReady() == false) {
392 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
396 if (mpPulseAudioClient->isInThread()) {
397 mpPulseAudioClient->flush();
400 mpPulseAudioClient->flush();
403 } catch (CAudioError e) {
404 if (!mpPulseAudioClient->isInThread()) {
411 CAudioInfo& CAudioIO::getAudioInfo() throw(CAudioError) {
412 if (__mIsInit == false) {
413 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
419 void CAudioIO::setStreamCallback(SStreamCallback callback) throw(CAudioError) {
420 if (__mIsInit == false) {
421 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
424 mStreamCallback = callback;
427 CAudioIO::SStreamCallback CAudioIO::getStreamCallback() throw(CAudioError) {
428 if (__mIsInit == false) {
429 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
432 return mStreamCallback;
435 void CAudioIO::setStateChangedCallback(SStateChangedCallback callback) throw(CAudioError) {
436 if (__mIsInit == false) {
437 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
440 mStateChangedCallback = callback;
443 CAudioIO::SStateChangedCallback CAudioIO::getStateChangedCallback() throw(CAudioError) {
444 if (__mIsInit == false) {
445 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
448 return mStateChangedCallback;
451 void CAudioIO::setInterruptCallback(SInterruptCallback callback) throw(CAudioError) {
452 if (__mIsInit == false) {
453 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
456 mInterruptCallback = callback;
459 CAudioIO::SInterruptCallback CAudioIO::getInterruptCallback() throw(CAudioError) {
460 if (__mIsInit == false) {
461 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
464 return mInterruptCallback;
467 void CAudioIO::ignoreSession() throw(CAudioError) {
468 if (__mIsInit == false)
469 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
474 if (mpPulseAudioClient != NULL && mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
475 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "An Operation is not permitted while started");
477 bool isSkip = mpAudioSessionHandler->isSkipSession();
478 if (isSkip == false && mpAudioSessionHandler->getId() >= 0)
479 mpAudioSessionHandler->unregisterSound();
481 mpAudioSessionHandler->finalize();
482 __mForceIgnore = true;
485 } catch (CAudioError e) {
491 void CAudioIO::setStreamInfo(sound_stream_info_h stream_info) throw(CAudioError) {
492 if (stream_info == NULL)
493 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "stream_info is NULL");
495 if (__mIsInit == false)
496 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
499 if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
500 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
502 int errorCode = SOUND_MANAGER_ERROR_NONE;
503 CAudioInfo::EAudioType audioType = CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA;
508 if ((errorCode = sound_manager_is_available_stream_information(stream_info, NATIVE_API_AUDIO_IO, &avail)) != SOUND_MANAGER_ERROR_NONE)
509 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info is invalid [ret:%d]", errorCode);
512 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED_TYPE, "Input stream is not supported");
514 if ((errorCode = sound_manager_get_type_from_stream_information(stream_info, &type)) != SOUND_MANAGER_ERROR_NONE)
515 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->stream_type is invalid [ret:%d]", errorCode);
517 if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN)
518 getAudioInfo().convertInputStreamType2AudioType(type, &audioType);
520 getAudioInfo().convertOutputStreamType2AudioType(type, &audioType);
521 getAudioInfo().setAudioType(audioType);
523 if ((errorCode = sound_manager_get_index_from_stream_information(stream_info, &index)) != SOUND_MANAGER_ERROR_NONE)
524 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->index is invalid [ret:%d]", errorCode);
526 getAudioInfo().setAudioIndex(index);
528 AUDIO_IO_LOGD("stream info(%p) is set", stream_info);
529 } catch (CAudioError e) {
534 void CAudioIO::setInternalStreamInfo() throw(CAudioError) {
535 if (__mIsInit == false)
536 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
539 if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
540 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
542 if (mpAudioSessionHandler &&
543 mpAudioSessionHandler->getMultimediaSession() == MM_SESSION_TYPE_VOIP) {
544 sound_stream_info_h stream_info = NULL;
545 mpAudioSessionHandler->getInternalVoipStreamInfo(&stream_info);
546 AUDIO_IO_LOGD("get internal VOIP stream info(%p)", stream_info);
547 setStreamInfo(stream_info);
549 } catch (CAudioError e) {