Fix build error due to -Wformat
[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 <mm.h>
19 #include <pthread.h>
20 #include <assert.h>
21 #include "CAudioIODef.h"
22
23 using namespace std;
24 using namespace tizen_media_audio;
25
26
27 /**
28  * class CAudioIO
29  */
30 CAudioIO::CAudioIO() :
31     mpAudioSessionHandler(NULL),
32     mpPulseAudioClient(NULL),
33     __mMutex(PTHREAD_MUTEX_INITIALIZER),
34     __mCond(PTHREAD_COND_INITIALIZER),
35     __mIsInit(false),
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;
40     mByPolicy = false;
41 }
42
43 CAudioIO::CAudioIO(CAudioInfo& audioInfo) :
44     mpAudioSessionHandler(NULL),
45     mpPulseAudioClient(NULL),
46     __mMutex(PTHREAD_MUTEX_INITIALIZER),
47     __mCond(PTHREAD_COND_INITIALIZER),
48     __mIsInit(false),
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;
54     mByPolicy = false;
55 }
56
57 CAudioIO::~CAudioIO() {
58 }
59
60 void CAudioIO::setInit(bool flag) {
61     __mIsInit = flag;
62 }
63
64 bool CAudioIO::isInit() {
65     return __mIsInit;
66 }
67
68 bool CAudioIO::IsReady() {
69     return ((mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING || mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)? true : false);
70 }
71
72 void CAudioIO::internalLock() throw(CAudioError) {
73     if (__mIsInit == false) {
74         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
75     }
76
77     if (pthread_mutex_lock(&__mMutex) != 0) {
78         THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
79     }
80 #ifdef _AUDIO_IO_DEBUG_TIMING_
81     AUDIO_IO_LOGD(COLOR_RED "LOCK" COLOR_END);
82 #endif
83 }
84
85 void CAudioIO::internalUnlock() throw(CAudioError) {
86     if (__mIsInit == false) {
87         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
88     }
89
90     if (pthread_mutex_unlock(&__mMutex) != 0) {
91         THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
92     }
93 #ifdef _AUDIO_IO_DEBUG_TIMING_
94     AUDIO_IO_LOGD(COLOR_GREEN "UNLOCK" COLOR_END);
95 #endif
96 }
97
98 void CAudioIO::internalWait() throw(CAudioError) {
99     if (__mIsInit == false) {
100         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
101     }
102
103 #ifdef _AUDIO_IO_DEBUG_TIMING_
104     AUDIO_IO_LOGD(COLOR_RED "WAIT" COLOR_END);
105 #endif
106
107     pthread_cond_wait(&__mCond, &__mMutex);
108 }
109
110 void CAudioIO::internalSignal() throw(CAudioError) {
111     if (__mIsInit == false) {
112         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
113     }
114
115 #ifdef _AUDIO_IO_DEBUG_TIMING_
116     AUDIO_IO_LOGD(COLOR_GREEN "SIGNAL" COLOR_END);
117 #endif
118
119     pthread_cond_signal(&__mCond);
120 }
121
122 bool CAudioIO::isForceIgnore() {
123     return __mForceIgnore;
124 }
125
126 void CAudioIO::initialize() throw(CAudioError) {
127     if (__mIsInit == true) {
128         return;
129     }
130
131     AUDIO_IO_LOGD("initialize");
132
133     int ret = pthread_mutex_init(&__mMutex, NULL);
134     if (ret != 0) {
135         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_init()");
136     }
137
138     ret = pthread_cond_init(&__mCond, NULL);
139     if (ret != 0) {
140         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_init()");
141     }
142
143     __mIsInit = true;
144 }
145
146 void CAudioIO::finalize() {
147     if (__mIsInit == false) {
148         return;
149     }
150
151     AUDIO_IO_LOGD("finalize");
152
153     int ret = pthread_mutex_destroy(&__mMutex);
154     if (ret != 0) {
155         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_destroy() ret:%d", ret);
156     }
157
158     ret = pthread_cond_destroy(&__mCond);
159     if (ret != 0) {
160         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_destroy() ret:%d", ret);
161     }
162
163     __mIsInit = false;
164 }
165
166 void CAudioIO::onStream(CPulseAudioClient* pClient, size_t length) {
167     assert(__mIsInit == true);
168     assert(pClient != NULL);
169     assert(length > 0);
170
171 #ifdef _AUDIO_IO_DEBUG_TIMING_
172     AUDIO_IO_LOGD("mStreamCallback.onStream(%p), pClient(%p), length(%zu)", mStreamCallback.onStream, pClient, length);
173 #endif
174
175     if (mStreamCallback.onStream != NULL) {
176         mStreamCallback.onStream(length, mStreamCallback.mUserData);
177     }
178 }
179
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);
183
184     mStatePrev = mState;
185     mState     = state;
186     mByPolicy  = byPolicy;
187
188     if (mState == mStatePrev)
189         return;
190
191     const char* state_string[] = { "NONE", "IDLE", "RUNNING", "PAUSED" };
192
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),
198                   mByPolicy);
199
200     if (mStateChangedCallback.onStateChanged != NULL) {
201         mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData);
202     }
203 }
204
205 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state) {
206     onStateChanged(state, false);
207 }
208
209 CAudioInfo::EAudioIOState CAudioIO::getState() {
210     return mState;
211 }
212
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) {
214     assert(pHandler);
215
216     int session_option = pHandler->getOptions();
217
218     if (id == -1) {
219         ///////////////////////////////////////
220         // Triggered by 'focus watch callback'
221         ///////////////////////////////////////
222
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...");
225             return;
226         }
227
228         if (state == FOCUS_IS_RELEASED) {
229             // Focus handle(id) of the other application was released, do resume if possible
230             internalLock();
231             if (mpPulseAudioClient) {
232                 mpPulseAudioClient->cork(false);
233                 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
234             }
235             internalUnlock();
236
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
241             internalLock();
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()");
247                     }
248                 }
249                 */
250                 mpPulseAudioClient->cork(true);
251                 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
252             }
253             internalUnlock();
254
255             // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
256             state = FOCUS_IS_RELEASED;
257         }
258     } else {
259         ///////////////////////////////////////
260         // Triggered by 'focus callback'
261         ///////////////////////////////////////
262
263         if (pHandler->getId() != id) {
264             AUDIO_IO_LOGW("Id is different, why? [mId : %d]", pHandler->getId());
265         }
266
267         if (session_option & MM_SESSION_OPTION_UNINTERRUPTIBLE) {
268             AUDIO_IO_LOGD("Session option is uninterruptible, skip...");
269             return;
270         }
271
272         if (state == FOCUS_IS_RELEASED) {
273             // Focus handle(id) was released, do pause here
274             internalLock();
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()");
280                     }
281                 }
282                 */
283                 mpPulseAudioClient->cork(true);
284                 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
285             }
286             internalUnlock();
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.
291             internalLock();
292             if (mpPulseAudioClient) {
293                 mpPulseAudioClient->cork(false);
294                 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
295             }
296             internalUnlock();
297         }
298     }
299
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);
304     }
305 }
306
307 void CAudioIO::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
308     assert(pHandler);
309
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...
317         }
318     }
319 }
320
321 void CAudioIO::prepare() throw(CAudioError) {
322     if (__mIsInit == false) {
323         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
324     }
325
326     try {
327         AUDIO_IO_LOGD("------> prepare done");
328         /* Do nothing */
329     } catch (CAudioError e) {
330         throw e;
331     }
332 }
333
334 void CAudioIO::unprepare() throw(CAudioError) {
335     if (__mIsInit == false) {
336         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
337     }
338
339     try {
340         AUDIO_IO_LOGD("unprepare ----->");
341         /* Do nothing */
342     } catch (CAudioError e) {
343         throw e;
344     }
345 }
346
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");
350     }
351
352     try {
353         internalLock();
354         AUDIO_IO_LOGD("pause");
355         mpPulseAudioClient->cork(true);
356         internalUnlock();
357     } catch (CAudioError e) {
358         internalUnlock();
359         throw e;
360     }
361 }
362
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");
366     }
367
368     try {
369         internalLock();
370         AUDIO_IO_LOGD("resume");
371         mpPulseAudioClient->cork(false);
372         internalUnlock();
373     } catch (CAudioError e) {
374         internalUnlock();
375         throw e;
376     }
377 }
378
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");
382     }
383
384     try {
385         if (mpPulseAudioClient->isInThread()) {
386             mpPulseAudioClient->drain();
387         } else {
388             internalLock();
389             mpPulseAudioClient->drain();
390             internalUnlock();
391         }
392     } catch (CAudioError e) {
393         if (!mpPulseAudioClient->isInThread()) {
394             internalUnlock();
395         }
396         throw e;
397     }
398 }
399
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");
403     }
404
405     try {
406         if (mpPulseAudioClient->isInThread()) {
407             mpPulseAudioClient->flush();
408         } else {
409             internalLock();
410             mpPulseAudioClient->flush();
411             internalUnlock();
412         }
413     } catch (CAudioError e) {
414         if (!mpPulseAudioClient->isInThread()) {
415             internalUnlock();
416         }
417         throw e;
418     }
419 }
420
421 CAudioInfo& CAudioIO::getAudioInfo() throw(CAudioError) {
422     if (__mIsInit == false) {
423         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
424     }
425
426     return mAudioInfo;
427 }
428
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");
432     }
433
434     mStreamCallback = callback;
435 }
436
437 CAudioIO::SStreamCallback CAudioIO::getStreamCallback() throw(CAudioError) {
438     if (__mIsInit == false) {
439         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
440     }
441
442     return mStreamCallback;
443 }
444
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");
448     }
449
450     mStateChangedCallback = callback;
451 }
452
453 CAudioIO::SStateChangedCallback CAudioIO::getStateChangedCallback() throw(CAudioError) {
454     if (__mIsInit == false) {
455         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
456     }
457
458     return mStateChangedCallback;
459 }
460
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");
464     }
465
466     mInterruptCallback = callback;
467 }
468
469 CAudioIO::SInterruptCallback CAudioIO::getInterruptCallback() throw(CAudioError) {
470     if (__mIsInit == false) {
471         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
472     }
473
474     return mInterruptCallback;
475 }
476
477 void CAudioIO::ignoreSession() throw(CAudioError) {
478     if (__mIsInit == false)
479         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
480
481     try {
482         internalLock();
483
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");
486
487         abandonInternalFocus();
488
489         internalUnlock();
490     } catch (CAudioError e) {
491         internalUnlock();
492         throw e;
493     }
494 }
495
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");
499
500     if (__mIsInit == false)
501         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
502
503     try {
504         if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
505             THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
506
507         abandonInternalFocus();
508
509         int errorCode = SOUND_MANAGER_ERROR_NONE;
510         CAudioInfo::EAudioType audioType = CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA;
511         char *type = NULL;
512         int index = -1;
513         bool avail = false;
514
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);
517
518         if (!avail)
519             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED_TYPE, "Input stream is not supported");
520
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);
523
524         if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN)
525             getAudioInfo().convertInputStreamType2AudioType(type, &audioType);
526         else
527             getAudioInfo().convertOutputStreamType2AudioType(type, &audioType);
528         getAudioInfo().setAudioType(audioType);
529
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);
532
533         getAudioInfo().setAudioIndex(index);
534
535         AUDIO_IO_LOGD("stream info(%p) is set", stream_info);
536     } catch (CAudioError e) {
537         throw e;
538     }
539 }
540
541 void CAudioIO::setInternalStreamInfo() throw(CAudioError) {
542     if (__mIsInit == false)
543         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
544
545     try {
546         if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
547             THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
548
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);
555         }
556     } catch (CAudioError e) {
557         throw e;
558     }
559 }
560
561 void CAudioIO::abandonInternalFocus() throw(CAudioError) {
562     bool isSkip = mpAudioSessionHandler->isSkipSession();
563     int id = mpAudioSessionHandler->getId();
564
565     try {
566         if (isSkip == false && id >= 0)
567             mpAudioSessionHandler->unregisterSound();
568
569         mpAudioSessionHandler->finalize();
570         __mForceIgnore = true;
571     } catch (CAudioError e) {
572         throw e;
573     }
574 }