fc3792f5ef0852f6748fdb2f7f625e57227fbb2c
[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 <audio-session-manager.h>
22 #include "CAudioIODef.h"
23
24 #define AUDIO_IO_DEBUG
25
26 using namespace std;
27 using namespace tizen_media_audio;
28
29
30 /**
31  * class CAudioIO
32  */
33 CAudioIO::CAudioIO() :
34     mpAudioSessionHandler(NULL),
35     mpPulseAudioClient(NULL),
36     __mIsInit(false),
37     __mForceIgnore(false) {
38     mState = CAudioInfo::AUDIO_IO_STATE_NONE;
39     mStatePrev = CAudioInfo::AUDIO_IO_STATE_NONE;
40     mByPolicy = false;
41 }
42
43 CAudioIO::CAudioIO(CAudioInfo& audioInfo) : mpAudioSessionHandler(NULL), mpPulseAudioClient(NULL), __mIsInit(false), __mForceIgnore(false) {
44     mAudioInfo = audioInfo;
45     mState = CAudioInfo::AUDIO_IO_STATE_NONE;
46     mStatePrev = CAudioInfo::AUDIO_IO_STATE_NONE;
47     mByPolicy = false;
48 }
49
50 CAudioIO::~CAudioIO() {
51 }
52
53 void CAudioIO::setInit(bool flag) {
54     __mIsInit = flag;
55 }
56
57 bool CAudioIO::isInit() {
58     return __mIsInit;
59 }
60
61 bool CAudioIO::IsReady() {
62     return ((mState == CAudioInfo::AUDIO_IO_STATE_RUNNING || mState == CAudioInfo::AUDIO_IO_STATE_PAUSED)? true : false);
63 }
64
65 void CAudioIO::internalLock() throw (CAudioError) {
66     if (__mIsInit == false) {
67         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
68     }
69
70     if (pthread_mutex_lock(&__mMutex) != 0) {
71         THROW_ERROR_MSG(CAudioError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
72     }
73 #ifdef _AUDIO_IO_DEBUG_TIMING_
74     AUDIO_IO_LOGD(COLOR_RED "LOCK" COLOR_END);
75 #endif
76 }
77
78 void CAudioIO::internalUnlock() throw (CAudioError) {
79     if (__mIsInit == false) {
80         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
81     }
82
83     if (pthread_mutex_unlock(&__mMutex) != 0) {
84         THROW_ERROR_MSG(CAudioError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
85     }
86 #ifdef _AUDIO_IO_DEBUG_TIMING_
87     AUDIO_IO_LOGD(COLOR_GREEN "UNLOCK" COLOR_END);
88 #endif
89 }
90
91 void CAudioIO::internalWait() throw (CAudioError) {
92     if (__mIsInit == false) {
93         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
94     }
95
96 #ifdef _AUDIO_IO_DEBUG_TIMING_
97     AUDIO_IO_LOGD(COLOR_RED "WAIT" COLOR_END);
98 #endif
99
100     pthread_cond_wait(&__mCond, &__mMutex);
101 }
102
103 void CAudioIO::internalSignal() throw (CAudioError) {
104     if (__mIsInit == false) {
105         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
106     }
107
108 #ifdef _AUDIO_IO_DEBUG_TIMING_
109     AUDIO_IO_LOGD(COLOR_GREEN "SIGNAL" COLOR_END);
110 #endif
111
112     pthread_cond_signal(&__mCond);
113 }
114
115 bool CAudioIO::isForceIgnore() {
116     return __mForceIgnore;
117 }
118
119 void CAudioIO::initialize() throw (CAudioError) {
120     if (__mIsInit == true) {
121         return;
122     }
123
124     AUDIO_IO_LOGD("initialize");
125
126     int ret = pthread_mutex_init(&__mMutex, NULL);
127     if (ret != 0) {
128         THROW_ERROR_MSG(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_init()");
129     }
130
131     ret = pthread_cond_init(&__mCond, NULL);
132     if (ret != 0) {
133         THROW_ERROR_MSG(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_init()");
134     }
135
136     __mIsInit = true;
137 }
138
139 void CAudioIO::finalize() {
140     if (__mIsInit == false) {
141         return;
142     }
143
144     AUDIO_IO_LOGD("finalize");
145
146     int ret = pthread_mutex_destroy(&__mMutex);
147     if (ret != 0) {
148         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_destroy() ret:%d", ret);
149     }
150
151     ret = pthread_cond_destroy(&__mCond);
152     if (ret != 0) {
153         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_destroy() ret:%d", ret);
154     }
155
156     __mIsInit = false;
157 }
158
159 void CAudioIO::onStream(CPulseAudioClient* pClient, size_t length) {
160     assert(__mIsInit == true);
161     assert(pClient != NULL);
162     assert(length > 0);
163
164 #ifdef _AUDIO_IO_DEBUG_TIMING_
165     AUDIO_IO_LOGD("mStreamCallback.onStream(%p), pClient(%p), length(%zu)", mStreamCallback.onStream, pClient, length);
166 #endif
167
168     if (mStreamCallback.onStream != NULL) {
169         mStreamCallback.onStream(length, mStreamCallback.mUserData);
170     }
171 }
172
173 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) {
174     assert(__mIsInit == true);
175     assert(state > 0);
176
177     mStatePrev = mState;
178     mState     = state;
179     mByPolicy  = byPolicy;
180
181     AUDIO_IO_LOGD("current(%d), previous(%d), by_policy(%d)", mState, mStatePrev, mByPolicy);
182
183     if (mStateChangedCallback.onStateChanged != NULL) {
184         mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData);
185     }
186 }
187
188 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state) {
189     onStateChanged(state, false);
190 }
191
192 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) {
193     assert(pHandler);
194
195     int session_option = pHandler->getOptions();
196
197     if (id == -1) {
198         ///////////////////////////////////////
199         // Triggered by 'focus watch callback'
200         ///////////////////////////////////////
201
202         if (session_option & (ASM_SESSION_OPTION_PAUSE_OTHERS | ASM_SESSION_OPTION_UNINTERRUPTIBLE)) {
203             AUDIO_IO_LOGD("Session option is pausing others or uninterruptible, skip...");
204             return;
205         }
206
207         if (state == FOCUS_IS_RELEASED) {
208             // Focus handle(id) of the other application was released, do resume if possible
209             internalLock();
210
211             mpPulseAudioClient->cork(false);
212             onStateChanged(CAudioInfo::AUDIO_IO_STATE_RUNNING);
213
214             internalUnlock();
215
216             // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
217             state = FOCUS_IS_ACQUIRED;
218         } else if (state == FOCUS_IS_ACQUIRED) {
219             // Focus handle(id) of the other application was acquired, do pause if possible
220             internalLock();
221
222             if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::STREAM_DIRECTION_PLAYBACK) {
223                 if (mpPulseAudioClient->drain() == false) {
224                     AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
225                 }
226             }
227
228             mpPulseAudioClient->cork(true);
229             onStateChanged(CAudioInfo::AUDIO_IO_STATE_PAUSED);
230
231             internalUnlock();
232
233             // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
234             state = FOCUS_IS_RELEASED;
235         }
236     } else {
237         ///////////////////////////////////////
238         // Triggered by 'focus callback'
239         ///////////////////////////////////////
240
241         if (pHandler->getId() != id) {
242             AUDIO_IO_LOGW("Id is different, why? [mId : %d]", pHandler->getId());
243         }
244
245         if (session_option & ASM_SESSION_OPTION_UNINTERRUPTIBLE) {
246             AUDIO_IO_LOGD("Session option is uninterruptible, skip...");
247             return;
248         }
249
250         if (state == FOCUS_IS_RELEASED) {
251             // Focus handle(id) was released, do pause here
252             internalLock();
253
254             if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::STREAM_DIRECTION_PLAYBACK) {
255                 if (mpPulseAudioClient->drain() == false) {
256                     AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
257                 }
258             }
259
260             mpPulseAudioClient->cork(true);
261             onStateChanged(CAudioInfo::AUDIO_IO_STATE_PAUSED);
262
263             internalUnlock();
264         } else if (state == FOCUS_IS_ACQUIRED) {
265             // Focus handle(id) was acquired again,
266             // check reason_for_change ("call-voice","call-video","voip","alarm","notification", ...)
267             // do resume here and call interrupt completed callback to application.
268             internalLock();
269
270             mpPulseAudioClient->cork(false);
271             onStateChanged(CAudioInfo::AUDIO_IO_STATE_RUNNING);
272
273             internalUnlock();
274         }
275     }
276
277     if (mInterruptCallback.onInterrupt != NULL) {
278         IAudioSessionEventListener::EInterruptCode e = IAudioSessionEventListener::INTERRUPT_COMPLETED;
279         e = IAudioSessionEventListener::convertInterruptedCode(state, reason_for_change);
280         mInterruptCallback.onInterrupt(e, mInterruptCallback.mUserData);
281     }
282 }
283
284 void CAudioIO::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
285     assert(pHandler);
286
287     if (signal == MM_SOUND_SIGNAL_RELEASE_INTERNAL_FOCUS) {
288         if (value == 1 && pHandler->getSubscribeId() >= 0) {
289             // Unregister focus watch callback & disable session handler
290             pHandler->disableSessionHandler();
291             AUDIO_IO_LOGD("Session handler disabled by signal");
292         } else if (value == 0) {
293             // Currently do nothing...
294         }
295     }
296 }
297
298 void CAudioIO::prepare() throw (CAudioError) {
299     if (__mIsInit == false) {
300         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
301     }
302
303     try {
304         AUDIO_IO_LOGD("prepare");
305         /* Do nothing */
306     } catch (CAudioError e) {
307         throw e;
308     }
309 }
310
311 void CAudioIO::unprepare() throw (CAudioError) {
312     if (__mIsInit == false) {
313         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
314     }
315
316     try {
317         AUDIO_IO_LOGD("unprepare");
318         /* Do nothing */
319     } catch (CAudioError e) {
320         throw e;
321     }
322 }
323
324 void CAudioIO::pause() throw (CAudioError) {
325     if (__mIsInit == false || IsReady() == false) {
326         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
327     }
328
329     try {
330         internalLock();
331         AUDIO_IO_LOGD("pause");
332         mpPulseAudioClient->cork(true);
333         internalUnlock();
334     } catch (CAudioError e) {
335         internalUnlock();
336         throw e;
337     }
338 }
339
340 void CAudioIO::resume() throw (CAudioError) {
341     if (__mIsInit == false || IsReady() == false) {
342         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
343     }
344
345     try {
346         internalLock();
347         AUDIO_IO_LOGD("resume");
348         mpPulseAudioClient->cork(false);
349         internalUnlock();
350     } catch (CAudioError e) {
351         internalUnlock();
352         throw e;
353     }
354 }
355
356 void CAudioIO::drain() throw (CAudioError) {
357     if (__mIsInit == false || IsReady() == false) {
358         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
359     }
360
361     try {
362         internalLock();
363         AUDIO_IO_LOGD("drain");
364         mpPulseAudioClient->drain();
365         internalUnlock();
366     } catch (CAudioError e) {
367         internalUnlock();
368         throw e;
369     }
370 }
371
372 void CAudioIO::flush() throw (CAudioError) {
373     if (__mIsInit == false || IsReady() == false) {
374         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
375     }
376
377     try {
378         internalLock();
379         AUDIO_IO_LOGD("flush");
380         mpPulseAudioClient->flush();
381         internalUnlock();
382     } catch (CAudioError e) {
383         internalUnlock();
384         throw e;
385     }
386 }
387
388 CAudioInfo CAudioIO::getAudioInfo() throw (CAudioError) {
389     if (__mIsInit == false) {
390         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
391     }
392
393     return mAudioInfo;
394 }
395
396 void CAudioIO::setStreamCallback(SStreamCallback callback) throw (CAudioError) {
397     if (__mIsInit == false) {
398         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
399     }
400
401     mStreamCallback = callback;
402 }
403
404 CAudioIO::SStreamCallback CAudioIO::getStreamCallback() throw (CAudioError) {
405     if (__mIsInit == false) {
406         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
407     }
408
409     return mStreamCallback;
410 }
411
412 void CAudioIO::setStateChangedCallback(SStateChangedCallback callback) throw (CAudioError) {
413     if (__mIsInit == false) {
414         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
415     }
416
417     mStateChangedCallback = callback;
418 }
419
420 CAudioIO::SStateChangedCallback CAudioIO::getStateChangedCallback() throw (CAudioError) {
421     if (__mIsInit == false) {
422         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
423     }
424
425     return mStateChangedCallback;
426 }
427
428 void CAudioIO::setInterruptCallback(SInterruptCallback callback) throw (CAudioError) {
429     if (__mIsInit == false) {
430         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
431     }
432
433     mInterruptCallback = callback;
434 }
435
436 CAudioIO::SInterruptCallback CAudioIO::getInterruptCallback() throw (CAudioError) {
437     if (__mIsInit == false) {
438         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
439     }
440
441     return mInterruptCallback;
442 }
443
444
445 void CAudioIO::ignoreSession() throw (CAudioError) {
446     if (__mIsInit == false) {
447         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
448     }
449
450     try {
451         internalLock();
452
453         if (mpPulseAudioClient != NULL && mpPulseAudioClient->isCorked() == false) {
454             THROW_ERROR_MSG(CAudioError::ERROR_INVALID_OPERATION, "An Operation is not permitted while started");
455         }
456
457         bool isSkip = mpAudioSessionHandler->isSkipSessionEvent();
458         if (isSkip == false && mpAudioSessionHandler->getId() >= 0) {
459             mpAudioSessionHandler->unregisterSound();
460             __mForceIgnore = true;
461         }
462
463         internalUnlock();
464     } catch (CAudioError e) {
465         internalUnlock();
466         throw e;
467     }
468 }