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