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